#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Digital Data - Zeiterfassung # ---------------------------------------------------------------------------- # Python-Script zur Buchung von Zeiten via QR/Barcode-Scanner inkl. Anbindung # an die Mesonic WinLine. Per tkinter-Bibliothek wird eine GUI erzeugt. # Via Webservice-Calls erfolgen verschiedene Abfragen. # # ---------------------------------------------------------------------------- # Copyright (c) 2021 by Digital Data GmbH # # Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim # Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works # ---------------------------------------------------------------------------- # Creation Date / Author: 15.09.2021 / MD # Version Date / Editor: 06.12.2021 / MD # Version Number: 1.1.0 #################################### # # Import # # #################################### import tkinter as tk from datetime import datetime import cv2 import numpy as np from pyzbar.pyzbar import decode import requests import configparser import cryptocode import logging from logging.handlers import TimedRotatingFileHandler from logging import Formatter ##################################### # # Server-Options # # # # In DD-Time.ini bearbeitbar # # ##################################### config = configparser.ConfigParser() config.sections() config.read('DD-Time.ini') ############################################### WebServiceURL = config['WEBSERVICE']['WebServiceURL'] WebServiceBenutzerName = config['WEBSERVICE']['WebServiceBenutzerName'] WebServiceBenutzerPasswort = config['WEBSERVICE']['WebServiceBenutzerPasswort'] WinLineMandant = config['WEBSERVICE']['WinLineMandant'] WebServiceTemplateSTART = config['WEBSERVICE']['WebServiceTemplateSTART'] WebServiceTemplateSTOP = config['WEBSERVICE']['WebServiceTemplateSTOP'] WebServiceType = config['WEBSERVICE']['WebServiceType'] Fehlzeitenstamm = config['WEBSERVICE']['Fehlzeitenstamm'] WeekRule = config['PAUSEKEYRULE']['WeekRule'] WebServiceActioncode = "1" ############################################### TextDayStart = config['STARTBUTTON']['TextDayStart'] ColorTextStartButton = config['STARTBUTTON']['ColorText'] BackgroundStartButton = config['STARTBUTTON']['ColorBackground'] ColorPressedStartButton = config['STARTBUTTON']['ColorPressed'] ############################################### TextDayEnd = config['ENDBUTTON']['TextDayEnd'] ColorTextEndButton = config['ENDBUTTON']['ColorText'] BackgroundEndButton = config['ENDBUTTON']['ColorBackground'] ColorPressedEndButton = config['ENDBUTTON']['ColorPressed'] ############################################### TextBreakStart = config['PAUSEBUTTON']['TextBreakStart'] ColorTextPauseButton = config['PAUSEBUTTON']['ColorText'] BackgroundPauseButton = config['PAUSEBUTTON']['ColorBackground'] ColorPressedPauseButton = config['PAUSEBUTTON']['ColorPressed'] ############################################### TextBreakEnd = config['ENDPAUSEBUTTON']['TextBreakEnd'] ColorTextEndPauseButton = config['ENDPAUSEBUTTON']['ColorText'] BackgroundEndPauseButton = config['ENDPAUSEBUTTON']['ColorBackground'] ColorPressedEndPauseButton = config['ENDPAUSEBUTTON']['ColorPressed'] ############################################### TextDefault = config['LABEL']['TextDefault'] TextStart = config['LABEL']['TextStart'] TextEnd = config['LABEL']['TextEnd'] TextPauseStart = config['LABEL']['TextPauseStart'] TextPauseEnd = config['LABEL']['TextPauseEnd'] TextErrorStart = config['LABEL']['TextErrorStart'] TextErrorEnd = config['LABEL']['TextErrorEnd'] TextErrorPause = config['LABEL']['TextErrorPause'] TextErrorPauseEnd = config['LABEL']['TextErrorPauseEnd'] TextWorkday = config['LABEL']['TextWorkday'] TextPause = config['LABEL']['TextPause'] ############################################### TimeToScan = int(config['TIMETOSCAN']['TimeToScan']) ############################################### TimeToShowScreens = int(config['TIMETOSHOWSCREENS']['TimeToShowScreens']) ############################################### Height = int(config['SCANWINDOWSIZE']['Height']) Width = int(config['SCANWINDOWSIZE']['Width']) ############################################### TextStartButton = TextWorkday + NewLine + TextDayStart TextEndButton = TextWorkday + NewLine + TextDayEnd TextPauseButton = TextPause + NewLine + TextBreakStart TextEndPauseButton = TextPause + NewLine + TextBreakEnd ############################################### Path = config['LOGGING']['Path'] When = config['LOGGING']['When'] Interval = int(config['LOGGING']['Interval']) BackupCount = int(config['LOGGING']['BackupCount']) Encoding = config['LOGGING']['Encoding'] ############################################### NewLine = "\n" dePW = cryptocode.decrypt(WebServiceBenutzerPasswort, "Zeiterfassung") class App(tk.Tk): def showError(self, exc, val, tb): #showerror("Error", message=str(val)) self.logger.error('') self.logger.error(exc) self.logger.error(val) self.logger.error(tb) ############################################ # # Frame # # # Erstellt die GUI inkl. Button und Labels # ############################################ def __init__(self): super().__init__() # LOG-File-Generator self.logger = logging.getLogger('mylogger') self.handler = TimedRotatingFileHandler(filename=Path, when=When, interval=int(Interval), backupCount=int(BackupCount), encoding=Encoding, delay=False) formatter = Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s') self.handler.setFormatter(formatter) self.logger.addHandler(self.handler) self.logger.setLevel(logging.DEBUG) ############################################### self.configure(bg="white") self.title("DD-Zeiterfassung") self.geometry("1024x768") #self.attributes("-topmost", True) -- tkinter gui immer im vordergrund #self.wm_overrideredirect(True) -- tkinter gui ohne Titlezeile self.wm_attributes('-type', 'splash') # -- tkinter gui Titelzeile klappt sich ein self.start_button = tk.Button(self, font=('Arial', 12), text=TextStartButton, height =15, width=15, fg=ColorTextStartButton, bg=BackgroundStartButton, activebackground=ColorPressedStartButton, command=self.Startbutton_action) self.stop_button = tk.Button(self, font=('Arial', 12), text=TextEndButton, height =15, width=15, fg=ColorTextEndButton, bg=BackgroundEndButton, activebackground=ColorPressedEndButton, command=self.Stopbutton_action) self.pause_button = tk.Button(self, font=('Arial', 12), text=TextPauseButton, height=15, width=15, fg=ColorTextPauseButton, bg=BackgroundPauseButton, activebackground=ColorPressedPauseButton, command=self.PauseStartbutton_action) self.pause_end_button = tk.Button(self, font=('Arial', 12), text=TextEndPauseButton, height=15, width=15, fg=ColorTextEndPauseButton, bg=BackgroundEndPauseButton, activebackground=ColorPressedEndPauseButton, command=self.PauseStopbutton_action) self.anweisungs_label = tk.Label(self, font=('Arial', 12), text=TextDefault) self.anweisungs_label2 = tk.Label(self, font=('Arial', 12), bg="white", fg="black") self.labelUhr = tk.Label(text="", font=('Arial', 12), fg='red') self.update_clock() self.labelUhr.grid(row=0,column=4) self.anweisungs_label.grid(row=0, column=2, pady=20, padx=10, sticky='ew') self.anweisungs_label2.grid(row=1, column=2, pady=20, padx=10, sticky='ew') self.start_button.grid(row=2, column=1, pady=20, padx=10, sticky='ew') self.stop_button.grid(row=2, column=2, pady=20, padx=10, sticky='ew') self.pause_button.grid(row=2, column=3, pady=20, padx=10, sticky='ew') self.pause_end_button.grid(row=2, column=4, pady=20, padx=10, sticky='ew') self.report_callback_exception = self.showError self.logger.info("Initialisierung abgeschlossen!") ######################################## # # Button-Action # # # # Legende # # # RequestTyp = 1 = Arbeitstag beginnen # # RequestTyp = 2 = Arbeitstag beenden # # RequestTyp = 3 = Pause beginnen # # RequestTyp = 4 = Pause beenden # ######################################## ##################################### # # Different-Buttons # # ##################################### def update_clock(self): now = datetime.now() clock = now.strftime('%H:%M:%S') self.labelUhr.configure(text=clock) self.after(1000, self.update_clock) ############################################### def Startbutton_action(self): RequestTyp = 1 self.Button_action(RequestTyp) ############################################### def Stopbutton_action(self): RequestTyp = 2 self.Button_action(RequestTyp) ############################################### def PauseStartbutton_action(self): RequestTyp = 3 self.Button_action(RequestTyp) ############################################### def PauseStopbutton_action(self): RequestTyp = 4 self.Button_action(RequestTyp) ############################################### def Button_action(self, RequestTyp): myData = Scanner(self) print(myData) #myDataListe = myData.split("-") now = datetime.now() actDateTime = now.strftime('%Y-%m-%d') + "T" + now.strftime('%H:%M:%S') MakeWinLineRequest(self, RequestTyp, myData, actDateTime, now) ###################################### # # Reset der GUI # # ###################################### def LabelReset(self): anweisungs_label = tk.Label(font=('Arial', 12), text=TextDefault) anweisungs_label.grid(row=0, column=2, sticky='ew') anweisungs_label2 = tk.Label(font=('Arial', 12), bg="white", text="") anweisungs_label2.grid(row=1, column=2, sticky='ew') self.configure(bg="white") app.event_generate('', warp=True, x=1, y=1) # Set the Mouse-Position to x/y ###################################### # # Build Start-XML # # ###################################### def BuildXMLStart(RequestTyp, myData, Datumvon): if RequestTyp == 1: ArbeitsZeitPause = "0" Fehlzeitenstamm =config['WEBSERVICE']['Fehlzeitenstamm'] else: ArbeitsZeitPause = "1" if WeekRule == 'on': Weekday = datetime.today().isoweekday() Fehlzeitenstamm = config['PAUSEKEYRULE']['Weekday' + str(Weekday)] else: Fehlzeitenstamm = "1" myDataListe = myData.split("-") AN_Gruppe_Ressource = myDataListe[0] XML_Start = "" XML_Start = XML_Start + "" XML_Start = XML_Start + "" XML_Start = XML_Start + "<" + WebServiceTemplateSTART + ">" XML_Start = XML_Start + "" + Datumvon + "" XML_Start = XML_Start + "" + Fehlzeitenstamm + "" XML_Start = XML_Start + "" + AN_Gruppe_Ressource + "" XML_Start = XML_Start + "" + ArbeitsZeitPause + "" XML_Start = XML_Start + "" XML_Start = XML_Start + "" print("XML-Start =" + XML_Start) return XML_Start ###################################### # # Build Start-URL # # ###################################### def BuildURLStart(XML_Start): URL_Start = "http://%SERVER%/ewlservice/import?User=%USER%&Password=%PASSWORD%&Company=%COMPANY%&Type=%TYPE%&Vorlage=%VORLAGE%&Actioncode=%ACTIONCODE%&byref=0&Data=%DATA%" URL_Start = URL_Start.replace("%SERVER%", WebServiceURL) URL_Start = URL_Start.replace("%USER%", WebServiceBenutzerName) URL_Start = URL_Start.replace("%PASSWORD%", dePW) URL_Start = URL_Start.replace("%COMPANY%", WinLineMandant) URL_Start = URL_Start.replace("%TYPE%", WebServiceType) URL_Start = URL_Start.replace("%VORLAGE%", WebServiceTemplateSTART) URL_Start = URL_Start.replace("%ACTIONCODE%", WebServiceActioncode) URL_Start = URL_Start.replace("%DATA%", XML_Start) return URL_Start ##################################### # # Build Export-URL # # ##################################### def BuildURLExport(myData,RequestTyp): myDataListe = myData.split("-") Arbeitnehmernummer = myDataListe[0] if RequestTyp == 1: WebServiceTemplateExport = "Export" Filter = "FilterZeit-" + Arbeitnehmernummer elif RequestTyp == 2: WebServiceTemplateExport = "Export" Filter = "FilterZeit-" + Arbeitnehmernummer elif RequestTyp == 3: WebServiceTemplateExport = "ExportPause" Filter = "FilterZeitP-" + Arbeitnehmernummer elif RequestTyp == 4: WebServiceTemplateExport = "ExportPause" Filter = "FilterZeitP-" + Arbeitnehmernummer XMLExport = Filter URL_Export = "http://%SERVER%/ewlservice/export?User=%USER%&Password=%PASSWORD%&Company=%COMPANY%&Type=%TYPE%&Vorlage=%VORLAGE%&Actioncode=%ACTIONCODE%&byref=0&Key=%KEY%" URL_Export = URL_Export.replace("%SERVER%", WebServiceURL) URL_Export = URL_Export.replace("%USER%", WebServiceBenutzerName) URL_Export = URL_Export.replace("%PASSWORD%", dePW) URL_Export = URL_Export.replace("%COMPANY%", WinLineMandant) URL_Export = URL_Export.replace("%TYPE%", WebServiceType) URL_Export = URL_Export.replace("%VORLAGE%", WebServiceTemplateExport) URL_Export = URL_Export.replace("%ACTIONCODE%", WebServiceActioncode) URL_Export = URL_Export.replace("%KEY%", XMLExport) URL_Export = URL_Export.replace("%FILTER%", Filter) print("URL_Export = " + URL_Export) HTTPRequest_Export = requests.post(URL_Export) IndexVonDatum = HTTPRequest_Export.text.find("") v1 = IndexVonDatum + len("") v2 = v1 + 19 Datumvon = HTTPRequest_Export.text[v1:v2] return Datumvon ##################################### # # Build End-XML # # ##################################### def BuildXMLEnde(RequestTyp, myData, OrgDateTime, ActDateTime): print(RequestTyp) if RequestTyp == 2: ArbeitsZeitPause = "0" Fehlzeitenstamm =config['WEBSERVICE']['Fehlzeitenstamm'] else: ArbeitsZeitPause = "1" if WeekRule == 'on': Weekday = datetime.today().isoweekday() Fehlzeitenstamm = config['PAUSEKEYRULE']['Weekday' + str(Weekday)] else: Fehlzeitenstamm = "1" myDataListe = myData.split("-") AN_Gruppe_Ressource = myDataListe[0] XML_Ende = "" XML_Ende = XML_Ende + "" XML_Ende = XML_Ende + "" XML_Ende = XML_Ende + "<" + WebServiceTemplateSTOP + ">" XML_Ende = XML_Ende + "" + OrgDateTime + "" XML_Ende = XML_Ende + "" + ActDateTime + "" XML_Ende = XML_Ende + "" + Fehlzeitenstamm + "" XML_Ende = XML_Ende + "" + AN_Gruppe_Ressource + "" XML_Ende = XML_Ende + "" + ArbeitsZeitPause + "" XML_Ende = XML_Ende + "" + "1" + "" XML_Ende = XML_Ende + "" XML_Ende = XML_Ende + "" return XML_Ende ##################################### # # Build End-URL # # ##################################### def BuildURLEnde(XML_Ende): URL_Ende = "http://%SERVER%/ewlservice/import?User=%USER%&Password=%PASSWORD%&Company=%COMPANY%&Type=%TYPE%&Vorlage=%VORLAGE%&Actioncode=%ACTIONCODE%&byref=0&Data=%DATA%" URL_Ende = URL_Ende.replace("%SERVER%", WebServiceURL) URL_Ende = URL_Ende.replace("%USER%", WebServiceBenutzerName) URL_Ende = URL_Ende.replace("%PASSWORD%", dePW) URL_Ende = URL_Ende.replace("%COMPANY%", WinLineMandant) URL_Ende = URL_Ende.replace("%TYPE%", WebServiceType) URL_Ende = URL_Ende.replace("%VORLAGE%", WebServiceTemplateSTOP) URL_Ende = URL_Ende.replace("%ACTIONCODE%", WebServiceActioncode) URL_Ende = URL_Ende.replace("%DATA%", XML_Ende) return URL_Ende ##################################### # # WinLine Requests # # ##################################### def MakeWinLineRequest(self, RequestTyp, myData, actDateTime, now): myDataListe = myData.split("-") name = myDataListe[1] TimeWithoutsec = now.strftime('%H:%M:%S') ############################################### if RequestTyp == 1: CheckStatus = BuildURLExport(myData, RequestTyp) print(CheckStatus) if CheckStatus == "sion=\"1.0\" encoding": XMLStart = BuildXMLStart(RequestTyp, myData, actDateTime) URLStart = BuildURLStart(XMLStart) HTTPRequest_Start = requests.post(URLStart) print(HTTPRequest_Start.text) self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextStart + str(name)) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.anweisungs_label2 = tk.Label(font=('Arial', 12), text="Es ist " + str(TimeWithoutsec) + " Uhr ") self.anweisungs_label2.grid(row=1, column=2, sticky='ew') self.configure(bg="green") self.after(TimeToShowScreens, LabelReset, self) else: self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextErrorStart) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(TimeToShowScreens, LabelReset, self) ############################################### elif RequestTyp == 2: OrgDateTime = BuildURLExport(myData, RequestTyp) Weekday = datetime.today().isoweekday() if OrgDateTime == "sion=\"1.0\" encoding": #if OrgDateTime == "Kein Datensatz für den Export vorhanden!": self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextErrorEnd) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(TimeToShowScreens, LabelReset, self) else: XMLEnde = BuildXMLEnde(RequestTyp, myData, OrgDateTime, actDateTime) URLEnde = BuildURLEnde(XMLEnde) HTTPRequest_Ende = requests.post(URLEnde) print(HTTPRequest_Ende.text) self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextEnd + str(name)) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.anweisungs_label2 = tk.Label(font=('Arial', 12), text="Es ist " + str(TimeWithoutsec) + " Uhr ") self.anweisungs_label2.grid(row=1, column=2, sticky='ew') self.configure(bg="green") self.after(TimeToShowScreens, LabelReset, self) ############################################### elif RequestTyp == 3: CheckStatus = BuildURLExport(myData, RequestTyp) if CheckStatus == "sion=\"1.0\" encoding": #if CheckStatus == "Kein Datensatz für den Export vorhanden!": XMLStart = BuildXMLStart(RequestTyp, myData, actDateTime) URLStart = BuildURLStart(XMLStart) HTTPRequest_Start = requests.post(URLStart) print(HTTPRequest_Start.text) self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextPause + str(name)) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.anweisungs_label2 = tk.Label(font=('Arial', 12), text="Es ist " + str(TimeWithoutsec) + " Uhr ") self.anweisungs_label2.grid(row=1, column=2, sticky='ew') self.configure(bg="green") self.after(TimeToShowScreens, LabelReset, self) else: self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextErrorPause) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(TimeToShowScreens, LabelReset, self) ############################################### elif RequestTyp == 4: OrgDateTime = BuildURLExport(myData, RequestTyp) if OrgDateTime == "sion=\"1.0\" encoding": self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextErrorPauseEnd) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(TimeToShowScreens, LabelReset, self) else: XMLEnde = BuildXMLEnde(RequestTyp, myData, OrgDateTime, actDateTime) URLEnde = BuildURLEnde(XMLEnde) HTTPRequest_Ende = requests.post(URLEnde) print(HTTPRequest_Ende.text) self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextPauseEnd + str(name)) self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.anweisungs_label2 = tk.Label(font=('Arial', 12), text="Es ist " + str(TimeWithoutsec) + " Uhr ") self.anweisungs_label2.grid(row=1, column=2, sticky='ew') self.configure(bg="green") self.after(TimeToShowScreens, LabelReset, self) ##################################### # # Scanner # # ##################################### def Scanner(self): app.event_generate('', warp=True, x=1, y=1) self.anweisungs_label = tk.Label(font=('Arial', 12), text=TextDefault) cap = cv2.VideoCapture(0) cap.set(3, Height) cap.set(4, Width) cv2.namedWindow('Result') cv2.moveWindow('Result', 2, 2) myData = None for x in range(0, TimeToScan): success, img = cap.read() for barcode in decode(img): myData = barcode.data.decode('utf-8') rotated_img = np.rot90(img, k=3) cv2.imshow('Result', rotated_img) cv2.waitKey(1) if myData is not None: cap.release() cv2.destroyAllWindows() return myData else: self.after(100) cap.release() cv2.destroyAllWindows() LabelReset(self) ##################################### # # Main # # ##################################### if __name__ == "__main__": app = App() app.mainloop()