source.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. from class_xml import XML
  2. import config
  3. import datetime
  4. from itertools import chain
  5. # SOURCE CLASSES
  6. class Source:
  7. pass
  8. class SourceProvider:
  9. def dic_of_name(self):
  10. """Returns a dictionnary mapping ids to name (for find.py)"""
  11. return []
  12. def dic_of_positions(self):
  13. """Returns a disctionnary mapping ids to position (for geocoding.py)"""
  14. return []
  15. def sources_of_ids(self, ids):
  16. """Returns a generator of Source these ids"""
  17. return []
  18. class Source_ratp(Source):
  19. def __init__(self, ident, status, message):
  20. self.source = 'ratp_trafic'
  21. self.id = ident
  22. self.status = status
  23. self.message = message
  24. def problem(self):
  25. return self.status != 'normal'
  26. class SourceProvider_ratp(SourceProvider):
  27. def __init__(self):
  28. self.names = None
  29. self.positions = None
  30. def get_names(self):
  31. if not self.names:
  32. print('Téléchargement de la liste des lignes ratp...')
  33. xml = XML(url='http://www.ratp.fr/meteo/', lang='html')
  34. self.names = {tag['id']: tag['id'].replace('_', ' ') for tag in xml.data.select('.encadre_ligne')}
  35. return self.names
  36. def dic_of_name(self):
  37. return get_names()
  38. def dic_of_positions(self):
  39. return {} # TODO API ratp
  40. def sources_of_ids(self, ids):
  41. for tag in XML(url="http://www.ratp.fr/meteo/", lang="html").data.select('div.encadre_ligne'):
  42. if tag['id'] in ids:
  43. yield Source_ratp(tag['id'], tag.img['alt'],\
  44. tag['id'].replace('_', ' ') + ' : ' + tag.select('span.perturb_message')[0].string)
  45. class Source_jcdecaux_vls(Source):
  46. def __init__(self, ident, nom, timestamp, status):
  47. self.source = 'jcdecaux_vls'
  48. self.id = ident
  49. self.status = status # TODO dans l'API pour 1 station il semble que c'est toujours OPEN :-(
  50. self.date = datetime.datetime.fromtimestamp(int(timestamp)/1000).strftime('à %Hh%M le %d/%m')
  51. if status != "OPEN":
  52. self.message = 'Station vélo ' + nom.lower() + ' ' + self.date + ' : fermée !'
  53. else:
  54. self.message = None
  55. def problem(self):
  56. return self.status != "OPEN"
  57. class Source_jcdecaux_vls_full(Source_jcdecaux_vls):
  58. def __init__(self, ident, nom, timestamp, places, status):
  59. super(Source_jcdecaux_vls_full, self).__init__(ident, nom, timestamp, status)
  60. self.id += "_full"
  61. self.places = int(places)
  62. if not self.message:
  63. self.message = 'Station vélo ' + nom.lower() + ' ' + self.date + ' : '
  64. if self.places == 0:
  65. self.message += 'plus de place !'
  66. elif self.places == 1:
  67. self.message += 'plus qu\'une place !'
  68. else:
  69. self.message += 'plus que ' + places + ' places disponibles !'
  70. def problem(self):
  71. return super(Source_jcdecaux_vls_full, self).problem() or self.places <= config.sources_params['jcdecaux_vls']['limit_full']
  72. class Source_jcdecaux_vls_empty(Source_jcdecaux_vls):
  73. def __init__(self, ident, nom, timestamp, bikes, status):
  74. super(Source_jcdecaux_vls_empty, self).__init__(ident, nom, timestamp, status)
  75. self.id += "_empty"
  76. self.bikes = int(bikes)
  77. if not self.message:
  78. self.message = 'Station vélo ' + nom.lower() + ' ' + self.date + ' : '
  79. if self.bikes == 0:
  80. self.message += 'plus de vélo !'
  81. elif self.bikes == 1:
  82. self.message += 'plus qu\'un vélo !'
  83. else:
  84. self.message += 'plus que ' + bikes + ' vélos !'
  85. def problem(self):
  86. return super(Source_jcdecaux_vls_empty, self).problem() or self.bikes <= config.sources_params['jcdecaux_vls']['limit_empty']
  87. class SourceProvider_jcdecaux_vls(SourceProvider):
  88. def __init__(self):
  89. pass
  90. def dic_of_name(self):
  91. return {} # TODO
  92. def dic_of_positions(self):
  93. return {} # TODO
  94. def sources_of_ids(self, ids):
  95. ids_set = set(map(lambda s : s.rsplit('_', 1)[0], ids))
  96. for station in ids_set:
  97. (contract, number) = list(station.split('_'))
  98. xml = XML(url="https://api.jcdecaux.com/vls/v1/stations/" + number + "?contract=" + contract + "&apiKey="+config.api_key['jcdecaux_vls'], lang="json")
  99. tag = xml.data.json
  100. if contract + '_' + number + '_full' in ids:
  101. yield Source_jcdecaux_vls_full(contract + '_' + number, tag.find('name').string, tag.last_update.string, tag.available_bike_stands.string, tag.status.string)
  102. if contract + '_' + number + '_empty' in ids:
  103. yield Source_jcdecaux_vls_empty(contract + '_' + number, tag.find('name').string, tag.last_update.string, tag.available_bikes.string, tag.status.string)
  104. class SourceProvider_transilien(SourceProvider):
  105. class Source_transilien(Source):
  106. def __init__(self, ident, message):
  107. self.source = 'transilien'
  108. self.id = ident
  109. self.message = message
  110. def problem(self):
  111. return self.message != 'Trafic normal'
  112. def __init__(self):
  113. self.names = None
  114. self.positions = None
  115. def get_names(self):
  116. if not self.names:
  117. print('Téléchargement de la liste des lignes ratp...')
  118. xml = XML(url='http://www.ratp.fr/meteo/', lang='html')
  119. self.names = {tag['id']: tag['id'].replace('_', ' ') for tag in xml.data.select('.encadre_ligne')}
  120. return self.names
  121. def dic_of_name(self):
  122. return get_names()
  123. def dic_of_positions(self):
  124. return {} # TODO API ratp
  125. def sources_of_ids(self, ids):
  126. xml = XML(url="http://www.transilien.com/info-trafic/temps-reel", lang="html").data
  127. container = xml.select('div.b_info_trafic')[0]
  128. for line in container.find_all('div', recursive=False):
  129. id = line.select('.picto-transport')[1].get_text()
  130. if id in ids:
  131. message = ""
  132. for c in line.select_one('.title').children:
  133. if c.name: # a tag
  134. if 'picto-transport' not in c.attrs.get('class', ''):
  135. message += c.get_text()
  136. else: # a string
  137. message += c
  138. for det in line.select('.item-disruption'):
  139. message += det.get_text()
  140. message = " ".join(message.split()) # delete multiple spaces
  141. yield self.Source_transilien(id, message)
  142. # interface functions
  143. def from_location(location):
  144. """return a list of source ids useful for location
  145. TODO : for the moment returns the whole config.sources"""
  146. return config.sources
  147. sp_ratp = SourceProvider_ratp()
  148. sp_jcdecaux_vls = SourceProvider_jcdecaux_vls()
  149. sp_transilien = SourceProvider_transilien()
  150. def gen_sources(ids):
  151. return chain(sp_ratp.sources_of_ids(ids.get('ratp_trafic', [])),\
  152. sp_transilien.sources_of_ids(ids.get('transilien', [])),\
  153. sp_jcdecaux_vls.sources_of_ids(ids.get('jcdecaux_vls', [])))