source: src/mlx/rpc.py@ 1033:330058d37574

python3
Last change on this file since 1033:330058d37574 was 1033:330058d37574, checked in by István Váradi <ivaradi@…>, 2 years ago

Updates for the new crew and passenger handling (re #357)

File size: 26.7 KB
Line 
1from . import const
2from . import rpccommon
3
4from .common import MAVA_BASE_URL, fixUnpickled
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.items():
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("Failed to convert value '%s' of attribute '%s':" % \
40 (value, key), file=sys.stderr)
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 0 if cs1==cs2 else -1 if cs1<cs2 else 1
114 except:
115 return 0 if self.callsign==other.callsign \
116 else -1 if self.callsign<other.callsign else 1
117 else:
118 v1 = getattr(self, name)
119 v2 = getattr(other, name)
120 return 0 if v1==v2 else -1 if v1<v2 else 1
121
122 def __repr__(self):
123 return "ScheduledFlight<%d, %d, %s, %s (%s) - %s (%s) -> %d, %d>" % \
124 (self.id, self.pairID, BookedFlight.TYPE2TYPECODE[self.aircraftType],
125 self.departureICAO, str(self.departureTime),
126 self.arrivalICAO, str(self.arrivalTime),
127 self.duration, self.type)
128
129#---------------------------------------------------------------------------------------
130
131class ScheduledFlightPair(object):
132 """A pair of scheduled flights.
133
134 Occasionally, one of the flights may be missing."""
135 @staticmethod
136 def scheduledFlights2Pairs(scheduledFlights, date):
137 """Convert the given list of scheduled flights into a list of flight
138 pairs."""
139 weekday = str(date.weekday()+1)
140
141 flights = {}
142 weekdayFlights = {}
143 for flight in scheduledFlights:
144 flights[flight.id] = flight
145 if (flight.type==ScheduledFlight.TYPE_NORMAL and
146 flight.arrivalICAO!="LHBP" and weekday in flight.days and
147 flight.validFrom<=date and flight.validTo>=date) or \
148 flight.type==ScheduledFlight.TYPE_VIP:
149 weekdayFlights[flight.id] = flight
150
151 flightPairs = []
152
153 while weekdayFlights:
154 (id, flight) = weekdayFlights.popitem()
155 if flight.type==ScheduledFlight.TYPE_NORMAL:
156 pairID = flight.pairID
157 if pairID in flights:
158 pairFlight = flights[pairID]
159 if flight.departureICAO=="LHBP" or \
160 (pairFlight.departureICAO!="LHBP" and
161 flight.callsign<pairFlight.callsign):
162 flightPairs.append(ScheduledFlightPair(flight, pairFlight))
163 else:
164 flightPairs.append(ScheduledFlightPair(pairFlight, flight))
165 del flights[pairID]
166 if pairID in weekdayFlights:
167 del weekdayFlights[pairID]
168 elif flight.type==ScheduledFlight.TYPE_VIP:
169 flightPairs.append(ScheduledFlightPair(flight))
170
171 flightPairs.sort(key = lambda pair: pair.flight0.date)
172
173 return flightPairs
174
175 def __init__(self, flight0, flight1 = None):
176 """Construct the pair with the given flights."""
177 self.flight0 = flight0
178 self.flight1 = flight1
179
180 def compareBy(self, other, name):
181 """Compare this flight pair with the other one according to the given
182 attribute name, considering the first flights."""
183 return self.flight0.compareBy(other.flight0, name)
184
185 def __repr__(self):
186 return "ScheduledFlightPair<%s, %s, %s>" % \
187 (self.flight0.callsign, self.flight0.departureICAO,
188 self.flight0.arrivalICAO)
189
190#---------------------------------------------------------------------------------------
191
192class BookedFlight(RPCObject):
193 """A booked flight."""
194 # FIXME: copied from web.BookedFlight
195 TYPECODE2TYPE = { "B736" : const.AIRCRAFT_B736,
196 "B737" : const.AIRCRAFT_B737,
197 "B738" : const.AIRCRAFT_B738,
198 "B73H" : const.AIRCRAFT_B738C,
199 "B732" : const.AIRCRAFT_B732,
200 "B733" : const.AIRCRAFT_B733,
201 "B734" : const.AIRCRAFT_B734,
202 "B735" : const.AIRCRAFT_B735,
203 "DH8D" : const.AIRCRAFT_DH8D,
204 "B762" : const.AIRCRAFT_B762,
205 "B763" : const.AIRCRAFT_B763,
206 "CRJ2" : const.AIRCRAFT_CRJ2,
207 "F70" : const.AIRCRAFT_F70,
208 "LI2" : const.AIRCRAFT_DC3,
209 "T134" : const.AIRCRAFT_T134,
210 "T154" : const.AIRCRAFT_T154,
211 "YK40" : const.AIRCRAFT_YK40,
212 "B462" : const.AIRCRAFT_B462,
213 "IL62" : const.AIRCRAFT_IL62 }
214
215 # FIXME: copied from web.BookedFlight
216 TYPE2TYPECODE = { const.AIRCRAFT_B736 : "B736",
217 const.AIRCRAFT_B737 : "B737",
218 const.AIRCRAFT_B738 : "B738",
219 const.AIRCRAFT_B738C : "B73H",
220 const.AIRCRAFT_B732 : "B732",
221 const.AIRCRAFT_B733 : "B733",
222 const.AIRCRAFT_B734 : "B734",
223 const.AIRCRAFT_B735 : "B735",
224 const.AIRCRAFT_DH8D : "DH8D",
225 const.AIRCRAFT_B762 : "B762",
226 const.AIRCRAFT_B763 : "B763",
227 const.AIRCRAFT_CRJ2 : "CRJ2",
228 const.AIRCRAFT_F70 : "F70",
229 const.AIRCRAFT_DC3 : "LI2",
230 const.AIRCRAFT_T134 : "T134",
231 const.AIRCRAFT_T154 : "T155",
232 const.AIRCRAFT_YK40 : "YK40",
233 const.AIRCRAFT_B462 : "B462",
234 const.AIRCRAFT_IL62 : "IL62" }
235
236 # FIXME: copied from web.BookedFlight
237 @staticmethod
238 def _decodeAircraftType(typeCode):
239 """Decode the aircraft type from the given typeCode."""
240 if typeCode in BookedFlight.TYPECODE2TYPE:
241 return BookedFlight.TYPECODE2TYPE[typeCode]
242 else:
243 raise Exception("Invalid aircraft type code: '" + typeCode + "'")
244
245 @staticmethod
246 def _decodeStatus(status):
247 """Decode the status from the status string."""
248 if status=="booked":
249 return BookedFlight.STATUS_BOOKED
250 elif status=="reported":
251 return BookedFlight.STATUS_REPORTED
252 elif status=="accepted":
253 return BookedFlight.STATUS_ACCEPTED
254 elif status=="rejected":
255 return BookedFlight.STATUS_REJECTED
256 else:
257 raise Exception("Invalid flight status code: '" + status + "'")
258
259 # FIXME: copied from web.BookedFlight
260 @staticmethod
261 def getDateTime(date, time):
262 """Get a datetime object from the given textual date and time."""
263 return datetime.datetime.strptime(date + " " + time,
264 "%Y-%m-%d %H:%M:%S")
265
266 # FIXME: copied from web.BookedFlight
267 STATUS_BOOKED = 1
268
269 # FIXME: copied from web.BookedFlight
270 STATUS_REPORTED = 2
271
272 # FIXME: copied from web.BookedFlight
273 STATUS_ACCEPTED = 3
274
275 # FIXME: copied from web.BookedFlight
276 STATUS_REJECTED = 4
277
278 # Flight type: scheduled
279 FLIGHT_TYPE_SCHEDULED = 0
280
281 # Flight type: VIP
282 FLIGHT_TYPE_VIP = 1
283
284 # Flight type: charter
285 FLIGHT_TYPE_CHARTER = 2
286
287 # The instructions for the construction
288 _instructions = {
289 "numPassengers" : int,
290 "numChildren" : int,
291 "numInfants" : int,
292 "numCabinCrew" : int,
293 "dowNumCabinCrew" : int,
294 "numCockpitCrew" : int,
295 "bagWeight" : int,
296 "cargoWeight" : int,
297 "mailWeight" : int,
298 "flightType" : int,
299 "dow": int,
300 "maxPassengers": int,
301 "aircraftType" : lambda value: BookedFlight._decodeAircraftType(value),
302 "status" : lambda value: BookedFlight._decodeStatus(value)
303 }
304
305 def __init__(self, value):
306 """Construct the booked flight object from the given RPC result
307 value."""
308 self.status = BookedFlight.STATUS_BOOKED
309 super(BookedFlight, self).__init__(value, BookedFlight._instructions)
310 self.departureTime = \
311 BookedFlight.getDateTime(self.date, self.departureTime)
312 self.arrivalTime = \
313 BookedFlight.getDateTime(self.date, self.arrivalTime)
314 if self.arrivalTime<self.departureTime:
315 self.arrivalTime += datetime.timedelta(days = 1)
316
317 def writeIntoFile(self, f):
318 """Write the flight into a file."""
319 print("callsign=%s" % (self.callsign,), file=f)
320 date = self.departureTime.date()
321 print("date=%04d-%02d-%0d" % (date.year, date.month, date.day), file=f)
322 print("dep_airport=%s" % (self.departureICAO,), file=f)
323 print("dest_airport=%s" % (self.arrivalICAO,), file=f)
324 print("planecode=%s" % \
325 (BookedFlight.TYPE2TYPECODE[self.aircraftType],), file=f)
326 print("planetype=%s" % (self.aircraftTypeName,), file=f)
327 print("tail_nr=%s" % (self.tailNumber,), file=f)
328 print("passenger=%d" % (self.numPassengers,), file=f)
329 print("crew=%d" % (self.numCrew,), file=f)
330 print("bag=%d" % (self.bagWeight,), file=f)
331 print("cargo=%d" % (self.cargoWeight,), file=f)
332 print("mail=%d" % (self.mailWeight,), file=f)
333 print("flight_route=%s" % (self.route,), file=f)
334 departureTime = self.departureTime
335 print("departure_time=%02d\\:%02d\\:%02d" % \
336 (departureTime.hour, departureTime.minute, departureTime.second), file=f)
337 arrivalTime = self.arrivalTime
338 print("arrival_time=%02d\\:%02d\\:%02d" % \
339 (arrivalTime.hour, arrivalTime.minute, arrivalTime.second), file=f)
340 print("foglalas_id=%s" % ("0" if self.id is None else self.id,), file=f)
341
342 def __setstate__(self, state):
343 """Set the state from the given unpickled dictionary."""
344 self.__dict__.update(fixUnpickled(state))
345
346#---------------------------------------------------------------------------------------
347
348class AcceptedFlight(RPCObject):
349 """A flight that has been already accepted."""
350 # The instructions for the construction
351 @staticmethod
352 def parseTimestamp(s):
353 """Parse the given RPC timestamp."""
354 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
355 return calendar.timegm(dt.utctimetuple())
356
357 _instructions = {
358 "bookedFlight" : lambda value: BookedFlight(value),
359 "numPassengers" : int,
360 "numChildren" : int,
361 "numInfants" : int,
362 "fuelUsed" : int,
363 "rating" : lambda value: float(value) if value else 0.0
364 }
365
366 def __init__(self, value):
367 """Construct the booked flight object from the given RPC result
368 value."""
369 super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
370 self.flightTimeStart = \
371 AcceptedFlight.parseTimestamp(self.flightDate + " " +
372 self.flightTimeStart)
373 self.flightTimeEnd = \
374 AcceptedFlight.parseTimestamp(self.flightDate + " " +
375 self.flightTimeEnd)
376 if self.flightTimeEnd<self.flightTimeStart:
377 self.flightTimeEnd += 24*60*60
378
379 self.totalNumPassengers = self.numPassengers + self.numChildren + self.numInfants
380
381#---------------------------------------------------------------------------------------
382
383class Plane(rpccommon.Plane, RPCObject):
384 """An airplane in the fleet."""
385 _instructions = {
386 "status" : lambda value: rpccommon.Plane.str2status(value),
387 "gateNumber" : lambda value: value if value else None,
388 "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
389 }
390
391 def __init__(self, value):
392 """Construct the plane."""
393 RPCObject.__init__(self, value, instructions = Plane._instructions)
394 self.aircraftType = self.typeCode
395 del self.typeCode
396
397#---------------------------------------------------------------------------------------
398
399class Fleet(rpccommon.Fleet):
400 """The fleet."""
401 def __init__(self, value):
402 """Construct the fleet."""
403 super(Fleet, self).__init__()
404 for planeValue in value:
405 self._addPlane(Plane(planeValue))
406
407#---------------------------------------------------------------------------------------
408
409class Registration(object):
410 """Data for registration."""
411 def __init__(self, surName, firstName, nameOrder,
412 yearOfBirth, emailAddress, emailAddressPublic,
413 vatsimID, ivaoID, phoneNumber, nationality, password):
414 """Construct the registration data."""
415 self.surName = surName
416 self.firstName = firstName
417 self.nameOrder = nameOrder
418 self.yearOfBirth = yearOfBirth
419 self.emailAddress = emailAddress
420 self.emailAddressPublic = 1 if emailAddressPublic is True else \
421 0 if emailAddressPublic is False else emailAddressPublic
422 self.vatsimID = "" if vatsimID is None else vatsimID
423 self.ivaoID = "" if ivaoID is None else ivaoID
424 self.phoneNumber = phoneNumber
425 self.nationality = nationality
426 self.password = password
427
428#---------------------------------------------------------------------------------------
429
430class RPCException(Exception):
431 """An exception thrown by RPC operations."""
432 def __init__(self, result, message = None):
433 """Construct the exception."""
434 self._result = result
435 if message is None:
436 message = "RPC call failed with result code: %d" % (result,)
437 super(RPCException, self).__init__(message)
438
439 @property
440 def result(self):
441 """Get the result code."""
442 return self._result
443
444#---------------------------------------------------------------------------------------
445
446class Client(object):
447 """The RPC client interface."""
448 # The client protocol version
449 VERSION = 2
450
451 # Result code: OK
452 RESULT_OK = 0
453
454 # Result code: the login has failed
455 RESULT_LOGIN_FAILED = 1
456
457 # Result code: the given session ID is unknown (it might have expired).
458 RESULT_SESSION_INVALID = 2
459
460 # Result code: some database error
461 RESULT_DATABASE_ERROR = 3
462
463 # Result code: invalid data
464 RESULT_INVALID_DATA = 4
465
466 # Result code: the flight does not exist
467 RESULT_FLIGHT_NOT_EXISTS = 101
468
469 # Result code: the flight has already been reported.
470 RESULT_FLIGHT_ALREADY_REPORTED = 102
471
472 # Result code: a user with the given e-mail address already exists
473 RESULT_EMAIL_ALREADY_REGISTERED = 103
474
475 def __init__(self, getCredentialsFn):
476 """Construct the client."""
477 self._getCredentialsFn = getCredentialsFn
478
479 self._server = jsonrpclib.Server(MAVA_BASE_URL + "/jsonrpc.php")
480
481 self._userName = None
482 self._passwordHash = None
483 self._sessionID = None
484 self._loginCount = 0
485
486 @property
487 def valid(self):
488 """Determine if the client is valid, i.e. there is a session ID
489 stored."""
490 return self._sessionID is not None
491
492 def setCredentials(self, userName, password):
493 """Set the credentials for future logins."""
494
495 self._userName = userName
496
497 md5 = hashlib.md5()
498 md5.update(password.encode("utf-8"))
499 self._passwordHash = md5.hexdigest()
500
501 self._sessionID = None
502
503 def register(self, registrationData):
504 """Register with the given data.
505
506 Returns a tuple of:
507 - the error code,
508 - the PID if there is no error."""
509 reply = Reply(self._server.register(registrationData))
510
511 return (reply.result,
512 reply.value["pid"] if reply.result==Client.RESULT_OK else None)
513
514 def login(self):
515 """Login using the given previously set credentials.
516
517 The session ID is stored in the object and used for later calls.
518
519 Returns the name of the pilot on success, or None on error."""
520 self._sessionID = None
521
522 reply = Reply(self._server.login(self._userName, self._passwordHash,
523 Client.VERSION))
524 if reply.result == Client.RESULT_OK:
525 self._loginCount += 1
526 self._sessionID = reply.value["sessionID"]
527
528 types = [BookedFlight.TYPECODE2TYPE[typeCode]
529 for typeCode in reply.value["typeCodes"]]
530
531 return (reply.value["name"], reply.value["rank"], types)
532 else:
533 return None
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(key = lambda flight: flight.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.