from __future__ import annotations
from datetime import UTC, date, datetime, timedelta
from pathlib import Path
from xml.sax.saxutils import escape
from zipfile import ZIP_DEFLATED, ZipFile
MAIN_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships"
CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types"
DOC_PROPS_NS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
CORE_PROPS_NS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
DC_NS = "http://purl.org/dc/elements/1.1/"
DCTERMS_NS = "http://purl.org/dc/terms/"
XSI_NS = "http://www.w3.org/2001/XMLSchema-instance"
def date_to_excel_serial(value: date | datetime | str) -> int:
if isinstance(value, str):
parsed = datetime.strptime(value, "%Y-%m-%d").date()
elif isinstance(value, datetime):
parsed = value.date()
else:
parsed = value
return (parsed - date(1899, 12, 30)).days
def _col_name(index: int) -> str:
letters = ""
while index:
index, remainder = divmod(index - 1, 26)
letters = chr(65 + remainder) + letters
return letters
def _cell_xml(value: object, row_index: int, col_index: int) -> str:
ref = f"{_col_name(col_index)}{row_index}"
if value is None or value == "":
return f''
if isinstance(value, bool):
return f'{1 if value else 0}'
if isinstance(value, (int, float)):
return f'{value}'
if isinstance(value, (date, datetime)):
return f'{date_to_excel_serial(value)}'
return f'{escape(str(value))}'
def export_xlsx(path: str | Path, sheet_name: str, rows: list[list[object]]) -> None:
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
max_col = 1
max_row = max(1, len(rows))
sheet_rows = []
for row_index, row in enumerate(rows, start=1):
max_col = max(max_col, len(row))
cells = "".join(_cell_xml(value, row_index, col_index) for col_index, value in enumerate(row, start=1))
sheet_rows.append(f'{cells}
')
dimension = f"A1:{_col_name(max_col)}{max_row}"
worksheet_xml = (
''
f''
f''
''
''
'' + "".join(sheet_rows) + ''
''
)
styles_xml = (
''
f''
''
''
''
''
''
''
''
''
)
content_types_xml = (
''
f''
''
''
''
''
''
''
''
''
)
root_rels_xml = (
''
f''
''
''
''
''
)
workbook_xml = (
''
f''
''
f''
''
)
workbook_rels_xml = (
''
f''
''
''
''
)
created = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
core_xml = (
''
f''
'Jewelry Inventory'
f'{created}'
f'{created}'
''
)
app_xml = (
''
f''
'Jewelry Inventory'
''
)
with ZipFile(path, "w", compression=ZIP_DEFLATED) as archive:
archive.writestr("[Content_Types].xml", content_types_xml)
archive.writestr("_rels/.rels", root_rels_xml)
archive.writestr("docProps/core.xml", core_xml)
archive.writestr("docProps/app.xml", app_xml)
archive.writestr("xl/workbook.xml", workbook_xml)
archive.writestr("xl/_rels/workbook.xml.rels", workbook_rels_xml)
archive.writestr("xl/styles.xml", styles_xml)
archive.writestr("xl/worksheets/sheet1.xml", worksheet_xml)