source: src/mlx/web.py@ 908:fcf3c44650f1

Last change on this file since 908:fcf3c44650f1 was 908:fcf3c44650f1, checked in by István Váradi <ivaradi@…>, 6 years ago

Basic functions work with the new website (re #332)

File size: 54.6 KB
Line 
1
2import const
3import util
4from rpc import Registration
5import rpc
6import rpccommon
7
8from common import MAVA_BASE_URL
9from pirep import PIREP
10
11import threading
12import sys
13import urllib
14import urllib2
15import hashlib
16import time
17import datetime
18import codecs
19import traceback
20import xml.sax
21import xmlrpclib
22import HTMLParser
23
24#---------------------------------------------------------------------------------------
25
26## @package mlx.web
27#
28# Web interface.
29#
30# This module implements a thread that can perform (HTTP) requests
31# asynchronously. When the request is performed, a callback is called. The main
32# interface is the \ref Handler class. Each of its functions creates a \ref
33# Request subclass instance and puts it to the request queue. The handler
34# thread then takes the requests one by one, and executes them.
35#
36# This module also defines some data classes the contents of which are
37# retrieved or sent via HTTP. \ref BookedFlight contains data of a flight
38# booked on the MAVA website, \ref Fleet and \ref Plane represents the MAVA
39# fleet and the gates at Ferihegy and \ref NOTAM is a NOTAM.
40
41#---------------------------------------------------------------------------------------
42
43def readline(f):
44 """Read a line from the given file.
45
46 The line is stripped and empty lines are discarded."""
47 while True:
48 line = f.readline()
49 if not line: return ""
50 line = line.strip()
51 if line:
52 return line
53
54#---------------------------------------------------------------------------------------
55
56class BookedFlight(object):
57 """A flight that was booked."""
58 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
59 "73G" : const.AIRCRAFT_B737,
60 "738" : const.AIRCRAFT_B738,
61 "73H" : const.AIRCRAFT_B738C,
62 "732" : const.AIRCRAFT_B732,
63 "733" : const.AIRCRAFT_B733,
64 "734" : const.AIRCRAFT_B734,
65 "735" : const.AIRCRAFT_B735,
66 "DH4" : const.AIRCRAFT_DH8D,
67 "762" : const.AIRCRAFT_B762,
68 "763" : const.AIRCRAFT_B763,
69 "CR2" : const.AIRCRAFT_CRJ2,
70 "F70" : const.AIRCRAFT_F70,
71 "LI2" : const.AIRCRAFT_DC3,
72 "TU3" : const.AIRCRAFT_T134,
73 "TU5" : const.AIRCRAFT_T154,
74 "YK4" : const.AIRCRAFT_YK40,
75 "146" : const.AIRCRAFT_B462,
76 "IL6" : const.AIRCRAFT_IL62 }
77
78 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "736",
79 const.AIRCRAFT_B737 : "73G",
80 const.AIRCRAFT_B738 : "738",
81 const.AIRCRAFT_B738C : "73H",
82 const.AIRCRAFT_B732 : "732",
83 const.AIRCRAFT_B733 : "733",
84 const.AIRCRAFT_B734 : "734",
85 const.AIRCRAFT_B735 : "735",
86 const.AIRCRAFT_DH8D : "DH4",
87 const.AIRCRAFT_B762 : "762",
88 const.AIRCRAFT_B763 : "763",
89 const.AIRCRAFT_CRJ2 : "CR2",
90 const.AIRCRAFT_F70 : "F70",
91 const.AIRCRAFT_DC3 : "LI2",
92 const.AIRCRAFT_T134 : "TU3",
93 const.AIRCRAFT_T154 : "TU5",
94 const.AIRCRAFT_YK40 : "YK4",
95 const.AIRCRAFT_B462 : "146",
96 const.AIRCRAFT_IL62 : "IL6" }
97
98 checkFlightTypes = [ const.AIRCRAFT_B736, const.AIRCRAFT_B737,
99 const.AIRCRAFT_B738, const.AIRCRAFT_DH8D ]
100
101 STATUS_BOOKED = 1
102
103 STATUS_REPORTED = 2
104
105 STATUS_ACCEPTED = 3
106
107 STATUS_REJECTED = 4
108
109 @staticmethod
110 def getDateTime(date, time):
111 """Get a datetime object from the given textual date and time."""
112 return datetime.datetime.strptime(date + " " + time,
113 "%Y-%m-%d %H:%M:%S")
114
115 @staticmethod
116 def forCheckFlight(aircraftType):
117 """Create a booked flight for a check flight with the given aircraft
118 type."""
119 flight = BookedFlight()
120
121 flight.departureICAO = "LHBP"
122 flight.arrivalICAO = "LHBP"
123
124 flight.aircraftType = aircraftType
125 flight.aircraftTypeName = BookedFlight.TYPE2TYPECODE[aircraftType]
126
127 # FIXME: perhaps find one for the type
128 flight.tailNumber = "HA-CHK"
129 flight.callsign = "HA-CHK"
130
131 flight.numPassengers = 0
132 flight.numCockpitCrew = 2
133 flight.numCabinCrew = 2
134 flight.numCrew = 2
135 flight.bagWeight = 0
136 flight.cargoWeight = 0
137 flight.mailWeight = 0
138 flight.route = "DCT"
139
140 t = datetime.datetime.now() + datetime.timedelta(minutes = 20)
141 flight.departureTime = datetime.datetime(t.year, t.month, t.day,
142 t.hour, t.minute)
143 t = flight.departureTime + datetime.timedelta(minutes = 30)
144 flight.arrivalTime = datetime.datetime(t.year, t.month, t.day,
145 t.hour, t.minute)
146
147 return flight
148
149 def __init__(self, id = None):
150 """Construct a booked flight with the given ID."""
151 self.id = id
152
153 @property
154 def status(self):
155 """Get the status of the flight.
156
157 For web-based flights this is always STATUS_BOOKED."""
158 return BookedFlight.STATUS_BOOKED
159
160 def readFromWeb(self, f):
161 """Read the data of the flight from the web via the given file
162 object."""
163 self.callsign = readline(f)
164
165 date = readline(f)
166 print "web.BookedFlight.readFromWeb: date:", date
167 if date=="0000-00-00": date = "0001-01-01"
168
169 self.departureICAO = readline(f)
170 self.arrivalICAO = readline(f)
171
172 self._readAircraftType(f)
173 self.tailNumber = readline(f)
174 self.numPassengers = int(readline(f))
175 self.numCrew = int(readline(f))
176 self.bagWeight = int(readline(f))
177 self.cargoWeight = int(readline(f))
178 self.mailWeight = int(readline(f))
179 self.route = readline(f)
180
181 departureTime = readline(f)
182 self.departureTime = BookedFlight.getDateTime(date, departureTime)
183
184 arrivalTime = readline(f)
185 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
186 if self.arrivalTime<self.departureTime:
187 self.arrivalTime += datetime.timedelta(days = 1)
188
189 if not readline(f)==".NEXT.":
190 raise Exception("Invalid line in flight data")
191
192 def readFromFile(self, f):
193 """Read the data of the flight from a file via the given file
194 object."""
195 date = None
196 departureTime = None
197 arrivalTime = None
198
199 line = f.readline()
200 lineNumber = 0
201 while line:
202 lineNumber += 1
203 line = line.strip()
204
205 hashIndex = line.find("#")
206 if hashIndex>=0: line = line[:hashIndex]
207 if line:
208 equalIndex = line.find("=")
209 lineOK = equalIndex>0
210
211 if lineOK:
212 key = line[:equalIndex].strip()
213 value = line[equalIndex+1:].strip().replace("\:", ":")
214
215 lineOK = key and value
216
217 if lineOK:
218 if key=="callsign": self.callsign = value
219 elif key=="date": date = value
220 elif key=="dep_airport": self.departureICAO = value
221 elif key=="dest_airport": self.arrivalICAO = value
222 elif key=="planecode": self.aircraftType = \
223 self._decodeAircraftType(value)
224 elif key=="planetype": self.aircraftTypeName = value
225 elif key=="tail_nr": self.tailNumber = value
226 elif key=="passenger": self.numPassengers = int(value)
227 elif key=="crew": self.numCrew = int(value)
228 elif key=="bag": self.bagWeight = int(value)
229 elif key=="cargo": self.cargoWeight = int(value)
230 elif key=="mail": self.mailWeight = int(value)
231 elif key=="flight_route": self.route = value
232 elif key=="departure_time": departureTime = value
233 elif key=="arrival_time": arrivalTime = value
234 elif key=="foglalas_id":
235 self.id = None if value=="0" else value
236 else: lineOK = False
237
238 if not lineOK:
239 print "web.BookedFlight.readFromFile: line %d is invalid" % \
240 (lineNumber,)
241
242 line = f.readline()
243
244 if date is not None:
245 if departureTime is not None:
246 self.departureTime = BookedFlight.getDateTime(date,
247 departureTime)
248 if arrivalTime is not None:
249 self.arrivalTime = BookedFlight.getDateTime(date,
250 arrivalTime)
251
252 self.numCockpitCrew = self.numCrew
253 self.numCabinCrew = 0
254
255 d = dir(self)
256 for attribute in ["callsign", "departureICAO", "arrivalICAO",
257 "aircraftType", "tailNumber", "numPassengers",
258 "numCrew", "numCockpitCrew", "numCabinCrew",
259 "bagWeight", "cargoWeight", "mailWeight",
260 "route", "departureTime", "arrivalTime"]:
261 if attribute not in d:
262 raise Exception("Attribute %s could not be read" % (attribute,))
263
264 if "aircraftTypeName" not in d:
265 self.aircraftTypeName = \
266 BookedFlight.TYPE2TYPECODE[self.aircraftType]
267
268 def setupFromPIREPData(self, pirepData):
269 """Setup the booked flight from the given PIREP data."""
270 bookedFlightData = pirepData["bookedFlight"]
271
272 self.callsign = bookedFlightData["callsign"]
273
274 date = bookedFlightData["date"]
275
276 departureTime = bookedFlightData["departureTime"]
277 self.departureTime = BookedFlight.getDateTime(date, departureTime)
278
279 arrivalTime = bookedFlightData["arrivalTime"]
280 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
281 if self.arrivalTime<self.departureTime:
282 self.arrivalTime += datetime.timedelta(days = 1)
283
284 self.departureICAO = bookedFlightData["departureICAO"]
285 self.arrivalICAO = bookedFlightData["arrivalICAO"]
286
287 self.aircraftType = \
288 self._decodeAircraftICAOType(bookedFlightData["aircraftType"])
289 self.tailNumber = bookedFlightData["tailNumber"]
290 self.numPassengers = int(bookedFlightData["numPassengers"])
291 self.numCockpitCrew = int(bookedFlightData["numCockpitCrew"])
292 self.numCabinCrew = int(bookedFlightData["numCabinCrew"])
293 self.numCrew = self.numCockpitCrew + self.numCabinCrew
294 self.bagWeight = int(bookedFlightData["bagWeight"])
295 self.cargoWeight = int(bookedFlightData["cargoWeight"])
296 self.mailWeight = int(bookedFlightData["mailWeight"])
297 self.route = bookedFlightData["route"]
298
299 def writeIntoFile(self, f):
300 """Write the flight into a file."""
301 print >> f, "callsign=%s" % (self.callsign,)
302 date = self.departureTime.date()
303 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
304 print >> f, "dep_airport=%s" % (self.departureICAO,)
305 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
306 print >> f, "planecode=%s" % \
307 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
308 print >> f, "planetype=%s" % (self.aircraftTypeName,)
309 print >> f, "tail_nr=%s" % (self.tailNumber,)
310 print >> f, "passenger=%d" % (self.numPassengers,)
311 print >> f, "crew=%d" % (self.numCrew,)
312 print >> f, "bag=%d" % (self.bagWeight,)
313 print >> f, "cargo=%d" % (self.cargoWeight,)
314 print >> f, "mail=%d" % (self.mailWeight,)
315 print >> f, "flight_route=%s" % (self.route,)
316 departureTime = self.departureTime
317 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
318 (departureTime.hour, departureTime.minute, departureTime.second)
319 arrivalTime = self.arrivalTime
320 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
321 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
322 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
323
324 def _readAircraftType(self, f):
325 """Read the aircraft type from the given file."""
326 line = readline(f)
327 typeCode = line[:3]
328 self.aircraftType = self._decodeAircraftType(typeCode)
329 self.aircraftTypeName = line[3:]
330
331 def _decodeAircraftType(self, typeCode):
332 """Decode the aircraft type from the given typeCode."""
333 if typeCode in self.TYPECODE2TYPE:
334 return self.TYPECODE2TYPE[typeCode]
335 else:
336 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
337
338 def _decodeAircraftICAOType(self, typeCode):
339 """Decode the aircraft type from the given typeCode."""
340 if typeCode in const.icao2Type:
341 return const.icao2Type[typeCode]
342 else:
343 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
344
345 def __repr__(self):
346 """Get a representation of the flight."""
347 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
348 self.arrivalICAO,
349 self.route,
350 self.departureTime, self.arrivalTime)
351 s += " %d %s," % (self.aircraftType, self.tailNumber)
352 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
353 (self.numPassengers, self.numCrew,
354 self.bagWeight, self.cargoWeight, self.mailWeight)
355 s += ">"
356 return s
357
358#------------------------------------------------------------------------------
359
360class Plane(rpccommon.Plane):
361 """Information about an airplane in the fleet."""
362 def __init__(self, s):
363 """Build a plane info based on the given string.
364
365 The string consists of three, space-separated fields.
366 The first field is the tail number, the second field is the gate
367 number, the third field is the plane's status as a character."""
368 super(Plane, self).__init__()
369
370 try:
371 words = s.split(" ")
372 tailNumber = words[0]
373 self.tailNumber = tailNumber
374
375 status = words[2] if len(words)>2 else None
376 self._setStatus(status)
377
378 gateNumber = words[1] if len(words)>1 else ""
379 self.gateNumber = gateNumber if gateNumber else None
380
381 except:
382 print >> sys.stderr, "Plane string is invalid: '" + s + "'"
383 self.tailNumber = None
384
385#------------------------------------------------------------------------------
386
387class Fleet(rpccommon.Fleet):
388 """Information about the whole fleet."""
389 def __init__(self, f):
390 """Construct the fleet information by reading the given file object."""
391 super(Fleet, self).__init__()
392
393 while True:
394 line = readline(f)
395 if not line or line == "#END": break
396
397 plane = Plane(line)
398 self._addPlane(plane)
399
400#------------------------------------------------------------------------------
401
402class NOTAM(object):
403 """A NOTAM for an airport."""
404 def __init__(self, ident, basic,
405 begin, notice, end = None, permanent = False,
406 repeatCycle = None):
407 """Construct the NOTAM."""
408 self.ident = ident
409 self.basic = basic
410 self.begin = begin
411 self.notice = notice
412 self.end = end
413 self.permanent = permanent
414 self.repeatCycle = repeatCycle
415
416 def __repr__(self):
417 """Get the representation of the NOTAM."""
418 s = "<NOTAM " + str(self.begin)
419 if self.end:
420 s += " - " + str(self.end)
421 elif self.permanent:
422 s += " - PERMANENT"
423 if self.repeatCycle:
424 s += " (" + self.repeatCycle + ")"
425 s += ": " + self.notice
426 s += ">"
427 return s
428
429 def __str__(self):
430 """Get the string representation of the NOTAM."""
431 s = ""
432 s += str(self.ident) + " " + str(self.basic) + "\n"
433 s += str(self.begin)
434 if self.end is not None:
435 s += " - " + str(self.end)
436 elif self.permanent:
437 s += " - PERMANENT"
438 s += "\n"
439 if self.repeatCycle:
440 s += "Repeat cycle: " + self.repeatCycle + "\n"
441 s += self.notice + "\n"
442 return s
443
444#------------------------------------------------------------------------------
445
446class NOTAMHandler(xml.sax.handler.ContentHandler):
447 """A handler for the NOTAM database."""
448 def __init__(self, airportICAOs):
449 """Construct the handler for the airports with the given ICAO code."""
450 self._notams = {}
451 for icao in airportICAOs:
452 self._notams[icao] = []
453
454 def startElement(self, name, attrs):
455 """Start an element."""
456 if name!="notam" or \
457 "ident" not in attrs or not attrs["ident"] or \
458 "Q" not in attrs or not attrs["Q"] or \
459 "A" not in attrs or not attrs["A"] or \
460 "B" not in attrs or not attrs["B"] or \
461 "E" not in attrs or not attrs["E"]:
462 return
463
464 icao = attrs["A"]
465 if icao not in self._notams:
466 return
467
468 begin = datetime.datetime.strptime(attrs["B"], "%Y-%m-%d %H:%M:%S")
469
470 c = attrs["C"] if "C" in attrs else None
471 end = datetime.datetime.strptime(c, "%Y-%m-%d %H:%M:%S") if c else None
472
473 permanent = attrs["C_flag"]=="PERM" if "C_flag" in attrs else False
474
475 repeatCycle = attrs["D"] if "D" in attrs else None
476
477 self._notams[icao].append(NOTAM(attrs["ident"], attrs["Q"],
478 begin, attrs["E"], end = end,
479 permanent = permanent,
480 repeatCycle = repeatCycle))
481
482 def get(self, icao):
483 """Get the NOTAMs for the given ICAO code."""
484 return self._notams[icao] if icao in self._notams else []
485
486#------------------------------------------------------------------------------
487
488class PilotsWebNOTAMsParser(HTMLParser.HTMLParser):
489 """XML handler for the NOTAM query results on the PilotsWeb website."""
490 def __init__(self):
491 """Construct the handler."""
492 HTMLParser.HTMLParser.__init__(self)
493
494 self._notams = []
495 self._currentNOTAM = ""
496 self._stage = 0
497
498 def handle_starttag(self, name, attrs):
499 """Start an element."""
500 if (self._stage==0 and name=="div" and ("id", "notamRight") in attrs) or \
501 (self._stage==1 and name=="span") or \
502 (self._stage==2 and name=="pre"):
503 self._stage += 1
504 if self._stage==1:
505 self._currentNOTAM = ""
506
507 def handle_data(self, content):
508 """Handle characters"""
509 if self._stage==3:
510 self._currentNOTAM += content
511
512 def handle_endtag(self, name):
513 """End an element."""
514 if (self._stage==3 and name=="pre") or \
515 (self._stage==2 and name=="span") or \
516 (self._stage==1 and name=="div"):
517 self._stage -= 1
518 if self._stage==0:
519 self._processCurrentNOTAM()
520
521 def getNOTAMs(self):
522 """Get the NOTAMs collected"""
523 return self._notams
524
525 def _processCurrentNOTAM(self):
526 """Parse the current NOTAM and append its contents to the list of
527 NOTAMS."""
528 notam = None
529 try:
530 notam = self._parseCurrentNOTAM2()
531 except Exception, e:
532 print "Error parsing current NOTAM: " + str(e)
533
534 if notam is None:
535 print "Could not parse NOTAM: " + self._currentNOTAM
536 if self._currentNOTAM:
537 self._notams.append(self._currentNOTAM + "\n")
538 else:
539 self._notams.append(notam)
540
541 def _parseCurrentNOTAM(self):
542 """Parse the current NOTAM, if possible, and return a NOTAM object."""
543 lines = self._currentNOTAM.splitlines()
544 lines = map(lambda line: line.strip(), lines)
545
546 if len(lines)<4:
547 return None
548
549 if not lines[1].startswith("Q)") or \
550 not lines[2].startswith("A)") or \
551 not (lines[3].startswith("E)") or
552 (lines[3].startswith("D)") and lines[4].startswith("E)"))):
553 return None
554
555 ident = lines[0].split()[0]
556 basic = lines[1][2:].strip()
557
558 words = lines[2].split()
559 if len(words)<4 or words[0]!="A)" or words[2]!="B)":
560 return None
561
562 begin = datetime.datetime.strptime(words[3], "%y%m%d%H%M")
563 end = None
564 permanent = False
565 if words[4]=="C)" and len(words)>=6:
566 if words[5] in ["PERM", "UFN"]:
567 permanent = True
568 else:
569 end = datetime.datetime.strptime(words[5], "%y%m%d%H%M")
570 else:
571 permanent = True
572
573 repeatCycle = None
574 noticeStartIndex = 3
575 if lines[3].startswith("D)"):
576 repeatCycle = lines[3][2:].strip()
577 noticeStartIndex = 4
578
579 notice = ""
580 for index in range(noticeStartIndex, len(lines)):
581 line = lines[index][2:] if index==noticeStartIndex else lines[index]
582 line = line.strip()
583
584 if line.lower().startswith("created:") or \
585 line.lower().startswith("source:"):
586 break
587
588 if notice: notice += " "
589 notice += line
590
591 return NOTAM(ident, basic, begin, notice, end = end,
592 permanent = permanent, repeatCycle = repeatCycle)
593
594 def _parseCurrentNOTAM2(self):
595 """Parse the current NOTAM with a second, more flexible method."""
596 lines = self._currentNOTAM.splitlines()
597 lines = map(lambda line: line.strip(), lines)
598
599 if not lines:
600 return None
601
602 ident = lines[0].split()[0]
603
604 lines = lines[1:]
605 for i in range(0, 2):
606 l = lines[-1].lower()
607 if l.startswith("created:") or l.startswith("source:"):
608 lines = lines[:-1]
609
610 lines = map(lambda line: line.strip(), lines)
611 contents = " ".join(lines).split()
612
613 items = {}
614 for i in ["Q)", "A)", "B)", "C)", "D)", "E)"]:
615 items[i] = ""
616
617 currentItem = None
618 for word in contents:
619 if word in items:
620 currentItem = word
621 elif currentItem in items:
622 s = items[currentItem]
623 if s: s+= " "
624 s += word
625 items[currentItem] = s
626
627 if not items["Q)"] or not items["A)"] or not items["B)"] or \
628 not items["E)"]:
629 return None
630
631 def parseTime(item):
632 try:
633 return datetime.datetime.strptime(item, "%y%m%d%H%M")
634 except ValueError:
635 return datetime.datetime.strptime(item, "%Y%m%d%H%M")
636
637 basic = items["Q)"]
638 begin = parseTime(items["B)"])
639
640 end = None
641 permanent = False
642 if items["C)"]:
643 endItem = items["C)"]
644 if endItem in ["PERM", "UFN"]:
645 permanent = True
646 else:
647 end = parseTime(items["C)"])
648 else:
649 permanent = True
650
651 repeatCycle = None
652 if items["D)"]:
653 repeatCycle = items["D)"]
654
655 notice = items["E)"]
656
657 return NOTAM(ident, basic, begin, notice, end = end,
658 permanent = permanent, repeatCycle = repeatCycle)
659
660#------------------------------------------------------------------------------
661
662class Result(object):
663 """A result object.
664
665 An instance of this filled with the appropriate data is passed to the
666 callback function on each request."""
667
668 def __repr__(self):
669 """Get a representation of the result."""
670 s = "<Result:"
671 for (key, value) in self.__dict__.iteritems():
672 s += " " + key + "=" + unicode(value)
673 s += ">"
674 return s
675
676#------------------------------------------------------------------------------
677
678class Request(object):
679 """Base class for requests.
680
681 It handles any exceptions and the calling of the callback.
682
683 If an exception occurs during processing, the callback is called with
684 the two parameters: a boolean value of False, and the exception object.
685
686 If no exception occurs, the callback is called with True and the return
687 value of the run() function.
688
689 If the callback function throws an exception, that is caught and logged
690 to the debug log."""
691 def __init__(self, callback):
692 """Construct the request."""
693 self._callback = callback
694
695 def perform(self):
696 """Perform the request.
697
698 The object's run() function is called. If it throws an exception,
699 the callback is called with False, and the exception. Otherwise the
700 callback is called with True and the return value of the run()
701 function. Any exceptions thrown by the callback are caught and
702 reported."""
703 try:
704 result = self.run()
705 returned = True
706 except Exception, e:
707 traceback.print_exc()
708 result = e
709 returned = False
710
711 try:
712 self._callback(returned, result)
713 except Exception, e:
714 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + util.utf2unicode(str(e))
715 #traceback.print_exc()
716
717#------------------------------------------------------------------------------
718
719class RPCRequest(Request):
720 """Common base class for RPC requests.
721
722 It stores the RPC client received from the handler."""
723 def __init__(self, client, callback):
724 """Construct the request."""
725 super(RPCRequest, self).__init__(callback)
726 self._client = client
727
728#------------------------------------------------------------------------------
729
730class Register(RPCRequest):
731 """A registration request."""
732 def __init__(self, client, callback, registrationData):
733 """Construct the request."""
734 super(Register, self).__init__(client, callback)
735 self._registrationData = registrationData
736
737 def run(self):
738 """Perform the registration."""
739
740 registrationData = self._registrationData
741
742 (resultCode, pilotID) = self._client.register(registrationData)
743 result = Result()
744 result.registered = resultCode==rpc.Client.RESULT_OK
745 if result.registered:
746 result.pilotID = pilotID
747
748 self._client.setCredentials(pilotID, registrationData.password)
749 LoginRPC.setupLoginResult(result, self._client, pilotID,
750 registrationData.password)
751
752 result.invalidData = \
753 resultCode==rpc.Client.RESULT_INVALID_DATA
754 result.emailAlreadyRegistered = \
755 resultCode==rpc.Client.RESULT_EMAIL_ALREADY_REGISTERED
756
757 return result
758
759#------------------------------------------------------------------------------
760
761class Login(Request):
762 """A login request."""
763 iso88592decoder = codecs.getdecoder("iso-8859-2")
764
765 def __init__(self, callback, pilotID, password):
766 """Construct the login request with the given pilot ID and
767 password."""
768 super(Login, self).__init__(callback)
769
770 self._pilotID = pilotID
771 self._password = password
772
773 def run(self):
774 """Perform the login request."""
775 md5 = hashlib.md5()
776 md5.update(self._pilotID)
777 pilotID = md5.hexdigest()
778
779 md5 = hashlib.md5()
780 md5.update(self._password)
781 password = md5.hexdigest()
782
783 url = MAVA_BASE_URL + "/leker2.php?pid=%s&psw=%s" % (pilotID, password)
784
785 result = Result()
786
787 f = urllib2.urlopen(url, timeout = 10.0)
788
789 status = readline(f)
790 result.loggedIn = status == ".OK."
791
792 if result.loggedIn:
793 result.pilotID = self._pilotID
794 result.password = self._password
795 result.rank = "FO"
796 result.flights = []
797
798 result.pilotName = self.iso88592decoder(readline(f))[0]
799 result.exams = readline(f)
800
801 while True:
802 line = readline(f)
803 if not line or line == "#ENDPIREP": break
804
805 flight = BookedFlight(line)
806 flight.readFromWeb(f)
807 result.flights.append(flight)
808
809 result.flights.sort(cmp = lambda flight1, flight2:
810 cmp(flight1.departureTime,
811 flight2.departureTime))
812
813 f.close()
814
815 return result
816
817#------------------------------------------------------------------------------
818
819class LoginRPC(RPCRequest):
820 """An RPC-based login request."""
821 @staticmethod
822 def setupLoginResult(result, client, pilotID, password):
823 """Setup the login result with the given client, pilot ID and
824 password."""
825 loginResult = client.login()
826 result.loggedIn = loginResult is not None
827 if result.loggedIn:
828 result.pilotID = pilotID
829 result.pilotName = loginResult[0]
830 result.rank = loginResult[1]
831 result.types = loginResult[2]
832 result.password = password
833
834 result.aircraftTypes = client.getTypes()
835
836 flights = client.getFlights()
837 result.flights = flights[0]
838 result.reportedFlights = flights[1]
839 result.rejectedFlights = flights[2]
840
841 if result.rank=="STU":
842 reply = client.getEntryExamStatus()
843 result.entryExamPassed = reply[0]
844 result.entryExamLink = reply[1]
845 result.checkFlightStatus = reply[2]
846 if reply[3]:
847 result.rank = "FO"
848
849
850 def __init__(self, client, callback, pilotID, password):
851 """Construct the login request with the given pilot ID and
852 password."""
853 super(LoginRPC, self).__init__(client, callback)
854
855 self._pilotID = pilotID
856 self._password = password
857
858 def run(self):
859 """Perform the login request."""
860 result = Result()
861
862 self._client.setCredentials(self._pilotID, self._password)
863 LoginRPC.setupLoginResult(result, self._client,
864 self._pilotID, self._password)
865
866 return result
867
868#------------------------------------------------------------------------------
869
870class GetEntryExamStatus(RPCRequest):
871 """A request to get the entry exam status."""
872 def __init__(self, client, callback):
873 """Construct the request."""
874 super(GetEntryExamStatus, self).__init__(client, callback)
875
876 def run(self):
877 """Perform the query."""
878 result = Result()
879
880 reply = self._client.getEntryExamStatus()
881
882 result.entryExamPassed = reply[0]
883 result.entryExamLink = reply[1]
884 result.checkFlightStatus = reply[2]
885 result.madeFO = reply[3]
886
887 return result
888
889#------------------------------------------------------------------------------
890
891class GetFleet(Request):
892 """Request to get the fleet from the website."""
893
894 def __init__(self, callback):
895 """Construct the fleet request."""
896 super(GetFleet, self).__init__(callback)
897
898 def run(self):
899 """Perform the login request."""
900 url = MAVA_BASE_URL + "/onlinegates_get.php"
901
902 f = urllib2.urlopen(url, timeout = 10.0)
903 result = Result()
904 result.fleet = Fleet(f)
905 f.close()
906
907 return result
908
909#------------------------------------------------------------------------------
910
911class GetFleetRPC(RPCRequest):
912 """Request to get the fleet from the website using RPC."""
913 def __init__(self, client, callback):
914 """Construct the request with the given client and callback function."""
915 super(GetFleetRPC, self).__init__(client, callback)
916
917 def run(self):
918 """Perform the login request."""
919 result = Result()
920
921 result.fleet = self._client.getFleet()
922
923 return result
924
925#------------------------------------------------------------------------------
926
927class UpdatePlane(Request):
928 """Update the status of one of the planes in the fleet."""
929 def __init__(self, callback, tailNumber, status, gateNumber = None):
930 """Construct the request."""
931 super(UpdatePlane, self).__init__(callback)
932 self._tailNumber = tailNumber
933 self._status = status
934 self._gateNumber = gateNumber
935
936 def run(self):
937 """Perform the plane update."""
938 url = MAVA_BASE_URL + "/onlinegates_set.php"
939
940 status = Plane.status2str(self._status)
941
942 gateNumber = self._gateNumber if self._gateNumber else ""
943
944 data = urllib.urlencode([("lajstrom", self._tailNumber),
945 ("status", status),
946 ("kapu", gateNumber)])
947
948 f = urllib2.urlopen(url, data, timeout = 10.0)
949 line = readline(f)
950
951 result = Result()
952 result.success = line == "OK"
953
954 return result
955
956#------------------------------------------------------------------------------
957
958class UpdatePlaneRPC(RPCRequest):
959 """RPC request to update the status and the position of a plane in the
960 fleet."""
961 def __init__(self, client, callback, tailNumber, status, gateNumber = None):
962 """Construct the request."""
963 super(UpdatePlaneRPC, self).__init__(client, callback)
964 self._tailNumber = tailNumber
965 self._status = status
966 self._gateNumber = gateNumber
967
968 def run(self):
969 """Perform the plane update."""
970 self._client.updatePlane(self._tailNumber, self._status, self._gateNumber)
971
972 # Otherwise an exception is thrown
973 result = Result()
974 result.success = True
975
976 return result
977
978#------------------------------------------------------------------------------
979
980class GetNOTAMs(Request):
981 """Get the NOTAMs from EURoutePro and select the ones we are interested
982 in."""
983 def __init__(self, callback, departureICAO, arrivalICAO):
984 """Construct the request for the given airports."""
985 super(GetNOTAMs, self).__init__(callback)
986 self._departureICAO = departureICAO
987 self._arrivalICAO = arrivalICAO
988
989 def run(self):
990 """Perform the retrieval of the NOTAMs."""
991 departureNOTAMs = self.getPilotsWebNOTAMs(self._departureICAO)
992 arrivalNOTAMs = self.getPilotsWebNOTAMs(self._arrivalICAO)
993
994 icaos = []
995 if not departureNOTAMs: icaos.append(self._departureICAO)
996 if not arrivalNOTAMs: icaos.append(self._arrivalICAO)
997
998 if icaos:
999 xmlParser = xml.sax.make_parser()
1000 notamHandler = NOTAMHandler(icaos)
1001 xmlParser.setContentHandler(notamHandler)
1002
1003 url = "http://notams.euroutepro.com/notams.xml"
1004
1005 f = urllib2.urlopen(url, timeout = 10.0)
1006 try:
1007 xmlParser.parse(f)
1008 finally:
1009 f.close()
1010
1011 for icao in icaos:
1012 if icao==self._departureICAO:
1013 departureNOTAMs = notamHandler.get(icao)
1014 else:
1015 arrivalNOTAMs = notamHandler.get(icao)
1016
1017 result = Result()
1018 result.departureNOTAMs = departureNOTAMs
1019 result.arrivalNOTAMs = arrivalNOTAMs
1020
1021 return result
1022
1023 def getPilotsWebNOTAMs(self, icao):
1024 """Try to get the NOTAMs from FAA's PilotsWeb site for the given ICAO
1025 code.
1026
1027 Returns a list of PilotsWEBNOTAM objects, or None in case of an error."""
1028 try:
1029 parser = PilotsWebNOTAMsParser()
1030
1031 url = "https://pilotweb.nas.faa.gov/PilotWeb/notamRetrievalByICAOAction.do?method=displayByICAOs&formatType=ICAO&retrieveLocId=%s&reportType=RAW&actionType=notamRetrievalByICAOs" % \
1032 (icao.upper(),)
1033
1034 f = urllib2.urlopen(url, timeout = 10.0)
1035 try:
1036 data = f.read(16384)
1037 while data:
1038 parser.feed(data)
1039 data = f.read(16384)
1040 finally:
1041 f.close()
1042
1043 return parser.getNOTAMs()
1044
1045 except Exception, e:
1046 traceback.print_exc()
1047 print "mlx.web.GetNOTAMs.getPilotsWebNOTAMs: failed to get NOTAMs for '%s': %s" % \
1048 (icao, str(e))
1049 return None
1050
1051#------------------------------------------------------------------------------
1052
1053class GetMETARs(Request):
1054 """Get the METARs from the NOAA website for certain airport ICAOs."""
1055
1056 def __init__(self, callback, airports):
1057 """Construct the request for the given airports."""
1058 super(GetMETARs, self).__init__(callback)
1059 self._airports = airports
1060
1061 def run(self):
1062 """Perform the retrieval opf the METARs."""
1063 url = "http://www.aviationweather.gov/adds/dataserver_current/httpparam?"
1064 data = urllib.urlencode([ ("dataSource" , "metars"),
1065 ("requestType", "retrieve"),
1066 ("format", "csv"),
1067 ("stationString", " ".join(self._airports)),
1068 ("hoursBeforeNow", "24"),
1069 ("mostRecentForEachStation", "constraint")])
1070 url += data
1071 f = urllib2.urlopen(url, timeout = 10.0)
1072 try:
1073 result = Result()
1074 result.metars = {}
1075 for line in iter(f.readline, ""):
1076 if len(line)>5 and line[4]==' ':
1077 icao = line[0:4]
1078 if icao in self._airports:
1079 result.metars[icao] = line.strip().split(",")[0]
1080 finally:
1081 f.close()
1082
1083 return result
1084
1085#------------------------------------------------------------------------------
1086
1087class SendPIREP(Request):
1088 """A request to send a PIREP to the MAVA website."""
1089 _latin2Encoder = codecs.getencoder("iso-8859-2")
1090
1091 def __init__(self, callback, pirep):
1092 """Construct the sending of the PIREP."""
1093 super(SendPIREP, self).__init__(callback)
1094 self._pirep = pirep
1095
1096 def run(self):
1097 """Perform the sending of the PIREP."""
1098 url = MAVA_BASE_URL + "/malevacars.php"
1099
1100 pirep = self._pirep
1101
1102 data = {}
1103 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
1104
1105 bookedFlight = pirep.bookedFlight
1106 data["foglalas_id"] = bookedFlight.id
1107 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
1108 data["fltnum"] = bookedFlight.callsign
1109 data["depap"] = bookedFlight.departureICAO
1110 data["arrap"] = bookedFlight.arrivalICAO
1111 data["pass"] = str(pirep.numPassengers)
1112 data["crew"] = str(pirep.numCrew)
1113 data["cargo"] = str(pirep.cargoWeight)
1114 data["bag"] = str(pirep.bagWeight)
1115 data["mail"] = str(pirep.mailWeight)
1116
1117 data["flttype"] = pirep.flightTypeText
1118 data["onoff"] = "1" if pirep.online else "0"
1119 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
1120 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
1121 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
1122 pirep.blockTimeStart)
1123 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
1124 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
1125 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
1126 pirep.flightTimeStart)
1127 data["timecomm"] = pirep.getTimeComment()
1128 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
1129 data["dep_rwy"] = pirep.departureRunway
1130 data["arr_rwy"] = pirep.arrivalRunway
1131 data["wea_dep"] = pirep.departureMETAR
1132 data["wea_arr"] = pirep.arrivalMETAR
1133 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
1134 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
1135 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
1136 else:
1137 data["mod_alt"] = ""
1138 data["sid"] = pirep.sid
1139 data["navroute"] = pirep.route
1140 data["star"] = pirep.getSTAR()
1141 data["aprtype"] = pirep.approachType
1142 data["diff"] = "2"
1143 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
1144 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
1145 data["kritika"] = pirep.getRatingText()
1146 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
1147 data["distance"] = "%.3f" % (pirep.flownDistance,)
1148 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
1149
1150 postData = urllib.urlencode(data)
1151 f = urllib2.urlopen(url, postData, timeout = 10.0)
1152 try:
1153 result = Result()
1154 line = f.readline().strip()
1155 print "PIREP result from website:", line
1156 result.success = line=="OK"
1157 result.alreadyFlown = line=="MARVOLT"
1158 result.notAvailable = line=="NOMORE"
1159 finally:
1160 f.close()
1161
1162 return result
1163#------------------------------------------------------------------------------
1164
1165class SendPIREPRPC(RPCRequest):
1166 """A request to send a PIREP to the MAVA website via the RPC interface."""
1167
1168 def __init__(self, client, callback, pirep, update):
1169 """Construct the sending of the PIREP."""
1170 super(SendPIREPRPC, self).__init__(client, callback)
1171 self._pirep = pirep
1172 self._update = update
1173
1174 def run(self):
1175 """Perform the sending of the PIREP."""
1176 pirep = self._pirep
1177 resultCode = self._client.addPIREP(pirep.bookedFlight.id, pirep,
1178 self._update)
1179
1180 result = Result()
1181 result.success = resultCode==rpc.Client.RESULT_OK
1182 result.alreadyFlown = resultCode==rpc.Client.RESULT_FLIGHT_ALREADY_REPORTED
1183 result.notAvailable = resultCode==rpc.Client.RESULT_FLIGHT_NOT_EXISTS
1184
1185 return result
1186
1187#------------------------------------------------------------------------------
1188
1189class SendACARS(Request):
1190 """A request to send an ACARS to the MAVA website."""
1191 _latin2Encoder = codecs.getencoder("iso-8859-2")
1192
1193 def __init__(self, callback, acars):
1194 """Construct the request for the given PIREP."""
1195 super(SendACARS, self).__init__(callback)
1196 self._acars = acars
1197
1198 def run(self):
1199 """Perform the sending of the ACARS."""
1200 print "Sending the online ACARS"
1201
1202 url = MAVA_BASE_URL + "/acars2/acarsonline.php"
1203
1204 acars = self._acars
1205 bookedFlight = acars.bookedFlight
1206
1207 data = {}
1208 data["pid"] = acars.pid
1209 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
1210
1211 data["pass"] = str(bookedFlight.numPassengers)
1212 data["callsign"] = bookedFlight.callsign
1213 data["airplane"] = bookedFlight.aircraftTypeName
1214 data["from"] = bookedFlight.departureICAO
1215 data["to"] = bookedFlight.arrivalICAO
1216 data["lajstrom"] = bookedFlight.tailNumber
1217
1218 data["block_time"] = acars.getBlockTimeText()
1219 data["longitude"] = str(acars.state.longitude)
1220 data["latitude"] = str(acars.state.latitude)
1221 data["altitude"] = str(acars.state.altitude)
1222 data["speed"] = str(acars.state.groundSpeed)
1223
1224 data["event"] = acars.getEventText()
1225
1226 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
1227 try:
1228 result = Result()
1229 finally:
1230 f.close()
1231
1232 return result
1233
1234#------------------------------------------------------------------------------
1235
1236class SendACARSRPC(RPCRequest):
1237 """A request to send an ACARS to the MAVA website via JSON-RPC."""
1238 def __init__(self, client, callback, acars):
1239 """Construct the request for the given PIREP."""
1240 super(SendACARSRPC, self).__init__(client, callback)
1241 self._acars = acars
1242
1243 def run(self):
1244 """Perform the sending of the ACARS."""
1245 print "Sending the online ACARS via JSON-RPC"
1246
1247 self._client.updateOnlineACARS(self._acars)
1248 return Result()
1249
1250#------------------------------------------------------------------------------
1251
1252class SendBugReport(Request):
1253 """A request to send a bug report to the project homepage."""
1254 _latin2Encoder = codecs.getencoder("iso-8859-2")
1255
1256 def __init__(self, callback, summary, description, email):
1257 """Construct the request for the given bug report."""
1258 super(SendBugReport, self).__init__(callback)
1259 self._summary = summary
1260 self._description = description
1261 self._email = email
1262
1263 def run(self):
1264 """Perform the sending of the bug report."""
1265 serverProxy = xmlrpclib.ServerProxy("http://mlx.varadiistvan.hu/rpc")
1266
1267 result = Result()
1268 result.success = False
1269
1270 attributes = {}
1271 if self._email:
1272 attributes["reporter"] = self._email
1273
1274 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
1275 attributes, True)
1276 print "Created ticket with ID:", result.ticketID
1277 result.success = True
1278
1279 return result
1280
1281#------------------------------------------------------------------------------
1282
1283class SetCheckFlightPassed(RPCRequest):
1284 """A request to mark the user as one having passed the check flight."""
1285 def __init__(self, client, callback, aircraftType):
1286 """Construct the request for the given type."""
1287 super(SetCheckFlightPassed, self).__init__(client, callback)
1288 self._aircraftType = aircraftType
1289
1290 def run(self):
1291 """Perform the update."""
1292 aircraftType = BookedFlight.TYPE2TYPECODE[self._aircraftType]
1293 self._client.setCheckFlightPassed(aircraftType)
1294 return Result()
1295
1296#------------------------------------------------------------------------------
1297
1298class GetPIREP(RPCRequest):
1299 """A request to retrieve the PIREP of a certain flight."""
1300 def __init__(self, client, callback, flightID):
1301 """Construct the request."""
1302 super(GetPIREP, self).__init__(client, callback)
1303 self._flightID = flightID
1304
1305 def run(self):
1306 """Perform the update."""
1307 result = Result()
1308
1309 pirepData = self._client.getPIREP(self._flightID)
1310 print "pirepData:", pirepData
1311
1312 bookedFlight = BookedFlight(self._flightID)
1313 bookedFlight.setupFromPIREPData(pirepData)
1314
1315 result.pirep = PIREP(None)
1316 result.pirep.setupFromPIREPData(pirepData, bookedFlight)
1317
1318 return result
1319
1320#------------------------------------------------------------------------------
1321
1322class ReflyFlights(RPCRequest):
1323 """A request to mark certain flights for reflying."""
1324 def __init__(self, client, callback, flightIDs):
1325 """Construct the request."""
1326 super(ReflyFlights, self).__init__(client, callback)
1327 self._flightIDs = flightIDs
1328
1329 def run(self):
1330 """Perform the update."""
1331 self._client.reflyFlights(self._flightIDs)
1332 return Result()
1333
1334#------------------------------------------------------------------------------
1335
1336class DeleteFlights(RPCRequest):
1337 """A request to delete certain flights."""
1338 def __init__(self, client, callback, flightIDs):
1339 """Construct the request."""
1340 super(DeleteFlights, self).__init__(client, callback)
1341 self._flightIDs = flightIDs
1342
1343 def run(self):
1344 """Perform the update."""
1345 self._client.deleteFlights(self._flightIDs)
1346 return Result()
1347
1348#------------------------------------------------------------------------------
1349
1350class GetAcceptedFlights(RPCRequest):
1351 """Request to get the accepted flights."""
1352 def __init__(self, client, callback):
1353 """Construct the request with the given client and callback function."""
1354 super(GetAcceptedFlights, self).__init__(client, callback)
1355
1356 def run(self):
1357 """Perform the login request."""
1358 result = Result()
1359
1360 result.flights = self._client.getAcceptedFlights()
1361
1362 return result
1363
1364#------------------------------------------------------------------------------
1365
1366class GetTimetable(RPCRequest):
1367 """Request to get the timetable."""
1368 def __init__(self, client, callback, date, types):
1369 """Construct the request with the given client and callback function."""
1370 super(GetTimetable, self).__init__(client, callback)
1371 self._date = date
1372 self._types = types
1373
1374 def run(self):
1375 """Perform the login request."""
1376 result = Result()
1377
1378 result.flightPairs = self._client.getTimetable(self._date, self._types)
1379
1380 return result
1381
1382#------------------------------------------------------------------------------
1383
1384class BookFlights(RPCRequest):
1385 """Request to book flights."""
1386 def __init__(self, client, callback, flightIDs, date, tailNumber):
1387 """Construct the request with the given client and callback function."""
1388 super(BookFlights, self).__init__(client, callback)
1389 self._flightIDs = flightIDs
1390 self._date = date
1391 self._tailNumber = tailNumber
1392
1393 def run(self):
1394 """Perform the login request."""
1395 result = Result()
1396
1397 result.bookedFlights = self._client.bookFlights(self._flightIDs,
1398 self._date,
1399 self._tailNumber)
1400
1401 return result
1402
1403#------------------------------------------------------------------------------
1404
1405class Handler(threading.Thread):
1406 """The handler for the web services.
1407
1408 It can process one request at a time. The results are passed to a callback
1409 function."""
1410 def __init__(self, config, getCredentialsFn):
1411 """Construct the handler."""
1412 super(Handler, self).__init__()
1413
1414 self._requests = []
1415 self._requestCondition = threading.Condition()
1416
1417 self.daemon = True
1418 self._config = config
1419 self._rpcClient = rpc.Client(getCredentialsFn)
1420 if config.rememberPassword:
1421 self._rpcClient.setCredentials(config.pilotID, config.password)
1422
1423 def register(self, callback, registrationData):
1424 """Enqueue a registration request."""
1425 self._addRequest(Register(self._rpcClient, callback, registrationData))
1426
1427 def login(self, callback, pilotID, password):
1428 """Enqueue a login request."""
1429 request = \
1430 LoginRPC(self._rpcClient, callback, pilotID, password) \
1431 if self._config.useRPC else Login(callback, pilotID, password)
1432
1433 self._addRequest(request)
1434
1435 def getEntryExamStatus(self, callback):
1436 """Get the entry exam status."""
1437 self._addRequest(GetEntryExamStatus(self._rpcClient, callback))
1438
1439 def getFleet(self, callback):
1440 """Enqueue a fleet retrieval request."""
1441 request = \
1442 GetFleetRPC(self._rpcClient, callback,) if self._config.useRPC \
1443 else GetFleet(callback)
1444 self._addRequest(request)
1445
1446 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
1447 """Update the status of the given plane."""
1448 request = \
1449 UpdatePlaneRPC(self._rpcClient, callback,
1450 tailNumber, status, gateNumber) \
1451 if self._config.useRPC \
1452 else UpdatePlane(callback, tailNumber, status, gateNumber)
1453 self._addRequest(request)
1454
1455 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
1456 """Get the NOTAMs for the given two airports."""
1457 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
1458
1459 def getMETARs(self, callback, airports):
1460 """Get the METARs for the given airports."""
1461 self._addRequest(GetMETARs(callback, airports))
1462
1463 def sendPIREP(self, callback, pirep, update = False):
1464 """Send the given PIREP."""
1465 request = \
1466 SendPIREPRPC(self._rpcClient, callback, pirep, update) \
1467 if self._config.useRPC else SendPIREP(callback, pirep)
1468 self._addRequest(request)
1469
1470 def sendACARS(self, callback, acars):
1471 """Send the given ACARS"""
1472 request = \
1473 SendACARSRPC(self._rpcClient, callback, acars) if self._config.useRPC \
1474 else SendACARS(callback, acars)
1475 self._addRequest(request)
1476
1477 def sendBugReport(self, callback, summary, description, email):
1478 """Send a bug report with the given data."""
1479 self._addRequest(SendBugReport(callback, summary, description, email))
1480
1481 def setCheckFlightPassed(self, callback, aircraftType):
1482 """Mark the check flight as passed."""
1483 self._addRequest(SetCheckFlightPassed(self._rpcClient,
1484 callback, aircraftType))
1485
1486 def getPIREP(self, callback, flightID):
1487 """Query the PIREP for the given flight."""
1488 self._addRequest(GetPIREP(self._rpcClient, callback, flightID))
1489
1490 def reflyFlights(self, callback, flightIDs):
1491 """Mark the flights with the given IDs for reflying."""
1492 self._addRequest(ReflyFlights(self._rpcClient, callback, flightIDs))
1493
1494 def deleteFlights(self, callback, flightIDs):
1495 """Delete the flights with the given IDs."""
1496 self._addRequest(DeleteFlights(self._rpcClient, callback, flightIDs))
1497
1498 def getAcceptedFlights(self, callback):
1499 """Enqueue a request to get the accepted flights."""
1500 self._addRequest(GetAcceptedFlights(self._rpcClient, callback))
1501
1502 def getTimetable(self, callback, date, types):
1503 """Enqueue a request to get the timetable."""
1504 self._addRequest(GetTimetable(self._rpcClient, callback, date, types))
1505
1506 def bookFlights(self, callback, flightIDs, date, tailNumber):
1507 """Enqueue a request to book some flights."""
1508 self._addRequest(BookFlights(self._rpcClient, callback,
1509 flightIDs, date, tailNumber))
1510
1511 def run(self):
1512 """Process the requests."""
1513 while True:
1514 with self._requestCondition:
1515 while not self._requests:
1516 self._requestCondition.wait()
1517 request = self._requests[0]
1518 del self._requests[0]
1519
1520 request.perform()
1521
1522 def _addRequest(self, request):
1523 """Add the given request to the queue."""
1524 with self._requestCondition:
1525 self._requests.append(request)
1526 self._requestCondition.notify()
1527
1528#------------------------------------------------------------------------------
1529
1530if __name__ == "__main__":
1531 import time
1532
1533 def callback(returned, result):
1534 print returned, unicode(result)
1535
1536 handler = Handler()
1537 handler.start()
1538
1539 #handler.login(callback, "P096", "V5fwj")
1540 #handler.getFleet(callback)
1541 # Plane: HA-LEG home (gate 67)
1542 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
1543 #time.sleep(3)
1544 #handler.getFleet(callback)
1545 #time.sleep(3)
1546
1547 #handler.getNOTAMs(callback, "LHBP", "EPWA")
1548 #handler.getMETARs(callback, ["LHBP", "EPWA"])
1549 #time.sleep(5)
1550
1551 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
1552 time.sleep(3)
1553
1554#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.