Merge branch 'dev-gamechanger' into dev
# Conflicts: # gamecard/templates/gamecard/gamecard.html
This commit is contained in:
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
@@ -32,3 +32,9 @@
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-gamechanger {
|
||||
color: #fff;
|
||||
border-color: #1b73bc;
|
||||
background-color: #1b73bc;
|
||||
}
|
||||
|
||||
80
benchcoach/static/images/gamechanger.svg
Normal file
80
benchcoach/static/images/gamechanger.svg
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 741.9 69.6" style="enable-background:new 0 0 741.9 69.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
.st1{filter:url(#Adobe_OpacityMaskFilter);}
|
||||
.st2{mask:url(#mask-2_00000067194953457310049560000009970419005216943005_);fill-rule:evenodd;clip-rule:evenodd;fill:#00F092;}
|
||||
</style>
|
||||
<g id="Logo-Presentation">
|
||||
<g id="Slide-1-Copy-23" transform="translate(-194.000000, -546.000000)">
|
||||
<g id="Group-10" transform="translate(194.000000, 546.500000)">
|
||||
<path id="Fill-1" class="st0" d="M497.6,38.9h-9.4l7.9-15l8,15H497.6z M492.2,15.6l-19.8,37.5h8.3l3.6-6.7h23.8l3.6,6.7h8.3
|
||||
l-19.9-37.5H492.2L492.2,15.6z"/>
|
||||
<polyline id="Fill-2" class="st0" points="557.8,42.2 557.7,42.1 533.8,15.6 525.3,15.6 525.3,53.1 532.7,53.1 532.7,25.1
|
||||
532.8,25.2 557.9,53.1 565.2,53.1 565.2,15.6 557.8,15.6 557.8,42.2 "/>
|
||||
<path id="Fill-3" class="st0" d="M702.5,33.6h-18.7V23h18.7c1.4,0,2.7,0.6,3.7,1.6l0,0c1,1,1.5,2.3,1.5,3.7s-0.5,2.7-1.5,3.7
|
||||
C705.2,33.1,703.9,33.6,702.5,33.6z M706.9,40.3l0.5-0.2c2.3-1,4.2-2.6,5.5-4.6c1.4-2.1,2.2-4.6,2.2-7.1c0-3.4-1.3-6.6-3.7-9
|
||||
c-2.4-2.5-5.5-3.8-8.9-3.8h-26v37.5h7.3V41h13.9l0,0l10.5,12.1h9.8L706.9,40.3L706.9,40.3z"/>
|
||||
<polyline id="Fill-4" class="st0" points="459.8,30.6 434.9,30.6 434.9,15.6 427.5,15.6 427.5,53.1 434.9,53.1 434.9,38.1
|
||||
459.8,38.1 459.8,53.1 467.1,53.1 467.1,15.6 459.8,15.6 459.8,30.6 "/>
|
||||
<polyline id="Fill-5" class="st0" points="318.6,15.6 315.1,15.6 303.2,42.8 291.2,15.6 287.7,15.6 287.7,15.6 280.7,15.6
|
||||
280.7,53.1 288.2,53.1 288.2,25.7 300.2,53.1 306.1,53.1 318.2,25.6 318.2,53.1 325.6,53.1 325.6,15.6 318.6,15.6 318.6,15.6
|
||||
"/>
|
||||
<g id="Group-9" transform="translate(0.000000, 0.008595)">
|
||||
<g id="Clip-7">
|
||||
</g>
|
||||
<path id="Fill-6" class="st0" d="M252.6,38.9L252.6,38.9h-9.4l7.9-15l8,15H252.6z M247.2,15.6l-19.8,37.5h8.3l3.6-6.7h23.8
|
||||
l3.6,6.7h8.3l-19.9-37.5H247.2L247.2,15.6z"/>
|
||||
<path id="Fill-8" class="st0" d="M199.2,36.9v0.4H218v0.1c-1.4,4.8-5.8,8.2-10.8,8.2h-10c-6.1,0-11.1-5-11.2-11.1
|
||||
c-0.1-3,1.1-5.9,3.2-8c2.2-2.2,5.2-3.5,8.2-3.5h9.1c4,0,7.3-3,7.8-7v-0.4h-17.2c-5,0-9.7,2-13.3,5.6c-3.5,3.6-5.4,8.4-5.3,13.4
|
||||
c0.2,10.2,8.6,18.5,18.9,18.5h9.5h0.1c10.1-0.1,18.5-8.4,18.7-18.5c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0-0.2v-0.1l0,0v-4.2H207
|
||||
C203,29.9,199.6,32.9,199.2,36.9"/>
|
||||
</g>
|
||||
<path id="Fill-10" class="st0" d="M594.4,36.9v0.4h18.8v0.1c-1.4,4.8-5.8,8.2-10.8,8.2h-10c-6.1,0-11.1-5-11.2-11.1
|
||||
c-0.1-3,1.1-5.9,3.2-8c2.2-2.2,5.2-3.5,8.2-3.5h9.1c4,0,7.3-3,7.8-7v-0.4h-17.1c-5,0-9.7,2-13.3,5.6c-3.5,3.6-5.4,8.4-5.4,13.4
|
||||
c0.2,10.2,8.6,18.5,18.9,18.5h9.5h0.1c10.2-0.1,18.6-8.4,18.7-18.4c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0-0.2v-0.1l0,0V30h-18.7
|
||||
C598.1,30,594.8,33,594.4,36.9"/>
|
||||
<path id="Fill-11" class="st0" d="M398,23h13.7c4,0,7.3-3,7.8-7v-0.4H398c-5,0-9.7,2-13.3,5.6c-3.5,3.6-5.4,8.4-5.4,13.4
|
||||
c0.2,10.2,8.6,18.5,18.9,18.5h13.4c4,0,7.3-3,7.8-7v-0.4h-21.3c-3.1,0-6.1-1.3-8.2-3.5s-3.3-5-3.2-8C386.9,28,391.9,23,398,23"/>
|
||||
<path id="Fill-12" class="st0" d="M367.1,23c4,0,7.3-3,7.7-7v-0.4h-38.6v37.5h30.9c4,0,7.3-3,7.7-7v-0.4h-31.3v-7.6h14
|
||||
c3.8,0,7-3.1,7-7v-0.5h-20.9V23H367.1"/>
|
||||
<path id="Fill-13" class="st0" d="M660.4,23c4,0,7.3-3,7.7-7v-0.4h-38.6v37.5h30.9c4,0,7.3-3,7.7-7v-0.4h-31.3v-7.6h13.9
|
||||
c3.8,0,7-3.1,7-7v-0.5h-20.9V23H660.4"/>
|
||||
<polyline id="Fill-14" class="st0" points="728.9,53.1 727.5,53.1 727.5,45.6 724.9,45.6 724.9,44.3 731.4,44.3 731.4,45.6
|
||||
728.9,45.6 728.9,53.1 "/>
|
||||
<path id="Fill-15" class="st0" d="M736.8,53.1l-2.5-7.2l0,0c0,1.1,0.1,2.1,0.1,3.1v4.2h-1.2v-8.7h1.9l2.4,6.9l0,0l2.5-6.9h1.9
|
||||
v8.7h-1.4v-4.3c0-0.4,0-1,0-1.7s0-1.1,0-1.2l0,0l-2.6,7.2H736.8"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Logo-Presentation_00000113341718855537428600000001450668188223146168_">
|
||||
<g id="Slide-1-Copy-23_00000093891632931889047450000011681990669367062677_" transform="translate(-551.000000, -849.000000)">
|
||||
<g id="DSG_GameChanger_Logomark-Copy-7" transform="translate(551.000000, 849.000000)">
|
||||
<g id="Clip-2">
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="0" y="0" width="142.9" height="69.6">
|
||||
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<mask maskUnits="userSpaceOnUse" x="0" y="0" width="142.9" height="69.6" id="mask-2_00000067194953457310049560000009970419005216943005_">
|
||||
<g class="st1">
|
||||
<polygon id="path-1_00000010310054758688279420000011862642130963442847_" class="st0" points="0,0 142.9,0 142.9,69.6 0,69.6
|
||||
"/>
|
||||
</g>
|
||||
</mask>
|
||||
<path id="Fill-1_00000052789438335131278620000014152493238395228080_" class="st2" d="M104.9,55.5c-2.9,0-5.8-0.7-8.4-1.9
|
||||
c-2.5-1.2-4.8-3-6.7-5.2c-1.8-2.2-3.2-4.8-4-7.5c-0.8-2.9-1-6-0.6-9c1.2-8.3,7.4-15.2,15.5-17.2c0.5-0.1,3.2-0.6,6.7-0.6l22.4,0
|
||||
c0.1,0,3.1,0,5.8-1.3c4.4-2.5,7.2-7.3,7.2-12.4V0h-38C89,0,75.3,10.9,71.5,26.5c0,0.1,0,0.1,0,0.2l0,0.1H50.7
|
||||
c-7.5,0-13.7,6-14,13.6l0,0.5h33l0,0c0,0.2-0.1,0.5-0.2,0.8c-2.3,6.8-8.1,12.1-15.1,13.9c0,0-2.9,0.7-6.8,0.6H37
|
||||
c-3.6,0-6.2-0.5-6.7-0.6c-9.1-2.3-15.6-10.4-15.7-19.8c0-1.1,0-2.4,0.2-3.7c1.2-8.3,7.4-15.3,15.5-17.3c0.5-0.1,3.2-0.6,6.7-0.6
|
||||
l11.4,0c0.2,0,4,0,7-1.9c3.9-2.7,6.2-7.1,6.2-11.8V0H34.4C15.7,0,0.3,15.3,0,34.2c-0.2,9.2,3.3,18,9.9,24.7
|
||||
c6.6,6.8,15.8,10.7,25.1,10.7h15.7c10.6,0,20.7-5.1,27.2-13.6c6.7,8.5,16.9,13.6,27.6,13.6h23.4c7.5,0,13.7-6,14-13.6l0-0.5
|
||||
H104.9"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
@@ -36,7 +36,7 @@
|
||||
|
||||
<body class="bg-light">
|
||||
|
||||
<div class="mb-1">
|
||||
<div class="mb-1 bg-light">
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-navbar">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
@@ -57,28 +57,45 @@
|
||||
<a class="nav-link" href="{% url 'about' %}">About</a>
|
||||
</li>
|
||||
{% if request.user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_dashboard' %}">{% translate "Dashboard" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_schedule' %}">{% translate "Schedule" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_choose_multiple_lineups' team_id=request.user.preferences.managed_team_id%}?num=3">{% translate "Multi-Lineup" %}</a>
|
||||
</li>
|
||||
{% if request.user.teamsnap_preferences %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_dashboard' %}">{% translate "Dashboard" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_schedule' %}">{% translate "Schedule" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_choose_multiple_lineups' team_id=request.user.teamsnap_preferences.managed_team_id%}?num=3">{% translate "Multi-Lineup" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Profile
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="navbarDropdown">
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_preferences' %}">{% translate "Preferences" %}</a>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'users:detail' request.user.username %}">{% translate "My Profile" %}</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% if request.user.teamsnap_preferences %}
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'teamsnap_preferences' %}">{% translate "TeamSnap Preferences" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% if request.user.gamechanger_account %}
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'gamechanger_account' %}">{% translate "GameChanger Account" %}</a>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'gamechanger_preferences' %}">{% translate "GameChanger Preferences" %}</a>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'gamechanger_import_roster' %}">{% translate "GameChanger Import Roster" %}</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
{% endif %}
|
||||
<li class="dropdown-item">
|
||||
<a class="nav-link" href="{% url 'account_logout' %}">{% translate "Sign Out" %}</a>
|
||||
</li>
|
||||
|
||||
@@ -17,6 +17,7 @@ urlpatterns = [
|
||||
path("accounts/", include("allauth.urls")),
|
||||
path("", include("teamsnap.urls")),
|
||||
path("", include("instagen.urls")),
|
||||
path("gc/", include("gamechanger.urls")),
|
||||
path("", include("gamecard.urls")),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
@@ -126,7 +126,9 @@
|
||||
<td class="cell-square"></td>
|
||||
<td class="cell-square cell-smalltext cell-mono available-status-code-{{ member.availability.status_code }} {% if member.in_starting_lineup %}in-starting-lineup{% endif %}">{{ member.member.jersey_number }}</td>
|
||||
<td class="cell-info-lastname available-status-code-{{ member.availability.status_code }} {% if member.in_starting_lineup %}in-starting-lineup{% endif %}">{{ member.member.last_name }}</td>
|
||||
<td class="statscell"></td>
|
||||
<td class="statscell">
|
||||
{{ member.stats.offensive.AVG }}/{{ member.stats.offensive.OBP }}/{{ member.stats.offensive.SLG }}:{{ member.stats.offensive.PA }}
|
||||
</td>
|
||||
|
||||
<td class="cell-square cell-checkbox narrow no-inside-border">
|
||||
{% if "P" in member.member.position %}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import vcr
|
||||
from django.shortcuts import render
|
||||
|
||||
import gamechanger.models as GcModels
|
||||
import gamechanger.utils.gamechanger
|
||||
from teamsnap.models import Opponent, Team
|
||||
from teamsnap.utils import get_teamsnap_client
|
||||
|
||||
@@ -62,6 +64,12 @@ def gamecard(request, team_id, event_id):
|
||||
ts_availability_lookup = {m.data["id"]: m for m in ts_availabilities}
|
||||
ts_lineup_entries_lookup = {m.data["member_id"]: m for m in ts_lineup_entries}
|
||||
|
||||
stats = gamechanger.utils.gamechanger.stats(request)
|
||||
stats_lookup = {
|
||||
GcModels.Player.objects.filter(id=k).first().teamsnap_member_id: stat_row
|
||||
for k, stat_row in stats.items()
|
||||
}
|
||||
|
||||
members = []
|
||||
|
||||
for member in ts_members:
|
||||
@@ -91,6 +99,7 @@ def gamecard(request, team_id, event_id):
|
||||
ts_availabilities_past,
|
||||
)
|
||||
)[:4],
|
||||
"stats": stats_lookup.get(member.data["id"]),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
0
gamechanger/__init__.py
Normal file
0
gamechanger/__init__.py
Normal file
8
gamechanger/admin.py
Normal file
8
gamechanger/admin.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Account, Player, Preferences
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Account)
|
||||
admin.site.register(Preferences)
|
||||
admin.site.register(Player)
|
||||
6
gamechanger/apps.py
Normal file
6
gamechanger/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GamechangerConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'gamechanger'
|
||||
45
gamechanger/forms.py
Normal file
45
gamechanger/forms.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from django import forms
|
||||
from django.forms import ModelForm, formset_factory
|
||||
|
||||
from .models import Account, Player, Preferences
|
||||
|
||||
|
||||
class PreferencesForm(ModelForm):
|
||||
class Meta:
|
||||
model = Preferences
|
||||
fields = ["user", "season_id", "team_id"]
|
||||
widgets = {
|
||||
"user": forms.HiddenInput(),
|
||||
"managed_team_id": forms.TextInput(),
|
||||
}
|
||||
labels = {"managed_team_id": "Selected Team"}
|
||||
|
||||
|
||||
class AccountForm(ModelForm):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = ["user", "email", "password"]
|
||||
widgets = {
|
||||
"user": forms.HiddenInput(),
|
||||
"email": forms.EmailInput(),
|
||||
"password": forms.PasswordInput(),
|
||||
}
|
||||
|
||||
|
||||
class PlayerForm(ModelForm):
|
||||
gamechanger_name = forms.Field()
|
||||
teamsnap_name = forms.Field()
|
||||
fname = forms.Field()
|
||||
lname = forms.Field()
|
||||
|
||||
class Meta:
|
||||
model = Player
|
||||
fields = ["id", "teamsnap_member_id"]
|
||||
widgets = {
|
||||
"teamsnap_member_id": forms.Select(
|
||||
choices=(), attrs={"class": "form-control"}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
PlayerFormSet = formset_factory(PlayerForm, can_delete=True, extra=0)
|
||||
34
gamechanger/migrations/0001_initial.py
Normal file
34
gamechanger/migrations/0001_initial.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-07 16:50
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Preferences',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('managed_team_id', models.IntegerField()),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gamechanger_preferences', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Account',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('password', models.CharField(max_length=255)),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='gamechanger_account', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
29
gamechanger/migrations/0002_auto_20220607_1259.py
Normal file
29
gamechanger/migrations/0002_auto_20220607_1259.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-07 17:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gamechanger', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='preferences',
|
||||
name='managed_team_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='preferences',
|
||||
name='season_id',
|
||||
field=models.CharField(default=1, max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='preferences',
|
||||
name='team_id',
|
||||
field=models.CharField(default=1, max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
22
gamechanger/migrations/0003_player.py
Normal file
22
gamechanger/migrations/0003_player.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-08 12:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gamechanger', '0002_auto_20220607_1259'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Player',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('teamsnap_member_id', models.IntegerField()),
|
||||
('fname', models.CharField(max_length=30)),
|
||||
('lname', models.CharField(max_length=30)),
|
||||
],
|
||||
),
|
||||
]
|
||||
18
gamechanger/migrations/0004_alter_player_id.py
Normal file
18
gamechanger/migrations/0004_alter_player_id.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-08 12:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gamechanger', '0003_player'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='player',
|
||||
name='id',
|
||||
field=models.CharField(max_length=30, primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
||||
17
gamechanger/migrations/0005_alter_preferences_options.py
Normal file
17
gamechanger/migrations/0005_alter_preferences_options.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-10 21:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('gamechanger', '0004_alter_player_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='preferences',
|
||||
options={'verbose_name_plural': 'preferences'},
|
||||
),
|
||||
]
|
||||
0
gamechanger/migrations/__init__.py
Normal file
0
gamechanger/migrations/__init__.py
Normal file
36
gamechanger/models.py
Normal file
36
gamechanger/models.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.db.models import CharField, EmailField
|
||||
|
||||
from benchcoach.users.models import User
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class Account(models.Model):
|
||||
user = models.OneToOneField(
|
||||
User, on_delete=models.CASCADE, related_name="gamechanger_account"
|
||||
)
|
||||
email = EmailField()
|
||||
password = CharField(max_length=255)
|
||||
|
||||
|
||||
class Preferences(models.Model):
|
||||
user = models.OneToOneField(
|
||||
User, on_delete=models.CASCADE, related_name="gamechanger_preferences"
|
||||
)
|
||||
season_id = CharField(max_length=255)
|
||||
team_id = CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "preferences"
|
||||
|
||||
|
||||
class Player(models.Model):
|
||||
id = models.CharField(primary_key=True, max_length=30)
|
||||
teamsnap_member_id = models.IntegerField()
|
||||
fname = CharField(max_length=30)
|
||||
lname = CharField(max_length=30)
|
||||
|
||||
widgets = {
|
||||
"teamsnap_member_id": forms.Select(choices=(), attrs={"class": "form-control"}),
|
||||
}
|
||||
7
gamechanger/templates/gamechanger/form.html
Normal file
7
gamechanger/templates/gamechanger/form.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Save">
|
||||
</form>
|
||||
{% endblock content %}
|
||||
48
gamechanger/templates/gamechanger/roster.html
Normal file
48
gamechanger/templates/gamechanger/roster.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{# {% for player in roster %}#}
|
||||
{# <li class="list-group-item">#}
|
||||
{# {{ player.fname }} {{ player.lname }}#}
|
||||
{# </li>#}
|
||||
{# {% endfor %}#}
|
||||
<form method="post" action='{% url 'gamechanger_import_roster' %}' >
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
{{ formset.0.gamechanger_name.label }}
|
||||
</td>
|
||||
<td>
|
||||
{{ formset.0.teamsnap_name.label }}
|
||||
</td>
|
||||
<td>
|
||||
{{ formset.0.DELETE.label }}
|
||||
</td>
|
||||
</tr>
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ form.gamechanger_name.value }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form.teamsnap_member_id }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form.DELETE }}
|
||||
</td>
|
||||
{{ 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 }}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<button class="btn btn-success" type="submit">
|
||||
{# <i class="bi bi-arrow-right"></i>#}
|
||||
Import
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
8
gamechanger/templates/gamechanger/stats.html
Normal file
8
gamechanger/templates/gamechanger/stats.html
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% for id, stat_row in stats.items %}
|
||||
<p>
|
||||
{{ id }}: {{ stat_row }}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
3
gamechanger/tests.py
Normal file
3
gamechanger/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
17
gamechanger/urls.py
Normal file
17
gamechanger/urls.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import (
|
||||
AccountFormView,
|
||||
PreferencesFormView,
|
||||
lineup_submit,
|
||||
roster_import,
|
||||
stats,
|
||||
)
|
||||
|
||||
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("lineup/submit", lineup_submit, name="gamechanger_lineup_submit"),
|
||||
path("stats", stats, name="gamechanger_stats"),
|
||||
]
|
||||
0
gamechanger/utils/__init__.py
Normal file
0
gamechanger/utils/__init__.py
Normal file
139
gamechanger/utils/gamechanger.py
Normal file
139
gamechanger/utils/gamechanger.py
Normal file
@@ -0,0 +1,139 @@
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
|
||||
import requests
|
||||
import vcr
|
||||
|
||||
url = "https://gc.com/t/{season_id}/{team_id}/{page}"
|
||||
|
||||
|
||||
# TODO Remove VCR
|
||||
@vcr.use_cassette(
|
||||
"gamechanger/fixtures/authenticated_session.yaml", record_mode="new_episodes"
|
||||
)
|
||||
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))
|
||||
initialize_page_json = re.search(
|
||||
r'page.initialize\(\$.parseJSON\("(.*?)"\)', r.content.decode("unicode_escape")
|
||||
)
|
||||
m = initialize_page_json.group(1)
|
||||
return json.loads(m)
|
||||
|
||||
|
||||
# TODO Remove VCR
|
||||
@vcr.use_cassette("gamechanger/fixtures/stats.yaml", record_mode="new_episodes")
|
||||
def stats(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"
|
||||
r = authenticated_session.get(
|
||||
url.format(season_id=season_id, team_id=team_id, page=page)
|
||||
)
|
||||
|
||||
with vcr.use_cassette(
|
||||
"gamechanger/fixtures/roster.yaml", record_mode="new_episodes"
|
||||
):
|
||||
roster = scrape_page(season_id, team_id, "roster")
|
||||
id_lookup = {
|
||||
(p.get("fname"), p.get("lname")): p.get("player_id")
|
||||
for p in roster["roster"]
|
||||
}
|
||||
|
||||
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"),
|
||||
)
|
||||
]
|
||||
defensive_keys = [
|
||||
(i, key)
|
||||
for i, key in enumerate(
|
||||
my_list[1][my_list[0].index("Defensive Stats") :],
|
||||
start=my_list[0].index("Defensive Stats"),
|
||||
)
|
||||
]
|
||||
|
||||
stats = {}
|
||||
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
|
||||
180
gamechanger/views.py
Normal file
180
gamechanger/views.py
Normal file
@@ -0,0 +1,180 @@
|
||||
from django.http import HttpResponse, HttpResponseNotAllowed, HttpResponseServerError
|
||||
from django.shortcuts import render
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
from teamsnap.views import get_teamsnap_client
|
||||
|
||||
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
|
||||
success_url = "/"
|
||||
|
||||
def form_valid(self, form):
|
||||
# This method is called when valid form data has been POSTed.
|
||||
# It should return an HttpResponse.
|
||||
if form.data["user"] == str(self.request.user.id):
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_initial(self):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
initial = super().get_initial()
|
||||
initial["user"] = self.request.user
|
||||
initial["email"] = self.request.user.username
|
||||
# initial['managed_team_id']
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
class AccountFormView(FormView):
|
||||
template_name = "gamechanger/form.html"
|
||||
form_class = AccountForm
|
||||
success_url = "/"
|
||||
|
||||
def form_valid(self, form):
|
||||
# This method is called when valid form data has been POSTed.
|
||||
# It should return an HttpResponse.
|
||||
if form.data["user"] == str(self.request.user.id):
|
||||
form.save()
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_initial(self):
|
||||
"""
|
||||
Returns the initial data to use for forms on this view.
|
||||
"""
|
||||
initial = super().get_initial()
|
||||
initial["user"] = self.request.user
|
||||
initial["email"] = self.request.user.username
|
||||
# initial['managed_team_id']
|
||||
|
||||
return initial
|
||||
|
||||
|
||||
def roster_import(request):
|
||||
if request.method == "GET":
|
||||
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.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)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def stats(request):
|
||||
s = gamechanger.stats(request)
|
||||
return render(request, "gamechanger/stats.html", context={"stats": s})
|
||||
pass
|
||||
@@ -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)
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
<tbody>
|
||||
<tr class="align-top mx-1">
|
||||
{% for event_data in contexts %}
|
||||
|
||||
<td class="px-1">
|
||||
{% 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 %}
|
||||
</td>
|
||||
@@ -45,20 +44,20 @@
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
/* Run whatever you want */
|
||||
const postForms = document.querySelectorAll("[id^=form-lineup]");
|
||||
for (postForm of postForms) {
|
||||
function handleSubmit(postForm) {
|
||||
postForm.addEventListener("submit", e => {
|
||||
const postSubmits = document.querySelectorAll("[id^=submit-lineup]");
|
||||
for (postSubmit of postSubmits) {
|
||||
function handleSubmit(postSubmit) {
|
||||
postSubmit.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
formData = new FormData(postForm);
|
||||
fetch(postForm.action, {
|
||||
formData = new FormData(postSubmit.form);
|
||||
fetch(postSubmit.formAction, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(response => response)
|
||||
.then(data => {
|
||||
{#postForm.reset();#}
|
||||
document.querySelector("#popup-messages-content").innerHTML = `<div class="alert alert-dismissible alert-success" role="alert">
|
||||
<strong>Success!</strong> ${data.formatted_title} <strong>saved</strong>.
|
||||
<strong>Success!</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div> `
|
||||
})
|
||||
@@ -68,7 +67,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
handleSubmit(postForm)
|
||||
handleSubmit(postSubmit)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load static %}
|
||||
<div class="card mx-auto benchcoach-lineup" style="max-width: 455px" id="benchcoach-lineup-{{ event_id }}">
|
||||
<form method="post" action='{% url 'teamsnap_submit_lineup' team_id=event.data.team_id event_id=event.data.id %}' id="form-lineup-{{ event.data.id }}">
|
||||
<form method="post" id="form-lineup-{{ event.data.id }}">
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
<div class="border-bottom p-2">
|
||||
@@ -12,33 +12,45 @@
|
||||
<button class="btn btn-primary btn-sm py-0 m-1" onclick="importFromClipboard(this)" type="button"><i class="bi bi-arrow-90deg-down"></i></i><i class="bi bi-file-spreadsheet"></i> </button>
|
||||
</div>
|
||||
<div class="col text-end d-inline">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle btn-sm py-0 m-1" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-share"></i> Export
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li>
|
||||
<a class="dropdown-item" href="javascript:;" onclick="copyEmailTable(this, '{{ event.data.start_date|date:"D, F j, Y g:i A" }}, {{ event.data.location_name }}, ({% if event.data.game_type == 'Away' %}@{% endif %}{{ event.data.opponent_name }})', '{% for form in formset %}{{ form.member.data.email_addresses.0 }},{% endfor %}')">
|
||||
<i class="bi bi-envelope"></i> Generate Lineup Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" onclick="sendToClipboard(this)">
|
||||
<i class="bi bi-file-spreadsheet"></i> Sheet format to Clipboard
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-teamsnap btn-sm py-0 m-1" type="submit">
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
TeamSnap
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle btn-sm py-0 m-1" type="button" id="dropdownMenuButton1"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-share"></i> Export
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li>
|
||||
<a class="dropdown-item" href="javascript:;"
|
||||
onclick="copyEmailTable(this, '{{ event.data.start_date|date:"D, F j, Y g:i A" }}, {{ event.data.location_name }}, ({% if event.data.game_type == 'Away' %}@{% endif %}{{ event.data.opponent_name }})', '
|
||||
{% for form in formset %}{{ form.member.data.email_addresses.0 }},{% endfor %}')">
|
||||
<i class="bi bi-envelope"></i> Generate Lineup Email
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" onclick="sendToClipboard(this)">
|
||||
<i class="bi bi-file-spreadsheet"></i> Sheet format to Clipboard
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<button type="Submit" class="btn btn-teamsnap btn-sm py-0 m-1" formaction="{% url 'teamsnap_submit_lineup' team_id=event.data.team_id event_id=event.data.id %}" form="form-lineup-{{ event.data.id }}" id="submit-lineup-teamsnap-{{ event.data.id }}">
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
TeamSnap
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{% if request.user.gamechanger_account %}
|
||||
<button type="Submit" class="btn btn-gamechanger btn-sm py-0 m-1 text-nowrap" formaction="{% url 'gamechanger_lineup_submit' %}" form="form-lineup-{{ event.data.id }}" id="submit-lineup-gamechanger-{{ event.data.id }}">
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
GameChanger
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-0 m-0">
|
||||
<div>
|
||||
<div class="row m-0">
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -234,7 +234,12 @@ def submit_lineup(request, team_id, event_id):
|
||||
return HttpResponseServerError
|
||||
|
||||
|
||||
def multi_lineup_choose(request, team_id):
|
||||
def multi_lineup_choose(request, team_id=None):
|
||||
if not team_id:
|
||||
return redirect(
|
||||
"teamsnap_choose_multiple_lineups",
|
||||
team_id=request.user.teamsnap_preferences.managed_team_id,
|
||||
)
|
||||
from django.forms import formset_factory
|
||||
from pyteamsnap.api import Event
|
||||
|
||||
|
||||
@@ -5,9 +5,14 @@ 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"
|
||||
|
||||
|
||||
class Team(models.Model):
|
||||
id = models.IntegerField(primary_key=True)
|
||||
|
||||
@@ -6,15 +6,12 @@ from allauth.socialaccount.providers.oauth2.views import (
|
||||
OAuth2CallbackView,
|
||||
OAuth2LoginView,
|
||||
)
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
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
|
||||
@@ -112,7 +109,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))
|
||||
@@ -141,7 +139,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 (
|
||||
@@ -217,6 +215,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}
|
||||
|
||||
@@ -292,6 +300,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"]
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -350,12 +361,11 @@ def edit_lineup(request, event_ids, team_id):
|
||||
|
||||
|
||||
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:
|
||||
@@ -397,13 +407,9 @@ 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()
|
||||
|
||||
|
||||
def multi_lineup_choose(request, team_id):
|
||||
|
||||
Reference in New Issue
Block a user