TL;DR: Potrzebuję wytłumaczyć Pythonowi bardziej złożony problem i mega się motam. Ściana tekstu przedstawia moją drogę do próby znalezienia rozwiązania. Problem polega na tym, żeby program sprawdzał czy nie został przekroczony czas jazdy 4,5h oraz czas jazdy i pracy 6h. Czas kasuje się po przerwie conajmniej 45 minutowej, która może być podzielona na pierszą część co najmniej 15 minut i drugą co najmniej 30 minut. Ten podział jest dla mnie najtrudniejszy. Na końcu w dziale ‚Jednak i tak nie działa!’ znajdziesz stan na tą chwilę.

UWAGA: Dzięki każdemu, kto zechciał mi pomóc. Okazało się, że błądziłem mocno a rozwiązanie było dużo prostsze. W poniższym wpisie się tego nie dowiesz, ale możesz pobawić się w Sherlocka i spróbować zgadnąć co było nie tak. Rozwiązanie tego problemu znajdziesz tutaj.

Myślę, że teraz nastał kolejny krok. O ile do tej pory uczyłem się przede wszystkim składni no i może podstawowych funkcjonalności Pythona o tyle po raz pierwszy stanąłem przed konkretnym problemem do rozwiązania.

Bo oto teraz nie tylko muszę zastosować składnię, ale również za jej pomocą rozwiązać jakiś konkretny i nieco bardziej złożony problem.

Kierowca może jechać łącznie bez przerwy 4,5h. Przed tym czasem musi zrobić 45 minut przerwy, która może być podzielona na dwie części z czego pierwsza przerwa musi mieć minimum 15 minut a druga minimum 30 minut.

Dodatkowo przed łącznym czasem jazdy i pracy, który wynosi 6 godzin też musi być przerwa. No i weź i to Pythonowi wytłumacz.

Przypomnijmy czym są wpisy.

Każdy wpis to klasa zawierająca informację o wybranym trybie wpisu (D:jazda, W:praca, P:dyspozycyjność i R:przerwa) oraz jego czasu. Jak można się domyślić informacje te możemy uzyskać za pomocą x.get_mode() oraz x.get_value(). No i zaczyna się zabawa.

Wyboista i kręta droga do rozwiązania

Najpierw postanowiłem zmierzyć się ze sprawdzeniem czy była przerwa minimum 45 min przed upływem 4,5 godziny.

UWAGA: Dla ułatwienia i szybkości sprawdzenia czy wszystko działa, na razie czas jazdy po którym powinien wyświetlić się komunikat jest ustawiony na 120 sekund czyli 2 min a przerwa nie musi być minium 45min, ważne, żeby w ogóle była.

Kod wyglądał tak, ale to nie działało, tak jak trzeba :/

def was_break(self):
    global info_list
    driving_time = 0
    info = ''
    info_list = []

    for x in self.records:
        print(x.get_mode())

        if x.get_mode() == 'D':
            driving_time += x.get_value()

        if x.get_mode() == 'R' or driving_time > 120:
            if driving_time > 120:

                info = "Too much driving"

                info_list.append(info)

            return info+str(driving_time)+str(info_list)
    return info+str(driving_time)

Nie macie pojęcia ile móżdżenia co jest nie tak to ode mnie wymagało.

Jaki był problem?

Program powinien przenalizować ciąg wpisów (czyli listę self.records) i zobaczyć w którym miejscu przepisy zostały naruszone. Jeśli zostały naruszone kilka razy to powinien o tym dać znać.

Natomiast program dawał znać o złamaniu przepisu w momencie, kiedy dodawało się za dużo jazdy natomiast po dodaniu odpowiedniej przerwy przestawał widzieć problem.

Problemem głównie był return (ten w „ifie”), który zwracał to co mu wpisałem jeśli wszystkie warunki zostały spełnione i do widzenia. ‚Zrobiłem swoją robotę, czego jeszcze ode mnie chcesz człowieku?’.

Zrozumieć co się dzieje

Druga sprawa, że za każdym razem kiedy pojawiło się złamanie przepisów co prawda dodawał to na listę, ale potem następował return, robota była zrobiona więc przy kolejnym użyciu pętli program wracał do punktu wyjścia, gdzie przecież info_list = [].

Kluczowe jest, żeby zrozumieć co się w ogóle tutaj dzieje. Wystarczyło, że usunąłem return z „ifa” i zacząłem pomału wychodzić na prostą. Trzeba było jednak w to miejsce wrzucić driving_time = 0, żeby po spełnieniu wszystkich warunków (czyli pojawieniu się przerwy) łączny czas jazdy wyzerować, aby można było zacząć sprawdzanie od nowa czyli od kolejnej przerwy.

def was_break(self):
    global info_list
    driving_time = 0
    info = ''
    info_list = []

    for x in self.records:
        print(x.get_mode())

        if x.get_mode() == 'D':
            driving_time += x.get_value()

        if x.get_mode() == 'R' or driving_time > 120:
            if driving_time > 120:

                info = "Too much driving"

                info_list.append(info)

            driving_time = 0
    return info_list

Return właściwy też musiał się zmienić. Nie ma sensu wyświetlać niczego innego niż info_list, bo tylko ona jest w stanie się zmieniać kilkukrotnie (elementy są dodawane lub odejmowane w zależności czy warunki zostały spełnione).

Uwaga na pułapki

Natomiast return info+str(driving_time) jest zupełnie bez sensu i wprowadzało tylko zamieszanie i zbijało mnie z tropu, bo w momencie, kiedy został spełniony warunek (czyli pojawiała się przerwa lub przekroczony był czas jazdy) info oraz driving_time zostały ustalone i pojawiały się od tego momentu zawsze takie same. Przez co myślałem, że w ogóle nic nie działa.

Potworek się rodzi

Strasznie zamotane. I wygląda nadal paskudnie, ale coś zaczęło pomału wychodzić.

Po poprawkach, sprawdzeniu dodaniu tego, odjęciu tamtego. Powstał ten „działający” potworek:

def was_break(self):
    driving_time = 0
    time_before_break = 0
    info_list = []
    first_break = False
    break_first = False

    for x in self.records:
        if x.get_mode() == 'D':
            driving_time += x.get_value()

        if (x.get_mode() == 'R' and (1800 <= x.get_value() < 2700)):
            first_break = True

        if (x.get_mode() == 'R' and x.get_value() <= 2700) or (driving_time > 16200)\
                or ((x.get_mode() == 'R' and (900 <= x.get_value() < 1800)) and (first_break is True)):  # 4,5h:             print(first_break)             if driving_time > 16200:

                info = "Break after 4,5h driving needed"

                info_list.append(info)
            driving_time = 0
            first_break = False

    for y in self.records:
        #print(y.get_mode())
        if y.get_mode() == 'D' or y.get_mode() == 'W':
            time_before_break += y.get_value()
        print(time_before_break)

        if (y.get_mode() == 'R' and (1800 <= y.get_value() < 2700)):              break_first = True         if (y.get_mode() == 'R' and y.get_value() >= 2700) or time_before_break > 21600\
                or ((y.get_mode() == 'R' and (900 <= y.get_value() < 1800)) and (break_first is True)):             if time_before_break > 21600:
                info_break = "Break after 6h work needed"
                info_list.append(info_break)
            time_before_break = 0

    if not info_list:
        return "No infringements found"

    else:
        all_infringements = ''
        for x in set(info_list):
            all_infringements += "{0}: {1}\n".format(x,info_list.count(x))
        return "Infringements:\n"+all_infringements

Przeanalizujmy po kolei co tutaj tak właściwie się dzieje.

Przy wywołaniu tej metody dla każdego wpisu w liście wpisów for x in self.records: sprawdzamy po kolei:

  1. Czy wpis jest jazdą, jeśli tak to dodajemy jego wartość do driving_time,
  2. Czy wpis jest przerwą większą niż 15 minut, ale mniejszą niż 30 minut, jeśli tak to zmieniamy first_brak na True. Jest to nam potrzebne w trzecim i ostatnim sprawdzeniu.
  3. Czy wpis jest przerwą 45 minutową lub został przekroczony czas jazdy (to jest potrzebne po to, aby pierwszym wpisem nie musiała być przerwa!) lub czy jest dzielona przerwa conajmniej 15 minut i conajmniej 30 minut.No i teraz się zaczyna właściwy numer.Jeśli któryś z tych warunków jest prawdą sprawdzamy czy czas jazdy został przekroczony. Jeśli tak to wyświetla się informacja.Niezależnie od powyższego zawsze zerujemy driving_time oraz ustawiamy spowrotem first_break na False. Niezależnie dlatego, że ważne jest, że mamy przerwę i należy zacząć liczyć od nowa nawet jeśli przepisy nie zostały złamane.

Potworek rośnie

Później mamy pętlę for y in self.records: robiącą właściwie to samo tyle tylko, że tutaj patrzy na łączny czas pracy i jazdy, czy nie zostało przekroczone 6 godzin. Postanowiłem to rozdzielić, bo pomimo tego, że prawie całkowicie te zależności się pokrywają, to jednak nie zawsze.

Tym wyjątkiem jest punkt 3. W pierwszej pętli jedną z możliwości jest przekroczenie czasu jazdy 4,5h. Gdyby było tylko first_break w tym momencie zostałoby ono przerobione na False co by bylo ok dla sprawdzenia czy przekroczony jest czas jazdy 4,5h, ale zupełnie nie ok dla zobaczenia czy zostało przekroczone 6h pracy i jazdy.

Już się witałem z gąską

Już myślałem, że wszystko jest ok, aż tu nagle nałem taki przypadek:

Jazda 4:30, Przerwa 00:15, Jazda 00:01, Przerwa 00:30, Jazda 4:30

W tym przypadku mamy przekroczony czas jazdy, bo zanim była przerwa 00:15 + 00:30 było łącznie 4:31 jazdy. Co ciekawe po usunięciu pierwszej jazdy 4:30 nadal był wyświetlny błąd! Dlaczego?

Okazało się, że program sprawdzał czy jest przerwa 15 min, jeśli tak to leciał dalej aż napotkał na przerwę 30 min. Wtedy miał spełnione wszystkie warunki dla przerwy 15 min i kasował czas w miejscu piętnastominutowej przerwy. Mówiąc inaczej rozumiał to tak, że skoro jest przerwa 00:15 i 00:30 to liczę czas jazdy od pojawienia się 00:15.

Ładnie to się przede mną ukryło, chociaż podejrzewałem, że może być coś nie halo skoro miałem sprawdzanie przerwy 15 min jako tej głównej, ale inaczej w ogóle nie chciało śmigać. Masakra.

Uprościć potworka

Znów trzeba wszystko przebudować. Tym razem postanowiłem, że nie będę starał się za wszelką cenę upychać wszystkiego w jednym „ifie”. Paradoksalnie wyszło mniej kodu i to bardziej sensownego.

Powstało więc to:

def was_break(self):
    driving_time = 0
    time_before_break = 0
    info_list = []
    first_break = False

    for x in self.records:
        if x.get_mode() == 'D':
            driving_time += x.get_value()

        if x.get_mode() == 'D' or x.get_mode() == 'W':
            time_before_break += x.get_value()

        if x.get_mode() == 'R' and (1800 <= x.get_value() < 2700):

            for i in self.records:
                if i.get_mode() == 'R' and (900 <= i.get_value() < 1800):                     first_break = True             if first_break is True:                 if driving_time > 16200:
                    info = "Break after 4,5h driving needed"
                    info_list.append(info)
                print(driving_time)
                driving_time = 0

                if time_before_break > 21600:
                    info_break = "Break after 6h work needed"
                    info_list.append(info_break)
                print(time_before_break)
                time_before_break = 0

        if (x.get_mode() == 'R' and x.get_value() >= 2700):
            if driving_time > 16200:
                info = "Break after 4,5h driving needed"
                info_list.append(info)
            driving_time = 0

            if time_before_break > 21600:
                info_break = "Break after 6h work needed"
                info_list.append(info_break)
            time_before_break = 0

        if driving_time > 16200:
            info = "Break after 4,5h driving needed"
            info_list.append(info)
            driving_time = 0

        if time_before_break > 21600:
            info_break = "Break after 6h work needed"
            info_list.append(info_break)
            time_before_break = 0

    if not info_list:
        return "No infringements found"

    else:
        all_infringements = ''
        for x in set(info_list):
            all_infringements += "{0}: {1}\n".format(x,info_list.count(x))
        return "Infringements:\n"+all_infringements

Miniej a  więcej

Przede wszystkim zniknęła druga pętla, która naprawdę robiła praktycznie to samo a przy okazji bardziej motała sytuację. Jedyna różnica polegała na tym, że w momencie kiedy nie ma przerwy na początku wpisów różne są warunki przekroczenia czasu jazdy (4,5h) i pracy i jazdy (6h).

Pozstanowiłem więc zrobić dwa osobne „ify” sprawdzające konkretnie te rzeczy, czyli if driving_time > 16200: (4,5h) oraz if time_before_break > 21600: (6h).

Szybkie wyjaśnienie: Cała ta metoda opiera się na tym, żeby liczyć i sprawdzać od pojawienia się przepisowej przerwy. Natomiast użytkownik może zacząć wprowadzać wpisy od jazdy i wtedy przestaje to działać tak jak trzeba. Z czasem mam zamiar wprowadzić przerwę wszędzie tam, gdzie użytkownik nic nie wprowadził wtedy te dwa „ify” znikną.

Dzięki temu if (x.get_mode() == ‚R’ and x.get_value() >= 2700): (czyli po wystąpieniu przerwy 45 minut) może być wspólny dla sprawdzenia driving_time i time_before_break (sprawdzane są osobno, ale w jednym „ifie”), to samo tyczy się if x.get_mode() == ‚R’ and (1800 <= x.get_value() < 2700): (dzielonej przerwy na 15 i 30 minut). Ten „if” też może być wspólny.

Podzielmy tą przerwę tak jak trzeba

Ten „if” jest zresztą kluczowy w tym nowym rozwiązaniu. Co tam się dzieje po kolei?

...
if x.get_mode() == 'R' and (1800 <= x.get_value() < 2700):

    for i in self.records:
        if i.get_mode() == 'R' and (900 <= i.get_value() < 1800):         first_break = True     if first_break is True:         if driving_time > 16200:
            info = "Break after 4,5h driving needed"
            info_list.append(info)

        driving_time = 0

        if time_before_break > 21600:
            info_break = "Break after 6h work needed"
            info_list.append(info_break)

        time_before_break = 0

On dotyczy sytuacji w której jest przerwa przynajmniej 30, ale mniej niż 45 minut. Wtedy pojawia się pętla, która sprawdza czy wcześniej była przerwa przynajmniej 15 minut, ale nie większa niż 30 minut.

Małe wyjaśnienie: Ważne są te konkretne wartości, żeby rodzaje przerw na siebie nie nachodziły. Gdyby zrobić tylko np. przerwa powyżej 15 minut to przy pojawieniu się przerwy 45 minutowej uruchamiane by były wszystkie „ify” dotyczące przerw i by był bałagan.

Jeśli była taka przerwa, to wtedy ustawiamy first_break na True. To jest ważny moment, bo potem mamy „ifa”, który w przypadku gdy była ta przerwa zaczyna robić dopiero robotę.

Najpierw sprawdza czy czas jazdy był przekroczony i robi co ma robić z tym po czym zeruje driving time i analogicznie dzieje się dla sprawdzenia łącznego czasu jazdy i pracy.

Jednak i tak nie działa!

Na tą chwilę wydawało mi się, że w końcu działa to tak jakbym tego oczekiwał.

Niestety. Jaki pojawia się problem? Ok mamy pół godzinną przerwę, program zaczyna sprawdzać czy była wcześniej 15 minutowa przerwa, ale nie ogarnia, że ma sprawdzić tylko od ostatniej przerwy. Jeżeli gdziekolwiek pojawiła się 15 minutowa przerwa, to potem wystarczy, że wszystkie inne będą przynajmniej 30 minutowe, żeby program nie widział problemu. Eh!

Próbowałem różnych opcji i nigdy nie jest tak jakbym chciał. Najbardziej zbliżonym rozwiązaniem jest to, że program patrzy czy była przerwa 15 minut przed przerwą 30 minutową (lub odwrotnie co też jest źle), ale jeśli tak się zdarzy to zaczyna liczyć czas od tej piętnastki zamiast od trzydziestki.

Screenshot from 2017-05-11 13-59-09croped

Do 5tego wpisu wszystko działa jak trzeba. Potem 9ty wpis powinien być potraktowany jako pełna przerwa, bo wcześniej w pozycji 7 była przerwa 15 minut. Tak się jednak nie dzieje. Widzi, że warunki zostały spełnione, ale zamiast skasować czas od przerwy 30tki robi to od 15tki. A w takim układzie faktycznie czas jazdy został przekroczony o pół godziny. Kod wygląda tak:


def was_break(self):
    driving_time = 0
    time_before_break = 0
    info_list = []
    first_break = False
    second_break = False

    for x in self.records:
        if x.get_mode() == 'D':
            driving_time += x.get_value()

        if x.get_mode() == 'D' or x.get_mode() == 'W':
            time_before_break += x.get_value()

        if x.get_mode() == 'R' and (900 <= x.get_value() < 1800):
            first_break = True

        if x.get_mode() == 'R' and (1800 <= x.get_value() < 2700):
            second_break = True

        if (x.get_mode() == 'R' and x.get_value() >= 2700) or (second_break and first_break is True): 

            if driving_time > 16200:

                info = "Break after 4,5h driving needed"

                info_list.append(info)
            driving_time = 0

            if time_before_break > 21600:
                info_break = "Break after 6h work needed"
                info_list.append(info_break)
            time_before_break = 0
            first_break = False
            second_break = False

        if driving_time > 16200:

            info = "Break after 4,5h driving needed"
            info_list.append(info)
            driving_time = 0

        if time_before_break > 21600:
            info_break = "Break after 6h work needed"
            info_list.append(info_break)
            time_before_break = 0

    if not info_list:
        return "No infringements found"

    else:
        all_infringements = ''
        for x in set(info_list):
            all_infringements += "{0}: {1}\n".format(x,info_list.count(x))
        return "Infringements:\n"+all_infringements

Czuję, że nie wybrnę z tego sam i bardzo proszę o wszelkie wskazówki jak się za to zabrać. Z góry dzięki.

3 uwagi do wpisu “Cierpliwości. Ale ile można?

  1. Nie jestem pewien, czy dobrze zrozumialem problem, ale czy to nie powinno dzialac dobrze?


    def was_break(self):
    driving_time = 0
    time_break = 0
    info_list = []
    short_break = False
    long_break = False
    max_time = 16200
    for record in self.records:
    if record.get_mode() == 'D':
    driving_time += record.get_value()
    if x.get_mode() == 'R'
    time_break += x.get_value()
    if 1800 <= time_break and not long_break
    long_break = True
    time_break -= 1800
    max_time += 1800
    elif 900 <= time_break and not short_break
    short_break = True
    time_break -= 900
    max_time += 900
    #podmiana short na long
    if 1800 <= time_break + 900 and short_break and not long_break
    short_break = false
    long_break = true
    time_break -= 900
    max_time += 900
    if driving_time > max_time:
    info = "Break needed!"
    info_list.append(info)
    return info_list

    view raw

    test_break.py

    hosted with ❤ by GitHub

    Polubione przez 1 osoba

    1. Hej, wielkie dzięki za komentarz. Wychodzi na to, że moim podstawowym błędem było to, że gdzieś po drodze zamieniłem dodawanie recordów do self.records z append (czyli na koniec) na index 0 (czyli początek) i pętla robiła się jakby od drugiej strony czego w ogóle nie brałem pod uwagę. Na razie jeszcze sprawdzam temat. Przyjrzę się też na spokojnie twojemu kodowi, bo może to być przydatne w określaniu pierwszej i drugiej przerwy. Wielkie dzięki za pomoc.

      Polubienie

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s