Crude section/teams implementation
do not merge to main, very buggy rough implementation of sorting between teams and sections. I have rewritten many areas.
This commit is contained in:
parent
75a97d762b
commit
e2ad5676c7
@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Peg, Member, Team
|
from .models import Member, Team, Section
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Peg)
|
# @admin.register(Peg)
|
||||||
class PegAdmin(admin.ModelAdmin):
|
# class PegAdmin(admin.ModelAdmin):
|
||||||
"""Admin model for the Peg model."""
|
# """Admin model for the Peg model."""
|
||||||
|
|
||||||
change_list_template = "entities/bulk_pegging.html"
|
# change_list_template = "entities/bulk_pegging.html"
|
||||||
readonly_fields = ("peg_number",)
|
# readonly_fields = ("peg_number",)
|
||||||
list_display = ("peg_number",)
|
# list_display = ("peg_number",)
|
||||||
search_fields = ("peg_number",)
|
# search_fields = ("peg_number",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Member)
|
@admin.register(Member)
|
||||||
@ -28,14 +28,14 @@ class MemberAdmin(admin.ModelAdmin):
|
|||||||
class TeamAdmin(admin.ModelAdmin):
|
class TeamAdmin(admin.ModelAdmin):
|
||||||
"""Admin model for the Team model."""
|
"""Admin model for the Team model."""
|
||||||
|
|
||||||
readonly_fields = ("team_number",)
|
readonly_fields = ("name",)
|
||||||
list_display = ("team_number",)
|
list_display = ("name",)
|
||||||
search_fields = ("team_number",)
|
search_fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
# @admin.register(Section)
|
@admin.register(Section)
|
||||||
# class SectionAdmin(admin.ModelAdmin):
|
class SectionAdmin(admin.ModelAdmin):
|
||||||
# """Admin model for the Section model."""
|
"""Admin model for the Section model."""
|
||||||
|
|
||||||
# list_display = ("character",)
|
list_display = ("name",)
|
||||||
# search_fields = ("character",)
|
search_fields = ("name",)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,71 +2,71 @@
|
|||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"fields": {
|
|
||||||
"section_letter": "D"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "mainapp.team",
|
|
||||||
"pk": 2,
|
|
||||||
"fields": {
|
|
||||||
"section_letter": "P"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "mainapp.team",
|
|
||||||
"pk": 3,
|
|
||||||
"fields": {
|
|
||||||
"section_letter": "L"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"model": "mainapp.team",
|
|
||||||
"pk": 4,
|
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "M"
|
"section_letter": "M"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"model": "mainapp.team",
|
||||||
|
"pk": 2,
|
||||||
|
"fields": {
|
||||||
|
"section_letter": "K"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "mainapp.team",
|
||||||
|
"pk": 3,
|
||||||
|
"fields": {
|
||||||
|
"section_letter": "Q"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "mainapp.team",
|
||||||
|
"pk": 4,
|
||||||
|
"fields": {
|
||||||
|
"section_letter": "V"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 5,
|
"pk": 5,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "Z"
|
"section_letter": "U"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 6,
|
"pk": 6,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "Y"
|
"section_letter": "H"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 7,
|
"pk": 7,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "J"
|
"section_letter": "A"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 8,
|
"pk": 8,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "W"
|
"section_letter": "D"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 9,
|
"pk": 9,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "T"
|
"section_letter": "P"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"model": "mainapp.team",
|
"model": "mainapp.team",
|
||||||
"pk": 10,
|
"pk": 10,
|
||||||
"fields": {
|
"fields": {
|
||||||
"section_letter": "F"
|
"section_letter": "G"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -1,12 +1,46 @@
|
|||||||
import random
|
|
||||||
import names
|
import names
|
||||||
import json
|
import json
|
||||||
|
from typing import Optional, List, Set
|
||||||
|
from string import ascii_uppercase
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from mainapp.models import Member, Team
|
|
||||||
|
from mainapp.models import Member, Team, SectionValidator
|
||||||
|
|
||||||
|
|
||||||
# TODO: refactor this file like create_teams_fixtures.py
|
# TODO: refactor this file like create_teams_fixtures.py
|
||||||
|
|
||||||
|
|
||||||
|
# SECTION_LETTERS = ascii_uppercase
|
||||||
|
|
||||||
|
|
||||||
|
# def get_next_available_section(member: Member, section_validator: SectionValidator) -> Optional[str]:
|
||||||
|
# """Returns the next available section for a member."""
|
||||||
|
# member_sections = member.sections.all().values_list('name', flat=True)
|
||||||
|
# used_sections = set(member_sections)
|
||||||
|
|
||||||
|
# if not member_sections:
|
||||||
|
# return "A"
|
||||||
|
|
||||||
|
# used_sections = sorted(used_sections)
|
||||||
|
|
||||||
|
# last_section = member_sections[-1]
|
||||||
|
# last_section_index = SECTION_LETTERS.index(last_section)
|
||||||
|
|
||||||
|
# for i in range(last_section_index + 1, len(SECTION_LETTERS)):
|
||||||
|
# section = SECTION_LETTERS[i]
|
||||||
|
# if section_validator.is_valid_section(section) and section not in used_sections:
|
||||||
|
# return section
|
||||||
|
|
||||||
|
# for i in range(last_section_index - 1, -1, -1):
|
||||||
|
# section = SECTION_LETTERS[i]
|
||||||
|
# if section_validator.is_valid_section(section) and section not in used_sections:
|
||||||
|
# return section
|
||||||
|
|
||||||
|
# return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Creates a fixture with randomly generated Member objects"
|
help = "Creates a fixture with randomly generated Member objects"
|
||||||
|
|
||||||
@ -25,11 +59,9 @@ class Command(BaseCommand):
|
|||||||
for _ in range(num_members):
|
for _ in range(num_members):
|
||||||
first_name = names.get_first_name()
|
first_name = names.get_first_name()
|
||||||
last_name = names.get_last_name()
|
last_name = names.get_last_name()
|
||||||
member = Member(first_name=first_name, last_name=last_name, team=team)
|
member = Member.objects.create(first_name=first_name, last_name=last_name, team=team)
|
||||||
members.append(member)
|
members.append(member)
|
||||||
|
|
||||||
Member.objects.bulk_create(members)
|
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"Created {num_members} members."))
|
self.stdout.write(self.style.SUCCESS(f"Created {num_members} members."))
|
||||||
|
|
||||||
# create a members fixture file
|
# create a members fixture file
|
||||||
@ -42,7 +74,7 @@ class Command(BaseCommand):
|
|||||||
"first_name": member.first_name,
|
"first_name": member.first_name,
|
||||||
"last_name": member.last_name,
|
"last_name": member.last_name,
|
||||||
"team": member.team_id,
|
"team": member.team_id,
|
||||||
"peg_number": member.peg_number
|
"peg_number": member.peg_number,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
members_fixture.append(member_fixture)
|
members_fixture.append(member_fixture)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 4.1.5 on 2023-05-07 21:57
|
# Generated by Django 4.1.5 on 2023-05-09 22:52
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@ -14,16 +14,16 @@ class Migration(migrations.Migration):
|
|||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Peg',
|
name='Section',
|
||||||
fields=[
|
fields=[
|
||||||
('peg_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(editable=False, max_length=3, unique=True)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Team',
|
name='Team',
|
||||||
fields=[
|
fields=[
|
||||||
('team_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
|
('team_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
|
||||||
('section_letter', models.CharField(choices=[('A', 'A'), ('B', 'B'), ('C', 'C'), ('D', 'D'), ('E', 'E'), ('F', 'F'), ('G', 'G'), ('H', 'H'), ('I', 'I'), ('J', 'J'), ('K', 'K'), ('L', 'L'), ('M', 'M'), ('N', 'N'), ('O', 'O'), ('P', 'P'), ('Q', 'Q'), ('R', 'R'), ('S', 'S'), ('T', 'T'), ('U', 'U'), ('V', 'V'), ('W', 'W'), ('X', 'X'), ('Y', 'Y'), ('Z', 'Z')], max_length=1, unique=True)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
@ -33,6 +33,7 @@ class Migration(migrations.Migration):
|
|||||||
('first_name', models.CharField(max_length=255)),
|
('first_name', models.CharField(max_length=255)),
|
||||||
('last_name', models.CharField(max_length=255)),
|
('last_name', models.CharField(max_length=255)),
|
||||||
('peg_number', models.PositiveIntegerField(null=True, unique=True)),
|
('peg_number', models.PositiveIntegerField(null=True, unique=True)),
|
||||||
|
('section', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.section')),
|
||||||
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='mainapp.team', validators=[mainapp.models.validate_team_size])),
|
('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='mainapp.team', validators=[mainapp.models.validate_team_size])),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.1.5 on 2023-05-09 23:20
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mainapp', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='team',
|
||||||
|
old_name='team_number',
|
||||||
|
new_name='name',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='section',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=3, unique=True),
|
||||||
|
),
|
||||||
|
]
|
@ -31,13 +31,101 @@ class ReusableAutoField(models.PositiveIntegerField):
|
|||||||
return self.get_next_available_id(self.model)
|
return self.get_next_available_id(self.model)
|
||||||
|
|
||||||
|
|
||||||
class Peg(models.Model):
|
# class Peg(models.Model):
|
||||||
"""Represents a person's peg"""
|
# """Represents a person's peg"""
|
||||||
|
|
||||||
peg_number = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
|
# peg_number = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
|
||||||
|
|
||||||
def __str__(self):
|
# def __str__(self):
|
||||||
return f"Peg {self.peg_number}"
|
# return f"Peg {self.peg_number}"
|
||||||
|
|
||||||
|
|
||||||
|
class SectionValidator:
|
||||||
|
"""Validation class for the `section` field on the `member` model."""
|
||||||
|
|
||||||
|
def __init__(self, max_value="ZZZ"):
|
||||||
|
self.max_value = max_value.upper()
|
||||||
|
self.alphabet_size = ord("Z") - ord("A") + 1
|
||||||
|
|
||||||
|
def is_valid(self, section: str, team_sections: list[str]=None) -> bool:
|
||||||
|
"""Returns boolean if the section passed is valid."""
|
||||||
|
section = section.upper()
|
||||||
|
|
||||||
|
if not self._is_alphanumeric(section):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_in_alphabet(section[0]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_in_range(section):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if team_sections:
|
||||||
|
if not self._is_unique(section, team_sections):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_not_adjacent(section, team_sections):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _is_alphanumeric(self, section: str) -> bool:
|
||||||
|
"""Returns boolean if all characters in the passed string are alphanumerical."""
|
||||||
|
return all(c.isalnum() for c in section)
|
||||||
|
|
||||||
|
def _is_in_alphabet(self, c) -> bool:
|
||||||
|
"""Returns boolean if the passed character is alphabetical."""
|
||||||
|
return "A" <= c <= "Z"
|
||||||
|
|
||||||
|
def _is_in_range(self, section) -> bool:
|
||||||
|
"""Returns boolean if the passed section less or equal to the max value."""
|
||||||
|
section_value = self._section_value(section)
|
||||||
|
max_value = self._section_value(self.max_value)
|
||||||
|
|
||||||
|
return section_value <= max_value
|
||||||
|
|
||||||
|
def _is_unique(self, section, team_sections) -> bool:
|
||||||
|
"""Returns boolean if the passed section is unique amongst `team_sections`."""
|
||||||
|
return section not in team_sections
|
||||||
|
|
||||||
|
def _is_not_adjacent(self, section, team_sections) -> bool:
|
||||||
|
"""Returns boolean if the passed section is not adjacent to any `team_sections`."""
|
||||||
|
for team_section in team_sections:
|
||||||
|
team_section_value = self._section_value(team_section)
|
||||||
|
section_value = self._section_value(section)
|
||||||
|
if abs(team_section_value - section_value) <= 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _section_value(self, section):
|
||||||
|
"""Returns the value of the passed section."""
|
||||||
|
n = len(section)
|
||||||
|
value = sum((ord(c) - ord("A") + 1) * self.alphabet_size ** (n - i - 1) for i, c in enumerate(section))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class Section(models.Model):
|
||||||
|
"""Represents a fishing area. Members can be assigned to a section,
|
||||||
|
but no 2 teammates can be in the same or adjacent section."""
|
||||||
|
|
||||||
|
name = models.CharField(max_length=3, unique=True, null=False)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
validator = SectionValidator(max_value="ZZZ")
|
||||||
|
if not validator.is_valid(self.name):
|
||||||
|
raise ValidationError("Invalid section value.")
|
||||||
|
|
||||||
|
self.name = self.name.upper()
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def members(self):
|
||||||
|
return Member.objects.filter(section=self)
|
||||||
|
|
||||||
|
|
||||||
def validate_team_size(value):
|
def validate_team_size(value):
|
||||||
@ -52,7 +140,7 @@ class Member(models.Model):
|
|||||||
last_name = models.CharField(max_length=255)
|
last_name = models.CharField(max_length=255)
|
||||||
team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, validators=(validate_team_size,), related_name='members')
|
team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, validators=(validate_team_size,), related_name='members')
|
||||||
peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
|
peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
|
||||||
# peg = models.OneToOneField("Peg", on_delete=models.SET_NULL, null=True, blank=True)
|
section = models.ForeignKey(to=Section, on_delete=models.SET_NULL, null=True, swappable=True)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -65,6 +153,13 @@ class Member(models.Model):
|
|||||||
if peg_numbers:
|
if peg_numbers:
|
||||||
self.peg_number = min (peg_numbers)
|
self.peg_number = min (peg_numbers)
|
||||||
|
|
||||||
|
# def clean(self):
|
||||||
|
# super().clean()
|
||||||
|
|
||||||
|
# validator = SectionValidator(max_value="ZZZ")
|
||||||
|
# if not validator.is_valid(self.section):
|
||||||
|
# raise ValidationError("Invalid section value.")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.first_name} {self.last_name} (team {self.team.team_number}))"
|
return f"{self.first_name} {self.last_name} (team {self.team.team_number}))"
|
||||||
|
|
||||||
@ -79,53 +174,25 @@ BLOCKED_SECTION_LETTERS = ["i"] # lowercase only, sections cannot be any of the
|
|||||||
class Team(models.Model):
|
class Team(models.Model):
|
||||||
"""Represents a team"""
|
"""Represents a team"""
|
||||||
|
|
||||||
team_number = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
|
name = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
|
||||||
section_letter = models.CharField(max_length=1, unique=True, choices=[
|
# section_letter = models.CharField(max_length=1, unique=True, choices=[
|
||||||
(char, char) for char in ascii_uppercase if char.lower() not in BLOCKED_SECTION_LETTERS
|
# (char, char) for char in ascii_uppercase if char.lower() not in BLOCKED_SECTION_LETTERS
|
||||||
])
|
# ])
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Team {self.team_number}"
|
return f"Team {self.name}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def members(self) -> list[Member]:
|
def members(self) -> list[Member]:
|
||||||
Member.objects.filter(team=self)
|
return Member.objects.filter(team=self)
|
||||||
|
|
||||||
|
|
||||||
def validate_section_character(value):
|
# def validate_section_character(value):
|
||||||
"""Validates the section character"""
|
# """Validates the section character"""
|
||||||
|
|
||||||
value = value.upper()
|
# value = value.upper()
|
||||||
banned_characters = ["I"]
|
# banned_characters = ["I"]
|
||||||
|
|
||||||
if value in banned_characters:
|
# if value in banned_characters:
|
||||||
raise ValidationError(f"The character <{value}> is a prohibited character.")
|
# raise ValidationError(f"The character <{value}> is a prohibited character.")
|
||||||
|
|
||||||
|
|
||||||
# class Section(models.Model):
|
|
||||||
# """Represents a section of the scoreboard"""
|
|
||||||
|
|
||||||
# # character field stores a single character but doesnt allow for 'I' or 'i'
|
|
||||||
# character = models.CharField(primary_key=True, max_length=1, validators=(validate_section_character, ))
|
|
||||||
|
|
||||||
# def clean(self):
|
|
||||||
# self.character = self.character.upper()
|
|
||||||
|
|
||||||
# def __str__(self):
|
|
||||||
# return f"Section {self.character}"
|
|
||||||
|
|
||||||
|
|
||||||
#class Scoreboard(models.Model):
|
|
||||||
# class Status(models.IntegerChoices):
|
|
||||||
# ACTIVE = 1, "Active"
|
|
||||||
# INACTIVE = 2, "Inactive"
|
|
||||||
# ARCHIVED = 3, "Archived"
|
|
||||||
#
|
|
||||||
# name = models.CharField(max_length=255)
|
|
||||||
# category = models.CharField(max_length=255)
|
|
||||||
# price = models.DecimalField(max_digits=10, decimal_places=2)
|
|
||||||
# cost = models.DecimalField(max_digits=10, decimal_places=2)
|
|
||||||
# status = models.PositiveSmallIntegerField(choices=Status.choices)
|
|
||||||
#
|
|
||||||
# def __str__(self):
|
|
||||||
# return self.name
|
|
@ -37,11 +37,11 @@
|
|||||||
<h3 class="h6 mb-3">Sort groups by</h3>
|
<h3 class="h6 mb-3">Sort groups by</h3>
|
||||||
<div>
|
<div>
|
||||||
<div class="d-inline-block form-check">
|
<div class="d-inline-block form-check">
|
||||||
<input type="radio" class="form-check-input" checked value="team_number" name="sortTeams" id="sortTeamsName">
|
<input type="radio" class="form-check-input" checked value="team" name="sortGroups" id="sortTeamsName">
|
||||||
<label for="sortTeamsName" class="form-check-label">Teams</label>
|
<label for="sortTeamsName" class="form-check-label">Teams</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-inline-block form-check ms-4">
|
<div class="d-inline-block form-check ms-4">
|
||||||
<input type="radio" class="form-check-input" value="section_letter" name="sortTeams" id="sortTeamsSection">
|
<input type="radio" class="form-check-input" value="section" name="sortGroups" id="sortTeamsSection">
|
||||||
<label for="sortTeamsSection" class="form-check-label">Sections</label>
|
<label for="sortTeamsSection" class="form-check-label">Sections</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ urlpatterns = [
|
|||||||
path('scoreboard/', views.scoreboard, name='scoreboard'),
|
path('scoreboard/', views.scoreboard, name='scoreboard'),
|
||||||
path('teams/', views.teams, name='teams'),
|
path('teams/', views.teams, name='teams'),
|
||||||
|
|
||||||
path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg'),
|
# path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg'),
|
||||||
path('get-teams/', views.get_teams, name='get-teams'),
|
path('get-teams/', views.get_teams, name='get-teams'),
|
||||||
path('update-member/', views.update_member, name='update-member')
|
path('update-member/', views.update_member, name='update-member')
|
||||||
]
|
]
|
@ -6,7 +6,7 @@ from django.shortcuts import render, redirect
|
|||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.db.models import Q, Case, When, Value, IntegerField
|
from django.db.models import Q, Case, When, Value, IntegerField
|
||||||
|
|
||||||
from .models import Peg, Team, Member
|
from .models import Team, Member, Section
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
@ -21,15 +21,15 @@ def scoreboard(request):
|
|||||||
def teams(request):
|
def teams(request):
|
||||||
return render(request, 'teams.html')
|
return render(request, 'teams.html')
|
||||||
|
|
||||||
def bulk_create_pegs(request):
|
# def bulk_create_pegs(request):
|
||||||
"""Bulk create pegs"""
|
# """Bulk create pegs"""
|
||||||
|
|
||||||
number_of_pegs = request.POST.get("pegAmount")
|
# number_of_pegs = request.POST.get("pegAmount")
|
||||||
for i in range(int(number_of_pegs)):
|
# for i in range(int(number_of_pegs)):
|
||||||
Peg.objects.create()
|
# Peg.objects.create()
|
||||||
|
|
||||||
# return to previous page
|
# # return to previous page
|
||||||
return redirect(request.META.get('HTTP_REFERER'))
|
# return redirect(request.META.get('HTTP_REFERER'))
|
||||||
|
|
||||||
def get_teams(request):
|
def get_teams(request):
|
||||||
"""Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams.
|
"""Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams.
|
||||||
@ -44,10 +44,11 @@ def get_teams(request):
|
|||||||
return
|
return
|
||||||
|
|
||||||
search = request.POST.get("search")
|
search = request.POST.get("search")
|
||||||
sort_teams = request.POST.get("sortTeams") or "team_number"
|
sort_groups = request.POST.get("sortGroups") or "team"
|
||||||
sort_members = request.POST.get("sortMembers") or "peg_number"
|
sort_members = request.POST.get("sortMembers") or "peg_number"
|
||||||
|
|
||||||
teams = Team.objects.order_by(sort_teams).all()
|
group_object = Team if sort_groups == "team" else Section
|
||||||
|
groups = group_object.objects.order_by("name").all()
|
||||||
|
|
||||||
# Filter out teams that don't contain members being searched for
|
# Filter out teams that don't contain members being searched for
|
||||||
if search:
|
if search:
|
||||||
@ -61,31 +62,32 @@ def get_teams(request):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
teams = teams.filter(members__in=members).distinct()
|
groups = groups.filter(members__in=members).distinct()
|
||||||
|
|
||||||
# Create a dictionary for the data that is JSON safe
|
# Create a dictionary for the data that is JSON safe
|
||||||
response_data = {"teams": []}
|
response_data = {"groups": []}
|
||||||
for team in teams:
|
for group in groups:
|
||||||
team_data = {
|
group_data = {
|
||||||
"team_number": team.team_number,
|
"name": group.name,
|
||||||
"section_letter": team.section_letter if team.section_letter else None,
|
|
||||||
"members": [
|
"members": [
|
||||||
{
|
{
|
||||||
"first": member.first_name,
|
"first": member.first_name,
|
||||||
"last": member.last_name,
|
"last": member.last_name,
|
||||||
"id": member.id,
|
"id": member.id,
|
||||||
"team": team.team_number,
|
|
||||||
"peg": member.peg_number,
|
"peg": member.peg_number,
|
||||||
"section": team.section_letter if team.section_letter else None
|
"team": member.team.name if member.team else None,
|
||||||
|
"section": member.section.name if member.section else None
|
||||||
}
|
}
|
||||||
for member in team.members.order_by(sort_members).all()
|
for member in group.members.order_by(sort_members).all()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
response_data["teams"].append(team_data)
|
response_data["groups"].append(group_data)
|
||||||
|
|
||||||
response_data["sortTeams"] = sort_teams
|
response_data["sortGroups"] = sort_groups
|
||||||
response_data["sortMembers"] = sort_members
|
response_data["sortMembers"] = sort_members
|
||||||
|
|
||||||
|
print(sort_groups, sort_members)
|
||||||
|
|
||||||
return JsonResponse(response_data)
|
return JsonResponse(response_data)
|
||||||
|
|
||||||
def update_member(request):
|
def update_member(request):
|
||||||
|
@ -34,9 +34,9 @@ $(document).ready(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load the last saved sort settings TODO: use local storage so it only saves on one reload
|
// Load the last saved sort settings TODO: use local storage so it only saves on one reload
|
||||||
const sortTeamsValue = localStorage.getItem("sortTeams");
|
const sortTeamsValue = localStorage.getItem("sortGroups");
|
||||||
if (sortTeamsValue !== null) {
|
if (sortTeamsValue !== null) {
|
||||||
$("#sortForm input[name='sortTeams']").val([sortTeamsValue]);
|
$("#sortForm input[name='sortGroups']").val([sortTeamsValue]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortMembersValue = localStorage.getItem("sortMembers");
|
const sortMembersValue = localStorage.getItem("sortMembers");
|
||||||
@ -70,30 +70,31 @@ $(document).ready(() => {
|
|||||||
function getFilters() {
|
function getFilters() {
|
||||||
return [
|
return [
|
||||||
$("#search").val(),
|
$("#search").val(),
|
||||||
$("input[name='sortTeams']:checked", "#sortForm").val(),
|
$("input[name='sortGroups']:checked", "#sortForm").val(),
|
||||||
$("input[name='sortMembers']:checked", "#sortForm").val()
|
$("input[name='sortMembers']:checked", "#sortForm").val()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadTeams(teams, highlightText="") {
|
function loadTeams(groups, highlightText="", groupType) {
|
||||||
$("#teamsContainer").html(""); // Clear the previous listed teams
|
$("#teamsContainer").html(""); // Clear the previous listed teams
|
||||||
|
|
||||||
if (teams.length < 1) {
|
if (groups.length < 1) {
|
||||||
$("#teamsNotFound").show();
|
$("#teamsNotFound").show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupType = groupType.toUpperCase();
|
||||||
|
|
||||||
// Iterate over and add each team
|
// Iterate over and add each team
|
||||||
teams.forEach((team) => {
|
groups.forEach((group) => {
|
||||||
$("#teamsContainer").append(
|
$("#teamsContainer").append(
|
||||||
`<div class='col-12 col-md-6 col-xl-4 mb-4'>
|
`<div class='col-12 col-md-6 col-xl-4 mb-4'>
|
||||||
<div
|
<div
|
||||||
class='team px-4 py-3 bg-body-tertiary bg-gradient rounded h-100 fluid-hover-zoom shadow-sm md-shadow-on-hover'
|
class='team px-4 py-3 bg-body-tertiary bg-gradient rounded h-100 fluid-hover-zoom shadow-sm md-shadow-on-hover'
|
||||||
data-number='${team.team_number}'
|
data-number='${group.name}'>
|
||||||
data-section='${team.section_letter}'>
|
|
||||||
<h3>
|
<h3>
|
||||||
<span class='fs-4'>
|
<span class='fs-4'>
|
||||||
<span class='fs-6'>TEAM</span>
|
<span class='fs-6'>${groupType}</span>
|
||||||
${team.team_number}
|
${group.name}
|
||||||
</span>
|
</span>
|
||||||
<button class='btn btn-sm btn-light float-end border-0 me-1'>
|
<button class='btn btn-sm btn-light float-end border-0 me-1'>
|
||||||
<i class='bi bi-gear fs-6'></i>
|
<i class='bi bi-gear fs-6'></i>
|
||||||
@ -106,8 +107,10 @@ function loadTeams(teams, highlightText="") {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// While we have the team, iterate over and add it's members
|
// While we have the team, iterate over and add it's members
|
||||||
team.members.forEach((member) => {
|
group.members.forEach((member) => {
|
||||||
const fullname = member.first + " " + member.last;
|
const fullname = member.first + " " + member.last;
|
||||||
|
const oppositeGroupType = groupType === "SECTION"? "Team" : "Section";
|
||||||
|
const oppositeGroupName = groupType === "SECTION"? member.team : member.section;
|
||||||
|
|
||||||
$("#teamsContainer").find(".team-members").last().append(
|
$("#teamsContainer").find(".team-members").last().append(
|
||||||
`<li class='mb-3 rounded w-100 fluid-hover-zoom'>
|
`<li class='mb-3 rounded w-100 fluid-hover-zoom'>
|
||||||
@ -117,13 +120,14 @@ function loadTeams(teams, highlightText="") {
|
|||||||
data-last='${member.last}'
|
data-last='${member.last}'
|
||||||
data-member-id='${member.id}'
|
data-member-id='${member.id}'
|
||||||
data-team-number='${member.team}'
|
data-team-number='${member.team}'
|
||||||
data-peg-number='${member.peg}'>
|
data-peg-number='${member.peg}'
|
||||||
|
data-section='${member.section}'>
|
||||||
<div class='px-3 py-2 w-100'>
|
<div class='px-3 py-2 w-100'>
|
||||||
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='Peg ${member.peg}' data-bs-toggle='tooltip'>
|
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='Peg ${member.peg}' data-bs-toggle='tooltip'>
|
||||||
${member.peg}
|
${member.peg}
|
||||||
</div>
|
</div>
|
||||||
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='Section ${member.section}' data-bs-toggle='tooltip'>
|
<div class='badge bg-secondary-subtle text-body fixed-badge' data-bs-title='${oppositeGroupType} ${oppositeGroupName}' data-bs-toggle='tooltip'>
|
||||||
${member.section}
|
${oppositeGroupName}
|
||||||
</div>
|
</div>
|
||||||
<div class='d-inline-block ms-2 team-member-fullname'>
|
<div class='d-inline-block ms-2 team-member-fullname'>
|
||||||
${fullname}
|
${fullname}
|
||||||
@ -176,20 +180,20 @@ function loadTeams(teams, highlightText="") {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchAndLoadTeams(search="", sortTeams="", sortMembers="") {
|
function fetchAndLoadTeams(search="", sortGroups="", sortMembers="") {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: getTeamsUrl,
|
url: getTeamsUrl,
|
||||||
type: "post",
|
type: "post",
|
||||||
data: {
|
data: {
|
||||||
"csrfmiddlewaretoken": csrfMiddlewareToken,
|
"csrfmiddlewaretoken": csrfMiddlewareToken,
|
||||||
"search": search,
|
"search": search,
|
||||||
"sortTeams": sortTeams,
|
"sortGroups": sortGroups,
|
||||||
"sortMembers": sortMembers
|
"sortMembers": sortMembers
|
||||||
},
|
},
|
||||||
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
||||||
success: (result) => {
|
success: (result) => {
|
||||||
teamsLoading(false);
|
teamsLoading(false);
|
||||||
loadTeams(result.teams, search);
|
loadTeams(result.groups, search, result.sortGroups);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -298,7 +302,7 @@ function saveEditMemberModal(memberId) {
|
|||||||
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); },
|
||||||
success: (result) => {
|
success: (result) => {
|
||||||
teamsLoading(false);
|
teamsLoading(false);
|
||||||
loadTeams(result.teams, search);
|
loadTeams(result.teams, search, result.sortGroups);
|
||||||
$("#editMemberModal").modal("hide");
|
$("#editMemberModal").modal("hide");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user