Updated design and backend
This commit is contained in:
parent
b5d909f832
commit
8b9ce75959
7 changed files with 158 additions and 67 deletions
|
@ -1,6 +1,7 @@
|
|||
import argparse
|
||||
import configparser
|
||||
import logging
|
||||
import locale
|
||||
## dev imports
|
||||
from pprint import pp
|
||||
import sys
|
||||
|
@ -23,6 +24,8 @@ args = parser.parse_args()
|
|||
config = configparser.ConfigParser()
|
||||
config.read(args.config)
|
||||
|
||||
locale.setlocale(locale.LC_ALL, config.get('locale', 'locale', fallback=None))
|
||||
|
||||
logging.basicConfig(level=config.get('logging', 'level', fallback='WARN'))
|
||||
log = logging.getLogger('saturn')
|
||||
|
||||
|
@ -30,7 +33,9 @@ log.info('Starting saturn')
|
|||
|
||||
|
||||
davclient = get_davclient(config['caldav'])
|
||||
app = create_app(davclient)
|
||||
app = create_app(
|
||||
davclient,
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import re
|
||||
from datetime import date, datetime, time
|
||||
|
||||
import caldav
|
||||
|
||||
from datetime import date, datetime
|
||||
from .event import Event
|
||||
|
||||
|
||||
def get_davclient(config):
|
||||
|
@ -10,6 +13,15 @@ def get_davclient(config):
|
|||
password=config.get('password', fallback=None),
|
||||
)
|
||||
|
||||
def get_comparable_dt(event):
|
||||
if type(event.dtstart.value) == date:
|
||||
return datetime.combine(
|
||||
event.dtstart.value,
|
||||
time.min,
|
||||
).timestamp()
|
||||
else:
|
||||
return event.dtstart.value.timestamp()
|
||||
|
||||
def get_events(davclient):
|
||||
pri = davclient.principal()
|
||||
|
||||
|
@ -18,12 +30,7 @@ def get_events(davclient):
|
|||
|
||||
for cal in pri.calendars():
|
||||
events += [event.instance.vevent
|
||||
for event in cal.date_search(start=date.today())
|
||||
if type(event.instance.vevent.dtstart.value) == datetime]
|
||||
events_day += [event.instance.vevent
|
||||
for event in cal.date_search(start=date.today())
|
||||
if type(event.instance.vevent.dtstart.value) != datetime]
|
||||
events.sort(key=lambda event:event.dtstart.value)
|
||||
events_day.sort(key=lambda event:event.dtstart.value)
|
||||
for event in cal.date_search(start=date.today())]
|
||||
events.sort(key=get_comparable_dt)
|
||||
|
||||
return events, events_day
|
||||
return [Event(event) for event in events]
|
||||
|
|
47
saturn/calendar/event.py
Normal file
47
saturn/calendar/event.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
import re
|
||||
|
||||
|
||||
class Event:
|
||||
def __init__(
|
||||
self,
|
||||
vobject,
|
||||
):
|
||||
for attr in [
|
||||
'dtstart',
|
||||
'dtend',
|
||||
'summary',
|
||||
'description',
|
||||
'location',
|
||||
'rrule',
|
||||
]:
|
||||
setattr(self, f'_{attr}',
|
||||
getattr(getattr(vobject, attr, None), 'value', None))
|
||||
|
||||
@property
|
||||
def dtstart(self):
|
||||
return self._dtstart
|
||||
|
||||
@property
|
||||
def dtend(self):
|
||||
return self._dtend
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
return self._summary
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return self._description
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
return self._location
|
||||
|
||||
@property
|
||||
def freq(self):
|
||||
if self._rrule is None: return None
|
||||
return re.search(
|
||||
r'FREQ=([A-Z]+?);',
|
||||
self._rrule,
|
||||
).group(1)
|
||||
|
|
@ -4,17 +4,18 @@ from datetime import date, datetime
|
|||
from ..calendar import get_events
|
||||
|
||||
|
||||
def create_app(davclient):
|
||||
def create_app(
|
||||
davclient,
|
||||
):
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def home():
|
||||
events, events_day = get_events(davclient)
|
||||
events = get_events(davclient)
|
||||
|
||||
return flask.render_template(
|
||||
'index.html',
|
||||
events = events,
|
||||
events_day = events_day,
|
||||
)
|
||||
|
||||
return app
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
body {
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
1fr
|
||||
1fr;
|
||||
grid-template-rows:
|
||||
auto
|
||||
|
@ -22,13 +21,10 @@ body > * {
|
|||
margin: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns:
|
||||
1fr
|
||||
6rem
|
||||
1fr;
|
||||
grid-template-rows:
|
||||
auto
|
||||
auto
|
||||
auto;
|
||||
gap: .5rem;
|
||||
row-gap: .5rem;
|
||||
column-gap: 1rem;
|
||||
background: var(--ui-01);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
@ -39,23 +35,33 @@ body > * {
|
|||
grid-column: 1 / span 2;
|
||||
}
|
||||
.event > .date {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
font-size: 1.1rem;
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.event > .start-date {
|
||||
grid-column: 1;
|
||||
text-align: right;
|
||||
.event > .date > div {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
.event > .end-date {
|
||||
.date .day {
|
||||
font-size: 3em;
|
||||
}
|
||||
.date .time {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.event .end-date,
|
||||
.event .other {
|
||||
color: var(--text-02);
|
||||
}
|
||||
.event > .info {
|
||||
grid-column: 2;
|
||||
}
|
||||
.event > .description {
|
||||
grid-column: 1;
|
||||
}
|
||||
.event > .other {
|
||||
grid-column: 2;
|
||||
}
|
||||
.event > .description,
|
||||
.event > .other > * {
|
||||
padding-top: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows:
|
||||
1fr
|
||||
auto;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
--ui-background: white;
|
||||
--ui-01: var(--gray-10);
|
||||
--text-01: var(--gray-100);
|
||||
--text-02: var(--gray-70);
|
||||
|
||||
--gray-10: #f4f4f4;
|
||||
--gray-70: #525252;
|
||||
--gray-100: #161616;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{% block style %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="/static/css/index.css" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/remixicon/fonts/remixicon.css" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
@ -11,41 +12,63 @@
|
|||
<div id="events">
|
||||
{% for event in events %}
|
||||
<div class="event">
|
||||
<div class="summary">{{ event.summary.value }}</div>
|
||||
<div class="date start-date">{{ event.dtstart.value.isoformat() }}</div>
|
||||
<div class="date end-date">{{ event.dtend.value.isoformat() }}</div>
|
||||
{% if event.description %}
|
||||
<div class="description">{{ event.description.value }}</div>
|
||||
<div class="summary">{{ event.summary }}</div>
|
||||
<div class="date">
|
||||
<div class="start-date">
|
||||
<span class="weekday">
|
||||
{{ event.dtstart.strftime('%A').capitalize() }}
|
||||
</span>
|
||||
<span class="day">
|
||||
{{ event.dtstart.strftime('%d') }}
|
||||
</span>
|
||||
<span class="month">
|
||||
{{ event.dtstart.strftime('%B').capitalize() }}
|
||||
</span>
|
||||
<span class="time">
|
||||
{{ event.dtstart.strftime('%H:%M') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="end-date">
|
||||
{% if not event.dtend.date or event.dtend.date() != event.dtstart.date() %}
|
||||
<span class="weekday">
|
||||
{{ event.dtend.strftime('%A').capitalize() }}
|
||||
</span>
|
||||
<span class="day">
|
||||
{{ event.dtend.strftime('%d') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="other">
|
||||
{% if event.location %}
|
||||
<div>{{ event.location.value }}</div>
|
||||
{% endif %}
|
||||
{% if event.rrule %}
|
||||
<div>{{ event.rrule.value }}</div>
|
||||
{% if event.dtend.month != event.dtstart.month %}
|
||||
<span class="month">
|
||||
{{ event.dtend.strftime('%B').capitalize() }}
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="time">
|
||||
{{ event.dtend.strftime('%H:%M') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="events_day">
|
||||
{% for event in events_day %}
|
||||
<div class="event">
|
||||
<div class="summary">{{ event.summary.value }}</div>
|
||||
<div class="date start-date">{{ event.dtstart.value }}</div>
|
||||
<div class="date end-date">{{ event.dtend.value }}</div>
|
||||
<div class="info">
|
||||
<div class="description">
|
||||
{% if event.description %}
|
||||
<div class="description">{{ event.description.value }}</div>
|
||||
{{ event.description }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="other">
|
||||
{% if event.location %}
|
||||
<div>{{ event.location.value }}</div>
|
||||
<div>
|
||||
<i class="ri-map-pin-line ri-fw ri-sm"></i>
|
||||
{{ event.location }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if event.rrule %}
|
||||
<div>{{ event.rrule.value }}</div>
|
||||
{% if event.freq %}
|
||||
<div>
|
||||
<i class="ri-repeat-line ri-fw ri-sm"></i>
|
||||
{{ event.freq}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
Reference in a new issue