Bien le bonjour !
Il y a quelques temps, je vous avais présenté le projet Humanized Opening Hours, un module Python qui permet de manipuler les champs opening_hours
d’OpenStreetMap. J’ai continué à avancer dessus, mais je me suis heurté à un problème que je n’arrive pas à résoudre.
Voici la logique de fonctionnement globale.
Un champ opening_hours
contient plusieurs objets Rule
, qui sont des indications d’heures d’ouvertures pour des dates données. Ces dates sont représentées par des objets RangeSelector
et les horaires d’ouverture par des objets TimeSpan
(stockés dans une liste accessible par l’attribut time_selectors
).
Par exemple, avec le champ Mo-Fr 08:00-19:00; Sa 09:30-12:00
…
- La première
Rule
estMo-Fr 08:00-19:00
, elle contient unRangeSelector
qui représenteMo-Fr
(du lundi au vendredi) et une liste qui contient unTimeSpan
qui représente08:00-19:00
(ouvert de 8h à 19h). - La seconde
Rule
estSa 09:30-12:00
, sonRangeSelector
représenteSa
et sonTimeSpan
représente09:30-12:00
.
Quand on donne une date au module pour récupérer les horaires d’ouverture pour celle-ci, il vérifie donc toutes les Rule
et voie si l’une d’elle correspond à la date donnée (le 9 juin 2018 correspondrait par exemple à Sa
ou à Jun Sa
(samedi en juin)). Une fois la Rule
correspondante trouvée, il peut savoir si l’endroit est ouvert à une heure donnée en vérifiant tous les TimeSpan
de la Rule
pour voir si l’heure donnée se trouve dans l’une de ces périodes.
Là où je bloque, c’est avec la méthode next_change()
, qui est sensée renvoyer le moment du prochain changement de statut (ouverture ou fermeture). Avec la plupart des champs, elle marche sans soucis, mais les problèmes arrivent quand une règle définie une ouverture continue sur plusieurs jours. Par exemple, Mo-Fr 00:00-24:00
définie que l’endroit est ouvert en continu du lundi au vendredi. Dans son état actuel, la méthode renvoie le prochain changement du jour actuel, c’est-à-dire que si l’on demande le prochain changement un lundi, on obtient "lundi à 24:00", et pas "vendredi à 24:00".
Voici la-dite fonction sur Github : https://github.com/rezemika/humanized_opening_hours/blob/e4317a6/humanized_opening_hours/main.py#L348-L403
Elle pourrait surement être améliorée, mais toutes les variantes que j’ai testé n’ont pas marché davantage, levant parfois une RecursionError
sur n’importe quel champ (ce qui ne devrait pouvoir arriver qu’avec le champ 24/7
, qui défini une ouverture illimitée, donc sans "prochain changement").
L’idée était donc de récupérer le prochain TimeSpan
, et de vérifier si le TimeSpan
suivant "démarrait" juste après ou non (00:00 étant juste après 23:59.999).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | def next_change(self, dt=None): """Gets the next opening status change. Parameters ---------- dt : datetime.datetime, optional The moment for which to check the opening. None default, meaning use the present time. Returns ------- datetime.datetime The datetime of the next change. """ if not dt: dt = datetime.datetime.now() days_offset, next_timespan = self._current_or_next_timespan(dt) new_time = dt.time() if days_offset == 0 else datetime.time.min new_dt = datetime.datetime.combine( dt.date() + datetime.timedelta(days_offset), new_time ) beginning_time, end_time = next_timespan.get_times( new_dt, self.solar_hours ) if dt < beginning_time: return beginning_time return end_time def _current_or_next_timespan(self, dt=None): if not dt: dt = datetime.datetime.now() current_rule = None i = 0 while current_rule is None: current_rule = self.get_current_rule( dt.date()+datetime.timedelta(i) ) if current_rule is None: i += 1 new_time = dt.time() if i == 0 else datetime.time.min new_dt = datetime.datetime.combine( dt.date() + datetime.timedelta(i), new_time ) for timespan in current_rule.time_selectors: beginning_time, end_time = timespan.get_times( new_dt.date(), self.solar_hours ) if new_dt < end_time: return (i, timespan) return self._current_or_next_timespan(new_dt) |
Quelqu’un aurait-il une idée d’une logique qui permettrait d’obtenir ce comportement ? (Je ne demande pas un code tout fait, je suis juste en gros manque d’idées pour progresser.)
Merci !