[Jinja2] init
This commit is contained in:
5
Makefile
5
Makefile
@@ -2,11 +2,11 @@
|
||||
|
||||
help:
|
||||
@echo "Available commands:"
|
||||
@echo " make prep - Create venv and install requirements"
|
||||
@echo " make build - Full website build sequence"
|
||||
@echo " make events - Update site from CSV (build pages + images)"
|
||||
@echo " make dev - Start development server"
|
||||
@echo " make stop - Stop development server"
|
||||
@echo " make build - Full website build sequence"
|
||||
@echo " make prep - Create venv and install requirements"
|
||||
@echo " make help - Show this help message"
|
||||
|
||||
prep:
|
||||
@@ -28,3 +28,4 @@ dev:
|
||||
|
||||
stop:
|
||||
nginx -p . -s stop
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! /usr/bin/env python3
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
import os
|
||||
|
||||
PAGES = [
|
||||
@@ -14,34 +14,52 @@ PAGES = [
|
||||
{'name': 'deconference', 'titleSR': 'Dekonferencija', 'titleEN': 'Deconference', 'style': 'deconference'},
|
||||
]
|
||||
|
||||
def buildPage(filename: str, pageTitle: str, pageHtml: str, pageStyle: str, template: str) -> str:
|
||||
template = template.replace('<!--TITLE-->', pageTitle)
|
||||
style = '' if not pageStyle else f'<link rel=\"stylesheet\" href=\"/styles/{pageStyle}.css\">'
|
||||
template = template.replace('<!--ADDITIONAL_STYLE-->', style)
|
||||
template = template.replace('PAGE_NAME', filename)
|
||||
template = template.replace('<!--MAIN-->', pageHtml)
|
||||
return template
|
||||
env = Environment(loader=FileSystemLoader('template'))
|
||||
|
||||
def main():
|
||||
os.makedirs('site/en/', exist_ok=True)
|
||||
with open('template/page-en.html') as fTempEN, open('template/page-sr.html') as fTempSR:
|
||||
templateSR = fTempSR.read()
|
||||
templateEN = fTempEN.read()
|
||||
for page in PAGES:
|
||||
# Build SR Page
|
||||
with open(f'pages/sr/{page["name"]}.html') as f:
|
||||
pageHtml = "<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Studenti su nasli bug' /></div>"
|
||||
pageHtml += f.read()
|
||||
html = buildPage(page['name'], page['titleSR'], pageHtml, page['style'], templateSR)
|
||||
f = open(f'site/{page["name"]}.html', 'w')
|
||||
f.write(html)
|
||||
f.close()
|
||||
page_content = "<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Studenti su nasli bug' /></div"
|
||||
page_content += f.read()
|
||||
|
||||
sr_html = env.get_template('page-sr.html').render(
|
||||
title=page['titleSR'],
|
||||
content=page_content,
|
||||
extra_styles=f'<link rel="stylesheet" href="/styles/{page["style"]}.css">' if page['style'] else '',
|
||||
lang="sr",
|
||||
sr_link=f"/en/{page['name']}"
|
||||
)
|
||||
|
||||
sr_filename = "index.html" if page['name'] == 'index' else f"{page['name']}.html"
|
||||
with open(f'site/{sr_filename}', 'w') as f:
|
||||
f.write(sr_html)
|
||||
|
||||
# Build EN Page
|
||||
with open(f'pages/en/{page["name"]}.html') as f:
|
||||
pageHtml = "<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Students found the bug' /></div>"
|
||||
pageHtml += f.read()
|
||||
html = buildPage(page['name'], page['titleEN'], pageHtml, page['style'], templateEN)
|
||||
f = open(f'site/en/{page["name"]}.html', 'w')
|
||||
f.write(html)
|
||||
f.close()
|
||||
page_content = "<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Students found the bug' /></div"
|
||||
page_content += f.read()
|
||||
|
||||
en_html = env.get_template('page-en.html').render(
|
||||
title=page['titleEN'],
|
||||
content=page_content,
|
||||
extra_styles=f'<link rel="stylesheet" href="/styles/{page["style"]}.css">' if page['style'] else '',
|
||||
lang="en",
|
||||
sr_link=f"/{page['name']}"
|
||||
)
|
||||
|
||||
en_filename = "index.html" if page['name'] == 'index' else f"{page['name']}.html"
|
||||
with open(f'site/en/{en_filename}', 'w') as f:
|
||||
f.write(en_html)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
252
prep.py
252
prep.py
@@ -1,7 +1,7 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
import csv
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
DAYS_SR = ["PON", "UTO", "SRE", "ČET", "PET", "SUB", "NED"]
|
||||
DAYS_EN = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]
|
||||
@@ -18,6 +18,8 @@ TYPES_DICT = {
|
||||
"party": ("zabava", "entertainment"),
|
||||
}
|
||||
|
||||
env = Environment(loader=FileSystemLoader('template'))
|
||||
|
||||
def load_events(csv_path:str) -> list[dict]:
|
||||
events = []
|
||||
with open(csv_path, encoding='utf-8') as csv_file:
|
||||
@@ -39,51 +41,11 @@ def load_events(csv_path:str) -> list[dict]:
|
||||
events.append(current_event)
|
||||
return events
|
||||
|
||||
|
||||
def build_html(events: list[dict], dayNames: list[str], typesNames: dict) -> str:
|
||||
events_html = []
|
||||
for event in events:
|
||||
title = event["title"]
|
||||
location = event["location"]
|
||||
date = event["date"]
|
||||
date = dayNames[date.weekday()]+", "+str(date.day)+". "+str(date.month)+". "+str(date.year)+", "
|
||||
time = event["time"]+"h"
|
||||
event_html = []
|
||||
event_html.append(f"<div class='date'>{date} {time}</div>")
|
||||
if event["link"] != "":
|
||||
event_html.append(f"<div class='title'><a href=\"{event['link']}\">{title}</a></div>")
|
||||
else:
|
||||
event_html.append(f"<div class='title'>{title}</div>")
|
||||
if "https://" in location:
|
||||
place,link = location.split("https://")
|
||||
event_html.append(f"<div class='place'><a href=\"https://{link}\" target='_blank'>@{place.strip()}</a></div>")
|
||||
else:
|
||||
event_html.append(f"<div class='place'>@{location.strip()}</div>")
|
||||
|
||||
if len(event["types"]) != 0:
|
||||
types_list = "<div class='types'>"
|
||||
last_item = event["types"][-1]
|
||||
for t in event["types"]:
|
||||
if typesNames.get(t) is not None:
|
||||
types_list += typesNames.get(t)
|
||||
if t != last_item:
|
||||
types_list += ', '
|
||||
else:
|
||||
print(f"Unknown type {t}!")
|
||||
types_list += "</div>"
|
||||
event_html.append(types_list)
|
||||
|
||||
event_html = "".join(event_html)
|
||||
events_html.append(f"\n<div class='event'>{event_html}</div>")
|
||||
return events_html
|
||||
|
||||
def build_ical(events: list[dict]) -> str:
|
||||
today = datetime.today().now()
|
||||
# Header
|
||||
events_ical = ""
|
||||
with open("template/head.ical", "r") as file:
|
||||
events_ical += file.read()
|
||||
# Events
|
||||
for event in events:
|
||||
title = event["title"]
|
||||
location = event["location"]
|
||||
@@ -92,85 +54,165 @@ def build_ical(events: list[dict]) -> str:
|
||||
url = event["link"]
|
||||
|
||||
uid = str(date.month).zfill(2) + str(date.day).zfill(2) + time[:2]
|
||||
date = str(date.year) + str(date.month).zfill(2) + str(date.day).zfill(2)
|
||||
date_str = str(date.year) + str(date.month).zfill(2) + str(date.day).zfill(2)
|
||||
created = str(today.year) + str(today.month).zfill(2) + str(today.day).zfill(2) + "T" + str(today.hour).zfill(2) + str(today.minute).zfill(2) + str(today.second).zfill(2) + "Z"
|
||||
date = date + "T" + time.replace(":", "") + "00"
|
||||
date_str = date_str + "T" + time.replace(":", "") + "00"
|
||||
|
||||
event_template = ""
|
||||
with open("template/event.ical", "r") as file:
|
||||
event_template += file.read()
|
||||
event_template = event_template.replace("<!--UID-->", uid)
|
||||
event_template = event_template.replace("<!--CREATED-->", created)
|
||||
event_template = event_template.replace("<!--DATE-->", date)
|
||||
event_template = event_template.replace("<!--TITLE-->", title)
|
||||
event_template = event_template.replace("<!--URL-->", url)
|
||||
if location.startswith("DC Krov"):
|
||||
event_template = event_template.replace("<!--LOCATION-->", "DC Krov\\, Kraljice Marije 47\\, 6\\, Beograd\\, Serbia")
|
||||
elif location.startswith("Matematički fakultet (Učionica 153)"):
|
||||
event_template = event_template.replace("<!--LOCATION-->", "Matematički fakultet\\, Svetog Nikole 39\\, Beograd\\, Serbia")
|
||||
else:
|
||||
event_template = event_template.replace("<!--LOCATION-->", location)
|
||||
event_template_str = env.get_template("event.ical").render(
|
||||
UID=uid,
|
||||
CREATED=created,
|
||||
DATE=date_str,
|
||||
TITLE=title,
|
||||
URL=url,
|
||||
LOCATION="DC Krov\\, Kraljice Marije 47\\, 6\\, Beograd\\, Serbia" if location.startswith("DC Krov") else ("Matematički fakultet\\, Svetog Nikole 39\\, Beograd\\, Serbia" if location.startswith("Matematički fakultet (Učionica 153)") else location)
|
||||
)
|
||||
events_ical += event_template_str
|
||||
|
||||
|
||||
events_ical += event_template
|
||||
# Footer
|
||||
with open("template/end.ical", "r") as file:
|
||||
events_ical += file.read()
|
||||
return events_ical
|
||||
|
||||
events = sorted(load_events("dogadjaji.csv"), key=lambda e: e["date"])
|
||||
def render_page(template_name, output_path, context):
|
||||
template = env.get_template(template_name)
|
||||
with open(output_path, "w") as file:
|
||||
file.write(template.render(context))
|
||||
|
||||
# Main execution
|
||||
events = sorted(load_events("dogadjaji.csv"), key=lambda e: e["date"])
|
||||
today = datetime.today().date()
|
||||
|
||||
past_events = list(filter(lambda e: e["date"] <= today, events))
|
||||
past_events.reverse()
|
||||
new_events = list(filter(lambda e: e["date"] >= today, events))
|
||||
past_events = sorted([e for e in events if e["date"] <= today], key=lambda e: e["date"], reverse=True)
|
||||
new_events = [e for e in events if e["date"] >= today]
|
||||
|
||||
sr_types = {k: v[0] for k, v in TYPES_DICT.items()}
|
||||
en_types = {k: v[1] for k, v in TYPES_DICT.items()}
|
||||
|
||||
page_template = ""
|
||||
# Build Serbian Pages
|
||||
render_page("events-sr.html", "pages/sr/events.html", {
|
||||
"lang": "sr",
|
||||
"title": "Događaji",
|
||||
"sr_link": "/events_archive",
|
||||
"events_html": env.from_string("""
|
||||
{% for event in events %}
|
||||
<div class='event'>
|
||||
<div class='date'>{{ event.date.strftime('%a, %d. %b. %Y') }}, {{ event.time }}h</div>
|
||||
{% if event.link %}
|
||||
<div class='title'><a href="{{ event.link }}">{{ event.title }}</a></div>
|
||||
{% else %}
|
||||
<div class='title'>{{ event.title }}</div>
|
||||
{% endif %}
|
||||
{% if 'https://' in event.location %}
|
||||
{% set place, link = event.location.split('https://') %}
|
||||
<div class='place'><a href="https://{{ link }}" target='_blank'>@{{ place.strip() }}</a></div>
|
||||
{% else %}
|
||||
<div class='place'>@{{ event.location.strip() }}</div>
|
||||
{% endif %}
|
||||
{% if event.types %}
|
||||
<div class='types'>
|
||||
{% for t in event.types %}
|
||||
{{ types_names.get(t, t) }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
""").render(events=new_events, types_names=sr_types)
|
||||
})
|
||||
|
||||
sr_types = {}
|
||||
en_types = {}
|
||||
render_page("events-en.html", "pages/en/events.html", {
|
||||
"lang": "en",
|
||||
"title": "Events",
|
||||
"sr_link": "/events_archive",
|
||||
"events_html": env.from_string("""
|
||||
{% for event in events %}
|
||||
<div class='event'>
|
||||
<div class='date'>{{ event.date.strftime('%a, %d. %b. %Y') }}, {{ event.time }}h</div>
|
||||
{% if event.link %}
|
||||
<div class='title'><a href="{{ event.link }}">{{ event.title }}</a></div>
|
||||
{% else %}
|
||||
<div class='title'>{{ event.title }}</div>
|
||||
{% endif %}
|
||||
{% if 'https://' in event.location %}
|
||||
{% set place, link = event.location.split('https://') %}
|
||||
<div class='place'><a href="https://{{ link }}" target='_blank'>@{{ place.strip() }}</a></div>
|
||||
{% else %}
|
||||
<div class='place'>@{{ event.location.strip() }}</div>
|
||||
{% endif %}
|
||||
{% if event.types %}
|
||||
<div class='types'>
|
||||
{% for t in event.types %}
|
||||
{{ types_names.get(t, t) }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
""").render(events=new_events, types_names=en_types)
|
||||
})
|
||||
|
||||
for key, value_pair in TYPES_DICT.items():
|
||||
sr_types[key] = value_pair[0]
|
||||
en_types[key] = value_pair[1]
|
||||
# Build Archive Pages
|
||||
render_page("events_archive-sr.html", "pages/sr/events_archive.html", {
|
||||
"lang": "sr",
|
||||
"title": "Arhiva događaja",
|
||||
"sr_link": "/events",
|
||||
"events_html": env.from_string("""
|
||||
{% for event in events %}
|
||||
<div class='event'>
|
||||
<div class='date'>{{ event.date.strftime('%a, %d. %b. %Y') }}, {{ event.time }}h</div>
|
||||
{% if event.link %}
|
||||
<div class='title'><a href="{{ event.link }}">{{ event.title }}</a></div>
|
||||
{% else %}
|
||||
<div class='title'>{{ event.title }}</div>
|
||||
{% endif %}
|
||||
{% if 'https://' in event.location %}
|
||||
{% set place, link = event.location.split('https://') %}
|
||||
<div class='place'><a href="https://{{ link }}" target='_blank'>@{{ place.strip() }}</a></div>
|
||||
{% else %}
|
||||
<div class='place'>@{{ event.location.strip() }}</div>
|
||||
{% endif %}
|
||||
{% if event.types %}
|
||||
<div class='types'>
|
||||
{% for t in event.types %}
|
||||
{{ types_names.get(t, t) }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
""").render(events=past_events, types_names=sr_types)
|
||||
})
|
||||
|
||||
# Build Serbian Events page
|
||||
new_events_html = build_html(new_events, DAYS_SR, sr_types)
|
||||
with open("template/events-sr.html", "r") as file:
|
||||
page_template = ([line for line in file])
|
||||
|
||||
with open("pages/sr/events.html", "w") as file:
|
||||
file.writelines(page_template + new_events_html)
|
||||
|
||||
# Build English Events page
|
||||
new_events_html = build_html(new_events, DAYS_EN, en_types)
|
||||
with open("template/events-en.html", "r") as file:
|
||||
page_template = ([line for line in file])
|
||||
|
||||
with open("pages/en/events.html", "w") as file:
|
||||
file.writelines(page_template + new_events_html)
|
||||
|
||||
# Build Serbian Archive page
|
||||
past_events_html = build_html(past_events, DAYS_SR, sr_types)
|
||||
with open("template/events_archive-sr.html", "r") as file:
|
||||
page_template = ([line for line in file])
|
||||
|
||||
with open("pages/sr/events_archive.html", "w") as file:
|
||||
file.writelines(page_template + past_events_html)
|
||||
|
||||
# Build English Archive page
|
||||
past_events_html = build_html(past_events, DAYS_EN, en_types)
|
||||
with open("template/events_archive-en.html", "r") as file:
|
||||
page_template = ([line for line in file])
|
||||
|
||||
with open("pages/en/events_archive.html", "w") as file:
|
||||
file.writelines(page_template + past_events_html)
|
||||
|
||||
new_events_ical = build_ical(new_events)
|
||||
render_page("events_archive-en.html", "pages/en/events_archive.html", {
|
||||
"lang": "en",
|
||||
"title": "Events archive",
|
||||
"sr_link": "/en/events",
|
||||
"events_html": env.from_string("""
|
||||
{% for event in events %}
|
||||
<div class='event'>
|
||||
<div class='date'>{{ event.date.strftime('%a, %d. %b. %Y') }}, {{ event.time }}h</div>
|
||||
{% if event.link %}
|
||||
<div class='title'><a href="{{ event.link }}">{{ event.title }}</a></div>
|
||||
{% else %}
|
||||
<div class='title'>{{ event.title }}</div>
|
||||
{% endif %}
|
||||
{% if 'https://' in event.location %}
|
||||
{% set place, link = event.location.split('https://') %}
|
||||
<div class='place'><a href="https://{{ link }}" target='_blank'>@{{ place.strip() }}</a></div>
|
||||
{% else %}
|
||||
<div class='place'>@{{ event.location.strip() }}</div>
|
||||
{% endif %}
|
||||
{% if event.types %}
|
||||
<div class='types'>
|
||||
{% for t in event.types %}
|
||||
{{ types_names.get(t, t) }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
""").render(events=past_events, types_names=en_types)
|
||||
})
|
||||
|
||||
# Build ical
|
||||
with open("site/events.ical", "w") as file:
|
||||
file.write(build_ical(new_events))
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@ freetype-py
|
||||
python-dateutil
|
||||
feedgen
|
||||
pillow
|
||||
jinja2
|
||||
|
||||
@@ -14,50 +14,26 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- <pre>
|
||||
|
||||
*@@*
|
||||
*@@@@*
|
||||
*@@*
|
||||
||
|
||||
||
|
||||
*@@* *@@* *@@*
|
||||
*@@@@*===*@@@@*===*@@@@*
|
||||
*@@* *@@* *@@*
|
||||
|| //
|
||||
|| //
|
||||
*@@*//
|
||||
*@@@@*
|
||||
*@@*
|
||||
|
||||
____ _____ ____ _____ _ _ _____ ____ _ _ _
|
||||
| _ \| ____/ ___| ____| \ | |_ _| _ \ / \ | | / \
|
||||
| | | | _|| | | _| | \| | | | | |_) | / _ \ | | / _ \
|
||||
| |_| | |__| |___| |___| |\ | | | | _ < / ___ \| |___ / ___ \
|
||||
|____/|_____\____|_____|_| \_| |_| |_| \_\/_/ \_\_____/_/ \_\
|
||||
|
||||
</pre> -->
|
||||
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/style.css" />
|
||||
<link rel="stylesheet" href="/styles/deconference.css">
|
||||
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon" />
|
||||
<script src="/scripts/main.js" defer></script>
|
||||
<script src="/scripts/main.js"></script>
|
||||
<title>Dekonferencija Decentrala</title>
|
||||
<link rel="alternate" hreflang="en" href="/en/deconference" />
|
||||
<link rel="alternate" hreflang="sr" href="/en/deconference" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a id="logo" href="/">
|
||||
<img src="/img/logo-light.svg" alt="Logo" />
|
||||
Decentrala
|
||||
<a id="logo" href="/index">
|
||||
<img src="/img/logo-light.svg" alt="Logo" /> Decentrala
|
||||
</a>
|
||||
<button id="theme-switcher"></button>
|
||||
<a class="lang" hreflang="en" href="/en/deconference">EN</a>
|
||||
<a class="lang" hreflang="sr" href="/en/deconference">SR</a>
|
||||
</header>
|
||||
<main>
|
||||
|
||||
<div class="page-wrap">
|
||||
<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Studenti su nasli bug' /></div><h1>Dekonferencija</h1>
|
||||
<div class='cover-wrap'><img src='/img/students_bug.jpg' alt='Studenti su nasli bug' /></div<h1>Dekonferencija</h1>
|
||||
|
||||
<h2 id="program"><a href="#program">Program</a></h2>
|
||||
<p>11:00 Otvaranje<p>
|
||||
@@ -94,17 +70,19 @@
|
||||
<p>Decentrala prihvata donacije isključivo od fizičkih lica.</p>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
<footer>
|
||||
<button class="hamburger closed">
|
||||
<img src="/img/strelica-closed-light.svg" alt="Menu" />
|
||||
</button>
|
||||
<nav class="menu">
|
||||
|
||||
<a href="/events">Događaji</a>
|
||||
<a href="/services">Servisi</a>
|
||||
<a href="/statute">Statut</a>
|
||||
<a href="/about">O nama</a>
|
||||
<a class="account" href="/account">Nalog</a>
|
||||
<a href="/account">Nalog</a>
|
||||
<a href="/support">Podrška</a>
|
||||
</nav>
|
||||
<span class="links">
|
||||
|
||||
65
template/base.html
Normal file
65
template/base.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ lang }}">
|
||||
<head>
|
||||
<script>
|
||||
(function () {
|
||||
const theme = localStorage.getItem("theme");
|
||||
const prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)",
|
||||
).matches;
|
||||
if (theme === "dark" || (!theme && prefersDark))
|
||||
document.documentElement.classList.add("dark");
|
||||
})();
|
||||
</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/style.css" />
|
||||
{% block extra_styles %}{% endblock %}
|
||||
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon" />
|
||||
<script src="/scripts/main.js"></script>
|
||||
<title>{{ title }} Decentrala</title>
|
||||
<link rel="alternate" hreflang="sr" href="{{ sr_link }}" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a id="logo" href="/{{ '/en/' if lang == 'en' else '' }}index">
|
||||
<img src="/img/logo-{{ 'dark' if is_dark else 'light' }}.svg" alt="Logo" /> Decentrala
|
||||
</a>
|
||||
<button id="theme-switcher"></button>
|
||||
<a class="lang" hreflang="sr" href="{{ sr_link }}">SR</a>
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<footer>
|
||||
<button class="hamburger closed">
|
||||
<img src="/img/strelica-closed-{{ 'dark' if is_dark else 'light' }}.svg" alt="Menu" />
|
||||
</button>
|
||||
<nav class="menu">
|
||||
{% set base_path = '/en/' if lang == 'en' else '/' %}
|
||||
<a href="{{ base_path }}events">{{ 'Događaji' if lang == 'sr' else 'Events' }}</a>
|
||||
<a href="{{ base_path }}services">{{ 'Servisi' if lang == 'sr' else 'Services' }}</a>
|
||||
<a href="{{ base_path }}statute">{{ 'Statut' if lang == 'sr' else 'Statute' }}</a>
|
||||
<a href="{{ base_path }}about">{{ 'O nama' if lang == 'sr' else 'About us' }}</a>
|
||||
<a href="{{ base_path }}account">{{ 'Nalog' if lang == 'sr' else 'Account' }}</a>
|
||||
<a href="{{ base_path }}support">{{ 'Podrška' if lang == 'sr' else 'Support' }}</a>
|
||||
</nav>
|
||||
<span class="links">
|
||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||
<img src="/img/cc-{{ 'dark' if is_dark else 'light' }}.svg" alt="CreativeCommons" />
|
||||
</a>
|
||||
<a href="{{ base_path }}webring">
|
||||
<img src="/img/w-{{ 'dark' if is_dark else 'light' }}.svg" alt="Webring" />
|
||||
</a>
|
||||
<a href="https://gitea.dmz.rs/Decentrala/website">
|
||||
<img src="/img/git-{{ 'dark' if is_dark else 'light' }}.svg" alt="SourceCode" />
|
||||
</a>
|
||||
<a href="https://balkan.fedive.rs/@decentrala">
|
||||
<img src="/img/mastodon-{{ 'dark' if is_dark else 'light' }}.svg" alt="Mastodon" />
|
||||
</a>
|
||||
</span>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +1,17 @@
|
||||
<h1>Events</h1>
|
||||
{% block title %}Events{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">Events</h1>
|
||||
|
||||
<div class="description">
|
||||
<p>
|
||||
Following list contains all forthcoming events. Held events are listed in
|
||||
<a href="/en/events_archive">archive</a>.
|
||||
<a href="/{{ lang }}/events_archive">archive</a>.
|
||||
</p>
|
||||
<p>
|
||||
Events are also available as an
|
||||
<a href="https://dmz.rs/events.ical">ical</a> file.
|
||||
</p>
|
||||
<p>We also provide <a href="https://dmz.rs/events.ical">ical</a> file</p>
|
||||
<p>
|
||||
<a href="https://wiki.dmz.rs/en/kako-pronaci-prostor" target="_blank">
|
||||
Short description
|
||||
@@ -17,3 +19,8 @@
|
||||
how to find a space
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="events-list">
|
||||
{{ events_html|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<h1>Događaji</h1>
|
||||
{% block title %}Događaji{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">Događaji</h1>
|
||||
|
||||
<div class="description">
|
||||
<p>
|
||||
Naredna lista sadrži sve predstojeće događaje. Za listu održanih događaja
|
||||
pogledaj <a href="/events_archive">arhivu</a>.
|
||||
pogledaj <a href="/{{ lang }}/events_archive">arhivu</a>.
|
||||
</p>
|
||||
<p>
|
||||
Događaje možeš učitati i sa
|
||||
@@ -16,3 +19,8 @@
|
||||
kako pronaci prostor
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="events-list">
|
||||
{{ events_html|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<h1>Events archive</h1>
|
||||
{% block title %}Events archive{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">Events archive</h1>
|
||||
<p>
|
||||
All events that we organized so far. You can find future events on
|
||||
<a href="/en/events">Events page</a>
|
||||
<a href="/{{ lang }}/events">Events page</a>
|
||||
</p>
|
||||
<br />
|
||||
|
||||
<div class="events-list">
|
||||
{{ events_html|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<h1>Arhiva događaja</h1>
|
||||
{% block title %}Arhiva događaja{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">Arhiva događaja</h1>
|
||||
<p>
|
||||
Svi događaji koje smo do sada organzivali. Predstojeće događaje možeš naći
|
||||
<a href="/events">ovde</a>
|
||||
<a href="/{{ lang }}/events">ovde</a>
|
||||
</p>
|
||||
<br />
|
||||
|
||||
<div class="events-list">
|
||||
{{ events_html|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,90 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
(function () {
|
||||
const theme = localStorage.getItem("theme");
|
||||
const prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)",
|
||||
).matches;
|
||||
if (theme === "dark" || (!theme && prefersDark))
|
||||
document.documentElement.classList.add("dark");
|
||||
})();
|
||||
</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
{% extends "base.html" %}
|
||||
|
||||
<!-- <pre>
|
||||
{% block title %}{{ title }} Decentrala{% endblock %}
|
||||
|
||||
*@@*
|
||||
*@@@@*
|
||||
*@@*
|
||||
||
|
||||
||
|
||||
*@@* *@@* *@@*
|
||||
*@@@@*===*@@@@*===*@@@@*
|
||||
*@@* *@@* *@@*
|
||||
|| //
|
||||
|| //
|
||||
*@@*//
|
||||
*@@@@*
|
||||
*@@*
|
||||
{% block extra_styles %}{{ extra_styles|safe }}{% endblock %}
|
||||
|
||||
____ _____ ____ _____ _ _ _____ ____ _ _ _
|
||||
| _ \| ____/ ___| ____| \ | |_ _| _ \ / \ | | / \
|
||||
| | | | _|| | | _| | \| | | | | |_) | / _ \ | | / _ \
|
||||
| |_| | |__| |___| |___| |\ | | | | _ < / ___ \| |___ / ___ \
|
||||
|____/|_____\____|_____|_| \_| |_| |_| \_\/_/ \_\_____/_/ \_\
|
||||
|
||||
</pre> -->
|
||||
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/style.css" />
|
||||
<!--ADDITIONAL_STYLE-->
|
||||
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon" />
|
||||
<script src="/scripts/main.js"></script>
|
||||
<title><!--TITLE--> Decentrala</title>
|
||||
<link rel="alternate" hreflang="sr" href="/PAGE_NAME" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a id="logo" href="/en/index">
|
||||
<img src="/img/logo-light.svg" alt="Logo" /> Decentrala
|
||||
</a>
|
||||
<button id="theme-switcher"></button>
|
||||
<a class="lang" hreflang="sr" href="/PAGE_NAME">SR</a>
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}
|
||||
<div class="page-wrap">
|
||||
<!--MAIN-->
|
||||
{{ content|safe }}
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<button class="hamburger closed">
|
||||
<img src="/img/strelica-closed-light.svg" alt="Menu" />
|
||||
</button>
|
||||
<nav class="menu">
|
||||
<a href="/en/events">Events</a>
|
||||
<a href="/en/services">Services</a>
|
||||
<a href="/en/statute">Statute</a>
|
||||
<a href="/en/about">About</a>
|
||||
<a href="/en/account">Account</a>
|
||||
<a href="/en/support">Support</a>
|
||||
</nav>
|
||||
<span class="links">
|
||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||
<img src="/img/cc-light.svg" alt="CreativeCommons" />
|
||||
</a>
|
||||
<a href="/en/webring">
|
||||
<img src="/img/w-light.svg" alt="Webring" />
|
||||
</a>
|
||||
<a href="https://gitea.dmz.rs/Decentrala/website">
|
||||
<img src="/img/git-light.svg" alt="SourceCode" />
|
||||
</a>
|
||||
<a href="https://balkan.fedive.rs/@decentrala">
|
||||
<img src="/img/mastodon-light.svg" alt="Mastodon" />
|
||||
</a>
|
||||
</span>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,91 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="sr">
|
||||
<head>
|
||||
<script>
|
||||
(function () {
|
||||
const theme = localStorage.getItem("theme");
|
||||
const prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)",
|
||||
).matches;
|
||||
if (theme === "dark" || (!theme && prefersDark))
|
||||
document.documentElement.classList.add("dark");
|
||||
})();
|
||||
</script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
{% extends "base.html" %}
|
||||
|
||||
<!-- <pre>
|
||||
{% block title %}{{ title }} Decentrala{% endblock %}
|
||||
|
||||
*@@*
|
||||
*@@@@*
|
||||
*@@*
|
||||
||
|
||||
||
|
||||
*@@* *@@* *@@*
|
||||
*@@@@*===*@@@@*===*@@@@*
|
||||
*@@* *@@* *@@*
|
||||
|| //
|
||||
|| //
|
||||
*@@*//
|
||||
*@@@@*
|
||||
*@@*
|
||||
{% block extra_styles %}{{ extra_styles|safe }}{% endblock %}
|
||||
|
||||
____ _____ ____ _____ _ _ _____ ____ _ _ _
|
||||
| _ \| ____/ ___| ____| \ | |_ _| _ \ / \ | | / \
|
||||
| | | | _|| | | _| | \| | | | | |_) | / _ \ | | / _ \
|
||||
| |_| | |__| |___| |___| |\ | | | | _ < / ___ \| |___ / ___ \
|
||||
|____/|_____\____|_____|_| \_| |_| |_| \_\/_/ \_\_____/_/ \_\
|
||||
|
||||
</pre> -->
|
||||
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/style.css" />
|
||||
<!--ADDITIONAL_STYLE-->
|
||||
<link rel="shortcut icon" href="/img/favicon.ico" type="image/x-icon" />
|
||||
<script src="/scripts/main.js" defer></script>
|
||||
<title><!--TITLE--> Decentrala</title>
|
||||
<link rel="alternate" hreflang="en" href="/en/PAGE_NAME" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a id="logo" href="/">
|
||||
<img src="/img/logo-light.svg" alt="Logo" />
|
||||
Decentrala
|
||||
</a>
|
||||
<button id="theme-switcher"></button>
|
||||
<a class="lang" hreflang="en" href="/en/PAGE_NAME">EN</a>
|
||||
</header>
|
||||
<main>
|
||||
{% block content %}
|
||||
<div class="page-wrap">
|
||||
<!--MAIN-->
|
||||
{{ content|safe }}
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<button class="hamburger closed">
|
||||
<img src="/img/strelica-closed-light.svg" alt="Menu" />
|
||||
</button>
|
||||
<nav class="menu">
|
||||
<a href="/events">Događaji</a>
|
||||
<a href="/services">Servisi</a>
|
||||
<a href="/statute">Statut</a>
|
||||
<a href="/about">O nama</a>
|
||||
<a class="account" href="/account">Nalog</a>
|
||||
<a href="/support">Podrška</a>
|
||||
</nav>
|
||||
<span class="links">
|
||||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
|
||||
<img src="/img/cc-light.svg" alt="CreativeCommons" />
|
||||
</a>
|
||||
<a href="/webring">
|
||||
<img src="/img/w-light.svg" alt="Webring" />
|
||||
</a>
|
||||
<a href="https://gitea.dmz.rs/Decentrala/website">
|
||||
<img src="/img/git-light.svg" alt="SourceCode" />
|
||||
</a>
|
||||
<a href="https://balkan.fedive.rs/@decentrala">
|
||||
<img src="/img/mastodon-light.svg" alt="Mastodon" />
|
||||
</a>
|
||||
</span>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
||||
21
todo.md
Normal file
21
todo.md
Normal file
@@ -0,0 +1,21 @@
|
||||
1. High Impact: Reliability & Security
|
||||
* Adopt a Templating Engine (e.g., Jinja2):
|
||||
* Problem: The current build scripts (build_pages.py, prep.py) use manual string replacement (e.g., .replace('<!--TITLE-->', ...)). This is brittle and prone to errors as the site grows.
|
||||
* Benefit: Using Jinja2 would make templates much more powerful (loops, conditionals) and, crucially, would prevent XSS vulnerabilities by automatically escaping data from your CSV files.
|
||||
|
||||
* Use csv.DictReader in Python:
|
||||
* Problem: prep.py accesses CSV columns by index (e.g., row[0]). If you add a column to dogadjaji.csv, the build will break.
|
||||
* Benefit: Accessing by name (row['title']) makes the code resilient to data schema changes.
|
||||
|
||||
2. Medium Impact: Developer Experience (DX)
|
||||
* Code Linting:
|
||||
* Benefit: Adding black or flake8 for Python and prettier for HTML/CSS would ensure consistent style across the repository.
|
||||
|
||||
3. Low Impact: Performance & Modernization
|
||||
* CSS Consolidation:
|
||||
* Problem: There are many small CSS files (one per page).
|
||||
* Benefit: While fine for a small site, consolidating these or using a preprocessor like Sass would make managing global styles easier.
|
||||
|
||||
* Asset Optimization:
|
||||
* Benefit: Implementing automated image compression (for the event posters) would reduce the final site payload.
|
||||
Which of these areas would you like me to focus on first? I recommend starting with the Jinja2 migration as it solves both maintainability and security issues.
|
||||
Reference in New Issue
Block a user