#!/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: 04.11.2021 / MD # Version Number: 1.0.1.1 #################################### # # 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 from tkinter.messagebox import showerror import logging from logging.handlers import TimedRotatingFileHandler from logging import Formatter ##################################### # # Server-Options # # # # In config.in bearbeitbar # # ##################################### config = configparser.ConfigParser() config.sections() config.read('config.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" 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='log/Zeiterfassung.log', when='D', interval=1, backupCount=30, encoding='utf-8', delay=False) # logging.basicConfig(format='%(asctime)s|%(levelname)s|%(message)s', level=logging.DEBUG, filename='Zeiterfassung2.log') 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="Arbeitstag\n beginnen", height =15, width=15, fg="white", bg="green", activebackground='blue', command=self.Startbutton_action) self.stop_button = tk.Button(self, font=('Arial', 12), text="Arbeitstag\n beenden ", height =15, width=15, fg="white", bg="red", activebackground='blue', command=self.Stopbutton_action) self.pause_button = tk.Button(self, font=('Arial', 12), text="Pause\n beginnen", height=15, width=15, fg="white", bg="#b47a0e", activebackground='blue', command=self.PauseStartbutton_action) self.pause_end_button = tk.Button(self, font=('Arial', 12), text="Pause\n beenden", height=15, width=15, fg="white", bg="#b47a0e", activebackground='blue', command=self.PauseStopbutton_action) self.anweisungs_label = tk.Label(self, font=('Arial', 12), text="Wählen Sie eine Buchungsart\n") 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 disableButtons(self): self.start_button['state'] = tk.DISABLED self.stop_button['state'] = tk.DISABLED self.pause_button['state'] = tk.DISABLED self.pause_end_button['state'] = tk.DISABLED def enableButtons(self): self.start_button['state'] = tk.NORMAL self.stop_button['state'] = tk.NORMAL self.pause_button['state'] = tk.NORMAL self.pause_end_button['state'] = tk.NORMAL 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) #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="Wählen Sie eine Buchungsart\n") 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 self.enableButtons(self) ###################################### # # 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 + ">" #Location for WinLine fields 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 + "" 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 = "FilterTest-" + Arbeitnehmernummer elif RequestTyp == 2: WebServiceTemplateExport = "Export" Filter = "FilterTest-" + Arbeitnehmernummer elif RequestTyp == 3: WebServiceTemplateExport = "ExportPause" Filter = "FilterTestP-" + Arbeitnehmernummer elif RequestTyp == 4: WebServiceTemplateExport = "ExportPause" Filter = "FilterTestP-" + 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) 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="Hallo,\n " + 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(5000, LabelReset, self) else: self.anweisungs_label = tk.Label(font=('Arial', 12), text= "Sie sind bereits eingeloggt\n") self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(5000, LabelReset, self) ################################### elif RequestTyp == 2: OrgDateTime = BuildURLExport(myData, RequestTyp) Weekday = datetime.today().isoweekday() if OrgDateTime == "sion=\"1.0\" encoding": self.anweisungs_label = tk.Label(font=('Arial', 12), text="Sie sind nicht eingeloggt\n") self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(5000, 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="Auf Wiedersehen,\n " + 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(5000, LabelReset, self) #################################### elif RequestTyp == 3: CheckStatus = BuildURLExport(myData, RequestTyp) 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=u"Eine schöne Pause,\n " + 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(5000, LabelReset, self) else: self.anweisungs_label = tk.Label(font=('Arial', 12), text="Sie sind bereits in Pause\n") self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(5000, LabelReset, self) ####################################### elif RequestTyp == 4: OrgDateTime = BuildURLExport(myData, RequestTyp) if OrgDateTime == "sion=\"1.0\" encoding": self.anweisungs_label = tk.Label(font=('Arial', 12), text="Sie sind nicht in Pause\n") self.anweisungs_label.grid(row=0, column=2, sticky='ew') self.configure(bg="red") self.after(5000, 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="Willkommen zurück,\n " + 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(5000, LabelReset, self) ###################################### # # Scanner # # ###################################### def Scanner(self): app.event_generate('', warp=True, x=1, y=1) self.anweisungs_label = tk.Label(font=('Arial', 12), text="Wählen Sie eine Buchungsart\n") cap = cv2.VideoCapture(0) cap.set(3, 160) # vorher 640 cap.set(4, 120) # vorher 480 cv2.namedWindow('Result') cv2.moveWindow('Result', 2, 2) myData = None #Abbruch = False for x in range(0, 100): 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()