Skip to content

Commit

Permalink
Added E-Mail Support and Improved Splitting
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidKrassnig committed Oct 14, 2023
1 parent d07d132 commit 2719abf
Show file tree
Hide file tree
Showing 3 changed files with 270 additions and 35 deletions.
2 changes: 2 additions & 0 deletions personal_db.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Personalnummer,Nachname,Vorname,E-Mail
000000,Mustermann,Max,[email protected]
238 changes: 238 additions & 0 deletions ubffm-buchungsjournal-email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import os
import re
import json
import csv
import win32com.client as win32
from datetime import datetime
import tqdm # Erweiterte Fortschrittsanzeige

verzeichnis_pfad = "buchungsjournale" # Pfad zum Verzeichnis (anpassen)
csv_datei_pfad = "personal_db.csv" # Pfad zur CSV-Datei (anpassen)

def datumKonvertierer(yyyymm_date):
try:
date_obj = datetime.strptime(yyyymm_date, '%Y%m')
german_month_names = [
"Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"
]
full_month_name = german_month_names[date_obj.month - 1] # Subtract 1 to account for 0-based index
full_date = f"{full_month_name} {date_obj.year}"
return full_date
except ValueError:
return "Ungültiges Datumsformat"

def datumExtrahierer(filename):
pattern = r'_(\d+)_\w+\.pdf$'
match = re.search(pattern, filename)
if match:
return match.group(1)
else:
return None

def personalnummerExtrahierer(dateiname):
treffer = re.search(r'_(\d+)\.pdf$', dateiname)
if treffer:
return treffer.group(1)
return None

def dictionaryFlacher(originalDict):
flacheresDict = {}

for key, value in originalDict.items():
if isinstance(value, dict):
for subkey, subvalue in value.items():
flacheresDict[subkey] = subvalue
else:
flacheresDict[key] = value

return flacheresDict

def csvLeser(csv_datei):
daten = {}
with open(csv_datei, 'r', newline='') as datei:
csv_leser = csv.reader(datei)
titelzeile = next(csv_leser) # Titelzeile überspringen
for zeile in csv_leser:
if len(zeile) >= 4:
personalnummer = zeile[0]
nachname = zeile[1]
vorname = zeile[2]
pdf = zeile[3]
daten[personalnummer] = {
"Nachname": nachname,
"Vorname": vorname,
"E-Mail": pdf
}
return daten

def datenbanks():
if os.path.exists(csv_datei_pfad) and os.path.isfile(csv_datei_pfad):
csv_daten = csvLeser(csv_datei_pfad)

if os.path.exists(verzeichnis_pfad) and os.path.isdir(verzeichnis_pfad):
datenbank = datenbankErsteller(verzeichnis_pfad, csv_daten)
csv_daten = None
return datenbank
else:
print(f"Das Verzeichnis '{verzeichnis_pfad}' existiert nicht oder ist kein Verzeichnis.")
exit(1)
else:
print(f"Die CSV-Datei '{csv_datei_pfad}' existiert nicht oder ist keine Datei.")
exit(1)

def datenbankErsteller(pfad, csv_daten):
datenbank = {}
for wurzel, verzeichnisse, dateien in os.walk(pfad):
aktuell = datenbank
ordner = wurzel.split(os.path.sep)[1:]
for ordnername in ordner:
aktuell = aktuell.setdefault(ordnername, {})
for datei in dateien:
personalnummer = None
if datei.lower().endswith('.pdf'):
vollerPdfPfad = os.path.abspath(os.path.join(wurzel, datei))
personalnummer = personalnummerExtrahierer(datei)
datum = datumKonvertierer(datumExtrahierer(datei))
if personalnummer in csv_daten:
aktuell[datei] = {
"Personalnummer": personalnummer,
"Nachname": csv_daten[personalnummer]["Nachname"],
"Vorname": csv_daten[personalnummer]["Vorname"],
"E-Mail": csv_daten[personalnummer]["E-Mail"],
"Pfad": vollerPdfPfad, # Add the full file path
"Datum": datum
}
else:
aktuell[datei] = {
"Personalnummer": personalnummer,
"Nachname": None,
"Vorname": None,
"E-Mail": None,
"Pfad": vollerPdfPfad, # Add the full file path
"Datum": datum
}

return datenbank

def auswahlAbteilung(abteilung):
while True:
print("\nVerfügbare Abteilungen:")
subkeys = list(abteilung.keys())
print("0. Alle Abteilungen")
for i, key in enumerate(subkeys):
print(f"{i + 1}. {key}")

choice = input("Geben Sie die Zahl der auszuwählenden Abteilung ein: ")
try:
choice = int(choice)
if 0 <= choice <= len(subkeys):
if choice == 0:
return dictionaryFlacher(abteilung)
else:
selected_subkey = subkeys[choice - 1]
return abteilung[selected_subkey]
else:
print("Keine valide Wahl. Bitte wählen Sie eine valide Nummer.")
except ValueError:
print("Keine valide Wahl. Bitte wählen Sie eine valide Nummer.")

def auswahlGesamt(datenbank):
while True:
print("\nVerfügbare Zeiträume:")
main_keys = sorted(list(datenbank.keys()), reverse=True)
for i, key in enumerate(main_keys):
print(f"{i + 1}. {key}")

choice = input("Geben Sie die Zahl des auszuwählenden Zeitraumes ein: ")
try:
choice = int(choice)
if 1 <= choice <= len(main_keys):
selected_key = main_keys[choice - 1]
abteilung = datenbank[selected_key]
selected_abteilung = auswahlAbteilung(abteilung)
return selected_abteilung
break # Exit the loop after selecting a subkey
else:
print("Keine valide Wahl. Bitte wählen Sie eine valide Nummer.")
except ValueError:
print("Keine valide Wahl. Bitte wählen Sie eine valide Nummer.")

def mailTextErzeuger(vorname, nachname, pdf, path, personalnummer, datum):
if None in (vorname, nachname, pdf, path):
return "Fehler!"
else:
message = f"Guten Tag {vorname} {nachname},\n\nBitte finden Sie dieser E-Mail beigefügt Ihr Buchungsjournal für {datum}.\n\n\nMit freundlichen Grüßen\n\nIhre Personalabteilung\n(dies ist eine automatisch generierte E-Mail)"
return message

keineMail = []
eineMail = []

def mailVerschicker(pdfs):
buchungsjournale_email_account_name = '[email protected]'
outlook = win32.Dispatch('outlook.application')
namespace = outlook.GetNamespace('MAPI')
buchungsjournale_email_account = None

for account in namespace.Accounts:
if account.DisplayName == buchungsjournale_email_account_name:
buchungsjournale_email_account = account
break
if buchungsjournale_email_account is None:
print(f"E-Mail-Account '{buchungsjournale_email_account_name}' wurde nicht gefunden.")
exit(1)

with tqdm.tqdm(total=len(pdfs)) as bar:
for pdf in pdfs:
vorname = pdfs[pdf]['Vorname']
nachname = pdfs[pdf]['Nachname']
emailAdresse = pdfs[pdf]['E-Mail']
anhangPfad = pdfs[pdf]['Pfad']
personalnummer = pdfs[pdf]['Personalnummer']
datum = pdfs[pdf]['Datum']
message = mailTextErzeuger(vorname, nachname, emailAdresse, anhangPfad, personalnummer, datum)
if message == 'Fehler!':
keineMail.append(pdf)
bar.update(1)
else:
mail = outlook.CreateItem(0)

mail._oleobj_.Invoke(*(64209, 0, 8, 0, buchungsjournale_email_account)) # Select the pdf account

mail.To = emailAdresse
mail.Subject = f'[Buchungsjournal] {datum}'
mail.Body = message
mail.Attachments.Add(anhangPfad)

mail.Send()

eineMail.append(pdf)

bar.update(1)

def dateiPrint(text):
print(text)
output_file.write(text+'\n')

if __name__ == '__main__':
datenbank = datenbanks()
auswahl = auswahlGesamt(datenbank)
mailVerschicker(auswahl)
anzahlKeineMail = len(keineMail)
anzahlEineMail = len(eineMail)
anzahlMail = len(auswahl)
jetzt = datetime.now().strftime('%Y%m%dT%H%M%S')
with open(f'logs/log_{jetzt}.txt', "w") as output_file:
dateiPrint(f'Es wurden {anzahlEineMail}/{anzahlMail} Buchungsjournale erfolgreich verschickt!')
print(f'Details zu (nicht) verschickten E-Mails stehen in der Log-Datei (log_{jetzt}.txt).')
output_file.write('--------------------------------------------------\n')
output_file.write('Folgende Buchungsjournale wurden via E-Mail verschickt:\n')
for i in eineMail:
output_file.write(i+'\n')
output_file.write('--------------------------------------------------\n')
output_file.write('Folgende Buchungsjournale konnten wegen fehlenden Informationen nicht via E-Mail verschickt werden:\n')
for i in keineMail:
output_file.write(i+'\n')

input('\nDrücken Sie die Eingabetaste, um das Skript zu beenden...')
exit(0)
65 changes: 30 additions & 35 deletions ubffm-buchungsjournal-splitter.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
import fitz # PyMuPDF für PDF-Befehle
import re # Regex
import progressbar # Erweiterte Fortschrittsanzeige
import tqdm # Erweiterte Fortschrittsanzeige
import sys # Interaktion mit Terminal
import os # Interaktion mit Betriebssystemen
import shutil # Einfachere Dateiverschiebung

if os.name == 'nt':
from colorama import just_fix_windows_console
just_fix_windows_console()

class formatierung: # Definiert verschiedene Formatierungsmöglichkeiten
if os.name == 'nt':
PURPLE = ''
CYAN = ''
DARKCYAN = ''
BLUE = ''
GREEN = ''
YELLOW = ''
RED = ''
BOLD = ''
UNDERLINE = ''
END = ''
else:
PURPLE = '\033[95m'
CYAN = '\033[96m'
DARKCYAN = '\033[36m'
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'
PURPLE = '\033[95m'
CYAN = '\033[96m'
DARKCYAN = '\033[36m'
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
END = '\033[0m'

def dateiNamenRegeln(s): # Vereinheitlicht Namen der Dateiausgabe (optimiert für Interaktionen im Terminal)
# Entfernt alle nicht-Wörterzeichen (alles abgesehen von Buchstaben und Ziffern)
Expand All @@ -42,13 +34,13 @@ def pdfDateiSucheBuchungsjournal(pdfDatei, suchText):
doc = fitz.open(pdfDatei)
buchungsjournalSeiten = []

with progressbar.ProgressBar(max_value=doc.page_count) as bar:
with tqdm.tqdm(total=doc.page_count) as bar:
for seiteJetzt in range(doc.page_count):
page = doc.load_page(seiteJetzt)
text = page.get_text()
if suchText in text:
buchungsjournalSeiten.append(seiteJetzt)
bar.update(seiteJetzt)
bar.update(1)

doc.close()
return buchungsjournalSeiten
Expand Down Expand Up @@ -91,7 +83,7 @@ def pdfDateiAuftrennungNachSeiten(pdfDatei, seiteGesamt, tempVerzeichnis):
if not os.path.exists(tempVerzeichnis):
os.makedirs(tempVerzeichnis)

with progressbar.ProgressBar(max_value=len(seiteGesamt)) as bar:
with tqdm.tqdm(total=len(seiteGesamt)) as bar:
for i, seiteJetzt in enumerate(seiteGesamt):
seiteStart = seiteJetzt
seiteEnde = seiteGesamt[i + 1] if i + 1 < len(seiteGesamt) else doc.page_count
Expand All @@ -103,15 +95,15 @@ def pdfDateiAuftrennungNachSeiten(pdfDatei, seiteGesamt, tempVerzeichnis):
neuPdfDatei = f'{tempVerzeichnis}/pages_{seiteStart + 1}_to_{seiteEnde}.pdf'
neuDoc.save(neuPdfDatei)
neuDoc.close()
bar.update(i)
bar.update(1)

doc.close()

def pdfDateiUmbenennung(tempVerzeichnis):
pdfDateien = [pdfFile for pdfFile in os.listdir(tempVerzeichnis) if pdfFile.endswith('.pdf')]
gesamtDateien = len(pdfDateien)

with progressbar.ProgressBar(max_value=gesamtDateien) as bar:
with tqdm.tqdm(total=gesamtDateien) as bar:
for pdfDatei in pdfDateien:
datenExtrahiert = pdfDateiPersonaldaten(os.path.join(tempVerzeichnis, pdfDatei))
if datenExtrahiert:
Expand All @@ -123,7 +115,7 @@ def pdfDateiUmbenennung(tempVerzeichnis):
os.makedirs(neuPdfPfad)
neuPdfDatei = f'{neuPdfPfad}/{neuPdfName}'
os.rename(os.path.join(tempVerzeichnis, pdfDatei), neuPdfDatei)
bar.update()
bar.update(1)


def machTitel(s):
Expand All @@ -135,17 +127,17 @@ def meldungErfolgreich():
def meldungFehlschlag():
print(formatierung.BOLD+formatierung.RED+'Fehler!'+formatierung.END)

def ueberschreiben(ziel):
def ueberschreibenFrage(ziel):
response = input(formatierung.BOLD+formatierung.YELLOW+f'{os.path.basename(ziel)} existiert bereits im Ausgabeverzeichnis. Überschreiben?'+formatierung.END+' [j/N]\n')
return response.lower().strip() == 'j'

tempVerzeichnis = '.temp_ubffm_pdf_splitter'
suchText = 'Buchungsjournal'
ausgabeVerzeichnis = 'buchungsjournale'

width = os.get_terminal_size().columns
def trennlinie():
print('-'*width)
w, h = shutil.get_terminal_size()
print("—" * w)

if __name__ == '__main__':
if len(sys.argv) == 1:
Expand Down Expand Up @@ -187,14 +179,15 @@ def trennlinie():
os.makedirs(ausgabeVerzeichnis)
tempVerzeichnis_items = os.listdir(tempVerzeichnis)

with progressbar.ProgressBar(max_value=len(tempVerzeichnis_items)) as bar:
with tqdm.tqdm(total=len(tempVerzeichnis_items)) as bar:
for item in tempVerzeichnis_items:
quelle = os.path.join(tempVerzeichnis, item)
ziel = os.path.join(ausgabeVerzeichnis, item)
if os.path.exists(ziel):
user_response = ueberschreiben(ziel)
user_response = ueberschreibenFrage(ziel)
if not user_response:
print(f'Überspringe {item}...')
bar.update(1)
continue
if os.path.isdir(ziel):
shutil.rmtree(ziel) # Entferne das Zielverzeichnis
Expand All @@ -204,10 +197,12 @@ def trennlinie():
shutil.move(quelle, ziel)
else:
shutil.copy2(quelle, ziel)
bar.update()
bar.update(1)

# Entferne das temporäre Verzeichnis
if os.path.exists(tempVerzeichnis):
shutil.rmtree(tempVerzeichnis)
meldungErfolgreich()
trennlinie()
input('Drücken Sie die Eingabetaste, um das Skript zu beenden')
exit(0)

0 comments on commit 2719abf

Please sign in to comment.