Several enhancements
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
,markus,Banjul,22.04.2025 16:01,file:///home/markus/.config/libreoffice/4;
|
|
||||||
@@ -6,38 +6,59 @@ import datetime
|
|||||||
import yaml
|
import yaml
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
#TODO: Day-format, Quantity-format
|
||||||
#TODO: Upload automatically!
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Export: # all necessary fields for ELSAP-importfiles
|
class Export: # all necessary fields for ELSAP-importfiles
|
||||||
sum_col: str
|
sum_col: str
|
||||||
desc_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"):
|
if not hasattr(export_timesheet, "row"):
|
||||||
export_timesheet.row = 4
|
export_timesheet.row = 4
|
||||||
with open(f"{csvexport_filename}.csv", 'w') as file:
|
workbook = Workbook()
|
||||||
writer = csv.writer(file, delimiter=';', lineterminator="\n")
|
sheet = workbook.active
|
||||||
|
sheet.title = "Timesheet"
|
||||||
|
|
||||||
period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1)
|
period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1)
|
||||||
writer.writerow(["","","","Current Period",f"{period.strftime('%d.%m.%Y')}"])
|
sheet.append(["", "", "", "Current Period", f"{period.strftime('%d.%m.%Y')}"])
|
||||||
writer.writerow("")
|
sheet.append([])
|
||||||
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"])
|
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
|
# Iterate through time entries
|
||||||
for time_entry in list_of_dicts:
|
for time_entry in list_of_dicts:
|
||||||
date_str_de_de = time_entry.get("date")
|
date_str_de_de = time_entry.get("date")
|
||||||
if date_str_de_de not in ["","Wochensumme","Summe (kumuliert)"]:
|
if date_str_de_de not in ["", "Wochensumme", "Summe (kumuliert)"]:
|
||||||
datetime_object = datetime.datetime.strptime(date_str_de_de, '%a., %d. %B %Y')
|
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)])
|
sum_col_value = time_entry.get(ex.sum_col)
|
||||||
export_timesheet.row += 1
|
|
||||||
|
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)
|
# 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
|
if os.path.getsize(file_path) <= 190: # Adjust this size threshold based on your expected header size
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
print(f"File {file_path} was deleted because it only contained the header.")
|
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"]:
|
for key in ["gems"]:
|
||||||
# extract period out of sheet-name
|
# extract period out of sheet-name
|
||||||
period = datetime.datetime.strptime(cfg['worksheet_name'], "%B %Y").date().replace(day=1)
|
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}",
|
ex1 = Export(sum_col=f"sum_{key}",
|
||||||
desc_col=f"desc_{key}")
|
desc_col=f"desc_{key}")
|
||||||
|
|
||||||
export_timesheet(csvexport_filename=csvexport_filename, ex=ex1)
|
export_timesheet(xlsxexport_filename=xlsxexport_filename, ex=ex1)
|
||||||
@@ -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.")
|
||||||
@@ -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"))
|
||||||
+2
-2
@@ -5,8 +5,8 @@
|
|||||||
'spread': 1.0},
|
'spread': 1.0},
|
||||||
'visualize_params': {'median_threshold': 6},
|
'visualize_params': {'median_threshold': 6},
|
||||||
'spreadsheet_name': 'Zeiterfassung alle Projekte',
|
'spreadsheet_name': 'Zeiterfassung alle Projekte',
|
||||||
'worksheet_name': 'April 2025',
|
'worksheet_name': 'Mai 2025',
|
||||||
'week': [10,11,12,13,14],
|
'week': [22],
|
||||||
'name': 'Markus Zimmerberger',
|
'name': 'Markus Zimmerberger',
|
||||||
'username': 'ZIM9003',
|
'username': 'ZIM9003',
|
||||||
'ansprechpartner_username': 'GRM0003',
|
'ansprechpartner_username': 'GRM0003',
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user