More dynamic forms and templates
This commit is contained in:
parent
7666bd3973
commit
e5549cfc24
12 changed files with 159 additions and 109 deletions
|
|
@ -1,6 +1,19 @@
|
|||
from django.contrib import admin
|
||||
from django.http import HttpResponse
|
||||
from django.contrib import admin, messages
|
||||
from django.utils.translation import ngettext
|
||||
|
||||
from ..models import Activity, UserEntry
|
||||
from ..utils import get_random_string
|
||||
from ..models import Activity, Award, Quota, UserEntry
|
||||
|
||||
|
||||
class QuotaInline(admin.TabularInline):
|
||||
model = Quota
|
||||
extra = 0
|
||||
|
||||
|
||||
class AwardsInline(admin.TabularInline):
|
||||
model = Award
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(Activity)
|
||||
|
|
@ -8,32 +21,48 @@ class ActivityAdmin(admin.ModelAdmin):
|
|||
fieldsets = [
|
||||
(None, {'fields': ['name']}),
|
||||
('Activity Information', {
|
||||
'fields': ['quota', 'time']}),
|
||||
'fields': ['default_quota', 'time', 'password']}),
|
||||
]
|
||||
|
||||
list_display = ['name', 'time', 'quota', 'teacher', 'password', 'hawk_signups',
|
||||
'great_grey_signups', 'snowy_signups', 'eagle_signups']
|
||||
list_display = ['name', 'time', 'teacher', 'password',
|
||||
'default_quota', 'hawk_signups', 'great_grey_signups', 'snowy_signups', 'eagle_signups']
|
||||
|
||||
@admin.display(description='Hawk Signups')
|
||||
inlines = [QuotaInline, AwardsInline]
|
||||
|
||||
actions = ['generate_passwords']
|
||||
|
||||
@admin.display(description='Hawk')
|
||||
def hawk_signups(self, obj):
|
||||
if (obj.time == None):
|
||||
return "- | -"
|
||||
return str(UserEntry.hawk.filterActivity1(obj).count()) + " | " + str((UserEntry.hawk.filterActivity2(obj).count(), "-")[obj.time == 60])
|
||||
return str(UserEntry.objects.filter(house__name='Hawk', activity1=obj).count()) + ' | ' + str((UserEntry.objects.filter(house__name='Hawk', activity2=obj).count(), '-')[obj.time == 60])
|
||||
|
||||
@admin.display(description='Great Grey Signups')
|
||||
@admin.display(description='Great Grey')
|
||||
def great_grey_signups(self, obj):
|
||||
if (obj.time == None):
|
||||
return "- | -"
|
||||
return str(UserEntry.great_grey.filterActivity1(obj).count()) + " | " + str((UserEntry.great_grey.filterActivity2(obj).count(), "-")[obj.time == 60])
|
||||
return str(UserEntry.objects.filter(house__name='Great Grey', activity1=obj).count()) + ' | ' + str((UserEntry.objects.filter(house__name='Great Grey', activity2=obj).count(), '-')[obj.time == 60])
|
||||
|
||||
@admin.display(description='Snowy Signups')
|
||||
@admin.display(description='Snowy')
|
||||
def snowy_signups(self, obj):
|
||||
if (obj.time == None):
|
||||
return "- | -"
|
||||
return str(UserEntry.snowy.filterActivity1(obj).count()) + " | " + str((UserEntry.snowy.filterActivity2(obj).count(), "-")[obj.time == 60])
|
||||
return str(UserEntry.objects.filter(house__name='Snowy', activity1=obj).count()) + ' | ' + str((UserEntry.objects.filter(house__name='Snowy', activity2=obj).count(), '-')[obj.time == 60])
|
||||
|
||||
@admin.display(description='Eagle Signups')
|
||||
@admin.display(description='Eagle')
|
||||
def eagle_signups(self, obj):
|
||||
if (obj.time == None):
|
||||
return "- | -"
|
||||
return str(UserEntry.eagle.filterActivity1(obj).count()) + " | " + str((UserEntry.eagle.filterActivity2(obj).count(), "-")[obj.time == 60])
|
||||
return str(UserEntry.objects.filter(house__name='Eagle', activity1=obj).count()) + ' | ' + str((UserEntry.objects.filter(house__name='Eagle', activity2=obj).count(), '-')[obj.time == 60])
|
||||
|
||||
@admin.action(description='Generate passwords for selected activities')
|
||||
def generate_passwords(self, request, queryset):
|
||||
for item in queryset:
|
||||
item.password = get_random_string(8)
|
||||
item.save()
|
||||
|
||||
self.message_user(request, ngettext(
|
||||
'Generated password for %d activity.',
|
||||
'Generated passwords for %d activities.',
|
||||
queryset.count(),
|
||||
) % queryset.count(), messages.SUCCESS)
|
||||
|
|
|
|||
|
|
@ -1,31 +1,17 @@
|
|||
from django.contrib import admin, messages
|
||||
from django.utils.translation import ngettext
|
||||
from django.db.models import Sum, F
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.contrib import admin
|
||||
|
||||
from ..models import House, PointsEntry
|
||||
from ..models import House
|
||||
|
||||
|
||||
@admin.register(House)
|
||||
class HouseAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'points', 'current_points', 'total_points')
|
||||
list_display = ('name', 'points')
|
||||
actions = ['finalize_points']
|
||||
|
||||
@admin.display(description='Current Housewars Points')
|
||||
def current_points(self, obj):
|
||||
return obj.current_points
|
||||
# @admin.display(description='Current Housewars Points')
|
||||
# def current_points(self, obj):
|
||||
# return obj.current_points
|
||||
|
||||
@admin.display(description='Total Points')
|
||||
def total_points(self, obj):
|
||||
return obj.total_points
|
||||
|
||||
@admin.action(description='Finalize selected house points')
|
||||
def finalize_points(self, request, queryset):
|
||||
updated = queryset.update(
|
||||
points=queryset.aggregate(points__sum=Coalesce(Sum('pointsentry__points'), 0)).get('points__sum') + F('points'))
|
||||
PointsEntry.objects.filter(pk__in=queryset).delete()
|
||||
self.message_user(request, ngettext(
|
||||
'Finalized house points for %d house.',
|
||||
'Finalized house points for %d houses.',
|
||||
updated,
|
||||
) % updated, messages.SUCCESS)
|
||||
# @admin.display(description='Total Points')
|
||||
# def total_points(self, obj):
|
||||
# return obj.total_points
|
||||
|
|
|
|||
|
|
@ -1,8 +1,32 @@
|
|||
from django.contrib import admin
|
||||
from django.db.models import F
|
||||
|
||||
from ..models import PointsEntry
|
||||
|
||||
|
||||
class ValidatedPasswordFilter(admin.SimpleListFilter):
|
||||
title = 'valid password'
|
||||
|
||||
parameter_name = 'valid'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return (('True', True), ('False', False))
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if (self.value() == 'True'):
|
||||
return queryset.filter(password=F('activity__password'))
|
||||
elif (self.value() == 'False'):
|
||||
return queryset.exclude(password=F('activity__password'))
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
||||
@admin.register(PointsEntry)
|
||||
class PointsEntryAdmin(admin.ModelAdmin):
|
||||
list_display = ('activity', 'name', 'house', 'points')
|
||||
list_display = ('activity', 'award', 'house', 'validated')
|
||||
|
||||
list_filter = ('activity', ValidatedPasswordFilter)
|
||||
|
||||
@admin.display(description='Validated', boolean=True)
|
||||
def validated(self, obj):
|
||||
return obj.password == obj.activity.password
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
from django.contrib import admin
|
||||
from django.db.models import F, Value
|
||||
from django.db.models.functions import Concat
|
||||
from django.http import HttpResponse
|
||||
|
||||
import csv
|
||||
|
||||
from ..models import UserEntry
|
||||
|
||||
|
|
@ -8,17 +11,9 @@ from ..models import UserEntry
|
|||
class MentorListFilter(admin.SimpleListFilter):
|
||||
title = 'mentor teacher'
|
||||
|
||||
# Parameter for the filter that will be used in the URL query.
|
||||
parameter_name = 'mentor'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
"""
|
||||
Returns a list of tuples. The first element in each
|
||||
tuple is the coded value for the option that will
|
||||
appear in the URL query. The second element is the
|
||||
human-readable name for the option that will appear
|
||||
in the right sidebar.
|
||||
"""
|
||||
qs = model_admin.get_queryset(request).annotate(full_name=Concat(F(
|
||||
'house__teacher__first_name'), Value(' '), F('house__teacher__last_name'))).values_list('house__teacher', 'full_name').distinct()
|
||||
|
||||
|
|
@ -31,7 +26,7 @@ class MentorListFilter(admin.SimpleListFilter):
|
|||
return queryset.filter(house__teacher=self.value(), grade=F('house__teacher__grade'))
|
||||
|
||||
|
||||
@ admin.register(UserEntry)
|
||||
@admin.register(UserEntry)
|
||||
class UserEntryAdmin(admin.ModelAdmin):
|
||||
fieldsets = [
|
||||
('Personal Information', {'fields': [
|
||||
|
|
@ -46,20 +41,49 @@ class UserEntryAdmin(admin.ModelAdmin):
|
|||
list_filter = ['house', 'grade', MentorListFilter, 'activity1',
|
||||
'activity1__teacher', 'activity2', 'activity2__teacher']
|
||||
|
||||
@ admin.display(description='Activity 1 Teacher')
|
||||
actions = ['export_to_csv']
|
||||
|
||||
@admin.display(description='Activity 1 Teacher')
|
||||
def activity1_teacher(self, obj):
|
||||
if (obj.activity1 == None):
|
||||
return None
|
||||
|
||||
return obj.activity1.teacher
|
||||
|
||||
@ admin.display(description='Mentor Teacher')
|
||||
@admin.display(description='Mentor Teacher')
|
||||
def mentor_teacher(self, obj):
|
||||
return obj.mentor
|
||||
|
||||
@ admin.display(description='Activity 2 Teacher')
|
||||
@admin.display(description='Activity 2 Teacher')
|
||||
def activity2_teacher(self, obj):
|
||||
if (obj.activity2 == None):
|
||||
return None
|
||||
|
||||
return obj.activity2.teacher
|
||||
|
||||
@admin.action(description='Export selected to csv')
|
||||
def export_to_csv(self, request, queryset):
|
||||
field_names = [field.name for field in queryset.model._meta.fields]
|
||||
|
||||
response = HttpResponse(content_type='text/csv', headers={
|
||||
'Content-Disposition': 'attachment; filename="export.csv"'},)
|
||||
|
||||
writer = csv.writer(response, delimiter=";")
|
||||
# Write a first row with header information
|
||||
writer.writerow(field_names)
|
||||
# Write data rows
|
||||
for row in queryset:
|
||||
values = []
|
||||
for field in field_names:
|
||||
value = getattr(row, field)
|
||||
if callable(value):
|
||||
try:
|
||||
value = value() or ''
|
||||
except:
|
||||
value = 'Error retrieving value'
|
||||
if value is None:
|
||||
value = ''
|
||||
values.append(value)
|
||||
writer.writerow(values)
|
||||
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from .Activity import ActivityAdmin
|
||||
from .House import HouseAdmin
|
||||
from .Teacher import TeacherAdmin
|
||||
from .UserEntry import UserEntryAdmin
|
||||
from .PointsEntry import PointsEntryAdmin
|
||||
from .House import House
|
||||
from .PointsEntry import PointsEntry
|
||||
from .Teacher import Teacher
|
||||
from .UserEntry import UserEntry
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.forms import (CharField, ChoiceField, EmailField, EmailInput, Form,
|
||||
ModelChoiceField, ModelForm, NumberInput, Select,
|
||||
Textarea, TextInput, ValidationError)
|
||||
ModelChoiceField, ModelForm, Select,
|
||||
TextInput, ValidationError)
|
||||
from smart_selects.form_fields import ChainedModelChoiceField
|
||||
|
||||
from .models import Activity, House, PointsEntry, UserEntry
|
||||
|
||||
|
|
@ -32,6 +33,11 @@ class UserEntryForm(Form):
|
|||
'placeholder': 'House'
|
||||
}))
|
||||
|
||||
def clean(self):
|
||||
email = self.cleaned_data['email']
|
||||
if UserEntry.objects.filter(email=email).exists():
|
||||
raise ValidationError("A signup with this email already exists")
|
||||
|
||||
|
||||
class ActivityForm(Form):
|
||||
activity1 = ModelChoiceField(Activity.objects.all(), widget=Select(attrs={
|
||||
|
|
@ -69,30 +75,20 @@ class PointsEntryForm(ModelForm):
|
|||
model = PointsEntry
|
||||
fields = '__all__'
|
||||
widgets = {
|
||||
'name': TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'id': 'first-name',
|
||||
'placeholder': 'Full Name'
|
||||
}),
|
||||
'activity': Select(attrs={
|
||||
'class': "form-select",
|
||||
'id': 'house',
|
||||
'placeholder': 'Activity'
|
||||
}),
|
||||
'house': Select(attrs={
|
||||
'class': "form-select",
|
||||
'id': 'house',
|
||||
'placeholder': 'House'
|
||||
}),
|
||||
'points': NumberInput(attrs={
|
||||
'class': "form-control",
|
||||
'id': 'last-name',
|
||||
'placeholder': 'Number of Points'
|
||||
}),
|
||||
'comment': Textarea(attrs={
|
||||
'password': TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'id': 'first-name',
|
||||
'placeholder': 'Comments',
|
||||
'style': 'height:10vh;'
|
||||
'placeholder': 'Password',
|
||||
'type': 'password'
|
||||
})
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PointsEntryForm, self).__init__(*args, **kwargs)
|
||||
self.fields['activity'].widget.attrs.update({'class': 'form-select'})
|
||||
self.fields['award'].widget.attrs.update({'class': 'form-select'})
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
from django.db.models import Manager
|
||||
|
||||
|
||||
class EntryManager(Manager):
|
||||
def filterActivity1(self, act):
|
||||
return self.filter(activity1=act)
|
||||
|
||||
def filterActivity2(self, act):
|
||||
return self.filter(activity2=act)
|
||||
|
||||
|
||||
class HawkEntryManager(EntryManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(house__name='Hawk')
|
||||
|
||||
|
||||
class EagleEntryManager(EntryManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(house__name='Eagle')
|
||||
|
||||
|
||||
class GreatGreyEntryManager(EntryManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(house__name='Great Grey')
|
||||
|
||||
|
||||
class SnowyEntryManager(EntryManager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(house__name='Snowy')
|
||||
1
housewars/utils/__init__.py
Normal file
1
housewars/utils/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .get_random_string import get_random_string
|
||||
9
housewars/utils/get_random_string.py
Normal file
9
housewars/utils/get_random_string.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import random
|
||||
import string
|
||||
|
||||
|
||||
def get_random_string(length):
|
||||
# choose from all lowercase letter
|
||||
letters = string.ascii_lowercase
|
||||
result_str = ''.join(random.choice(letters) for i in range(length))
|
||||
return result_str
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
from django.contrib import messages
|
||||
from django.db.models import Count, F, Q
|
||||
from django.db.models import Count, F, Q, Subquery, OuterRef
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.views.generic import CreateView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
|
||||
from .forms import ActivityForm, PointsEntryForm, UserEntryForm
|
||||
from .models import Activity, PointsEntry, UserEntry
|
||||
from .models import Activity, Quota, PointsEntry, UserEntry
|
||||
|
||||
FORMS = [("user", UserEntryForm),
|
||||
("activity", ActivityForm)]
|
||||
|
|
@ -37,10 +38,14 @@ class EntryCreateView(SessionWizardView):
|
|||
if step == 'activity':
|
||||
cd = self.storage.get_step_data('user')
|
||||
|
||||
filtered1 = Activity.objects.filter(time__isnull=False).annotate(count=Count(
|
||||
'activity1', filter=Q(activity1__house=cd.get('user-house')))).filter(quota__gt=F('count'))
|
||||
filtered2 = Activity.objects.filter(time__isnull=False).annotate(count=Count(
|
||||
'activity2', filter=Q(activity2__house=cd.get('user-house')))).filter(quota__gt=F('count'), time=30)
|
||||
quota = Quota.objects.filter(
|
||||
house=cd.get('user-house'), activity=OuterRef('id')).values('quota')
|
||||
|
||||
filtered1 = Activity.objects.filter(time__isnull=False).annotate(final_quota=Coalesce(Subquery(quota[:1]), F('default_quota'))).annotate(count=Count(
|
||||
'activity1', filter=Q(activity1__house=cd.get('user-house')))).filter(final_quota__gt=F('count'))
|
||||
|
||||
filtered2 = Activity.objects.filter(time__isnull=False).annotate(final_quota=Coalesce(Subquery(quota[:1]), F('default_quota'))).annotate(count=Count(
|
||||
'activity1', filter=Q(activity1__house=cd.get('user-house')))).filter(final_quota__gt=F('count'), time=30)
|
||||
|
||||
form.fields['activity1'].queryset = filtered1
|
||||
form.fields['activity2'].queryset = filtered2
|
||||
|
|
@ -48,7 +53,7 @@ class EntryCreateView(SessionWizardView):
|
|||
return form
|
||||
|
||||
def done(self, form_list, **kwargs):
|
||||
UserEntry.objects.create(self.get_all_cleaned_data())
|
||||
UserEntry.objects.create(**self.get_all_cleaned_data())
|
||||
|
||||
messages.success(self.request, 'Your entry has been submitted')
|
||||
return redirect('housewars:signup')
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ asgiref==3.5.2
|
|||
autopep8==1.6.0
|
||||
Django==4.0.4
|
||||
django-formtools==2.3
|
||||
django-smart-selects==1.6.0
|
||||
gunicorn==20.1.0
|
||||
mysqlclient==2.1.1
|
||||
pycodestyle==2.8.0
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href='{% static "css/main.css" %}' />
|
||||
<link rel="icon" type="image/x-icon" href='{% static "favicon.ico" %}'>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
|
||||
<script src=" {% static 'smart-selects/admin/js/chainedfk.js' %}"></script>
|
||||
<script src="{% static 'smart-selects/admin/js/chainedm2m.js' %}"></script>
|
||||
|
||||
{% block title %}
|
||||
<title>House Wars</title>
|
||||
|
|
|
|||
Reference in a new issue