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
Django~=3.0.8
\ No newline at end of file
Django~=3.2.7
import csv
from shared.views import CSVWriteView
from django.http import HttpResponse
class CSVWriteViewForAdmin(CSVWriteView):
def get_values(self):
return self.queryset.values()
class ExportCsvMixin:
"""
class abstraite pour permettre l'export CSV rapide d'un modele
utiliser csv_export_exclude pour exclure des colonnes du fichier généré
"""
def export_as_csv(self, request, queryset):
"""renvoie un fichier CSV contenant l'information du queryset"""
meta = self.model._meta
field_names = [field.name for field in meta.fields]
if self.csv_export_exclude:
field_names = [field for field in field_names if not field in self.csv_export_exclude]
filename = None
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response)
csv_export_exclude = []
csv_export_fields = None
writer.writerow(field_names)
for obj in queryset:
writer.writerow([getattr(obj, field) for field in field_names])
def get_filename(self):
if self.filename:
return self.filename
return str(self.model._meta)
return response
def export_as_csv(self, request, queryset):
"""renvoie un fichier CSV contenant l'information du queryset"""
view = CSVWriteViewForAdmin(
request=request, queryset=queryset, model=self.model,
filename=self.get_filename(), exclude_fields=self.csv_export_exclude,
fields=self.csv_export_fields,
)
return view.get(request)
export_as_csv.short_description = "Exporter au format CSV"
actions = ["export_as_csv"]
csv_export_exclude = None
from django.db import models
from django.contrib.auth.base_user import BaseUserManager
# Create your models here.
def normalize_email(email: str) -> str:
"""Normalizes an email address"""
return BaseUserManager.normalize_email(email)
from django.shortcuts import render
import csv
# Create your views here.
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views import View
from django.http import HttpResponse
class SuperuserRequiredMixin(UserPassesTestMixin):
"""Classe restreignant l'accès d'une vue aux superusers"""
raise_exception = True
permission_denied_message = "Seul les superutilisateurs ont accès à cette page"
def test_func(self):
user = self.request.user
return user.is_authenticated and user.is_superuser
class CSVWriteView(View):
filename = "csv_file.csv"
headers = None
model = None
exclude_fields = []
fields = None
def get_filename(self):
return self.filename
def get_headers(self):
"""overload this to dynamicaly generate headers"""
if self.headers:
return self.headers
if self.model:
return self.get_field_names()
return None
def get_values(self):
"""overload to change queryset used in self.get_rows"""
if self.model:
return self.model.objects.values()
raise NotImplementedError("{}.get_values() isn't implemented when model is None".format(
self.__class__.__name__
))
def get_field_names(self):
"""overload to limit/change field names
default to:
- the value of self.field if not None
- all fields minus those in exclude_fields otherwise"""
if self.fields is not None:
return self.fields
return [
field.name for field in self.model._meta.get_fields()
if not field.name in self.exclude_fields
]
def get_rows(self):
"""overload this to return the list of rows"""
queryset = self.get_values()
fields = self.get_field_names()
table = []
for row in queryset:
table.append([row[field] for field in fields])
return table
def get(self, request, *args, **kwargs):
response = HttpResponse(content_type='text/csv')
filename = self.get_filename()
if not filename.endswith(".csv"):
filename += ".csv"
response['Content-Disposition'] = 'attachment; filename="{}"'.format(self.filename)
writer = csv.writer(response)
headers = self.get_headers()
if headers is not None:
writer.writerow(headers)
for row in self.get_rows():
writer.writerow(row)
return response
......@@ -14,4 +14,9 @@ class SingletonModelAdmin(admin.ModelAdmin):
@admin.register(SiteSettings)
class SiteSettingsAdmin(SingletonModelAdmin):
pass
\ No newline at end of file
def planning_file_link(self, obj):
if obj.file:
return "<a href='%s'>download</a>" % (obj.file.url,)
else:
return "No attachment"
# The website version number
WEBSITE_VERSION = "2.0.0-beta"
WEBSITE_VERSION_DATE = "2021-10-05"
WEBSITE_FULL_VERSION = "{} - {}".format(
WEBSITE_VERSION, WEBSITE_VERSION_DATE
)
# Update this to force reload of cached css
CSS_VERSION = "1.0"
from site_settings import constants
from site_settings.models import SiteSettings
def settings(request):
return {'settings': SiteSettings.load()}
return {
'settings': SiteSettings.load(),
'constants' : constants,
}
# Generated by Django 3.2.7 on 2021-10-05 18:45
from django.db import migrations, models
import site_settings.models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='SiteSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contact_email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email contact')),
('date_start', models.DateField(blank=True, null=True, verbose_name='Date de début')),
('date_end', models.DateField(blank=True, null=True, verbose_name='Date de fin')),
('registrations_open', models.BooleanField(default=True, verbose_name='Ouvrir la création de compte')),
('inscriptions_open', models.BooleanField(default=False, verbose_name='Ouvrir les inscriptions')),
('activity_submission_open', models.BooleanField(default=False, help_text='Permet de proposer une activité via le formulaire dédié', verbose_name="Ouvrir l'ajout d'activité")),
('show_host_emails', models.BooleanField(default=False, help_text='Ces mail sont affichés sur la page activités pour que les gens puissent les contacter', verbose_name="Afficher les mails des orgas d'activités")),
('inscriptions_start', models.DateTimeField(blank=True, help_text="Cette date n'est qu'informative. Les inscription s'ouvrent via la checkbox uniquement", null=True, verbose_name='Ouverture des inscriptions')),
('inscriptions_end', models.DateTimeField(blank=True, help_text="Cette date n'est qu'informative. Les inscription se ferment via la checkbox uniquement", null=True, verbose_name='Fermeture des inscriptions')),
('display_planning', models.BooleanField(default=False, verbose_name='Afficher le planning')),
('planning_file', models.FileField(blank=True, null=True, storage=site_settings.models.OverwriteStorage(), upload_to='', verbose_name='Version PDF du planning')),
('activities_allocated', models.BooleanField(default=False, help_text="Suppose que l'allocation des activités a été effectuée.", verbose_name='Afficher les activités obtenues')),
('discord_link', models.CharField(blank=True, max_length=200, null=True, verbose_name='Lien du serveur discord')),
('allow_mass_mail', models.BooleanField(default=False, help_text="Par sécurité, n'activez ceci qu'au moment d'envoyer les emails et désactivez le après", verbose_name="Permettre l'envoi de mails collectifs (aux utilisateurs et orgas)")),
('user_notified', models.BooleanField(default=False, help_text="Ce champ existe pour éviter l'envoie de plusieurs mails successifs. Le decocher permet de renvoyer tous les mails", verbose_name="L'email de répartition des activités a été envoyé")),
('orga_notified', models.BooleanField(default=False, help_text="Ce champ existe pour éviter l'envoie de plusieurs mails successifs. Le decocher permet de renvoyer tous les mails", verbose_name="L'email de liste des participants a été envoyé")),
('global_message', models.TextField(blank=True, help_text='Message affiché en haut de chaque page (si non vide)', null=True, verbose_name='Message global')),
('global_message_as_html', models.BooleanField(default=False, help_text='Assurez vous que le message est bien formaté, cela peut casser toutes les pages du site', verbose_name='Message global au format HTML')),
('caption_red', models.CharField(blank=True, default='Jeux de rôle grandeur nature', max_length=200, null=True, verbose_name='Légende planning (rouge)')),
('caption_orange', models.CharField(blank=True, default='Jeux de rôle sur table', max_length=200, null=True, verbose_name='Légende planning (orange)')),
('caption_yellow', models.CharField(blank=True, default='Activités libres', max_length=200, null=True, verbose_name='Légende planning (jaune)')),
('caption_green', models.CharField(blank=True, default='Tournois', max_length=200, null=True, verbose_name='Légende planning (vert)')),
('caption_blue', models.CharField(blank=True, default='Événements de début et fin', max_length=200, null=True, verbose_name='Légende planning (bleu)')),
('caption_dark_blue', models.CharField(blank=True, default='Jeux vidéos', max_length=200, null=True, verbose_name='Légende planning (bleu foncé)')),
('caption_black', models.CharField(blank=True, default='Autre', max_length=200, null=True, verbose_name='Légende planning (noir)')),
],
options={
'verbose_name': 'paramètres',
'verbose_name_plural': 'paramètres',
},
),
]
# Generated by Django 3.2.9 on 2021-11-07 19:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('site_settings', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='hosting_school',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name="École hébergeant l'événement"),
),
]
# Generated by Django 3.2.16 on 2022-11-08 18:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('site_settings', '0002_sitesettings_hosting_school'),
]
operations = [
migrations.AddField(
model_name='sitesettings',
name='activity_inscriptions_open',
field=models.BooleanField(default=False, help_text="Permet d'ouvrir la partie du formulaire d'inscription pour les activités (nécessite l'ouverture des inscriptions)", verbose_name="Ouvrir l'inscription aux activitées"),
),
migrations.AlterField(
model_name='sitesettings',
name='inscriptions_open',
field=models.BooleanField(default=False, help_text="Permet d'ouvrir le formulaire d'inscription (repas et dodo; l'inscription aux activité est ouvrable en plus)", verbose_name='Ouvrir les inscriptions'),
),
]
from datetime import timedelta
from pathlib import Path
from django.db import models
from django.core.cache import cache
from django.core.files.storage import FileSystemStorage
from django.utils.timezone import now
class Colors(models.TextChoices):
"""Couleur d'affichage dans le planning
Leur code HTML est hardcodé dans la template '_planning.html'."""
RED = "a", "Rouge"
ORANGE = "b", "Orange"
YELLOW = "c", "Jaune"
GREEN = "d", "Vert"
BLUE = "e", "Bleu"
DARK_BLUE = "f", "Bleu foncé"
BLACK = "g", "Noir"
class OverwriteStorage(FileSystemStorage):
"""used to enforcing a fixed filename to upload file
This allow for a constant link to a changeable file"""
filename = "PlanningInterludes"
def get_available_name(self, name, **kwargs):
"""
Returns a filename that's free on the target storage system, and
available for new content to be written to.
"""
# If the filename already exists, remove it as if it was a true file system
extension = Path(name).suffix
new_name = self.filename + extension
if self.exists(new_name):
self.delete(new_name)
return super(FileSystemStorage, self).get_available_name(new_name, **kwargs)
class SingletonModel(models.Model):
"""Table de la BDD qui ne possède qu'un seul élément"""
......@@ -32,17 +68,141 @@ class SingletonModel(models.Model):
class SiteSettings(SingletonModel):
"""Réglages globaux du site"""
contact_email = models.EmailField("Email contact", blank=True, null=True)
hosting_school = models.CharField(
"École hébergeant l'événement", max_length=50, blank=True, null=True
)
date_start = models.DateField("Date de début", blank=True, null=True)
date_end = models.DateField("Date de fin", blank=True, null=True)
registrations_open = models.BooleanField("Ouvrir la création de compte", default=True)
inscriptions_open = models.BooleanField("Ouvrir les inscriptions", default=False, help_text="Permet d'ouvrir le formulaire d'inscription (repas et dodo; l'inscription aux activité est ouvrable en plus)")
activity_inscriptions_open = models.BooleanField("Ouvrir l'inscription aux activitées", default=False, help_text="Permet d'ouvrir la partie du formulaire d'inscription pour les activités (nécessite l'ouverture des inscriptions)")
activity_submission_open = models.BooleanField(
"Ouvrir l'ajout d'activité", default=False,
help_text="Permet de proposer une activité via le formulaire dédié"
)
show_host_emails = models.BooleanField(
"Afficher les mails des orgas d'activités", default=False,
help_text="Ces mail sont affichés sur la page activités pour que les gens puissent les contacter",
)
registrations_open = models.BooleanField("Ouvrir la création de compte", default=False)
inscriptions_open = models.BooleanField("Ouvrir les inscriptions", default=False)
inscriptions_start = models.DateTimeField("Ouverture des inscriptions",
blank=True, null=True,
help_text="Cette date n'est qu'informative. Les inscription s'ouvrent via la checkbox uniquement"
)
inscriptions_end = models.DateTimeField("Fermeture des inscriptions",
blank=True, null=True,
help_text="Cette date n'est qu'informative. Les inscription se ferment via la checkbox uniquement"
)
display_planning = models.BooleanField("Afficher le planning", default=False)
planning_file = models.FileField(
verbose_name="Version PDF du planning", null=True, blank=True,
storage=OverwriteStorage(),
)
activities_allocated = models.BooleanField(
"Afficher les activités obtenues", default=False,
help_text="Suppose que l'allocation des activités a été effectuée."
)
discord_link = models.CharField(
"Lien du serveur discord", max_length=200, blank=True, null=True
)
allow_mass_mail = models.BooleanField(
"Permettre l'envoi de mails collectifs (aux utilisateurs et orgas)", default=False,
help_text="Par sécurité, n'activez ceci qu'au moment d'envoyer les emails et désactivez le après"
)
user_notified = models.BooleanField(
"L'email de répartition des activités a été envoyé", default=False,
help_text="Ce champ existe pour éviter l'envoie de plusieurs mails successifs. Le decocher permet de renvoyer tous les mails"
)
orga_notified = models.BooleanField(
"L'email de liste des participants a été envoyé", default=False,
help_text="Ce champ existe pour éviter l'envoie de plusieurs mails successifs. Le decocher permet de renvoyer tous les mails"
)
global_message = models.TextField("Message global", blank=True, null=True,
help_text="Message affiché en haut de chaque page (si non vide)"
)
global_message_as_html = models.BooleanField(
"Message global au format HTML", default=False,
help_text="Assurez vous que le message est bien formaté, cela peut casser toutes les pages du site",
)
# Légende du planning modifiable
caption_red = models.CharField(
"Légende planning (rouge)", default="Jeux de rôle grandeur nature",
blank=True, null=True, max_length=200,
)
caption_orange = models.CharField(
"Légende planning (orange)", default="Jeux de rôle sur table",
blank=True, null=True, max_length=200,
)
caption_yellow = models.CharField(
"Légende planning (jaune)", default="Activités libres",
blank=True, null=True, max_length=200,
)
caption_green = models.CharField(
"Légende planning (vert)", default="Tournois",
blank=True, null=True, max_length=200,
)
caption_blue = models.CharField(
"Légende planning (bleu)", default="Événements de début et fin",
blank=True, null=True, max_length=200,
)
caption_dark_blue = models.CharField(
"Légende planning (bleu foncé)", default="Jeux vidéos",
blank=True, null=True, max_length=200,
)
caption_black = models.CharField(
"Légende planning (noir)", default="Autre",
blank=True, null=True, max_length=200,
)
@property
def contact_email_reversed(self) -> str:
return self.contact_email[::-1]
@property
def inscriptions_not_open_yet(self) -> bool:
if self.inscriptions_start:
return now() <= self.inscriptions_start
return False
@property
def inscriptions_have_closed(self) -> bool:
if self.inscriptions_end:
return now() >= self.inscriptions_end
return False
@property
def date_2(self):
"""The date of the second day"""
if self.date_start:
return self.date_start + timedelta(days=1)
@property
def has_caption(self) -> bool:
"""Vérifie si l'une des légende est non-nulle"""
return self.caption_red \
or self.caption_orange \
or self.caption_yellow \
or self.caption_green \
or self.caption_blue \
or self.caption_dark_blue \
or self.caption_black
class Meta:
verbose_name = "paramètres"
verbose_name_plural = "paramètres"
def __str__(self) -> str:
return "Modifier les paramètres"