a lot of teamsnap sync work. not entirely happy with all of it

This commit is contained in:
2021-12-21 17:18:39 -06:00
parent 33c772bd2f
commit acf90d4256
8 changed files with 739 additions and 211 deletions

View File

@@ -1,4 +1,4 @@
# Generated by Django 3.2.6 on 2021-11-20 23:53 # Generated by Django 3.2.6 on 2021-12-17 21:35
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@@ -9,31 +9,47 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('teams', '0001_initial'), ('benchcoach', '0001_initial'),
('players', '0003_player_team'),
('venues', '0001_initial'),
('events', '0004_delete_availability'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='User', name='Team',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
('teamsnap_id', models.CharField(max_length=10)), ('name', models.CharField(max_length=50, null=True)),
('access_token', models.CharField(max_length=50)), ('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')),
], ],
options={ options={
'abstract': False, 'abstract': False,
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='Team', name='User',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
('teamsnap_id', models.CharField(max_length=10)), ('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)), ('name', models.CharField(max_length=50, null=True)),
('team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teams.team')), ('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={ options={
'abstract': False, 'abstract': False,
@@ -42,10 +58,15 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Member', name='Member',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
('teamsnap_id', models.CharField(max_length=10)), ('created_at', models.DateTimeField(null=True)),
('name', models.CharField(max_length=50, null=True)), ('updated_at', models.DateTimeField(null=True)),
('player', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='players.player')), ('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={ options={
'abstract': False, 'abstract': False,
@@ -54,10 +75,12 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Location', name='Location',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
('teamsnap_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=50, null=True)), ('name', models.CharField(max_length=50, null=True)),
('venue', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='venues.venue')), ('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={ options={
'abstract': False, 'abstract': False,
@@ -66,17 +89,54 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Event', name='Event',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.CharField(max_length=50, primary_key=True, serialize=False, unique=True)),
('teamsnap_id', models.CharField(max_length=10)), ('created_at', models.DateTimeField(null=True)),
('name', models.CharField(max_length=50, null=True)), ('updated_at', models.DateTimeField(null=True)),
('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)),
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.event')), ('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')), ('location', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.location')),
('opponent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')), ('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={ options={
'abstract': False, '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')},
},
),
] ]

View File

@@ -2,7 +2,6 @@
{% block title %} {{ title }}{% endblock %} {% block title %} {{ title }}{% endblock %}
{% block content %} {% block content %}
<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>
@@ -14,29 +13,48 @@
<span class="visually-hidden">Loading...</span> <span class="visually-hidden">Loading...</span>
</div> </div>
</button> </button>
<button type="button" class="btn btn-primary m-1" onclick="sync_teamsnap_db()">
<textarea id="output_box" name="comment" disabled class="w-100 m" style="min-height: 400px"></textarea> Sync TeamSnap DB
<button type="button" class="btn btn-outline-secondary m-1" onclick="clear_output_box()">Clear</button> <div id="teamsnap-sync-spinner" class="spinner-border spinner-border-sm d-none" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</button>
{% include 'messages.html' %}
<div id="message-area"> </div>
<script> <script>
const output_box = document.getElementById("output_box");
const progress_spinner = document.getElementById("teamsnap-sync-spinner") const progress_spinner = document.getElementById("teamsnap-sync-spinner")
const message_area = document.getElementById("message-area")
function sync_teamsnap_db(){ function sync_teamsnap_db(){
const Http = new XMLHttpRequest(); const Http = new XMLHttpRequest();
const url='{% url 'sync teamsnap' %}'; const url='{% url 'sync teamsnap db' %}';
console.log(progress_spinner) console.log(progress_spinner)
progress_spinner.classList.remove("d-none"); progress_spinner.classList.remove("d-none");
fetch(url) fetch(url)
.then((response) => { .then((response) => {
progress_spinner.classList.add("d-none") progress_spinner.classList.add("d-none")
message_area.innerText = "test"
return response.json(); return response.json();
}) })
.then((myJson) => { .then((myJson) => {
var s = 'Number of objects updated: ' console.log(myJson)
for (i in myJson) { message_area.innerHTML = myJson.msg
s += myJson[i] });
{#s += myJson[i].name +" ("+ myJson[i].id+")" + "\r\n"#}
} }
output_box.value = s;
function sync_teamsnap_db_2(){
const Http = new XMLHttpRequest();
const url='{% url 'sync teamsnap db' %}';
console.log(progress_spinner)
progress_spinner.classList.remove("d-none");
fetch(url)
.then((response) => {
progress_spinner.classList.add("d-none")
message_area.innerText = "test"
return response.json();
})
.then((myJson) => {
console.log(myJson)
message_area.innerHTML = myJson.msg
}); });
} }

View File

@@ -1,4 +1,4 @@
{% extends 'base.html' %}{% block title %} {{ title }} {% endblock %}{% load crispy_forms_tags %}{% load static %} {% extends 'base.html' %}{% block title %} {{ title }} {% endblock %}{% load static %}
{% block content %} {% block content %}
<div class="w-100 text-center mx-auto text-center"> <div class="w-100 text-center mx-auto text-center">

View File

@@ -10,6 +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('from_teamsnap', views.sync_teamsnap, name="sync teamsnap"), path('sync_teamsnap_db', views.sync_teamsnap_db, name="sync teamsnap db"),
# path('import_teamsnap', views.import_teamsnap, name="import teamsnap"), # path('import_teamsnap', views.import_teamsnap, name="import teamsnap"),
] ]

View File

@@ -1,156 +1,185 @@
import os import os
import sys import sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "benchcoach.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "benchcoachproject.settings")
os.environ["DJANGO_SETTINGS_MODULE"] = "benchcoach.settings" os.environ["DJANGO_SETTINGS_MODULE"] = "benchcoachproject.settings"
import django import django
django.setup() django.setup()
from teamsnap.teamsnap.api import TeamSnap from teamsnap.teamsnap.api import TeamSnap
import teamsnap.teamsnap.api import teamsnap.teamsnap.api
from teamsnap.models import User, Member, Team, Event, Location, Availability, Opponent from teamsnap.models import User, Member, Team, Event, Location, Availability, Opponent, TeamsnapBaseModel
from typing import List from typing import List
from benchcoach.models import Profile as BenchcoachUser from benchcoachproject.models import Profile as BenchcoachUser
from teams.models import Team as BenchcoachTeam import benchcoach.models
from events.models import Event as BenchcoachEvent from django.db import models
import pytz import pytz
def update_object_from_teamsnap( def update_teamsnap_object(d, teamsnap_object: TeamsnapBaseModel, benchcoach_model: models.Model ,create_benchcoach_object: bool = True, create_related: bool = False):
TeamsnapApiObject: teamsnap.teamsnap.api.ApiObject, ''' Function to update from a teamsnap object to Benchcoach object. This method is a simple method when there are no related objects
TeamsnapDbModelClass: teamsnap.models.TeamsnapBaseModel,
TeamsnapClient: teamsnap.teamsnap.api.TeamSnap,
teamsnap_search_kwargs: dict,
fetching_keys: List[tuple] = ['id'],
default_keys: List[tuple] = ['name']
):
""" :param d: The information to update.
Import or Update database objects from TeamSnap API :param teamsnap_object: The teamsnap object from which to update.
:param create_benchcoach_object: If true, will create the benchcoach object if it doesn't exist
additional_fetching_keys (key,) or (api_key, db_field_name) or (api_key, db_field_name, callable) :param create_related: This is here for decoration only. It doesn't do anything.
Additional kwargs used to fetch an object from the database. ('id', 'teamsnap_id') are already included :return: a list of tuples in the form (obj, did_create) for created or modified objects.
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 = [] r = []
for data in [items.data for items in api_response]:
kwargs, defaults = {}, {} if teamsnap_object.benchcoach_object:
for api_key, db_field_name, callable_function in fetching_keys: #TODO I'm not sure this does anything. need to make sure.
if api_key in data.keys(): benchcoach_object = benchcoach_model.objects.filter(id=teamsnap_object.benchcoach_object.id).first()
kwargs[db_field_name] = callable_function(data[api_key]) if callable_function else data[api_key] created = False
for api_key, db_field_name, callable_function in default_keys: r.append((benchcoach_object, created))
if api_key in data.keys(): elif not teamsnap_object.benchcoach_object and create_benchcoach_object:
defaults[db_field_name] = callable_function(data[api_key]) if callable_function else data[api_key] benchcoach_object = benchcoach_model(**d) # create new benchcoach object
defaults ={k:v for k,v in defaults.items() if v is not None} teamsnap_object.benchcoach_object = benchcoach_object
obj, created = TeamsnapDbModelClass.objects.update_or_create(**kwargs, defaults=defaults) benchcoach_object.save()
r.append((obj,created)) teamsnap_object.save()
created = True
r.append((benchcoach_object, created))
elif not teamsnap_object.benchcoach_object:
raise benchcoach.models.Team.DoesNotExist
return r return r
def update_locations (client, **kwargs): def update_event(event: Event, create_benchcoach_object: bool = True, create_related=False):
return update_object_from_teamsnap( benchcoach_model = benchcoach.models.Event
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): d = {
teams = update_object_from_teamsnap( 'start': event.start_date,
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): r = []
return update_object_from_teamsnap(
TeamsnapApiObject=teamsnap.teamsnap.api.Member, if event.team:
TeamsnapDbModelClass=Member, if event.team.benchcoach_object:
TeamsnapClient=client, if event.game_type == "Home":
teamsnap_search_kwargs=kwargs, d['home_team'] = event.team.benchcoach_object
default_keys=['first_name','last_name','jersey_number','is_non_player', 'created_at', 'updated_at', elif event.game_type == "Away":
('team_id', 'managed_by_team_id'), d['away_team'] = event.team.benchcoach_object
] elif not event.team.benchcoach_object and create_related:
) # create team object
# update_opponent
# r.append
pass pass
elif not event.team.benchcoach_object:
raise benchcoach.models.Team.DoesNotExist
def update_availabilities(client, **kwargs): if event.opponent:
return update_object_from_teamsnap( if event.opponent.benchcoach_object:
TeamsnapApiObject=teamsnap.teamsnap.api.Availability, if event.game_type == 'Home':
TeamsnapDbModelClass=Availability, d['away_team'] = event.opponent.benchcoach_object
TeamsnapClient=client, elif event.game_type == 'Away':
teamsnap_search_kwargs=kwargs, d['home_team'] = event.opponent.benchcoach_object
default_keys=[ 'status_code', elif not event.opponent.benchcoach_object and create_related:
'member_id', # Create opponent object
'event_id', # update_opponent()
'created_at', # r.append
'updated_at', pass
('team_id', 'managed_by_team_id') elif not event.opponent.benchcoach_object:
] raise benchcoach.models.Team.DoesNotExist
)
def update_events(client, **kwargs): if event.location:
return update_object_from_teamsnap( if event.location.benchcoach_object:
TeamsnapApiObject=teamsnap.teamsnap.api.Event, if event.location:
TeamsnapDbModelClass=Event, d['venue'] = event.location.benchcoach_object
TeamsnapClient=client, elif not event.location.benchcoach_object and create_related:
teamsnap_search_kwargs=kwargs, # Need to account for if no loacation assigned to teamsnap object.
default_keys=['formatted_title','label','points_for_opponent','points_for_team','is_game','opponent_id','location_id', # create team object
'start_date', 'created_at', 'updated_at', # update_opponent
('team_id', 'managed_by_team_id') # r.append
] pass
) elif not event.location.benchcoach_object:
raise benchcoach.models.Venue.DoesNotExist
r += update_teamsnap_object(d, teamsnap_object=event, benchcoach_model=benchcoach_model, create_benchcoach_object=create_benchcoach_object)
return r
def update_opponent(opponent: Opponent, create_benchcoach_object: bool = True, create_related: bool = False):
benchcoach_model = benchcoach.models.Team
d = {
'name': opponent.name,
}
r = update_teamsnap_object(d, teamsnap_object=opponent, benchcoach_model=benchcoach_model, create_benchcoach_object= create_benchcoach_object, create_related = create_related)
return r
def update_team(teamsnap_object: Team, create_benchcoach_object: bool = True, create_related: bool = False):
benchcoach_model = benchcoach.models.Team
d = {
'name': teamsnap_object.name,
}
r = update_teamsnap_object(d, teamsnap_object=teamsnap_object, benchcoach_model=benchcoach_model, create_benchcoach_object=create_benchcoach_object,
create_related=create_related)
return r
def update_location(teamsnap_object: Location, create_benchcoach_object: bool = True, create_related: bool = False):
benchcoach_model = benchcoach.models.Venue
d = {
'name': teamsnap_object.name,
}
r = update_teamsnap_object(d, teamsnap_object=teamsnap_object, benchcoach_model=benchcoach_model, create_benchcoach_object=create_benchcoach_object,
create_related=create_related)
return r
def update_member(teamsnap_object: Member, create_benchcoach_object: bool = True, create_related: bool = False):
benchcoach_model = benchcoach.models.Player
d = {
'first_name': teamsnap_object.first_name,
'last_name': teamsnap_object.last_name,
'jersey_number': teamsnap_object.jersey_number,
}
r = update_teamsnap_object(d, teamsnap_object=teamsnap_object, benchcoach_model=benchcoach_model, create_benchcoach_object=create_benchcoach_object,
create_related=create_related)
return r
def update_availability(availability: Availability, create_benchcoach_object: bool = True, create_related: bool = False):
benchcoach_model = benchcoach.models.Availability
translation = {
Availability.YES: benchcoach.models.Availability.YES,
Availability.NO: benchcoach.models.Availability.NO,
Availability.MAYBE: benchcoach.models.Availability.MAYBE
}
d = {
'available': translation.get(availability.status_code, benchcoach.models.Availability.UNKNOWN),
'player': availability.member.benchcoach_object,
'event': availability.event.benchcoach_object
}
r = []
if availability.member.benchcoach_object:
d['player'] = availability.member.benchcoach_object
elif not availability.member.benchcoach_object and create_related:
r += update_member(availability.member, create_benchcoach_object = True)
d['player'] = availability.member.benchcoach_object
elif not availability.member.benchcoach_object and not create_related:
raise benchcoach.models.Availability.DoesNotExist
if availability.event.benchcoach_object:
d['event'] = availability.event.benchcoach_object
elif not availability.event.benchcoach_object and create_related:
r += update_event(availability.member, create_benchcoach_object = True)
d['event'] = availability.event.benchcoach_object
elif not availability.event.benchcoach_object and not create_related:
raise benchcoach.models.Event.DoesNotExist
r += update_teamsnap_object(d, teamsnap_object=availability, benchcoach_model=benchcoach_model, create_benchcoach_object=create_benchcoach_object,
create_related=create_related)
return r
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(): def import_teamsnap():
user = BenchcoachUser.objects.get(id=1) user = BenchcoachUser.objects.get(id=1)
@@ -158,33 +187,25 @@ def import_teamsnap():
USER_ID = user.teamsnap_user.id USER_ID = user.teamsnap_user.id
TEAM_ID = user.teamsnapsettings.managed_team.id TEAM_ID = user.teamsnapsettings.managed_team.id
CLIENT = TeamSnap(token=TOKEN) CLIENT = TeamSnap(token=TOKEN)
update_users(CLIENT, id=USER_ID)
l = [] l = []
for team in Opponent.objects.filter(managed_by_team_id=TEAM_ID): for team in Opponent.objects.filter(team_id=TEAM_ID):
d = { l += update_opponent(team, create_benchcoach_object=True, create_related=True)
'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): for team in Team.objects.filter(id=TEAM_ID):
d = { l += update_team(team, create_benchcoach_object=True, create_related=True)
'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): for location in Location.objects.filter(team_id=TEAM_ID):
d = { l += update_location(location, create_benchcoach_object=True, create_related=True)
'start':event.start_date,
} for member in Member.objects.filter(team_id=TEAM_ID, is_non_player=False):
obj, created = BenchcoachEvent.objects.update_or_create(teamsnap_event=event, defaults=d) l += update_member(member, create_benchcoach_object= True, create_related=True)
# event.benchcoach_object = obj
for event in Event.objects.filter(team_id=TEAM_ID):
l += update_event(event, create_benchcoach_object=True, create_related=True)
for availability in Availability.objects.filter(team_id=TEAM_ID):
l += update_availability(availability, create_benchcoach_object=True, create_related=True)
pass pass
# l += update_teams(CLIENT, team_id=TEAM_ID) # l += update_teams(CLIENT, team_id=TEAM_ID)

View File

@@ -0,0 +1,198 @@
import os
import sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "benchcoachproject.settings")
os.environ["DJANGO_SETTINGS_MODULE"] = "benchcoachproject.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, TeamsnapBaseModel
from typing import List, Type, Tuple
from benchcoachproject.models import Profile as BenchcoachUser
import benchcoach.models
from django.db import models
import pytz
def update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel: Type[TeamsnapBaseModel],
teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
fields: List[str]= ['created_at', 'updated_at'],
additional_fields: List[str] = [],
related_fields: List[Tuple[str, Type[TeamsnapBaseModel]]] = [],
create = True,
create_related = True,
**teamsnap_search_kwargs
):
api_response_objects = TeamsnapModel.ApiObject.search(
client=teamsnap_client, **teamsnap_search_kwargs
)
r = []
for response_object in api_response_objects:
d = {k:response_object.data[k] for k in fields}
d.update({k:response_object.data[k] for k in additional_fields})
for related_field_name, RelatedTeamSnapModel in related_fields:
related_field_name = f"{related_field_name}_id"
filter_criteria = {related_field_name:response_object.data[related_field_name]}
if related_field_name == "team_id" and RelatedTeamSnapModel.__name__ == "Team":
filter_criteria = {'id':response_object.data[related_field_name]}
related_teamsnap_object = RelatedTeamSnapModel.objects.filter(**filter_criteria).first()
if related_teamsnap_object:
d[related_field_name] = related_teamsnap_object
elif not related_teamsnap_object and create_related:
related_teamsnap_object = RelatedTeamSnapModel(**{related_field_name:response_object.data[related_field_name]})
related_teamsnap_object.save()
elif not related_teamsnap_object and not create_related:
raise RelatedTeamSnapModel.DoesNotExist
teamsnap_object = TeamsnapModel.objects.filter(id=response_object.data['id'])
if teamsnap_object:
teamsnap_object.update(**d)
created = False
r.append((teamsnap_object.first(), created))
elif not teamsnap_object and create:
new_teamsnap_object = TeamsnapModel(**d) # create new benchcoach object
new_teamsnap_object.save()
created = True
r.append((new_teamsnap_object, created))
elif not teamsnap_object and not create:
raise TeamsnapModel.DoesNotExist
return r
def update_events(
teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
create: bool = True,
create_related: bool = False,
**teamsnap_search_kwargs
):
r = update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel= Event,
teamsnap_client= teamsnap_client,
additional_fields=['label', 'start_date', 'formatted_title', 'points_for_opponent', 'points_for_team', 'is_game', 'game_type'],
related_fields=[('location', Location), ('opponent', Opponent), ('team', Team)],
create=create,
create_related=create_related,
**teamsnap_search_kwargs
)
return r
def update_opponents(
teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
create: bool = True,
create_related: bool = False,
**teamsnap_search_kwargs
):
r = update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel= Opponent,
teamsnap_client= teamsnap_client,
additional_fields=['name'],
related_fields=[('team', Team)],
create=create,
create_related=create_related,
**teamsnap_search_kwargs
)
return r
def update_teams(
teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
create: bool = True,
create_related: bool = False,
**teamsnap_search_kwargs
):
r = update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel=Team,
teamsnap_client=teamsnap_client,
additional_fields=['name'],
create=create,
create_related=create_related,
**teamsnap_search_kwargs
)
def update_locations(
teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
create: bool = True,
create_related: bool = False,
**teamsnap_search_kwargs
):
r = update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel=Location,
teamsnap_client=teamsnap_client,
additional_fields=['name'],
related_fields=[('team', Team)],
create=create,
create_related=create_related,
**teamsnap_search_kwargs
)
def update_availabilities(teamsnap_client: teamsnap.teamsnap.api.TeamSnap,
create: bool = True,
create_related: bool = False,
**teamsnap_search_kwargs
):
r = update_teamsnapbasemodel_from_teamsnap(
TeamsnapModel= Availability,
teamsnap_client= teamsnap_client,
related_fields=[('event', Event), ('member', Member), ('team', Team)],
create=create,
create_related=create_related,
**teamsnap_search_kwargs
)
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)
l = []
for team in Opponent.objects.filter(managed_by_team_id=TEAM_ID):
l += update_opponent(team, create_benchcoach_object=True, create_related=True)
for team in Team.objects.filter(id=TEAM_ID):
l += update_team(team, create_benchcoach_object=True, create_related=True)
for location in Location.objects.filter(managed_by_team_id=TEAM_ID):
l += update_location(location, create_benchcoach_object=True, create_related=True)
for member in Member.objects.filter(managed_by_team_id=TEAM_ID, is_non_player=False):
l += update_member(member, create_benchcoach_object= True, create_related=True)
for event in Event.objects.filter(managed_by_team_id=TEAM_ID):
l += update_event(event, create_benchcoach_object=True, create_related=True)
for availability in Availability.objects.filter(managed_by_team_id=TEAM_ID):
l += update_availability(availability, create_benchcoach_object=True, create_related=True)
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__":
TOKEN = BenchcoachUser.objects.get(id=1).teamsnap_access_token
USER_ID = BenchcoachUser.objects.get(id=1).teamsnap_user_id
TEAM_ID = BenchcoachUser.objects.get(id=1).teamsnapsettings.managed_team_id
CLIENT = TeamSnap(token=TOKEN)
for Obj in [User]:
a = Obj.ApiObject.search(CLIENT, id=USER_ID)
for _a in a:
obj, created = Obj.update_or_create_from_teamsnap_api(_a.data)
for Obj in [Event, Availability, Location, Member, Opponent, Team]:
a = Obj.ApiObject.search(CLIENT, team_id=TEAM_ID)
for _a in a:
obj, created = Obj.update_or_create_from_teamsnap_api(_a.data)
# update_opponents(CLIENT, team_id=TEAM_ID)
# update_events(CLIENT, team_id=TEAM_ID)

View File

@@ -0,0 +1,165 @@
import os
import sys
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "benchcoachproject.settings")
os.environ["DJANGO_SETTINGS_MODULE"] = "benchcoachproject.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, TeamsnapBaseModel
from typing import List
from benchcoachproject.models import Profile as BenchcoachUser
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'],
related_objects: List[tuple] = []
):
"""
Import or Update database objects from TeamSnap API. Currently this depends on being run in a particular order when the database is empty.
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 related_object in related_objects:
pass
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'
],
related_objects = [
('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', 'game_type',
('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',]
)
if __name__ == "__main__":
TOKEN = BenchcoachUser.objects.get(id=1).teamsnap_access_token
USER_ID = BenchcoachUser.objects.get(id=1).teamsnap_user_id
TEAM_ID = BenchcoachUser.objects.get(id=1).teamsnapsettings.managed_team_id
CLIENT = TeamSnap(token=TOKEN)
# update_users(CLIENT, id=USER_ID)
update_teams(CLIENT, team_id=TEAM_ID)

View File

@@ -2,21 +2,22 @@ from django.shortcuts import render, redirect
from .teamsnap.api import Event as TsApiEvent from .teamsnap.api import Event as TsApiEvent
from .teamsnap.api import TeamSnap from .teamsnap.api import TeamSnap
from .models import User, Member, Team, Event, Location, LineupEntry from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability
from django.views.generic.list import ListView
from lib.views import BenchcoachListView from lib.views import BenchcoachListView
from .forms import LineupEntryForm, LineupEntryFormSet, EventForm, EventFormSet from .forms import LineupEntryForm, LineupEntryFormSet, EventForm, EventFormSet
from django.forms.models import model_to_dict
from django.urls import reverse from django.urls import reverse
from django.db.models import Case, When 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 import benchcoachproject.models
from events.models import Event as BenchcoachEvent import benchcoach.models
import teamsnap.teamsnap.api import teamsnap.teamsnap.api
import json import json
from django.http import JsonResponse from django.http import JsonResponse
from .utils.import_teamsnap import update_users, update_teams, update_events, update_members, update_locations, update_availabilities from .utils.teamsnap_object_utils import update_users, update_teams, update_events, update_members, update_locations, update_availabilities
from django.contrib import messages
from django.template.loader import render_to_string
from .utils.import_teamsnap import update_team, update_event, update_member, update_location, update_opponent, update_availability, update_teamsnap_object
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
@@ -77,13 +78,13 @@ class LocationListView(BenchcoachListView):
page_title = "TeamSnap Locations" page_title = "TeamSnap Locations"
def update_from_teamsnap_event(request): def update_from_teamsnap_event(request):
TOKEN = BenchcoachUser.objects.get(id=1).teamsnap_access_token TOKEN = benchcoachproject.models.User.objects.get(id=1).teamsnap_access_token
CLIENT = TeamSnap(token=TOKEN) CLIENT = TeamSnap(token=TOKEN)
teamsnap_event_id=request.POST.get('teamsnap event') teamsnap_event_id=request.POST.get('teamsnap event')
benchcoach_event_id=request.POST.get('teamsnap event') benchcoach_event_id=request.POST.get('teamsnap event')
if teamsnap_event_id: if teamsnap_event_id:
benchcoach_event = BenchcoachEvent.objects.get(id=benchcoach_event_id) benchcoach_event = benchcoach.models.Event.objects.get(id=benchcoach_event_id)
teamsnap_object = Event.objects.get(id=teamsnap_event_id) teamsnap_object = benchcoach.models.Event.objects.get(id=teamsnap_event_id)
teamsnap_id = teamsnap_object.teamsnap_id teamsnap_id = teamsnap_object.teamsnap_id
teamsnap_response = TsApiEvent.search(client=CLIENT, id=teamsnap_id) teamsnap_response = TsApiEvent.search(client=CLIENT, id=teamsnap_id)
if teamsnap_response[0]: if teamsnap_response[0]:
@@ -93,20 +94,85 @@ def update_from_teamsnap_event(request):
return HttpResponse(f'Success, {data}') return HttpResponse(f'Success, {data}')
def sync_teamsnap(request): def sync_with_teamsnap_api(request):
'''
This sync the internal TeamSnap Database with the TeamSnap API
'''
TOKEN = request.user.profile.teamsnap_access_token TOKEN = request.user.profile.teamsnap_access_token
USER_ID = request.user.profile.teamsnap_user.id USER_ID = request.user.profile.teamsnap_user.id
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
CLIENT = TeamSnap(token=TOKEN) 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)
return JsonResponse({'number of objects updated':len(l)}) r = {}
for Obj in [User]:
r[Obj.__name__] = []
a = Obj.ApiObject.search(CLIENT, id=USER_ID)
for _a in a:
obj, created = Obj.update_or_create_from_teamsnap_api(_a.data)
r[Obj.__name__].append((obj, created))
for Obj in [Event, Availability, Location, Member, Opponent, Team]:
r[Obj.__name__] = []
a = Obj.ApiObject.search(CLIENT, team_id=TEAM_ID)
for _a in a:
obj, created = Obj.update_or_create_from_teamsnap_api(_a.data)
r[Obj.__name__].append((obj, created))
for object_name, results in r.items():
if len(r) == 0:
messages.error(request, f"Error! No {object_name} objects created or updated")
else:
result = [created for obj, created in results]
messages.success(request, f"Success! {sum(result)} {object_name} objects created, {len(result)-sum(result)} {object_name} objects updated.")
data = {
'msg': render_to_string('messages.html', {}, request),
}
return JsonResponse(data)
def sync_teamsnap_db(request):
'''
This syncs the internal BenchCoach Database and the TeamSnap Database
'''
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
r={}
r['team/opponent'] = []
for team in Opponent.objects.filter(team_id=TEAM_ID):
r['team/opponent'] += update_opponent(team, create_benchcoach_object=True, create_related=True)
for team in Team.objects.filter(id=TEAM_ID):
r['team/opponent'] += update_team(team, create_benchcoach_object=True, create_related=True)
r['location'] = []
for location in Location.objects.filter(team_id=TEAM_ID):
r['team/location'] += update_location(location, create_benchcoach_object=True, create_related=True)
r['member'] = []
for member in Member.objects.filter(team_id=TEAM_ID, is_non_player=False):
r['member'] += update_member(member, create_benchcoach_object=True, create_related=True)
r['event'] = []
for event in Event.objects.filter(team_id=TEAM_ID):
r['event'] += update_event(event, create_benchcoach_object=True, create_related=True)
r['availability'] = []
for availability in Availability.objects.filter(team_id=TEAM_ID):
r['availability'] += update_availability(availability, create_benchcoach_object=True, create_related=True)
for object_name, results in r.items():
if len(r) == 0:
messages.error(request, f"Error! No {object_name} objects created or updated")
else:
result = [created for obj, created in results]
messages.success(request, f"Success! {sum(result)} {object_name} objects created, {len(result)-sum(result)} {object_name} objects updated.")
data = {
'msg': render_to_string('messages.html', {}, request),
}
return JsonResponse(data)