# -*- coding: utf-8 -*- from django.contrib import admin from django import forms from django.forms import widgets from reversion.admin import VersionAdmin from solo.admin import SingletonModelAdmin from ckeditor.widgets import CKEditorWidget from django.db.models import Count from django.db import models from django.utils.safestring import mark_safe from django.core.urlresolvers import reverse from django.contrib.auth.models import User from seminar.models import Skola, Resitel, Rocnik, Cislo, Problem, Reseni, PrilohaReseni, Nastaveni, Soustredeni, Soustredeni_Ucastnici, Soustredeni_Organizatori, Novinky, Organizator, Prispevek, Pohadka, Konfera from autocomplete_light import shortcuts as autocomplete_light class UserModelChoiceField(forms.ModelChoiceField): u"""Vlastní ModelChoiceField pro uživatele. Zobrazí kromě loginu i jméno. """ def label_from_instance(self, obj): return u"{} ({})".format(obj.get_full_name(), obj.username) def get_form_predvypln_autora(self, request, obj=None, *args, **kwargs): u"""get_form fce pro Adminy. Předvyplňí přihlášeného uživatele jako autora. """ form = super(self.__class__, self).get_form(request, *args, **kwargs) form.base_fields['autor'].initial = request.user.id return form def make_set_action(atribut, hodnota, nazev): u""" Pomocnik pro rychle vytvareni hromadnych admin akci ktere jen nastavuji atribut (dany jako string) na danou hodnotu. nazev je krátký popis akce pro admin rozhraní.""" def action_f(modeladmin, request, queryset): for obj in queryset: assert atribut in obj.__dict__ obj.__setattr__(atribut, hodnota) obj.save() action_f.short_description = nazev # Trik: je potřeba, aby se funkce různě (ale libovolně) jmenovaly, # jinak je Django v seznamu akcí splácne do jedné action_f.__name__ = 'action_f_%d_%d_%d' % (id(atribut), id(hodnota), id(nazev), ) return action_f ### Globální nastavení admin.site.register(Nastaveni, SingletonModelAdmin) ### UTILS (pro verbose_name a help_text) def field_labels(model, fieldname): f = [i for i in model._meta.fields if i.name == fieldname][0] return {'label': f.verbose_name.capitalize(), 'help_text': f.help_text, } def create_modeladmin(modeladmin, model, name = None, verbose_name = None, verbose_name_plural = None): class Meta: proxy = True app_label = model._meta.app_label Meta.verbose_name = verbose_name Meta.verbose_name_plural = verbose_name_plural attrs = {'__module__': '', 'Meta': Meta} newmodel = type(name, (model,), attrs) admin.site.register(newmodel, modeladmin) return modeladmin ### INLINES class ResitelInline(admin.TabularInline): model = Resitel fields = ['jmeno', 'prijmeni', 'skola', 'mesto', 'rok_maturity', ] readonly_fields = ['jmeno', 'prijmeni', 'skola', 'mesto', 'rok_maturity', ] extra = 0 view_on_site = False def has_add_permission(self, req): return False class CisloInline(admin.TabularInline): model = Cislo fields = ['cislo', 'datum_vydani', 'datum_deadline', 'datum_deadline_soustredeni', 'verejne_db', 'poznamka'] readonly_fields = ['cislo'] extra = 0 formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } view_on_site = Cislo.verejne_url def has_add_permission(self, req): return False class PrilohaReseniInline(admin.StackedInline): model = PrilohaReseni fields = ['timestamp', 'soubor', 'poznamka'] readonly_fields = ['timestamp'] formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } view_on_site = False extra = 0 class ProblemInline(admin.TabularInline): model = Problem fk_name = 'cislo_zadani' fields = ['kod', 'typ', 'nazev', 'body', 'opravovatel', 'stav'] formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } view_on_site = Problem.verejne_url extra = 0 class ReseniKProblemuInline(admin.TabularInline): form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['resitel'], fields=['resitel']) model = Reseni fields = ['resitel', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] readonly_fields = ['timestamp'] extra = 0 formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } view_on_site = False def get_queryset(self, request): qs = super(ReseniKProblemuInline, self).get_queryset(request) return qs.select_related('problem', 'cislo_body', 'resitel') # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) def formfield_for_dbfield(self, db_field, **kwargs): formfield = super(ReseniKProblemuInline, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name == 'cislo_body': # dirty trick so queryset is evaluated and cached in .choices formfield.choices = formfield.choices return formfield class ReseniKResiteliInline(admin.TabularInline): model = Reseni fields = ['problem', 'forma', 'body', 'cislo_body', 'timestamp', 'poznamka'] readonly_fields = ['timestamp', 'problem'] extra = 0 formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } view_on_site = False def has_add_permission(self, req): return False def get_queryset(self, request): qs = super(ReseniKResiteliInline, self).get_queryset(request) return qs.select_related('problem', 'cislo_body', 'resitel') # Potenciální DB HOG (cislo_body se dotazovalo na cisla pri kazdem zobrazeni jejich selectu ...) def formfield_for_dbfield(self, db_field, **kwargs): formfield = super(ReseniKResiteliInline, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name == 'cislo_body': # dirty trick so queryset is evaluated and cached in .choices formfield.choices = formfield.choices return formfield class Soustredeni_UcastniciInline(admin.TabularInline): form = autocomplete_light.modelform_factory(Soustredeni_Ucastnici, autocomplete_fields=['resitel'], fields=['resitel']) model = Soustredeni_Ucastnici fields = ['resitel', 'poznamka', ] extra = 0 formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } def get_queryset(self, request): qs = super(Soustredeni_UcastniciInline, self).get_queryset(request) return qs.select_related('resitel', 'soustredeni') class Soustredeni_OrganizatoriInline(admin.TabularInline): form = autocomplete_light.modelform_factory(Soustredeni_Organizatori, autocomplete_fields=['organizator'], fields=['organizator'],) model = Soustredeni_Organizatori fields = ['organizator', 'poznamka', ] extra = 0 formfield_overrides = { models.TextField: {'widget': forms.TextInput}, } def get_queryset(self, request): qs = super(Soustredeni_OrganizatoriInline, self).get_queryset(request) return qs.select_related('organizator', 'soustredeni') ### Resitel class ResitelAdmin(VersionAdmin): form = autocomplete_light.modelform_factory(Resitel, autocomplete_fields=['skola'], fields=['skola']) fieldsets = [ (None, {'fields': ['jmeno', 'prijmeni', 'user']}), (u'Škola', {'fields': ['skola', 'rok_maturity']}), (u'Seminář', {'fields': ['datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_prihlaseni', 'zasilat']}), (u'Osobní údaje', {'fields': ['pohlavi_muz', 'datum_narozeni', 'email', 'telefon']}), (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), ] list_display = ['jmeno', 'prijmeni', 'user', 'pohlavi_muz', 'skola', 'rok_maturity', 'pocet_reseni'] list_filter = ['pohlavi_muz', 'rok_maturity', 'zasilat'] search_fields = ['jmeno', 'prijmeni', 'ulice', 'mesto', 'email'] inlines = [ReseniKResiteliInline] view_on_site = False def get_queryset(self, request): qs = super(ResitelAdmin, self).get_queryset(request) return qs.select_related('skola', 'user').annotate(pocet_reseni=Count('reseni')) def pocet_reseni(self, obj): return obj.pocet_reseni admin.site.register(Resitel, ResitelAdmin) ### Skola class SkolaAdmin(VersionAdmin): fieldsets = [ (None, {'fields': ['nazev', 'kratky_nazev', 'je_zs', 'je_ss']}), (u'Interní ID', {'fields': ['aesop_id', 'izo'], 'classes': ['collapse']}), (u'Adresa', {'fields': ['ulice', 'mesto', 'psc', 'stat']}), (None, {'fields': ['poznamka']}), ] list_display = ['nazev', 'aesop_id', 'mesto', 'ulice', 'stat', 'je_zs', 'je_ss'] list_filter = ['stat', 'je_zs', 'je_ss'] search_fields = ['nazev', 'mesto', 'ulice'] inlines = [ResitelInline] view_on_site = False admin.site.register(Skola, SkolaAdmin) ### Cislo class CisloAdmin(VersionAdmin): fieldsets = [ (None, { 'fields': [ 'cislo', 'rocnik', 'verejne_db', 'verejna_vysledkovka', 'faze', 'poznamka', 'pdf' ] }), (u'Data', {'fields': ['datum_vydani', 'datum_deadline', 'datum_deadline_soustredeni']}), ] list_display = [ 'kod', 'rocnik', 'cislo', 'datum_vydani', 'datum_deadline', 'verejne', 'verejna_vysledkovka' ] list_filter = ['rocnik'] view_on_site = Cislo.verejne_url actions = [ make_set_action('verejne_db', True, u'Zveřejnit číslo'), make_set_action('verejne_db', False, u'Skrýt (zneveřejnit) číslo'), make_set_action('verejna_vysledkovka', True, u'Zveřejnit výsledkovku'), make_set_action('verejna_vysledkovka', False, u'Skrýt (zneveřejnit) výsledkovku'), ] inlines = [ProblemInline] def get_queryset(self, request): qs = super(CisloAdmin, self).get_queryset(request) return qs.select_related('rocnik') admin.site.register(Cislo, CisloAdmin) ### Rocnik class RocnikAdmin(VersionAdmin): fieldsets = [ (None, {'fields': ['rocnik', 'prvni_rok', 'exportovat']}), ] list_display = ['rocnik', 'prvni_rok', 'exportovat', 'verejne'] inlines = [CisloInline] view_on_site = Rocnik.verejne_url actions = [ make_set_action('exportovat', True, u'Nastavit pro AESOP export'), make_set_action('exportovat', False, u'Skrýt pro AESOP export'), ] admin.site.register(Rocnik, RocnikAdmin) ### PrilohaReseni # NOTE: Nemá pravděpodobně smysl používat # class PrilohaReseniAdmin(reversion.VersionAdmin): # readonly_fields = ['timestamp', 'reseni'] # fieldsets = [ # (None, {'fields': ['reseni', 'soubor', 'timestamp']}), # (u'Poznámky', {'fields': ['poznamka']}), # ] # list_display = ['reseni', 'soubor', 'timestamp'] # list_filter = ['reseni', 'timestamp'] # search_fields = [] # # admin.site.register(PrilohaReseni, PrilohaReseniAdmin) ### Reseni class ReseniAdmin(VersionAdmin): form = autocomplete_light.modelform_factory(Reseni, autocomplete_fields=['problem', 'resitel'], fields=['problem', 'resitel']) fieldsets = [ (None, {'fields': ['problem', 'resitel', 'forma', 'body', 'cislo_body', 'timestamp']}), (u'Poznámky', {'fields': ['poznamka']}), ] readonly_fields = ['timestamp'] list_display = ['problem', 'resitel', 'forma', 'body', 'timestamp', 'cislo_body'] list_filter = ['body', 'timestamp', 'forma'] search_fields = [] inlines = [PrilohaReseniInline] view_on_site = False def get_queryset(self, request): qs = super(ReseniAdmin, self).get_queryset(request) return qs.select_related('resitel', 'problem', 'cislo_body') admin.site.register(Reseni, ReseniAdmin) ### Pohádka class PohadkaAdminForm(forms.ModelForm): class Meta: model = Pohadka exclude = [] autor = UserModelChoiceField(User.objects.filter(is_staff=True)) uloha = forms.ModelChoiceField( Problem.objects.filter(typ=Problem.TYP_ULOHA) ) def __init__(self, *args, **kwargs): super(PohadkaAdminForm, self).__init__(*args, **kwargs) instance = getattr(self, 'instance', None) # viz ProblemAdminForm.__init__ if instance and instance.pk: if instance.uloha and instance.uloha.cislo_zadani: if instance.uloha.cislo_zadani.faze != 'admin': self.fields['text'].widget.attrs['readonly'] = True class PohadkaAdmin(VersionAdmin): form = PohadkaAdminForm view_on_site = False def get_kod_ulohy(self, obj): return obj.uloha.kod_v_rocniku() get_kod_ulohy.short_description = u'Kód úlohy' def get_rocnik(self, obj): return obj.uloha.cislo_zadani.rocnik.rocnik get_rocnik.short_description = u'Ročník' list_display = [ '__str__', 'get_rocnik', 'get_kod_ulohy', 'uloha', 'autor', 'timestamp' ] get_form = get_form_predvypln_autora class PohadkaKProblemuInline(admin.TabularInline): form = PohadkaAdminForm model = Pohadka exclude = [] extra = 0 admin.site.register(Pohadka, PohadkaAdmin) ### Problem from autocomplete_light.contrib.taggit_field import TaggitField, TaggitWidget class ProblemAdminForm(forms.ModelForm): text_zadani = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_zadani')) text_reseni = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_reseni')) text_org = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Problem, 'text_org')) zamereni = TaggitField(widget=TaggitWidget('TagAutocomplete'), required=False) autor = UserModelChoiceField(User.objects.filter(is_staff=True)) opravovatel = UserModelChoiceField(User.objects.filter(is_staff=True), required=False) class Meta: model = Problem exclude = [] def __init__(self, *args, **kwargs): super(ProblemAdminForm, self).__init__(*args, **kwargs) instance = getattr(self, 'instance', None) # Nedovol měnit název a zadání resp. řešení, pokud je cislo_zadani # resp. cislo_reseni mimo fázi admin. # # Nastavení readonly fields sice vypadá lépe (nevygeneruje input tag), # ale při ukládání změny vypíše admin nespecifikovanou chybu, která je # způsobena zřejmě tím, že se neodešle žádná hodnota pro povinné pole # nazev. Navíc by se smazalo nepovinné pole zadání. # # Toto řešení je z http://stackoverflow.com/a/325038/4786205. # # TODO Django 1.9: použít field s atributem disabled? if instance and instance.pk: if instance.cislo_zadani and instance.cislo_zadani.faze != 'admin': # CKEditor neumí readonly ... self.fields['text_zadani'] = forms.CharField( widget=forms.Textarea, required=False ) for f in ['nazev', 'text_zadani', 'body']: self.fields[f].widget.attrs['readonly'] = True if instance.cislo_reseni and instance.cislo_reseni.faze != 'admin': self.fields['text_reseni'] = forms.CharField( widget=forms.Textarea, required=False ) self.fields['text_reseni'].widget.attrs['readonly'] = True class ProblemAdmin(VersionAdmin): form = ProblemAdminForm fieldsets = [ (None, {'fields': ['nazev', 'typ', 'stav', 'autor', 'zamereni', 'body', 'timestamp', 'import_dakos_id']}), (u'Vydání', {'fields': ['cislo_zadani', 'kod', 'cislo_reseni', 'opravovatel',]}), (None, {'fields': ['text_zadani', 'text_reseni', 'text_org',]}), ] list_select_related = True search_fields = ['nazev', 'text_zadani', 'text_reseni', 'text_org'] view_on_site = Problem.verejne_url ordering = ['-timestamp'] readonly_fields = ['timestamp', 'import_dakos_id'] def get_queryset(self, request): qs = super(ProblemAdmin, self).get_queryset(request) return qs.select_related('autor', 'opravovatel', 'cislo_zadani', 'cislo_reseni') def pocet_reseni(self, obj): return obj.pocet_reseni class ProblemNavrhAdmin(ProblemAdmin): list_display = ['nazev', 'typ', 'stav', 'autor', 'timestamp'] list_filter = ['typ', 'zamereni', 'timestamp', 'stav'] def get_queryset(self, request): qs = super(ProblemNavrhAdmin, self).get_queryset(request) return qs.filter(stav__in=[Problem.STAV_NAVRH, Problem.STAV_SMAZANY]) get_form = get_form_predvypln_autora create_modeladmin(ProblemNavrhAdmin, Problem, 'ProblemNavrh', verbose_name=u'Problém (návrh)', verbose_name_plural=u'Problémy (návrhy)') class ProblemZadanyAdmin(ProblemAdmin): list_display = [ 'nazev', 'typ', 'cislo_zadani_link', 'cislo_reseni_link', 'autor', 'opravovatel', 'kod', 'verejne' ] list_filter = [ 'typ', 'zamereni', 'cislo_zadani__cislo', 'cislo_zadani__rocnik' ] def cislo_zadani_link(self, obj): return mark_safe('{}'.format( reverse("admin:seminar_cislo_change", args=(obj.cislo_zadani.pk,)), obj.cislo_zadani )) cislo_zadani_link.short_description = u'Číslo zadání' # TODO pokud se budou odkazy v adminu více používat, možná by se hodilo je # nějak zjednodušit, např. tímto? # https://github.com/gitaarik/django-admin-relation-links def cislo_reseni_link(self, obj): return mark_safe('{}'.format( reverse("admin:seminar_cislo_change", args=(obj.cislo_reseni.pk,)), obj.cislo_reseni )) cislo_reseni_link.short_description = u'Číslo řešení' def get_inline_instances(self, request, obj=None): if obj and obj.typ == Problem.TYP_ULOHA: inlines = [ReseniKProblemuInline, PohadkaKProblemuInline] else: inlines = [ReseniKProblemuInline] return [inline(self.model, self.admin_site) for inline in inlines] def get_queryset(self, request): qs = super(ProblemZadanyAdmin, self).get_queryset(request) return qs.filter(stav=Problem.STAV_ZADANY) get_form = get_form_predvypln_autora create_modeladmin(ProblemZadanyAdmin, Problem, 'ProblemZadany', verbose_name=u'Problém (zadaný)', verbose_name_plural=u'Problémy (zadané)') #admin.site.register(Problem, ProblemAdmin) ### Prispevek (k tematkum) class PrispevekAdminForm(forms.ModelForm): text_org = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Prispevek, 'text_org')) text_resitel = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Prispevek, 'text_resitel')) class Meta: model = Prispevek exclude = [] class PrispevekAdmin(VersionAdmin): form = PrispevekAdminForm list_display = ['nazev', 'problem', 'reseni', 'zverejnit'] admin.site.register(Prispevek, PrispevekAdmin) ### Soustredeni class SoustredeniAdminForm(forms.ModelForm): text = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Soustredeni, 'text')) class Meta: model = Soustredeni exclude = [] class SoustredeniAdmin(VersionAdmin): form = SoustredeniAdminForm fieldsets = [ (None, {'fields': ['rocnik', 'misto', 'typ', 'verejne_db', 'exportovat', 'text']}), (u'Data', {'fields': ['datum_zacatku', 'datum_konce']}), ] list_display = ['rocnik', 'misto', 'datum_zacatku', 'typ', 'exportovat', 'verejne'] inlines = [Soustredeni_UcastniciInline, Soustredeni_OrganizatoriInline] list_filter = ['typ', 'rocnik'] view_on_site = Soustredeni.verejne_url actions = [ make_set_action('verejne_db', True, u'Zveřejnit soustředění'), make_set_action('verejne_db', False, u'Skrýt (zneveřejnit) soustředění'), make_set_action('exportovat', True, u'Nastavit pro AESOP export'), make_set_action('exportovat', False, u'Skrýt pro AESOP export'), ] admin.site.register(Soustredeni, SoustredeniAdmin) ### Konfery class KonferaAdminForm(forms.ModelForm): class Meta: model=Konfera exclude = [] class KonferaAdmin(VersionAdmin): form = KonferaAdminForm list_filter = ['soustredeni'] list_display = ['nazev','soustredeni','organizator','typ_prezentace'] # inlines = [Konfera_UcastniciInline] admin.site.register(Konfera,KonferaAdmin) ### Novinky class NovinkyAdminForm(forms.ModelForm): text = forms.CharField(widget=CKEditorWidget(), required=False, **field_labels(Novinky, 'text')) autor = UserModelChoiceField(User.objects.filter(is_staff=True)) class Meta: model = Novinky exclude = [] def zverejnit_novinky(modeladmin, request, queryset): ''' zverejni vybrane novinky ''' for novinka in queryset: novinka.zverejneno = True novinka.save() zverejnit_novinky.short_description = 'Zveřejnit vybané novinky' def zneverejnit_novinky(modeladmin, request, queryset): ''' zneverejni vybrane novinky''' for novinka in queryset: novinka.zverejneno = False novinka.save() zneverejnit_novinky.short_description = 'Zneveřejnit vybrané novinky' class NovinkyAdmin(VersionAdmin): form = NovinkyAdminForm list_display = ['datum', 'autor', 'text', 'zverejneno', 'obrazek'] actions = [zverejnit_novinky, zneverejnit_novinky] get_form = get_form_predvypln_autora admin.site.register(Novinky, NovinkyAdmin) ### Organizator def jmeno_organizatora(obj): ''' vraci jmeno organizatora ''' jmeno = obj.user.first_name if obj.prezdivka: jmeno = jmeno + ' "' + obj.prezdivka + '"' jmeno = jmeno + ' ' + obj.user.last_name if jmeno == ' ': # zobrazeni bezejmennych orgu return 'org' return jmeno jmeno_organizatora.short_description = 'Jméno organizátora' def je_organizator_aktivni(obj): ''' zjisti, zda-li je organizator aktivni ''' return obj.user.is_active je_organizator_aktivni.short_description = 'Aktivní' je_organizator_aktivni.boolean = True def zaktivovat_organizatory(modeladmin, request, queryset): ''' vybrane organizatory oznaci jako aktivni ''' for org in queryset: org.user.is_active = True org.user.save() zaktivovat_organizatory.short_description = 'Zaktivovat organizátory' def deaktivovat_organizatory(modeladmin, request, queryset): ''' deaktivuje vybrane organizatory ''' for org in queryset: org.user.is_active = False org.user.save() deaktivovat_organizatory.short_description = 'Deaktivovat organizátory' @admin.register(Organizator) class OrganizatorAdmin(VersionAdmin): list_filter = ['organizuje_do_roku'] list_display = [jmeno_organizatora, je_organizator_aktivni,] actions = [zaktivovat_organizatory, deaktivovat_organizatory,]