Compare commits

..

17 commits

Author SHA1 Message Date
b5107202e9 Letní čas vypnut 2024-11-05 19:00:23 +01:00
f25d0b0bfa fixes "empty topic tag bug"
Honestly just stupid change that unbreaks this code (something I should do at the beginning). Stupid fix but works
2024-06-06 19:02:28 +02:00
c9a6c61f69 dockerfile fix 2024-04-10 19:41:34 +02:00
be0969854c fixes stupid mistake 2024-04-10 19:32:52 +02:00
48f742fea5 Fixes timezone change 2024-04-10 19:29:52 +02:00
d9b4128359 fixes weird time bug
for some reason, reocurring events were offset. Don't know why.
2024-03-06 22:50:39 +01:00
936aa10e19 docker file update 2024-02-27 21:27:32 +01:00
e249065308 dockerfile 2024-02-27 18:20:55 +01:00
db6d465c37 wsgi init 2024-02-27 18:20:47 +01:00
5f009e8500 fixes bug 2024-02-25 18:58:58 +01:00
60562b2aec fixes favicon 2024-02-25 12:01:10 +01:00
6faf1f83bd updates requirements.txt 2024-02-25 11:48:03 +01:00
768c3e61a1 removes artefact from previous version 2024-02-25 10:09:52 +01:00
6427a50272 changes to json events from array
Adds support for webhook creation and init of events.json. I have no clue how i fixed it, or why does it work.
2024-02-25 10:09:00 +01:00
87e14c8d7b moves previous version to Old version folder 2024-02-25 09:15:08 +01:00
caa554547d working version 2024-02-25 09:13:54 +01:00
dd839e54ca init 2024-02-25 09:13:31 +01:00
12 changed files with 398 additions and 2 deletions

4
.env-example Normal file
View file

@ -0,0 +1,4 @@
API-KEY=
URL=
TIMEZONE=
WEBHOOK-URL=

163
.gitignore vendored Normal file
View file

@ -0,0 +1,163 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
#MacOS BS
.DS_Store
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

7
Dockerfile Normal file
View file

@ -0,0 +1,7 @@
FROM python:3.12.2-bookworm
WORKDIR /trhlina-calendar
COPY requirements.txt .
RUN pip install --no-cache-dir --use-pep5 -r requirements.txt
COPY . .
EXPOSE 80
CMD ["gunicorn", "wsgi:app", "-b", "0.0.0.0:80"]

85
conf/templates/index.html Normal file
View file

@ -0,0 +1,85 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Kalendář Trhliny</title>
<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' rel='stylesheet'>
<link href='https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css' rel='stylesheet'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ical.js/1.4.0/ical.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.4/index.global.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fullcalendar/icalendar@6.1.4/index.global.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.4/locales-all.global.min.js"></script>
<style>
html, body {
overflow: hidden; /* don't do scrollbars */
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
#calendar-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.demo-topbar { /* will be stripped out */
position: fixed;
top: 0;
left: 0;
right: 0;
height: 40px;
}
.demo-topbar + #calendar-container { /* will be stripped out */
top: 40px;
}
.fc-header-toolbar {
/*
the calendar will be butting up against the edges,
but let's scoot in the header's buttons
*/
padding-top: 1em;
padding-left: 1em;
padding-right: 1em;
}
</style>
</head>
<body>
<div id="calendar"></div>
<script>
let calendarEl = document.getElementById('calendar');
let calendar = new FullCalendar.Calendar(calendarEl, {
height: '100%',
themeSystem: 'bootstrap5',
locale: 'cs',
initialView: "dayGridMonth",
firstDay: 1,
headerToolbar: {
left: "prev,next",
center: "title",
right: "dayGridMonth,listMonth",
},
eventTimeFormat: { // like '14:30:00'
hour: '2-digit',
minute: '2-digit',
hour12: false
},
failure: function() {
alert('There was an error while fetching events from /events! ');
},
events: '/events',
});
calendar.render();
</script>
</body>
</html>

1
events.json Normal file
View file

@ -0,0 +1 @@
[{"title": "Closed", "start": "2024-02-25T09:00:00", "end": "2024-02-25T15:00:00"}, {"title": "Antigender - druh\u00e9 kolo", "start": "2024-03-05T18:00:00", "end": "2024-03-05T19:30:00"}, {"title": "Queer stitches nevim", "start": "2024-03-06T18:30:00", "end": "2024-03-06T20:00:00"}, {"title": "Queer stitches nevim", "start": "2024-03-20T19:30:00", "end": "2024-03-20T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-04-03T19:30:00", "end": "2024-04-03T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-04-17T19:30:00", "end": "2024-04-17T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-05-01T19:30:00", "end": "2024-05-01T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-05-15T19:30:00", "end": "2024-05-15T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-05-29T19:30:00", "end": "2024-05-29T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-06-12T19:30:00", "end": "2024-06-12T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-06-26T19:30:00", "end": "2024-06-26T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-07-10T19:30:00", "end": "2024-07-10T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-07-24T19:30:00", "end": "2024-07-24T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-08-07T19:30:00", "end": "2024-08-07T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-08-21T19:30:00", "end": "2024-08-21T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-09-04T19:30:00", "end": "2024-09-04T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-09-18T19:30:00", "end": "2024-09-18T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-10-02T19:30:00", "end": "2024-10-02T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-10-16T19:30:00", "end": "2024-10-16T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-10-30T19:30:00", "end": "2024-10-30T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-11-13T19:30:00", "end": "2024-11-13T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-11-27T19:30:00", "end": "2024-11-27T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-12-11T19:30:00", "end": "2024-12-11T21:00:00"}, {"title": "Queer stitches nevim", "start": "2024-12-25T19:30:00", "end": "2024-12-25T21:00:00"}, {"title": "Queer stitches nevim", "start": "2025-01-08T19:30:00", "end": "2025-01-08T21:00:00"}, {"title": "Queer stitches nevim", "start": "2025-01-22T19:30:00", "end": "2025-01-22T21:00:00"}, {"title": "Queer stitches nevim", "start": "2025-02-05T19:30:00", "end": "2025-02-05T21:00:00"}, {"title": "Queer stitches nevim", "start": "2025-02-19T19:30:00", "end": "2025-02-19T21:00:00"}, {"title": "Queer stitches nevim", "start": "2025-03-05T19:30:00", "end": "2025-03-05T21:00:00"}, {"title": "Prom\u00edt\u00e1n\u00ed T\u00e1bora solidarity", "start": "2024-03-22T18:00:00", "end": "2024-03-22T19:30:00"}]

93
get_json.py Normal file
View file

@ -0,0 +1,93 @@
import requests as r
import json
from datetime import datetime as dt
from datetime import timedelta
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.environ.get("API-KEY")
URL = os.environ.get("URL")
timezone = int(os.environ.get("TIMEZONE"))
def get_json(url):
headers={ 'Api-Key' :api_key}
result = r.get(url, headers=headers)
return result.json()
class Event:
def __init__(self, title, start_date, end_date):
self.title = title
self.start_date = start_date
self.end_date = end_date
def offset_time(timestamp, offset):
return timestamp + timedelta(hours=offset)
def save_to_json(event_list):
event_array=[]
for event in event_list:
event_array.append({"title":event[1], "start":event[0].strftime("%Y-%m-%dT%H:%M:%S"), "end":event[2].strftime("%Y-%m-%dT%H:%M:%S")})
with open('events.json', 'w') as fp:
json.dump(event_array, fp)
def set_name(raw_event):
try:
if raw_event["post"]["topic"]["tags"][0] == 'closed-event':
event_name = "Closed"
return event_name
except:
event_name = raw_event["post"]["topic"]["title"]
return event_name
def parse_time(timestring):
if timestring!= None:
datetime=dt.strptime(timestring, '%Y-%m-%dT%H:%M:%S.%fZ') + timedelta(hours=timezone)
return datetime
def create_event_list(json):
event_list = []
for raw_event in json["events"]:
event_first_start_datetime = parse_time(raw_event["starts_at"])
# If letní čas, odkomentovat tento komentář
#event_first_start_datetime = offset_time(event_first_start_datetime, 1)
event_end_time = parse_time(raw_event["ends_at"])
if event_end_time is not None:
event_end_time = offset_time(event_end_time, 1)
if event_end_time == None or event_first_start_datetime==event_end_time:
event_end_time=offset_time(event_first_start_datetime, 1.5)
event_name = set_name(raw_event)
event_list.append((event_first_start_datetime,event_name,event_end_time))
try:
for reocurring_event in raw_event["upcoming_dates"]:
event_start_time = parse_time(reocurring_event["starts_at"][0:reocurring_event["starts_at"].find("+")]+"Z")
event_start_time = offset_time(event_start_time, -1)
event_end_time = parse_time(reocurring_event["ends_at"][0:reocurring_event["ends_at"].find("+")]+"Z")
event_end_time = offset_time(event_end_time, -1)
if event_end_time == None or event_start_time == event_end_time:
event_end_time = offset_time(event_start_time, 1.5)
event_list.append((event_start_time,event_name,event_end_time))
except:
pass
return event_list
def main():
event_json = get_json(URL)
event_list = create_event_list(event_json)
save_to_json(event_list)
if __name__ == "__main__":
main()

39
main.py Normal file
View file

@ -0,0 +1,39 @@
from flask import Flask, render_template, request, send_from_directory
import get_json
from dotenv import load_dotenv
import os
import json
from os.path import join, dirname
load_dotenv()
webhook_update_url = os.environ.get("WEBHOOK-URL")
app = Flask(__name__, template_folder='./conf/templates')
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
@app.route('/')
def home():
return render_template('index.html')
@app.route("/events")
def server_json():
with open("events.json") as event_json:
return json.load(event_json)
@app.route(webhook_update_url, methods=['GET', 'POST'])
def update():
if request.method == 'GET':
return "This is Webhook endpoint that accepts data through POST"
if request.method == 'POST':
get_json.main()
return "Updated"

View file

@ -1,4 +1,4 @@
flask
ics
python-dotenv
requests
arrow
gunicorn

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

4
wsgi.py Normal file
View file

@ -0,0 +1,4 @@
from main import app
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)