diff --git a/.~lock.GEMS_2025-04 LNW Markus Zimmerberger.xlsx# b/.~lock.GEMS_2025-04 LNW Markus Zimmerberger.xlsx# deleted file mode 100644 index 9e5de00..0000000 --- a/.~lock.GEMS_2025-04 LNW Markus Zimmerberger.xlsx# +++ /dev/null @@ -1 +0,0 @@ -,markus,Banjul,22.04.2025 16:01,file:///home/markus/.config/libreoffice/4; \ No newline at end of file diff --git a/app/GEMS export timesheets.py b/app/GEMS export timesheets.py index cb059a8..05b2fc1 100644 --- a/app/GEMS export timesheets.py +++ b/app/GEMS export timesheets.py @@ -6,38 +6,59 @@ import datetime import yaml import os from pathlib import Path +from openpyxl import Workbook +from openpyxl.utils import get_column_letter - -#TODO: Upload automatically! +#TODO: Day-format, Quantity-format @dataclass class Export: # all necessary fields for ELSAP-importfiles sum_col: str desc_col: str -def export_timesheet(csvexport_filename, ex: Export): +from openpyxl import Workbook + +def export_timesheet(xlsxexport_filename, ex: Export): if not hasattr(export_timesheet, "row"): export_timesheet.row = 4 - with open(f"{csvexport_filename}.csv", 'w') as file: - writer = csv.writer(file, delimiter=';', lineterminator="\n") + workbook = Workbook() + sheet = workbook.active + sheet.title = "Timesheet" - period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1) - writer.writerow(["","","","Current Period",f"{period.strftime('%d.%m.%Y')}"]) - writer.writerow("") - writer.writerow(["Name","PersNr","CostCenter","Day","Date","Status","WBS element","Description","PARMA","Task Name","Activity Code","ActType","Profit center","Quantity","Unit","Project description","Customer name","Statistical key figure","A/AType","Description A/A type","AI","Activity","Short text"]) - - # iterate - for time_entry in list_of_dicts: - date_str_de_de = time_entry.get("date") - if date_str_de_de not in ["","Wochensumme","Summe (kumuliert)"]: - datetime_object = datetime.datetime.strptime(date_str_de_de, '%a., %d. %B %Y') - writer.writerow([cfg['name'],cfg['pers_nr'],"",f"=WEEKDAY(E{export_timesheet.row})",datetime_object.strftime('%d.%m.%Y'),"",cfg['WBS_element'],cfg['WBS_desc'],"","","","","",time_entry.get(ex.sum_col),"Hours",time_entry.get(ex.desc_col)]) - export_timesheet.row += 1 + period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1) + sheet.append(["", "", "", "Current Period", f"{period.strftime('%d.%m.%Y')}"]) + sheet.append([]) + sheet.append(["Name", "PersNr", "CostCenter", "Day", "Date", "Status", "WBS element", "Description", "PARMA", + "Task Name", "Activity Code", "ActType", "Profit center", "Quantity", "Unit", "Project description", + "Customer name", "Statistical key figure", "A/AType", "Description A/A type", "AI", "Activity", + "Short text"]) + + # Iterate through time entries + for time_entry in list_of_dicts: + date_str_de_de = time_entry.get("date") + if date_str_de_de not in ["", "Wochensumme", "Summe (kumuliert)"]: + datetime_object = datetime.datetime.strptime(date_str_de_de, '%a., %d. %B %Y') + sum_col_value = time_entry.get(ex.sum_col) + + sheet.append([ + cfg['name'], cfg['pers_nr'], "", f"=WEEKDAY(E{export_timesheet.row})", + datetime_object.strftime('%d.%m.%Y'), "", cfg['WBS_element'], cfg['WBS_desc'], "", "", "", "", "", + sum_col_value, "Hours", "", "", "", "", "", "", "", "", time_entry.get(ex.desc_col) + ]) + export_timesheet.row += 1 + + # Hide columns + for col in ['B', 'C', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'P', 'Q', 'R', 'S', 'T', 'U', 'V']: + sheet.column_dimensions[col].hidden= True + + for idx, col in enumerate(sheet.columns, 1): + sheet.column_dimensions[get_column_letter(idx)].auto_size = True + + # Save the workbook + file_path = f"{xlsxexport_filename}.xlsx" + workbook.save(file_path) # Check if the file size is small (indicating only the header) - file_path = f"{csvexport_filename}.csv" - - # Use os.path.getsize to get the file size if os.path.getsize(file_path) <= 190: # Adjust this size threshold based on your expected header size os.remove(file_path) print(f"File {file_path} was deleted because it only contained the header.") @@ -68,9 +89,9 @@ list_of_dicts = worksheet.get_all_records(numericise_ignore=['all']) for key in ["gems"]: # extract period out of sheet-name period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1) - csvexport_filename = f"GEMS_{period.strftime('%Y-%m')} LNW {cfg['name']}" + xlsxexport_filename = f"GEMS_{period.strftime('%Y-%m')} LNW {cfg['name']}" ex1 = Export(sum_col=f"sum_{key}", desc_col=f"desc_{key}") - export_timesheet(csvexport_filename=csvexport_filename, ex=ex1) \ No newline at end of file + export_timesheet(xlsxexport_filename=xlsxexport_filename, ex=ex1) \ No newline at end of file diff --git a/app/calenderExport/EWSAtos.py b/app/calenderExport/EWSAtos.py new file mode 100644 index 0000000..e69de29 diff --git a/app/calenderExport/graph_all_events.py b/app/calenderExport/graph_all_events.py new file mode 100644 index 0000000..f236c1b --- /dev/null +++ b/app/calenderExport/graph_all_events.py @@ -0,0 +1,55 @@ +import requests +import csv + +# === Configuration === +ACCESS_TOKEN = "YOUR_ACCESS_TOKEN_HERE" +START_DATETIME = "2025-05-19T00:00:00Z" +END_DATETIME = "2025-05-26T23:59:59Z" +OUTPUT_FILE = "calendar.csv" + +# === Setup headers === +headers = { + "Authorization": f"Bearer {ACCESS_TOKEN}", + "Prefer": 'outlook.timezone="UTC"' +} + +# === Initial API endpoint === +base_url = "https://graph.microsoft.com/v1.0/me/calendarview" +params = { + "$select": "subject,start,end", + "startDateTime": START_DATETIME, + "endDateTime": END_DATETIME +} + +# === Event storage === +all_events = [] + +# === Fetch all pages === +print("Fetching events...") +url = base_url +while url: + response = requests.get(url, headers=headers, params=params if url == base_url else None) + if response.status_code != 200: + print("Failed to fetch events:", response.status_code, response.text) + break + + data = response.json() + events = data.get("value", []) + all_events.extend(events) + + # Get next page link + url = data.get("@odata.nextLink") + +# === Write to CSV === +print(f"Writing {len(all_events)} events to {OUTPUT_FILE}...") +with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f: + writer = csv.DictWriter(f, fieldnames=["subject", "start", "end"]) + writer.writeheader() + for e in all_events: + writer.writerow({ + "subject": e.get("subject", ""), + "start": e.get("start", {}).get("dateTime", ""), + "end": e.get("end", {}).get("dateTime", "") + }) + +print("Done.") diff --git a/app/calenderExport/graph_get_token.py b/app/calenderExport/graph_get_token.py new file mode 100644 index 0000000..1229185 --- /dev/null +++ b/app/calenderExport/graph_get_token.py @@ -0,0 +1,21 @@ +from msal import PublicClientApplication + +CLIENT_ID = "2a1cb9f1-2c0c-46ad-9887-fd2bb835bc2e" +AUTHORITY = "https://login.microsoftonline.com/consumers" # or your tenant ID +SCOPES = ["https://graph.microsoft.com/Calendars.Read"] + +USERNAME = "zisco@zisco.at" +PASSWORD = "3S1punk08_114$" # ⚠️ Storing passwords is risky + +app = PublicClientApplication(CLIENT_ID, authority=AUTHORITY) + +result = app.acquire_token_by_username_password( + username=USERNAME, + password=PASSWORD, + scopes=SCOPES +) + +if "access_token" in result: + print("Access token:", result["access_token"]) +else: + print("Error:", result.get("error_description")) diff --git a/app/config.yaml b/app/config.yaml index 7d88ea4..83b0328 100644 --- a/app/config.yaml +++ b/app/config.yaml @@ -5,8 +5,8 @@ 'spread': 1.0}, 'visualize_params': {'median_threshold': 6}, 'spreadsheet_name': 'Zeiterfassung alle Projekte', - 'worksheet_name': 'April 2025', - 'week': [10,11,12,13,14], + 'worksheet_name': 'Mai 2025', + 'week': [22], 'name': 'Markus Zimmerberger', 'username': 'ZIM9003', 'ansprechpartner_username': 'GRM0003', diff --git a/app/createTravelAccounting/createTravelAccounting.py b/app/createTravelAccounting/createTravelAccounting.py new file mode 100644 index 0000000..4e10310 --- /dev/null +++ b/app/createTravelAccounting/createTravelAccounting.py @@ -0,0 +1,103 @@ +import pandas as pd +from datetime import datetime, timedelta +import holidays +import os +from pathlib import Path +import random +import csv + +# Define date range +start_date = datetime(2023, 1, 1) +end_date = datetime(2023, 12, 31) + +# Austrian public holidays +at_holidays = holidays.country_holidays('AT', years=[2023]) + +# Exclude dates in external CSV-defined ranges +exclude_ranges_path = Path.home() / 'Documents' / 'zisco' / 'exclude_ranges.csv' +exclude_ranges = [] +if exclude_ranges_path.exists(): + with open(exclude_ranges_path, newline='') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + try: + start = datetime.strptime(row[0], '%Y-%m-%d') + end = datetime.strptime(row[1], '%Y-%m-%d') + exclude_ranges.append((start, end)) + except Exception: + continue + +def is_in_exclude_ranges(date): + for start, end in exclude_ranges: + if start <= date <= end: + return True + return False + +# Generate all dates in range +dates = [] +current = start_date +while current <= end_date: + if current.weekday() in [1, 3]: # 1=Tuesday, 3=Thursday + if current not in at_holidays and not is_in_exclude_ranges(current): + dates.append((current, "Wien")) + current += timedelta(days=1) + +# Add meetings from separate CSV file +meetings_path = Path.home() / 'Documents' / 'zisco' / 'meetings_2023.csv' +meetings = [] +if meetings_path.exists(): + with open(meetings_path, newline='') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + try: + date = datetime.strptime(row[1], '%d.%m.%Y') + ziel = row[3] + #if date not in at_holidays and not is_in_exclude_ranges(date): + if not is_in_exclude_ranges(date): + meetings.append((date, ziel)) + except Exception: + continue + +# Combine dates and meetings, ensuring no duplicates +# all_entries = dates+meetings +# all_entries.sort() + +# Combine and remove duplicates (as tuples to deduplicate) +all_entries = list({tuple(row) for row in meetings + dates}) + +# Sort by date (index 0) +all_entries.sort(key=lambda x: x[0]) + +df = pd.DataFrame({ + 'Datum': [d.strftime('%Y-%m-%d') for d, _ in all_entries], + 'Ziel': [z for _, z in all_entries], + 'Zweck': ['Besprechung'] * len(all_entries), + 'Std.': [random.randint(3, 7) for _ in all_entries], + 'Inland': [''] * len(all_entries), + 'Ausland': [''] * len(all_entries), + 'Tage': [''] * len(all_entries), + 'INLAND': [''] * len(all_entries), + 'AUSLAND': [''] * len(all_entries) +}) + +# df = pd.DataFrame({ +# 'Datum': [d.strftime('%Y-%m-%d') for d in dates], +# 'Ziel': ['Wien'] * len(dates), +# 'Zweck': ['Besprechung'] * len(dates), +# 'Std.': [random.randint(3, 9) for _ in dates], +# 'Inland': [''] * len(dates), +# 'Ausland': [''] * len(dates), +# 'Tage': [''] * len(dates), +# 'INLAND': [''] * len(dates), +# 'AUSLAND': [''] * len(dates) +# }) + +headers = [['', '', '', '', '', '', '', 'Taggeld', 'Nächtigungen'], + ['Datum', 'Ziel', 'Zweck', 'Std.', 'Inland', 'Ausland', 'Tage', 'Inland', 'Ausland']] +multi_header = pd.MultiIndex.from_arrays(headers) +df.columns = multi_header + +documents_dir = Path.home() / 'Documents' +csv_path = documents_dir / 'zisco' / 'travel_schedule.csv' +os.makedirs(csv_path.parent, exist_ok=True) +df.to_csv(csv_path, index=False) \ No newline at end of file