kpsul/gestioncof/admin.py
Basile Clement c960d97b67 Extrait les petits cours dans une application séparée
L'application `petitscours` reste assez fortement couplée à
`gestioncof`, et n'est pas (encore ?) faite pour être utilisée
séparément.

De façon similaire, et afin de minimiser de potentiels problèmes dûs à
des migrations, les modèles de l'application `petitscours` utilisent
`app_label = "gestioncof"` pour que Django les considère comme faisant
partie de l'application `"gestioncof"`.  Ils pourront être migrés dans
un second temps si cela s'avère nécessaire.

Les changements sont nombreux, mais assez simples: il s'agit
principalement de déplacer des fichiers et changer des imports.  J'ai
également profité de l'occasion pour réorganiser les templates afin de
les placer dans l'espace de nom "petitscours/".

cof/
 * settings/common.py: Add `petitscours` app
 * urls.py: Use `petitscours.urls`

petitscours/
 * __init__.py: Added.
 * tests/__init__.py: Added.
 * tests/utils.py: Added.
 * urls.py: Added.

gestioncof/
 * admin.py:
 * management/commands/loaddevdata.py:
 * models.py:
 * signals.py: Typo.
 * urls.py:
       Moved petitscours_patterns to petitscours.urls
 * petits_cours_forms.py:
       Moved to petitscours/forms.py
 * petits_cours_models.py:
       Moved to petitscours/models.py
 * petits_cours_views.py:
       Moved to petitscours/views.py
 * tests/utils.py:
 * tests/test_petitscours_views.py:
       Moved to petitscours/tests/test_petitscours_views.py
 * templates/base_title_petitscours.html:
       Moved to petitscours/templates/petitscours/base_title.html
 * templates/demande-petit-cours.html:
       Moved topetitscours/templates/petitscours/demande.html
 * templates/gestioncof/details_demande_petit_cours.html:
       Moved to petitscours/templates/petitscours/demande_detail.html
 * templates/petits_cours_demandes_list.html:
       Moved to petitscours/templates/petitscours/demande_list.html
 * templates/demande-petit-cours-raw.html:
       Moved to petitscours/templates/petitscours/demande_raw.html
 * templates/details_demande_petit_cours_infos.html:
       Moved to petitscours/templates/petitscours/details_demande_infos.html
 * templates/inscription-petit-cours.html:
       Moved to petitscours/templates/petitscours/inscription.html
 * templates/inscription-petit-cours-formset.html:
       Moved to petitscours/templates/petitscours/inscription_formset.html
 * templates/gestioncof/traitement_demande_petit_cours.html:
       Moved to petitscours/templates/petitscours/traitement_demande.html
 * templates/gestioncof/traitement_demande_petit_cours_autre_niveau.html:
       Moved to petitscours/templates/petitscours/traitement_demande_autre_niveau.html
 * templates/gestioncof/traitement_demande_petit_cours_success.html:
       Moved to petitscours/templates/petitscours/traitement_demande_success.html
2018-11-25 13:25:16 +01:00

333 lines
9.9 KiB
Python

from dal.autocomplete import ModelSelect2
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group, Permission, User
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from petitscours.models import (
PetitCoursAbility,
PetitCoursAttribution,
PetitCoursAttributionCounter,
PetitCoursDemande,
PetitCoursSubject,
)
from gestioncof.models import (
Club,
CofProfile,
Event,
EventCommentField,
EventOption,
EventOptionChoice,
EventRegistration,
Survey,
SurveyQuestion,
SurveyQuestionAnswer,
)
def add_link_field(target_model="", field="", link_text=str, desc_text=str):
def add_link(cls):
reverse_name = target_model or cls.model.__name__.lower()
def link(self, instance):
app_name = instance._meta.app_label
reverse_path = "admin:%s_%s_change" % (app_name, reverse_name)
link_obj = getattr(instance, field, None) or instance
if not link_obj.id:
return ""
url = reverse(reverse_path, args=(link_obj.id,))
return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))
link.allow_tags = True
link.short_description = desc_text(reverse_name + " link")
cls.link = link
cls.readonly_fields = list(getattr(cls, "readonly_fields", [])) + ["link"]
return cls
return add_link
class SurveyQuestionAnswerInline(admin.TabularInline):
model = SurveyQuestionAnswer
@add_link_field(
desc_text=lambda x: "Réponses", link_text=lambda x: "Éditer les réponses"
)
class SurveyQuestionInline(admin.TabularInline):
model = SurveyQuestion
class SurveyQuestionAdmin(admin.ModelAdmin):
search_fields = ("survey__title", "answer")
inlines = [SurveyQuestionAnswerInline]
class SurveyAdmin(admin.ModelAdmin):
search_fields = ("title", "details")
inlines = [SurveyQuestionInline]
class EventOptionChoiceInline(admin.TabularInline):
model = EventOptionChoice
@add_link_field(desc_text=lambda x: "Choix", link_text=lambda x: "Éditer les choix")
class EventOptionInline(admin.TabularInline):
model = EventOption
class EventCommentFieldInline(admin.TabularInline):
model = EventCommentField
class EventOptionAdmin(admin.ModelAdmin):
search_fields = ("event__title", "name")
inlines = [EventOptionChoiceInline]
class EventAdmin(admin.ModelAdmin):
search_fields = ("title", "location", "description")
inlines = [EventOptionInline, EventCommentFieldInline]
class CofProfileInline(admin.StackedInline):
model = CofProfile
inline_classes = ("collapse open",)
class FkeyLookup(object):
def __init__(self, fkeydecl, short_description=None, admin_order_field=None):
self.fk, fkattrs = fkeydecl.split("__", 1)
self.fkattrs = fkattrs.split("__")
self.short_description = short_description or self.fkattrs[-1]
self.admin_order_field = admin_order_field or fkeydecl
def __get__(self, obj, klass):
if obj is None:
"""
hack required to make Django validate (if obj is
None, then we're a class, and classes are callable
<wink>)
"""
return self
item = getattr(obj, self.fk)
for attr in self.fkattrs:
item = getattr(item, attr)
return item
def ProfileInfo(field, short_description, boolean=False):
def getter(self):
try:
return getattr(self.profile, field)
except CofProfile.DoesNotExist:
return ""
getter.short_description = short_description
getter.boolean = boolean
return getter
User.profile_login_clipper = FkeyLookup("profile__login_clipper", "Login clipper")
User.profile_phone = ProfileInfo("phone", "Téléphone")
User.profile_occupation = ProfileInfo("occupation", "Occupation")
User.profile_departement = ProfileInfo("departement", "Departement")
User.profile_mailing_cof = ProfileInfo("mailing_cof", "ML COF", True)
User.profile_mailing_bda = ProfileInfo("mailing_bda", "ML BdA", True)
User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", "ML BdA-R", True)
class UserProfileAdmin(UserAdmin):
def is_buro(self, obj):
try:
return obj.profile.is_buro
except CofProfile.DoesNotExist:
return False
is_buro.short_description = "Membre du Buro"
is_buro.boolean = True
def is_cof(self, obj):
try:
return obj.profile.is_cof
except CofProfile.DoesNotExist:
return False
is_cof.short_description = "Membre du COF"
is_cof.boolean = True
list_display = UserAdmin.list_display + (
"profile_login_clipper",
"profile_phone",
"profile_occupation",
"profile_mailing_cof",
"profile_mailing_bda",
"profile_mailing_bda_revente",
"is_cof",
"is_buro",
)
list_display_links = ("username", "email", "first_name", "last_name")
list_filter = UserAdmin.list_filter + (
"profile__is_cof",
"profile__is_buro",
"profile__mailing_cof",
"profile__mailing_bda",
)
search_fields = UserAdmin.search_fields + ("profile__phone",)
inlines = [CofProfileInline]
staff_fieldsets = [
(None, {"fields": ["username", "password"]}),
(_("Personal info"), {"fields": ["first_name", "last_name", "email"]}),
]
def get_fieldsets(self, request, user=None):
if not request.user.is_superuser:
return self.staff_fieldsets
return super().get_fieldsets(request, user)
def save_model(self, request, user, form, change):
cof_group, created = Group.objects.get_or_create(name="COF")
if created:
# Si le groupe COF n'était pas déjà dans la bdd
# On lui assigne les bonnes permissions
perms = Permission.objects.filter(
Q(content_type__app_label="gestioncof")
| Q(content_type__app_label="bda")
| (Q(content_type__app_label="auth") & Q(content_type__model="user"))
)
cof_group.permissions = perms
# On y associe les membres du Burô
cof_group.user_set = User.objects.filter(profile__is_buro=True)
# Sauvegarde
cof_group.save()
# le Burô est staff et appartient au groupe COF
if user.profile.is_buro:
user.is_staff = True
user.groups.add(cof_group)
else:
user.is_staff = False
user.groups.remove(cof_group)
user.save()
# FIXME: This is absolutely horrible.
def user_str(self):
if self.first_name and self.last_name:
return "{} ({})".format(self.get_full_name(), self.username)
else:
return self.username
User.__str__ = user_str
class EventRegistrationAdminForm(forms.ModelForm):
class Meta:
widgets = {"user": ModelSelect2(url="cof-user-autocomplete")}
class EventRegistrationAdmin(admin.ModelAdmin):
form = EventRegistrationAdminForm
list_display = ("__str__", "event", "user", "paid")
list_filter = ("paid",)
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"event__title",
)
class PetitCoursAbilityAdmin(admin.ModelAdmin):
list_display = ("user", "matiere", "niveau", "agrege")
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"matiere__name",
"niveau",
)
list_filter = ("matiere", "niveau", "agrege")
class PetitCoursAttributionAdmin(admin.ModelAdmin):
list_display = ("user", "demande", "matiere", "rank")
search_fields = ("user__username", "matiere__name")
class PetitCoursAttributionCounterAdmin(admin.ModelAdmin):
list_display = ("user", "matiere", "count")
list_filter = ("matiere",)
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"user__email",
"matiere__name",
)
actions = ["reset"]
actions_on_bottom = True
def reset(self, request, queryset):
queryset.update(count=0)
reset.short_description = "Remise à zéro du compteur"
class PetitCoursDemandeAdmin(admin.ModelAdmin):
list_display = (
"name",
"email",
"agrege_requis",
"niveau",
"created",
"traitee",
"processed",
)
list_filter = ("traitee", "niveau")
search_fields = ("name", "email", "phone", "lieu", "remarques")
class ClubAdminForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
respos = cleaned_data.get("respos")
members = cleaned_data.get("membres")
for respo in respos.all():
if respo not in members:
raise forms.ValidationError(
"Erreur : le respo %s n'est pas membre du club."
% respo.get_full_name()
)
return cleaned_data
class ClubAdmin(admin.ModelAdmin):
list_display = ["name"]
form = ClubAdminForm
admin.site.register(Survey, SurveyAdmin)
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile)
admin.site.register(Club, ClubAdmin)
admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
admin.site.register(PetitCoursAttributionCounter, PetitCoursAttributionCounterAdmin)
admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin)
admin.site.register(EventRegistration, EventRegistrationAdmin)