Skip to content

Commit

Permalink
Merge pull request #32 from filips123/feed-and-calendar-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
filips123 authored Sep 1, 2021
2 parents 13b7fd0 + 213fe30 commit c225585
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 15 deletions.
75 changes: 66 additions & 9 deletions API/gimvicurnik/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from .database import Class, Classroom, Document, Entity, LunchMenu, LunchSchedule, Session, SnackMenu, Teacher
from .errors import ConfigError, ConfigParseError, ConfigReadError, ConfigValidationError
from .utils.flask import DateConverter, ListConverter
from .utils.ical import create_calendar
from .utils.ical import create_schedule_calendar, create_school_calendar
from .utils.url import tokenize_url


Expand Down Expand Up @@ -115,6 +115,7 @@ def __init__(self, configfile):
self.convert_date_objects()

self.register_route_converters()
self.register_jinja_filters()
self.register_commands()
self.register_routes()

Expand Down Expand Up @@ -256,6 +257,16 @@ def register_route_converters(self):
self.app.url_map.converters["date"] = DateConverter
self.app.url_map.converters["list"] = ListConverter

def register_jinja_filters(self):
"""Register all custom Jinja filters."""

def format_date(date):
return date.strftime("%d. %m. %Y")

filters = self.app.jinja_env.filters
filters["date_format_daily"] = format_date
filters["date_format_weekly"] = lambda date: f"{format_date(date)}{format_date((date + timedelta(days=4)))}"

def register_commands(self):
"""Register all application commands."""

Expand All @@ -271,7 +282,7 @@ def register_commands(self):
def register_routes(self):
"""Register all application routes."""

def create_feed(filter, name, type, format):
def create_feed(filter, name, type, format, display_date=None, display_date_type="daily"):
query = (
self.session.query(Document.date, Document.type, Document.url, Document.description)
.filter(filter)
Expand All @@ -285,6 +296,8 @@ def create_feed(filter, name, type, format):
type=type,
entries=query,
last_updated=max(model.date for model in query),
display_date=display_date,
display_date_type=display_date_type,
)

return (
Expand Down Expand Up @@ -433,6 +446,7 @@ def _circulars_get_atom():
name="Okrožnice",
type="circulars",
format="atom",
display_date=False,
)

@self.app.route("/feeds/circulars.rss")
Expand All @@ -442,23 +456,52 @@ def _circulars_get_rss():
name="Okrožnice",
type="circulars",
format="rss",
display_date=False,
)

@self.app.route("/feeds/substitutions.atom")
def _substitutions_get_atom():
return create_feed(filter=Document.type == "substitutions", name="Nadomeščanja", type="substitutions", format="atom")
return create_feed(
filter=Document.type == "substitutions",
name="Nadomeščanja",
type="substitutions",
format="atom",
display_date=True,
display_date_type="daily",
)

@self.app.route("/feeds/substitutions.rss")
def _substitutions_get_rss():
return create_feed(filter=Document.type == "substitutions", name="Nadomeščanja", type="substitutions", format="rss")
return create_feed(
filter=Document.type == "substitutions",
name="Nadomeščanja",
type="substitutions",
format="rss",
display_date=True,
display_date_type="daily",
)

@self.app.route("/feeds/schedules.atom")
def _schedules_get_atom():
return create_feed(filter=Document.type == "lunch-schedule", name="Razporedi kosil", type="schedules", format="atom")
return create_feed(
filter=Document.type == "lunch-schedule",
name="Razporedi delitve kosila",
type="schedules",
format="atom",
display_date=True,
display_date_type="daily",
)

@self.app.route("/feeds/schedules.rss")
def _schedules_get_rss():
return create_feed(filter=Document.type == "lunch-schedule", name="Razporedi kosil", type="schedules", format="rss")
return create_feed(
filter=Document.type == "lunch-schedule",
name="Razporedi delitve kosila",
type="schedules",
format="rss",
display_date=True,
display_date_type="daily",
)

@self.app.route("/feeds/menus.atom")
def _menu_get_atom():
Expand All @@ -467,6 +510,8 @@ def _menu_get_atom():
name="Jedilniki",
type="menus",
format="atom",
display_date=True,
display_date_type="weekly",
)

@self.app.route("/feeds/menus.rss")
Expand All @@ -476,11 +521,13 @@ def _menu_get_rss():
name="Jedilniki",
type="menus",
format="rss",
display_date=True,
display_date_type="weekly",
)

@self.app.route("/calendar/combined/<list:classes>")
def _get_calendar_for_classes(classes):
return create_calendar(
return create_school_calendar(
Class.get_substitutions(self.session, None, classes),
Class.get_lessons(self.session, classes),
self.config["hourtimes"],
Expand All @@ -489,7 +536,7 @@ def _get_calendar_for_classes(classes):

@self.app.route("/calendar/timetable/<list:classes>")
def _get_calendar_timetable_for_classes(classes):
return create_calendar(
return create_school_calendar(
Class.get_substitutions(self.session, None, classes),
Class.get_lessons(self.session, classes),
self.config["hourtimes"],
Expand All @@ -499,14 +546,24 @@ def _get_calendar_timetable_for_classes(classes):

@self.app.route("/calendar/substitutions/<list:classes>")
def _get_calendar_substitutions_for_classes(classes):
return create_calendar(
return create_school_calendar(
Class.get_substitutions(self.session, None, classes),
Class.get_lessons(self.session, classes),
self.config["hourtimes"],
f"Nadomeščanja - {', '.join(classes)} - Gimnazija Vič",
timetable=False,
)

@self.app.route("/calendar/schedules/<list:classes>")
def _get_calendar_schedules_for_classes(classes):
return create_schedule_calendar(
self.session.query(LunchSchedule)
.join(Class)
.filter(Class.name.in_(classes))
.order_by(LunchSchedule.time, LunchSchedule.class_),
f"Razporedi delitve kosila - {', '.join(classes)} - Gimnazija Vič",
)


def create_app():
"""Application factory that accepts a configuration file from environment variable."""
Expand Down
5 changes: 4 additions & 1 deletion API/gimvicurnik/templates/atom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

{% for model in entries %}
<entry>
<title>{{ model.description }}</title>
{% if display_date and display_date_type == "daily" %}<title>{{ model.description }}, {{ model.date | date_format_daily }}</title>
{% elif display_date and display_date_type == "weekly" %}<title>{{ model.description }}, {{ model.date | date_format_weekly }}</title>
{% else %}<title>{{ model.description }}</title>
{% endif %}
<link href="{{ model.url if not config["shareToken"] else tokenize_url(model.url, config, token) }}" />
<id>{{ model.url if not config["shareToken"] else tokenize_url(model.url, config, token) }}</id>
<updated>{{ model.date.strftime("%Y-%m-%dT%H:%M:%S%z") }}</updated>
Expand Down
5 changes: 4 additions & 1 deletion API/gimvicurnik/templates/rss.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

{% for model in entries %}
<item>
<title>{{ model.description }}</title>
{% if display_date and display_date_type == "daily" %}<title>{{ model.description }}, {{ model.date | date_format_daily }}</title>
{% elif display_date and display_date_type == "weekly" %}<title>{{ model.description }}, {{ model.date | date_format_weekly }}</title>
{% else %}<title>{{ model.description }}</title>
{% endif %}
<link>{{ model.url if not config["shareToken"] else tokenize_url(model.url, config, token) }}</link>
<guid>{{ model.url if not config["shareToken"] else tokenize_url(model.url, config, token) }}</guid>
<pubDate>{{ model.date.strftime("%a, %d %b %Y %H:%M:%S %Z") }}</pubDate>
Expand Down
51 changes: 48 additions & 3 deletions API/gimvicurnik/utils/ical.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def datecount(start_date, i):


@with_span(op="generate")
def create_calendar(details, timetables, hours, name, timetable=True, substitutions=True):
def create_school_calendar(details, timetables, hours, name, timetable=True, substitutions=True):
logger = logging.getLogger(__name__)

calendar = Calendar()
Expand Down Expand Up @@ -93,7 +93,6 @@ def create_calendar(details, timetables, hours, name, timetable=True, substituti
logger.info("Preparing iCalendar event", extra={"type": "substitution", "source": subject})

event = Event()

event.add("dtstamp", datetime.now())
event.add("CATEGORIES", vText("SUBSTITUTION"))
event.add("COLOR", vText("darkred"))
Expand Down Expand Up @@ -132,5 +131,51 @@ def create_calendar(details, timetables, hours, name, timetable=True, substituti

response = make_response(calendar.to_ical().decode("utf-8").replace("\\", ""))
response.headers["Content-Disposition"] = "attachment; filename=calendar.ics"
response.headers["Content-Type"] = "text/calendar"
response.headers["Content-Type"] = "text/calendar; charset=utf-8"
return response


@with_span(op="generate")
def create_schedule_calendar(query, name):
logger = logging.getLogger(__name__)

calendar = Calendar()
calendar.add("prodid", "gimvicurnik")
calendar.add("version", "2.0")
calendar.add("X-WR-TIMEZONE", "Europe/Ljubljana")
calendar.add("X-WR-CALNAME", name)
calendar.add("X-WR-CALDESC", name)
calendar.add("NAME", name)
calendar.add("X-PUBLISHED-TTL", vDuration(timedelta(hours=12)))
calendar.add("REFRESH-INTERVAL", vDuration(timedelta(hours=12)))

for model in query:
with start_span(op="event") as span:
span.set_tag("event.type", "lunch-schedule")
span.set_tag("event.date", model.date)
span.set_tag("event.time", model.time)
span.set_data("event.source", model)

logger.info("Preparing iCalendar event", extra={"type": "lunch-schedule", "source": model})

event = Event()
event.add("dtstamp", datetime.now())
event.add("CATEGORIES", vText("LUNCH"))
event.add("COLOR", vText("darkblue"))
event.add(
"UID",
sha256((str(model.date) + str(model.time) + str(model.class_.name) + str(model.location)).encode()).hexdigest(),
)

event.add("summary", "Kosilo")
event.add("description", model.notes or "")
event.add("location", vText(model.location))
event.add("dtstart", datetime.combine(model.date, model.time))
event.add("dtend", datetime.combine(model.date, model.time) + timedelta(minutes=15))

calendar.add_component(event)

response = make_response(calendar.to_ical().decode("utf-8").replace("\\", ""))
response.headers["Content-Disposition"] = "attachment; filename=calendar.ics"
response.headers["Content-Type"] = "text/calendar; charset=utf-8"
return response
3 changes: 2 additions & 1 deletion website/src/views/Subscribe.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<url-display label="Okrožnice" :value="`${vueAppApi}/feeds/circulars.atom`"></url-display>
<url-display label="Nadomeščanja" :value="`${vueAppApi}/feeds/substitutions.atom`"></url-display>
<url-display label="Jedilniki" :value="`${vueAppApi}/feeds/menus.atom`"></url-display>
<url-display label="Razporedi kosil" :value="`${vueAppApi}/feeds/schedules.atom`"></url-display>
<url-display label="Razporedi delitve kosila" :value="`${vueAppApi}/feeds/schedules.atom`"></url-display>
</div>

<div class="pt-6" ref="calendarLinks">
<h2 class="text-h5 pb-4">Koledar</h2>
<url-display label="Urnik & Nadomeščanja" :value="`${vueAppApi}/calendar/combined/${selectedEntity}`"></url-display>
<url-display label="Urnik" :value="`${vueAppApi}/calendar/timetable/${selectedEntity}`"></url-display>
<url-display label="Nadomeščanja" :value="`${vueAppApi}/calendar/substitutions/${selectedEntity}`"></url-display>
<url-display label="Razporedi delitve kosila" :value="`${vueAppApi}/calendar/schedules/${selectedEntity}`"></url-display>
</div>
</div>
</template>
Expand Down

0 comments on commit c225585

Please sign in to comment.