diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2025-11-06 00:48:00 +0000 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2025-11-06 00:48:00 +0000 |
| commit | 6f1bcc3db2c8ee807b68fa8a47a1a8cb7aabcc48 (patch) | |
| tree | 1c9e920b22a6d7d526b0ecb51cb9d73e61e75823 | |
| parent | 95ae3c51cf05425baf739919af9938608f62ad64 (diff) | |
Create a create_app helper to be used from wsgi.py
| -rwxr-xr-x | halfnarp2.py | 54 | ||||
| -rw-r--r-- | static/faq.html | 4 | ||||
| -rw-r--r-- | static/halfnarp.js | 2 | ||||
| -rw-r--r-- | wsgi.py | 6 |
4 files changed, 37 insertions, 29 deletions
diff --git a/halfnarp2.py b/halfnarp2.py index 5ac9405..24d099a 100755 --- a/halfnarp2.py +++ b/halfnarp2.py | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #!venv/bin/python | 1 | #!venv/bin/python |
| 2 | 2 | ||
| 3 | from flask import Flask, render_template, jsonify, request, abort, send_file, url_for | 3 | from flask import Flask, Blueprint, render_template, jsonify, request, abort, send_file, url_for |
| 4 | from flask_sqlalchemy import SQLAlchemy | 4 | from flask_sqlalchemy import SQLAlchemy |
| 5 | from flask_cors import CORS | 5 | from flask_cors import CORS |
| 6 | from lxml import etree | 6 | from lxml import etree |
| @@ -13,10 +13,9 @@ from datetime import datetime, time, timedelta | |||
| 13 | from html_sanitizer import Sanitizer | 13 | from html_sanitizer import Sanitizer |
| 14 | from hashlib import sha256 | 14 | from hashlib import sha256 |
| 15 | 15 | ||
| 16 | app = Flask(__name__) | 16 | bp = Blueprint("main", __name__) |
| 17 | db = SQLAlchemy() | 17 | db = SQLAlchemy() |
| 18 | 18 | ||
| 19 | |||
| 20 | class TalkPreference(db.Model): | 19 | class TalkPreference(db.Model): |
| 21 | """A preference of halfnarp frontend. An array of strings""" | 20 | """A preference of halfnarp frontend. An array of strings""" |
| 22 | 21 | ||
| @@ -26,17 +25,17 @@ class TalkPreference(db.Model): | |||
| 26 | 25 | ||
| 27 | 26 | ||
| 28 | """ | 27 | """ |
| 29 | @app.route("/") | 28 | @bp.route("/") |
| 30 | def root(): | 29 | def root(): |
| 31 | return render_template("index.html") | 30 | return render_template("index.html") |
| 32 | """ | 31 | """ |
| 33 | 32 | ||
| 34 | @app.route("/-/talkpreferences", methods=["GET"]) | 33 | @bp.route("/-/talkpreferences", methods=["GET"]) |
| 35 | def sessions(): | 34 | def sessions(): |
| 36 | return send_file("var/talks_local", mimetype="application/json") | 35 | return send_file("var/talks_local", mimetype="application/json") |
| 37 | 36 | ||
| 38 | 37 | ||
| 39 | @app.route("/-/talkpreferences/<uid>", methods=["GET"]) | 38 | @bp.route("/-/talkpreferences/<uid>", methods=["GET"]) |
| 40 | def get_own_preferences(uid): | 39 | def get_own_preferences(uid): |
| 41 | pref = db.session.get(TalkPreference, uid) | 40 | pref = db.session.get(TalkPreference, uid) |
| 42 | if pref == None: | 41 | if pref == None: |
| @@ -57,7 +56,7 @@ def get_own_preferences(uid): | |||
| 57 | ) | 56 | ) |
| 58 | 57 | ||
| 59 | 58 | ||
| 60 | @app.route("/-/talkpreferences/", methods=["POST"]) | 59 | @bp.route("/-/talkpreferences/", methods=["POST"]) |
| 61 | def store_preferences(): | 60 | def store_preferences(): |
| 62 | try: | 61 | try: |
| 63 | content = request.json | 62 | content = request.json |
| @@ -92,7 +91,7 @@ def store_preferences(): | |||
| 92 | ) | 91 | ) |
| 93 | 92 | ||
| 94 | 93 | ||
| 95 | @app.route("/-/talkpreferences/<uid>", methods=["POST", "PUT"]) | 94 | @bp.route("/-/talkpreferences/<uid>", methods=["POST", "PUT"]) |
| 96 | def update_preferences(uid): | 95 | def update_preferences(uid): |
| 97 | pref = db.session.get(TalkPreference, uid) | 96 | pref = db.session.get(TalkPreference, uid) |
| 98 | if pref == None: | 97 | if pref == None: |
| @@ -105,7 +104,7 @@ def update_preferences(uid): | |||
| 105 | return jsonify({"uid": pref.uid, "hashed_uid": pref.public_uid}) | 104 | return jsonify({"uid": pref.uid, "hashed_uid": pref.public_uid}) |
| 106 | 105 | ||
| 107 | 106 | ||
| 108 | @app.route("/-/talkpreferences/public/<public_uid>", methods=["GET"]) | 107 | @bp.route("/-/talkpreferences/public/<public_uid>", methods=["GET"]) |
| 109 | def get_preferences(public_uid): | 108 | def get_preferences(public_uid): |
| 110 | pref = ( | 109 | pref = ( |
| 111 | db.session.query(TalkPreference) | 110 | db.session.query(TalkPreference) |
| @@ -238,7 +237,7 @@ def export_prefs(config): | |||
| 238 | print("[]]") | 237 | print("[]]") |
| 239 | 238 | ||
| 240 | 239 | ||
| 241 | if __name__ == "__main__": | 240 | def parse_args(): |
| 242 | parser = ArgumentParser(description="halfnarp2") | 241 | parser = ArgumentParser(description="halfnarp2") |
| 243 | parser.add_argument( | 242 | parser.add_argument( |
| 244 | "-i", | 243 | "-i", |
| @@ -257,20 +256,20 @@ if __name__ == "__main__": | |||
| 257 | parser.add_argument( | 256 | parser.add_argument( |
| 258 | "-c", "--config", help="Config file location", default="./config.json" | 257 | "-c", "--config", help="Config file location", default="./config.json" |
| 259 | ) | 258 | ) |
| 260 | args = parser.parse_args() | 259 | return parser.parse_args() |
| 261 | 260 | ||
| 262 | with open(args.config, mode="r", encoding="utf-8") as json_file: | ||
| 263 | config = json.load(json_file) | ||
| 264 | config["pretalx-api-url"] = ( | ||
| 265 | config["pretalx-url"] + "api/events/" + config["pretalx-conference"] | ||
| 266 | ) | ||
| 267 | 261 | ||
| 268 | app.config["SQLALCHEMY_DATABASE_URI"] = config.get( | 262 | def create_app(config_file="./config.json"): |
| 263 | app = Flask(__name__) | ||
| 264 | with open(config_file, mode="r", encoding="utf-8") as config_file: | ||
| 265 | app.config["halfnarp"] = json.load(config_file) | ||
| 266 | |||
| 267 | app.config["SQLALCHEMY_DATABASE_URI"] = app.config["halfnarp"].get( | ||
| 269 | "database-uri", "sqlite:///test.db" | 268 | "database-uri", "sqlite:///test.db" |
| 270 | ) | 269 | ) |
| 271 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False | 270 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False |
| 272 | app.config["SERVER_NAME"] = config.get("server-name", "localhost") | 271 | app.config["SERVER_NAME"] = app.config["halfnarp"].get("server-name", "localhost") |
| 273 | app.config["SECRET_KEY"] = config.get("server-secret", "<YOUR SERVER SECRET HERE>") | 272 | app.config["SECRET_KEY"] = app.config["halfnarp"].get("server-secret", "<YOUR SERVER SECRET HERE>") |
| 274 | 273 | ||
| 275 | if app.config["SECRET_KEY"] == "<YOUR SERVER SECRET HERE>": | 274 | if app.config["SECRET_KEY"] == "<YOUR SERVER SECRET HERE>": |
| 276 | print("You must set the server-secret in your config.json") | 275 | print("You must set the server-secret in your config.json") |
| @@ -281,15 +280,24 @@ if __name__ == "__main__": | |||
| 281 | CORS() | 280 | CORS() |
| 282 | 281 | ||
| 283 | db.init_app(app) | 282 | db.init_app(app) |
| 283 | app.register_blueprint(bp) | ||
| 284 | return app | ||
| 284 | 285 | ||
| 286 | if __name__ == "__main__": | ||
| 287 | args = parse_args() | ||
| 288 | app = create_app(args.config) | ||
| 289 | |||
| 290 | app.config["halfnarp"]["pretalx-api-url"] = ( | ||
| 291 | app.config["halfnarp"]["pretalx-url"] + "api/events/" + app.config["halfnarp"]["pretalx-conference"] | ||
| 292 | ) | ||
| 285 | with app.app_context(): | 293 | with app.app_context(): |
| 286 | db.create_all() | 294 | db.create_all() |
| 287 | if args.pretalx_import: | 295 | if args.pretalx_import: |
| 288 | fetch_talks(config) | 296 | fetch_talks(app.config["halfnarp"]) |
| 289 | elif args.fullnarp_export: | 297 | elif args.fullnarp_export: |
| 290 | export_prefs(config) | 298 | export_prefs(app.config["halfnarp"]) |
| 291 | else: | 299 | else: |
| 292 | app.run( | 300 | app.run( |
| 293 | host=config.get("host", "127.0.0.1"), | 301 | host=app.config["halfnarp"].get("host", "127.0.0.1"), |
| 294 | port=int(config.get("port", "8080")), | 302 | port=int(app.config["halfnarp"].get("port", "8080")), |
| 295 | ) | 303 | ) |
diff --git a/static/faq.html b/static/faq.html index aefd03d..a90b4b5 100644 --- a/static/faq.html +++ b/static/faq.html | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | </head> | 7 | </head> |
| 8 | <body> | 8 | <body> |
| 9 | 9 | ||
| 10 | <div class="headline">The 38C3 halfnarp FAQ</div> | 10 | <div class="headline">The 39C3 halfnarp FAQ</div> |
| 11 | 11 | ||
| 12 | <dl> | 12 | <dl> |
| 13 | <dt>Q: What is halfnarp?</dt> | 13 | <dt>Q: What is halfnarp?</dt> |
| @@ -18,7 +18,7 @@ | |||
| 18 | </dd> | 18 | </dd> |
| 19 | <dt>Q: How does it work?</dt> | 19 | <dt>Q: How does it work?</dt> |
| 20 | <dd> | 20 | <dd> |
| 21 | <p>A: 38C3 Fahrplan is curated by six teams each responsible for one track. By default, lectures are sorted by these tracks.</p> | 21 | <p>A: 39C3 Fahrplan is curated by six teams each responsible for one track. By default, lectures are sorted by these tracks.</p> |
| 22 | <ul> | 22 | <ul> |
| 23 | <li>On a desktop browser, hovering over an event’s description reveals the full abstract. Clicking on an event adds or removes events to/from your favorites list – they turn green.</li> | 23 | <li>On a desktop browser, hovering over an event’s description reveals the full abstract. Clicking on an event adds or removes events to/from your favorites list – they turn green.</li> |
| 24 | <li>On mobile browsers, tapping an event once selects it and reveals the whole content. Tapping a selected event adds or removes events to/from your favorites list – they turn green.</li> | 24 | <li>On mobile browsers, tapping an event once selects it and reveals the whole content. Tapping a selected event adds or removes events to/from your favorites list – they turn green.</li> |
diff --git a/static/halfnarp.js b/static/halfnarp.js index b7f4a45..5cd1ef3 100644 --- a/static/halfnarp.js +++ b/static/halfnarp.js | |||
| @@ -93,7 +93,7 @@ function redraw_calendar(myuid, ids) { | |||
| 93 | calendar += 'DTSTART:' + start.toISOString().replace(/-|;|:|\./g, '').replace(/...Z$/, 'Z') + '\r\n'; | 93 | calendar += 'DTSTART:' + start.toISOString().replace(/-|;|:|\./g, '').replace(/...Z$/, 'Z') + '\r\n'; |
| 94 | calendar += 'DURATION:PT' + item.duration + 'S\r\n'; | 94 | calendar += 'DURATION:PT' + item.duration + 'S\r\n'; |
| 95 | calendar += 'LOCATION:' + item.room_name + '\r\n'; | 95 | calendar += 'LOCATION:' + item.room_name + '\r\n'; |
| 96 | calendar += 'URL:http://events.ccc.de/congress/2023/Fahrplan/events/' + item.event_id + '.html\r\n'; | 96 | calendar += 'URL:http://events.ccc.de/congress/2025/Fahrplan/events/' + item.event_id + '.html\r\n'; |
| 97 | calendar += 'SUMMARY:' + item.title + '\r\n'; | 97 | calendar += 'SUMMARY:' + item.title + '\r\n'; |
| 98 | calendar += 'DESCRIPTION:' + item.abstract.replace(/\n|\r/g, ' ') + '\r\n'; | 98 | calendar += 'DESCRIPTION:' + item.abstract.replace(/\n|\r/g, ' ') + '\r\n'; |
| 99 | // console.log( 'id:' + id + ' ' + all_events[id] ); | 99 | // console.log( 'id:' + id + ' ' + all_events[id] ); |
| @@ -1,4 +1,4 @@ | |||
| 1 | from halfnarp2 import app | 1 | from halfnarp2 import create_app |
| 2 | 2 | ||
| 3 | if __name__ == "__main__": | 3 | app = create_app("config.json") |
| 4 | app.run(debug=False) | 4 | #app.run(debug=False) |
