diff --git a/teamsnap/migrations/0019_auto_20211216_1851.py b/teamsnap/migrations/0019_auto_20211216_1851.py new file mode 100644 index 0000000..af24b49 --- /dev/null +++ b/teamsnap/migrations/0019_auto_20211216_1851.py @@ -0,0 +1,72 @@ +# Generated by Django 3.2.6 on 2021-12-16 18:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0004_delete_availability'), + ('teamsnap', '0018_user_managed_teams'), + ] + + operations = [ + migrations.RenameField( + model_name='availability', + old_name='team', + new_name='managed_by_team', + ), + migrations.RenameField( + model_name='event', + old_name='team', + new_name='managed_by_team', + ), + migrations.RenameField( + model_name='member', + old_name='team', + new_name='managed_by_team', + ), + migrations.RenameField( + model_name='team', + old_name='bencoach_team', + new_name='benchcoach_object', + ), + migrations.AddField( + model_name='lineupentry', + name='managed_by_team', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team'), + ), + migrations.AddField( + model_name='location', + name='managed_by_team', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team'), + ), + migrations.AddField( + model_name='team', + name='managed_by_team', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team'), + ), + migrations.AlterField( + model_name='event', + name='benchcoach_object', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnap_event', to='events.event'), + ), + migrations.CreateModel( + name='Opponent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('teamsnap_id', models.CharField(max_length=10, unique=True)), + ('name', models.CharField(max_length=50, null=True)), + ('managed_by_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='event', + name='opponent', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='opponent', to='teamsnap.opponent'), + ), + ] diff --git a/teamsnap/migrations/0020_auto_20211216_1903.py b/teamsnap/migrations/0020_auto_20211216_1903.py new file mode 100644 index 0000000..17853fd --- /dev/null +++ b/teamsnap/migrations/0020_auto_20211216_1903.py @@ -0,0 +1,41 @@ +# Generated by Django 3.2.6 on 2021-12-16 19:03 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0019_auto_20211216_1851'), + ] + + operations = [ + migrations.RemoveField( + model_name='availability', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='event', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='location', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='member', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='opponent', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='team', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='user', + name='teamsnap_id', + ), + ] diff --git a/teamsnap/migrations/0021_auto_20211216_1905.py b/teamsnap/migrations/0021_auto_20211216_1905.py new file mode 100644 index 0000000..a49bd6b --- /dev/null +++ b/teamsnap/migrations/0021_auto_20211216_1905.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.6 on 2021-12-16 19:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0020_auto_20211216_1903'), + ] + + operations = [ + migrations.AlterField( + model_name='availability', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='event', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='lineupentry', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='location', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='member', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='opponent', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='team', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='user', + name='id', + field=models.CharField(max_length=10, primary_key=True, serialize=False, unique=True), + ), + ] diff --git a/teamsnap/migrations/0022_auto_20211216_1909.py b/teamsnap/migrations/0022_auto_20211216_1909.py new file mode 100644 index 0000000..2c0c034 --- /dev/null +++ b/teamsnap/migrations/0022_auto_20211216_1909.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.6 on 2021-12-16 19:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0021_auto_20211216_1905'), + ] + + operations = [ + migrations.AlterField( + model_name='availability', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='event', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='lineupentry', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='location', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='member', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='opponent', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='team', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + migrations.AlterField( + model_name='user', + name='id', + field=models.CharField(max_length=50, primary_key=True, serialize=False, unique=True), + ), + ] diff --git a/teamsnap/migrations/0023_auto_20211216_1951.py b/teamsnap/migrations/0023_auto_20211216_1951.py new file mode 100644 index 0000000..51de447 --- /dev/null +++ b/teamsnap/migrations/0023_auto_20211216_1951.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.6 on 2021-12-16 19:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0022_auto_20211216_1909'), + ] + + operations = [ + migrations.RemoveField( + model_name='lineupentry', + name='teamsnap_id', + ), + migrations.RemoveField( + model_name='team', + name='managed_by_team', + ), + ] diff --git a/teamsnap/migrations/0024_auto_20211216_1703.py b/teamsnap/migrations/0024_auto_20211216_1703.py new file mode 100644 index 0000000..5bdcb94 --- /dev/null +++ b/teamsnap/migrations/0024_auto_20211216_1703.py @@ -0,0 +1,109 @@ +# Generated by Django 3.2.6 on 2021-12-16 23:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teamsnap', '0023_auto_20211216_1951'), + ] + + operations = [ + migrations.AddField( + model_name='availability', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='availability', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='event', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='event', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='lineupentry', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='lineupentry', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='location', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='location', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='member', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='member', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='opponent', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='opponent', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='team', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='team', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='created_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + migrations.AddField( + model_name='user', + name='updated_at', + field=models.DateTimeField(default='2000-10-31T01:30:00.000-05:00'), + preserve_default=False, + ), + ] diff --git a/teamsnap/migrations/0025_opponent_benchcoach_object.py b/teamsnap/migrations/0025_opponent_benchcoach_object.py new file mode 100644 index 0000000..15fa911 --- /dev/null +++ b/teamsnap/migrations/0025_opponent_benchcoach_object.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.6 on 2021-12-16 23:31 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0001_initial'), + ('teamsnap', '0024_auto_20211216_1703'), + ] + + operations = [ + migrations.AddField( + model_name='opponent', + name='benchcoach_object', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teams.team'), + ), + ] diff --git a/teamsnap/migrations/0026_auto_20211216_2003.py b/teamsnap/migrations/0026_auto_20211216_2003.py new file mode 100644 index 0000000..7f1017c --- /dev/null +++ b/teamsnap/migrations/0026_auto_20211216_2003.py @@ -0,0 +1,100 @@ +# Generated by Django 3.2.6 on 2021-12-17 02:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0001_initial'), + ('teamsnap', '0025_opponent_benchcoach_object'), + ] + + operations = [ + migrations.AlterField( + model_name='availability', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='availability', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='event', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='event', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='lineupentry', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='lineupentry', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='location', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='location', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='member', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='member', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='opponent', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='opponent', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='team', + name='benchcoach_object', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teamsnapteam', to='teams.team'), + ), + migrations.AlterField( + model_name='team', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='team', + name='updated_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='user', + name='created_at', + field=models.DateTimeField(null=True), + ), + migrations.AlterField( + model_name='user', + name='updated_at', + field=models.DateTimeField(null=True), + ), + ] diff --git a/teamsnap/models.py b/teamsnap/models.py index fe06ded..068adda 100644 --- a/teamsnap/models.py +++ b/teamsnap/models.py @@ -7,104 +7,114 @@ import players.models import events.models class TeamsnapBaseModel(models.Model): - teamsnap_id = models.CharField(max_length=10, unique=True) + type = None + id = models.CharField(max_length=50, unique=True, primary_key=True) name = models.CharField(max_length=50, null=True) + created_at = models.DateTimeField(null=True) + updated_at = models.DateTimeField(null=True) class Meta: abstract = True def __str__(self): - return f"TeamSnap {self.__class__.__name__} Object ({self.teamsnap_id})" + 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): - bencoach_team = models.ForeignKey(teams.models.Team, null=True, on_delete=models.CASCADE) + type = 'team' + managed_by_team = None + benchcoach_object = models.ForeignKey(teams.models.Team, null=True, on_delete=models.CASCADE,related_name="teamsnapteam") + +class User(TeamsnapBaseModel): + type = 'user' + name = None + 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) + +class TeamsnapManagedObjectModel(TeamsnapBaseModel): + managed_by_team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE) + + class Meta: + abstract = True @property - def view_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/team/view/{self.teamsnap_id}" + def url(self, endpoint='view'): + return f"https://go.teamsnap.com/{self.managed_by_team.id}/{self.type}/{endpoint}/{self.id}" - @property - def edit_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/team/edit/{self.teamsnap_id}" +class Opponent(TeamsnapManagedObjectModel): + type = 'opponent' + benchcoach_object = models.ForeignKey(teams.models.Team, null=True, on_delete=models.CASCADE) -class Location(TeamsnapBaseModel): +class Location(TeamsnapManagedObjectModel): benchcoach_object = models.ForeignKey(venues.models.Venue, null=True, on_delete=models.CASCADE) - @property - def view_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/location/view/{self.teamsnap_id}" - - @property - def edit_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/location/edit/{self.teamsnap_id}" - -class Member(TeamsnapBaseModel): - name = None +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' benchcoach_object = models.ForeignKey(players.models.Player, null=True, on_delete=models.CASCADE) - team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE) 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() def __str__(self): - return f"{self.last_name}, {self.first_name} ({self.teamsnap_id})" + return f"{self.last_name}, {self.first_name} ({self.id})" @property - def view_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/player/{self.teamsnap_id}" + def name(self): + return f"{self.first_name} {self.last_name}" - @property - def edit_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/edit/{self.teamsnap_id}" - -class Event(TeamsnapBaseModel): +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' name = None - benchcoach_object = models.ForeignKey(events.models.Event, null=True, on_delete=models.CASCADE) + benchcoach_object = models.ForeignKey(events.models.Event, null=True, 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(Team, null=True, on_delete=models.CASCADE, related_name="opponent") - team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE) + 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() - @property - def view_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/schedule/view_game/{self.teamsnap_id}" - - @property - def edit_url(self): - return f"https://go.teamsnap.com/{self.team.teamsnap_id}/schedule/edit_game/{self.teamsnap_id}" - def __str__(self): - return f"{self.formatted_title} ({self.teamsnap_id})" + return f"{self.formatted_title} ({self.id})" -class Availability(TeamsnapBaseModel): +class Availability(TeamsnapManagedObjectModel): + YES = 1 + NO = 0 + MAYBE = 2 + UNKNOWN = None status_codes = [ - (1, 'Yes'), - (0, 'No'), - (2, 'Maybe'), - (None, 'Unknown') + (YES, 'Yes'), + (NO, 'No'), + (MAYBE, 'Maybe'), + (UNKNOWN, 'Unknown') ] name = None - team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE) event = models.ForeignKey(Event, null=True, on_delete=models.CASCADE) member = models.ForeignKey(Member, null=True, on_delete=models.CASCADE) benchcoach_object = models.ForeignKey(lineups.models.Availability, null=True, on_delete=models.CASCADE) status_code = models.SmallIntegerField(null=True, choices=status_codes, default=None) def __str__(self): - return f"{self.member} - {self.event} ({self.teamsnap_id})" + return f"{self.member} - {self.event} ({self.id})" class Meta: verbose_name_plural = "availabilities" -class LineupEntry(TeamsnapBaseModel): +class LineupEntry(TeamsnapManagedObjectModel): name = None - teamsnap_id = models.CharField(max_length=10, unique=True, null=True, blank=True) member = models.ForeignKey(Member, on_delete=models.CASCADE) event = models.ForeignKey(Event, on_delete=models.CASCADE) positions = [ @@ -124,11 +134,4 @@ class LineupEntry(TeamsnapBaseModel): sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True) class Meta: - unique_together = ('member', 'event',) - -class User(TeamsnapBaseModel): - name = None - 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) \ No newline at end of file + unique_together = ('member', 'event',) \ No newline at end of file diff --git a/teamsnap/teamsnap/__init__.py b/teamsnap/teamsnap/__init__.py index 31d7a75..a0d7ca1 100644 --- a/teamsnap/teamsnap/__init__.py +++ b/teamsnap/teamsnap/__init__.py @@ -1,3 +1,3 @@ -from .api import TeamSnap - -__all__ = ['TeamSnap'] \ No newline at end of file +# from .utils import TeamSnap +# +# __all__ = ['TeamSnap'] \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/home.html b/teamsnap/templates/teamsnap/home.html index 9a9fe24..c284b61 100644 --- a/teamsnap/templates/teamsnap/home.html +++ b/teamsnap/templates/teamsnap/home.html @@ -6,5 +6,44 @@

Currently Logged in as

BenchCoach: {{ user }} ({{ user.email }})

TeamSnap: {{ teamsnap_user.email }}

+

TeamSnap Managed Team: {{ teamsnap_team.name }}

+ + + + + + {% endblock %} \ No newline at end of file diff --git a/teamsnap/urls.py b/teamsnap/urls.py index 03f0c39..0e53057 100644 --- a/teamsnap/urls.py +++ b/teamsnap/urls.py @@ -10,5 +10,6 @@ urlpatterns = [ path('events', views.EventsListView.as_view(), name="teamsnap list events"), path('events-table', views.EventsTableView.as_view(), name="teamsnap table events"), path('edit/event/', views.edit_event, name='teamsnap edit event'), - path('edit/lineup/', views.edit_lineup, name='teamsnap edit lineup') + path('from_teamsnap', views.sync_teamsnap, name="sync teamsnap"), + # path('import_teamsnap', views.import_teamsnap, name="import teamsnap"), ] \ No newline at end of file diff --git a/teamsnap/utils/__init__.py b/teamsnap/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/teamsnap/utils/import_teamsnap.py b/teamsnap/utils/import_teamsnap.py new file mode 100644 index 0000000..ec7d223 --- /dev/null +++ b/teamsnap/utils/import_teamsnap.py @@ -0,0 +1,197 @@ +import os +import sys + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "benchcoach.settings") +os.environ["DJANGO_SETTINGS_MODULE"] = "benchcoach.settings" +import django +django.setup() + +from teamsnap.teamsnap.api import TeamSnap +import teamsnap.teamsnap.api +from teamsnap.models import User, Member, Team, Event, Location, Availability, Opponent +from typing import List +from benchcoach.models import Profile as BenchcoachUser +from teams.models import Team as BenchcoachTeam +from events.models import Event as BenchcoachEvent +import pytz + +def update_object_from_teamsnap( + TeamsnapApiObject: teamsnap.teamsnap.api.ApiObject, + TeamsnapDbModelClass: teamsnap.models.TeamsnapBaseModel, + TeamsnapClient: teamsnap.teamsnap.api.TeamSnap, + teamsnap_search_kwargs: dict, + fetching_keys: List[tuple] = ['id'], + default_keys: List[tuple] = ['name'] +): + + """ + Import or Update database objects from TeamSnap API + + additional_fetching_keys (key,) or (api_key, db_field_name) or (api_key, db_field_name, callable) + Additional kwargs used to fetch an object from the database. ('id', 'teamsnap_id') are already included + Callable will be run with the retrieved value as an argument. Example uses for this callable are to sanitize the value + such as for a date, or to retrieve another database object + + additional_default_keys + Additional Keys used to update the object (key,) or (api_key, db_field_name) or (api_key, db_field_name, callable) + ('name',) is already included + Callable will be run with the retrieved value as an argument. Example uses for this callable are to sanitize the value + such as for a date, or to retrieve another database object + + :rtype: object + """ + api_response = TeamsnapApiObject.search(client=TeamsnapClient, **teamsnap_search_kwargs) + + # This routine allows, for convenience, simplers tuples in which the additional detail is not needed + # for example [('key', 'key', None)] can be passed as ['key'] if the TeamsnapApi key is the same as the TeamsnapDb field/key + # and doesn't need to be sanitized + for d in [fetching_keys, fetching_keys, default_keys]: + for i, key in enumerate(d): + if isinstance(key, tuple): + if len(key) == 1: + d[i] = (key[0], key[0], None) + if len(key) == 2: + d[i] = (key[0], key[1], None) + elif isinstance(key, str): + d[i] = (key, key, None) + + r = [] + for data in [items.data for items in api_response]: + kwargs, defaults = {}, {} + for api_key, db_field_name, callable_function in fetching_keys: + if api_key in data.keys(): + kwargs[db_field_name] = callable_function(data[api_key]) if callable_function else data[api_key] + for api_key, db_field_name, callable_function in default_keys: + if api_key in data.keys(): + defaults[db_field_name] = callable_function(data[api_key]) if callable_function else data[api_key] + defaults ={k:v for k,v in defaults.items() if v is not None} + obj, created = TeamsnapDbModelClass.objects.update_or_create(**kwargs, defaults=defaults) + r.append((obj,created)) + return r + +def update_locations (client, **kwargs): + return update_object_from_teamsnap( + teamsnap.teamsnap.api.Location, + Location, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=[ + 'name', 'created_at', 'updated_at', + ('team_id', 'managed_by_team_id') + ] + ) + +def update_teams (client, **kwargs): + teams = update_object_from_teamsnap( + teamsnap.teamsnap.api.Team, + Team, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=[ + 'name', 'created_at', 'updated_at', + ('team_id', 'managed_by_team_id') + ] + ) + opponents = update_object_from_teamsnap( + teamsnap.teamsnap.api.Opponent, + Opponent, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys= [ + 'name', 'created_at', 'updated_at', + ('team_id', 'managed_by_team_id') + ] + ) + return teams + opponents + +def update_members (client, **kwargs): + return update_object_from_teamsnap( + TeamsnapApiObject=teamsnap.teamsnap.api.Member, + TeamsnapDbModelClass=Member, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=['first_name','last_name','jersey_number','is_non_player', 'created_at', 'updated_at', + ('team_id', 'managed_by_team_id'), + ] + ) + pass + +def update_availabilities(client, **kwargs): + return update_object_from_teamsnap( + TeamsnapApiObject=teamsnap.teamsnap.api.Availability, + TeamsnapDbModelClass=Availability, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=[ 'status_code', + 'member_id', + 'event_id', + 'created_at', + 'updated_at', + ('team_id', 'managed_by_team_id') + ] + ) + +def update_events(client, **kwargs): + return update_object_from_teamsnap( + TeamsnapApiObject=teamsnap.teamsnap.api.Event, + TeamsnapDbModelClass=Event, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=['formatted_title','label','points_for_opponent','points_for_team','is_game','opponent_id','location_id', + 'start_date', 'created_at', 'updated_at', + ('team_id', 'managed_by_team_id') + ] + ) + +def update_users(client, **kwargs): + return update_object_from_teamsnap( + TeamsnapApiObject=teamsnap.teamsnap.api.User, + TeamsnapDbModelClass=User, + TeamsnapClient=client, + teamsnap_search_kwargs=kwargs, + default_keys=['first_name', 'last_name', 'email', 'created_at', 'updated_at',] + ) + +def import_teamsnap(): + user = BenchcoachUser.objects.get(id=1) + TOKEN = user.teamsnap_access_token + USER_ID = user.teamsnap_user.id + TEAM_ID = user.teamsnapsettings.managed_team.id + CLIENT = TeamSnap(token=TOKEN) + update_users(CLIENT, id=USER_ID) + + l = [] + for team in Opponent.objects.filter(managed_by_team_id=TEAM_ID): + d = { + 'name': team.name, + } + obj, created = BenchcoachTeam.objects.update_or_create(opponent=team, defaults=d) + team.benchcoach_object = obj + team.save() + l.append((obj,created)) + + for team in Team.objects.filter(id=TEAM_ID): + d = { + 'name': team.name, + } + obj_id = BenchcoachTeam.objects.filter(id=team.benchcoach_object.id).update(**d) + team.benchcoach_object = BenchcoachTeam.objects.get(id=obj_id) + team.save() + l.append((obj,created)) + + for event in Event.objects.filter(managed_by_team_id=TEAM_ID): + d = { + 'start':event.start_date, + } + obj, created = BenchcoachEvent.objects.update_or_create(teamsnap_event=event, defaults=d) + # event.benchcoach_object = obj + + pass + # l += update_teams(CLIENT, team_id=TEAM_ID) + # l += update_members(CLIENT, team_id=TEAM_ID) + # l += update_locations(CLIENT, team_id=TEAM_ID) + # l += update_events(CLIENT, team_id=TEAM_ID) + # l += update_availabilities(CLIENT, team_id=TEAM_ID) + +if __name__ == "__main__": + import_teamsnap() \ No newline at end of file diff --git a/teamsnap/views.py b/teamsnap/views.py index 152bf5f..82f0883 100644 --- a/teamsnap/views.py +++ b/teamsnap/views.py @@ -1,6 +1,7 @@ from django.shortcuts import render, redirect -# from .teamsnap.api import TeamSnap, Team, Event, Availability +from .teamsnap.api import Event as TsApiEvent +from .teamsnap.api import TeamSnap from .models import User, Member, Team, Event, Location, LineupEntry from django.views.generic.list import ListView from lib.views import BenchcoachListView @@ -11,6 +12,11 @@ from django.db.models import Case, When from django.views import View from django.http import HttpResponse from benchcoach.models import Profile as BenchcoachUser +from events.models import Event as BenchcoachEvent +import teamsnap.teamsnap.api +import json +from django.http import JsonResponse +from .utils.import_teamsnap import update_users, update_teams, update_events, update_members, update_locations, update_availabilities def queryset_from_ids(Model, id_list): #https://stackoverflow.com/questions/4916851/django-get-a-queryset-from-array-of-ids-in-specific-order @@ -70,65 +76,40 @@ class LocationListView(BenchcoachListView): list_url = 'teamsnap list locations' page_title = "TeamSnap Locations" -def edit_lineup(request, event_id): +def update_from_teamsnap_event(request): + TOKEN = BenchcoachUser.objects.get(id=1).teamsnap_access_token + CLIENT = TeamSnap(token=TOKEN) + teamsnap_event_id=request.POST.get('teamsnap event') + benchcoach_event_id=request.POST.get('teamsnap event') + if teamsnap_event_id: + benchcoach_event = BenchcoachEvent.objects.get(id=benchcoach_event_id) + teamsnap_object = Event.objects.get(id=teamsnap_event_id) + teamsnap_id = teamsnap_object.teamsnap_id + teamsnap_response = TsApiEvent.search(client=CLIENT, id=teamsnap_id) + if teamsnap_response[0]: + data = teamsnap_response[0].data + location = Location.objects.get(teamsnap_id=data['location_id']) + opponent = Team.objects.get(teamsnap_id=data['opponent_id']) - if request.method == 'POST': - # create a form instance and populate it with data from the request: - formset = LineupEntryFormSet(request.POST) - for form in formset: - if form.is_valid(): - # process the data in form.cleaned_data as required - # ... - # redirect to a new URL: + return HttpResponse(f'Success, {data}') - if isinstance(form.cleaned_data['id'], LineupEntry): - positioning_id = form.cleaned_data.pop('id').id #FIXME this is a workaround, not sure why it is necessary - positioning = LineupEntry.objects.filter(id=positioning_id) - positioning.update(**form.cleaned_data) - did_create = False - else: - positioning = LineupEntry.objects.create(**form.cleaned_data, event_id=event_id) - did_create = True - else: - pass - return render(request, 'success.html', {'call_back':'teamsnap edit lineup','id':event_id, 'errors':[error for error in formset.errors if error]}, status=200) - # return render(request, 'success.html', {'call_back':'schedule'}) - event = Event.objects.get(id=event_id) - members = Member.objects.filter(is_non_player=False).prefetch_related('availability_set', 'lineupentry_set') - # players_d.sort(key=lambda d: (-d['availability'].available, d['last_name'])) +def sync_teamsnap(request): + TOKEN = request.user.profile.teamsnap_access_token + USER_ID = request.user.profile.teamsnap_user.id + TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id + CLIENT = TeamSnap(token=TOKEN) + l = [] + l += update_users(CLIENT, id=USER_ID) + l += update_teams(CLIENT, team_id=TEAM_ID) + l += update_members(CLIENT, team_id=TEAM_ID) + l += update_locations(CLIENT, team_id=TEAM_ID) + l += update_events(CLIENT, team_id=TEAM_ID) + l += update_availabilities(CLIENT, team_id=TEAM_ID) - for member in members: - LineupEntry.objects.get_or_create(member_id=member.id, event_id=event_id) + return JsonResponse({'number of objects updated':len(l)}) - qs_starting_lineup = LineupEntry.objects.filter(event_id=event_id, sequence__isnull=False, sequence__gt=0).order_by('sequence') - qs_bench = LineupEntry.objects.filter(event_id=event_id, sequence=0).prefetch_related('member__availability_set').order_by('member__last_name') - # This is all a compromise to get the sorting just the way I wanted. THERE'S GOT TO BE A BETTER WAY - ids_starting_lineup = [item.id for item in qs_starting_lineup] - ids_bench_available = [item.id for item in qs_bench - if item.member.availability_set.get(event_id=event_id).status_code == 1] - ids_bench_maybe = [item.id for item in qs_bench - if item.member.availability_set.get(event_id=event_id).status_code == 2] - ids_bench_no = [item.id for item in qs_bench - if item.member.availability_set.get(event_id=event_id).status_code == 0] - ids_bench_unknown = [item.id for item in qs_bench - if item.member.availability_set.get(event_id=event_id).status_code is None] - qset = queryset_from_ids(LineupEntry, ids_starting_lineup + ids_bench_available + ids_bench_maybe + ids_bench_no + ids_bench_unknown) - formset = LineupEntryFormSet(queryset=qset) - for f in formset: - if f.instance.member_id: - f.availability = f.instance.member.availability_set.get(event_id=event_id) - # f.statline = f.instance.member.statline_set.get() - formset_lineup = [f for f in formset if f.instance.sequence] - formset_bench = [f for f in formset if f not in formset_lineup] - formset_dhd = [f for f in formset if not f.instance.sequence and f.instance.label] - return render(request, 'teamsnap/lineup.html', {'title': 'Lineup', - 'event': event, - 'formset_lineup': formset_lineup, - 'formset_bench':formset_bench, - 'formset_dhd':formset_dhd - }) \ No newline at end of file