diff --git a/benchcoach/tests.py b/benchcoach/tests.py index 4ea6dfc..700ec9d 100644 --- a/benchcoach/tests.py +++ b/benchcoach/tests.py @@ -242,7 +242,7 @@ class TestVenueViews(TestCase): fixtures = ["blaseball"] def test_venue_list(self): - response = self.client.get(reverse("venues list")) + response = self.client.get(reverse("locations list")) self.assertEqual(response.status_code, 200) self.assertIn( {"id": 1, "title": "Chesapeake Racetrack and Ballpark"}, diff --git a/benchcoach/urls.py b/benchcoach/urls.py index 77d2b69..574b8a8 100644 --- a/benchcoach/urls.py +++ b/benchcoach/urls.py @@ -8,7 +8,7 @@ urlpatterns = [ path('events//', login_required(views.event), name="event"), 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('venues/list/', login_required(views.VenueListView.as_view()), name="venue list"), + path('locations/list/', login_required(views.VenueListView.as_view()), name="venue list"), path('events//card', login_required(views.lineupcard), name="lineup card"), path('events//csv', views.csv_export, name="lineup csv"), path('events//', login_required(views.event), name="event") diff --git a/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-192x192.png b/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..f427f25 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-192x192.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-512x512.png b/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..196fedf Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/android-chrome-512x512.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/apple-touch-icon.png b/benchcoachproject/static/teamsnap/ig/favicon/apple-touch-icon.png new file mode 100644 index 0000000..2c1e958 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/apple-touch-icon.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/browserconfig.xml b/benchcoachproject/static/teamsnap/ig/favicon/browserconfig.xml new file mode 100644 index 0000000..48dbfc6 --- /dev/null +++ b/benchcoachproject/static/teamsnap/ig/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #323669 + + + diff --git a/benchcoachproject/static/teamsnap/ig/favicon/favicon-16x16.png b/benchcoachproject/static/teamsnap/ig/favicon/favicon-16x16.png new file mode 100644 index 0000000..54a2bc5 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/favicon-16x16.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/favicon-32x32.png b/benchcoachproject/static/teamsnap/ig/favicon/favicon-32x32.png new file mode 100644 index 0000000..4ac4169 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/favicon-32x32.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/favicon.ico b/benchcoachproject/static/teamsnap/ig/favicon/favicon.ico new file mode 100644 index 0000000..6d7bae2 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/favicon.ico differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/mstile-150x150.png b/benchcoachproject/static/teamsnap/ig/favicon/mstile-150x150.png new file mode 100644 index 0000000..651c226 Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/favicon/mstile-150x150.png differ diff --git a/benchcoachproject/static/teamsnap/ig/favicon/safari-pinned-tab.svg b/benchcoachproject/static/teamsnap/ig/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..e834542 --- /dev/null +++ b/benchcoachproject/static/teamsnap/ig/favicon/safari-pinned-tab.svg @@ -0,0 +1,97 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + diff --git a/benchcoachproject/static/teamsnap/ig/favicon/site.webmanifest b/benchcoachproject/static/teamsnap/ig/favicon/site.webmanifest new file mode 100644 index 0000000..71cc43c --- /dev/null +++ b/benchcoachproject/static/teamsnap/ig/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/static/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/static/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/benchcoachproject/static/teamsnap/ig/graphics/hounds-insta.jpg b/benchcoachproject/static/teamsnap/ig/graphics/hounds-insta.jpg new file mode 100644 index 0000000..615ee0e Binary files /dev/null and b/benchcoachproject/static/teamsnap/ig/graphics/hounds-insta.jpg differ diff --git a/benchcoachproject/static/teamsnap/js/lineup-table.js b/benchcoachproject/static/teamsnap/js/lineup-table.js new file mode 100644 index 0000000..0c39e29 --- /dev/null +++ b/benchcoachproject/static/teamsnap/js/lineup-table.js @@ -0,0 +1,137 @@ +function positionSelectChanged(elem) { + let row = elem.parentElement.parentElement + let original_table = elem.parentElement.parentElement.parentElement + let lineup_table = document.getElementById('lineup') + let bench_table = document.getElementById('bench') + let dhd_table = document.getElementById('dhd') + let player_order = row.querySelector('[id^="player-order"]') + let player_available = row.querySelector('[id^="player-availability"]') + + console.dir(original_table == bench_table) + if (elem.value == "P" && original_table == bench_table ) { + dhd_table.appendChild(row) + player_order.classList.remove('d-none') + // player_available.classList.add('d-none') + refresh_lineup_order() + } + else if (elem.value && original_table == dhd_table ) { + dhd_table.appendChild(row) + player_order.classList.remove('d-none') + // player_available.classList.add('d-none') + refresh_lineup_order() + } + else if (elem.value) { + lineup_table.appendChild(row) + player_order.classList.remove('d-none') + // player_available.classList.add('d-none') + refresh_lineup_order() + } + else { + bench_table.prepend(row) + // player_order.classList.add('d-none') + player_available.classList.remove('d-none') + } +} + +function colorPositions(){ + var player_rows = []; + player_rows.push.apply(player_rows, document.getElementById('table-players-lineup').querySelectorAll('tr')); + player_rows.push.apply(player_rows, document.getElementById('table-players-bench').querySelectorAll('tr')); + var label_value_array = [] + player_rows.forEach(function (player_row, index){ + console.dir(player_row) + if (player_row.querySelector('[name$="label"]')){ + console.dir(player_row.querySelector('select[name$="label"]')) + console.dir(player_row.querySelector('select[name$="label"]').value) + label_value_array.push( + player_row.querySelector('select[name$="label"]').value) + } + } + ) + document.querySelectorAll('[id^="position-status"]').forEach(function(position_status,index){ + if (label_value_array.includes(position_status.innerHTML)){ + if (position_status.classList.contains("text-danger")){ + position_status.classList.remove('text-danger') + } + position_status.classList.add('text-success') + } else { + if (position_status.classList.contains("text-success")){ + position_status.classList.remove('text-success') + } + position_status.classList.add('text-danger') + } + }) + console.dir(label_value_array) +} + +function refresh_lineup_order (){ + var player_rows = document.getElementById('table-players-lineup').querySelectorAll('tr') + for (let i = 0; i < player_rows.length; i++) { + var player_order = player_rows[i].querySelector('[id^="sequence"]') + var form_element_order = player_rows[i].querySelector('[id$="sequence"]') + player_order.innerText = parseInt(player_rows[i].dataset.order) + player_rows[i].dataset.order = i + form_element_order.value = i + player_order.innerHTML = i+1 + } + var player_rows = document.getElementById('table-players-bench').querySelectorAll('tr') + for (let i = 0; i < player_rows.length; i++) { + var player_order = player_rows[i].querySelector('[id^="player-order"]') + var form_element_order = player_rows[i].querySelector('[id$="sequence"]') + player_rows[i].dataset.order = null + form_element_order.value = null + player_order.innerHTML = null + } +} + +var lineup = new Sortable.create( + document.getElementById('tbody-players-lineup'), { + animation: 150, + handle: ".drag-handle", + ghostClass:"ghost", + group:{ + put:true, + pull:true + }, + onAdd: function (/**Event*/evt) { + // Add to Lineup + var itemEl = evt.item; // dragged HTMLElement + var player_order = itemEl.querySelector('[id^="sequence-member"]') + var player_available =itemEl.querySelector('[class^="member-availability-status"]') + refresh_lineup_order() + if (player_order.classList.contains('d-none')){ + player_order.classList.remove('d-none') + } + // player_available.classList.add('d-none') + }, + onUpdate: function (/**Event*/evt) { + console.log('update to lineup') + var itemEl = evt.item; // dragged HTMLElement + refresh_lineup_order() + }, + }); + +var bench = new Sortable.create( + document.getElementById('tbody-players-bench'), { + animation: 150, + ghostClass:"ghost", + sort: false, + handle: ".drag-handle", +// handle: ".bars-move", + group:{ + put:true, + pull:true + }, + onAdd: function (/**Event*/evt) { + var itemEl = evt.item; // dragged HTMLElement + var player_order = itemEl.querySelector('[id^="sequence-member"]') + var player_available =itemEl.querySelector('[class^="member-availability-status"]') + refresh_lineup_order() + // player_order.classList.add('d-none') + if (player_order.classList.contains('d-none')){ + player_available.classList.remove('d-none') + } + } + }); +//xxx +colorPositions() \ No newline at end of file diff --git a/teamsnap/forms.py b/teamsnap/forms.py index fefcf35..ce062af 100644 --- a/teamsnap/forms.py +++ b/teamsnap/forms.py @@ -1,6 +1,6 @@ from django import forms from .models import Team, Location, Opponent, Event, Member -from django.forms import modelformset_factory +from django.forms import modelformset_factory, formset_factory, inlineformset_factory select_kwargs = { 'attrs':{'class': 'form-control form-control-sm'} @@ -74,4 +74,45 @@ class LocationForm(forms.ModelForm): "benchcoach_object": forms.Select(**select_kwargs) } +class LineupEntryForm(forms.Form): + member = None + availability = None + lineup_entry = None + + event_lineup_entry_id = forms.Field(required=False) + event_lineup_id = forms.Field(required=False) + event_id = forms.Field() + member_id = forms.Field() + sequence = forms.IntegerField(required=False) + label = forms.ChoiceField(required=False, choices=[ + ("--", "--"), + ("P","P"), + ("C","C"), + ("1B","1B"), + ("2B", "2B"), + ("3B", "3B"), + ("SS", "SS"), + ('LF','LF'), + ('CF','CF'), + ('RF','RF'), + ('DH','DH'), + ('DR','DR'), + ('EH','EH') + ], + widget=forms.Select( + attrs = {'onchange' : "colorPositions();"} + ) + ) + + +class EventChooseForm(forms.Form): + event_id = forms.ChoiceField() + + # checked = forms.BooleanField(required=False) + # def __init__(self, events, *args, **kwargs): + # super(EventChooseForm, self).__init__(*args, **kwargs) + # self.fields['foo'].choices = [e.data['id'] for e in events] + +LineupEntryFormset = formset_factory(LineupEntryForm, can_delete=True, can_order=True, extra=0) + diff --git a/teamsnap/models.py b/teamsnap/models.py index 9cdeed2..1d65e1e 100644 --- a/teamsnap/models.py +++ b/teamsnap/models.py @@ -1,7 +1,7 @@ from django.db import models import benchcoach.models -import teamsnap.teamsnap.api +import pyteamsnap.api from django.utils.timezone import localtime class TeamsnapBaseModel(models.Model): @@ -9,7 +9,7 @@ class TeamsnapBaseModel(models.Model): id = models.CharField(max_length=50, unique=True, primary_key=True) created_at = models.DateTimeField(null=True) updated_at = models.DateTimeField(null=True) - ApiObject = teamsnap.teamsnap.api.ApiObject + ApiObject = pyteamsnap.api.ApiObject class Meta: abstract = True @@ -29,7 +29,7 @@ class Team(TeamsnapBaseModel): on_delete=models.CASCADE, related_name="teamsnap_team" ) - ApiObject = teamsnap.teamsnap.api.Team + ApiObject = pyteamsnap.api.Team class User(TeamsnapBaseModel): type = 'user' @@ -37,7 +37,7 @@ class User(TeamsnapBaseModel): last_name = models.CharField(max_length = 50, null=True) email = models.EmailField(null=True) managed_teams = models.ManyToManyField(Team) - ApiObject = teamsnap.teamsnap.api.User + ApiObject = pyteamsnap.api.User @classmethod def update_or_create_from_teamsnap_api(cls, teamsnap_data): @@ -76,7 +76,7 @@ class Opponent(TeamsnapManagedObjectModel): on_delete=models.CASCADE, related_name="teamsnap_opponent" ) - ApiObject = teamsnap.teamsnap.api.Opponent + ApiObject = pyteamsnap.api.Opponent class Location(TeamsnapManagedObjectModel): type = 'location' @@ -86,7 +86,7 @@ class Location(TeamsnapManagedObjectModel): on_delete=models.CASCADE, related_name="teamsnap_location" ) - ApiObject = teamsnap.teamsnap.api.Location + ApiObject = pyteamsnap.api.Location class Member(TeamsnapManagedObjectModel): # url format is @@ -103,7 +103,7 @@ class Member(TeamsnapManagedObjectModel): last_name = models.CharField(max_length = 50, null=True) jersey_number = models.IntegerField(null=True) is_non_player = models.BooleanField(null=True) - ApiObject = teamsnap.teamsnap.api.Member + ApiObject = pyteamsnap.api.Member def __str__(self): return f"{self.last_name}, {self.first_name} ({self.id})" @@ -131,7 +131,7 @@ class Event(TeamsnapManagedObjectModel): points_for_team = models.PositiveSmallIntegerField(null=True) is_game = models.BooleanField(null=True) game_type = models.CharField(max_length = 50, null=True) - ApiObject = teamsnap.teamsnap.api.Event + ApiObject = pyteamsnap.api.Event @property def csv_event_title(self)->str: @@ -166,7 +166,7 @@ class Availability(TeamsnapManagedObjectModel): related_name="teamsnap_availability" ) status_code = models.SmallIntegerField(choices=status_codes, null=True, blank=True, default=None) - ApiObject = teamsnap.teamsnap.api.Availability + ApiObject = pyteamsnap.api.Availability def __str__(self): return f"{self.member} - {self.event} ({self.id})" @@ -197,7 +197,7 @@ class LineupEntry(TeamsnapManagedObjectModel): ) label = models.PositiveSmallIntegerField(choices=positions, default=None, null=True, blank=True) sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True) - ApiObject = teamsnap.teamsnap.api.EventLineupEntry + ApiObject = pyteamsnap.api.EventLineupEntry @classmethod def update_or_create_from_teamsnap_api(cls, teamsnap_data): diff --git a/teamsnap/teamsnap/__init__.py b/teamsnap/teamsnap/__init__.py deleted file mode 100644 index a0d7ca1..0000000 --- a/teamsnap/teamsnap/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# from .utils import TeamSnap -# -# __all__ = ['TeamSnap'] \ No newline at end of file diff --git a/teamsnap/teamsnap/api.py b/teamsnap/teamsnap/api.py deleted file mode 100644 index bdcb816..0000000 --- a/teamsnap/teamsnap/api.py +++ /dev/null @@ -1,618 +0,0 @@ -__all__ = ['TeamSnap', 'Team', 'Event', 'Availability', 'Member', 'Location', 'Me'] -from apiclient import APIClient, HeaderAuthentication, JsonResponseHandler - -class ApiObject(): - rel = None - version = None - template = 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" - version = "3.866.0" - template = { - "data": [ - { - "name": "first_name" - }, - { - "name": "last_name" - }, - { - "name": "password" - }, - { - "name": "birthday" - }, - { - "name": "email" - }, - { - "name": "facebook_id", - "deprecated": True, - "prompt": "facebook_id is deprecated and has been removed. Continued use of facebook_id is not recommended it will no longer be stored." - }, - { - "name": "facebook_access_token", - "deprecated": True, - "prompt": "facebook_access_token is deprecated and has been removed. Continued use of facebook_access_token is not recommended it will no longer be stored." - }, - { - "name": "type", - "value": "user" - }, - { - "name": "is_lab_rat" - }, - { - "name": "receives_newsletter" - } - ] - } - - def __init__(self, client): - super().__init__(client=client, rel=self.rel, data=client.get(client.link(self.rel))) - -class User (ApiObject): - rel = "users" - version = "3.866.0" - template = { - "data": [ - { - "name": "first_name" - }, - { - "name": "last_name" - }, - { - "name": "password" - }, - { - "name": "birthday" - }, - { - "name": "email" - }, - { - "name": "facebook_id", - "deprecated": True, - "prompt": "facebook_id is deprecated and has been removed. Continued use of facebook_id is not recommended it will no longer be stored." - }, - { - "name": "facebook_access_token", - "deprecated": True, - "prompt": "facebook_access_token is deprecated and has been removed. Continued use of facebook_access_token is not recommended it will no longer be stored." - }, - { - "name": "type", - "value": "user" - }, - { - "name": "is_lab_rat" - }, - { - "name": "receives_newsletter" - } - ] - } - -class Event (ApiObject): - rel = "events" - version = "3.866.0" - template = { - "data": [ - { - "name": "type", - "value": "event" - }, - { - "name": "additional_location_details" - }, - { - "name": "browser_time_zone" - }, - { - "name": "division_location_id" - }, - { - "name": "doesnt_count_towards_record" - }, - { - "name": "duration_in_minutes" - }, - { - "name": "game_type_code" - }, - { - "name": "icon_color" - }, - { - "name": "is_canceled" - }, - { - "name": "is_game" - }, - { - "name": "is_overtime" - }, - { - "name": "is_shootout" - }, - { - "name": "is_tbd" - }, - { - "name": "label" - }, - { - "name": "location_id" - }, - { - "name": "minutes_to_arrive_early" - }, - { - "name": "name" - }, - { - "name": "notes" - }, - { - "name": "notify_opponent" - }, - { - "name": "notify_opponent_contacts_email" - }, - { - "name": "notify_opponent_contacts_name" - }, - { - "name": "notify_opponent_notes" - }, - { - "name": "notify_team" - }, - { - "name": "notify_team_as_member_id" - }, - { - "name": "opponent_id" - }, - { - "name": "points_for_opponent" - }, - { - "name": "points_for_team" - }, - { - "name": "repeating_include", - "prompt": "When updating a repeating event, this is a required field. Values are: \"all\" - updates all events in this series, \"future\" - updates this event and all that occur after, \"none\" - only updates a single event." - }, - { - "name": "repeating_type_code", - "prompt": "A code for the frequency of the repeated event, this is required with the \"repeating_include\" attribute when creating a repeating event. Valid values are: \"1\" - repeat an event daily, \"2\" - repeat an event weekly." - }, - { - "name": "repeating_until", - "prompt": "A date when the repeating event should end, this is inclusive so an event will be created on this day if it falls before the next event specified by \"repeating_type_code\". This attribute is required with \"repeating_type_code\" when creating a repeating event." - }, - { - "name": "results" - }, - { - "name": "results_url" - }, - { - "name": "shootout_points_for_opponent" - }, - { - "name": "shootout_points_for_team" - }, - { - "name": "start_date" - }, - { - "name": "team_id" - }, - { - "name": "time_zone" - }, - { - "name": "tracks_availability" - }, - { - "name": "uniform" - } - ]} - -class Team (ApiObject): - rel = "teams" - version = "3.866.0" - template = { - "data": [ - { - "name": "name" - }, - { - "name": "location_country" - }, - { - "name": "location_postal_code" - }, - { - "name": "time_zone", - "prompt": "The time_zone parameter is required when creating a team, but for changing a team's time_zone, use the update_time_zone command" - }, - { - "name": "sport_id" - }, - { - "name": "division_id" - }, - { - "name": "division_name" - }, - { - "name": "season_name" - }, - { - "name": "league_name" - }, - { - "name": "league_url" - }, - { - "name": "owner_first_name" - }, - { - "name": "owner_last_name" - }, - { - "name": "owner_email" - }, - { - "name": "is_ownership_pending" - }, - { - "name": "ad_unit_hero_id" - }, - { - "name": "ad_unit_hero_template_id" - }, - { - "name": "ad_unit_inline_id" - }, - { - "name": "type", - "value": "team" - } - ] - } - -class Availability (ApiObject): - rel = "availabilities" - version = "3.866.0" - template = { - "data": [ - { - "name": "status_code" - }, - { - "name": "notes" - }, - { - "name": "event_id" - }, - { - "name": "member_id" - }, - { - "name": "notes_author_member_id" - }, - { - "name": "source" - }, - { - "name": "type", - "value": "availability" - } - ] - } - -class Member (ApiObject): - rel = "members" - version = "3.866.0" - template = { - "data": [ - { - "name": "first_name" - }, - { - "name": "last_name" - }, - { - "name": "gender" - }, - { - "name": "position" - }, - { - "name": "is_manager" - }, - { - "name": "birthday" - }, - { - "name": "hide_age", - "deprecated": True, - "prompt": "hide_age is deprecated and will be removed in a future version, use is_age_hidden instead." - }, - { - "name": "is_age_hidden" - }, - { - "name": "hide_address", - "deprecated": True, - "prompt": "hide_address is deprecated and will be removed in a future version, use is_address_hidden instead." - }, - { - "name": "is_address_hidden" - }, - { - "name": "is_non_player" - }, - { - "name": "address_street1" - }, - { - "name": "address_street2" - }, - { - "name": "address_city" - }, - { - "name": "address_state" - }, - { - "name": "address_zip" - }, - { - "name": "jersey_number" - }, - { - "name": "team_id" - }, - { - "name": "is_ownership_pending" - }, - { - "name": "source_action" - }, - { - "name": "type", - "value": "member" - } - ] - }, - -class Location (ApiObject): - rel = "locations" - version = "3.866.0" - template = { - "data": [ - { - "name": "name" - }, - { - "name": "url" - }, - { - "name": "phone" - }, - { - "name": "notes" - }, - { - "name": "address" - }, - { - "name": "latitude" - }, - { - "name": "longitude" - }, - { - "name": "team_id" - }, - { - "name": "is_retired" - }, - { - "name": "type", - "value": "location" - } - ] - } - -class Opponent (ApiObject): - rel = "opponents" - version = "3.866.0" - template = { - "data": [ - { - "name": "name" - }, - { - "name": "contacts_name" - }, - { - "name": "contacts_phone" - }, - { - "name": "contacts_email" - }, - { - "name": "notes" - }, - { - "name": "team_id" - }, - { - "name": "type", - "value": "opponent" - } - ] - } - -class EventLineupEntry (ApiObject): - rel = "event_lineup_entries" - version = "3.866.0" - template = { - "data": [ - { - "name": "event_lineup_id" - }, - { - "name": "member_id" - }, - { - "name": "sequence" - }, - { - "name": "label" - }, - { - "name": "type", - "value": "event_lineup_entry" - } - ] - } - -class Statistics (ApiObject): - rel = "statistics" - version = "3.866.0" - template = { - "data": [ - { - "name": "acronym" - }, - { - "name": "always_display_decimals" - }, - { - "name": "formula" - }, - { - "name": "is_in_descending_order" - }, - { - "name": "display_zero_totals" - }, - { - "name": "is_percentage" - }, - { - "name": "is_private" - }, - { - "name": "is_team_statistic" - }, - { - "name": "is_top_statistic" - }, - { - "name": "name" - }, - { - "name": "precision" - }, - { - "name": "statistic_group_id" - }, - { - "name": "team_id" - }, - { - "name": "type", - "value": "statistic" - } - ] - } - -class MemberStatistics (ApiObject): - rel = "member_statistics" - -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] - diff --git a/teamsnap/templates/teamsnap/availabilities.html b/teamsnap/templates/teamsnap/availabilities.html new file mode 100644 index 0000000..d7ed145 --- /dev/null +++ b/teamsnap/templates/teamsnap/availabilities.html @@ -0,0 +1,27 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ title }}{% endblock %} +{% block page_heading %}Schedule{% endblock %} +{% block content %} +{% load tz %} +
+ +{# #} +{# #} + + {% for event in events %} + + + + + + {% endfor %} + +
+ {{ event.data.formatted_title }} + + {{ event.data.start_date | localtime}} + + {{ event.data.location_name }} +
+
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/edit_lineup.html b/teamsnap/templates/teamsnap/edit_lineup.html deleted file mode 100644 index fdc1141..0000000 --- a/teamsnap/templates/teamsnap/edit_lineup.html +++ /dev/null @@ -1,116 +0,0 @@ -{% extends "base.html" %}{% load static %} -{% block title %} {{ event.data.formatted_title }}{% endblock %} -{% block page_heading %}{{ event.data.formatted_title }}{% endblock %} -{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %} - -{% block content %} - -
Lineup
-
- {{ formset.management_form }} - {% csrf_token %} -
- - - {% for form in formset %} - {% if form.event_lineup_entry_id.value %} - - {{ form.event_lineup_entry_id.as_hidden }} - {{ form.event_lineup_id.as_hidden }} - {{ form.event_id.as_hidden }} - {{ form.member_id.as_hidden }} - {{ form.sequence.as_hidden }} - {{ form.label.as_hidden }} - {{ form.member_name.as_hidden }} - - - - - - - - {% endif %} - {% endfor %} - -
- {{ forloop.counter }} - - {{ form.member_name.value }} - -
{{ form.label }}
-
-
-
- - {{ form.DELETE.as_hidden }} -
-
-
- -
-
- -
-
Bench
-
- - {# #} - {# #} - {# #} - {# #} - {# #} - {# #} - - {% for availability in availablities %} - {% if availability.data.member_is_non_player is False %} - - - - - - - - {% endif %} - {% endfor %} - - -
#} - {# StatusLastFirst
- {% if availability.data.status_code == 1 %} - - {% endif %} - {% if availability.data.status_code == 0 %} - - {% endif %} - {% if availability.data.status_code == None %} - - {% endif %} - {% if availability.data.status_code == 2 %} - - {% endif %} - - {{ availability.data.member_first_name }} {{ availability.data.member_last_name }} - - -
-
- -
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/event/instagen.html b/teamsnap/templates/teamsnap/event/instagen.html new file mode 100644 index 0000000..b53ec3b --- /dev/null +++ b/teamsnap/templates/teamsnap/event/instagen.html @@ -0,0 +1,76 @@ + +{% extends "base.html" %}{% load static %} +{% block title %} {{ event.data.formatted_title }} - Instagenerator{% endblock %} +{% block page_heading %} +
+
+ +
+
Hounds Instagenerator
+
+{% endblock %} +{% block page_subheading %}{{ event.data.formatted_title }}, {{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %} +{% block content %} + +
+ +
+ +
+ +
+ Background +
+ + +
+ +
+ + +
+
+ + +
+
+ +
+ Layout +
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/lineup/edit.html b/teamsnap/templates/teamsnap/lineup/edit.html new file mode 100644 index 0000000..33c12b8 --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/edit.html @@ -0,0 +1,12 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ event.data.formatted_title }}{% endblock %} +{% block page_heading %}{{ event.data.formatted_title }}{% endblock %} +{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %} + +{% block content %} + {% include 'teamsnap/lineup/widgets/lineup.html' with formset_lineup=formset_lineup formset_bench=formset_bench%} + + + + +{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/lineup/multiple_choose.html b/teamsnap/templates/teamsnap/lineup/multiple_choose.html new file mode 100644 index 0000000..f3e0c62 --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/multiple_choose.html @@ -0,0 +1,95 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ title }}{% endblock %} + +{% csrf_token %} + +{% block page_heading %} +
+
+ Schedule +
+
+ +{% endblock %} +{% block content %} +
+ {{ formset.management_form }} + + {% load tz %} +
+ + {# #} + {# #} + + {% for form in formset %} + + {{ form.event_id }} + + {% endfor %} + +
+
+
+ + + + + +{% endblock %} diff --git a/teamsnap/templates/teamsnap/lineup/multiple_choose_2.html b/teamsnap/templates/teamsnap/lineup/multiple_choose_2.html new file mode 100644 index 0000000..5a89acd --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/multiple_choose_2.html @@ -0,0 +1,45 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ title }}{% endblock %} + +{% csrf_token %} + +{% block page_heading %} +
+
+ Schedule +
+
+ +{% endblock %} +{% block content %} +
+ {{ formset.management_form }} + + {% load tz %} +
+ + {# #} + {# #} + + {% for form in formset %} + {{ form.event_id.as_hidden }} + + + + + + + {% endfor %} + +
+ {{ form.checked }} + + {{ form.event.data.formatted_title }} + + {{ form.event.data.start_date | localtime}} + + {{ form.event.data.location_name }} +
+
+
+{% endblock %} diff --git a/teamsnap/templates/teamsnap/lineup/multiple_edit.html b/teamsnap/templates/teamsnap/lineup/multiple_edit.html new file mode 100644 index 0000000..d69a505 --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/multiple_edit.html @@ -0,0 +1,100 @@ +{% extends "base.html" %}{% load static %} +{% block title %} Edit Lineups {% endblock %} +{% block page_heading %}Edit Lineups{% endblock %} +{% block page_subheading %}{% endblock %} + +{% block content %} + {#
#} + {#
#} + {# {% for event_data in contexts %}#} + {#
#} + {#
#} + {#

{{ event_data.event.data.formatted_title }}

#} + {#
{{ event_data.data.start_date }}
#} + {#
#} + {# {% include 'teamsnap/lineup/widgets/lineup.html' with formset_lineup=event_data.formset_lineup formset_bench=event_data.formset_bench event_id=event_data.event.data.id %}#} + {#
#} + {# {% endfor %}#} + {#
#} + {#
#} +
+
+ + + + {% for event_data in contexts %} + + {% endfor %} + + + + + + {% for event_data in contexts %} + + {% endfor %} + + + + + + {% for event_data in contexts %} + + {% endfor %} + + + + + + {% for event_data in contexts %} + + {% endfor %} + + + +
+

{{ event_data.event.data.formatted_title }}

+
{{ event_data.data.start_date }}
+ {% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %} + {% for position in position_list.split %} + {{ position }} + {% endfor %} + {% endwith %} +

Lineup

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_lineup table_id="players-lineup" %} +

Bench

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_bench table_id="players-bench" %} +

Out

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_out table_id="players-bench" %} +
+ {#
#} + {#
#} + {#
#} + {# {% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %}#} + {# {% for position in position_list.split %}#} + {# {{ position }}#} + {# {% endfor %}#} + {# {% endwith %}#} + {#
#} + {#
#} + {#
#} + {#
#} + {# {{ formset.management_form }}#} + {# {% csrf_token %}#} + + {#

Lineup

#} + {# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_lineup table_id="players-lineup" %}#} + {# #} + {##} + {#

Bench

#} + {# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_bench table_id="players-bench" %}#} + {##} + {#

Out

#} + {# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_out table_id="players-out" %}#} + + {#
#} + + +
+
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/lineup/widgets/lineup.html b/teamsnap/templates/teamsnap/lineup/widgets/lineup.html new file mode 100644 index 0000000..a9e5cc5 --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/widgets/lineup.html @@ -0,0 +1,28 @@ +
+
+
+{#
#} + {% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %} + {% for position in position_list.split %} + {{ position }} + {% endfor %} + {% endwith %} +{#
#} +
+
+
+ {{ formset.management_form }} + {% csrf_token %} + +

Lineup

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_lineup table_id="players-lineup" %} + + +

Bench

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_bench table_id="players-bench" %} + +

Out

+ {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_out table_id="players-out" %} + +
+
\ No newline at end of file diff --git a/teamsnap/templates/teamsnap/lineup/widgets/lineup_table.html b/teamsnap/templates/teamsnap/lineup/widgets/lineup_table.html new file mode 100644 index 0000000..771e3f1 --- /dev/null +++ b/teamsnap/templates/teamsnap/lineup/widgets/lineup_table.html @@ -0,0 +1,59 @@ + + + {% for form in formset %} + + {{ form.event_lineup_entry_id.as_hidden }} + {{ form.event_lineup_id.as_hidden }} + {{ form.event_id.as_hidden }} + {{ form.member_id.as_hidden }} + {{ form.sequence.as_hidden }} + {{ form.label.as_hidden }} + {{ form.member_name.as_hidden }} + + + + + + + {# #} + + {% endfor %} + +
+ {{ form.sequence.value | add:"1" }} + +
+ + {% if form.order.value > 0 %} {{ form.order.value | add:"1" }} {% endif %} + + + + {% if form.availability.data.status_code == 2 %} + + {% elif form.availability.data.status_code == 1 %} + + {% elif form.availability.data.status_code == 0 %} + + {% else %} + + {% endif %} + +
+
+
+ + {{ form.member.data.first_name }} + + {{ form.member.data.last_name }} + + #{{ form.member.data.jersey_number }} + +
+ {#
{{ form.statline }}#} +
+ {{ form.label }} + + + {{ form.instance.position }}
\ No newline at end of file diff --git a/teamsnap/templates/teamsnap/location/view.html b/teamsnap/templates/teamsnap/location/view.html new file mode 100644 index 0000000..a6427b9 --- /dev/null +++ b/teamsnap/templates/teamsnap/location/view.html @@ -0,0 +1,22 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ location.data.name }}{% endblock %} +{% block page_heading %}{{ location.data.name }}{% endblock %} +{% block content %} +
+ +{# #} +{# #} + + {% for key, value in location.data.items %} + + + + + {% endfor %} + +
+ {{ key }} + + {{ value }} +
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/opponent.html b/teamsnap/templates/teamsnap/opponent.html new file mode 100644 index 0000000..e4626b1 --- /dev/null +++ b/teamsnap/templates/teamsnap/opponent.html @@ -0,0 +1,22 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ opponent.data.name }}{% endblock %} +{% block page_heading %}{{ opponent.data.name }}{% endblock %} +{% block content %} +
+ +{# #} +{# #} + + {% for key, value in opponent.data.items %} + + + + + {% endfor %} + +
+ {{ key }} + + {{ value }} +
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/schedule.html b/teamsnap/templates/teamsnap/schedule.html new file mode 100644 index 0000000..112708a --- /dev/null +++ b/teamsnap/templates/teamsnap/schedule.html @@ -0,0 +1,47 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ title }}{% endblock %} +{% block page_heading %} +
+
+ Schedule +
+ + +
+ +{% endblock %} +{% block content %} +{% load tz %} +
+ + {# #} + {# #} + + {% for event in events %} + + + + + + {% endfor %} + +
+ {{ event.data.formatted_title }} + + {{ event.data.start_date | localtime}} + + {{ event.data.location_name }} +
+
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/templates/teamsnap/view_event.html b/teamsnap/templates/teamsnap/view_event.html new file mode 100644 index 0000000..4e534bd --- /dev/null +++ b/teamsnap/templates/teamsnap/view_event.html @@ -0,0 +1,137 @@ +{% extends "base.html" %}{% load static %} +{% block title %} {{ event.data.formatted_title }}{% endblock %} +{% block page_heading %}{{ event.data.formatted_title }}{% endblock %} +{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %} +{% block content %} +
+ + + {# #} + {# #} + + + + + + + + + + + + + + +
+ Date + + {{ event.data.start_date }} +
+ Opponent + + {{ event.data.opponent_name }} +
+ Location + + {{ event.data.location_name }} +
+
+

Availabilities

+
+ + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + + + + + + + + + + + + + + + + + + + + + + +
#} + {# StatusLastFirst
+ + + {{ availability_summary.data.player_going_count }} +
+ + + {{ availability_summary.data.player_maybe_count }} +
+ + + {{ availability_summary.data.player_not_going_count }} +
+ + + {{ availability_summary.data.player_unknown_count }} +
+
+
+

Lineup Entries

+ Edit +
+
+ + {# #} + {# #} + {# #} + {# #} + {# #} + {# #} + + {% for lineup_entry in lineup_entries %} + + + + + + + {% endfor %} + +
#} + {# StatusLastFirst
+ {{ lineup_entry.data.sequence }} + + {{ lineup_entry.data.member_name }} + + {{ lineup_entry.data.label }} +
+
+

Functions

+
+
+ + + + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/teamsnap/urls.py b/teamsnap/urls.py index 143615b..b73a56b 100644 --- a/teamsnap/urls.py +++ b/teamsnap/urls.py @@ -6,8 +6,19 @@ from functools import partial from . import views urlpatterns = [ - path('', views.home, name='teamsnap home'), + path('', views.home, name='teamsnap_home'), path('edit/event/', views.edit_event, name='teamsnap edit event'), path('sync/download', views.sync_from_teamsnap, name="sync from teamsnap"), - path('import/', views.import_teamsnap, name="import") + path('import/', views.import_teamsnap, name="import"), + + path('/schedule/', views.schedule, name='teamsnap_schedule'), + path('/schedule/view_event/', views.event, name='teamsnap_view_event'), + path('/opponent/view/', views.opponent, name='teamsnap_opponent'), + path('/location/view/', views.location, name='teamsnap_location'), + path('/event//edit_lineup/', views.edit_lineup, name='teamsnap_edit_lineup'), + path('/event//submit_lineup/', views.submit_lineup, name='teamsnap_submit_lineup'), + path('/event//image_generator/', views.image_generator, name='teamsnap_image_generator'), + path('/event//image_generator/generate', views.get_matchup_image, name='teamsnap_image_generator_generate'), + path('/multievent/edit_lineups', views.edit_multiple_lineups, name='teamsnap_edit_multiple_lineups'), + path('/multievent/choose', views.multi_lineup_choose, name='teamsnap_choose_multiple_lineups') ] \ No newline at end of file diff --git a/teamsnap/utils/gen_image.py b/teamsnap/utils/gen_image.py new file mode 100644 index 0000000..5b41524 --- /dev/null +++ b/teamsnap/utils/gen_image.py @@ -0,0 +1,331 @@ +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFilter, ImageFont +from pathlib import Path +import os +from datetime import datetime +from zoneinfo import ZoneInfo +from typing import List +from dataclasses import dataclass + + +# image_directory = 'input/images/logos-bw/{filename}.{ext}' + +# font_regular_path = "input/fonts/DINAlternate-Bold.ttf" +# font_condensed_path = "input/fonts/DINCondensed-Bold.ttf" +font_regular_path = "benchcoachproject/static/teamsnap/ig/fonts/ScalaSans-BoldLF.otf" +font_condensed_path = "benchcoachproject/static/teamsnap/ig/fonts/ScalaSans-BoldLF.otf" + +@dataclass +class Team: + name: str + winlosstie: List[int] = None + image_directory: str = '../input/images/logos-bw/{filename}.{ext}' + + @property + def id(self): + return self.name.lower().replace(' ', '-') + + @property + def image(self): + path = self.image_directory.format(filename=self.id, ext="png") + if os.path.isfile(path): + return path + else: + return None + +@dataclass +class Location: + name: str + address1: str = "" + address2: str = "" + image_directory: str = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}' + + @property + def id(self): + return self.name.lower().replace(' ', '-') + + @property + def image(self): + path = self.image_directory.format(filename=self.id, ext="png") + if os.path.isfile(path): + return path + else: + return None + + @property + def address(self): + return ",".join([self.address1,self.address2]) + +args = { + "team_fave" : Team("Hounds"), + "team_opponent" : Team("Trojans"), + "home": False, + "date" : "2021-05-08 12:30 pm", + "location" : Location("Maywood", image_directory="benchcoachproject/static/teamsnap/ig/locations/maywood.{ext}"), + "runs_for": 8, + "runs_against": 9 +} + +def gen_image (team_fave, team_opponent, date, location=None, + location_name = None, + home=False, + background='location', + address = None, + width = 1080, + height = 1080, + *kwargs, + **args + ): + if not isinstance(date, datetime): + # date = parser.parse(date) + # date = date.astimezone(ZoneInfo("America/Chicago")) + pass + + if location.image and background == 'location': + background_image = Image.open(location.image).copy() + background_image = background_image.resize((width, height)) + # background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5)) + background_image = background_image.convert("RGBA") + elif background == 'transparent': + background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0)) + else: + background_image = Image.new('RGBA', (width, height), (50, 55, 102)) + + title_images = [] + for team in [team_fave, team_opponent]: + if team.image: + title_images.append(Image.open(team.image).copy()) + else: + title_images.append(Image.new('RGBA', (1080, 1080))) + + title_image_left = title_images[0] + title_image_right = title_images[1] + + # Make a blank image for the rectangle, initialized to a completely + # transparent color. + tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0)) + + # Create a drawing context for it. + draw = ImageDraw.Draw(tmp) + + # section margin describes the margin of the section rectangles from the sides of the image + section_margin_pct = .05 + llx = int(section_margin_pct * background_image.size[0]) + urx = int((1 - section_margin_pct) * background_image.size[0]) + lly = int((1 - section_margin_pct) * background_image.size[1]) + ury = int(.50 * background_image.size[1]) + + lly2 = int(.49 * background_image.size[1]) + ury2 = int(.05 * background_image.size[1]) + + section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png"))) + section_info_draw = ImageDraw.Draw(section_info) + + section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png"))) + section_title_draw = ImageDraw.Draw(section_title) + + # First line: Date + font = ImageFont.truetype(font_regular_path, 62) + text = "{:%a, %B %-d %-I:%M %p}".format(date).upper() + # text = date + text_size = draw.textsize(text, font) + loc = ( + 1050, + 280 + ) + section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra") + + # Second line: Venue + font = ImageFont.truetype(font_condensed_path, 34) + if not location_name: + text = location.name.upper() + else: + text = location_name.upper() + text_size = section_info_draw.textsize(text, font) + loc = ( + 1050, + 355 + ) + section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra") + + font = ImageFont.truetype(font_regular_path, 80) + if home: + text = "VS" + else: + text = "AT" + text_size = section_title_draw.textsize(text, font) + loc = ( + 540, + 120 + ) + color = (255, 255, 255) + section_title_draw.text(loc, text, color, font=font, anchor="mm") + + # Alpha composite the two images together. + background_image = Image.alpha_composite(background_image, tmp) + + # Title Image Left + title_image_left.thumbnail([350, 350]) + loc = ( + 50, -50 + ) + section_title.paste(title_image_left, loc, title_image_left) + + # Title Image Right + title_image_right.thumbnail([350, 350]) + loc = ( + 650, -50 + ) + section_title.paste(title_image_right, loc, title_image_right) + + # background_image.paste(section_info, (llx, ury), section_info) + # background_image.paste(section_title, (llx, ury2), section_title) + section_title.paste(section_info,(0,0),section_info) + section_title.thumbnail([800, 800]) + + if background=="badge": + return section_title + + background_image.paste(section_title,( + int((background_image.size[0]-section_title.size[0])/2), + height - 360 + ),section_title) + + return background_image + +def gen_results_image (team_fave, team_opponent, date, + location=None, + location_name = None, + home=False, + background='location', + address = None, + width = 1080, + height = 1080, + runs_for=0, + runs_against=0, + *kwargs, + **args + ): + if not isinstance(date, datetime): + # date = parser.parse(date) + # date = date.astimezone(ZoneInfo("America/Chicago")) + pass + + if location.image and background == 'location': + background_image = Image.open(location.image).copy() + background_image = background_image.resize((width, height)) + # background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5)) + background_image = background_image.convert("RGBA") + elif background == 'transparent': + background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0)) + else: + background_image = Image.new('RGBA', (width, height), (50, 55, 102)) + + title_images = [] + for team in [team_fave, team_opponent]: + if team.image: + title_images.append(Image.open(team.image).copy()) + else: + title_images.append(Image.new('RGBA', (1080, 1080))) + + title_image_left = title_images[0] + title_image_right = title_images[1] + + # Make a blank image for the rectangle, initialized to a completely + # transparent color. + tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0)) + + # Create a drawing context for it. + draw = ImageDraw.Draw(tmp) + + # section margin describes the margin of the section rectangles from the sides of the image + section_margin_pct = .05 + llx = int(section_margin_pct * background_image.size[0]) + urx = int((1 - section_margin_pct) * background_image.size[0]) + lly = int((1 - section_margin_pct) * background_image.size[1]) + ury = int(.50 * background_image.size[1]) + + lly2 = int(.49 * background_image.size[1]) + ury2 = int(.05 * background_image.size[1]) + + #todo fix path + section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png"))) + section_info_draw = ImageDraw.Draw(section_info) + + section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png"))) + section_title_draw = ImageDraw.Draw(section_title) + + # First line: Results + loc = ( + 1050, + 265 + ) + if runs_for > runs_against: + result_letter = "W" + elif runs_for < runs_against: + result_letter = "L" + elif runs_for == runs_against: + result_letter = "T" + font = ImageFont.truetype(font_regular_path, 100) + section_info_draw.text(loc, f"FINAL: {result_letter} {runs_for}-{runs_against}", (14,42,28), font=font, anchor="ra") + + # Second line: Date + text = "{:%a, %B %-d %-I:%M %p}".format(date).upper() + # text = date + font = ImageFont.truetype(font_condensed_path, 34) + text_size = section_info_draw.textsize(text, font) + loc = ( + 1050, + 355 + ) + section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra") + + font = ImageFont.truetype(font_regular_path, 80) + if home: + text = "VS" + else: + text = "AT" + text_size = section_title_draw.textsize(text, font) + loc = ( + 540, + 120 + ) + color = (255, 255, 255) + section_title_draw.text(loc, text, color, font=font, anchor="mm") + + # Alpha composite the two images together. + background_image = Image.alpha_composite(background_image, tmp) + + # Title Image Left + title_image_left.thumbnail([350, 350]) + loc = ( + 50, -50 + ) + section_title.paste(title_image_left, loc, title_image_left) + + # Title Image Right + title_image_right.thumbnail([350, 350]) + loc = ( + 650, -50 + ) + section_title.paste(title_image_right, loc, title_image_right) + + # background_image.paste(section_info, (llx, ury), section_info) + # background_image.paste(section_title, (llx, ury2), section_title) + section_title.paste(section_info,(0,0),section_info) + section_title.thumbnail([800, 800]) + + if background=="badge": + return section_title + + background_image.paste(section_title,( + int((background_image.size[0]-section_title.size[0])/2), + height - 360 + ),section_title) + + # background_image.show() + + return background_image + +# gen_results_image(**args) \ No newline at end of file diff --git a/teamsnap/utils/teamsnap_sync_engine.py b/teamsnap/utils/teamsnap_sync_engine.py index 449ed07..02f6c35 100644 --- a/teamsnap/utils/teamsnap_sync_engine.py +++ b/teamsnap/utils/teamsnap_sync_engine.py @@ -3,7 +3,8 @@ 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 +from pyteamsnap.api import TeamSnap +import pyteamsnap import teamsnap.models from django.db.models import QuerySet @@ -50,14 +51,14 @@ class TeamsnapSyncEngine(AbstractSyncEngine): } 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 + teamsnap.models.Availability: pyteamsnap.api.Availability, + teamsnap.models.Event: pyteamsnap.api.Event, + # teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet + teamsnap.models.Location: pyteamsnap.api.Location, + teamsnap.models.Member: pyteamsnap.api.Member, + teamsnap.models.Opponent: pyteamsnap.api.Opponent, + teamsnap.models.Team: pyteamsnap.api.Team, + # teamsnap.models.User:pyteamsnap.api.User # Not implemented yet } apiobject_to_teamsnapmodel = {v:k for k,v in teamsnapmodel_to_apiobject.items()} @@ -104,14 +105,14 @@ class TeamsnapSyncEngine(AbstractSyncEngine): } 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 + teamsnap.models.Availability: pyteamsnap.api.Availability, + teamsnap.models.Event: pyteamsnap.api.Event, + # teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet + teamsnap.models.Location: pyteamsnap.api.Location, + teamsnap.models.Member: pyteamsnap.api.Member, + teamsnap.models.Opponent: pyteamsnap.api.Opponent, + teamsnap.models.Team: pyteamsnap.api.Team, + # teamsnap.models.User:pyteamsnap.api.User # Not implemented yet } if isinstance(benchcoach_instance, benchcoach.models.Team): @@ -128,9 +129,9 @@ class TeamsnapSyncEngine(AbstractSyncEngine): 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: + def _update_from_teamsnapdata(self, teamsnap_instance:teamsnap.models.TeamsnapBaseModel, teamsnap_data: pyteamsnap.api.ApiObject) -> teamsnap.models.TeamsnapBaseModel: '''''' - if isinstance(teamsnap_data, teamsnap.teamsnap.api.ApiObject): + if isinstance(teamsnap_data, pyteamsnap.api.ApiObject): teamsnap_data = teamsnap_data.data else: raise TypeError @@ -390,7 +391,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): 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 pyteamsnap.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']): teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first() @@ -417,7 +418,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): # 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 pyteamsnap.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 @@ -435,7 +436,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): # 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'] = [] - for teamsnap_data in teamsnap.teamsnap.api.Location.search(**kwargs): + for teamsnap_data in pyteamsnap.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 @@ -455,7 +456,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): # self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail. 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 pyteamsnap.api.Member.search(**kwargs, is_non_player = False ): if teamsnap_data.data['is_non_player'] == True: @@ -478,7 +479,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): # 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'] = [] - for teamsnap_data in teamsnap.teamsnap.api.Event.search(**kwargs): + for teamsnap_data in pyteamsnap.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 @@ -504,7 +505,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine): # 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)] - for teamsnap_data in teamsnap.teamsnap.api.Availability.search(**kwargs, + for teamsnap_data in pyteamsnap.api.Availability.search(**kwargs, member_id=",".join(player_ids) ): if teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']): diff --git a/teamsnap/views.py b/teamsnap/views.py index 94ad9d0..63e2057 100644 --- a/teamsnap/views.py +++ b/teamsnap/views.py @@ -1,11 +1,16 @@ +import operator +import time + from django.shortcuts import render, redirect from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseNotAllowed import benchcoach.models from django.contrib import messages from django.contrib.auth.decorators import login_required from .utils.teamsnap_sync_engine import TeamsnapSyncEngine +from django.templatetags.static import static +import datetime def edit_event(request, id): ''' @@ -112,6 +117,7 @@ def sync_from_teamsnap(request, object_name=None, object_id=None): return HttpResponse(404) + def import_teamsnap(request): TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id TOKEN = request.user.profile.teamsnap_access_token @@ -127,5 +133,437 @@ def import_teamsnap(request): return redirect('teamsnap home') +def schedule(request, team_id): + TEAM_ID = team_id + TOKEN = request.user.profile.teamsnap_access_token + no_past = bool(request.GET.get('no_past', 0)) + games_only = bool(request.GET.get('games_only',0)) + from pyteamsnap.api import TeamSnap, Event, Location, Opponent + client = TeamSnap(token=TOKEN) + time.sleep(.5) + ts_events = Event.search(client, team_id=TEAM_ID) + if no_past: + ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)] + if games_only: + ts_events = [e for e in ts_events if e.data['is_game']] + ts_events = {e.data['id']:e for e in ts_events} + # ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)} + # ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)} + # for event in ts_events: + pass + return render(request, "teamsnap/schedule.html", context={"events":ts_events.values(), "team_id":team_id}) + +def event(request, event_id, team_id): + TOKEN = request.user.profile.teamsnap_access_token + + from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary + client = TeamSnap(token=TOKEN) + time.sleep(0.5) + ts_bulkload = client.bulk_load(team_id=team_id, + types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member], + event__id=event_id) + ts_event = [i for i in ts_bulkload if isinstance(i, Event)][0] + # ts_availabilities = Availability.search(client, event_id=ts_event.data['id']) + ts_availability_summary = \ + [i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0] + ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id] + + ts_members = [i for i in ts_bulkload if isinstance(i, Member)] + ts_member_lookup = {m.data['id']: m for m in ts_members} + # ts_availability_lookup = {m.data['member_id']: m for m in ts_availabilities} + ts_lineup_entries_lookup = {m.data['member_id']: m for m in ts_lineup_entries} + + members = [] + + return render(request, "teamsnap/view_event.html", context={ + "availability_summary":ts_availability_summary, + "event":ts_event, + "availablities":[], + "lineup_entries": ts_lineup_entries, + }) + +def location(request, id, team_id): + TOKEN = request.user.profile.teamsnap_access_token + + from pyteamsnap.api import TeamSnap, Location + client = TeamSnap(token=TOKEN) + return render(request, "teamsnap/location/view.html", context={"location": Location.get(client, id=id)}) + pass + +def opponent(request, id): + TOKEN = request.user.profile.teamsnap_access_token + + from pyteamsnap.api import TeamSnap, Opponent + time.sleep(0.5) + client = TeamSnap(token=TOKEN) + return render(request, "teamsnap/opponent.html", context={"opponent": Opponent.get(client, id=id)}) + pass + +def edit_lineup(request, event_id, team_id): + TOKEN = request.user.profile.teamsnap_access_token + + from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary, Opponent + client = TeamSnap(token=TOKEN) + time.sleep(0.5) + + ts_bulkload = client.bulk_load(team_id=team_id, + types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member], + event__id=event_id) + ts_event = [i for i in ts_bulkload if isinstance(i, Event)][0] + ts_availabilities = Availability.search(client, event_id=ts_event.data['id']) + ts_availability_summary = \ + [i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0] + ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id] + + if ts_lineup_entries: + ts_lineup = EventLineup.get(client, id=ts_lineup_entries[0].data['event_lineup_id']) + else: + ts_lineup = EventLineup.search(client, event_id=event_id) + + ts_members = [i for i in ts_bulkload if isinstance(i, Member)] + ts_member_lookup = {m.data['id']: m for m in ts_members} + ts_availability_lookup = {m.data['member_id']: m for m in ts_availabilities} + ts_lineup_entries_lookup = {m.data['member_id']: m for m in ts_lineup_entries} + + members=[] + + for member in ts_members: + members.append ({ + "member":getattr(member, 'data'), + "availability": getattr(ts_availability_lookup.get(member.data['id'], {}), 'data', {}), + "lineup_entry": getattr(ts_lineup_entries_lookup.get(member.data['id'], {}), 'data', {}) + } + ) + + members = sorted(members, key=lambda d: ( + { + None:3, # No Response + 0:2, # No + 2:1, # Maybe + 1:0 # Yes + + }.get(d['availability'].get('status_code')), + d['member'].get('last_name')) + ) + + + from teamsnap.forms import LineupEntryFormset, LineupEntryForm + + formset = LineupEntryFormset( + initial=[ + { + "event_lineup_entry_id" : member['lineup_entry'].get('id'), + "event_lineup_id" : member['lineup_entry'].get('event_lineup_id'), + "event_id": event_id, + "member_id" : member['member']['id'], + "sequence" : member['lineup_entry'].get('sequence'), + "label" : member['lineup_entry'].get('label'), + } + for member in members + ] + ) + + for form in formset: + form.member = ts_member_lookup.get(form['member_id'].initial) + form.availability = ts_availability_lookup.get(form['member_id'].initial) + + formset_lineup = [form for form in formset if form.initial.get('event_lineup_entry_id')] + formset_lineup = sorted( + formset_lineup, + key=lambda d: d.initial.get('sequence',100) + ) + formset_bench = [form for form in formset if + form not in formset_lineup and + form.availability.data['status_code'] in [2, 1] + ] + formset_out = [form for form in formset if + form not in formset_lineup and + form not in formset_bench and + not form.member.data['is_non_player'] + ] + + return render(request, "teamsnap/lineup/edit.html", context={ + "team_id": team_id, + "event_id": event_id, + "event": ts_event, + "formset": formset, + "formset_lineup":formset_lineup, + "formset_bench": formset_bench, + "formset_out": formset_out, + "lineup": ts_lineup + }) + +def edit_multiple_lineups(request, team_id): + TOKEN = request.user.profile.teamsnap_access_token + from django.forms import formset_factory + from teamsnap.forms import EventChooseForm + from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary, Opponent + client = TeamSnap(token=TOKEN) + time.sleep(0.5) + + ts_events = Event.search(client, team_id=team_id) + EventChooseFormset = formset_factory(EventChooseForm) + formset = EventChooseFormset(request.GET) + choices = [(e.data['id'], e.data['formatted_title']) for e in ts_events] + + for form in formset: + form.fields['event_id'].choices = choices + + if formset.is_valid(): + event_ids = [f.cleaned_data['event_id'] for f in formset] + else: + event_ids = request.GET.get("event_ids").split(",") + + ts_bulkload = client.bulk_load(team_id=team_id, + types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member], + event__id=",".join(event_ids)) + event_ids = [int(i) for i in event_ids] + formsets_lineup = [] + formsets_bench = [] + formsets = [] + events = [] + contexts = [] + for event_id in event_ids: + ts_event = [i for i in ts_bulkload if isinstance(i, Event) and i.data['id']==event_id][0] + ts_availabilities = Availability.search(client, event_id=ts_event.data['id']) + ts_availability_summary = \ + [i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0] + ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id] + + if ts_lineup_entries: + ts_lineup = EventLineup.get(client, id=ts_lineup_entries[0].data['event_lineup_id']) + else: + ts_lineup = EventLineup.search(client, event_id=event_id) + + ts_members = [i for i in ts_bulkload if isinstance(i, Member)] + ts_member_lookup = {m.data['id']: m for m in ts_members} + ts_availability_lookup = {m.data['member_id']: m for m in ts_availabilities} + ts_lineup_entries_lookup = {m.data['member_id']: m for m in ts_lineup_entries} + + members=[] + + for member in ts_members: + members.append ({ + "member":getattr(member, 'data'), + "availability": getattr(ts_availability_lookup.get(member.data['id'], {}), 'data', {}), + "lineup_entry": getattr(ts_lineup_entries_lookup.get(member.data['id'], {}), 'data', {}) + } + ) + + members = sorted(members, key=lambda d: ( + { + None:3, # No Response + 0:2, # No + 2:1, # Maybe + 1:0 # Yes + + }.get(d['availability'].get('status_code')), + d['member'].get('last_name')) + ) + + + from teamsnap.forms import LineupEntryFormset, LineupEntryForm + + formset = LineupEntryFormset( + initial=[ + { + "event_lineup_entry_id" : member['lineup_entry'].get('id'), + "event_lineup_id" : member['lineup_entry'].get('event_lineup_id'), + "event_id": event_id, + "member_id" : member['member']['id'], + "sequence" : member['lineup_entry'].get('sequence'), + "label" : member['lineup_entry'].get('label'), + } + for member in members + ] + ) + + for form in formset: + form.member = ts_member_lookup.get(form['member_id'].initial) + form.availability = ts_availability_lookup.get(form['member_id'].initial) + + formset_lineup = [form for form in formset if form.initial.get('event_lineup_entry_id')] + formset_lineup = sorted( + formset_lineup, + key=lambda d: d.initial.get('sequence',100) + ) + formset_bench = [form for form in formset if + form not in formset_lineup and + form.availability.data['status_code'] in [2, 1] + ] + formset_out = [form for form in formset if + form not in formset_lineup and + form not in formset_bench and + not form.member.data['is_non_player'] + ] + + contexts.append({ + "event":ts_event, + "formset": formset, + "formset_bench":formset_bench, + "formset_lineup":formset_lineup, + "formset_out":formset_out + }) + + return render(request, "teamsnap/lineup/multiple_edit.html", context={ + "team_id": team_id, + "contexts":contexts + }) + +def submit_lineup(request, team_id, event_id): + from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup + from teamsnap.forms import LineupEntryFormset + TOKEN = request.user.profile.teamsnap_access_token + client = TeamSnap(token=TOKEN) + time.sleep(0.5) + ts_lineup = EventLineup.search(client, event_id=event_id) + event_lineup_id = ts_lineup[0].data['id'] + if request.GET: + return HttpResponseNotAllowed() + if request.POST: + formset = LineupEntryFormset(request.POST) + if formset.is_valid(): + r = [] + for form in formset: + data = form.cleaned_data + if data.get('event_lineup_entry_id'): + event_lineup_entry = EventLineupEntry.get(client, id=data.get('event_lineup_entry_id')) + event_lineup_entry.data.update(data) + # breakpoint() + r.append(event_lineup_entry.put()) + # breakpoint() + pass + elif data.get('sequence') is not None and data.get('label'): + event_lineup_entry = EventLineupEntry.new(client) + event_lineup_entry.data.update(data) + event_lineup_entry.data.update({"event_lineup_id":event_lineup_id}) + r.append(event_lineup_entry.post()) + else: + pass + else: + # breakpoint() + pass + # breakpoint() + pass + return HttpResponse(f'{team_id} {event_id}') + pass + + return HttpResponse(f'{team_id} {event_id}') + +def image_generator(request, team_id, event_id): + TOKEN = request.user.profile.teamsnap_access_token + + from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary + client = TeamSnap(token=TOKEN) + time.sleep(0.5) + + ts_event = Event.get(client, id=event_id) + return render(request, "teamsnap/event/instagen.html", context = {"event":ts_event}) + +# @app.route('/get_matchup_image') +def get_matchup_image(request, team_id, event_id, dimensions=None, background=None): + from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup, Event, Team, Opponent, Location + from .utils.gen_image import Team as ImagegenTeam, Location as ImagegenLocation + from .utils.gen_image import gen_image, gen_results_image + import io + TOKEN = request.user.profile.teamsnap_access_token + if request.GET: + POSTPONED = request.GET.get('postponed', 'false') == 'true' + INCLUDE_WINLOSS = request.GET.get('winloss', 'false') == 'true' + BACKGROUND = request.GET.get('background', 'location') + game_id = event_id + dimensions = request.GET.get('dimensions') + width = int(dimensions.split("x")[0]) + height = int(dimensions.split("x")[1]) + + teamsnap = TeamSnap(TOKEN) + time.sleep(0.5) + ts_event = Event.get(teamsnap, game_id).data + fave_team = Team.get(teamsnap, ts_event['team_id']).data + opponent_team = Opponent.get(teamsnap, ts_event['opponent_id']).data + location = Location.get(teamsnap, ts_event['location_id']).data + formatted_results = ts_event['formatted_results'] + if formatted_results: + # L 4-3 + runs_for = formatted_results.split(" ")[1].split("-")[0] + runs_against = formatted_results.split(" ")[1].split("-")[1] + else: + runs_for, runs_against = None, None + + logo_image_directory = 'benchcoachproject/static/teamsnap/ig/logos-bw/{filename}.{ext}' + venue_image_directory = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}' + + def shortname_from_name(name): + return name.replace(" ", "").lower() + + # date = parser.parse(ts_event['start_date']) + # date = date.astimezone(ZoneInfo("America/Chicago")) + game_info = { + "date": ts_event['start_date'], + "team_fave": ImagegenTeam( + name=fave_team["name"], + image_directory=logo_image_directory.format(filename=shortname_from_name(fave_team["name"]), ext="png") + ), + "team_opponent": ImagegenTeam( + name=opponent_team["name"], + image_directory=logo_image_directory.format(filename=shortname_from_name(opponent_team["name"]), + ext="png") + ), + "location": ImagegenLocation( + name=location['name'], + image_directory=venue_image_directory.format(filename=shortname_from_name(location["name"]), ext="png"), + # address=location['address'] + ), + "runs_for": runs_for, + "runs_against": runs_against + } + + if not game_info['runs_for'] and not game_info['runs_against']: + image = gen_image(**game_info, background=BACKGROUND, width=width, height=height) + elif game_info['runs_for'] and game_info['runs_against']: + image = gen_results_image(**game_info, background=BACKGROUND, width=width, height=height) + else: + raise Exception + + imgByteArr = io.BytesIO() + image.save(imgByteArr, format='PNG') + imgByteArr = imgByteArr.getvalue() + + return HttpResponse(imgByteArr, content_type="image/png") + +def multi_lineup_choose(request, team_id): + from teamsnap.forms import EventChooseForm + from django.forms import formset_factory + if not request.GET.get('num'): + return HttpResponse(500) + else: + num = int(request.GET.get('num')) + TEAM_ID = team_id + TOKEN = request.user.profile.teamsnap_access_token + no_past = bool(request.GET.get('no_past', 0)) + games_only = bool(request.GET.get('games_only', 0)) + from pyteamsnap.api import TeamSnap, Event, Location, Opponent + client = TeamSnap(token=TOKEN) + time.sleep(.5) + ts_events = Event.search(client, team_id=TEAM_ID) + if no_past: + ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)] + if games_only: + ts_events = [e for e in ts_events if e.data['is_game']] + ts_events = {e.data['id']: e for e in ts_events} + # ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)} + # ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)} + # for event in ts_events: + + EventChooseFormset = formset_factory(EventChooseForm, extra=num) + formset = EventChooseFormset() + + choices= [(id, e.data['formatted_title']) for id, e in ts_events.items()] + + for form in formset: + form.fields['event_id'].choices = choices + + pass + return render(request, "teamsnap/lineup/multiple_choose.html", context={"formset": formset, "team_id": team_id}) \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index f219da4..0366286 100644 --- a/templates/base.html +++ b/templates/base.html @@ -3,11 +3,9 @@ {% block title %}Title{% endblock %} - {% include 'base_layout.html' %} + {% include 'head.html' %} - -
@@ -18,24 +16,12 @@
-
- - - - \ No newline at end of file + diff --git a/templates/base_layout.html b/templates/head.html similarity index 100% rename from templates/base_layout.html rename to templates/head.html diff --git a/templates/home.html b/templates/home.html index 4b088aa..ffe1860 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,9 +1,16 @@ {% extends "base.html" %}{% load static %} {% block title %}Bench Coach Home{% endblock %} +{% block page_heading %} +
+

+ Welcome to Bench Coach +

+
+{% endblock %} {% block content %}
-

Welcome to Bench Coach

+

Quisque at curabitur mollis ornare, malesuada maecenas. Orci elit tristique, malesuada eu pharetra. Est praesent tortor porttitor aptent, amet quisque.

{#
#} diff --git a/templates/navbar.html b/templates/navbar.html index 2288204..474f6eb 100644 --- a/templates/navbar.html +++ b/templates/navbar.html @@ -1,22 +1,48 @@ {% load static %} -