initial commit
This commit is contained in:
@@ -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',
|
||||||
|
|||||||
@@ -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
0
teamsnap/__init__.py
Normal file
9
teamsnap/admin.py
Normal file
9
teamsnap/admin.py
Normal 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
6
teamsnap/apps.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TeamsnapConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'teamsnap'
|
||||||
82
teamsnap/migrations/0001_initial.py
Normal file
82
teamsnap/migrations/0001_initial.py
Normal 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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
43
teamsnap/migrations/0002_auto_20211121_0035.py
Normal file
43
teamsnap/migrations/0002_auto_20211121_0035.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
teamsnap/migrations/__init__.py
Normal file
0
teamsnap/migrations/__init__.py
Normal file
33
teamsnap/models.py
Normal file
33
teamsnap/models.py
Normal 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)
|
||||||
3
teamsnap/teamsnap/__init__.py
Normal file
3
teamsnap/teamsnap/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .api import TeamSnap
|
||||||
|
|
||||||
|
__all__ = ['TeamSnap']
|
||||||
109
teamsnap/teamsnap/api.py
Normal file
109
teamsnap/teamsnap/api.py
Normal 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]
|
||||||
|
|
||||||
23
teamsnap/templates/teamsnap/event_list.html
Normal file
23
teamsnap/templates/teamsnap/event_list.html
Normal 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
3
teamsnap/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
teamsnap/urls.py
Normal file
9
teamsnap/urls.py
Normal 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
15
teamsnap/views.py
Normal 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
|
||||||
Reference in New Issue
Block a user