diff --git a/TODO b/TODO deleted file mode 100644 index bd669d2..0000000 --- a/TODO +++ /dev/null @@ -1,25 +0,0 @@ -x Ajouter un menu pour revenir à l'élément parent facilement -x Repenser l'interface pour la rendre plus simple -x Refaire une interface plus simple (moins de boutons partout, qu'on s'y retrouve plus) -x Uniformiser le nom des choses. Livraison → Distribution -x Ajouter un numéro de téléphone de la personne référente de la commande -x Il semble que les prix trop précis ne sont pas acceptés -x Utiliser une version lowercase des emails -x Permettre l'ajout de produits -x Ajouter un moyen d'ajouter un⋅e producteurice -x Ajouter un moyen de changer le nom de la personne référente pour un producteur / productrice -x Faciliter la duplication de distribution -x Si un produit est en rupture de stock, alors il n'est pas compté dans les totaux -x Permettre la suppression de producteurs -x Ajouter une info « prix mis à jour » pour les référent⋅e⋅s -x Gérer les frais de livraison -x Ajouter une note explicative pour la répartition des chèques -x Permettre la supression des produits (terminer) -x Gérer le souci d'URL pour l'édition d'Apiluly -x Rendre plus visible l'action de modifier une commande -x Ajouter la trame (agenda d’une distribution) dans la boite à outil du coordinateur -x Repasser sur les tests -x Changer les liens d'emergement, de paiements et ???, trouver de meilleurs dénominations - -Faire un refactoring des modèles -Create an "url_for" utility to simplify URL management. \ No newline at end of file diff --git a/copanier/models.py b/copanier/models.py index ededa8d..bfae37c 100644 --- a/copanier/models.py +++ b/copanier/models.py @@ -295,6 +295,7 @@ class Delivery(PersistedBase): producers: Dict[str, Producer] = field(default_factory=dict) orders: Dict[str, Order] = field(default_factory=dict) shipping: Dict[str, price_field] = field(default_factory=dict) + is_archived: bool = False def __post_init__(self): self.id = None # Not a field because we don't want to persist it. @@ -365,10 +366,6 @@ class Delivery(PersistedBase): def needs_adjustment(self): return self.has_packing and any(self.product_missing(p) for p in self.products) - @property - def is_archived(self): - return self.id and self.id.startswith("archive/") - @classmethod def init_fs(cls): cls.get_root().mkdir(parents=True, exist_ok=True) @@ -387,14 +384,16 @@ class Delivery(PersistedBase): return delivery @classmethod - def all(cls, is_archived=False): + def all(cls): root = cls.get_root() - if is_archived: - root = root / "archive" for path in root.glob("*.yml"): id_ = str(path.relative_to(cls.get_root())).replace(".yml", "") yield Delivery.load(id_) + @classmethod + def archived(cls): + return [d for d in cls.all() if not d.is_archived] + @classmethod def incoming(cls): return sorted( @@ -419,18 +418,14 @@ class Delivery(PersistedBase): def archive(self): if self.is_archived: raise ValueError("La distribution est déjà archivée") - current = self.path - self.id = f"archive/{self.id}" - current.rename(self.path) + self.is_archived = True def unarchive(self): if not self.is_archived: raise ValueError( "Impossible de désarchiver une distribution qui n'est pas archivée" ) - current = self.path - self.id = self.path.stem - current.rename(self.path) + self.is_archived = False def product_wanted(self, product): total = 0 diff --git a/copanier/templates/delivery/edit_delivery.html b/copanier/templates/delivery/edit_delivery.html index 98d28b8..c021c59 100644 --- a/copanier/templates/delivery/edit_delivery.html +++ b/copanier/templates/delivery/edit_delivery.html @@ -6,11 +6,6 @@

Modifier la distribution

{% else %} -

Il n'y a aucune distribution à venir.

+

Il n'y a aucune distribution à venir. Vous pouvez en lancer une nouvelle ! {% endif %} diff --git a/copanier/views/delivery.py b/copanier/views/delivery.py index bb65538..4bf79cc 100644 --- a/copanier/views/delivery.py +++ b/copanier/views/delivery.py @@ -23,21 +23,21 @@ async def home(request, response): "delivery/list_deliveries.html", incoming=Delivery.incoming(), former=Delivery.former(), - archives=list(Delivery.all(is_archived=True)), + archives=list(Delivery.archived()), ) @app.route("/archives", methods=["GET"]) async def list_archives(request, response): response.html( - "delivery/list_archives.html", {"deliveries": Delivery.all(is_archived=True)} + "delivery/list_archives.html", {"deliveries": Delivery.archived()} ) @app.route("/distribution/archive/{id}", methods=["GET"]) async def view_archive(request, response, id): delivery = Delivery.load(f"archive/{id}") - response.html("delivery/show.html", {"delivery": delivery}) + response.html("delivery/show_delivery.html", {"delivery": delivery}) @app.route("/distribution/{id}/archiver", methods=["GET"]) diff --git a/remote/__main__.py b/remote/__main__.py deleted file mode 100644 index e99d76c..0000000 --- a/remote/__main__.py +++ /dev/null @@ -1,213 +0,0 @@ -import os -import sys -from io import StringIO - -import minicli -from usine import chown, config, connect, env, exists, mkdir, put, run, sudo, template - - -@minicli.cli -def pip(*command): - """Run a pip command on the remote server. - """ - with sudo(user="copanier"): - run(f"/srv/copanier/venv/bin/pip {' '.join(command)}") - - -@minicli.cli -def system(): - """Setup the system.""" - with sudo(): - run("apt update") - run( - "apt install -y nginx git software-properties-common gcc " - "python3.7 python3.7-dev python3.7-venv pkg-config" - ) - mkdir("/srv/copanier/logs") - run("useradd -N copanier -d /srv/copanier/ || exit 0") - chown("copanier:users", "/srv/copanier/") - run("chsh -s /bin/bash copanier") - - -@minicli.cli -def venv(): - """Setup the python virtualenv.""" - path = "/srv/copanier/venv/" - if not exists(path): - with sudo(user="copanier"): - run(f"python3.7 -m venv {path}") - pip("install pip -U") - - -@minicli.cli -def http(): - """Configure Nginx and letsencrypt.""" - # When we'll have a domain. - put("remote/nginx-snippet.conf", "/etc/nginx/snippets/copanier.conf") - put("remote/letsencrypt.conf", "/etc/nginx/snippets/letsencrypt.conf") - put("remote/ssl.conf", "/etc/nginx/snippets/ssl.conf") - domain = config.domains[0] - pempath = f"/etc/letsencrypt/live/{domain}/fullchain.pem" - with sudo(): - if exists(pempath): - print(f"{pempath} found, using https configuration") - conf = template( - "remote/nginx-https.conf", - domains=" ".join(config.domains), - domain=domain, - ) - else: - print(f"{pempath} not found, using http configuration") - # Before letsencrypt. - conf = template( - "remote/nginx-http.conf", - domains=" ".join(config.domains), - domain=domain, - ) - put(conf, "/etc/nginx/sites-enabled/copanier.conf") - restart("nginx") - - -@minicli.cli -def letsencrypt(): - """Configure letsencrypt.""" - with sudo(): - run("add-apt-repository --yes ppa:certbot/certbot") - run("apt update") - run("apt install -y certbot") - mkdir("/var/www/letsencrypt/.well-known/acme-challenge") - domains = ",".join(list(config.domains)) - certbot_conf = template("remote/certbot.ini", domains=domains) - put(certbot_conf, "/var/www/certbot.ini") - run("certbot certonly -c /var/www/certbot.ini --non-interactive " "--agree-tos") - - -@minicli.cli -def bootstrap(): - """Bootstrap the system.""" - system() - venv() - service() - http() - - -@minicli.cli -def cli(command): - """Run the copanier executable on the remote server. - """ - with sudo(user="copanier"), env(COPANIER_DATA_ROOT="/srv/copanier/data"): - run(f"/srv/copanier/venv/bin/copanier {command}") - - -@minicli.cli -def service(): - """Deploy/update the copanier systemd service.""" - with sudo(): - put("remote/copanier.service", "/etc/systemd/system/copanier.service") - systemctl("enable copanier.service") - - -@minicli.cli -def deploy(): - """Deploy/update the copanier code base.""" - with sudo(user="copanier"): - put("remote/gunicorn.conf", "/srv/copanier/gunicorn.conf") - pip("install gunicorn") - base = "https://framagit.org/ybon/copanier" - pip(f"install -U git+{base}") - restart() - - -@minicli.cli -def restart(*services): - """Restart the systemd services.""" - services = services or ["copanier", "nginx"] - with sudo(): - systemctl(f"restart {' '.join(services)}") - - -@minicli.cli -def systemctl(*args): - """Run a systemctl command on the remote server. - - :command: the systemctl command to run. - """ - run(f'systemctl {" ".join(args)}') - - -@minicli.cli -def logs(lines=50): - """Display the copanier logs. - - :lines: number of lines to retrieve - """ - with sudo(): - run(f"journalctl --lines {lines} --unit copanier --follow") - - -@minicli.cli -def status(): - """Get the services status.""" - systemctl("status nginx copanier") - - -@minicli.cli -def access_logs(): - """See the nginx access logs.""" - with sudo(): - run("tail -F /var/log/nginx/access.log") - - -@minicli.cli -def error_logs(): - """See the nginx error logs.""" - with sudo(): - run("tail -F /var/log/nginx/error.log") - - -@minicli.cli -def data_logs(): - """See the app data logs.""" - with sudo(): - run("tail -F /srv/copanier/logs/copanier-requests.log") - - -@minicli.cli -def upload_env(): - """Upload environment vars to the server. - - Use those to deal with info not commitable. - """ - vars_ = { - "COPANIER_DATA_ROOT": "/srv/copanier/data", - "COPANIER_LOG_ROOT": "/srv/copanier/logs", - "COPANIER_SEND_EMAILS": "1", - "COPANIER_SMTP_PASSWORD": None, - "COPANIER_SMTP_LOGIN": None, - "COPANIER_SMTP_HOST": None, - "COPANIER_STAFF": None, - } - content = "" - table = str.maketrans({'"': r"\""}) - for key, value in vars_.items(): - value = value or os.environ[key] - try: - content += f'{key}="{str(value).translate(table)}"\n' - except KeyError: - sys.exit(f"The {key} environment variable does not exist.") - path = "/srv/copanier/env" - if exists(path): - run(f"cat {path}") - put(StringIO(content), path) - if exists(path): - run(f"cat {path}") - - -@minicli.wrap -def wrapper(hostname, configpath): - with connect(hostname=hostname, configpath=configpath): - yield - - -if __name__ == "__main__": - minicli.run(hostname="qa", configpath="remote/config.yml") diff --git a/remote/certbot.ini b/remote/certbot.ini deleted file mode 100644 index 08de4c8..0000000 --- a/remote/certbot.ini +++ /dev/null @@ -1,4 +0,0 @@ -authenticator = webroot -webroot-path = /var/www/letsencrypt -domains = $$domains -email = yohanboniface@free.fr diff --git a/remote/config.yml b/remote/config.yml deleted file mode 100644 index dcd9d54..0000000 --- a/remote/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -domains: -- copanier.epinamap.org -username: ubuntu -hostname: 185.145.251.38 diff --git a/remote/copanier.service b/remote/copanier.service deleted file mode 100644 index 8382514..0000000 --- a/remote/copanier.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=COPANIER -After=syslog.target network.target - -[Service] -Type=simple -User=copanier -ExecStart=/srv/copanier/venv/bin/gunicorn copanier:app --config gunicorn.conf -WorkingDirectory=/srv/copanier/ -EnvironmentFile=/srv/copanier/env -Restart=always -RestartSec=5 -StandardOutput=journal -SyslogIdentifier=COPANIER - -[Install] -WantedBy=default.target diff --git a/remote/gunicorn.conf b/remote/gunicorn.conf deleted file mode 100644 index 92e0c79..0000000 --- a/remote/gunicorn.conf +++ /dev/null @@ -1,4 +0,0 @@ -worker_class = 'roll.worker.Worker' -workers = 4 -bind = 'unix:/tmp/copanier.sock' -log_level = 'debug' diff --git a/remote/letsencrypt.conf b/remote/letsencrypt.conf deleted file mode 100644 index cf753de..0000000 --- a/remote/letsencrypt.conf +++ /dev/null @@ -1,5 +0,0 @@ -location ^~ /.well-known/acme-challenge/ { - default_type "text/plain"; - root /var/www/letsencrypt; - auth_basic off; -} diff --git a/remote/nginx-http.conf b/remote/nginx-http.conf deleted file mode 100644 index 0f5f3b1..0000000 --- a/remote/nginx-http.conf +++ /dev/null @@ -1,14 +0,0 @@ -upstream copanier { - server unix:///tmp/copanier.sock; -} - -server { - listen 80; - server_name $${domains}; - - charset utf-8; - client_max_body_size 1M; - - include /etc/nginx/snippets/letsencrypt.conf; - include /etc/nginx/snippets/copanier.conf; -} diff --git a/remote/nginx-https.conf b/remote/nginx-https.conf deleted file mode 100644 index e7bf798..0000000 --- a/remote/nginx-https.conf +++ /dev/null @@ -1,40 +0,0 @@ -upstream copanier { - server unix:///tmp/copanier.sock; -} - -server { - listen 80; - - server_name $${domains}; - include /etc/nginx/snippets/letsencrypt.conf; - - location /nginx_status { - stub_status on; - access_log off; - allow 127.0.0.1; - deny all; - } - - location / { - return 301 https://$host$request_uri; - } -} - - -server { - server_name $${domains}; - - listen 443 ssl http2; - listen [::]:443 ssl http2; - - ssl_certificate /etc/letsencrypt/live/$$domain/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/$$domain/privkey.pem; - ssl_trusted_certificate /etc/letsencrypt/live/$$domain/fullchain.pem; - include /etc/nginx/snippets/ssl.conf; - charset utf-8; - client_max_body_size 1M; - - include /etc/nginx/snippets/letsencrypt.conf; - - include /etc/nginx/snippets/copanier.conf; -} diff --git a/remote/nginx-snippet.conf b/remote/nginx-snippet.conf deleted file mode 100644 index 3a30c0f..0000000 --- a/remote/nginx-snippet.conf +++ /dev/null @@ -1,11 +0,0 @@ -root /srv/copanier; - -location /static/ { - alias /srv/copanier/venv/lib/python3.7/site-packages/copanier/static/; -} - -location / { - proxy_pass http://copanier/; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $remote_addr; -} diff --git a/remote/ssl-renew b/remote/ssl-renew deleted file mode 100644 index 6e5a544..0000000 --- a/remote/ssl-renew +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -certbot -c /srv/eurordis/certbot.ini renew - -systemctl reload nginx diff --git a/remote/ssl.conf b/remote/ssl.conf deleted file mode 100644 index bab5987..0000000 --- a/remote/ssl.conf +++ /dev/null @@ -1,15 +0,0 @@ -ssl_session_timeout 1d; -ssl_session_cache shared:SSL:50m; -ssl_session_tickets off; - -ssl_protocols TLSv1.2; -ssl_ciphers EECDH+AESGCM:EECDH+AES; -ssl_ecdh_curve secp384r1; -ssl_prefer_server_ciphers on; - -ssl_stapling on; -ssl_stapling_verify on; - -add_header Strict-Transport-Security "max-age=15768000; includeSubdomains; preload"; -add_header X-Frame-Options DENY; -add_header X-Content-Type-Options nosniff; diff --git a/tests/test_models.py b/tests/test_models.py index 60d5f9b..92493e4 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -163,26 +163,6 @@ def test_productorder_quantity(): assert choice.quantity == 2 -def test_archive_delivery(delivery): - delivery.persist() - old_id = delivery.id - old_path = delivery.path - assert str(old_path).endswith(f"delivery/{delivery.id}.yml") - assert old_path.exists() - delivery.archive() - assert delivery.is_archived - assert delivery.id.startswith("archive/") - new_path = delivery.path - assert str(new_path).endswith(f"delivery/archive/{old_id}.yml") - assert not old_path.exists() - assert new_path.exists() - delivery.unarchive() - assert not delivery.id.startswith("archive/") - assert old_path.exists() - assert not new_path.exists() - assert not delivery.is_archived - - def test_group_management(): ndp = Group( id="nid-de-poules", name="Nid de poules", members=["someone@domain.tld"]