cleanup and added comments
This commit is contained in:
@@ -1,19 +0,0 @@
|
|||||||
{% extends 'benchcoach/detail.html' %}
|
|
||||||
|
|
||||||
{% block navbar %}
|
|
||||||
{% with events_tab="active" %}
|
|
||||||
{{ block.super }}
|
|
||||||
{% endwith %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block page_heading %}
|
|
||||||
Event
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block rows %}
|
|
||||||
<tr><th>Date</th><td>{{ event.start.date|date }}</td></tr>
|
|
||||||
<tr><th>Time</th><td>{{ event.start.time|time }}</td></tr>
|
|
||||||
<tr><th>Away</th><td>{{ event.away_team.name }}</td></tr>
|
|
||||||
<tr><th>Home</th><td>{{ event.home_team.name }}</td></tr>
|
|
||||||
<tr><th>Venue</th><td>{{ event.venue }}</td></tr>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -18,7 +18,7 @@ Events
|
|||||||
<h6>{{ event.away_team|default_if_none:"" }} vs. {{ event.home_team|default_if_none:"" }}</h6>
|
<h6>{{ event.away_team|default_if_none:"" }} vs. {{ event.home_team|default_if_none:"" }}</h6>
|
||||||
{{ event.start|date:"l, F j, Y g:i A" }} <br>
|
{{ event.start|date:"l, F j, Y g:i A" }} <br>
|
||||||
{{ event.venue.name }} <br>
|
{{ event.venue.name }} <br>
|
||||||
<a href="{% url 'edit lineup' event_id=event.id %}">Edit Lineup...</a>
|
<a href="{% url 'event' event_id=event.id %}">Edit Lineup...</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -28,18 +28,18 @@
|
|||||||
<ul class="nav nav-tabs nav-fill bg-white" role="tablist">
|
<ul class="nav nav-tabs nav-fill bg-white" role="tablist">
|
||||||
{% if previous_event %}
|
{% if previous_event %}
|
||||||
<li class="nav-item m-1">
|
<li class="nav-item m-1">
|
||||||
<a href="{% url 'edit lineup' event_id=previous_event.id active_tab=active_tab %}">
|
<a href="{% url 'event' event_id=previous_event.id active_tab=active_tab %}">
|
||||||
<i class="bi bi-chevron-left"></i>{{ previous_event.start|date:"D" }} {{ previous_event.start|date:"n/j" }}
|
<i class="bi bi-chevron-left"></i>{{ previous_event.start|date:"D" }} {{ previous_event.start|date:"n/j" }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<li class="nav-item m-1" role="presentation">
|
<li class="nav-item m-1" role="presentation">
|
||||||
<a id="event-details-tab" class="nav-link {% if active_tab == "details" %}active{% endif %} px-2 py-0" href="{% url 'edit lineup' event_id=event.id active_tab='details'%}">Details</a></li>
|
<a id="event-details-tab" class="nav-link {% if active_tab == "details" %}active{% endif %} px-2 py-0" href="{% url 'event' event_id=event.id active_tab='details'%}">Details</a></li>
|
||||||
<li class="nav-item m-1" role="presentation"><a id="event-lineup-tab" class="nav-link {% if active_tab == "lineup" %}active{% endif %} px-2 py-0" href="{% url 'edit lineup' event_id=event.id active_tab='lineup'%}">Lineup</a></li>
|
<li class="nav-item m-1" role="presentation"><a id="event-lineup-tab" class="nav-link {% if active_tab == "lineup" %}active{% endif %} px-2 py-0" href="{% url 'event' event_id=event.id active_tab='lineup'%}">Lineup</a></li>
|
||||||
{% if next_event %}
|
{% if next_event %}
|
||||||
<li class="nav-item m-1">
|
<li class="nav-item m-1">
|
||||||
<a href="{% url 'edit lineup' event_id=next_event.id active_tab=active_tab%}" role="button">
|
<a href="{% url 'event' event_id=next_event.id active_tab=active_tab%}" role="button">
|
||||||
{{ next_event.start|date:"D" }} {{ next_event.start|date:"n/j" }}<i class="bi bi-chevron-right"></i>
|
{{ next_event.start|date:"D" }} {{ next_event.start|date:"n/j" }}<i class="bi bi-chevron-right"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="event-lineup" class="tab-pane {% if active_tab == "lineup" %}show active{% endif %}" role="tabpanel" aria-labelledby="event-lineup-tab">
|
<div id="event-lineup" class="tab-pane {% if active_tab == "lineup" %}show active{% endif %}" role="tabpanel" aria-labelledby="event-lineup-tab">
|
||||||
<form action="{% url 'edit lineup' event_id=event.id active_tab=active_tab%}" method="post">
|
<form action="{% url 'event' event_id=event.id active_tab=active_tab%}" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ formset.management_form }}
|
{{ formset.management_form }}
|
||||||
<div class="row w-100">
|
<div class="row w-100">
|
||||||
@@ -4,14 +4,12 @@ from django.contrib.auth.decorators import login_required
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('lineup/edit/<int:event_id>/', login_required(views.lineup_edit), name="edit lineup"),
|
|
||||||
path('lineup/edit/<int:event_id>/<str:active_tab>', login_required(views.lineup_edit), name="edit lineup"),
|
|
||||||
path('events/list/', login_required(views.EventListView.as_view()), name="event list"),
|
path('events/list/', login_required(views.EventListView.as_view()), name="event list"),
|
||||||
path('events/<int:pk>/detail', login_required(views.EventDetailView.as_view()), name="event detail"),
|
path('events/<int:event_id>/', login_required(views.event), name="event"),
|
||||||
path('events/<int:pk>/lineup', login_required(views.EventDetailView.as_view()), name="event lineup"),
|
|
||||||
path('players/list/', login_required(views.PlayerListView.as_view()), name="player list"),
|
path('players/list/', login_required(views.PlayerListView.as_view()), name="player list"),
|
||||||
path('teams/list/', login_required(views.TeamListView.as_view()), name="team list"),
|
path('teams/list/', login_required(views.TeamListView.as_view()), name="team list"),
|
||||||
path('venues/list/', login_required(views.VenueListView.as_view()), name="venue list"),
|
path('venues/list/', login_required(views.VenueListView.as_view()), name="venue list"),
|
||||||
path('events/<int:event_id>/card', login_required(views.lineupcard), name="lineup card"),
|
path('events/<int:event_id>/card', login_required(views.lineupcard), name="lineup card"),
|
||||||
path('events/<int:event_id>/csv', views.csv_export, name="lineup csv")
|
path('events/<int:event_id>/csv', views.csv_export, name="lineup csv"),
|
||||||
|
path('events/<int:event_id>/<str:active_tab>', login_required(views.event), name="event")
|
||||||
]
|
]
|
||||||
@@ -3,25 +3,30 @@ from abc import ABC, abstractmethod
|
|||||||
import django.db.models
|
import django.db.models
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
import benchcoach.models
|
||||||
|
|
||||||
class AbstractSyncEngine(ABC):
|
class AbstractSyncEngine(ABC):
|
||||||
models: List[django.db.models.Model]
|
'''
|
||||||
|
Class used for importing and syncing Bench Coach models.
|
||||||
|
'''
|
||||||
|
models: List[benchcoach.models.BenchcoachModel]
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def sync(self, qs: django.db.models.QuerySet = None, instance: django.db.models.Model = None, direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
def sync(self, qs: django.db.models.QuerySet = None, instance: benchcoach.models.BenchcoachModel = None, direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
||||||
'''
|
'''
|
||||||
Syncs the input from/to the service. Either a query set or instance should be provided, but not both.
|
Syncs the input from/to the service. Either a query set or instance should be provided, but not both.
|
||||||
:param qs: the queryset to be updated. If set to 'download', it will be updated from the service, if set to uplad, its contents
|
It does not create Bench Coach objects.
|
||||||
will be sent to the server
|
:param qs: the queryset to be updated.
|
||||||
:param instance: the instance to be updated. If set to 'download', it will be updated from the service, if set to uplad, its contents
|
:param instance: the instance to be updated.
|
||||||
will be sent to the server.
|
:param direction: the sync direction, either 'download' or 'upload'. If set to 'download', it will be updated from the service, if set to upload, its contents
|
||||||
:param direction: the sync direction, either 'download' or 'upload'.
|
will be sent to the service
|
||||||
:return: a list of tuples in the form of (created/updated object, true if created/false if not)
|
:return: a list of BenchCoach objects that have been iterated (but not necessarily changed) during sync.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def import_items(self):
|
def import_items(self):
|
||||||
'''
|
'''
|
||||||
Imports the items from the service.
|
Imports the items from the service. It imports all models specified in the class property 'model'.
|
||||||
:return: a list of tuples in the form of (created/updated object, true if created/false if not)
|
It creates BenchCoach objects, but should not create duplicates.
|
||||||
|
:return: a list of BenchCoach objects that have been iterated (but not necessarily changed) during import.
|
||||||
'''
|
'''
|
||||||
@@ -50,7 +50,15 @@ class VenueListView(ListView):
|
|||||||
context['venues_tab_active'] ='active'
|
context['venues_tab_active'] ='active'
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def lineup_edit(request, event_id, active_tab='details'):
|
def event(request, event_id, active_tab='details'):
|
||||||
|
'''
|
||||||
|
Event is the main page for showing an event.
|
||||||
|
:param request: django request
|
||||||
|
:param event_id: The Bench Coach event ID to display
|
||||||
|
:param active_tab: The desired active tab, supports "lineup" and "details"
|
||||||
|
:return: 'details' renders a page with event information, 'lineup' with lineup information.
|
||||||
|
Either gives context to the template with the event information and formset for the lineup.
|
||||||
|
'''
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
# create a form instance and populate it with data from the request:
|
# create a form instance and populate it with data from the request:
|
||||||
@@ -123,7 +131,7 @@ def lineup_edit(request, event_id, active_tab='details'):
|
|||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"benchcoach/lineup.html",
|
"benchcoach/event.html",
|
||||||
{
|
{
|
||||||
"title": "Lineup",
|
"title": "Lineup",
|
||||||
"active_tab":active_tab,
|
"active_tab":active_tab,
|
||||||
@@ -139,6 +147,13 @@ def lineup_edit(request, event_id, active_tab='details'):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def lineupcard(request, event_id):
|
def lineupcard(request, event_id):
|
||||||
|
'''
|
||||||
|
Lineup Card is an first attempt at replicating the "Lineup Card" from Google sheets.
|
||||||
|
It is incomplete.
|
||||||
|
:param request:
|
||||||
|
:param event_id: The Event ID to generate
|
||||||
|
:return: It generates a page layout. The context has info for event, event details, starting players and all players (both as a queryset)
|
||||||
|
'''
|
||||||
previous_event = Event.objects.filter(id=event_id - 1).first()
|
previous_event = Event.objects.filter(id=event_id - 1).first()
|
||||||
|
|
||||||
event = Event.objects.get(id=event_id)
|
event = Event.objects.get(id=event_id)
|
||||||
@@ -181,6 +196,13 @@ def lineupcard(request, event_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def csv_export(request, event_id):
|
def csv_export(request, event_id):
|
||||||
|
'''
|
||||||
|
Exports a CSV to interface with the Google Sheet. The idea is to bring lineup info into the sheet for backwards compatibility.
|
||||||
|
The row numbers follow each line as comments.
|
||||||
|
:param request:
|
||||||
|
:param event_id:
|
||||||
|
:return: A CSV file.
|
||||||
|
'''
|
||||||
response = HttpResponse(
|
response = HttpResponse(
|
||||||
content_type='text/csv',
|
content_type='text/csv',
|
||||||
headers={'Content-Disposition': f'attachment; filename=lineup-event-{event_id}.csv'},
|
headers={'Content-Disposition': f'attachment; filename=lineup-event-{event_id}.csv'},
|
||||||
@@ -201,17 +223,17 @@ def csv_export(request, event_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
|
# Row number (starts at row 2)
|
||||||
rows.append(event.teamsnap_event.csv_event_title) # 2
|
rows.append(event.teamsnap_event.csv_event_title) # 2
|
||||||
rows.append(event.venue.name) # 3
|
rows.append(event.venue.name) # 3
|
||||||
[rows.append('') for i in range(3)] #4-6
|
[rows.append('') for i in range(3)] # 4-6
|
||||||
p = qs.filter(position='P').first()
|
p = qs.filter(position='P').first()
|
||||||
if p:
|
if p:
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}") #7
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 7
|
||||||
else:
|
else:
|
||||||
rows.append('')
|
rows.append('')
|
||||||
[rows.append('') for i in range(3)] #8-10
|
[rows.append('') for i in range(3)] # 8-10
|
||||||
for pos in ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH']: #11-19
|
for pos in ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH']: # 11-19
|
||||||
p = qs.filter(position=pos).first()
|
p = qs.filter(position=pos).first()
|
||||||
if p:
|
if p:
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
||||||
@@ -220,22 +242,22 @@ def csv_export(request, event_id):
|
|||||||
ehs = qs.filter(position='EH')
|
ehs = qs.filter(position='EH')
|
||||||
if len(ehs) > 0:
|
if len(ehs) > 0:
|
||||||
p=qs.filter(position='EH')[0]
|
p=qs.filter(position='EH')[0]
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 20
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 20
|
||||||
else:
|
else:
|
||||||
rows.append('')
|
rows.append('')
|
||||||
if len(ehs) > 1:
|
if len(ehs) > 1:
|
||||||
p=qs.filter(position='EH')[1]
|
p=qs.filter(position='EH')[1]
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 21
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 21
|
||||||
else:
|
else:
|
||||||
rows.append('')
|
rows.append('')
|
||||||
rows.append('') #22
|
rows.append('') #22
|
||||||
p=qs.filter(position__isnull=False, order=0).first()
|
p=qs.filter(position__isnull=False, order=0).first()
|
||||||
if p:
|
if p:
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 23
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 23
|
||||||
else:
|
else:
|
||||||
rows.append('')
|
rows.append('')
|
||||||
rows.append('')
|
rows.append('') # 24
|
||||||
for p in qs.filter(order__gt=0).order_by('order'):
|
for p in qs.filter(order__gt=0).order_by('order'): # 25-34
|
||||||
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
||||||
|
|
||||||
writer = csv.writer(response)
|
writer = csv.writer(response)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<form method="post" id="formSync" action="{% url 'send' %}">{% csrf_token %}
|
<form method="post" id="formSync" action="{% url 'sync from teamsnap' %}">{% csrf_token %}
|
||||||
<input type="hidden" name="object_name" value="{{ object_name }}">
|
<input type="hidden" name="object_name" value="{{ object_name }}">
|
||||||
<input type="hidden" name="object_id" value={{ object_id }}>
|
<input type="hidden" name="object_id" value={{ object_id }}>
|
||||||
<input type="hidden" name="next" value="{{ next }}">
|
<input type="hidden" name="next" value="{{ next }}">
|
||||||
|
|||||||
@@ -7,15 +7,7 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.home, name='teamsnap home'),
|
path('', views.home, name='teamsnap home'),
|
||||||
# path('events', views.EventsListView.as_view(), name="teamsnap list events"),
|
|
||||||
# path('event-table', views.EventsTableView.as_view(), name="teamsnap table events"),
|
|
||||||
path('table/<str:object>', views.TeamsnapObjTableView.as_view(), name="teamsnap table obj"),
|
|
||||||
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('sync_teamsnap_db', views.sync_teamsnapdb_with_teamsnapapi, name="sync with teamsnapapi"),
|
path('sync/download', views.sync_from_teamsnap, name="sync from teamsnap"),
|
||||||
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('send', views.send_to_benchcoach, name="send"),
|
|
||||||
path('sync/', views.sync, name="sync"),
|
|
||||||
path('import/', views.import_teamsnap, name="import")
|
path('import/', views.import_teamsnap, name="import")
|
||||||
# path('import_teamsnap', views.import_teamsnap, name="import teamsnap"),
|
|
||||||
]
|
]
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
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
|
|
||||||
import benchcoach.models
|
|
||||||
from django.db import models
|
|
||||||
import pytz
|
|
||||||
|
|
||||||
def update_teamsnap_object(d, teamsnap_object: TeamsnapBaseModel, benchcoach_model: models.Model ,create_benchcoach_object: bool = True, create_related: bool = False):
|
|
||||||
''' Function to update from a teamsnap object to Benchcoach object. This method is a simple method when there are no related objects
|
|
||||||
|
|
||||||
: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.
|
|
||||||
'''
|
|
||||||
|
|
||||||
r = []
|
|
||||||
|
|
||||||
if teamsnap_object.benchcoach_object:
|
|
||||||
#TODO I'm not sure this does anything. need to make sure.
|
|
||||||
benchcoach_object = benchcoach_model.objects.filter(id=teamsnap_object.benchcoach_object.id)
|
|
||||||
benchcoach_object.update(**d)
|
|
||||||
created = False
|
|
||||||
r.append((benchcoach_object.first(), created))
|
|
||||||
elif not teamsnap_object.benchcoach_object and create_benchcoach_object:
|
|
||||||
benchcoach_object = benchcoach_model(**d) # create new benchcoach object
|
|
||||||
teamsnap_object.benchcoach_object = benchcoach_object
|
|
||||||
benchcoach_object.save()
|
|
||||||
teamsnap_object.save()
|
|
||||||
created = True
|
|
||||||
r.append((benchcoach_object, created))
|
|
||||||
elif not teamsnap_object.benchcoach_object:
|
|
||||||
raise benchcoach.models.Team.DoesNotExist
|
|
||||||
|
|
||||||
return r
|
|
||||||
|
|
||||||
def update_event(event: Event, create_benchcoach_object: bool = True, create_related=False):
|
|
||||||
benchcoach_model = benchcoach.models.Event
|
|
||||||
|
|
||||||
d = {
|
|
||||||
'start': event.start_date,
|
|
||||||
}
|
|
||||||
|
|
||||||
r = []
|
|
||||||
|
|
||||||
if event.team:
|
|
||||||
if event.team.benchcoach_object:
|
|
||||||
if event.game_type == "Home":
|
|
||||||
d['home_team'] = event.team.benchcoach_object
|
|
||||||
elif event.game_type == "Away":
|
|
||||||
d['away_team'] = event.team.benchcoach_object
|
|
||||||
elif not event.team.benchcoach_object and create_related:
|
|
||||||
# create team object
|
|
||||||
# update_opponent
|
|
||||||
# r.append
|
|
||||||
pass
|
|
||||||
elif not event.team.benchcoach_object:
|
|
||||||
raise benchcoach.models.Team.DoesNotExist
|
|
||||||
|
|
||||||
if event.opponent:
|
|
||||||
if event.opponent.benchcoach_object:
|
|
||||||
if event.game_type == 'Home':
|
|
||||||
d['away_team'] = event.opponent.benchcoach_object
|
|
||||||
elif event.game_type == 'Away':
|
|
||||||
d['home_team'] = event.opponent.benchcoach_object
|
|
||||||
elif not event.opponent.benchcoach_object and create_related:
|
|
||||||
# Create opponent object
|
|
||||||
# update_opponent()
|
|
||||||
# r.append
|
|
||||||
pass
|
|
||||||
elif not event.opponent.benchcoach_object:
|
|
||||||
raise benchcoach.models.Team.DoesNotExist
|
|
||||||
|
|
||||||
if event.location:
|
|
||||||
if event.location.benchcoach_object:
|
|
||||||
if event.location:
|
|
||||||
d['venue'] = event.location.benchcoach_object
|
|
||||||
elif not event.location.benchcoach_object and create_related:
|
|
||||||
# Need to account for if no loacation assigned to teamsnap object.
|
|
||||||
# create team object
|
|
||||||
# update_opponent
|
|
||||||
# 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 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(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(team_id=TEAM_ID):
|
|
||||||
l += update_location(location, create_benchcoach_object=True, create_related=True)
|
|
||||||
|
|
||||||
for member in Member.objects.filter(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(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
|
|
||||||
# l += update_teams(CLIENT, team_id=TEAM_ID)
|
|
||||||
# l += update_members(CLIENT, team_id=TEAM_ID)
|
|
||||||
# l += update_locations(CLIENT, team_id=TEAM_ID)
|
|
||||||
# l += update_events(CLIENT, team_id=TEAM_ID)
|
|
||||||
# l += update_availabilities(CLIENT, team_id=TEAM_ID)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import_teamsnap()
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -23,13 +23,23 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
self.managed_teamsnap_team_id = managed_team_teamsnap_id
|
self.managed_teamsnap_team_id = managed_team_teamsnap_id
|
||||||
self.client = TeamSnap(token=teamsnap_token)
|
self.client = TeamSnap(token=teamsnap_token)
|
||||||
|
|
||||||
def _bulk_sync_from_teamsnap(self, qs:QuerySet):
|
def _bulk_sync_from_teamsnap(self, qs:QuerySet)-> List[BenchcoachModel]:
|
||||||
# -------------------------------------------------------------------------------------------
|
'''
|
||||||
|
Syncs BenchCoach instances (in the form of a QuerySet) from TeamSnap.
|
||||||
|
This function fetches the actual information from teamsnap, then hands it off to the self._update* functions.
|
||||||
|
This funciton cuts down on the number of API calls and should be speedier then doing them one by one.
|
||||||
|
Upating of models from the data is still done one by one.
|
||||||
|
:param benchcoach_instance: instance to be synced
|
||||||
|
:return: List of BenchCoach objects that have been processed (but not necessarily changed) during sync.
|
||||||
|
'''
|
||||||
|
|
||||||
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
# 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
|
# 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
|
# 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.
|
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||||
# -------------------------------------------------------------------------------------------
|
if qs.model not in self.models:
|
||||||
|
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
||||||
|
|
||||||
benchcoachmodel_to_teamsnapfield = {
|
benchcoachmodel_to_teamsnapfield = {
|
||||||
Availability:'teamsnap_availability',
|
Availability:'teamsnap_availability',
|
||||||
Player:'teamsnap_member',
|
Player:'teamsnap_member',
|
||||||
@@ -72,14 +82,18 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
r.append(response)
|
r.append(response)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def _sync_from_teamsnap(self, benchcoach_instance:BenchcoachModel):
|
def _sync_from_teamsnap(self, benchcoach_instance:BenchcoachModel)->BenchcoachModel:
|
||||||
|
'''
|
||||||
|
Syncs BenchCoach instance from TeamSnap. This function fetches the actual information from teamsnap, then
|
||||||
|
hands it off to the self._update* functions.
|
||||||
|
:param benchcoach_instance: instance to be synced
|
||||||
|
:return: BenchCoach object that has been processed (but not necessarily changed) during sync.
|
||||||
|
'''
|
||||||
|
|
||||||
# -------------------------------------------------------------------------------------------
|
|
||||||
# I hate having this translation. What I really want is just a property for "teamsnap_object"
|
# 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
|
# 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
|
# 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.
|
# think I need to be too much smarter to figure this out, but alas I am not smart enough.
|
||||||
# -------------------------------------------------------------------------------------------
|
|
||||||
benchcoachmodel_to_teamsnapfield = {
|
benchcoachmodel_to_teamsnapfield = {
|
||||||
Availability:'teamsnap_availability',
|
Availability:'teamsnap_availability',
|
||||||
Player:'teamsnap_member',
|
Player:'teamsnap_member',
|
||||||
@@ -276,6 +290,12 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
return benchcoach_instance
|
return benchcoach_instance
|
||||||
|
|
||||||
def _find_counterpart(self, instance):
|
def _find_counterpart(self, instance):
|
||||||
|
'''
|
||||||
|
find the counterpart BenchCoach object from the TeamSnap object.
|
||||||
|
NOT CURRENTLY USED.
|
||||||
|
:param instance:
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
instance_type = type(instance)
|
instance_type = type(instance)
|
||||||
counterpart_instance = None
|
counterpart_instance = None
|
||||||
if instance_type == Availability:
|
if instance_type == Availability:
|
||||||
@@ -307,13 +327,17 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
return counterpart_instance
|
return counterpart_instance
|
||||||
|
|
||||||
def _sync_qs (self, qs, direction):
|
def _sync_qs (self, qs, direction):
|
||||||
if qs.model not in self.models:
|
if direction == 'download':
|
||||||
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
if qs.model not in self.models:
|
||||||
|
raise TypeError(f"Sync engine does not sync {qs.model} models")
|
||||||
|
|
||||||
r=[]
|
r=[]
|
||||||
r = self._bulk_sync_from_teamsnap(qs)
|
r = self._bulk_sync_from_teamsnap(qs)
|
||||||
# for instance in qs:
|
|
||||||
# r += self._sync_instance(instance, direction=direction)
|
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
|
return r
|
||||||
|
|
||||||
@@ -329,7 +353,6 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def sync(self, qs: django.db.models.QuerySet = None, instance: benchcoach.models.BenchcoachModel = None,
|
def sync(self, qs: django.db.models.QuerySet = None, instance: benchcoach.models.BenchcoachModel = None,
|
||||||
direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
direction='download') -> List[Tuple[django.db.models.Model, bool]]:
|
||||||
if not isinstance(qs, QuerySet) and not isinstance(instance, benchcoach.models.BenchcoachModel):
|
if not isinstance(qs, QuerySet) and not isinstance(instance, benchcoach.models.BenchcoachModel):
|
||||||
@@ -343,30 +366,57 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def import_items(self, object_name=None, object_names=[]):
|
def import_items(self):
|
||||||
# order is important
|
'''
|
||||||
|
Implementation of import items from the abstract base class AbstractSyncEngine.
|
||||||
|
Imports objects from TeamSnap into BenchCoach, creating BenchCoach objects when necessary.
|
||||||
|
It runs through all supported TeamSnap Objects every execution.
|
||||||
|
NOTE: The number of availability objects causes this function to choke, so consider not updating
|
||||||
|
those on import.
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
|
|
||||||
['team', 'opponent', 'location', 'member', 'event', 'availability']
|
['team', 'opponent', 'location', 'member', 'event', 'availability']
|
||||||
|
|
||||||
|
# the common kwargs for searching the API. Most objects just need the client and currently managed team id.
|
||||||
kwargs = {'client':self.client,'team_id': self.managed_teamsnap_team_id}
|
kwargs = {'client':self.client,'team_id': self.managed_teamsnap_team_id}
|
||||||
|
|
||||||
|
# r is the result dictionary, the key is the name of the benchcoach object, and the value is the list of BenchCoach objects that
|
||||||
|
# have been iterated (but not necessarily changed) during this import.
|
||||||
r = {}
|
r = {}
|
||||||
|
|
||||||
|
# Walking through each TeamSnap object. There is a fair amount of repetition that could use clean-up.
|
||||||
# ---team---
|
# ---team---
|
||||||
r['team'] = []
|
r['team'] = []
|
||||||
|
|
||||||
|
# Search API for objects belonging to currently managed team, and iterate
|
||||||
for teamsnap_data in teamsnap.teamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
for teamsnap_data in teamsnap.teamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
||||||
|
# check if TeamSnap ID already exists in the Teamsnap DB.
|
||||||
if teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']):
|
if teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']):
|
||||||
teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first()
|
teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
|
# If it does, retrieve the BenchCoach instance attached.
|
||||||
|
# It is enforced (by this import function) that every teamsnap instance has a related BenchCoach instance attached.
|
||||||
|
# No other function can create TeamSnap instances (or create related BenchCoach instances from the TeamSnap service)
|
||||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||||
else:
|
else:
|
||||||
|
# Otherwise create TeamSnap instance
|
||||||
teamsnap_instance = teamsnap.models.Team()
|
teamsnap_instance = teamsnap.models.Team()
|
||||||
|
# and create related BenchCoach instance
|
||||||
benchcoach_instance = benchcoach.models.Team()
|
benchcoach_instance = benchcoach.models.Team()
|
||||||
|
# and attach it to the BenchCoach instance
|
||||||
teamsnap_instance.benchcoach_object=benchcoach_instance
|
teamsnap_instance.benchcoach_object=benchcoach_instance
|
||||||
benchcoach_instance.save()
|
benchcoach_instance.save()
|
||||||
|
# Now, update the data from the API to the retrieved/created instances
|
||||||
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
response = self._update_from_teamsnapdata(teamsnap_instance, teamsnap_data)
|
||||||
teamsnap_instance.save()
|
teamsnap_instance.save()
|
||||||
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
response = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||||
r['team'].append(response)
|
r['team'].append(response)
|
||||||
|
|
||||||
# ---opponent---
|
# ---opponent---
|
||||||
|
# See first object for additional comments on the steps followed.
|
||||||
|
# Opponents from teamsnap go to the BenchCoach "Team" database.
|
||||||
|
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||||
|
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||||
for teamsnap_data in teamsnap.teamsnap.api.Opponent.search(**kwargs):
|
for teamsnap_data in teamsnap.teamsnap.api.Opponent.search(**kwargs):
|
||||||
if teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']):
|
if teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']):
|
||||||
teamsnap_instance = teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']).first()
|
teamsnap_instance = teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']).first()
|
||||||
@@ -381,6 +431,9 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
r['team'].append(response)
|
r['team'].append(response)
|
||||||
|
|
||||||
# ---location---
|
# ---location---
|
||||||
|
# See first object for additional comments on the steps followed.
|
||||||
|
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||||
|
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||||
r['location'] = []
|
r['location'] = []
|
||||||
for teamsnap_data in teamsnap.teamsnap.api.Location.search(**kwargs):
|
for teamsnap_data in teamsnap.teamsnap.api.Location.search(**kwargs):
|
||||||
if teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']):
|
if teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']):
|
||||||
@@ -397,8 +450,11 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
r['location'].append(response)
|
r['location'].append(response)
|
||||||
|
|
||||||
# ---member---
|
# ---member---
|
||||||
# Note: Non players not included in sync.
|
# See first object for additional comments on the steps followed.
|
||||||
|
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||||
|
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||||
r['member'] = []
|
r['member'] = []
|
||||||
|
# Search API for members to import. Note: Non players are not included in sync.
|
||||||
for teamsnap_data in teamsnap.teamsnap.api.Member.search(**kwargs,
|
for teamsnap_data in teamsnap.teamsnap.api.Member.search(**kwargs,
|
||||||
is_non_player = False
|
is_non_player = False
|
||||||
):
|
):
|
||||||
@@ -418,6 +474,9 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
r['member'].append(response)
|
r['member'].append(response)
|
||||||
|
|
||||||
# ---event---
|
# ---event---
|
||||||
|
# See first object for additional comments on the steps followed.
|
||||||
|
# Dependent on Team, Opponent, Location. These objects need to be available to attach as related objects or the functions
|
||||||
|
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||||
r['event'] = []
|
r['event'] = []
|
||||||
for teamsnap_data in teamsnap.teamsnap.api.Event.search(**kwargs):
|
for teamsnap_data in teamsnap.teamsnap.api.Event.search(**kwargs):
|
||||||
if teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']):
|
if teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']):
|
||||||
@@ -434,8 +493,16 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
|||||||
r['event'].append(response)
|
r['event'].append(response)
|
||||||
|
|
||||||
# ---availability---
|
# ---availability---
|
||||||
# Note: Non players not included in sync
|
# See first object for additional comments on the steps followed.
|
||||||
|
# Availability was a bit tricky to implement, because there are "not null" contstraints for the Availability object in Bench Coach
|
||||||
|
# Ideally, there probably should be more "not null" constraints on more of the BenchCoach models, so a generalized function should
|
||||||
|
# look more like this than the ones above.
|
||||||
|
# Dependent on Team, Member, Event. These objects need to be available to attach as related objects or the functions
|
||||||
|
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||||
|
#TODO this import is wonky and causes errors on servers. maybe skip this import, or do it in chunks?
|
||||||
r['availability'] = []
|
r['availability'] = []
|
||||||
|
|
||||||
|
# Search API for members to import. Note: Non players are not included in sync.
|
||||||
player_ids = [member.id for member in teamsnap.models.Member.objects.filter(is_non_player=False)]
|
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,
|
for teamsnap_data in teamsnap.teamsnap.api.Availability.search(**kwargs,
|
||||||
member_id=",".join(player_ids)
|
member_id=",".join(player_ids)
|
||||||
|
|||||||
@@ -1,33 +1,19 @@
|
|||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
from .teamsnap.api import Event as TsApiEvent
|
|
||||||
from .teamsnap.api import TeamSnap
|
|
||||||
from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability
|
from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability
|
||||||
from lib.views import BenchcoachListView
|
|
||||||
from .forms import EventForm, MemberForm, OpponentForm, TeamForm, LocationForm
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.db.models import Case, When
|
|
||||||
from django.views import View
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
import benchcoachproject.models
|
|
||||||
import benchcoach.models
|
import benchcoach.models
|
||||||
from django.http import JsonResponse
|
|
||||||
from django.contrib import messages
|
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
|
|
||||||
from django.forms import modelformset_factory
|
|
||||||
import django.db.models
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from .utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||||
|
|
||||||
|
|
||||||
def queryset_from_ids(Model, id_list):
|
|
||||||
#https://stackoverflow.com/questions/4916851/django-get-a-queryset-from-array-of-ids-in-specific-order
|
|
||||||
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(id_list)])
|
|
||||||
queryset = Model.objects.filter(pk__in=id_list).order_by(preserved)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def edit_event(request, id):
|
def edit_event(request, id):
|
||||||
|
'''
|
||||||
|
redirect to teamsnap.com page for editing of event.
|
||||||
|
:param request:
|
||||||
|
:param id:
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
event = Event.objects.get(id = id)
|
event = Event.objects.get(id = id)
|
||||||
return redirect(event.edit_url)
|
return redirect(event.edit_url)
|
||||||
|
|
||||||
@@ -61,121 +47,8 @@ def home(request):
|
|||||||
}
|
}
|
||||||
return render(request, 'teamsnap/home.html', context)
|
return render(request, 'teamsnap/home.html', context)
|
||||||
|
|
||||||
class TeamsnapObjTableView(View):
|
|
||||||
Model = None
|
|
||||||
Form = None
|
|
||||||
template = 'teamsnap/table.html'
|
|
||||||
options = {
|
|
||||||
'event': (Event, EventForm),
|
|
||||||
'location': (Location, LocationForm),
|
|
||||||
'member': (Member, MemberForm),
|
|
||||||
'opponent': (Opponent, OpponentForm),
|
|
||||||
'team': (Team, TeamForm)
|
|
||||||
}
|
|
||||||
|
|
||||||
def post(self, request, object):
|
|
||||||
self.Model, self.Form = self.options[object]
|
|
||||||
self.Formset = modelformset_factory(
|
|
||||||
model=self.Model,
|
|
||||||
form=self.Form,
|
|
||||||
extra=0
|
|
||||||
)
|
|
||||||
formset = self.Formset(request.POST, request.FILES)
|
|
||||||
if formset.is_valid():
|
|
||||||
formset.save()
|
|
||||||
return HttpResponse(200)
|
|
||||||
else:
|
|
||||||
return HttpResponse(422)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get(self, request, object):
|
|
||||||
self.Model, self.Form = self.options[object]
|
|
||||||
self.Formset = modelformset_factory(
|
|
||||||
model=self.Model,
|
|
||||||
form=self.Form,
|
|
||||||
extra=0
|
|
||||||
)
|
|
||||||
qs = self.Model.objects.all()
|
|
||||||
formset = self.Formset(queryset=qs)
|
|
||||||
return render(request, self.template, context={'formset': formset})
|
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def sync(request):
|
def sync_from_teamsnap(request, object_name=None, object_id=None):
|
||||||
object_name = request.POST.get('object_name')
|
|
||||||
object_id = request.POST.get('object_id')
|
|
||||||
if request.POST and request.POST.get('object_name') and request.POST.get('object_id'):
|
|
||||||
TOKEN = request.user.profile.teamsnap_access_token
|
|
||||||
USER_ID = request.user.profile.teamsnap_user.id
|
|
||||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
|
||||||
CLIENT = TeamSnap(token=TOKEN)
|
|
||||||
|
|
||||||
object_name = request.POST.get('object_name')
|
|
||||||
object_id = request.POST.get('object_id')
|
|
||||||
Object = {
|
|
||||||
obj.__name__.lower(): obj
|
|
||||||
for obj in
|
|
||||||
[Availability, Event, LineupEntry, Location, Member, Opponent, Team]
|
|
||||||
}.get(object_name)
|
|
||||||
|
|
||||||
r={}
|
|
||||||
r[Object.__name__.lower()] = []
|
|
||||||
a = Object.ApiObject.search(CLIENT, id=object_id)
|
|
||||||
if a and len(a) == 1:
|
|
||||||
obj, created = Object.update_or_create_from_teamsnap_api(a[0].data)
|
|
||||||
r[Object.__name__.lower()].append((obj, created))
|
|
||||||
|
|
||||||
for object_name, results in r.items():
|
|
||||||
if len(results) == 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.")
|
|
||||||
|
|
||||||
return redirect(request.POST.get('next'))
|
|
||||||
|
|
||||||
return HttpResponse('404')
|
|
||||||
|
|
||||||
|
|
||||||
@login_required()
|
|
||||||
def update_teamsnapdb_from_teamsnapapi(request, object_name, object_id=None):
|
|
||||||
TOKEN = request.user.profile.teamsnap_access_token
|
|
||||||
USER_ID = request.user.profile.teamsnap_user.id
|
|
||||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
|
||||||
CLIENT = TeamSnap(token=TOKEN)
|
|
||||||
|
|
||||||
Object = {
|
|
||||||
obj.__name__.lower():obj
|
|
||||||
for obj in
|
|
||||||
[Availability, Event, LineupEntry, Location, Member, Opponent, Team, User]
|
|
||||||
}.get(object_name)
|
|
||||||
|
|
||||||
r = {}
|
|
||||||
|
|
||||||
for Obj in [Object]:
|
|
||||||
r[Obj.__name__.lower()] = []
|
|
||||||
if not object_id:
|
|
||||||
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__.lower()].append((obj, created))
|
|
||||||
else:
|
|
||||||
a = Obj.ApiObject.search(CLIENT, id=object_id)[0]
|
|
||||||
obj, created = Obj.update_or_create_from_teamsnap_api(a.data)
|
|
||||||
r[Obj.__name__.lower()].append((obj, created))
|
|
||||||
|
|
||||||
for object_name, results in r.items():
|
|
||||||
if len(results) == 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.")
|
|
||||||
|
|
||||||
return redirect('teamsnap home')
|
|
||||||
|
|
||||||
@login_required()
|
|
||||||
def send_to_benchcoach(request, object_name=None, object_id=None): #TODO rename
|
|
||||||
if request.POST:
|
if request.POST:
|
||||||
next = request.POST.get('next')
|
next = request.POST.get('next')
|
||||||
object_name = request.POST.get('object_name')
|
object_name = request.POST.get('object_name')
|
||||||
@@ -238,90 +111,7 @@ def send_to_benchcoach(request, object_name=None, object_id=None): #TODO rename
|
|||||||
else:
|
else:
|
||||||
return HttpResponse(404)
|
return HttpResponse(404)
|
||||||
|
|
||||||
def sync_teamsnapdb_with_teamsnapapi(request):
|
|
||||||
'''
|
|
||||||
This sync the internal TeamSnap Database with the TeamSnap API
|
|
||||||
'''
|
|
||||||
TOKEN = request.user.profile.teamsnap_access_token
|
|
||||||
USER_ID = request.user.profile.teamsnap_user.id
|
|
||||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
|
||||||
CLIENT = TeamSnap(token=TOKEN)
|
|
||||||
|
|
||||||
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]:
|
|
||||||
r[Obj.__name__] = []
|
|
||||||
a = Obj.ApiObject.search(CLIENT, team_id=TEAM_ID)
|
|
||||||
for _a in a:
|
|
||||||
print(f"importing {_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_teamsnapdb_to_benchcoachdb(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['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, member__is_non_player=False):
|
|
||||||
r['availability'] += update_availability(availability, create_benchcoach_object=True, create_related=True)
|
|
||||||
pass
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
from .utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
|
||||||
def import_teamsnap(request):
|
def import_teamsnap(request):
|
||||||
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
|
TOKEN = request.user.profile.teamsnap_access_token
|
||||||
|
|||||||
Reference in New Issue
Block a user