Comment structurer cette fonction pour éviter une erreur de récursion ?

a marqué ce sujet comme résolu.
Auteur du sujet

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 est Mo-Fr 08:00-19:00, elle contient un RangeSelector qui représente Mo-Fr (du lundi au vendredi) et une liste qui contient un TimeSpan qui représente 08:00-19:00 (ouvert de 8h à 19h).
  • La seconde Rule est Sa 09:30-12:00, son RangeSelector représente Sa et son TimeSpan représente 09: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 ! :)

"Les accidents dans un système doivent se produire, mais il n’est pas obligatoire qu’ils produisent pour vous et moi." Laurence Gonzales - Deep Survival

+0 -0
Auteur du sujet

ne connaissant pas ton projet, je vais tenter de te proposer une logique pour "parser" ce champ

artragis

Merci pour ta réponse ! Cependant, ce n’est pas le parsing du champ (confié au module Lark) qui pose problème mais plutôt l’utilisation des données résultant du parsing (le champ "brut" étant transformé en objets Python Rules). :)

"Les accidents dans un système doivent se produire, mais il n’est pas obligatoire qu’ils produisent pour vous et moi." Laurence Gonzales - Deep Survival

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte