diff options
| -rw-r--r-- | Makefile | 18 | ||||
| -rwxr-xr-x | halfnarp2.py | 107 | ||||
| -rwxr-xr-x | scripts/gen_css_tables.py | 2 | ||||
| -rw-r--r-- | static/faq.html | 8 | ||||
| -rw-r--r-- | static/fullnarp.html | 28 | ||||
| -rw-r--r-- | static/fullnarp.js | 4 | ||||
| -rw-r--r-- | static/halfnarp.js | 43 | ||||
| -rw-r--r-- | static/index.html | 37 | ||||
| -rw-r--r-- | wsgi.py | 6 |
9 files changed, 137 insertions, 116 deletions
| @@ -1,20 +1,28 @@ | |||
| 1 | all: install | 1 | all: install |
| 2 | 2 | ||
| 3 | import: venv | 3 | install: venv/bin/flask |
| 4 | |||
| 5 | import: install | ||
| 4 | venv/bin/python3 ./halfnarp2.py -i | 6 | venv/bin/python3 ./halfnarp2.py -i |
| 5 | 7 | ||
| 6 | do-export: venv | 8 | do-export: install |
| 7 | venv/bin/python3 ./halfnarp2.py -e | 9 | venv/bin/python3 ./halfnarp2.py -e |
| 8 | 10 | ||
| 9 | run: venv | 11 | run: install |
| 10 | PYTHONIOENCODING=utf-8 venv/bin/python3 ./halfnarp2.py | 12 | PYTHONIOENCODING=utf-8 venv/bin/python3 ./halfnarp2.py |
| 11 | 13 | ||
| 12 | run-fullnarp: venv | 14 | run-halfnarp-waitress: install venv/bin/waitress-serve |
| 15 | PYTHONIOENCODING=utf-8 venv/bin/python3 venv/bin/waitress-serve --host 127.0.0.1 --port 5023 wsgi:app | ||
| 16 | |||
| 17 | run-fullnarp: install | ||
| 13 | PYTHONIOENCODING=utf-8 venv/bin/python3 ./fullnarp.py | 18 | PYTHONIOENCODING=utf-8 venv/bin/python3 ./fullnarp.py |
| 14 | 19 | ||
| 20 | venv/bin/waitress-serve: install | ||
| 21 | venv/bin/pip install waitress | ||
| 22 | |||
| 15 | venv: | 23 | venv: |
| 16 | python3 -m venv ./venv | 24 | python3 -m venv ./venv |
| 17 | 25 | ||
| 18 | install: venv | 26 | venv/bin/flask: venv |
| 19 | venv/bin/pip install --upgrade pip | 27 | venv/bin/pip install --upgrade pip |
| 20 | venv/bin/pip install -r requirements.txt | 28 | venv/bin/pip install -r requirements.txt |
diff --git a/halfnarp2.py b/halfnarp2.py index 0cc3906..fcc8788 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: |
| @@ -46,7 +45,7 @@ def get_own_preferences(uid): | |||
| 46 | { | 45 | { |
| 47 | "hashed_uid": pref.public_uid, | 46 | "hashed_uid": pref.public_uid, |
| 48 | "public_url": url_for( | 47 | "public_url": url_for( |
| 49 | "get_preferences", | 48 | "main.get_preferences", |
| 50 | public_uid=public_uid, | 49 | public_uid=public_uid, |
| 51 | _external=True, | 50 | _external=True, |
| 52 | _scheme="https", | 51 | _scheme="https", |
| @@ -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 |
| @@ -80,19 +79,19 @@ def store_preferences(): | |||
| 80 | "uid": uid, | 79 | "uid": uid, |
| 81 | "hashed_uid": str(public_uid), | 80 | "hashed_uid": str(public_uid), |
| 82 | "public_url": url_for( | 81 | "public_url": url_for( |
| 83 | "get_preferences", | 82 | "main.get_preferences", |
| 84 | public_uid=public_uid, | 83 | public_uid=public_uid, |
| 85 | _external=True, | 84 | _external=True, |
| 86 | _scheme="https", | 85 | _scheme="https", |
| 87 | ), | 86 | ), |
| 88 | "update_url": url_for( | 87 | "update_url": url_for( |
| 89 | "update_preferences", uid=uid, _external=True, _scheme="https" | 88 | "main.update_preferences", uid=uid, _external=True, _scheme="https" |
| 90 | ), | 89 | ), |
| 91 | } | 90 | } |
| 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) |
| @@ -121,14 +120,14 @@ def get_preferences(public_uid): | |||
| 121 | def filter_keys_halfnarp(session): | 120 | def filter_keys_halfnarp(session): |
| 122 | abstract_html = markdown.markdown(session["abstract"], enable_attributes=False) | 121 | abstract_html = markdown.markdown(session["abstract"], enable_attributes=False) |
| 123 | abstract_clean_html = Sanitizer().sanitize(abstract_html) | 122 | abstract_clean_html = Sanitizer().sanitize(abstract_html) |
| 124 | slot = session["slot"] | 123 | slot = next(iter(session["slots"]), {}) |
| 125 | 124 | ||
| 126 | return { | 125 | return { |
| 127 | "title": session.get("title", "!!! NO TITLE !!!"), | 126 | "title": session.get("title", "!!! NO TITLE !!!"), |
| 128 | "duration": 60 * session.get("duration", 40), | 127 | "duration": 60 * session.get("duration", 40), |
| 129 | "event_id": session["code"], | 128 | "event_id": session["code"], |
| 130 | "language": session.get("content_locale", "de"), | 129 | "language": session.get("content_locale", "de"), |
| 131 | "track_id": session["track_id"], | 130 | "track_id": session["track"], |
| 132 | "speaker_names": ", ".join( | 131 | "speaker_names": ", ".join( |
| 133 | [speaker.get("name", "unnamed") for speaker in session.get("speakers", {})] | 132 | [speaker.get("name", "unnamed") for speaker in session.get("speakers", {})] |
| 134 | ), | 133 | ), |
| @@ -141,7 +140,7 @@ def filter_keys_halfnarp(session): | |||
| 141 | def filter_keys_fullnarp(session, speakers): | 140 | def filter_keys_fullnarp(session, speakers): |
| 142 | abstract_html = markdown.markdown(session["abstract"], enable_attributes=False) | 141 | abstract_html = markdown.markdown(session["abstract"], enable_attributes=False) |
| 143 | abstract_clean_html = Sanitizer().sanitize(abstract_html) | 142 | abstract_clean_html = Sanitizer().sanitize(abstract_html) |
| 144 | slot = session["slot"] | 143 | slot = next(iter(session["slots"]), {}) |
| 145 | 144 | ||
| 146 | speaker_info = [] | 145 | speaker_info = [] |
| 147 | for speaker in session.get("speakers", {}): | 146 | for speaker in session.get("speakers", {}): |
| @@ -181,7 +180,7 @@ def filter_keys_fullnarp(session, speakers): | |||
| 181 | "duration": 60 * session.get("duration", 40), | 180 | "duration": 60 * session.get("duration", 40), |
| 182 | "event_id": session["code"], | 181 | "event_id": session["code"], |
| 183 | "language": session.get("content_locale", "de"), | 182 | "language": session.get("content_locale", "de"), |
| 184 | "track_id": session["track_id"], | 183 | "track_id": session["track"], |
| 185 | "speakers": speaker_info, | 184 | "speakers": speaker_info, |
| 186 | "speaker_names": ", ".join( | 185 | "speaker_names": ", ".join( |
| 187 | [speaker.get("name", "unnamed") for speaker in session.get("speakers", {})] | 186 | [speaker.get("name", "unnamed") for speaker in session.get("speakers", {})] |
| @@ -194,27 +193,28 @@ def filter_keys_fullnarp(session, speakers): | |||
| 194 | 193 | ||
| 195 | def fetch_talks(config): | 194 | def fetch_talks(config): |
| 196 | sess = requests.Session() | 195 | sess = requests.Session() |
| 197 | 196 | headers = {'Accept': 'application/json', | |
| 198 | response = sess.get( | 197 | 'Authorization': "Token " + config['pretalx-token'], |
| 199 | config["pretalx-api-url"] + "/submissions/?format=json&limit=20000", | 198 | 'Pretalx-Version' : 'v1'} |
| 200 | stream=True, | 199 | |
| 201 | headers={"Authorization": "Token " + config["pretalx-token"]}, | 200 | speakers_result = [] |
| 202 | ) | 201 | url = config['pretalx-api-url'] + '/speakers/?format=json&limit=20000' |
| 203 | # with open('dump.txt', mode='wb') as localfile: | 202 | while url: |
| 204 | # localfile.write(response.content) | 203 | response = sess.get(url, stream=True, headers=headers).json() |
| 205 | talks_json = json.loads(response.text) | 204 | speakers_result.extend(response['results']) |
| 206 | 205 | url = response['next'] | |
| 207 | response = sess.get( | 206 | speakers = { speaker['code']: speaker for speaker in speakers_result } |
| 208 | config["pretalx-api-url"] + "/speakers/?format=json&limit=20000", | 207 | |
| 209 | stream=True, | 208 | session_results = [] |
| 210 | headers={"Authorization": "Token " + config["pretalx-token"]}, | 209 | url = config['pretalx-api-url'] + '/submissions/?expand=speakers,slot,speakers.availabilities&format=json&limit=20000' |
| 211 | ) | 210 | while url: |
| 212 | speakers_json = json.loads(response.text) | 211 | response = sess.get(url, stream=True, headers=headers).json() |
| 213 | speakers = dict((speaker["code"], speaker) for speaker in speakers_json["results"]) | 212 | session_results.extend(response['results']) |
| 213 | url = response['next'] | ||
| 214 | 214 | ||
| 215 | sessions = [ | 215 | sessions = [ |
| 216 | filter_keys_halfnarp(submission) | 216 | filter_keys_halfnarp(submission) |
| 217 | for submission in talks_json["results"] | 217 | for submission in session_results |
| 218 | if submission["state"] == "confirmed" | 218 | if submission["state"] == "confirmed" |
| 219 | and not "non-public" in submission.get("tags", {}) | 219 | and not "non-public" in submission.get("tags", {}) |
| 220 | ] | 220 | ] |
| @@ -223,7 +223,7 @@ def fetch_talks(config): | |||
| 223 | 223 | ||
| 224 | sessions = [ | 224 | sessions = [ |
| 225 | filter_keys_fullnarp(submission, speakers) | 225 | filter_keys_fullnarp(submission, speakers) |
| 226 | for submission in talks_json["results"] | 226 | for submission in session_results |
| 227 | if submission["state"] == "confirmed" or submission["state"] == "accepted" | 227 | if submission["state"] == "confirmed" or submission["state"] == "accepted" |
| 228 | ] | 228 | ] |
| 229 | with open("var/talks_local_fullnarp", mode="w", encoding="utf8") as sessionsfile: | 229 | with open("var/talks_local_fullnarp", mode="w", encoding="utf8") as sessionsfile: |
| @@ -237,7 +237,7 @@ def export_prefs(config): | |||
| 237 | print("[]]") | 237 | print("[]]") |
| 238 | 238 | ||
| 239 | 239 | ||
| 240 | if __name__ == "__main__": | 240 | def parse_args(): |
| 241 | parser = ArgumentParser(description="halfnarp2") | 241 | parser = ArgumentParser(description="halfnarp2") |
| 242 | parser.add_argument( | 242 | parser.add_argument( |
| 243 | "-i", | 243 | "-i", |
| @@ -256,20 +256,20 @@ if __name__ == "__main__": | |||
| 256 | parser.add_argument( | 256 | parser.add_argument( |
| 257 | "-c", "--config", help="Config file location", default="./config.json" | 257 | "-c", "--config", help="Config file location", default="./config.json" |
| 258 | ) | 258 | ) |
| 259 | args = parser.parse_args() | 259 | return parser.parse_args() |
| 260 | 260 | ||
| 261 | with open(args.config, mode="r", encoding="utf-8") as json_file: | ||
| 262 | config = json.load(json_file) | ||
| 263 | config["pretalx-api-url"] = ( | ||
| 264 | config["pretalx-url"] + "api/events/" + config["pretalx-conference"] | ||
| 265 | ) | ||
| 266 | 261 | ||
| 267 | 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( | ||
| 268 | "database-uri", "sqlite:///test.db" | 268 | "database-uri", "sqlite:///test.db" |
| 269 | ) | 269 | ) |
| 270 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False | 270 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False |
| 271 | app.config["SERVER_NAME"] = config.get("server-name", "localhost") | 271 | app.config["SERVER_NAME"] = app.config["halfnarp"].get("server-name", "localhost") |
| 272 | 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>") |
| 273 | 273 | ||
| 274 | if app.config["SECRET_KEY"] == "<YOUR SERVER SECRET HERE>": | 274 | if app.config["SECRET_KEY"] == "<YOUR SERVER SECRET HERE>": |
| 275 | print("You must set the server-secret in your config.json") | 275 | print("You must set the server-secret in your config.json") |
| @@ -280,15 +280,24 @@ if __name__ == "__main__": | |||
| 280 | CORS() | 280 | CORS() |
| 281 | 281 | ||
| 282 | db.init_app(app) | 282 | db.init_app(app) |
| 283 | app.register_blueprint(bp) | ||
| 284 | return app | ||
| 283 | 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 | ) | ||
| 284 | with app.app_context(): | 293 | with app.app_context(): |
| 285 | db.create_all() | 294 | db.create_all() |
| 286 | if args.pretalx_import: | 295 | if args.pretalx_import: |
| 287 | fetch_talks(config) | 296 | fetch_talks(app.config["halfnarp"]) |
| 288 | elif args.fullnarp_export: | 297 | elif args.fullnarp_export: |
| 289 | export_prefs(config) | 298 | export_prefs(app.config["halfnarp"]) |
| 290 | else: | 299 | else: |
| 291 | app.run( | 300 | app.run( |
| 292 | host=config.get("host", "127.0.0.1"), | 301 | host=app.config["halfnarp"].get("host", "127.0.0.1"), |
| 293 | port=int(config.get("port", "8080")), | 302 | port=int(app.config["halfnarp"].get("port", "8080")), |
| 294 | ) | 303 | ) |
diff --git a/scripts/gen_css_tables.py b/scripts/gen_css_tables.py index 4bc5ea8..0eb88f4 100755 --- a/scripts/gen_css_tables.py +++ b/scripts/gen_css_tables.py | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | start_y = 400 | 8 | start_y = 400 |
| 9 | starttime = 10 | 9 | starttime = 10 |
| 10 | endtime = 28 | 10 | endtime = 28 |
| 11 | rooms = 3 | 11 | rooms = 4 |
| 12 | days = 4 | 12 | days = 4 |
| 13 | columns = days * rooms # how many columns does | 13 | columns = days * rooms # how many columns does |
| 14 | event_gap = { 'large': 5, 'medium': 3, 'small': 2 } # how much is an event shorter than what the grid would allow in px | 14 | event_gap = { 'large': 5, 'medium': 3, 'small': 2 } # how much is an event shorter than what the grid would allow in px |
diff --git a/static/faq.html b/static/faq.html index aefd03d..e5f53cd 100644 --- a/static/faq.html +++ b/static/faq.html | |||
| @@ -3,11 +3,11 @@ | |||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>halfnarp FAQ</title> | 5 | <title>halfnarp FAQ</title> |
| 6 | <link rel="stylesheet" href="style.css"> | 6 | <link rel="stylesheet" href="style_39c3.css"> |
| 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> |
| @@ -38,7 +38,7 @@ | |||
| 38 | </dd> | 38 | </dd> |
| 39 | <dt>Q: How can I help?</dt> | 39 | <dt>Q: How can I help?</dt> |
| 40 | <dd> | 40 | <dd> |
| 41 | <p>A: Submitting your preferences to the halfnarp servers helps a lot. You can find and create pull requests for <a href="https://github.com/tomster/halfnarp">halfnarp on its github home</a>.</p> | 41 | <p>A: Submitting your preferences to the halfnarp servers helps a lot. You can the halfnarp living <a href="https://erdgeist.org/gitweb/halfnarp2">halfnarp2 on its git home</a>.</p> |
| 42 | </dd> | 42 | </dd> |
| 43 | 43 | ||
| 44 | </html> | 44 | </html> |
diff --git a/static/fullnarp.html b/static/fullnarp.html index a00f89e..3b86d6c 100644 --- a/static/fullnarp.html +++ b/static/fullnarp.html | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>FULLnarp web scheduling helper app</title> | 5 | <title>FULLnarp web scheduling helper app</title> |
| 6 | <link rel="stylesheet" href="style_38c3.css"> | 6 | <link rel="stylesheet" href="style_39c3.css"> |
| 7 | <link rel="stylesheet" href="style_38c3_tables.css"> | 7 | <link rel="stylesheet" href="style_39c3_tables.css"> |
| 8 | <script src="fullnarp.js"></script> | 8 | <script src="fullnarp.js"></script> |
| 9 | <script> | 9 | <script> |
| 10 | document.addEventListener('DOMContentLoaded', () => { do_the_fullnarp(); }); | 10 | document.addEventListener('DOMContentLoaded', () => { do_the_fullnarp(); }); |
| @@ -20,7 +20,7 @@ | |||
| 20 | </div> | 20 | </div> |
| 21 | <div class="trashbin">🗑️</div> | 21 | <div class="trashbin">🗑️</div> |
| 22 | <div class="version">version</div> | 22 | <div class="version">version</div> |
| 23 | <div class="headline">The 38C3 fullnarp</div> | 23 | <div class="headline">The 39C3 fullnarp</div> |
| 24 | <div class="header"> | 24 | <div class="header"> |
| 25 | <div class="leftbox"> | 25 | <div class="leftbox"> |
| 26 | <input id="filter" type="text" placeholder="Filter events"/> | 26 | <input id="filter" type="text" placeholder="Filter events"/> |
| @@ -46,10 +46,10 @@ | |||
| 46 | </div> | 46 | </div> |
| 47 | </div> | 47 | </div> |
| 48 | </div> | 48 | </div> |
| 49 | <div class="day_1 room-label room1">Saal 1</div><div class="day_1 room-label room2">Saal 2</div><div class="day_1 room-label room3">Saal 3</div></div> | 49 | <div class="day_1 room-label room1">Saal 1</div><div class="day_1 room-label room2">Saal 2</div><div class="day_1 room-label room3">Saal 3</div><div class="day_1 room-label room4">Saal 4</div></div> |
| 50 | <div class="day_2 room-label room1">Saal 1</div><div class="day_2 room-label room2">Saal 2</div><div class="day_2 room-label room3">Saal 3</div></div> | 50 | <div class="day_2 room-label room1">Saal 1</div><div class="day_2 room-label room2">Saal 2</div><div class="day_2 room-label room3">Saal 3</div><div class="day_2 room-label room4">Saal 4</div></div> |
| 51 | <div class="day_3 room-label room1">Saal 1</div><div class="day_3 room-label room2">Saal 2</div><div class="day_3 room-label room3">Saal 3</div></div> | 51 | <div class="day_3 room-label room1">Saal 1</div><div class="day_3 room-label room2">Saal 2</div><div class="day_3 room-label room3">Saal 3</div><div class="day_3 room-label room4">Saal 4</div></div> |
| 52 | <div class="day_4 room-label room1">Saal 1</div><div class="day_4 room-label room2">Saal 2</div><div class="day_4 room-label room3">Saal 3</div></div> | 52 | <div class="day_4 room-label room1">Saal 1</div><div class="day_4 room-label room2">Saal 2</div><div class="day_4 room-label room3">Saal 3</div><div class="day_4 room-label room4">Saal 4</div></div> |
| 53 | 53 | ||
| 54 | <div class=" time_1815 duration_3600 guide pause ">P A U S E</div> | 54 | <div class=" time_1815 duration_3600 guide pause ">P A U S E</div> |
| 55 | <div class=" time_1445 wholeblock room1 guide day_1"></div> | 55 | <div class=" time_1445 wholeblock room1 guide day_1"></div> |
| @@ -65,13 +65,13 @@ | |||
| 65 | <div class="wholeday room1 day_1 guide uneven"></div> | 65 | <div class="wholeday room1 day_1 guide uneven"></div> |
| 66 | <div class="wholeday room1 day_3 guide uneven"></div> | 66 | <div class="wholeday room1 day_3 guide uneven"></div> |
| 67 | 67 | ||
| 68 | <div class="track" id="6"><h2>Security</h2></div> | 68 | <div class="track" id="31"><h2>Security</h2></div> |
| 69 | <div class="track" id="4"><h2>Hardware & Making</h2></div> | 69 | <div class="track" id="32"><h2>Hardware & Making</h2></div> |
| 70 | <div class="track" id="7"><h2>Ethics, Society & Politics</h2></div> | 70 | <div class="track" id="28"><h2>Ethics, Society & Politics</h2></div> |
| 71 | <div class="track" id="2"><h2>CCC</h2></div> | 71 | <div class="track" id="30"><h2>CCC</h2></div> |
| 72 | <div class="track" id="3"><h2>Entertainment</h2></div> | 72 | <div class="track" id="27"><h2>Entertainment</h2></div> |
| 73 | <div class="track" id="5"><h2>Science</h2></div> | 73 | <div class="track" id="29"><h2>Science</h2></div> |
| 74 | <div class="track" id="1"><h2>Art & Beauty</div> | 74 | <div class="track" id="33"><h2>Art & Beauty</div> |
| 75 | <div class="track" id="999"><h2>Other</div> | 75 | <div class="track" id="999"><h2>Other</div> |
| 76 | 76 | ||
| 77 | </body> | 77 | </body> |
diff --git a/static/fullnarp.js b/static/fullnarp.js index c4e24f4..34a59c4 100644 --- a/static/fullnarp.js +++ b/static/fullnarp.js | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | let ws; // WebSocket instance | 1 | let ws; // WebSocket instance |
| 2 | let allrooms = ['1','2','3'] | 2 | let allrooms = ['1','2','3','4'] |
| 3 | let allminutes = ['00','05','10','15','20','25','30','35','40','45','50','55'] | 3 | let allminutes = ['00','05','10','15','20','25','30','35','40','45','50','55'] |
| 4 | let allhours = ['10','11','12','13','14','15','16','17','18','19','20','21','22','23','00','01','02']; | 4 | let allhours = ['10','11','12','13','14','15','16','17','18','19','20','21','22','23','00','01','02']; |
| 5 | let alldays = ['1','2','3','4']; | 5 | let alldays = ['1','2','3','4']; |
| @@ -395,7 +395,7 @@ function signalFullnarpConnect(state) { | |||
| 395 | 395 | ||
| 396 | function getFullnarpData() { | 396 | function getFullnarpData() { |
| 397 | signalFullnarpConnect('fullnarp-connecting'); | 397 | signalFullnarpConnect('fullnarp-connecting'); |
| 398 | connect = window.location.href.replace('http', 'ws') + '/ws/'; | 398 | connect = window.location.href.replace('http', 'ws') + 'ws/'; |
| 399 | ws = new WebSocket(connect); | 399 | ws = new WebSocket(connect); |
| 400 | 400 | ||
| 401 | ws.onopen = () => { | 401 | ws.onopen = () => { |
diff --git a/static/halfnarp.js b/static/halfnarp.js index b7f4a45..e40c84e 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] ); |
| @@ -102,14 +102,14 @@ function redraw_calendar(myuid, ids) { | |||
| 102 | } | 102 | } |
| 103 | }); | 103 | }); |
| 104 | calendar += 'END:VCALENDAR\r\n'; | 104 | calendar += 'END:VCALENDAR\r\n'; |
| 105 | $('.export-url-a').attr( 'href', "data:text/calendar;filename=38C3.ics," + encodeURIComponent(calendar) ); | 105 | $('.export-url-a').attr( 'href', "data:text/calendar;filename=39C3.ics," + encodeURIComponent(calendar) ); |
| 106 | $('.export-url').removeClass( 'hidden' ); | 106 | $('.export-url').removeClass( 'hidden' ); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | function do_the_halfnarp() { | 109 | function do_the_halfnarp() { |
| 110 | // var halfnarpAPI = 'talks_36C3.json'; | 110 | // var halfnarpAPI = 'talks_36C3.json'; |
| 111 | var halfnarpAPI = '/-/talkpreferences'; | 111 | var halfnarpAPI = '/-/talkpreferences'; |
| 112 | var halfnarpCorrs = 'corr_array_38c3.json'; | 112 | var halfnarpCorrs = 'corr_array_39c3.json'; |
| 113 | var halfnarpPubAPI = halfnarpAPI + '/public/'; | 113 | var halfnarpPubAPI = halfnarpAPI + '/public/'; |
| 114 | var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0)); | 114 | var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0)); |
| 115 | window.top.all_events = new Object(); | 115 | window.top.all_events = new Object(); |
| @@ -179,8 +179,8 @@ function do_the_halfnarp() { | |||
| 179 | return $(this).attr('event_id'); | 179 | return $(this).attr('event_id'); |
| 180 | }).get(); | 180 | }).get(); |
| 181 | try { | 181 | try { |
| 182 | localStorage['38C3-halfnarp'] = ids; | 182 | localStorage['39C3-halfnarp'] = ids; |
| 183 | myapi = localStorage.getItem('38C3-halfnarp-api'); | 183 | myapi = localStorage.getItem('39C3-halfnarp-api'); |
| 184 | if (myapi) { | 184 | if (myapi) { |
| 185 | myapi = myapi.replace(/.*?:\//g, ""); | 185 | myapi = myapi.replace(/.*?:\//g, ""); |
| 186 | myapi = 'https:/' + myapi.replace(/.*?:\//g, ""); | 186 | myapi = 'https:/' + myapi.replace(/.*?:\//g, ""); |
| @@ -207,9 +207,9 @@ function do_the_halfnarp() { | |||
| 207 | $('.info').text('submitted'); | 207 | $('.info').text('submitted'); |
| 208 | $('.info').removeClass('hidden'); | 208 | $('.info').removeClass('hidden'); |
| 209 | try { | 209 | try { |
| 210 | localStorage['38C3-halfnarp-api'] = data['update_url']; | 210 | localStorage['39C3-halfnarp-api'] = data['update_url']; |
| 211 | localStorage['38C3-halfnarp-pid'] = mypid = data['hashed_uid']; | 211 | localStorage['39C3-halfnarp-pid'] = mypid = data['hashed_uid']; |
| 212 | localStorage['38C3-halfnarp-uid'] = myuid = data['uid']; | 212 | localStorage['39C3-halfnarp-uid'] = myuid = data['uid']; |
| 213 | window.location.hash = mypid; | 213 | window.location.hash = mypid; |
| 214 | } catch(err) {} | 214 | } catch(err) {} |
| 215 | }, 'json' ).fail(function() { | 215 | }, 'json' ).fail(function() { |
| @@ -225,9 +225,9 @@ function do_the_halfnarp() { | |||
| 225 | contentType: "application/json", | 225 | contentType: "application/json", |
| 226 | dataType: 'json', | 226 | dataType: 'json', |
| 227 | }).done(function(data) { | 227 | }).done(function(data) { |
| 228 | localStorage['38C3-halfnarp-uid'] = myuid = data['uid']; | 228 | localStorage['39C3-halfnarp-uid'] = myuid = data['uid']; |
| 229 | if( localStorage['38C3-halfnarp-pid'] ) { | 229 | if( localStorage['39C3-halfnarp-pid'] ) { |
| 230 | window.location.hash = localStorage['38C3-halfnarp-pid']; | 230 | window.location.hash = localStorage['39C3-halfnarp-pid']; |
| 231 | } | 231 | } |
| 232 | $('.info').text('updated'); | 232 | $('.info').text('updated'); |
| 233 | $('.info').removeClass('hidden'); | 233 | $('.info').removeClass('hidden'); |
| @@ -279,18 +279,21 @@ function do_the_halfnarp() { | |||
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | /* Add callbacks for view selector */ | 281 | /* Add callbacks for view selector */ |
| 282 | if (0) { | ||
| 282 | document.querySelector('.vlist').onclick = function() { toggle_grid(0); }; | 283 | document.querySelector('.vlist').onclick = function() { toggle_grid(0); }; |
| 283 | document.querySelector('.vday1').onclick = function() { toggle_grid(1); }; | 284 | document.querySelector('.vday1').onclick = function() { toggle_grid(1); }; |
| 284 | document.querySelector('.vday2').onclick = function() { toggle_grid(2); }; | 285 | document.querySelector('.vday2').onclick = function() { toggle_grid(2); }; |
| 285 | document.querySelector('.vday3').onclick = function() { toggle_grid(3); }; | 286 | document.querySelector('.vday3').onclick = function() { toggle_grid(3); }; |
| 286 | document.querySelector('.vday4').onclick = function() { toggle_grid(4); }; | 287 | document.querySelector('.vday4').onclick = function() { toggle_grid(4); }; |
| 287 | document.querySelector('.vdays').onclick = function() { toggle_grid(5); }; | 288 | document.querySelector('.vdays').onclick = function() { toggle_grid(5); }; |
| 288 | 289 | } | |
| 289 | document.querySelector('.vlang').onclick = function() { document.body.classList.toggle('languages'); }; | 290 | document.querySelector('.vlang').onclick = function() { document.body.classList.toggle('languages'); }; |
| 290 | document.querySelector('.vtrack').onclick = function() { document.body.classList.toggle('all-tracks'); }; | 291 | document.querySelector('.vtrack').onclick = function() { document.body.classList.toggle('all-tracks'); }; |
| 291 | document.querySelector('.vnarpr').onclick = function() { $('.narpr').toggleClass('hidden'); set_random_event(); }; | 292 | document.querySelector('.vnarpr').onclick = function() { $('.narpr').toggleClass('hidden'); set_random_event(); }; |
| 292 | 293 | ||
| 294 | if (0) { | ||
| 293 | document.querySelector('.vcorr').onclick = toggle_corr_mode; | 295 | document.querySelector('.vcorr').onclick = toggle_corr_mode; |
| 296 | } | ||
| 294 | 297 | ||
| 295 | $('.vclass').click( function() { toggle_classifier( $(this).attr('classifier'), $(this).hasClass('track'), $(this).hasClass('two_poles')); }); | 298 | $('.vclass').click( function() { toggle_classifier( $(this).attr('classifier'), $(this).hasClass('track'), $(this).hasClass('two_poles')); }); |
| 296 | 299 | ||
| @@ -308,10 +311,10 @@ function do_the_halfnarp() { | |||
| 308 | /* If we've been here before, try to get local preferences. They are authoratative */ | 311 | /* If we've been here before, try to get local preferences. They are authoratative */ |
| 309 | var selection = [], friends = { }; | 312 | var selection = [], friends = { }; |
| 310 | try { | 313 | try { |
| 311 | selection = localStorage['38C3-halfnarp'] || []; | 314 | selection = localStorage['39C3-halfnarp'] || []; |
| 312 | friends = localStorage['38C3-halfnarp-friends'] || { }; | 315 | friends = localStorage['39C3-halfnarp-friends'] || { }; |
| 313 | myuid = localStorage['38C3-halfnarp-uid'] || ''; | 316 | myuid = localStorage['39C3-halfnarp-uid'] || ''; |
| 314 | mypid = localStorage['38C3-halfnarp-pid'] || ''; | 317 | mypid = localStorage['39C3-halfnarp-pid'] || ''; |
| 315 | } catch(err) { | 318 | } catch(err) { |
| 316 | } | 319 | } |
| 317 | 320 | ||
| @@ -354,7 +357,7 @@ function do_the_halfnarp() { | |||
| 354 | if( hour > 23) | 357 | if( hour > 23) |
| 355 | hour -= 24; | 358 | hour -= 24; |
| 356 | 359 | ||
| 357 | /* Fix up room for 38C3 */ | 360 | /* Fix up room for 39C3 */ |
| 358 | room = (item.room_id || '').toString().replace('1','room1').replace('2','room2').replace('3','room3'); | 361 | room = (item.room_id || '').toString().replace('1','room1').replace('2','room2').replace('3','room3'); |
| 359 | 362 | ||
| 360 | /* Apply attributes to sort events into calendar */ | 363 | /* Apply attributes to sort events into calendar */ |
| @@ -400,8 +403,8 @@ function do_the_halfnarp() { | |||
| 400 | } | 403 | } |
| 401 | }); | 404 | }); |
| 402 | 405 | ||
| 403 | $.getJSON( halfnarpCorrs, { format: 'json' }).done(function(data) { window.top.all_votes = data; }); | 406 | // $.getJSON( halfnarpCorrs, { format: 'json' }).done(function(data) { window.top.all_votes = data; }); |
| 404 | toggle_grid(5); | 407 | toggle_grid(0); |
| 405 | 408 | ||
| 406 | /* Check for a new friends public uid in location's #hash */ | 409 | /* Check for a new friends public uid in location's #hash */ |
| 407 | var shared = window.location.hash; | 410 | var shared = window.location.hash; |
| @@ -439,7 +442,7 @@ function do_the_halfnarp() { | |||
| 439 | $.getJSON( halfnarpPubAPI + friends.pid, { format: 'json' }) | 442 | $.getJSON( halfnarpPubAPI + friends.pid, { format: 'json' }) |
| 440 | .done(function( data ) { | 443 | .done(function( data ) { |
| 441 | friend.prefs = data.talk_ids; | 444 | friend.prefs = data.talk_ids; |
| 442 | localStorage['38C3-halfnarp-friends'] = friends; | 445 | localStorage['39C3-halfnarp-friends'] = friends; |
| 443 | update_friends(); | 446 | update_friends(); |
| 444 | }); | 447 | }); |
| 445 | } | 448 | } |
diff --git a/static/index.html b/static/index.html index bb680d1..bc8da67 100644 --- a/static/index.html +++ b/static/index.html | |||
| @@ -3,8 +3,8 @@ | |||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>halfnarp web scheduling helper app</title> | 5 | <title>halfnarp web scheduling helper app</title> |
| 6 | <link rel="stylesheet" href="style_38c3.css"> | 6 | <link rel="stylesheet" href="style_39c3.css"> |
| 7 | <link rel="stylesheet" href="style_38c3_tables.css"> | 7 | <link rel="stylesheet" href="style_39c3_tables.css"> |
| 8 | <script src="jquery-3.7.1.min.js"></script> | 8 | <script src="jquery-3.7.1.min.js"></script> |
| 9 | <script src="jquery.qrcode.min.js"></script> | 9 | <script src="jquery.qrcode.min.js"></script> |
| 10 | <script src="halfnarp.js"></script> | 10 | <script src="halfnarp.js"></script> |
| @@ -14,14 +14,14 @@ | |||
| 14 | </head> | 14 | </head> |
| 15 | <body class="size-small in-list halfnarp"> | 15 | <body class="size-small in-list halfnarp"> |
| 16 | <header class="header"> | 16 | <header class="header"> |
| 17 | <!--div class="views touch-only hidden vnarpr">narpr</div--> | 17 | <!--div class="views touch-only hidden vnarpr">narpr</div> |
| 18 | <div class="views vlist" title="Display all events sorted by track (Hotkey: L)">list</div> | 18 | <div class="views vlist" title="Display all events sorted by track (Hotkey: L)">list</div> |
| 19 | <div class="views vdays" title="Display all events in a 4-day-view (Hotkey: 0)">days</div> | 19 | <div class="views vdays" title="Display all events in a 4-day-view (Hotkey: 0)">days</div> |
| 20 | <div class="views vday1" title="Display all events on Day 1 (Hotkey: 1)">day 1</div> | 20 | <div class="views vday1" title="Display all events on Day 1 (Hotkey: 1)">day 1</div> |
| 21 | <div class="views vday2" title="Display all events on Day 2 (Hotkey: 2)">day 2</div> | 21 | <div class="views vday2" title="Display all events on Day 2 (Hotkey: 2)">day 2</div> |
| 22 | <div class="views vday3" title="Display all events on Day 3 (Hotkey: 3)">day 3</div> | 22 | <div class="views vday3" title="Display all events on Day 3 (Hotkey: 3)">day 3</div> |
| 23 | <div class="views vday4" title="Display all events on Day 4 (Hotkey: 4)">day 4</div> | 23 | <div class="views vday4" title="Display all events on Day 4 (Hotkey: 4)">day 4</div> |
| 24 | <div class="views vcorr" title="Enter correlation view (Hotkey: C)">half</div> | 24 | <div class="views vcorr" title="Enter correlation view (Hotkey: C)">half</div--> |
| 25 | <div class="views vtrack" title="Color event by track (Hotkey: T)">track</div> | 25 | <div class="views vtrack" title="Color event by track (Hotkey: T)">track</div> |
| 26 | <div class="views vlang" title="Color event by language (Hotkey: I)">lang</div> | 26 | <div class="views vlang" title="Color event by language (Hotkey: I)">lang</div> |
| 27 | <div class="header__right"> | 27 | <div class="header__right"> |
| @@ -51,9 +51,9 @@ | |||
| 51 | </div> | 51 | </div> |
| 52 | 52 | ||
| 53 | <div class="intro"> | 53 | <div class="intro"> |
| 54 | <h1>The 38C3 halfnarp</h1> | 54 | <h1>The 39C3 halfnarp</h1> |
| 55 | <div class="touch-only hidden vnarpr narpr-beta">narpr<sup>β</sup></div> | 55 | <div class="touch-only hidden vnarpr narpr-beta">narpr<sup>β</sup></div> |
| 56 | <p>Help us to reduce conflicts of scheduling 38C3's Fahrplan: Click on the talks you would like to watch and press submit.</p> | 56 | <p>Help us to reduce conflicts of scheduling 39C3's Fahrplan: Click on the talks you would like to watch and press submit.</p> |
| 57 | <p>For questions please read <a href="faq.html">our FAQ</a> and <a href="http://events.ccc.de/2014/11/25/lets-do-the-halfnarp/">our blogpost</a> for details. Please report problems to <a href="mailto:erdgeist@ccc.de">erdgeist@ccc.de</a>.</p> | 57 | <p>For questions please read <a href="faq.html">our FAQ</a> and <a href="http://events.ccc.de/2014/11/25/lets-do-the-halfnarp/">our blogpost</a> for details. Please report problems to <a href="mailto:erdgeist@ccc.de">erdgeist@ccc.de</a>.</p> |
| 58 | <p class="corr-hint">Select an event below to display halfnarp correlations, the darker the stronger.</p> | 58 | <p class="corr-hint">Select an event below to display halfnarp correlations, the darker the stronger.</p> |
| 59 | <p class="touch-only hidden">NEW on mobile: Swipe the narpr!</p> | 59 | <p class="touch-only hidden">NEW on mobile: Swipe the narpr!</p> |
| @@ -75,10 +75,10 @@ | |||
| 75 | </div--> | 75 | </div--> |
| 76 | <div style="clear:both"></div> | 76 | <div style="clear:both"></div> |
| 77 | 77 | ||
| 78 | <div class="day_1 room-label room1">Saal 1</div><div class="day_1 room-label room2">Saal GLITCH</div><div class="day_1 room-label room3">Saal ZIGZAG</div> | 78 | <div class="day_1 room-label room1">Saal One</div><div class="day_1 room-label room2">Saal Zero</div><div class="day_1 room-label room3">Saal Ground</div><div class="day_1 room-label room4">Saal Fuse</div> |
| 79 | <div class="day_2 room-label room1">Saal 1</div><div class="day_2 room-label room2">Saal GLITCH</div><div class="day_2 room-label room3">Saal ZIGZAG</div> | 79 | <div class="day_2 room-label room1">Saal One</div><div class="day_2 room-label room2">Saal Zero</div><div class="day_2 room-label room3">Saal Ground</div><div class="day_2 room-label room4">Saal Fuse</div> |
| 80 | <div class="day_3 room-label room1">Saal 1</div><div class="day_3 room-label room2">Saal GLITCH</div><div class="day_3 room-label room3">Saal ZIGZAG</div> | 80 | <div class="day_3 room-label room1">Saal One</div><div class="day_3 room-label room2">Saal Zero</div><div class="day_3 room-label room3">Saal Ground</div><div class="day_3 room-label room4">Saal Fuse</div> |
| 81 | <div class="day_4 room-label room1">Saal 1</div><div class="day_4 room-label room2">Saal GLITCH</div><div class="day_4 room-label room3">Saal ZIGZAG</div> | 81 | <div class="day_4 room-label room1">Saal One</div><div class="day_4 room-label room2">Saal Zero</div><div class="day_4 room-label room3">Saal Ground</div><div class="day_4 room-label room4">Saal Fuse</div> |
| 82 | 82 | ||
| 83 | <div class=" time_1815 duration_3600 guide pause pause1">P A U S E</div> | 83 | <div class=" time_1815 duration_3600 guide pause pause1">P A U S E</div> |
| 84 | <!--div class=" time_1950 duration_3600 guide pause ">P A U S E</div--> | 84 | <!--div class=" time_1950 duration_3600 guide pause ">P A U S E</div--> |
| @@ -90,13 +90,14 @@ | |||
| 90 | <div class="wholeday room1 day_3 guide uneven">Day 3</div> | 90 | <div class="wholeday room1 day_3 guide uneven">Day 3</div> |
| 91 | <div class="wholeday room1 day_4 guide">Day 4</div> | 91 | <div class="wholeday room1 day_4 guide">Day 4</div> |
| 92 | 92 | ||
| 93 | <div class="track" id="6"><h2>Security</h2></div> | 93 | <div class="track" id="31"><h2>Security</h2></div> |
| 94 | <div class="track" id="4"><h2>Hardware & Making</h2></div> | 94 | <div class="track" id="32"><h2>Hardware & Making</h2></div> |
| 95 | <div class="track" id="7"><h2>Ethics, Society & Politics</h2></div> | 95 | <div class="track" id="28"><h2>Ethics, Society & Politics</h2></div> |
| 96 | <div class="track" id="2"><h2>CCC</h2></div> | 96 | <div class="track" id="30"><h2>CCC</h2></div> |
| 97 | <div class="track" id="3"><h2>Entertainment</h2></div> | 97 | <div class="track" id="27"><h2>Entertainment</h2></div> |
| 98 | <div class="track" id="5"><h2>Science</h2></div> | 98 | <div class="track" id="29"><h2>Science</h2></div> |
| 99 | <div class="track" id="1"><h2>Art & Beauty</div> | 99 | <div class="track" id="33"><h2>Art & Beauty</div> |
| 100 | <!--div class="track" id="999"><h2>Other</div--> | ||
| 100 | 101 | ||
| 101 | <footer class="footer"> | 102 | <footer class="footer"> |
| 102 | <div class="footer__filter-side"> | 103 | <div class="footer__filter-side"> |
| @@ -107,7 +108,7 @@ | |||
| 107 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 28" enable-background="new 0 0 22 28"><style type="text/css">.st0{fill:none;}</style><path class="st0" d="M-854-2242h1400v3600h-1400v-3600z"/><path d="M0 0v28l22-14-22-14z"/><path class="st0" d="M-16-10h48v48h-48v-48z"/></svg> | 108 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 28" enable-background="new 0 0 22 28"><style type="text/css">.st0{fill:none;}</style><path class="st0" d="M-854-2242h1400v3600h-1400v-3600z"/><path d="M0 0v28l22-14-22-14z"/><path class="st0" d="M-16-10h48v48h-48v-48z"/></svg> |
| 108 | </div> | 109 | </div> |
| 109 | <div class="info hidden"></div> | 110 | <div class="info hidden"></div> |
| 110 | <a class="export-url-a" download="38C3.ics" type="text/calendar" href="#"><div class="export-url hidden">38C3.ics</div></a> | 111 | <a class="export-url-a" download="39C3.ics" type="text/calendar" href="#"><div class="export-url hidden">39C3.ics</div></a> |
| 111 | <div id="qrcode" class="hidden"></div> | 112 | <div id="qrcode" class="hidden"></div> |
| 112 | </footer> | 113 | </footer> |
| 113 | 114 | ||
| @@ -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) |
