Changeset 859:1e789c934953


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

Flight booking works (re #304).

Files:
9 edited

Legend:

Unmodified
Added
Removed
  • locale/en/mlx.po

    r858 r859  
    138138msgid "button_save"
    139139msgstr "_Save"
     140
     141msgid "button_book"
     142msgstr "_Book"
     143
     144msgid "button_close"
     145msgstr "_Close"
    140146
    141147msgid "menu_file"
     
    26692675msgstr "Filtering criteria"
    26702676
     2677msgid "timetable_popup_book"
     2678msgstr "_Book"
     2679
     2680msgid "timetable_book_title"
     2681msgstr "Book flight"
     2682
     2683msgid "timetable_book_frame_title"
     2684msgstr "Flight details"
     2685
     2686msgid "timetable_book_callsign"
     2687msgstr "<b>Flight number</b>:"
     2688
     2689msgid "timetable_book_from_to"
     2690msgstr "<b>From - to</b>:"
     2691
     2692msgid "timetable_book_flightDate"
     2693msgstr "<b>Flight date</b>:"
     2694
     2695msgid "timetable_book_flightDate_tooltip"
     2696msgstr "You can select a different date for your flight."
     2697
     2698msgid "timetable_book_dep_arr"
     2699msgstr "<b>Departure - arrival</b>:"
     2700
     2701msgid "timetable_book_duration"
     2702msgstr "<b>Duration</b>:"
     2703
     2704msgid "timetable_book_tailNumber"
     2705msgstr "<b>Tail number</b>:"
     2706
     2707msgid "timetable_book_tailNumber_tooltip"
     2708msgstr ""
     2709"Select the tail number of aircraft which "
     2710"you would like to perform the flight with"
     2711
     2712msgid "bookflights_busy"
     2713msgstr "Booking flights..."
     2714
     2715msgid "bookflights_successful"
     2716msgstr "The booking is successful."
     2717
     2718msgid "bookflights_successful_secondary"
     2719msgstr "Have a nice flight!"
     2720
     2721msgid "bookflights_failed"
     2722msgstr "Failed to book flights."
     2723
     2724msgid "bookflights_failed_secondary"
     2725msgstr ""
     2726"The MAVA server may not work temporarily. "
     2727"Retry the booking some time later. "
     2728"If the problem persists, report a bug."
     2729
    26712730msgid "error_communication"
    26722731msgstr "Communication error"
  • locale/hu/mlx.po

    r858 r859  
    138138msgid "button_save"
    139139msgstr "_Mentés"
     140
     141msgid "button_book"
     142msgstr "_Foglalás"
     143
     144msgid "button_close"
     145msgstr "_Bezár"
    140146
    141147msgid "menu_file"
     
    26862692msgstr "Szűrési feltételek"
    26872693
     2694msgid "timetable_popup_book"
     2695msgstr "_Fogalás"
     2696
     2697msgid "timetable_book_title"
     2698msgstr "Járat lefoglalása"
     2699
     2700msgid "timetable_book_frame_title"
     2701msgstr "A járat részletei"
     2702
     2703msgid "timetable_book_callsign"
     2704msgstr "<b>Járatszám</b>:"
     2705
     2706msgid "timetable_book_flightDate"
     2707msgstr "<b>Dátum</b>:"
     2708
     2709msgid "timetable_book_flightDate_tooltip"
     2710msgstr "A megadottól eltérő dátumot is választhatsz."
     2711
     2712msgid "timetable_book_from_to"
     2713msgstr "<b>Honnan - hová</b>:"
     2714
     2715msgid "timetable_book_dep_arr"
     2716msgstr "<b>Érkezés - indulás</b>:"
     2717
     2718msgid "timetable_book_duration"
     2719msgstr "<b>Duration</b>:"
     2720
     2721msgid "timetable_book_tailNumber"
     2722msgstr "<b>Lajstromjel</b>:"
     2723
     2724msgid "timetable_book_tailNumber_tooltip"
     2725msgstr ""
     2726"Válaszd ki annak a gépnek a lajstromjelét, "
     2727"amelyikkel teljesíteni szeretnéd a járatot."
     2728
     2729msgid "bookflights_busy"
     2730msgstr "Járat foglalása..."
     2731
     2732msgid "bookflights_successful"
     2733msgstr "A foglalás sikerült."
     2734
     2735msgid "bookflights_successful_secondary"
     2736msgstr "Jó repülést!"
     2737
     2738msgid "bookflights_failed"
     2739msgstr "Nem sikerült lefoglalni a járatot."
     2740
     2741msgid "bookflights_failed_secondary"
     2742msgstr ""
     2743"Lehet, hogy a MAVA szerver átmenetileg nem működik. "
     2744"Próbáld meg később a foglalást. "
     2745"Ha a hiba továbbra is fennáll, jelents be."
     2746
    26882747msgid "error_communication"
    26892748msgstr "Kommunikációs hiba"
  • src/mlx/const.py

    r858 r859  
    11
    22import sys
     3import datetime
    34
    45#-------------------------------------------------------------------------------
     
    559560
    560561#-------------------------------------------------------------------------------
     562
     563defaultDate = datetime.date(1900, 1, 1)
     564
     565#-------------------------------------------------------------------------------
  • src/mlx/gui/flight.py

    r854 r859  
    528528        if self._wizard.loggedIn:
    529529            for flight in loginResult.flights:
    530                 self._addFlight(flight)
     530                self.addFlight(flight)
    531531            for flight in loginResult.reportedFlights:
    532532                self._pendingFlightsWindow.addReportedFlight(flight)
     
    536536        self._updatePendingButton()
    537537
    538     def _addFlight(self, flight):
     538    def addFlight(self, flight):
    539539        """Add the given file to the list of flights."""
    540540        self._flights.append(flight)
     
    543543    def _reflyFlight(self, flight):
    544544        """Refly the given flight."""
    545         self._addFlight(flight)
     545        self.addFlight(flight)
    546546        self._updatePending()
    547547
     
    641641                with open(fileName, "rt") as f:
    642642                    bookedFlight.readFromFile(f)
    643                 self._addFlight(bookedFlight)
     643                self.addFlight(bookedFlight)
    644644            except Exception, e:
    645645                print "Failed to load flight:", util.utf2unicode(str(e))
     
    54795479        """Reload the flights from the MAVA server."""
    54805480        self.login(callback, None, None)
     5481
     5482    def addFlight(self, bookedFlight):
     5483        """Add the given booked flight to the flight selection page."""
     5484        self._flightSelectionPage.addFlight(bookedFlight)
    54815485
    54825486    def reflyFlight(self, bookedFlight):
  • src/mlx/gui/flightlist.py

    r858 r859  
    2828        self._defaultDescending = defaultDescending
    2929        self._cellDataFn = cellDataFn
     30
     31    @property
     32    def attribute(self):
     33        """Get the attribute the column belongs to."""
     34        return self._attribute
    3035
    3136    @property
  • src/mlx/gui/gui.py

    r858 r859  
    9797        self._credentialsPassword = None
    9898
     99        self._bookFlightsUserCallback = None
     100        self._bookFlightsBusyCallback = None
     101
    99102        self.webHandler = web.Handler(config, self._getCredentialsCallback)
    100103        self.webHandler.start()
     
    557560        self._flightInfo.enable(aircraftType)
    558561
     562    def bookFlights(self, callback, flightIDs, date, tailNumber,
     563                    busyCallback = None):
     564        """Initiate the booking of flights with the given timetable IDs and
     565        other data"""
     566        self._bookFlightsUserCallback = callback
     567        self._bookFlightsBusyCallback = busyCallback
     568
     569        self.beginBusy(xstr("bookflights_busy"))
     570        if busyCallback is not None:
     571            busyCallback(True)
     572
     573        self.webHandler.bookFlights(self._bookFlightsCallback,
     574                                    flightIDs, date, tailNumber)
     575
     576    def _bookFlightsCallback(self, returned, result):
     577        """Called when the booking of flights has finished."""
     578        gobject.idle_add(self._handleBookFlightsResult, returned, result)
     579
     580    def _handleBookFlightsResult(self, returned, result):
     581        """Called when the booking of flights is done.
     582
     583        If it was successful, the booked flights are added to the list of the
     584        flight selector."""
     585        if self._bookFlightsBusyCallback is not None:
     586            self._bookFlightsBusyCallback(False)
     587        self.endBusy()
     588
     589        if returned:
     590            for bookedFlight in result.bookedFlights:
     591                self._wizard.addFlight(bookedFlight)
     592
     593        self._bookFlightsUserCallback(returned, result)
     594
    559595    def cancelFlight(self):
    560596        """Cancel the current file, if the user confirms it."""
     
    853889        gobject.idle_add(self.getFleet, callback, force)
    854890
    855     def getFleet(self, callback = None, force = False):
     891    def getFleet(self, callback = None, force = False, busyCallback = None):
    856892        """Get the fleet.
    857893
     
    860896        if self._fleet is None or force:
    861897            self._fleetCallback = callback
     898            self._fleetBusyCallback = busyCallback
     899            if busyCallback is not None:
     900                busyCallback(True)
    862901            self.beginBusy(xstr("fleet_busy"))
    863902            self.webHandler.getFleet(self._fleetResultCallback)
     
    896935        """Handle the fleet result."""
    897936        self.endBusy()
     937        if self._fleetBusyCallback is not None:
     938            self._fleetBusyCallback(False)
    898939        if returned:
    899940            self._fleet = result.fleet
     
    911952        callback = self._fleetCallback
    912953        self._fleetCallback = None
     954        self._fleetBusyCallback = None
    913955        if  callback is not None:
    914956            callback(self._fleet)
  • src/mlx/gui/timetable.py

    r858 r859  
    55from mlx.gui.common import *
    66from flightlist import ColumnDescriptor
     7from mlx.rpc import ScheduledFlight
    78
    89import mlx.const as const
    910
    1011import datetime
     12import random
    1113
    1214#-----------------------------------------------------------------------------
     
    3840                         "%02d:%02d" % (duration/3600,
    3941                                        (duration%3600)/60)),
    40         ColumnDescriptor("spec", xstr("timetable_vip"), type = bool,
     42        ColumnDescriptor("type", xstr("timetable_vip"), type = bool,
    4143                         renderer = _getVIPRenderer(),
    4244                         sortable = True,
    43                          convertFn = lambda spec, flight: spec==1)
     45                         convertFn = lambda type, flight:
     46                             type==ScheduledFlight.TYPE_VIP)
    4447    ]
     48
     49    columnOrdering = ["callsign", "aircraftType",
     50                      "date", "departureTime", "arrivalTime",
     51                      "departureICAO", "arrivalICAO", "duration", "type"]
    4552
    4653    @staticmethod
     
    4855        """Determine if the given flight is selected by the given
    4956        filtering conditions."""
    50         return ((regularEnabled and flight.spec==0) or \
    51                 (vipEnabled and flight.spec==1)) and \
     57        return ((regularEnabled and flight.type==ScheduledFlight.TYPE_NORMAL) or \
     58                (vipEnabled and flight.type==ScheduledFlight.TYPE_VIP)) and \
    5259               flight.aircraftType in aircraftTypes
    5360
     
    7582        self._view = gtk.TreeView(self._model)
    7683
     84        self._tooltips = gtk.Tooltips()
     85        self._tooltips.disable()
     86        self._view.connect("motion-notify-event", self._updateTooltip)
     87
    7788        flightPairIndexColumn = gtk.TreeViewColumn()
    7889        flightPairIndexColumn.set_visible(False)
     
    8394            column = columnDescriptor.getViewColumn(index)
    8495            self._view.append_column(column)
     96            self._model.set_sort_func(index, self._compareFlights,
     97                                      columnDescriptor.attribute)
    8598            index += 1
    8699
     
    110123
    111124    @property
     125    def selectedIndexes(self):
     126        """Get the indexes of the selected entries, if any.
     127
     128        The indexes are sorted."""
     129        selection = self._view.get_selection()
     130        (model, rows) = selection.get_selected_rows()
     131
     132        indexes = [self._getIndexForPath(path) for path in rows]
     133        indexes.sort()
     134        return indexes
     135
     136    @property
    112137    def hasFlightPairs(self):
    113138        """Determine if the timetable contains any flights."""
     
    125150
    126151        self._flightPairs = flightPairs
     152
     153    def getFlightPair(self, index):
     154        """Get the flight pair with the given index."""
     155        return self._flightPairs[index]
    127156
    128157    def updateList(self, regularEnabled, vipEnabled, types):
     
    140169            index += 1
    141170
     171    def _getIndexForPath(self, path):
     172        """Get the index for the given path."""
     173        iter = self._model.get_iter(path)
     174        return self._model.get_value(iter, 0)
     175
    142176    def _rowActivated(self, flightList, path, column):
    143177        """Called when a row is selected."""
    144         print "_rowActivated"
     178        self.emit("row-activated", self._getIndexForPath(path))
    145179
    146180    def _buttonPressEvent(self, widget, event):
    147181        """Called when a mouse button is pressed or released."""
    148         print "_buttonPressEvent", event
     182        if event.type!=EVENT_BUTTON_PRESS or event.button!=3 or \
     183           self._popupMenuProducer is None:
     184            return
     185
     186        (path, _, _, _) = self._view.get_path_at_pos(int(event.x),
     187                                                     int(event.y))
     188        selection = self._view.get_selection()
     189        selection.unselect_all()
     190        selection.select_path(path)
     191
     192        if self._popupMenu is None:
     193            self._popupMenu = self._popupMenuProducer()
     194        menu = self._popupMenu
     195        if pygobject:
     196            menu.popup(None, None, None, None, event.button, event.time)
     197        else:
     198            menu.popup(None, None, None, event.button, event.time)
    149199
    150200    def _selectionChanged(self, selection):
    151201        """Called when the selection has changed."""
    152         print "_selectionChanged"
     202        self.emit("selection-changed", self.selectedIndexes)
     203
     204    def _compareFlights(self, model, iter1, iter2, mainColumn):
     205        """Compare the flights at the given iterators according to the given
     206        main column."""
     207        index1 = self._model.get_value(iter1, 0)
     208        index2 = self._model.get_value(iter2, 0)
     209
     210        flightPair1 = self._flightPairs[index1]
     211        flightPair2 = self._flightPairs[index2]
     212
     213        result = flightPair1.compareBy(flightPair2, mainColumn)
     214        if result==0:
     215            for column in Timetable.columnOrdering:
     216                if column!=mainColumn:
     217                    result = flightPair1.compareBy(flightPair2, column)
     218                    if result!=0:
     219                        break
     220        return result
     221
     222    def _updateTooltip(self, widget, event):
     223        """Update the tooltip for the position of the given event."""
     224        try:
     225            (path, col, x, y) = widget.get_path_at_pos( int(event.x), int(event.y))
     226            index = self._getIndexForPath(path)
     227
     228            flight = self._flightPairs[index].flight0
     229            comment = flight.comment
     230            date = flight.date
     231
     232            if comment or date!=const.defaultDate:
     233                text = ""
     234                if comment:
     235                    text = comment
     236                if date!=const.defaultDate:
     237                    if text:
     238                        text += "; "
     239                    text += date.strftime("%Y-%m-%d")
     240
     241                self._tooltips.set_tip(widget, text)
     242                self._tooltips.enable()
     243            else:
     244                self._tooltips.set_tip(widget, "")
     245                self._tooltips.disable()
     246        except Exception, e:
     247            print e
     248            self._tooltips.set_tip(widget, "")
     249            self._tooltips.disable()
    153250
    154251#-----------------------------------------------------------------------------
     
    207304#-----------------------------------------------------------------------------
    208305
     306class BookDialog(gtk.Dialog):
     307    """The dialog box to select additional data for a booking."""
     308    def __init__(self, timetableWindow, flightPair, planes):
     309        """Construct the dialog box."""
     310        super(BookDialog, self).__init__(title = WINDOW_TITLE_BASE +
     311                                         " - " +
     312                                         xstr("timetable_book_title"),
     313                                         parent = timetableWindow)
     314        contentArea = self.get_content_area()
     315
     316        frame = gtk.Frame(xstr("timetable_book_frame_title"))
     317        frame.set_size_request(600, -1)
     318
     319        mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
     320                                      xscale = 0.0, yscale = 0.0)
     321        mainAlignment.set_padding(padding_top = 16, padding_bottom = 12,
     322                                  padding_left = 8, padding_right = 8)
     323
     324        table = gtk.Table(6, 2)
     325        table.set_row_spacings(8)
     326        table.set_col_spacings(16)
     327
     328        row = 0
     329        label = gtk.Label()
     330        label.set_markup(xstr("timetable_book_callsign"))
     331        label.set_alignment(0.0, 0.5)
     332        table.attach(label, 0, 1, row, row + 1)
     333
     334        text = flightPair.flight0.callsign
     335        if flightPair.flight1 is not None:
     336            text += " / " + flightPair.flight1.callsign
     337        label = gtk.Label(text)
     338        label.set_alignment(0.0, 0.5)
     339        table.attach(label, 1, 2, row, row + 1)
     340
     341        row += 1
     342
     343        label = gtk.Label()
     344        label.set_markup(xstr("timetable_book_from_to"))
     345        label.set_alignment(0.0, 0.5)
     346        table.attach(label, 0, 1, row, row + 1)
     347
     348        text = flightPair.flight0.departureICAO + " - " + \
     349               flightPair.flight0.arrivalICAO
     350        if flightPair.flight1 is not None:
     351            text += " - " + flightPair.flight1.arrivalICAO
     352        label = gtk.Label(text)
     353        label.set_alignment(0.0, 0.5)
     354        table.attach(label, 1, 2, row, row + 1)
     355
     356        row += 1
     357
     358        if flightPair.flight0.type==ScheduledFlight.TYPE_VIP and \
     359           flightPair.flight0.date!=const.defaultDate:
     360            label = gtk.Label()
     361            label.set_markup(xstr("timetable_book_flightDate"))
     362            label.set_use_underline(True)
     363            label.set_alignment(0.0, 0.5)
     364            table.attach(label, 0, 1, row, row + 1)
     365
     366            self._flightDate = gtk.Button()
     367            self._flightDate.connect("clicked", self._flightDateClicked)
     368            self._flightDate.set_tooltip_text(xstr("timetable_book_flightDate_tooltip"))
     369            label.set_mnemonic_widget(self._flightDate)
     370
     371            table.attach(self._flightDate, 1, 2, row, row + 1)
     372
     373            self._calendarWindow = calendarWindow = CalendarWindow()
     374            calendarWindow.set_transient_for(self)
     375            calendarWindow.connect("delete-event", self._calendarWindowDeleted)
     376            calendarWindow.connect("date-selected", self._calendarWindowDateSelected)
     377
     378            self._setDate(flightPair.flight0.date)
     379
     380            row += 1
     381        else:
     382            self._flightDate = None
     383            self._calendarWindow = None
     384
     385        label = gtk.Label()
     386        label.set_markup(xstr("timetable_book_dep_arr"))
     387        label.set_alignment(0.0, 0.5)
     388        table.attach(label, 0, 1, row, row + 1)
     389
     390        text = str(flightPair.flight0.departureTime) + " - " + \
     391               str(flightPair.flight0.arrivalTime)
     392        if flightPair.flight1 is not None:
     393            text += " / " + str(flightPair.flight1.departureTime) + " - " + \
     394                    str(flightPair.flight1.arrivalTime)
     395        label = gtk.Label(text)
     396        label.set_alignment(0.0, 0.5)
     397        table.attach(label, 1, 2, row, row + 1)
     398
     399        row += 1
     400
     401        label = gtk.Label()
     402        label.set_markup(xstr("timetable_book_duration"))
     403        label.set_alignment(0.0, 0.5)
     404        table.attach(label, 0, 1, row, row + 1)
     405
     406
     407        duration = flightPair.flight0.duration
     408        text = "%02d:%02d" % (duration/3600, (duration%3600)/60)
     409        if flightPair.flight1 is not None:
     410            duration = flightPair.flight0.duration
     411            text += " / %02d:%02d" % (duration/3600, (duration%3600)/60)
     412        label = gtk.Label(text)
     413        label.set_alignment(0.0, 0.5)
     414        table.attach(label, 1, 2, row, row + 1)
     415
     416        row += 2
     417
     418        label = gtk.Label()
     419        label.set_markup(xstr("timetable_book_tailNumber"))
     420        label.set_alignment(0.0, 0.5)
     421        table.attach(label, 0, 1, row, row + 1)
     422
     423        self._planes = planes
     424        tailNumbersModel = gtk.ListStore(str)
     425        for plane in planes:
     426            tailNumbersModel.append((plane.tailNumber,))
     427
     428        self._tailNumber = gtk.ComboBox(model = tailNumbersModel)
     429        renderer = gtk.CellRendererText()
     430        self._tailNumber.pack_start(renderer, True)
     431        self._tailNumber.add_attribute(renderer, "text", 0)
     432        self._tailNumber.set_tooltip_text(xstr("timetable_book_tailNumber_tooltip"))
     433        self._tailNumber.set_active(random.randint(0, len(planes)-1))
     434
     435        table.attach(self._tailNumber, 1, 2, row, row + 1)
     436
     437        mainAlignment.add(table)
     438
     439        frame.add(mainAlignment)
     440        contentArea.pack_start(frame, True, True, 4)
     441
     442        self.add_button(xstr("button_cancel"), RESPONSETYPE_CANCEL)
     443
     444        self._okButton = self.add_button(xstr("button_book"), RESPONSETYPE_OK)
     445        self._okButton.set_use_underline(True)
     446        self._okButton.set_can_default(True)
     447
     448    @property
     449    def plane(self):
     450        """Get the currently selected plane."""
     451        return self._planes[self._tailNumber.get_active()]
     452
     453    @property
     454    def date(self):
     455        """Get the flight date, if selected."""
     456        return None if self._calendarWindow is None \
     457          else self._calendarWindow.getDate()
     458
     459    def _setDate(self, date):
     460        """Set the date to the given one."""
     461        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
     462        self._calendarWindow.setDate(date)
     463
     464    def _flightDateClicked(self, button):
     465        """Called when the flight date button is clicked."""
     466        self._calendarWindow.set_position(gtk.WIN_POS_MOUSE)
     467        self.set_focus(self._calendarWindow)
     468        self._calendarWindow.show_all()
     469
     470    def _calendarWindowDeleted(self, window, event):
     471        """Called when the flight date window is deleted."""
     472        self._calendarWindow.hide()
     473
     474    def _calendarWindowDateSelected(self, window):
     475        """Called when the flight date window is deleted."""
     476        self._calendarWindow.hide()
     477        date = window.getDate()
     478        self._flightDate.set_label(date.strftime("%Y-%m-%d"))
     479
     480#-----------------------------------------------------------------------------
     481
    209482class TimetableWindow(gtk.Window):
    210483    """The window to display the timetable."""
     
    255528
    256529        self._flightDate = gtk.Button()
     530        self._flightDate.connect("clicked", self._flightDateClicked)
    257531        self._flightDate.connect("clicked", self._flightDateClicked)
    258532        self._flightDate.set_tooltip_text(xstr("timetable_flightdate_tooltip"))
     
    317591        vbox.pack_start(filterAlignment, False, False, 2)
    318592
    319         self._timetable = Timetable()
     593        self._timetable = Timetable(popupMenuProducer =
     594                                        self._createTimetablePopupMenu)
     595        self._timetable.connect("row-activated", self._rowActivated)
     596        self._timetable.connect("selection-changed", self._selectionChanged)
    320597        vbox.pack_start(self._timetable, True, True, 2)
    321598
    322         alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
     599        alignment = gtk.Alignment(xalign = 1.0, yalign = 0.5,
    323600                                  xscale = 0.0, yscale = 0.0)
    324         self._closeButton = gtk.Button(xstr("button_ok"))
     601        buttonBox = gtk.HBox()
     602
     603        self._bookButton = gtk.Button(xstr("button_book"))
     604        self._bookButton.set_use_underline(True)
     605        self._bookButton.set_can_default(True)
     606        self._bookButton.connect("clicked", self._bookClicked)
     607        self._bookButton.set_sensitive(False)
     608        buttonBox.pack_start(self._bookButton, False, False, 4);
     609
     610        self._closeButton = gtk.Button(xstr("button_close"))
     611        self._closeButton.set_use_underline(True)
    325612        self._closeButton.connect("clicked", self._closeClicked)
    326         alignment.add(self._closeButton)
     613        buttonBox.pack_start(self._closeButton, False, False, 4);
     614
     615        alignment.add(buttonBox)
    327616        vbox.pack_start(alignment, False, False, 2)
    328617
     
    337626
    338627        self.setDate(datetime.date.today())
     628
     629        self._flightPairToBook = None
    339630
    340631    @property
     
    423714                                   self.isVIPEnabled,
    424715                                   aircraftTypes)
     716
     717    def _bookClicked(self, button):
     718        """Called when the book button has been clicked."""
     719        self._book(self._timetable.getFlightPair(self._timetable.selectedIndexes[0]))
     720
     721    def _rowActivated(self, timetable, index):
     722        """Called when a row has been activated (e.g. double-clicked) in the
     723        timetable."""
     724        self._book(self._timetable.getFlightPair(index))
     725
     726    def _selectionChanged(self, timetable, indexes):
     727        """Called when the selection has changed.
     728
     729        It sets the sensitivity of the book button based on whether a row is
     730        selected or not."""
     731        self._bookButton.set_sensitive(len(indexes)>0)
     732
     733    def _book(self, flightPair):
     734        """Try to book the given flight pair."""
     735        self._flightPairToBook = flightPair
     736        self._gui.getFleet(callback = self._continueBook,
     737                           busyCallback = self._busyCallback)
     738
     739    def _busyCallback(self, busy):
     740        """Called when the busy state has changed."""
     741        self.set_sensitive(not busy)
     742
     743    def _continueBook(self, fleet):
     744        """Continue booking, once the fleet is available."""
     745        flightPair = self._flightPairToBook
     746        aircraftType = flightPair.flight0.aircraftType
     747        planes = [plane for plane in fleet
     748                  if plane.aircraftType == aircraftType]
     749        planes.sort(cmp = lambda p1, p2: cmp(p1.tailNumber, p2.tailNumber))
     750
     751        dialog = BookDialog(self, flightPair, planes)
     752        dialog.show_all()
     753        result = dialog.run()
     754        dialog.hide()
     755        if result==RESPONSETYPE_OK:
     756            flightIDs = [flightPair.flight0.id]
     757            if flightPair.flight1 is not None:
     758                flightIDs.append(flightPair.flight1.id)
     759
     760            date = dialog.date
     761            if date is None:
     762                date = self._calendarWindow.getDate()
     763
     764            self._gui.bookFlights(self._bookFlightsCallback,
     765                                  flightIDs, date, dialog.plane.tailNumber,
     766                                  busyCallback = self._busyCallback)
     767
     768    def _bookFlightsCallback(self, returned, result):
     769        """Called when the booking has finished."""
     770        if returned:
     771            dialog = gtk.MessageDialog(parent = self,
     772                                       type = MESSAGETYPE_INFO,
     773                                       message_format = xstr("bookflights_successful"))
     774            dialog.format_secondary_markup(xstr("bookflights_successful_secondary"))
     775        else:
     776            dialog = gtk.MessageDialog(parent = self,
     777                                       type = MESSAGETYPE_ERROR,
     778                                       message_format = xstr("bookflights_failed"))
     779            dialog.format_secondary_markup(xstr("bookflights_failed_secondary"))
     780
     781        dialog.add_button(xstr("button_ok"), RESPONSETYPE_OK)
     782        dialog.set_title(WINDOW_TITLE_BASE)
     783
     784        dialog.run()
     785        dialog.hide()
     786
     787    def _createTimetablePopupMenu(self):
     788        """Get the popuop menu for the timetable."""
     789        menu = gtk.Menu()
     790
     791        menuItem = gtk.MenuItem()
     792        menuItem.set_label(xstr("timetable_popup_book"))
     793        menuItem.set_use_underline(True)
     794        menuItem.connect("activate", self._popupBook)
     795        menuItem.show()
     796
     797        menu.append(menuItem)
     798
     799        return menu
     800
     801    def _popupBook(self, menuItem):
     802        """Try to book the given flight pair."""
     803        index = self._timetable.selectedIndexes[0]
     804        self._book(self._timetable.getFlightPair(index))
  • src/mlx/rpc.py

    r858 r859  
    6666        "arrivalTime": lambda value: ScheduledFlight._decodeTime(value),
    6767        "duration": lambda value: ScheduledFlight._decodeDuration(value),
    68         "type": int,
    69         "spec": int
     68        "spec": int,
     69        "validFrom": lambda value: ScheduledFlight._decodeDate(value),
     70        "validTo": lambda value: ScheduledFlight._decodeDate(value),
     71        "date": lambda value: ScheduledFlight._decodeDate(value)
    7072        }
    7173
     
    7476        """Decode the given value as a time value."""
    7577        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()
    7686
    7787    @staticmethod
     
    90100        del self.typeCode
    91101
     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
    92119    def __repr__(self):
    93120        return "ScheduledFlight<%d, %d, %s, %s (%s) - %s (%s) -> %d, %d>" % \
     
    95122           self.departureICAO, str(self.departureTime),
    96123           self.arrivalICAO, str(self.arrivalTime),
    97            self.duration, self.spec)
     124           self.duration, self.type)
    98125
    99126#---------------------------------------------------------------------------------------
     
    104131    Occasionally, one of the flights may be missing."""
    105132    @staticmethod
    106     def scheduledFlights2Pairs(scheduledFlights):
     133    def scheduledFlights2Pairs(scheduledFlights, date):
    107134        """Convert the given list of scheduled flights into a list of flight
    108135        pairs."""
     136        weekday = str(date.weekday()+1)
     137
    109138        flights = {}
     139        weekdayFlights = {}
    110140        for flight in scheduledFlights:
    111141            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
    112147
    113148        flightPairs = []
    114149
    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:
     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:
    127166                flightPairs.append(ScheduledFlightPair(flight))
     167
     168        flightPairs.sort(cmp = lambda pair1, pair2:
     169                         cmp(pair1.flight0.date, pair2.flight0.date))
    128170
    129171        return flightPairs
     
    133175        self.flight0 = flight0
    134176        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)
    135187
    136188#---------------------------------------------------------------------------------------
     
    280332    _instructions = {
    281333        "status" : lambda value: rpccommon.Plane.str2status(value),
    282         "gateNumber" : lambda value: value if value else None
     334        "gateNumber" : lambda value: value if value else None,
     335        "typeCode": lambda value: BookedFlight._decodeAircraftType(value)
    283336        }
    284337
     
    286339        """Construct the plane."""
    287340        RPCObject.__init__(self, value, instructions = Plane._instructions)
     341        self.aircraftType = self.typeCode
     342        del self.typeCode
    288343
    289344#---------------------------------------------------------------------------------------
     
    524579                                   self._server.getTimetable(sessionID,
    525580                                                             date.strftime("%Y-%m-%d"),
    526                                                              date.weekday() + 1,
     581                                                             date.weekday()+1,
    527582                                                             typeCodes))
    528583        return ScheduledFlightPair.scheduledFlights2Pairs([ScheduledFlight(value)
    529                                                           for value in values])
     584                                                          for value in values],
     585                                                          date)
     586
     587    def bookFlights(self, flightIDs, date, tailNumber):
     588        """Book the flights with the given IDs on the given date to be flown
     589        with the plane of the given tail number."""
     590        values = self._performCall(lambda sessionID:
     591                                   self._server.bookFlights(sessionID,
     592                                                            flightIDs,
     593                                                            date.strftime("%Y-%m-%d"),
     594                                                            tailNumber))
     595        return [BookedFlight(value) for value in values]
    530596
    531597    def _performCall(self, callFn, acceptResults = []):
  • src/mlx/web.py

    r858 r859  
    13621362#------------------------------------------------------------------------------
    13631363
     1364class BookFlights(RPCRequest):
     1365    """Request to book flights."""
     1366    def __init__(self, client, callback, flightIDs, date, tailNumber):
     1367        """Construct the request with the given client and callback function."""
     1368        super(BookFlights, self).__init__(client, callback)
     1369        self._flightIDs = flightIDs
     1370        self._date = date
     1371        self._tailNumber = tailNumber
     1372
     1373    def run(self):
     1374        """Perform the login request."""
     1375        result = Result()
     1376
     1377        result.bookedFlights = self._client.bookFlights(self._flightIDs,
     1378                                                        self._date,
     1379                                                        self._tailNumber)
     1380
     1381        return result
     1382
     1383#------------------------------------------------------------------------------
     1384
    13641385class Handler(threading.Thread):
    13651386    """The handler for the web services.
     
    14631484        self._addRequest(GetTimetable(self._rpcClient, callback, date, types))
    14641485
     1486    def bookFlights(self, callback, flightIDs, date, tailNumber):
     1487        """Enqueue a request to book some flights."""
     1488        self._addRequest(BookFlights(self._rpcClient, callback,
     1489                                     flightIDs, date, tailNumber))
     1490
    14651491    def run(self):
    14661492        """Process the requests."""
Note: See TracChangeset for help on using the changeset viewer.