consolidated objects into one benchcoach app
created benchcoachproject app to contain all the settings and stuff some other changes that got grouped with this commit is the adding the ApiObject to the TeamSnap object model
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
from django.contrib import admin
|
||||
from .models import Profile, TeamsnapSettings
|
||||
from .models import Event, Availability, Positioning, Team, Venue, Player, StatLine
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Profile)
|
||||
admin.site.register(TeamsnapSettings)
|
||||
admin.site.register(Event)
|
||||
admin.site.register(Availability)
|
||||
admin.site.register(Positioning)
|
||||
admin.site.register(Team)
|
||||
admin.site.register(Venue)
|
||||
admin.site.register(Player)
|
||||
admin.site.register(StatLine)
|
||||
6
benchcoach/apps.py
Normal file
6
benchcoach/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BenchcoachConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'benchcoach'
|
||||
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
ASGI config for events project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'events.settings')
|
||||
|
||||
application = get_asgi_application()
|
||||
4215
benchcoach/fixtures/2021cmba.json
Normal file
4215
benchcoach/fixtures/2021cmba.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
60
benchcoach/forms.py
Normal file
60
benchcoach/forms.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from django import forms
|
||||
from .models import Event, Positioning, Team, Venue, Player
|
||||
from teamsnap.models import Event as TeamsnapEvent
|
||||
from django.forms import modelformset_factory
|
||||
|
||||
class EventForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ['start', 'home_team', 'away_team', 'venue']
|
||||
class PositioningForm(forms.ModelForm):
|
||||
availability = None
|
||||
class Meta:
|
||||
model = Positioning
|
||||
widgets = {
|
||||
'position': forms.Select(attrs={'class': 'form-control form-control-sm'})
|
||||
}
|
||||
exclude = ()
|
||||
|
||||
PositioningFormSet = modelformset_factory(
|
||||
model=Positioning,
|
||||
form=PositioningForm,
|
||||
# fields=['order', 'position','player'],
|
||||
# min_num=9,
|
||||
extra=0
|
||||
)
|
||||
|
||||
class TeamsnapEventForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TeamsnapEventForm, self).__init__(*args, **kwargs)
|
||||
if self.instance and self.instance.teamsnap_event.first():
|
||||
initial = (self.instance.teamsnap_event.first().id, self.instance.teamsnap_event.first())
|
||||
else:
|
||||
initial = None
|
||||
self.fields = {}
|
||||
choices = [("","-----")]
|
||||
choices += [(choice.id, choice) for choice in TeamsnapEvent.objects.all()]
|
||||
self.fields['teamsnap event'] = forms.MultipleChoiceField(
|
||||
widget=forms.Select(attrs={'class': 'form-control'}),
|
||||
choices=choices,
|
||||
initial=initial
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ['start', 'home_team', 'away_team', 'venue']
|
||||
|
||||
class PlayerForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Player
|
||||
fields = ['first_name', 'last_name', 'jersey_number', 'team']
|
||||
|
||||
class TeamForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Team
|
||||
fields = ['name']
|
||||
|
||||
class VenueForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Venue
|
||||
fields = ['name']
|
||||
@@ -1,6 +1,8 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-12 23:36
|
||||
# Generated by Django 3.2.6 on 2021-12-17 21:33
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@@ -12,13 +14,89 @@ class Migration(migrations.Migration):
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
name='Player',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=50, null=True)),
|
||||
('last_name', models.CharField(max_length=50, null=True)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('teamsnap_access_token', models.CharField(max_length=50, null=True)),
|
||||
('first_name', models.CharField(max_length=200)),
|
||||
('last_name', models.CharField(max_length=200)),
|
||||
('jersey_number', models.IntegerField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Season',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Team',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
('image', models.FileField(null=True, upload_to='images/', validators=[django.core.validators.FileExtensionValidator(['jpg', 'png', 'svg'])])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Venue',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StatLine',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('batting_avg', models.DecimalField(decimal_places=3, default=0, max_digits=4)),
|
||||
('onbase_pct', models.DecimalField(decimal_places=3, default=0, max_digits=4)),
|
||||
('slugging_pct', models.DecimalField(decimal_places=3, default=0, max_digits=4)),
|
||||
('player', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.player')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='player',
|
||||
name='team',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.team'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Event',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start', models.DateTimeField(null=True)),
|
||||
('away_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='away_team', to='benchcoach.team')),
|
||||
('home_team', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='home_team', to='benchcoach.team')),
|
||||
('venue', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='benchcoach.venue')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Positioning',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('position', models.CharField(blank=True, choices=[('EH', 'EH'), ('P', 'P'), ('C', 'C'), ('1B', '1B'), ('2B', '2B'), ('3B', '3B'), ('SS', 'SS'), ('LF', 'LF'), ('CF', 'CF'), ('RF', 'RF'), ('DH', 'DH')], default=None, max_length=2, null=True)),
|
||||
('order', models.PositiveSmallIntegerField(blank=True, default=None, null=True)),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.event')),
|
||||
('player', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.player')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('player', 'event')},
|
||||
},
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='player',
|
||||
unique_together={('first_name', 'last_name')},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Availability',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('available', models.IntegerField(choices=[(2, 'Yes'), (0, 'No'), (1, 'Maybe'), (-1, 'Unknown')], default=-1)),
|
||||
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.event')),
|
||||
('player', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.player')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'availabilities',
|
||||
'unique_together': {('event', 'player')},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
18
benchcoach/migrations/0002_alter_player_jersey_number.py
Normal file
18
benchcoach/migrations/0002_alter_player_jersey_number.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-19 01:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='player',
|
||||
name='jersey_number',
|
||||
field=models.IntegerField(null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,20 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-12 23:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0016_auto_20211212_2240'),
|
||||
('benchcoach', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='teamsnap_user',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.user'),
|
||||
),
|
||||
]
|
||||
@@ -1,29 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-13 00:58
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('teamsnap', '0018_user_managed_teams'),
|
||||
('benchcoach', '0002_user_teamsnap_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('teamsnap_access_token', models.CharField(max_length=50, null=True)),
|
||||
('teamsnap_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.user')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='User',
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-13 01:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0003_auto_20211213_0058'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='avatar',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='benchcoach/static/user_files/'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-13 01:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0004_profile_avatar'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='avatar',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='avatars'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-13 01:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('benchcoach', '0005_alter_profile_avatar'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='avatar',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='avatar'),
|
||||
),
|
||||
]
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-16 18:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0019_auto_20211216_1851'),
|
||||
('benchcoach', '0006_alter_profile_avatar'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TeamsnapSettings',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('managed_team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='teamsnap.team')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='benchcoach.profile')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -1,20 +0,0 @@
|
||||
# Generated by Django 3.2.6 on 2021-12-16 18:56
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('teamsnap', '0019_auto_20211216_1851'),
|
||||
('benchcoach', '0007_teamsnapsettings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='teamsnap_user',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='teamsnap.user'),
|
||||
),
|
||||
]
|
||||
@@ -1,18 +1,128 @@
|
||||
from django.db import models
|
||||
from teamsnap.models import User as TeamsnapUser, Team as TeamsnapTeam
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import FileExtensionValidator
|
||||
|
||||
|
||||
def user_directory_path(instance, filename):
|
||||
# file will be uploaded to MEDIA_ROOT / user_<id>/<filename>
|
||||
return 'benchcoach/static/user_files/user_{0}/{1}'.format(instance.user.id, filename)
|
||||
class Team(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
image = models.FileField(
|
||||
upload_to="images/",
|
||||
validators=[FileExtensionValidator(["jpg", "png", "svg"])],
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
teamsnap_access_token = models.CharField(null=True, max_length=50)
|
||||
teamsnap_user = models.ForeignKey(TeamsnapUser, on_delete=models.CASCADE, null=True, blank=True)
|
||||
avatar = models.ImageField(upload_to="avatar", null=True, blank=True)
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
||||
|
||||
class TeamsnapSettings(models.Model):
|
||||
user = models.OneToOneField(Profile, on_delete=models.CASCADE)
|
||||
managed_team = models.ForeignKey(TeamsnapTeam, on_delete=models.CASCADE)
|
||||
|
||||
class Venue(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
||||
|
||||
|
||||
class Event(models.Model):
|
||||
start = models.DateTimeField(null=True)
|
||||
venue = models.ForeignKey(Venue, null=True, on_delete=models.CASCADE)
|
||||
home_team = models.ForeignKey(
|
||||
Team, null=True, on_delete=models.CASCADE, related_name="home_team"
|
||||
)
|
||||
away_team = models.ForeignKey(
|
||||
Team, null=True, on_delete=models.CASCADE, related_name="away_team"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.start:%Y-%m-%d %H:%M}"
|
||||
|
||||
|
||||
class Season(models.Model):
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
|
||||
class Player(models.Model):
|
||||
first_name = models.CharField(max_length=200)
|
||||
last_name = models.CharField(max_length=200)
|
||||
jersey_number = models.IntegerField(null=True)
|
||||
team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.last_name}, {self.first_name}"
|
||||
|
||||
class Meta:
|
||||
unique_together = (
|
||||
"first_name",
|
||||
"last_name",
|
||||
)
|
||||
|
||||
|
||||
class StatLine(models.Model):
|
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE)
|
||||
batting_avg = models.DecimalField(max_digits=4, decimal_places=3, default=0)
|
||||
onbase_pct = models.DecimalField(max_digits=4, decimal_places=3, default=0)
|
||||
slugging_pct = models.DecimalField(max_digits=4, decimal_places=3, default=0)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.slash_line}"
|
||||
|
||||
@property
|
||||
def slash_line(self):
|
||||
return "/".join(
|
||||
[
|
||||
f"{self.batting_avg:.3f}".lstrip("0"),
|
||||
f"{self.onbase_pct:.3f}".lstrip("0"),
|
||||
f"{self.slugging_pct:.3f}".replace("0.", "."),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class Positioning(models.Model):
|
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE)
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
positions = [
|
||||
("EH", "EH"),
|
||||
("P", "P"),
|
||||
("C", "C"),
|
||||
("1B", "1B"),
|
||||
("2B", "2B"),
|
||||
("3B", "3B"),
|
||||
("SS", "SS"),
|
||||
("LF", "LF"),
|
||||
("CF", "CF"),
|
||||
("RF", "RF"),
|
||||
("DH", "DH"),
|
||||
]
|
||||
position = models.CharField(
|
||||
choices=positions, default=None, max_length=2, null=True, blank=True
|
||||
)
|
||||
order = models.PositiveSmallIntegerField(default=None, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = (
|
||||
"player",
|
||||
"event",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.player}; {self.event};"
|
||||
|
||||
|
||||
class Availability(models.Model):
|
||||
YES = 2
|
||||
MAYBE = 1
|
||||
NO = 0
|
||||
UNKNOWN = -1
|
||||
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE)
|
||||
choices = [(YES, "Yes"), (NO, "No"), (MAYBE, "Maybe"), (UNKNOWN, "Unknown")]
|
||||
available = models.IntegerField(choices=choices, default=UNKNOWN)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.event}; {self.player}; {self.available}"
|
||||
|
||||
class Meta:
|
||||
unique_together = (
|
||||
"event",
|
||||
"player",
|
||||
)
|
||||
verbose_name_plural = "availabilities"
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
"""
|
||||
Django settings for events project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.2.6.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-qib_j&47o$5l3*gi7y#8#3pjh_88sfdqn@dmp&gx+2)&1nzmor'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ["smithers-ii.local", "127.0.0.1", "10.0.1.4", "benchcoach.ascorrea.com"]
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'events.apps.EventsConfig',
|
||||
'teams.apps.TeamsConfig',
|
||||
'venues.apps.VenuesConfig',
|
||||
'players.apps.PlayersConfig',
|
||||
'lineups.apps.LineupsConfig',
|
||||
'teamsnap.apps.TeamsnapConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'crispy_forms',
|
||||
'benchcoach'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'benchcoach.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'benchcoach.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = False
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 17 KiB |
@@ -1 +0,0 @@
|
||||
@import url("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css");
|
||||
5051
benchcoach/static/css/bootstrap-grid.css
vendored
5051
benchcoach/static/css/bootstrap-grid.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/css/bootstrap-grid.min.css
vendored
7
benchcoach/static/css/bootstrap-grid.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5050
benchcoach/static/css/bootstrap-grid.rtl.css
vendored
5050
benchcoach/static/css/bootstrap-grid.rtl.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
485
benchcoach/static/css/bootstrap-reboot.css
vendored
485
benchcoach/static/css/bootstrap-reboot.css
vendored
@@ -1,485 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
:root {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-bg: #fff;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
background-color: currentColor;
|
||||
border: 0;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
hr:not([size]) {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-bs-original-title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.2em;
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0d6efd;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
direction: ltr /* rtl:ignore */;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: #d63384;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.875em;
|
||||
color: #fff;
|
||||
background-color: #212529;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
482
benchcoach/static/css/bootstrap-reboot.rtl.css
vendored
482
benchcoach/static/css/bootstrap-reboot.rtl.css
vendored
@@ -1,482 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v5.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2021 The Bootstrap Authors
|
||||
* Copyright 2011-2021 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
:root {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-bg: #fff;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
background-color: currentColor;
|
||||
border: 0;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
hr:not([size]) {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-bs-original-title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.2em;
|
||||
background-color: #fcf8e3;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0d6efd;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
color: #0a58ca;
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
direction: ltr ;
|
||||
unicode-bidi: bidi-override;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: #d63384;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.875em;
|
||||
color: #fff;
|
||||
background-color: #212529;
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: #6c757d;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: textfield;
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4866
benchcoach/static/css/bootstrap-utilities.css
vendored
4866
benchcoach/static/css/bootstrap-utilities.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4857
benchcoach/static/css/bootstrap-utilities.rtl.css
vendored
4857
benchcoach/static/css/bootstrap-utilities.rtl.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11266
benchcoach/static/css/bootstrap.css
vendored
11266
benchcoach/static/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/css/bootstrap.min.css
vendored
7
benchcoach/static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11242
benchcoach/static/css/bootstrap.rtl.css
vendored
11242
benchcoach/static/css/bootstrap.rtl.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/css/bootstrap.rtl.min.css
vendored
7
benchcoach/static/css/bootstrap.rtl.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
File diff suppressed because it is too large
Load Diff
6812
benchcoach/static/js/bootstrap.bundle.js
vendored
6812
benchcoach/static/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/js/bootstrap.bundle.min.js
vendored
7
benchcoach/static/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4999
benchcoach/static/js/bootstrap.esm.js
vendored
4999
benchcoach/static/js/bootstrap.esm.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/js/bootstrap.esm.min.js
vendored
7
benchcoach/static/js/bootstrap.esm.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5046
benchcoach/static/js/bootstrap.js
vendored
5046
benchcoach/static/js/bootstrap.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
benchcoach/static/js/bootstrap.min.js
vendored
7
benchcoach/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
281
benchcoach/tests.py
Normal file
281
benchcoach/tests.py
Normal file
@@ -0,0 +1,281 @@
|
||||
from django.test import TestCase
|
||||
from .models import Event, Player, Team, Venue, Positioning
|
||||
from .forms import PositioningFormSet
|
||||
from datetime import datetime
|
||||
from django.urls import reverse
|
||||
|
||||
FIXTURES = ["blaseball"]
|
||||
|
||||
|
||||
class TestEventModel(TestCase):
|
||||
fixtures = FIXTURES
|
||||
|
||||
def test_query_event(self):
|
||||
"""
|
||||
Return the desired event
|
||||
"""
|
||||
event = Event.objects.get(id=1)
|
||||
self.assertEqual("Chicago Firefighters", event.away_team.name)
|
||||
self.assertEqual("Dallas Steaks", event.home_team.name)
|
||||
self.assertEqual("George Fourman Stadium", event.venue.name)
|
||||
self.assertEqual(
|
||||
datetime(year=2020, month=8, day=24, hour=16, minute=0, second=1),
|
||||
event.start,
|
||||
)
|
||||
|
||||
|
||||
class TestEventViews(TestCase):
|
||||
fixtures = FIXTURES
|
||||
|
||||
def test_event_list(self):
|
||||
response = self.client.get(reverse("schedule"))
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
def test_event_edit(self):
|
||||
response = self.client.get(reverse("edit event", args=[2]))
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
# create new event
|
||||
new_event_data = {
|
||||
"home_team": 23,
|
||||
"away_team": 24,
|
||||
"start": datetime(year=2021, month=1, day=1, hour=9, minute=0, second=0),
|
||||
"venue": 19,
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
reverse("edit event", args=[0]), data=new_event_data
|
||||
)
|
||||
self.assertEqual(201, response.status_code)
|
||||
new_event = Event.objects.get(id=response.context["id"])
|
||||
self.assertEqual(new_event_data["home_team"], new_event.home_team.id)
|
||||
self.assertEqual(new_event_data["away_team"], new_event.away_team_id)
|
||||
self.assertEqual(new_event_data["start"], new_event.start)
|
||||
|
||||
# modify event
|
||||
modified_event_data = {
|
||||
"home_team": 23,
|
||||
"away_team": 24,
|
||||
"start": datetime(year=2021, month=1, day=1, hour=9, minute=0, second=0),
|
||||
"venue": 19,
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("edit event", args=[1]), data=modified_event_data
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(1, response.context["id"])
|
||||
modified_event = Event.objects.get(id=response.context["id"])
|
||||
|
||||
self.assertEqual(modified_event_data["home_team"], modified_event.home_team.id)
|
||||
self.assertEqual(modified_event_data["away_team"], modified_event.away_team.id)
|
||||
self.assertEqual(modified_event_data["start"], modified_event.start)
|
||||
self.assertEqual(modified_event_data["venue"], modified_event.venue.id)
|
||||
|
||||
|
||||
class TestVenueViews(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_positioning_list(self):
|
||||
response = self.client.get(reverse("edit lineup", args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_positioning_formset(self):
|
||||
event = 1
|
||||
|
||||
sample_data = [
|
||||
# first player positioning
|
||||
(1, Player.objects.get(id=1).id, "P"),
|
||||
(2, Player.objects.get(id=2).id, "C"),
|
||||
(3, Player.objects.get(id=3).id, "1B"),
|
||||
]
|
||||
data = {}
|
||||
|
||||
for i, (order, player, position) in enumerate(sample_data):
|
||||
data[f"form-{i}-order"] = order
|
||||
data[f"form-{i}-player"] = player
|
||||
data[f"form-{i}-position"] = position
|
||||
|
||||
management = {
|
||||
"form-INITIAL_FORMS": "0",
|
||||
"form-TOTAL_FORMS": len(sample_data),
|
||||
"form-MAX_NUM_FORMS": "",
|
||||
}
|
||||
|
||||
formset = PositioningFormSet({**management, **data})
|
||||
|
||||
self.assertTrue(formset.is_valid())
|
||||
for form in formset:
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
response = self.client.post(
|
||||
reverse("edit lineup", args=[event]), {**management, **data}
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
for d in sample_data:
|
||||
with self.subTest(d):
|
||||
p = Positioning.objects.get(player_id=d[1], event_id=event)
|
||||
self.assertEqual(d[0], p.order)
|
||||
self.assertEqual(d[2], p.position)
|
||||
pass
|
||||
|
||||
|
||||
class TestPlayerModel(TestCase):
|
||||
fixtures = FIXTURES
|
||||
|
||||
def test_query_player(self):
|
||||
"""
|
||||
Return the desired player
|
||||
"""
|
||||
player = Player.objects.get(id=1)
|
||||
self.assertEqual(player.first_name, "Edric")
|
||||
self.assertEqual(player.last_name, "Tosser")
|
||||
self.assertEqual(player.jersey_number, 1)
|
||||
self.assertEqual(player.team.name, "Chicago Firefighters")
|
||||
|
||||
|
||||
class TestPlayerViews(TestCase):
|
||||
fixtures = FIXTURES
|
||||
|
||||
def test_player_list(self):
|
||||
response = self.client.get(reverse("players list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_player_edit(self):
|
||||
response = self.client.get(reverse("edit player", args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# create new player
|
||||
new_player_data = {
|
||||
"first_name": "A new player first name",
|
||||
"last_name": "A new player last name",
|
||||
"jersey_number": 99,
|
||||
"team": 1,
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
reverse("edit player", args=[0]), data=new_player_data
|
||||
)
|
||||
self.assertEqual(201, response.status_code)
|
||||
new_player = Player.objects.get(id=response.context["id"])
|
||||
self.assertEqual(new_player_data["first_name"], new_player.first_name)
|
||||
self.assertEqual(new_player_data["last_name"], new_player.last_name)
|
||||
|
||||
# modify player
|
||||
modified_player_data = {
|
||||
"first_name": "A changed player first name",
|
||||
"last_name": "A changed player last name",
|
||||
"jersey_number": 99,
|
||||
"team": 1,
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse("edit player", args=[1]), data=modified_player_data
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(1, response.context["id"])
|
||||
modified_player = Player.objects.get(id=response.context["id"])
|
||||
self.assertEqual(modified_player_data["first_name"], modified_player.first_name)
|
||||
self.assertEqual(modified_player_data["last_name"], modified_player.last_name)
|
||||
|
||||
|
||||
class TestTeamModel(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_query_team(self):
|
||||
"""
|
||||
Return the desired team
|
||||
"""
|
||||
team = Team.objects.get(id=1)
|
||||
self.assertEqual(team.name, "Chicago Firefighters")
|
||||
|
||||
|
||||
class TestTeamViews(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_team_list(self):
|
||||
response = self.client.get(reverse("teams list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(
|
||||
{"id": 1, "title": "Chicago Firefighters"}, response.context["items"]
|
||||
)
|
||||
self.assertIn({"id": 2, "title": "Boston Flowers"}, response.context["items"])
|
||||
self.assertIn({"id": 24, "title": "Baltimore Crabs"}, response.context["items"])
|
||||
|
||||
def test_team_edit(self):
|
||||
response = self.client.get(reverse("edit team", args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# create new team
|
||||
response = self.client.post(
|
||||
reverse("edit team", args=[0]), data={"name": "A new team"}
|
||||
)
|
||||
self.assertEqual(201, response.status_code)
|
||||
new_team = Team.objects.get(id=response.context["id"])
|
||||
self.assertEqual(
|
||||
"A new team",
|
||||
new_team.name,
|
||||
)
|
||||
|
||||
# modify team
|
||||
response = self.client.post(
|
||||
reverse("edit team", args=[1]), data={"name": "A different team name"}
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(1, response.context["id"])
|
||||
modified_team = Team.objects.get(id=response.context["id"])
|
||||
self.assertEqual("A different team name", modified_team.name)
|
||||
|
||||
|
||||
class TestVenueModel(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_query_venue(self):
|
||||
"""
|
||||
Return the desired venue
|
||||
"""
|
||||
venue = Venue.objects.get(id=1)
|
||||
self.assertEqual(venue.name, "Chesapeake Racetrack and Ballpark")
|
||||
|
||||
|
||||
class TestVenueViews(TestCase):
|
||||
fixtures = ["blaseball"]
|
||||
|
||||
def test_venue_list(self):
|
||||
response = self.client.get(reverse("venues list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(
|
||||
{"id": 1, "title": "Chesapeake Racetrack and Ballpark"},
|
||||
response.context["items"],
|
||||
)
|
||||
self.assertIn(
|
||||
{"id": 2, "title": "Tokyo Fitness Center"}, response.context["items"]
|
||||
)
|
||||
self.assertIn(
|
||||
{"id": 25, "title": "ILB Historical Preservation Site"},
|
||||
response.context["items"],
|
||||
)
|
||||
|
||||
def test_venue_edit(self):
|
||||
response = self.client.get(reverse("edit venue", args=[1]))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# create new venue
|
||||
response = self.client.post(
|
||||
reverse("edit venue", args=[0]), data={"name": "A new venue"}
|
||||
)
|
||||
self.assertEqual(201, response.status_code)
|
||||
new_venue = Venue.objects.get(id=response.context["id"])
|
||||
self.assertEqual(
|
||||
"A new venue",
|
||||
new_venue.name,
|
||||
)
|
||||
|
||||
# modify venue
|
||||
response = self.client.post(
|
||||
reverse("edit venue", args=[1]), data={"name": "A different venue name"}
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
self.assertEqual(1, response.context["id"])
|
||||
modified_venue = Venue.objects.get(id=response.context["id"])
|
||||
self.assertEqual("A different venue name", modified_venue.name)
|
||||
@@ -1,33 +1,12 @@
|
||||
"""events URL Configuration
|
||||
from django.urls import path
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf.urls.static import static
|
||||
from django.conf import settings
|
||||
|
||||
from .views import welcome, user_login
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', welcome, name="home"),
|
||||
path('admin/', admin.site.urls),
|
||||
path('events/', include('events.urls')),
|
||||
path('teams/', include('teams.urls')),
|
||||
path('venues/', include('venues.urls')),
|
||||
path('players/', include('players.urls')),
|
||||
path('lineups/', include('lineups.urls')),
|
||||
path('teamsnap/', include('teamsnap.urls')),
|
||||
path('login', user_login, name="login")
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
path('lineup/edit/<int:event_id>', views.lineup_edit, name="edit lineup"),
|
||||
path('events/list', views.EventListView.as_view(), name="event list"),
|
||||
path('events/<int:pk>/detail', views.EventDetailView.as_view(), name="event detail"),
|
||||
path('players/list', views.PlayerListView.as_view(), name="player list"),
|
||||
path('teams/list', views.TeamListView.as_view(), name="team list"),
|
||||
path('venues/list', views.VenueListView.as_view(), name="venue list")
|
||||
]
|
||||
@@ -1,31 +1,142 @@
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render,redirect, reverse, HttpResponseRedirect
|
||||
from django.contrib.auth import login,authenticate
|
||||
from django.shortcuts import render
|
||||
from .models import Event, Team, Player, Positioning, Venue
|
||||
from .forms import PositioningFormSet, TeamsnapEventForm
|
||||
from django.contrib import messages
|
||||
from django.db.models import F
|
||||
from django.views.generic import ListView, DetailView
|
||||
|
||||
def welcome(request):
|
||||
pages = ['events list', 'teams list', 'venues list', 'players list', 'teamsnap list events', 'teamsnap home', 'login']
|
||||
return render(request,'home.html',{'pages':pages})
|
||||
class BenchCoachListView(ListView):
|
||||
title = None
|
||||
|
||||
class EventDetailView(DetailView):
|
||||
model = Event
|
||||
context_object_name = "event"
|
||||
template_name = 'benchcoach/event-detail.html'
|
||||
|
||||
def user_login(request):
|
||||
if request.method == 'POST':
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('password')
|
||||
class EventListView(ListView):
|
||||
model = Event
|
||||
context_object_name = "events"
|
||||
template_name = 'benchcoach/event-list.html'
|
||||
|
||||
try:
|
||||
user = authenticate(request, username=username, password=password)
|
||||
if user is not None:
|
||||
print('Login')
|
||||
login(request,user)
|
||||
return redirect(reverse('home'))
|
||||
else:
|
||||
print("Someone tried to login and failed.")
|
||||
print("They used username: {} and password: {}".format(username, password))
|
||||
class PlayerListView(ListView):
|
||||
model = Player
|
||||
template_name = 'benchcoach/list.html'
|
||||
|
||||
return redirect('/')
|
||||
except Exception as identifier:
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = "Players"
|
||||
context['members_tab_active'] ='active'
|
||||
return context
|
||||
|
||||
return redirect('/')
|
||||
class TeamListView(ListView):
|
||||
model = Team
|
||||
template_name = 'benchcoach/list.html'
|
||||
|
||||
else:
|
||||
return render(request, 'login.html')
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = "Teams"
|
||||
context['opponents_tab_active'] ='active'
|
||||
return context
|
||||
|
||||
class VenueListView(ListView):
|
||||
model = Venue
|
||||
template_name = 'benchcoach/list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['page_title'] = "Venues"
|
||||
context['venues_tab_active'] ='active'
|
||||
return context
|
||||
|
||||
def lineup_edit(request, event_id):
|
||||
|
||||
if request.method == "POST":
|
||||
# create a form instance and populate it with data from the request:
|
||||
formset = PositioningFormSet(request.POST)
|
||||
is_valid = [f.is_valid() for f in formset]
|
||||
for form in formset:
|
||||
if form.is_valid():
|
||||
# process the data in form.cleaned_data as required
|
||||
# ...
|
||||
# redirect to a new URL:
|
||||
|
||||
if isinstance(form.cleaned_data["id"], Positioning):
|
||||
positioning_id = form.cleaned_data.pop(
|
||||
"id"
|
||||
).id # FIXME this is a workaround, not sure why it is necessary
|
||||
positioning = Positioning.objects.filter(id=positioning_id)
|
||||
positioning.update(**form.cleaned_data)
|
||||
did_create = False
|
||||
else:
|
||||
positioning = Positioning.objects.create(
|
||||
**form.cleaned_data, event_id=event_id
|
||||
)
|
||||
did_create = True
|
||||
else:
|
||||
messages.error(
|
||||
request, f"Error submitting lineup. {form.instance} {form.errors}"
|
||||
)
|
||||
pass
|
||||
if (True in is_valid) and (False in is_valid):
|
||||
messages.warning(request, "Lineup partially submitted.")
|
||||
elif True in is_valid:
|
||||
messages.success(request, "Lineup submitted successfully.")
|
||||
elif True not in is_valid:
|
||||
messages.error(request, f"Error submitting lineup.")
|
||||
else:
|
||||
messages.error(request, f"Error submitting lineup.")
|
||||
# return HttpResponse(status=204)
|
||||
# return render(request, 'success.html', {'call_back':'edit lineup','id':event_id, 'errors':[error for error in formset.errors if error]}, status=200)
|
||||
previous_event = Event.objects.filter(id=event_id - 1).first()
|
||||
|
||||
event = Event.objects.get(id=event_id)
|
||||
next_event = Event.objects.get(id=event_id + 1)
|
||||
players = Player.objects.prefetch_related("availability_set", "positioning_set")
|
||||
|
||||
for player in players:
|
||||
Positioning.objects.get_or_create(player_id=player.id, event_id=event_id)
|
||||
|
||||
qs = (
|
||||
event.positioning_set.all()
|
||||
.filter(player__availability__event=event_id)
|
||||
.order_by("order", "-player__availability__available", "player__last_name")
|
||||
.annotate(event_availability=F("player__availability__available"))
|
||||
)
|
||||
|
||||
formset = PositioningFormSet(queryset=qs)
|
||||
|
||||
formset_lineup = [f for f in formset if f.instance.order]
|
||||
formset_dhd = [f for f in formset if not f.instance.order and f.instance.position]
|
||||
formset_bench = [
|
||||
f for f in formset if f not in formset_lineup and f not in formset_dhd
|
||||
]
|
||||
|
||||
teamsnap_form = TeamsnapEventForm(instance=event)
|
||||
|
||||
details = {
|
||||
"Away Team": event.away_team,
|
||||
"Home Team": event.home_team,
|
||||
"Date": event.start.date(),
|
||||
"Time": event.start.time(),
|
||||
"Venue": event.venue,
|
||||
"TeamSnap": teamsnap_form
|
||||
# "TeamSnap Link": event.event_set.first()
|
||||
# "TeamSnap Link": f'<a href="{reverse("teamsnap edit event", kwargs={"id": event.event_set.first().id})}"> {event.event_set.first()} </a>' if event.event_set.first() else None
|
||||
}
|
||||
|
||||
return render(
|
||||
request,
|
||||
"benchcoach/lineup.html",
|
||||
{
|
||||
"title": "Lineup",
|
||||
"event": event,
|
||||
"details": details,
|
||||
"previous_event": previous_event,
|
||||
"teamsnap_form": teamsnap_form,
|
||||
"next_event": next_event,
|
||||
"formset": formset,
|
||||
"formset_lineup": formset_lineup,
|
||||
"formset_bench": formset_bench,
|
||||
"formset_dhd": formset_dhd,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
"""
|
||||
WSGI config for events project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'events.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Reference in New Issue
Block a user