initial commit

This commit is contained in:
2021-11-20 18:53:21 -06:00
parent 5e7b35926d
commit 9445940df2
15 changed files with 338 additions and 1 deletions

View File

@@ -36,6 +36,7 @@ INSTALLED_APPS = [
'venues.apps.VenuesConfig', 'venues.apps.VenuesConfig',
'players.apps.PlayersConfig', 'players.apps.PlayersConfig',
'lineups.apps.LineupsConfig', 'lineups.apps.LineupsConfig',
'teamsnap.apps.TeamsnapConfig',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',

View File

@@ -27,5 +27,6 @@ urlpatterns = [
path('teams/', include('teams.urls')), path('teams/', include('teams.urls')),
path('venues/', include('venues.urls')), path('venues/', include('venues.urls')),
path('players/', include('players.urls')), path('players/', include('players.urls')),
path('lineups/', include('lineups.urls')) path('lineups/', include('lineups.urls')),
path('teamsnap/', include('teamsnap.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

0
teamsnap/__init__.py Normal file
View File

9
teamsnap/admin.py Normal file
View File

@@ -0,0 +1,9 @@
from django.contrib import admin
from .models import User, Team, Location, Event, Member
# Register your models here.
admin.site.register(User)
admin.site.register(Team)
admin.site.register(Event)
admin.site.register(Location)
admin.site.register(Member)

6
teamsnap/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class TeamsnapConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'teamsnap'

View File

@@ -0,0 +1,82 @@
# Generated by Django 3.2.6 on 2021-11-20 23:53
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('teams', '0001_initial'),
('players', '0003_player_team'),
('venues', '0001_initial'),
('events', '0004_delete_availability'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('teamsnap_id', models.CharField(max_length=10)),
('access_token', models.CharField(max_length=50)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Team',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('teamsnap_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=50, null=True)),
('team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teams.team')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Member',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('teamsnap_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=50, null=True)),
('player', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='players.player')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Location',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('teamsnap_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=50, null=True)),
('venue', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='venues.venue')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('teamsnap_id', models.CharField(max_length=10)),
('name', models.CharField(max_length=50, null=True)),
('label', models.CharField(max_length=50, null=True)),
('start_date', models.DateTimeField(null=True)),
('event', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='events.event')),
('location', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.location')),
('opponent', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,43 @@
# Generated by Django 3.2.6 on 2021-11-21 00:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('teamsnap', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='event',
name='formatted_title',
field=models.CharField(max_length=50, null=True),
),
migrations.AlterField(
model_name='event',
name='teamsnap_id',
field=models.CharField(max_length=10, unique=True),
),
migrations.AlterField(
model_name='location',
name='teamsnap_id',
field=models.CharField(max_length=10, unique=True),
),
migrations.AlterField(
model_name='member',
name='teamsnap_id',
field=models.CharField(max_length=10, unique=True),
),
migrations.AlterField(
model_name='team',
name='teamsnap_id',
field=models.CharField(max_length=10, unique=True),
),
migrations.AlterField(
model_name='user',
name='teamsnap_id',
field=models.CharField(max_length=10, unique=True),
),
]

View File

33
teamsnap/models.py Normal file
View File

@@ -0,0 +1,33 @@
from django.db import models
import teams.models
import venues.models
import players.models
import events.models
class TeamsnapBaseModel(models.Model):
teamsnap_id = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=50, null=True)
class Meta:
abstract = True
class User(TeamsnapBaseModel):
access_token = models.CharField(max_length = 50)
name = None
class Team(TeamsnapBaseModel):
team = models.ForeignKey(teams.models.Team, null=True, on_delete=models.CASCADE)
class Location(TeamsnapBaseModel):
venue = models.ForeignKey(venues.models.Venue, null=True, on_delete=models.CASCADE)
class Member(TeamsnapBaseModel):
player = models.ForeignKey(players.models.Player, null=True, on_delete=models.CASCADE)
class Event(TeamsnapBaseModel):
event = models.ForeignKey(events.models.Event, null=True, on_delete=models.CASCADE)
label = models.CharField(max_length = 50, null=True)
start_date = models.DateTimeField(null=True)
opponent = models.ForeignKey(Team, null=True, on_delete=models.CASCADE)
location = models.ForeignKey(Location, null=True, on_delete=models.CASCADE)
formatted_title = models.CharField(max_length = 50, null=True)

View File

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

109
teamsnap/teamsnap/api.py Normal file
View File

@@ -0,0 +1,109 @@
__all__ = ['TeamSnap', 'Team', 'Event', 'Availability', 'Member', 'Location', 'Me']
from apiclient import APIClient, HeaderAuthentication, JsonResponseHandler
class ApiObject():
rel = None
def __init__(self, client, rel=rel, data={}):
self.client = client
self.data = data
self.rel = rel
@classmethod
def search(cls, client, **kwargs):
results = client.query(cls.rel, "search", **kwargs)
return [cls(client,rel=cls.rel, data=r) for r in results]
@classmethod
def get(cls, client, id):
r = client.get(f"{client.link(cls.rel)}/{id}")
return cls(client, cls.rel, client.parse_response(r)[0])
class Me (ApiObject):
rel = "me"
def __init__(self, client):
super().__init__(client=client, rel=self.rel, data=client.get(client.link(self.rel)))
class Event (ApiObject):
rel = "events"
class Team (ApiObject):
rel = "teams"
pass
class Availability (ApiObject):
rel = "availabilities"
pass
class Member (ApiObject):
rel = "members"
class Location (ApiObject):
rel = "locations"
class Opponent (ApiObject):
rel = "opponents"
class TeamSnap(APIClient):
base_url = 'https://api.teamsnap.com/v3'
def __init__(self, token, *args, **kwargs):
super().__init__(*args,
authentication_method=HeaderAuthentication(token=token),
response_handler=JsonResponseHandler,
**kwargs)
self._root_collection = self.get(self.base_url)['collection']
self._links = self._by_rel(self.base_url, 'links')
self._queries = self._by_rel(self.base_url, 'queries')
self._commands = self._by_rel(self.base_url, 'commands')
pass
def link(self, link_name):
d = {l['rel']:l['href'] for l in self._root_collection["links"]}
return d.get(link_name)
def _by_rel (self, url, k):
try:
{l['rel']: l for l in self._root_collection[k]}
except Exception as e:
return {}
self.get(url)['collection'][k]
return {l['rel']:l for l in self.get(url)['collection'][k]}
def query (self, rel, query, **kwargs):
queries = self._by_rel(self._get_href(rel), 'queries')
response = self.get(self._get_href(query, queries), params=kwargs)
return self.parse_response(response)
def command (self, rel, command, **kwargs):
commands = self._by_rel(self._get_href(rel), 'commands')
response = self.get(self._get_href(command, commands), params=kwargs)
return self.parse_response(response)
def _get_href (self, rel: str, links:dict = None, url = base_url) -> str:
"""returns a hyperlink from a the links dictionary. Each item in the links dictionary is a
dictionary with a rel and href key"""
if links is None: links = self._by_rel(url, 'links')
link = links[rel]['href']
return link
def get_item (self, rel, id):
r = self.get(f"{self.link(rel)}/{id}")
return self.parse_response(r)[0]
@classmethod
def parse_response(self, response):
result = []
items = [item['data'] for item in response['collection'].get('items',[])]
for item in response['collection'].get('items',[]):
details = {}
for detail in item['data']:
# TODO type casting and validation based on item['type']
details[detail['name']] = detail['value']
result.append(details)
return result
# return [{detail['name']: detail['value'] for detail in item} for item in items]

View File

@@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block title %} {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<ol class="list-group">
{% for item in object_list %}
<li class="list-group-item">
<span class="fs-5 fw-bold">{{ item.formatted_title }}</span>
<span class="fs-6">{{ item.subtitle }}</span>
{# {% if item.body %}#}
{# <br><span class="fs-6">{{ item.body }}</span>#}
<br>{{ item.start_date|date:"D, M j, g:i A" }},<br>{{item.location.name}}
{# {% endif %}#}
<br>
{% for button in item.buttons %}
<a class="btn btn-primary btn-sm" href="{{ button.href }}" role="button">{{ button.label }}</a>
{% endfor %}
</li>
{% endfor %}
</ol>
{% endblock %}

3
teamsnap/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
teamsnap/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.contrib import admin
from django.urls import path, include
from . import views
urlpatterns = [
path('events', views.EventsListView.as_view(), name="teamsnap events list")
]

15
teamsnap/views.py Normal file
View File

@@ -0,0 +1,15 @@
from django.shortcuts import render
# from .teamsnap.api import TeamSnap, Team, Event, Availability
from .models import User, Member, Team, Event, Location
from django.views.generic.list import ListView
class EventsListView(ListView):
model = Event
class TeamListView(ListView):
model = Team
class LocationListView(ListView):
model = Location