|
@@ -1,116 +1,10 @@
|
|
|
-import hashlib
|
|
|
-import logging
|
|
|
-import os
|
|
|
-import re
|
|
|
-import shutil
|
|
|
-import pandas as pd
|
|
|
import json
|
|
|
+import logging
|
|
|
from pathlib import Path
|
|
|
-from datetime import datetime, timedelta
|
|
|
-from sqlalchemy import create_engine
|
|
|
-from suds.client import Client
|
|
|
-from cryptography.fernet import Fernet
|
|
|
-
|
|
|
-
|
|
|
-logging.basicConfig(filename="logs/nasa.log", level=logging.DEBUG)
|
|
|
-logger = logging.getLogger()
|
|
|
-
|
|
|
-
|
|
|
-def get_config():
|
|
|
- fernet_key = b"YBckeKYt-8g7LFvpG7XqAAcEbsYESnI-yl8by9rjeQQ="
|
|
|
- fernet = Fernet(fernet_key)
|
|
|
-
|
|
|
- if Path("config/nasa_config.json").exists():
|
|
|
- with open("config/nasa_config.json", "r") as f:
|
|
|
- config = json.load(f)
|
|
|
- with open("config/nasa_config.crypt", "wb") as f:
|
|
|
- f.write(fernet.encrypt(json.dumps(config).encode()))
|
|
|
- else:
|
|
|
- with open("config/nasa_config.crypt", "rb") as f:
|
|
|
- config = json.loads(fernet.decrypt(f.read()).decode())
|
|
|
- return config
|
|
|
-
|
|
|
-
|
|
|
-def conn_string(dsn: dict[str, str]):
|
|
|
- return f"mssql+pyodbc://{dsn['user']}:{dsn['pass']}@{dsn['server']}/{dsn['database']}?driver=SQL+Server+Native+Client+11.0"
|
|
|
-
|
|
|
-
|
|
|
-def load_data(config: dict[str, str], source: str, period: str):
|
|
|
- year = period[:4]
|
|
|
- month = period[4:6]
|
|
|
-
|
|
|
- select_befehl_auftraege = f"SELECT * FROM [Auftraege_NASA_gruppiert] WHERE Periode = '{period}'"
|
|
|
- select_befehl_mitarbeiter = f"SELECT * FROM [Mitarbeiter_NASA] WHERE Periode = '{period}'"
|
|
|
-
|
|
|
- source_auftraege = "data/Auftraege_NASA_gruppiert.csv"
|
|
|
- source_mitarbeiter = "data/Mitarbeiter_NASA.csv"
|
|
|
-
|
|
|
- payload = {
|
|
|
- "HaendlerNr": config["client_id"],
|
|
|
- "Filiale": config["client_id"],
|
|
|
- "Jahr": year,
|
|
|
- "Monat": month,
|
|
|
- "Fabrikat": "Mazda",
|
|
|
- "AnzahlMitarbeiter": 0,
|
|
|
- "AnzahlProduktiv": 0.0,
|
|
|
- "WerkstattDurchlaeufe": 0,
|
|
|
- "Token": config["credentials"]["token"],
|
|
|
- }
|
|
|
-
|
|
|
- if source == "database":
|
|
|
- source_db = create_engine(conn_string(config["source_dsn"]))
|
|
|
- df = pd.read_sql(select_befehl_auftraege, con=source_db)
|
|
|
- rename_from = ["AuftragsArt", "AuftragsTyp"]
|
|
|
- rename_to = ["AuftragsArtId_Name", "AuftragsArt"]
|
|
|
- df = df.rename(columns=dict(zip(rename_from, rename_to)))
|
|
|
-
|
|
|
- else:
|
|
|
- df = pd.read_csv(source_auftraege, sep=";", encoding="latin-1", decimal=",")
|
|
|
- df = df[df["Periode"] == period]
|
|
|
-
|
|
|
- # AuftragsArt = ["Inspektion", "Karosseriearbeit", "Lackierung", "Verschleißteile", "Sonstiges"]
|
|
|
- # AuftragsArtId = {"1": "Extern", "2": "Garantie", "3": "Intern", "4": "Theke"]
|
|
|
-
|
|
|
- columns = [
|
|
|
- "AuftragsArt",
|
|
|
- "AuftragsArtId",
|
|
|
- "TeileUmsatz",
|
|
|
- "LohnUmsatz",
|
|
|
- "SonstigeUmsatz",
|
|
|
- "GesamtUmsatz",
|
|
|
- "AnzahlAuftraege",
|
|
|
- ]
|
|
|
-
|
|
|
- df = df[columns]
|
|
|
-
|
|
|
- df.to_csv(
|
|
|
- f"{config['export_dir']}/csv/{period}_auftraege.csv",
|
|
|
- sep=";",
|
|
|
- encoding="latin-1",
|
|
|
- decimal=",",
|
|
|
- index=False,
|
|
|
- )
|
|
|
|
|
|
- payload["WerkstattDurchlaeufe"] = int(df["AnzahlAuftraege"].sum())
|
|
|
- payload["AfterSalesPositionen"] = df.to_dict("records")
|
|
|
-
|
|
|
- # Mitarbeiter gesamt und produktiv
|
|
|
- if source == "database":
|
|
|
- df = pd.read_sql(select_befehl_mitarbeiter, con=source_db)
|
|
|
- else:
|
|
|
- df = pd.read_csv(source_mitarbeiter, sep=";", encoding="latin-1", decimal=",")
|
|
|
-
|
|
|
- df.to_csv(
|
|
|
- f"{config['export_dir']}/csv/{period}_mitarbeiter.csv",
|
|
|
- sep=";",
|
|
|
- encoding="latin-1",
|
|
|
- decimal=",",
|
|
|
- index=False,
|
|
|
- )
|
|
|
+from suds.client import Client
|
|
|
|
|
|
- payload["AnzahlMitarbeiter"] = df.shape[0]
|
|
|
- payload["AnzahlProduktiv"] = int(df["produktiv"].sum())
|
|
|
- return payload
|
|
|
+logger = logging.getLogger("nasa")
|
|
|
|
|
|
|
|
|
def submit_data(config: dict[str, str], payload):
|
|
@@ -123,7 +17,7 @@ def submit_data(config: dict[str, str], payload):
|
|
|
try:
|
|
|
return client.service.MeldeAfterSalesDaten(payload)
|
|
|
except Exception as e:
|
|
|
- print(e)
|
|
|
+ logger.Error(e)
|
|
|
return -1
|
|
|
|
|
|
|
|
@@ -131,6 +25,7 @@ def print_result(period: str, result: str, len_pos: int):
|
|
|
print("Periode: " + period)
|
|
|
if len_pos == result:
|
|
|
print(f"Erfolgreich {result} Datensätze übertragen")
|
|
|
+ logger.Info(f"Erfolgreich {result} Datensätze übertragen")
|
|
|
return
|
|
|
|
|
|
print("Übertragung der Datensätze Fehlgeschlagen.")
|
|
@@ -138,88 +33,14 @@ def print_result(period: str, result: str, len_pos: int):
|
|
|
print("Fehler! Es waren keine Datensätze vorhanden.")
|
|
|
else:
|
|
|
print(f"{len_pos - result} von {len_pos} Datensätzen nicht verarbeitet!")
|
|
|
-
|
|
|
-
|
|
|
-def workflow(config: dict[str, str], year, month):
|
|
|
- period = f"{year}{month}"
|
|
|
- payload = load_data(config, "csv", period)
|
|
|
- result = submit_data(config, payload)
|
|
|
- len_pos = len(payload["AfterSalesPositionen"])
|
|
|
- print_result(period, result, len_pos)
|
|
|
-
|
|
|
-
|
|
|
-def export_all_periods(config) -> None:
|
|
|
- dt = datetime.now()
|
|
|
- prev = str(dt.year - 1)
|
|
|
- periods = [f"{prev}{x:02}" for x in range(1, 13)] + [f"{dt.year}{x:02}" for x in range(1, dt.month)]
|
|
|
-
|
|
|
- for period in periods:
|
|
|
- payload = load_data(config, "database", period)
|
|
|
- json.dump(
|
|
|
- payload,
|
|
|
- open(f"export/NASA/temp/NASA_{config['client_id']}_{period}_{config['timestamp']}.json", "w"),
|
|
|
- indent=2,
|
|
|
- )
|
|
|
-
|
|
|
-
|
|
|
-def file_get_hash(filename: str) -> str:
|
|
|
- with open(filename, "r") as frh:
|
|
|
- data = frh.read()
|
|
|
- return calculate_sha256(data)
|
|
|
-
|
|
|
-
|
|
|
-def calculate_sha256(data: str) -> str:
|
|
|
- return hashlib.sha256(data.encode()).hexdigest()
|
|
|
-
|
|
|
-
|
|
|
-def archive_files(export_dir: str):
|
|
|
- last_week = (datetime.now() - timedelta(days=6)).timestamp()
|
|
|
- for file in Path(export_dir).glob("*.json"):
|
|
|
- if file.stat().st_ctime < last_week:
|
|
|
- file.unlink()
|
|
|
-
|
|
|
- archive_path = Path(export_dir + "/Archiv")
|
|
|
- for file in Path(export_dir + "/temp").glob("*.json"):
|
|
|
- p = re.search(r"NASA_\d{5}_(20\d{4})_", file.name)
|
|
|
- if not p:
|
|
|
- continue
|
|
|
- period = p[1]
|
|
|
- year = period[:4]
|
|
|
- dest_folder = archive_path / year / period
|
|
|
- os.makedirs(dest_folder, exist_ok=True)
|
|
|
- file_hash = file_get_hash(file)
|
|
|
-
|
|
|
- if has_identical_file(dest_folder, file_hash):
|
|
|
- file.unlink()
|
|
|
- continue
|
|
|
- shutil.copy(file, archive_path.parent / file.name)
|
|
|
- file.rename(dest_folder / file.name)
|
|
|
-
|
|
|
-
|
|
|
-def has_identical_file(target: Path, file_hash: str) -> bool:
|
|
|
- for archived_file in Path(target).glob("*.json"):
|
|
|
- if file_get_hash(archived_file) == file_hash:
|
|
|
- return True
|
|
|
- return False
|
|
|
+ logger.Error(f"{len_pos - result} von {len_pos} Datensätzen nicht verarbeitet!")
|
|
|
|
|
|
|
|
|
def submit_changes(config):
|
|
|
for file in Path(config["export_dir"] + "/temp").glob("NASA_*.json"):
|
|
|
payload = json.load(file.open("r"))
|
|
|
period = payload["Jahr"] + payload["Monat"]
|
|
|
+ logger.Info("Periode: " + period)
|
|
|
len_pos = len(payload["AfterSalesPositionen"])
|
|
|
result = submit_data(config, payload)
|
|
|
print_result(period, result, len_pos)
|
|
|
-
|
|
|
-
|
|
|
-def main():
|
|
|
- config = get_config()
|
|
|
- config["timestamp"] = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
- config["export_dir"] = str(Path(".").resolve() / "export" / "NASA")
|
|
|
- export_all_periods(config)
|
|
|
- archive_files(config["export_dir"])
|
|
|
- submit_changes(config)
|
|
|
-
|
|
|
-
|
|
|
-if __name__ == "__main__":
|
|
|
- main()
|