first init of this template.
@@ -242,7 +242,7 @@ class TestVenueViews(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_venue_list(self):
|
||||
response = self.client.get(reverse("venues list"))
|
||||
response = self.client.get(reverse("locations list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(
|
||||
{"id": 1, "title": "Chesapeake Racetrack and Ballpark"},
|
||||
|
||||
@@ -8,7 +8,7 @@ urlpatterns = [
|
||||
path('events/<int:event_id>/', login_required(views.event), name="event"),
|
||||
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('venues/list/', login_required(views.VenueListView.as_view()), name="venue list"),
|
||||
path('locations/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', views.csv_export, name="lineup csv"),
|
||||
path('events/<int:event_id>/<str:active_tab>', login_required(views.event), name="event")
|
||||
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/static/mstile-150x150.png"/>
|
||||
<TileColor>#323669</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
BIN
benchcoachproject/static/teamsnap/ig/favicon/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
benchcoachproject/static/teamsnap/ig/favicon/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
benchcoachproject/static/teamsnap/ig/favicon/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
benchcoachproject/static/teamsnap/ig/favicon/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3438 6215 c-2 -1 -59 -5 -128 -9 -69 -3 -136 -7 -150 -10 -14 -2
|
||||
-47 -7 -75 -10 -394 -45 -765 -221 -900 -428 -61 -92 -81 -110 -181 -158 -60
|
||||
-29 -95 -54 -133 -96 -50 -55 -190 -259 -280 -409 -39 -65 -192 -317 -396
|
||||
-650 -47 -77 -135 -216 -195 -310 -60 -93 -112 -176 -116 -183 -4 -8 -34 -56
|
||||
-68 -107 -34 -52 -115 -176 -181 -277 -99 -153 -318 -493 -362 -564 -44 -70
|
||||
-147 -250 -174 -304 -31 -59 -34 -73 -33 -150 0 -98 23 -165 89 -265 44 -66
|
||||
187 -214 280 -291 33 -27 62 -52 65 -55 3 -3 73 -57 155 -119 433 -328 650
|
||||
-537 768 -740 14 -25 46 -94 71 -154 50 -121 64 -141 94 -132 88 28 293 365
|
||||
376 617 34 105 40 131 52 224 4 28 7 51 9 53 1 1 10 -39 19 -90 39 -205 152
|
||||
-362 290 -402 30 -9 112 -55 195 -109 l142 -94 747 3 c411 1 776 5 812 8 97 8
|
||||
147 27 256 95 55 34 140 82 189 106 50 23 109 59 132 79 61 53 108 155 137
|
||||
296 l23 115 12 -57 c6 -32 16 -84 22 -115 54 -276 345 -764 435 -729 16 6 45
|
||||
68 76 164 69 214 291 461 665 738 276 205 354 266 445 352 225 214 318 376
|
||||
313 543 -2 70 -11 90 -119 267 -95 157 -186 299 -451 702 -282 429 -473 732
|
||||
-803 1275 -303 499 -392 632 -466 698 -30 26 -144 87 -165 87 -20 0 -73 55
|
||||
-88 91 -40 101 -343 322 -525 383 -64 21 -232 63 -293 72 -27 5 -52 9 -55 9
|
||||
-3 1 -32 5 -65 9 -33 5 -73 10 -90 12 -16 3 -100 7 -185 10 -85 3 -163 7 -172
|
||||
8 -9 2 -18 3 -20 1z m272 -361 c41 -8 92 -19 113 -25 142 -38 280 -156 263
|
||||
-224 -3 -13 -24 -61 -46 -106 -23 -50 -35 -88 -30 -93 5 -5 27 -4 54 4 86 24
|
||||
79 -1 -19 -64 -133 -87 -258 -249 -332 -431 -38 -93 -32 -111 25 -72 21 15 41
|
||||
27 44 27 4 0 -11 -51 -31 -112 -21 -62 -44 -144 -50 -183 -12 -73 -20 -202
|
||||
-13 -209 2 -3 51 18 108 46 57 28 107 48 110 45 10 -9 -15 -43 -90 -122 -67
|
||||
-71 -81 -93 -47 -76 51 28 421 160 450 161 15 0 -38 -40 -132 -101 -281 -181
|
||||
-420 -281 -407 -291 191 -146 660 -634 788 -820 30 -44 55 -114 46 -129 -3 -5
|
||||
-20 -6 -39 -3 -42 6 -55 -13 -55 -81 0 -121 56 -296 171 -533 91 -188 110
|
||||
-243 114 -329 7 -133 -104 -466 -169 -508 -35 -23 -117 -16 -154 14 -65 51
|
||||
-181 102 -451 196 -91 31 -196 71 -234 88 l-70 31 6 61 c8 87 46 167 95 203
|
||||
22 16 91 50 154 77 179 77 248 128 283 212 32 76 14 130 -70 221 -164 178
|
||||
-316 234 -615 226 -63 -1 -137 -8 -165 -14 -27 -7 -61 -14 -75 -16 -47 -9
|
||||
-155 -57 -217 -96 -111 -70 -175 -161 -179 -255 -6 -122 35 -168 238 -270 204
|
||||
-102 282 -180 295 -293 4 -36 2 -40 -34 -63 -46 -29 -134 -64 -307 -124 -249
|
||||
-85 -406 -152 -406 -172 0 -16 -68 -41 -113 -41 -74 0 -96 30 -158 207 -52
|
||||
149 -67 221 -67 317 0 105 29 206 92 316 115 201 169 353 180 506 7 86 -7 109
|
||||
-64 109 -54 0 -55 6 -4 109 33 65 64 110 139 195 158 181 491 517 597 602 63
|
||||
50 75 65 66 76 -17 21 -113 92 -198 147 -205 134 -330 217 -330 222 0 7 57 -4
|
||||
85 -16 11 -4 61 -22 110 -40 50 -17 99 -36 110 -41 11 -5 38 -15 60 -23 22 -8
|
||||
58 -22 80 -31 l40 -17 -20 23 c-11 13 -41 46 -67 74 -62 67 -88 101 -82 108
|
||||
10 9 63 -11 140 -52 42 -23 78 -41 81 -41 8 0 -4 176 -16 235 -5 28 -26 99
|
||||
-45 159 -20 60 -36 110 -36 112 0 10 18 2 36 -15 11 -10 31 -22 44 -26 22 -7
|
||||
23 -5 16 26 -9 46 -78 185 -135 272 -59 89 -134 164 -223 223 -99 66 -104 85
|
||||
-18 64 28 -7 54 -9 57 -6 4 3 -12 48 -35 99 -53 116 -54 133 -14 183 69 86
|
||||
168 134 347 169 87 17 335 16 425 -1z m-1377 -1583 c78 -31 138 -63 144 -76 8
|
||||
-22 -23 -43 -74 -51 -24 -3 -43 -12 -43 -18 0 -20 62 -74 105 -92 50 -21 136
|
||||
-21 194 0 43 16 108 58 123 80 5 8 8 8 8 0 0 -17 -60 -107 -88 -133 -108 -96
|
||||
-284 -79 -363 36 -53 76 -107 231 -93 267 6 15 23 12 87 -13z m2410 -3 c6 -20
|
||||
-20 -143 -39 -180 -65 -126 -190 -192 -307 -162 -82 21 -131 65 -182 163 l-17
|
||||
35 43 -35 c56 -44 78 -55 131 -65 87 -15 167 2 222 48 53 43 46 66 -25 88 -31
|
||||
9 -61 20 -68 24 -18 12 4 33 47 46 20 7 51 18 67 25 49 21 59 23 92 24 17 1
|
||||
33 -5 36 -11z m383 -978 c12 -149 7 -382 -11 -510 -2 -14 -6 -52 -10 -85 -4
|
||||
-33 -9 -62 -10 -65 -2 -3 -6 -34 -10 -70 -4 -36 -9 -67 -12 -70 -2 -3 -12 5
|
||||
-21 17 -9 12 -50 60 -90 107 l-73 85 45 138 c25 76 75 232 112 346 36 115 68
|
||||
207 69 205 2 -2 7 -46 11 -98z m-3208 -22 c16 -46 65 -191 107 -322 88 -271
|
||||
88 -241 -2 -343 -103 -117 -113 -127 -114 -112 0 8 -2 23 -4 34 -2 11 -7 45
|
||||
-10 75 -4 30 -8 69 -10 85 -2 17 -7 59 -10 95 -4 36 -8 81 -11 100 -5 41 -4
|
||||
359 1 410 2 19 4 41 4 48 2 32 22 4 49 -70z m1752 -452 c81 -9 181 -39 240
|
||||
-72 l45 -25 -40 7 c-83 15 -129 11 -176 -13 -66 -34 -225 -46 -377 -29 -62 7
|
||||
-83 13 -132 41 -25 14 -100 14 -165 0 -27 -5 -27 -4 10 16 134 74 351 101 595
|
||||
75z m265 -1150 c138 -8 161 -12 169 -33 10 -25 -11 -49 -92 -111 -91 -68 -151
|
||||
-99 -207 -108 -80 -12 -488 -11 -579 3 -96 14 -135 32 -244 114 -81 61 -107
|
||||
93 -88 108 19 16 117 27 268 30 73 1 134 4 135 5 5 4 524 -2 638 -8z"/>
|
||||
<path d="M3065 3166 c-23 -23 -26 -60 -9 -88 17 -27 121 21 131 60 12 51 -78
|
||||
72 -122 28z"/>
|
||||
<path d="M3453 3149 c-57 -21 -47 -89 16 -111 49 -16 144 -16 165 1 33 28 9
|
||||
76 -49 99 -41 16 -105 21 -132 11z"/>
|
||||
<path d="M3837 3141 c-37 -18 -44 -38 -23 -66 20 -27 95 -28 125 -1 26 24 27
|
||||
41 1 63 -26 22 -63 24 -103 4z"/>
|
||||
<path d="M2788 2934 c-28 -8 -57 -26 -72 -45 -29 -35 -32 -51 -12 -68 30 -25
|
||||
141 28 176 83 22 35 -24 51 -92 30z"/>
|
||||
<path d="M4140 2860 c-27 -50 33 -118 126 -144 14 -4 27 1 39 14 17 19 17 21
|
||||
1 53 -23 42 -96 97 -129 97 -16 0 -30 -8 -37 -20z"/>
|
||||
<path d="M2579 2469 c-43 -43 11 -177 83 -202 44 -15 78 61 56 125 -9 26 -61
|
||||
87 -76 89 -4 0 -15 2 -25 4 -10 2 -27 -5 -38 -16z"/>
|
||||
<path d="M4333 2428 c-54 -14 -109 -111 -98 -171 5 -27 50 -67 75 -67 50 0 99
|
||||
89 94 174 -2 53 -29 76 -71 64z"/>
|
||||
<path d="M3933 2113 c-34 -7 -74 -59 -68 -91 5 -28 37 -61 61 -63 8 -1 20 -2
|
||||
27 -3 21 -4 66 23 83 49 13 20 14 30 4 59 -6 20 -15 38 -19 40 -13 8 -65 13
|
||||
-88 9z"/>
|
||||
<path d="M3033 2083 c-34 -7 -42 -51 -13 -75 26 -21 109 -25 128 -6 40 40 -39
|
||||
95 -115 81z"/>
|
||||
<path d="M2765 1984 c-6 -1 -27 -5 -49 -9 -48 -8 -86 -45 -86 -83 0 -35 12
|
||||
-42 71 -36 89 8 127 43 120 110 -1 11 -37 23 -56 18z"/>
|
||||
<path d="M4314 1938 c-12 -5 -27 -21 -33 -35 -16 -35 13 -83 51 -83 15 0 30 5
|
||||
33 10 4 6 10 8 14 5 5 -2 21 7 36 21 21 20 26 30 20 52 -4 15 -12 30 -18 34
|
||||
-17 11 -78 9 -103 -4z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/static/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/static/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
BIN
benchcoachproject/static/teamsnap/ig/graphics/hounds-insta.jpg
Normal file
|
After Width: | Height: | Size: 174 KiB |
137
benchcoachproject/static/teamsnap/js/lineup-table.js
Normal file
@@ -0,0 +1,137 @@
|
||||
function positionSelectChanged(elem) {
|
||||
let row = elem.parentElement.parentElement
|
||||
let original_table = elem.parentElement.parentElement.parentElement
|
||||
let lineup_table = document.getElementById('lineup')
|
||||
let bench_table = document.getElementById('bench')
|
||||
let dhd_table = document.getElementById('dhd')
|
||||
let player_order = row.querySelector('[id^="player-order"]')
|
||||
let player_available = row.querySelector('[id^="player-availability"]')
|
||||
|
||||
console.dir(original_table == bench_table)
|
||||
if (elem.value == "P" && original_table == bench_table ) {
|
||||
dhd_table.appendChild(row)
|
||||
player_order.classList.remove('d-none')
|
||||
// player_available.classList.add('d-none')
|
||||
refresh_lineup_order()
|
||||
}
|
||||
else if (elem.value && original_table == dhd_table ) {
|
||||
dhd_table.appendChild(row)
|
||||
player_order.classList.remove('d-none')
|
||||
// player_available.classList.add('d-none')
|
||||
refresh_lineup_order()
|
||||
}
|
||||
else if (elem.value) {
|
||||
lineup_table.appendChild(row)
|
||||
player_order.classList.remove('d-none')
|
||||
// player_available.classList.add('d-none')
|
||||
refresh_lineup_order()
|
||||
}
|
||||
else {
|
||||
bench_table.prepend(row)
|
||||
// player_order.classList.add('d-none')
|
||||
player_available.classList.remove('d-none')
|
||||
}
|
||||
}
|
||||
|
||||
function colorPositions(){
|
||||
var player_rows = [];
|
||||
player_rows.push.apply(player_rows, document.getElementById('table-players-lineup').querySelectorAll('tr'));
|
||||
player_rows.push.apply(player_rows, document.getElementById('table-players-bench').querySelectorAll('tr'));
|
||||
var label_value_array = []
|
||||
player_rows.forEach(function (player_row, index){
|
||||
console.dir(player_row)
|
||||
if (player_row.querySelector('[name$="label"]')){
|
||||
console.dir(player_row.querySelector('select[name$="label"]'))
|
||||
console.dir(player_row.querySelector('select[name$="label"]').value)
|
||||
label_value_array.push(
|
||||
player_row.querySelector('select[name$="label"]').value)
|
||||
}
|
||||
}
|
||||
)
|
||||
document.querySelectorAll('[id^="position-status"]').forEach(function(position_status,index){
|
||||
if (label_value_array.includes(position_status.innerHTML)){
|
||||
if (position_status.classList.contains("text-danger")){
|
||||
position_status.classList.remove('text-danger')
|
||||
}
|
||||
position_status.classList.add('text-success')
|
||||
} else {
|
||||
if (position_status.classList.contains("text-success")){
|
||||
position_status.classList.remove('text-success')
|
||||
}
|
||||
position_status.classList.add('text-danger')
|
||||
}
|
||||
})
|
||||
console.dir(label_value_array)
|
||||
}
|
||||
|
||||
function refresh_lineup_order (){
|
||||
var player_rows = document.getElementById('table-players-lineup').querySelectorAll('tr')
|
||||
for (let i = 0; i < player_rows.length; i++) {
|
||||
var player_order = player_rows[i].querySelector('[id^="sequence"]')
|
||||
var form_element_order = player_rows[i].querySelector('[id$="sequence"]')
|
||||
player_order.innerText = parseInt(player_rows[i].dataset.order)
|
||||
player_rows[i].dataset.order = i
|
||||
form_element_order.value = i
|
||||
player_order.innerHTML = i+1
|
||||
}
|
||||
var player_rows = document.getElementById('table-players-bench').querySelectorAll('tr')
|
||||
for (let i = 0; i < player_rows.length; i++) {
|
||||
var player_order = player_rows[i].querySelector('[id^="player-order"]')
|
||||
var form_element_order = player_rows[i].querySelector('[id$="sequence"]')
|
||||
player_rows[i].dataset.order = null
|
||||
form_element_order.value = null
|
||||
player_order.innerHTML = null
|
||||
}
|
||||
}
|
||||
|
||||
var lineup = new Sortable.create(
|
||||
document.getElementById('tbody-players-lineup'), {
|
||||
animation: 150,
|
||||
handle: ".drag-handle",
|
||||
ghostClass:"ghost",
|
||||
group:{
|
||||
put:true,
|
||||
pull:true
|
||||
},
|
||||
onAdd: function (/**Event*/evt) {
|
||||
// Add to Lineup
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
var player_order = itemEl.querySelector('[id^="sequence-member"]')
|
||||
var player_available =itemEl.querySelector('[class^="member-availability-status"]')
|
||||
refresh_lineup_order()
|
||||
if (player_order.classList.contains('d-none')){
|
||||
player_order.classList.remove('d-none')
|
||||
}
|
||||
// player_available.classList.add('d-none')
|
||||
},
|
||||
onUpdate: function (/**Event*/evt) {
|
||||
console.log('update to lineup')
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
refresh_lineup_order()
|
||||
},
|
||||
});
|
||||
|
||||
var bench = new Sortable.create(
|
||||
document.getElementById('tbody-players-bench'), {
|
||||
animation: 150,
|
||||
ghostClass:"ghost",
|
||||
sort: false,
|
||||
handle: ".drag-handle",
|
||||
// handle: ".bars-move",
|
||||
group:{
|
||||
put:true,
|
||||
pull:true
|
||||
},
|
||||
onAdd: function (/**Event*/evt) {
|
||||
var itemEl = evt.item; // dragged HTMLElement
|
||||
var player_order = itemEl.querySelector('[id^="sequence-member"]')
|
||||
var player_available =itemEl.querySelector('[class^="member-availability-status"]')
|
||||
refresh_lineup_order()
|
||||
// player_order.classList.add('d-none')
|
||||
if (player_order.classList.contains('d-none')){
|
||||
player_available.classList.remove('d-none')
|
||||
}
|
||||
}
|
||||
});
|
||||
//xxx
|
||||
colorPositions()
|
||||
@@ -1,6 +1,6 @@
|
||||
from django import forms
|
||||
from .models import Team, Location, Opponent, Event, Member
|
||||
from django.forms import modelformset_factory
|
||||
from django.forms import modelformset_factory, formset_factory, inlineformset_factory
|
||||
|
||||
select_kwargs = {
|
||||
'attrs':{'class': 'form-control form-control-sm'}
|
||||
@@ -74,4 +74,45 @@ class LocationForm(forms.ModelForm):
|
||||
"benchcoach_object": forms.Select(**select_kwargs)
|
||||
}
|
||||
|
||||
class LineupEntryForm(forms.Form):
|
||||
member = None
|
||||
availability = None
|
||||
lineup_entry = None
|
||||
|
||||
event_lineup_entry_id = forms.Field(required=False)
|
||||
event_lineup_id = forms.Field(required=False)
|
||||
event_id = forms.Field()
|
||||
member_id = forms.Field()
|
||||
sequence = forms.IntegerField(required=False)
|
||||
label = forms.ChoiceField(required=False, choices=[
|
||||
("--", "--"),
|
||||
("P","P"),
|
||||
("C","C"),
|
||||
("1B","1B"),
|
||||
("2B", "2B"),
|
||||
("3B", "3B"),
|
||||
("SS", "SS"),
|
||||
('LF','LF'),
|
||||
('CF','CF'),
|
||||
('RF','RF'),
|
||||
('DH','DH'),
|
||||
('DR','DR'),
|
||||
('EH','EH')
|
||||
],
|
||||
widget=forms.Select(
|
||||
attrs = {'onchange' : "colorPositions();"}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class EventChooseForm(forms.Form):
|
||||
event_id = forms.ChoiceField()
|
||||
|
||||
# checked = forms.BooleanField(required=False)
|
||||
# def __init__(self, events, *args, **kwargs):
|
||||
# super(EventChooseForm, self).__init__(*args, **kwargs)
|
||||
# self.fields['foo'].choices = [e.data['id'] for e in events]
|
||||
|
||||
LineupEntryFormset = formset_factory(LineupEntryForm, can_delete=True, can_order=True, extra=0)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.db import models
|
||||
|
||||
import benchcoach.models
|
||||
import teamsnap.teamsnap.api
|
||||
import pyteamsnap.api
|
||||
from django.utils.timezone import localtime
|
||||
|
||||
class TeamsnapBaseModel(models.Model):
|
||||
@@ -9,7 +9,7 @@ class TeamsnapBaseModel(models.Model):
|
||||
id = models.CharField(max_length=50, unique=True, primary_key=True)
|
||||
created_at = models.DateTimeField(null=True)
|
||||
updated_at = models.DateTimeField(null=True)
|
||||
ApiObject = teamsnap.teamsnap.api.ApiObject
|
||||
ApiObject = pyteamsnap.api.ApiObject
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -29,7 +29,7 @@ class Team(TeamsnapBaseModel):
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_team"
|
||||
)
|
||||
ApiObject = teamsnap.teamsnap.api.Team
|
||||
ApiObject = pyteamsnap.api.Team
|
||||
|
||||
class User(TeamsnapBaseModel):
|
||||
type = 'user'
|
||||
@@ -37,7 +37,7 @@ class User(TeamsnapBaseModel):
|
||||
last_name = models.CharField(max_length = 50, null=True)
|
||||
email = models.EmailField(null=True)
|
||||
managed_teams = models.ManyToManyField(Team)
|
||||
ApiObject = teamsnap.teamsnap.api.User
|
||||
ApiObject = pyteamsnap.api.User
|
||||
|
||||
@classmethod
|
||||
def update_or_create_from_teamsnap_api(cls, teamsnap_data):
|
||||
@@ -76,7 +76,7 @@ class Opponent(TeamsnapManagedObjectModel):
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_opponent"
|
||||
)
|
||||
ApiObject = teamsnap.teamsnap.api.Opponent
|
||||
ApiObject = pyteamsnap.api.Opponent
|
||||
|
||||
class Location(TeamsnapManagedObjectModel):
|
||||
type = 'location'
|
||||
@@ -86,7 +86,7 @@ class Location(TeamsnapManagedObjectModel):
|
||||
on_delete=models.CASCADE,
|
||||
related_name="teamsnap_location"
|
||||
)
|
||||
ApiObject = teamsnap.teamsnap.api.Location
|
||||
ApiObject = pyteamsnap.api.Location
|
||||
|
||||
class Member(TeamsnapManagedObjectModel):
|
||||
# url format is
|
||||
@@ -103,7 +103,7 @@ class Member(TeamsnapManagedObjectModel):
|
||||
last_name = models.CharField(max_length = 50, null=True)
|
||||
jersey_number = models.IntegerField(null=True)
|
||||
is_non_player = models.BooleanField(null=True)
|
||||
ApiObject = teamsnap.teamsnap.api.Member
|
||||
ApiObject = pyteamsnap.api.Member
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.last_name}, {self.first_name} ({self.id})"
|
||||
@@ -131,7 +131,7 @@ class Event(TeamsnapManagedObjectModel):
|
||||
points_for_team = models.PositiveSmallIntegerField(null=True)
|
||||
is_game = models.BooleanField(null=True)
|
||||
game_type = models.CharField(max_length = 50, null=True)
|
||||
ApiObject = teamsnap.teamsnap.api.Event
|
||||
ApiObject = pyteamsnap.api.Event
|
||||
|
||||
@property
|
||||
def csv_event_title(self)->str:
|
||||
@@ -166,7 +166,7 @@ class Availability(TeamsnapManagedObjectModel):
|
||||
related_name="teamsnap_availability"
|
||||
)
|
||||
status_code = models.SmallIntegerField(choices=status_codes, null=True, blank=True, default=None)
|
||||
ApiObject = teamsnap.teamsnap.api.Availability
|
||||
ApiObject = pyteamsnap.api.Availability
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.member} - {self.event} ({self.id})"
|
||||
@@ -197,7 +197,7 @@ class LineupEntry(TeamsnapManagedObjectModel):
|
||||
)
|
||||
label = models.PositiveSmallIntegerField(choices=positions, default=None, null=True, blank=True)
|
||||
sequence = models.PositiveSmallIntegerField(default=0, null=True, blank=True)
|
||||
ApiObject = teamsnap.teamsnap.api.EventLineupEntry
|
||||
ApiObject = pyteamsnap.api.EventLineupEntry
|
||||
|
||||
@classmethod
|
||||
def update_or_create_from_teamsnap_api(cls, teamsnap_data):
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# from .utils import TeamSnap
|
||||
#
|
||||
# __all__ = ['TeamSnap']
|
||||
@@ -1,618 +0,0 @@
|
||||
__all__ = ['TeamSnap', 'Team', 'Event', 'Availability', 'Member', 'Location', 'Me']
|
||||
from apiclient import APIClient, HeaderAuthentication, JsonResponseHandler
|
||||
|
||||
class ApiObject():
|
||||
rel = None
|
||||
version = None
|
||||
template = None
|
||||
|
||||
def __init__(self, client, rel=rel, data={}):
|
||||
self.client = client
|
||||
self.data = data
|
||||
self.rel = rel
|
||||
|
||||
@classmethod
|
||||
def search(cls, client, **kwargs):
|
||||
results = client.query(cls.rel, "search", **kwargs)
|
||||
return [cls(client,rel=cls.rel, data=r) for r in results]
|
||||
|
||||
@classmethod
|
||||
def get(cls, client, id):
|
||||
r = client.get(f"{client.link(cls.rel)}/{id}")
|
||||
return cls(client, cls.rel, client.parse_response(r)[0])
|
||||
|
||||
class Me (ApiObject):
|
||||
rel = "me"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "first_name"
|
||||
},
|
||||
{
|
||||
"name": "last_name"
|
||||
},
|
||||
{
|
||||
"name": "password"
|
||||
},
|
||||
{
|
||||
"name": "birthday"
|
||||
},
|
||||
{
|
||||
"name": "email"
|
||||
},
|
||||
{
|
||||
"name": "facebook_id",
|
||||
"deprecated": True,
|
||||
"prompt": "facebook_id is deprecated and has been removed. Continued use of facebook_id is not recommended it will no longer be stored."
|
||||
},
|
||||
{
|
||||
"name": "facebook_access_token",
|
||||
"deprecated": True,
|
||||
"prompt": "facebook_access_token is deprecated and has been removed. Continued use of facebook_access_token is not recommended it will no longer be stored."
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "user"
|
||||
},
|
||||
{
|
||||
"name": "is_lab_rat"
|
||||
},
|
||||
{
|
||||
"name": "receives_newsletter"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client=client, rel=self.rel, data=client.get(client.link(self.rel)))
|
||||
|
||||
class User (ApiObject):
|
||||
rel = "users"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "first_name"
|
||||
},
|
||||
{
|
||||
"name": "last_name"
|
||||
},
|
||||
{
|
||||
"name": "password"
|
||||
},
|
||||
{
|
||||
"name": "birthday"
|
||||
},
|
||||
{
|
||||
"name": "email"
|
||||
},
|
||||
{
|
||||
"name": "facebook_id",
|
||||
"deprecated": True,
|
||||
"prompt": "facebook_id is deprecated and has been removed. Continued use of facebook_id is not recommended it will no longer be stored."
|
||||
},
|
||||
{
|
||||
"name": "facebook_access_token",
|
||||
"deprecated": True,
|
||||
"prompt": "facebook_access_token is deprecated and has been removed. Continued use of facebook_access_token is not recommended it will no longer be stored."
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "user"
|
||||
},
|
||||
{
|
||||
"name": "is_lab_rat"
|
||||
},
|
||||
{
|
||||
"name": "receives_newsletter"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class Event (ApiObject):
|
||||
rel = "events"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "type",
|
||||
"value": "event"
|
||||
},
|
||||
{
|
||||
"name": "additional_location_details"
|
||||
},
|
||||
{
|
||||
"name": "browser_time_zone"
|
||||
},
|
||||
{
|
||||
"name": "division_location_id"
|
||||
},
|
||||
{
|
||||
"name": "doesnt_count_towards_record"
|
||||
},
|
||||
{
|
||||
"name": "duration_in_minutes"
|
||||
},
|
||||
{
|
||||
"name": "game_type_code"
|
||||
},
|
||||
{
|
||||
"name": "icon_color"
|
||||
},
|
||||
{
|
||||
"name": "is_canceled"
|
||||
},
|
||||
{
|
||||
"name": "is_game"
|
||||
},
|
||||
{
|
||||
"name": "is_overtime"
|
||||
},
|
||||
{
|
||||
"name": "is_shootout"
|
||||
},
|
||||
{
|
||||
"name": "is_tbd"
|
||||
},
|
||||
{
|
||||
"name": "label"
|
||||
},
|
||||
{
|
||||
"name": "location_id"
|
||||
},
|
||||
{
|
||||
"name": "minutes_to_arrive_early"
|
||||
},
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "notes"
|
||||
},
|
||||
{
|
||||
"name": "notify_opponent"
|
||||
},
|
||||
{
|
||||
"name": "notify_opponent_contacts_email"
|
||||
},
|
||||
{
|
||||
"name": "notify_opponent_contacts_name"
|
||||
},
|
||||
{
|
||||
"name": "notify_opponent_notes"
|
||||
},
|
||||
{
|
||||
"name": "notify_team"
|
||||
},
|
||||
{
|
||||
"name": "notify_team_as_member_id"
|
||||
},
|
||||
{
|
||||
"name": "opponent_id"
|
||||
},
|
||||
{
|
||||
"name": "points_for_opponent"
|
||||
},
|
||||
{
|
||||
"name": "points_for_team"
|
||||
},
|
||||
{
|
||||
"name": "repeating_include",
|
||||
"prompt": "When updating a repeating event, this is a required field. Values are: \"all\" - updates all events in this series, \"future\" - updates this event and all that occur after, \"none\" - only updates a single event."
|
||||
},
|
||||
{
|
||||
"name": "repeating_type_code",
|
||||
"prompt": "A code for the frequency of the repeated event, this is required with the \"repeating_include\" attribute when creating a repeating event. Valid values are: \"1\" - repeat an event daily, \"2\" - repeat an event weekly."
|
||||
},
|
||||
{
|
||||
"name": "repeating_until",
|
||||
"prompt": "A date when the repeating event should end, this is inclusive so an event will be created on this day if it falls before the next event specified by \"repeating_type_code\". This attribute is required with \"repeating_type_code\" when creating a repeating event."
|
||||
},
|
||||
{
|
||||
"name": "results"
|
||||
},
|
||||
{
|
||||
"name": "results_url"
|
||||
},
|
||||
{
|
||||
"name": "shootout_points_for_opponent"
|
||||
},
|
||||
{
|
||||
"name": "shootout_points_for_team"
|
||||
},
|
||||
{
|
||||
"name": "start_date"
|
||||
},
|
||||
{
|
||||
"name": "team_id"
|
||||
},
|
||||
{
|
||||
"name": "time_zone"
|
||||
},
|
||||
{
|
||||
"name": "tracks_availability"
|
||||
},
|
||||
{
|
||||
"name": "uniform"
|
||||
}
|
||||
]}
|
||||
|
||||
class Team (ApiObject):
|
||||
rel = "teams"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "location_country"
|
||||
},
|
||||
{
|
||||
"name": "location_postal_code"
|
||||
},
|
||||
{
|
||||
"name": "time_zone",
|
||||
"prompt": "The time_zone parameter is required when creating a team, but for changing a team's time_zone, use the update_time_zone command"
|
||||
},
|
||||
{
|
||||
"name": "sport_id"
|
||||
},
|
||||
{
|
||||
"name": "division_id"
|
||||
},
|
||||
{
|
||||
"name": "division_name"
|
||||
},
|
||||
{
|
||||
"name": "season_name"
|
||||
},
|
||||
{
|
||||
"name": "league_name"
|
||||
},
|
||||
{
|
||||
"name": "league_url"
|
||||
},
|
||||
{
|
||||
"name": "owner_first_name"
|
||||
},
|
||||
{
|
||||
"name": "owner_last_name"
|
||||
},
|
||||
{
|
||||
"name": "owner_email"
|
||||
},
|
||||
{
|
||||
"name": "is_ownership_pending"
|
||||
},
|
||||
{
|
||||
"name": "ad_unit_hero_id"
|
||||
},
|
||||
{
|
||||
"name": "ad_unit_hero_template_id"
|
||||
},
|
||||
{
|
||||
"name": "ad_unit_inline_id"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "team"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class Availability (ApiObject):
|
||||
rel = "availabilities"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "status_code"
|
||||
},
|
||||
{
|
||||
"name": "notes"
|
||||
},
|
||||
{
|
||||
"name": "event_id"
|
||||
},
|
||||
{
|
||||
"name": "member_id"
|
||||
},
|
||||
{
|
||||
"name": "notes_author_member_id"
|
||||
},
|
||||
{
|
||||
"name": "source"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "availability"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class Member (ApiObject):
|
||||
rel = "members"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "first_name"
|
||||
},
|
||||
{
|
||||
"name": "last_name"
|
||||
},
|
||||
{
|
||||
"name": "gender"
|
||||
},
|
||||
{
|
||||
"name": "position"
|
||||
},
|
||||
{
|
||||
"name": "is_manager"
|
||||
},
|
||||
{
|
||||
"name": "birthday"
|
||||
},
|
||||
{
|
||||
"name": "hide_age",
|
||||
"deprecated": True,
|
||||
"prompt": "hide_age is deprecated and will be removed in a future version, use is_age_hidden instead."
|
||||
},
|
||||
{
|
||||
"name": "is_age_hidden"
|
||||
},
|
||||
{
|
||||
"name": "hide_address",
|
||||
"deprecated": True,
|
||||
"prompt": "hide_address is deprecated and will be removed in a future version, use is_address_hidden instead."
|
||||
},
|
||||
{
|
||||
"name": "is_address_hidden"
|
||||
},
|
||||
{
|
||||
"name": "is_non_player"
|
||||
},
|
||||
{
|
||||
"name": "address_street1"
|
||||
},
|
||||
{
|
||||
"name": "address_street2"
|
||||
},
|
||||
{
|
||||
"name": "address_city"
|
||||
},
|
||||
{
|
||||
"name": "address_state"
|
||||
},
|
||||
{
|
||||
"name": "address_zip"
|
||||
},
|
||||
{
|
||||
"name": "jersey_number"
|
||||
},
|
||||
{
|
||||
"name": "team_id"
|
||||
},
|
||||
{
|
||||
"name": "is_ownership_pending"
|
||||
},
|
||||
{
|
||||
"name": "source_action"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "member"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
class Location (ApiObject):
|
||||
rel = "locations"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "url"
|
||||
},
|
||||
{
|
||||
"name": "phone"
|
||||
},
|
||||
{
|
||||
"name": "notes"
|
||||
},
|
||||
{
|
||||
"name": "address"
|
||||
},
|
||||
{
|
||||
"name": "latitude"
|
||||
},
|
||||
{
|
||||
"name": "longitude"
|
||||
},
|
||||
{
|
||||
"name": "team_id"
|
||||
},
|
||||
{
|
||||
"name": "is_retired"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "location"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class Opponent (ApiObject):
|
||||
rel = "opponents"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "contacts_name"
|
||||
},
|
||||
{
|
||||
"name": "contacts_phone"
|
||||
},
|
||||
{
|
||||
"name": "contacts_email"
|
||||
},
|
||||
{
|
||||
"name": "notes"
|
||||
},
|
||||
{
|
||||
"name": "team_id"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "opponent"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class EventLineupEntry (ApiObject):
|
||||
rel = "event_lineup_entries"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "event_lineup_id"
|
||||
},
|
||||
{
|
||||
"name": "member_id"
|
||||
},
|
||||
{
|
||||
"name": "sequence"
|
||||
},
|
||||
{
|
||||
"name": "label"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "event_lineup_entry"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class Statistics (ApiObject):
|
||||
rel = "statistics"
|
||||
version = "3.866.0"
|
||||
template = {
|
||||
"data": [
|
||||
{
|
||||
"name": "acronym"
|
||||
},
|
||||
{
|
||||
"name": "always_display_decimals"
|
||||
},
|
||||
{
|
||||
"name": "formula"
|
||||
},
|
||||
{
|
||||
"name": "is_in_descending_order"
|
||||
},
|
||||
{
|
||||
"name": "display_zero_totals"
|
||||
},
|
||||
{
|
||||
"name": "is_percentage"
|
||||
},
|
||||
{
|
||||
"name": "is_private"
|
||||
},
|
||||
{
|
||||
"name": "is_team_statistic"
|
||||
},
|
||||
{
|
||||
"name": "is_top_statistic"
|
||||
},
|
||||
{
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"name": "precision"
|
||||
},
|
||||
{
|
||||
"name": "statistic_group_id"
|
||||
},
|
||||
{
|
||||
"name": "team_id"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "statistic"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
class MemberStatistics (ApiObject):
|
||||
rel = "member_statistics"
|
||||
|
||||
class TeamSnap(APIClient):
|
||||
base_url = 'https://api.teamsnap.com/v3'
|
||||
|
||||
def __init__(self, token, *args, **kwargs):
|
||||
super().__init__(*args,
|
||||
authentication_method=HeaderAuthentication(token=token),
|
||||
response_handler=JsonResponseHandler,
|
||||
**kwargs)
|
||||
self._root_collection = self.get(self.base_url)['collection']
|
||||
self._links = self._by_rel(self.base_url, 'links')
|
||||
self._queries = self._by_rel(self.base_url, 'queries')
|
||||
self._commands = self._by_rel(self.base_url, 'commands')
|
||||
pass
|
||||
|
||||
def link(self, link_name):
|
||||
d = {l['rel']:l['href'] for l in self._root_collection["links"]}
|
||||
return d.get(link_name)
|
||||
|
||||
def _by_rel (self, url, k):
|
||||
try:
|
||||
{l['rel']: l for l in self._root_collection[k]}
|
||||
except Exception as e:
|
||||
return {}
|
||||
self.get(url)['collection'][k]
|
||||
return {l['rel']:l for l in self.get(url)['collection'][k]}
|
||||
|
||||
def query (self, rel, query, **kwargs):
|
||||
queries = self._by_rel(self._get_href(rel), 'queries')
|
||||
response = self.get(self._get_href(query, queries), params=kwargs)
|
||||
return self.parse_response(response)
|
||||
|
||||
def command (self, rel, command, **kwargs):
|
||||
commands = self._by_rel(self._get_href(rel), 'commands')
|
||||
response = self.get(self._get_href(command, commands), params=kwargs)
|
||||
return self.parse_response(response)
|
||||
|
||||
def _get_href (self, rel: str, links:dict = None, url = base_url) -> str:
|
||||
"""returns a hyperlink from a the links dictionary. Each item in the links dictionary is a
|
||||
dictionary with a rel and href key"""
|
||||
if links is None: links = self._by_rel(url, 'links')
|
||||
link = links[rel]['href']
|
||||
return link
|
||||
|
||||
def get_item (self, rel, id):
|
||||
r = self.get(f"{self.link(rel)}/{id}")
|
||||
return self.parse_response(r)[0]
|
||||
|
||||
@classmethod
|
||||
def parse_response(self, response):
|
||||
result = []
|
||||
items = [item['data'] for item in response['collection'].get('items',[])]
|
||||
for item in response['collection'].get('items',[]):
|
||||
details = {}
|
||||
for detail in item['data']:
|
||||
# TODO type casting and validation based on item['type']
|
||||
details[detail['name']] = detail['value']
|
||||
result.append(details)
|
||||
|
||||
return result
|
||||
# return [{detail['name']: detail['value'] for detail in item} for item in items]
|
||||
|
||||
27
teamsnap/templates/teamsnap/availabilities.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}Schedule{% endblock %}
|
||||
{% block content %}
|
||||
{% load tz %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_event' id=event.data.id team_id=schedule.html %}">{{ event.data.formatted_title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.start_date | localtime}}
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.location_name }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,116 +0,0 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_heading %}{{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h6>Lineup</h6>
|
||||
<form method="post" action="{% url 'teamsnap_submit_lineup' team_id=team_id event_id=event_id %}">
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm" id="table-event-lineup">
|
||||
<tbody id="tablebody-event-lineup">
|
||||
{% for form in formset %}
|
||||
{% if form.event_lineup_entry_id.value %}
|
||||
<tr>
|
||||
{{ form.event_lineup_entry_id.as_hidden }}
|
||||
{{ form.event_lineup_id.as_hidden }}
|
||||
{{ form.event_id.as_hidden }}
|
||||
{{ form.member_id.as_hidden }}
|
||||
{{ form.sequence.as_hidden }}
|
||||
{{ form.label.as_hidden }}
|
||||
{{ form.member_name.as_hidden }}
|
||||
|
||||
<td class="col-1 px-1">
|
||||
{{ forloop.counter }}
|
||||
</td>
|
||||
<td class="col-auto">
|
||||
{{ form.member_name.value }}
|
||||
</td>
|
||||
<td class="col-3">
|
||||
<div class="form-select-sm">{{ form.label }}</div>
|
||||
</td>
|
||||
<td class="col-1">
|
||||
<div class="d-flex">
|
||||
<div class="px-1">
|
||||
<i class="bi bi-dash-circle text-danger"></i>
|
||||
{{ form.DELETE.as_hidden }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-1 drag-handle">
|
||||
<i class="bi bi-grip-vertical text-secondary"></i>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<input class="btn btn-success" type="submit" value="Submit">
|
||||
</form>
|
||||
<h6>Bench</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# <th scope="row">#}
|
||||
{# <td>Status</td>#}
|
||||
{# <td>Last</td>#}
|
||||
{# <td>First</td>#}
|
||||
{# </th>#}
|
||||
{# </thead>#}
|
||||
|
||||
{% for availability in availablities %}
|
||||
{% if availability.data.member_is_non_player is False %}
|
||||
<tr>
|
||||
<td class="col-1">
|
||||
{% if availability.data.status_code == 1 %}
|
||||
<i class="bi bi-check-circle-fill text-success"></i>
|
||||
{% endif %}
|
||||
{% if availability.data.status_code == 0 %}
|
||||
<i class="bi bi-x-circle-fill text-danger"></i>
|
||||
{% endif %}
|
||||
{% if availability.data.status_code == None %}
|
||||
<i class="bi bi-circle"></i>
|
||||
{% endif %}
|
||||
{% if availability.data.status_code == 2 %}
|
||||
<i class="bi bi-circle-half text-info"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-auto">
|
||||
{{ availability.data.member_first_name }} {{ availability.data.member_last_name }}
|
||||
</td>
|
||||
<td class="col-3">
|
||||
</td>
|
||||
<td class="col-1">
|
||||
<div class="d-flex">
|
||||
<div class="px-1">
|
||||
<i class="bi bi-plus-circle"></i>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="{% static 'js/Sortable.js' %}"></script>
|
||||
<script id="sortable">
|
||||
var lineup = new Sortable.create(
|
||||
document.getElementById('tablebody-event-lineup'), {
|
||||
animation: 150,
|
||||
ghostClass:"ghost",
|
||||
handle: ".drag-handle",
|
||||
group:{
|
||||
put:true,
|
||||
pull:true
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
76
teamsnap/templates/teamsnap/event/instagen.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<html lang="en">
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ event.data.formatted_title }} - Instagenerator{% endblock %}
|
||||
{% block page_heading %}
|
||||
<div class="row d-inline-flex">
|
||||
<div class="col">
|
||||
<img src="{% static 'teamsnap/ig/graphics/hounds-insta.jpg' %}" class="mx-auto d-block img-fluid shadow-sm" style="height:30px;border-radius: 8px;">
|
||||
</div>
|
||||
<div class="col text-nowrap px-0">Hounds Instagenerator</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block page_subheading %}{{ event.data.formatted_title }}, {{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<form method="get" action="generate">
|
||||
|
||||
<div class="mb-3">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected value="{{event.data.id}}">{{event.data.formatted_title}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<legend class="">Background</legend>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="background" id="backgroundLocation" checked value="location">
|
||||
<label class="form-check-label" for="backgroundLocation">
|
||||
Location
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="background" id="backgroundTransparent" value="transparent">
|
||||
<label class="form-check-label" for="backgroundTransparent">
|
||||
Transparent
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="background" id="badge_only" value="badge">
|
||||
<label class="form-check-label" for="badge_only">
|
||||
Badge only
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<legend class="">Layout</legend>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="dimensions" id="square" checked value="1080x1080">
|
||||
<label class="form-check-label" for="square">
|
||||
Square
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="dimensions" id="portrait" value="1080x1920">
|
||||
<label class="form-check-label" for="portrait">
|
||||
Portrait
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="dimensions" id="portrait" value="1920x1080">
|
||||
<label class="form-check-label" for="portrait">
|
||||
Landscape
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-lg-flex justify-content-lg-end align-items-lg-center mb-3">
|
||||
<button type="submit" class="btn btn-primary btn-block">Get Image</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
12
teamsnap/templates/teamsnap/lineup/edit.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_heading %}{{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'teamsnap/lineup/widgets/lineup.html' with formset_lineup=formset_lineup formset_bench=formset_bench%}
|
||||
|
||||
<script src="{% static 'js/Sortable.js' %}"></script>
|
||||
<script src="{% static 'teamsnap/js/lineup-table.js' %}"></script>
|
||||
|
||||
{% endblock %}
|
||||
95
teamsnap/templates/teamsnap/lineup/multiple_choose.html
Normal file
@@ -0,0 +1,95 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
{% block page_heading %}
|
||||
<div class="row d-inline-flex">
|
||||
<div class="col">
|
||||
Schedule
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<form method="get" action="{% url 'teamsnap_edit_multiple_lineups' team_id=team_id%}">
|
||||
{{ formset.management_form }}
|
||||
<input class="btn btn-sm btn-outline-primary text-nowrap" type="submit" value="Submit">
|
||||
{% load tz %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
{{ form.event_id }}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- create_normal.html :: part 4 -->
|
||||
|
||||
<script type='text/javascript'>
|
||||
function updateElementIndex(el, prefix, ndx) {
|
||||
var id_regex = new RegExp('(' + prefix + '-\\d+)');
|
||||
var replacement = prefix + '-' + ndx;
|
||||
if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
|
||||
if (el.id) el.id = el.id.replace(id_regex, replacement);
|
||||
if (el.name) el.name = el.name.replace(id_regex, replacement);
|
||||
}
|
||||
function cloneMore(selector, prefix) {
|
||||
var newElement = $(selector).clone(true);
|
||||
var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
|
||||
newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
|
||||
var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
|
||||
var id = 'id_' + name;
|
||||
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
|
||||
});
|
||||
newElement.find('label').each(function() {
|
||||
var forValue = $(this).attr('for');
|
||||
if (forValue) {
|
||||
forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
|
||||
$(this).attr({'for': forValue});
|
||||
}
|
||||
});
|
||||
total++;
|
||||
$('#id_' + prefix + '-TOTAL_FORMS').val(total);
|
||||
$(selector).after(newElement);
|
||||
var conditionRow = $('.form-row:not(:last)');
|
||||
conditionRow.find('.btn.add-form-row')
|
||||
.removeClass('btn-success').addClass('btn-danger')
|
||||
.removeClass('add-form-row').addClass('remove-form-row')
|
||||
.html('<span class="glyphicon glyphicon-minus" aria-hidden="true"></span>');
|
||||
return false;
|
||||
}
|
||||
function deleteForm(prefix, btn) {
|
||||
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
|
||||
if (total > 1){
|
||||
btn.closest('.form-row').remove();
|
||||
var forms = $('.form-row');
|
||||
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
|
||||
for (var i=0, formCount=forms.length; i<formCount; i++) {
|
||||
$(forms.get(i)).find(':input').each(function() {
|
||||
updateElementIndex(this, prefix, i);
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$(document).on('click', '.add-form-row', function(e){
|
||||
e.preventDefault();
|
||||
cloneMore('.form-row:last', 'form');
|
||||
return false;
|
||||
});
|
||||
$(document).on('click', '.remove-form-row', function(e){
|
||||
e.preventDefault();
|
||||
deleteForm('form', $(this));
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
45
teamsnap/templates/teamsnap/lineup/multiple_choose_2.html
Normal file
@@ -0,0 +1,45 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
{% block page_heading %}
|
||||
<div class="row d-inline-flex">
|
||||
<div class="col">
|
||||
Schedule
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<form method="get" action="{% url 'teamsnap_edit_multiple_lineups' team_id=team_id%}">
|
||||
{{ formset.management_form }}
|
||||
<input class="btn btn-sm btn-outline-primary text-nowrap" type="submit" value="Submit">
|
||||
{% load tz %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for form in formset %}
|
||||
{{ form.event_id.as_hidden }}
|
||||
<tr>
|
||||
<td>
|
||||
{{ form.checked }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_view_event' event_id=form.event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id%}">{{ form.event.data.formatted_title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ form.event.data.start_date | localtime}}
|
||||
</td>
|
||||
<td>
|
||||
{{ form.event.data.location_name }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
100
teamsnap/templates/teamsnap/lineup/multiple_edit.html
Normal file
@@ -0,0 +1,100 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} Edit Lineups {% endblock %}
|
||||
{% block page_heading %}Edit Lineups{% endblock %}
|
||||
{% block page_subheading %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{# <div class="container overflow-scroll mx-0 px-0">#}
|
||||
{# <div class="row flex-row flex-nowrap">#}
|
||||
{# {% for event_data in contexts %}#}
|
||||
{# <div class="col border-start border-end">#}
|
||||
{# <div class = "border-bottom">#}
|
||||
{# <h4>{{ event_data.event.data.formatted_title }}</h4>#}
|
||||
{# <h6 class="text-muted" >{{ event_data.data.start_date }}</h6>#}
|
||||
{# </div>#}
|
||||
{# {% include 'teamsnap/lineup/widgets/lineup.html' with formset_lineup=event_data.formset_lineup formset_bench=event_data.formset_bench event_id=event_data.event.data.id %}#}
|
||||
{# </div>#}
|
||||
{# {% endfor %}#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
<div class="container overflow-scroll">
|
||||
<div class="row flex-row flex-nowrap">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% for event_data in contexts %}
|
||||
<td>
|
||||
<h4>{{ event_data.event.data.formatted_title }}</h4>
|
||||
<h6 class="text-muted" >{{ event_data.data.start_date }}</h6>
|
||||
{% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %}
|
||||
{% for position in position_list.split %}
|
||||
<span class="position-status fw-bold small px-1" id="position-status-{{ position }}">{{ position }}</span>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-light" colspan="{{ contexts|length }}"><h4>Lineup</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% for event_data in contexts %}
|
||||
<td class="align-top">
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_lineup table_id="players-lineup" %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-light" colspan="{{ contexts|length }}"><h4>Bench</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% for event_data in contexts %}
|
||||
<td class="align-top">
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_bench table_id="players-bench" %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-light" colspan="{{ contexts|length }}"><h4>Out</h4></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% for event_data in contexts %}
|
||||
<td class="align-top">
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=event_data.formset_out table_id="players-bench" %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{# <div class="align-content-start">#}
|
||||
{# <div class = "bg-light rounded-3 p-2">#}
|
||||
{# <div class="row">#}
|
||||
{# {% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %}#}
|
||||
{# {% for position in position_list.split %}#}
|
||||
{# <span class="position-status fw-bold small px-1" id="position-status-{{ position }}">{{ position }}</span>#}
|
||||
{# {% endfor %}#}
|
||||
{# {% endwith %}#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# <form method="post" action="{% url 'teamsnap_submit_lineup' team_id=team_id event_id=event_id%}">#}
|
||||
{# {{ formset.management_form }}#}
|
||||
{# {% csrf_token %}#}
|
||||
|
||||
{# <h4 class="border-bottom">Lineup</h4>#}
|
||||
{# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_lineup table_id="players-lineup" %}#}
|
||||
{# <input class="btn btn-success" type="submit" value="Submit">#}
|
||||
{##}
|
||||
{# <h4 class="border-bottom">Bench</h4>#}
|
||||
{# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_bench table_id="players-bench" %}#}
|
||||
{##}
|
||||
{# <h4 class="border-bottom">Out</h4>#}
|
||||
{# {% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_out table_id="players-out" %}#}
|
||||
|
||||
{# </form>#}
|
||||
<script src="{% static 'js/Sortable.js' %}"></script>
|
||||
<script src="{% static 'teamsnap/js/lineup-table.js' %}"></script>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
28
teamsnap/templates/teamsnap/lineup/widgets/lineup.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<div class="align-content-start">
|
||||
<div class = "bg-light rounded-3 p-2">
|
||||
{# <div class="row">#}
|
||||
{% with 'P C 1B 2B 3B SS LF CF RF EH DH' as position_list %}
|
||||
{% for position in position_list.split %}
|
||||
<span class="position-status fw-bold small px-1" id="position-status-{{ position }}">{{ position }}</span>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{# </div>#}
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="{% url 'teamsnap_submit_lineup' team_id=team_id event_id=event_id%}">
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
|
||||
<h4 class="border-bottom">Lineup</h4>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_lineup table_id="players-lineup" %}
|
||||
<input class="btn btn-success" type="submit" value="Submit">
|
||||
|
||||
<h4 class="border-bottom">Bench</h4>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_bench table_id="players-bench" %}
|
||||
|
||||
<h4 class="border-bottom">Out</h4>
|
||||
{% include 'teamsnap/lineup/widgets/lineup_table.html' with formset=formset_out table_id="players-out" %}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
59
teamsnap/templates/teamsnap/lineup/widgets/lineup_table.html
Normal file
@@ -0,0 +1,59 @@
|
||||
<table class="table table-sm my-0" style="min-height: 1rem" id="table-{{ table_id }}">
|
||||
<tbody id="tbody-{{ table_id }}">
|
||||
{% for form in formset %}
|
||||
<tr data-player-id="{{ form.member.data.id }}"
|
||||
data-position="{{ form.label.value }}"
|
||||
data-order="{{ form.sequence.value }}">
|
||||
{{ form.event_lineup_entry_id.as_hidden }}
|
||||
{{ form.event_lineup_id.as_hidden }}
|
||||
{{ form.event_id.as_hidden }}
|
||||
{{ form.member_id.as_hidden }}
|
||||
{{ form.sequence.as_hidden }}
|
||||
{{ form.label.as_hidden }}
|
||||
{{ form.member_name.as_hidden }}
|
||||
<th class="col-1" id="sequence-member-{{ form.member.data.id }}">
|
||||
{{ form.sequence.value | add:"1" }}
|
||||
</th>
|
||||
<td class="col-1">
|
||||
<div class="mx-1">
|
||||
<span id="player-order-{{ form.member.id }}" class="lineup-sequence-value">
|
||||
{% if form.order.value > 0 %} {{ form.order.value | add:"1" }} {% endif %}
|
||||
</span>
|
||||
|
||||
<span id="player-availability-{{ form.member.id }}" class="member-availability-status small">
|
||||
{% if form.availability.data.status_code == 2 %}
|
||||
<i class="bi bi-question-circle-fill text-info"></i>
|
||||
{% elif form.availability.data.status_code == 1 %}
|
||||
<i class="bi bi-check-circle-fill text-success"></i>
|
||||
{% elif form.availability.data.status_code == 0 %}
|
||||
<i class="bi bi-x-circle-fill text-danger"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-question-circle"></i>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<th class="">
|
||||
<div class="text-nowrap">
|
||||
<span class="d-none d-md-inline-block">
|
||||
{{ form.member.data.first_name }}
|
||||
</span>
|
||||
{{ form.member.data.last_name }}
|
||||
<span class="small text-muted fw-light d-none d-lg-inline-block">
|
||||
#{{ form.member.data.jersey_number }}
|
||||
</span>
|
||||
</div>
|
||||
{# <br><code><small>{{ form.statline }}</small></code>#}
|
||||
</th>
|
||||
<td class="col-2">
|
||||
{{ form.label }}
|
||||
</td>
|
||||
<td class="col-1 drag-handle">
|
||||
<i class="bi bi-grip-vertical text-secondary"></i>
|
||||
</td>
|
||||
{# <td>{{ form.instance.position }}</td>#}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
22
teamsnap/templates/teamsnap/location/view.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ location.data.name }}{% endblock %}
|
||||
{% block page_heading %}{{ location.data.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for key, value in location.data.items %}
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{{ key }}
|
||||
</th>
|
||||
<td>
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
22
teamsnap/templates/teamsnap/opponent.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ opponent.data.name }}{% endblock %}
|
||||
{% block page_heading %}{{ opponent.data.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for key, value in opponent.data.items %}
|
||||
<tr>
|
||||
<th scope="col">
|
||||
{{ key }}
|
||||
</th>
|
||||
<td>
|
||||
{{ value }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
47
teamsnap/templates/teamsnap/schedule.html
Normal file
@@ -0,0 +1,47 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ title }}{% endblock %}
|
||||
{% block page_heading %}
|
||||
<div class="row d-inline-flex">
|
||||
<div class="col">
|
||||
Schedule
|
||||
</div>
|
||||
<div hidden class="col">
|
||||
<div class="container m-2">
|
||||
<div class="container m-2">
|
||||
<div class="btn-group">
|
||||
<form action="">
|
||||
<a class="btn btn-sm btn-outline-primary text-nowrap" href="{% url 'teamsnap_schedule' team_id=team_id%}?filters=no_past">No Past Events</a>
|
||||
<button class="btn btn-sm btn-outline-primary text-nowrap">Games Only</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% load tz %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for event in events %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_view_event' event_id=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id%}">{{ event.data.formatted_title }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.start_date | localtime}}
|
||||
</td>
|
||||
<td>
|
||||
{{ event.data.location_name }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
137
teamsnap/templates/teamsnap/view_event.html
Normal file
@@ -0,0 +1,137 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
{% block title %} {{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_heading %}{{ event.data.formatted_title }}{% endblock %}
|
||||
{% block page_subheading %}{{ event.data.start_date }}, {{ event.data.location_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="table-responsive">
|
||||
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
Date
|
||||
</th>
|
||||
<td>
|
||||
{{ event.data.start_date }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
Opponent
|
||||
</th>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_opponent' team_id=request.user.profile.teamsnapsettings.managed_team.id id=event.data.opponent_id %}">{{ event.data.opponent_name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
Location
|
||||
</th>
|
||||
<td>
|
||||
<a href="{% url 'teamsnap_location' team_id=request.user.profile.teamsnapsettings.managed_team.id id=event.data.location_id %}">{{ event.data.location_name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h3>Availabilities</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# <th scope="row">#}
|
||||
{# <td>Status</td>#}
|
||||
{# <td>Last</td>#}
|
||||
{# <td>First</td>#}
|
||||
{# </th>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="col" class="col-1">
|
||||
<i class="bi bi-check-circle-fill text-success"></i>
|
||||
</th>
|
||||
<td>
|
||||
{{ availability_summary.data.player_going_count }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="col" class="col-1">
|
||||
<i class="bi bi-question-circle-fill text-info"></i>
|
||||
</th>
|
||||
<td>
|
||||
{{ availability_summary.data.player_maybe_count }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="col" class="col-1">
|
||||
<i class="bi bi-x-circle-fill text-danger"></i>
|
||||
</th>
|
||||
<td>
|
||||
{{ availability_summary.data.player_not_going_count }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="col" class="col-1">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</th>
|
||||
<td>
|
||||
{{ availability_summary.data.player_unknown_count }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="">
|
||||
<h3>Lineup Entries</h3>
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'teamsnap_edit_lineup' event_id=event.data.id team_id=request.user.profile.teamsnapsettings.managed_team.id%}" role="button">Edit</a>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
{# <thead>#}
|
||||
{# <th scope="row">#}
|
||||
{# <td>Status</td>#}
|
||||
{# <td>Last</td>#}
|
||||
{# <td>First</td>#}
|
||||
{# </th>#}
|
||||
{# </thead>#}
|
||||
<tbody>
|
||||
{% for lineup_entry in lineup_entries %}
|
||||
<tr>
|
||||
<td class="col-1">
|
||||
{{ lineup_entry.data.sequence }}
|
||||
</td>
|
||||
<td>
|
||||
{{ lineup_entry.data.member_name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ lineup_entry.data.label }}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h3>Functions</h3>
|
||||
<div class="">
|
||||
<form method="get" action="{% url "teamsnap_image_generator_generate" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}">
|
||||
<select hidden class="form-select" name="game_id" id="game_id">
|
||||
<optgroup label="Events">
|
||||
<option value="" disabled="disabled">Select an event...</option>
|
||||
<option selected value="{{event.data.id}}">{{event.data.formatted_title}}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input hidden class="form-check-input" type="radio" name="background" id="backgroundLocation" checked value="location">
|
||||
<input hidden class="form-check-input" type="radio" name="dimensions" id="square" checked value="1080x1080">
|
||||
<button type="submit" class="btn btn-primary btn-block"><i class="bi bi-instagram"></i></button>
|
||||
</form>
|
||||
<a class="btn btn-primary" href="{% url "teamsnap_image_generator" team_id=request.user.profile.teamsnapsettings.managed_team.id event_id=event.data.id %}" role="button"><div class="d-inline-block"><i class="bi bi-instagram"></i> <i class="bi bi-three-dots"></i></div></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -6,8 +6,19 @@ from functools import partial
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.home, name='teamsnap home'),
|
||||
path('', views.home, name='teamsnap_home'),
|
||||
path('edit/event/<int:id>', views.edit_event, name='teamsnap edit event'),
|
||||
path('sync/download', views.sync_from_teamsnap, name="sync from teamsnap"),
|
||||
path('import/', views.import_teamsnap, name="import")
|
||||
path('import/', views.import_teamsnap, name="import"),
|
||||
|
||||
path('<int:team_id>/schedule/', views.schedule, name='teamsnap_schedule'),
|
||||
path('<int:team_id>/schedule/view_event/<int:event_id>', views.event, name='teamsnap_view_event'),
|
||||
path('<int:team_id>/opponent/view/<int:id>', views.opponent, name='teamsnap_opponent'),
|
||||
path('<int:team_id>/location/view/<int:id>', views.location, name='teamsnap_location'),
|
||||
path('<int:team_id>/event/<int:event_id>/edit_lineup/', views.edit_lineup, name='teamsnap_edit_lineup'),
|
||||
path('<int:team_id>/event/<int:event_id>/submit_lineup/', views.submit_lineup, name='teamsnap_submit_lineup'),
|
||||
path('<int:team_id>/event/<int:event_id>/image_generator/', views.image_generator, name='teamsnap_image_generator'),
|
||||
path('<int:team_id>/event/<int:event_id>/image_generator/generate', views.get_matchup_image, name='teamsnap_image_generator_generate'),
|
||||
path('<int:team_id>/multievent/edit_lineups', views.edit_multiple_lineups, name='teamsnap_edit_multiple_lineups'),
|
||||
path('<int:team_id>/multievent/choose', views.multi_lineup_choose, name='teamsnap_choose_multiple_lineups')
|
||||
]
|
||||
331
teamsnap/utils/gen_image.py
Normal file
@@ -0,0 +1,331 @@
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFilter, ImageFont
|
||||
from pathlib import Path
|
||||
import os
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
from typing import List
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# image_directory = 'input/images/logos-bw/{filename}.{ext}'
|
||||
|
||||
# font_regular_path = "input/fonts/DINAlternate-Bold.ttf"
|
||||
# font_condensed_path = "input/fonts/DINCondensed-Bold.ttf"
|
||||
font_regular_path = "benchcoachproject/static/teamsnap/ig/fonts/ScalaSans-BoldLF.otf"
|
||||
font_condensed_path = "benchcoachproject/static/teamsnap/ig/fonts/ScalaSans-BoldLF.otf"
|
||||
|
||||
@dataclass
|
||||
class Team:
|
||||
name: str
|
||||
winlosstie: List[int] = None
|
||||
image_directory: str = '../input/images/logos-bw/{filename}.{ext}'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.name.lower().replace(' ', '-')
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
path = self.image_directory.format(filename=self.id, ext="png")
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
else:
|
||||
return None
|
||||
|
||||
@dataclass
|
||||
class Location:
|
||||
name: str
|
||||
address1: str = ""
|
||||
address2: str = ""
|
||||
image_directory: str = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.name.lower().replace(' ', '-')
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
path = self.image_directory.format(filename=self.id, ext="png")
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
return ",".join([self.address1,self.address2])
|
||||
|
||||
args = {
|
||||
"team_fave" : Team("Hounds"),
|
||||
"team_opponent" : Team("Trojans"),
|
||||
"home": False,
|
||||
"date" : "2021-05-08 12:30 pm",
|
||||
"location" : Location("Maywood", image_directory="benchcoachproject/static/teamsnap/ig/locations/maywood.{ext}"),
|
||||
"runs_for": 8,
|
||||
"runs_against": 9
|
||||
}
|
||||
|
||||
def gen_image (team_fave, team_opponent, date, location=None,
|
||||
location_name = None,
|
||||
home=False,
|
||||
background='location',
|
||||
address = None,
|
||||
width = 1080,
|
||||
height = 1080,
|
||||
*kwargs,
|
||||
**args
|
||||
):
|
||||
if not isinstance(date, datetime):
|
||||
# date = parser.parse(date)
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
pass
|
||||
|
||||
if location.image and background == 'location':
|
||||
background_image = Image.open(location.image).copy()
|
||||
background_image = background_image.resize((width, height))
|
||||
# background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5))
|
||||
background_image = background_image.convert("RGBA")
|
||||
elif background == 'transparent':
|
||||
background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
else:
|
||||
background_image = Image.new('RGBA', (width, height), (50, 55, 102))
|
||||
|
||||
title_images = []
|
||||
for team in [team_fave, team_opponent]:
|
||||
if team.image:
|
||||
title_images.append(Image.open(team.image).copy())
|
||||
else:
|
||||
title_images.append(Image.new('RGBA', (1080, 1080)))
|
||||
|
||||
title_image_left = title_images[0]
|
||||
title_image_right = title_images[1]
|
||||
|
||||
# Make a blank image for the rectangle, initialized to a completely
|
||||
# transparent color.
|
||||
tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0))
|
||||
|
||||
# Create a drawing context for it.
|
||||
draw = ImageDraw.Draw(tmp)
|
||||
|
||||
# section margin describes the margin of the section rectangles from the sides of the image
|
||||
section_margin_pct = .05
|
||||
llx = int(section_margin_pct * background_image.size[0])
|
||||
urx = int((1 - section_margin_pct) * background_image.size[0])
|
||||
lly = int((1 - section_margin_pct) * background_image.size[1])
|
||||
ury = int(.50 * background_image.size[1])
|
||||
|
||||
lly2 = int(.49 * background_image.size[1])
|
||||
ury2 = int(.05 * background_image.size[1])
|
||||
|
||||
section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png")))
|
||||
section_info_draw = ImageDraw.Draw(section_info)
|
||||
|
||||
section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png")))
|
||||
section_title_draw = ImageDraw.Draw(section_title)
|
||||
|
||||
# First line: Date
|
||||
font = ImageFont.truetype(font_regular_path, 62)
|
||||
text = "{:%a, %B %-d %-I:%M %p}".format(date).upper()
|
||||
# text = date
|
||||
text_size = draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
280
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
# Second line: Venue
|
||||
font = ImageFont.truetype(font_condensed_path, 34)
|
||||
if not location_name:
|
||||
text = location.name.upper()
|
||||
else:
|
||||
text = location_name.upper()
|
||||
text_size = section_info_draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
355
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
font = ImageFont.truetype(font_regular_path, 80)
|
||||
if home:
|
||||
text = "VS"
|
||||
else:
|
||||
text = "AT"
|
||||
text_size = section_title_draw.textsize(text, font)
|
||||
loc = (
|
||||
540,
|
||||
120
|
||||
)
|
||||
color = (255, 255, 255)
|
||||
section_title_draw.text(loc, text, color, font=font, anchor="mm")
|
||||
|
||||
# Alpha composite the two images together.
|
||||
background_image = Image.alpha_composite(background_image, tmp)
|
||||
|
||||
# Title Image Left
|
||||
title_image_left.thumbnail([350, 350])
|
||||
loc = (
|
||||
50, -50
|
||||
)
|
||||
section_title.paste(title_image_left, loc, title_image_left)
|
||||
|
||||
# Title Image Right
|
||||
title_image_right.thumbnail([350, 350])
|
||||
loc = (
|
||||
650, -50
|
||||
)
|
||||
section_title.paste(title_image_right, loc, title_image_right)
|
||||
|
||||
# background_image.paste(section_info, (llx, ury), section_info)
|
||||
# background_image.paste(section_title, (llx, ury2), section_title)
|
||||
section_title.paste(section_info,(0,0),section_info)
|
||||
section_title.thumbnail([800, 800])
|
||||
|
||||
if background=="badge":
|
||||
return section_title
|
||||
|
||||
background_image.paste(section_title,(
|
||||
int((background_image.size[0]-section_title.size[0])/2),
|
||||
height - 360
|
||||
),section_title)
|
||||
|
||||
return background_image
|
||||
|
||||
def gen_results_image (team_fave, team_opponent, date,
|
||||
location=None,
|
||||
location_name = None,
|
||||
home=False,
|
||||
background='location',
|
||||
address = None,
|
||||
width = 1080,
|
||||
height = 1080,
|
||||
runs_for=0,
|
||||
runs_against=0,
|
||||
*kwargs,
|
||||
**args
|
||||
):
|
||||
if not isinstance(date, datetime):
|
||||
# date = parser.parse(date)
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
pass
|
||||
|
||||
if location.image and background == 'location':
|
||||
background_image = Image.open(location.image).copy()
|
||||
background_image = background_image.resize((width, height))
|
||||
# background_image = background_image.filter(ImageFilter.GaussianBlur(radius=5))
|
||||
background_image = background_image.convert("RGBA")
|
||||
elif background == 'transparent':
|
||||
background_image = Image.new('RGBA', (width, height), (0, 0, 0, 0))
|
||||
else:
|
||||
background_image = Image.new('RGBA', (width, height), (50, 55, 102))
|
||||
|
||||
title_images = []
|
||||
for team in [team_fave, team_opponent]:
|
||||
if team.image:
|
||||
title_images.append(Image.open(team.image).copy())
|
||||
else:
|
||||
title_images.append(Image.new('RGBA', (1080, 1080)))
|
||||
|
||||
title_image_left = title_images[0]
|
||||
title_image_right = title_images[1]
|
||||
|
||||
# Make a blank image for the rectangle, initialized to a completely
|
||||
# transparent color.
|
||||
tmp = Image.new('RGBA', background_image.size, (0, 0, 0, 0))
|
||||
|
||||
# Create a drawing context for it.
|
||||
draw = ImageDraw.Draw(tmp)
|
||||
|
||||
# section margin describes the margin of the section rectangles from the sides of the image
|
||||
section_margin_pct = .05
|
||||
llx = int(section_margin_pct * background_image.size[0])
|
||||
urx = int((1 - section_margin_pct) * background_image.size[0])
|
||||
lly = int((1 - section_margin_pct) * background_image.size[1])
|
||||
ury = int(.50 * background_image.size[1])
|
||||
|
||||
lly2 = int(.49 * background_image.size[1])
|
||||
ury2 = int(.05 * background_image.size[1])
|
||||
|
||||
#todo fix path
|
||||
section_info = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-tan", ext=".png")))
|
||||
section_info_draw = ImageDraw.Draw(section_info)
|
||||
|
||||
section_title = Image.open(Path('benchcoachproject/static/teamsnap/ig/graphics/{name}{ext}'.format(name="sign-green", ext=".png")))
|
||||
section_title_draw = ImageDraw.Draw(section_title)
|
||||
|
||||
# First line: Results
|
||||
loc = (
|
||||
1050,
|
||||
265
|
||||
)
|
||||
if runs_for > runs_against:
|
||||
result_letter = "W"
|
||||
elif runs_for < runs_against:
|
||||
result_letter = "L"
|
||||
elif runs_for == runs_against:
|
||||
result_letter = "T"
|
||||
font = ImageFont.truetype(font_regular_path, 100)
|
||||
section_info_draw.text(loc, f"FINAL: {result_letter} {runs_for}-{runs_against}", (14,42,28), font=font, anchor="ra")
|
||||
|
||||
# Second line: Date
|
||||
text = "{:%a, %B %-d %-I:%M %p}".format(date).upper()
|
||||
# text = date
|
||||
font = ImageFont.truetype(font_condensed_path, 34)
|
||||
text_size = section_info_draw.textsize(text, font)
|
||||
loc = (
|
||||
1050,
|
||||
355
|
||||
)
|
||||
section_info_draw.text(loc, text, (14,42,28), font=font, anchor="ra")
|
||||
|
||||
font = ImageFont.truetype(font_regular_path, 80)
|
||||
if home:
|
||||
text = "VS"
|
||||
else:
|
||||
text = "AT"
|
||||
text_size = section_title_draw.textsize(text, font)
|
||||
loc = (
|
||||
540,
|
||||
120
|
||||
)
|
||||
color = (255, 255, 255)
|
||||
section_title_draw.text(loc, text, color, font=font, anchor="mm")
|
||||
|
||||
# Alpha composite the two images together.
|
||||
background_image = Image.alpha_composite(background_image, tmp)
|
||||
|
||||
# Title Image Left
|
||||
title_image_left.thumbnail([350, 350])
|
||||
loc = (
|
||||
50, -50
|
||||
)
|
||||
section_title.paste(title_image_left, loc, title_image_left)
|
||||
|
||||
# Title Image Right
|
||||
title_image_right.thumbnail([350, 350])
|
||||
loc = (
|
||||
650, -50
|
||||
)
|
||||
section_title.paste(title_image_right, loc, title_image_right)
|
||||
|
||||
# background_image.paste(section_info, (llx, ury), section_info)
|
||||
# background_image.paste(section_title, (llx, ury2), section_title)
|
||||
section_title.paste(section_info,(0,0),section_info)
|
||||
section_title.thumbnail([800, 800])
|
||||
|
||||
if background=="badge":
|
||||
return section_title
|
||||
|
||||
background_image.paste(section_title,(
|
||||
int((background_image.size[0]-section_title.size[0])/2),
|
||||
height - 360
|
||||
),section_title)
|
||||
|
||||
# background_image.show()
|
||||
|
||||
return background_image
|
||||
|
||||
# gen_results_image(**args)
|
||||
@@ -3,7 +3,8 @@ from typing import List, Tuple
|
||||
|
||||
import benchcoach.models
|
||||
from benchcoach.models import BenchcoachModel, Availability, Player, Team, Positioning, Event, Venue
|
||||
from teamsnap.teamsnap.api import TeamSnap
|
||||
from pyteamsnap.api import TeamSnap
|
||||
import pyteamsnap
|
||||
import teamsnap.models
|
||||
from django.db.models import QuerySet
|
||||
|
||||
@@ -50,14 +51,14 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
}
|
||||
|
||||
teamsnapmodel_to_apiobject = {
|
||||
teamsnap.models.Availability: teamsnap.teamsnap.api.Availability,
|
||||
teamsnap.models.Event: teamsnap.teamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:teamsnap.teamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: teamsnap.teamsnap.api.Location,
|
||||
teamsnap.models.Member: teamsnap.teamsnap.api.Member,
|
||||
teamsnap.models.Opponent: teamsnap.teamsnap.api.Opponent,
|
||||
teamsnap.models.Team: teamsnap.teamsnap.api.Team,
|
||||
# teamsnap.models.User:teamsnap.teamsnap.api.User # Not implemented yet
|
||||
teamsnap.models.Availability: pyteamsnap.api.Availability,
|
||||
teamsnap.models.Event: pyteamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: pyteamsnap.api.Location,
|
||||
teamsnap.models.Member: pyteamsnap.api.Member,
|
||||
teamsnap.models.Opponent: pyteamsnap.api.Opponent,
|
||||
teamsnap.models.Team: pyteamsnap.api.Team,
|
||||
# teamsnap.models.User:pyteamsnap.api.User # Not implemented yet
|
||||
}
|
||||
|
||||
apiobject_to_teamsnapmodel = {v:k for k,v in teamsnapmodel_to_apiobject.items()}
|
||||
@@ -104,14 +105,14 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
}
|
||||
|
||||
teamsnapmodel_to_apiobject = {
|
||||
teamsnap.models.Availability: teamsnap.teamsnap.api.Availability,
|
||||
teamsnap.models.Event: teamsnap.teamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:teamsnap.teamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: teamsnap.teamsnap.api.Location,
|
||||
teamsnap.models.Member: teamsnap.teamsnap.api.Member,
|
||||
teamsnap.models.Opponent: teamsnap.teamsnap.api.Opponent,
|
||||
teamsnap.models.Team: teamsnap.teamsnap.api.Team,
|
||||
# teamsnap.models.User:teamsnap.teamsnap.api.User # Not implemented yet
|
||||
teamsnap.models.Availability: pyteamsnap.api.Availability,
|
||||
teamsnap.models.Event: pyteamsnap.api.Event,
|
||||
# teamsnap.models.LineupEntry:pyteamsnap.api.LineupEntry, # Not implemented Yet
|
||||
teamsnap.models.Location: pyteamsnap.api.Location,
|
||||
teamsnap.models.Member: pyteamsnap.api.Member,
|
||||
teamsnap.models.Opponent: pyteamsnap.api.Opponent,
|
||||
teamsnap.models.Team: pyteamsnap.api.Team,
|
||||
# teamsnap.models.User:pyteamsnap.api.User # Not implemented yet
|
||||
}
|
||||
|
||||
if isinstance(benchcoach_instance, benchcoach.models.Team):
|
||||
@@ -128,9 +129,9 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
r = self._update_teamsnapdb_to_benchcoachdb(teamsnap_instance, benchcoach_instance)
|
||||
return r
|
||||
|
||||
def _update_from_teamsnapdata(self, teamsnap_instance:teamsnap.models.TeamsnapBaseModel, teamsnap_data: teamsnap.teamsnap.api.ApiObject) -> teamsnap.models.TeamsnapBaseModel:
|
||||
def _update_from_teamsnapdata(self, teamsnap_instance:teamsnap.models.TeamsnapBaseModel, teamsnap_data: pyteamsnap.api.ApiObject) -> teamsnap.models.TeamsnapBaseModel:
|
||||
''''''
|
||||
if isinstance(teamsnap_data, teamsnap.teamsnap.api.ApiObject):
|
||||
if isinstance(teamsnap_data, pyteamsnap.api.ApiObject):
|
||||
teamsnap_data = teamsnap_data.data
|
||||
else:
|
||||
raise TypeError
|
||||
@@ -390,7 +391,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
r['team'] = []
|
||||
|
||||
# Search API for objects belonging to currently managed team, and iterate
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
||||
for teamsnap_data in pyteamsnap.api.Team.search(client=self.client, id=self.managed_teamsnap_team_id):
|
||||
# check if TeamSnap ID already exists in the Teamsnap DB.
|
||||
if teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Team.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
@@ -417,7 +418,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
# Opponents from teamsnap go to the BenchCoach "Team" database.
|
||||
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Opponent.search(**kwargs):
|
||||
for teamsnap_data in pyteamsnap.api.Opponent.search(**kwargs):
|
||||
if teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Opponent.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
@@ -435,7 +436,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
# Dependent on Team. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['location'] = []
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Location.search(**kwargs):
|
||||
for teamsnap_data in pyteamsnap.api.Location.search(**kwargs):
|
||||
if teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Location.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
@@ -455,7 +456,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['member'] = []
|
||||
# Search API for members to import. Note: Non players are not included in sync.
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Member.search(**kwargs,
|
||||
for teamsnap_data in pyteamsnap.api.Member.search(**kwargs,
|
||||
is_non_player = False
|
||||
):
|
||||
if teamsnap_data.data['is_non_player'] == True:
|
||||
@@ -478,7 +479,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
# Dependent on Team, Opponent, Location. These objects need to be available to attach as related objects or the functions
|
||||
# self._update_from teamsnapdata and self.update_teamsnapdb_to_benchcoachdb may fail.
|
||||
r['event'] = []
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Event.search(**kwargs):
|
||||
for teamsnap_data in pyteamsnap.api.Event.search(**kwargs):
|
||||
if teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']):
|
||||
teamsnap_instance = teamsnap.models.Event.objects.filter(id=teamsnap_data.data['id']).first()
|
||||
benchcoach_instance = teamsnap_instance.benchcoach_object
|
||||
@@ -504,7 +505,7 @@ class TeamsnapSyncEngine(AbstractSyncEngine):
|
||||
|
||||
# Search API for members to import. Note: Non players are not included in sync.
|
||||
player_ids = [member.id for member in teamsnap.models.Member.objects.filter(is_non_player=False)]
|
||||
for teamsnap_data in teamsnap.teamsnap.api.Availability.search(**kwargs,
|
||||
for teamsnap_data in pyteamsnap.api.Availability.search(**kwargs,
|
||||
member_id=",".join(player_ids)
|
||||
):
|
||||
if teamsnap.models.Availability.objects.filter(id=teamsnap_data.data['id']):
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import operator
|
||||
import time
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
|
||||
from .models import User, Member, Team, Event, Location, LineupEntry, Opponent, Availability
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponse, HttpResponseNotAllowed
|
||||
import benchcoach.models
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from .utils.teamsnap_sync_engine import TeamsnapSyncEngine
|
||||
from django.templatetags.static import static
|
||||
import datetime
|
||||
|
||||
def edit_event(request, id):
|
||||
'''
|
||||
@@ -112,6 +117,7 @@ def sync_from_teamsnap(request, object_name=None, object_id=None):
|
||||
return HttpResponse(404)
|
||||
|
||||
|
||||
|
||||
def import_teamsnap(request):
|
||||
TEAM_ID = request.user.profile.teamsnapsettings.managed_team.id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
@@ -127,5 +133,437 @@ def import_teamsnap(request):
|
||||
|
||||
return redirect('teamsnap home')
|
||||
|
||||
def schedule(request, team_id):
|
||||
TEAM_ID = team_id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
no_past = bool(request.GET.get('no_past', 0))
|
||||
games_only = bool(request.GET.get('games_only',0))
|
||||
from pyteamsnap.api import TeamSnap, Event, Location, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(.5)
|
||||
ts_events = Event.search(client, team_id=TEAM_ID)
|
||||
if no_past:
|
||||
ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)]
|
||||
if games_only:
|
||||
ts_events = [e for e in ts_events if e.data['is_game']]
|
||||
ts_events = {e.data['id']:e for e in ts_events}
|
||||
# ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)}
|
||||
# ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)}
|
||||
# for event in ts_events:
|
||||
|
||||
|
||||
pass
|
||||
return render(request, "teamsnap/schedule.html", context={"events":ts_events.values(), "team_id":team_id})
|
||||
|
||||
def event(request, event_id, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
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)][0]
|
||||
# ts_availabilities = Availability.search(client, event_id=ts_event.data['id'])
|
||||
ts_availability_summary = \
|
||||
[i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0]
|
||||
ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_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}
|
||||
# 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 = []
|
||||
|
||||
return render(request, "teamsnap/view_event.html", context={
|
||||
"availability_summary":ts_availability_summary,
|
||||
"event":ts_event,
|
||||
"availablities":[],
|
||||
"lineup_entries": ts_lineup_entries,
|
||||
})
|
||||
|
||||
def location(request, id, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Location
|
||||
client = TeamSnap(token=TOKEN)
|
||||
return render(request, "teamsnap/location/view.html", context={"location": Location.get(client, id=id)})
|
||||
pass
|
||||
|
||||
def opponent(request, id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Opponent
|
||||
time.sleep(0.5)
|
||||
client = TeamSnap(token=TOKEN)
|
||||
return render(request, "teamsnap/opponent.html", context={"opponent": Opponent.get(client, id=id)})
|
||||
pass
|
||||
|
||||
def edit_lineup(request, event_id, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
|
||||
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)][0]
|
||||
ts_availabilities = Availability.search(client, event_id=ts_event.data['id'])
|
||||
ts_availability_summary = \
|
||||
[i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0]
|
||||
ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id]
|
||||
|
||||
if ts_lineup_entries:
|
||||
ts_lineup = EventLineup.get(client, id=ts_lineup_entries[0].data['event_lineup_id'])
|
||||
else:
|
||||
ts_lineup = EventLineup.search(client, event_id=event_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}
|
||||
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:
|
||||
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, # No Response
|
||||
0:2, # No
|
||||
2:1, # Maybe
|
||||
1:0 # Yes
|
||||
|
||||
}.get(d['availability'].get('status_code')),
|
||||
d['member'].get('last_name'))
|
||||
)
|
||||
|
||||
|
||||
from teamsnap.forms import LineupEntryFormset, LineupEntryForm
|
||||
|
||||
formset = LineupEntryFormset(
|
||||
initial=[
|
||||
{
|
||||
"event_lineup_entry_id" : member['lineup_entry'].get('id'),
|
||||
"event_lineup_id" : member['lineup_entry'].get('event_lineup_id'),
|
||||
"event_id": event_id,
|
||||
"member_id" : member['member']['id'],
|
||||
"sequence" : member['lineup_entry'].get('sequence'),
|
||||
"label" : member['lineup_entry'].get('label'),
|
||||
}
|
||||
for member in members
|
||||
]
|
||||
)
|
||||
|
||||
for form in formset:
|
||||
form.member = ts_member_lookup.get(form['member_id'].initial)
|
||||
form.availability = ts_availability_lookup.get(form['member_id'].initial)
|
||||
|
||||
formset_lineup = [form for form in formset if form.initial.get('event_lineup_entry_id')]
|
||||
formset_lineup = sorted(
|
||||
formset_lineup,
|
||||
key=lambda d: d.initial.get('sequence',100)
|
||||
)
|
||||
formset_bench = [form for form in formset if
|
||||
form not in formset_lineup and
|
||||
form.availability.data['status_code'] in [2, 1]
|
||||
]
|
||||
formset_out = [form for form in formset if
|
||||
form not in formset_lineup and
|
||||
form not in formset_bench and
|
||||
not form.member.data['is_non_player']
|
||||
]
|
||||
|
||||
return render(request, "teamsnap/lineup/edit.html", context={
|
||||
"team_id": team_id,
|
||||
"event_id": event_id,
|
||||
"event": ts_event,
|
||||
"formset": formset,
|
||||
"formset_lineup":formset_lineup,
|
||||
"formset_bench": formset_bench,
|
||||
"formset_out": formset_out,
|
||||
"lineup": ts_lineup
|
||||
})
|
||||
|
||||
def edit_multiple_lineups(request, team_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
from django.forms import formset_factory
|
||||
from teamsnap.forms import EventChooseForm
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
|
||||
ts_events = Event.search(client, team_id=team_id)
|
||||
EventChooseFormset = formset_factory(EventChooseForm)
|
||||
formset = EventChooseFormset(request.GET)
|
||||
choices = [(e.data['id'], e.data['formatted_title']) for e in ts_events]
|
||||
|
||||
for form in formset:
|
||||
form.fields['event_id'].choices = choices
|
||||
|
||||
if formset.is_valid():
|
||||
event_ids = [f.cleaned_data['event_id'] for f in formset]
|
||||
else:
|
||||
event_ids = request.GET.get("event_ids").split(",")
|
||||
|
||||
ts_bulkload = client.bulk_load(team_id=team_id,
|
||||
types=[Event, EventLineup, EventLineupEntry, AvailabilitySummary, Member],
|
||||
event__id=",".join(event_ids))
|
||||
event_ids = [int(i) for i in event_ids]
|
||||
formsets_lineup = []
|
||||
formsets_bench = []
|
||||
formsets = []
|
||||
events = []
|
||||
contexts = []
|
||||
for event_id in event_ids:
|
||||
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_availability_summary = \
|
||||
[i for i in ts_bulkload if isinstance(i, AvailabilitySummary) and i.data['event_id'] == event_id][0]
|
||||
ts_lineup_entries = [i for i in ts_bulkload if isinstance(i, EventLineupEntry) and i.data['event_id'] == event_id]
|
||||
|
||||
if ts_lineup_entries:
|
||||
ts_lineup = EventLineup.get(client, id=ts_lineup_entries[0].data['event_lineup_id'])
|
||||
else:
|
||||
ts_lineup = EventLineup.search(client, event_id=event_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}
|
||||
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:
|
||||
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, # No Response
|
||||
0:2, # No
|
||||
2:1, # Maybe
|
||||
1:0 # Yes
|
||||
|
||||
}.get(d['availability'].get('status_code')),
|
||||
d['member'].get('last_name'))
|
||||
)
|
||||
|
||||
|
||||
from teamsnap.forms import LineupEntryFormset, LineupEntryForm
|
||||
|
||||
formset = LineupEntryFormset(
|
||||
initial=[
|
||||
{
|
||||
"event_lineup_entry_id" : member['lineup_entry'].get('id'),
|
||||
"event_lineup_id" : member['lineup_entry'].get('event_lineup_id'),
|
||||
"event_id": event_id,
|
||||
"member_id" : member['member']['id'],
|
||||
"sequence" : member['lineup_entry'].get('sequence'),
|
||||
"label" : member['lineup_entry'].get('label'),
|
||||
}
|
||||
for member in members
|
||||
]
|
||||
)
|
||||
|
||||
for form in formset:
|
||||
form.member = ts_member_lookup.get(form['member_id'].initial)
|
||||
form.availability = ts_availability_lookup.get(form['member_id'].initial)
|
||||
|
||||
formset_lineup = [form for form in formset if form.initial.get('event_lineup_entry_id')]
|
||||
formset_lineup = sorted(
|
||||
formset_lineup,
|
||||
key=lambda d: d.initial.get('sequence',100)
|
||||
)
|
||||
formset_bench = [form for form in formset if
|
||||
form not in formset_lineup and
|
||||
form.availability.data['status_code'] in [2, 1]
|
||||
]
|
||||
formset_out = [form for form in formset if
|
||||
form not in formset_lineup and
|
||||
form not in formset_bench and
|
||||
not form.member.data['is_non_player']
|
||||
]
|
||||
|
||||
contexts.append({
|
||||
"event":ts_event,
|
||||
"formset": formset,
|
||||
"formset_bench":formset_bench,
|
||||
"formset_lineup":formset_lineup,
|
||||
"formset_out":formset_out
|
||||
})
|
||||
|
||||
return render(request, "teamsnap/lineup/multiple_edit.html", context={
|
||||
"team_id": team_id,
|
||||
"contexts":contexts
|
||||
})
|
||||
|
||||
def submit_lineup(request, team_id, event_id):
|
||||
from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup
|
||||
from teamsnap.forms import LineupEntryFormset
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_lineup = EventLineup.search(client, event_id=event_id)
|
||||
event_lineup_id = ts_lineup[0].data['id']
|
||||
if request.GET:
|
||||
return HttpResponseNotAllowed()
|
||||
if request.POST:
|
||||
formset = LineupEntryFormset(request.POST)
|
||||
if formset.is_valid():
|
||||
r = []
|
||||
for form in formset:
|
||||
data = form.cleaned_data
|
||||
if data.get('event_lineup_entry_id'):
|
||||
event_lineup_entry = EventLineupEntry.get(client, id=data.get('event_lineup_entry_id'))
|
||||
event_lineup_entry.data.update(data)
|
||||
# breakpoint()
|
||||
r.append(event_lineup_entry.put())
|
||||
# breakpoint()
|
||||
pass
|
||||
elif data.get('sequence') is not None and data.get('label'):
|
||||
event_lineup_entry = EventLineupEntry.new(client)
|
||||
event_lineup_entry.data.update(data)
|
||||
event_lineup_entry.data.update({"event_lineup_id":event_lineup_id})
|
||||
r.append(event_lineup_entry.post())
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
# breakpoint()
|
||||
pass
|
||||
# breakpoint()
|
||||
pass
|
||||
return HttpResponse(f'{team_id} {event_id}')
|
||||
pass
|
||||
|
||||
return HttpResponse(f'{team_id} {event_id}')
|
||||
|
||||
def image_generator(request, team_id, event_id):
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
|
||||
from pyteamsnap.api import TeamSnap, Event, Availability, Member, EventLineupEntry, EventLineup, AvailabilitySummary
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(0.5)
|
||||
|
||||
ts_event = Event.get(client, id=event_id)
|
||||
return render(request, "teamsnap/event/instagen.html", context = {"event":ts_event})
|
||||
|
||||
# @app.route('/get_matchup_image')
|
||||
def get_matchup_image(request, team_id, event_id, dimensions=None, background=None):
|
||||
from pyteamsnap.api import TeamSnap, EventLineupEntry, EventLineup, Event, Team, Opponent, Location
|
||||
from .utils.gen_image import Team as ImagegenTeam, Location as ImagegenLocation
|
||||
from .utils.gen_image import gen_image, gen_results_image
|
||||
import io
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
if request.GET:
|
||||
POSTPONED = request.GET.get('postponed', 'false') == 'true'
|
||||
INCLUDE_WINLOSS = request.GET.get('winloss', 'false') == 'true'
|
||||
BACKGROUND = request.GET.get('background', 'location')
|
||||
game_id = event_id
|
||||
dimensions = request.GET.get('dimensions')
|
||||
width = int(dimensions.split("x")[0])
|
||||
height = int(dimensions.split("x")[1])
|
||||
|
||||
teamsnap = TeamSnap(TOKEN)
|
||||
time.sleep(0.5)
|
||||
ts_event = Event.get(teamsnap, game_id).data
|
||||
fave_team = Team.get(teamsnap, ts_event['team_id']).data
|
||||
opponent_team = Opponent.get(teamsnap, ts_event['opponent_id']).data
|
||||
location = Location.get(teamsnap, ts_event['location_id']).data
|
||||
formatted_results = ts_event['formatted_results']
|
||||
if formatted_results:
|
||||
# L 4-3
|
||||
runs_for = formatted_results.split(" ")[1].split("-")[0]
|
||||
runs_against = formatted_results.split(" ")[1].split("-")[1]
|
||||
else:
|
||||
runs_for, runs_against = None, None
|
||||
|
||||
logo_image_directory = 'benchcoachproject/static/teamsnap/ig/logos-bw/{filename}.{ext}'
|
||||
venue_image_directory = 'benchcoachproject/static/teamsnap/ig/locations/{filename}.{ext}'
|
||||
|
||||
def shortname_from_name(name):
|
||||
return name.replace(" ", "").lower()
|
||||
|
||||
# date = parser.parse(ts_event['start_date'])
|
||||
# date = date.astimezone(ZoneInfo("America/Chicago"))
|
||||
game_info = {
|
||||
"date": ts_event['start_date'],
|
||||
"team_fave": ImagegenTeam(
|
||||
name=fave_team["name"],
|
||||
image_directory=logo_image_directory.format(filename=shortname_from_name(fave_team["name"]), ext="png")
|
||||
),
|
||||
"team_opponent": ImagegenTeam(
|
||||
name=opponent_team["name"],
|
||||
image_directory=logo_image_directory.format(filename=shortname_from_name(opponent_team["name"]),
|
||||
ext="png")
|
||||
),
|
||||
"location": ImagegenLocation(
|
||||
name=location['name'],
|
||||
image_directory=venue_image_directory.format(filename=shortname_from_name(location["name"]), ext="png"),
|
||||
# address=location['address']
|
||||
),
|
||||
"runs_for": runs_for,
|
||||
"runs_against": runs_against
|
||||
}
|
||||
|
||||
if not game_info['runs_for'] and not game_info['runs_against']:
|
||||
image = gen_image(**game_info, background=BACKGROUND, width=width, height=height)
|
||||
elif game_info['runs_for'] and game_info['runs_against']:
|
||||
image = gen_results_image(**game_info, background=BACKGROUND, width=width, height=height)
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
imgByteArr = io.BytesIO()
|
||||
image.save(imgByteArr, format='PNG')
|
||||
imgByteArr = imgByteArr.getvalue()
|
||||
|
||||
return HttpResponse(imgByteArr, content_type="image/png")
|
||||
|
||||
def multi_lineup_choose(request, team_id):
|
||||
from teamsnap.forms import EventChooseForm
|
||||
from django.forms import formset_factory
|
||||
if not request.GET.get('num'):
|
||||
return HttpResponse(500)
|
||||
else:
|
||||
num = int(request.GET.get('num'))
|
||||
TEAM_ID = team_id
|
||||
TOKEN = request.user.profile.teamsnap_access_token
|
||||
no_past = bool(request.GET.get('no_past', 0))
|
||||
games_only = bool(request.GET.get('games_only', 0))
|
||||
from pyteamsnap.api import TeamSnap, Event, Location, Opponent
|
||||
client = TeamSnap(token=TOKEN)
|
||||
time.sleep(.5)
|
||||
ts_events = Event.search(client, team_id=TEAM_ID)
|
||||
if no_past:
|
||||
ts_events = [e for e in ts_events if e.data['start_date'] > datetime.datetime.now(datetime.timezone.utc)]
|
||||
if games_only:
|
||||
ts_events = [e for e in ts_events if e.data['is_game']]
|
||||
ts_events = {e.data['id']: e for e in ts_events}
|
||||
# ts_opponents = {o.data['id']:o for o in Opponent.search(client, team_id=TEAM_ID)}
|
||||
# ts_locations = {l.data['id']:l for l in Location.search(client, team_id=TEAM_ID)}
|
||||
# for event in ts_events:
|
||||
|
||||
EventChooseFormset = formset_factory(EventChooseForm, extra=num)
|
||||
formset = EventChooseFormset()
|
||||
|
||||
choices= [(id, e.data['formatted_title']) for id, e in ts_events.items()]
|
||||
|
||||
for form in formset:
|
||||
form.fields['event_id'].choices = choices
|
||||
|
||||
pass
|
||||
return render(request, "teamsnap/lineup/multiple_choose.html", context={"formset": formset, "team_id": team_id})
|
||||
@@ -3,11 +3,9 @@
|
||||
|
||||
<head>
|
||||
<title>{% block title %}Title{% endblock %}</title>
|
||||
{% include 'base_layout.html' %}
|
||||
{% include 'head.html' %}
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body class="bg-light">
|
||||
<header class="min-vw-100">
|
||||
<div>
|
||||
@@ -18,24 +16,12 @@
|
||||
|
||||
</header>
|
||||
|
||||
<div class="d-inline-flex min-vw-100 min-vh-100" style="padding-top:3rem;">
|
||||
<div id="sidebar" class="">
|
||||
{% block sidebar %}
|
||||
{% include 'sidebar.html' %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="w-100 m-0">
|
||||
<div class="bg-white border border-bottom">
|
||||
<h1 class="m-2">{% block page_heading %}{% endblock %}</h1>
|
||||
<h6 class="m-2 text-muted">{% block page_subheading %}{% endblock %}</h6>
|
||||
<main class="container-sm bg-white rounded-3 shadow-sm my-2">
|
||||
<div class="p-2">
|
||||
<h2 class="m border-bottom">{% block page_heading %}{% endblock %}</h2>
|
||||
<h6 class="m text-muted">{% block page_subheading %}{% endblock %}</h6>
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</main>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
{% extends "base.html" %}{% load static %}
|
||||
|
||||
{% block title %}Bench Coach Home{% endblock %}
|
||||
{% block page_heading %}
|
||||
<div class="text-center my-2">
|
||||
<h1><img class="mx-auto" src="{% static 'benchcoach.svg' %}" style="width: 64px;" />
|
||||
<strong>Welcome to Bench Coach</strong>
|
||||
</h1>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="text-center my-2">
|
||||
<h1><img class="mx-auto" src="{% static 'benchcoach.svg' %}" style="width: 64px;" /><strong>Welcome to Bench Coach</strong></h1>
|
||||
|
||||
<div class="col-lg-6 m-auto">
|
||||
<p class="lead mb-4">Quisque at curabitur mollis ornare, malesuada maecenas. Orci elit tristique, malesuada eu pharetra. Est praesent tortor porttitor aptent, amet quisque.</p>
|
||||
{# <div class="d-grid gap-2 d-sm-flex justify-content-sm-center mx-1">#}
|
||||
|
||||
@@ -1,22 +1,48 @@
|
||||
{% load static %}
|
||||
<nav class="navbar navbar-dark navbar-expand-md py-0 fixed-top" style="background: #323669;height:3rem">
|
||||
<a class="navbar-brand d-flex text-uppercase fw-bold m-1 mx-2 text-dark" data-bs-toggle="collapse" data-bs-target="#sidebar">
|
||||
<nav class="navbar navbar-dark navbar-expand-lg" style="background: #323669">
|
||||
<a class="navbar-brand text-uppercase fw-bold mx-2 text-dark">
|
||||
<img class="d-inline-block" width="32" height="32" src="{% static 'benchcoach.svg' %}" />
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">bench coach</span>
|
||||
<span class="text-white">bench coach</span>
|
||||
</a>
|
||||
<button class="navbar-toggler m-2 mx-4 p-0 d-none" data-bs-toggle="collapse" data-bs-target="#navcol-2">
|
||||
<span class="visually-hidden">Toggle navigation</span>
|
||||
<span class="navbar-toggler-icon m-1"></span></button>
|
||||
<div id="navcol-2" class="collapse navbar-collapse">
|
||||
<button class="navbar-toggler mx-2" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mx-2">
|
||||
<li class="nav-item"><a class="nav-link {{ events_tab }}" href="{% url 'event list' %}">Events</a></li>
|
||||
<li class="nav-item"><a class="nav-link {{ members_tab }}" href="{% url 'player list' %}">Members</a></li>
|
||||
<li class="nav-item"><a class="nav-link {{ opponents_tab }}" href="{% url 'team list' %}">Opponents</a></li>
|
||||
<li class="nav-item"><a class="nav-link {{ venues_tab }}" href="{% url 'venue list' %}">Venues</a></li>
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link{% if request.resolver_match.url_name == 'home' %} active{% endif %} text-decoration-none" href="{% url 'home'%}">
|
||||
<i class="bi bi-house-fill"></i>
|
||||
<span class="">
|
||||
Home
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if request.resolver_match.url_name == 'teamsnap_home' %} active{% endif %} text-decoration-none" href="{% url 'teamsnap_home'%}">
|
||||
<i class="bi bi-asterisk"></i>
|
||||
<span class="">
|
||||
Teamsnap
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if request.resolver_match.url_name == 'teamsnap_schedule' %} active{% endif %} text-decoration-none" href="{% url 'teamsnap_schedule' team_id=request.user.profile.teamsnapsettings.managed_team.id%}">
|
||||
<i class="bi bi-calendar-fill"></i>
|
||||
<span class="">
|
||||
Schedule
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if request.resolver_match.url_name == 'teamsnap_choose_multiple_lineups' %} active{% endif %} text-decoration-none" href="{% url 'teamsnap_choose_multiple_lineups' team_id=request.user.profile.teamsnapsettings.managed_team.id%}?num=3">
|
||||
<i class="bi bi-layout-three-columns"></i>
|
||||
<span class="">
|
||||
Multi-Lineup
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<span class="mx-auto"></span>
|
||||
<span class="navbar-text"></span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,53 +1,14 @@
|
||||
<div class="bg-dark h-100">
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ events_tab }} text-white" href="{% url 'event list' %}">
|
||||
<a class="nav-link {{ events_tab }} text-white" href="{% url 'teamsnap_schedule' team_id=request.user.profile.teamsnapsettings.managed_team.id%}">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<i class="bi bi-calendar3"></i>
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">
|
||||
Events
|
||||
Schedule
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ members_tab }} text-white" href="{% url 'player list' %}">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<i class="bi bi-person-fill"></i>
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">
|
||||
Players
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ opponents_tab }} text-white" href="{% url 'team list' %}">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<i class="bi bi-people-fill"></i>
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">
|
||||
Teams
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ venues_tab }} text-white" href="{% url 'venue list' %}">
|
||||
<div class="d-flex flex-nowrap"><i class="bi bi-geo"></i>
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">
|
||||
Venues
|
||||
</span></div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-white" href="{% url 'teamsnap home' %}">
|
||||
<div class="d-flex flex-nowrap"><i class="bi bi-asterisk"></i>
|
||||
<span class="d-none d-print-inline-block d-sm-inline-block d-md-inline-block d-lg-inline-block d-xl-inline-block d-xxl-inline-block mx-1 my-auto text-white">
|
||||
Sync
|
||||
</span></div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||