Compare commits
36 Commits
bda40b1935
...
ssl/no_ver
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dab312a74 | ||
|
|
364c6391c9 | ||
|
|
439e637a86 | ||
|
|
d9808cc66e | ||
|
|
f139fa4bc4 | ||
|
|
1af0e1f834 | ||
|
|
b0a88b1916 | ||
|
|
cf95503954 | ||
|
|
6cdf83b6d0 | ||
|
|
21a747e097 | ||
|
|
05faf28c26 | ||
|
|
154ab9089d | ||
|
|
429e2d221e | ||
|
|
f3d697699d | ||
|
|
02645b7ef7 | ||
|
|
9112763234 | ||
|
|
7a2fdec8d4 | ||
|
|
674edf2bf2 | ||
|
|
e76d9815c9 | ||
|
|
204269b849 | ||
|
|
c969dc0e2d | ||
|
|
b183059f85 | ||
|
|
b1e05e8e4d | ||
|
|
10bea4b46b | ||
|
|
0ede5055e2 | ||
|
|
d17c6f6142 | ||
|
|
fe7b0a9e41 | ||
|
|
3b659ab076 | ||
|
|
1819826151 | ||
|
|
614c16750d | ||
|
|
a7a52e7d4c | ||
|
|
50e7693e9a | ||
|
|
5c1bc42918 | ||
|
|
a4ebd66b1d | ||
|
|
3f9888d678 | ||
|
|
e302017fe6 |
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,2 +0,0 @@
|
|||||||
{
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,8 @@ app:
|
|||||||
name: THSF
|
name: THSF
|
||||||
real_url: https://www.thsf.net
|
real_url: https://www.thsf.net
|
||||||
pretalx:
|
pretalx:
|
||||||
url: https://23.thsf.net
|
url: https://thsf.tetaneutral.net
|
||||||
|
ssl_verify: False
|
||||||
apiprefix: api
|
apiprefix: api
|
||||||
apikey: bb770a53b15467dfb67c03d178004aca9e4819d6
|
apikey: bb770a53b15467dfb67c03d178004aca9e4819d6
|
||||||
event: thsf-2023
|
event: thsf-2023
|
||||||
|
|||||||
13
maintenance/reboot.py
Executable file
13
maintenance/reboot.py
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
for container in client.containers.list():
|
||||||
|
if container.name == "thsf":
|
||||||
|
print("Content-type: text/html\n\n")
|
||||||
|
print("[+] Rebooting container<br>")
|
||||||
|
print(" '-> Stopping container<br>")
|
||||||
|
container.stop()
|
||||||
|
print(" '-> Starting container<br>")
|
||||||
|
container.start()
|
||||||
|
print("[+] Container restarted<br>")
|
||||||
2
maintenance/scripts/jquery-3.7.0.min.js
vendored
Normal file
2
maintenance/scripts/jquery-3.7.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
maintenance/scripts/maintenance.js
Normal file
7
maintenance/scripts/maintenance.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
$("#reboot").on("click", function(){
|
||||||
|
$.get("https://www.thsf.net/thsf.net/maintenance/reboot.py", function(data, status){
|
||||||
|
console.log(status);
|
||||||
|
$("#logs").html = data;
|
||||||
|
setTimeout(function () {document.location.reload()}, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
19
maintenance/status.py
Executable file
19
maintenance/status.py
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
import docker
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
base_path = "/var/www/www.thsf.net/thsf.net/maintenance/templates"
|
||||||
|
status_template = "status.html"
|
||||||
|
|
||||||
|
env = jinja2.Environment(loader=jinja2.FileSystemLoader(base_path),
|
||||||
|
autoescape=True)
|
||||||
|
template = env.get_template(status_template)
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
for container in client.containers.list():
|
||||||
|
if container.name == "thsf":
|
||||||
|
cont = dict()
|
||||||
|
cont["name"] = container.name
|
||||||
|
cont["status"] = container.status
|
||||||
|
cont["logs"] = container.logs().decode('utf-8').replace('\n', '<br>')
|
||||||
|
print(template.render(containers=[cont]))
|
||||||
66
maintenance/style/maintenance.css
Normal file
66
maintenance/style/maintenance.css
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
body {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
.page_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.center_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.container_list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
width: 48em;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 3em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0.5em;
|
||||||
|
}
|
||||||
|
.table_headers, .line {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.header, .status {
|
||||||
|
font-weight: bold;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
width: 16em;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
.table_content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.container_log {
|
||||||
|
text-align: left;
|
||||||
|
width: 49em;
|
||||||
|
padding: 0.2em;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
46
maintenance/templates/status.html
Normal file
46
maintenance/templates/status.html
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
Content-type: text/html
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang='zxx'>
|
||||||
|
<head>
|
||||||
|
<title>THSF 2023: Status page</title>
|
||||||
|
<meta name="viewport" content="initial-scale=1.0">
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="/thsf.net/maintenance/style/maintenance.css">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<script src="/thsf.net/maintenance/scripts/jquery-3.7.0.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page_wrapper">
|
||||||
|
<div class="center_wrapper">
|
||||||
|
<div class="container_list">
|
||||||
|
<div class="line">
|
||||||
|
<span class="title">THSF Status</span>
|
||||||
|
</div>
|
||||||
|
<div class="table_headers">
|
||||||
|
<div class="header">Name</div>
|
||||||
|
<div class="header">Status</div>
|
||||||
|
<div class="header">Action</div>
|
||||||
|
</div>
|
||||||
|
<div class="table_content">
|
||||||
|
{% for container in containers %}
|
||||||
|
<div class="line">
|
||||||
|
<div class="status">{{ container.name }}</div>
|
||||||
|
<div class="status">{{ container.status }}</div>
|
||||||
|
<div class="status">
|
||||||
|
<i id="reboot" class="button fa-solid fa-recycle" title="Reboot container" alt="Reboot container"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container_log" id="logs">
|
||||||
|
{{ container.logs | safe }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="/thsf.net/maintenance/scripts/maintenance.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
resources/fourmidable.jpg
Normal file
BIN
resources/fourmidable.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 325 KiB |
BIN
resources/thsf_2023.png
Normal file
BIN
resources/thsf_2023.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@@ -1,5 +1,5 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
version = 0.0.1
|
version = 1.0.2
|
||||||
classifiers =
|
classifiers =
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
License :: OSI Approved :: MIT License
|
License :: OSI Approved :: MIT License
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
from logging import config
|
from logging import config
|
||||||
import yaml
|
import yaml
|
||||||
from flask import Flask, render_template, redirect, request, url_for
|
from flask import Flask, render_template, redirect, url_for
|
||||||
from flask_minify import minify
|
from flask_minify import minify
|
||||||
from thsf.backend import Backend
|
from thsf.backend import Backend
|
||||||
from thsf.schedule import Schedule
|
from thsf.schedule import Schedule
|
||||||
@@ -37,7 +35,8 @@ try:
|
|||||||
config.dictConfig(app.local_config["log"])
|
config.dictConfig(app.local_config["log"])
|
||||||
backend = Backend(url=app.local_config["pretalx"]["url"],
|
backend = Backend(url=app.local_config["pretalx"]["url"],
|
||||||
apiprefix=app.local_config["pretalx"]["apiprefix"],
|
apiprefix=app.local_config["pretalx"]["apiprefix"],
|
||||||
apikey=app.local_config["pretalx"]["apikey"])
|
apikey=app.local_config["pretalx"]["apikey"],
|
||||||
|
ssl_verify=app.local_config["pretalx"]["ssl_verify"])
|
||||||
schedule = Schedule()
|
schedule = Schedule()
|
||||||
navbar = Navbar(config=app.local_config["navbar"])
|
navbar = Navbar(config=app.local_config["navbar"])
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@@ -47,6 +46,7 @@ except Exception as err:
|
|||||||
if app.local_config["log"]["root"]["level"] != "DEBUG":
|
if app.local_config["log"]["root"]["level"] != "DEBUG":
|
||||||
minify(app=app, html=True, js=True, cssless=True)
|
minify(app=app, html=True, js=True, cssless=True)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# -- Tools
|
# -- Tools
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -63,7 +63,7 @@ def get_speaker_biography(code):
|
|||||||
try:
|
try:
|
||||||
speaker_info = backend.get(endpoint=f"events/{app.local_config['pretalx']['event']}/speakers/{code}/").json()
|
speaker_info = backend.get(endpoint=f"events/{app.local_config['pretalx']['event']}/speakers/{code}/").json()
|
||||||
return speaker_info.get("biography").strip()
|
return speaker_info.get("biography").strip()
|
||||||
except Exception as err:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -125,9 +125,10 @@ def planning():
|
|||||||
for slot in slots.get("slots"):
|
for slot in slots.get("slots"):
|
||||||
for speaker in slot.get("speakers"):
|
for speaker in slot.get("speakers"):
|
||||||
speaker["biography"] = get_speaker_biography(speaker.get("code"))
|
speaker["biography"] = get_speaker_biography(speaker.get("code"))
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/planning"),
|
navbar=navbar.get_from_page(page="/planning"),
|
||||||
filter=["concert", "dj set", "panel discussion", "talk", "screening"])
|
filter=["concert", "dj set", "panel discussion", "talk", "screening"])
|
||||||
|
|
||||||
@@ -153,9 +154,10 @@ def goodies():
|
|||||||
@app.route('/concerts', methods=['GET'])
|
@app.route('/concerts', methods=['GET'])
|
||||||
def concerts():
|
def concerts():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/concerts"),
|
navbar=navbar.get_from_page(page="/concerts"),
|
||||||
filter=["concert", "dj set"])
|
filter=["concert", "dj set"])
|
||||||
|
|
||||||
@@ -163,9 +165,10 @@ def concerts():
|
|||||||
@app.route('/workshops', methods=['GET'])
|
@app.route('/workshops', methods=['GET'])
|
||||||
def workshops():
|
def workshops():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/workshops"),
|
navbar=navbar.get_from_page(page="/workshops"),
|
||||||
filter=["workshop"])
|
filter=["workshop"])
|
||||||
|
|
||||||
@@ -173,9 +176,10 @@ def workshops():
|
|||||||
@app.route('/screenings', methods=['GET'])
|
@app.route('/screenings', methods=['GET'])
|
||||||
def screenings():
|
def screenings():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/screenings"),
|
navbar=navbar.get_from_page(page="/screenings"),
|
||||||
filter=["screening"])
|
filter=["screening"])
|
||||||
|
|
||||||
@@ -183,9 +187,10 @@ def screenings():
|
|||||||
@app.route('/discussions', methods=['GET'])
|
@app.route('/discussions', methods=['GET'])
|
||||||
def discussions():
|
def discussions():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/discussions"),
|
navbar=navbar.get_from_page(page="/discussions"),
|
||||||
filter=["panel discussion"])
|
filter=["panel discussion"])
|
||||||
|
|
||||||
@@ -193,9 +198,10 @@ def discussions():
|
|||||||
@app.route('/exhibitions', methods=['GET'])
|
@app.route('/exhibitions', methods=['GET'])
|
||||||
def exhibitions():
|
def exhibitions():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/exhibitions"),
|
navbar=navbar.get_from_page(page="/exhibitions"),
|
||||||
filter=["installation"])
|
filter=["installation"])
|
||||||
|
|
||||||
@@ -203,9 +209,10 @@ def exhibitions():
|
|||||||
@app.route('/talks', methods=['GET'])
|
@app.route('/talks', methods=['GET'])
|
||||||
def talks():
|
def talks():
|
||||||
slots = get_slots()
|
slots = get_slots()
|
||||||
|
slots = sorted(slots.get("slots"),
|
||||||
|
key=lambda slot: slot.get("slot").get("start"))
|
||||||
return render_template("planning.html",
|
return render_template("planning.html",
|
||||||
slots=sorted(slots.get("slots"),
|
slots=slots,
|
||||||
key=lambda slot: slot.get("slot").get("start")),
|
|
||||||
navbar=navbar.get_from_page(page="/talks"),
|
navbar=navbar.get_from_page(page="/talks"),
|
||||||
filter=["talk", "light talk"])
|
filter=["talk", "light talk"])
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
import requests
|
import requests
|
||||||
import logging
|
|
||||||
|
|
||||||
class Backend:
|
class Backend:
|
||||||
def __init__(self, url, apiprefix, apikey):
|
def __init__(self, url, apiprefix, apikey, ssl_verify):
|
||||||
self.url = url
|
self.url = url
|
||||||
self.apiprefix = apiprefix
|
self.apiprefix = apiprefix
|
||||||
self.apikey = apikey
|
self.apikey = apikey
|
||||||
|
self.ssl_verify = ssl_verify
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
|
|
||||||
def get(self, endpoint, params=None):
|
def get(self, endpoint, params=None):
|
||||||
url = f"{self.url}/{self.apiprefix}/{endpoint}"
|
url = f"{self.url}/{self.apiprefix}/{endpoint}"
|
||||||
headers = {"Authorization": f"Token {self.apikey}",
|
headers = {"Authorization": f"Token {self.apikey}",
|
||||||
"Accept": "application/json"}
|
"Accept": "application/json"}
|
||||||
return self.session.get(url, params=params, headers=headers)
|
return self.session.get(url,
|
||||||
|
params=params,
|
||||||
|
headers=headers,
|
||||||
|
verify=self.ssl_verify)
|
||||||
|
|||||||
@@ -48,6 +48,12 @@
|
|||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.goodies, .food {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
.goodies_pic, .food_pic {
|
||||||
|
max-width: 7em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 44em) {
|
@media screen and (max-width: 44em) {
|
||||||
@@ -100,10 +106,10 @@
|
|||||||
color: var(--main-color);
|
color: var(--main-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.goodies {
|
.goodies, .food {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
.goodies_pic {
|
.goodies_pic, .food_pic {
|
||||||
max-width: 5em;
|
max-width: 7em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/thsf/static/images/fourmidable01.png
Normal file
BIN
src/thsf/static/images/fourmidable01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
BIN
src/thsf/static/images/fourmidable02.png
Normal file
BIN
src/thsf/static/images/fourmidable02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
BIN
src/thsf/static/images/fourmidable03.png
Normal file
BIN
src/thsf/static/images/fourmidable03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
BIN
src/thsf/static/images/fourmidable04.png
Normal file
BIN
src/thsf/static/images/fourmidable04.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 207 KiB |
@@ -7,8 +7,34 @@
|
|||||||
title="THSF 2023 - S/Extraire">
|
title="THSF 2023 - S/Extraire">
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h2>Restauration</h2>
|
<h2>Gastronomie régionale et artisanat</h2>
|
||||||
<p>La restauration sur place est prévue.</p>
|
<p class="food">
|
||||||
<p>Détails à venir...</p>
|
<img class="left food_pic"
|
||||||
|
src="{{url_for('static', filename='images/fourmidable01.png')}}"
|
||||||
|
alt="Fourmidable!"
|
||||||
|
title="Fourmidable!">
|
||||||
|
Depuis 2020, l'association toulousaine <strong>Fourmidable!</strong>, œuvre pour promouvoir la gastronomie régionale et l'artisanat local. Cette initiative collective et solidaire s'est donnée pour mission de valoriser les produits locaux, de qualité et à petits prix.
|
||||||
|
</p>
|
||||||
|
<p class="food">
|
||||||
|
<img class="right food_pic"
|
||||||
|
src="{{url_for('static', filename='images/fourmidable02.png')}}"
|
||||||
|
alt="Fourmidable!"
|
||||||
|
title="Fourmidable!">
|
||||||
|
En mai 2022, <strong>Fourmidable!</strong> inaugure une cuisine associative au sein de <a href="https://www.terresderencontres.fr/restofourmidable">l'espace Terres de Rencontres</a>. Une occasion pour les gourmands de découvrir une cuisine de qualité, élaborée avec des produits locaux et de saison.
|
||||||
|
</p>
|
||||||
|
<p class="food">
|
||||||
|
<img class="left food_pic"
|
||||||
|
src="{{url_for('static', filename='images/fourmidable03.png')}}"
|
||||||
|
alt="Fourmidable!"
|
||||||
|
title="Fourmidable!">
|
||||||
|
Au menu, des plats du jour, des flammekueche, des sandwichs, des desserts, des boissons fermentées et bien d'autres délices. Pour <strong>Fourmidable!</strong>, il est essentiel de proposer des plats savoureux et sains, tout en préservant l'environnement et le circuit court. Les aliments sont donc rigoureusement sélectionnés auprès des producteurs locaux pour une cuisine éthique et responsable.
|
||||||
|
</p>
|
||||||
|
<p class="food">
|
||||||
|
<img class="right food_pic"
|
||||||
|
src="{{url_for('static', filename='images/fourmidable04.png')}}"
|
||||||
|
alt="Fourmidable!"
|
||||||
|
title="Fourmidable!">
|
||||||
|
La cuisine associative de <strong>Fourmidable!</strong> est une invitation à découvrir la richesse de la gastronomie régionale, à travers des produits locaux et de qualité mais est également un lieu d'ateliers ouverts à tous. Les amateurs de cuisine pourront apprendre à faire du pain au levain, de la pâte à pizza, brasser de la bière, du kombucha, du kéfir, etc. <strong>Fourmidable!</strong> encourage ainsi la transmission des savoir-faire culinaires et artisanaux, dans une ambiance conviviale et solidaire.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -9,21 +9,21 @@
|
|||||||
<div class="content" class="goodies">
|
<div class="content" class="goodies">
|
||||||
<h2>Nous avons besoin de votre soutien</h2>
|
<h2>Nous avons besoin de votre soutien</h2>
|
||||||
<p class="goodies">
|
<p class="goodies">
|
||||||
<img class="left"
|
<img class="left goodies_pic"
|
||||||
src="{{ url_for('static', filename='images/stickers.webp') }}"
|
src="{{ url_for('static', filename='images/stickers.webp') }}"
|
||||||
alt="Stickers"
|
alt="Stickers"
|
||||||
title="Stickers">
|
title="Stickers">
|
||||||
Pour faire du <strong>Toulouse Hacker Space Factory</strong> un événement toujours différent des autres festivals <a href="https://www.leetchi.com/c/thsf23" target="_new">votre soutien financier</a> est nécessaire.
|
Pour faire du <strong>Toulouse Hacker Space Factory</strong> un événement toujours différent des autres festivals <a href="https://www.leetchi.com/c/thsf23" target="_new">votre soutien financier</a> est nécessaire.
|
||||||
</p>
|
</p>
|
||||||
<p class="goodies">
|
<p class="goodies">
|
||||||
<img class="right"
|
<img class="right goodies_pic"
|
||||||
src="{{ url_for('static', filename='images/tshirt.webp') }}"
|
src="{{ url_for('static', filename='images/tshirt.webp') }}"
|
||||||
alt="T-Shirt"
|
alt="T-Shirt"
|
||||||
title="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.
|
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>
|
||||||
<p class="goodies">
|
<p class="goodies">
|
||||||
<img class="left"
|
<img class="left goodies_pic"
|
||||||
src="{{ url_for('static', filename='images/sweatshirt.webp') }}"
|
src="{{ url_for('static', filename='images/sweatshirt.webp') }}"
|
||||||
alt="Sweat shirt"
|
alt="Sweat shirt"
|
||||||
title="Sweat shirt">
|
title="Sweat shirt">
|
||||||
|
|||||||
@@ -23,5 +23,7 @@
|
|||||||
<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>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>Retrouvez <a href="/planning">le programme du THSF</a> avec ses <a href="/workshops">ateliers</a>, ses <a href="/exhibitions">expositions et installations</a> mais également ses <a href="/concerts">concerts</a>, ses <a href="/projections">projections</a>, ses <a href="/talks">présentations</a> et ses <a href="/discussions">tables rondes</a>.</p>
|
<p>Retrouvez <a href="/planning">le programme du THSF</a> avec ses <a href="/workshops">ateliers</a>, ses <a href="/exhibitions">expositions et installations</a> mais également ses <a href="/concerts">concerts</a>, ses <a href="/projections">projections</a>, ses <a href="/talks">présentations</a> et ses <a href="/discussions">tables rondes</a>.</p>
|
||||||
|
<p>Accéder aux conférences, tables rondes et reportages grâce à nos amis de TVBruits: <a href="https://tvbruits.org/spip.php?mot87">https://tvbruits.org/spip.php?mot87</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<p>Accéder aux conférences, tables rondes et reportages grâce à nos amis de TVBruits: <a href="https://tvbruits.org/spip.php?mot87">https://tvbruits.org/spip.php?mot87</a>
|
||||||
|
</p>
|
||||||
{% for slot in slots %}
|
{% for slot in slots %}
|
||||||
{% set loop_index = loop.index %}
|
{% set loop_index = loop.index %}
|
||||||
{% if filter %}
|
{% if filter %}
|
||||||
|
|||||||
Reference in New Issue
Block a user