beta (#4)
Co-authored-by: mco-system <michael.costa@mcos.nc> Reviewed-on: #4
202
src/thsf/__init__.py
Normal file
@@ -0,0 +1,202 @@
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
from logging import config
|
||||
import yaml
|
||||
from flask import Flask, render_template, redirect, request, url_for
|
||||
from flask_minify import minify
|
||||
from thsf.backend import Backend
|
||||
from thsf.schedule import Schedule
|
||||
from thsf.navbar import Navbar
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
class AppConfig:
|
||||
""" Flask application config """
|
||||
CONFIG_FILENAME = "config.yml"
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Application
|
||||
# ------------------------------------------------------------------------------
|
||||
logger = logging.getLogger('wsgi')
|
||||
app = Flask(__name__)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Local configuration
|
||||
# ------------------------------------------------------------------------------
|
||||
app.config.from_object(__name__ + '.AppConfig')
|
||||
try:
|
||||
with open(app.config["CONFIG_FILENAME"], mode="r", encoding="utf-8") as local_config_file:
|
||||
app.local_config = yaml.load(local_config_file, Loader=yaml.SafeLoader)
|
||||
app.config["SECRET_KEY"] = app.local_config["app"]["secret_key"]
|
||||
app.config["LANGUAGES"] = app.local_config["app"]["languages"]
|
||||
config.dictConfig(app.local_config["log"])
|
||||
backend = Backend(url=app.local_config["pretalx"]["url"],
|
||||
apiprefix=app.local_config["pretalx"]["apiprefix"],
|
||||
apikey=app.local_config["pretalx"]["apikey"])
|
||||
schedule = Schedule()
|
||||
navbar = Navbar(config=app.local_config["navbar"])
|
||||
except Exception as err:
|
||||
logger.critical("[{}] {}".format(err.__class__, str(err)))
|
||||
sys.exit(1)
|
||||
|
||||
if app.local_config["log"]["root"]["level"] != "DEBUG":
|
||||
minify(app=app, html=True, js=True, cssless=True)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Tools
|
||||
# ------------------------------------------------------------------------------
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(err):
|
||||
return redirect(url_for('index'))
|
||||
|
||||
def get_slots():
|
||||
return backend.get(endpoint=f"events/{app.local_config['pretalx']['event']}/schedules/{app.local_config['pretalx']['schedule']}/").json()
|
||||
|
||||
def get_speaker_biography(name):
|
||||
try:
|
||||
speaker_info = backend.get(endpoint=f"events/{app.local_config['pretalx']['event']}/speakers/", params={"q": name}).json()
|
||||
logging.info(speaker_info)
|
||||
return speaker_info["results"][0]["biography"].strip()
|
||||
except Exception as err:
|
||||
logging.error(f"UnknownSpeakerError: {name}")
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Custom filters
|
||||
# ------------------------------------------------------------------------------
|
||||
@app.template_filter('date2dmyhm')
|
||||
def date2dmyhm(date):
|
||||
splitted_date = date.split("-")
|
||||
splitted_time = splitted_date[2].split("T")[1].split(":")
|
||||
year, month, day = (splitted_date[0],
|
||||
splitted_date[1],
|
||||
splitted_date[2].split("T")[0])
|
||||
hour, minutes = (splitted_time[0], splitted_time[1].split("+")[0])
|
||||
return f"{day}/{month} {hour}:{minutes}"
|
||||
|
||||
@app.template_filter('date2dayclass')
|
||||
def date2dayclass(date):
|
||||
classes = {"26/05": "bg1",
|
||||
"27/05": "bg2",
|
||||
"28/05": "bg3",}
|
||||
splitted_date = date.split("-")
|
||||
month, day = (splitted_date[1],
|
||||
splitted_date[2].split("T")[0])
|
||||
return classes[f"{day}/{month}"]
|
||||
|
||||
@app.template_filter('toicon')
|
||||
def date2dmyhm(slot_type):
|
||||
slot_types = {"Projection": "fa-solid fa-film",
|
||||
"Presentation Courte": "fa-solid fa-person-chalkboard",
|
||||
"DJ Set": "fa-solid fa-guitar",
|
||||
"Concert": "fa-solid fa-guitar",
|
||||
"Présentation": "fa-solid fa-person-chalkboard",
|
||||
"Table Ronde": "fa-solid fa-users-line",
|
||||
"Atelier": "fa-solid fa-screwdriver-wrench",
|
||||
"Exposition": "fa-solid fa-palette"}
|
||||
return slot_types[slot_type]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Routes
|
||||
# ------------------------------------------------------------------------------
|
||||
@app.route('/favicon.ico', methods=['GET'])
|
||||
def favicon():
|
||||
return redirect(url_for('static', filename='images/favicon.png'))
|
||||
|
||||
|
||||
@app.route('/', methods=['GET'])
|
||||
def index():
|
||||
return render_template("index.html",
|
||||
navbar=navbar.get_from_page(page="/"))
|
||||
|
||||
@app.route('/planning', methods=['GET'])
|
||||
def planning():
|
||||
slots = get_slots()
|
||||
for slot in slots.get("slots"):
|
||||
for speaker in slot.get("speakers"):
|
||||
speaker["biography"] = get_speaker_biography(speaker.get("name"))
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/planning"))
|
||||
|
||||
@app.route('/place', methods=['GET'])
|
||||
def place():
|
||||
return render_template("index.html",
|
||||
navbar=navbar.get_from_page(page="/place"))
|
||||
|
||||
@app.route('/food', methods=['GET'])
|
||||
def food():
|
||||
return render_template("index.html",
|
||||
navbar=navbar.get_from_page(page="/food"))
|
||||
|
||||
@app.route('/goodies', methods=['GET'])
|
||||
def goodies():
|
||||
return render_template("goodies.html",
|
||||
navbar=navbar.get_from_page(page="/goodies"))
|
||||
|
||||
@app.route('/concerts', methods=['GET'])
|
||||
def concerts():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/concerts"),
|
||||
filter=["concert", "dj set"])
|
||||
|
||||
@app.route('/workshops', methods=['GET'])
|
||||
def workshops():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/workshops"),
|
||||
filter=["workshop"])
|
||||
|
||||
@app.route('/screenings', methods=['GET'])
|
||||
def screenings():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/screenings"),
|
||||
filter=["screening"])
|
||||
|
||||
@app.route('/discussions', methods=['GET'])
|
||||
def discussions():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/discussions"),
|
||||
filter=["panel discussion"])
|
||||
|
||||
@app.route('/exhibitions', methods=['GET'])
|
||||
def exhibitions():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/exhibitions"),
|
||||
filter=["exhibition"])
|
||||
|
||||
@app.route('/talks', methods=['GET'])
|
||||
def talks():
|
||||
slots = get_slots()
|
||||
return render_template("planning.html",
|
||||
slots=sorted(slots.get("slots"),
|
||||
key=lambda slot: slot.get("slot").get("start")),
|
||||
navbar=navbar.get_from_page(page="/talks"),
|
||||
filter=["talk", "light talk"])
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# -- Main
|
||||
# ------------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
app.run(host='127.0.0.1', port=5000, debug=True)
|
||||
15
src/thsf/backend/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import requests
|
||||
import logging
|
||||
|
||||
class Backend:
|
||||
def __init__(self, url, apiprefix, apikey):
|
||||
self.url = url
|
||||
self.apiprefix = apiprefix
|
||||
self.apikey = apikey
|
||||
self.session = requests.Session()
|
||||
|
||||
def get(self, endpoint, params=None):
|
||||
url = f"{self.url}/{self.apiprefix}/{endpoint}"
|
||||
headers = {"Authorization": f"Token {self.apikey}",
|
||||
"Accept": "application/json"}
|
||||
return self.session.get(url, params=params, headers=headers)
|
||||
6
src/thsf/navbar/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class Navbar:
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def get_from_page(self, page):
|
||||
return [item for item in self.config["items"] if item["url"] != page]
|
||||
6
src/thsf/schedule/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class Schedule:
|
||||
def __init__(self):
|
||||
self.slots = list()
|
||||
|
||||
def set_slots(self, slots):
|
||||
self.slots = slots
|
||||
49
src/thsf/static/css/colors.css
Normal file
@@ -0,0 +1,49 @@
|
||||
@font-face {
|
||||
font-family: pfdintextcomppromedium;
|
||||
src: url(../fonts/PFDinTextCompPro-Medium.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: pfdintextcompprothin;
|
||||
src: url(../fonts/PFDinTextCompPro-Thin.ttf);
|
||||
}
|
||||
|
||||
:root {
|
||||
--main-bg-color: #e6007e;
|
||||
--alt-bg-color: #E59730;
|
||||
--alt2-bg-color: #9EBF43;
|
||||
--alt3-bg-color: #3096E5;
|
||||
--main-color: #ffffff;
|
||||
--alt-main-color: #1A000D;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
||||
.black {
|
||||
color: var(--alt-main-color);
|
||||
}
|
||||
|
||||
.thin {
|
||||
font-family: pfdintextcompprothin;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-family: pfdintextcomppromedium;
|
||||
}
|
||||
|
||||
.bg1 {
|
||||
background-color: var(--alt-bg-color);
|
||||
border-color: var(--alt-bg-color);
|
||||
}
|
||||
|
||||
.bg2 {
|
||||
background-color: var(--alt2-bg-color);
|
||||
border-color: var(--alt2-bg-color);
|
||||
}
|
||||
|
||||
.bg3 {
|
||||
background-color: var(--alt3-bg-color);
|
||||
border-color: var(--alt3-bg-color);
|
||||
}
|
||||
109
src/thsf/static/css/custom.css
Normal file
@@ -0,0 +1,109 @@
|
||||
@media screen and (min-width: 45em) {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
text-align: center;
|
||||
font-size: 9.75em;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
}
|
||||
.logo {
|
||||
width: inherit;
|
||||
}
|
||||
.header > span {
|
||||
margin: 0;
|
||||
}
|
||||
.subheader {
|
||||
margin: -1em 0 0 0;
|
||||
font-size: 3.47em;
|
||||
}
|
||||
.place {
|
||||
margin: 0;
|
||||
font-size: 2.145em;
|
||||
}
|
||||
.important {
|
||||
font-family: pfdintextcomppromedium;
|
||||
}
|
||||
.left {
|
||||
float: left;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.logo_partner {
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
margin: 1em;
|
||||
}
|
||||
.button {
|
||||
font-size: 2.5em;
|
||||
transition-property: color;
|
||||
transition-duration: 1s;
|
||||
}
|
||||
.button:hover {
|
||||
color: var(--main-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 44em) {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
text-align: center;
|
||||
font-size: 6.75em;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
}
|
||||
.header > span {
|
||||
margin: 0;
|
||||
}
|
||||
.logo {
|
||||
width: inherit;
|
||||
}
|
||||
.subheader {
|
||||
margin: -1em 0 0 0;
|
||||
font-size: 2.30em;
|
||||
}
|
||||
.place {
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.important {
|
||||
font-family: pfdintextcomppromedium;
|
||||
}
|
||||
.left {
|
||||
float: left;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
margin-left: 0.25em;
|
||||
}
|
||||
.logo_partner {
|
||||
max-width: 125px;
|
||||
max-height: 125px;
|
||||
margin: 0.25em;
|
||||
}
|
||||
.button {
|
||||
font-size: 1.2em;
|
||||
transition-property: color;
|
||||
transition-duration: 1s;
|
||||
}
|
||||
.button:hover {
|
||||
color: var(--main-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.goodies {
|
||||
text-align: justify;
|
||||
}
|
||||
.goodies_pic {
|
||||
max-width: 5em;
|
||||
}
|
||||
}
|
||||
18
src/thsf/static/css/elements.css
Normal file
@@ -0,0 +1,18 @@
|
||||
a {
|
||||
font-family: pfdintextcomppromedium;
|
||||
font-weight: 250;
|
||||
color: var(--alt-main-color);
|
||||
transition-property: color;
|
||||
transition-duration: 1s;
|
||||
text-decoration: wavy;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--main-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content > p,
|
||||
.content > h2 {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
66
src/thsf/static/css/slot.css
Normal file
@@ -0,0 +1,66 @@
|
||||
.slot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
margin-bottom: 1em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.slot_header {
|
||||
margin: 0;
|
||||
width: 20em;
|
||||
font-family: pfdintextcompprothin;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.slot_title {
|
||||
border-radius: 1em 0 0 0;
|
||||
color: var(--main-color);
|
||||
padding: 0.5em;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
width: 20em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slot_title .title{
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.slot_info {
|
||||
background-color: var(--main-color);
|
||||
color: var(--alt-main-color);
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
align-items: flex-start;
|
||||
border-radius: 0 0 0 1em;
|
||||
}
|
||||
|
||||
.speaker {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.speakers > .speaker > .name > span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.speaker_details,
|
||||
.slot_details {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.speaker_img,
|
||||
.slot_img {
|
||||
width: 18em;
|
||||
}
|
||||
15
src/thsf/static/css/style.css
Normal file
@@ -0,0 +1,15 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--main-bg-color);
|
||||
font-family: pfdintextcomppromedium;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
/* background-color: yellow; */
|
||||
}
|
||||
57
src/thsf/static/css/tooltip.css
Normal file
@@ -0,0 +1,57 @@
|
||||
.tooltip,
|
||||
.slot_tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
background-color: var(--alt-main-color);
|
||||
color: var(--main-color);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transform: translateX(-25%) translateY(-2em);
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.slot_tooltip .slot_tooltiptext {
|
||||
visibility: hidden;
|
||||
background-color: var(--main-color);
|
||||
color: var(--alt-main-color);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transform: translateX(2em);
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext::after,
|
||||
.slot_tooltip .slot_tooltiptext::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext,
|
||||
.slot_tooltip:hover .slot_tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
font-size: 0.7em;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
|
||||
72
src/thsf/static/css/wemakeporn.css
Normal file
@@ -0,0 +1,72 @@
|
||||
@media screen and (min-width: 45em) {
|
||||
.wemakeporn {
|
||||
background-color: #FFD036;
|
||||
color: #000000;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
border-radius: 1em;
|
||||
border-width: 1em;
|
||||
border-style: solid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
padding: 0.5em;
|
||||
transform: rotate(-20deg) translate(2em, -6em);
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
}
|
||||
.wemakeporn:hover{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.wemake {
|
||||
font-size: 7em;
|
||||
font-weight: bold;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.porn {
|
||||
font-size: 12em;
|
||||
font-weight: bold;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 44em) {
|
||||
.wemakeporn {
|
||||
background-color: #FFD036;
|
||||
color: #000000;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
border-radius: 0.5em;
|
||||
border-width: 0.5em;
|
||||
border-style: solid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
padding: 0.5em;
|
||||
transform: rotate(-20deg) translate(-1.5em, 8em);
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
}
|
||||
.wemakeporn:hover{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.wemake {
|
||||
font-size: 3.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.porn {
|
||||
font-size: 6em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
125
src/thsf/static/css/wrappers.css
Normal file
@@ -0,0 +1,125 @@
|
||||
@media screen and (min-width: 45em) {
|
||||
.page_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
padding-bottom: 5em;
|
||||
/* background-color: green; */
|
||||
}
|
||||
.center_wrapper,
|
||||
.header_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.logo_wrapper {
|
||||
margin: 1em 0 1em 0;
|
||||
width: 40em;
|
||||
}
|
||||
.navbar_wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding: 1em 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1.4em;
|
||||
background-color: var(--main-bg-color);
|
||||
width: 40em;
|
||||
transform: translateZ(2);
|
||||
}
|
||||
.content {
|
||||
font-size: 2em;
|
||||
font-family: pfdintextcompprothin;
|
||||
color: var(--main-color);
|
||||
width: 20em;
|
||||
text-align:justify;
|
||||
text-justify: inter-word;
|
||||
}
|
||||
.partners {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.subpartners {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 44em) {
|
||||
.page_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
padding-bottom: 2em;
|
||||
width: 21em;
|
||||
/* background-color: green; */
|
||||
}
|
||||
.center_wrapper ,
|
||||
.header_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
width: 21em;
|
||||
/* background-color: orangered; */
|
||||
}
|
||||
.logo_wrapper {
|
||||
margin-top: 0.5em;
|
||||
width: 21em;
|
||||
/* background-color: red; */
|
||||
}
|
||||
.navbar_wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
padding: 0.5em 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1em;
|
||||
background-color: var(--main-bg-color);
|
||||
width: 21em;
|
||||
transform: translateZ(2);
|
||||
/* background-color: white; */
|
||||
}
|
||||
.content {
|
||||
font-size: 1em;
|
||||
font-family: pfdintextcompprothin;
|
||||
color: var(--main-color);
|
||||
width: 21em;
|
||||
text-align:justify;
|
||||
text-justify: inter-word;
|
||||
}
|
||||
.partners {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.subpartners {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
BIN
src/thsf/static/fonts/PFDinTextCompPro-Medium.ttf
Normal file
BIN
src/thsf/static/fonts/PFDinTextCompPro-Thin.ttf
Normal file
BIN
src/thsf/static/fonts/Uni Sans Bold.otf
Normal file
BIN
src/thsf/static/fonts/Uni Sans Book.otf
Normal file
BIN
src/thsf/static/images/antistatik.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
src/thsf/static/images/bg.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src/thsf/static/images/clutch.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
src/thsf/static/images/favicon.png
Executable file
|
After Width: | Height: | Size: 31 KiB |
301
src/thsf/static/images/logo.svg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/thsf/static/images/pretalx-header.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
src/thsf/static/images/stickers.webp
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
src/thsf/static/images/sweatshirt.webp
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
src/thsf/static/images/terranova.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
src/thsf/static/images/tetalab.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
src/thsf/static/images/tetaneutral.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
src/thsf/static/images/totebag.webp
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/thsf/static/images/tshirt.webp
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
14
src/thsf/static/scripts/thsf23.js
Normal file
@@ -0,0 +1,14 @@
|
||||
function switch_visibility(item){
|
||||
console.log(item.style.visibility);
|
||||
console.log(item.style.display);
|
||||
if (item.style.visibility == "hidden" || item.style.display == "none") {
|
||||
item.style.visibility = "visible";
|
||||
item.style.display = "block";
|
||||
} else if (item.style.visibility == "" && item.style.display == "") {
|
||||
item.style.visibility = "visible";
|
||||
item.style.display = "block";
|
||||
} else {
|
||||
item.style.visibility = "hidden";
|
||||
item.style.display = "none";
|
||||
}
|
||||
}
|
||||
95
src/thsf/templates/base.html
Normal file
@@ -0,0 +1,95 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang='zxx'>
|
||||
<head>
|
||||
<title>THSF 2023: S/Extraire</title>
|
||||
<meta name="viewport" content="initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||
<script src="{{ url_for('static', filename='scripts/thsf23.js') }}"></script>
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/colors.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/wemakeporn.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/wrappers.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/custom.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/elements.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/tooltip.css') }}">
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/slot.css') }}">
|
||||
<link rel="icon"
|
||||
type="image/svg+xml"
|
||||
href="{{ url_for('static', filename='images/logo.svg') }}">
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
{% block headers %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page_wrapper">
|
||||
<div class="center_wrapper">
|
||||
<div class="header_wrapper">
|
||||
<div class="header">
|
||||
<span class="black bold">THSF</span>
|
||||
<span class="white thin">2023</span>
|
||||
</div>
|
||||
<div class="subheader">
|
||||
<span class="white thin">Toulouse Hacker Space Factory</span>
|
||||
</div>
|
||||
<div class="place">
|
||||
<span class="black thin">26 28 mai 2023 - </span>
|
||||
<span class="white bold">CINÉMA UTOPIA BORDEROUGE</span>
|
||||
</div>
|
||||
</div>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
<div class="partners">
|
||||
<div class="subpartners">
|
||||
<a href="https://www.tetalab.org/" target="_new">
|
||||
<img src="{{ url_for('static', filename='images/tetalab.png')}}"
|
||||
alt="Tetalab"
|
||||
title="Tetalab"
|
||||
class="logo_partner">
|
||||
</a>
|
||||
<a href="https://www.librairie-terranova.fr/" target="_new">
|
||||
<img src="{{ url_for('static', filename='images/terranova.jpg')}}"
|
||||
alt="Librairie Terra Nova"
|
||||
title="Librairie Terra Nova"
|
||||
class="logo_partner">
|
||||
</a>
|
||||
</div>
|
||||
<div class="subpartners">
|
||||
<a href="https://www.tetaneutral.net/" target="_new">
|
||||
<img src="{{ url_for('static', filename='images/tetaneutral.png')}}"
|
||||
alt="Tetaneutral"
|
||||
title="Tetaneutral"
|
||||
class="logo_partner">
|
||||
</a>
|
||||
</div>
|
||||
<div class="subpartners">
|
||||
<a href="https://clutchmag.fr/" target="_new">
|
||||
<img src="{{ url_for('static', filename='images/clutch.png')}}"
|
||||
alt="Clutch"
|
||||
title="Clutch"
|
||||
class="logo_partner">
|
||||
</a>
|
||||
<a href="https://www.antistatik.store/" target="_new">
|
||||
<img src="{{ url_for('static', filename='images/antistatik.png')}}"
|
||||
alt="Antistatik"
|
||||
title="Antistatik"
|
||||
class="logo_partner">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% block navbar %}
|
||||
{% include "navbar.html" %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
33
src/thsf/templates/goodies.html
Normal file
@@ -0,0 +1,33 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="logo_wrapper">
|
||||
<img class="logo"
|
||||
src="{{ url_for('static', filename='images/logo.svg') }}"
|
||||
alt="THSF 2023 - S/Extraire"
|
||||
title="THSF 2023 - S/Extraire">
|
||||
</div>
|
||||
<div class="content" class="goodies">
|
||||
<h2>Nous avons besoin de votre soutien</h2>
|
||||
<p class="goodies">
|
||||
<img class="left"
|
||||
src="{{ url_for('static', filename='images/stickers.webp') }}"
|
||||
alt="Stickers"
|
||||
title="Stickers">
|
||||
Pour faire du <strong>Toulouse Hacker Space Factory</strong> un événement toujours différent des autres festivals <strong>votre soutien financier</strong> est nécessaire.
|
||||
</p>
|
||||
<p class="goodies">
|
||||
<img class="right"
|
||||
src="{{ url_for('static', filename='images/tshirt.webp') }}"
|
||||
alt="T-Shirt"
|
||||
title="T-Shirt">
|
||||
Pour faciliter la collecte des dons et vous remercier de votre participation, nous avons mis en place <a href="https://www.leetchi.com/c/thsf23" target="_new">une cagnote Leetchi</a> qui nous permettra de recenser les dons et vous permettra de suivre l'évolution du financement de notre festival.
|
||||
</p>
|
||||
<p class="goodies">
|
||||
<img class="left"
|
||||
src="{{ url_for('static', filename='images/sweatshirt.webp') }}"
|
||||
alt="Sweat shirt"
|
||||
title="Sweat shirt">
|
||||
Nous sommes également conscients que la situation actuelle est particulièrement difficile. C'est pourquoi nous nous engageons à ce que <span class="important">toutes les sommes qui dépasseront notre objectif de financement de 2 000 € seront reversées aux caisses des grévistes de la réforme des retraites</span>. Ainsi, votre contribution permettra également de soutenir une cause importante et de <span class="important">faire une différence dans la vie de ceux qui se battent pour nos droits</span>.
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
27
src/thsf/templates/index.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="logo_wrapper">
|
||||
<img class="logo"
|
||||
src="{{ url_for('static', filename='images/logo.svg') }}"
|
||||
alt="THSF 2023 - S/Extraire"
|
||||
title="THSF 2023 - S/Extraire"
|
||||
onclick="document.getElementById('wemakeporn').style.visibility='visible';">
|
||||
<div id="wemakeporn" class="wemakeporn"
|
||||
onclick="document.getElementById('wemakeporn').style.visibility='hidden';">
|
||||
<div class="wemake">WE MAKE</div>
|
||||
<div class="porn">PORN</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2>Le THSF est enfin de retour !</h2>
|
||||
<p>Nous vous invitons à passer un week-end de 3 jours à <a href="https://www.cinemas-utopia.org/toulouse/" target="_new">Utopia Borderouge Toulouse</a> pour partager avec vous nos projets, nos réflexions, nos performances, nos poésies et nos doutes sur la technologie.</p>
|
||||
|
||||
<p>Après avoir été soutenu et accueilli pendant 10 ans par <a href="https://vive.mixart-myrys.org/" target="_new">Mix'Art Myrys</a>, le <strong>Toulouse HackerSpace Factory</strong> se tient désormais dans un autre lieu où l'utopie nécessaire est inscrite programme.</p>
|
||||
|
||||
<p>Cette année nous mettons en avant des réflexions sur <a href="https://fr.wikipedia.org/wiki/Extractivisme" target="_new">l'extractivisme des ressources</a> planétaires, des données, de la valeur travail.</p>
|
||||
|
||||
<p>Comme toujours, notre objectif est de créer un festival qui poétise les bifurcations de nos idées communes et qui réinvente le sens de certains schémas imposés par notre époque. Rejoignez-nous pour une expérience enrichissante et pleine de surprises !</p>
|
||||
|
||||
<p>Consulter <a href="/planning">le programme du THSF</a>.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
8
src/thsf/templates/navbar.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<div class="navbar_wrapper">
|
||||
{% for item in navbar %}
|
||||
<i class="button tooltip black {{ item.classes }}"
|
||||
onclick="document.location='{{item.url}}'">
|
||||
<span class="tooltiptext thin">{{item.name}}</span>
|
||||
</i>
|
||||
{% endfor %}
|
||||
</div>
|
||||
15
src/thsf/templates/planning.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
{% for slot in slots %}
|
||||
{% set loop_index = loop.index %}
|
||||
{% if filter %}
|
||||
{% if slot.submission_type.en | lower in filter %}
|
||||
{% include "slot.html" %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% include "slot.html" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
64
src/thsf/templates/slot.html
Normal file
@@ -0,0 +1,64 @@
|
||||
<div class="slot">
|
||||
<div class="slot_header">
|
||||
<div class="slot_title {{ slot.slot.start | date2dayclass}}"
|
||||
onclick="switch_visibility(document.getElementById('{{slot.code}}_{{loop_index}}'))">
|
||||
<i class="black {{slot.submission_type.fr | toicon }}"></i>
|
||||
<span class="title">{{slot.title}}</span>
|
||||
</div>
|
||||
<div class="slot_info">
|
||||
<div class="start">
|
||||
<i class="fa-solid fa-caret-right"></i>
|
||||
{{slot.slot.start | date2dmyhm}} - {{slot.duration}} minutes ({{slot.content_locale | capitalize}})
|
||||
</div>
|
||||
<div class="room">
|
||||
<i class="fa-solid fa-caret-right"></i>
|
||||
{{slot.slot.room.fr}}
|
||||
</div>
|
||||
<div class="speakers">
|
||||
{% for speaker in slot.speakers %}
|
||||
<div class="speaker">
|
||||
<div class="name" onclick="switch_visibility(document.getElementById('{{speaker.code}}_{{loop_index}}'))">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
<span>{{speaker.name | title}}</span>
|
||||
</div>
|
||||
{% if speaker.avatar or speaker.biography %}
|
||||
<div id="{{speaker.code}}_{{loop_index}}" class="speaker_details">
|
||||
{% if speaker.avatar %}
|
||||
<div class="speaker_avatar">
|
||||
<img class="speaker_img"
|
||||
src="{{speaker.avatar}}"
|
||||
alt="{{speaker.name | title}}"
|
||||
title="{{speaker.name | title}}">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if speaker.biography %}
|
||||
<div class="speaker_biography">
|
||||
<div class="speaker_biography">
|
||||
{{ speaker.biography }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="{{slot.code}}_{{loop_index}}" class="slot_details">
|
||||
<div class="abstract">
|
||||
{% if slot.image %}
|
||||
<img class="slot_img"
|
||||
src="{{slot.image}}"
|
||||
alt="{{slot.name}}"
|
||||
title="{{slot.name}}">
|
||||
{% endif %}
|
||||
{% if slot.abstract %}
|
||||
<p>{{slot.abstract}}</p>
|
||||
{% endif %}
|
||||
{% if slot.description %}
|
||||
<p>{{slot.description}}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
5
src/thsf_wsgi.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from thsf import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
application = app
|
||||
application.run(host="127.0.0.1", port=8043)
|
||||