start card and csv views
This commit is contained in:
280
benchcoach/templates/benchcoach/card.html
Normal file
280
benchcoach/templates/benchcoach/card.html
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
{% load static %}
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Gamecard</title>
|
||||||
|
<link rel="stylesheet" href="{% static "css/base.css"%}">
|
||||||
|
<link rel="stylesheet" href="{% static "css/paper.css"%}">
|
||||||
|
<style> @page { size: letter }
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inconsolata">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Open+Sans">
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
src: url("{% static 'fonts/OpenSans.ttf' %}") format("truetype-variations");
|
||||||
|
font-weight: 1 999;
|
||||||
|
font-stretch: 0% 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inconsolata';
|
||||||
|
src: url("{% static 'fonts/Inconsolata.ttf' %}") format("truetype-variations");
|
||||||
|
font-weight: 1 999;
|
||||||
|
font-stretch: 0% 100%
|
||||||
|
}
|
||||||
|
.whole-card {
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
height: 12.5cm;
|
||||||
|
width: 17.5cm;
|
||||||
|
outline: .8px dashed lightgray;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.half-card {
|
||||||
|
margin: auto;
|
||||||
|
padding: .1in;
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
card-left {
|
||||||
|
float: left;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
card-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
{#outline: solid grey;#}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-family: "Open Sans";
|
||||||
|
border-collapse: collapse;
|
||||||
|
empty-cells: show;
|
||||||
|
font-size:11px;
|
||||||
|
table-layout: fixed;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
/* box-sizing: content-box; */
|
||||||
|
border: 0.5px solid black;
|
||||||
|
height: 17px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 0px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gametitle {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: condensed;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homeaway {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: extra-expanded;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.numbercell {
|
||||||
|
font-family: "Inconsolata";
|
||||||
|
text-align: center;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statscell {
|
||||||
|
font-family: "Inconsolata";
|
||||||
|
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;
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.condensedNameCell{
|
||||||
|
width: 70px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-stretch: condensed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square {
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.available{
|
||||||
|
background-color: #B7E1CD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notavailable{
|
||||||
|
background-color: #F4C7C3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maybeavailable{
|
||||||
|
background-color: #B7E1CD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starting{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="letter">
|
||||||
|
<section class="sheet padding-10mm">
|
||||||
|
<div class="whole-card">
|
||||||
|
<div id="NW" class="half-card red">
|
||||||
|
<div class="content card-left">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="9" class="gametitle">{{ event.teamsnap_event.csv_event_title }}</th>
|
||||||
|
<th colspan="3" class="homeaway">
|
||||||
|
{% if event.home_team.name == user.profile.teamsnapsettings.managed_team.name %}
|
||||||
|
HOME
|
||||||
|
{% elif event.away_team.name == user.profile.teamsnapsettings.managed_team.name %}
|
||||||
|
AWAY
|
||||||
|
{% else %}
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="customcol"></td>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="numbercell">1</td>
|
||||||
|
<th class="numbercell">2</td>
|
||||||
|
<th class="numbercell">3</td>
|
||||||
|
<th class="numbercell">4</td>
|
||||||
|
<th class="numbercell">5</td>
|
||||||
|
<th class="numbercell">6</td>
|
||||||
|
<th class="numbercell">7</td>
|
||||||
|
<th class="numbercell">X</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for positioning in positionings_starting %}
|
||||||
|
{% if positioning.order == 0 %}<tr style="border: 1px solid;">
|
||||||
|
{% else %}<tr>{% endif %}
|
||||||
|
<td class="numbercell">{{ positioning.order }}</td>
|
||||||
|
<td class="customcol">{{ positioning.player.last_name }}</td>
|
||||||
|
<td class="numbercell">{{ positioning.player.jersey_number }}</td>
|
||||||
|
<td class="numbercell">{{ positioning.position|default_if_none:'' }}</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>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{% for line in empty_lines %}
|
||||||
|
<tr>
|
||||||
|
<td class="numbercell"></td>
|
||||||
|
<td class="customcol"></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 class="half-card">
|
||||||
|
<div class="content card-right">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="customcol"></td>
|
||||||
|
<th class="numbercell"></td>
|
||||||
|
<th class="numbercell">1</td>
|
||||||
|
<th class="numbercell">2</td>
|
||||||
|
<th class="numbercell">3</td>
|
||||||
|
<th class="numbercell">4</td>
|
||||||
|
<th class="numbercell">5</td>
|
||||||
|
<th class="numbercell">6</td>
|
||||||
|
<th class="numbercell">7</td>
|
||||||
|
<th class="numbercell">X</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for positioning in positionings %}
|
||||||
|
<tr>
|
||||||
|
<td class="numbercell"></td>
|
||||||
|
<td class="numbercell">{{ positioning.player.jersey_number }}</td>
|
||||||
|
<td class="customcol{% if positioning.event_availability == 2 %} available{% endif %}{% if positioning.event_availability == 0 %} notavailable{% endif %}{% if positioning.order or positioning.position %} starting{% endif %}">{{ positioning.player.last_name }}</td>
|
||||||
|
<td class="numbercell"><i class="bi bi-check-lg"></i></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>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
@@ -11,5 +11,7 @@ urlpatterns = [
|
|||||||
path('events/<int:pk>/lineup', login_required(views.EventDetailView.as_view()), name="event lineup"),
|
path('events/<int:pk>/lineup', login_required(views.EventDetailView.as_view()), name="event lineup"),
|
||||||
path('players/list/', login_required(views.PlayerListView.as_view()), name="player list"),
|
path('players/list/', login_required(views.PlayerListView.as_view()), name="player list"),
|
||||||
path('teams/list/', login_required(views.TeamListView.as_view()), name="team list"),
|
path('teams/list/', login_required(views.TeamListView.as_view()), name="team list"),
|
||||||
path('venues/list/', login_required(views.VenueListView.as_view()), name="venue list")
|
path('venues/list/', login_required(views.VenueListView.as_view()), name="venue list"),
|
||||||
|
path('events/<int:event_id>/card', login_required(views.lineupcard), name="lineup card"),
|
||||||
|
path('events/<int:event_id>/csv', login_required(views.csv_export), name="lineup csv")
|
||||||
]
|
]
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render, HttpResponse
|
||||||
from .models import Event, Team, Player, Positioning, Venue
|
from .models import Event, Team, Player, Positioning, Venue
|
||||||
from .forms import PositioningFormSet, TeamsnapEventForm
|
from .forms import PositioningFormSet, TeamsnapEventForm
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
|
import csv
|
||||||
|
|
||||||
|
|
||||||
class BenchCoachListView(ListView):
|
class BenchCoachListView(ListView):
|
||||||
@@ -136,3 +137,102 @@ def lineup_edit(request, event_id, active_tab='details'):
|
|||||||
"formset_dhd": formset_dhd,
|
"formset_dhd": formset_dhd,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def lineupcard(request, event_id):
|
||||||
|
previous_event = Event.objects.filter(id=event_id - 1).first()
|
||||||
|
|
||||||
|
event = Event.objects.get(id=event_id)
|
||||||
|
next_event = Event.objects.get(id=event_id + 1)
|
||||||
|
players = Player.objects.prefetch_related("availability_set", "positioning_set")
|
||||||
|
|
||||||
|
for player in players:
|
||||||
|
Positioning.objects.get_or_create(player_id=player.id, event_id=event_id)
|
||||||
|
|
||||||
|
qs = (
|
||||||
|
event.positioning_set.all()
|
||||||
|
.filter(player__availability__event=event_id, player__teamsnap_member__is_non_player=False)
|
||||||
|
.order_by("-player__availability__available", "player__last_name", "order")
|
||||||
|
.annotate(event_availability=F("player__availability__available"))
|
||||||
|
)
|
||||||
|
|
||||||
|
qs_starting = qs.filter(order__isnull=False).order_by("order")
|
||||||
|
|
||||||
|
details = {
|
||||||
|
"Away Team": event.away_team,
|
||||||
|
"Home Team": event.home_team,
|
||||||
|
"Date": event.start.date(),
|
||||||
|
"Time": event.start.time(),
|
||||||
|
"Venue": event.venue,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"benchcoach/card.html",
|
||||||
|
{
|
||||||
|
"title": "Lineup",
|
||||||
|
"event": event,
|
||||||
|
"details": details,
|
||||||
|
"previous_event": previous_event,
|
||||||
|
"next_event": next_event,
|
||||||
|
"positionings": qs,
|
||||||
|
"positionings_starting": qs_starting,
|
||||||
|
"empty_lines": range(14)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def csv_export(request, event_id):
|
||||||
|
response = HttpResponse(
|
||||||
|
content_type='text/csv',
|
||||||
|
headers={'Content-Disposition': f'attachment; filename=lineup-event-{event_id}.csv'},
|
||||||
|
)
|
||||||
|
previous_event = Event.objects.filter(id=event_id - 1).first()
|
||||||
|
|
||||||
|
event = Event.objects.get(id=event_id)
|
||||||
|
players = Player.objects.prefetch_related("availability_set", "positioning_set")
|
||||||
|
|
||||||
|
for player in players:
|
||||||
|
Positioning.objects.get_or_create(player_id=player.id, event_id=event_id)
|
||||||
|
|
||||||
|
qs = (
|
||||||
|
event.positioning_set.all()
|
||||||
|
.filter(player__availability__event=event_id, player__teamsnap_member__is_non_player=False)
|
||||||
|
.order_by("-player__availability__available", "player__last_name", "order")
|
||||||
|
.annotate(event_availability=F("player__availability__available"))
|
||||||
|
)
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
rows.append(event.teamsnap_event.csv_event_title) # 2
|
||||||
|
rows.append(event.venue.name) # 3
|
||||||
|
[rows.append('') for i in range(3)] #4-6
|
||||||
|
p = qs.filter(position='P').first()
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") #7
|
||||||
|
[rows.append('') for i in range(3)] #8-10
|
||||||
|
for pos in ['C', '1B', '2B', '3B', 'SS', 'LF', 'CF', 'RF', 'DH']: #11-19
|
||||||
|
p = qs.filter(position=pos).first()
|
||||||
|
if p:
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
||||||
|
else:
|
||||||
|
rows.append('')
|
||||||
|
ehs = qs.filter(position='EH')
|
||||||
|
if len(ehs) > 0:
|
||||||
|
p=qs.filter(position='EH')[0]
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 20
|
||||||
|
else:
|
||||||
|
rows.append('')
|
||||||
|
if len(ehs) > 1:
|
||||||
|
p=qs.filter(position='EH')[1]
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 21
|
||||||
|
else:
|
||||||
|
rows.append('')
|
||||||
|
rows.append('') #22
|
||||||
|
p=qs.filter(position__isnull=False, order=0).first()
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}") # 23
|
||||||
|
rows.append('')
|
||||||
|
for p in qs.filter(order__gt=0).order_by('order'):
|
||||||
|
rows.append(f"{p.player.last_name}, {p.player.first_name}")
|
||||||
|
|
||||||
|
writer = csv.writer(response)
|
||||||
|
for row in rows:
|
||||||
|
writer.writerow([row])
|
||||||
|
return response
|
||||||
38
benchcoachproject/static/css/paper.css
Normal file
38
benchcoachproject/static/css/paper.css
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@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.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 }
|
||||||
|
|
||||||
|
/** 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.letter, body.legal { width: 216mm }
|
||||||
|
body.letter.landscape { width: 280mm }
|
||||||
|
body.legal.landscape { width: 357mm }
|
||||||
|
}
|
||||||
1
benchcoachproject/static/css/paper.min.css
vendored
Normal file
1
benchcoachproject/static/css/paper.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@page{margin:0}body{margin:0}.sheet{margin:0;overflow:hidden;position:relative;box-sizing:border-box;page-break-after:always}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.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}.sheet.padding-10mm{padding:10mm}.sheet.padding-15mm{padding:15mm}.sheet.padding-20mm{padding:20mm}.sheet.padding-25mm{padding:25mm}@media screen{body{background:#e0e0e0}.sheet{background:#fff;box-shadow:0 .5mm 2mm rgba(0,0,0,.3);margin:5mm auto}}@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.legal,body.letter{width:216mm}body.letter.landscape{width:280mm}body.legal.landscape{width:357mm}}
|
||||||
BIN
benchcoachproject/static/fonts/Inconsolata.ttf
Normal file
BIN
benchcoachproject/static/fonts/Inconsolata.ttf
Normal file
Binary file not shown.
BIN
benchcoachproject/static/fonts/OpenSans-Italic.ttf
Normal file
BIN
benchcoachproject/static/fonts/OpenSans-Italic.ttf
Normal file
Binary file not shown.
BIN
benchcoachproject/static/fonts/OpenSans.ttf
Normal file
BIN
benchcoachproject/static/fonts/OpenSans.ttf
Normal file
Binary file not shown.
Reference in New Issue
Block a user