v2 initial commit
This commit is contained in:
@@ -1,11 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from .models import User, Team, Location, Event, Member, Availability, Opponent
|
||||
|
||||
from .models import Preferences
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(User)
|
||||
admin.site.register(Team)
|
||||
admin.site.register(Event)
|
||||
admin.site.register(Location)
|
||||
admin.site.register(Member)
|
||||
admin.site.register(Availability)
|
||||
admin.site.register(Opponent)
|
||||
admin.site.register(Preferences)
|
||||
|
||||
@@ -2,5 +2,5 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class TeamsnapConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'teamsnap'
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "teamsnap"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,78 +1,21 @@
|
||||
from django import forms
|
||||
from .models import Team, Location, Opponent, Event, Member
|
||||
from django.forms import modelformset_factory, formset_factory, inlineformset_factory
|
||||
from django.forms import ModelForm, formset_factory
|
||||
|
||||
select_kwargs = {
|
||||
'attrs':{'class': 'form-control form-control-sm'}
|
||||
}
|
||||
from .models import Preferences
|
||||
|
||||
text_input_kwargs = {
|
||||
'attrs':{"readonly": "readonly", 'class':'form-control form-control-sm'}
|
||||
}
|
||||
|
||||
class MemberForm(forms.ModelForm):
|
||||
class PreferencesForm(ModelForm):
|
||||
class Meta:
|
||||
model = Member
|
||||
fields = ('first_name', 'last_name', 'benchcoach_object')
|
||||
labels = {
|
||||
'benchcoach_object': 'BenchCoach Link',
|
||||
}
|
||||
model = Preferences
|
||||
fields = ["user", "managed_team_id"]
|
||||
widgets = {
|
||||
"benchcoach_object": forms.Select(**select_kwargs),
|
||||
"first_name": forms.TextInput(**text_input_kwargs),
|
||||
"last_name": forms.TextInput(**text_input_kwargs),
|
||||
"user": forms.HiddenInput(),
|
||||
"managed_team_id": forms.Select(
|
||||
choices=(), attrs={"class": "form-control"}
|
||||
),
|
||||
}
|
||||
labels = {"managed_team_id": "Selected Team"}
|
||||
|
||||
class EventForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ('formatted_title', 'start_date', 'benchcoach_object')
|
||||
labels ={
|
||||
'formatted_title':"Title",
|
||||
'benchcoach_object':'BenchCoach Link',
|
||||
'start_date':'Date/Time'
|
||||
}
|
||||
widgets = {
|
||||
"benchcoach_object": forms.Select(**select_kwargs),
|
||||
"formatted_title":forms.TextInput(**text_input_kwargs),
|
||||
"start_date": forms.DateTimeInput(**text_input_kwargs)
|
||||
}
|
||||
|
||||
class TeamForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Team
|
||||
fields = ('name', 'benchcoach_object')
|
||||
labels ={
|
||||
'benchcoach_object':'BenchCoach Link',
|
||||
}
|
||||
widgets = {
|
||||
"name":forms.TextInput(**text_input_kwargs),
|
||||
"benchcoach_object": forms.Select(**select_kwargs)
|
||||
}
|
||||
|
||||
class OpponentForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Opponent
|
||||
fields = ('name', 'benchcoach_object')
|
||||
labels ={
|
||||
'benchcoach_object':'BenchCoach Link',
|
||||
}
|
||||
widgets = {
|
||||
"name":forms.TextInput(**text_input_kwargs),
|
||||
"benchcoach_object": forms.Select(**select_kwargs)
|
||||
}
|
||||
|
||||
class LocationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Location
|
||||
fields = ('name', 'benchcoach_object')
|
||||
labels ={
|
||||
'benchcoach_object':'BenchCoach Link',
|
||||
}
|
||||
widgets = {
|
||||
"name":forms.TextInput(**text_input_kwargs),
|
||||
"benchcoach_object": forms.Select(**select_kwargs)
|
||||
}
|
||||
|
||||
class LineupEntryForm(forms.Form):
|
||||
member = None
|
||||
@@ -85,35 +28,27 @@ class LineupEntryForm(forms.Form):
|
||||
member_id = forms.Field()
|
||||
position_only = forms.BooleanField(initial=False, required=False)
|
||||
sequence = forms.IntegerField(required=False)
|
||||
label = forms.ChoiceField(required=False, choices=[
|
||||
("", "--"),
|
||||
("P","P"),
|
||||
("C","C"),
|
||||
("1B","1B"),
|
||||
("2B", "2B"),
|
||||
("3B", "3B"),
|
||||
("SS", "SS"),
|
||||
('LF','LF'),
|
||||
('CF','CF'),
|
||||
('RF','RF'),
|
||||
('DH','DH'),
|
||||
('DR','DR'),
|
||||
('EH','EH')
|
||||
],
|
||||
widget=forms.Select(
|
||||
attrs = {'onchange' : "colorPositions();"}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EventChooseForm(forms.Form):
|
||||
event_id = forms.ChoiceField()
|
||||
|
||||
# checked = forms.BooleanField(required=False)
|
||||
# def __init__(self, events, *args, **kwargs):
|
||||
# super(EventChooseForm, self).__init__(*args, **kwargs)
|
||||
# self.fields['foo'].choices = [e.data['id'] for e in events]
|
||||
|
||||
LineupEntryFormset = formset_factory(LineupEntryForm, can_delete=True, can_order=True, extra=0)
|
||||
label = forms.ChoiceField(
|
||||
required=False,
|
||||
choices=[
|
||||
("", "--"),
|
||||
("P", "P"),
|
||||
("C", "C"),
|
||||
("1B", "1B"),
|
||||
("2B", "2B"),
|
||||
("3B", "3B"),
|
||||
("SS", "SS"),
|
||||
("LF", "LF"),
|
||||
("CF", "CF"),
|
||||
("RF", "RF"),
|
||||
("DH", "DH"),
|
||||
("DR", "DR"),
|
||||
("EH", "EH"),
|
||||
],
|
||||
widget=forms.Select(attrs={"onchange": "colorPositions();"}),
|
||||
)
|
||||
|
||||
|
||||
LineupEntryFormset = formset_factory(
|
||||
LineupEntryForm, can_delete=True, can_order=True, extra=0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-17 21:35
|
||||
# Generated by Django 3.2.13 on 2022-06-02 13:20
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
@@ -9,134 +10,16 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Team',
|
||||
name='Preferences',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, null=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnapteam', to='benchcoach.team')),
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('managed_team_id', models.IntegerField()),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('first_name', models.CharField(max_length=50, null=True)),
|
||||
('last_name', models.CharField(max_length=50, null=True)),
|
||||
('email', models.EmailField(max_length=254, null=True)),
|
||||
('managed_teams', models.ManyToManyField(to='teamsnap.Team')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Opponent',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, null=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.team')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Member',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('first_name', models.CharField(max_length=50, null=True)),
|
||||
('last_name', models.CharField(max_length=50, null=True)),
|
||||
('jersey_number', models.IntegerField(null=True)),
|
||||
('is_non_player', models.BooleanField()),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.player')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Location',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('name', models.CharField(max_length=50, null=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.venue')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Event',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('label', models.CharField(max_length=50, null=True)),
|
||||
('start_date', models.DateTimeField(null=True)),
|
||||
('formatted_title', models.CharField(max_length=50, null=True)),
|
||||
('points_for_opponent', models.PositiveSmallIntegerField(null=True)),
|
||||
('points_for_team', models.PositiveSmallIntegerField(null=True)),
|
||||
('is_game', models.BooleanField()),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='benchcoach.event')),
|
||||
('location', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.location')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
('opponent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opponent', to='teamsnap.opponent')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Availability',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('status_code', models.SmallIntegerField(choices=[(1, 'Yes'), (0, 'No'), (2, 'Maybe'), (None, 'Unknown')], default=None, null=True)),
|
||||
('benchcoach_object', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.availability')),
|
||||
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.event')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
('member', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.member')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'availabilities',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='LineupEntry',
|
||||
fields=[
|
||||
('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
|
||||
('created_at', models.DateTimeField(null=True)),
|
||||
('updated_at', models.DateTimeField(null=True)),
|
||||
('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(blank=True, default=0, null=True)),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teamsnap.event')),
|
||||
('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teamsnap.member')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('member', 'event')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-18 23:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='game_type',
|
||||
field=models.CharField(max_length=50, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,108 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-20 02:58
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0002_alter_player_jersey_number'),
|
||||
('teamsnap', '0002_event_game_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='availability',
|
||||
name='managed_by_team',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='event',
|
||||
name='managed_by_team',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='location',
|
||||
name='managed_by_team',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='member',
|
||||
name='managed_by_team',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='opponent',
|
||||
name='managed_by_team',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='availability',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lineupentry',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_lineupentry', to='benchcoach.positioning'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='lineupentry',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='location',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='member',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='opponent',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team', verbose_name='managed by team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_availability', to='benchcoach.availability'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='benchcoach.event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_location', to='benchcoach.venue'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_member', to='benchcoach.player'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opponent',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_opponent', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_team', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='lineupentry',
|
||||
unique_together=set(),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='lineupentry',
|
||||
name='managed_by_team',
|
||||
),
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-22 15:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0003_auto_20211219_2058'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='is_game',
|
||||
field=models.BooleanField(null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='is_non_player',
|
||||
field=models.BooleanField(null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,50 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-24 16:42
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0002_alter_player_jersey_number'),
|
||||
('teamsnap', '0004_auto_20211222_0957'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_availability', to='benchcoach.availability'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='benchcoach.event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lineupentry',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_lineupentry', to='benchcoach.positioning'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_location', to='benchcoach.venue'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_member', to='benchcoach.player'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opponent',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_opponent', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_team', to='benchcoach.team'),
|
||||
),
|
||||
]
|
||||
@@ -1,50 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-29 01:06
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0002_alter_player_jersey_number'),
|
||||
('teamsnap', '0004_auto_20211222_0957'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.availability'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lineupentry',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.positioning'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.venue'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.player'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opponent',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_object+', to='benchcoach.team'),
|
||||
),
|
||||
]
|
||||
@@ -1,50 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-29 01:07
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0002_alter_player_jersey_number'),
|
||||
('teamsnap', '0005_auto_20211228_1906'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_availability', to='benchcoach.availability'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='benchcoach.event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lineupentry',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_lineupentry', to='benchcoach.positioning'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_location', to='benchcoach.venue'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_member', to='benchcoach.player'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opponent',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_opponent', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_team', to='benchcoach.team'),
|
||||
),
|
||||
]
|
||||
@@ -1,50 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-29 01:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0002_alter_player_jersey_number'),
|
||||
('teamsnap', '0006_auto_20211228_1907'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_availability', to='benchcoach.availability'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='benchcoach.event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='lineupentry',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_lineupentry', to='benchcoach.positioning'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='location',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_location', to='benchcoach.venue'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_member', to='benchcoach.player'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='opponent',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_opponent', to='benchcoach.team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='benchcoach_object',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_team', to='benchcoach.team'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-29 14:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0007_auto_20211228_1910'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='status_code',
|
||||
field=models.SmallIntegerField(choices=[(1, 'Yes'), (0, 'No'), (2, 'Maybe'), (None, 'Unknown')], default=None),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-29 16:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0008_alter_availability_status_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='availability',
|
||||
name='status_code',
|
||||
field=models.SmallIntegerField(blank=True, choices=[(1, 'Yes'), (0, 'No'), (2, 'Maybe'), (None, 'Unknown')], default=None, null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,14 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2022-05-06 15:31
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0005_auto_20211224_1042'),
|
||||
('teamsnap', '0009_alter_availability_status_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
@@ -1,221 +1,9 @@
|
||||
# Create your models here.
|
||||
from django.db import models
|
||||
|
||||
import benchcoach.models
|
||||
import pyteamsnap.api
|
||||
from django.utils.timezone import localtime
|
||||
from benchcoach.users.models import User
|
||||
|
||||
class TeamsnapBaseModel(models.Model):
|
||||
type = None
|
||||
id = models.CharField(max_length=50, unique=True, primary_key=True)
|
||||
created_at = models.DateTimeField(null=True)
|
||||
updated_at = models.DateTimeField(null=True)
|
||||
ApiObject = pyteamsnap.api.ApiObject
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return f"TeamSnap {self.__class__.__name__} Object ({self.id})"
|
||||
|
||||
@property
|
||||
def api_url(self):
|
||||
return "https://api.teamsnap.com/v3/{type}/{id}".format(type=self.type, id=self.id)
|
||||
|
||||
class Team(TeamsnapBaseModel):
|
||||
type = 'team'
|
||||
name = models.CharField(max_length=50, null=True)
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Team,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_team"
|
||||
)
|
||||
ApiObject = pyteamsnap.api.Team
|
||||
|
||||
class User(TeamsnapBaseModel):
|
||||
type = 'user'
|
||||
first_name = models.CharField(max_length=50, null=True)
|
||||
last_name = models.CharField(max_length = 50, null=True)
|
||||
email = models.EmailField(null=True)
|
||||
managed_teams = models.ManyToManyField(Team)
|
||||
ApiObject = pyteamsnap.api.User
|
||||
|
||||
@classmethod
|
||||
def update_or_create_from_teamsnap_api(cls, teamsnap_data):
|
||||
fields = ['id', 'first_name', 'last_name', 'email']
|
||||
user_data = {k:teamsnap_data[k] for k in fields}
|
||||
managed_teams = []
|
||||
for managed_team_id in teamsnap_data['managed_team_ids']:
|
||||
obj, created = Team.objects.get_or_create(id=managed_team_id)
|
||||
managed_teams.append(obj)
|
||||
pass
|
||||
id = user_data.pop('id')
|
||||
user, created = cls.objects.update_or_create(id=id, defaults=user_data)
|
||||
user.managed_teams.add(*managed_teams)
|
||||
return (user, created)
|
||||
|
||||
class TeamsnapManagedObjectModel(TeamsnapBaseModel):
|
||||
team = models.ForeignKey(
|
||||
Team,
|
||||
verbose_name="managed by team",
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def url(self, endpoint='view'):
|
||||
return f"https://go.teamsnap.com/{self.team.id}/{self.type}/{endpoint}/{self.id}"
|
||||
|
||||
class Opponent(TeamsnapManagedObjectModel):
|
||||
type = 'opponent'
|
||||
name = models.CharField(max_length=50, null=True)
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Team,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_opponent"
|
||||
)
|
||||
ApiObject = pyteamsnap.api.Opponent
|
||||
|
||||
class Location(TeamsnapManagedObjectModel):
|
||||
type = 'location'
|
||||
name = models.CharField(max_length=50, null=True)
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Venue,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_location"
|
||||
)
|
||||
ApiObject = pyteamsnap.api.Location
|
||||
|
||||
class Member(TeamsnapManagedObjectModel):
|
||||
# url format is
|
||||
# f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/player/{self.teamsnap_id}"
|
||||
# f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/edit/{self.teamsnap_id}"
|
||||
type = 'member'
|
||||
name = models.CharField(max_length=50, null=True)
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Player,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_member"
|
||||
)
|
||||
first_name = models.CharField(max_length = 50, null=True)
|
||||
last_name = models.CharField(max_length = 50, null=True)
|
||||
jersey_number = models.IntegerField(null=True)
|
||||
is_non_player = models.BooleanField(null=True)
|
||||
ApiObject = pyteamsnap.api.Member
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.last_name}, {self.first_name} ({self.id})"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
|
||||
class Event(TeamsnapManagedObjectModel):
|
||||
# url is
|
||||
# f"https://go.teamsnap.com/{self.team.teamsnap_id}/schedule/view_game/{self.teamsnap_id}"
|
||||
# f"https://go.teamsnap.com/{self.team.teamsnap_id}/schedule/edit_game/{self.teamsnap_id}"
|
||||
type = 'event'
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_event"
|
||||
)
|
||||
label = models.CharField(max_length = 50, null=True)
|
||||
start_date = models.DateTimeField(null=True)
|
||||
opponent = models.ForeignKey(Opponent, null=True, on_delete=models.CASCADE, related_name="opponent")
|
||||
location = models.ForeignKey(Location, null=True, on_delete=models.CASCADE)
|
||||
formatted_title = models.CharField(max_length = 50, null=True)
|
||||
points_for_opponent = models.PositiveSmallIntegerField(null=True)
|
||||
points_for_team = models.PositiveSmallIntegerField(null=True)
|
||||
is_game = models.BooleanField(null=True)
|
||||
game_type = models.CharField(max_length = 50, null=True)
|
||||
ApiObject = pyteamsnap.api.Event
|
||||
|
||||
@property
|
||||
def csv_event_title(self)->str:
|
||||
'''
|
||||
TeamSnap has a title format that it uses for the csv export which includes the date and time
|
||||
:return: formatted string
|
||||
'''
|
||||
# activate(zone)
|
||||
start_date = localtime(self.start_date)
|
||||
return f"{self.formatted_title} {start_date:%m/%d/%Y}" + f"{start_date:%-I:%M %p}".rjust(9)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.formatted_title} ({self.id})"
|
||||
|
||||
class Availability(TeamsnapManagedObjectModel):
|
||||
type='availability'
|
||||
YES = 1
|
||||
NO = 0
|
||||
MAYBE = 2
|
||||
UNKNOWN = None
|
||||
status_codes = [
|
||||
(YES, 'Yes'),
|
||||
(NO, 'No'),
|
||||
(MAYBE, 'Maybe'),
|
||||
(UNKNOWN, 'Unknown')
|
||||
]
|
||||
event = models.ForeignKey(Event, null=True, on_delete=models.CASCADE)
|
||||
member = models.ForeignKey(Member, null=True, on_delete=models.CASCADE)
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Availability,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_availability"
|
||||
)
|
||||
status_code = models.SmallIntegerField(choices=status_codes, null=True, blank=True, default=None)
|
||||
ApiObject = pyteamsnap.api.Availability
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.member} - {self.event} ({self.id})"
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "availabilities"
|
||||
|
||||
class LineupEntry(TeamsnapManagedObjectModel):
|
||||
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')
|
||||
]
|
||||
benchcoach_object = models.OneToOneField(
|
||||
benchcoach.models.Positioning,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_lineupentry"
|
||||
)
|
||||
label = models.PositiveSmallIntegerField(choices=positions, default=None, null=True, blank=True)
|
||||
sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True)
|
||||
ApiObject = pyteamsnap.api.EventLineupEntry
|
||||
|
||||
@classmethod
|
||||
def update_or_create_from_teamsnap_api(cls, teamsnap_data):
|
||||
fields = [
|
||||
'id',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'label',
|
||||
'sequence'
|
||||
]
|
||||
lineup_entry_data = {k: teamsnap_data[k] for k in fields}
|
||||
member, created = Member.objects.get_or_create(id=teamsnap_data['member_id'])
|
||||
team, created = Team.objects.get_or_create(id=teamsnap_data['team_id'])
|
||||
event, created = Event.objects.get_or_create(id=teamsnap_data['event_id'])
|
||||
id = lineup_entry_data.pop('id')
|
||||
lineup_entry, created = cls.objects.update_or_create(id=id, defaults=lineup_entry_data)
|
||||
lineup_entry.team = team
|
||||
lineup_entry.event = event
|
||||
lineup_entry.member = member
|
||||
lineup_entry.save()
|
||||
return (lineup_entry, created)
|
||||
class Preferences(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
managed_team_id = models.IntegerField()
|
||||
|
||||
31
teamsnap/provider.py
Normal file
31
teamsnap/provider.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from allauth.socialaccount import providers
|
||||
from allauth.socialaccount.providers.base import ProviderAccount
|
||||
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
|
||||
|
||||
|
||||
class TeamsnapAccount(ProviderAccount):
|
||||
pass
|
||||
|
||||
|
||||
class TeamsnapProvider(OAuth2Provider):
|
||||
id = "teamsnap"
|
||||
name = "TeamSnap"
|
||||
account_class = TeamsnapAccount
|
||||
|
||||
def extract_uid(self, data):
|
||||
return str(data["id"])
|
||||
|
||||
def extract_common_fields(self, data):
|
||||
return dict(
|
||||
username=data["email"],
|
||||
email=data["email"],
|
||||
first_name=data["first_name"],
|
||||
last_name=data["last_name"],
|
||||
)
|
||||
|
||||
def get_default_scope(self):
|
||||
scope = ["read"]
|
||||
return scope
|
||||
|
||||
|
||||
providers.registry.register(TeamsnapProvider)
|
||||
148
teamsnap/templates/dashboard.html
Normal file
148
teamsnap/templates/dashboard.html
Normal file
@@ -0,0 +1,148 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}{% endblock %}
|
||||
{% block content %}
|
||||
<h3>Dashboard</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md pb-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Upcoming Games</h4>
|
||||
</div>
|
||||
<div class="card-body p-0 m-0">
|
||||
|
||||
{% for event, availability_summary in events_availabilities|slice:":4" %}
|
||||
<div class="row m-0 p-2 border-bottom">
|
||||
<div class="col p-0 m-auto" style="flex: 0 0 100px;">
|
||||
<div class="d-inline-flex m-0 p-0">
|
||||
<div class="chart-container" style="height: 100px;width: 100px;">
|
||||
<canvas id="availability-donut-{{ event.data.id }}" class="availability-donut"
|
||||
data-event-id="{{ event.data.id }}"
|
||||
data-available-yes="{{ availability_summary.data.player_going_count }}"
|
||||
data-available-no="{{ availability_summary.data.player_not_going_count }}"
|
||||
data-available-maybe="{{ availability_summary.data.player_maybe_count }}"
|
||||
data-available-unknown="{{ availability_summary.data.player_unknown_count }}"
|
||||
>
|
||||
</canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div>
|
||||
<h4><strong><a class="text-decoration-none text-black" href="{% url 'teamsnap_view_event' team_id=event.data.team_id event_id=event.data.id %}">{{ event.data.formatted_title }}</a></strong></h4>
|
||||
<h6 class="text-muted mb-2">{{ event.data.start_date|date:"D, F j, g:i A" }}</h6>
|
||||
<h6 class="text-muted mb-2">{{ event.data.location_name }}</h6>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<a class="btn btn-primary btn-sm mx-1" role="button" href="{% url 'teamsnap_edit_lineup' event_ids=event.data.id team_id=event.data.team_id %}">Go to Lineup</a>
|
||||
<form method="get"
|
||||
action="{% url 'instagen_generate' team_id=event.data.team_id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected
|
||||
value="{{ event.data.id }}">{{ event.data.formatted_title }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input hidden class="form-check-input" type="radio" name="background"
|
||||
id="backgroundLocation" checked value="location">
|
||||
<input hidden class="form-check-input" type="radio" name="dimensions" id="square" checked
|
||||
value="1080x1080">
|
||||
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-instagram"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md pb-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Past Games</h4>
|
||||
</div>
|
||||
<div class="card-body p-0 m-0">
|
||||
|
||||
{% for event in ts_events_past|slice:":4" %}
|
||||
<div class="row m-0 p-2 border-bottom">
|
||||
<div class="col p-0 m-auto rounded-circle bg-light" style="flex: 0 0 100px;">
|
||||
<div class="d-inline-flex m-0 p-0">
|
||||
<div class="d-flex align-items-center justify-content-center" style="height: 100px;width: 100px;">
|
||||
<h4 class="text-center"><strong>{{ event.data.formatted_results }}</strong></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4 class=""><strong><a class="text-decoration-none text-black" href="{% url 'teamsnap_view_event' team_id=event.data.team_id event_id=event.data.id %}">{{ event.data.formatted_title }}</a></strong></h4>
|
||||
<h6 class="text-muted mb-2">{{ event.data.start_date|date:"D, F j" }}</h6>
|
||||
{# <h6 class="text-muted mb-2">{{ event.data.location_name }}</h6><a class="btn btn-primary btn-sm" role="button" href="{% url 'teamsnap_edit_lineup' event_ids=event.data.id team_id=request.user.teamsnapsettings.managed_team.id %}">Go to Lineup</a>#}
|
||||
|
||||
<div class="d-flex">
|
||||
<form method="get"
|
||||
action="{% url 'instagen_generate' team_id=event.data.team_id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected
|
||||
value="{{ event.data.id }}">{{ event.data.formatted_title }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input hidden class="form-check-input" type="radio" name="background"
|
||||
id="backgroundLocation" checked value="location">
|
||||
<input hidden class="form-check-input" type="radio" name="dimensions" id="square" checked
|
||||
value="1080x1080">
|
||||
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-instagram"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block inline_javascript %}
|
||||
<script>
|
||||
function donut(ctx, yes_count, maybe_count, no_count, unknown_count) {
|
||||
var style = getComputedStyle(document.body);
|
||||
const myChart = new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
responsive: 'true',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Availability',
|
||||
labels: [
|
||||
'Yes',
|
||||
'Maybe',
|
||||
'No',
|
||||
'Unknown'
|
||||
],
|
||||
data: [yes_count, maybe_count, no_count, unknown_count],
|
||||
backgroundColor: [
|
||||
style.getPropertyValue('--bs-success'),
|
||||
style.getPropertyValue('--bs-info'),
|
||||
style.getPropertyValue('--bs-danger'),
|
||||
style.getPropertyValue('--bs-secondary')
|
||||
],
|
||||
hoverOffset: 4
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
for (ctx of document.querySelectorAll('.availability-donut')){
|
||||
donut(ctx,
|
||||
ctx.dataset.availableYes,
|
||||
ctx.dataset.availableMaybe,
|
||||
ctx.dataset.availableNo,
|
||||
ctx.dataset.availableUnknown,
|
||||
)
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -73,4 +73,4 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ event.data.formatted_title }}{% endblock %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card mx-auto" style="max-width: 455px">
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="row">
|
||||
<div class="col text-end">
|
||||
<form method="get"
|
||||
action="{% url "teamsnap_image_generator_generate" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}">
|
||||
action="{% url 'instagen_generate' team_id=event.data.team_id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
@@ -26,7 +26,7 @@
|
||||
</button>
|
||||
</form>
|
||||
<a class="btn btn-primary btn-sm py-0 m-1"
|
||||
href="{% url "teamsnap_image_generator" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}"
|
||||
href="{% url "instagen" team_id=event.data.team_id event_id=event.data.id %}"
|
||||
role="button">
|
||||
<div class="d-inline-block"><i class="bi bi-instagram"></i> <i class="bi bi-three-dots"></i>
|
||||
</div>
|
||||
@@ -61,7 +61,7 @@
|
||||
Opponent
|
||||
</th>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_opponent' team_id=request.user.profile.teamsnapsettings.managed_team.id id=event.data.opponent_id %}">{{ event.data.opponent_name }}</a>
|
||||
<a href="">{{ event.data.opponent_name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -69,7 +69,7 @@
|
||||
Location
|
||||
</th>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_location' team_id=request.user.profile.teamsnapsettings.managed_team.id id=event.data.location_id %}">{{ event.data.location_name }}</a>
|
||||
<a href="">{{ event.data.location_name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -134,11 +134,11 @@
|
||||
<div class="row m-0">
|
||||
<div class="flex-column m-2">
|
||||
<a class="btn btn-primary btn-sm"
|
||||
href="{% url 'teamsnap_edit_lineup' event_ids=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id %}"
|
||||
href="{% url 'teamsnap_edit_lineup' team_id=event.data.team_id event_ids=event.data.id %}"
|
||||
role="button">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -7,4 +7,4 @@
|
||||
{% include 'teamsnap/lineup/widgets/lineup.html' with formset_lineup=formset_lineup formset_bench=formset_bench%}
|
||||
<script src="{% static 'js/Sortable.js' %}"></script>
|
||||
<script src="{% static 'teamsnap/js/lineup-table.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -28,7 +28,7 @@
|
||||
{{ form.checked }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_view_event' event_id=form.event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id%}">{{ form.event.data.formatted_title }}</a>
|
||||
<a href="{% url 'teamsnap_view_event' event_id=form.event.data.id team_id=request.user.teamsnapsettings.managed_team.id%}">{{ form.event.data.formatted_title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ form.event.data.start_date | localtime}}
|
||||
@@ -24,7 +24,7 @@
|
||||
<tr class="align-top mx-1">
|
||||
{% for event_data in contexts %}
|
||||
<td class="px-1">
|
||||
{% include "teamsnap/lineup/widgets/lineup.html" with event=event_data.event event_id=event_data.event.data.id formset=event_data.formset formset_startinglineup=event_data.formset_startinglineup formset_bench=event_data.formset_bench formset_out=event_data.formset_out formset_startingpositionalonly=event_data.formset_startingpositionalonly %}
|
||||
{% include "lineup/widgets/lineup.html" with event=event_data.event event_id=event_data.event.data.id formset=event_data.formset formset_startinglineup=event_data.formset_startinglineup formset_bench=event_data.formset_bench formset_out=event_data.formset_out formset_startingpositionalonly=event_data.formset_startingpositionalonly %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
@@ -34,4 +34,4 @@
|
||||
</div>
|
||||
<script src="{% static 'js/Sortable.js' %}"></script>
|
||||
<script src="{% static 'teamsnap/js/lineup-table.js' %}"></script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="card mx-auto benchcoach-lineup" style="max-width: 455px" id="benchcoach-lineup-{{ event_id }}">
|
||||
<form method="post" action="{% url 'teamsnap_submit_lineup' team_id=team_id event_id=event_id%}">
|
||||
<form method="post" action="">
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
<div class="border-bottom p-2">
|
||||
@@ -39,7 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_startinglineup table_id="benchcoach-startinglineup" %}
|
||||
{% include 'lineup/widgets/lineup_table.html' with formset=formset_startinglineup table_id="benchcoach-startinglineup" %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -50,7 +50,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_startingpositionalonly table_id="benchcoach-startingpositionalonly" %}
|
||||
{% include 'lineup/widgets/lineup_table.html' with formset=formset_startingpositionalonly table_id="benchcoach-startingpositionalonly" %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -60,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_bench table_id="benchcoach-bench" %}
|
||||
{% include 'lineup/widgets/lineup_table.html' with formset=formset_bench table_id="benchcoach-bench" %}
|
||||
|
||||
<div>
|
||||
<div class="row m-0">
|
||||
@@ -69,7 +69,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_out table_id="benchcoach-out" %}
|
||||
{% include 'lineup/widgets/lineup_table.html' with formset=formset_out table_id="benchcoach-out" %}
|
||||
|
||||
|
||||
</div>
|
||||
@@ -66,4 +66,4 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
7
teamsnap/templates/preferences.html
Normal file
7
teamsnap/templates/preferences.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
{% endblock content %}
|
||||
@@ -1,22 +1,6 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}
|
||||
<div class="row d-none" >
|
||||
<div hidden class="col">
|
||||
<div class="container m-2">
|
||||
<div class="container m-2">
|
||||
<div class="btn-group">
|
||||
<form action="">
|
||||
<a class="btn btn-sm btn-outline-primary text-nowrap" href="{% url 'teamsnap_schedule' team_id=team_id%}?filters=no_past">No Past Events</a>
|
||||
<button class="btn btn-sm btn-outline-primary text-nowrap">Games Only</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% load tz %}
|
||||
<div class="">
|
||||
@@ -51,7 +35,7 @@
|
||||
{{ event.data.location_name }}
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'teamsnap_view_event' event_id=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id%}"><i class="bi bi-three-dots"></i></a>
|
||||
<a class="btn btn-outline-secondary btn-sm" href="{% url 'teamsnap_view_event' team_id=event.data.team_id event_id=event.data.id %}"><i class="bi bi-three-dots"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -61,4 +45,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -1,27 +0,0 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}Schedule{% endblock %}
|
||||
{% block content %}
|
||||
{% load tz %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_event' id=event.data.id team_id=schedule.html %}">{{ event.data.formatted_title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.start_date | localtime}}
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.location_name }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,150 +0,0 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}{% endblock %}
|
||||
{% block content %}
|
||||
<h3>Dashboard</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md pb-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Upcoming Games</h4>
|
||||
</div>
|
||||
<div class="card-body p-0 m-0">
|
||||
|
||||
{% for event, availability_summary in events_availabilities|slice:":4" %}
|
||||
<div class="row m-0 p-2 border-bottom">
|
||||
<div class="col p-0 m-auto" style="flex: 0 0 100px;">
|
||||
<div class="d-inline-flex m-0 p-0">
|
||||
<div class="chart-container" style="height: 100px;width: 100px;">
|
||||
<canvas id="availability-donut-{{ event.data.id }}" class="availability-donut"
|
||||
data-event-id="{{ event.data.id }}"
|
||||
data-available-yes="{{ availability_summary.data.player_going_count }}"
|
||||
data-available-no="{{ availability_summary.data.player_not_going_count }}"
|
||||
data-available-maybe="{{ availability_summary.data.player_maybe_count }}"
|
||||
data-available-unknown="{{ availability_summary.data.player_unknown_count }}"
|
||||
>
|
||||
</canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div>
|
||||
<h4><strong><a class="text-decoration-none text-black" href="{% url 'teamsnap_view_event' team_id=team_id event_id=event.data.id %}">{{ event.data.formatted_title }}</a></strong></h4>
|
||||
<h6 class="text-muted mb-2">{{ event.data.start_date|date:"D, F j, g:i A" }}</h6>
|
||||
<h6 class="text-muted mb-2">{{ event.data.location_name }}</h6>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<a class="btn btn-primary btn-sm mx-1" role="button" href="{% url 'teamsnap_edit_lineup' event_ids=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id %}">Go to Lineup</a>
|
||||
<form method="get"
|
||||
action="{% url "teamsnap_image_generator_generate" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected
|
||||
value="{{ event.data.id }}">{{ event.data.formatted_title }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input hidden class="form-check-input" type="radio" name="background"
|
||||
id="backgroundLocation" checked value="location">
|
||||
<input hidden class="form-check-input" type="radio" name="dimensions" id="square" checked
|
||||
value="1080x1080">
|
||||
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-instagram"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md pb-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Past Games</h4>
|
||||
</div>
|
||||
<div class="card-body p-0 m-0">
|
||||
|
||||
{% for event in ts_events_past|slice:":4" %}
|
||||
<div class="row m-0 p-2 border-bottom">
|
||||
<div class="col p-0 m-auto rounded-circle bg-light" style="flex: 0 0 100px;">
|
||||
<div class="d-inline-flex m-0 p-0">
|
||||
<div class="d-flex align-items-center justify-content-center" style="height: 100px;width: 100px;">
|
||||
<h4 class="text-center"><strong>{{ event.data.formatted_results }}</strong></h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4 class=""><strong><a class="text-decoration-none text-black" href="{% url 'teamsnap_view_event' team_id=team_id event_id=event.data.id %}">{{ event.data.formatted_title }}</a></strong></h4>
|
||||
<h6 class="text-muted mb-2">{{ event.data.start_date|date:"D, F j" }}</h6>
|
||||
{# <h6 class="text-muted mb-2">{{ event.data.location_name }}</h6><a class="btn btn-primary btn-sm" role="button" href="{% url 'teamsnap_edit_lineup' event_ids=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id %}">Go to Lineup</a>#}
|
||||
|
||||
<div class="d-flex">
|
||||
<form method="get"
|
||||
action="{% url "teamsnap_image_generator_generate" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected
|
||||
value="{{ event.data.id }}">{{ event.data.formatted_title }}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input hidden class="form-check-input" type="radio" name="background"
|
||||
id="backgroundLocation" checked value="location">
|
||||
<input hidden class="form-check-input" type="radio" name="dimensions" id="square" checked
|
||||
value="1080x1080">
|
||||
<button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-instagram"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function donut(ctx, yes_count, maybe_count, no_count, unknown_count) {
|
||||
var style = getComputedStyle(document.body);
|
||||
const myChart = new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
responsive: 'true',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Availability',
|
||||
labels: [
|
||||
'Yes',
|
||||
'Maybe',
|
||||
'No',
|
||||
'Unknown'
|
||||
],
|
||||
data: [yes_count, maybe_count, no_count, unknown_count],
|
||||
backgroundColor: [
|
||||
style.getPropertyValue('--bs-success'),
|
||||
style.getPropertyValue('--bs-info'),
|
||||
style.getPropertyValue('--bs-danger'),
|
||||
style.getPropertyValue('--bs-secondary')
|
||||
],
|
||||
hoverOffset: 4
|
||||
}]
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
for (ctx of document.querySelectorAll('.availability-donut')){
|
||||
donut(ctx,
|
||||
ctx.dataset.availableYes,
|
||||
ctx.dataset.availableMaybe,
|
||||
ctx.dataset.availableNo,
|
||||
ctx.dataset.availableUnknown,
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,28 +0,0 @@
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="text-center my-2">
|
||||
<h1><img class="mx-auto" src="{% static 'benchcoach.svg' %}" style="width: 64px;"/>
|
||||
<strong>Welcome to <span class="text-nowrap">Bench Coach</span></strong>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="text-center my-2">
|
||||
<div class="col-lg-6 m-auto">
|
||||
<p class="lead mb-4">Quisque at curabitur mollis ornare, malesuada maecenas. Orci elit
|
||||
tristique,
|
||||
malesuada eu pharetra. Est praesent tortor porttitor aptent, amet quisque.</p>
|
||||
{# <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mx-1">#}
|
||||
{# <button class="btn btn-primary" type="button">Login</button><button class="btn btn-outline-secondary" type="button">Sign Up</button>#}
|
||||
{# </div>#}
|
||||
</div>
|
||||
|
||||
<div class="container-sm">
|
||||
{# <ul class="nav nav-pills flex-column mb-auto">#}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,22 +0,0 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ location.data.name }}{% endblock %}
|
||||
{% block page_heading %}{{ location.data.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for key, value in location.data.items %}
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{{ key }}
|
||||
</th>
|
||||
<td>
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -1,22 +0,0 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ opponent.data.name }}{% endblock %}
|
||||
{% block page_heading %}{{ opponent.data.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for key, value in opponent.data.items %}
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{{ key }}
|
||||
</th>
|
||||
<td>
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -1,22 +0,0 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{{ formset.management_form }}{% csrf_token %}
|
||||
<table class="table">
|
||||
<tr>
|
||||
{% for _, field in formset.0.base_fields.items %}
|
||||
<th>{{ field.label }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
{% for field in form %}
|
||||
<td>{{ field }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,8 +0,0 @@
|
||||
<form method="post" id="formSync" action="{% url 'sync from teamsnap' %}">{% csrf_token %}
|
||||
<input type="hidden" name="object_name" value="{{ object_name }}">
|
||||
<input type="hidden" name="object_id" value={{ object_id }}>
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<button type="submit" value="update_event" class="btn btn-sm btn-outline-secondary d-xl-flex align-items-xl-center mx-1">
|
||||
<i class="bi bi-arrow-clockwise"></i><i class="bi bi-asterisk"></i>
|
||||
</button>
|
||||
</form>
|
||||
@@ -1,3 +1 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
from django.contrib import admin
|
||||
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||
from django.urls import path
|
||||
|
||||
from django.urls import path, include
|
||||
from functools import partial
|
||||
from .provider import TeamsnapProvider
|
||||
from .views import (
|
||||
PreferencesFormView,
|
||||
dashboard,
|
||||
edit_lineup,
|
||||
schedule_view,
|
||||
view_event,
|
||||
)
|
||||
|
||||
from . import views
|
||||
urlpatterns = default_urlpatterns(TeamsnapProvider)
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.home, name='teamsnap_home'),
|
||||
path('<int:team_id>/dashboard/', views.dashboard, name='teamsnap_dashboard'),
|
||||
path('edit/event/<int:id>', views.edit_event, name='teamsnap edit event'),
|
||||
path('sync/download', views.sync_from_teamsnap, name="sync from teamsnap"),
|
||||
path('import/', views.import_teamsnap, name="import"),
|
||||
|
||||
path('<int:team_id>/schedule/', views.schedule, name='teamsnap_schedule'),
|
||||
path('<int:team_id>/schedule/view_event/<int:event_id>', views.event, name='teamsnap_view_event'),
|
||||
path('<int:team_id>/opponent/view/<int:id>', views.opponent, name='teamsnap_opponent'),
|
||||
path('<int:team_id>/location/view/<int:id>', views.location, name='teamsnap_location'),
|
||||
path('<int:team_id>/event/<int:event_ids>/edit_lineup/', views.edit_lineup, name='teamsnap_edit_lineup'),
|
||||
path('<int:team_id>/event/<str:event_ids>/edit_lineup/', views.edit_lineup, name='teamsnap_edit_multiple_lineups'),
|
||||
path('<int:team_id>/event/<int:event_id>/submit_lineup/', views.submit_lineup, name='teamsnap_submit_lineup'),
|
||||
path('<int:team_id>/event/<int:event_id>/image_generator/', views.image_generator, name='teamsnap_image_generator'),
|
||||
path('<int:team_id>/event/<int:event_id>/image_generator/generate', views.get_matchup_image, name='teamsnap_image_generator_generate'),
|
||||
path('<int:team_id>/multievent/choose', views.multi_lineup_choose, name='teamsnap_choose_multiple_lineups')
|
||||
]
|
||||
urlpatterns += [
|
||||
path("preferences/", PreferencesFormView.as_view(), name="teamsnap_preferences"),
|
||||
path("<int:team_id>/schedule/", schedule_view, name="teamsnap_schedule"),
|
||||
path("<int:team_id>/dashboard/", dashboard, name="teamsnap_dashboard"),
|
||||
path("dashboard/", dashboard, name="teamsnap_dashboard"),
|
||||
path("schedule/", schedule_view, name="teamsnap_schedule"),
|
||||
path(
|
||||
"<int:team_id>/schedule/view_event/<int:event_id>",
|
||||
view_event,
|
||||
name="teamsnap_view_event",
|
||||
),
|
||||
path(
|
||||
"<int:team_id>/schedule/edit_lineup/<int:event_ids>",
|
||||
edit_lineup,
|
||||
name="teamsnap_edit_lineup",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFilter, ImageFont
|
||||
from pathlib import Path
|
||||
import os
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
from typing import List
|
||||
from dataclasses import dataclass
|
||||
|
||||
# image_directory = 'input/images/logos-bw/{filename}.{ext}'
|
||||
|
||||
# font_regular_path = "input/fonts/DINAlternate-Bold.ttf"
|
||||
# font_condensed_path = "input/fonts/DINCondensed-Bold.ttf"
|
||||
font_regular_path = "benchcoachproject/static/teamsnap/fonts/scala/ScalaSans-BoldLF.otf"
|
||||
font_condensed_path = "benchcoachproject/static/teamsnap/fonts/scala/ScalaSans-BoldLF.otf"
|
||||
|
||||
@dataclass
|
||||
class Team:
|
||||
name: str
|
||||
winlosstie: List[int] = None
|
||||
image_directory: str = '../input/images/logos-bw/{filename}.{ext}'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.name.lower().replace(' ', '-')
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
path = self.image_directory.format(filename=self.id, ext="png")
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
else:
|
||||
return None
|
||||
|
||||
@dataclass
|
||||
class Location:
|
||||
name: str
|
||||
address1: str = ""
|
||||
address2: str = ""
|
||||
image_directory: str = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.name.lower().replace(' ', '-')
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
path = self.image_directory.format(filename=self.id, ext="png")
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
return ",".join([self.address1,self.address2])
|
||||
|
||||
args = {
|
||||
"team_fave" : Team("Hounds"),
|
||||
"team_opponent" : Team("Trojans"),
|
||||
"home": False,
|
||||
"date" : "2021-05-08 12:30 pm",
|
||||
"location" : Location("Maywood", image_directory="benchcoachproject/static/teamsnap/ig/locations/maywood.{ext}"),
|
||||
"runs_for": 8,
|
||||
"runs_against": 9
|
||||
}
|
||||
|
||||
def gen_image (team_fave, team_opponent, date, location=None,
|
||||
location_name = None,
|
||||
home=False,
|
||||
background='location',
|
||||
address = None,
|
||||
width = 1080,
|
||||
height = 1080,
|
||||
*kwargs,
|
||||
**args
|
||||
):
|
||||
if not isinstance(date, datetime):
|
||||
# date = parser.parse(date)
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
pass
|
||||
|
||||
if location.image and background == 'location':
|
||||
background_image = Image.open(location.image).copy()
|
||||
background_image = background_image.resize((width, height))
|
||||
# background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5))
|
||||
background_image = background_image.convert("RGBA")
|
||||
elif background == 'transparent':
|
||||
background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
else:
|
||||
background_image = Image.new('RGBA', (width, height), (50, 55, 102))
|
||||
|
||||
title_images = []
|
||||
for team in [team_fave, team_opponent]:
|
||||
if team.image:
|
||||
title_images.append(Image.open(team.image).copy())
|
||||
else:
|
||||
title_images.append(Image.new('RGBA', (1080, 1080)))
|
||||
|
||||
title_image_left = title_images[0]
|
||||
title_image_right = title_images[1]
|
||||
|
||||
# Make a blank image for the rectangle, initialized to a completely
|
||||
# transparent color.
|
||||
tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0))
|
||||
|
||||
# Create a drawing context for it.
|
||||
draw = ImageDraw.Draw(tmp)
|
||||
|
||||
# section margin describes the margin of the section rectangles from the sides of the image
|
||||
section_margin_pct = .05
|
||||
llx = int(section_margin_pct * background_image.size[0])
|
||||
urx = int((1 - section_margin_pct) * background_image.size[0])
|
||||
lly = int((1 - section_margin_pct) * background_image.size[1])
|
||||
ury = int(.50 * background_image.size[1])
|
||||
|
||||
lly2 = int(.49 * background_image.size[1])
|
||||
ury2 = int(.05 * background_image.size[1])
|
||||
|
||||
section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png")))
|
||||
section_info_draw = ImageDraw.Draw(section_info)
|
||||
|
||||
section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png")))
|
||||
section_title_draw = ImageDraw.Draw(section_title)
|
||||
|
||||
# First line: Date
|
||||
font = ImageFont.truetype(font_regular_path, 62)
|
||||
text = "{:%a, %B %-d %-I:%M %p}".format(date).upper()
|
||||
# text = date
|
||||
text_size = draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
280
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
# Second line: Venue
|
||||
font = ImageFont.truetype(font_condensed_path, 34)
|
||||
if not location_name:
|
||||
text = location.name.upper()
|
||||
else:
|
||||
text = location_name.upper()
|
||||
text_size = section_info_draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
355
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
font = ImageFont.truetype(font_regular_path, 80)
|
||||
if home:
|
||||
text = "VS"
|
||||
else:
|
||||
text = "AT"
|
||||
text_size = section_title_draw.textsize(text, font)
|
||||
loc = (
|
||||
540,
|
||||
120
|
||||
)
|
||||
color = (255, 255, 255)
|
||||
section_title_draw.text(loc, text, color, font=font, anchor="mm")
|
||||
|
||||
# Alpha composite the two images together.
|
||||
background_image = Image.alpha_composite(background_image, tmp)
|
||||
|
||||
# Title Image Left
|
||||
title_image_left.thumbnail([350, 350])
|
||||
loc = (
|
||||
50, -50
|
||||
)
|
||||
section_title.paste(title_image_left, loc, title_image_left)
|
||||
|
||||
# Title Image Right
|
||||
title_image_right.thumbnail([350, 350])
|
||||
loc = (
|
||||
650, -50
|
||||
)
|
||||
section_title.paste(title_image_right, loc, title_image_right)
|
||||
|
||||
# background_image.paste(section_info, (llx, ury), section_info)
|
||||
# background_image.paste(section_title, (llx, ury2), section_title)
|
||||
section_title.paste(section_info,(0,0),section_info)
|
||||
section_title.thumbnail([800, 800])
|
||||
|
||||
if background=="badge":
|
||||
return section_title
|
||||
|
||||
background_image.paste(section_title,(
|
||||
int((background_image.size[0]-section_title.size[0])/2),
|
||||
height - 360
|
||||
),section_title)
|
||||
|
||||
return background_image
|
||||
|
||||
def gen_results_image (team_fave, team_opponent, date,
|
||||
location=None,
|
||||
location_name = None,
|
||||
home=False,
|
||||
background='location',
|
||||
address = None,
|
||||
width = 1080,
|
||||
height = 1080,
|
||||
runs_for=0,
|
||||
runs_against=0,
|
||||
*kwargs,
|
||||
**args
|
||||
):
|
||||
if not isinstance(date, datetime):
|
||||
# date = parser.parse(date)
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
pass
|
||||
|
||||
if location.image and background == 'location':
|
||||
background_image = Image.open(location.image).copy()
|
||||
background_image = background_image.resize((width, height))
|
||||
# background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5))
|
||||
background_image = background_image.convert("RGBA")
|
||||
elif background == 'transparent':
|
||||
background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
else:
|
||||
background_image = Image.new('RGBA', (width, height), (50, 55, 102))
|
||||
|
||||
title_images = []
|
||||
for team in [team_fave, team_opponent]:
|
||||
if team.image:
|
||||
title_images.append(Image.open(team.image).copy())
|
||||
else:
|
||||
title_images.append(Image.new('RGBA', (1080, 1080)))
|
||||
|
||||
title_image_left = title_images[0]
|
||||
title_image_right = title_images[1]
|
||||
|
||||
# Make a blank image for the rectangle, initialized to a completely
|
||||
# transparent color.
|
||||
tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0))
|
||||
|
||||
# Create a drawing context for it.
|
||||
draw = ImageDraw.Draw(tmp)
|
||||
|
||||
# section margin describes the margin of the section rectangles from the sides of the image
|
||||
section_margin_pct = .05
|
||||
llx = int(section_margin_pct * background_image.size[0])
|
||||
urx = int((1 - section_margin_pct) * background_image.size[0])
|
||||
lly = int((1 - section_margin_pct) * background_image.size[1])
|
||||
ury = int(.50 * background_image.size[1])
|
||||
|
||||
lly2 = int(.49 * background_image.size[1])
|
||||
ury2 = int(.05 * background_image.size[1])
|
||||
|
||||
#todo fix path
|
||||
section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png")))
|
||||
section_info_draw = ImageDraw.Draw(section_info)
|
||||
|
||||
section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png")))
|
||||
section_title_draw = ImageDraw.Draw(section_title)
|
||||
|
||||
# First line: Results
|
||||
loc = (
|
||||
1050,
|
||||
265
|
||||
)
|
||||
if runs_for > runs_against:
|
||||
result_letter = "W"
|
||||
elif runs_for < runs_against:
|
||||
result_letter = "L"
|
||||
elif runs_for == runs_against:
|
||||
result_letter = "T"
|
||||
font = ImageFont.truetype(font_regular_path, 100)
|
||||
section_info_draw.text(loc, f"FINAL: {result_letter} {runs_for}-{runs_against}", (14,42,28), font=font, anchor="ra")
|
||||
|
||||
# Second line: Date
|
||||
text = "{:%a, %B %-d %-I:%M %p}".format(date).upper()
|
||||
# text = date
|
||||
font = ImageFont.truetype(font_condensed_path, 34)
|
||||
text_size = section_info_draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
355
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
font = ImageFont.truetype(font_regular_path, 80)
|
||||
if home:
|
||||
text = "VS"
|
||||
else:
|
||||
text = "AT"
|
||||
text_size = section_title_draw.textsize(text, font)
|
||||
loc = (
|
||||
540,
|
||||
120
|
||||
)
|
||||
color = (255, 255, 255)
|
||||
section_title_draw.text(loc, text, color, font=font, anchor="mm")
|
||||
|
||||
# Alpha composite the two images together.
|
||||
background_image = Image.alpha_composite(background_image, tmp)
|
||||
|
||||
# Title Image Left
|
||||
title_image_left.thumbnail([350, 350])
|
||||
loc = (
|
||||
50, -50
|
||||
)
|
||||
section_title.paste(title_image_left, loc, title_image_left)
|
||||
|
||||
# Title Image Right
|
||||
title_image_right.thumbnail([350, 350])
|
||||
loc = (
|
||||
650, -50
|
||||
)
|
||||
section_title.paste(title_image_right, loc, title_image_right)
|
||||
|
||||
# background_image.paste(section_info, (llx, ury), section_info)
|
||||
# background_image.paste(section_title, (llx, ury2), section_title)
|
||||
section_title.paste(section_info,(0,0),section_info)
|
||||
section_title.thumbnail([800, 800])
|
||||
|
||||
if background=="badge":
|
||||
return section_title
|
||||
|
||||
background_image.paste(section_title,(
|
||||
int((background_image.size[0]-section_title.size[0])/2),
|
||||
height - 360
|
||||
),section_title)
|
||||
|
||||
# background_image.show()
|
||||
|
||||
return background_image
|
||||
|
||||
# gen_results_image(**args)
|
||||
@@ -1,525 +0,0 @@
|
||||
import django.db.models
|
||||
from typing import List, Tuple
|
||||
|
||||
import benchcoach.models
|
||||
from benchcoach.models import BenchcoachModel, Availability, Player, Team, Positioning, Event, Venue
|
||||
from pyteamsnap.api import TeamSnap
|
||||
import pyteamsnap
|
||||
import teamsnap.models
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from benchcoach.utils.sync_engine import AbstractSyncEngine
|
||||
|
||||
class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
models = [
|
||||
Availability,
|
||||
Player,
|
||||
Team,
|
||||
# Positioning, # Not Implemented
|
||||
Event,
|
||||
Venue
|
||||
]
|
||||
|
||||
def __init__(self, managed_team_teamsnap_id, teamsnap_token):
|
||||
self.managed_teamsnap_team_id = managed_team_teamsnap_id
|
||||
self.client = TeamSnap(token=teamsnap_token)
|
||||
|
||||
def _bulk_sync_from_teamsnap(self, qs:QuerySet)-> List[BenchcoachModel]:
|
||||
'''
|
||||
Syncs BenchCoach instances (in the form of a QuerySet) from TeamSnap.
|
||||
This function fetches the actual information from teamsnap, then hands it off to the self._update* functions.
|
||||
This funciton cuts down on the number of API calls and should be speedier then doing them one by one.
|
||||
Upating of models from the data is still done one by one.
|
||||
:param benchcoach_instance: instance to be synced
|
||||
:return: List of BenchCoach objects that have been processed (but not necessarily changed) during sync.
|
||||
'''
|
||||
|
||||
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
||||
# which would simplify all this, but I couldn't figure out how to implement in the
|
||||
# teamsnap model foreign key and "related_name" that didn't cause conflicts. I don't
|
||||
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||
if qs.model not in self.models:
|
||||
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
||||
|
||||
benchcoachmodel_to_teamsnapfield = {
|
||||
Availability:'teamsnap_availability',
|
||||
Player:'teamsnap_member',
|
||||
Team:'teamsnap_opponent',
|
||||
# Positioning:'teamsnap_lineupentry', # Not Implemented Yet, but will be 'teamsnap_lineupentry'
|
||||
Event:'teamsnap_event',
|
||||
Venue:'teamsnap_location'
|
||||
}
|
||||
|
||||
teamsnapmodel_to_apiobject = {
|
||||
teamsnap.models.Availability: pyteamsnap.api.Availability,
|
||||
teamsnap.models.Event: pyteamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: pyteamsnap.api.Location,
|
||||
teamsnap.models.Member: pyteamsnap.api.Member,
|
||||
teamsnap.models.Opponent: pyteamsnap.api.Opponent,
|
||||
teamsnap.models.Team: pyteamsnap.api.Team,
|
||||
# teamsnap.models.User:pyteamsnap.api.User # Not implemented yet
|
||||
}
|
||||
|
||||
apiobject_to_teamsnapmodel = {v:k for k,v in teamsnapmodel_to_apiobject.items()}
|
||||
|
||||
if isinstance(qs.first(), benchcoach.models.Team):
|
||||
# This situation requires special attention because opponents and teams share a table in BenchCoach
|
||||
if getattr(qs.first(), 'teamsnap_team', None):
|
||||
teamsnap_attribute_name = 'teamsnap_team'
|
||||
elif getattr(qs.first(), 'teamsnap_opponent', None):
|
||||
teamsnap_attribute_name = 'teamsnap_opponent'
|
||||
else:
|
||||
teamsnap_attribute_name = benchcoachmodel_to_teamsnapfield.get(type(qs.first()))
|
||||
|
||||
ids = [getattr(i, teamsnap_attribute_name).id for i in qs]
|
||||
ApiObject = teamsnapmodel_to_apiobject.get(type(getattr(qs.first(), teamsnap_attribute_name)))
|
||||
api_responses = ApiObject.search(client=self.client, id=",".join(ids))
|
||||
r = []
|
||||
for api_response in api_responses:
|
||||
teamsnap_instance = apiobject_to_teamsnapmodel.get(type(api_response)).objects.get(id=api_response.data['id'])
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, api_response)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, teamsnap_instance.benchcoach_object)
|
||||
r.append(response)
|
||||
return r
|
||||
|
||||
def _sync_from_teamsnap(self, benchcoach_instance:BenchcoachModel)->BenchcoachModel:
|
||||
'''
|
||||
Syncs BenchCoach instance from TeamSnap. This function fetches the actual information from teamsnap, then
|
||||
hands it off to the self._update* functions.
|
||||
:param benchcoach_instance: instance to be synced
|
||||
:return: BenchCoach object that has been processed (but not necessarily changed) during sync.
|
||||
'''
|
||||
|
||||
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
||||
# which would simplify all this, but I couldn't figure out how to implement in the
|
||||
# teamsnap model foreign key and "related_name" that didn't cause conflicts. I don't
|
||||
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||
benchcoachmodel_to_teamsnapfield = {
|
||||
Availability:'teamsnap_availability',
|
||||
Player:'teamsnap_member',
|
||||
Team:'teamsnap_opponent',
|
||||
# Positioning:'teamsnap_lineupentry', # Not Implemented Yet, but will be 'teamsnap_lineupentry'
|
||||
Event:'teamsnap_event',
|
||||
Venue:'teamsnap_location'
|
||||
}
|
||||
|
||||
teamsnapmodel_to_apiobject = {
|
||||
teamsnap.models.Availability: pyteamsnap.api.Availability,
|
||||
teamsnap.models.Event: pyteamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: pyteamsnap.api.Location,
|
||||
teamsnap.models.Member: pyteamsnap.api.Member,
|
||||
teamsnap.models.Opponent: pyteamsnap.api.Opponent,
|
||||
teamsnap.models.Team: pyteamsnap.api.Team,
|
||||
# teamsnap.models.User:pyteamsnap.api.User # Not implemented yet
|
||||
}
|
||||
|
||||
if isinstance(benchcoach_instance, benchcoach.models.Team):
|
||||
# This situation requires special attention because opponents and teams share a table in BenchCoach
|
||||
teamsnap_instance = getattr(benchcoach_instance, 'teamsnap_team', None)
|
||||
if not teamsnap_instance: teamsnap_instance = getattr(benchcoach_instance, 'teamsnap_opponent')
|
||||
else:
|
||||
teamsnap_instance = getattr(benchcoach_instance, benchcoachmodel_to_teamsnapfield.get(type(benchcoach_instance)))
|
||||
|
||||
ApiObject = teamsnapmodel_to_apiobject.get(type(teamsnap_instance))
|
||||
api_response = ApiObject.get(self.client, teamsnap_instance.id)
|
||||
|
||||
r = self._update_from_teamsnapdata(teamsnap_instance, api_response)
|
||||
r = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
return r
|
||||
|
||||
def _update_from_teamsnapdata(self, teamsnap_instance:teamsnap.models.TeamsnapBaseModel, teamsnap_data: pyteamsnap.api.ApiObject) -> teamsnap.models.TeamsnapBaseModel:
|
||||
''''''
|
||||
if isinstance(teamsnap_data, pyteamsnap.api.ApiObject):
|
||||
teamsnap_data = teamsnap_data.data
|
||||
else:
|
||||
raise TypeError
|
||||
if not teamsnap_data['type'] == teamsnap_instance.type:
|
||||
raise Exception()
|
||||
data_type = teamsnap_data['type']
|
||||
fields = ['id', 'created_at', 'updated_at']
|
||||
related_objects = {}
|
||||
if data_type in ['opponent', 'team', 'location']:
|
||||
fields += ['name']
|
||||
elif data_type == 'event':
|
||||
fields += [
|
||||
'label',
|
||||
'start_date',
|
||||
'formatted_title',
|
||||
'points_for_opponent',
|
||||
'points_for_team',
|
||||
'is_game',
|
||||
'game_type'
|
||||
]
|
||||
if teamsnap_data.get('location_id'):
|
||||
related_objects['location'] = teamsnap.models.Location.objects.get(id=teamsnap_data['location_id'])
|
||||
if teamsnap_data.get('opponent_id'):
|
||||
related_objects['opponent'] = teamsnap.models.Opponent.objects.get(id=teamsnap_data['opponent_id'])
|
||||
pass
|
||||
|
||||
elif data_type == 'member':
|
||||
fields += [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'jersey_number',
|
||||
'is_non_player'
|
||||
]
|
||||
|
||||
elif data_type == 'availability':
|
||||
fields += ['status_code']
|
||||
related_objects['member'] = teamsnap.models.Member.objects.get(id=teamsnap_data['member_id'])
|
||||
related_objects['event'] = teamsnap.models.Event.objects.get(id=teamsnap_data['event_id'])
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
if teamsnap_data.get('team_id'):
|
||||
related_objects['team'] = teamsnap.models.Team.objects.filter(id=teamsnap_data['team_id']).first()
|
||||
|
||||
for field in fields:
|
||||
value = teamsnap_data.get(field)
|
||||
# if value is None:
|
||||
# continue
|
||||
# else:
|
||||
setattr(teamsnap_instance,field,value)
|
||||
|
||||
for related_object_name, related_object in related_objects.items():
|
||||
setattr(teamsnap_instance, related_object_name, related_object)
|
||||
|
||||
teamsnap_instance.save()
|
||||
|
||||
return teamsnap_instance
|
||||
|
||||
def _update_teamsnapdb_to_benchcoachdb(self, teamsnap_instance, benchcoach_instance) -> List[Tuple[django.db.models.Model, bool]]:
|
||||
|
||||
if isinstance(teamsnap_instance, teamsnap.models.Event):
|
||||
benchcoach_model = Event
|
||||
|
||||
d = {
|
||||
'start': teamsnap_instance.start_date,
|
||||
}
|
||||
|
||||
if teamsnap_instance.team:
|
||||
if teamsnap_instance.team.benchcoach_object:
|
||||
if teamsnap_instance.game_type == "Home":
|
||||
d['home_team'] = teamsnap_instance.team.benchcoach_object
|
||||
elif teamsnap_instance.game_type == "Away":
|
||||
d['away_team'] = teamsnap_instance.team.benchcoach_object
|
||||
elif not teamsnap_instance.team.benchcoach_object:
|
||||
raise Team.DoesNotExist
|
||||
|
||||
if teamsnap_instance.opponent:
|
||||
if teamsnap_instance.opponent.benchcoach_object:
|
||||
if teamsnap_instance.game_type == 'Home':
|
||||
d['away_team'] = teamsnap_instance.opponent.benchcoach_object
|
||||
elif teamsnap_instance.game_type == 'Away':
|
||||
d['home_team'] = teamsnap_instance.opponent.benchcoach_object
|
||||
elif not teamsnap_instance.opponent.benchcoach_object:
|
||||
raise Team.DoesNotExist
|
||||
pass
|
||||
|
||||
if teamsnap_instance.location:
|
||||
if teamsnap_instance.location.benchcoach_object:
|
||||
if teamsnap_instance.location:
|
||||
d['venue'] = teamsnap_instance.location.benchcoach_object
|
||||
elif not teamsnap_instance.location.benchcoach_object:
|
||||
raise Venue.DoesNotExist
|
||||
|
||||
elif isinstance(teamsnap_instance, teamsnap.models.Opponent):
|
||||
benchcoach_model = Team
|
||||
d = {
|
||||
'name': teamsnap_instance.name,
|
||||
}
|
||||
|
||||
elif isinstance(teamsnap_instance, teamsnap.models.Team):
|
||||
benchcoach_model = Team
|
||||
d = {
|
||||
'name': teamsnap_instance.name,
|
||||
}
|
||||
|
||||
elif isinstance(teamsnap_instance, teamsnap.models.Location):
|
||||
benchcoach_model = Venue
|
||||
d = {
|
||||
'name': teamsnap_instance.name,
|
||||
}
|
||||
|
||||
elif isinstance(teamsnap_instance, teamsnap.models.Member):
|
||||
benchcoach_model = Player
|
||||
d = {
|
||||
'first_name': teamsnap_instance.first_name,
|
||||
'last_name': teamsnap_instance.last_name,
|
||||
'jersey_number': teamsnap_instance.jersey_number,
|
||||
}
|
||||
|
||||
elif isinstance(teamsnap_instance, teamsnap.models.Availability):
|
||||
benchcoach_model = Availability
|
||||
|
||||
translation = {
|
||||
teamsnap_instance.YES: Availability.YES,
|
||||
teamsnap_instance.NO: Availability.NO,
|
||||
teamsnap_instance.MAYBE: Availability.MAYBE
|
||||
}
|
||||
|
||||
d = {
|
||||
'available': translation.get(teamsnap_instance.status_code, Availability.UNKNOWN),
|
||||
'player': teamsnap_instance.member.benchcoach_object,
|
||||
'event': teamsnap_instance.event.benchcoach_object
|
||||
}
|
||||
|
||||
r = []
|
||||
|
||||
if teamsnap_instance.member.benchcoach_object:
|
||||
d['player'] = teamsnap_instance.member.benchcoach_object
|
||||
elif not teamsnap_instance.member.benchcoach_object:
|
||||
raise Player.DoesNotExist
|
||||
|
||||
if teamsnap_instance.event.benchcoach_object:
|
||||
d['event'] = teamsnap_instance.event.benchcoach_object
|
||||
elif not teamsnap_instance.event.benchcoach_object:
|
||||
raise Event.DoesNotExist
|
||||
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
for field, value in d.items():
|
||||
setattr(benchcoach_instance, field, value)
|
||||
|
||||
benchcoach_instance.save()
|
||||
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||
teamsnap_instance.save()
|
||||
return benchcoach_instance
|
||||
|
||||
def _find_counterpart(self, instance):
|
||||
'''
|
||||
find the counterpart BenchCoach object from the TeamSnap object.
|
||||
NOT CURRENTLY USED.
|
||||
:param instance:
|
||||
:return:
|
||||
'''
|
||||
instance_type = type(instance)
|
||||
counterpart_instance = None
|
||||
if instance_type == Availability:
|
||||
counterpart_instance = instance.teamsnap_availability
|
||||
|
||||
elif instance_type == Player:
|
||||
counterpart_instance = instance.teamsnap_member
|
||||
|
||||
elif instance_type == Event:
|
||||
counterpart_instance = instance.teamsnap_event
|
||||
|
||||
elif instance_type == Venue:
|
||||
counterpart_instance = instance.teamsnap_location
|
||||
|
||||
elif instance_type == Team:
|
||||
if hasattr(instance, 'teamsnap_opponent'):
|
||||
counterpart_instance = instance.teamsnap_opponent
|
||||
elif hasattr(instance, 'teamsnap_team'):
|
||||
counterpart_instance = instance.teamsnap_team
|
||||
else:
|
||||
raise ValueError("instance doesn't seem to be an teamsnap opponent or a teamsnap team")
|
||||
|
||||
elif instance_type == Positioning:
|
||||
counterpart_instance = instance.teamsnap_lineupentry
|
||||
|
||||
else:
|
||||
raise Exception()
|
||||
|
||||
return counterpart_instance
|
||||
|
||||
def _sync_qs (self, qs, direction):
|
||||
if direction == 'download':
|
||||
if qs.model not in self.models:
|
||||
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
||||
|
||||
r=[]
|
||||
r = self._bulk_sync_from_teamsnap(qs)
|
||||
|
||||
elif direction == 'upload':
|
||||
raise NotImplementedError('Uploading not supported by this sync engine yet.')
|
||||
else:
|
||||
raise TypeError(f"Direction {direction} not supported. 'upload' or 'download' must be specified")
|
||||
|
||||
return r
|
||||
|
||||
def _sync_instance(self, instance, direction, data=None):
|
||||
r=[]
|
||||
if direction == 'download':
|
||||
r.append(self._sync_from_teamsnap(instance))
|
||||
|
||||
elif direction == 'upload':
|
||||
raise NotImplementedError('Uploading not supported by this sync engine yet.')
|
||||
else:
|
||||
raise TypeError(f"Direction {direction} not supported. 'upload' or 'download' must be specified")
|
||||
|
||||
return r
|
||||
|
||||
def sync(self, qs: django.db.models.QuerySet = None, instance: benchcoach.models.BenchcoachModel = None,
|
||||
direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
||||
if not isinstance(qs, QuerySet) and not isinstance(instance, benchcoach.models.BenchcoachModel):
|
||||
raise TypeError(f"sync requires either a QuerySet or model instance to be provided")
|
||||
if qs and instance:
|
||||
raise TypeError(f"sync requires either a QuerySet or model instance to be provided, but not both")
|
||||
elif qs:
|
||||
r = self._sync_qs(qs, direction)
|
||||
elif instance:
|
||||
r = self._sync_instance(instance, direction)
|
||||
|
||||
return r
|
||||
|
||||
def import_items(self):
|
||||
'''
|
||||
Implementation of import items from the abstract base class AbstractSyncEngine.
|
||||
Imports objects from TeamSnap into BenchCoach, creating BenchCoach objects when necessary.
|
||||
It runs through all supported TeamSnap Objects every execution.
|
||||
NOTE: The number of availability objects causes this function to choke, so consider not updating
|
||||
those on import.
|
||||
:return:
|
||||
'''
|
||||
|
||||
['team', 'opponent', 'location', 'member', 'event', 'availability']
|
||||
|
||||
# the common kwargs for searching the API. Most objects just need the client and currently managed team id.
|
||||
kwargs = {'client':self.client,'team_id': self.managed_teamsnap_team_id}
|
||||
|
||||
# r is the result dictionary, the key is the name of the benchcoach object, and the value is the list of BenchCoach objects that
|
||||
# have been iterated (but not necessarily changed) during this import.
|
||||
r = {}
|
||||
|
||||
# Walking through each TeamSnap object. There is a fair amount of repetition that could use clean-up.
|
||||
# ---team---
|
||||
r['team'] = []
|
||||
|
||||
# Search API for objects belonging to currently managed team, and iterate
|
||||
for teamsnap_data in pyteamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
||||
# check if TeamSnap ID already exists in the Teamsnap DB.
|
||||
if teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
# If it does, retrieve the BenchCoach instance attached.
|
||||
# It is enforced (by this import function) that every teamsnap instance has a related BenchCoach instance attached.
|
||||
# No other function can create TeamSnap instances (or create related BenchCoach instances from the TeamSnap service)
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
# Otherwise create TeamSnap instance
|
||||
teamsnap_instance = teamsnap.models.Team()
|
||||
# and create related BenchCoach instance
|
||||
benchcoach_instance = benchcoach.models.Team()
|
||||
# and attach it to the BenchCoach instance
|
||||
teamsnap_instance.benchcoach_object=benchcoach_instance
|
||||
benchcoach_instance.save()
|
||||
# Now, update the data from the API to the retrieved/created instances
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
teamsnap_instance.save()
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['team'].append(response)
|
||||
|
||||
# ---opponent---
|
||||
# See first object for additional comments on the steps followed.
|
||||
# Opponents from teamsnap go to the BenchCoach "Team" database.
|
||||
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
for teamsnap_data in pyteamsnap.api.Opponent.search(**kwargs):
|
||||
if teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
teamsnap_instance = teamsnap.models.Opponent()
|
||||
benchcoach_instance = benchcoach.models.Team()
|
||||
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||
benchcoach_instance.save()
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['team'].append(response)
|
||||
|
||||
# ---location---
|
||||
# See first object for additional comments on the steps followed.
|
||||
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['location'] = []
|
||||
for teamsnap_data in pyteamsnap.api.Location.search(**kwargs):
|
||||
if teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
teamsnap_instance = teamsnap.models.Location()
|
||||
benchcoach_instance = benchcoach.models.Venue()
|
||||
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||
benchcoach_instance.save()
|
||||
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['location'].append(response)
|
||||
|
||||
# ---member---
|
||||
# See first object for additional comments on the steps followed.
|
||||
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['member'] = []
|
||||
# Search API for members to import. Note: Non players are not included in sync.
|
||||
for teamsnap_data in pyteamsnap.api.Member.search(**kwargs,
|
||||
is_non_player = False
|
||||
):
|
||||
if teamsnap_data.data['is_non_player'] == True:
|
||||
continue
|
||||
if teamsnap.models.Member.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Member.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
teamsnap_instance = teamsnap.models.Member()
|
||||
benchcoach_instance = benchcoach.models.Player()
|
||||
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||
benchcoach_instance.save()
|
||||
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['member'].append(response)
|
||||
|
||||
# ---event---
|
||||
# See first object for additional comments on the steps followed.
|
||||
# Dependent on Team, Opponent, Location. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['event'] = []
|
||||
for teamsnap_data in pyteamsnap.api.Event.search(**kwargs):
|
||||
if teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
teamsnap_instance = teamsnap.models.Event()
|
||||
benchcoach_instance = benchcoach.models.Event()
|
||||
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||
benchcoach_instance.save()
|
||||
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['event'].append(response)
|
||||
|
||||
# ---availability---
|
||||
# See first object for additional comments on the steps followed.
|
||||
# Availability was a bit tricky to implement, because there are "not null" contstraints for the Availability object in Bench Coach
|
||||
# Ideally, there probably should be more "not null" constraints on more of the BenchCoach models, so a generalized function should
|
||||
# look more like this than the ones above.
|
||||
# Dependent on Team, Member, Event. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
#TODO this import is wonky and causes errors on servers. maybe skip this import, or do it in chunks?
|
||||
r['availability'] = []
|
||||
|
||||
# Search API for members to import. Note: Non players are not included in sync.
|
||||
player_ids = [member.id for member in teamsnap.models.Member.objects.filter(is_non_player=False)]
|
||||
for teamsnap_data in pyteamsnap.api.Availability.search(**kwargs,
|
||||
member_id=",".join(player_ids)
|
||||
):
|
||||
if teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
else:
|
||||
teamsnap_instance = teamsnap.models.Availability()
|
||||
event_instance = benchcoach.models.Event.objects.get(teamsnap_event__id=teamsnap_data.data['event_id'])
|
||||
player_instance = benchcoach.models.Player.objects.get(teamsnap_member__id=teamsnap_data.data['member_id'])
|
||||
benchcoach_instance = benchcoach.models.Availability(event=event_instance, player=player_instance)
|
||||
benchcoach_instance.save()
|
||||
teamsnap_instance.benchcoach_object_id = benchcoach_instance.id
|
||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
r['availability'].append(response)
|
||||
|
||||
return r
|
||||
@@ -1,51 +0,0 @@
|
||||
from django.test import TestCase
|
||||
import os
|
||||
|
||||
from teamsnap.utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||
|
||||
import benchcoach.models
|
||||
import teamsnap.models
|
||||
|
||||
TEAMSNAP_TOKEN = os.environ['TEAMSNAP_TOKEN']
|
||||
TEAM_TEAMSNAP_ID = os.environ['TEAM_TEAMSNAP_ID']
|
||||
|
||||
pass
|
||||
|
||||
# syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
||||
# r = syncengine.import_items()
|
||||
|
||||
class TestSync(TestCase):
|
||||
fixtures = ['minimal']
|
||||
|
||||
def setUp(self):
|
||||
self.syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
||||
r = self.syncengine.import_items()
|
||||
pass
|
||||
|
||||
def test_syncengine(self):
|
||||
# test that the import can be run again
|
||||
r = self.syncengine.import_items()
|
||||
benchcoach_objects = {
|
||||
'availability': list(benchcoach.models.Availability.objects.all()),
|
||||
'event': list(benchcoach.models.Event.objects.all()),
|
||||
'player': list(benchcoach.models.Player.objects.all()),
|
||||
'positioning': list(benchcoach.models.Positioning.objects.all()),
|
||||
'team': list(benchcoach.models.Team.objects.all()),
|
||||
'venue': list(benchcoach.models.Venue.objects.all())
|
||||
}
|
||||
teamsnap_objects = {
|
||||
'availability': list(teamsnap.models.Availability.objects.all()),
|
||||
'event': list(teamsnap.models.Event.objects.all()),
|
||||
'member': list(teamsnap.models.Member.objects.all()),
|
||||
'lineupentry': list(teamsnap.models.LineupEntry.objects.all()),
|
||||
'team': list(teamsnap.models.Team.objects.all()),
|
||||
'opponent': list(teamsnap.models.Opponent.objects.all()),
|
||||
'location': list(teamsnap.models.Location.objects.all())
|
||||
}
|
||||
self.assertIsNotNone(r)
|
||||
|
||||
def test_all_models(self):
|
||||
pass
|
||||
self.syncengine.sync(qs=benchcoach.models.Event.objects.all())
|
||||
breakpoint()
|
||||
pass
|
||||
@@ -1,553 +1,398 @@
|
||||
import operator
|
||||
import time
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability
|
||||
from django.http import HttpResponse, HttpResponseNotAllowed
|
||||
import benchcoach.models
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||
from django.templatetags.static import static
|
||||
import datetime
|
||||
from datetime import timezone
|
||||
import re
|
||||
|
||||
@login_required()
|
||||
def edit_event(request, id):
|
||||
'''
|
||||
redirect to teamsnap.com page for editing of event.
|
||||
:param request:
|
||||
:param id:
|
||||
:return:
|
||||
'''
|
||||
event = Event.objects.get(id = id)
|
||||
return redirect(event.edit_url)
|
||||
import requests
|
||||
from allauth.socialaccount.providers.oauth2.views import (
|
||||
OAuth2Adapter,
|
||||
OAuth2CallbackView,
|
||||
OAuth2LoginView,
|
||||
)
|
||||
from django.shortcuts import redirect, render
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
@login_required()
|
||||
def home(request):
|
||||
current_benchcoach_user = request.user
|
||||
current_teamsnap_user = request.user.profile.teamsnap_user
|
||||
current_teamsnap_team = request.user.profile.teamsnapsettings.managed_team
|
||||
teamsnap_objects = {}
|
||||
for teamsnap_obj, benchcoach_object in [
|
||||
(Availability, benchcoach.models.Availability),
|
||||
(Event, benchcoach.models.Event),
|
||||
(LineupEntry, benchcoach.models.Positioning),
|
||||
(Location, benchcoach.models.Venue),
|
||||
(Member, benchcoach.models.Player),
|
||||
(Opponent, benchcoach.models.Team),
|
||||
(Team, benchcoach.models.Team),
|
||||
# (User, {'name':})
|
||||
]:
|
||||
teamsnap_objects[teamsnap_obj.__name__.lower()] = {}
|
||||
teamsnap_objects[teamsnap_obj.__name__.lower()]['object_count'] = teamsnap_obj.objects.count()
|
||||
if benchcoach_object:
|
||||
teamsnap_objects[teamsnap_obj.__name__.lower()]['counterpart'] = {'name':benchcoach_object.__name__.lower()}
|
||||
teamsnap_objects[teamsnap_obj.__name__.lower()]['counterpart']['object_count'] = benchcoach_object.objects.count()
|
||||
from .forms import PreferencesForm
|
||||
from .models import Preferences
|
||||
from .provider import TeamsnapProvider
|
||||
|
||||
context= {
|
||||
'benchcoach_user': current_benchcoach_user,
|
||||
'teamsnap_user': current_teamsnap_user,
|
||||
'teamsnap_team':current_teamsnap_team,
|
||||
'teamsnap_objects': teamsnap_objects
|
||||
}
|
||||
return render(request, 'teamsnap/home.html', context)
|
||||
|
||||
@login_required()
|
||||
def dashboard(request, team_id):
|
||||
current_benchcoach_user = request.user
|
||||
current_teamsnap_user = request.user.profile.teamsnap_user
|
||||
current_teamsnap_team = request.user.profile.teamsnapsettings.managed_team
|
||||
teamsnap_objects = {}
|
||||
class TeamsnapAdapter(OAuth2Adapter):
|
||||
provider_id = TeamsnapProvider.id
|
||||
|
||||
TEAM_ID = team_id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
no_past = bool(request.GET.get('no_past', 0))
|
||||
games_only = bool(request.GET.get('games_only', 0))
|
||||
from pyteamsnap.api import TeamSnap, Event, AvailabilitySummary
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(.5)
|
||||
ts_events = Event.search(client, team_id=TEAM_ID)
|
||||
ts_availability_summaries_d = {a.data['id']:a for a in AvailabilitySummary.search(client, team_id=team_id)}
|
||||
ts_events_future = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)]
|
||||
ts_events_past = [e for e in reversed(ts_events) if e.data['start_date'] < datetime.datetime.now(datetime.timezone.utc)]
|
||||
# Fetched programmatically, must be reachable from container
|
||||
access_token_url = "{}/oauth/token/".format("https://auth.teamsnap.com")
|
||||
profile_url = "{}/me/".format("https://api.teamsnap.com/v3/")
|
||||
|
||||
return render(request, 'teamsnap/dashboard.html', {
|
||||
'team_id':team_id,
|
||||
'ts_events_future':ts_events_future,
|
||||
'ts_events_past': ts_events_past,
|
||||
'events_availabilities' : [(e, ts_availability_summaries_d[e.data['id']]) for e in ts_events_future]
|
||||
})
|
||||
# Accessed by the user browser, must be reachable by the host
|
||||
authorize_url = "{}/oauth/authorize/".format("https://auth.teamsnap.com/")
|
||||
|
||||
@login_required()
|
||||
def sync_from_teamsnap(request, object_name=None, object_id=None):
|
||||
if request.POST:
|
||||
next = request.POST.get('next')
|
||||
object_name = request.POST.get('object_name')
|
||||
object_id = request.POST.get('object_id')
|
||||
# NOTE: trailing slashes in URLs are important, don't miss it
|
||||
|
||||
Object = {
|
||||
obj.__name__.lower(): obj
|
||||
for obj in
|
||||
[Availability, Event, LineupEntry, Location, Member, Opponent, Team, User]
|
||||
}.get(object_name)
|
||||
def complete_login(self, request, app, token, **kwargs):
|
||||
headers = {"Authorization": f"Bearer {token.token}"}
|
||||
resp = requests.get(self.profile_url, headers=headers)
|
||||
j = resp.json()
|
||||
if j.get("collection", {}).get("items"):
|
||||
extra_data = {
|
||||
i["name"]: i["value"] for i in j["collection"]["items"][0]["data"]
|
||||
}
|
||||
return self.get_provider().sociallogin_from_response(request, extra_data)
|
||||
|
||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
def populate_user(self, request, sociallogin, data):
|
||||
user = super().populate_user(request, sociallogin, data)
|
||||
user.username = user.email
|
||||
return user
|
||||
|
||||
sync_engine = TeamsnapSyncEngine(teamsnap_token=TOKEN, managed_team_teamsnap_id=TEAM_ID)
|
||||
r = {}
|
||||
# def get_callback_url(self, request, app):
|
||||
# callback_url = reverse(self.provider_id + "_callback")
|
||||
# protocol = self.redirect_uri_protocol
|
||||
# return build_absolute_uri(request, callback_url, protocol)
|
||||
# return "urn:ietf:wg:oauth:2.0:oob"
|
||||
|
||||
r[object_name]=[]
|
||||
|
||||
if object_name == 'team':
|
||||
if object_id:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Team.objects.filter(id=object_id))
|
||||
else:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Team.objects.all())
|
||||
oauth2_login = OAuth2LoginView.adapter_view(TeamsnapAdapter)
|
||||
oauth2_callback = OAuth2CallbackView.adapter_view(TeamsnapAdapter)
|
||||
|
||||
if object_name == 'venue':
|
||||
if object_id:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Venue.objects.filter(id=object_id))
|
||||
else:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Venue.objects.all())
|
||||
|
||||
if object_name == 'player':
|
||||
if object_id:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Player.objects.filter(id=object_id))
|
||||
else:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Player.objects.all())
|
||||
class PreferencesFormView(FormView):
|
||||
template_name = "preferences.html"
|
||||
form_class = PreferencesForm
|
||||
success_url = "/"
|
||||
|
||||
if object_name == 'event':
|
||||
if object_id:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Event.objects.filter(id=object_id))
|
||||
r['availability'] = sync_engine.sync(qs=benchcoach.models.Event.objects.get(id=object_id).availability_set.all())
|
||||
else:
|
||||
r[object_name] = sync_engine.sync(qs=benchcoach.models.Event.objects.all())
|
||||
def form_valid(self, form):
|
||||
# This method is called when valid form data has been POSTed.
|
||||
# It should return an HttpResponse.
|
||||
if form.data["user"] == str(self.request.user.id):
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
if object_name == 'availability':
|
||||
r[object_name] = []
|
||||
if object_id:
|
||||
r[object_name] += sync_engine.sync(qs=benchcoach.models.Availability.objects.filter(id=object_id))
|
||||
else:
|
||||
for event in benchcoach.models.Player.objects.all():
|
||||
r[object_name] += sync_engine.sync(qs=event.availability_set.all())
|
||||
def get_initial(self):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
initial = super().get_initial()
|
||||
|
||||
for object_name, results in r.items():
|
||||
if len(results) == 0:
|
||||
messages.error(request, f"Error! No {object_name} objects updated")
|
||||
else:
|
||||
messages.success(request, f"Success! {len(results)} {object_name} objects updated.")
|
||||
initial["user"] = self.request.user
|
||||
# initial['managed_team_id']
|
||||
|
||||
return redirect(next)
|
||||
else:
|
||||
return HttpResponse(404)
|
||||
return initial
|
||||
|
||||
@login_required()
|
||||
def import_teamsnap(request):
|
||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
def get_form(self):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
import pyteamsnap
|
||||
|
||||
sync_engine = TeamsnapSyncEngine(teamsnap_token=TOKEN, managed_team_teamsnap_id=TEAM_ID)
|
||||
r = sync_engine.import_items()
|
||||
ts_account = self.request.user.socialaccount_set.first()
|
||||
ts_token = ts_account.socialtoken_set.first()
|
||||
# ts_token =
|
||||
ts = pyteamsnap.TeamSnap(token=ts_token)
|
||||
|
||||
for object_name, results in r.items():
|
||||
if len(results) == 0:
|
||||
messages.error(request, f"Error! No {object_name} objects created or updated")
|
||||
else:
|
||||
messages.success(request, f"Success! {len(results)} {object_name} objects imported")
|
||||
me = pyteamsnap.api.Me(ts)
|
||||
|
||||
return redirect('teamsnap home')
|
||||
teams = [
|
||||
(id, pyteamsnap.api.Team.get(ts, id=id))
|
||||
for id in me.data["managed_team_ids"]
|
||||
]
|
||||
|
||||
@login_required()
|
||||
def schedule(request, team_id):
|
||||
TEAM_ID = team_id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
no_past = bool(request.GET.get('no_past', 0))
|
||||
games_only = bool(request.GET.get('games_only',0))
|
||||
from pyteamsnap.api import TeamSnap, Event, Location, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(.5)
|
||||
ts_events = Event.search(client, team_id=TEAM_ID)
|
||||
try:
|
||||
contact = Preferences.objects.get(user=self.request.user)
|
||||
form = PreferencesForm(instance=contact, **self.get_form_kwargs())
|
||||
except Preferences.DoesNotExist:
|
||||
form = super().get_form(self.form_class)
|
||||
|
||||
choices = [
|
||||
(id, f"{team.data['name']} ({team.data['season_name']})")
|
||||
for id, team in teams
|
||||
]
|
||||
form.fields["managed_team_id"].widget.choices = choices
|
||||
|
||||
return form
|
||||
|
||||
|
||||
def schedule_view(request, team_id=None):
|
||||
if not team_id:
|
||||
return redirect(
|
||||
"teamsnap_schedule", team_id=request.user.preferences.managed_team_id
|
||||
)
|
||||
request.user.socialaccount_set.filter(provider="teamsnap").first()
|
||||
current_teamsnap_user = request.user.socialaccount_set.filter(
|
||||
provider="teamsnap"
|
||||
).first()
|
||||
|
||||
ts_token = (
|
||||
current_teamsnap_user.socialtoken_set.order_by("-expires_at").first().token
|
||||
)
|
||||
no_past = bool(request.GET.get("no_past", 0))
|
||||
games_only = bool(request.GET.get("games_only", 0))
|
||||
from pyteamsnap.api import Event, TeamSnap
|
||||
|
||||
client = TeamSnap(token=ts_token)
|
||||
ts_events = Event.search(client, team_id=team_id)
|
||||
if no_past:
|
||||
ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)]
|
||||
ts_events = [
|
||||
e
|
||||
for e in ts_events
|
||||
if e.data["start_date"] > datetime.datetime.now(datetime.timezone.utc)
|
||||
]
|
||||
if games_only:
|
||||
ts_events = [e for e in ts_events if e.data['is_game']]
|
||||
ts_events = {e.data['id']:e for e in ts_events}
|
||||
# ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)}
|
||||
# ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)}
|
||||
# for event in ts_events:
|
||||
ts_events = [e for e in ts_events if e.data["is_game"]]
|
||||
ts_events = {e.data["id"]: e for e in ts_events}
|
||||
|
||||
pass
|
||||
return render(request, "teamsnap/schedule.html", context={"events":ts_events.values(), "team_id":team_id})
|
||||
return render(
|
||||
request,
|
||||
"schedule.html",
|
||||
context={"events": ts_events.values(), "team_id": team_id},
|
||||
)
|
||||
|
||||
@login_required()
|
||||
def event(request, event_id, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_bulkload = client.bulk_load(team_id=team_id,
|
||||
types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member],
|
||||
event__id=event_id)
|
||||
def view_event(request, event_id, team_id=None):
|
||||
if not team_id:
|
||||
return redirect(
|
||||
"teamsnap_event", team_id=request.user.preferences.managed_team_id
|
||||
)
|
||||
request.user.socialaccount_set.filter(provider="teamsnap").first()
|
||||
current_teamsnap_user = request.user.socialaccount_set.filter(
|
||||
provider="teamsnap"
|
||||
).first()
|
||||
|
||||
ts_token = (
|
||||
current_teamsnap_user.socialtoken_set.order_by("-expires_at").first().token
|
||||
)
|
||||
|
||||
from pyteamsnap.api import (
|
||||
AvailabilitySummary,
|
||||
Event,
|
||||
EventLineup,
|
||||
EventLineupEntry,
|
||||
Member,
|
||||
TeamSnap,
|
||||
)
|
||||
|
||||
client = TeamSnap(token=ts_token)
|
||||
ts_bulkload = client.bulk_load(
|
||||
team_id=team_id,
|
||||
types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member],
|
||||
event__id=event_id,
|
||||
)
|
||||
ts_event = [i for i in ts_bulkload if isinstance(i, Event)][0]
|
||||
# ts_availabilities = Availability.search(client, event_id=ts_event.data['id'])
|
||||
ts_availability_summary = \
|
||||
[i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0]
|
||||
ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id]
|
||||
ts_availability_summary = [
|
||||
i
|
||||
for i in ts_bulkload
|
||||
if isinstance(i, AvailabilitySummary) and i.data["event_id"] == event_id
|
||||
][0]
|
||||
ts_lineup_entries = [
|
||||
i
|
||||
for i in ts_bulkload
|
||||
if isinstance(i, EventLineupEntry) and i.data["event_id"] == event_id
|
||||
]
|
||||
|
||||
ts_members = [i for i in ts_bulkload if isinstance(i, Member)]
|
||||
ts_member_lookup = {m.data['id']: m for m in ts_members}
|
||||
# ts_availability_lookup = {m.data['member_id']: m for m in ts_availabilities}
|
||||
ts_lineup_entries_lookup = {m.data['member_id']: m for m in ts_lineup_entries}
|
||||
return render(
|
||||
request,
|
||||
"event/view_event.html",
|
||||
context={
|
||||
"availability_summary": ts_availability_summary,
|
||||
"event": ts_event,
|
||||
"availablities": [],
|
||||
"lineup_entries": ts_lineup_entries,
|
||||
},
|
||||
)
|
||||
|
||||
members = []
|
||||
|
||||
return render(request, "teamsnap/event/view_event.html", context={
|
||||
"availability_summary":ts_availability_summary,
|
||||
"event":ts_event,
|
||||
"availablities":[],
|
||||
"lineup_entries": ts_lineup_entries,
|
||||
})
|
||||
|
||||
@login_required()
|
||||
def location(request, id, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Location
|
||||
client = TeamSnap(token=TOKEN)
|
||||
return render(request, "teamsnap/location/view.html", context={"location": Location.get(client, id=id)})
|
||||
pass
|
||||
|
||||
@login_required()
|
||||
def opponent(request, team_id, id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Opponent
|
||||
time.sleep(0.5)
|
||||
client = TeamSnap(token=TOKEN)
|
||||
return render(request, "teamsnap/opponent.html", context={"opponent": Opponent.get(client, id=id)})
|
||||
pass
|
||||
|
||||
@login_required()
|
||||
def edit_lineup(request, event_ids, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
from django.forms import formset_factory
|
||||
from teamsnap.forms import EventChooseForm
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
import re
|
||||
|
||||
from teamsnap.forms import LineupEntryFormset
|
||||
|
||||
request.user.socialaccount_set.filter(provider="teamsnap").first()
|
||||
current_teamsnap_user = request.user.socialaccount_set.filter(
|
||||
provider="teamsnap"
|
||||
).first()
|
||||
ts_token = (
|
||||
current_teamsnap_user.socialtoken_set.order_by("-expires_at").first().token
|
||||
)
|
||||
|
||||
from pyteamsnap.api import (
|
||||
Availability,
|
||||
AvailabilitySummary,
|
||||
Event,
|
||||
EventLineup,
|
||||
EventLineupEntry,
|
||||
Member,
|
||||
TeamSnap,
|
||||
)
|
||||
|
||||
client = TeamSnap(token=ts_token)
|
||||
|
||||
event_ids = str(event_ids).split(",")
|
||||
|
||||
ts_bulkload = client.bulk_load(team_id=team_id,
|
||||
types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member],
|
||||
event__id=",".join(event_ids))
|
||||
ts_bulkload = client.bulk_load(
|
||||
team_id=team_id,
|
||||
types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member],
|
||||
event__id=",".join(event_ids),
|
||||
)
|
||||
event_ids = [int(i) for i in event_ids]
|
||||
formsets_lineup = []
|
||||
formsets_bench = []
|
||||
formsets = []
|
||||
events = []
|
||||
|
||||
contexts = []
|
||||
for event_id in event_ids:
|
||||
ts_event = [i for i in ts_bulkload if isinstance(i, Event) and i.data['id']==event_id][0]
|
||||
ts_availabilities = Availability.search(client, event_id=ts_event.data['id'])
|
||||
ts_availability_summary = \
|
||||
[i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0]
|
||||
ts_event = [
|
||||
i for i in ts_bulkload if isinstance(i, Event) and i.data["id"] == event_id
|
||||
][0]
|
||||
ts_availabilities = Availability.search(client, event_id=ts_event.data["id"])
|
||||
ts_lineup_entries = EventLineupEntry.search(client, event_id=event_id)
|
||||
|
||||
if ts_lineup_entries:
|
||||
ts_lineup = EventLineup.get(client, id=ts_lineup_entries[0].data['event_lineup_id'])
|
||||
else:
|
||||
ts_lineup = EventLineup.search(client, event_id=event_id)
|
||||
|
||||
ts_members = [i for i in ts_bulkload if isinstance(i, Member)]
|
||||
ts_member_lookup = {m.data['id']: m for m in ts_members}
|
||||
ts_availability_lookup = {m.data['member_id']: m for m in ts_availabilities}
|
||||
ts_lineup_entries_lookup = {m.data['member_id']: m for m in ts_lineup_entries}
|
||||
ts_member_lookup = {m.data["id"]: m for m in ts_members}
|
||||
ts_availability_lookup = {m.data["member_id"]: m for m in ts_availabilities}
|
||||
ts_lineup_entries_lookup = {m.data["member_id"]: m for m in ts_lineup_entries}
|
||||
|
||||
members=[]
|
||||
members = []
|
||||
|
||||
for member in ts_members:
|
||||
members.append ({
|
||||
"member":getattr(member, 'data'),
|
||||
"availability": getattr(ts_availability_lookup.get(member.data['id'], {}), 'data', {}),
|
||||
"lineup_entry": getattr(ts_lineup_entries_lookup.get(member.data['id'], {}), 'data', {})
|
||||
}
|
||||
members.append(
|
||||
{
|
||||
"member": getattr(member, "data"),
|
||||
"availability": getattr(
|
||||
ts_availability_lookup.get(member.data["id"], {}), "data", {}
|
||||
),
|
||||
"lineup_entry": getattr(
|
||||
ts_lineup_entries_lookup.get(member.data["id"], {}), "data", {}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
members = sorted(members, key=lambda d: (
|
||||
{
|
||||
None:3, # No Response
|
||||
0:2, # No
|
||||
2:1, # Maybe
|
||||
1:0 # Yes
|
||||
|
||||
}.get(d['availability'].get('status_code')),
|
||||
d['member'].get('last_name'))
|
||||
)
|
||||
|
||||
from teamsnap.forms import LineupEntryFormset, LineupEntryForm
|
||||
|
||||
members = sorted(
|
||||
members,
|
||||
key=lambda d: (
|
||||
{None: 3, 0: 2, 2: 1, 1: 0}.get( # No Response # No # Maybe # Yes
|
||||
d["availability"].get("status_code")
|
||||
),
|
||||
d["member"].get("last_name"),
|
||||
),
|
||||
)
|
||||
|
||||
initial = []
|
||||
for member in members:
|
||||
if not member['member']['is_non_player']:
|
||||
initial_member = {}
|
||||
if re.search(r'([A-Z0-9]+)(?:\s+\[(.*)\])?', member['lineup_entry'].get('label','')):
|
||||
position, position_note = re.search(r'([A-Z0-9]+)(?:\s+\[(.*)\])?', member['lineup_entry'].get('label','')).groups()
|
||||
if not member["member"]["is_non_player"]:
|
||||
if re.search(
|
||||
r"([A-Z0-9]+)(?:\s+\[(.*)\])?",
|
||||
member["lineup_entry"].get("label", ""),
|
||||
):
|
||||
position, position_note = re.search(
|
||||
r"([A-Z0-9]+)(?:\s+\[(.*)\])?",
|
||||
member["lineup_entry"].get("label", ""),
|
||||
).groups()
|
||||
else:
|
||||
position, position_note = ("","")
|
||||
position, position_note = ("", "")
|
||||
position_only = position_note == "PO"
|
||||
initial.append({
|
||||
"event_lineup_entry_id": member['lineup_entry'].get('id'),
|
||||
"event_lineup_id": member['lineup_entry'].get('event_lineup_id'),
|
||||
"event_id": event_id,
|
||||
"position_only": position_only,
|
||||
"member_id": member['member']['id'],
|
||||
"sequence": member['lineup_entry'].get('sequence'),
|
||||
"label": position,
|
||||
}
|
||||
|
||||
initial.append(
|
||||
{
|
||||
"event_lineup_entry_id": member["lineup_entry"].get("id"),
|
||||
"event_lineup_id": member["lineup_entry"].get(
|
||||
"event_lineup_id"
|
||||
),
|
||||
"event_id": event_id,
|
||||
"position_only": position_only,
|
||||
"member_id": member["member"]["id"],
|
||||
"sequence": member["lineup_entry"].get("sequence"),
|
||||
"label": position,
|
||||
}
|
||||
)
|
||||
|
||||
formset = LineupEntryFormset(
|
||||
initial=initial
|
||||
)
|
||||
formset = LineupEntryFormset(initial=initial)
|
||||
|
||||
for form in formset:
|
||||
form.member = ts_member_lookup.get(form['member_id'].initial)
|
||||
form.availability = ts_availability_lookup.get(form['member_id'].initial)
|
||||
form.member = ts_member_lookup.get(form["member_id"].initial)
|
||||
form.availability = ts_availability_lookup.get(form["member_id"].initial)
|
||||
|
||||
formset_startinglineup = [form for form in formset if form.initial.get('event_lineup_entry_id') and not form.initial.get('position_only')]
|
||||
formset_startinglineup = [
|
||||
form
|
||||
for form in formset
|
||||
if form.initial.get("event_lineup_entry_id")
|
||||
and not form.initial.get("position_only")
|
||||
]
|
||||
formset_startinglineup = sorted(
|
||||
formset_startinglineup,
|
||||
key=lambda d: d.initial.get('sequence',100)
|
||||
formset_startinglineup, key=lambda d: d.initial.get("sequence", 100)
|
||||
)
|
||||
formset_startingpositiononly = [form for form in formset if
|
||||
form.initial.get('event_lineup_entry_id') and form not in formset_startinglineup]
|
||||
formset_startingpositiononly = [
|
||||
form
|
||||
for form in formset
|
||||
if form.initial.get("event_lineup_entry_id")
|
||||
and form not in formset_startinglineup
|
||||
]
|
||||
formset_startingpositiononly = sorted(
|
||||
formset_startingpositiononly,
|
||||
key=lambda d: d.initial.get('sequence', 100)
|
||||
formset_startingpositiononly, key=lambda d: d.initial.get("sequence", 100)
|
||||
)
|
||||
formset_bench = [form for form in formset if
|
||||
form not in formset_startinglineup and
|
||||
form not in formset_startingpositiononly and
|
||||
form.availability.data['status_code'] in [2, 1]
|
||||
]
|
||||
formset_out = [form for form in formset if
|
||||
form not in formset_startinglineup and
|
||||
form not in formset_bench and
|
||||
form not in formset_startingpositiononly and
|
||||
not form.member.data['is_non_player']
|
||||
]
|
||||
formset_bench = [
|
||||
form
|
||||
for form in formset
|
||||
if form not in formset_startinglineup
|
||||
and form not in formset_startingpositiononly
|
||||
and form.availability.data["status_code"] in [2, 1]
|
||||
]
|
||||
formset_out = [
|
||||
form
|
||||
for form in formset
|
||||
if form not in formset_startinglineup
|
||||
and form not in formset_bench
|
||||
and form not in formset_startingpositiononly
|
||||
and not form.member.data["is_non_player"]
|
||||
]
|
||||
|
||||
contexts.append({
|
||||
"event":ts_event,
|
||||
"formset": formset,
|
||||
"formset_bench":formset_bench,
|
||||
"formset_startinglineup":formset_startinglineup,
|
||||
"formset_startingpositionalonly":formset_startingpositiononly,
|
||||
"formset_out":formset_out
|
||||
})
|
||||
contexts.append(
|
||||
{
|
||||
"event": ts_event,
|
||||
"formset": formset,
|
||||
"formset_bench": formset_bench,
|
||||
"formset_startinglineup": formset_startinglineup,
|
||||
"formset_startingpositionalonly": formset_startingpositiononly,
|
||||
"formset_out": formset_out,
|
||||
}
|
||||
)
|
||||
|
||||
return render(request, "teamsnap/lineup/multiple_edit.html", context={
|
||||
"team_id": team_id,
|
||||
"contexts":contexts
|
||||
})
|
||||
return render(
|
||||
request, "lineup/multiple_edit.html", context={"contexts": contexts}
|
||||
)
|
||||
|
||||
@login_required()
|
||||
def submit_lineup(request, team_id, event_id):
|
||||
from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup
|
||||
from teamsnap.forms import LineupEntryFormset
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_lineup = EventLineup.search(client, event_id=event_id)
|
||||
event_lineup_id = ts_lineup[0].data['id']
|
||||
if request.GET:
|
||||
return HttpResponseNotAllowed()
|
||||
if request.POST:
|
||||
formset = LineupEntryFormset(request.POST)
|
||||
if formset.is_valid():
|
||||
r = []
|
||||
for form in formset:
|
||||
data = form.cleaned_data
|
||||
if data.get('event_lineup_entry_id'):
|
||||
event_lineup_entry = EventLineupEntry.get(client, id=data.get('event_lineup_entry_id'))
|
||||
if data.get('position_only'):
|
||||
data['label'] = data['label'] + ' [PO]'
|
||||
event_lineup_entry.data.update(data)
|
||||
if not data.get('sequence') and not data.get('label'):
|
||||
try:
|
||||
r.append(event_lineup_entry.delete())
|
||||
except Exception as e:
|
||||
raise e
|
||||
else:
|
||||
try:
|
||||
r.append(event_lineup_entry.put())
|
||||
except:
|
||||
pass
|
||||
pass
|
||||
elif data.get('sequence') is not None and data.get('label'):
|
||||
event_lineup_entry = EventLineupEntry.new(client)
|
||||
if data.get('position_only'):
|
||||
data['label'] = data['label'] + ' [PO]'
|
||||
event_lineup_entry.data.update(data)
|
||||
event_lineup_entry.data.update({"event_lineup_id": event_lineup_id})
|
||||
try:
|
||||
r.append(event_lineup_entry.post())
|
||||
except Exception as e:
|
||||
raise e
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
# breakpoint()
|
||||
pass
|
||||
# breakpoint()
|
||||
pass
|
||||
return HttpResponse(f'{r}')
|
||||
pass
|
||||
return HttpResponse(f'{team_id} {event_id}')
|
||||
|
||||
@login_required()
|
||||
def image_generator(request, team_id, event_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
def dashboard(request, team_id=None):
|
||||
if not team_id:
|
||||
return redirect(
|
||||
"teamsnap_dashboard", team_id=request.user.preferences.managed_team_id
|
||||
)
|
||||
request.user.socialaccount_set.filter(provider="teamsnap").first()
|
||||
current_teamsnap_user = request.user.socialaccount_set.filter(
|
||||
provider="teamsnap"
|
||||
).first()
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_token = (
|
||||
current_teamsnap_user.socialtoken_set.order_by("-expires_at").first().token
|
||||
)
|
||||
from pyteamsnap.api import AvailabilitySummary, Event, TeamSnap
|
||||
|
||||
ts_event = Event.get(client, id=event_id)
|
||||
return render(request, "teamsnap/event/instagen.html", context = {"event":ts_event})
|
||||
client = TeamSnap(token=ts_token)
|
||||
ts_events = Event.search(client, team_id=team_id)
|
||||
ts_availability_summaries_d = {
|
||||
a.data["id"]: a for a in AvailabilitySummary.search(client, team_id=team_id)
|
||||
}
|
||||
ts_events_future = [
|
||||
e
|
||||
for e in ts_events
|
||||
if e.data["start_date"] > datetime.datetime.now(datetime.timezone.utc)
|
||||
]
|
||||
ts_events_past = [
|
||||
e
|
||||
for e in reversed(ts_events)
|
||||
if e.data["start_date"] < datetime.datetime.now(datetime.timezone.utc)
|
||||
]
|
||||
|
||||
@login_required()
|
||||
def get_matchup_image(request, team_id, event_id, dimensions=None, background=None):
|
||||
from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup, Event, Team, Opponent, Location
|
||||
from .utils.gen_image import Team as ImagegenTeam, Location as ImagegenLocation
|
||||
from .utils.gen_image import gen_image, gen_results_image
|
||||
import io
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
if request.GET:
|
||||
POSTPONED = request.GET.get('postponed', 'false') == 'true'
|
||||
INCLUDE_WINLOSS = request.GET.get('winloss', 'false') == 'true'
|
||||
BACKGROUND = request.GET.get('background', 'location')
|
||||
game_id = event_id
|
||||
dimensions = request.GET.get('dimensions')
|
||||
width = int(dimensions.split("x")[0])
|
||||
height = int(dimensions.split("x")[1])
|
||||
|
||||
teamsnap = TeamSnap(TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_event = Event.get(teamsnap, game_id).data
|
||||
fave_team = Team.get(teamsnap, ts_event['team_id']).data
|
||||
opponent_team = Opponent.get(teamsnap, ts_event['opponent_id']).data
|
||||
location = Location.get(teamsnap, ts_event['location_id']).data
|
||||
formatted_results = ts_event['formatted_results']
|
||||
if formatted_results:
|
||||
# L 4-3
|
||||
runs_for = formatted_results.split(" ")[1].split("-")[0]
|
||||
runs_against = formatted_results.split(" ")[1].split("-")[1]
|
||||
else:
|
||||
runs_for, runs_against = None, None
|
||||
|
||||
logo_image_directory = 'benchcoachproject/static/teamsnap/ig/logos-bw/{filename}.{ext}'
|
||||
venue_image_directory = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}'
|
||||
|
||||
def shortname_from_name(name):
|
||||
return name.replace(" ", "").lower()
|
||||
|
||||
# date = parser.parse(ts_event['start_date'])
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
game_info = {
|
||||
"date": ts_event['start_date'].astimezone(tz=datetime.datetime.now(timezone.utc).astimezone().tzinfo),
|
||||
"team_fave": ImagegenTeam(
|
||||
name=fave_team["name"],
|
||||
image_directory=logo_image_directory.format(filename=shortname_from_name(fave_team["name"]), ext="png")
|
||||
),
|
||||
"team_opponent": ImagegenTeam(
|
||||
name=opponent_team["name"],
|
||||
image_directory=logo_image_directory.format(filename=shortname_from_name(opponent_team["name"]),
|
||||
ext="png")
|
||||
),
|
||||
"location": ImagegenLocation(
|
||||
name=location['name'],
|
||||
image_directory=venue_image_directory.format(filename=shortname_from_name(location["name"]), ext="png"),
|
||||
# address=location['address']
|
||||
),
|
||||
"runs_for": runs_for,
|
||||
"runs_against": runs_against
|
||||
}
|
||||
|
||||
if not game_info['runs_for'] and not game_info['runs_against']:
|
||||
image = gen_image(**game_info, background=BACKGROUND, width=width, height=height)
|
||||
elif game_info['runs_for'] and game_info['runs_against']:
|
||||
image = gen_results_image(**game_info, background=BACKGROUND, width=width, height=height)
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
imgByteArr = io.BytesIO()
|
||||
image.save(imgByteArr, format='PNG')
|
||||
imgByteArr = imgByteArr.getvalue()
|
||||
|
||||
return HttpResponse(imgByteArr, content_type="image/png")
|
||||
|
||||
@login_required()
|
||||
def multi_lineup_choose(request, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
from teamsnap.forms import EventChooseForm
|
||||
from django.forms import formset_factory
|
||||
from pyteamsnap.api import TeamSnap, Event
|
||||
client = TeamSnap(token=TOKEN)
|
||||
|
||||
if request.POST:
|
||||
ts_events = Event.search(client, team_id=team_id)
|
||||
EventChooseFormset = formset_factory(EventChooseForm)
|
||||
formset = EventChooseFormset(request.POST)
|
||||
choices = [(e.data['id'], e.data['formatted_title']) for e in ts_events]
|
||||
|
||||
for form in formset:
|
||||
form.fields['event_id'].choices = choices
|
||||
|
||||
if formset.is_valid():
|
||||
event_ids = [f.cleaned_data['event_id'] for f in formset]
|
||||
else:
|
||||
event_ids = request.GET.get("event_ids").split(",")
|
||||
EventChooseFormset = formset_factory(EventChooseForm)
|
||||
formset = EventChooseFormset(request.POST)
|
||||
|
||||
return redirect('teamsnap_edit_multiple_lineups',team_id=team_id, event_ids=",".join(event_ids))
|
||||
elif not request.GET.get('num'):
|
||||
return HttpResponse(500)
|
||||
else:
|
||||
num = int(request.GET.get('num'))
|
||||
TEAM_ID = team_id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
no_past = bool(request.GET.get('no_past', 0))
|
||||
games_only = bool(request.GET.get('games_only', 0))
|
||||
from pyteamsnap.api import TeamSnap, Event, Location, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(.5)
|
||||
ts_events = Event.search(client, team_id=TEAM_ID)
|
||||
if no_past:
|
||||
ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)]
|
||||
if games_only:
|
||||
ts_events = [e for e in ts_events if e.data['is_game']]
|
||||
ts_events = {e.data['id']: e for e in ts_events}
|
||||
# ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)}
|
||||
# ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)}
|
||||
# for event in ts_events:
|
||||
|
||||
EventChooseFormset = formset_factory(EventChooseForm, extra=num)
|
||||
formset = EventChooseFormset()
|
||||
|
||||
choices= [(id, e.data['formatted_title']) for id, e in ts_events.items()]
|
||||
|
||||
for form in formset:
|
||||
form.fields['event_id'].choices = choices
|
||||
|
||||
pass
|
||||
return render(request, "teamsnap/lineup/multiple_choose.html", context={"formset": formset, "team_id": team_id})
|
||||
return render(
|
||||
request,
|
||||
"dashboard.html",
|
||||
{
|
||||
"ts_events_future": ts_events_future,
|
||||
"ts_events_past": ts_events_past,
|
||||
"events_availabilities": [
|
||||
(e, ts_availability_summaries_d[e.data["id"]]) for e in ts_events_future
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user