Merge branch 'dev-gamechanger' into dev
# Conflicts: # gamechanger/utils/gamechanger.py # gamechanger/views.py
This commit is contained in:
2
.idea/benchcoach.iml
generated
2
.idea/benchcoach.iml
generated
@@ -21,6 +21,8 @@
|
|||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.9 (benchcoach) (2)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.9 (benchcoach) (2)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module" module-name="pyteamsnap" />
|
||||||
|
<orderEntry type="module" module-name="gamescrapyr" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PackageRequirementsSettings">
|
<component name="PackageRequirementsSettings">
|
||||||
<option name="requirementsPath" value="$MODULE_DIR$/requirements/local.txt" />
|
<option name="requirementsPath" value="$MODULE_DIR$/requirements/local.txt" />
|
||||||
|
|||||||
2
.idea/modules.xml
generated
2
.idea/modules.xml
generated
@@ -3,6 +3,8 @@
|
|||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/benchcoach.iml" filepath="$PROJECT_DIR$/.idea/benchcoach.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/benchcoach.iml" filepath="$PROJECT_DIR$/.idea/benchcoach.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/../gamescrapyr/.idea/gamescrapyr.iml" filepath="$PROJECT_DIR$/../gamescrapyr/.idea/gamescrapyr.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/../pyteamsnap/.idea/pyteamsnap.iml" filepath="$PROJECT_DIR$/../pyteamsnap/.idea/pyteamsnap.iml" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
3
.idea/vcs.xml
generated
3
.idea/vcs.xml
generated
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/../gamescrapyr" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,243 +1,33 @@
|
|||||||
import csv
|
import logging
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
|
|
||||||
import pytz
|
from gamescrapyr.gamescrapyr import GameChangerClient
|
||||||
import requests
|
|
||||||
from bs4 import BeautifulSoup
|
# This retrieves a Python logging instance (or creates it)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
url = "https://gc.com/t/{season_id}/{team_slug}-{team_id}/{page}"
|
url = "https://gc.com/t/{season_id}/{team_slug}-{team_id}/{page}"
|
||||||
|
|
||||||
|
|
||||||
def get_authenticated_session(request):
|
def get_gamechanger_client(request):
|
||||||
gc_username = request.user.gamechanger_account.user
|
managed_team = request.user.gamechanger_preferences.managed_team
|
||||||
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.managed_team.season_slug
|
|
||||||
team_slug = request.user.gamechanger_preferences.managed_team.slug
|
|
||||||
team_id = request.user.gamechanger_preferences.managed_team.id
|
|
||||||
authenticated_session.headers.update(
|
|
||||||
{
|
|
||||||
"referer": url.format(
|
|
||||||
season_id=season_id,
|
|
||||||
team_slug=team_slug,
|
|
||||||
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=f"https://gc.com/do-save-lineup/{team_id}",
|
|
||||||
json={"lineup": lineup},
|
|
||||||
)
|
|
||||||
if r.status_code == 20 and r.content == b"OK":
|
|
||||||
return r
|
|
||||||
else:
|
|
||||||
raise requests.exceptions.RequestException(
|
|
||||||
f"Returned {r.status_code} for {r.reason}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def scrape_page(season_id, team_id, team_slug, page):
|
|
||||||
r = requests.get(
|
|
||||||
url.format(season_id=season_id, team_id=team_id, team_slug=team_slug, 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)
|
|
||||||
|
|
||||||
|
|
||||||
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.managed_team.season_slug
|
|
||||||
team_id = request.user.gamechanger_preferences.managed_team.id
|
|
||||||
team_slug = request.user.gamechanger_preferences.managed_team.slug
|
|
||||||
page = "stats/batting/Qualified/standard/csv"
|
|
||||||
authenticated_session.headers.update(
|
|
||||||
{
|
|
||||||
"referer": url.format(
|
|
||||||
season_id=season_id, team_id=team_id, team_slug=team_slug, page="stats"
|
|
||||||
),
|
|
||||||
"x-csrftoken": authenticated_session.cookies.get("csrftoken"),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
r = authenticated_session.get(
|
|
||||||
cookies=authenticated_session.cookies,
|
|
||||||
url=url.format(
|
|
||||||
season_id=season_id, team_id=team_id, team_slug=team_slug, page=page
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
r.status_code != 200
|
not request.session.get("gamechanger_client")
|
||||||
or "Please sign in or join to continue." in r.content.decode("utf-8")
|
or not request.session["gamechanger_client"].is_authorized()
|
||||||
):
|
):
|
||||||
raise Exception("Stats fetch failed.")
|
logger.info("GameChanger client not found or not authorized, creating...")
|
||||||
|
gc_username = request.user.gamechanger_account.user
|
||||||
roster = scrape_page(
|
gc_password = request.user.gamechanger_account.password
|
||||||
season_id=season_id, team_id=team_id, team_slug=team_slug, page="roster"
|
client = GameChangerClient(
|
||||||
)
|
email=gc_username,
|
||||||
id_lookup = {
|
password=gc_password,
|
||||||
(p.get("fname"), p.get("lname")): p.get("player_id") for p in roster["roster"]
|
team_id=managed_team.id,
|
||||||
}
|
team_slug=managed_team.slug,
|
||||||
|
season_slug=managed_team.season_slug,
|
||||||
decoded_content = r.content.decode("utf-8")
|
|
||||||
|
|
||||||
cr = csv.reader(decoded_content.splitlines(), delimiter=",")
|
|
||||||
my_list = list(cr)
|
|
||||||
player_keys = [
|
|
||||||
(i, key)
|
|
||||||
for i, key in enumerate(my_list[1][: my_list[0].index("Offensive Stats")])
|
|
||||||
]
|
|
||||||
offensive_keys = [
|
|
||||||
(i, key)
|
|
||||||
for i, key in enumerate(
|
|
||||||
my_list[1][
|
|
||||||
my_list[0]
|
|
||||||
.index("Offensive Stats") : my_list[0]
|
|
||||||
.index("Defensive Stats")
|
|
||||||
- 1
|
|
||||||
],
|
|
||||||
start=my_list[0].index("Offensive Stats"),
|
|
||||||
)
|
)
|
||||||
]
|
request.session["gamechanger_client"] = client
|
||||||
defensive_keys = [
|
else:
|
||||||
(i, key)
|
logger.info("GameChanger client found and authorized, loading...")
|
||||||
for i, key in enumerate(
|
client = request.session["gamechanger_client"]
|
||||||
my_list[1][my_list[0].index("Defensive Stats") :],
|
|
||||||
start=my_list[0].index("Defensive Stats"),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
stats = {}
|
return client
|
||||||
for row in my_list[2:]:
|
|
||||||
player_keys
|
|
||||||
number, lname, fname = row[:3]
|
|
||||||
if number == "Team":
|
|
||||||
break
|
|
||||||
gamechanger_id = id_lookup[(fname, lname)]
|
|
||||||
stats[gamechanger_id] = {
|
|
||||||
"offensive": {k: row[i] for i, k in offensive_keys},
|
|
||||||
"defensive": {k: row[i] for i, k in defensive_keys},
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats
|
|
||||||
|
|
||||||
|
|
||||||
# d = scrape_page(season_id, team_id, page)
|
|
||||||
pass
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from teamsnap.views import get_teamsnap_client
|
|||||||
from .forms import AccountForm, PlayerFormSet, PreferencesForm
|
from .forms import AccountForm, PlayerFormSet, PreferencesForm
|
||||||
from .models import Account, Player, Preferences, Team
|
from .models import Account, Player, Preferences, Team
|
||||||
from .utils import gamechanger
|
from .utils import gamechanger
|
||||||
|
from .utils.gamechanger import get_gamechanger_client
|
||||||
|
|
||||||
|
|
||||||
def teams(request):
|
def teams(request):
|
||||||
@@ -124,32 +125,31 @@ class AccountFormView(FormView):
|
|||||||
|
|
||||||
|
|
||||||
def roster(request):
|
def roster(request):
|
||||||
season_id = request.user.gamechanger_preferences.managed_team.season_slug
|
gamechanger = get_gamechanger_client(request)
|
||||||
team_id = request.user.gamechanger_preferences.id
|
roster = gamechanger.get_roster()
|
||||||
page = "roster"
|
|
||||||
d = gamechanger.scrape_page(season_id, team_id, page)
|
|
||||||
roster = d["roster"]
|
|
||||||
return render(request, "gamechanger/roster.html", context={"roster": roster})
|
return render(request, "gamechanger/roster.html", context={"roster": roster})
|
||||||
|
|
||||||
|
|
||||||
def roster_import(request):
|
def roster_import(request):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
from pyteamsnap.api import Member
|
from pyteamsnap.objects import Member
|
||||||
|
|
||||||
client = get_teamsnap_client(request)
|
gc_client = get_gamechanger_client(request)
|
||||||
season_id = request.user.gamechanger_preferences.managed_team.season_slug
|
season_slug = request.user.gamechanger_preferences.managed_team.season_slug
|
||||||
team_slug = request.user.gamechanger_preferences.managed_team.slug
|
team_slug = request.user.gamechanger_preferences.managed_team.slug
|
||||||
team_id = request.user.gamechanger_preferences.managed_team.id
|
team_id = request.user.gamechanger_preferences.managed_team.id
|
||||||
|
|
||||||
|
ts_client = get_teamsnap_client(request)
|
||||||
teamsnap_team_id = request.user.teamsnap_preferences.managed_team_id
|
teamsnap_team_id = request.user.teamsnap_preferences.managed_team_id
|
||||||
teamsnap_members = {
|
teamsnap_members = {
|
||||||
f"{member.data['first_name']} {member.data['last_name']}": member
|
f"{member.data['first_name']} {member.data['last_name']}": member
|
||||||
for member in Member.search(client, team_id=teamsnap_team_id)
|
for member in Member.search(ts_client, team_id=teamsnap_team_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
page = "roster"
|
roster = gc_client.get_roster(
|
||||||
|
team_id=team_id, team_slug=team_slug, season_slug=season_slug
|
||||||
d = gamechanger.scrape_page(season_id, team_id, team_slug, page)
|
)
|
||||||
roster = d["roster"]
|
|
||||||
initial = [
|
initial = [
|
||||||
{
|
{
|
||||||
"gamechanger_name": f"{player['fname']} {player['lname']}",
|
"gamechanger_name": f"{player['fname']} {player['lname']}",
|
||||||
@@ -215,6 +215,9 @@ def lineup_submit(request):
|
|||||||
if request.GET:
|
if request.GET:
|
||||||
return HttpResponseNotAllowed()
|
return HttpResponseNotAllowed()
|
||||||
if request.POST:
|
if request.POST:
|
||||||
|
from gamechanger.utils.gamechanger import get_gamechanger_client
|
||||||
|
|
||||||
|
gc_client = get_gamechanger_client(request)
|
||||||
formset = LineupEntryFormset(request.POST)
|
formset = LineupEntryFormset(request.POST)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
lineup_data = [
|
lineup_data = [
|
||||||
@@ -251,7 +254,7 @@ def lineup_submit(request):
|
|||||||
)
|
)
|
||||||
elif lineup_entry["label"] != "DR":
|
elif lineup_entry["label"] != "DR":
|
||||||
lineup.append(d)
|
lineup.append(d)
|
||||||
r = gamechanger.submit_lineup(request, lineup)
|
r = gc_client.submit_lineup(lineup)
|
||||||
if r.content == b"OK":
|
if r.content == b"OK":
|
||||||
return HttpResponse(status=200)
|
return HttpResponse(status=200)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user