From f788fb99325a1cfbe8635bc79401183ea605c7c1 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 8 Jun 2022 14:08:33 -0500 Subject: [PATCH] implement lineup send to gamechanger --- benchcoach/static/css/project.css | 6 + benchcoach/static/images/gamechanger.svg | 80 ++++++++++ benchcoach/templates/base.html | 3 + config/settings/base.py | 2 +- gamechanger/admin.py | 4 +- gamechanger/forms.py | 18 ++- gamechanger/migrations/0003_player.py | 22 +++ .../migrations/0004_alter_player_id.py | 18 +++ gamechanger/models.py | 22 ++- gamechanger/templates/gamechanger/roster.html | 12 +- gamechanger/urls.py | 12 +- gamechanger/utils/gamechanger.py | 67 +++++++- gamechanger/views.py | 149 ++++++++++++++---- teamsnap/forms.py | 1 + teamsnap/migrations/0001_initial.py | 1 + teamsnap/migrations/__init__.py | 0 teamsnap/models.py | 7 +- teamsnap/templates/lineup/multiple_edit.html | 20 ++- teamsnap/templates/lineup/widgets/lineup.html | 10 +- .../lineup/widgets/lineup_table.html | 1 + teamsnap/views.py | 36 +++-- 21 files changed, 409 insertions(+), 82 deletions(-) create mode 100644 benchcoach/static/images/gamechanger.svg create mode 100644 gamechanger/migrations/0003_player.py create mode 100644 gamechanger/migrations/0004_alter_player_id.py create mode 100644 teamsnap/migrations/__init__.py diff --git a/benchcoach/static/css/project.css b/benchcoach/static/css/project.css index c0b67da..05c0400 100644 --- a/benchcoach/static/css/project.css +++ b/benchcoach/static/css/project.css @@ -32,3 +32,9 @@ vertical-align: middle; white-space: nowrap; } + +.btn-gamechanger { + color: #fff; + border-color: #1b73bc; + background-color: #1b73bc; +} diff --git a/benchcoach/static/images/gamechanger.svg b/benchcoach/static/images/gamechanger.svg new file mode 100644 index 0000000..5da9c86 --- /dev/null +++ b/benchcoach/static/images/gamechanger.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/benchcoach/templates/base.html b/benchcoach/templates/base.html index 045b0fd..42dc21a 100644 --- a/benchcoach/templates/base.html +++ b/benchcoach/templates/base.html @@ -85,6 +85,9 @@ +
  • #} {# {% endfor %}#} -
    + {{ formset.management_form }} {% csrf_token %} @@ -23,16 +23,20 @@ {% for form in formset %} - {{ form.gamechanger_id.as_hidden }} + {{ form.gamechanger_name.as_hidden }} + {{ form.teamsnap_name.as_hidden }} + {{ form.id.as_hidden }} {{ form.teamsnap_member_id.as_hidden }} + {{ form.fname.as_hidden }} + {{ form.lname.as_hidden }} {% endfor %}
    - {{ form.gamechanger_name }} + {{ form.gamechanger_name.value }} - {{ form.teamsnap_name }} + {{ form.teamsnap_member_id }} {{ form.DELETE }}
    diff --git a/gamechanger/urls.py b/gamechanger/urls.py index 8766fa3..f33fbeb 100644 --- a/gamechanger/urls.py +++ b/gamechanger/urls.py @@ -1,16 +1,10 @@ -from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from django.urls import path -from .views import ( - PreferencesFormView, - AccountFormView, - roster_view, - roster_save -) +from .views import AccountFormView, PreferencesFormView, lineup_submit, roster_import urlpatterns = [ path("account/", AccountFormView.as_view(), name="gamechanger_account"), path("preferences/", PreferencesFormView.as_view(), name="gamechanger_preferences"), - path("roster/", roster_view, name="gamechanger_roster"), - path("roster/save", roster_save, name="gamechanger_save"), + path("roster/import", roster_import, name="gamechanger_import_roster"), + path("lineup/submit", lineup_submit, name="gamechanger_lineup_submit"), ] diff --git a/gamechanger/utils/gamechanger.py b/gamechanger/utils/gamechanger.py index c49083f..972c937 100644 --- a/gamechanger/utils/gamechanger.py +++ b/gamechanger/utils/gamechanger.py @@ -1,15 +1,70 @@ -import requests -import re import json +import re - +import requests url = "https://gc.com/t/{season_id}/{team_id}/{page}" + +def get_authenticated_session(request): + gc_username = request.user.gamechanger_account.user + gc_password = request.user.gamechanger_account.password + s = requests.Session() + s.headers.update({"referer": "https://gc.com/do-login"}) + s.get("https://gc.com/login") + r2 = s.post( + "https://gc.com/do-login", + cookies=s.cookies, + data={ + "csrfmiddlewaretoken": s.cookies.get("csrftoken"), + "email": gc_username, + "password": gc_password, + }, + ) + if r2.status_code == 200: + return s + else: + raise requests.exceptions.RequestException( + f"Returned {r2.status_code} for {r2.reason}" + ) + + +def submit_lineup(request, lineup): + authenticated_session = get_authenticated_session(request) + season_id = request.user.gamechanger_preferences.season_id + team_id = request.user.gamechanger_preferences.team_id + authenticated_session.headers.update( + { + "referer": url.format( + season_id=season_id, team_id=team_id, page="lineup_edit" + ), + "x-csrftoken": authenticated_session.cookies.get("csrftoken"), + "Content-Type": "application/x-www-form-urlencoded;", + } + ) + r = authenticated_session.post( + cookies=authenticated_session.cookies, + url="https://gc.com/do-save-lineup/{team_id}".format( + team_id=team_id.split("-").pop() + ), + json={"lineup": lineup}, + ) + if r.status_code == 200: + return r + else: + raise requests.exceptions.RequestException( + f"Returned {r.status_code} for {r.reason}" + ) + + def scrape_page(season_id, team_id, page): - r=requests.get(url.format(season_id=season_id, team_id=team_id, page=page)) - j=initialize_page_json = re.search(r'page.initialize\(\$.parseJSON\("(.*?)"\)', r.content.decode('unicode_escape')) - m=j.group(1) + r = requests.get(url.format(season_id=season_id, team_id=team_id, page=page)) + initialize_page_json = re.search( + r'page.initialize\(\$.parseJSON\("(.*?)"\)', r.content.decode("unicode_escape") + ) + m = initialize_page_json.group(1) return json.loads(m) + + # d = scrape_page(season_id, team_id, page) pass diff --git a/gamechanger/views.py b/gamechanger/views.py index 5e4ea0e..33ff9a2 100644 --- a/gamechanger/views.py +++ b/gamechanger/views.py @@ -1,11 +1,14 @@ +from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseServerError from django.shortcuts import render from django.views.generic.edit import FormView -from django.views.generic.list import ListView -from .forms import PreferencesForm, AccountForm, PlayerFormSet -from .utils.gamechanger import scrape_page + from teamsnap.views import get_teamsnap_client -# Create your views here. +from .forms import AccountForm, PlayerFormSet, PreferencesForm +from .models import Player +from .utils import gamechanger + + class PreferencesFormView(FormView): template_name = "gamechanger/form.html" form_class = PreferencesForm @@ -29,6 +32,7 @@ class PreferencesFormView(FormView): return initial + class AccountFormView(FormView): template_name = "gamechanger/form.html" form_class = AccountForm @@ -52,30 +56,119 @@ class AccountFormView(FormView): return initial -def roster_view(request): - from pyteamsnap.api import Member - client = get_teamsnap_client(request) - season_id = request.user.gamechanger_preferences.season_id - team_id = request.user.gamechanger_preferences.team_id - teamsnap_team_id = request.user.preferences.managed_team_id - teamsnap_members = { - f"{member.data['first_name']} {member.data['last_name']}":member - for member in Member.search(client, team_id=teamsnap_team_id) - } - page = "roster" +def roster_import(request): + if request.method == "GET": + from pyteamsnap.api import Member - d = scrape_page(season_id, team_id, page) - roster = d['roster'] - initial = [{ - 'gamechanger_name':f"{player['fname']} {player['lname']}", - 'teamsnap_name':"{first_name} {last_name}".format(**teamsnap_members[f"{player['fname']} {player['lname']}"].data), - 'gamechanger_id':player.get('player_id'), - 'teamsnap_member_id':teamsnap_members[f"{player['fname']} {player['lname']}"].data['id'], - } for player in roster] - formset = PlayerFormSet(initial=initial) - return render(request, "gamechanger/roster.html", context={'roster':roster, 'formset':formset}) - pass + client = get_teamsnap_client(request) + season_id = request.user.gamechanger_preferences.season_id + team_id = request.user.gamechanger_preferences.team_id + teamsnap_team_id = request.user.teamsnap_preferences.managed_team_id + teamsnap_members = { + f"{member.data['first_name']} {member.data['last_name']}": member + for member in Member.search(client, team_id=teamsnap_team_id) + } -def roster_save(request): - pass + page = "roster" + + d = gamechanger.scrape_page(season_id, team_id, page) + roster = d["roster"] + initial = [ + { + "gamechanger_name": f"{player['fname']} {player['lname']}", + "fname": player["fname"], + "lname": player["lname"], + "teamsnap_name": "{first_name} {last_name}".format( + **teamsnap_members[f"{player['fname']} {player['lname']}"].data + ), + "id": player.get("player_id"), + "teamsnap_member_id": teamsnap_members[ + f"{player['fname']} {player['lname']}" + ].data["id"], + } + for player in roster + ] + formset = PlayerFormSet(initial=initial) + choices = [ + ( + teamsnap_member.data["id"], + f"{teamsnap_member.data['first_name']} {teamsnap_member.data['last_name']}", + ) + for teamsnap_member in teamsnap_members.values() + ] + for form in formset: + form.fields["teamsnap_member_id"].widget.choices = choices + pass + return render( + request, + "gamechanger/roster.html", + context={"roster": roster, "formset": formset}, + ) + elif request.POST: + formset = PlayerFormSet(request.POST) + if formset.is_valid(): + r = [] + for form in formset: + data = form.cleaned_data + data.pop("DELETE") + data.pop("gamechanger_name") + data.pop("teamsnap_name") + obj, did_create = Player.objects.update_or_create(**data) + obj.save() + r.append(obj) + return HttpResponse(status=200) + else: + return HttpResponseServerError() + + return HttpResponseServerError() + else: + return HttpResponseServerError() + + +def lineup_submit(request): + + from teamsnap.forms import LineupEntryFormset + + if request.GET: + return HttpResponseNotAllowed() + if request.POST: + formset = LineupEntryFormset(request.POST) + if formset.is_valid(): + lineup_data = [ + form.cleaned_data + for form in formset + if form.cleaned_data.get("label") + and form.cleaned_data.get("sequence") is not None + ] + lineup_data.sort(key=lambda x: x.get("sequence")) + lineup = [] + + for lineup_entry in lineup_data: + d = { + "player_id": lineup_entry["gamechanger_player_id"], + "position": lineup_entry["label"], + } + if lineup_entry["label"] == "DH": + for_whom = [ + e + for e in lineup_data + if e["position_only"] and e["label"] != "DR" + ][0] + d["forwhom"] = for_whom["gamechanger_player_id"] + lineup.append(d) + if for_whom in lineup: + lineup_data.remove(for_whom) + if for_whom in lineup_data: + lineup_data.remove(for_whom) + lineup.append( + { + "player_id": for_whom["gamechanger_player_id"], + "position": for_whom["label"], + } + ) + elif lineup_entry["label"] != "DR": + lineup.append(d) + gamechanger.submit_lineup(request, lineup) + return HttpResponse(status=200) + return HttpResponseServerError() diff --git a/teamsnap/forms.py b/teamsnap/forms.py index a86a08b..ed16189 100644 --- a/teamsnap/forms.py +++ b/teamsnap/forms.py @@ -21,6 +21,7 @@ class LineupEntryForm(forms.Form): member = None availability = None lineup_entry = None + gamechanger_player_id = forms.Field(required=False) event_lineup_entry_id = forms.Field(required=False) event_lineup_id = forms.Field(required=False) diff --git a/teamsnap/migrations/0001_initial.py b/teamsnap/migrations/0001_initial.py index 6a5fc68..df1512f 100644 --- a/teamsnap/migrations/0001_initial.py +++ b/teamsnap/migrations/0001_initial.py @@ -31,6 +31,7 @@ class Migration(migrations.Migration): "user", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, + related_name='teamsnap_preferences', to=settings.AUTH_USER_MODEL, ), ), diff --git a/teamsnap/migrations/__init__.py b/teamsnap/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/teamsnap/models.py b/teamsnap/models.py index 7a48f0a..2ea4645 100644 --- a/teamsnap/models.py +++ b/teamsnap/models.py @@ -5,5 +5,10 @@ from benchcoach.users.models import User class Preferences(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE) + user = models.OneToOneField( + User, on_delete=models.CASCADE, related_name="teamsnap_preferences" + ) managed_team_id = models.IntegerField() + + class Meta: + verbose_name_plural = "preferences" diff --git a/teamsnap/templates/lineup/multiple_edit.html b/teamsnap/templates/lineup/multiple_edit.html index 006fd4d..ca86be7 100644 --- a/teamsnap/templates/lineup/multiple_edit.html +++ b/teamsnap/templates/lineup/multiple_edit.html @@ -25,7 +25,6 @@ {% for event_data in contexts %} - {% include "lineup/widgets/lineup.html" with event=event_data.event event_id=event_data.event.data.id formset=event_data.formset formset_startinglineup=event_data.formset_startinglineup formset_bench=event_data.formset_bench formset_out=event_data.formset_out formset_startingpositionalonly=event_data.formset_startingpositionalonly %} @@ -41,25 +40,24 @@ {% block inline_javascript %} {{ block.super }} - diff --git a/teamsnap/templates/lineup/widgets/lineup.html b/teamsnap/templates/lineup/widgets/lineup.html index afea21f..0df68ec 100644 --- a/teamsnap/templates/lineup/widgets/lineup.html +++ b/teamsnap/templates/lineup/widgets/lineup.html @@ -1,6 +1,6 @@ {% load static %}
    - + {{ formset.management_form }} {% csrf_token %}
    @@ -34,11 +34,17 @@
    -
    +
    + +
    diff --git a/teamsnap/templates/lineup/widgets/lineup_table.html b/teamsnap/templates/lineup/widgets/lineup_table.html index 20d5fdc..7318251 100644 --- a/teamsnap/templates/lineup/widgets/lineup_table.html +++ b/teamsnap/templates/lineup/widgets/lineup_table.html @@ -8,6 +8,7 @@ data-player-name="{{ form.member.data.last_name }}, {{ form.member.data.first_name }}" data-availability-statuscode="{{ form.availability.data.status_code }}" > + {{ form.gamechanger_player_id.as_hidden }} {{ form.event_lineup_entry_id.as_hidden }} {{ form.event_lineup_id.as_hidden }} {{ form.event_id.as_hidden }} diff --git a/teamsnap/views.py b/teamsnap/views.py index b086ac2..7501820 100644 --- a/teamsnap/views.py +++ b/teamsnap/views.py @@ -7,10 +7,12 @@ from allauth.socialaccount.providers.oauth2.views import ( OAuth2CallbackView, OAuth2LoginView, ) -from django.http import HttpResponseNotAllowed, HttpResponseServerError, JsonResponse +from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseServerError from django.shortcuts import redirect, render from django.views.generic.edit import FormView +from gamechanger.models import Player as GamechangerPlayer + from .forms import PreferencesForm from .models import Preferences from .provider import TeamsnapProvider @@ -120,7 +122,8 @@ class PreferencesFormView(FormView): def schedule_view(request, team_id=None): if not team_id: return redirect( - "teamsnap_schedule", team_id=request.user.preferences.managed_team_id + "teamsnap_schedule", + team_id=request.user.teamsnap_preferences.managed_team_id, ) client = get_teamsnap_client(request) no_past = bool(request.GET.get("no_past", 0)) @@ -149,7 +152,7 @@ def schedule_view(request, team_id=None): def view_event(request, event_id, team_id=None): if not team_id: return redirect( - "teamsnap_event", team_id=request.user.preferences.managed_team_id + "teamsnap_event", team_id=request.user.teamsnap_preferences.managed_team_id ) from pyteamsnap.api import ( @@ -225,6 +228,16 @@ def edit_lineup(request, event_ids, team_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} + gc_player_lookup = { + m.data["id"]: getattr( + GamechangerPlayer.objects.filter( + teamsnap_member_id=m.data["id"] + ).first(), + "id", + None, + ) + 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} @@ -300,6 +313,9 @@ def edit_lineup(request, event_ids, team_id): "member_id": member["member"]["id"], "sequence": member["lineup_entry"].get("sequence"), "label": position, + "gamechanger_player_id": gc_player_lookup.get( + member["member"]["id"] + ), } ) @@ -360,7 +376,8 @@ def edit_lineup(request, event_ids, team_id): def dashboard(request, team_id=None): if not team_id: return redirect( - "teamsnap_dashboard", team_id=request.user.preferences.managed_team_id + "teamsnap_dashboard", + team_id=request.user.teamsnap_preferences.managed_team_id, ) from pyteamsnap.api import AvailabilitySummary, Event @@ -395,12 +412,11 @@ def dashboard(request, team_id=None): def submit_lineup(request, team_id, event_id): - from pyteamsnap.api import Event, EventLineup, EventLineupEntry + from pyteamsnap.api import EventLineup, EventLineupEntry from teamsnap.forms import LineupEntryFormset client = get_teamsnap_client(request) - ts_event = Event.get(client, event_id) ts_lineup = EventLineup.search(client, event_id=event_id) event_lineup_id = ts_lineup[0].data["id"] if request.GET: @@ -443,10 +459,6 @@ def submit_lineup(request, team_id, event_id): else: pass else: - # breakpoint() pass - # breakpoint() - pass - return JsonResponse(ts_event.data) - pass - return HttpResponseServerError + return HttpResponse(status=200) + return HttpResponseServerError()