Seite 1 von 1

Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Sonntag 10. Mai 2020, 12:40
von benpython
Hallo Zusammen,

ich versuche mit dem sched Modul von Python3 eine Funktion jeden Tag zu einer bestimmten Uhrzeit auszuführen.
Ich habe eine Funktion sendEmail() diese soll immer um12 Uhr ausgeführt werden.

Die Dokumentation unter https://docs.python.org/3/library/sched.html habe ich mir durchgelesen aber ich werde nicht schlau daraus.
Folgendes habe ich probiert.

Code: Alles auswählen

import time
import sched


def sendEmail():
    print("sending Email")


t = time.strftime("12:00")  # String in Zeitformat umrechnen?!?!?
scheduler = sched.scheduler(time.time, time.sleep)
scheduler.enterabs(t, 1, sendEmail)
scheduler.run()
Fehlermeldung ist: "TypeError: '>' not supported between instances of 'str' and 'float'"

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Sonntag 10. Mai 2020, 16:51
von __blackjack__
@benpython: Du solltest `scheduler()` nichts übergeben. Beim zweiten Argument übergibst Du sowieso den Defaultwert, und beim ersten ersetzt Du `time.monotonic()` durch das schlechtere `time.time()`.

`t` ist eine Zeichenkette. Und zwar "12:00". `time.strftime()` ist also ziemlich sinnlos wenn man damit eine konstante Zeichenkette ”formatiert”. Das ist einfach ``t = "12:00"``. `scheduler` will aber einen Wert haben der zu `time.monotonic()`/`time.time()` passt. Also die Sekunden seit „Epoch“. Da ist auch das Datum enthalten. Wenn das also morgen um 12 Uhr ausgeführt werden soll, dann musst Du da auch morgen um 12 als Sekundenanzahl seit „Epoch“ angeben. Und wenn das jeden Tag passieren soll auch für jeden Tag ein Ereignis eintragen. Wobei man das dann natürlich auch immer dann eintragen könnte wenn ein Ereignis abgearbeitet wurde.

`t` und `scheduler` haben auf Modulebene nichts zu suchen, das sollte in einer Funktion stehen. Üblicherweise heisst die `main()`.

`t` ist kein guter Name weil der nichts aussagt.

Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Eventuell könnte auch das externe `apscheduler`-Modul interessant sein, wenn man anfängt sich mit `sched` selbst so etwas zu basteln.

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Sonntag 10. Mai 2020, 17:11
von noisefloor
Hallo,

warum lässt du das nicht einfach das Betriebssystem machen, unter Linux z.B. mit einer systemd Timer Unit?

Gruß, noisefloor

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Freitag 30. Oktober 2020, 15:10
von benpython
__blackjack__ hat geschrieben: Sonntag 10. Mai 2020, 16:51 Eventuell könnte auch das externe `apscheduler`-Modul interessant sein, wenn man anfängt sich mit `sched` selbst so etwas zu basteln.
Bin umgeschwenkt auf apscheduler. Nachfolgendes Skript soll jeden Tag um 23 Uhr DPDPrint.exe beenden, um 4 Uhr morgens wieder starten, einloggen und den Imprortservice starten.
Leider verpasst apscheduler die Ausführung des Jobs und ich weiss nicht warum das so ist. Finde dazu im Netz nicht viel. Und das was ich gefunden habe begreife ich einfach nicht.

Mein Skript soweit:

Code: Alles auswählen

import os, pyautogui, time
import pygetwindow as gw
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger

PASSWORD = os.environ.get('dpdPrintPassword') # set ur environment Variable with os.environ['dpdPrintPassword'] = 'yoursecretpassword'

def killDPD():
    os.system('"taskkill /f /im DPDPrint.exe"')
    os.system('"taskkill /f /im DPDPrint.exe"')
    os.system('"taskkill /f /im DPDPrint.exe"')
    
def restartDPD():
    windows = gw.getAllTitles()
    for window in windows:
        window.minimize()
    os.system('"C:\Program Files (x86)\DPD\DPDPrint\DPDPrint.exe"')
    time.sleep(60) # Wait till login is loaded
    dpdprint = gw.getWindowsWithTitle('DPD Print')[0]
    dpdprint.activate()
    pyautogui.click(859, 496)
    time.sleep(1)
    pyautogui.write(PASSWORD, interval=0.05) 
    time.sleep(1)
    pyautogui.click(735, 527)
    time.sleep(60)

    # START IMPORTSERVICE
    dpdprint = gw.getWindowsWithTitle('DPD Print')[0]
    dpdprint.activate()
    time.sleep(2)
    if not dpdprint.isMaximized:
        dpdprint.maximize()
    time.sleep(2)
    pyautogui.click(1355, 127)

scheduler = BlockingScheduler(timezone="Europe/Berlin")
scheduler.add_job(killDPD, CronTrigger(hour=23))
scheduler.add_job(restartDPD, CronTrigger(hour=4))
scheduler.print_jobs()
scheduler.start()
Vielleicht kennt sich jemand von euch besser mit apscheduler aus und kann Hilfestellung geben.

Im Voraus vielen Dank!

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Freitag 30. Oktober 2020, 15:18
von Sirius3
os.system sollte man aus gutem Grund nicht verwenden, und so wie Du es verwendest, sieht es sehr falsch aus.

Woher weißt Du, dass apscheduler die Ausführung des Jobs verpasst?
Wie startest Du und wo läut das ganze?

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Freitag 30. Oktober 2020, 16:01
von __blackjack__
@benpython: Noch mal der Hinweis auf die Namenschreibweise: klein_mit_unterstrichen.

Der Code auf Modulebene sollte in einer Hauptfunktion verschwinden.

Den Pfad zur EXE würde ich als Konstante definieren, denn der Pfad beziehungsweise der Dateiname wird ja mehrfach im Code benötigt und sollte nicht mehrfach dort stehen.

`os.system()` sollte man nicht mehr verwenden. Zum starten von externen Prozessen gibt es das `subprocess`-Modul. Damit kann man die Prozesse dann unter anderem auch ohne eine unnötige zusätzliche Shell ausführen.

Funktioniert das beenden des/der Prozesse(s) so überhaupt? Die " sagen der Shell ja, dass das *ein* Element ist, also dass das Programm ``taskkill /f /im DPDPrint.exe`` heisst und keine Argumente bekommt. Das Programm heisst doch aber nur ``taskkill``und der Rest sind die Argumente. Warum wird das dreimal ausgeführt?

Ich würde an der Stelle auch eher das `psutil`-Modul verwenden, statt ein externes Programm zu starten.

Funktioniert das starten grundsätzlich nicht oder nur einmal? Denn der Code funktioniert so wie er da steht ja nur wenn das Starten von ``DPDPrint.exe`` nicht blockiert.

Code: Alles auswählen

#!/usr/bin/env python3
import os
import subprocess
import time
from pathlib import Path

import pyautogui
import pygetwindow
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger

#
# Set your environment variable with
# ``os.environ["dpdPrintPassword"] = "yoursecretpassword"``.
#
PASSWORD = os.environ.get("dpdPrintPassword")
DPD_EXE_PATH = Path(R"C:\Program Files (x86)\DPD\DPDPrint\DPDPrint.exe")
WINDOW_TITLE = "DPD Print"


def kill_dpd():
    #
    # TODO Use `psutil` instead of starting an external program.
    #
    for _ in range(3):
        subprocess.run(
            ["taskkill", "/f", "/im", DPD_EXE_PATH.name], check=True
        )


def activate_first_window(window_title):
    window = pygetwindow.getWindowsWithTitle(window_title)[0]
    window.activate()
    return window


def restart_dpd():
    for window in pygetwindow.getAllTitles():
        window.minimize()
    #
    # FIXME Is this blocking?
    #
    subprocess.run([str(DPD_EXE_PATH)], check=True)

    time.sleep(60)  # Wait till login is loaded.
    activate_first_window(WINDOW_TITLE)
    pyautogui.click(859, 496)  # TODO Document what this position means.
    time.sleep(1)
    pyautogui.write(PASSWORD, interval=0.05)
    time.sleep(1)
    pyautogui.click(735, 527)  # TODO Document what this position means.
    time.sleep(60)
    #
    # Start import service.
    #
    dpd_print_window = activate_first_window(WINDOW_TITLE)
    time.sleep(2)
    if not dpd_print_window.isMaximized:
        dpd_print_window.maximize()
    time.sleep(2)
    pyautogui.click(1355, 127)  # TODO Document what this position means.


def main():
    scheduler = BlockingScheduler(timezone="Europe/Berlin")
    scheduler.add_job(kill_dpd, CronTrigger(hour=23))
    scheduler.add_job(restart_dpd, CronTrigger(hour=4))
    scheduler.print_jobs()
    scheduler.start()


if __name__ == "__main__":
    main()

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Samstag 14. November 2020, 09:15
von benpython
Vielen Dank für eure Hilfe. Ich habe den Code jetzt nochmal umgeschrieben. Vielen Dank blackjack für den verbesserten Code.
Leider funktioniert das Programm so immer noch nicht. Genauer gesagt funktioniert das beenden von DPDPrint.exe nicht.
Das hab ich jetzt auf euer anraten mit psutil gelöst.

Alle Funktionen funktionieren einzeln. Aber wenn das Programm läuft, wird DPDPrint.exe nicht beendet und wenn das Script dann versucht die DPDPrint.exe wieder zu starten läuft ja bereits eine instanz der Anwendung.
Ich führe das Script in Powershell aus.
Bild

Code: Alles auswählen

import os
import subprocess
import time
from pathlib import Path
import psutil

import pyautogui
import pygetwindow as gw
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger

#
# Set your environment variable with
# ``os.environ["dpdPrintPassword"] = "yoursecretpassword"``.
#
PASSWORD = os.environ.get("dpdPrintPassword")
DPD_EXE_PATH = Path(R"C:\Program Files (x86)\DPD\DPDPrint\DPDPrint.exe")
WINDOW_TITLE = "DPD Print"

def minimize_windows():
    windows = gw.getAllTitles()
    realWindows = []
    for window in windows:
        if window != "":
            realWindows.append(window)
    for window in realWindows:
        cWindow = gw.getWindowsWithTitle(window)[0]
        cWindow.minimize()

def kill_dpd():
    PROCNAME = 'DPDPrint.exe'
    for proc in psutil.process_iter():
        try:
            if proc.name() == PROCNAME:
                p = psutil.Process(proc.pid)
                p.kill()
        except Exception as e:
            print(e)
            pass


def activate_first_window(window_title):
    window = gw.getWindowsWithTitle(window_title)[0]
    window.activate()
    return window


def restart_dpd():
    minimize_windows()
    subprocess.run([str(DPD_EXE_PATH)], check=True)

    time.sleep(60)  # Wait till login is loaded.
    activate_first_window(WINDOW_TITLE)
    pyautogui.click(859, 496)  # Click into password field.
    time.sleep(1)
    pyautogui.write(PASSWORD, interval=0.05) # Write password to the field
    time.sleep(1)
    pyautogui.click(735, 527)  # Click on submit button.
    time.sleep(60)
    #
    # Start import service.
    #
    dpd_print_window = activate_first_window(WINDOW_TITLE)
    time.sleep(2)
    if not dpd_print_window.isMaximized:
        dpd_print_window.maximize()
    time.sleep(2)
    pyautogui.click(1355, 127)  # Cick the button in the top right corner to start the DPD Print importservice.


def main():
    scheduler = BlockingScheduler(timezone="Europe/Berlin")
    scheduler.add_job(kill_dpd, CronTrigger(hour=23))
    scheduler.add_job(restart_dpd, CronTrigger(hour=4))
    scheduler.print_jobs()
    scheduler.start()


if __name__ == "__main__":
    main()
    
Meine Fragen:
@__blackjack__ : Was macht die Zeile:

Code: Alles auswählen

 if __name__ == "__main__": main() 
Muss ich die Zeit in den CronTrigger anders eintragen? Oder was ist mein Fehler.

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Montag 16. November 2020, 08:58
von benpython
Jetzt hab ich noch ein Problem festgestellt. Wenn das Programm beendet ist und
subprocess.run([str(DPD_EXE_PATH)], check=True) die exe wieder starten will meldet er jedesmal:
CalledProcessError: Command '['C:\\Program Files (x86)\\DPD\\DPDPrint\\DPDPrint.exe']' returned non-zero exit status 3.

Re: Eine Funktion jeden Tag zu einer bestimmten Uhrzeit ausführen mit dem Python3 sched Modul

Verfasst: Freitag 20. November 2020, 12:46
von __blackjack__
@benpython: Gibt es denn Dokumentation über die Exit-Codes von DPDPrint.exe was beispielsweise 6 und 3 bedeuten?