diff --git a/gamechanger/templates/gamechanger/events.html b/gamechanger/templates/gamechanger/events.html new file mode 100644 index 0000000..98f885b --- /dev/null +++ b/gamechanger/templates/gamechanger/events.html @@ -0,0 +1,16 @@ +{% extends "base.html" %}{% load tz %} +{% block title_header %}GameChanger Events{% endblock title_header %} +{% block content %} + + {% for event in events %} + + + + + {% endfor %} +
+ {{ event.title }} + + {{ event.start|localtime }} +
+{% endblock %} diff --git a/gamechanger/templates/gamechanger/roster.html b/gamechanger/templates/gamechanger/roster.html index bbf1b4f..66bbdd6 100644 --- a/gamechanger/templates/gamechanger/roster.html +++ b/gamechanger/templates/gamechanger/roster.html @@ -1,48 +1,15 @@ {% extends "base.html" %} {% block content %} - {# {% for player in roster %}#} - {#
  • #} - {# {{ player.fname }} {{ player.lname }}#} - {#
  • #} - {# {% endfor %}#} -
    - {{ formset.management_form }} - {% csrf_token %} - - - - - - - {% for form in formset %} +
    - {{ formset.0.gamechanger_name.label }} - - {{ formset.0.teamsnap_name.label }} - - {{ formset.0.DELETE.label }} -
    + {% for member in roster %} + - - - {{ 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 %}
    + {{ member.fname }} {{ member.lname }} + - {{ form.gamechanger_name.value }} + {{ member }} - {{ form.teamsnap_member_id }} - - {{ form.DELETE }} -
    - -
    {% endblock %} diff --git a/gamechanger/templates/gamechanger/roster_import.html b/gamechanger/templates/gamechanger/roster_import.html new file mode 100644 index 0000000..64f8932 --- /dev/null +++ b/gamechanger/templates/gamechanger/roster_import.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} +{% block content %} + {# {% for player in roster %}#} + {#
  • #} + {# {{ player.fname }} {{ player.lname }}#} + {#
  • #} + {# {% endfor %}#} +
    + {{ formset.management_form }} + {% csrf_token %} + + + + + + + {% for form in formset %} + + + + + {{ 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 %} +
    + {{ formset.0.gamechanger_name.label }} + + {{ formset.0.teamsnap_name.label }} + + {{ formset.0.DELETE.label }} +
    + {{ form.gamechanger_name.value }} + + {{ form.teamsnap_member_id }} + + {{ form.DELETE }} +
    + +
    +{% endblock %} diff --git a/gamechanger/templates/gamechanger/teams.html b/gamechanger/templates/gamechanger/teams.html new file mode 100644 index 0000000..b924773 --- /dev/null +++ b/gamechanger/templates/gamechanger/teams.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% block content %} + + {% for team in teams %} + + + + + {% endfor %} +
    + {{ team.name }} + + {{ team }} +
    +{% endblock %} diff --git a/gamechanger/tests.py b/gamechanger/tests.py index 7ce503c..a8221a2 100644 --- a/gamechanger/tests.py +++ b/gamechanger/tests.py @@ -1,3 +1,86 @@ -from django.test import TestCase +import vcr +from django.conf import settings +from django.contrib.auth import ( + BACKEND_SESSION_KEY, + HASH_SESSION_KEY, + SESSION_KEY, + get_user_model, +) +from django.contrib.sessions.backends.db import SessionStore +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from selenium import webdriver -# Create your tests here. + +def create_session_cookie(): + + # First, create a new test user + user = get_user_model() + # user.objects.create_user(username=username, password=password) + user = get_user_model().objects.get(pk=2) + + # Then create the authenticated session using the new user credentials + session = SessionStore() + session[SESSION_KEY] = user.pk + session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0] + session[HASH_SESSION_KEY] = user.get_session_auth_hash() + session.save() + + # Finally, create the cookie dictionary + cookie = { + "name": settings.SESSION_COOKIE_NAME, + "value": session.session_key, + "secure": False, + "path": "/", + } + return cookie + + +@vcr.use_cassette( + "gamechanger/fixtures/tests.yaml", + ignore_localhost=True, + # record_mode="new_episodes", + decode_compressed_response=True, + allow_playback_repeats=True, +) +class GameChangerTests(StaticLiveServerTestCase): + fixtures = ["benchcoach/fixtures/dump.json"] + + @classmethod + def setUpClass(cls): + cls.port = 8000 + super().setUpClass() + cls.selenium = webdriver.Chrome("/opt/homebrew/bin/chromedriver") + cls.selenium.implicitly_wait(10) + + @classmethod + def tearDownClass(cls): + cls.selenium.quit() + super().tearDownClass() + + def setUp(self): + session_cookie = create_session_cookie() + self.selenium.get("{}{}".format(self.live_server_url, "/")) + self.selenium.add_cookie(session_cookie) + self.selenium.refresh() + pass + + def test_gamechanger_teams(self): + + self.selenium.get( + "{url}/gc/{page}".format(url=self.live_server_url, page="teams") + ) + breakpoint() + + def test_gamechanger_events(self): + + self.selenium.get( + "{url}/gc/{page}".format(url=self.live_server_url, page="events") + ) + breakpoint() + + def test_gamechanger_roster(self): + + self.selenium.get( + "{url}/gc/{page}".format(url=self.live_server_url, page="roster") + ) + breakpoint() diff --git a/gamechanger/urls.py b/gamechanger/urls.py index 4863408..ced0305 100644 --- a/gamechanger/urls.py +++ b/gamechanger/urls.py @@ -3,15 +3,21 @@ from django.urls import path from .views import ( AccountFormView, PreferencesFormView, + events, lineup_submit, + roster, roster_import, stats, + teams, ) urlpatterns = [ path("account/", AccountFormView.as_view(), name="gamechanger_account"), path("preferences/", PreferencesFormView.as_view(), name="gamechanger_preferences"), path("roster/import", roster_import, name="gamechanger_import_roster"), + path("roster", roster, name="gamechanger_roster"), path("lineup/submit", lineup_submit, name="gamechanger_lineup_submit"), path("stats", stats, name="gamechanger_stats"), + path("teams", teams, name="gamechanger_teams"), + path("events", events, name="gamechanger_events"), ] diff --git a/gamechanger/utils/gamechanger.py b/gamechanger/utils/gamechanger.py index 999290a..64cfab3 100644 --- a/gamechanger/utils/gamechanger.py +++ b/gamechanger/utils/gamechanger.py @@ -1,8 +1,11 @@ import csv +import datetime import json import re +import pytz import requests +from bs4 import BeautifulSoup url = "https://gc.com/t/{season_id}/{team_id}/{page}" @@ -67,6 +70,94 @@ def scrape_page(season_id, team_id, page): return json.loads(m) +def get_teams(session): + url = "https://gc.com/account/teams" + session.headers.update( + { + "referer": url.format("https://gc.com/account/profile"), + "x-csrftoken": session.cookies.get("csrftoken"), + } + ) + page = session.get(cookies=session.cookies, url=url) + soup = BeautifulSoup(page.content, "html.parser") + team_elements = [i for i in soup.find_all("li") if i.attrs.get("data-team-id")] + teams = [] + for i, team_element in enumerate(team_elements): + league_type, number_of_games = [ + c.text.strip() for c in team_element.findChildren("li") + ][1:3] + season_slug, team_slug = ( + team_element.find("a").attrs.get("href", "///").split("/")[2:] + ) + teams.append( + { + "name": team_element.find("a").text, + "id": team_element.attrs.get("data-team-id"), + "season": team_element.findPrevious("header").text, + "league_type": league_type, + "number_of_games": number_of_games, + "season_slug": season_slug, + "team_slug": team_slug, + } + ) + return teams + pass + + +def get_events(request): + authenticated_session = get_authenticated_session(request) + season_id = request.user.gamechanger_preferences.season_id + team_id = request.user.gamechanger_preferences.team_id + page = "stats/batting/Qualified/standard/csv" + authenticated_session.get( + url.format(season_id=season_id, team_id=team_id, page=page) + ) + authenticated_session.headers.update( + { + "x-csrftoken": authenticated_session.cookies.get("csrftoken"), + } + ) + page = authenticated_session.get( + cookies=authenticated_session.cookies, + url=url.format(season_id=season_id, team_id=team_id, page="schedule/games"), + ) + soup = BeautifulSoup(page.content, "html.parser") + game_elements = [r for r in soup.find_all("tr") if "game" in r.attrs.get("class")] + games = [] + for i, game_element in enumerate(game_elements): + game_slug = game_element.find("a").attrs.get("href").split("/")[1] + title = game_element.find("a").text + jslocaldate, jslocaltime_start, jslocaltime_arrival = ( + t.attrs.get("datetime") for t in game_element.findAll("time") + ) + games.append( + { + "id": game_element.attrs.get("data-id"), + "title": title, + "game_slug": game_slug, + "start": pytz.timezone("utc").localize( + datetime.datetime.fromisoformat(jslocaltime_start) + ), + } + ) + return games + + +def stream(): + # game_page = authenticated_session.get( + # cookies=authenticated_session.cookies, + # url=f"https://gc.com/{game_slug}", + # ) + # game_soup = BeautifulSoup(game_page.content, "html.parser") + # data_push_url_rel = game_soup.find("body").attrs.get("data-push-url")[2:] + # data_push_url = f"https://{data_push_url_rel}?sabertooth_aware=true" + # stream_page = authenticated_session.get( + # cookies=authenticated_session.cookies, url=data_push_url + # ) + # game_stream = json.loads(stream_page.content) + pass + + def stats(request): authenticated_session = get_authenticated_session(request) season_id = request.user.gamechanger_preferences.season_id diff --git a/gamechanger/views.py b/gamechanger/views.py index 0b6dd04..3c56e97 100644 --- a/gamechanger/views.py +++ b/gamechanger/views.py @@ -9,6 +9,19 @@ from .models import Account, Player, Preferences from .utils import gamechanger +def teams(request): + # season_id = request.user.gamechanger_preferences.season_id + # team_id = request.user.gamechanger_preferences.team_id + s = gamechanger.get_authenticated_session(request) + teams = gamechanger.get_teams(s) + return render(request, "gamechanger/teams.html", context={"teams": teams}) + + +def events(request): + events = gamechanger.get_events(request) + return render(request, "gamechanger/events.html", context={"events": events}) + + class PreferencesFormView(FormView): template_name = "gamechanger/form.html" form_class = PreferencesForm @@ -83,6 +96,15 @@ class AccountFormView(FormView): return form +def roster(request): + season_id = request.user.gamechanger_preferences.season_id + team_id = request.user.gamechanger_preferences.team_id + page = "roster" + d = gamechanger.scrape_page(season_id, team_id, page) + roster = d["roster"] + return render(request, "gamechanger/roster.html", context={"roster": roster}) + + def roster_import(request): if request.method == "GET": from pyteamsnap.api import Member @@ -128,7 +150,7 @@ def roster_import(request): pass return render( request, - "gamechanger/roster.html", + "gamechanger/roster_import.html", context={"roster": roster, "formset": formset}, ) elif request.POST: