source: src/mlx/rpc.py@ 898:55da787ce6e1

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

Aircraft type information (currently DOW, and crew-specific DOW values) are queried via RPC when logging in.

File size: 26.8 KB
Line 
1import const
2import rpccommon
3
4from common import MAVA_BASE_URL
5
6import jsonrpclib
7import hashlib
8import datetime
9import calendar
10import sys
11
12#---------------------------------------------------------------------------------------
13
14class RPCObject(object):
15 """Base class for objects read from RPC calls.
16
17 It is possible to construct it from a dictionary."""
18 def __init__(self, value, instructions = {}):
19 """Construct the object.
20
21 value is the dictionary returned by the call.
22
23 info is a mapping from names to 'instructions' on what to do with the
24 corresponding values. If the instruction is None, it will be ignored.
25 If the instruction is a function, the value will be passed to it and
26 the return value will be stored in the object.
27
28 For all other names, the value will be stored as the same-named
29 attribute."""
30 for (key, value) in value.iteritems():
31 if key in instructions:
32 instruction = instructions[key]
33 if instruction is None:
34 continue
35
36 try:
37 value = instruction(value)
38 except:
39 print >> sys.stderr, "Failed to convert value '%s' of attribute '%s':" % \
40 (value, key)
41 import traceback
42 traceback.print_exc()
43 setattr(self, key, value)
44
45#---------------------------------------------------------------------------------------
46
47class Reply(RPCObject):
48 """The generic reply structure."""
49
50#---------------------------------------------------------------------------------------
51
52class ScheduledFlight(RPCObject):
53 """A scheduled flight in the time table."""
54 # The instructions for the construction
55 # Type: normal flight
56 TYPE_NORMAL = 0
57
58 # Type: VIP flight
59 TYPE_VIP = 1
60
61 _instructions = {
62 "id" : int,
63 "pairID": int,
64 "typeCode": lambda value: BookedFlight._decodeAircraftType(value),
65 "departureTime": lambda value: ScheduledFlight._decodeTime(value),
66 "arrivalTime": lambda value: ScheduledFlight._decodeTime(value),
67 "duration": lambda value: ScheduledFlight._decodeDuration(value),
68 "spec": int,
69 "validFrom": lambda value: ScheduledFlight._decodeDate(value),
70 "validTo": lambda value: ScheduledFlight._decodeDate(value),
71 "date": lambda value: ScheduledFlight._decodeDate(value)
72 }
73
74 @staticmethod
75 def _decodeTime(value):
76 """Decode the given value as a time value."""
77 return datetime.datetime.strptime(value, "%H:%M:%S").time()
78
79 @staticmethod
80 def _decodeDate(value):
81 """Decode the given value as a date value."""
82 if not value or value=="0000-00-00":
83 return const.defaultDate
84 else:
85 return datetime.datetime.strptime(value, "%Y-%m-%d").date()
86
87 @staticmethod
88 def _decodeDuration(value):
89 """Decode the given value as a duration.
90
91 A number of seconds will be returned."""
92 t = datetime.datetime.strptime(value, "%H:%M:%S")
93 return (t.hour*60 + t.minute) * 60 + t.second
94
95 def __init__(self, value):
96 """Construct the scheduled flight object from the given JSON value."""
97 super(ScheduledFlight, self).__init__(value,
98 ScheduledFlight._instructions)
99 self.aircraftType = self.typeCode
100 del self.typeCode
101
102 self.type = ScheduledFlight.TYPE_VIP if self.spec==1 \
103 else ScheduledFlight.TYPE_NORMAL
104 del self.spec
105
106 def compareBy(self, other, name):
107 """Compare this flight with the other one according to the given
108 attribute name."""
109 if name=="callsign":
110 try:
111 cs1 = int(self.callsign[2:])
112 cs2 = int(other.callsign[2:])
113 return cmp(cs1, cs2)
114 except:
115 return cmp(self.callsign, other.callsign)
116 else:
117 return cmp(getattr(self, name), getattr(other, name))
118
119 def __repr__(self):
120 return "ScheduledFlight<%d, %d, %s, %s (%s) - %s (%s) -> %d, %d>" % \
121 (self.id, self.pairID, BookedFlight.TYPE2TYPECODE[self.aircraftType],
122 self.departureICAO, str(self.departureTime),
123 self.arrivalICAO, str(self.arrivalTime),
124 self.duration, self.type)
125
126#---------------------------------------------------------------------------------------
127
128class ScheduledFlightPair(object):
129 """A pair of scheduled flights.
130
131 Occasionally, one of the flights may be missing."""
132 @staticmethod
133 def scheduledFlights2Pairs(scheduledFlights, date):
134 """Convert the given list of scheduled flights into a list of flight
135 pairs."""
136 weekday = str(date.weekday()+1)
137
138 flights = {}
139 weekdayFlights = {}
140 for flight in scheduledFlights:
141 flights[flight.id] = flight
142 if (flight.type==ScheduledFlight.TYPE_NORMAL and
143 flight.arrivalICAO!="LHBP" and weekday in flight.days and
144 flight.validFrom<=date and flight.validTo>=date) or \
145 flight.type==ScheduledFlight.TYPE_VIP:
146 weekdayFlights[flight.id] = flight
147
148 flightPairs = []
149
150 while weekdayFlights:
151 (id, flight) = weekdayFlights.popitem()
152 if flight.type==ScheduledFlight.TYPE_NORMAL:
153 pairID = flight.pairID
154 if pairID in flights:
155 pairFlight = flights[pairID]
156 if flight.departureICAO=="LHBP" or \
157 (pairFlight.departureICAO!="LHBP" and
158 flight.callsign<pairFlight.callsign):
159 flightPairs.append(ScheduledFlightPair(flight, pairFlight))
160 else:
161 flightPairs.append(ScheduledFlightPair(pairFlight, flight))
162 del flights[pairID]
163 if pairID in weekdayFlights:
164 del weekdayFlights[pairID]
165 elif flight.type==ScheduledFlight.TYPE_VIP:
166 flightPairs.append(ScheduledFlightPair(flight))
167
168 flightPairs.sort(cmp = lambda pair1, pair2:
169 cmp(pair1.flight0.date, pair2.flight0.date))
170
171 return flightPairs
172
173 def __init__(self, flight0, flight1 = None):
174 """Construct the pair with the given flights."""
175 self.flight0 = flight0
176 self.flight1 = flight1
177
178 def compareBy(self, other, name):
179 """Compare this flight pair with the other one according to the given
180 attribute name, considering the first flights."""
181 return self.flight0.compareBy(other.flight0, name)
182
183 def __repr__(self):
184 return "ScheduledFlightPair<%s, %s, %s>" % \
185 (self.flight0.callsign, self.flight0.departureICAO,
186 self.flight0.arrivalICAO)
187
188#---------------------------------------------------------------------------------------
189
190class BookedFlight(RPCObject):
191 """A booked flight."""
192 # FIXME: copied from web.BookedFlight
193 TYPECODE2TYPE = { "736" : const.AIRCRAFT_B736,
194 "73G" : const.AIRCRAFT_B737,
195 "738" : const.AIRCRAFT_B738,
196 "73H" : const.AIRCRAFT_B738C,
197 "732" : const.AIRCRAFT_B732,
198 "733" : const.AIRCRAFT_B733,
199 "734" : const.AIRCRAFT_B734,
200 "735" : const.AIRCRAFT_B735,
201 "DH4" : const.AIRCRAFT_DH8D,
202 "762" : const.AIRCRAFT_B762,
203 "763" : const.AIRCRAFT_B763,
204 "CR2" : const.AIRCRAFT_CRJ2,
205 "F70" : const.AIRCRAFT_F70,
206 "LI2" : const.AIRCRAFT_DC3,
207 "TU3" : const.AIRCRAFT_T134,
208 "TU5" : const.AIRCRAFT_T154,
209 "YK4" : const.AIRCRAFT_YK40,
210 "146" : const.AIRCRAFT_B462,
211 "IL6" : const.AIRCRAFT_IL62 }
212
213 # FIXME: copied from web.BookedFlight
214 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "736",
215 const.AIRCRAFT_B737 : "73G",
216 const.AIRCRAFT_B738 : "738",
217 const.AIRCRAFT_B738C : "73H",
218 const.AIRCRAFT_B732 : "732",
219 const.AIRCRAFT_B733 : "733",
220 const.AIRCRAFT_B734 : "734",
221 const.AIRCRAFT_B735 : "735",
222 const.AIRCRAFT_DH8D : "DH4",
223 const.AIRCRAFT_B762 : "762",
224 const.AIRCRAFT_B763 : "763",
225 const.AIRCRAFT_CRJ2 : "CR2",
226 const.AIRCRAFT_F70 : "F70",
227 const.AIRCRAFT_DC3 : "LI2",
228 const.AIRCRAFT_T134 : "TU3",
229 const.AIRCRAFT_T154 : "TU5",
230 const.AIRCRAFT_YK40 : "YK4",
231 const.AIRCRAFT_B462 : "146",
232 const.AIRCRAFT_IL62 : "IL6" }
233
234 # FIXME: copied from web.BookedFlight
235 @staticmethod
236 def _decodeAircraftType(typeCode):
237 """Decode the aircraft type from the given typeCode."""
238 if typeCode in BookedFlight.TYPECODE2TYPE:
239 return BookedFlight.TYPECODE2TYPE[typeCode]
240 else:
241 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
242
243 @staticmethod
244 def _decodeStatus(status):
245 """Decode the status from the status string."""
246 if status=="booked":
247 return BookedFlight.STATUS_BOOKED
248 elif status=="reported":
249 return BookedFlight.STATUS_REPORTED
250 elif status=="accepted":
251 return BookedFlight.STATUS_ACCEPTED
252 elif status=="rejected":
253 return BookedFlight.STATUS_REJECTED
254 else:
255 raise Exception("Invalid flight status code: '" + status + "'")
256
257 # FIXME: copied from web.BookedFlight
258 @staticmethod
259 def getDateTime(date, time):
260 """Get a datetime object from the given textual date and time."""
261 return datetime.datetime.strptime(date + " " + time,
262 "%Y-%m-%d %H:%M:%S")
263
264 # FIXME: copied from web.BookedFlight
265 STATUS_BOOKED = 1
266
267 # FIXME: copied from web.BookedFlight
268 STATUS_REPORTED = 2
269
270 # FIXME: copied from web.BookedFlight
271 STATUS_ACCEPTED = 3
272
273 # FIXME: copied from web.BookedFlight
274 STATUS_REJECTED = 4
275
276 # The instructions for the construction
277 _instructions = {
278 "numPassengers" : int,
279 "numCrew" : int,
280 "bagWeight" : int,
281 "cargoWeight" : int,
282 "mailWeight" : int,
283 "aircraftType" : lambda value: BookedFlight._decodeAircraftType(value),
284 "status" : lambda value: BookedFlight._decodeStatus(value)
285 }
286
287 def __init__(self, value):
288 """Construct the booked flight object from the given RPC result
289 value."""
290 self.status = BookedFlight.STATUS_BOOKED
291 super(BookedFlight, self).__init__(value, BookedFlight._instructions)
292 self.departureTime = \
293 BookedFlight.getDateTime(self.date, self.departureTime)
294 self.arrivalTime = \
295 BookedFlight.getDateTime(self.date, self.arrivalTime)
296 if self.arrivalTime<self.departureTime:
297 self.arrivalTime += datetime.timedelta(days = 1)
298
299 def writeIntoFile(self, f):
300 """Write the flight into a file."""
301 print >> f, "callsign=%s" % (self.callsign,)
302 date = self.departureTime.date()
303 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
304 print >> f, "dep_airport=%s" % (self.departureICAO,)
305 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
306 print >> f, "planecode=%s" % \
307 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
308 print >> f, "planetype=%s" % (self.aircraftTypeName,)
309 print >> f, "tail_nr=%s" % (self.tailNumber,)
310 print >> f, "passenger=%d" % (self.numPassengers,)
311 print >> f, "crew=%d" % (self.numCrew,)
312 print >> f, "bag=%d" % (self.bagWeight,)
313 print >> f, "cargo=%d" % (self.cargoWeight,)
314 print >> f, "mail=%d" % (self.mailWeight,)
315 print >> f, "flight_route=%s" % (self.route,)
316 departureTime = self.departureTime
317 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
318 (departureTime.hour, departureTime.minute, departureTime.second)
319 arrivalTime = self.arrivalTime
320 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
321 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
322 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
323
324#---------------------------------------------------------------------------------------
325
326class AcceptedFlight(RPCObject):
327 """A flight that has been already accepted."""
328 # The instructions for the construction
329 @staticmethod
330 def parseTimestamp(s):
331 """Parse the given RPC timestamp."""
332 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
333 return calendar.timegm(dt.utctimetuple())
334
335 _instructions = {
336 "bookedFlight" : lambda value: BookedFlight(value),
337 "numPassengers" : int,
338 "fuelUsed" : int,
339 "rating" : lambda value: float(value) if value else 0.0
340 }
341
342 def __init__(self, value):
343 """Construct the booked flight object from the given RPC result
344 value."""
345 super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
346 self.flightTimeStart = \
347 AcceptedFlight.parseTimestamp(self.flightDate + " " +
348 self.flightTimeStart)
349 self.flightTimeEnd = \
350 AcceptedFlight.parseTimestamp(self.flightDate + " " +
351 self.flightTimeEnd)
352 if self.flightTimeEnd<self.flightTimeStart:
353 self.flightTimeEnd += 24*60*60
354
355#---------------------------------------------------------------------------------------
356
357class Plane(rpccommon.Plane, RPCObject):
358 """An airplane in the fleet."""
359 _instructions = {
360 "status" : lambda value: rpccommon.Plane.str2status(value),
361 "gateNumber" : lambda value: value if value else None,
362 "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
363 }
364
365 def __init__(self, value):
366 """Construct the plane."""
367 RPCObject.__init__(self, value, instructions = Plane._instructions)
368 self.aircraftType = self.typeCode
369 del self.typeCode
370
371#---------------------------------------------------------------------------------------
372
373class Fleet(rpccommon.Fleet):
374 """The fleet."""
375 def __init__(self, value):
376 """Construct the fleet."""
377 super(Fleet, self).__init__()
378 for planeValue in value:
379 self._addPlane(Plane(planeValue))
380
381#---------------------------------------------------------------------------------------
382
383class AircraftTypeInfo(RPCObject):
384 """Information about an aircraft type."""
385 # The instructions for the construction
386 _instructions = {
387 "dow" : int,
388 "dowCockpit" : int,
389 "dowCabin" : int,
390 }
391
392 def __init__(self, value):
393 """Construct the booked flight object from the given RPC result
394 value."""
395 super(AircraftTypeInfo, self).__init__(value,
396 AircraftTypeInfo._instructions)
397 self.aircraftType = const.icao2Type[value["typeCode"]]
398
399#---------------------------------------------------------------------------------------
400
401class Registration(object):
402 """Data for registration."""
403 def __init__(self, surName, firstName, nameOrder,
404 yearOfBirth, emailAddress, emailAddressPublic,
405 vatsimID, ivaoID, phoneNumber, nationality, password):
406 """Construct the registration data."""
407 self.surName = surName
408 self.firstName = firstName
409 self.nameOrder = nameOrder
410 self.yearOfBirth = yearOfBirth
411 self.emailAddress = emailAddress
412 self.emailAddressPublic = 1 if emailAddressPublic is True else \
413 0 if emailAddressPublic is False else emailAddressPublic
414 self.vatsimID = "" if vatsimID is None else vatsimID
415 self.ivaoID = "" if ivaoID is None else ivaoID
416 self.phoneNumber = phoneNumber
417 self.nationality = nationality
418 self.password = password
419
420#---------------------------------------------------------------------------------------
421
422class RPCException(Exception):
423 """An exception thrown by RPC operations."""
424 def __init__(self, result, message = None):
425 """Construct the exception."""
426 self._result = result
427 if message is None:
428 message = "RPC call failed with result code: %d" % (result,)
429 super(RPCException, self).__init__(message)
430
431 @property
432 def result(self):
433 """Get the result code."""
434 return self._result
435
436#---------------------------------------------------------------------------------------
437
438class Client(object):
439 """The RPC client interface."""
440 # The client protocol version
441 VERSION = 2
442
443 # Result code: OK
444 RESULT_OK = 0
445
446 # Result code: the login has failed
447 RESULT_LOGIN_FAILED = 1
448
449 # Result code: the given session ID is unknown (it might have expired).
450 RESULT_SESSION_INVALID = 2
451
452 # Result code: some database error
453 RESULT_DATABASE_ERROR = 3
454
455 # Result code: invalid data
456 RESULT_INVALID_DATA = 4
457
458 # Result code: the flight does not exist
459 RESULT_FLIGHT_NOT_EXISTS = 101
460
461 # Result code: the flight has already been reported.
462 RESULT_FLIGHT_ALREADY_REPORTED = 102
463
464 # Result code: a user with the given e-mail address already exists
465 RESULT_EMAIL_ALREADY_REGISTERED = 103
466
467 def __init__(self, getCredentialsFn):
468 """Construct the client."""
469 self._getCredentialsFn = getCredentialsFn
470
471 self._server = jsonrpclib.Server(MAVA_BASE_URL + "/jsonrpc.php")
472
473 self._userName = None
474 self._passwordHash = None
475 self._sessionID = None
476 self._loginCount = 0
477
478 @property
479 def valid(self):
480 """Determine if the client is valid, i.e. there is a session ID
481 stored."""
482 return self._sessionID is not None
483
484 def setCredentials(self, userName, password):
485 """Set the credentials for future logins."""
486
487 self._userName = userName
488
489 md5 = hashlib.md5()
490 md5.update(password)
491 self._passwordHash = md5.hexdigest()
492
493 self._sessionID = None
494
495 def register(self, registrationData):
496 """Register with the given data.
497
498 Returns a tuple of:
499 - the error code,
500 - the PID if there is no error."""
501 reply = Reply(self._server.register(registrationData))
502
503 return (reply.result,
504 reply.value["pid"] if reply.result==Client.RESULT_OK else None)
505
506 def login(self):
507 """Login using the given previously set credentials.
508
509 The session ID is stored in the object and used for later calls.
510
511 Returns the name of the pilot on success, or None on error."""
512 self._sessionID = None
513
514 reply = Reply(self._server.login(self._userName, self._passwordHash,
515 Client.VERSION))
516 if reply.result == Client.RESULT_OK:
517 self._loginCount += 1
518 self._sessionID = reply.value["sessionID"]
519
520 types = [BookedFlight.TYPECODE2TYPE[typeCode]
521 for typeCode in reply.value["typeCodes"]]
522
523 return (reply.value["name"], reply.value["rank"], types)
524 else:
525 return None
526
527 def getTypes(self):
528 """Get the data of the aircraft types supported by us."""
529 value = self._performCall(lambda sessionID:
530 self._server.getTypes(sessionID))
531
532 return [AircraftTypeInfo(t) for t in value]
533
534 def getFlights(self):
535 """Get the flights available for performing."""
536 bookedFlights = []
537 reportedFlights = []
538 rejectedFlights = []
539
540 value = self._performCall(lambda sessionID:
541 self._server.getFlights(sessionID))
542 for flightData in value:
543 flight = BookedFlight(flightData)
544 if flight.status == BookedFlight.STATUS_BOOKED:
545 bookedFlights.append(flight)
546 elif flight.status == BookedFlight.STATUS_REPORTED:
547 reportedFlights.append(flight)
548 elif flight.status == BookedFlight.STATUS_REJECTED:
549 rejectedFlights.append(flight)
550
551 for flights in [bookedFlights, reportedFlights, rejectedFlights]:
552 flights.sort(cmp = lambda flight1, flight2:
553 cmp(flight1.departureTime, flight2.departureTime))
554
555 return (bookedFlights, reportedFlights, rejectedFlights)
556
557 def getAcceptedFlights(self):
558 """Get the flights that are already accepted."""
559 value = self._performCall(lambda sessionID:
560 self._server.getAcceptedFlights(sessionID))
561 flights = []
562 for flight in value:
563 flights.append(AcceptedFlight(flight))
564 return flights
565
566 def getEntryExamStatus(self):
567 """Get the status of the exams needed for joining MAVA."""
568 value = self._performCall(lambda sessionID:
569 self._server.getEntryExamStatus(sessionID))
570 return (value["entryExamPassed"], value["entryExamLink"],
571 value["checkFlightStatus"], value["madeFO"])
572
573 def getFleet(self):
574 """Query and return the fleet."""
575 value = self._performCall(lambda sessionID:
576 self._server.getFleet(sessionID))
577
578 return Fleet(value)
579
580 def updatePlane(self, tailNumber, status, gateNumber):
581 """Update the state and position of the plane with the given tail
582 number."""
583 status = rpccommon.Plane.status2str(status)
584 self._performCall(lambda sessionID:
585 self._server.updatePlane(sessionID, tailNumber,
586 status, gateNumber))
587
588 def addPIREP(self, flightID, pirep, update = False):
589 """Add the PIREP for the given flight."""
590 (result, _value) = \
591 self._performCall(lambda sessionID:
592 self._server.addPIREP(sessionID, flightID, pirep,
593 update),
594 acceptResults = [Client.RESULT_FLIGHT_ALREADY_REPORTED,
595 Client.RESULT_FLIGHT_NOT_EXISTS])
596 return result
597
598 def updateOnlineACARS(self, acars):
599 """Update the online ACARS from the given data."""
600 self._performCall(lambda sessionID:
601 self._server.updateOnlineACARS(sessionID, acars))
602
603 def setCheckFlightPassed(self, type):
604 """Mark the check flight of the user passed with the given type."""
605 self._performCall(lambda sessionID:
606 self._server.setCheckFlightPassed(sessionID, type))
607
608 def getPIREP(self, flightID):
609 """Get the PIREP data for the flight with the given ID."""
610 value = self._performCall(lambda sessionID:
611 self._server.getPIREP(sessionID, flightID))
612 return value
613
614 def reflyFlights(self, flightIDs):
615 """Mark the flights with the given IDs for reflying."""
616 self._performCall(lambda sessionID:
617 self._server.reflyFlights(sessionID, flightIDs))
618
619 def deleteFlights(self, flightIDs):
620 """Delete the flights with the given IDs."""
621 self._performCall(lambda sessionID:
622 self._server.deleteFlights(sessionID, flightIDs))
623
624 def getTimetable(self, date, types = None):
625 """Get the time table for the given date restricted to the given list
626 of type codes, if any."""
627 typeCodes = None if types is None else \
628 [BookedFlight.TYPE2TYPECODE[type] for type in types]
629
630 values = self._performCall(lambda sessionID:
631 self._server.getTimetable(sessionID,
632 date.strftime("%Y-%m-%d"),
633 date.weekday()+1,
634 typeCodes))
635 return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
636 for value in values],
637 date)
638
639 def bookFlights(self, flightIDs, date, tailNumber):
640 """Book the flights with the given IDs on the given date to be flown
641 with the plane of the given tail number."""
642 values = self._performCall(lambda sessionID:
643 self._server.bookFlights(sessionID,
644 flightIDs,
645 date.strftime("%Y-%m-%d"),
646 tailNumber))
647 return [BookedFlight(value) for value in values]
648
649 def _performCall(self, callFn, acceptResults = []):
650 """Perform a call using the given call function.
651
652 acceptResults should be a list of result codes that should be accepted
653 besides RESULT_OK. If this list is not empty, the returned value is a
654 tuple of the result code and the corresponding value. Otherwise only
655 RESULT_OK is accepted, and the value is returned.
656
657 All other error codes are converted to exceptions."""
658 numAttempts = 0
659 while True:
660 reply = Reply(callFn(self._ensureSession()))
661 numAttempts += 1
662 result = reply.result
663 if result==Client.RESULT_SESSION_INVALID:
664 self._sessionID = None
665 if numAttempts==3:
666 raise RPCException(result)
667 elif result!=Client.RESULT_OK and result not in acceptResults:
668 raise RPCException(result)
669 elif acceptResults:
670 return (result, reply.value)
671 else:
672 return reply.value
673
674 def _ensureSession(self):
675 """Ensure that there is a valid session ID."""
676 while self._sessionID is None:
677 if self._userName is not None and self._passwordHash is not None:
678 if not self.login():
679 self._userName = self._passwordHash = None
680
681 if self._userName is None or self._passwordHash is None:
682 (self._userName, password) = self._getCredentialsFn()
683 if self._userName is None:
684 raise RPCException(Client.RESULT_LOGIN_FAILED)
685
686 md5 = hashlib.md5()
687 md5.update(password)
688 self._passwordHash = md5.hexdigest()
689
690 return self._sessionID
691
692#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.