split gamecard into its own app, add models to teamsnap for custom fields (images)

This commit is contained in:
2022-06-09 07:46:53 -05:00
parent aa897f6f49
commit abdab2d30b
56 changed files with 273 additions and 58 deletions

0
gamecard/__init__.py Normal file
View File

6
gamecard/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class GamecardConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "gamecard"

View File

View File

@@ -0,0 +1,152 @@
@import url("../../css/paper.css");
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
@import url('../fonts/vera/bitstreamvera.css');
@import url('../fonts/verdana/verdanapro.css');
@import url('../fonts/m+1m/m+1m.css');
@import url('../fonts/dinpro/dinpro.css');
@import url('../fonts/refrigerator/refigerator.css');
@import url('https://fonts.googleapis.com/css2?family=Pacifico&display=swap');
@page { size: B5; }
* {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: "VerdanaPro";
/*font-family: -apple-system;*/
/*font-weight: bold;*/
font-size: 10px;
}
.whole-card {
margin:0;
padding:0;
height: 12.5cm;
width: 17.5cm;
outline: solid black;
margin: auto;
display: flex;
}
.half-card {
margin:0;
padding: .1in;
height: 100%;
width: 50%;
}
card-left {
float: left;
}
card-right {
float: right;
}
.content {
height: 100%;
width: 100%;
/* padding: .1in; */
outline: solid grey;
}
table {
border-collapse: collapse;
empty-cells: show;
font-size:11px;
table-layout: fixed;
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
width: 100%;
}
th, tr, td {
/* box-sizing: content-box; */
border: 0.5px solid black;
height: 17px;
text-overflow: ellipsis;
overflow-x: hidden;
}
.gametitle {
font-weight: normal;
text-transform: uppercase;
font-stretch: condensed;
}
.homeaway {
text-transform: uppercase;
font-stretch: normal;
font-weight: bolder;
}
.numbercell {
font-family: "m+1m";
text-align: center;
font-stretch: condensed;
font-size: 10px;
}
.statscell {
font-family: "m+1m";
text-align: center;
font-stretch: extra-condensed;
font-size: 9px;
width: 60px;
}
tr:nth-child(even) {background-color: #f2f2f2;}
th{
background: black;
color: white;
border: none;
}
.customcol{
width: 120px;
text-transform: uppercase;
}
.condensedNameCell{
width: 70px;
text-transform: uppercase;
font-stretch: condensed;
}
.square {
height: 14px;
width: 14px;
}
.available-status-code-1{
background-color: #B7E1CD;
}
.available-status-code-0{
background-color: #F4C7C3;
}
.available-status-code-2{
background-color: #ACC9FE;
}
.starting{
font-weight: bold;
}
.grid-container {
display: flex;
grid-template-columns: auto auto auto;
/*background-color: #2196F3;*/
/*padding: 10px;*/
}
.grid-item {
/*background-color: rgba(255, 255, 255, 0.8);*/
/*border: 1px solid rgba(0, 0, 0, 0.8);*/
/*padding: 20px;*/
font-size: 30px;
text-align: center;
}

View File

@@ -0,0 +1,52 @@
@page { margin: 0 }
body { margin: 0 }
.sheet {
margin: 0;
overflow: hidden;
position: relative;
box-sizing: border-box;
page-break-after: always;
}
/** Paper sizes **/
body.A3 .sheet { width: 297mm; height: 419mm }
body.A3.landscape .sheet { width: 420mm; height: 296mm }
body.A4 .sheet { width: 210mm; height: 296mm }
body.A4.landscape .sheet { width: 297mm; height: 209mm }
body.A5 .sheet { width: 148mm; height: 209mm }
body.A5.landscape .sheet { width: 210mm; height: 147mm }
body.b5 .sheet { width: 176mm; height: 250mm }
body.b5.landscape .sheet { width: 250mm; height: 176mm }
body.letter .sheet { width: 216mm; height: 279mm }
body.letter.landscape .sheet { width: 280mm; height: 215mm }
body.legal .sheet { width: 216mm; height: 356mm }
body.legal.landscape .sheet { width: 357mm; height: 215mm }
/** Padding area **/
.sheet.padding-10mm { padding: 10mm }
.sheet.padding-15mm { padding: 15mm }
.sheet.padding-20mm { padding: 20mm }
.sheet.padding-25mm { padding: 25mm }
/** For screen preview **/
@media screen {
body { background: #e0e0e0 }
.sheet {
background: white;
box-shadow: 0 .5mm 2mm rgba(0,0,0,.3);
margin: 5mm auto;
}
}
/** Fix for Chrome issue #273306 **/
@media print {
body.A3.landscape { width: 420mm }
body.A3, body.A4.landscape { width: 297mm }
body.A4, body.A5.landscape { width: 210mm }
body.A5 { width: 148mm }
body.b5 { width: 190mm }
body.b5.landscape { width: 250mm }
body.letter, body.legal { width: 216mm }
body.letter.landscape { width: 280mm }
body.legal.landscape { width: 357mm }
}

View File

@@ -0,0 +1,38 @@
@font-face {
font-family: 'm+1m';
src: url('mplus-1m-bold-webfont.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'm+1m';
src: url('mplus-1m-light-webfont.woff') format('woff');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'm+1m';
src: url('mplus-1m-medium-webfont.woff') format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'm+1m';
src: url('mplus-1m-regular-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'm+1m';
src: url('mplus-1m-thin-webfont.woff') format('woff');
font-weight: 100;
font-style: normal;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,68 @@
@font-face {
font-family: 'Bitstream Vera Sans';
src: url('Vera-Bold-webfont.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Bitstream Vera Sans';
src: url('Vera-Bold-Italic-webfont.woff') format('woff');
font-weight: bold;
font-style: oblique;
}
@font-face {
font-family: 'Bitstream Vera Sans';
src: url('Vera-Italic-webfont.woff') format('woff');
font-weight: normal;
font-style: oblique;
}
@font-face {
font-family: 'Bitstream Vera Sans';
src: url('Vera-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Bitstream Vera Sans Mono';
src: url('VeraMono-Bold-webfont.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Bitstream Vera Sans Mono';
src: url('VeraMono-Bold-Italic-webfont.woff') format('woff');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Bitstream Vera Sans Mono';
src: url('VeraMono-Italic-webfont.woff') format('woff');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Bitstream Vera Sans Mono';
src: url('VeraMono-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Bitstream Vera Sans Mono';
src: url('VeraMono-webfont.woff') format('woff');
font-weight: 800;
font-style: normal;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,123 @@
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-Black.ttf') format('truetype');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-BlackItalic.ttf') format('truetype');
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-BoldItalic.ttf') format('truetype');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondBlack.ttf') format('truetype');
font-stretch: condensed;
font-weight: 800;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondBlackItalic.ttf') format('truetype');
font-stretch: condensed;
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondBold.ttf') format('truetype');
font-stretch: condensed;
font-weight: bold;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondBoldItalic.ttf') format('truetype');
font-stretch: condensed;
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondItalic.ttf') format('truetype');
font-stretch: condensed;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondLight.ttf') format('truetype');
font-stretch: condensed;
font-weight: 300;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondLightItalic.ttf') format('truetype');
font-stretch: condensed;
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondRegular.ttf') format('truetype');
font-stretch: condensed;
font-style: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondSemiBold.ttf') format('truetype');
font-stretch: condensed;
font-weight: 600;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-CondSemiBoldItalic.ttf') format('truetype');
font-stretch: condensed;
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-Italic.ttf') format('truetype');
font-style: italic;
font-weight: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-Light.ttf') format('truetype');
font-weight: 300;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-LightItalic.ttf') format('truetype');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-SemiBold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'VerdanaPro';
src: url('VerdanaPro-SemiBoldItalic.ttf') format('truetype');
font-weight: 600;
font-style: italic;
}

View File

@@ -0,0 +1,191 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="{% static 'css/gamecard.css' %}">
<title>Title</title>
</head>
<body class="b5">
<section class="sheet">
<div class="whole-card">
<div class="half-card">
<div class="content card-left">
<table>
<thead>
<tr>
<th colspan="8" class="gametitle">
{{ event.data.formatted_title }} {{ event.data.start_date|date:'m/d/Y g:i A' }}
{# G#01 at Browns 05/01/2021 12:30 PM#}
</th>
<th class="homeaway" colspan="4">{{ event.data.game_type }}</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th class="numbercell">
</th>
<th class="customcol">
</th>
<th class="numbercell">
</th>
<th class="numbercell">
</th>
<th class="numbercell">1
</th>
<th class="numbercell">2
</th>
<th class="numbercell">3
</th>
<th class="numbercell">4
</th>
<th class="numbercell">5
</th>
<th class="numbercell">6
</th>
<th class="numbercell">7
</th>
<th class="numbercell">X
</th>
</tr>
</thead>
<tbody>
{% for member in members_startinglineup %}
<tr>
<td class="numbercell">{{ member.lineup_entry.sequence | add:"1" }}</td>
<td class="customcol">{{ member.member.last_name }}</td>
<td class="numbercell">{{ member.member.jersey_number }}</td>
<td class="numbercell">{{ member.lineup_entry.label }}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
<table>
<tbody>
{% for member in members_startingpositiononly %}
<tr>
<td class="numbercell"></td>
<td class="customcol">{{ member.member.last_name }}</td>
<td class="numbercell">{{ member.member.jersey_number }}</td>
<td class="numbercell">{{ member.lineup_entry.label }}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="half-card">
<div class="content card-right">
<table class="tg">
<thead>
<tr>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="condensedNameCell">Available</th>
<th class="statscell">AVG/OBP/SLG:PA</th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
<th class="numbercell"></th>
</tr>
</thead>
<tbody>
{% for member in members %}
<tr>
<td class="numbercell"></td>
<td class="numbercell available-status-code-{{ member.availability.status_code }}">{{ member.member.jersey_number }}</td>
<td class="condensedNameCell available-status-code-{{ member.availability.status_code }}">{{ member.member.last_name }}</td>
<td class="statscell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
<td class="numbercell"></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="whole-card">
<div class="half-card">
<div class="content card-left"></div>
</div>
<div class="half-card">
<div class="content card-right">
<div>
<table>
<thead>
<tr>
<th class="numbercell" style="background-color: #323669">
{{ event.data.start_date|date:"D, F j, Y g:i A" }}
</th>
</tr>
<tr>
<th class="numbercell" style="background-color: #323669">
{{ event.data.location_name }}
</th>
</tr>
<tr>
<th class="numbercell" style="background-color: lightgray">
</th>
</tr>
</thead>
</table>
<div>
<div class="" width="100%">
<img src="{{ ts_team.logo.url }}"
height="120px"
>
</div>
<div class="" width="100%" style="text-align: center;font-size: xxx-large; font-family: Pacifico">
VS.
</div>
<div class="" width="100%" style="text-align: right">
<img src="{{ ts_opponent.logo.url }}"
width="120px"
>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</body>
</html>

3
gamecard/tests.py Normal file
View File

@@ -0,0 +1,3 @@
# from django.test import TestCase
# Create your tests here.

7
gamecard/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from .views import gamecard
urlpatterns = [
path("<int:team_id>/event/<int:event_id>/gamecard/", gamecard, name="gamecard")
]

View File

109
gamecard/views.py Normal file
View File

@@ -0,0 +1,109 @@
from django.shortcuts import render
from teamsnap.models import Opponent, Team
from teamsnap.utils import get_teamsnap_client
def gamecard(request, team_id, event_id):
import re
from pyteamsnap.api import (
Availability,
AvailabilitySummary,
Event,
EventLineup,
EventLineupEntry,
Member,
)
client = get_teamsnap_client(request)
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) and i.data["id"] == event_id
][0]
ts_availabilities = Availability.search(client, event_id=ts_event.data["id"])
ts_lineup_entries = EventLineupEntry.search(client, event_id=event_id)
if ts_lineup_entries:
# ts_lineup = EventLineup.get(
# client, id=ts_lineup_entries[0].data["event_lineup_id"]
# )
pass
else:
# ts_lineup = EventLineup.search(client, event_id=event_id)
pass
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:
if not member.data["is_non_player"]:
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, 0: 2, 2: 1, 1: 0}.get( # No Response # No # Maybe # Yes
d["availability"].get("status_code")
),
d["member"].get("last_name"),
),
)
members_startinglineup = []
members_startingpositiononly = []
for member in members:
if re.search(
r"([A-Z0-9]+)(?:\s+\[(.*)\])?", member["lineup_entry"].get("label", "")
):
position, position_note = re.search(
r"([A-Z0-9]+)(?:\s+\[(.*)\])?", member["lineup_entry"].get("label", "")
).groups()
else:
position, position_note = ("", "")
position_only = position_note == "PO"
if position_only:
member["lineup_entry"]["label"] = position
if member["lineup_entry"].get("id") and not position_only:
members_startinglineup.append(member)
elif member["lineup_entry"].get("id") and position_only:
members_startingpositiononly.append(member)
members_startinglineup = sorted(
members_startinglineup,
key=lambda d: d.get("lineup_entry", {}).get("sequence", 100),
)
context = {
"event": ts_event,
"members": members,
"members_startinglineup": members_startinglineup,
"members_startingpositiononly": members_startingpositiononly,
"ts_team": Team.objects.get(id=team_id),
"ts_opponent": Opponent.objects.get(id=ts_event.data["opponent_id"]),
}
return render(request, "gamecard/gamecard.html", context=context)