revamp teamsnap syncing
This commit is contained in:
@@ -1,354 +0,0 @@
|
|||||||
import django.db.models
|
|
||||||
from typing import List, Tuple
|
|
||||||
from benchcoach.models import Availability, Player, Team, Positioning, Event, Venue
|
|
||||||
from teamsnap.teamsnap.api import TeamSnap
|
|
||||||
import teamsnap.models
|
|
||||||
|
|
||||||
from benchcoach.utils.sync_engine import AbstractSyncEngine
|
|
||||||
|
|
||||||
class TeamsnapSyncEngine(AbstractSyncEngine):
|
|
||||||
models = [
|
|
||||||
Availability,
|
|
||||||
Player,
|
|
||||||
Team,
|
|
||||||
# Positioning, # Not Implemented
|
|
||||||
Event,
|
|
||||||
Venue
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, managed_team_teamsnap_id, teamsnap_token):
|
|
||||||
self.managed_teamsnap_team_id = managed_team_teamsnap_id
|
|
||||||
self.client = TeamSnap(token=teamsnap_token)
|
|
||||||
|
|
||||||
def _update_teamsnapdb_from_teamsnapapi(self, teamsnap_instance):
|
|
||||||
ApiObject = {
|
|
||||||
teamsnap.models.Availability:teamsnap.teamsnap.api.Availability,
|
|
||||||
teamsnap.models.Member:teamsnap.teamsnap.api.Member,
|
|
||||||
teamsnap.models.Team:teamsnap.teamsnap.api.Team,
|
|
||||||
teamsnap.models.Opponent:teamsnap.teamsnap.api.Opponent,
|
|
||||||
# teamsnap.models.LineupEntry # Not Implemented
|
|
||||||
teamsnap.models.Event:teamsnap.teamsnap.api.Event,
|
|
||||||
teamsnap.models.Location:teamsnap.teamsnap.api.Location
|
|
||||||
}.get(teamsnap_instance._meta.model)
|
|
||||||
teamsnap_model = teamsnap_instance._meta.model
|
|
||||||
new_data = ApiObject.get(client=self.client, id=teamsnap_instance.id).data
|
|
||||||
obj, created = self._update_or_create_from_teamsnapapi(teamsnap_model, new_data)
|
|
||||||
return [(obj, created)]
|
|
||||||
|
|
||||||
def _update_or_create_from_teamsnapapi(self, teamsnap_model, teamsnap_data, create_benchcoach_object = False):
|
|
||||||
related_objects = {}
|
|
||||||
fields = ['id', 'created_at', 'updated_at']
|
|
||||||
if teamsnap_model == teamsnap.models.Event:
|
|
||||||
fields += [
|
|
||||||
'label',
|
|
||||||
'start_date',
|
|
||||||
'formatted_title',
|
|
||||||
'points_for_opponent',
|
|
||||||
'points_for_team',
|
|
||||||
'is_game',
|
|
||||||
'game_type'
|
|
||||||
]
|
|
||||||
if teamsnap_data.get('location_id'):
|
|
||||||
related_objects['location'] = self._update_or_create_from_teamsnapapi(
|
|
||||||
teamsnap.models.Location,
|
|
||||||
{'id':teamsnap_data['location_id']}
|
|
||||||
)
|
|
||||||
if teamsnap_data.get('opponent_id'):
|
|
||||||
related_objects['opponent'] = self._update_or_create_from_teamsnapapi(
|
|
||||||
teamsnap.models.Opponent,
|
|
||||||
{'id':teamsnap_data['opponent_id']}
|
|
||||||
)
|
|
||||||
|
|
||||||
elif teamsnap_model == teamsnap.models.Opponent:
|
|
||||||
fields += ['name']
|
|
||||||
|
|
||||||
elif teamsnap_model == teamsnap.models.Team:
|
|
||||||
fields += ['name']
|
|
||||||
|
|
||||||
elif teamsnap_model == teamsnap.models.Location:
|
|
||||||
fields += ['name']
|
|
||||||
|
|
||||||
elif teamsnap_model == teamsnap.models.Member:
|
|
||||||
fields += [
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
'jersey_number',
|
|
||||||
'is_non_player'
|
|
||||||
]
|
|
||||||
elif teamsnap_model == teamsnap.models.Availability:
|
|
||||||
fields += ['status_code']
|
|
||||||
|
|
||||||
related_objects['member'] = self._update_or_create_from_teamsnapapi(
|
|
||||||
teamsnap.models.Member,
|
|
||||||
{'id': teamsnap_data['member_id']}
|
|
||||||
)
|
|
||||||
|
|
||||||
related_objects['event'] = self._update_or_create_from_teamsnapapi(
|
|
||||||
teamsnap.models.Event,
|
|
||||||
{'id': teamsnap_data['event_id']}
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
if teamsnap_data.get('team_id'):
|
|
||||||
related_objects['team'] = self._update_or_create_from_teamsnapapi(teamsnap.models.Team,
|
|
||||||
{"id":teamsnap_data['team_id']})
|
|
||||||
|
|
||||||
data = {field: teamsnap_data[field] for field in fields if teamsnap_data.get(field) != None}
|
|
||||||
id = data.pop('id')
|
|
||||||
instance, created = teamsnap_model.objects.update_or_create(id=id, defaults=data)
|
|
||||||
r_related_objects = []
|
|
||||||
for related_object_name, related_objectcreated_list in related_objects.items():
|
|
||||||
related_object, created = related_objectcreated_list[0] #FIXME This can't be right, do we need a list for related?
|
|
||||||
setattr(instance, related_object_name, related_object)
|
|
||||||
r_related_objects.append((related_object, created))
|
|
||||||
instance.save()
|
|
||||||
# if create_benchcoach_object:
|
|
||||||
# ben
|
|
||||||
return [(instance, created)] + r_related_objects
|
|
||||||
|
|
||||||
def _update_teamsnapdb_to_benchcoachdb(self, benchcoach_instance, teamsnap_instance,
|
|
||||||
create_if_doesnt_exist: bool = False) -> List[Tuple[django.db.models.Model, bool]]:
|
|
||||||
''' Function to update from a teamsnap object to Benchcoach object.
|
|
||||||
|
|
||||||
:param d: The information to update.
|
|
||||||
: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
|
|
||||||
:param create_related: This is here for decoration only. It doesn't do anything.
|
|
||||||
:return: a list of tuples in the form (obj, did_create) for created or modified objects.
|
|
||||||
'''
|
|
||||||
|
|
||||||
if isinstance(teamsnap_instance, teamsnap.models.Event):
|
|
||||||
benchcoach_model = Event
|
|
||||||
|
|
||||||
d = {
|
|
||||||
'start': teamsnap_instance.start_date,
|
|
||||||
}
|
|
||||||
|
|
||||||
if teamsnap_instance.team:
|
|
||||||
if teamsnap_instance.team.benchcoach_object:
|
|
||||||
if teamsnap_instance.game_type == "Home":
|
|
||||||
d['home_team'] = teamsnap_instance.team.benchcoach_object
|
|
||||||
elif teamsnap_instance.game_type == "Away":
|
|
||||||
d['away_team'] = teamsnap_instance.team.benchcoach_object
|
|
||||||
elif not teamsnap_instance.team.benchcoach_object:
|
|
||||||
raise Team.DoesNotExist
|
|
||||||
|
|
||||||
if teamsnap_instance.opponent:
|
|
||||||
if teamsnap_instance.opponent.benchcoach_object:
|
|
||||||
if teamsnap_instance.game_type == 'Home':
|
|
||||||
d['away_team'] = teamsnap_instance.opponent.benchcoach_object
|
|
||||||
elif teamsnap_instance.game_type == 'Away':
|
|
||||||
d['home_team'] = teamsnap_instance.opponent.benchcoach_object
|
|
||||||
elif not teamsnap_instance.opponent.benchcoach_object:
|
|
||||||
raise Team.DoesNotExist
|
|
||||||
|
|
||||||
if teamsnap_instance.location:
|
|
||||||
if teamsnap_instance.location.benchcoach_object:
|
|
||||||
if teamsnap_instance.location:
|
|
||||||
d['venue'] = teamsnap_instance.location.benchcoach_object
|
|
||||||
elif not teamsnap_instance.location.benchcoach_object:
|
|
||||||
raise Venue.DoesNotExist
|
|
||||||
|
|
||||||
elif isinstance(teamsnap_instance, teamsnap.models.Opponent):
|
|
||||||
benchcoach_model = Team
|
|
||||||
d = {
|
|
||||||
'name': teamsnap_instance.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
elif isinstance(teamsnap_instance, teamsnap.models.Team):
|
|
||||||
benchcoach_model = Team
|
|
||||||
d = {
|
|
||||||
'name': teamsnap_instance.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
elif isinstance(teamsnap_instance, teamsnap.models.Location):
|
|
||||||
benchcoach_model = Venue
|
|
||||||
d = {
|
|
||||||
'name': teamsnap_instance.name,
|
|
||||||
}
|
|
||||||
|
|
||||||
elif isinstance(teamsnap_instance, teamsnap.models.Member):
|
|
||||||
benchcoach_model = Player
|
|
||||||
d = {
|
|
||||||
'first_name': teamsnap_instance.first_name,
|
|
||||||
'last_name': teamsnap_instance.last_name,
|
|
||||||
'jersey_number': teamsnap_instance.jersey_number,
|
|
||||||
}
|
|
||||||
|
|
||||||
elif isinstance(teamsnap_instance, teamsnap.models.Availability):
|
|
||||||
benchcoach_model = Availability
|
|
||||||
|
|
||||||
translation = {
|
|
||||||
teamsnap_instance.YES: Availability.YES,
|
|
||||||
teamsnap_instance.NO: Availability.NO,
|
|
||||||
teamsnap_instance.MAYBE: Availability.MAYBE
|
|
||||||
}
|
|
||||||
|
|
||||||
d = {
|
|
||||||
'available': translation.get(teamsnap_instance.status_code, Availability.UNKNOWN),
|
|
||||||
'player': teamsnap_instance.member.benchcoach_object,
|
|
||||||
'event': teamsnap_instance.event.benchcoach_object
|
|
||||||
}
|
|
||||||
|
|
||||||
r = []
|
|
||||||
|
|
||||||
if teamsnap_instance.member.benchcoach_object:
|
|
||||||
d['player'] = teamsnap_instance.member.benchcoach_object
|
|
||||||
elif not teamsnap_instance.member.benchcoach_object:
|
|
||||||
raise Availability.DoesNotExist
|
|
||||||
|
|
||||||
if teamsnap_instance.event.benchcoach_object:
|
|
||||||
d['event'] = teamsnap_instance.event.benchcoach_object
|
|
||||||
elif not teamsnap_instance.event.benchcoach_object:
|
|
||||||
raise Event.DoesNotExist
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
r=[]
|
|
||||||
if teamsnap_instance.benchcoach_object:
|
|
||||||
benchcoach_object = benchcoach_model.objects.filter(id=teamsnap_instance.benchcoach_object.id)
|
|
||||||
benchcoach_object.update(**d)
|
|
||||||
created = False
|
|
||||||
r.append((benchcoach_object.first(), created))
|
|
||||||
# elif not teamsnap_instance.benchcoach_object and create_if_doesnt_exist:
|
|
||||||
elif not teamsnap_instance.benchcoach_object:
|
|
||||||
raise django.db.models.Model.DoesNotExist
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def _find_counterpart(self, instance):
|
|
||||||
instance_type = type(instance)
|
|
||||||
if instance_type == Availability:
|
|
||||||
counterpart_instance = instance.teamsnap_availability
|
|
||||||
|
|
||||||
elif instance_type == Player:
|
|
||||||
counterpart_instance = instance.teamsnap_member
|
|
||||||
|
|
||||||
elif instance_type == Event:
|
|
||||||
counterpart_instance = instance.teamsnap_event
|
|
||||||
|
|
||||||
elif instance_type == Venue:
|
|
||||||
counterpart_instance = instance.teamsnap_location
|
|
||||||
|
|
||||||
elif instance_type == Team:
|
|
||||||
if hasattr(instance, 'teamsnap_opponent'):
|
|
||||||
counterpart_instance = instance.teamsnap_opponent
|
|
||||||
elif hasattr(instance, 'teamsnap_team'):
|
|
||||||
counterpart_instance = instance.teamsnap_team
|
|
||||||
else:
|
|
||||||
raise ValueError("instance doesn't seem to be an teamsnap opponent or a teamsnap team")
|
|
||||||
|
|
||||||
elif instance_type == Positioning:
|
|
||||||
counterpart_instance = instance.teamsnap_lineupentry
|
|
||||||
|
|
||||||
if not counterpart_instance: raise Exception()
|
|
||||||
|
|
||||||
return counterpart_instance
|
|
||||||
|
|
||||||
def _fetch_new_data(self, instance):
|
|
||||||
api_object = instance.ApiObject.get(client=self.client, id=instance.id)
|
|
||||||
return api_object.data
|
|
||||||
|
|
||||||
def _fetch_sync(self, instance):
|
|
||||||
r=[]
|
|
||||||
counterpart_instance = self._find_counterpart(instance)
|
|
||||||
r += self._update_teamsnapdb_from_teamsnapapi(counterpart_instance)
|
|
||||||
r += self._update_teamsnapdb_to_benchcoachdb(instance, counterpart_instance)
|
|
||||||
return r
|
|
||||||
|
|
||||||
def _sync_qs (self, qs, direction):
|
|
||||||
if qs.model not in self.models:
|
|
||||||
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
|
||||||
|
|
||||||
r=[]
|
|
||||||
|
|
||||||
for instance in qs:
|
|
||||||
r += self._sync_instance(instance, direction=direction)
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def _sync_instance(self, instance, direction, data=None):
|
|
||||||
r=[]
|
|
||||||
if direction == 'download':
|
|
||||||
r += self._fetch_sync(instance)
|
|
||||||
|
|
||||||
elif direction == 'upload':
|
|
||||||
raise NotImplementedError('Uploading not supported by this sync engine yet.')
|
|
||||||
else:
|
|
||||||
raise TypeError(f"Direction {direction} not supported. 'upload' or 'download' must be specified")
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def sync(self, qs: django.db.models.QuerySet = None, instance: django.db.models.Model = None,
|
|
||||||
direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
|
||||||
if not qs and not instance:
|
|
||||||
raise TypeError(f"sync requires either a QuerySet or model instance to be provided")
|
|
||||||
if qs and instance:
|
|
||||||
raise TypeError(f"sync requires either a QuerySet or model instance to be provided, but not both")
|
|
||||||
elif qs:
|
|
||||||
r = self._sync_qs(qs, direction)
|
|
||||||
elif instance:
|
|
||||||
r = self._sync_instance(instance, direction)
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def import_items(self, object_name):
|
|
||||||
object_names = []
|
|
||||||
if object_name == 'team':
|
|
||||||
object_names += ['opponent', 'team']
|
|
||||||
elif object_name == 'player':
|
|
||||||
object_names = ['member']
|
|
||||||
elif object_name == 'venue':
|
|
||||||
object_name == ['location']
|
|
||||||
elif object_name in [model.__name__.lower() for model in self.models]:
|
|
||||||
object_names += [object_name]
|
|
||||||
|
|
||||||
if len(object_names) == 0:
|
|
||||||
raise ValueError('no valid keyword provided')
|
|
||||||
|
|
||||||
for object_name in object_names:
|
|
||||||
Object = {
|
|
||||||
obj.__name__.lower(): obj
|
|
||||||
for obj in
|
|
||||||
[
|
|
||||||
teamsnap.models.Availability,
|
|
||||||
teamsnap.models.Event,
|
|
||||||
# teamsnap.models.LineupEntry,
|
|
||||||
teamsnap.models.Location,
|
|
||||||
teamsnap.models.Member,
|
|
||||||
teamsnap.models.Opponent,
|
|
||||||
teamsnap.models.Team,
|
|
||||||
# teamsnap.models.User
|
|
||||||
]
|
|
||||||
}.get(object_name)
|
|
||||||
|
|
||||||
ApiObject = {
|
|
||||||
apiobj.__name__.lower(): apiobj
|
|
||||||
for apiobj in
|
|
||||||
[
|
|
||||||
teamsnap.teamsnap.api.Availability,
|
|
||||||
teamsnap.teamsnap.api.Event,
|
|
||||||
# teamsnap.teamsnap.api.LineupEntry,
|
|
||||||
teamsnap.teamsnap.api.Location,
|
|
||||||
teamsnap.teamsnap.api.Member,
|
|
||||||
teamsnap.teamsnap.api.Opponent,
|
|
||||||
teamsnap.teamsnap.api.Team,
|
|
||||||
# teamsnap.teamsnap.api.User
|
|
||||||
]
|
|
||||||
}.get(object_name)
|
|
||||||
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not Object: raise KeyError(f"key {object_name} not found.")
|
|
||||||
r = []
|
|
||||||
|
|
||||||
a = ApiObject.search(self.client, team_id=self.managed_teamsnap_team_id)
|
|
||||||
for _a in a:
|
|
||||||
response = self._update_or_create_from_teamsnapapi(Object, _a.data)
|
|
||||||
r += response
|
|
||||||
|
|
||||||
return r
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
import os
|
|
||||||
|
|
||||||
from benchcoach.utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
|
||||||
|
|
||||||
import benchcoach.models
|
|
||||||
|
|
||||||
TEAMSNAP_TOKEN = os.environ['TEAMSNAP_TOKEN']
|
|
||||||
TEAM_TEAMSNAP_ID = os.environ['TEAM_TEAMSNAP_ID']
|
|
||||||
|
|
||||||
syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
|
||||||
r=syncengine.import_items('event')
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TestEventModel(TestCase):
|
|
||||||
fixtures = ['minimal']
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
|
||||||
|
|
||||||
def test_all_models(self):
|
|
||||||
for Model in self.syncengine.models:
|
|
||||||
with self.subTest():
|
|
||||||
self.syncengine.import_items(Model.__name__.lower())
|
|
||||||
pass
|
|
||||||
@@ -25,8 +25,6 @@ class Team(TeamsnapBaseModel):
|
|||||||
name = models.CharField(max_length=50, null=True)
|
name = models.CharField(max_length=50, null=True)
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Team,
|
benchcoach.models.Team,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_team"
|
related_name="teamsnap_team"
|
||||||
)
|
)
|
||||||
@@ -74,8 +72,6 @@ class Opponent(TeamsnapManagedObjectModel):
|
|||||||
name = models.CharField(max_length=50, null=True)
|
name = models.CharField(max_length=50, null=True)
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Team,
|
benchcoach.models.Team,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_opponent"
|
related_name="teamsnap_opponent"
|
||||||
)
|
)
|
||||||
@@ -86,8 +82,6 @@ class Location(TeamsnapManagedObjectModel):
|
|||||||
name = models.CharField(max_length=50, null=True)
|
name = models.CharField(max_length=50, null=True)
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Venue,
|
benchcoach.models.Venue,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_location"
|
related_name="teamsnap_location"
|
||||||
)
|
)
|
||||||
@@ -101,8 +95,6 @@ class Member(TeamsnapManagedObjectModel):
|
|||||||
name = models.CharField(max_length=50, null=True)
|
name = models.CharField(max_length=50, null=True)
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Player,
|
benchcoach.models.Player,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_member"
|
related_name="teamsnap_member"
|
||||||
)
|
)
|
||||||
@@ -126,8 +118,6 @@ class Event(TeamsnapManagedObjectModel):
|
|||||||
type = 'event'
|
type = 'event'
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Event,
|
benchcoach.models.Event,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_event"
|
related_name="teamsnap_event"
|
||||||
)
|
)
|
||||||
@@ -161,8 +151,6 @@ class Availability(TeamsnapManagedObjectModel):
|
|||||||
member = models.ForeignKey(Member, null=True, on_delete=models.CASCADE)
|
member = models.ForeignKey(Member, null=True, on_delete=models.CASCADE)
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Availability,
|
benchcoach.models.Availability,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_availability"
|
related_name="teamsnap_availability"
|
||||||
)
|
)
|
||||||
@@ -193,8 +181,6 @@ class LineupEntry(TeamsnapManagedObjectModel):
|
|||||||
]
|
]
|
||||||
benchcoach_object = models.OneToOneField(
|
benchcoach_object = models.OneToOneField(
|
||||||
benchcoach.models.Positioning,
|
benchcoach.models.Positioning,
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name="teamsnap_lineupentry"
|
related_name="teamsnap_lineupentry"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,10 @@
|
|||||||
|
|
||||||
{% include 'messages.html' %}
|
{% include 'messages.html' %}
|
||||||
|
|
||||||
|
<a class="btn btn-primary" href="{% url 'import' %}" role="button">
|
||||||
|
<i class="bi bi-arrow-clockwise"></i><i class="bi bi-asterisk"></i> Import
|
||||||
|
</a>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for obj_name, obj_data in teamsnap_objects.items %}
|
{% for obj_name, obj_data in teamsnap_objects.items %}
|
||||||
@@ -17,11 +21,8 @@
|
|||||||
<td>{{ obj_data.object_count }}</td>
|
<td>{{ obj_data.object_count }}</td>
|
||||||
<th>{{ obj_data.counterpart.name }} </th>
|
<th>{{ obj_data.counterpart.name }} </th>
|
||||||
<td>{{ obj_data.counterpart.object_count }}</td>
|
<td>{{ obj_data.counterpart.object_count }}</td>
|
||||||
<td><a class="btn btn-primary" href="{% url 'update' object_name=obj_name %}" role="button">
|
<td><a class="btn btn-primary" href="{% url 'send' object_name=obj_data.counterpart.name %}" role="button">
|
||||||
<i class="bi bi-cloud-fill"></i><i class="bi bi-arrow-right"></i><i class="bi bi-asterisk"></i>
|
<i class="bi bi-arrow-clockwise"></i><i class="bi bi-asterisk"></i> Sync
|
||||||
</a></td>
|
|
||||||
<td><a class="btn btn-primary" href="{% url 'send' object_name=obj_name %}" role="button">
|
|
||||||
<i class="bi bi-asterisk"/><i class="bi bi-arrow-right"/><i class="bi bi-clipboard"/>
|
|
||||||
</a></td>
|
</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ urlpatterns = [
|
|||||||
path('sync_benchcoach_db', views.sync_teamsnapdb_to_benchcoachdb, name="sync benchcoach"),
|
path('sync_benchcoach_db', views.sync_teamsnapdb_to_benchcoachdb, name="sync benchcoach"),
|
||||||
path('update/<str:object_name>', views.update_teamsnapdb_from_teamsnapapi, name="update"),
|
path('update/<str:object_name>', views.update_teamsnapdb_from_teamsnapapi, name="update"),
|
||||||
path('send/<str:object_name>', views.send_to_benchcoach, name="send"),
|
path('send/<str:object_name>', views.send_to_benchcoach, name="send"),
|
||||||
path('sync/', views.sync, name="sync")
|
path('sync/', views.sync, name="sync"),
|
||||||
|
path('import/', views.import_teamsnap, name="import")
|
||||||
# path('import_teamsnap', views.import_teamsnap, name="import teamsnap"),
|
# path('import_teamsnap', views.import_teamsnap, name="import teamsnap"),
|
||||||
]
|
]
|
||||||
457
teamsnap/utils/teamsnap_sync_engine.py
Normal file
457
teamsnap/utils/teamsnap_sync_engine.py
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
import django.db.models
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
import benchcoach.models
|
||||||
|
from benchcoach.models import BenchcoachModel, Availability, Player, Team, Positioning, Event, Venue
|
||||||
|
from teamsnap.teamsnap.api import TeamSnap
|
||||||
|
import teamsnap.models
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
|
from benchcoach.utils.sync_engine import AbstractSyncEngine
|
||||||
|
|
||||||
|
class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||||
|
models = [
|
||||||
|
Availability,
|
||||||
|
Player,
|
||||||
|
Team,
|
||||||
|
# Positioning, # Not Implemented
|
||||||
|
Event,
|
||||||
|
Venue
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, managed_team_teamsnap_id, teamsnap_token):
|
||||||
|
self.managed_teamsnap_team_id = managed_team_teamsnap_id
|
||||||
|
self.client = TeamSnap(token=teamsnap_token)
|
||||||
|
|
||||||
|
def _bulk_sync_from_teamsnap(self, qs:QuerySet):
|
||||||
|
# -------------------------------------------------------------------------------------------
|
||||||
|
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
||||||
|
# which would simplify all this, but I couldn't figure out how to implement in the
|
||||||
|
# teamsnap model foreign key and "related_name" that didn't cause conflicts. I don't
|
||||||
|
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||||
|
# -------------------------------------------------------------------------------------------
|
||||||
|
benchcoachmodel_to_teamsnapfield = {
|
||||||
|
Availability:'teamsnap_availability',
|
||||||
|
Player:'teamsnap_member',
|
||||||
|
Team:'teamsnap_opponent',
|
||||||
|
# Positioning:'teamsnap_lineupentry', # Not Implemented Yet, but will be 'teamsnap_lineupentry'
|
||||||
|
Event:'teamsnap_event',
|
||||||
|
Venue:'teamsnap_location'
|
||||||
|
}
|
||||||
|
|
||||||
|
teamsnapmodel_to_apiobject = {
|
||||||
|
teamsnap.models.Availability: teamsnap.teamsnap.api.Availability,
|
||||||
|
teamsnap.models.Event: teamsnap.teamsnap.api.Event,
|
||||||
|
# teamsnap.models.LineupEntry:teamsnap.teamsnap.api.LineupEntry, # Not implemented Yet
|
||||||
|
teamsnap.models.Location: teamsnap.teamsnap.api.Location,
|
||||||
|
teamsnap.models.Member: teamsnap.teamsnap.api.Member,
|
||||||
|
teamsnap.models.Opponent: teamsnap.teamsnap.api.Opponent,
|
||||||
|
teamsnap.models.Team: teamsnap.teamsnap.api.Team,
|
||||||
|
# teamsnap.models.User:teamsnap.teamsnap.api.User # Not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
|
apiobject_to_teamsnapmodel = {v:k for k,v in teamsnapmodel_to_apiobject.items()}
|
||||||
|
|
||||||
|
if isinstance(qs.first(), benchcoach.models.Team):
|
||||||
|
# This situation requires special attention because opponents and teams share a table in BenchCoach
|
||||||
|
if getattr(qs.first(), 'teamsnap_team', None):
|
||||||
|
teamsnap_attribute_name = 'teamsnap_team'
|
||||||
|
elif getattr(qs.first(), 'teamsnap_opponent', None):
|
||||||
|
teamsnap_attribute_name = 'teamsnap_opponent'
|
||||||
|
else:
|
||||||
|
teamsnap_attribute_name = benchcoachmodel_to_teamsnapfield.get(type(qs.first()))
|
||||||
|
|
||||||
|
ids = [getattr(i, teamsnap_attribute_name).id for i in qs]
|
||||||
|
ApiObject = teamsnapmodel_to_apiobject.get(type(getattr(qs.first(), teamsnap_attribute_name)))
|
||||||
|
api_responses = ApiObject.search(client=self.client, id=",".join(ids))
|
||||||
|
r = []
|
||||||
|
for api_response in api_responses:
|
||||||
|
teamsnap_instance = apiobject_to_teamsnapmodel.get(type(api_response)).objects.get(id=api_response.data['id'])
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, api_response)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, teamsnap_instance.benchcoach_object)
|
||||||
|
r.append(response)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def _sync_from_teamsnap(self, benchcoach_instance:BenchcoachModel):
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------------------
|
||||||
|
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
||||||
|
# which would simplify all this, but I couldn't figure out how to implement in the
|
||||||
|
# teamsnap model foreign key and "related_name" that didn't cause conflicts. I don't
|
||||||
|
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||||
|
# -------------------------------------------------------------------------------------------
|
||||||
|
benchcoachmodel_to_teamsnapfield = {
|
||||||
|
Availability:'teamsnap_availability',
|
||||||
|
Player:'teamsnap_member',
|
||||||
|
Team:'teamsnap_opponent',
|
||||||
|
# Positioning:'teamsnap_lineupentry', # Not Implemented Yet, but will be 'teamsnap_lineupentry'
|
||||||
|
Event:'teamsnap_event',
|
||||||
|
Venue:'teamsnap_location'
|
||||||
|
}
|
||||||
|
|
||||||
|
teamsnapmodel_to_apiobject = {
|
||||||
|
teamsnap.models.Availability: teamsnap.teamsnap.api.Availability,
|
||||||
|
teamsnap.models.Event: teamsnap.teamsnap.api.Event,
|
||||||
|
# teamsnap.models.LineupEntry:teamsnap.teamsnap.api.LineupEntry, # Not implemented Yet
|
||||||
|
teamsnap.models.Location: teamsnap.teamsnap.api.Location,
|
||||||
|
teamsnap.models.Member: teamsnap.teamsnap.api.Member,
|
||||||
|
teamsnap.models.Opponent: teamsnap.teamsnap.api.Opponent,
|
||||||
|
teamsnap.models.Team: teamsnap.teamsnap.api.Team,
|
||||||
|
# teamsnap.models.User:teamsnap.teamsnap.api.User # Not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
|
if isinstance(benchcoach_instance, benchcoach.models.Team):
|
||||||
|
# This situation requires special attention because opponents and teams share a table in BenchCoach
|
||||||
|
teamsnap_instance = getattr(benchcoach_instance, 'teamsnap_team', None)
|
||||||
|
if not teamsnap_instance: teamsnap_instance = getattr(benchcoach_instance, 'teamsnap_opponent')
|
||||||
|
else:
|
||||||
|
teamsnap_instance = getattr(benchcoach_instance, benchcoachmodel_to_teamsnapfield.get(type(benchcoach_instance)))
|
||||||
|
|
||||||
|
ApiObject = teamsnapmodel_to_apiobject.get(type(teamsnap_instance))
|
||||||
|
api_response = ApiObject.get(self.client, teamsnap_instance.id)
|
||||||
|
|
||||||
|
r = self._update_from_teamsnapdata(teamsnap_instance, api_response)
|
||||||
|
r = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def _update_from_teamsnapdata(self, teamsnap_instance:teamsnap.models.TeamsnapBaseModel, teamsnap_data: teamsnap.teamsnap.api.ApiObject) -> teamsnap.models.TeamsnapBaseModel:
|
||||||
|
''''''
|
||||||
|
if isinstance(teamsnap_data, teamsnap.teamsnap.api.ApiObject):
|
||||||
|
teamsnap_data = teamsnap_data.data
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
if not teamsnap_data['type'] == teamsnap_instance.type:
|
||||||
|
raise Exception()
|
||||||
|
data_type = teamsnap_data['type']
|
||||||
|
fields = ['id', 'created_at', 'updated_at']
|
||||||
|
related_objects = {}
|
||||||
|
if data_type in ['opponent', 'team', 'location']:
|
||||||
|
fields += ['name']
|
||||||
|
elif data_type == 'event':
|
||||||
|
fields += [
|
||||||
|
'label',
|
||||||
|
'start_date',
|
||||||
|
'formatted_title',
|
||||||
|
'points_for_opponent',
|
||||||
|
'points_for_team',
|
||||||
|
'is_game',
|
||||||
|
'game_type'
|
||||||
|
]
|
||||||
|
if teamsnap_data.get('location_id'):
|
||||||
|
related_objects['location'] = teamsnap.models.Location.objects.get(id=teamsnap_data['location_id'])
|
||||||
|
if teamsnap_data.get('opponent_id'):
|
||||||
|
related_objects['opponent'] = teamsnap.models.Opponent.objects.get(id=teamsnap_data['opponent_id'])
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif data_type == 'member':
|
||||||
|
fields += [
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
'jersey_number',
|
||||||
|
'is_non_player'
|
||||||
|
]
|
||||||
|
|
||||||
|
elif data_type == 'availability':
|
||||||
|
fields += ['status_code']
|
||||||
|
related_objects['member'] = teamsnap.models.Member.objects.get(id=teamsnap_data['member_id'])
|
||||||
|
related_objects['event'] = teamsnap.models.Event.objects.get(id=teamsnap_data['event_id'])
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
if teamsnap_data.get('team_id'):
|
||||||
|
related_objects['team'] = teamsnap.models.Team.objects.filter(id=teamsnap_data['team_id']).first()
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
value = teamsnap_data.get(field)
|
||||||
|
# if value is None:
|
||||||
|
# continue
|
||||||
|
# else:
|
||||||
|
setattr(teamsnap_instance,field,value)
|
||||||
|
|
||||||
|
for related_object_name, related_object in related_objects.items():
|
||||||
|
setattr(teamsnap_instance, related_object_name, related_object)
|
||||||
|
|
||||||
|
teamsnap_instance.save()
|
||||||
|
|
||||||
|
return teamsnap_instance
|
||||||
|
|
||||||
|
def _update_teamsnapdb_to_benchcoachdb(self, teamsnap_instance, benchcoach_instance) -> List[Tuple[django.db.models.Model, bool]]:
|
||||||
|
|
||||||
|
if isinstance(teamsnap_instance, teamsnap.models.Event):
|
||||||
|
benchcoach_model = Event
|
||||||
|
|
||||||
|
d = {
|
||||||
|
'start': teamsnap_instance.start_date,
|
||||||
|
}
|
||||||
|
|
||||||
|
if teamsnap_instance.team:
|
||||||
|
if teamsnap_instance.team.benchcoach_object:
|
||||||
|
if teamsnap_instance.game_type == "Home":
|
||||||
|
d['home_team'] = teamsnap_instance.team.benchcoach_object
|
||||||
|
elif teamsnap_instance.game_type == "Away":
|
||||||
|
d['away_team'] = teamsnap_instance.team.benchcoach_object
|
||||||
|
elif not teamsnap_instance.team.benchcoach_object:
|
||||||
|
raise Team.DoesNotExist
|
||||||
|
|
||||||
|
if teamsnap_instance.opponent:
|
||||||
|
if teamsnap_instance.opponent.benchcoach_object:
|
||||||
|
if teamsnap_instance.game_type == 'Home':
|
||||||
|
d['away_team'] = teamsnap_instance.opponent.benchcoach_object
|
||||||
|
elif teamsnap_instance.game_type == 'Away':
|
||||||
|
d['home_team'] = teamsnap_instance.opponent.benchcoach_object
|
||||||
|
elif not teamsnap_instance.opponent.benchcoach_object:
|
||||||
|
raise Team.DoesNotExist
|
||||||
|
pass
|
||||||
|
|
||||||
|
if teamsnap_instance.location:
|
||||||
|
if teamsnap_instance.location.benchcoach_object:
|
||||||
|
if teamsnap_instance.location:
|
||||||
|
d['venue'] = teamsnap_instance.location.benchcoach_object
|
||||||
|
elif not teamsnap_instance.location.benchcoach_object:
|
||||||
|
raise Venue.DoesNotExist
|
||||||
|
|
||||||
|
elif isinstance(teamsnap_instance, teamsnap.models.Opponent):
|
||||||
|
benchcoach_model = Team
|
||||||
|
d = {
|
||||||
|
'name': teamsnap_instance.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
elif isinstance(teamsnap_instance, teamsnap.models.Team):
|
||||||
|
benchcoach_model = Team
|
||||||
|
d = {
|
||||||
|
'name': teamsnap_instance.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
elif isinstance(teamsnap_instance, teamsnap.models.Location):
|
||||||
|
benchcoach_model = Venue
|
||||||
|
d = {
|
||||||
|
'name': teamsnap_instance.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
elif isinstance(teamsnap_instance, teamsnap.models.Member):
|
||||||
|
benchcoach_model = Player
|
||||||
|
d = {
|
||||||
|
'first_name': teamsnap_instance.first_name,
|
||||||
|
'last_name': teamsnap_instance.last_name,
|
||||||
|
'jersey_number': teamsnap_instance.jersey_number,
|
||||||
|
}
|
||||||
|
|
||||||
|
elif isinstance(teamsnap_instance, teamsnap.models.Availability):
|
||||||
|
benchcoach_model = Availability
|
||||||
|
|
||||||
|
translation = {
|
||||||
|
teamsnap_instance.YES: Availability.YES,
|
||||||
|
teamsnap_instance.NO: Availability.NO,
|
||||||
|
teamsnap_instance.MAYBE: Availability.MAYBE
|
||||||
|
}
|
||||||
|
|
||||||
|
d = {
|
||||||
|
'available': translation.get(teamsnap_instance.status_code, Availability.UNKNOWN),
|
||||||
|
'player': teamsnap_instance.member.benchcoach_object,
|
||||||
|
'event': teamsnap_instance.event.benchcoach_object
|
||||||
|
}
|
||||||
|
|
||||||
|
r = []
|
||||||
|
|
||||||
|
if teamsnap_instance.member.benchcoach_object:
|
||||||
|
d['player'] = teamsnap_instance.member.benchcoach_object
|
||||||
|
elif not teamsnap_instance.member.benchcoach_object:
|
||||||
|
raise Player.DoesNotExist
|
||||||
|
|
||||||
|
if teamsnap_instance.event.benchcoach_object:
|
||||||
|
d['event'] = teamsnap_instance.event.benchcoach_object
|
||||||
|
elif not teamsnap_instance.event.benchcoach_object:
|
||||||
|
raise Event.DoesNotExist
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
for field, value in d.items():
|
||||||
|
setattr(benchcoach_instance, field, value)
|
||||||
|
|
||||||
|
benchcoach_instance.save()
|
||||||
|
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||||
|
teamsnap_instance.save()
|
||||||
|
return benchcoach_instance
|
||||||
|
|
||||||
|
def _find_counterpart(self, instance):
|
||||||
|
instance_type = type(instance)
|
||||||
|
counterpart_instance = None
|
||||||
|
if instance_type == Availability:
|
||||||
|
counterpart_instance = instance.teamsnap_availability
|
||||||
|
|
||||||
|
elif instance_type == Player:
|
||||||
|
counterpart_instance = instance.teamsnap_member
|
||||||
|
|
||||||
|
elif instance_type == Event:
|
||||||
|
counterpart_instance = instance.teamsnap_event
|
||||||
|
|
||||||
|
elif instance_type == Venue:
|
||||||
|
counterpart_instance = instance.teamsnap_location
|
||||||
|
|
||||||
|
elif instance_type == Team:
|
||||||
|
if hasattr(instance, 'teamsnap_opponent'):
|
||||||
|
counterpart_instance = instance.teamsnap_opponent
|
||||||
|
elif hasattr(instance, 'teamsnap_team'):
|
||||||
|
counterpart_instance = instance.teamsnap_team
|
||||||
|
else:
|
||||||
|
raise ValueError("instance doesn't seem to be an teamsnap opponent or a teamsnap team")
|
||||||
|
|
||||||
|
elif instance_type == Positioning:
|
||||||
|
counterpart_instance = instance.teamsnap_lineupentry
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
return counterpart_instance
|
||||||
|
|
||||||
|
def _sync_qs (self, qs, direction):
|
||||||
|
if qs.model not in self.models:
|
||||||
|
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
||||||
|
|
||||||
|
r=[]
|
||||||
|
r = self._bulk_sync_from_teamsnap(qs)
|
||||||
|
# for instance in qs:
|
||||||
|
# r += self._sync_instance(instance, direction=direction)
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
def _sync_instance(self, instance, direction, data=None):
|
||||||
|
r=[]
|
||||||
|
if direction == 'download':
|
||||||
|
r.append(self._sync_from_teamsnap(instance))
|
||||||
|
|
||||||
|
elif direction == 'upload':
|
||||||
|
raise NotImplementedError('Uploading not supported by this sync engine yet.')
|
||||||
|
else:
|
||||||
|
raise TypeError(f"Direction {direction} not supported. 'upload' or 'download' must be specified")
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def sync(self, qs: django.db.models.QuerySet = None, instance: django.db.models.Model = None,
|
||||||
|
direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
||||||
|
if not qs and not instance:
|
||||||
|
raise TypeError(f"sync requires either a QuerySet or model instance to be provided")
|
||||||
|
if qs and instance:
|
||||||
|
raise TypeError(f"sync requires either a QuerySet or model instance to be provided, but not both")
|
||||||
|
elif qs:
|
||||||
|
r = self._sync_qs(qs, direction)
|
||||||
|
elif instance:
|
||||||
|
r = self._sync_instance(instance, direction)
|
||||||
|
|
||||||
|
return r
|
||||||
|
|
||||||
|
def import_items(self, object_name=None, object_names=[]):
|
||||||
|
# order is important
|
||||||
|
|
||||||
|
['team', 'opponent', 'location', 'member', 'event', 'availability']
|
||||||
|
kwargs = {'client':self.client,'team_id': self.managed_teamsnap_team_id}
|
||||||
|
r = {}
|
||||||
|
|
||||||
|
# ---team---
|
||||||
|
r['team'] = []
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
||||||
|
if teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Team()
|
||||||
|
benchcoach_instance = benchcoach.models.Team()
|
||||||
|
teamsnap_instance.benchcoach_object=benchcoach_instance
|
||||||
|
benchcoach_instance.save()
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
teamsnap_instance.save()
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['team'].append(response)
|
||||||
|
|
||||||
|
# ---opponent---
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Opponent.search(**kwargs):
|
||||||
|
if teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Opponent()
|
||||||
|
benchcoach_instance = benchcoach.models.Team()
|
||||||
|
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||||
|
benchcoach_instance.save()
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['team'].append(response)
|
||||||
|
|
||||||
|
# ---location---
|
||||||
|
r['location'] = []
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Location.search(**kwargs):
|
||||||
|
if teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Location()
|
||||||
|
benchcoach_instance = benchcoach.models.Venue()
|
||||||
|
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||||
|
benchcoach_instance.save()
|
||||||
|
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['location'].append(response)
|
||||||
|
|
||||||
|
# ---member---
|
||||||
|
# Note: Non players not included in sync.
|
||||||
|
r['member'] = []
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Member.search(**kwargs,
|
||||||
|
is_non_player = False
|
||||||
|
):
|
||||||
|
if teamsnap_data.data['is_non_player'] == True:
|
||||||
|
continue
|
||||||
|
if teamsnap.models.Member.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Member.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Member()
|
||||||
|
benchcoach_instance = benchcoach.models.Player()
|
||||||
|
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||||
|
benchcoach_instance.save()
|
||||||
|
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['member'].append(response)
|
||||||
|
|
||||||
|
# ---event---
|
||||||
|
r['event'] = []
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Event.search(**kwargs):
|
||||||
|
if teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Event()
|
||||||
|
benchcoach_instance = benchcoach.models.Event()
|
||||||
|
teamsnap_instance.benchcoach_object = benchcoach_instance
|
||||||
|
benchcoach_instance.save()
|
||||||
|
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['event'].append(response)
|
||||||
|
|
||||||
|
# ---availability---
|
||||||
|
# Note: Non players not included in sync
|
||||||
|
r['availability'] = []
|
||||||
|
player_ids = [member.id for member in teamsnap.models.Member.objects.filter(is_non_player=False)]
|
||||||
|
for teamsnap_data in teamsnap.teamsnap.api.Availability.search(**kwargs,
|
||||||
|
member_id=",".join(player_ids)
|
||||||
|
):
|
||||||
|
if teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']):
|
||||||
|
teamsnap_instance = teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
|
else:
|
||||||
|
teamsnap_instance = teamsnap.models.Availability()
|
||||||
|
event_instance = benchcoach.models.Event.objects.get(teamsnap_event__id=teamsnap_data.data['event_id'])
|
||||||
|
player_instance = benchcoach.models.Player.objects.get(teamsnap_member__id=teamsnap_data.data['member_id'])
|
||||||
|
benchcoach_instance = benchcoach.models.Availability(event=event_instance, player=player_instance)
|
||||||
|
benchcoach_instance.save()
|
||||||
|
teamsnap_instance.benchcoach_object_id = benchcoach_instance.id
|
||||||
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
|
r['availability'].append(response)
|
||||||
|
|
||||||
|
return r
|
||||||
51
teamsnap/utils/test_sync.py
Normal file
51
teamsnap/utils/test_sync.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
import os
|
||||||
|
|
||||||
|
from teamsnap.utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||||
|
|
||||||
|
import benchcoach.models
|
||||||
|
import teamsnap.models
|
||||||
|
|
||||||
|
TEAMSNAP_TOKEN = os.environ['TEAMSNAP_TOKEN']
|
||||||
|
TEAM_TEAMSNAP_ID = os.environ['TEAM_TEAMSNAP_ID']
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
# syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
||||||
|
# r = syncengine.import_items()
|
||||||
|
|
||||||
|
class TestSync(TestCase):
|
||||||
|
fixtures = ['minimal']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.syncengine = TeamsnapSyncEngine(managed_team_teamsnap_id=TEAM_TEAMSNAP_ID, teamsnap_token=TEAMSNAP_TOKEN)
|
||||||
|
r = self.syncengine.import_items()
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_syncengine(self):
|
||||||
|
# test that the import can be run again
|
||||||
|
r = self.syncengine.import_items()
|
||||||
|
benchcoach_objects = {
|
||||||
|
'availability': list(benchcoach.models.Availability.objects.all()),
|
||||||
|
'event': list(benchcoach.models.Event.objects.all()),
|
||||||
|
'player': list(benchcoach.models.Player.objects.all()),
|
||||||
|
'positioning': list(benchcoach.models.Positioning.objects.all()),
|
||||||
|
'team': list(benchcoach.models.Team.objects.all()),
|
||||||
|
'venue': list(benchcoach.models.Venue.objects.all())
|
||||||
|
}
|
||||||
|
teamsnap_objects = {
|
||||||
|
'availability': list(teamsnap.models.Availability.objects.all()),
|
||||||
|
'event': list(teamsnap.models.Event.objects.all()),
|
||||||
|
'member': list(teamsnap.models.Member.objects.all()),
|
||||||
|
'lineupentry': list(teamsnap.models.LineupEntry.objects.all()),
|
||||||
|
'team': list(teamsnap.models.Team.objects.all()),
|
||||||
|
'opponent': list(teamsnap.models.Opponent.objects.all()),
|
||||||
|
'location': list(teamsnap.models.Location.objects.all())
|
||||||
|
}
|
||||||
|
self.assertIsNotNone(r)
|
||||||
|
|
||||||
|
def test_all_models(self):
|
||||||
|
pass
|
||||||
|
self.syncengine.sync(qs=benchcoach.models.Event.objects.all())
|
||||||
|
breakpoint()
|
||||||
|
pass
|
||||||
@@ -45,7 +45,7 @@ def home(request):
|
|||||||
(Member, benchcoach.models.Player),
|
(Member, benchcoach.models.Player),
|
||||||
(Opponent, benchcoach.models.Team),
|
(Opponent, benchcoach.models.Team),
|
||||||
(Team, benchcoach.models.Team),
|
(Team, benchcoach.models.Team),
|
||||||
(User, None)
|
# (User, {'name':})
|
||||||
]:
|
]:
|
||||||
teamsnap_objects[teamsnap_obj.__name__.lower()] = {}
|
teamsnap_objects[teamsnap_obj.__name__.lower()] = {}
|
||||||
teamsnap_objects[teamsnap_obj.__name__.lower()]['object_count'] = teamsnap_obj.objects.count()
|
teamsnap_objects[teamsnap_obj.__name__.lower()]['object_count'] = teamsnap_obj.objects.count()
|
||||||
@@ -183,41 +183,37 @@ def send_to_benchcoach(request, object_name):
|
|||||||
}.get(object_name)
|
}.get(object_name)
|
||||||
|
|
||||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
||||||
|
TOKEN = request.user.profile.teamsnap_access_token
|
||||||
|
|
||||||
|
sync_engine = TeamsnapSyncEngine(teamsnap_token=TOKEN, managed_team_teamsnap_id=TEAM_ID)
|
||||||
r = {}
|
r = {}
|
||||||
|
|
||||||
r[object_name]=[]
|
r[object_name]=[]
|
||||||
|
|
||||||
if object_name == 'team':
|
if object_name == 'team':
|
||||||
for team in Object.objects.filter(id=TEAM_ID):
|
r[object_name] = sync_engine.sync(qs=benchcoach.models.Team.objects.all())
|
||||||
r[object_name] += update_opponent(team, create_benchcoach_object=True, create_related=True)
|
|
||||||
|
|
||||||
if object_name == 'opponent':
|
if object_name == 'venue':
|
||||||
for team in Object.objects.filter(team_id=TEAM_ID):
|
r[object_name] = sync_engine.sync(qs=benchcoach.models.Venue.objects.all())
|
||||||
r[object_name] += update_team(team, create_benchcoach_object=True, create_related=True)
|
pass
|
||||||
|
|
||||||
if object_name == 'location':
|
if object_name == 'player':
|
||||||
for location in Location.objects.filter(team_id=TEAM_ID):
|
r[object_name] = sync_engine.sync(qs=benchcoach.models.Player.objects.all())
|
||||||
r[object_name] += update_location(location, create_benchcoach_object=True, create_related=True)
|
|
||||||
|
|
||||||
if object_name == 'member':
|
|
||||||
for member in Member.objects.filter(team_id=TEAM_ID, is_non_player=False):
|
|
||||||
r[object_name] += update_member(member, create_benchcoach_object=True, create_related=True)
|
|
||||||
|
|
||||||
if object_name == 'event':
|
if object_name == 'event':
|
||||||
for event in Event.objects.filter(team_id=TEAM_ID):
|
r[object_name] = sync_engine.sync(qs=benchcoach.models.Event.objects.all())
|
||||||
r[object_name] += update_event(event, create_benchcoach_object=True, create_related=True)
|
pass
|
||||||
|
|
||||||
if object_name == 'availability':
|
if object_name == 'availability':
|
||||||
for availability in Availability.objects.filter(team_id=TEAM_ID, member__is_non_player=False):
|
r[object_name] = []
|
||||||
r[object_name] += update_availability(availability, create_benchcoach_object=True, create_related=True)
|
for event in benchcoach.models.Player.objects.all():
|
||||||
|
r[object_name] += sync_engine.sync(qs=event.availability_set.all())
|
||||||
|
|
||||||
for object_name, results in r.items():
|
for object_name, results in r.items():
|
||||||
if len(r) == 0:
|
if len(results) == 0:
|
||||||
messages.error(request, f"Error! No {object_name} objects created or updated")
|
messages.error(request, f"Error! No {object_name} objects updated")
|
||||||
else:
|
else:
|
||||||
result = [created for obj, created in results]
|
messages.success(request, f"Success! {len(results)} {object_name} objects updated.")
|
||||||
messages.success(request,
|
|
||||||
f"Success! {sum(result)} {object_name} objects created, {len(result) - sum(result)} {object_name} objects updated.")
|
|
||||||
|
|
||||||
return redirect('teamsnap home')
|
return redirect('teamsnap home')
|
||||||
|
|
||||||
@@ -304,8 +300,21 @@ def sync_teamsnapdb_to_benchcoachdb(request):
|
|||||||
}
|
}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
from .utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||||
|
def import_teamsnap(request):
|
||||||
|
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
||||||
|
TOKEN = request.user.profile.teamsnap_access_token
|
||||||
|
|
||||||
|
sync_engine = TeamsnapSyncEngine(teamsnap_token=TOKEN, managed_team_teamsnap_id=TEAM_ID)
|
||||||
|
r = sync_engine.import_items()
|
||||||
|
|
||||||
|
for object_name, results in r.items():
|
||||||
|
if len(results) == 0:
|
||||||
|
messages.error(request, f"Error! No {object_name} objects created or updated")
|
||||||
|
else:
|
||||||
|
messages.success(request, f"Success! {len(results)} {object_name} objects imported")
|
||||||
|
|
||||||
|
return redirect('teamsnap home')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user