begin implementation of teamsnap syncing

This commit is contained in:
2021-12-17 12:59:01 -06:00
parent 643565d14a
commit e9f91126e5
15 changed files with 805 additions and 115 deletions

View File

@@ -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'),
),
]

View File

@@ -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',
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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',
),
]

View File

@@ -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,
),
]

View File

@@ -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'),
),
]

View File

@@ -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),
),
]

View File

@@ -7,104 +7,114 @@ import players.models
import events.models import events.models
class TeamsnapBaseModel(models.Model): 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) name = models.CharField(max_length=50, null=True)
created_at = models.DateTimeField(null=True)
updated_at = models.DateTimeField(null=True)
class Meta: class Meta:
abstract = True abstract = True
def __str__(self): 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): 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 @property
def view_url(self): def url(self, endpoint='view'):
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/team/view/{self.teamsnap_id}" return f"https://go.teamsnap.com/{self.managed_by_team.id}/{self.type}/{endpoint}/{self.id}"
@property class Opponent(TeamsnapManagedObjectModel):
def edit_url(self): type = 'opponent'
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/team/edit/{self.teamsnap_id}" 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) benchcoach_object = models.ForeignKey(venues.models.Venue, null=True, on_delete=models.CASCADE)
@property class Member(TeamsnapManagedObjectModel):
def view_url(self): # url format is
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/location/view/{self.teamsnap_id}" # 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}"
@property type = 'member'
def edit_url(self):
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/location/edit/{self.teamsnap_id}"
class Member(TeamsnapBaseModel):
name = None
benchcoach_object = models.ForeignKey(players.models.Player, null=True, on_delete=models.CASCADE) 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) first_name = models.CharField(max_length = 50, null=True)
last_name = models.CharField(max_length = 50, null=True) last_name = models.CharField(max_length = 50, null=True)
jersey_number = models.IntegerField(null=True) jersey_number = models.IntegerField(null=True)
is_non_player = models.BooleanField() is_non_player = models.BooleanField()
def __str__(self): 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 @property
def view_url(self): def name(self):
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/player/{self.teamsnap_id}" return f"{self.first_name} {self.last_name}"
@property class Event(TeamsnapManagedObjectModel):
def edit_url(self): # url is
return f"https://go.teamsnap.com/{self.team.teamsnap_id}/roster/edit/{self.teamsnap_id}" # 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}"
class Event(TeamsnapBaseModel): type = 'event'
name = None 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) label = models.CharField(max_length = 50, null=True)
start_date = models.DateTimeField(null=True) start_date = models.DateTimeField(null=True)
opponent = models.ForeignKey(Team, null=True, on_delete=models.CASCADE, related_name="opponent") opponent = models.ForeignKey(Opponent, null=True, on_delete=models.CASCADE, related_name="opponent")
team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE)
location = models.ForeignKey(Location, null=True, on_delete=models.CASCADE) location = models.ForeignKey(Location, null=True, on_delete=models.CASCADE)
formatted_title = models.CharField(max_length = 50, null=True) formatted_title = models.CharField(max_length = 50, null=True)
points_for_opponent = models.PositiveSmallIntegerField(null=True) points_for_opponent = models.PositiveSmallIntegerField(null=True)
points_for_team = models.PositiveSmallIntegerField(null=True) points_for_team = models.PositiveSmallIntegerField(null=True)
is_game = models.BooleanField() 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): 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 = [ status_codes = [
(1, 'Yes'), (YES, 'Yes'),
(0, 'No'), (NO, 'No'),
(2, 'Maybe'), (MAYBE, 'Maybe'),
(None, 'Unknown') (UNKNOWN, 'Unknown')
] ]
name = None name = None
team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE)
event = models.ForeignKey(Event, 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) member = models.ForeignKey(Member, null=True, on_delete=models.CASCADE)
benchcoach_object = models.ForeignKey(lineups.models.Availability, 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) status_code = models.SmallIntegerField(null=True, choices=status_codes, default=None)
def __str__(self): def __str__(self):
return f"{self.member} - {self.event} ({self.teamsnap_id})" return f"{self.member} - {self.event} ({self.id})"
class Meta: class Meta:
verbose_name_plural = "availabilities" verbose_name_plural = "availabilities"
class LineupEntry(TeamsnapBaseModel): class LineupEntry(TeamsnapManagedObjectModel):
name = None name = None
teamsnap_id = models.CharField(max_length=10, unique=True, null=True, blank=True)
member = models.ForeignKey(Member, on_delete=models.CASCADE) member = models.ForeignKey(Member, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE) event = models.ForeignKey(Event, on_delete=models.CASCADE)
positions = [ positions = [
@@ -124,11 +134,4 @@ class LineupEntry(TeamsnapBaseModel):
sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True) sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True)
class Meta: class Meta:
unique_together = ('member', 'event',) 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)

View File

@@ -1,3 +1,3 @@
from .api import TeamSnap # from .utils import TeamSnap
#
__all__ = ['TeamSnap'] # __all__ = ['TeamSnap']

View File

@@ -6,5 +6,44 @@
<h1>Currently Logged in as</h1> <h1>Currently Logged in as</h1>
<p><b>BenchCoach: </b>{{ user }} ({{ user.email }})</p> <p><b>BenchCoach: </b>{{ user }} ({{ user.email }})</p>
<p><b>TeamSnap: </b>{{ teamsnap_user.email }}</p> <p><b>TeamSnap: </b>{{ teamsnap_user.email }}</p>
<p><b>TeamSnap Managed Team: </b>{{ teamsnap_team.name }}</p>
<button type="button" class="btn btn-primary m-1" onclick="sync_teamsnap_db()">
Sync TeamSnap DB
<div id="teamsnap-sync-spinner" class="spinner-border spinner-border-sm d-none" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</button>
<textarea id="output_box" name="comment" disabled class="w-100 m" style="min-height: 400px"></textarea>
<button type="button" class="btn btn-outline-secondary m-1" onclick="clear_output_box()">Clear</button>
<script>
const output_box = document.getElementById("output_box");
const progress_spinner = document.getElementById("teamsnap-sync-spinner")
function sync_teamsnap_db(){
const Http = new XMLHttpRequest();
const url='{% url 'sync teamsnap' %}';
console.log(progress_spinner)
progress_spinner.classList.remove("d-none");
fetch(url)
.then((response) => {
progress_spinner.classList.add("d-none")
return response.json();
})
.then((myJson) => {
var s = 'Number of objects updated: '
for (i in myJson) {
s += myJson[i]
{#s += myJson[i].name +" ("+ myJson[i].id+")" + "\r\n"#}
}
output_box.value = s;
});
}
function clear_output_box(){
progress_spinner.classList.add('d-none')
}
</script>
{% endblock %} {% endblock %}

View File

@@ -10,5 +10,6 @@ urlpatterns = [
path('events', views.EventsListView.as_view(), name="teamsnap list events"), path('events', views.EventsListView.as_view(), name="teamsnap list events"),
path('events-table', views.EventsTableView.as_view(), name="teamsnap table events"), path('events-table', views.EventsTableView.as_view(), name="teamsnap table events"),
path('edit/event/<int:id>', views.edit_event, name='teamsnap edit event'), path('edit/event/<int:id>', views.edit_event, name='teamsnap edit event'),
path('edit/lineup/<int:event_id>', 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"),
] ]

View File

View File

@@ -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()

View File

@@ -1,6 +1,7 @@
from django.shortcuts import render, redirect 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 .models import User, Member, Team, Event, Location, LineupEntry
from django.views.generic.list import ListView from django.views.generic.list import ListView
from lib.views import BenchcoachListView from lib.views import BenchcoachListView
@@ -11,6 +12,11 @@ from django.db.models import Case, When
from django.views import View from django.views import View
from django.http import HttpResponse from django.http import HttpResponse
from benchcoach.models import Profile as BenchcoachUser 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): def queryset_from_ids(Model, id_list):
#https://stackoverflow.com/questions/4916851/django-get-a-queryset-from-array-of-ids-in-specific-order #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' list_url = 'teamsnap list locations'
page_title = "TeamSnap 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': return HttpResponse(f'Success, {data}')
# create a form instance and populate it with data from the request:
formset = LineupEntryFormSet(request.POST)
for form in formset:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
if isinstance(form.cleaned_data['id'], LineupEntry): def sync_teamsnap(request):
positioning_id = form.cleaned_data.pop('id').id #FIXME this is a workaround, not sure why it is necessary TOKEN = request.user.profile.teamsnap_access_token
positioning = LineupEntry.objects.filter(id=positioning_id) USER_ID = request.user.profile.teamsnap_user.id
positioning.update(**form.cleaned_data) TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
did_create = False CLIENT = TeamSnap(token=TOKEN)
else: l = []
positioning = LineupEntry.objects.create(**form.cleaned_data, event_id=event_id) l += update_users(CLIENT, id=USER_ID)
did_create = True l += update_teams(CLIENT, team_id=TEAM_ID)
else: l += update_members(CLIENT, team_id=TEAM_ID)
pass l += update_locations(CLIENT, team_id=TEAM_ID)
return render(request, 'success.html', {'call_back':'teamsnap edit lineup','id':event_id, 'errors':[error for error in formset.errors if error]}, status=200) l += update_events(CLIENT, team_id=TEAM_ID)
# return render(request, 'success.html', {'call_back':'schedule'}) l += update_availabilities(CLIENT, team_id=TEAM_ID)
event = Event.objects.get(id=event_id)
members = Member.objects.filter(is_non_player=False).prefetch_related('availability_set', 'lineupentry_set')
# players_d.sort(key=lambda d: (-d['availability'].available, d['last_name']))
for member in members: return JsonResponse({'number of objects updated':len(l)})
LineupEntry.objects.get_or_create(member_id=member.id, event_id=event_id)
qs_starting_lineup = LineupEntry.objects.filter(event_id=event_id, sequence__isnull=False, sequence__gt=0).order_by('sequence')
qs_bench = LineupEntry.objects.filter(event_id=event_id, sequence=0).prefetch_related('member__availability_set').order_by('member__last_name')
# This is all a compromise to get the sorting just the way I wanted. THERE'S GOT TO BE A BETTER WAY
ids_starting_lineup = [item.id for item in qs_starting_lineup]
ids_bench_available = [item.id for item in qs_bench
if item.member.availability_set.get(event_id=event_id).status_code == 1]
ids_bench_maybe = [item.id for item in qs_bench
if item.member.availability_set.get(event_id=event_id).status_code == 2]
ids_bench_no = [item.id for item in qs_bench
if item.member.availability_set.get(event_id=event_id).status_code == 0]
ids_bench_unknown = [item.id for item in qs_bench
if item.member.availability_set.get(event_id=event_id).status_code is None]
qset = queryset_from_ids(LineupEntry, ids_starting_lineup + ids_bench_available + ids_bench_maybe + ids_bench_no + ids_bench_unknown)
formset = LineupEntryFormSet(queryset=qset)
for f in formset:
if f.instance.member_id:
f.availability = f.instance.member.availability_set.get(event_id=event_id)
# f.statline = f.instance.member.statline_set.get()
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
})