source: src/mlx/web.py@ 743:6bc880ac41eb

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

Prepared the ACARS and PIREP objects for serialization (re #283).

File size: 38.3 KB
Line 
1
2import const
3import util
4import rpccommon
5
6from common import MAVA_BASE_URL
7
8import threading
9import sys
10import urllib
11import urllib2
12import hashlib
13import time
14import datetime
15import codecs
16import traceback
17import xml.sax
18import xmlrpclib
19import HTMLParser
20
21#---------------------------------------------------------------------------------------
22
23## @package mlx.web
24#
25# Web interface.
26#
27# This module implements a thread that can perform (HTTP) requests
28# asynchronously. When the request is performed, a callback is called. The main
29# interface is the \ref Handler class. Each of its functions creates a \ref
30# Request subclass instance and puts it to the request queue. The handler
31# thread then takes the requests one by one, and executes them.
32#
33# This module also defines some data classes the contents of which are
34# retrieved or sent via HTTP. \ref BookedFlight contains data of a flight
35# booked on the MAVA website, \ref Fleet and \ref Plane represents the MAVA
36# fleet and the gates at Ferihegy and \ref NOTAM is a NOTAM.
37
38#---------------------------------------------------------------------------------------
39
40def readline(f):
41 """Read a line from the given file.
42
43 The line is stripped and empty lines are discarded."""
44 while True:
45 line = f.readline()
46 if not line: return ""
47 line = line.strip()
48 if line:
49 return line
50
51#---------------------------------------------------------------------------------------
52
53class BookedFlight(object):
54 """A flight that was booked."""
55 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
56 "73G" : const.AIRCRAFT_B737,
57 "738" : const.AIRCRAFT_B738,
58 "73H" : const.AIRCRAFT_B738C,
59 "733" : const.AIRCRAFT_B733,
60 "734" : const.AIRCRAFT_B734,
61 "735" : const.AIRCRAFT_B735,
62 "DH4" : const.AIRCRAFT_DH8D,
63 "762" : const.AIRCRAFT_B762,
64 "763" : const.AIRCRAFT_B763,
65 "CR2" : const.AIRCRAFT_CRJ2,
66 "F70" : const.AIRCRAFT_F70,
67 "LI2" : const.AIRCRAFT_DC3,
68 "TU3" : const.AIRCRAFT_T134,
69 "TU5" : const.AIRCRAFT_T154,
70 "YK4" : const.AIRCRAFT_YK40,
71 "146" : const.AIRCRAFT_B462 }
72
73 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "736",
74 const.AIRCRAFT_B737 : "73G",
75 const.AIRCRAFT_B738 : "738",
76 const.AIRCRAFT_B738C : "73H",
77 const.AIRCRAFT_B733 : "733",
78 const.AIRCRAFT_B734 : "734",
79 const.AIRCRAFT_B735 : "735",
80 const.AIRCRAFT_DH8D : "DH4",
81 const.AIRCRAFT_B762 : "762",
82 const.AIRCRAFT_B763 : "763",
83 const.AIRCRAFT_CRJ2 : "CR2",
84 const.AIRCRAFT_F70 : "F70",
85 const.AIRCRAFT_DC3 : "LI2",
86 const.AIRCRAFT_T134 : "TU3",
87 const.AIRCRAFT_T154 : "TU5",
88 const.AIRCRAFT_YK40 : "YK4",
89 const.AIRCRAFT_B462 : "146" }
90
91 @staticmethod
92 def getDateTime(date, time):
93 """Get a datetime object from the given textual date and time."""
94 return datetime.datetime.strptime(date + " " + time,
95 "%Y-%m-%d %H:%M:%S")
96
97 def __init__(self, id = None):
98 """Construct a booked flight with the given ID."""
99 self.id = id
100
101 def readFromWeb(self, f):
102 """Read the data of the flight from the web via the given file
103 object."""
104 self.callsign = readline(f)
105
106 date = readline(f)
107 print "web.BookedFlight.readFromWeb: date:", date
108 if date=="0000-00-00": date = "0001-01-01"
109
110 self.departureICAO = readline(f)
111 self.arrivalICAO = readline(f)
112
113 self._readAircraftType(f)
114 self.tailNumber = readline(f)
115 self.numPassengers = int(readline(f))
116 self.numCrew = int(readline(f))
117 self.bagWeight = int(readline(f))
118 self.cargoWeight = int(readline(f))
119 self.mailWeight = int(readline(f))
120 self.route = readline(f)
121
122 departureTime = readline(f)
123 self.departureTime = BookedFlight.getDateTime(date, departureTime)
124
125 arrivalTime = readline(f)
126 self.arrivalTime = BookedFlight.getDateTime(date, arrivalTime)
127 if self.arrivalTime<self.departureTime:
128 self.arrivalTime += datetime.timedelta(days = 1)
129
130 if not readline(f)==".NEXT.":
131 raise Exception("Invalid line in flight data")
132
133 def readFromFile(self, f):
134 """Read the data of the flight from a file via the given file
135 object."""
136 date = None
137 departureTime = None
138 arrivalTime = None
139
140 line = f.readline()
141 lineNumber = 0
142 while line:
143 lineNumber += 1
144 line = line.strip()
145
146 hashIndex = line.find("#")
147 if hashIndex>=0: line = line[:hashIndex]
148 if line:
149 equalIndex = line.find("=")
150 lineOK = equalIndex>0
151
152 if lineOK:
153 key = line[:equalIndex].strip()
154 value = line[equalIndex+1:].strip().replace("\:", ":")
155
156 lineOK = key and value
157
158 if lineOK:
159 if key=="callsign": self.callsign = value
160 elif key=="date": date = value
161 elif key=="dep_airport": self.departureICAO = value
162 elif key=="dest_airport": self.arrivalICAO = value
163 elif key=="planecode": self.aircraftType = \
164 self._decodeAircraftType(value)
165 elif key=="planetype": self.aircraftTypeName = value
166 elif key=="tail_nr": self.tailNumber = value
167 elif key=="passenger": self.numPassengers = int(value)
168 elif key=="crew": self.numCrew = int(value)
169 elif key=="bag": self.bagWeight = int(value)
170 elif key=="cargo": self.cargoWeight = int(value)
171 elif key=="mail": self.mailWeight = int(value)
172 elif key=="flight_route": self.route = value
173 elif key=="departure_time": departureTime = value
174 elif key=="arrival_time": arrivalTime = value
175 elif key=="foglalas_id":
176 self.id = None if value=="0" else value
177 else: lineOK = False
178
179 if not lineOK:
180 print "web.BookedFlight.readFromFile: line %d is invalid" % \
181 (lineNumber,)
182
183 line = f.readline()
184
185 if date is not None:
186 if departureTime is not None:
187 self.departureTime = BookedFlight.getDateTime(date,
188 departureTime)
189 if arrivalTime is not None:
190 self.arrivalTime = BookedFlight.getDateTime(date,
191 arrivalTime)
192
193 d = dir(self)
194 for attribute in ["callsign", "departureICAO", "arrivalICAO",
195 "aircraftType", "tailNumber",
196 "numPassengers", "numCrew",
197 "bagWeight", "cargoWeight", "mailWeight",
198 "route", "departureTime", "arrivalTime"]:
199 if attribute not in d:
200 raise Exception("Attribute %s could not be read" % (attribute,))
201
202 if "aircraftTypeName" not in d:
203 self.aircraftTypeName = \
204 BookedFlight.TYPE2TYPECODE[self.aircraftType]
205
206 def writeIntoFile(self, f):
207 """Write the flight into a file."""
208 print >> f, "callsign=%s" % (self.callsign,)
209 date = self.departureTime.date()
210 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
211 print >> f, "dep_airport=%s" % (self.departureICAO,)
212 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
213 print >> f, "planecode=%s" % \
214 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
215 print >> f, "planetype=%s" % (self.aircraftTypeName,)
216 print >> f, "tail_nr=%s" % (self.tailNumber,)
217 print >> f, "passenger=%d" % (self.numPassengers,)
218 print >> f, "crew=%d" % (self.numCrew,)
219 print >> f, "bag=%d" % (self.bagWeight,)
220 print >> f, "cargo=%d" % (self.cargoWeight,)
221 print >> f, "mail=%d" % (self.mailWeight,)
222 print >> f, "flight_route=%s" % (self.route,)
223 departureTime = self.departureTime
224 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
225 (departureTime.hour, departureTime.minute, departureTime.second)
226 arrivalTime = self.arrivalTime
227 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
228 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
229 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
230
231 def _readAircraftType(self, f):
232 """Read the aircraft type from the given file."""
233 line = readline(f)
234 typeCode = line[:3]
235 self.aircraftType = self._decodeAircraftType(typeCode)
236 self.aircraftTypeName = line[3:]
237
238 def _decodeAircraftType(self, typeCode):
239 """Decode the aircraft type from the given typeCode."""
240 if typeCode in self.TYPECODE2TYPE:
241 return self.TYPECODE2TYPE[typeCode]
242 else:
243 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
244
245 def __repr__(self):
246 """Get a representation of the flight."""
247 s = "<Flight: %s-%s, %s, %s-%s," % (self.departureICAO,
248 self.arrivalICAO,
249 self.route,
250 self.departureTime, self.arrivalTime)
251 s += " %d %s," % (self.aircraftType, self.tailNumber)
252 s += " pax=%d, crew=%d, bag=%d, cargo=%d, mail=%d" % \
253 (self.numPassengers, self.numCrew,
254 self.bagWeight, self.cargoWeight, self.mailWeight)
255 s += ">"
256 return s
257
258#------------------------------------------------------------------------------
259
260class Plane(rpccommon.Plane):
261 """Information about an airplane in the fleet."""
262 def __init__(self, s):
263 """Build a plane info based on the given string.
264
265 The string consists of three, space-separated fields.
266 The first field is the tail number, the second field is the gate
267 number, the third field is the plane's status as a character."""
268 super(Plane, self).__init__()
269
270 try:
271 words = s.split(" ")
272 tailNumber = words[0]
273 self.tailNumber = tailNumber
274
275 status = words[2] if len(words)>2 else None
276 self._setStatus(status)
277
278 gateNumber = words[1] if len(words)>1 else ""
279 self.gateNumber = gateNumber if gateNumber else None
280
281 except:
282 print >> sys.stderr, "Plane string is invalid: '" + s + "'"
283 self.tailNumber = None
284
285#------------------------------------------------------------------------------
286
287class Fleet(rpccommon.Fleet):
288 """Information about the whole fleet."""
289 def __init__(self, f):
290 """Construct the fleet information by reading the given file object."""
291 super(Fleet, self).__init__()
292
293 while True:
294 line = readline(f)
295 if not line or line == "#END": break
296
297 plane = Plane(line)
298 self._addPlane(plane)
299
300#------------------------------------------------------------------------------
301
302class NOTAM(object):
303 """A NOTAM for an airport."""
304 def __init__(self, ident, basic,
305 begin, notice, end = None, permanent = False,
306 repeatCycle = None):
307 """Construct the NOTAM."""
308 self.ident = ident
309 self.basic = basic
310 self.begin = begin
311 self.notice = notice
312 self.end = end
313 self.permanent = permanent
314 self.repeatCycle = repeatCycle
315
316 def __repr__(self):
317 """Get the representation of the NOTAM."""
318 s = "<NOTAM " + str(self.begin)
319 if self.end:
320 s += " - " + str(self.end)
321 elif self.permanent:
322 s += " - PERMANENT"
323 if self.repeatCycle:
324 s += " (" + self.repeatCycle + ")"
325 s += ": " + self.notice
326 s += ">"
327 return s
328
329 def __str__(self):
330 """Get the string representation of the NOTAM."""
331 s = ""
332 s += str(self.ident) + " " + str(self.basic) + "\n"
333 s += str(self.begin)
334 if self.end is not None:
335 s += " - " + str(self.end)
336 elif self.permanent:
337 s += " - PERMANENT"
338 s += "\n"
339 if self.repeatCycle:
340 s += "Repeat cycle: " + self.repeatCycle + "\n"
341 s += self.notice + "\n"
342 return s
343
344#------------------------------------------------------------------------------
345
346class NOTAMHandler(xml.sax.handler.ContentHandler):
347 """A handler for the NOTAM database."""
348 def __init__(self, airportICAOs):
349 """Construct the handler for the airports with the given ICAO code."""
350 self._notams = {}
351 for icao in airportICAOs:
352 self._notams[icao] = []
353
354 def startElement(self, name, attrs):
355 """Start an element."""
356 if name!="notam" or \
357 "ident" not in attrs or not attrs["ident"] or \
358 "Q" not in attrs or not attrs["Q"] or \
359 "A" not in attrs or not attrs["A"] or \
360 "B" not in attrs or not attrs["B"] or \
361 "E" not in attrs or not attrs["E"]:
362 return
363
364 icao = attrs["A"]
365 if icao not in self._notams:
366 return
367
368 begin = datetime.datetime.strptime(attrs["B"], "%Y-%m-%d %H:%M:%S")
369
370 c = attrs["C"] if "C" in attrs else None
371 end = datetime.datetime.strptime(c, "%Y-%m-%d %H:%M:%S") if c else None
372
373 permanent = attrs["C_flag"]=="PERM" if "C_flag" in attrs else False
374
375 repeatCycle = attrs["D"] if "D" in attrs else None
376
377 self._notams[icao].append(NOTAM(attrs["ident"], attrs["Q"],
378 begin, attrs["E"], end = end,
379 permanent = permanent,
380 repeatCycle = repeatCycle))
381
382 def get(self, icao):
383 """Get the NOTAMs for the given ICAO code."""
384 return self._notams[icao] if icao in self._notams else []
385
386#------------------------------------------------------------------------------
387
388class PilotsWebNOTAMsParser(HTMLParser.HTMLParser):
389 """XML handler for the NOTAM query results on the PilotsWeb website."""
390 def __init__(self):
391 """Construct the handler."""
392 HTMLParser.HTMLParser.__init__(self)
393
394 self._notams = []
395 self._currentNOTAM = ""
396 self._stage = 0
397
398 def handle_starttag(self, name, attrs):
399 """Start an element."""
400 if (self._stage==0 and name=="div" and ("id", "notamRight") in attrs) or \
401 (self._stage==1 and name=="span") or \
402 (self._stage==2 and name=="pre"):
403 self._stage += 1
404 if self._stage==1:
405 self._currentNOTAM = ""
406
407 def handle_data(self, content):
408 """Handle characters"""
409 if self._stage==3:
410 self._currentNOTAM += content
411
412 def handle_endtag(self, name):
413 """End an element."""
414 if (self._stage==3 and name=="pre") or \
415 (self._stage==2 and name=="span") or \
416 (self._stage==1 and name=="div"):
417 self._stage -= 1
418 if self._stage==0:
419 self._processCurrentNOTAM()
420
421 def getNOTAMs(self):
422 """Get the NOTAMs collected"""
423 return self._notams
424
425 def _processCurrentNOTAM(self):
426 """Parse the current NOTAM and append its contents to the list of
427 NOTAMS."""
428 notam = None
429 try:
430 notam = self._parseCurrentNOTAM2()
431 except Exception, e:
432 print "Error parsing current NOTAM: " + str(e)
433
434 if notam is None:
435 print "Could not parse NOTAM: " + self._currentNOTAM
436 if self._currentNOTAM:
437 self._notams.append(self._currentNOTAM + "\n")
438 else:
439 self._notams.append(notam)
440
441 def _parseCurrentNOTAM(self):
442 """Parse the current NOTAM, if possible, and return a NOTAM object."""
443 lines = self._currentNOTAM.splitlines()
444 lines = map(lambda line: line.strip(), lines)
445
446 if len(lines)<4:
447 return None
448
449 if not lines[1].startswith("Q)") or \
450 not lines[2].startswith("A)") or \
451 not (lines[3].startswith("E)") or
452 (lines[3].startswith("D)") and lines[4].startswith("E)"))):
453 return None
454
455 ident = lines[0].split()[0]
456 basic = lines[1][2:].strip()
457
458 words = lines[2].split()
459 if len(words)<4 or words[0]!="A)" or words[2]!="B)":
460 return None
461
462 begin = datetime.datetime.strptime(words[3], "%y%m%d%H%M")
463 end = None
464 permanent = False
465 if words[4]=="C)" and len(words)>=6:
466 if words[5] in ["PERM", "UFN"]:
467 permanent = True
468 else:
469 end = datetime.datetime.strptime(words[5], "%y%m%d%H%M")
470 else:
471 permanent = True
472
473 repeatCycle = None
474 noticeStartIndex = 3
475 if lines[3].startswith("D)"):
476 repeatCycle = lines[3][2:].strip()
477 noticeStartIndex = 4
478
479 notice = ""
480 for index in range(noticeStartIndex, len(lines)):
481 line = lines[index][2:] if index==noticeStartIndex else lines[index]
482 line = line.strip()
483
484 if line.lower().startswith("created:") or \
485 line.lower().startswith("source:"):
486 break
487
488 if notice: notice += " "
489 notice += line
490
491 return NOTAM(ident, basic, begin, notice, end = end,
492 permanent = permanent, repeatCycle = repeatCycle)
493
494 def _parseCurrentNOTAM2(self):
495 """Parse the current NOTAM with a second, more flexible method."""
496 lines = self._currentNOTAM.splitlines()
497 lines = map(lambda line: line.strip(), lines)
498
499 if not lines:
500 return None
501
502 ident = lines[0].split()[0]
503
504 lines = lines[1:]
505 for i in range(0, 2):
506 l = lines[-1].lower()
507 if l.startswith("created:") or l.startswith("source:"):
508 lines = lines[:-1]
509
510 lines = map(lambda line: line.strip(), lines)
511 contents = " ".join(lines).split()
512
513 items = {}
514 for i in ["Q)", "A)", "B)", "C)", "D)", "E)"]:
515 items[i] = ""
516
517 currentItem = None
518 for word in contents:
519 if word in items:
520 currentItem = word
521 elif currentItem in items:
522 s = items[currentItem]
523 if s: s+= " "
524 s += word
525 items[currentItem] = s
526
527 if not items["Q)"] or not items["A)"] or not items["B)"] or \
528 not items["E)"]:
529 return None
530
531 basic = items["Q)"]
532 begin = datetime.datetime.strptime(items["B)"], "%y%m%d%H%M")
533
534 end = None
535 permanent = False
536 if items["C)"]:
537 endItem = items["C)"]
538 if endItem in ["PERM", "UFN"]:
539 permanent = True
540 else:
541 end = datetime.datetime.strptime(items["C)"], "%y%m%d%H%M")
542 else:
543 permanent = True
544
545 repeatCycle = None
546 if items["D)"]:
547 repeatCycle = items["D)"]
548
549 notice = items["E)"]
550
551 return NOTAM(ident, basic, begin, notice, end = end,
552 permanent = permanent, repeatCycle = repeatCycle)
553
554#------------------------------------------------------------------------------
555
556class Result(object):
557 """A result object.
558
559 An instance of this filled with the appropriate data is passed to the
560 callback function on each request."""
561
562 def __repr__(self):
563 """Get a representation of the result."""
564 s = "<Result:"
565 for (key, value) in self.__dict__.iteritems():
566 s += " " + key + "=" + unicode(value)
567 s += ">"
568 return s
569
570#------------------------------------------------------------------------------
571
572class Request(object):
573 """Base class for requests.
574
575 It handles any exceptions and the calling of the callback.
576
577 If an exception occurs during processing, the callback is called with
578 the two parameters: a boolean value of False, and the exception object.
579
580 If no exception occurs, the callback is called with True and the return
581 value of the run() function.
582
583 If the callback function throws an exception, that is caught and logged
584 to the debug log."""
585 def __init__(self, callback):
586 """Construct the request."""
587 self._callback = callback
588
589 def perform(self):
590 """Perform the request.
591
592 The object's run() function is called. If it throws an exception,
593 the callback is called with False, and the exception. Otherwise the
594 callback is called with True and the return value of the run()
595 function. Any exceptions thrown by the callback are caught and
596 reported."""
597 try:
598 result = self.run()
599 returned = True
600 except Exception, e:
601 traceback.print_exc()
602 result = e
603 returned = False
604
605 try:
606 self._callback(returned, result)
607 except Exception, e:
608 print >> sys.stderr, "web.Handler.Request.perform: callback throwed an exception: " + util.utf2unicode(str(e))
609 #traceback.print_exc()
610
611#------------------------------------------------------------------------------
612
613class Login(Request):
614 """A login request."""
615 iso88592decoder = codecs.getdecoder("iso-8859-2")
616
617 def __init__(self, callback, pilotID, password, entranceExam):
618 """Construct the login request with the given pilot ID and
619 password."""
620 super(Login, self).__init__(callback)
621
622 self._pilotID = pilotID
623 self._password = password
624 self._entranceExam = entranceExam
625
626 def run(self):
627 """Perform the login request."""
628 md5 = hashlib.md5()
629 md5.update(self._pilotID)
630 pilotID = md5.hexdigest()
631
632 md5 = hashlib.md5()
633 md5.update(self._password)
634 password = md5.hexdigest()
635
636 if self._entranceExam:
637 url = MAVA_BASE_URL + "/ellenorzo/getflightplan.php?pid=%s" % \
638 (pilotID,)
639 else:
640 url = MAVA_BASE_URL + "/leker2.php?pid=%s&psw=%s" % \
641 (pilotID, password)
642
643 result = Result()
644 result.entranceExam = self._entranceExam
645
646 f = urllib2.urlopen(url, timeout = 10.0)
647
648 status = readline(f)
649 if self._entranceExam:
650 result.loggedIn = status != "#NOEXAM"
651 else:
652 result.loggedIn = status == ".OK."
653
654 if result.loggedIn:
655 result.pilotID = self._pilotID
656 result.password = self._password
657 result.flights = []
658 # FIXME: this may not be the correct behaviour
659 # for an entrance exam, but the website returns
660 # an error
661 if self._entranceExam:
662 result.pilotName = result.pilotID
663 result.exams = ""
664 else:
665 result.pilotName = self.iso88592decoder(readline(f))[0]
666 result.exams = readline(f)
667
668 while True:
669 line = readline(f)
670 if not line or line == "#ENDPIREP": break
671
672 flight = BookedFlight(line)
673 flight.readFromWeb(f)
674 result.flights.append(flight)
675
676 result.flights.sort(cmp = lambda flight1, flight2:
677 cmp(flight1.departureTime,
678 flight2.departureTime))
679
680 f.close()
681
682 return result
683
684#------------------------------------------------------------------------------
685
686class GetFleet(Request):
687 """Request to get the fleet from the website."""
688
689 def __init__(self, callback):
690 """Construct the fleet request."""
691 super(GetFleet, self).__init__(callback)
692
693 def run(self):
694 """Perform the login request."""
695 url = MAVA_BASE_URL + "/onlinegates_get.php"
696
697 f = urllib2.urlopen(url, timeout = 10.0)
698 result = Result()
699 result.fleet = Fleet(f)
700 f.close()
701
702 return result
703
704#------------------------------------------------------------------------------
705
706class UpdatePlane(Request):
707 """Update the status of one of the planes in the fleet."""
708 def __init__(self, callback, tailNumber, status, gateNumber = None):
709 """Construct the request."""
710 super(UpdatePlane, self).__init__(callback)
711 self._tailNumber = tailNumber
712 self._status = status
713 self._gateNumber = gateNumber
714
715 def run(self):
716 """Perform the plane update."""
717 url = MAVA_BASE_URL + "/onlinegates_set.php"
718
719 status = Plane.status2str(self._status)
720
721 gateNumber = self._gateNumber if self._gateNumber else ""
722
723 data = urllib.urlencode([("lajstrom", self._tailNumber),
724 ("status", status),
725 ("kapu", gateNumber)])
726
727 f = urllib2.urlopen(url, data, timeout = 10.0)
728 line = readline(f)
729
730 result = Result()
731 result.success = line == "OK"
732
733 return result
734
735#------------------------------------------------------------------------------
736
737class GetNOTAMs(Request):
738 """Get the NOTAMs from EURoutePro and select the ones we are interested
739 in."""
740 def __init__(self, callback, departureICAO, arrivalICAO):
741 """Construct the request for the given airports."""
742 super(GetNOTAMs, self).__init__(callback)
743 self._departureICAO = departureICAO
744 self._arrivalICAO = arrivalICAO
745
746 def run(self):
747 """Perform the retrieval of the NOTAMs."""
748 departureNOTAMs = self.getPilotsWebNOTAMs(self._departureICAO)
749 arrivalNOTAMs = self.getPilotsWebNOTAMs(self._arrivalICAO)
750
751 icaos = []
752 if not departureNOTAMs: icaos.append(self._departureICAO)
753 if not arrivalNOTAMs: icaos.append(self._arrivalICAO)
754
755 if icaos:
756 xmlParser = xml.sax.make_parser()
757 notamHandler = NOTAMHandler(icaos)
758 xmlParser.setContentHandler(notamHandler)
759
760 url = "http://notams.euroutepro.com/notams.xml"
761
762 f = urllib2.urlopen(url, timeout = 10.0)
763 try:
764 xmlParser.parse(f)
765 finally:
766 f.close()
767
768 for icao in icaos:
769 if icao==self._departureICAO:
770 departureNOTAMs = notamHandler.get(icao)
771 else:
772 arrivalNOTAMs = notamHandler.get(icao)
773
774 result = Result()
775 result.departureNOTAMs = departureNOTAMs
776 result.arrivalNOTAMs = arrivalNOTAMs
777
778 return result
779
780 def getPilotsWebNOTAMs(self, icao):
781 """Try to get the NOTAMs from FAA's PilotsWeb site for the given ICAO
782 code.
783
784 Returns a list of PilotsWEBNOTAM objects, or None in case of an error."""
785 try:
786 parser = PilotsWebNOTAMsParser()
787
788 url = "https://pilotweb.nas.faa.gov/PilotWeb/notamRetrievalByICAOAction.do?method=displayByICAOs&formatType=ICAO&retrieveLocId=%s&reportType=RAW&actionType=notamRetrievalByICAOs" % \
789 (icao.upper(),)
790
791 f = urllib2.urlopen(url, timeout = 10.0)
792 try:
793 data = f.read(16384)
794 while data:
795 parser.feed(data)
796 data = f.read(16384)
797 finally:
798 f.close()
799
800 return parser.getNOTAMs()
801
802 except Exception, e:
803 traceback.print_exc()
804 print "mlx.web.GetNOTAMs.getPilotsWebNOTAMs: failed to get NOTAMs for '%s': %s" % \
805 (icao, str(e))
806 return None
807
808#------------------------------------------------------------------------------
809
810class GetMETARs(Request):
811 """Get the METARs from the NOAA website for certain airport ICAOs."""
812
813 def __init__(self, callback, airports):
814 """Construct the request for the given airports."""
815 super(GetMETARs, self).__init__(callback)
816 self._airports = airports
817
818 def run(self):
819 """Perform the retrieval opf the METARs."""
820 url = "http://www.aviationweather.gov/adds/dataserver_current/httpparam?"
821 data = urllib.urlencode([ ("dataSource" , "metars"),
822 ("requestType", "retrieve"),
823 ("format", "csv"),
824 ("stationString", " ".join(self._airports)),
825 ("hoursBeforeNow", "24"),
826 ("mostRecentForEachStation", "constraint")])
827 url += data
828 f = urllib2.urlopen(url, timeout = 10.0)
829 try:
830 result = Result()
831 result.metars = {}
832 for line in iter(f.readline, ""):
833 if len(line)>5 and line[4]==' ':
834 icao = line[0:4]
835 if icao in self._airports:
836 result.metars[icao] = line.strip().split(",")[0]
837 finally:
838 f.close()
839
840 return result
841
842#------------------------------------------------------------------------------
843
844class SendPIREP(Request):
845 """A request to send a PIREP to the MAVA website."""
846 _latin2Encoder = codecs.getencoder("iso-8859-2")
847
848 def __init__(self, callback, pirep):
849 """Construct the sending of the PIREP."""
850 super(SendPIREP, self).__init__(callback)
851 self._pirep = pirep
852
853 def run(self):
854 """Perform the sending of the PIREP."""
855 url = MAVA_BASE_URL + "/malevacars.php"
856
857 pirep = self._pirep
858
859 data = {}
860 data["acarsdata"] = SendPIREP._latin2Encoder(pirep.getACARSText())[0]
861
862 bookedFlight = pirep.bookedFlight
863 data["foglalas_id"] = bookedFlight.id
864 data["repdate"] = bookedFlight.departureTime.date().strftime("%Y-%m-%d")
865 data["fltnum"] = bookedFlight.callsign
866 data["depap"] = bookedFlight.departureICAO
867 data["arrap"] = bookedFlight.arrivalICAO
868 data["pass"] = str(pirep.numPassengers)
869 data["crew"] = str(pirep.numCrew)
870 data["cargo"] = str(pirep.cargoWeight)
871 data["bag"] = str(pirep.bagWeight)
872 data["mail"] = str(pirep.mailWeight)
873
874 data["flttype"] = pirep.flightTypeText
875 data["onoff"] = "1" if pirep.online else "0"
876 data["bt_dep"] = util.getTimestampString(pirep.blockTimeStart)
877 data["bt_arr"] = util.getTimestampString(pirep.blockTimeEnd)
878 data["bt_dur"] = util.getTimeIntervalString(pirep.blockTimeEnd -
879 pirep.blockTimeStart)
880 data["ft_dep"] = util.getTimestampString(pirep.flightTimeStart)
881 data["ft_arr"] = util.getTimestampString(pirep.flightTimeEnd)
882 data["ft_dur"] = util.getTimeIntervalString(pirep.flightTimeEnd -
883 pirep.flightTimeStart)
884 data["timecomm"] = pirep.getTimeComment()
885 data["fuel"] = "%.2f" % (pirep.fuelUsed,)
886 data["dep_rwy"] = pirep.departureRunway
887 data["arr_rwy"] = pirep.arrivalRunway
888 data["wea_dep"] = pirep.departureMETAR
889 data["wea_arr"] = pirep.arrivalMETAR
890 data["alt"] = "FL%.0f" % (pirep.filedCruiseAltitude/100.0,)
891 if pirep.filedCruiseAltitude!=pirep.cruiseAltitude:
892 data["mod_alt"] = "FL%.0f" % (pirep.cruiseAltitude/100.0,)
893 else:
894 data["mod_alt"] = ""
895 data["sid"] = pirep.sid
896 data["navroute"] = pirep.route
897 data["star"] = pirep.getSTAR()
898 data["aprtype"] = pirep.approachType
899 data["diff"] = "2"
900 data["comment"] = SendPIREP._latin2Encoder(pirep.comments)[0]
901 data["flightdefect"] = SendPIREP._latin2Encoder(pirep.flightDefects)[0]
902 data["kritika"] = pirep.getRatingText()
903 data["flightrating"] = "%.1f" % (max(0.0, pirep.rating),)
904 data["distance"] = "%.3f" % (pirep.flownDistance,)
905 data["insdate"] = datetime.date.today().strftime("%Y-%m-%d")
906
907 postData = urllib.urlencode(data)
908 f = urllib2.urlopen(url, postData, timeout = 10.0)
909 try:
910 result = Result()
911 line = f.readline().strip()
912 print "PIREP result from website:", line
913 result.success = line=="OK"
914 result.alreadyFlown = line=="MARVOLT"
915 result.notAvailable = line=="NOMORE"
916 finally:
917 f.close()
918
919 return result
920#------------------------------------------------------------------------------
921
922class SendACARS(Request):
923 """A request to send an ACARS to the MAVA website."""
924 _latin2Encoder = codecs.getencoder("iso-8859-2")
925
926 def __init__(self, callback, acars):
927 """Construct the request for the given PIREP."""
928 super(SendACARS, self).__init__(callback)
929 self._acars = acars
930
931 def run(self):
932 """Perform the sending of the ACARS."""
933 print "Sending the online ACARS"
934
935 url = MAVA_BASE_URL + "/acars2/acarsonline.php"
936
937 acars = self._acars
938 bookedFlight = acars.bookedFlight
939
940 data = {}
941 data["pid"] = acars.pid
942 data["pilot"] = SendACARS._latin2Encoder(acars.pilotName)[0]
943
944 data["pass"] = str(bookedFlight.numPassengers)
945 data["callsign"] = bookedFlight.callsign
946 data["airplane"] = bookedFlight.aircraftTypeName
947 data["from"] = bookedFlight.departureICAO
948 data["to"] = bookedFlight.arrivalICAO
949 data["lajstrom"] = bookedFlight.tailNumber
950
951 data["block_time"] = acars.getBlockTimeText()
952 data["longitude"] = str(acars.state.longitude)
953 data["latitude"] = str(acars.state.latitude)
954 data["altitude"] = str(acars.state.altitude)
955 data["speed"] = str(acars.state.groundSpeed)
956
957 data["event"] = acars.getEventText()
958
959 f = urllib2.urlopen(url, urllib.urlencode(data), timeout = 10.0)
960 try:
961 result = Result()
962 finally:
963 f.close()
964
965 return result
966
967#------------------------------------------------------------------------------
968
969class SendBugReport(Request):
970 """A request to send a bug report to the project homepage."""
971 _latin2Encoder = codecs.getencoder("iso-8859-2")
972
973 def __init__(self, callback, summary, description, email):
974 """Construct the request for the given bug report."""
975 super(SendBugReport, self).__init__(callback)
976 self._summary = summary
977 self._description = description
978 self._email = email
979
980 def run(self):
981 """Perform the sending of the bug report."""
982 serverProxy = xmlrpclib.ServerProxy("http://mlx.varadiistvan.hu/rpc")
983
984 result = Result()
985 result.success = False
986
987 attributes = {}
988 if self._email:
989 attributes["reporter"] = self._email
990
991 result.ticketID = serverProxy.ticket.create(self._summary, self._description,
992 attributes, True)
993 print "Created ticket with ID:", result.ticketID
994 result.success = True
995
996 return result
997
998#------------------------------------------------------------------------------
999
1000class Handler(threading.Thread):
1001 """The handler for the web services.
1002
1003 It can process one request at a time. The results are passed to a callback
1004 function."""
1005 def __init__(self):
1006 """Construct the handler."""
1007 super(Handler, self).__init__()
1008
1009 self._requests = []
1010 self._requestCondition = threading.Condition()
1011
1012 self.daemon = True
1013
1014 def login(self, callback, pilotID, password, entranceExam = False):
1015 """Enqueue a login request."""
1016 self._addRequest(Login(callback, pilotID, password, entranceExam))
1017
1018 def getFleet(self, callback):
1019 """Enqueue a fleet retrieval request."""
1020 self._addRequest(GetFleet(callback))
1021
1022 def updatePlane(self, callback, tailNumber, status, gateNumber = None):
1023 """Update the status of the given plane."""
1024 self._addRequest(UpdatePlane(callback, tailNumber, status, gateNumber))
1025
1026 def getNOTAMs(self, callback, departureICAO, arrivalICAO):
1027 """Get the NOTAMs for the given two airports."""
1028 self._addRequest(GetNOTAMs(callback, departureICAO, arrivalICAO))
1029
1030 def getMETARs(self, callback, airports):
1031 """Get the METARs for the given airports."""
1032 self._addRequest(GetMETARs(callback, airports))
1033
1034 def sendPIREP(self, callback, pirep):
1035 """Send the given PIREP."""
1036 self._addRequest(SendPIREP(callback, pirep))
1037
1038 def sendACARS(self, callback, acars):
1039 """Send the given ACARS"""
1040 self._addRequest(SendACARS(callback, acars))
1041
1042 def sendBugReport(self, callback, summary, description, email):
1043 """Send a bug report with the given data."""
1044 self._addRequest(SendBugReport(callback, summary, description, email))
1045
1046 def run(self):
1047 """Process the requests."""
1048 while True:
1049 with self._requestCondition:
1050 while not self._requests:
1051 self._requestCondition.wait()
1052 request = self._requests[0]
1053 del self._requests[0]
1054
1055 request.perform()
1056
1057 def _addRequest(self, request):
1058 """Add the given request to the queue."""
1059 with self._requestCondition:
1060 self._requests.append(request)
1061 self._requestCondition.notify()
1062
1063#------------------------------------------------------------------------------
1064
1065if __name__ == "__main__":
1066 import time
1067
1068 def callback(returned, result):
1069 print returned, unicode(result)
1070
1071 handler = Handler()
1072 handler.start()
1073
1074 #handler.login(callback, "P096", "V5fwj")
1075 #handler.getFleet(callback)
1076 # Plane: HA-LEG home (gate 67)
1077 #handler.updatePlane(callback, "HA-LQC", const.PLANE_AWAY, "72")
1078 #time.sleep(3)
1079 #handler.getFleet(callback)
1080 #time.sleep(3)
1081
1082 #handler.getNOTAMs(callback, "LHBP", "EPWA")
1083 #handler.getMETARs(callback, ["LHBP", "EPWA"])
1084 #time.sleep(5)
1085
1086 handler.updatePlane(callback, "HA-LON", const.PLANE_AWAY, "")
1087 time.sleep(3)
1088
1089#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.