source: src/mlx/web.py@ 953:ad2ac5a19fc2

Last change on this file since 953:ad2ac5a19fc2 was 859:1e789c934953, checked in by István Váradi <ivaradi@…>, 7 years ago

Flight booking works (re #304).

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