8
0
2024-01-24 16:42:38 +01:00

536 lines
22 KiB
Python

#!/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('<Motion>', 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 version=\"1.0\" encoding=\"UTF-8\"?>"
XML_Start = XML_Start + "<MESOWebService TemplateType=\"" + WebServiceType + "\"" " Template=\"" + WebServiceTemplateSTART + "\">"
XML_Start = XML_Start + "<" + WebServiceTemplateSTART + ">"
XML_Start = XML_Start + "<Datumvon>" + Datumvon + "</Datumvon>"
XML_Start = XML_Start + "<Fehlzeitenstamm>" + Fehlzeitenstamm + "</Fehlzeitenstamm>"
XML_Start = XML_Start + "<ANGruppeRessource>" + AN_Gruppe_Ressource + "</ANGruppeRessource>"
XML_Start = XML_Start + "<TypZeitartPause>" + ArbeitsZeitPause + "</TypZeitartPause>"
XML_Start = XML_Start + "</" + WebServiceTemplateSTART + ">"
XML_Start = XML_Start + "</MESOWebService>"
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("<Datumvon>")
v1 = IndexVonDatum + len("<Datumvon>")
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 version=\"1.0\" encoding=\"UTF-8\"?>"
XML_Ende = XML_Ende + "<MESOWebService TemplateType=\"" + WebServiceType + "\"" " Template=\"" + WebServiceTemplateSTOP + "\">"
XML_Ende = XML_Ende + "<" + WebServiceTemplateSTOP + ">"
XML_Ende = XML_Ende + "<Datumvon>" + OrgDateTime + "</Datumvon>"
XML_Ende = XML_Ende + "<Datumbis>" + ActDateTime + "</Datumbis>"
XML_Ende = XML_Ende + "<Fehlzeitenstamm>" + Fehlzeitenstamm + "</Fehlzeitenstamm>"
XML_Ende = XML_Ende + "<ANGruppeRessource>" + AN_Gruppe_Ressource + "</ANGruppeRessource>"
XML_Ende = XML_Ende + "<TypZeitartPause>" + ArbeitsZeitPause + "</TypZeitartPause>"
XML_Ende = XML_Ende + "<Importoption>" + "1" + "</Importoption>"
XML_Ende = XML_Ende + "</" + WebServiceTemplateSTOP + ">"
XML_Ende = XML_Ende + "</MESOWebService>"
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('<Motion>', 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()