Compare commits

...

2 commits

Author SHA1 Message Date
sinavir
02d07cb83d
fix(trombonoscope): Breaking changes in django-avatar 2024-12-19 16:22:20 +01:00
sinavir
fbb7cbe11a
feat: update and switch to dgnum django framework 2024-12-19 16:22:16 +01:00
21 changed files with 225 additions and 127 deletions

1
.credentials/SECRET_KEY Normal file
View file

@ -0,0 +1 @@
insecure-secret_key

View file

@ -1,39 +1,26 @@
import os from pathlib import Path
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from loadcredential import Credentials
try: credentials = Credentials(env_prefix="ERNESTOPHONE_")
from . import secret
except ImportError: BASE_DIR = Path(__file__).resolve().parent.parent
raise ImportError(
"The secret.py file is missing.\n" DEBUG = credentials.get_json("DEBUG", False)
"For a development environment, simply copy secret_example.py"
) ALLOWED_HOSTS = credentials.get_json("ALLOWED_HOSTS", [])
SECRET_KEY = credentials["SECRET_KEY"]
ADMINS = credentials.get_json("ADMINS", [])
SERVER_EMAIL = credentials.get("SERVER_EMAIL", "ernesto@localhost")
def import_secret(name): ACCOUNT_CREATION_PASS = credentials.get("ACCOUNT_CREATION_PASS", "dummy")
"""
Shorthand for importing a value from the secret module and raising an
informative exception if a secret is missing.
"""
try:
return getattr(secret, name)
except AttributeError:
raise RuntimeError("Secret missing: {}".format(name))
SECRET_KEY = import_secret("SECRET_KEY")
ADMINS = import_secret("ADMINS")
SERVER_EMAIL = import_secret("SERVER_EMAIL")
DBNAME = import_secret("DBNAME")
DBUSER = import_secret("DBUSER")
DBPASSWD = import_secret("DBPASSWD")
ACCOUNT_CREATION_PASS = import_secret("ACCOUNT_CREATION_PASS")
BASE_DIR = os.path.join(os.path.dirname(__file__), "..", "..")
INSTALLED_APPS = [ INSTALLED_APPS = [
"propositions", "propositions",
"trombonoscope", "trombonoscope",
@ -90,15 +77,22 @@ TEMPLATES = [
WSGI_APPLICATION = "Ernestophone.wsgi.application" WSGI_APPLICATION = "Ernestophone.wsgi.application"
DATABASES = {
"default": { ###
"ENGINE": "django.db.backends.postgresql", # Database configuration
"NAME": DBNAME, # -> https://docs.djangoproject.com/en/4.2/ref/settings/#databases
"USER": DBUSER,
"PASSWORD": DBPASSWD, DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
"HOST": "localhost",
} DATABASES = credentials.get_json(
} "DATABASES",
{
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
},
)
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
@ -129,7 +123,7 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),) LOCALE_PATHS = (BASE_DIR / "locale",)
AUTH_PROFILE_MODEL = "gestion.ErnestoUser" AUTH_PROFILE_MODEL = "gestion.ErnestoUser"
@ -150,3 +144,18 @@ AVATAR_PROVIDERS = (
"avatar.providers.DefaultAvatarProvider", "avatar.providers.DefaultAvatarProvider",
) )
AVATAR_THUMB_FORMAT = "JPEG" AVATAR_THUMB_FORMAT = "JPEG"
###
# Staticfiles configuration
STATIC_ROOT = credentials["STATIC_ROOT"]
STATIC_URL = "/static/"
MEDIA_ROOT = credentials.get("MEDIA_ROOT", BASE_DIR / "media")
MEDIA_URL = "/media/"
if DEBUG:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE

View file

@ -1,23 +0,0 @@
import os
from .common import * # noqa
from .common import BASE_DIR, INSTALLED_APPS, MIDDLEWARE
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DEBUG = True
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware"] + MIDDLEWARE
STATIC_URL = "/static/"
STATIC_ROOT = "static"
MEDIA_URL = "/media/"
MEDIA_ROOT = "media"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}

View file

@ -1,17 +0,0 @@
import os
from .common import * # noqa
from .common import BASE_DIR
DEBUG = False
ALLOWED_HOSTS = [
"ernestophone.ens.fr",
"www.ernestophone.ens.fr",
]
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "..", "..", "public", "ernesto", "static")
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "..", "media")

View file

@ -1,9 +0,0 @@
SECRET_KEY = "vpb%-1@$ha98w5^ji#@9v2_kxj)zdk5+e!9!fqniu2$#eg+46="
ADMINS = None
SERVER_EMAIL = "ernesto@localhost"
DBNAME = "ernesto"
DBUSER = "ernesto"
DBPASSWD = "YNp2rrXowJnDAFF3"
ACCOUNT_CREATION_PASS = "dummy"

View file

@ -13,13 +13,13 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.conf import settings from django.conf import settings
from django.conf.urls.i18n import i18n_patterns from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.urls import include, path from django.urls import include, path
from gestion import views as gestion_views from gestion import views as gestion_views
urlpatterns = [] urlpatterns = []
@ -69,4 +69,5 @@ urlpatterns += i18n_patterns(
), ),
prefix_default_language=False, prefix_default_language=False,
) )
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -1,5 +1,5 @@
{% extends "gestion/base.html" %} {% extends "gestion/base.html" %}
{% load staticfiles %} {% load static %}
{% load frenchmonth %} {% load frenchmonth %}
{% load i18n %} {% load i18n %}
{% load translate %} {% load translate %}
@ -43,35 +43,35 @@
<tr> <tr>
<td width="20%" align="left"> <td width="20%" align="left">
&lt;&lt; &lt;&lt;
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
<a href="{% url "calendrier:view-month" PreviousYear PreviousMonth %}">{{PreviousMonthName|frenchmonth}} {{PreviousYear}}</a> <a href="{% url "calendrier:view-month" PreviousYear PreviousMonth %}">{{PreviousMonthName|frenchmonth}} {{PreviousYear}}</a>
{% else %} {% else %}
<a href="{% url "calendrier:view-month" PreviousYear PreviousMonth %}">{{PreviousMonthName}} {{PreviousYear}}</a> <a href="{% url "calendrier:view-month" PreviousYear PreviousMonth %}">{{PreviousMonthName}} {{PreviousYear}}</a>
{% endifequal %} {% endif %}
</td> </td>
<td width="20%" align="center"><a href="{% url "calendrier:home" %}">{% trans "Aujourd'hui" %}</a></td> <td width="20%" align="center"><a href="{% url "calendrier:home" %}">{% trans "Aujourd'hui" %}</a></td>
<td width="20%" align="right"> <td width="20%" align="right">
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
<a href="{% url "calendrier:view-month" NextYear NextMonth %}">{{NextMonthName|frenchmonth}} {{NextYear}}</a> &gt;&gt; <a href="{% url "calendrier:view-month" NextYear NextMonth %}">{{NextMonthName|frenchmonth}} {{NextYear}}</a> &gt;&gt;
{% else %} {% else %}
<a href="{% url "calendrier:view-month" NextYear NextMonth %}">{{NextMonthName}} {{NextYear}}</a> &gt;&gt; <a href="{% url "calendrier:view-month" NextYear NextMonth %}">{{NextMonthName}} {{NextYear}}</a> &gt;&gt;
{% endifequal %} {% endif %}
</td> </td>
</tr> </tr>
</table> </table>
<div id="calendar"> <div id="calendar">
{% if user.profile.is_chef_event or user.profile.is_chef %} {% if user.profile.is_chef_event or user.profile.is_chef %}
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
{{Calendar_chef|translate}} {{Calendar_chef|translate}}
{% else %} {% else %}
{{Calendar_chef}} {{Calendar_chef}}
{% endifequal %} {% endif %}
{% else %} {% else %}
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
{{Calendar|translate}} {{Calendar|translate}}
{% else %} {% else %}
{{Calendar}} {{Calendar}}
{% endifequal %} {% endif %}
{% endif%} {% endif%}
</div> </div>
{% if user.profile.is_chef_event or user.profile.is_chef %} {% if user.profile.is_chef_event or user.profile.is_chef %}

View file

@ -261,11 +261,11 @@ singleEvent.createWidget('#single-normal', function() {
console.log('#single-normal widget has been created'); console.log('#single-normal widget has been created');
}); });
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
singleEvent.setOption({ lang: 'fr' }); singleEvent.setOption({ lang: 'fr' });
{% else %} {% else %}
singleEvent.setOption({ lang: 'en' }); singleEvent.setOption({ lang: 'en' });
{% endifequal %} {% endif %}
</script> </script>
{% if event.id == 573 %} {% if event.id == 573 %}
<script src="https://unpkg.com/fireworks-js@2.x/dist/index.umd.js"></script> <script src="https://unpkg.com/fireworks-js@2.x/dist/index.umd.js"></script>

View file

@ -124,11 +124,11 @@
{% endif %} {% endif %}
<li class="nav-item"> <li class="nav-item">
{% ifequal current_language "fr" %} {% if current_language == "fr" %}
<a class="nav-link" href="{% changelang "en" %}" ><img src="{% static 'images\en_flag.jpg' %}" width="60" height="40" style="vertical-align: middle"></a> <a class="nav-link" href="{% changelang "en" %}" ><img src="{% static 'images\en_flag.jpg' %}" width="60" height="40" style="vertical-align: middle"></a>
{% else %} {% else %}
<a class="nav-link" href="{% changelang "fr" %}" ><img src="{% static 'images\fr_flag.jpg' %}" width="60" height="40" style="vertical-align: middle"></a> <a class="nav-link" href="{% changelang "fr" %}" ><img src="{% static 'images\fr_flag.jpg' %}" width="60" height="40" style="vertical-align: middle"></a>
{% endifequal %} {% endif %}
</li> </li>
</ul> </ul>

View file

@ -15,9 +15,9 @@
<div class="7u 12u$(small)"> <div class="7u 12u$(small)">
<p>{% trans "Propriétaire : "%} {% if instru.owner %}{{instru.owner}} {% else %}-{% endif %}<br> <p>{% trans "Propriétaire : "%} {% if instru.owner %}{{instru.owner}} {% else %}-{% endif %}<br>
{% trans "Statut : "%} {{instru.statut}} <br> {% trans "Statut : "%} {{instru.statut}} <br>
{% ifequal instru.statut 'Prêté' %} {% if instru.statut == 'Prêté' %}
{% trans "Utilisateur : "%} {% if instru.user %}{{instru.user}} {% else %}-{% endif %}<br> {% trans "Utilisateur : "%} {% if instru.user %}{{instru.user}} {% else %}-{% endif %}<br>
{% endifequal %} {% endif %}
{% trans "Marque : "%} {% if instru.marque %}{{instru.marque}} {% else %}-{% endif %} <br> {% trans "Marque : "%} {% if instru.marque %}{{instru.marque}} {% else %}-{% endif %} <br>
{% trans "Modele : "%} {% if instru.model %}{{instru.model}} {% else %}-{% endif %}<br> {% trans "Modele : "%} {% if instru.model %}{{instru.model}} {% else %}-{% endif %}<br>
{% trans "Numéro de série : "%} {% if instru.serial %}{{instru.serial}}{% else %}-{% endif %} <br> {% trans "Numéro de série : "%} {% if instru.serial %}{{instru.serial}}{% else %}-{% endif %} <br>
@ -55,7 +55,7 @@
<tr> <tr>
<td> {{ rep.date }} </td> <td> {{ rep.date }} </td>
<td> {% ifequal current_language "fr" %} <td> {% if current_language == "fr" %}
{{ rep.description }} {{ rep.description }}
{% else %} {% else %}
{% if instru.description_en %} {% if instru.description_en %}
@ -63,7 +63,7 @@
{% else %} {% else %}
{{ rep.description }} {{ rep.description }}
{% endif %} {% endif %}
{% endifequal %} </td> {% endif %} </td>
<td> {{ rep.prix }} </td> <td> {{ rep.prix }} </td>
<td> {{ rep.lieux }} </td> <td> {{ rep.lieux }} </td>
{%if user.profile.is_chef or user.profile.is_chef_instru %} {%if user.profile.is_chef or user.profile.is_chef_instru %}

View file

@ -3,7 +3,7 @@ import os
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Ernestophone.settings.local") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Ernestophone.settings")
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line

80
npins/default.nix Normal file
View file

@ -0,0 +1,80 @@
# Generated by npins. Do not modify; will be overwritten regularly
let
data = builtins.fromJSON (builtins.readFile ./sources.json);
version = data.version;
mkSource =
spec:
assert spec ? type;
let
path =
if spec.type == "Git" then
mkGitSource spec
else if spec.type == "GitRelease" then
mkGitSource spec
else if spec.type == "PyPi" then
mkPyPiSource spec
else if spec.type == "Channel" then
mkChannelSource spec
else
builtins.throw "Unknown source type ${spec.type}";
in
spec // { outPath = path; };
mkGitSource =
{
repository,
revision,
url ? null,
hash,
branch ? null,
...
}:
assert repository ? type;
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
# In the latter case, there we will always be an url to the tarball
if url != null then
(builtins.fetchTarball {
inherit url;
sha256 = hash; # FIXME: check nix version & use SRI hashes
})
else
assert repository.type == "Git";
let
urlToName =
url: rev:
let
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
short = builtins.substring 0 7 rev;
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
in
"${if matched == null then "source" else builtins.head matched}${appendShort}";
name = urlToName repository.url revision;
in
builtins.fetchGit {
url = repository.url;
rev = revision;
inherit name;
# hash = hash;
};
mkPyPiSource =
{ url, hash, ... }:
builtins.fetchurl {
inherit url;
sha256 = hash;
};
mkChannelSource =
{ url, hash, ... }:
builtins.fetchTarball {
inherit url;
sha256 = hash;
};
in
if version == 3 then
builtins.mapAttrs (_: mkSource) data.pins
else
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"

22
npins/sources.json Normal file
View file

@ -0,0 +1,22 @@
{
"pins": {
"nix-pkgs": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.hubrecht.ovh/hubrecht/nix-pkgs.git"
},
"branch": "main",
"revision": "cc01e1c2a6ecb1e38fde35ee54995a6a639fb057",
"url": null,
"hash": "17a9vlwrk9365ccyl7a5xspqsn9wizcpwdpvr3qdimvq4fpwhjal"
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.05pre723402.4989a246d7a3/nixexprs.tar.xz",
"hash": "0hjng6rhkjiql1dqbanjm6jl6npik29q2lmba032j897fhyzin91"
}
},
"version": 3
}

View file

@ -1,4 +0,0 @@
-r requirements.txt
django-debug-toolbar
ipython

View file

@ -1,8 +0,0 @@
Django==2.2.28
django-appconf==1.0.4
django-avatar==5.0.0
django-colorful==1.3
gunicorn==20.1.0
Pillow==9.2.0
#Pour le prod
#psycopg2==2.8.6

46
shell.nix Normal file
View file

@ -0,0 +1,46 @@
{
sources ? import ./npins,
pkgs ? import sources.nixpkgs {
overlays = [
(import "${sources.nix-pkgs}/overlay.nix").default
];
},
dev ? true,
}:
pkgs.mkShell {
packages = [
(pkgs.python3.withPackages (
ps:
[
ps.django
ps.django-avatar
ps.django-colorful
ps.gunicorn
ps.pillow
ps.loadcredential
]
++ (
if dev then
[
ps.ipython
ps.django-debug-toolbar
]
else
[ ]
)
))
];
env = {
DJANGO_SETTINGS_MODULE = "Ernestophone.settings";
CREDENTIALS_DIRECTORY = builtins.toString ./.credentials;
ERNESTOPHONE_DEBUG = builtins.toJSON true;
ERNESTOPHONE_STATIC_ROOT = builtins.toString ./.static;
};
shellHook = ''
if [ ! -d .static ]; then
mkdir .static
fi
'';
}

View file

@ -12,7 +12,7 @@
<p>{% trans "Avatar courant: " %}</p> <p>{% trans "Avatar courant: " %}</p>
{% avatar user %} {% avatar user %}
<form enctype="multipart/form-data" method="POST" action="{% url 'avatar_add' %}"> <form enctype="multipart/form-data" method="POST" action="{% url 'avatar:add' %}">
{{ upload_avatar_form.as_p }} {{ upload_avatar_form.as_p }}
<p>{% csrf_token %}<input type="submit" value="{% trans "Ajouter une nouvelle image" %}" /></p> <p>{% csrf_token %}<input type="submit" value="{% trans "Ajouter une nouvelle image" %}" /></p>
</form> </form>

View file

@ -19,7 +19,7 @@
<p>{% trans "Tu n'as pas encore ajouté d'avatar. Le logo Ernestophone sera utilisé par defaut." %}</p> <p>{% trans "Tu n'as pas encore ajouté d'avatar. Le logo Ernestophone sera utilisé par defaut." %}</p>
{% endif %} {% endif %}
<form enctype="multipart/form-data" method="POST" action="{% url 'avatar_add' %}"> <form enctype="multipart/form-data" method="POST" action="{% url 'avatar:add' %}">
{{ upload_avatar_form.as_p }} {{ upload_avatar_form.as_p }}
<p>{% csrf_token %}<input type="submit" value="{% trans "Modifier mon avatar" %}" /></p> <p>{% csrf_token %}<input type="submit" value="{% trans "Modifier mon avatar" %}" /></p>
</form> </form>
@ -27,7 +27,7 @@
<a href="{% url "trombonoscope:change" %}" class="button alt">{% trans "Retour au profil"%}</a> <a href="{% url "trombonoscope:change" %}" class="button alt">{% trans "Retour au profil"%}</a>
{% if avatars %} {% if avatars %}
&nbsp;<a class="button alt" href="{% url 'avatar_delete' %}">{% trans "Supprimer l'avatar "%}</a> &nbsp;<a class="button alt" href="{% url 'avatar:delete' %}">{% trans "Supprimer l'avatar "%}</a>
{% endif %} {% endif %}
</div></div></div></section></div> </div></div></div></section></div>
{% endblock %} {% endblock %}

View file

@ -26,9 +26,9 @@
{% endif %} {% endif %}
<p> <p>
<a class="button alt" href="{% url 'avatar_change' %}">{% trans "Changer l'avatar "%}</a> <a class="button alt" href="{% url 'avatar:change' %}">{% trans "Changer l'avatar "%}</a>
{% if request.user|has_avatar %} {% if request.user|has_avatar %}
&nbsp;<a class="button alt" href="{% url 'avatar_delete' %}">{% trans "Supprimer l'avatar "%}</a> &nbsp;<a class="button alt" href="{% url 'avatar:delete' %}">{% trans "Supprimer l'avatar "%}</a>
{% endif %} {% endif %}
</p> </p>

View file

@ -10,12 +10,12 @@
<div class="6u 12u$(small)"> <div class="6u 12u$(small)">
<h2>{% trans "Suppression de l'avatar" %} :</h2> <h2>{% trans "Suppression de l'avatar" %} :</h2>
{% if not avatars %} {% if not avatars %}
{% url 'avatar_change' as avatar_change_url %} {% url 'avatar:change' as avatar_change_url %}
<p>{% trans "Vous n'avez pas d'avatar à supprimer."%}</p> <p>{% trans "Vous n'avez pas d'avatar à supprimer."%}</p>
<a href="{{ avatar_change_url }}" class="button">{% trans "Télécharger un avatar" %}</a>&nbsp;<a href="{% url "trombonoscope:change" %}" class="button alt">{% trans "Retour au profil"%}</a> <a href="{{ avatar_change_url }}" class="button">{% trans "Télécharger un avatar" %}</a>&nbsp;<a href="{% url "trombonoscope:change" %}" class="button alt">{% trans "Retour au profil"%}</a>
{% else %} {% else %}
<p>{% trans "Séléctione l'avatar pour le supprimer" %}</p> <p>{% trans "Séléctione l'avatar pour le supprimer" %}</p>
<form method="POST" action="{% url 'avatar_delete' %}"> <form method="POST" action="{% url 'avatar:delete' %}">
{% for field in delete_avatar_form %} {% for field in delete_avatar_form %}