# -*- 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_uploader.widgets import CKEditorUploadingWidget 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, Pohadka, Konfera, Uloha 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=['resitele'], fields=['resitele']) model = Reseni fields = ['resitele', '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', 'resitele') # 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', 'resitele'], fields=['problem', 'resitele']) fieldsets = [ (None, {'fields': ['problem', 'resitele', 'forma', 'body', 'cislo_body', 'timestamp']}), (u'Poznámky', {'fields': ['poznamka']}), ] readonly_fields = ['timestamp'] list_display = ['problem', 'resitele', '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('resitele', '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( Uloha.objects.all() ) 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=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_zadani')) #text_reseni = forms.CharField(widget=CKEditorUploadingWidget(), required=False, **field_labels(Problem, 'text_reseni')) #text_org = forms.CharField(widget=CKEditorUploadingWidget(), 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 = ['nadproblem'] 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 #FIXME #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): if not obj.cislo_zadani: return "" 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): if not obj.cislo_reseni: return "" 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 #FIXME #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=CKEditorUploadingWidget(), required=False, # **field_labels(Prispevek, 'text_org')) # text_resitel = forms.CharField(widget=CKEditorUploadingWidget(), 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=CKEditorUploadingWidget(), 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=CKEditorUploadingWidget(), 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,]