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:
2024-12-15 08:42:51 -06:00
parent dc90143c09
commit 9a180f973b
18 changed files with 110 additions and 112 deletions

1
.gitignore vendored
View File

@@ -58,4 +58,5 @@ ENV/
.idea/**/uiDesigner.xml
.idea/**/gradle.xml
.idea/**/libraries
.idea/**
*.iws /out/

0
.tmp Normal file
View File

View File

@@ -1,9 +1,9 @@
FROM tiangolo/uwsgi-nginx-flask
ENV STATIC_URL /static
ENV STATIC_PATH /var/www/app/static
ENV cal_id 9E2AC562-4328-4CA0-B4D1-D730D9F5E9EF
ENV username a@correa.co
ENV password jyxq-avwz-qxfd-dklo
ENV caldav_url https://caldav.icloud.com/
# Install system dependencies and wkhtmltoimage
RUN apt-get update && apt-get install -y \
wkhtmltopdf \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
COPY ./requirements.txt /var/www/requirements.txt
COPY ./server ./app
RUN pip install -r /var/www/requirements.txt

View File

@@ -1,3 +0,0 @@
from flask import Flask
app = Flask(__name__)
from app import views

View File

@@ -1,95 +0,0 @@
@import url('fonts/dinpro/dinpro.css');
@import url('fonts/noto-emoji.css');
@import url('fonts/openmoji.css');
@import url('fonts/weather-icons/weather-icons.css');
@font-face {
font-family: "Hellovetica";
src: url("fonts/hellovetica.ttf") format("truetype");
}
@font-face {
font-family: "Helvetica";
}
.dashboard {
font-family: "Helvetica", sans-serif;
height: 699px;
width: 600px;
text-align: left;
font-size: 17px;
}
.panel {
position: relative;
height: 50%;
verical-align: top;
/* height: 250px; */
width: 100%;
}
.panel.top {
height: 35%;
}
.panel.bottom {
height: 65%;
}
.subpanel {
position: relative;
height:100%;
width:48%;
display: inline-block;
text-align: center;
}
.weather-icon {
position: relative;
height: 100%;
width: 100%;
object-fit: scale-down;
}
.week {
text-align: center;
}
.day {
border-style: solid;
display: inline-block;
min-height: 220px;
max-height: 220px;
width: 135px;
vertical-align: top;
margin: 1px;
align-items: center;
text-align: left;
}
.day-title{
background-color: black;
text-align: center;
color: white;
text-transform: uppercase;
font-weight: bold;
}
.event {
margin: 3pt;
background-color: white;
border-style: solid;
border-radius: 5px;
padding: 2px;
font-family: "Helvetica";
}
.daterange {
font-size: smaller;
}
.weather-icon {
font-size: 200px;
}

10
compose.yml Normal file
View File

@@ -0,0 +1,10 @@
services:
webcal:
build: .
env_file:
- .env
ports:
- "56733:80"
volumes:
- ./server:/app

View File

@@ -1 +0,0 @@
from app import app

BIN
out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -1,4 +1,7 @@
caldav~=0.9.0
requests~=2.27.1
icalendar~=4.0.9
Flask~=2.1.2
Flask~=2.1.2
Werkzeug==2.2.2
imgkit
pillow

8
server/app/__init__.py Normal file
View File

@@ -0,0 +1,8 @@
# app/__init__.py
from flask import Flask
# Create Flask app
app = Flask(__name__,static_url_path='', static_folder='static')
# Import views to register routes
from app import views

0
server/app/main.py Normal file
View File

View File

@@ -2,10 +2,12 @@
<html>
<head>
<title>Dashboard ({{today.strftime('%-m/%-d, %-H:%M')}})</title>
<link rel="stylesheet" href="static/style.css">
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=600, height=800" />
</head>
<body>
<div class="dashboard">
<div style="height:1em"></div>
<div class="panel top">
<div class="subpanel">
<div><i class="wi wi-owm-{{ weather.id }} weather-icon"></i></div>

View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<title>Dashboard ({{today.strftime('%-m/%-d, %-H:%M')}})</title>
<link rel="stylesheet" href="/style.css">
<meta name="viewport" content="width=600, height=800" />
</head>
<body>
<div class="dashboard">
<div style="height:1em"></div>
<div class="panel top">
<div class="subpanel">
<div><i class="wi wi-owm-{{ weather.id }} weather-icon"></i></div>
<div>{{weather.main}}</div>
</div>
<div class="subpanel" style="font-size: 60px;font-weight: bold;vertical-align:top; text-align:right;">
{{ today.strftime('%a') }}<br>
{{ today.strftime('%b %-d') }}<br>
{{ today.strftime('%Y') }}<br>
</div>
</div>
<div class="panel bottom week">
{% for day, events in days %}
<div class="day">
<div id="dotw-1" class="day-title">
{{ day.strftime('%A') }}
</div>
{% for event in events %}
<div class="event">
{{ event.summary }}<br>
{% if not event.is_all_day %}
<span class="daterange">{{ event.range_str }}</span>
{% endif %}
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</body>
</html>

View File

@@ -4,9 +4,12 @@ import os
import caldav
import datetime
from icalendar import cal, Event
from flask import render_template
from flask import render_template, url_for, send_file, send_from_directory
from .models import Event
import requests
import imgkit
from PIL import Image, ImageOps
import io
caldav_url = os.getenv('caldav_url')
username = os.getenv('username')
password = os.getenv('password')
@@ -88,9 +91,38 @@ def dashboard():
# breakpoint()
pass
# r = "<br>".join([event.vobject_instance.vevent.summary.value for event in events_fetched if event.vobject_instance.vevent.dtstart.value < datetime.now()])
return render_template("dashboard.html",
return render_template("dashboard_static.html",
days=days,
today=today,
weather=weather()
)
@app.route('/image')
def dashboard_image():
page = dashboard()
out_file = os.path.join(os.path.dirname(__file__),
'static', "out.bmp"
)
s = imgkit.from_string(
page,
out_file,
options={
'width':600,
'height':800,
"disable-smart-width": "",
'enable-local-file-access': "",
'allow': os.path.join(os.path.dirname(__file__),
'static',
)
},
css=os.path.join(os.path.dirname(__file__),
'static',
'style.css'
)
)
image_file = Image.open(out_file) # open colour image
image_file = ImageOps.grayscale(image_file)
image_file.save(out_file, 'BMP')
return send_from_directory(directory = 'static', path='out.bmp', as_attachment=False, attachment_filename='out.bmp')

View File

@@ -10,8 +10,7 @@ URL = "https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid
def weather(api_key=os.getenv('openweathermap_api_key'), lat="41.98", lon="-87.90"):
url = URL.format(api_key=api_key, lat=lat, lon=lon)
data = json.loads(requests.get(url).content)
return data['weather'][0]
f=weather()
# f=weather()
pass

View File

@@ -1,4 +1,4 @@
[uwsgi]
module = main
module = app
callable = app
master = true

BIN
test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB