Changeset 854:3e7dca86c1ed


Ignore:
Timestamp:
05/21/17 15:21:55 (7 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
hg-Phase:
(<MercurialRepository 1 'hg:/home/ivaradi/mlx/hg' '/'>, 'public')
Message:

The accepted flights can be queried and viewed (re #307)

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • locale/en/mlx.po

    r853 r854  
    126126msgid "menu_tools"
    127127msgstr "Tools"
     128
     129msgid "menu_tools_flights"
     130msgstr "Accepted _flights"
     131
     132msgid "menu_tools_flights_key"
     133msgstr "f"
    128134
    129135msgid "menu_tools_chklst"
     
    25342540msgstr "Are you sure to delete the selected flight(s)?"
    25352541
     2542msgid "acceptedflt_title"
     2543msgstr "Accepted flights"
     2544
     2545msgid "acceptedflt_refresh"
     2546msgstr "_Refresh"
     2547
     2548msgid "acceptedflt_view"
     2549msgstr "_View..."
     2550
     2551msgid "acceptedflt_failed"
     2552msgstr "Failed to query the accepted flights."
     2553
     2554msgid "acceptedflt_flight_duration"
     2555msgstr "Dur."
     2556
     2557msgid "acceptedflt_num_pax"
     2558msgstr "PAX"
     2559
     2560msgid "acceptedflt_fuel"
     2561msgstr "Fuel"
     2562
     2563msgid "acceptedflt_rating"
     2564msgstr "Rating"
     2565
    25362566msgid "error_communication"
    25372567msgstr "Communication error"
  • locale/hu/mlx.po

    r853 r854  
    126126msgid "menu_tools"
    127127msgstr "Eszközök"
     128
     129msgid "menu_tools_flights"
     130msgstr "Elfogadott _járatok"
     131
     132msgid "menu_tools_flights_key"
     133msgstr "j"
    128134
    129135msgid "menu_tools_chklst"
     
    25512557msgstr "Biztosan törölni szerednéd a kiválasztott járato(ka)t?"
    25522558
     2559msgid "acceptedflt_title"
     2560msgstr "Elfogadott járatok"
     2561
     2562msgid "acceptedflt_refresh"
     2563msgstr "_Frissítés"
     2564
     2565msgid "acceptedflt_view"
     2566msgstr "_Megtekintés..."
     2567
     2568msgid "acceptedflt_failed"
     2569msgstr "Nem sikerült letölteni az elfogadott járatokat."
     2570
     2571msgid "acceptedflt_flight_duration"
     2572msgstr "Idő"
     2573
     2574msgid "acceptedflt_num_pax"
     2575msgstr "Utasok"
     2576
     2577msgid "acceptedflt_fuel"
     2578msgstr "Üza."
     2579
     2580msgid "acceptedflt_rating"
     2581msgstr "Pontszám"
     2582
    25532583msgid "error_communication"
    25542584msgstr "Kommunikációs hiba"
  • src/mlx/gui/flight.py

    r836 r854  
    55565556            if result.loggedIn:
    55575557                self._loginResult = result
     5558                self.gui.loginSuccessful()
    55585559            else:
    55595560                if isReload:
  • src/mlx/gui/flightlist.py

    r830 r854  
    1212    """A descriptor for a column in the list."""
    1313    def __init__(self, attribute, heading, type = str,
    14                  convertFn = None, renderer = gtk.CellRendererText(),
     14                 convertFn = None, renderer = None,
    1515                 extraColumnAttributes = None, sortable = False,
    16                  defaultSortable = False):
     16                 defaultSortable = False, defaultDescending = False,
     17                 cellDataFn = None):
    1718        """Construct the descriptor."""
    1819        self._attribute = attribute
     
    2021        self._type = type
    2122        self._convertFn = convertFn
    22         self._renderer = renderer
     23        self._renderer = \
     24          gtk.CellRendererText() if renderer is None else renderer
    2325        self._extraColumnAttributes = extraColumnAttributes
    2426        self._sortable = sortable
    2527        self._defaultSortable = defaultSortable
     28        self._defaultDescending = defaultDescending
     29        self._cellDataFn = cellDataFn
    2630
    2731    @property
     
    3842
    3943        @param index is the 0-based index of the column."""
    40         if self._extraColumnAttributes is None:
    41             if isinstance(self._renderer, gtk.CellRendererText):
    42                 extraColumnAttributes = {"text" : index}
    43             else:
    44                 extraColumnAttributes = {}
    45         else:
    46             extraColumnAttributes = self._extraColumnAttributes
    47 
    4844        column = gtk.TreeViewColumn(self._heading, self._renderer,
    4945                                    text = index)
     
    5349            column.set_sort_indicator(True)
    5450
     51        if self._extraColumnAttributes is not None:
     52            for (key, value) in self._extraColumnAttributes.iteritems():
     53                if key=="alignment":
     54                    self._renderer.set_alignment(value, 0.5)
     55                else:
     56                    raise Exception("unhandled extra column attribute '" +
     57                                    key + "'")
     58        if self._cellDataFn is not None:
     59            column.set_cell_data_func(self._renderer, self._cellDataFn)
     60
    5561        return column
    5662
    5763    def getValueFrom(self, flight):
    5864        """Get the value from the given flight."""
    59         value = getattr(flight, self._attribute)
     65        attributes = self._attribute.split(".")
     66        value = getattr(flight, attributes[0])
     67        for attr in attributes[1:]:
     68            value = getattr(value, attr)
    6069        return self._type(value) if self._convertFn is None \
    6170            else self._convertFn(value, flight)
     
    96105        self._model = gtk.ListStore(*types)
    97106        if defaultSortableIndex is not None:
    98             self._model.set_sort_column_id(defaultSortableIndex,
    99                                            SORT_ASCENDING)
     107            sortOrder = SORT_DESCENDING \
     108              if self._columnDescriptors[defaultSortableIndex-1]._defaultDescending \
     109              else SORT_ASCENDING
     110            self._model.set_sort_column_id(defaultSortableIndex, sortOrder)
    100111        self._view = gtk.TreeView(self._model)
    101112
     
    223234
    224235    It contains the list and the buttons available."""
     236    @staticmethod
    225237    def getAircraft(tailNumber, bookedFlight):
    226238        """Get the aircraft from the given booked flight.
     
    230242        return tailNumber + \
    231243            " (" + const.icaoCodes[bookedFlight.aircraftType] + ")"
     244
     245    def _getAcft(tailNumber, bookedFlight):
     246        return PendingFlightsFrame.getAircraft(tailNumber, bookedFlight)
    232247
    233248    columnDescriptors = [
     
    240255                         sortable = True),
    241256        ColumnDescriptor("tailNumber", xstr("pendflt_acft"),
    242                          convertFn = getAircraft)
     257                         convertFn = _getAcft)
    243258    ]
    244259
     
    501516
    502517#-----------------------------------------------------------------------------
     518
     519class AcceptedFlightsWindow(gtk.Window):
     520    """A window for a list of accepted flights."""
     521    def getFlightDuration(flightTimeStart, flight):
     522        """Get the flight duration for the given flight."""
     523        minutes = int(round((flight.flightTimeEnd - flightTimeStart)/60.0))
     524        return "%02d:%02d" % (minutes/60, minutes%60)
     525
     526    columnDescriptors = [
     527        ColumnDescriptor("bookedFlight.callsign", xstr("flightsel_no")),
     528        ColumnDescriptor("bookedFlight.departureTime", xstr("flightsel_deptime"),
     529                         sortable = True, defaultSortable = True,
     530                         defaultDescending = True),
     531        ColumnDescriptor("bookedFlight.departureICAO", xstr("flightsel_from"),
     532                         sortable = True),
     533        ColumnDescriptor("bookedFlight.arrivalICAO", xstr("flightsel_to"),
     534                         sortable = True),
     535        ColumnDescriptor("bookedFlight.tailNumber", xstr("pendflt_acft"),
     536                         convertFn = lambda value, flight:
     537                         PendingFlightsFrame.getAircraft(value,
     538                                                         flight.bookedFlight)),
     539        ColumnDescriptor("flightTimeStart", xstr("acceptedflt_flight_duration"),
     540                         convertFn = getFlightDuration, sortable = True,
     541                         extraColumnAttributes =
     542                             { "alignment": 0.5 } ),
     543        ColumnDescriptor("numPassengers", xstr("acceptedflt_num_pax"),
     544                         type = int, sortable = True,
     545                         extraColumnAttributes =
     546                             { "alignment": 1.0 } ),
     547        ColumnDescriptor("fuelUsed", xstr("acceptedflt_fuel"),
     548                         type = int, sortable = True,
     549                         extraColumnAttributes =
     550                             { "alignment": 1.0 } ),
     551        ColumnDescriptor("rating", xstr("acceptedflt_rating"),
     552                         type = float, sortable = True,
     553                         extraColumnAttributes =
     554                             { "alignment": 1.0 },
     555                         cellDataFn = lambda col, cell, model, iter:
     556                             cell.set_property("text",
     557                                               "%.0f" %
     558                                               (model.get(iter, 9)[0],)))
     559    ]
     560
     561    def __init__(self, gui):
     562        """Construct the window."""
     563        super(AcceptedFlightsWindow, self).__init__()
     564
     565        self._gui = gui
     566
     567        self.set_title(WINDOW_TITLE_BASE + " - " + xstr("acceptedflt_title"))
     568        self.set_size_request(-1, 700)
     569        self.set_transient_for(gui.mainWindow)
     570
     571        alignment = gtk.Alignment(xscale = 1.0, yscale = 1.0)
     572        alignment.set_padding(padding_top = 2, padding_bottom = 8,
     573                              padding_left = 4, padding_right = 4)
     574
     575        vbox = gtk.VBox()
     576
     577        hbox = gtk.HBox()
     578        vbox.pack_start(hbox, True, True, 4)
     579
     580        self._flights = []
     581        self._flightList = FlightList(columnDescriptors =
     582                                      AcceptedFlightsWindow.columnDescriptors,
     583                                      widthRequest = 750,
     584                                      multiSelection = False)
     585        self._flightList.connect("selection-changed", self._selectionChanged)
     586
     587        hbox.pack_start(self._flightList, True, True, 4)
     588
     589        buttonBox = gtk.VBox()
     590
     591        self._refreshButton = gtk.Button(xstr("acceptedflt_refresh"))
     592        self._refreshButton.set_sensitive(True)
     593        self._refreshButton.connect("clicked", self._refreshClicked)
     594        buttonBox.pack_start(self._refreshButton, False, False, 2)
     595
     596        filler = gtk.Alignment(xalign = 0.0, yalign = 0.0,
     597                               xscale = 1.0, yscale = 1.0)
     598        filler.set_size_request(-1, 4)
     599        buttonBox.pack_start(filler, False, False, 0)
     600
     601        self._viewButton = gtk.Button(xstr("acceptedflt_view"))
     602        self._viewButton.set_sensitive(False)
     603        self._viewButton.connect("clicked", self._viewClicked)
     604        buttonBox.pack_start(self._viewButton, False, False, 2)
     605
     606        hbox.pack_start(buttonBox, False, False, 4)
     607
     608        buttonAlignment = gtk.Alignment(xscale = 0.0, yscale = 0.0,
     609                                        xalign = 0.5, yalign = 0.5)
     610
     611        self._closeButton =  gtk.Button(xstr("button_ok"))
     612        self._closeButton.connect("clicked", self._closeClicked)
     613
     614        buttonAlignment.add(self._closeButton)
     615        vbox.pack_start(buttonAlignment, False, False, 2)
     616
     617        alignment.add(vbox)
     618
     619        self.add(alignment)
     620
     621        self.connect("key-press-event", self._keyPressed)
     622
     623    @property
     624    def hasFlights(self):
     625        """Determine if there are any flights that we know of."""
     626        return len(self._flights)>0
     627
     628    def clear(self):
     629        """Clear the flight list."""
     630        self._flights = []
     631        self._flightList.clear()
     632
     633    def addFlight(self, flight):
     634        """Add the given flight."""
     635        self._flights.append(flight)
     636        self._flightList.addFlight(flight)
     637
     638    def _selectionChanged(self, flightList, selectedIndexes):
     639        """Called when the selection has changed."""
     640        self._viewButton.set_sensitive(len(selectedIndexes)==1)
     641
     642    def _refreshClicked(self, button):
     643        """Called when the refresh button has been clicked."""
     644        self.clear()
     645        self._gui.showFlights(None)
     646
     647    def _viewClicked(self, button):
     648        """Called when the view button has been clicked."""
     649        gui = self._gui
     650        gui.beginBusy(xstr("pendflt_pirep_busy"))
     651        self.set_sensitive(False)
     652
     653        indexes = self._flightList.selectedIndexes
     654        assert(len(indexes)==1)
     655
     656        flightID = self._flights[indexes[0]].bookedFlight.id
     657        gui.webHandler.getPIREP(self._pirepResultCallback, flightID)
     658
     659    def _pirepResultCallback(self, returned, result):
     660        """Called when the PIREP query result is available."""
     661        gobject.idle_add(self._handlePIREPResult, returned, result)
     662
     663    def _handlePIREPResult(self, returned, result):
     664        """Handle the refly result."""
     665        self.set_sensitive(True)
     666        gui = self._gui
     667        gui.endBusy()
     668
     669        if returned:
     670            gui.viewPIREP(result.pirep)
     671
     672    def _closeClicked(self, button):
     673        """Called when the Close button is clicked.
     674
     675        A 'delete-event' is emitted to close the window."""
     676        self.emit("delete-event", None)
     677
     678    def _keyPressed(self, window, event):
     679        """Called when a key is pressed in the window.
     680
     681        If the Escape key is pressed, 'delete-event' is emitted to close the
     682        window."""
     683        if gdk.keyval_name(event.keyval) == "Escape":
     684            self.emit("delete-event", None)
     685            return True
     686
     687#-----------------------------------------------------------------------------
  • src/mlx/gui/gui.py

    r853 r854  
    1313from mlx.gui.checklist import ChecklistEditor
    1414from mlx.gui.callouts import ApproachCalloutsEditor
     15from mlx.gui.flightlist import AcceptedFlightsWindow
    1516from mlx.gui.pirep import PIREPViewer, PIREPEditor
    1617from mlx.gui.bugreport import BugReportDialog
     
    119120
    120121        self._preferences = Preferences(self)
     122        self._flightsWindow = AcceptedFlightsWindow(self)
     123        self._flightsWindow.connect("delete-event", self._hideFlightsWindow)
    121124        self._checklistEditor = ChecklistEditor(self)
    122125        self._approachCalloutsEditor = ApproachCalloutsEditor(self)
     
    10591062        toolsMenuItem.set_submenu(toolsMenu)
    10601063        menuBar.append(toolsMenuItem)
     1064
     1065        self._flightsMenuItem = flightsMenuItem = \
     1066          gtk.ImageMenuItem(gtk.STOCK_SPELL_CHECK)
     1067        flightsMenuItem.set_use_stock(True)
     1068        flightsMenuItem.set_label(xstr("menu_tools_flights"))
     1069        flightsMenuItem.add_accelerator("activate", accelGroup,
     1070                                        ord(xstr("menu_tools_flights_key")),
     1071                                        CONTROL_MASK, ACCEL_VISIBLE)
     1072        flightsMenuItem.connect("activate", self.showFlights)
     1073        self._flightsMenuItem.set_sensitive(False)
     1074        toolsMenu.append(flightsMenuItem)
    10611075
    10621076        checklistMenuItem = gtk.ImageMenuItem(gtk.STOCK_APPLY)
     
    12221236            self._mainWindow.set_default(None)
    12231237
     1238    def loginSuccessful(self):
     1239        """Called when the login is successful."""
     1240        self._flightsMenuItem.set_sensitive(True)
     1241
    12241242    def isWizardActive(self):
    12251243        """Determine if the flight wizard is active."""
    12261244        return self._notebook.get_current_page()==0
     1245
     1246    def showFlights(self, menuItem):
     1247        """Callback for showing the flight list."""
     1248        if self._flightsWindow.hasFlights:
     1249            self._flightsWindow.show_all()
     1250        else:
     1251            self.beginBusy(xstr("acceptedflt_query_busy"))
     1252            self.webHandler.getAcceptedFlights(self._acceptedFlightsCallback)
     1253
     1254    def _acceptedFlightsCallback(self, returned, result):
     1255        """Called when the accepted flights have been received."""
     1256        gobject.idle_add(self._handleAcceptedFlights, returned, result)
     1257
     1258    def _handleAcceptedFlights(self, returned, result):
     1259        """Handle the result of the query for accepted flights."""
     1260        self.endBusy()
     1261        if returned:
     1262            self._flightsWindow.clear()
     1263            for flight in result.flights:
     1264                self._flightsWindow.addFlight(flight)
     1265            self._flightsWindow.show_all()
     1266        else:
     1267            dialog = gtk.MessageDialog(parent = self.mainWindow,
     1268                                       type = MESSAGETYPE_ERROR,
     1269                                       message_format = xstr("acceptedflt_failed"))
     1270            dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
     1271            dialog.set_title(WINDOW_TITLE_BASE)
     1272            dialog.run()
     1273            dialog.hide()
     1274
     1275    def _hideFlightsWindow(self, window, event):
     1276        """Hide the window of the accepted flights."""
     1277        self._flightsWindow.hide()
     1278        return True
    12271279
    12281280    def _editChecklist(self, menuItem):
  • src/mlx/pirep.py

    r839 r854  
    170170
    171171        self.filedCruiseAltitude = int(pirepData["filedCruiseLevel"][2:])*100
    172         cruiseLevel = pirepData["cruiseLevel"]
     172        cruiseLevel = pirepData["cruiseLevel"].strip()
     173        if cruiseLevel:
     174            if cruiseLevel.startswith("FL"):
     175                cruiseLevel = cruiseLevel[2:]
    173176        if cruiseLevel:
    174177            self.cruiseAltitude = int(cruiseLevel[2:])*100
  • src/mlx/rpc.py

    r853 r854  
    77import hashlib
    88import datetime
     9import calendar
     10import sys
    911
    1012#---------------------------------------------------------------------------------------
     
    3234                    continue
    3335
    34                 value = instruction(value)
     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()
    3543            setattr(self, key, value)
    3644
     
    131139#---------------------------------------------------------------------------------------
    132140
     141class AcceptedFlight(RPCObject):
     142    """A flight that has been already accepted."""
     143    # The instructions for the construction
     144    @staticmethod
     145    def parseTimestamp(s):
     146        """Parse the given RPC timestamp."""
     147        dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
     148        return calendar.timegm(dt.utctimetuple())
     149
     150    _instructions = {
     151        "bookedFlight" : lambda value: BookedFlight(value),
     152        "numPassengers" : int,
     153        "fuelUsed" : int,
     154        "rating" : lambda value: float(value) if value else 0.0
     155        }
     156
     157    def __init__(self, value):
     158        """Construct the booked flight object from the given RPC result
     159        value."""
     160        super(AcceptedFlight, self).__init__(value, AcceptedFlight._instructions)
     161        self.flightTimeStart = \
     162          AcceptedFlight.parseTimestamp(self.flightDate + " " +
     163                                        self.flightTimeStart)
     164        self.flightTimeEnd = \
     165          AcceptedFlight.parseTimestamp(self.flightDate + " " +
     166                                        self.flightTimeEnd)
     167        if self.flightTimeEnd<self.flightTimeStart:
     168            self.flightTimeEnd += 24*60*60
     169
     170#---------------------------------------------------------------------------------------
     171
    133172class Plane(rpccommon.Plane, RPCObject):
    134173    """An airplane in the fleet."""
     
    299338        return (bookedFlights, reportedFlights, rejectedFlights)
    300339
     340    def getAcceptedFlights(self):
     341        """Get the flights that are already accepted."""
     342        value = self._performCall(lambda sessionID:
     343                                  self._server.getAcceptedFlights(sessionID))
     344        flights = []
     345        for flight in value:
     346            flights.append(AcceptedFlight(flight))
     347        return flights
     348
    301349    def getEntryExamStatus(self):
    302350        """Get the status of the exams needed for joining MAVA."""
  • src/mlx/web.py

    r853 r854  
    13261326#------------------------------------------------------------------------------
    13271327
     1328class GetAcceptedFlights(RPCRequest):
     1329    """Request to get the accepted flights."""
     1330    def __init__(self, client, callback):
     1331        """Construct the request with the given client and callback function."""
     1332        super(GetAcceptedFlights, self).__init__(client, callback)
     1333
     1334    def run(self):
     1335        """Perform the login request."""
     1336        result = Result()
     1337
     1338        result.flights = self._client.getAcceptedFlights()
     1339
     1340        return result
     1341
     1342#------------------------------------------------------------------------------
     1343
    13281344class Handler(threading.Thread):
    13291345    """The handler for the web services.
     
    14191435        self._addRequest(DeleteFlights(self._rpcClient, callback, flightIDs))
    14201436
     1437    def getAcceptedFlights(self, callback):
     1438        """Enqueue a request to get the accepted flights."""
     1439        self._addRequest(GetAcceptedFlights(self._rpcClient, callback))
     1440
    14211441    def run(self):
    14221442        """Process the requests."""
Note: See TracChangeset for help on using the changeset viewer.