base version master
authorVincent <vincent@Phobos.(none)>
Mon, 30 Mar 2009 10:08:13 +0000 (12:08 +0200)
committerVincent <vincent@Phobos.(none)>
Mon, 30 Mar 2009 10:08:13 +0000 (12:08 +0200)
87 files changed:
Makefile [new file with mode: 0644]
debian/README.Debian [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/files [new file with mode: 0644]
debian/pystan/DEBIAN/control [new file with mode: 0644]
debian/pystan/DEBIAN/md5sums [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/__init__.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/__init__.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/gui.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/stan.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/lib/timetable_parser.pyc [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.py [new file with mode: 0644]
debian/pystan/usr/lib/python2.5/site-packages/pystan/pystan.pyc [new file with mode: 0644]
debian/pystan/usr/share/applications/hildon/pystan.desktop [new file with mode: 0644]
debian/pystan/usr/share/doc/pystan/changelog.Debian [new file with mode: 0644]
debian/pystan/usr/share/pixmaps/pystan_icon_26x26.png [new file with mode: 0644]
debian/rules [new file with mode: 0755]
setup.py [new file with mode: 0644]
src/pystan.desktop [new file with mode: 0644]
src/pystan/__init__.py [new file with mode: 0644]
src/pystan/lib/__init__.py [new file with mode: 0644]
src/pystan/lib/__init__.pyo [new file with mode: 0644]
src/pystan/lib/gui.py [new file with mode: 0644]
src/pystan/lib/gui.pyo [new file with mode: 0644]
src/pystan/lib/stan.py [new file with mode: 0644]
src/pystan/lib/stan.pyo [new file with mode: 0644]
src/pystan/lib/timetable.py [new file with mode: 0644]
src/pystan/lib/timetable.pyo [new file with mode: 0644]
src/pystan/lib/timetable_parser.py [new file with mode: 0644]
src/pystan/lib/timetable_parser.pyo [new file with mode: 0644]
src/pystan/pystan.py [new file with mode: 0755]
src/pystan/q.log [new file with mode: 0644]
src/pystan/res/linesimg/111_112.jpg [new file with mode: 0644]
src/pystan/res/linesimg/114.jpg [new file with mode: 0644]
src/pystan/res/linesimg/115.jpg [new file with mode: 0644]
src/pystan/res/linesimg/116.jpg [new file with mode: 0644]
src/pystan/res/linesimg/121.jpg [new file with mode: 0644]
src/pystan/res/linesimg/122-126.jpg [new file with mode: 0644]
src/pystan/res/linesimg/123.jpg [new file with mode: 0644]
src/pystan/res/linesimg/124.jpg [new file with mode: 0644]
src/pystan/res/linesimg/129.jpg [new file with mode: 0644]
src/pystan/res/linesimg/130.jpg [new file with mode: 0644]
src/pystan/res/linesimg/131_132_133.jpg [new file with mode: 0644]
src/pystan/res/linesimg/134_135.jpg [new file with mode: 0644]
src/pystan/res/linesimg/136_137.jpg [new file with mode: 0644]
src/pystan/res/linesimg/138.jpg [new file with mode: 0644]
src/pystan/res/linesimg/139.jpg [new file with mode: 0644]
src/pystan/res/linesimg/141_142.jpg [new file with mode: 0644]
src/pystan/res/linesimg/161.jpg [new file with mode: 0644]
src/pystan/res/linesimg/162.JPG [new file with mode: 0644]
src/pystan/res/linesimg/163.jpg [new file with mode: 0644]
src/pystan/res/linesimg/321.gif [new file with mode: 0644]
src/pystan/res/linesimg/322.gif [new file with mode: 0644]
src/pystan/res/linesimg/323.gif [new file with mode: 0644]
src/pystan/res/linesimg/324.gif [new file with mode: 0644]
src/pystan/res/linesimg/325.gif [new file with mode: 0644]
src/pystan/res/linesimg/511.gif [new file with mode: 0644]
src/pystan/res/linesimg/512.gif [new file with mode: 0644]
src/pystan/res/linesimg/514.gif [new file with mode: 0644]
src/pystan/res/linesimg/521.gif [new file with mode: 0644]
src/pystan/res/linesimg/522.gif [new file with mode: 0644]
src/pystan/res/linesimg/523.gif [new file with mode: 0644]
src/pystan/res/linesimg/621.gif [new file with mode: 0644]
src/pystan/res/linesimg/622.gif [new file with mode: 0644]
src/pystan/res/linesimg/623.gif [new file with mode: 0644]
src/pystan/res/linesimg/624.gif [new file with mode: 0644]
src/pystan/res/linesimg/minibus.jpg [new file with mode: 0644]
src/pystan/res/linesimg/picto-web-171-172-access.jpg [new file with mode: 0644]
src/pystan/res/linesimg/ptit-stan-colline.jpg [new file with mode: 0644]
src/pystan/res/linesimg/ptit-stan-laxou.gif [new file with mode: 0644]
src/pystan/res/linesimg/ptit-stan-vandoeuvre.jpg [new file with mode: 0644]
src/pystan/res/linesimg/ptit-stan-villers.jpg [new file with mode: 0644]
src/pystan/res/linesimg/stan-u.jpg [new file with mode: 0644]
src/pystan/texput.log [new file with mode: 0644]
src/pystan_icon_26x26.png [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..18c4c44
--- /dev/null
@@ -0,0 +1,7 @@
+pystan for Debian
+-----------------
+
+<possible notes regarding this package - if none, delete this file>
+
+ -- 
+ <vincent.lark@gmail.com>  Mon, 16 Mar 2009 07:58:38 +0100
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..0501a34
--- /dev/null
@@ -0,0 +1,6 @@
+pystan (0.1-1) unstable; urgency=low
+
+  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
+
+ -- Vincent Lark <vincent.lark@gmail.com>  Mon, 16 Mar 2009 07:58:38 +0100
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..1a2f81d
--- /dev/null
@@ -0,0 +1,24 @@
+Source: pystan
+Section: user/other
+Priority: optional
+Maintainer: Vincent Lark <vincent.lark@gmail.com>
+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 (file)
index 0000000..96afffd
--- /dev/null
@@ -0,0 +1,37 @@
+This package was debianized by 
+ <vincent.lark@gmail.com> on
+Mon, 16 Mar 2009 07:58:38 +0100.
+
+It was downloaded from <fill in http/ftp site>
+
+Upstream Author: <put author(s) name and email here>
+
+Copyright: <put the year(s) of the copyright, and the names of the
+            copyright holder(s) here>
+
+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, 
+ <vincent.lark@gmail.com> 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 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/debian/files b/debian/files
new file mode 100644 (file)
index 0000000..b432144
--- /dev/null
@@ -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 (file)
index 0000000..821659b
--- /dev/null
@@ -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 <vincent.lark@gmail.com>
+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 (file)
index 0000000..b327031
--- /dev/null
@@ -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 (file)
index 0000000..8b13789
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..72caa77
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f4e858c
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..73b6f70
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..132ba71
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..73e7fd1
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..2af320e
--- /dev/null
@@ -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 (file)
index 0000000..0501a34
--- /dev/null
@@ -0,0 +1,6 @@
+pystan (0.1-1) unstable; urgency=low
+
+  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
+
+ -- Vincent Lark <vincent.lark@gmail.com>  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 (file)
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 (executable)
index 0000000..bb82628
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..2af320e
--- /dev/null
@@ -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 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/pystan/lib/__init__.py b/src/pystan/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pystan/lib/__init__.pyo b/src/pystan/lib/__init__.pyo
new file mode 100644 (file)
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 (file)
index 0000000..ca70b28
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..337672d
--- /dev/null
@@ -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('(<div id="goatResult">.*)<div id="footer">', re.DOTALL).findall(file_data)
+
+               if len(file_data) != 1:
+                       print "ERROR:\tCannot store a valid html section (%s occurences found)" % len(file_data)
+                       return False
+               else:
+                 file_data = file_data[0]
+
+               file = self.schedule_cache_filename(params)
+               print "WRITING:\t%s" % file
+               file_dest = open(file, 'w')
+               file_dest.write(file_data)
+               file_dest.close()
+               return True
+
+       def extract_stations_infos(self, stations_urls):
+               stations_ids = []
+               for station_url in stations_urls:
+                       station_id = urlparse.urlsplit(station_url)[3]
+                       station_id = cgi.parse_qs(station_id)
+
+                       if not station_id.has_key('pa_id1') or len(station_id['pa_id1']) != 1:
+                               print "ERROR:\tCannot extract pa_id1 from url %s " % station_url
+                               continue
+
+                       station_id = station_id['pa_id1'][0]
+
+                       url = 'http://www.reseau-stan.com/ri/cartegoogle.asp'
+
+                       if False:
+                               req = urllib2.Request(url, urllib.urlencode({ 'pa_iad1':station_id }))
+                               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: %s" % e
+
+                       stations_ids.append({
+                               'id': station_id,
+                               'href': "http://www.reseau-stan.com/ri/cartegoogle.asp?%s" % urllib.urlencode({ 'pa_iad1':station_id })
+                       })
+
+               return stations_ids
+
diff --git a/src/pystan/lib/stan.pyo b/src/pystan/lib/stan.pyo
new file mode 100644 (file)
index 0000000..248c8b5
Binary files /dev/null and b/src/pystan/lib/stan.pyo differ
diff --git a/src/pystan/lib/timetable.py b/src/pystan/lib/timetable.py
new file mode 100644 (file)
index 0000000..3839e42
--- /dev/null
@@ -0,0 +1,164 @@
+# -*- 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
+        self.navigation_pages = []
+
+    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:
+            station = self.timetable_stations[ cell_position[0] ]
+            station['name'] = self.timetable_struct[ cell_position[0] ][0]
+            self.cell_activated(station)
+
+    def navigation_buttons(self):
+        box = gtk.HBox()
+
+        current = self.navigation_pages[len(self.navigation_pages)-1]
+
+        #previous_page = None
+        #if len(self.navigation_pages) > 0:
+        #    previous_page = gtk.Button(u"<")
+        #    previous_page.connect('clicked', self.load_by_params, self.navigation_pages[len(self.navigation_pages)-1])
+
+        if self.timetable_navigation.has_key('next'):
+            image = gtk.Image()
+            image.set_from_stock(gtk.STOCK_GO_FORWARD, 48)
+            next = gtk.Button()
+            next.add(image)
+            index_next = self.timetable_navigation['next']
+            next_nav = current.copy(); next_nav.update({ 'hour':int(index_next) })
+            next.connect('clicked',  self.load_by_params, next_nav)
+            box.pack_end(next,False,False)
+
+        if self.timetable_navigation.has_key('prev'):
+            image = gtk.Image()
+            image.set_from_stock(gtk.STOCK_GO_BACK, 48)
+            index_prev = self.timetable_navigation['prev']
+            prev = gtk.Button()
+            prev.add(image)
+            prev_nav = current.copy(); prev_nav.update({'hour':int(index_prev) })
+            prev.connect('clicked',  self.load_by_params, prev_nav)
+            box.pack_start(prev,False,False)
+
+        invert = gtk.Button()
+        invert_nav = current.copy(); invert_nav.update({'direction':int(not invert_nav['direction'])})
+        invert.connect('clicked',  self.load_by_params, invert_nav)
+        image = gtk.Image()
+        if current['direction']:
+            image.set_from_stock(gtk.STOCK_SORT_DESCENDING, 48)
+        else:
+            image.set_from_stock(gtk.STOCK_SORT_ASCENDING, 48)
+        invert.add(image)
+        box.add(invert)
+
+        alignment_box_navigation = gtk.Alignment(xscale=1.0,yscale=0.1)
+        alignment_box_navigation.add(box)
+        return alignment_box_navigation
+
+    def load_by_params(self, widget, params):
+        if params.has_key('pop') and params['pop'] and len(self.navigation_pages) > 0:
+            self.navigation_pages.pop(len(self.navigation_pages)-1)
+
+        self.load(params['lineobj'], params['date'], params['hour'], params['direction'], params['bypass'])
+
+    def load(self, lineobj, date, hour, direction, bypass=False):
+
+        params = {'lineobj':lineobj, 'date':date, 'hour':hour, 'direction':direction, 'bypass':True }
+
+        if (self.current_timetable):
+            self.remove(self.current_timetable)
+
+        if (self.navigation):
+            self.remove(self.navigation)
+
+        self.timetable_struct = STAN().load_timetable(lineobj['id'], params, bypass)
+
+        if self.timetable_struct is None or (self.timetable_struct.has_key('timetable') and len(self.timetable_struct['timetable']) == 0):
+            self.current_timetable = gtk.VBox()
+            self.current_timetable.add(gtk.Label(u"No timetable available for line \n%s" % lineobj['name']))
+
+            if len(self.navigation_pages) > 0:
+                go_previous_page = gtk.Button(u"Get back")
+                back_params = self.navigation_pages[len(self.navigation_pages)-1]
+                back_params['pop'] = True
+                go_previous_page.connect('clicked', self.load_by_params, back_params)
+                self.current_timetable.add(go_previous_page)
+
+        elif self.timetable_struct == False:
+            self.current_timetable = gtk.VBox()
+            self.current_timetable.add(gtk.Label(u"No recent timetable available for line \n%s" % lineobj['name']))
+            bypass_button = gtk.Button(u"See old local timetable")
+            bypass_button.connect("clicked", self.load_by_params, params)
+            self.current_timetable.add(bypass_button)
+
+        elif len(self.timetable_struct['timetable']) > 0:
+            self.navigation_pages.append(params)
+
+            self.timetable_navigation = self.timetable_struct['navigation']
+            self.timetable_stations = self.timetable_struct['stations']
+            self.timetable_struct = self.timetable_struct['timetable']
+
+            #self.timetable_store = gtk.ListStore(([str] * len(self.timetable_struct[0])))
+            # beurk
+
+            if len(self.timetable_struct[0]) < 7:
+                for k,v in enumerate(self.timetable_struct):
+                    self.timetable_struct[k].append('-')
+
+            self.timetable_store = gtk.ListStore(str, str, str, str, str, str, str)
+            for k,line in enumerate(self.timetable_struct):
+                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()
+            cell.set_property('scale', 0.8)
+            column_name.pack_start(cell, True)
+            column_name.add_attribute(cell, 'text', 0)
+
+            nb_curses_columns = len(self.timetable_struct[0])-1
+
+            for i in range(nb_curses_columns):
+                column = gtk.TreeViewColumn(u"Schedules")
+                self.timetable_view.append_column(column)
+                cell = gtk.CellRendererText()
+                cell.set_property('xalign', 0.5)
+                cell.set_property('scale', 0.8)
+                cell.set_property('xpad', 9)
+                column.pack_end(cell, True)
+                column.add_attribute(cell, 'text', i+1)
+
+            #self.timetable_view.set_property('search_column', 0)
+            #self.timetable_view.set_property('enable-search', True)
+            #self.timetable_view.set_property('headers-clickable', True)
+
+            self.current_timetable = gtk.ScrolledWindow()
+            self.current_timetable.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+
+            self.current_timetable.add_with_viewport(self.timetable_view)
+
+            if self.navigation:
+                self.remove(self.navigation)
+            self.navigation = self.navigation_buttons()
+            self.add(self.navigation)
+
+        self.add(self.current_timetable)
+        self.show_all()
diff --git a/src/pystan/lib/timetable.pyo b/src/pystan/lib/timetable.pyo
new file mode 100644 (file)
index 0000000..cc8b9fd
Binary files /dev/null and b/src/pystan/lib/timetable.pyo differ
diff --git a/src/pystan/lib/timetable_parser.py b/src/pystan/lib/timetable_parser.py
new file mode 100644 (file)
index 0000000..95fff2d
--- /dev/null
@@ -0,0 +1,91 @@
+
+import re
+
+from HTMLParser import HTMLParser
+
+class StanTimetableParser(HTMLParser):
+
+    def __init__(self):
+        HTMLParser.__init__(self)
+
+        self.result = {
+            'navigation': {},
+            'timetable': [],
+            'stations': []
+        }
+
+        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):
+
+        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 == 'a' and self.last_tag[0] == 'td' and 'plan' in self.last_tag[1]['class']:
+                self.result['stations'].append(attributes['href'])
+                #self.current_tt_line.append(attributes['href'])
+            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
+
+        self.last_tag = [ tag, attributes ]
+        pass
+
+
+
+    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()
+
+            # slice doubles
+            data = data.replace(" - ", "\n")
+            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/src/pystan/lib/timetable_parser.pyo b/src/pystan/lib/timetable_parser.pyo
new file mode 100644 (file)
index 0000000..86f84a5
Binary files /dev/null and b/src/pystan/lib/timetable_parser.pyo differ
diff --git a/src/pystan/pystan.py b/src/pystan/pystan.py
new file mode 100755 (executable)
index 0000000..dda6f19
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env python2.5
+
+import sys
+import pygtk
+pygtk.require('2.0')
+
+from lib.gui import StanGUI
+
+app = StanGUI()
+app.run()
diff --git a/src/pystan/q.log b/src/pystan/q.log
new file mode 100644 (file)
index 0000000..c451b75
--- /dev/null
@@ -0,0 +1,16 @@
+This is TeX, Version 3.141592 (Web2C 7.5.4) (format=tex 2009.1.22)  24 MAR 2009 18:51
+**&tex --help
+(/scratchbox/devkits/doctools/share/texmf-dist/tex/latex/tools/q.tex
+! Interruption.
+l.2 
+    %% This is file `q.tex',
+? Q
+OK, entering \batchmode...
+ File ignored
+)
+! Emergency stop.
+<*> &tex --help
+               
+*** (job aborted, no legal \end found)
+
+No pages of output.
diff --git a/src/pystan/res/linesimg/111_112.jpg b/src/pystan/res/linesimg/111_112.jpg
new file mode 100644 (file)
index 0000000..d4ee3d5
Binary files /dev/null and b/src/pystan/res/linesimg/111_112.jpg differ
diff --git a/src/pystan/res/linesimg/114.jpg b/src/pystan/res/linesimg/114.jpg
new file mode 100644 (file)
index 0000000..8cf2216
Binary files /dev/null and b/src/pystan/res/linesimg/114.jpg differ
diff --git a/src/pystan/res/linesimg/115.jpg b/src/pystan/res/linesimg/115.jpg
new file mode 100644 (file)
index 0000000..8d7d77b
Binary files /dev/null and b/src/pystan/res/linesimg/115.jpg differ
diff --git a/src/pystan/res/linesimg/116.jpg b/src/pystan/res/linesimg/116.jpg
new file mode 100644 (file)
index 0000000..1366fdb
Binary files /dev/null and b/src/pystan/res/linesimg/116.jpg differ
diff --git a/src/pystan/res/linesimg/121.jpg b/src/pystan/res/linesimg/121.jpg
new file mode 100644 (file)
index 0000000..0b9a06d
Binary files /dev/null and b/src/pystan/res/linesimg/121.jpg differ
diff --git a/src/pystan/res/linesimg/122-126.jpg b/src/pystan/res/linesimg/122-126.jpg
new file mode 100644 (file)
index 0000000..52f1a72
Binary files /dev/null and b/src/pystan/res/linesimg/122-126.jpg differ
diff --git a/src/pystan/res/linesimg/123.jpg b/src/pystan/res/linesimg/123.jpg
new file mode 100644 (file)
index 0000000..f801b90
Binary files /dev/null and b/src/pystan/res/linesimg/123.jpg differ
diff --git a/src/pystan/res/linesimg/124.jpg b/src/pystan/res/linesimg/124.jpg
new file mode 100644 (file)
index 0000000..0f9d697
Binary files /dev/null and b/src/pystan/res/linesimg/124.jpg differ
diff --git a/src/pystan/res/linesimg/129.jpg b/src/pystan/res/linesimg/129.jpg
new file mode 100644 (file)
index 0000000..0c5896b
Binary files /dev/null and b/src/pystan/res/linesimg/129.jpg differ
diff --git a/src/pystan/res/linesimg/130.jpg b/src/pystan/res/linesimg/130.jpg
new file mode 100644 (file)
index 0000000..6aa3873
Binary files /dev/null and b/src/pystan/res/linesimg/130.jpg differ
diff --git a/src/pystan/res/linesimg/131_132_133.jpg b/src/pystan/res/linesimg/131_132_133.jpg
new file mode 100644 (file)
index 0000000..36aeade
Binary files /dev/null and b/src/pystan/res/linesimg/131_132_133.jpg differ
diff --git a/src/pystan/res/linesimg/134_135.jpg b/src/pystan/res/linesimg/134_135.jpg
new file mode 100644 (file)
index 0000000..56a1bf2
Binary files /dev/null and b/src/pystan/res/linesimg/134_135.jpg differ
diff --git a/src/pystan/res/linesimg/136_137.jpg b/src/pystan/res/linesimg/136_137.jpg
new file mode 100644 (file)
index 0000000..a2431c5
Binary files /dev/null and b/src/pystan/res/linesimg/136_137.jpg differ
diff --git a/src/pystan/res/linesimg/138.jpg b/src/pystan/res/linesimg/138.jpg
new file mode 100644 (file)
index 0000000..8d6f54b
Binary files /dev/null and b/src/pystan/res/linesimg/138.jpg differ
diff --git a/src/pystan/res/linesimg/139.jpg b/src/pystan/res/linesimg/139.jpg
new file mode 100644 (file)
index 0000000..7390bdc
Binary files /dev/null and b/src/pystan/res/linesimg/139.jpg differ
diff --git a/src/pystan/res/linesimg/141_142.jpg b/src/pystan/res/linesimg/141_142.jpg
new file mode 100644 (file)
index 0000000..86d23da
Binary files /dev/null and b/src/pystan/res/linesimg/141_142.jpg differ
diff --git a/src/pystan/res/linesimg/161.jpg b/src/pystan/res/linesimg/161.jpg
new file mode 100644 (file)
index 0000000..ea94e85
Binary files /dev/null and b/src/pystan/res/linesimg/161.jpg differ
diff --git a/src/pystan/res/linesimg/162.JPG b/src/pystan/res/linesimg/162.JPG
new file mode 100644 (file)
index 0000000..23256ec
Binary files /dev/null and b/src/pystan/res/linesimg/162.JPG differ
diff --git a/src/pystan/res/linesimg/163.jpg b/src/pystan/res/linesimg/163.jpg
new file mode 100644 (file)
index 0000000..4c6f569
Binary files /dev/null and b/src/pystan/res/linesimg/163.jpg differ
diff --git a/src/pystan/res/linesimg/321.gif b/src/pystan/res/linesimg/321.gif
new file mode 100644 (file)
index 0000000..b52eeb7
Binary files /dev/null and b/src/pystan/res/linesimg/321.gif differ
diff --git a/src/pystan/res/linesimg/322.gif b/src/pystan/res/linesimg/322.gif
new file mode 100644 (file)
index 0000000..9458cfa
Binary files /dev/null and b/src/pystan/res/linesimg/322.gif differ
diff --git a/src/pystan/res/linesimg/323.gif b/src/pystan/res/linesimg/323.gif
new file mode 100644 (file)
index 0000000..5fd3167
Binary files /dev/null and b/src/pystan/res/linesimg/323.gif differ
diff --git a/src/pystan/res/linesimg/324.gif b/src/pystan/res/linesimg/324.gif
new file mode 100644 (file)
index 0000000..dc34e83
Binary files /dev/null and b/src/pystan/res/linesimg/324.gif differ
diff --git a/src/pystan/res/linesimg/325.gif b/src/pystan/res/linesimg/325.gif
new file mode 100644 (file)
index 0000000..2184ebb
Binary files /dev/null and b/src/pystan/res/linesimg/325.gif differ
diff --git a/src/pystan/res/linesimg/511.gif b/src/pystan/res/linesimg/511.gif
new file mode 100644 (file)
index 0000000..c6c12d1
Binary files /dev/null and b/src/pystan/res/linesimg/511.gif differ
diff --git a/src/pystan/res/linesimg/512.gif b/src/pystan/res/linesimg/512.gif
new file mode 100644 (file)
index 0000000..8489908
Binary files /dev/null and b/src/pystan/res/linesimg/512.gif differ
diff --git a/src/pystan/res/linesimg/514.gif b/src/pystan/res/linesimg/514.gif
new file mode 100644 (file)
index 0000000..07f871a
Binary files /dev/null and b/src/pystan/res/linesimg/514.gif differ
diff --git a/src/pystan/res/linesimg/521.gif b/src/pystan/res/linesimg/521.gif
new file mode 100644 (file)
index 0000000..dc53b5b
Binary files /dev/null and b/src/pystan/res/linesimg/521.gif differ
diff --git a/src/pystan/res/linesimg/522.gif b/src/pystan/res/linesimg/522.gif
new file mode 100644 (file)
index 0000000..3828966
Binary files /dev/null and b/src/pystan/res/linesimg/522.gif differ
diff --git a/src/pystan/res/linesimg/523.gif b/src/pystan/res/linesimg/523.gif
new file mode 100644 (file)
index 0000000..ed3051a
Binary files /dev/null and b/src/pystan/res/linesimg/523.gif differ
diff --git a/src/pystan/res/linesimg/621.gif b/src/pystan/res/linesimg/621.gif
new file mode 100644 (file)
index 0000000..211e0a6
Binary files /dev/null and b/src/pystan/res/linesimg/621.gif differ
diff --git a/src/pystan/res/linesimg/622.gif b/src/pystan/res/linesimg/622.gif
new file mode 100644 (file)
index 0000000..083ac0c
Binary files /dev/null and b/src/pystan/res/linesimg/622.gif differ
diff --git a/src/pystan/res/linesimg/623.gif b/src/pystan/res/linesimg/623.gif
new file mode 100644 (file)
index 0000000..59d072a
Binary files /dev/null and b/src/pystan/res/linesimg/623.gif differ
diff --git a/src/pystan/res/linesimg/624.gif b/src/pystan/res/linesimg/624.gif
new file mode 100644 (file)
index 0000000..3cf8b8f
Binary files /dev/null and b/src/pystan/res/linesimg/624.gif differ
diff --git a/src/pystan/res/linesimg/minibus.jpg b/src/pystan/res/linesimg/minibus.jpg
new file mode 100644 (file)
index 0000000..54b246d
Binary files /dev/null and b/src/pystan/res/linesimg/minibus.jpg differ
diff --git a/src/pystan/res/linesimg/picto-web-171-172-access.jpg b/src/pystan/res/linesimg/picto-web-171-172-access.jpg
new file mode 100644 (file)
index 0000000..b51811c
Binary files /dev/null and b/src/pystan/res/linesimg/picto-web-171-172-access.jpg differ
diff --git a/src/pystan/res/linesimg/ptit-stan-colline.jpg b/src/pystan/res/linesimg/ptit-stan-colline.jpg
new file mode 100644 (file)
index 0000000..444d5bb
Binary files /dev/null and b/src/pystan/res/linesimg/ptit-stan-colline.jpg differ
diff --git a/src/pystan/res/linesimg/ptit-stan-laxou.gif b/src/pystan/res/linesimg/ptit-stan-laxou.gif
new file mode 100644 (file)
index 0000000..5f497e4
Binary files /dev/null and b/src/pystan/res/linesimg/ptit-stan-laxou.gif differ
diff --git a/src/pystan/res/linesimg/ptit-stan-vandoeuvre.jpg b/src/pystan/res/linesimg/ptit-stan-vandoeuvre.jpg
new file mode 100644 (file)
index 0000000..34e9295
Binary files /dev/null and b/src/pystan/res/linesimg/ptit-stan-vandoeuvre.jpg differ
diff --git a/src/pystan/res/linesimg/ptit-stan-villers.jpg b/src/pystan/res/linesimg/ptit-stan-villers.jpg
new file mode 100644 (file)
index 0000000..5634088
Binary files /dev/null and b/src/pystan/res/linesimg/ptit-stan-villers.jpg differ
diff --git a/src/pystan/res/linesimg/stan-u.jpg b/src/pystan/res/linesimg/stan-u.jpg
new file mode 100644 (file)
index 0000000..a77328f
Binary files /dev/null and b/src/pystan/res/linesimg/stan-u.jpg differ
diff --git a/src/pystan/texput.log b/src/pystan/texput.log
new file mode 100644 (file)
index 0000000..b119dfe
--- /dev/null
@@ -0,0 +1,14 @@
+This is TeX, Version 3.141592 (Web2C 7.5.4) (format=tex 2009.1.22)  24 MAR 2009 18:51
+**&tex
+
+*
+! Interruption.
+<*> 
+    
+? exit
+Type <return> to proceed, S to scroll future error messages,
+R to run without stopping, Q to run quietly,
+I to insert something, 
+H for help, X to quit.
+? X
+No pages of output.
diff --git a/src/pystan_icon_26x26.png b/src/pystan_icon_26x26.png
new file mode 100644 (file)
index 0000000..0d50c48
Binary files /dev/null and b/src/pystan_icon_26x26.png differ