Here’s a detailed commit message based on the provided diff:
Commit Message: feat: Implement web calendar application with Flask, Docker, and calendar integration Description: 1. Server Refactor: • Moved application logic from main.py to a structured directory server/app/. • Added server/app/__init__.py for Flask app initialization. • Introduced server/app/views.py to handle routes (dashboard and dashboard_image). • Created server/app/models.py for event modeling, supporting CalDAV and iCalendar events. • Added server/app/weather.py to fetch weather data using OpenWeatherMap API. 2. New Features: • Added an image generation route (/image) to render calendar views as BMP images. • Integrated OpenWeatherMap API for weather data on the dashboard. 3. Environment and Configurations: • Added a Dockerfile to build and deploy the app using uwsgi-nginx-flask. • Introduced compose.yml for running the app with Docker Compose. • Moved uwsgi.ini configuration to server/uwsgi.ini for modular organization. 4. Dependencies: • Updated requirements.txt to include new dependencies: imgkit, pillow, and Werkzeug==2.2.2. 5. Static Assets: • Added placeholder images out.png and test.png. 6. Code Cleanup: • Removed old files (main.py and root-level uwsgi.ini). • Updated .gitignore to include .idea/ folder. Additional Notes: • Enhanced event parsing to handle all-day and time-specific events using server/app/models.py. • Utilized Flask’s render_template for dynamic HTML rendering and imgkit for HTML-to-image conversion. • Integrated multiple calendar sources (CalDAV and public iCal feeds). Let me know if you need further adjustments!
This commit is contained in:
98
server/app/models.py
Normal file
98
server/app/models.py
Normal file
@@ -0,0 +1,98 @@
|
||||
from datetime import datetime, date, timedelta, timezone
|
||||
from caldav.objects import Event as CaldavEvent
|
||||
import dataclasses
|
||||
|
||||
from icalendar import Event as IcEvent
|
||||
from icalendar import Calendar as Icalendar
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Event():
|
||||
summary: str
|
||||
dtstart: datetime
|
||||
dtend: datetime
|
||||
is_all_day: bool
|
||||
|
||||
@classmethod
|
||||
def fromCalDavEvents(cls, caldav_events: [CaldavEvent]) -> list['Event']:
|
||||
|
||||
events = []
|
||||
|
||||
for event in caldav_events:
|
||||
date_value = event.vobject_instance.vevent.dtstart.value
|
||||
if isinstance(date_value, datetime):
|
||||
dt_start = date_value
|
||||
is_all_day = False
|
||||
elif isinstance(date_value, date):
|
||||
d_start = date_value
|
||||
dt_start = datetime(d_start.year, d_start.month, d_start.day, tzinfo=datetime.now(timezone.utc).astimezone().tzinfo)
|
||||
is_all_day = True
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
date_value = event.vobject_instance.vevent.dtend.value
|
||||
if isinstance(date_value, datetime):
|
||||
dt_end = date_value
|
||||
elif isinstance(date_value, date):
|
||||
d_end = date_value - timedelta(days=1)
|
||||
dt_end = datetime(d_end.year, d_end.month, d_end.day,tzinfo=datetime.now(timezone.utc).astimezone().tzinfo)
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
events.append(Event(
|
||||
summary=event.vobject_instance.vevent.summary.value,
|
||||
dtstart=dt_start,
|
||||
dtend=dt_end,
|
||||
is_all_day = is_all_day
|
||||
))
|
||||
return events
|
||||
|
||||
@classmethod
|
||||
def fromIcalendar(cls, icalendar: Icalendar) -> list['Event']:
|
||||
events = []
|
||||
for event in [e for e in icalendar.subcomponents if isinstance(e, IcEvent)]:
|
||||
date_value = event['DTSTART'].dt
|
||||
if isinstance(date_value, datetime):
|
||||
dt_start = date_value
|
||||
is_all_day = False
|
||||
elif isinstance(date_value, date):
|
||||
d_start = date_value
|
||||
dt_start = datetime(d_start.year, d_start.month, d_start.day, tzinfo=datetime.now(timezone.utc).astimezone().tzinfo)
|
||||
is_all_day = True
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
date_value = event['DTEND'].dt if event.get('DTEND') else event['DTSTART'].dt
|
||||
if isinstance(date_value, datetime):
|
||||
dt_end = date_value
|
||||
elif isinstance(date_value, date):
|
||||
d_end = date_value
|
||||
dt_end = datetime(d_end.year, d_end.month, d_end.day,tzinfo=datetime.now(timezone.utc).astimezone().tzinfo)
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
events.append(Event(
|
||||
summary=event['summary'],
|
||||
dtstart=dt_start,
|
||||
dtend=dt_end,
|
||||
is_all_day = is_all_day
|
||||
))
|
||||
|
||||
return events
|
||||
|
||||
@property
|
||||
def range_str(self) -> str:
|
||||
start_time_str = self.dtstart.strftime('%-I')
|
||||
end_time_str = self.dtend.strftime('%-I')
|
||||
|
||||
if not(self.dtstart.hour and self.dtend.hour):
|
||||
return ""
|
||||
|
||||
if self.dtstart.minute: start_time_str += self.dtstart.strftime(':%M')
|
||||
if self.dtend.minute: end_time_str += self.dtend.strftime(':%M')
|
||||
|
||||
if not ((self.dtstart.hour < 12 and self.dtend.hour < 12) or (self.dtstart.hour > 12 and self.dtend.hour > 12)):
|
||||
start_time_str += self.dtstart.strftime("%p")
|
||||
|
||||
end_time_str += self.dtend.strftime("%p")
|
||||
|
||||
return f"{start_time_str}-{end_time_str}"
|
||||
Reference in New Issue
Block a user