source: src/mlx/web.py@ 485:b254e5cd222e

Last change on this file since 485:b254e5cd222e was 484:e6fc7bff478b, checked in by István Váradi <ivaradi@…>, 12 years ago

Implemented the GUI logic of the bug report sending (re #190)

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