Add calendar generator
This commit is contained in:
parent
d574d938da
commit
ac8055314c
2 changed files with 220 additions and 0 deletions
219
generate_calendar.py
Normal file
219
generate_calendar.py
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Configurable Calendar Generator for ODS format
|
||||||
|
Usage: python3 generate_calendar.py [year] [output_file]
|
||||||
|
Example: python3 generate_calendar.py 2026 calendar_2026.ods
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import zipfile
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def create_calendar_ods(year, output_file):
|
||||||
|
month_names = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||||
|
'July', 'August', 'September', 'October', 'November', 'December']
|
||||||
|
day_headers = ['M', 'T', 'W', 'T', 'F', 'S', 'S']
|
||||||
|
|
||||||
|
def get_month_calendar(year, month):
|
||||||
|
"""Returns list of weeks for a month, each week has 7 days (0 = empty)"""
|
||||||
|
first_day = datetime(year, month, 1)
|
||||||
|
first_weekday = first_day.weekday() # 0=Monday, 6=Sunday
|
||||||
|
|
||||||
|
if month == 12:
|
||||||
|
last_day = 31
|
||||||
|
else:
|
||||||
|
last_day = (datetime(year, month + 1, 1) - timedelta(days=1)).day
|
||||||
|
|
||||||
|
weeks = []
|
||||||
|
week = [0] * first_weekday
|
||||||
|
|
||||||
|
for day in range(1, last_day + 1):
|
||||||
|
week.append(day)
|
||||||
|
if len(week) == 7:
|
||||||
|
weeks.append(week)
|
||||||
|
week = []
|
||||||
|
|
||||||
|
if week:
|
||||||
|
week.extend([0] * (7 - len(week)))
|
||||||
|
weeks.append(week)
|
||||||
|
|
||||||
|
return weeks
|
||||||
|
|
||||||
|
# Generate calendar for all months
|
||||||
|
month_rows_xml = ""
|
||||||
|
|
||||||
|
for row in range(3):
|
||||||
|
start_month = row * 4 + 1
|
||||||
|
months_in_row = [start_month, start_month + 1, start_month + 2, start_month + 3]
|
||||||
|
|
||||||
|
# Title row
|
||||||
|
month_rows_xml += ' <table:table-row style:style="ro1">\n'
|
||||||
|
for month in months_in_row:
|
||||||
|
month_rows_xml += f' <table:table-cell style:style="ce_header" table:number-columns-spanned="7"><text:p>{month_names[month-1]} {year}</text:p></table:table-cell>\n'
|
||||||
|
month_rows_xml += ' <table:table-cell/>\n'
|
||||||
|
month_rows_xml += ' </table:table-row>\n'
|
||||||
|
|
||||||
|
# Day headers
|
||||||
|
month_rows_xml += ' <table:table-row style:style="ro1">\n'
|
||||||
|
for month in months_in_row:
|
||||||
|
for day_header in day_headers:
|
||||||
|
month_rows_xml += f' <table:table-cell style:style="ce_dayheader"><text:p>{day_header}</text:p></table:table-cell>\n'
|
||||||
|
month_rows_xml += ' <table:table-cell/>\n'
|
||||||
|
month_rows_xml += ' </table:table-row>\n'
|
||||||
|
|
||||||
|
# Get calendar data for all months
|
||||||
|
month_cals = [get_month_calendar(year, m) for m in months_in_row]
|
||||||
|
max_weeks = max(len(cal) for cal in month_cals)
|
||||||
|
|
||||||
|
# Days
|
||||||
|
for week_idx in range(max_weeks):
|
||||||
|
month_rows_xml += ' <table:table-row style:style="ro2">\n'
|
||||||
|
|
||||||
|
for month_idx, month in enumerate(months_in_row):
|
||||||
|
if week_idx < len(month_cals[month_idx]):
|
||||||
|
week = month_cals[month_idx][week_idx]
|
||||||
|
for day_idx, day in enumerate(week):
|
||||||
|
if day == 0:
|
||||||
|
month_rows_xml += ' <table:table-cell style:style="ce_empty"><text:p></text:p></table:table-cell>\n'
|
||||||
|
else:
|
||||||
|
# Weekend is Saturday (day_idx=5) and Sunday (day_idx=6)
|
||||||
|
if day_idx >= 5:
|
||||||
|
month_rows_xml += f' <table:table-cell style:style="ce_weekend"><text:p>{day}</text:p></table:table-cell>\n'
|
||||||
|
else:
|
||||||
|
month_rows_xml += f' <table:table-cell style:style="ce_day"><text:p>{day}</text:p></table:table-cell>\n'
|
||||||
|
else:
|
||||||
|
for _ in range(7):
|
||||||
|
month_rows_xml += ' <table:table-cell style:style="ce_empty"><text:p></text:p></table:table-cell>\n'
|
||||||
|
|
||||||
|
month_rows_xml += ' <table:table-cell/>\n'
|
||||||
|
|
||||||
|
month_rows_xml += ' </table:table-row>\n'
|
||||||
|
|
||||||
|
month_rows_xml += ' <table:table-row style:style="ro1"><table:table-cell/></table:table-row>\n'
|
||||||
|
|
||||||
|
content_xml = f'''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||||
|
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||||
|
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
||||||
|
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
||||||
|
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
|
||||||
|
office:version="1.2">
|
||||||
|
<office:font-face-decls>
|
||||||
|
<style:font-face style:name="Liberation Sans" svg:font-family="Liberation Sans"/>
|
||||||
|
</office:font-face-decls>
|
||||||
|
<office:styles>
|
||||||
|
<style:style style:name="co1" style:family="table-column">
|
||||||
|
<style:table-column-properties style:column-width="1.4cm"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ro1" style:family="table-row">
|
||||||
|
<style:table-row-properties style:row-height="0.4cm"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ro2" style:family="table-row">
|
||||||
|
<style:table-row-properties style:row-height="1.5cm"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ce_header" style:family="table-cell">
|
||||||
|
<style:table-cell-properties fo:background-color="#1F4E78" fo:border="0.05cm solid #1F4E78"/>
|
||||||
|
<style:text-properties fo:font-size="11pt" fo:font-weight="bold" fo:color="#ffffff" style:text-align="center"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ce_dayheader" style:family="table-cell">
|
||||||
|
<style:table-cell-properties fo:background-color="#d3d3d3" fo:border="0.05cm solid #999999"/>
|
||||||
|
<style:text-properties fo:font-size="8pt" fo:font-weight="bold" style:text-align="center"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ce_day" style:family="table-cell">
|
||||||
|
<style:table-cell-properties fo:border="0.05cm solid #cccccc" style:vertical-align="top"/>
|
||||||
|
<style:text-properties fo:font-size="11pt" style:text-align="center"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ce_weekend" style:family="table-cell">
|
||||||
|
<style:table-cell-properties fo:border="0.05cm solid #cccccc" fo:background-color="#ffffcc" style:vertical-align="top"/>
|
||||||
|
<style:text-properties fo:font-size="11pt" fo:color="#0066cc" style:text-align="center"/>
|
||||||
|
</style:style>
|
||||||
|
<style:style style:name="ce_empty" style:family="table-cell">
|
||||||
|
<style:table-cell-properties fo:border="0.05cm solid #cccccc" fo:background-color="#ffffff"/>
|
||||||
|
</style:style>
|
||||||
|
</office:styles>
|
||||||
|
<office:body>
|
||||||
|
<office:spreadsheet>
|
||||||
|
<table:table table:name="Calendar">
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
<table:table-column style:style="co1"/>
|
||||||
|
{month_rows_xml}
|
||||||
|
</table:table>
|
||||||
|
</office:spreadsheet>
|
||||||
|
</office:body>
|
||||||
|
</office:document-content>'''
|
||||||
|
|
||||||
|
manifest_xml = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
|
||||||
|
<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:full-path="/"/>
|
||||||
|
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
|
||||||
|
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
|
||||||
|
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
|
||||||
|
</manifest:manifest>'''
|
||||||
|
|
||||||
|
styles_xml = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||||
|
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||||
|
office:version="1.2">
|
||||||
|
</office:document-styles>'''
|
||||||
|
|
||||||
|
meta_xml = f'''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
|
||||||
|
office:version="1.2">
|
||||||
|
<office:meta>
|
||||||
|
<dc:title>Calendar {year}</dc:title>
|
||||||
|
<meta:creation-date>{datetime.now().isoformat()}</meta:creation-date>
|
||||||
|
</office:meta>
|
||||||
|
</office:document-meta>'''
|
||||||
|
|
||||||
|
with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as ods:
|
||||||
|
ods.writestr('mimetype', 'application/vnd.oasis.opendocument.spreadsheet', compress_type=zipfile.ZIP_STORED)
|
||||||
|
ods.writestr('META-INF/manifest.xml', manifest_xml)
|
||||||
|
ods.writestr('content.xml', content_xml)
|
||||||
|
ods.writestr('styles.xml', styles_xml)
|
||||||
|
ods.writestr('meta.xml', meta_xml)
|
||||||
|
|
||||||
|
print(f"✓ Calendar for {year} created: {output_file}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
year = int(input("Enter year: "))
|
||||||
|
output = f"calendar_{year}.ods"
|
||||||
|
else:
|
||||||
|
year = int(sys.argv[1])
|
||||||
|
output = sys.argv[2] if len(sys.argv) > 2 else f"calendar_{year}.ods"
|
||||||
|
|
||||||
|
try:
|
||||||
|
create_calendar_ods(year, output)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
sys.exit(1)
|
||||||
1
lua/datetime.lua
Normal file
1
lua/datetime.lua
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
print(os.date("%Y-%m-%d"))
|
||||||
Loading…
Reference in a new issue