From: Vincent Date: Mon, 30 Mar 2009 10:08:13 +0000 (+0200) Subject: base version X-Git-Url: http://git.maemo.org/git/?p=pystan;a=commitdiff_plain;h=refs%2Fheads%2Fmaster base version --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..698808d --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +all: + python2.5 setup.py build +clean: + python2.5 setup.py clean --all +install: + python2.5 setup.py install --root $(DESTDIR) diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..18c4c44 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,7 @@ +pystan for Debian +----------------- + + + + -- + Mon, 16 Mar 2009 07:58:38 +0100 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..0501a34 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +pystan (0.1-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Vincent Lark Mon, 16 Mar 2009 07:58:38 +0100 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..1a2f81d --- /dev/null +++ b/debian/control @@ -0,0 +1,24 @@ +Source: pystan +Section: user/other +Priority: optional +Maintainer: Vincent Lark +Build-Depends: debhelper (>= 5), python2.5-dev +Standards-Version: 3.7.2 + +Package: pystan +Architecture: all +Depends: python2.5, python2.5-hildon, python2.5-gtk2 +Description: View STAN schedules + View Stan (nancy, france) bus schedules +XB-Maemo-Icon-26: + iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+g + vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gURDQoYya0JlwAAAU9J + REFUSMftlL1KA0EUhb/NZl/ggnHQxsJUxt5CUucVJCCkDfgyKdIGG5/A0s5HEBtJ + EdDAQGBgmw0YJmMzgXXYza5CtNkDW9zZw5z7c+ZCgwb/Ai3i9sVl/Bq8RIs4LRK1 + gJDsKvJyNXmJMuYTsMoY1zpgozaABdYArQNPZQ1kfyGU7SpqVwxzAMwABWhgpIwp + 4vWBB+AUWAI3ypjnfEXtPU4bLKx9vErTeCeiRSYF+fTn1j5dp2myE9EiU+DSi3wX + ymeqRQAmZ3EcA5E/fgO6BULT8zhOcrwXoJdrXRa2Lgps2y2odAUcBUIXQdz78YyC + SldAp8b7+bXrIv91qjZBietqCc2DjbAt4b2WxJkyZljVujlwp0U0cPxuLcAIuC+4 + dKxFlsDJarvdAGP/b6hFnDImYs+uG3hbO2AB3Jbsur63tQM+fFx3bzZocEB8AdV2 + gJBZgKTwAAAAAElFTkSuQmCC + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..96afffd --- /dev/null +++ b/debian/copyright @@ -0,0 +1,37 @@ +This package was debianized by + on +Mon, 16 Mar 2009 07:58:38 +0100. + +It was downloaded from + +Upstream Author: + +Copyright: + +License: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The Debian packaging is (C) 2009, + and +is licensed under the GPL, see above. + + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..ca882bb --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e69de29 diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..b432144 --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +pystan_0.1-1_all.deb user/other optional diff --git a/debian/pystan/DEBIAN/control b/debian/pystan/DEBIAN/control new file mode 100644 index 0000000..821659b --- /dev/null +++ b/debian/pystan/DEBIAN/control @@ -0,0 +1,21 @@ +Package: pystan +Version: 0.1-1 +Section: user/other +Priority: optional +Architecture: all +Depends: python2.5, python2.5-hildon, python2.5-gtk2 +Installed-Size: 140 +Maintainer: Vincent Lark +Description: View STAN schedules + View Stan (nancy, france) bus schedules +Maemo-Icon-26: + iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABmJLR0QA/wD/AP+g + vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gURDQoYya0JlwAAAU9J + REFUSMftlL1KA0EUhb/NZl/ggnHQxsJUxt5CUucVJCCkDfgyKdIGG5/A0s5HEBtJ + EdDAQGBgmw0YJmMzgXXYza5CtNkDW9zZw5z7c+ZCgwb/Ai3i9sVl/Bq8RIs4LRK1 + gJDsKvJyNXmJMuYTsMoY1zpgozaABdYArQNPZQ1kfyGU7SpqVwxzAMwABWhgpIwp + 4vWBB+AUWAI3ypjnfEXtPU4bLKx9vErTeCeiRSYF+fTn1j5dp2myE9EiU+DSi3wX + ymeqRQAmZ3EcA5E/fgO6BULT8zhOcrwXoJdrXRa2Lgps2y2odAUcBUIXQdz78YyC + SldAp8b7+bXrIv91qjZBietqCc2DjbAt4b2WxJkyZljVujlwp0U0cPxuLcAIuC+4 + dKxFlsDJarvdAGP/b6hFnDImYs+uG3hbO2AB3Jbsur63tQM+fFx3bzZocEB8AdV2 + gJBZgKTwAAAAAElFTkSuQmCC diff --git a/debian/pystan/DEBIAN/md5sums b/debian/pystan/DEBIAN/md5sums new file mode 100644 index 0000000..b327031 --- /dev/null +++ b/debian/pystan/DEBIAN/md5sums @@ -0,0 +1,17 @@ +f466787e3d793b02fcd02a845e2bd0ad usr/share/applications/hildon/pystan.desktop +047cc0470ebd6f123bffcc95a2b1decb usr/share/doc/pystan/changelog.Debian +1573ad5a1f2e997723daec8e3ba6a9eb usr/share/pixmaps/pystan_icon_26x26.png +8c5ed46684f553197b9b34ae3e12352b usr/lib/python2.5/site-packages/pystan/pystan.py +7e06be438b2120dd372435d8af7cb599 usr/lib/python2.5/site-packages/pystan/lib/timetable.py +80d225b8f76996af038148d01df8ec7b usr/lib/python2.5/site-packages/pystan/lib/stan.pyc +4717daecc957dacc7f4c535746345c47 usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.pyc +2cbfdb2885c9704e22d50bcebacd0815 usr/lib/python2.5/site-packages/pystan/lib/gui.pyc +3ffa8db925d680e6d9de7794fa389949 usr/lib/python2.5/site-packages/pystan/lib/gui.py +fb461ddfb6169b759ac33f69ee529d83 usr/lib/python2.5/site-packages/pystan/lib/stan.py +d41d8cd98f00b204e9800998ecf8427e usr/lib/python2.5/site-packages/pystan/lib/__init__.py +4910bdeea33e5438204b26925a520da0 usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.py +dd0b4563e07a766fa929a68a9367431e usr/lib/python2.5/site-packages/pystan/lib/timetable.pyc +3b7e76444d1dbfb32dab30fd83be0e57 usr/lib/python2.5/site-packages/pystan/lib/__init__.pyc +e1c78567dd054fa5b9a28c4fb0072d52 usr/lib/python2.5/site-packages/pystan/pystan.pyc +68b329da9893e34099c7d8ad5cb9c940 usr/lib/python2.5/site-packages/pystan/__init__.py +166934514c8c29a2d8926fd0d41a4d43 usr/lib/python2.5/site-packages/pystan/__init__.pyc diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.py @@ -0,0 +1 @@ + diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.pyc new file mode 100644 index 0000000..ab48230 Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.pyc new file mode 100644 index 0000000..0f99fb7 Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.py new file mode 100644 index 0000000..72caa77 --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.py @@ -0,0 +1,131 @@ +# -*- coding: UTF-8 -*- + +import gtk +import hildon +import webbrowser + +from stan import STAN +from timetable import StanTimetable + +class StanGUI(hildon.Program): + # + def __init__(self): + hildon.Program.__init__(self) + self.stan = STAN() + self.window = hildon.Window() + self.window.set_title('pySTAN') + self.window.connect("destroy", gtk.main_quit) + self.add_window(self.window) + + self.tabs = self.main_tabs() + self.window.add(self.tabs) + # + self.last_station = False + + def main_tabs(self): + tabs = gtk.Notebook() + tabs.set_tab_pos(gtk.POS_TOP) + tabs.append_page(self.tab_lines(), gtk.Label('Par lignes')) + #tabs.append_page(self.tab_stations(), gtk.Label('Par arrets')) + #tabs.append_page(self.tab_search(), gtk.Label('Recherche')) + return tabs + # + def tab_search(self): + tab_search = gtk.Frame(None) #'Recherche de trajet') + + from_location = gtk.Entry() + from_location_label = gtk.Label(u"Départ") + from_city = gtk.Entry() + from_city_label = gtk.Label(u"à") + + to_location = gtk.Entry() + to_location_label = gtk.Label(u"Arrivée") + to_city = gtk.Entry() + to_city_label = gtk.Label(u"à") + + datetime_select = gtk.Calendar() + datetime_select.display_options(gtk.CALENDAR_SHOW_DAY_NAMES | gtk.CALENDAR_WEEK_START_MONDAY) + + restriction = gtk.Combo() + restriction.set_popdown_strings([u"partir après", u"arriver avant"]) + + vbox = gtk.VBox() + vbox.add(from_location_label) + vbox.add(from_location) + vbox.add(from_city) + vbox.add(to_location_label) + vbox.add(to_location) + vbox.add(to_city) + + vbox.add(datetime_select) + vbox.add(restriction) + + tab_search.add(vbox) + + return tab_search + + def tab_lines(self): + tab_lines = tab_lines = gtk.Frame(None) #'Recherche par lignes') + + list_lines = gtk.combo_box_new_text() + list_lines.connect('changed', self.show_line_timetable) + + for line in self.stan.lines(): + list_lines.append_text( '-'.join(line['numbers']) + "\t" + line['name'] ) + + vbox = gtk.VBox() + + alignment_list = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=1.0, yscale=0.10) + alignment_list.add(list_lines) + vbox.add(alignment_list) + + self.timetable = StanTimetable() + self.timetable.create() + self.timetable.set_activated_callback(self.timetable_row_clicked) + + alignment_tt = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=1.0, yscale=0.90) + alignment_tt.add(self.timetable) + vbox.add(alignment_tt) + + tab_lines.add(vbox) + return tab_lines + + def show_line_timetable(self, selection): + self.timetable.load( self.stan.lines()[selection.get_active()], '08/03/2009', '12' ) + + def timetable_row_clicked(self, station_name): + if self.last_station != station_name: + if self.last_station != False: + self.tabs.remove(self.tab_map) + self.last_station = station_name + self.tab_map = self.tab_geomap(station=station_name) + self.tabs.append_page(self.tab_map, gtk.Label(station_name)) + self.tabs.show_all() + self.tabs.set_current_page(1) + + def tab_stations(self): + tab_stations = gtk.Frame(None) #'Recherche par arrets') + + list_stations = gtk.Combo() + list_stations.set_popdown_strings([]) + + tab_stations.add(list_stations) + + return tab_stations + + def tab_geomap(self, station): + tab_map = gtk.Frame(None) + map_button = gtk.Button() + #map_label = gtk.Label(u"Link to map") + map_button.connect('clicked', self.open_map, 'http://www.google.fr') + + tab_map.add(map_button) + return tab_map + + def open_map(self, data, url): + webbrowser.open(url) + + def run(self): + self.window.show_all() + gtk.main() + diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.pyc new file mode 100644 index 0000000..098575f Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.py new file mode 100644 index 0000000..f4e858c --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.py @@ -0,0 +1,239 @@ +# -*- coding: UTF-8 -*- + +import urllib2 +from urllib2 import URLError + + +from timetable_parser import StanTimetableParser + +class STAN: + + RSS_URL = 'http://www.reseau-stan.com/rss' + + def lines(self): + + return [ + { 'id': 158, + 'name': u"Nancy République / Champigneulles", + 'numbers': ["321"] + }, + { 'id': 144, + 'name': u"Nancy République / Bouxières-aux-Dames", + 'numbers': ["322"] + }, + { 'id': 146, + 'name': u"Nancy République / Frouard Le Nid", + 'numbers': ["323"] + }, + { 'id': 148, + 'name': u"Nancy République / Pompey Les Vannes", + 'numbers': ["324"] + }, + { 'id': 150, + 'name': u"Nancy République / Pompey Fonds de Lavaux", + 'numbers': ["325"] + }, + { 'id': 145, + 'name': u"Vandoeuvre Vélodrome / Chaligny", + 'numbers': ["511"] + }, + { 'id': 147, + 'name': u"Vandoeuvre Vélodrome / Pont Saint-Vincent", + 'numbers': ["512"] + }, + { 'id': 149, + 'name': u"Vandoeuvre Vélodrome / Messein", + 'numbers': ["514"] + }, + { 'id': 151, + 'name': u"Nancy Place Carnot / Chaligny", + 'numbers': ["521"] + }, + { 'id': 153, + 'name': u"Nancy Place Carnot / Pont Saint-Vincent", + 'numbers': ["522"] + }, + { 'id': 155, + 'name': u"Nancy Place Carnot / Bainville-sur-Madon", + 'numbers': ["523"] + }, + { 'id': 152, + 'name': u"Nancy République / Saint-Nicolas Le Nid", + 'numbers': ["621"] + }, + { 'id': 157, + 'name': u"Nancy République / Saint-Nicolas République", + 'numbers': ["622"] + }, + { 'id': 156, + 'name': u"Nancy République / Dombasle Stand", + 'numbers': ["623"] + }, + { 'id': 154, + 'name': u"Nancy République / Dombasle Maroc", + 'numbers': ["624"] + }, + { 'id': 126, + 'name': u"Vandoeuvre Parc des Expositions / Nancy République", + 'numbers': ["129"] + }, + { 'id': 133, + 'name': u"Ligne 80", + 'numbers': ["80"] + }, + { 'id': 138, + 'name': u"Vandoeuvre CHU Brabois / Essey Mouzimpré", + 'numbers': ["Tram 1"] + }, + { 'id': 141, + 'name': u"Vandoeuvre CHU Brabois / Centre d\'accueil - Villers les Essarts", + 'numbers': ["Ptit Stan Vilers"] + }, + { 'id': 143, + 'name': u"Vandoeuvre CHU Brabois / Vandoeuvre Nations - Médecine préventive ", + 'numbers': ["Ptit Stan Vandoeuvre"] + }, + { 'id': 140, + 'name': u"Laxou Sapinière / Laxou Provinces", + 'numbers': ["Ptit Stan Laxou"] + }, + { 'id': 142, + 'name': u"Nancy Carnot", + 'numbers': ["Ptit Stan Colline"] + }, + { 'id': 134, + 'name': u"Malzéville Sadi Carnot - Nancy Cimetière du Sud", + 'numbers': ["Minibus"] + }, + { 'id': 159, + 'name': u"StanU", + 'numbers': ["110"] + }, + { 'id': 112, + 'name': u"Maxéville Champ le Boeuf - Nancy Haut du Lièvre / Jarville Sion", + 'numbers': ["111", "112"] + }, + { 'id': 96, + 'name': u"Houdemont / Vandoeuvre Charmois", + 'numbers': ["114"] + }, + { 'id': 98, + 'name': u"Ludres / Villers Lycée Stanislas", + 'numbers': ["115"] + }, + { 'id': 101, + 'name': u"Fléville Fleurychamp - Heillecourt / Villers Lycée Stanislas", + 'numbers': ["116"] + }, + { 'id': 103, + 'name': u"Nancy Beauregard / Nancy République", + 'numbers': ["121"] + }, + { 'id': 135, + 'name': u"Villers Clairlieu / Nancy République", + 'numbers': ["122", "126"] + }, + { 'id': 106, + 'name': u"Vandoeuvre Nations / Nancy République", + 'numbers': ["123"] + }, + { 'id': 109, + 'name': u"Laxou Mouzon / Nancy République", + 'numbers': ["124"] + }, + { 'id': 118, + 'name': u"Laxou Provinces / Seichamps", + 'numbers': ["130"] + }, + { 'id': 95, + 'name': u"Maxéville Mairie / Jarville Sion - Heillecourt - Fléville De la Noue", + 'numbers': ["131", "132", "133"] + }, + { 'id': 97, + 'name': u"Villers Lycée Stanislas / Maxéville Lafayette - Malzéville Savlons", + 'numbers': ["134", "135"] + }, + { 'id': 100, + 'name': u"Nancy Cours Léopold / Laneuveville - La Madeleine", + 'numbers': ["136", "137"] + }, + { 'id': 102, + 'name': u"Laxou Champ le Boeuf / Vandoeurvre Roberval", + 'numbers': ["138"] + }, + { 'id': 104, + 'name': u"Nancy Cours Léopold / Ludres", + 'numbers': ["139"] + }, + { 'id': 114, + 'name': u"Malzéville / Saulxures Forêt - Art sur Meurthe", + 'numbers': ["141", "142"] + }, + { 'id': 108, + 'name': u"Malzéville Pixérécourt / Saulxures", + 'numbers': ["161"] + }, + { 'id': 116, + 'name': u"Essey C.E.S / Gérard Barrois Stade Marcel Picot", + 'numbers': ["162"] + }, + { 'id': 107, + 'name': u"Dommartemont / Jarville Sion", + 'numbers': ["163"] + }, + { 'id': 110, + 'name': u"Essey Mouzimpré / Seichamps - Pulnoy (Lignes accessibles PMR)", + 'numbers': ["171", "172"] + } + ] + + + def stations(self): + ''' + Extract from http://www.reseau-stan.com/horaires_arret/index7f96.html?rub_code=28 + and ...... lieux publics + ''' + return { + } + + + def search(self, from_location, from_city, to_location, to_city, datetime, restrict=68): + cached_result = self.cached_search(from_location, from_city, to_location, to_city, datetime, restrict) + if cached_search is not None: + return cached_result + + ''' + POST to http://www.reseau-stan.com/ri/index.asp + from_location: keywordsDep + from_city: comDep + to_location: keywordsArr + to_city: comArr + date: laDate (11/03/2009) + hour: lHeure (13 = 13h) + minute: laMinute + restrict: typeDate (68 = partir apres, 65 = arriver avant) + ''' + pass + + + def cached_search(self, from_location, from_city, to_location, to_city, datetime, restrict=68): + cache_id = '-'.join([ from_location, from_city, to_location, to_city, datetime, restrict ]) + pass + + def load_timetable(self, lineid, date, hour): + parser = StanTimetableParser() + timetable_raw_html_file = None + try: + timetable_raw_html_file = self.load_timetable_remote(lineid, date, hour) + except URLError, e: + print e + + parser.feed(timetable_raw_html_file) + parser.close() + return parser.result + + def load_timetable_remote(self, lineid, date, hour): + url = 'http://www.reseau-stan.com/horaires_ligne/index.asp?rub_code=6&thm_id=7&gpl_id=0&lign_id=%s' % lineid + print url + response = urllib2.urlopen(url) + return response.read() diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.pyc new file mode 100644 index 0000000..177b227 Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.py new file mode 100644 index 0000000..73b6f70 --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.py @@ -0,0 +1,90 @@ +# -*- coding: UTF-8 -*- + +import gtk +import sys + +from stan import STAN + +class StanTimetable(gtk.VBox): + + def create(self): + self.current_timetable = False + self.navigation = False + + def set_activated_callback(self, new_callback): + self.cell_activated = new_callback + + def cell_activated(self, data): + print data + + def __cell_activated(self, timetable_view=None, cell_position=None, column_view=None): + if len(cell_position) == 1: + data = self.timetable_struct[ cell_position[0] ][0] + self.cell_activated(data) + + def navigation_buttons(self): + box = gtk.HBox() + + next = gtk.Button() + prev = gtk.Button() + + box.add(prev) + box.add(next) + return box + + def load(self, lineobj, date, time): + + if (self.current_timetable): + self.remove(self.current_timetable) + + self.timetable_struct = STAN().load_timetable(lineobj['id'], date, time) + + self.timetable_navigation = self.timetable_struct['navigation'] + self.timetable_struct = self.timetable_struct['timetable'] + + self.timetable_store = gtk.ListStore(*([str] * len(self.timetable_struct[0]))) + for line in self.timetable_struct: + print line + + self.timetable_store.append( line ) + self.timetable_view = gtk.TreeView(self.timetable_store) + + self.timetable_view.connect("row-activated", self.__cell_activated) + + # + + column_name = gtk.TreeViewColumn(u"Name") + self.timetable_view.append_column(column_name) + cell = gtk.CellRendererText() + column_name.pack_start(cell, True) + column_name.add_attribute(cell, 'text', 0) + + nb_curses_columns = len(self.timetable_struct[0])-1 + #nb_curses_columns -= 2 + + for i in range(nb_curses_columns): + column = gtk.TreeViewColumn(None) + self.timetable_view.append_column(column) + cell = gtk.CellRendererText() + cell.set_property('xalign', 0.5) + column.pack_start(cell, True) + column.add_attribute(cell, 'text', i+1) + + self.timetable_view.set_search_column(0) + + self.current_timetable = gtk.ScrolledWindow() + + self.current_timetable.add(self.timetable_view) + + if self.navigation != False: + self.remove(self.navigation) + self.navigation = self.navigation_buttons() + self.add(self.navigation) + + self.add(self.current_timetable) + + self.show_all() + + + + pass diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.pyc new file mode 100644 index 0000000..f773e0e Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.py new file mode 100644 index 0000000..132ba71 --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.py @@ -0,0 +1,82 @@ + +import re + +from HTMLParser import HTMLParser + +class StanTimetableParser(HTMLParser): + + def __init__(self): + HTMLParser.__init__(self) + + self.result = { + 'navigation': {}, + 'timetable': [] + } + + self.current_tt_line = None + self.state = None + self.TT_CAPTURING_TIMETABLE = 'TT_CAPTURING_TIMETABLE' + self.TT_CAPTURING_NAVIGATION = 'TT_CAPTURING_NAVIGATION' + self.TT_STOP_HOUR = 'TT_STOP_HOUR' + self.TT_STOP_NAME = 'TT_STOP_NAME' + self.TT_NAVIG_PREV = 'TT_NAVIG_PREV' + self.TT_NAVIG_NEXT = 'TT_NAVIG_NEXT' + self.TT_NAVIG_PREV_LINK = 'TT_NAVIG_PREV_LINK' + self.TT_NAVIG_NEXT_LINK = 'TT_NAVIG_NEXT_LINK' + + def handle_starttag(self, tag, attrs): + self.last_tag = tag + + attributes = {} + for attr in attrs: + attributes[attr[0]] = attr[1] + + if tag == 'div' and attributes.has_key('class') and attributes['class'] == 'goatResultTop': + self.state = self.TT_CAPTURING_NAVIGATION + + elif tag == 'table' and attributes.has_key('id') and attributes['id'] == 'linehour': + self.state = self.TT_CAPTURING_TIMETABLE + + elif self.state == self.TT_CAPTURING_TIMETABLE: + if tag == 'tr': + self.current_tt_line = [] + elif tag == 'strong': + self.state = self.TT_STOP_NAME + elif tag == 'td' and attributes.has_key('class') and 'hour' in attributes['class']: + self.state = self.TT_STOP_HOUR + + elif self.state == self.TT_CAPTURING_NAVIGATION: + if tag == 'div' and attributes.has_key('class') and attributes['class'] == 'linehourPrev': + self.state = self.TT_NAVIG_PREV + elif tag == 'div' and attributes.has_key('class') and attributes['class'] == 'linehourNext': + self.state = self.TT_NAVIG_NEXT + + elif self.state == self.TT_NAVIG_PREV and tag == 'a': + self.result['navigation']['prev'] = attributes['href'] + self.state = self.TT_CAPTURING_NAVIGATION + + elif self.state == self.TT_NAVIG_NEXT and tag == 'a': + self.result['navigation']['next'] = attributes['href'] + self.state = self.TT_CAPTURING_NAVIGATION + + + + def handle_data(self, data): + if self.state == self.TT_STOP_HOUR: + self.current_tt_line.append(data) + self.state = self.TT_CAPTURING_TIMETABLE + + elif self.state == self.TT_STOP_NAME: + # remove in-parenthesis + data = re.compile('^[^\(]+').match(data).group() + self.current_tt_line.append(data) + self.state = self.TT_CAPTURING_TIMETABLE + + + + def handle_endtag(self, tag): + if tag == 'tr' and self.state == self.TT_CAPTURING_TIMETABLE and self.current_tt_line is not None and len(self.current_tt_line) > 0: + self.result['timetable'].append(self.current_tt_line) + + elif tag == 'table' and self.state == self.TT_CAPTURING_TIMETABLE: + self.state = None diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.pyc new file mode 100644 index 0000000..1fd46c7 Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.pyc differ diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.py b/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.py new file mode 100644 index 0000000..73e7fd1 --- /dev/null +++ b/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python2.5 + +from lib.gui import StanGUI + +app = StanGUI() +app.run() diff --git a/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.pyc b/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.pyc new file mode 100644 index 0000000..8324c90 Binary files /dev/null and b/debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.pyc differ diff --git a/debian/pystan/usr/share/applications/hildon/pystan.desktop b/debian/pystan/usr/share/applications/hildon/pystan.desktop new file mode 100644 index 0000000..2af320e --- /dev/null +++ b/debian/pystan/usr/share/applications/hildon/pystan.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Version=1.0.0 +Encoding=UTF-8 +Name=Maemo Stan +Exec=/usr/bin/maemostan +Icon=maemostan_icon_26x26 +Type=Application diff --git a/debian/pystan/usr/share/doc/pystan/changelog.Debian b/debian/pystan/usr/share/doc/pystan/changelog.Debian new file mode 100644 index 0000000..0501a34 --- /dev/null +++ b/debian/pystan/usr/share/doc/pystan/changelog.Debian @@ -0,0 +1,6 @@ +pystan (0.1-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Vincent Lark Mon, 16 Mar 2009 07:58:38 +0100 + diff --git a/debian/pystan/usr/share/pixmaps/pystan_icon_26x26.png b/debian/pystan/usr/share/pixmaps/pystan_icon_26x26.png new file mode 100644 index 0000000..0d50c48 Binary files /dev/null and b/debian/pystan/usr/share/pixmaps/pystan_icon_26x26.png differ diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..bb82628 --- /dev/null +++ b/debian/rules @@ -0,0 +1,77 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #docbook-to-man debian/pystan.sgml > pystan.1 + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/pystan. + $(MAKE) DESTDIR=$(CURDIR)/debian/pystan install + + +# Build architecture-independent files here. +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-dependent files here. +binary-arch: build install + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b3db184 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ + +import os + +from distutils.core import setup + +setup( name='pyStan', + version='0.1.0', + package_dir = {'': 'src'}, + packages = ['pystan', 'pystan.lib'], + data_files = [ + ('share/pixmaps', ['src/pystan_icon_26x26.png']), + ('share/applications/hildon', ['src/pystan.desktop']), + ] +) diff --git a/src/pystan.desktop b/src/pystan.desktop new file mode 100644 index 0000000..2af320e --- /dev/null +++ b/src/pystan.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Version=1.0.0 +Encoding=UTF-8 +Name=Maemo Stan +Exec=/usr/bin/maemostan +Icon=maemostan_icon_26x26 +Type=Application diff --git a/src/pystan/__init__.py b/src/pystan/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/pystan/__init__.py @@ -0,0 +1 @@ + diff --git a/src/pystan/lib/__init__.py b/src/pystan/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/pystan/lib/__init__.pyo b/src/pystan/lib/__init__.pyo new file mode 100644 index 0000000..7d5c383 Binary files /dev/null and b/src/pystan/lib/__init__.pyo differ diff --git a/src/pystan/lib/gui.py b/src/pystan/lib/gui.py new file mode 100644 index 0000000..ca70b28 --- /dev/null +++ b/src/pystan/lib/gui.py @@ -0,0 +1,204 @@ +# -*- coding: UTF-8 -*- + +import gtk +import hildon +import webbrowser + +import sys, time + +from stan import STAN +from timetable import StanTimetable + +class StanGUI(hildon.Program): + # + def __init__(self): + hildon.Program.__init__(self) + self.stan = STAN() + self.window = hildon.Window() + self.window.set_title('pySTAN') + self.window.connect("destroy", gtk.main_quit) + self.add_window(self.window) + + self.tabs = self.main_tabs() + self.window.add(self.tabs) + # + self.last_station = False + + def main_tabs(self): + tabs = gtk.Notebook() + tabs.set_tab_pos(gtk.POS_TOP) + tabs.append_page(self.tab_lines(), gtk.Label('Par lignes')) + #tabs.append_page(self.tab_stations(), gtk.Label('Par arrets')) + #tabs.append_page(self.tab_search(), gtk.Label('Recherche')) + return tabs + # + def tab_search(self): + tab_search = gtk.Frame(None) #'Recherche de trajet') + + from_location = gtk.Entry() + from_location_label = gtk.Label(u"Départ") + from_city = gtk.Entry() + from_city_label = gtk.Label(u"à") + + to_location = gtk.Entry() + to_location_label = gtk.Label(u"Arrivée") + to_city = gtk.Entry() + to_city_label = gtk.Label(u"à") + + datetime_select = gtk.Calendar() + datetime_select.display_options(gtk.CALENDAR_SHOW_DAY_NAMES | gtk.CALENDAR_WEEK_START_MONDAY) + + restriction = gtk.Combo() + restriction.set_popdown_strings([u"partir après", u"arriver avant"]) + + vbox = gtk.VBox() + vbox.add(from_location_label) + vbox.add(from_location) + vbox.add(from_city) + vbox.add(to_location_label) + vbox.add(to_location) + vbox.add(to_city) + + vbox.add(datetime_select) + vbox.add(restriction) + + tab_search.add(vbox) + + return tab_search + + # special callback for drawing line numbers in combo box + def line_number_cell_cb(self, treeviewcolumn, cellrenderer, modele, iter): + for line in self.stan.lines(): + cell_line_name = modele.get_value(iter, 1) + cell_line_id = modele.get_value(iter, 2) + + if line['id'] == cell_line_id: + text = '-'.join(line['numbers']) + cellrenderer.set_property('text', text) + + if len(text) <= 10: + cellrenderer.set_property('scale', 0.8) + else: + cellrenderer.set_property('scale', 0.6) + + if not line.has_key('fg-color'): + #print "ERROR:\tline %s do not have foreground color" % line['id'] + pass + else: + cellrenderer.set_property('foreground-gdk', gtk.gdk.color_parse(line['fg-color'])) + + if not line.has_key('bg-color'): + #print "ERROR:\tline %s do not have background color" % line['id'] + pass + else: + cellrenderer.set_property('background-gdk', gtk.gdk.color_parse(line['bg-color'])) + return + + def set_previous_button_cb(self, callback): + self._previous_button_cb = callback + + def _previous_button_cb(self, data): + pass + + def set_active_lineid(self, lineid): + pass + + def tab_lines(self): + tab_lines = gtk.Frame(None) #'Recherche par lignes') + + #lines_navigation = gtk.Table(rows=2, columns=2) + lines_navigation = gtk.HBox() + + #previous_button = gtk.Button(u"<") + #previous_button.connect('clicked', self._previous_button_cb) + #alignment_previous_button = gtk.Alignment(xalign=0, xscale=0.02) + #alignment_previous_button.add(previous_button) + #lines_navigation.attach(previous_button, 0, 1, 0, 1, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK, xpadding=1, ypadding=0) + #lines_navigation.pack_start(previous_button) + + lines_list = gtk.ComboBox() + lines_model = gtk.ListStore(str, str, int) + lines_item_number = gtk.CellRendererText() + lines_item_number.set_property('scale', 0.8) + lines_item_name = gtk.CellRendererText() + lines_item_name.set_property('scale', 0.8) + + lines_list.pack_start(lines_item_number, True) + lines_list.pack_start(lines_item_name, True) + + lines_list.add_attribute(lines_item_number, 'text', 0) + lines_list.add_attribute(lines_item_name, 'text', 1) + + lines_list.set_cell_data_func(lines_item_number, self.line_number_cell_cb) + + for line in self.stan.lines(): + lines_model.append([ '' , line['name'], line['id'] ]) + + lines_list.connect('changed', self.show_line_timetable) + lines_list.set_model(lines_model) + + alignment_lines_list = gtk.Alignment(yscale=0.1) + alignment_lines_list.add(lines_list) + lines_navigation.pack_end(alignment_lines_list) + #lines_navigation.attach(lines_list, 1, 2, 0, 1, xoptions=gtk.SHRINK, yoptions=gtk.FILL) + + vbox = gtk.VBox() + + alignment_lines_navigation = gtk.Alignment(xscale=1.0, yscale=0.1) + alignment_lines_navigation.add(lines_navigation) + + vbox.pack_start(alignment_lines_navigation, False) + + self.timetable = StanTimetable() + self.timetable.create() + self.timetable.set_activated_callback(self.timetable_row_clicked) + + #alignment_tt = gtk.Alignment(yscale=0.9) + #alignment_tt.add(self.timetable) + vbox.pack_start(self.timetable, True, True) + + #lines_navigation.attach(self.timetable, 0, 2, 1, 2, xoptions=gtk.EXPAND, yoptions=gtk.EXPAND, xpadding=1, ypadding=0) + #lines_navigation.pack_start(self.timetable) + + tab_lines.add(vbox) + return tab_lines + + def show_line_timetable(self, selection): + self.timetable.load( self.stan.lines()[selection.get_active()], time.strftime("%d/%m/%Y"), time.strftime("%H"), 0 ) + + def timetable_row_clicked(self, station): + if self.last_station != station['name']: + if self.last_station != False: + self.tabs.remove(self.tab_map) + self.last_station = station['name'] + self.tab_map = self.tab_geomap(station) + self.tabs.append_page(self.tab_map, gtk.Label(station['name'])) + self.tabs.show_all() + self.tabs.set_current_page(1) + + def tab_stations(self): + tab_stations = gtk.Frame(None) #'Recherche par arrets') + + list_stations = gtk.Combo() + list_stations.set_popdown_strings([]) + + tab_stations.add(list_stations) + + return tab_stations + + def tab_geomap(self, station): + tab_map = gtk.Frame(None) + map_button = gtk.Button() + #map_label = gtk.Label(u"Link to map") + map_button.connect('clicked', self.open_map, station['href']) + + tab_map.add(map_button) + return tab_map + + def open_map(self, data, url): + webbrowser.open(url) + + def run(self): + self.window.show_all() + gtk.main() + diff --git a/src/pystan/lib/gui.pyo b/src/pystan/lib/gui.pyo new file mode 100644 index 0000000..5cdff96 Binary files /dev/null and b/src/pystan/lib/gui.pyo differ diff --git a/src/pystan/lib/stan.py b/src/pystan/lib/stan.py new file mode 100644 index 0000000..337672d --- /dev/null +++ b/src/pystan/lib/stan.py @@ -0,0 +1,478 @@ +# -*- coding: UTF-8 -*- + +import os, sys, time, re +import urllib, urllib2, urlparse, cgi +from urllib2 import URLError + + +from timetable_parser import StanTimetableParser + +class STAN: + + RSS_URL = 'http://www.reseau-stan.com/rss' + + get_params = [ 'rub_code', 'gpl_id', 'thm_id', 'lineid', 'date', 'hour', 'direction' ] + + def lines(self): + + return [ + { 'id': 144, + 'name': u"Nancy République / Bouxières-aux-Dames", + 'numbers': ["322"], + 'fg-color': '#fff', + 'bg-color': '#6ecbf1' + }, + { 'id': 145, + 'name': u"Vandoeuvre Vᅢᄅlodrome / Chaligny", + 'numbers': ["511"], + 'fg-color': '#fff', + 'bg-color': '#a3b909' + }, + { 'id': 146, + 'name': u"Nancy République / Frouard Le Nid", + 'numbers': ["323"], + 'fg-color': '#fff', + 'bg-color': '#f5a3c8' + }, + { 'id': 147, + 'name': u"Vandoeuvre Vélodrome / Pont Saint-Vincent", + 'numbers': ["512"], + 'fg-color': '#fff', + 'bg-color': '#c00266' + }, + { 'id': 148, + 'name': u"Nancy République / Pompey Les Vannes", + 'numbers': ["324"], + 'fg-color': '#fff', + 'bg-color': '#d80b8c' + }, + { 'id': 149, + 'name': u"Vandoeuvre Vélodrome / Messein", + 'numbers': ["514"], + 'fg-color': '#fff', + 'bg-color': '#006d2c' + }, + { 'id': 150, + 'name': u"Nancy République / Pompey Fonds de Lavaux", + 'numbers': ["325"], + 'fg-color': '#fff', + 'bg-color': '#b4d8be' + }, + { 'id': 151, + 'name': u"Nancy Place Carnot / Chaligny", + 'numbers': ["521"], + 'fg-color': '#a3b909', + 'bg-color': '#fff' + }, + { 'id': 152, + 'name': u"Nancy République / Saint-Nicolas Le Nid", + 'numbers': ["621"], + 'fg-color': '#fff', + 'bg-color': '#0b0a4b' + }, + { 'id': 153, + 'name': u"Nancy Place Carnot / Pont Saint-Vincent", + 'numbers': ["522"], + 'fg-color': '#c00266', + 'bg-color': '#fff' + }, + { 'id': 154, + 'name': u"Nancy République / Dombasle Maroc", + 'numbers': ["624"], + 'fg-color': '#fff', + 'bg-color': '#fb1518' + }, + { 'id': 155, + 'name': u"Nancy Place Carnot / Bainville-sur-Madon", + 'numbers': ["523"], + 'fg-color': '#81d0e3', + 'bg-color': '#fff' + }, + { 'id': 157, + 'name': u"Nancy République / Saint-Nicolas République", + 'numbers': ["622"], + 'fg-color': '#fff', + 'bg-color': '#0d97a8' + }, + { 'id': 156, + 'name': u"Nancy République / Dombasle Stand", + 'numbers': ["623"], + 'fg-color': '#fff', + 'bg-color': '#fe6612' + }, + { 'id': 158, + 'name': u"Nancy République / Champigneulles", + 'numbers': ["321"], + 'fg-color': '#fff', + 'bg-color': '#d3b6d1' + }, + { 'id': 126, + 'name': u"Vandoeuvre Parc des Expositions / Nancy République", + 'numbers': ["129"], + 'fg-color': '#fff', + 'bg-color': '#92bbad' + }, + { 'id': 133, + 'name': u"Ligne 80", + 'numbers': ["80"], + 'bg-color': 'yellow' + }, + { 'id': 138, + 'name': u"Vandoeuvre CHU Brabois / Essey Mouzimpré", + 'numbers': ["Tram 1"], + 'fg-color': '#fff', + 'bg-color': '#ff0000' + }, + { 'id': 141, + 'name': u"Vandoeuvre CHU Brabois / Centre d\'accueil - Villers les Essarts", + 'numbers': ["Ptit Stan Vilers"], + 'bg-color': 'yellow' + }, + { 'id': 143, + 'name': u"Vandoeuvre CHU Brabois / Vandoeuvre Nations - Médecine préventive ", + 'numbers': ["Ptit Stan Vandoeuvre"], + 'bg-color': 'yellow' + }, + { 'id': 140, + 'name': u"Laxou Sapinière / Laxou Provinces", + 'numbers': ["Ptit Stan Laxou"], + 'bg-color': 'yellow' + }, + { 'id': 142, + 'name': u"Nancy Carnot", + 'numbers': ["Ptit Stan Colline"], + 'bg-color': 'yellow' + }, + { 'id': 134, + 'name': u"Malzéville Sadi Carnot - Nancy Cimetière du Sud", + 'numbers': ["Minibus"], + 'bg-color': 'yellow' + }, + { 'id': 159, + 'name': u"StanU", + 'numbers': ["110"], + 'bg-color': 'yellow' + }, + { 'id': 112, + 'name': u"Maxéville Champ le Boeuf - Nancy Haut du Lièvre / Jarville Sion", + 'numbers': ["111", "112"], + 'fg-color': '#fff', + 'bg-color': '#71228d' + }, + { 'id': 96, + 'name': u"Houdemont / Vandoeuvre Charmois", + 'numbers': ["114"], + 'fg-color': '#000', + 'bg-color': '#b6a6d3' + }, + { 'id': 98, + 'name': u"Ludres / Villers Lycée Stanislas", + 'numbers': ["115"], + 'fg-color': '#fff', + 'bg-color': '#705aac' + }, + { 'id': 101, + 'name': u"Fléville Fleurychamp - Heillecourt / Villers Lycée Stanislas", + 'numbers': ["116"], + 'fg-color': '#fff', + 'bg-color': '#755eac' + }, + { 'id': 103, + 'name': u"Nancy Beauregard / Nancy République", + 'numbers': ["121"], + 'fg-color': '#fff', + 'bg-color': '#339d2f' + }, + { 'id': 135, + 'name': u"Villers Clairlieu / Nancy République", + 'numbers': ["122", "126"], + 'fg-color': '#000', + 'bg-color': '#9cca1f' + }, + { 'id': 106, + 'name': u"Vandoeuvre Nations / Nancy République", + 'numbers': ["123"], + 'fg-color': '#000', + 'bg-color': '#a9d72f' + }, + { 'id': 109, + 'name': u"Laxou Mouzon / Nancy République", + 'numbers': ["124"], + 'fg-color': '#fff', + 'bg-color': '#39a63b' + }, + + { 'id': 118, + 'name': u"Laxou Provinces / Seichamps", + 'numbers': ["130"], + 'fg-color': '#fff', + 'bg-color': '#048a96' + }, + { 'id': 95, + 'name': u"Maxéville Mairie / Jarville Sion - Heillecourt - Fléville De la Noue", + 'numbers': ["131", "132", "133"], + 'fg-color': '#000', + 'bg-color': '#82cee6' + }, + { 'id': 97, + 'name': u"Villers Lycée Stanislas / Maxéville Lafayette - Malzéville Savlons", + 'numbers': ["134", "135"], + 'fg-color': '#fff', + 'bg-color': '#1693cb' + }, + { 'id': 100, + 'name': u"Nancy Cours Léopold / Laneuveville - La Madeleine", + 'numbers': ["136", "137"], + 'fg-color': '#fff', + 'bg-color': '#6f88c1' + }, + { 'id': 102, + 'name': u"Laxou Champ le Boeuf / Vandoeurvre Roberval", + 'numbers': ["138"], + 'fg-color': '#fff', + 'bg-color': '#123e97' + }, + { 'id': 104, + 'name': u"Nancy Cours Léopold / Ludres", + 'numbers': ["139"], + 'fg-color': '#fff', + 'bg-color': '#6f87c7' + }, + { 'id': 114, + 'name': u"Malzéville / Saulxures Forêt - Art sur Meurthe", + 'numbers': ["141", "142"], + 'fg-color': '#fff', + 'bg-color': '#ff750e' + }, + { 'id': 108, + 'name': u"Malzéville Pixérécourt / Saulxures", + 'numbers': ["161"], + 'fg-color': '#000', + 'bg-color': '#ffff33' + }, + { 'id': 116, + 'name': u"Essey C.E.S / Gérard Barrois Stade Marcel Picot", + 'numbers': ["162"], + 'fg-color': '#000', + 'bg-color': '#f59638' + }, + { 'id': 107, + 'name': u"Dommartemont / Jarville Sion", + 'numbers': ["163"], + 'fg-color': '#000', + 'bg-color': '#fad956' + }, + { 'id': 110, + 'name': u"Essey Mouzimpré / Seichamps - Pulnoy (Lignes accessibles PMR)", + 'numbers': ["171", "172"], + 'bg-color': '#f38ab3' + } + ] + + def schedules_cache_dirname(self): + pystan_schedules = os.path.expanduser('~/.pystan/cache/schedules') + if not os.path.isdir(pystan_schedules): + #os.makedirs('~/.pystan/cache/schedules') + pass + return pystan_schedules + + def filter_params(self, params): + only_wanted = {} + for key in params: + if key in self.get_params: + only_wanted[key] = params[key] + return only_wanted + + def schedule_cache_filename(self, params): + params['date'] = self.schedules_cache_dateformat(params['date']) + params = [ str(key)+"="+str(params[key]) for key in iter(params)] + params.sort() + filename = '_'.join(params) + filename = filename.replace('/', '-') + return "%s/%s" % (self.schedules_cache_dirname(), filename) + + def schedules_cache_dateformat(self, date): + date = time.strptime(date, "%d/%m/%Y") + day_n = int(time.strftime("%w", date)) + # saturday / sunday + if day_n == 0 or day_n == 6: + date = time.strftime("%a/%m/%Y", date) + # week + else: + date = time.strftime("week/%m/%Y", date) + return date + + + def default_timetable_params(self, params): + for param, default in { 'rub_code':6, 'thm_id':6, 'gpl_id':0 }.items(): #{'sens':1, 'date':'01/01/01', 'hour':'12', 'index':'1'}: + if not params.has_key(param) or params[param] is None: + params[param] = default + return params + + def search(self, from_location, from_city, to_location, to_city, datetime, restrict=68): + cached_result = self.cached_search(from_location, from_city, to_location, to_city, datetime, restrict) + if cached_search is not None: + return cached_result + + ''' + POST to http://www.reseau-stan.com/ri/index.asp + from_location: keywordsDep + from_city: comDep + to_location: keywordsArr + to_city: comArr + date: laDate (11/03/2009) + hour: lHeure (13 = 13h) + minute: laMinute + restrict: typeDate (68 = partir apres, 65 = arriver avant) + ''' + pass + + def cached_search(self, from_location, from_city, to_location, to_city, datetime, restrict=68): + cache_id = '-'.join([ from_location, from_city, to_location, to_city, datetime, restrict ]) + pass + + def load_timetable(self, lineid, params=None, bypass=False): + params = self.default_timetable_params(params) + params['lineid'] = lineid + params = self.filter_params(params) + + timetable_raw_html_file = None + + if timetable_raw_html_file is None: + timetable_raw_html_file = self.load_timetable_local(params, bypass) + + if timetable_raw_html_file is None: + timetable_raw_html_file = self.load_timetable_remote(params) + + if timetable_raw_html_file is None: + print "WARNING:\tNo cache file and no internet connection" + + if timetable_raw_html_file: + parser = StanTimetableParser() + parser.feed(timetable_raw_html_file) + parser.close() + parsed = parser.result + + if parsed['navigation'].has_key('next'): + index_next = urlparse.urlsplit(parsed['navigation']['next'])[3] + index_next = cgi.parse_qs(index_next) + parsed['navigation']['next'] = index_next['index'][0] + + if parsed['navigation'].has_key('prev'): + index_prev = urlparse.urlsplit(parsed['navigation']['prev'])[3] + index_prev = cgi.parse_qs(index_prev) + parsed['navigation']['prev'] = index_prev['index'][0] + + if len(parsed['stations']) > 0: + parsed['stations'] = self.extract_stations_infos(parsed['stations']) + pass + + return parsed + + else: + return timetable_raw_html_file + + def load_timetable_remote(self, params): + url = 'http://www.reseau-stan.com/horaires_ligne/index.asp' + + get_params = params.copy() + get_params['lign_id'] = get_params['lineid']; del(get_params['lineid']) + + if get_params.has_key('direction') and int(get_params['direction']) > 0: + get_params['sens'] = 2 + del(get_params['direction']) + else: + get_params['sens'] = 1 + + if get_params.has_key('hour'): + get_params['index'] = get_params['hour'] + del(get_params['hour']) + + req = urllib2.Request(url, urllib.urlencode(get_params)) + + print "GET FROM:\t%s?%s" % (url, urllib.urlencode(get_params)) + + file_data = None + + try: + response = urllib2.urlopen(req) + file_data = response.read() + except URLError, e: + print "WARNING:\t%s" % e + + if file_data is not None: + self.save_timetable_local(file_data, params) + return file_data + + def load_timetable_local(self, params, bypass=False): + file = self.schedule_cache_filename(params) + file_data = None + max_age = 999L + + print "GET FROM:\t%s" % file + + if not os.path.isfile(file): + #print "WARNING: %s does not exists" % file + file_data = None + + elif bypass or os.stat(file).st_mtime <= int(time.time()) + max_age: + response = open(file) + try: + file_data = response.read() + finally: + response.close() + else: + print "WARNING:\t%s exists but is too old" % file + file_data = False + + return file_data + + def save_timetable_local(self, file_data, params): + + file_data = re.compile('(
.*)