diff --git a/teamsnap/forms.py b/teamsnap/forms.py new file mode 100644 index 0000000..f1fd691 --- /dev/null +++ b/teamsnap/forms.py @@ -0,0 +1,21 @@ +from django import forms +from .models import LineupEntry +from events.models import Event +from players.models import Player +from django.forms import modelformset_factory, inlineformset_factory, BaseModelFormSet,formset_factory +from crispy_forms.helper import FormHelper, Layout + +class LineupEntryForm(forms.ModelForm): + availability = None + class Meta: + model = LineupEntry + widgets = { + 'label': forms.Select(attrs={'class': 'form-control form-control-sm'}) + } + exclude = () + +LineupEntryFormSet = modelformset_factory( + model=LineupEntry, + form=LineupEntryForm, + extra=0 +) diff --git a/teamsnap/migrations/0011_lineupentry.py b/teamsnap/migrations/0011_lineupentry.py new file mode 100644 index 0000000..da9e16b --- /dev/null +++ b/teamsnap/migrations/0011_lineupentry.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.6 on 2021-11-21 18:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0010_event_is_game'), + ] + + operations = [ + migrations.CreateModel( + name='LineupEntry', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.PositiveSmallIntegerField(blank=True, choices=[(11, 'EH'), (1, 'P'), (2, 'C'), (3, '1B'), (4, '2B'), (5, '3B'), (6, 'SS'), (7, 'LF'), (8, 'CF'), (9, 'RF'), (10, 'DH')], default=None, null=True)), + ('sequence', models.PositiveSmallIntegerField(default=0, null=True)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teamsnap.event')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teamsnap.member')), + ], + options={ + 'unique_together': {('member', 'event')}, + }, + ), + ] diff --git a/teamsnap/migrations/0012_auto_20211121_2010.py b/teamsnap/migrations/0012_auto_20211121_2010.py new file mode 100644 index 0000000..2294c80 --- /dev/null +++ b/teamsnap/migrations/0012_auto_20211121_2010.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.6 on 2021-11-21 20:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0011_lineupentry'), + ] + + operations = [ + migrations.AddField( + model_name='lineupentry', + name='name', + field=models.CharField(max_length=50, null=True), + ), + migrations.AddField( + model_name='lineupentry', + name='teamsnap_id', + field=models.CharField(max_length=10, null=True, unique=True), + ), + ] diff --git a/teamsnap/migrations/0013_remove_lineupentry_name.py b/teamsnap/migrations/0013_remove_lineupentry_name.py new file mode 100644 index 0000000..763bba4 --- /dev/null +++ b/teamsnap/migrations/0013_remove_lineupentry_name.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.6 on 2021-11-21 20:10 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0012_auto_20211121_2010'), + ] + + operations = [ + migrations.RemoveField( + model_name='lineupentry', + name='name', + ), + ] diff --git a/teamsnap/migrations/0014_alter_lineupentry_teamsnap_id.py b/teamsnap/migrations/0014_alter_lineupentry_teamsnap_id.py new file mode 100644 index 0000000..214a198 --- /dev/null +++ b/teamsnap/migrations/0014_alter_lineupentry_teamsnap_id.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.6 on 2021-11-21 20:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0013_remove_lineupentry_name'), + ] + + operations = [ + migrations.AlterField( + model_name='lineupentry', + name='teamsnap_id', + field=models.CharField(blank=True, max_length=10, null=True, unique=True), + ), + ] diff --git a/teamsnap/models.py b/teamsnap/models.py index 45cb086..f1aa5ea 100644 --- a/teamsnap/models.py +++ b/teamsnap/models.py @@ -106,4 +106,28 @@ class Availability(TeamsnapBaseModel): return f"{self.member} - {self.event} ({self.teamsnap_id})" class Meta: - verbose_name_plural = "availabilities" \ No newline at end of file + verbose_name_plural = "availabilities" + +class LineupEntry(TeamsnapBaseModel): + name = None + teamsnap_id = models.CharField(max_length=10, unique=True, null=True, blank=True) + member = models.ForeignKey(Member, on_delete=models.CASCADE) + event = models.ForeignKey(Event, on_delete=models.CASCADE) + positions = [ + (11, 'EH'), + (1, 'P'), + (2, 'C'), + (3, '1B'), + (4, '2B'), + (5, '3B'), + (6, 'SS'), + (7, 'LF'), + (8, 'CF'), + (9, 'RF'), + (10,'DH') + ] + label = models.PositiveSmallIntegerField(choices=positions, default=None, null=True, blank=True) + sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True) + + class Meta: + unique_together = ('member', 'event',) diff --git a/teamsnap/views.py b/teamsnap/views.py index e4e3da4..9702667 100644 --- a/teamsnap/views.py +++ b/teamsnap/views.py @@ -1,9 +1,19 @@ from django.shortcuts import render, redirect # from .teamsnap.api import TeamSnap, Team, Event, Availability -from .models import User, Member, Team, Event, Location +from .models import User, Member, Team, Event, Location, LineupEntry from django.views.generic.list import ListView from lib.views import BenchcoachListView +from .forms import LineupEntryForm, LineupEntryFormSet +from django.forms.models import model_to_dict +from django.urls import reverse +from django.db.models import Case, When + +def queryset_from_ids(Model, id_list): + #https://stackoverflow.com/questions/4916851/django-get-a-queryset-from-array-of-ids-in-specific-order + preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(id_list)]) + queryset = Model.objects.filter(pk__in=id_list).order_by(preserved) + return queryset def edit_event(request, id): event = Event.objects.get(id = id) @@ -17,19 +27,82 @@ class EventsListView(BenchcoachListView): title_strf = '{item.formatted_title}' body_strf = "{item.start_date:%a, %b %-d, %-I:%M %p},\n{item.location.name}" - # def get_context_data(self): - # context = super().get_context_data() - # for item in context['items']: - # item['buttons'].append( - # { - # 'label': 'Edit Lineup', - # 'href': reverse('edit lineup', args=[item['id']]) - # } - # ) - # return context + def get_context_data(self): + context = super().get_context_data() + for item in context['items']: + item['buttons'].append( + { + 'label': 'Edit Lineup', + 'href': reverse('teamsnap edit lineup', args=[item['id']]) + } + ) + return context -class TeamListView(ListView): - model = Team +class TeamListView(BenchcoachListView): + Model = Team + edit_url = 'teamsnap edit team' + list_url = 'teamsnap list teams' + page_title = "TeamSnap Teams" -class LocationListView(ListView): - model = Location \ No newline at end of file +class LocationListView(BenchcoachListView): + Model = Location + edit_url = 'teamsnap edit location' + list_url = 'teamsnap list locations' + page_title = "TeamSnap Locations" + +def edit_lineup(request, event_id): + + if request.method == 'POST': + # create a form instance and populate it with data from the request: + formset = LineupEntryFormSet(request.POST) + for form in formset: + if form.is_valid(): + # process the data in form.cleaned_data as required + # ... + # redirect to a new URL: + + if isinstance(form.cleaned_data['id'], LineupEntry): + positioning_id = form.cleaned_data.pop('id').id #FIXME this is a workaround, not sure why it is necessary + positioning = LineupEntry.objects.filter(id=positioning_id) + positioning.update(**form.cleaned_data) + did_create = False + else: + positioning = LineupEntry.objects.create(**form.cleaned_data, event_id=event_id) + did_create = True + else: + pass + return render(request, 'success.html', {'call_back':'teamsnap edit lineup','id':event_id, 'errors':[error for error in formset.errors if error]}, status=200) + # return render(request, 'success.html', {'call_back':'schedule'}) + event = Event.objects.get(id=event_id) + members = Member.objects.filter(is_non_player=False).prefetch_related('availability_set', 'lineupentry_set') + # players_d.sort(key=lambda d: (-d['availability'].available, d['last_name'])) + + for member in members: + LineupEntry.objects.get_or_create(member_id=member.id, event_id=event_id) + + qs_starting_lineup = LineupEntry.objects.filter(event_id=event_id, sequence__isnull=False, sequence__gt=0).order_by('sequence') + qs_bench = LineupEntry.objects.filter(event_id=event_id, sequence=0).prefetch_related('member__availability_set').order_by('member__last_name') + + # This is all a compromise to get the sorting just the way I wanted. THERE'S GOT TO BE A BETTER WAY + ids_starting_lineup = [item.id for item in qs_starting_lineup] + ids_bench_available = [item.id for item in qs_bench + if item.member.availability_set.get(event_id=event_id).status_code == 1] + ids_bench_maybe = [item.id for item in qs_bench + if item.member.availability_set.get(event_id=event_id).status_code == 2] + ids_bench_no = [item.id for item in qs_bench + if item.member.availability_set.get(event_id=event_id).status_code == 0] + ids_bench_unknown = [item.id for item in qs_bench + if item.member.availability_set.get(event_id=event_id).status_code is None] + qset = queryset_from_ids(LineupEntry, ids_starting_lineup + ids_bench_available + ids_bench_maybe + ids_bench_no + ids_bench_unknown) + + formset = LineupEntryFormSet(queryset=qset) + + for f in formset: + if f.instance.member_id: + f.availability = f.instance.member.availability_set.get(event_id=event_id) + # f.statline = f.instance.member.statline_set.get() + + return render(request, 'teamsnap/lineup.html', {'title': 'Lineup', + 'event': event, + 'formset': formset, + }) \ No newline at end of file