Changeset 858:1f655516b7ae for src/mlx


Ignore:
Timestamp:
06/08/17 18:36:07 (7 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

The timetable can be queried, displayed and filtered (re #304)

Location:
src/mlx
Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/const.py

    r835 r858  
    119119                 AIRCRAFT_YK40, AIRCRAFT_DC3,
    120120                 AIRCRAFT_B462]
     121
     122#-------------------------------------------------------------------------------
     123
     124## Aircraft type family: Boeing 737 NG
     125AIRCRAFT_FAMILY_B737NG = 1
     126
     127## Aircraft type family: Boeing 737 Classic
     128AIRCRAFT_FAMILY_B737CL = 2
     129
     130## Aircraft type family: Bombardier Dash-8 Q400
     131AIRCRAFT_FAMILY_DH8D = 3
     132
     133## Aircraft type family: Boeing 767
     134AIRCRAFT_FAMILY_B767 = 4
     135
     136## Aircraft type family: Canadair CRJ-200
     137AIRCRAFT_FAMILY_CRJ2 = 5
     138
     139## Aircraft type family: Fokker F-70
     140AIRCRAFT_FAMILY_F70 = 6
     141
     142## Aircraft type family: Lisunov Li-2
     143AIRCRAFT_FAMILY_DC3 = 7
     144
     145## Aircraft type family: Tupolev Tu-134
     146AIRCRAFT_FAMILY_T134 = 8
     147
     148## Aircraft type family: Tupolev Tu-154
     149AIRCRAFT_FAMILY_T154 = 9
     150
     151## Aircraft type family: Yakovlev Yak-40
     152AIRCRAFT_FAMILY_YK40 = 10
     153
     154## Aircraft type family: British Aerospace BAe-146
     155AIRCRAFT_FAMILY_B462 = 11
     156
     157#-------------------------------------------------------------------------------
     158
     159## Map aircraft families to the list of the types they comprise of
     160aircraftFamily2Types = {
     161    AIRCRAFT_FAMILY_B737NG: [AIRCRAFT_B736, AIRCRAFT_B737, AIRCRAFT_B738,
     162                             AIRCRAFT_B738C],
     163
     164    AIRCRAFT_FAMILY_B737CL: [AIRCRAFT_B732, AIRCRAFT_B733, AIRCRAFT_B734,
     165                             AIRCRAFT_B735],
     166
     167    AIRCRAFT_FAMILY_DH8D: [AIRCRAFT_DH8D],
     168
     169    AIRCRAFT_FAMILY_B767: [AIRCRAFT_B762, AIRCRAFT_B763],
     170
     171    AIRCRAFT_FAMILY_CRJ2: [AIRCRAFT_CRJ2],
     172
     173    AIRCRAFT_FAMILY_F70: [AIRCRAFT_F70],
     174
     175    AIRCRAFT_FAMILY_DC3: [AIRCRAFT_DC3],
     176
     177    AIRCRAFT_FAMILY_T134: [AIRCRAFT_T134],
     178
     179    AIRCRAFT_FAMILY_T154: [AIRCRAFT_T154],
     180
     181    AIRCRAFT_FAMILY_YK40: [AIRCRAFT_YK40],
     182
     183    AIRCRAFT_FAMILY_B462: [AIRCRAFT_B462]
     184
     185    }
     186
     187#-------------------------------------------------------------------------------
     188
     189def aircraftType2Family(aircraftType):
     190    """Get the family for the given aircraft type."""
     191    for (family, types) in aircraftFamily2Types.iteritems():
     192        if aircraftType in types:
     193            return family
     194    assert False
    121195
    122196#-------------------------------------------------------------------------------
  • src/mlx/gui/common.py

    r852 r858  
    106106
    107107    SELECTION_MULTIPLE = gtk.SELECTION_MULTIPLE
     108
     109    WINDOW_POPUP = gtk.WINDOW_POPUP
    108110
    109111    pixbuf_new_from_file = gdk.pixbuf_new_from_file
     
    549551#------------------------------------------------------------------------------
    550552
     553aircraftFamilyNames = {
     554    _const.AIRCRAFT_FAMILY_B737NG: xstr("aircraft_family_b737ng"),
     555
     556    _const.AIRCRAFT_FAMILY_B737CL: xstr("aircraft_family_b737cl"),
     557
     558    _const.AIRCRAFT_FAMILY_DH8D: xstr("aircraft_family_dh8d"),
     559
     560    _const.AIRCRAFT_FAMILY_B767: xstr("aircraft_family_b767"),
     561
     562    _const.AIRCRAFT_FAMILY_CRJ2: xstr("aircraft_family_crj2"),
     563
     564    _const.AIRCRAFT_FAMILY_F70: xstr("aircraft_family_f70"),
     565
     566    _const.AIRCRAFT_FAMILY_DC3: xstr("aircraft_family_dc3"),
     567
     568    _const.AIRCRAFT_FAMILY_T134: xstr("aircraft_family_t134"),
     569
     570    _const.AIRCRAFT_FAMILY_T154: xstr("aircraft_family_t154"),
     571
     572    _const.AIRCRAFT_FAMILY_YK40: xstr("aircraft_family_yk40"),
     573
     574    _const.AIRCRAFT_FAMILY_B462: xstr("aircraft_family_b462")
     575}
     576
     577#------------------------------------------------------------------------------
     578
    551579def formatFlightLogLine(timeStr, line):
    552580    """Format the given flight log line."""
  • src/mlx/gui/flightlist.py

    r857 r858  
    4242
    4343        @param index is the 0-based index of the column."""
    44         column = gtk.TreeViewColumn(self._heading, self._renderer,
    45                                     text = index)
     44        if isinstance(self._renderer, gtk.CellRendererText):
     45            column = gtk.TreeViewColumn(self._heading, self._renderer,
     46                                        text = index)
     47        elif isinstance(self._renderer, gtk.CellRendererToggle):
     48            column = gtk.TreeViewColumn(self._heading, self._renderer,
     49                                        active = index)
     50        else:
     51            column = gtk.TreeViewColumn(self._heading, self._renderer)
    4652        column.set_expand(True)
    4753        if self._sortable:
  • src/mlx/gui/gui.py

    r855 r858  
    1717from mlx.gui.bugreport import BugReportDialog
    1818from mlx.gui.acars import ACARS
     19from mlx.gui.timetable import TimetableWindow
    1920import cef
    2021
     
    120121
    121122        self._preferences = Preferences(self)
     123        self._timetableWindow = TimetableWindow(self)
     124        self._timetableWindow.connect("delete-event", self._hideTimetableWindow)
    122125        self._flightsWindow = AcceptedFlightsWindow(self)
    123126        self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
     
    10631066        toolsMenuItem.set_submenu(toolsMenu)
    10641067        menuBar.append(toolsMenuItem)
     1068
     1069        self._timetableMenuItem = timetableMenuItem = \
     1070          gtk.ImageMenuItem(gtk.STOCK_INDENT)
     1071        timetableMenuItem.set_use_stock(True)
     1072        timetableMenuItem.set_label(xstr("menu_tools_timetable"))
     1073        timetableMenuItem.add_accelerator("activate", accelGroup,
     1074                                          ord(xstr("menu_tools_timetable_key")),
     1075                                          CONTROL_MASK, ACCEL_VISIBLE)
     1076        timetableMenuItem.connect("activate", self.showTimetable)
     1077        self._timetableMenuItem.set_sensitive(False)
     1078        toolsMenu.append(timetableMenuItem)
    10651079
    10661080        self._flightsMenuItem = flightsMenuItem = \
     
    12401254        """Called when the login is successful."""
    12411255        self._flightsMenuItem.set_sensitive(True)
     1256        self._timetableMenuItem.set_sensitive(True)
    12421257
    12431258    def isWizardActive(self):
    12441259        """Determine if the flight wizard is active."""
    12451260        return self._notebook.get_current_page()==0
     1261
     1262    def showTimetable(self, menuItem):
     1263        """Callback for showing the timetable."""
     1264        if self._timetableWindow.hasFlightPairs:
     1265            self._timetableWindow.show_all()
     1266        else:
     1267            date = datetime.date.today()
     1268            self._timetableWindow.setTypes(self.loginResult.types)
     1269            self._timetableWindow.setDate(date)
     1270            self.updateTimeTable(date)
     1271            self.beginBusy(xstr("timetable_query_busy"))
     1272
     1273    def updateTimeTable(self, date):
     1274        """Update the time table for the given date."""
     1275        self.beginBusy(xstr("timetable_query_busy"))
     1276        self._timetableWindow.set_sensitive(False)
     1277        window = self._timetableWindow.get_window()
     1278        if window is not None:
     1279            window.set_cursor(self._busyCursor)
     1280        self.webHandler.getTimetable(self._timetableCallback, date,
     1281                                     self.loginResult.types)
     1282
     1283    def _timetableCallback(self, returned, result):
     1284        """Called when the timetable has been received."""
     1285        gobject.idle_add(self._handleTimetable, returned, result)
     1286
     1287    def _handleTimetable(self, returned, result):
     1288        """Handle the result of the query for the timetable."""
     1289        self.endBusy()
     1290        window = self._timetableWindow.get_window()
     1291        if window is not None:
     1292            window.set_cursor(None)
     1293        self._timetableWindow.set_sensitive(True)
     1294        if returned:
     1295            self._timetableWindow.setFlightPairs(result.flightPairs)
     1296            self._timetableWindow.show_all()
     1297        else:
     1298            dialog = gtk.MessageDialog(parent = self.mainWindow,
     1299                                       type = MESSAGETYPE_ERROR,
     1300                                       message_format = xstr("timetable_failed"))
     1301            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
     1302            dialog.set_title(WINDOW_TITLE_BASE)
     1303            dialog.run()
     1304            dialog.hide()
     1305            self._timetableWindow.clear()
    12461306
    12471307    def showFlights(self, menuItem):
     
    12731333            dialog.run()
    12741334            dialog.hide()
     1335
     1336    def _hideTimetableWindow(self, window, event):
     1337        """Hide the window of the timetable."""
     1338        self._timetableWindow.hide()
     1339        return True
    12751340
    12761341    def _hideFlightsWindow(self, window, event):
  • src/mlx/rpc.py

    r854 r858  
    4747class Reply(RPCObject):
    4848    """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        "type": int,
     69        "spec": int
     70        }
     71
     72    @staticmethod
     73    def _decodeTime(value):
     74        """Decode the given value as a time value."""
     75        return datetime.datetime.strptime(value, "%H:%M:%S").time()
     76
     77    @staticmethod
     78    def _decodeDuration(value):
     79        """Decode the given value as a duration.
     80
     81        A number of seconds will be returned."""
     82        t = datetime.datetime.strptime(value, "%H:%M:%S")
     83        return (t.hour*60 + t.minute) * 60 + t.second
     84
     85    def __init__(self, value):
     86        """Construct the scheduled flight object from the given JSON value."""
     87        super(ScheduledFlight, self).__init__(value,
     88                                              ScheduledFlight._instructions)
     89        self.aircraftType = self.typeCode
     90        del self.typeCode
     91
     92    def __repr__(self):
     93        return "ScheduledFlight<%d, %d, %s, %s (%s) - %s (%s) -> %d, %d>" % \
     94          (self.id, self.pairID, BookedFlight.TYPE2TYPECODE[self.aircraftType],
     95           self.departureICAO, str(self.departureTime),
     96           self.arrivalICAO, str(self.arrivalTime),
     97           self.duration, self.spec)
     98
     99#---------------------------------------------------------------------------------------
     100
     101class ScheduledFlightPair(object):
     102    """A pair of scheduled flights.
     103
     104    Occasionally, one of the flights may be missing."""
     105    @staticmethod
     106    def scheduledFlights2Pairs(scheduledFlights):
     107        """Convert the given list of scheduled flights into a list of flight
     108        pairs."""
     109        flights = {}
     110        for flight in scheduledFlights:
     111            flights[flight.id] = flight
     112
     113        flightPairs = []
     114
     115        while flights:
     116            (id, flight) = flights.popitem()
     117            pairID = flight.pairID
     118            if pairID in flights:
     119                pairFlight = flights[pairID]
     120                if flight.departureICAO=="LHBP" or \
     121                  (pairFlight.departureICAO!="LHBP" and id<pairID):
     122                    flightPairs.append(ScheduledFlightPair(flight, pairFlight))
     123                else:
     124                    flightPairs.append(ScheduledFlightPair(pairFlight, flight))
     125                del flights[pairID]
     126            else:
     127                flightPairs.append(ScheduledFlightPair(flight))
     128
     129        return flightPairs
     130
     131    def __init__(self, flight0, flight1 = None):
     132        """Construct the pair with the given flights."""
     133        self.flight0 = flight0
     134        self.flight1 = flight1
    49135
    50136#---------------------------------------------------------------------------------------
     
    73159
    74160    # FIXME: copied from web.BookedFlight
     161    TYPE2TYPECODE = { const.AIRCRAFT_B736  : "736",
     162                      const.AIRCRAFT_B737  : "73G",
     163                      const.AIRCRAFT_B738  : "738",
     164                      const.AIRCRAFT_B738C : "73H",
     165                      const.AIRCRAFT_B732  : "732",
     166                      const.AIRCRAFT_B733  : "733",
     167                      const.AIRCRAFT_B734  : "734",
     168                      const.AIRCRAFT_B735  : "735",
     169                      const.AIRCRAFT_DH8D  : "DH4",
     170                      const.AIRCRAFT_B762  : "762",
     171                      const.AIRCRAFT_B763  : "763",
     172                      const.AIRCRAFT_CRJ2  : "CR2",
     173                      const.AIRCRAFT_F70   : "F70",
     174                      const.AIRCRAFT_DC3   : "LI2",
     175                      const.AIRCRAFT_T134  : "TU3",
     176                      const.AIRCRAFT_T154  : "TU5",
     177                      const.AIRCRAFT_YK40  : "YK4",
     178                      const.AIRCRAFT_B462  : "146" }
     179
     180    # FIXME: copied from web.BookedFlight
    75181    @staticmethod
    76182    def _decodeAircraftType(typeCode):
     
    311417            self._loginCount += 1
    312418            self._sessionID = reply.value["sessionID"]
    313             return (reply.value["name"], reply.value["rank"])
     419
     420            types = [BookedFlight.TYPECODE2TYPE[typeCode]
     421                     for typeCode in reply.value["typeCodes"]]
     422
     423            return (reply.value["name"], reply.value["rank"], types)
    314424        else:
    315425            return None
     
    404514        self._performCall(lambda sessionID:
    405515                          self._server.deleteFlights(sessionID, flightIDs))
     516
     517    def getTimetable(self, date, types = None):
     518        """Get the time table for the given date restricted to the given list
     519        of type codes, if any."""
     520        typeCodes = None if types is None else \
     521            [BookedFlight.TYPE2TYPECODE[type] for type in types]
     522
     523        values = self._performCall(lambda sessionID:
     524                                   self._server.getTimetable(sessionID,
     525                                                             date.strftime("%Y-%m-%d"),
     526                                                             date.weekday() + 1,
     527                                                             typeCodes))
     528        return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
     529                                                          for value in values])
    406530
    407531    def _performCall(self, callFn, acceptResults = []):
  • src/mlx/web.py

    r854 r858  
    813813            result.pilotName = loginResult[0]
    814814            result.rank = loginResult[1]
     815            result.types = loginResult[2]
    815816            result.password = password
    816817            flights = client.getFlights()
     
    825826                if reply[3]:
    826827                    result.rank = "FO"
     828
    827829
    828830    def __init__(self, client, callback, pilotID, password):
     
    13421344#------------------------------------------------------------------------------
    13431345
     1346class GetTimetable(RPCRequest):
     1347    """Request to get the timetable."""
     1348    def __init__(self, client, callback, date, types):
     1349        """Construct the request with the given client and callback function."""
     1350        super(GetTimetable, self).__init__(client, callback)
     1351        self._date = date
     1352        self._types = types
     1353
     1354    def run(self):
     1355        """Perform the login request."""
     1356        result = Result()
     1357
     1358        result.flightPairs = self._client.getTimetable(self._date, self._types)
     1359
     1360        return result
     1361
     1362#------------------------------------------------------------------------------
     1363
    13441364class Handler(threading.Thread):
    13451365    """The handler for the web services.
     
    14391459        self._addRequest(GetAcceptedFlights(self._rpcClient, callback))
    14401460
     1461    def getTimetable(self, callback, date, types):
     1462        """Enqueue a request to get the timetable."""
     1463        self._addRequest(GetTimetable(self._rpcClient, callback, date, types))
     1464
    14411465    def run(self):
    14421466        """Process the requests."""
Note: See TracChangeset for help on using the changeset viewer.