Merge branch 'france-rsvp-styled' into 'main'
Styled the website & added info for guests See merge request VickyRampin/vicky-remi-wedding-website!1
This commit is contained in:
commit
fd18c064d9
|
@ -0,0 +1,35 @@
|
|||
.env
|
||||
|
||||
# python
|
||||
__pycache__
|
||||
*.py[co]
|
||||
.ipynb_checkpoints
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
|
||||
# Eclipse PyDev
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# ViM
|
||||
.*.swp
|
||||
|
||||
# Emacs
|
||||
\#*#
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
desktop.ini
|
||||
|
||||
# Archives
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
*.zip
|
||||
*.whl
|
|
@ -0,0 +1,69 @@
|
|||
import logging
|
||||
import random
|
||||
import sys
|
||||
|
||||
from .database import get_db
|
||||
|
||||
|
||||
ALPHABET = '0123456789bcdefghjkmnpqrstuvwxyz'
|
||||
assert len(ALPHABET) == 32
|
||||
|
||||
LENGTH = 4
|
||||
|
||||
|
||||
CORRECT = {
|
||||
'i': '1',
|
||||
'l': '1',
|
||||
'o': '0',
|
||||
}
|
||||
|
||||
|
||||
def correct_code(code):
|
||||
code = code.lower()
|
||||
fixed_code = ''.join(CORRECT.get(c, c) for c in code)
|
||||
return fixed_code
|
||||
|
||||
|
||||
def list_errors(code):
|
||||
for place in range(LENGTH):
|
||||
for replacement in ALPHABET:
|
||||
if replacement == code[place]:
|
||||
continue
|
||||
new_code = code[:place] + replacement + code[place + 1:]
|
||||
yield new_code
|
||||
|
||||
|
||||
def main():
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
if len(sys.argv) <= 1:
|
||||
number = 1
|
||||
elif len(sys.argv) == 2:
|
||||
number = int(sys.argv[1], 10)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
# Get the current codes
|
||||
with get_db() as db:
|
||||
correct_codes = set(row[0] for row in db.execute(
|
||||
'''\
|
||||
SELECT code FROM families;
|
||||
''',
|
||||
))
|
||||
|
||||
# Augment them with possible errored codes
|
||||
errored_codes = set()
|
||||
for code in correct_codes:
|
||||
errored_codes.update(list_errors(code))
|
||||
assert len(errored_codes) == LENGTH * (len(ALPHABET) - 1) * len(correct_codes)
|
||||
|
||||
# Generate new codes
|
||||
generated = 0
|
||||
while generated < number:
|
||||
code = ''.join(random.choice(ALPHABET) for _ in range(LENGTH))
|
||||
if code in correct_codes or code in errored_codes:
|
||||
continue
|
||||
correct_codes.add(code)
|
||||
errored_codes.update(list_errors(code))
|
||||
print(code)
|
||||
generated += 1
|
|
@ -0,0 +1,77 @@
|
|||
import contextlib
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
import sqlite3
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DATABASE_FILE = os.environ['DATABASE']
|
||||
|
||||
assert os.path.isfile(DATABASE_FILE)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def get_db():
|
||||
db = sqlite3.connect(DATABASE_FILE)
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_name_and_replies(code):
|
||||
with get_db() as db:
|
||||
rows = db.execute(
|
||||
'''\
|
||||
WITH latest_reply AS (
|
||||
SELECT
|
||||
adults, children
|
||||
FROM replies
|
||||
WHERE family_code = :code
|
||||
ORDER BY date DESC
|
||||
LIMIT 1
|
||||
)
|
||||
SELECT
|
||||
families.name,
|
||||
(SELECT adults FROM latest_reply) AS adults,
|
||||
(SELECT children FROM latest_reply) AS children
|
||||
FROM families
|
||||
WHERE families.code = :code
|
||||
LIMIT 1;
|
||||
''',
|
||||
dict(code=code),
|
||||
)
|
||||
try:
|
||||
return next(rows)
|
||||
except StopIteration:
|
||||
raise ValueError("Invalid code")
|
||||
|
||||
|
||||
def record_reply(code, adults, children):
|
||||
assert isinstance(adults, int)
|
||||
assert isinstance(children, int)
|
||||
assert 0 <= adults and 0 <= children
|
||||
assert adults > 0 or children == 0
|
||||
|
||||
with get_db() as db:
|
||||
# Insert update
|
||||
cursor = db.cursor()
|
||||
date = datetime.utcnow()
|
||||
cursor.execute(
|
||||
'''\
|
||||
INSERT INTO replies(family_code, date, adults, children)
|
||||
VALUES(:code, :date, :adults, :children);
|
||||
''',
|
||||
dict(code=code, date=date, adults=adults, children=children),
|
||||
)
|
||||
cursor.execute('COMMIT;')
|
||||
logger.info(
|
||||
"Recorded update for code=%r date=%r adults=%d children=%d",
|
||||
code,
|
||||
date,
|
||||
adults,
|
||||
children,
|
||||
)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,43 @@
|
|||
/* timeline from https://mdbootstrap.com/docs/standard/extended/timeline/ */
|
||||
|
||||
.timeline-with-icons {
|
||||
border-left: 3px solid hsl(0, 0%, 90%);
|
||||
position: relative;
|
||||
list-style: none;
|
||||
margin-top:1.3rem;
|
||||
}
|
||||
|
||||
.timeline-with-icons .timeline-item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-with-icons .timeline-item:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.timeline-with-icons .timeline-icon {
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
background-color: hsl(217, 88.2%, 90%);
|
||||
color: hsl(217, 88.8%, 35.1%);
|
||||
border-radius: 60%;
|
||||
height: 38px;
|
||||
width: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size: 1.5rem;
|
||||
background-image: url("../imgs/background.png");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
body > nav > div {
|
||||
font-size:1.5rem;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 167 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,36 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}RSVP -{% endblock %}
|
||||
|
||||
{% block navitems %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('index') }}">Info</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="{{ url_for('form') }}">RSVP <span class="visually-hidden">(actuel)</span></a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if error %}
|
||||
<p class="error text-danger">{{ error }}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="{{ url_for('form') }}" method="POST">
|
||||
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-auto">
|
||||
<label for="code" class="col-form-label text-dark">Code sur l'invitation :</label>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<input type="text" name="code" id="code" class="form-control" placeholder="ABCD">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" value="RSVP" class="btn btn-primary btn-lg mt-3">RSVP</button>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,93 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block navitems %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">Info <span class="visually-hidden">(actuel)</span></a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('form') }}">RSVP</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Rémi & Vicky renouvellent leurs voeux de mariage ! </h1>
|
||||
|
||||
<p class="text-dark">Rémi & Vicky se sont mariés lors d'une petite cérémonie au Massachusetts le 30 janvier 2021. Exactement un an et demi plus tard, nous pouvons enfin venir en France pour célébrer avec tout le monde et renouveler nos voeux ! Nous espérons vous y voir.</p>
|
||||
|
||||
<a class="btn btn-primary btn-lg mb-3" href="{{ url_for('form') }}" role="button">RSVP <i class="bi bi-heart-arrow"></i></a>
|
||||
|
||||
<h4><i class="bi bi-geo-alt"></i> La Chapelle-Taillefert</h4>
|
||||
<h4><i class="bi bi-calendar-heart"></i> 30 juillet 2022</h4>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col offset-sm-1 align-self-end">
|
||||
<ul class="timeline-with-icons">
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-house-heart"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Cérémonie en mairie</h4>
|
||||
<p class="mb-2 fw-bold">11h</p>
|
||||
<p class="text-muted mb-2 fw-bold">La Mairie de La Chapelle-Taillefert</p>
|
||||
</li>
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-person-hearts"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Vin d'honneur</h4>
|
||||
<p class="mb-2 fw-bold">11h30</p>
|
||||
</li>
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-egg-fried"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Repas</h4>
|
||||
<p class="mb-2 fw-bold">12h30</p>
|
||||
<p class="text-muted mb-2 fw-bold">Salle des fêtes</p>
|
||||
</li>
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-dribbble"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Décontraction et pétanque</h4>
|
||||
<p class="mb-2 fw-bold">16h00</p>
|
||||
<p class="text-muted mb-2 fw-bold">Apportez vos boules de pétanques !</p>
|
||||
</li>
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-balloon-heart"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Apéro et BBQ</h4>
|
||||
<p class="mb-2 fw-bold">19h30</p>
|
||||
<p class="text-muted mb-2 fw-bold">Salle des fêtes</p>
|
||||
</li>
|
||||
|
||||
<li class="timeline-item mb-5">
|
||||
<span class="timeline-icon">
|
||||
<i class="bi bi-speaker"></i>
|
||||
</span>
|
||||
|
||||
<h4 class="fw-bold">Karaoké, musique et danse</h4>
|
||||
<p class="mb-2 fw-bold">21h30</p>
|
||||
<p class="text-muted mb-2 fw-bold">Salle des fêtes</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,38 @@
|
|||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
{% block head %}
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='/css/bootstrap.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='/css/bootstrap-icons.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='/css/custom.css') }}">
|
||||
<title>{% block title %}{% endblock %} Rémi & Vicky</title>
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<span class="navbar-text pe-3">Rémi & Vicky</span>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse" id="navbarText">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
{% block navitems %}{% endblock %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="content" class="container mt-2">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<div id="footer" class="mt-3">
|
||||
<div class="text-center container mb-2 text-dark">En cas de problème, e-mailez-moi à <a href="mailto:remi@rampin.org">remi@rampin.org</a></div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='/js/bootstrap.bundle.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}RSVP -{% endblock %}
|
||||
|
||||
{% block navitems %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('index') }}">Info</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if error %}
|
||||
<p class="error">{{ error }}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="POST">
|
||||
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-auto">
|
||||
<label for="adults" class="col-form-label text-dark">Adultes :</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input
|
||||
type="number" class="form-control"
|
||||
name="adults" id="adults"
|
||||
min="1" max="5"
|
||||
{% if adults is not none %}
|
||||
value="{{ adults }}"
|
||||
{% else %}
|
||||
value="1"
|
||||
{% endif %}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 align-items-center">
|
||||
<div class="col-auto">
|
||||
<label for="children" class="col-form-label text-dark">Enfants :</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input
|
||||
type="number" class="form-control"
|
||||
name="children" id="children"
|
||||
min="0" max="5"
|
||||
{% if children is not none %}
|
||||
value="{{ children }}"
|
||||
{% else %}
|
||||
value="0"
|
||||
{% endif %}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" value="RSVP" class="btn btn-lg btn-primary mt-3">RSVP</button>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,15 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}MERCI -{% endblock %}
|
||||
{% block navitems %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('index') }}">Info</a>
|
||||
</li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Merci !</h1>
|
||||
|
||||
<p class="text-dark">Nous avons hâte de vous voir. Pour le programme et les adresses, voir <a href="{{ url_for('index') }}">la page d'info</a>.</p>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,70 @@
|
|||
from flask import Flask, render_template, redirect, request, url_for
|
||||
import logging
|
||||
|
||||
from .codes import correct_code
|
||||
from .database import get_name_and_replies, record_reply
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = Flask('france_rsvp')
|
||||
|
||||
|
||||
@app.route('/france/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/france/rsvp', methods=['GET', 'POST'])
|
||||
def form():
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
if (
|
||||
'code' in request.form
|
||||
and len(request.form['code']) == 4
|
||||
):
|
||||
code = correct_code(request.form['code'])
|
||||
try:
|
||||
get_name_and_replies(code)
|
||||
except ValueError:
|
||||
error = "Code invalide"
|
||||
else:
|
||||
return redirect(url_for('rsvp', code=code))
|
||||
else:
|
||||
error = "Code invalide"
|
||||
return render_template('form.html', error=error)
|
||||
|
||||
|
||||
@app.route('/france/thanks')
|
||||
def thanks():
|
||||
return render_template('thanks.html')
|
||||
|
||||
|
||||
@app.route('/france/<code>', methods=['GET', 'POST'])
|
||||
def rsvp(code):
|
||||
# Lookup guests from code
|
||||
adults = None
|
||||
children = None
|
||||
try:
|
||||
name, adults, children = get_name_and_replies(code)
|
||||
except ValueError:
|
||||
return render_template('form.html', error="Code invalide")
|
||||
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
adults = int(request.form['adults'], 10)
|
||||
children = int(request.form['children'], 10)
|
||||
except (KeyError, ValueError, OverflowError):
|
||||
logger.warning("Invalid reply", exc_info=True)
|
||||
error = "Nombres invalides"
|
||||
else:
|
||||
record_reply(code, adults, children)
|
||||
return redirect(url_for('thanks'))
|
||||
|
||||
return render_template(
|
||||
'rsvp.html',
|
||||
adults=adults,
|
||||
children=children,
|
||||
error=error,
|
||||
)
|
|
@ -0,0 +1,201 @@
|
|||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "flask"
|
||||
version = "2.1.2"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0"
|
||||
importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
|
||||
itsdangerous = ">=2.0"
|
||||
Jinja2 = ">=3.0"
|
||||
Werkzeug = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
async = ["asgiref (>=3.2)"]
|
||||
dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "4.11.3"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "itsdangerous"
|
||||
version = "2.1.2"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "uwsgi"
|
||||
version = "2.0.20"
|
||||
description = "The uWSGI server"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "2.1.2"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
watchdog = ["watchdog"]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.8.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
|
||||
|
||||
[extras]
|
||||
uwsgi = ["uWSGI"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "22dc31ed2f24fc96f3cd412113fe05f63b85c9bbc73c92b74c8d4d915d3cd092"
|
||||
|
||||
[metadata.files]
|
||||
click = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
flask = [
|
||||
{file = "Flask-2.1.2-py3-none-any.whl", hash = "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"},
|
||||
{file = "Flask-2.1.2.tar.gz", hash = "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"},
|
||||
{file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"},
|
||||
]
|
||||
itsdangerous = [
|
||||
{file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
|
||||
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
|
||||
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
|
||||
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
|
||||
]
|
||||
uwsgi = [
|
||||
{file = "uwsgi-2.0.20.tar.gz", hash = "sha256:88ab9867d8973d8ae84719cf233b7dafc54326fcaec89683c3f9f77c002cdff9"},
|
||||
]
|
||||
werkzeug = [
|
||||
{file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"},
|
||||
{file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
|
||||
{file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
[tool.poetry]
|
||||
name = "france-rsvp"
|
||||
version = "1.0.0"
|
||||
description = ""
|
||||
authors = ["Remi Rampin <remi@rampin.org>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
Flask = "^2.1.2"
|
||||
|
||||
uWSGI = {version = "*", optional = true}
|
||||
|
||||
[tool.poetry.extras]
|
||||
uwsgi = ["uWSGI"]
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
[tool.poetry.scripts]
|
||||
generate-codes = "france_rsvp.codes:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
|
@ -0,0 +1,12 @@
|
|||
CREATE TABLE families(
|
||||
code VARCHAR(8) PRIMARY KEY,
|
||||
name TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE replies(
|
||||
family_code VARCHAR(8) NOT NULL,
|
||||
date DATETIME NOT NULL,
|
||||
adults INTEGER NOT NULL,
|
||||
children INTEGER NOT NULL,
|
||||
FOREIGN KEY(family_code) REFERENCES families(code)
|
||||
);
|
|
@ -0,0 +1,9 @@
|
|||
INSERT INTO families(code, name) VALUES
|
||||
('bbbb', 'one'),
|
||||
('cccc', 'two'),
|
||||
('dddd', 'three');
|
||||
|
||||
INSERT INTO replies(family_code, date, adults, children) VALUES
|
||||
('bbbb', '2022-05-10 20:37:39', 2, 0),
|
||||
('bbbb', '2022-05-10 23:55:55', 0, 0),
|
||||
('cccc', '2022-05-10 20:22:11', 2, 1);
|
Loading…
Reference in New Issue