Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • mediatek/site-interludes
  • aeltheos/site-kwei
  • mediatek/site-kwei
3 results
Show changes
Showing
with 889 additions and 126 deletions
{% extends "base.html" %}
{% load static %}
{% block nav_faq %}current{% endblock %}
{% block "content" %}
<h2>Comment se rendre à l'ENS Ulm ?</h2>
<p>Plan transport + instructions</p>
<h2>Conditions sanitaires ?</h2>
<p>On ne sait pas trop...</p>
<h2>Quelle différence entre l'inscription sur HelloAsso et sur le site&nbsp;?</h2>
<p>L'inscription HelloAsso vous inscrit à l'événement, au logement et aux repas (selon l'option que vous prenez). Sur le site internet, vous pouvez créer un compte pour vous inscrire aux différentes activités qui seront proposées lors de l'événement. </p>
<h2>Quelles seront les mesures de protection sanitaire&nbsp;?</h2>
<p>Les mesures définitives vous seront communiquées à l'arrivée à l'événement. Elles incluront probablement&nbsp;:</p>
<ul>
<li>Contrôle des passes sanitaires chaque jour</li>
<li>Port du masque obligatoire en permanence</li>
<li>Lavage de main entre chaque jeu/activité conseillé</li>
<li>Des QR Codes à scanner sur chaque table (pour garder trace des cas contacts si une personne est déclarée positive)</li>
<li>Salles aérées en permanence et joueur&middot;ses invité&middot;es à se rendre dehors si le taux de CO2 devient trop élevé</li>
</ul>
<h2>Comment proposer une activité&nbsp;?</h2>
{% if settings.activity_submission_open %}
<p>Vous pouver proposer une activité en remplissant <a href="{% url 'activity_submission' %}">ce formulaire</a>.</p>
<p>Il vous faudra être connecté et renseigner, le titre, une description, le nombre de places, la durée, les horaires possibles/idéaux, et tout autre besoin particulier...</p>
{% else %}
<p>L'appel a activités est fermé pour le moment.
Contactez nous en cas de besoin urgent ou d'idée trop géniale pour être délaissée.</p>
{% endif %}
<h2>Quelles seront les conditions pour dormir/manger&nbsp;?</h2>
<p> Nous proposerons un logement en tente, pour celleux qui le souhaitent</p>
<p> Les repas seront préparés et servis sur place.</p>
<p> Si vous dormez dans les tentes, pensez à prendre un tapis de sol et duvet.</p>
<h2>Comment se rendre à l'événement&nbsp;?</h2>
<div>
<p class="centered">
<i class="fas fa-map-marker-alt"></i>
</p>
<iframe id="interactive-map" width="750" height="400"
src="https://www.openstreetmap.org/export/embed.html?bbox=4.82691%2C45.73663%2C4.84084%2C45.72936&amp;layer=hot&amp;marker=45.73298%2C4.83397"></iframe>
</div>
<div id="public-transport-info">
<div id="transport-tcl-metro">
<img src="{% static 'imgs/tcl/B.svg' %}" alt="Métro B">
</div>
<div id="transport-tcl-tram-1">
<img src="{% static 'imgs/tcl/T1.svg' %}" alt="Tram T1">
</div>
<div id="transport-tcl-tram-2">
<img src="{% static 'imgs/tcl/T6.svg' %}" alt="Tram T6">
</div>
<div id="transport-tcl-bus-1">
<img src="{% static 'imgs/tcl/34.svg' %}" alt="Bus 34">
</div>
<div id="transport-tcl-bus-2">
<img src="{% static 'imgs/tcl/60.svg' %}" alt="Bus 60">
</div>
<div id="transport-tcl-bus-3">
<img src="{% static 'imgs/tcl/64.svg' %}" alt="Bus 64">
</div>
<span id="transport-tcl-stop">Arrêt : Debourg</span>
</div>
<h2>Conditions pour dormir/manger</h2>
<p>Une réponse</p>
<h2>Comment sont réparties les activités&nbsp;?</h2>
<p>
La répartition est faite par un algorithme puis vérifiée à la main.
Dans la mesure du possible, l'algorithme essaie d'attribue au moins une activité par personne. Par conséquent, si vous ne mettez qu'une seule activité, vous avez plus de chance de l'avoir.
</p>
<p>
Les activités qui n'ont pas de limite de place (et toutes les activités avec moins de demande que de places) ne comptent pas pour ce système, donc vous pouvez les mettre et vous ne serez pas pénalisés.
</p>
<p>
Vous ne pourrez pas avoir deux activités qui se déroulent en même temps. Si l'une vous est attribué l'autre souhait sera ignoré.
</p>
<p>On n'a pas trouvé le code des années précédentes, mais je suspecte fortement que ce soit un algo similaire en départageant les égalités aléatoirement plutôt qu'au shotgun.</p>
<p>Plus précisément : l'algorithme se base sur le problème hôpital-résident&nbsp;:</p>
<ol>
<li>Il commence par essayer d'attribuer une activité à chaque participant.es au mieux possible, en utilisant la librairie matching de python. Les égalités sont départagées aléatoirement. Plus un choix est haut dans votre liste de souhait, plus vous avez de chance de vous le voir attribuer. Si vous n'avez qu'un seul choix, vous avez plus de chance de vous le voir attribué. (les participant.es avec un seul choix sont automatique placé.es avant ceux qui en ont plusieurs)</li>
<li>Toutes les activités attribuées sont supprimées, ainsi que les voeux résolus des joueurs.</li>
<li>Tant qu'il reste des place dans des activités et des participant.es qui veulent y participer, on recommence à l'étape 1.</li>
</ol>
<p>Le code est sur <a href="https://github.com/Imakoala/InterludesMatchings">github</a>, il ne marche pas encore parfaitement, et on risque de devoir bidouiller à la main en plus pour résoudre tous les cas particuliers (conflits d'horaires, activité présente plusieurs fois...).
<h2>Proposer une activité ?</h2>
<p>Une réponse</p>
{% endblock %}
\ No newline at end of file
<h2>J'ai encore une question, je fais quoi ?</h2>
<p> Hésite pas à nous passer un mail à <span class="antispam">{{ settings.contact_email_reversed }}</span> pour nous poser tes questions !</p>
{% endblock %}
......@@ -3,17 +3,83 @@
{% block nav_home %}current{% endblock %}
{% block "content" %}
<h2>Présentation</h2>
{% if settings.discord_link %}
<ul class="messagelist"><li class="info">
Rejoins notre <a href="{{ settings.discord_link }}">serveur discord</a> pour participer à l'événement.{{ settings.global_message }}
</li></ul>
{% endif %}
<p>
Les interludes, ou interENS ludiques, regroupent annuellement
les étudiants des quatres ENS de France autour d'activités ludiques.
les étudiants des quatre ENS de France autour d'activités ludiques.
Jeux de plateau, jeux de rôles, jeux vidéos, murders et autres sont
à l'honneur durant ce week-end de trois jours.
</p>
<p>
Cette année, c'est au tour de l'ENS Ulm d'organiser les interludes.
L'événement a habituellement lieu en février mais à cause du COVID,
il a été retardé et est prévu le week-end du vendredi 9 au dimanche 11
avril 2021.
Cette année, c'est au tour de l'{{ settings.hosting_school }} d'organiser les interludes.
Elles auront lieu
{% if settings.date_start %}{% if settings.date_end %}
le week-end du <strong>{{ settings.date_start.day }}-{{ settings.date_end }}</strong>.
{% else %}
le week-end du <strong>{{ settings.date_start }}</strong>.
{% endif %}
{% else %}
à une date non-fixée.
{% endif %}
</p>
<h2>Inscriptions</h2>
<h3>Inscription à l'événement</h3>
<p>
L'inscription à l'événement,
à l'hébergement et aux repas se fait sur <a
href="https://www.helloasso.com/associations/bureau-ludique-de-l-ens-de-lyon/evenements/interludes-ens-de-lyon">le
formulaire HelloAsso</a>
</p>
<h3>Inscription aux activités</h3>
<p>
Une fois votre inscription à l'événement effectuée, vous pourrez vous <a href="{% url 'inscription'%}">inscrire aux activités</a>
</p>
{% if settings.inscriptions_start and settings.inscriptions_end %}
<p>Les inscriptions seront ouvertes
du <strong>{{ settings.inscriptions_start|date:"l d F Y à H:i" }}</strong>
au <strong>{{ settings.inscriptions_end|date:"l d F Y à H:i" }}</strong>.
</p>
{% elif settings.inscriptions_start %}
<p>Les inscriptions ouvrirons le <strong>{{ settings.inscriptions_start|date:"l d F Y à H:i" }}</strong>.</p>
{% elif settings.inscriptions_end %}
<p>Les inscriptions fermerons le <strong>{{ settings.inscriptions_end|date:"l d F Y à H:i" }}</strong>.</p>
{% endif %}
<h2>Tarifs</h2>
<p>
Les tarifs sont différenciés entre salarié·es et non-salarié·es.
<ul>
<li><strong>Participation à l'événement :</strong> 6€/4€</li>
<li><strong>Repas (du vendredi soir au dimanche midi) :</strong> 10€/6€</li>
<li><strong>Logement en tente :</strong> 3€</li>
</ul>
</p>
<h2>Menu des repas</h2>
<p>
Cette année, le menu sera entièrement végétarien, avec options végan et/ou sans gluten possibles.
<ul>
<li><strong>Vendredi soir :</strong> Chili sin carne, riz & compote</li>
<li><strong>Samedi midi :</strong> Brunch (Salade bar, Croques pesto-mozza, Houmous, Gâteaux)</li>
<li><strong>Samedi soir :</strong> Soupe de saison & Tartiflette (option vegan : gratin de pommes de terre aux champignons)</li>
<li><strong>Dimanche midi :</strong> Sandwich au choix</li>
</ul>
<h2>Liens divers</h2>
<ul>
<li>Le code source de ce site est sur <a href="https://github.com/Pantoofle/site-interludes">github</a>.</li>
<li>Un historique des interludes avec leurs visuels, site webs et photos est sur le <a
href="https://wiki.crans.org/VieBdl/InterLudes">wiki de Paris-Saclay</a>.</li>
</ul>
{% endblock %}
......@@ -3,9 +3,29 @@
{% block nav_inscription %}current{% endblock %}
{% block "content" %}
<h2>Inscriptions</h2>
<p>
Les inscriptions ne sont pas encores ouvertes.
Nous communiquerons pas mail via les BDE des différentes écoles pour leur ouverture.
</p>
<h2>Inscriptions à l'événement</h2>
<p>L'inscription à l'événement,
l'hébergement et aux repas se fait sur <a
href="https://www.helloasso.com/associations/bureau-ludique-de-l-ens-de-lyon/evenements/interludes-ens-de-lyon">le
formulaire HelloAsso</a>.</p>
<h2>Inscriptions aux activités</h2>
{% if settings.inscriptions_not_open_yet %}
<p>Les inscriptions aux activités ne sont pas encores ouvertes.</p>
<p>Leur ouverture est prévue le <strong>{{ settings.inscriptions_start|date:"l d F Y à H:i" }}</strong>.</p>
<p>Nous communiquerons par mail via les BDE des différentes écoles pour leur ouverture.</p>
{% elif settings.inscriptions_have_closed %}
<p>Les inscriptions aux activités sont fermées.</p>
<p>
Les inscriptions cette année sont facultatives,
tu peux quand même rejoindre le {% if settings.discord_link %}<a href="{{ settings.discord_link }}">serveur discord</a>{% else %}serveur discord{% endif %} et participer aux jeux libres et
aux activités qui ne nécessitent pas d'inscription.
</p>
{% if settings.contact_email %}
<p>Pour tout problème, contacter&nbsp;:<br><span class="antispam">{{ settings.contact_email_reversed }}</span></p>
{% endif %}
{% else %}
<p>Les inscriptions aux activités ne sont pas encores ouvertes ou ont été fermées.</p>
<p>Nous communiquerons par mail via les BDE des différentes écoles pour leur ouverture.</p>
{% endif %}
{% endblock %}
{% extends "base.html" %}
{% load static %}
{% block nav_inscription %}current{% endblock %}
{% block "content" %}
<h2>Inscriptions</h2>
{% if settings.discord_link %}
<ul class="messagelist">
<li class="info">Rejoignez aussi <a href="{{ settings.discord_link }}">le serveur discord</a>,
C'est là que s'organiseront la plupart des activités.</li>
</ul>
{% endif %}
<form id="main_form" method="post" action="{% url 'inscription' %}">
{% csrf_token %}
Cette année, l'événement étant en distanciel, il est gratuit.
<p>{{ form.school.label_tag }} {{ form.school }}</p>
<h3>Choix d'activités</h3>
<p>Saissisez les activités auquelles vous voulez vous inscrire, par ordre de préférence.
Vous trouverez une description des activités sur <a href="{% url 'activites' %}">cette page</a>.
</p>
<p>
Vous pouvez revenir modifier votre choix jusqu'à la fermeture des
inscriptions{% if settings.inscriptions_end %} (le {{ settings.inscriptions_end|date:"l d F Y à H:i" }}){% endif %}.
</p>
<p>Si vous vous inscrivez à une activité qui nécessite préparation, nous communiquerons
votre email aux orgas pour qu'iels puissent vous contacter.
</p>
{% if formset.non_form_errors %}
{{ formset.non_form_errors }}
{% endif %}
{{ formset.management_data }}
{{ formset.management_form }}
{% for form in formset %}
<div class="activity-form flex">
{{ form.as_p }}
<button class="button delete-activity" style="align-self: center; flex-grow: 0;">Supprimer</button>
</div>
{% endfor %}
<button class="button" id="add-activity">Ajouter une activité</button>
<div class="flex">
<input type="submit" value="Valider">
<a class="button" href="{% url 'profile' %}">Annuler</a>
</div>
</form>
<script>
const button_add_activity = document.querySelector("#add-activity");
const button_submit_form = document.querySelector('[type="submit"]');
const activity_form = document.getElementsByClassName("activity-form");
const main_form = document.querySelector("#main_form");
const total_forms = document.querySelector("#id_form-TOTAL_FORMS");
const form_regex = /form-(\d*)-/g;
var form_count = activity_form.length - 1;
function add_new_form(event) {
// adds a new activity form when clicking on the + button
event.preventDefault();
// clone the first form and replaces it's id
const new_form = activity_form[0].cloneNode(true);
form_count++;
new_form.innerHTML = new_form.innerHTML.replace(form_regex, `form-${form_count}-`);
// add it and increment form total
main_form.insertBefore(new_form, button_add_activity);
new_form.querySelector("select").value = "";
total_forms.setAttribute("value", `${form_count+1}`);
}
button_add_activity.addEventListener("click", add_new_form);
function delete_form(event) {
if (!event.target.classList.contains("delete-activity")) return;
event.preventDefault();
if (form_count == 0) {
// don't delete the first element
activity_form[0].querySelector("select").value = "";
return;
}
event.target.parentElement.remove();
form_count--;
total_forms.setAttribute("value", `${form_count+1}`);
// update form numbers
let count = 0;
for (const form of activity_form) {
// the replace changes the field value
// so we save and restore it
const select = form.querySelector("select");
const value = select.value;
form.innerHTML = form.innerHTML.replace(form_regex, `form-${count++}-`);
form.querySelector("select").value = value;
}
}
main_form.addEventListener("click", delete_form);
</script>
{% endblock %}
......@@ -3,17 +3,49 @@
{% block nav_inscription %}current{% endblock %}
{% block "head" %}
<!--<script src="{% static 'js/formset.js' %}"></script>-->
{% endblock %}
{% block "content" %}
<h2>Inscriptions</h2>
<h2>Inscription à l'événement</h2>
L'inscription à l'événement,
à l'hébergement et aux repas se fait sur <a
href="https://www.helloasso.com/associations/bureau-ludique-de-l-ens-de-lyon/evenements/interludes-ens-de-lyon">le
formulaire HelloAsso</a>
<h2>Inscription aux activités</h2>
Une fois votre inscription à l'événement effectuée, vous pourrez
vous inscrire à certaines activités sur cette page. La plupart des
activités ne demandent pas d'inscription et seront en libre accès
durant tout l'événement, mais certaines demandent une inscription à
l'avance.
<form id="main_form" method="post" action="{% url 'inscription' %}">
{% csrf_token %}
{{ form.as_p }}
<p>{{ form.school.label_tag }} {{ form.school }}</p>
<p>{{ form.meal_friday_evening.label_tag }} {{ form.meal_friday_evening }} </p>
<p>{{ form.meal_saturday_morning.label_tag }} {{ form.meal_saturday_morning }} </p>
<p>{{ form.meal_saturday_midday.label_tag }} {{ form.meal_saturday_midday }}</p>
<p>{{ form.meal_saturday_evening.label_tag }} {{ form.meal_saturday_evening }} </p>
<p>{{ form.meal_sunday_morning.label_tag }} {{ form.meal_sunday_morning}} </p>
<p>{{ form.meal_sunday_midday.label_tag }} {{ form.meal_sunday_midday }}</p>
<p>Vous êtes considéré⋅e commme extérieur si vous n'êtes pas actuellement scolarisé⋅e ou inscrit⋅e à l'ENS Paris-Saclay. Nous avons besoin de cette information pour transmettre la liste des extérieurs à l'administration de l'ENS.</p>
<h3>Choix d'activités</h3>
<p>Saissisez les activités auquelles vous voulez vous inscrire, par ordre de préférence.
Vous trouverez une description des activités sur <a href="{% url 'activites' %}">cette page</a>.
</p>
<p>
Vous pouvez revenir modifier votre choix jusqu'à la fermeture des
inscriptions{% if settings.inscriptions_end %} (le {{ settings.inscriptions_end|date:"l d F Y à H:i" }}){% endif %}.
</p>
<p>Si vous vous inscrivez à une activité qui nécessite préparation, nous communiquerons
votre email aux orgas pour qu'iels puissent vous contacter.
</p>
{% if formset.non_form_errors %}
{{ formset.non_form_errors }}
{% endif %}
{{ formset.management_data }}
{{ formset.management_form }}
{% for form in formset %}
......@@ -26,7 +58,7 @@
<div class="flex">
<input type="submit" value="Valider">
<a class="button" href="{% url 'accounts:profile' %}">Annuler</a>
<a class="button" href="{% url 'profile' %}">Annuler</a>
</div>
</form>
......@@ -50,6 +82,7 @@ function add_new_form(event) {
new_form.innerHTML = new_form.innerHTML.replace(form_regex, `form-${form_count}-`);
// add it and increment form total
main_form.insertBefore(new_form, button_add_activity);
new_form.querySelector("select").value = "";
total_forms.setAttribute("value", `${form_count+1}`);
}
......@@ -58,24 +91,26 @@ button_add_activity.addEventListener("click", add_new_form);
function delete_form(event) {
if (!event.target.classList.contains("delete-activity")) return;
event.preventDefault();
if (form_count == 0) return;
if (form_count == 0) {
// don't delete the first element
activity_form[0].querySelector("select").value = "";
return;
}
event.target.parentElement.remove();
form_count--;
total_forms.setAttribute("value", `${form_count+1}`);
// update form numbers
let count = 0;
for (const form of activity_form) {
console.log(form.children.item(0).find("option:selected").val())
// the replace changes the field value
// so we save and restore it
const select = form.querySelector("select");
const value = select.value;
form.innerHTML = form.innerHTML.replace(form_regex, `form-${count++}-`);
form.querySelector("select").value = value;
}
}
function change(event) {
console.log(event.target.value)
}
main_form.addEventListener("click", delete_form);
main_form.addEventListener("change", change)
</script>
{% endblock %}
......@@ -4,7 +4,9 @@
{% block "content" %}
<h2>Inscriptions</h2>
<p>Vous devez être connecté pour pouvoir vous inscrire à l'événement.</p>
<p>Aller à la page de <a href="{% url 'accounts:login' %}">connexion</a> pour vous connectez
ou à celle de <a href="{% url 'accounts:create' %}">création de compte</a> si vous n'avez pas de compte.</p>
<p>Vous devez être connecté pour pouvoir vous inscrire à des activités.
<p>Aller à la page de <a href="{% url 'account_login' %}">connexion</a> pour vous connecter ou créer un compte.
{% if settings.inscriptions_end %}
<p>Les inscriptions seront ouvertes jusqu'au {{ settings.inscriptions_end|date:"l d F Y à H:i" }})</p>
{% endif %}
{% endblock %}
{% extends "base.html" %}
{% block nav_profile %}current{% endblock %}
{% block "content" %}
<h2>Mon compte</h2>
<p>Connecté en tant que {{ user.first_name }} {{ user.last_name }} ({{ user.email }})</p>
{% if user.is_superuser %}
<ul class="messagelist">
<li class="info">
Vous avez les droits d'administrateurs.
<br>Aller à la <a href="{% url 'admin_pages:index' %}">page d'administration du site</a>
{% if user.is_staff %}
<br>Aller à la <a href="{% url 'admin:index' %}">page d'administration de django</a>
(N'y modifier rien sy vous n'êtes pas sûrs de vous)
{% endif %}
</li>
</ul>{% endif %}
{% if user.profile.is_registered %}
<strong>Mon inscription&nbsp;:</strong>
{% if settings.inscriptions_open and settings.inscriptions_end %}
modifiable jusqu'au {{ settings.inscriptions_end|date:"l d F Y à H:i" }}
{% endif %}
<ul>
<!-- <li>{% if user.profile.sleeps %}Inscrit pour dormir sur place{% else %}Ne dors pas sur place{% endif %}</li> -->
<!-- <li>{% if user.profile.mug %}Commandse une tasse{% else %}Ne commande pas de tasse{% endif %}</li> -->
<!-- <li>Inscrit à {{ user.profile.nb_meals }} repas.</li> -->
{% if settings.activities_allocated %}
{% if my_choices %}
<li>Inscrit à {{ my_choices|length }} activités&nbsp;:
<ul>
{% for choice in my_choices %}
<li><a href="{% url 'activites' %}#{{ choice.slot.activity.slug }}">{{ choice.slot }}</a>
{% if choice.slot.on_planning %}
(le {{ choice.slot.start|date:"l à H:i" }}<!-- en {{ choice.slot.room }} -->)
{% endif %}
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li>Inscrit à aucune activité</li>
{% endif %}
{% else %}
{% if my_choices %}
<li>{{ my_choices|length }} activités souhaitées&nbsp;:
<ol>
{% for choice in my_choices %}
<li><a href="{% url 'activites' %}#{{ choice.slot.activity.slug }}">{{ choice.slot }}</a>
{% if choice.slot.on_planning %}
(le {{ choice.slot.start|date:"l à H:i" }}<!-- en {{ choice.slot.room }} -->)
{% endif %}
</li>
{% endfor %}
</ol>
</li>
{% else %}
<li>Aucune activité souhaitée</li>
{% endif %}
{% endif %}
</ul>
{% else %}
<strong>Vous n'avez pas encore renseigné vos choix d'activités.</strong>
{% if not settings.inscriptions_open %}
{% if settings.inscriptions_not_open_yet %}
<p>Les inscriptions aux activités ne sont pas encores ouvertes. Elles ouvrirons le <strong>{{ settings.inscriptions_start|date:"l d F Y à H:i" }}</strong>.</p>
{% elif settings.inscriptions_have_closed %}
<p>Les inscriptions aux activités sont fermées.</p>
{% else %}
<p>Les inscriptions aux activités ne sont pas encores ouvertes ou ont été fermées.</p>
{% endif %}
{% elif settings.inscriptions_end %}
<p>les inscriptions aux activités sont ouvertes jusqu'au {{ settings.inscriptions_end|date:"l d F Y à H:i" }}).</p>
{% endif %}
<br><br>
{% endif %}
<div class="flex wrap">
{% if user.profile.is_registered %}
{% if settings.inscriptions_open %}
<a class="button" href="{% url 'desinscription' %}">Me désinscrire</a>
<a class="button" href="{% url 'inscription' %}">Modifier mon inscription</a>
{% else %}
<script type="text/javascript">
function clicked() {
if (confirm(
'Les inscriptions étant fermées, vous ne pourrez pas vous réinscrire.\nVoulez vous vraiment vous désinscrire?'
))
window.location = "{% url 'desinscription' %}";
}
</script>
<button class="button" onclick="clicked();">Me désinscrire</button>
{% endif %}
{% elif settings.inscriptions_open %}
<a class="button" href="{% url 'inscription' %}">S'inscrire</a>
{% endif %}
<a class="button" href="{% url 'accounts:update' %}">Modifier mes informations</a>
<a class="button" href="{% url 'account_logout' %}">Déconnexion</a>
</div>
{% endblock %}
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.sitemaps.views import sitemap
from django.views.generic import RedirectView
from django.urls import path, include
......@@ -7,14 +9,21 @@ from home import views
sitemaps = {"static_pages": views.StaticViewSitemap}
urlpatterns = [
path('', views.HomeView.as_view(), {"template": "home.html"}, name = 'home'),
path('', views.HomeView.as_view(), name = 'home'),
path('inscription/', views.RegisterView.as_view(), name = 'inscription'),
path('activites/', views.ActivityView.as_view(), {"template":"activites.html"}, name = 'activites'),
path('faq/', views.FAQView.as_view(), {"template":"faq.html"}, name = 'FAQ'),
path('desinscription/', views.UnregisterView.as_view(), name="desinscription"),
path('activites/', views.ActivityView.as_view(), name = 'activites'),
path('activites/nouvelle/', views.ActivitySubmissionView.as_view(), name = 'activity_submission'),
path('faq/', views.FAQView.as_view(), name = 'FAQ'),
path("profil/", views.ProfileView.as_view(), name="profile"),
path('favicon.ico', RedirectView.as_view(url='/static/imgs/favicon.ico')),
path(
'sitemap.xml', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'
),
path('accounts/', include("accounts.urls")),
path('admin_pages/', include(('admin_pages.urls', 'admin_pages'), namespace="admin_pages")),
path('comptes/', include("accounts.urls")),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
from datetime import timedelta
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.sitemaps import Sitemap
from django.forms import modelformset_factory
from django.forms import formset_factory
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.generic import UpdateView, TemplateView, View
from django.urls import reverse, reverse_lazy
from django.views.generic import FormView, RedirectView, TemplateView, View
from home.models import ActivityList, InterludesActivity
from home.forms import ActivityForm, InscriptionForm
from home import models
from home.forms import ActivityForm, ActivitySubmissionForm, BaseActivityFormSet, InscriptionForm
from site_settings.models import SiteSettings
# ==============================
# Site static pages
# ==============================
class HomeView(TemplateView):
"""Vue pour la page d'acceuil"""
template_name = "home.html"
def get_planning_context():
"""Returns the context dict needed to display the planning"""
settings = SiteSettings.load()
context = dict()
context['planning'] = models.SlotModel.objects.filter(on_planning=True).order_by("title")
if settings.date_start is not None:
context['friday'] = settings.date_start.day
context['saturday'] = (settings.date_start + timedelta(days=1)).day
context['sunday'] = (settings.date_start + timedelta(days=2)).day
else:
context['friday'] = 1
context['saturday'] = 2
context['sunday'] = 3
return context
class ActivityView(TemplateView):
"""Vue pour la liste des activités"""
......@@ -23,7 +44,8 @@ class ActivityView(TemplateView):
def get_context_data(self, **kwargs):
"""ajoute la liste des activités au contexte"""
context = super(ActivityView, self).get_context_data(**kwargs)
context['activities'] = InterludesActivity.objects.filter(display=True).order_by("title")
context['activities'] = models.ActivityModel.objects.filter(display=True).order_by("title")
context.update(get_planning_context())
return context
......@@ -32,6 +54,33 @@ class FAQView(TemplateView):
template_name = "faq.html"
# ==============================
# Profile and registration
# ==============================
class ProfileView(LoginRequiredMixin, TemplateView):
"""Vue des actions de gestion de son profil"""
template_name = "profile.html"
redirect_field_name = "next"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
settings = SiteSettings.load()
if settings.activities_allocated:
my_choices = models.ActivityChoicesModel.objects.filter(
participant=self.request.user.profile,
accepted=True
)
else:
my_choices = models.ActivityChoicesModel.objects.filter(
participant=self.request.user.profile
)
context["my_choices"] = my_choices
return context
class RegisterClosed(TemplateView):
"""Vue pour quand les inscriptions ne sont pas ouvertes"""
template_name = "inscription/closed.html"
......@@ -41,35 +90,53 @@ class RegisterSignIn(TemplateView):
l'utilisateur n'est pas connecté"""
template_name = "inscription/signin.html"
class RegisterUpdateView(LoginRequiredMixin, UpdateView):
class RegisterUpdateView(LoginRequiredMixin, TemplateView):
"""Vue pour s'inscrire et modifier son inscription"""
template_name = "inscription/form.html"
form_class = InscriptionForm
formset = modelformset_factory(ActivityList, form=ActivityForm, extra=3)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["formset"] = self.formset(queryset=ActivityList.objects.none())
return context
formset_class = formset_factory(form=ActivityForm, extra=3, formset=BaseActivityFormSet)
success_url = reverse_lazy("profile")
@staticmethod
def get_slots(participant):
activities = models.ActivityChoicesModel.objects.filter(participant=participant).order_by("priority")
return [{"slot": act.slot} for act in activities]
@staticmethod
def set_activities(participant, formset):
# delete old activites
models.ActivityChoicesModel.objects.filter(participant=participant).delete()
priority = 0
for form in formset:
data = form.cleaned_data
if data:
slot = data["slot"]
models.ActivityChoicesModel(
priority=priority, participant=participant, slot=slot
).save()
priority += 1
def get(self, request, *args, **kwargs):
participant = request.user.profile
slots = self.get_slots(participant)
form = self.form_class(instance=participant)
formset = self.formset_class(initial=slots)
context = {"form": form, "formset": formset}
return render(request, self.template_name, context)
def get_object(self):
return self.request.user.profile
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, instance=request.user.profile)
formset = self.formset_class(request.POST)
if not (form.is_valid() and formset.is_valid()):
context = {"form": form, "formset": formset}
return render(request, self.template_name, context)
def get_success_url(self):
return reverse("accounts:profile")
form.save()
self.set_activities(request.user.profile, formset)
def form_valid(self, form):
messages.success(self.request, "Votre inscription a été enregistrée")
return super().form_valid(form)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
formset = self.formset(request.POST)
if formset.is_valid():
print("\n\n{} {}\n\n".format(len(formset), formset))
else:
print("\n\nInvalid\n\n")
return super().post(request, *args, **kwargs)
messages.success(request, "Votre inscription a bien été enregistrée")
return redirect(self.success_url, permanent=False)
class RegisterView(View):
"""Vue pour l'inscription
......@@ -83,6 +150,73 @@ class RegisterView(View):
return RegisterUpdateView.as_view()(request)
class UnregisterView(LoginRequiredMixin, RedirectView):
pattern_name = "profile"
def get_redirect_url(self, *args, **kwargs):
participant = self.request.user.profile
participant.is_registered = False
participant.save()
messages.success(self.request, "Vous avez été désinscrit")
return reverse(self.pattern_name)
# ==============================
# Activity Submission Form
# ==============================
class ActivitySubmissionView(LoginRequiredMixin, FormView):
"""Vue pour proposer une activité"""
template_name = "activity_submission.html"
form_class = ActivitySubmissionForm
success_url = reverse_lazy("profile")
@staticmethod
def submission_check():
"""Vérifie si le formulaire est ouvert ou non"""
settings = SiteSettings.load()
return settings.activity_submission_open
def get_initial(self):
init = super().get_initial()
user = self.request.user
init.update({
"host_name": "{} {}".format(user.first_name, user.last_name),
"host_email": user.email,
})
return init
def not_open(self, request):
"""Appelé quand le formulaire est désactivé"""
messages.error(request, "La soumission d'activité est desactivée")
return redirect(self.success_url, permanent=False)
def get(self, request, *args, **kwargs):
if not self.submission_check():
return self.not_open(request)
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if not self.submission_check():
return self.not_open(request)
form = self.form_class(request.POST)
if not form.is_valid():
context = self.get_context_data()
context["form"] = form
return render(request, self.template_name, context)
form.save()
messages.success(request, "Votre activité a bien été enregistrée. Elle sera affichée sur le site après relecture par les admins.")
return redirect(self.success_url, permanent=False)
# ==============================
# Sitemap
# ==============================
class StaticViewSitemap(Sitemap):
"""Vue générant la sitemap.xml du site"""
changefreq = 'monthly'
......
# Secrets that must be changed in production
SECRET_KEY = "i*4$=*fa(644(*!9m2)0-*&sows2uz$b^brb(=)elfn3+y6#1n"
ADMINS = [("superuser", "superuser@admin.fr"),]
DB_NAME = "db.sqlite3"
SERVER_EMAIL = "root@localhost"
DEFAULT_FROM_EMAIL = "webmaster@localhost"
EMAIL_HOST = "localhost"
EMAIL_PORT = 587
EMAIL_HOST_USER = None
EMAIL_HOST_PASSWORD = None
......@@ -19,16 +19,62 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'tx$xi%n!8cghirp377zb)gd24g#=&w*ik(bx2h(i8ji0_&9_5l'
# SECURITY WARNING: don't run with debug turned on in production!
try:
from . import secret
except ImportError:
raise ImportError(
"The interludes/secret.py file is missing.\n"
"Run 'make secret' to generate a secret."
)
def import_secret(name):
"""
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))
SITE_ID=1
SECRET_KEY = import_secret("SECRET_KEY")
DB_NAME = import_secret("DB_NAME")
ADMINS = import_secret("ADMINS")
SERVER_EMAIL = import_secret("SERVER_EMAIL")
DEFAULT_FROM_EMAIL = import_secret("DEFAULT_FROM_EMAIL")
EMAIL_HOST = import_secret("EMAIL_HOST")
EMAIL_PORT = import_secret("EMAIL_PORT")
EMAIL_HOST_USER = import_secret("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = import_secret("EMAIL_HOST_PASSWORD")
EMAIL_USE_SSL = True
# FIXME - set to False in production
DEBUG = True
ADMINS = [("respos", "respointerludes2021@ens.psl.eu"),]
# FIXME - set hosts in production
ALLOWED_HOSTS = ["127.0.0.1", "localhost","kwei-dev.crans.org"]
if DEBUG:
# This will display emails in Console.
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
else:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
ALLOWED_HOSTS = []
SECURE_SSL_REDIRECT = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_SECONDS = 3600
SECURE_HSTS_PRELOAD = True
SECURE_REFERRER_POLICY = "same-origin"
# Application definition
......@@ -40,11 +86,19 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sitemaps',
'django.contrib.sites',
'home.apps.HomeConfig',
'admin_pages.apps.AdminPagesConfig',
'accounts.apps.AccountsConfig',
'site_settings.apps.SiteSettingsConfig',
'shared.apps.SharedConfig'
'shared.apps.SharedConfig',
# allauth support.
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth_note_kfet',
]
MIDDLEWARE = [
......@@ -78,6 +132,8 @@ TEMPLATES = [
WSGI_APPLICATION = 'interludes.wsgi.application'
# Auto primary key type
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
......@@ -85,40 +141,48 @@ WSGI_APPLICATION = 'interludes.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'NAME': os.path.join(BASE_DIR, DB_NAME),
}
}
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
)
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_USER_MODEL = 'accounts.EmailUser'
AUTH_PROFILE_MODULE = 'home.InterludesParticipant'
AUTH_PROFILE_MODULE = 'home.ParticipantModel'
# Tell oauth how the user is configured.
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
SOCIALACCOUNT_PROVIDERS = {
'notekfet': {
'DOMAIN': 'note-dev.crans.org', # À remplacer si nécessaire
# 'SCOPE': ['1_1'], # Adapter les scopes demandées (par défaut lecture des champs utilisateur⋅rice)
},
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
{ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', },
{ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', },
{ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', },
{ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', },
]
# Session time in seconds
SESSION_COOKIE_AGE = 3600
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'fr-fr'
LANGUAGE_CODE = 'fr'
TIME_ZONE = 'CET'
......@@ -135,8 +199,17 @@ USE_TZ = True
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
LOGIN_URL = "accounts:login"
LOGIN_REDIRECT_URL = "accounts:profile"
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
LOGIN_URL = 'accounts:login'
LOGIN_REDIRECT_URL = 'profile'
# Prefix to mails to admins
EMAIL_SUBJECT_PREFIX = '[DJANGO WEBLUDES] '
# Signature to mails to admins
EMAIL_SIGNATURE = '-- Site Interludes (mail généré automatiquement)'
# This will display email in Console.
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Prefix to mails to users
USER_EMAIL_SUBJECT_PREFIX = "[interludes] "
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.