source: src/mlx/rpc.py@ 918:cfa4d89368fa

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

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

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 const.icao2Type:
239 return const.icao2Type[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 "numCockpitCrew" : int,
280 "numCabinCrew" : int,
281 "bagWeight" : int,
282 "cargoWeight" : int,
283 "mailWeight" : int,
284 "aircraftType" : lambda value: BookedFlight._decodeAircraftType(value),
285 "status" : lambda value: BookedFlight._decodeStatus(value)
286 }
287
288 def __init__(self, value):
289 """Construct the booked flight object from the given RPC result
290 value."""
291 self.status = BookedFlight.STATUS_BOOKED
292 super(BookedFlight, self).__init__(value, BookedFlight._instructions)
293 self.departureTime = \
294 BookedFlight.getDateTime(self.date, self.departureTime)
295 self.arrivalTime = \
296 BookedFlight.getDateTime(self.date, self.arrivalTime)
297 if self.arrivalTime<self.departureTime:
298 self.arrivalTime += datetime.timedelta(days = 1)
299
300 def writeIntoFile(self, f):
301 """Write the flight into a file."""
302 print >> f, "callsign=%s" % (self.callsign,)
303 date = self.departureTime.date()
304 print >> f, "date=%04d-%02d-%0d" % (date.year, date.month, date.day)
305 print >> f, "dep_airport=%s" % (self.departureICAO,)
306 print >> f, "dest_airport=%s" % (self.arrivalICAO,)
307 print >> f, "planecode=%s" % \
308 (BookedFlight.TYPE2TYPECODE[self.aircraftType],)
309 print >> f, "planetype=%s" % (self.aircraftTypeName,)
310 print >> f, "tail_nr=%s" % (self.tailNumber,)
311 print >> f, "passenger=%d" % (self.numPassengers,)
312 print >> f, "crew=%d" % (self.numCockpitrew + self.numCabinCrew,)
313 print >> f, "bag=%d" % (self.bagWeight,)
314 print >> f, "cargo=%d" % (self.cargoWeight,)
315 print >> f, "mail=%d" % (self.mailWeight,)
316 print >> f, "flight_route=%s" % (self.route,)
317 departureTime = self.departureTime
318 print >> f, "departure_time=%02d\\:%02d\\:%02d" % \
319 (departureTime.hour, departureTime.minute, departureTime.second)
320 arrivalTime = self.arrivalTime
321 print >> f, "arrival_time=%02d\\:%02d\\:%02d" % \
322 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second)
323 print >> f, "foglalas_id=%s" % ("0" if self.id is None else self.id,)
324
325#---------------------------------------------------------------------------------------
326
327class AcceptedFlight(RPCObject):
328 """A flight that has been already accepted."""
329 # The instructions for the construction
330 @staticmethod
331 def parseTimestamp(s):
332 """Parse the given RPC timestamp."""
333 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
334 return calendar.timegm(dt.utctimetuple())
335
336 _instructions = {
337 "bookedFlight" : lambda value: BookedFlight(value),
338 "numPassengers" : int,
339 "fuelUsed" : int,
340 "rating" : lambda value: float(value) if value else 0.0
341 }
342
343 def __init__(self, value):
344 """Construct the booked flight object from the given RPC result
345 value."""
346 super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
347 self.flightTimeStart = \
348 AcceptedFlight.parseTimestamp(self.flightDate + " " +
349 self.flightTimeStart)
350 self.flightTimeEnd = \
351 AcceptedFlight.parseTimestamp(self.flightDate + " " +
352 self.flightTimeEnd)
353 if self.flightTimeEnd<self.flightTimeStart:
354 self.flightTimeEnd += 24*60*60
355
356#---------------------------------------------------------------------------------------
357
358class Plane(rpccommon.Plane, RPCObject):
359 """An airplane in the fleet."""
360 _instructions = {
361 "status" : lambda value: rpccommon.Plane.str2status(value),
362 "gateNumber" : lambda value: value if value else None,
363 "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
364 }
365
366 def __init__(self, value):
367 """Construct the plane."""
368 RPCObject.__init__(self, value, instructions = Plane._instructions)
369 self.aircraftType = self.typeCode
370 del self.typeCode
371
372#---------------------------------------------------------------------------------------
373
374class Fleet(rpccommon.Fleet):
375 """The fleet."""
376 def __init__(self, value):
377 """Construct the fleet."""
378 super(Fleet, self).__init__()
379 for planeValue in value:
380 self._addPlane(Plane(planeValue))
381
382#---------------------------------------------------------------------------------------
383
384class AircraftTypeInfo(RPCObject):
385 """Information about an aircraft type."""
386 # The instructions for the construction
387 _instructions = {
388 "dow" : int,
389 "dowCockpit" : int,
390 "dowCabin" : int,
391 }
392
393 def __init__(self, value):
394 """Construct the booked flight object from the given RPC result
395 value."""
396 super(AircraftTypeInfo, self).__init__(value,
397 AircraftTypeInfo._instructions)
398 self.aircraftType = const.icao2Type[value["typeCode"]]
399
400#---------------------------------------------------------------------------------------
401
402class Registration(object):
403 """Data for registration."""
404 def __init__(self, surName, firstName, nameOrder,
405 yearOfBirth, emailAddress, emailAddressPublic,
406 vatsimID, ivaoID, phoneNumber, nationality, password):
407 """Construct the registration data."""
408 self.surName = surName
409 self.firstName = firstName
410 self.nameOrder = nameOrder
411 self.yearOfBirth = yearOfBirth
412 self.emailAddress = emailAddress
413 self.emailAddressPublic = 1 if emailAddressPublic is True else \
414 0 if emailAddressPublic is False else emailAddressPublic
415 self.vatsimID = "" if vatsimID is None else vatsimID
416 self.ivaoID = "" if ivaoID is None else ivaoID
417 self.phoneNumber = phoneNumber
418 self.nationality = nationality
419 self.password = password
420
421#---------------------------------------------------------------------------------------
422
423class RPCException(Exception):
424 """An exception thrown by RPC operations."""
425 def __init__(self, result, message = None):
426 """Construct the exception."""
427 self._result = result
428 if message is None:
429 message = "RPC call failed with result code: %d" % (result,)
430 super(RPCException, self).__init__(message)
431
432 @property
433 def result(self):
434 """Get the result code."""
435 return self._result
436
437#---------------------------------------------------------------------------------------
438
439class Client(object):
440 """The RPC client interface."""
441 # The client protocol version
442 VERSION = 2
443
444 # Result code: OK
445 RESULT_OK = 0
446
447 # Result code: the login has failed
448 RESULT_LOGIN_FAILED = 1
449
450 # Result code: the given session ID is unknown (it might have expired).
451 RESULT_SESSION_INVALID = 2
452
453 # Result code: some database error
454 RESULT_DATABASE_ERROR = 3
455
456 # Result code: invalid data
457 RESULT_INVALID_DATA = 4
458
459 # Result code: the flight does not exist
460 RESULT_FLIGHT_NOT_EXISTS = 101
461
462 # Result code: the flight has already been reported.
463 RESULT_FLIGHT_ALREADY_REPORTED = 102
464
465 # Result code: a user with the given e-mail address already exists
466 RESULT_EMAIL_ALREADY_REGISTERED = 103
467
468 def __init__(self, getCredentialsFn):
469 """Construct the client."""
470 self._getCredentialsFn = getCredentialsFn
471
472 self._server = jsonrpclib.Server(MAVA_BASE_URL + "/jsonrpc.php")
473
474 self._userName = None
475 self._passwordHash = None
476 self._sessionID = None
477 self._loginCount = 0
478
479 @property
480 def valid(self):
481 """Determine if the client is valid, i.e. there is a session ID
482 stored."""
483 return self._sessionID is not None
484
485 def setCredentials(self, userName, password):
486 """Set the credentials for future logins."""
487
488 self._userName = userName
489
490 md5 = hashlib.md5()
491 md5.update(password)
492 self._passwordHash = md5.hexdigest()
493
494 self._sessionID = None
495
496 def register(self, registrationData):
497 """Register with the given data.
498
499 Returns a tuple of:
500 - the error code,
501 - the PID if there is no error."""
502 reply = Reply(self._server.register(registrationData))
503
504 return (reply.result,
505 reply.value["pid"] if reply.result==Client.RESULT_OK else None)
506
507 def login(self):
508 """Login using the given previously set credentials.
509
510 The session ID is stored in the object and used for later calls.
511
512 Returns the name of the pilot on success, or None on error."""
513 self._sessionID = None
514
515 reply = Reply(self._server.login(self._userName, self._passwordHash,
516 Client.VERSION))
517 if reply.result == Client.RESULT_OK:
518 self._loginCount += 1
519 self._sessionID = reply.value["sessionID"]
520
521 types = [const.icao2Type[typeCode]
522 for typeCode in reply.value["typeCodes"]]
523
524 return (reply.value["name"], reply.value["rank"], types)
525 else:
526 return None
527
528 def getTypes(self):
529 """Get the data of the aircraft types supported by us."""
530 value = self._performCall(lambda sessionID:
531 self._server.getTypes(sessionID))
532
533 return [AircraftTypeInfo(t) for t in value]
534
535 def getFlights(self):
536 """Get the flights available for performing."""
537 bookedFlights = []
538 reportedFlights = []
539 rejectedFlights = []
540
541 value = self._performCall(lambda sessionID:
542 self._server.getFlights(sessionID))
543 for flightData in value:
544 flight = BookedFlight(flightData)
545 if flight.status == BookedFlight.STATUS_BOOKED:
546 bookedFlights.append(flight)
547 elif flight.status == BookedFlight.STATUS_REPORTED:
548 reportedFlights.append(flight)
549 elif flight.status == BookedFlight.STATUS_REJECTED:
550 rejectedFlights.append(flight)
551
552 for flights in [bookedFlights, reportedFlights, rejectedFlights]:
553 flights.sort(cmp = lambda flight1, flight2:
554 cmp(flight1.departureTime, flight2.departureTime))
555
556 return (bookedFlights, reportedFlights, rejectedFlights)
557
558 def getAcceptedFlights(self):
559 """Get the flights that are already accepted."""
560 value = self._performCall(lambda sessionID:
561 self._server.getAcceptedFlights(sessionID))
562 flights = []
563 for flight in value:
564 flights.append(AcceptedFlight(flight))
565 return flights
566
567 def getEntryExamStatus(self):
568 """Get the status of the exams needed for joining MAVA."""
569 value = self._performCall(lambda sessionID:
570 self._server.getEntryExamStatus(sessionID))
571 return (value["entryExamPassed"], value["entryExamLink"],
572 value["checkFlightStatus"], value["madeFO"])
573
574 def getFleet(self):
575 """Query and return the fleet."""
576 value = self._performCall(lambda sessionID:
577 self._server.getFleet(sessionID))
578
579 return Fleet(value)
580
581 def updatePlane(self, tailNumber, status, gateNumber):
582 """Update the state and position of the plane with the given tail
583 number."""
584 status = rpccommon.Plane.status2str(status)
585 self._performCall(lambda sessionID:
586 self._server.updatePlane(sessionID, tailNumber,
587 status, gateNumber))
588
589 def addPIREP(self, flightID, pirep, update = False):
590 """Add the PIREP for the given flight."""
591 (result, _value) = \
592 self._performCall(lambda sessionID:
593 self._server.addPIREP(sessionID, flightID, pirep,
594 update),
595 acceptResults = [Client.RESULT_FLIGHT_ALREADY_REPORTED,
596 Client.RESULT_FLIGHT_NOT_EXISTS])
597 return result
598
599 def updateOnlineACARS(self, acars):
600 """Update the online ACARS from the given data."""
601 self._performCall(lambda sessionID:
602 self._server.updateOnlineACARS(sessionID, acars))
603
604 def setCheckFlightPassed(self, type):
605 """Mark the check flight of the user passed with the given type."""
606 self._performCall(lambda sessionID:
607 self._server.setCheckFlightPassed(sessionID, type))
608
609 def getPIREP(self, flightID):
610 """Get the PIREP data for the flight with the given ID."""
611 value = self._performCall(lambda sessionID:
612 self._server.getPIREP(sessionID, flightID))
613 return value
614
615 def reflyFlights(self, flightIDs):
616 """Mark the flights with the given IDs for reflying."""
617 self._performCall(lambda sessionID:
618 self._server.reflyFlights(sessionID, flightIDs))
619
620 def deleteFlights(self, flightIDs):
621 """Delete the flights with the given IDs."""
622 self._performCall(lambda sessionID:
623 self._server.deleteFlights(sessionID, flightIDs))
624
625 def getTimetable(self, date, types = None):
626 """Get the time table for the given date restricted to the given list
627 of type codes, if any."""
628 typeCodes = None if types is None else \
629 [const.icaoCodes[type] for type in types]
630
631 values = self._performCall(lambda sessionID:
632 self._server.getTimetable(sessionID,
633 date.strftime("%Y-%m-%d"),
634 date.weekday()+1,
635 typeCodes))
636 return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
637 for value in values],
638 date)
639
640 def bookFlights(self, flightIDs, date, tailNumber):
641 """Book the flights with the given IDs on the given date to be flown
642 with the plane of the given tail number."""
643 values = self._performCall(lambda sessionID:
644 self._server.bookFlights(sessionID,
645 flightIDs,
646 date.strftime("%Y-%m-%d"),
647 tailNumber))
648 return [BookedFlight(value) for value in values]
649
650 def _performCall(self, callFn, acceptResults = []):
651 """Perform a call using the given call function.
652
653 acceptResults should be a list of result codes that should be accepted
654 besides RESULT_OK. If this list is not empty, the returned value is a
655 tuple of the result code and the corresponding value. Otherwise only
656 RESULT_OK is accepted, and the value is returned.
657
658 All other error codes are converted to exceptions."""
659 numAttempts = 0
660 while True:
661 reply = Reply(callFn(self._ensureSession()))
662 numAttempts += 1
663 result = reply.result
664 if result==Client.RESULT_SESSION_INVALID:
665 self._sessionID = None
666 if numAttempts==3:
667 raise RPCException(result)
668 elif result!=Client.RESULT_OK and result not in acceptResults:
669 raise RPCException(result)
670 elif acceptResults:
671 return (result, reply.value)
672 else:
673 return reply.value
674
675 def _ensureSession(self):
676 """Ensure that there is a valid session ID."""
677 while self._sessionID is None:
678 if self._userName is not None and self._passwordHash is not None:
679 if not self.login():
680 self._userName = self._passwordHash = None
681
682 if self._userName is None or self._passwordHash is None:
683 (self._userName, password) = self._getCredentialsFn()
684 if self._userName is None:
685 raise RPCException(Client.RESULT_LOGIN_FAILED)
686
687 md5 = hashlib.md5()
688 md5.update(password)
689 self._passwordHash = md5.hexdigest()
690
691 return self._sessionID
692
693#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.