Compare commits
No commits in common. "main" and "what-is-this-branch-for" have entirely different histories.
main
...
what-is-th
166
.gitignore
vendored
@ -1,160 +1,8 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
/venv
|
||||
venv
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
pyvenv.cfg
|
||||
*.pyc
|
||||
venv/pyvenv.cfg
|
||||
src/db.sqlite3
|
||||
*.sqlite3
|
||||
|
19
.vscode/launch.json
vendored
@ -1,19 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Django",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}\\src\\manage.py",
|
||||
"args": [
|
||||
"runserver"
|
||||
],
|
||||
"django": true,
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
@echo off
|
||||
echo The purpose of this file is to populate the database with dummy data for testing. Running this WILL DELETE your current sqlite3 database.
|
||||
set /p confirm=Do you want to continue? [y/n]
|
||||
|
||||
if /i "%confirm%"=="y" (
|
||||
cd /d %~dp0
|
||||
|
||||
if exist src\db.sqlite3 (
|
||||
del src\db.sqlite3
|
||||
)
|
||||
|
||||
call venv\Scripts\activate.bat
|
||||
|
||||
python src/manage.py migrate
|
||||
python src/manage.py create_teams_fixture 10
|
||||
python src/manage.py loaddata teams_fixture
|
||||
python src/manage.py create_members_fixture 3
|
||||
python src/manage.py loaddata members_fixture
|
||||
|
||||
deactivate
|
||||
) else (
|
||||
echo Exiting script...
|
||||
)
|
106
specs.md
@ -1,106 +0,0 @@
|
||||
# Project Name: AT Results
|
||||
## Overview
|
||||
|
||||
This project involves the creation of a website for the Angling Trust to manage members, teams, and other aspects of their fishing events. The website will serve as a central hub for Angling Trust staff to access and manage data related to events, participants, and teams. The website will be designed to streamline the event management process, allowing staff to easily add and edit event details, manage participants and teams, and generate reports in the form of scoreboards.
|
||||
|
||||
## Client Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- The website should allow staff to manage participant and team registrations, including adding and editing participant and team details.
|
||||
|
||||
- The website should allow staff to display a scoreboard on event participation, team rosters, and other relevant data.
|
||||
|
||||
### Content Requirements
|
||||
|
||||
- The website should contain event details, including participants and their assigned teams.
|
||||
|
||||
- The website should contain participant and team details, including names..
|
||||
|
||||
### Design Requirements
|
||||
|
||||
- The website should follow the Angling Trust branding and colour scheme.
|
||||
|
||||
- The website must be easy to navigate and use. Displayed instructions must be clear and interfaces must be intuitive.
|
||||
|
||||
- The website must be responsive and mobile-friendly.
|
||||
|
||||
- Staff should be able to add and edit: members, teams and sections.
|
||||
|
||||
- Staff should be able to assign: members to teams and members to sections.
|
||||
|
||||
- An error should be displayed when a staff member tries to assign a member to a section that contains another member in the same team.
|
||||
|
||||
- An error should appear if a staff member tries to change a peg number to that of another member's peg number, or, should recieve the option to swap the peg numbers.
|
||||
|
||||
- Members that aren't yet assigned to a team should be shown as such, and scoreboards should not include these members.
|
||||
|
||||
- Members that aren't yet assigned to a section should be shown as such, and scoreboards should not include these members.
|
||||
|
||||
- Staff should be able to prohibit certain alphabetic characters from being used as section identifiers.
|
||||
|
||||
### Technical Requirements
|
||||
|
||||
- The website should be built using a modern web development framework.
|
||||
|
||||
- The website should be hosted on a reliable and secure server.
|
||||
|
||||
- The website should incorporate appropriate security measures, such as encryption and authentication.
|
||||
|
||||
#### Teams
|
||||
|
||||
- Represents a team of members
|
||||
|
||||
- Must have a name represented by a whole number.
|
||||
|
||||
- No members on a team can have be on the same section.
|
||||
|
||||
- Teams may be limited to a set amount of members, add a setting to allow for this.
|
||||
|
||||
#### Members
|
||||
|
||||
- Members must represent a participant in the event
|
||||
|
||||
- Members should have a first and last name
|
||||
|
||||
- Members should have a unique whole number as their peg.
|
||||
|
||||
- A member must be assigned to a team.
|
||||
|
||||
- Members must be assigned to a section
|
||||
|
||||
- A member cannot be assigned to a section that already contains one of their teammates.
|
||||
|
||||
#### Section
|
||||
|
||||
- Sections represent a fishing area
|
||||
|
||||
- Sections must be uniquely identified by an alphabetical character.
|
||||
|
||||
- Unique identifiers should use an additional character should all other options be used.
|
||||
|
||||
- A section cannot have 2 members assigned to it that share the same team.
|
||||
|
||||
## Deliverables
|
||||
|
||||
- A fully functional website that meets the client's functional, content, design, and technical requirements.
|
||||
|
||||
- A user manual or other documentation that explains how to use and maintain the website.
|
||||
|
||||
## Timeline
|
||||
|
||||
- Project kickoff: September 2022.
|
||||
|
||||
- No specific end date set.
|
||||
|
||||
## Budget
|
||||
|
||||
- Total project budget is a Costco hotdog with no onions.
|
||||
|
||||
## Stakeholders
|
||||
|
||||
- Angling Trust staff members responsible for managing events and overseeing participant and team registrations.
|
||||
|
||||
## Approval
|
||||
|
||||
- A project demo presentation was carried out for Angling Trust staff
|
@ -59,7 +59,7 @@ ROOT_URLCONF = 'Results.urls'
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'DIRS': [BASE_DIR / 'mainapp/templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
|
BIN
src/db.sqlite3
Normal file
@ -2,7 +2,7 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Peg, Member, Team
|
||||
from .models import Peg, Member, Team, Section
|
||||
|
||||
|
||||
@admin.register(Peg)
|
||||
@ -19,9 +19,9 @@ class PegAdmin(admin.ModelAdmin):
|
||||
class MemberAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Member model."""
|
||||
|
||||
list_display = ("first_name", "last_name", "team")
|
||||
search_fields = ("first_name", "last_name", "team")
|
||||
list_filter = ("team",)
|
||||
list_display = ("first_name", "last_name", "user", "team", "peg")
|
||||
search_fields = ("first_name", "last_name", "user", "team", "peg")
|
||||
list_filter = ("team", "peg")
|
||||
|
||||
|
||||
@admin.register(Team)
|
||||
@ -33,9 +33,9 @@ class TeamAdmin(admin.ModelAdmin):
|
||||
search_fields = ("team_number",)
|
||||
|
||||
|
||||
# @admin.register(Section)
|
||||
# class SectionAdmin(admin.ModelAdmin):
|
||||
# """Admin model for the Section model."""
|
||||
@admin.register(Section)
|
||||
class SectionAdmin(admin.ModelAdmin):
|
||||
"""Admin model for the Section model."""
|
||||
|
||||
# list_display = ("character",)
|
||||
# search_fields = ("character",)
|
||||
list_display = ("character",)
|
||||
search_fields = ("character",)
|
||||
|
@ -1,302 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"first_name": "Robert",
|
||||
"last_name": "Reid",
|
||||
"team": 1,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"first_name": "Ronald",
|
||||
"last_name": "Jones",
|
||||
"team": 1,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"first_name": "Casey",
|
||||
"last_name": "Cohen",
|
||||
"team": 1,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"first_name": "James",
|
||||
"last_name": "Scudder",
|
||||
"team": 2,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"first_name": "Randall",
|
||||
"last_name": "Young",
|
||||
"team": 2,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"first_name": "Helen",
|
||||
"last_name": "Doak",
|
||||
"team": 2,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"first_name": "Brenda",
|
||||
"last_name": "Powell",
|
||||
"team": 3,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"first_name": "Constance",
|
||||
"last_name": "Abild",
|
||||
"team": 3,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"first_name": "Patsy",
|
||||
"last_name": "Branham",
|
||||
"team": 3,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"first_name": "Cheryl",
|
||||
"last_name": "Sears",
|
||||
"team": 4,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"first_name": "Justin",
|
||||
"last_name": "Cramer",
|
||||
"team": 4,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"first_name": "Theodore",
|
||||
"last_name": "Wilson",
|
||||
"team": 4,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"first_name": "Geneva",
|
||||
"last_name": "Low",
|
||||
"team": 5,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"first_name": "John",
|
||||
"last_name": "Burtt",
|
||||
"team": 5,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"first_name": "Alfred",
|
||||
"last_name": "Diaz",
|
||||
"team": 5,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"first_name": "Arthur",
|
||||
"last_name": "Alton",
|
||||
"team": 6,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"first_name": "Vicki",
|
||||
"last_name": "Greer",
|
||||
"team": 6,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"first_name": "Lewis",
|
||||
"last_name": "Segovia",
|
||||
"team": 6,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"first_name": "Vince",
|
||||
"last_name": "Robinson",
|
||||
"team": 7,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"first_name": "Blake",
|
||||
"last_name": "Mueller",
|
||||
"team": 7,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"first_name": "Luis",
|
||||
"last_name": "Hazel",
|
||||
"team": 7,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"first_name": "Diane",
|
||||
"last_name": "Lloyd",
|
||||
"team": 8,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"first_name": "Jamey",
|
||||
"last_name": "Mendes",
|
||||
"team": 8,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"first_name": "Virgilio",
|
||||
"last_name": "Nixon",
|
||||
"team": 8,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 25,
|
||||
"fields": {
|
||||
"first_name": "Rodney",
|
||||
"last_name": "White",
|
||||
"team": 9,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"first_name": "Kathleen",
|
||||
"last_name": "Ashe",
|
||||
"team": 9,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 27,
|
||||
"fields": {
|
||||
"first_name": "Stephanie",
|
||||
"last_name": "Taylor",
|
||||
"team": 9,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 28,
|
||||
"fields": {
|
||||
"first_name": "John",
|
||||
"last_name": "Brennan",
|
||||
"team": 10,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 29,
|
||||
"fields": {
|
||||
"first_name": "Kenneth",
|
||||
"last_name": "Duff",
|
||||
"team": 10,
|
||||
"peg_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.member",
|
||||
"pk": 30,
|
||||
"fields": {
|
||||
"first_name": "Matthew",
|
||||
"last_name": "Whitesell",
|
||||
"team": 10,
|
||||
"peg_number": null
|
||||
}
|
||||
}
|
||||
]
|
@ -1,72 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"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": {
|
||||
"section_letter": "M"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"section_letter": "Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"section_letter": "Y"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"section_letter": "J"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"section_letter": "W"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"section_letter": "T"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "mainapp.team",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"section_letter": "F"
|
||||
}
|
||||
}
|
||||
]
|
@ -1,52 +0,0 @@
|
||||
import random
|
||||
import names
|
||||
import json
|
||||
from django.core.management.base import BaseCommand
|
||||
from mainapp.models import Member, Team
|
||||
|
||||
|
||||
# TODO: refactor this file like create_teams_fixtures.py
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Creates a fixture with randomly generated Member objects"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("num_members", type=int)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
num_members = options["num_members"]
|
||||
teams = Team.objects.all()
|
||||
if not teams:
|
||||
print("No teams found. Please create some teams first.")
|
||||
return
|
||||
|
||||
members = []
|
||||
for team in teams:
|
||||
for _ in range(num_members):
|
||||
first_name = names.get_first_name()
|
||||
last_name = names.get_last_name()
|
||||
member = Member(first_name=first_name, last_name=last_name, team=team)
|
||||
members.append(member)
|
||||
|
||||
Member.objects.bulk_create(members)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"Created {num_members} members."))
|
||||
|
||||
# create a members fixture file
|
||||
members_fixture = []
|
||||
for member in members:
|
||||
member_fixture = {
|
||||
"model": "mainapp.member",
|
||||
"pk": member.pk,
|
||||
"fields": {
|
||||
"first_name": member.first_name,
|
||||
"last_name": member.last_name,
|
||||
"team": member.team_id,
|
||||
"peg_number": member.peg_number
|
||||
}
|
||||
}
|
||||
members_fixture.append(member_fixture)
|
||||
|
||||
with open("src/mainapp/fixtures/members_fixture.json", "w") as f:
|
||||
f.write(json.dumps(members_fixture, indent=2))
|
||||
self.stdout.write(self.style.SUCCESS("Created members_fixture.json."))
|
@ -1,64 +0,0 @@
|
||||
"""Command to create test data fixture for teams."""
|
||||
|
||||
import random
|
||||
import json
|
||||
from string import ascii_uppercase
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.utils import IntegrityError
|
||||
from mainapp.models import Team, BLOCKED_SECTION_LETTERS
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Creates a fixture file for Team objects"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("amount_of_teams", type=int, help="Number of teams to create")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
existing_teams = Team.objects.all()
|
||||
|
||||
# Available sections
|
||||
available_sections = [
|
||||
char for char in [*ascii_uppercase]
|
||||
if char.lower() not in BLOCKED_SECTION_LETTERS
|
||||
and char not in [team.section_letter for team in existing_teams]
|
||||
]
|
||||
|
||||
if not available_sections:
|
||||
self.stdout.write(self.style.ERROR(
|
||||
f"There are no available sections for new teams."
|
||||
))
|
||||
return
|
||||
|
||||
teams_amount = options["amount_of_teams"]
|
||||
max_teams = len(available_sections)
|
||||
|
||||
if teams_amount > max_teams:
|
||||
self.stdout.write(self.style.ERROR(
|
||||
f"Number of teams is too large [{teams_amount}/{max_teams}]."
|
||||
))
|
||||
return
|
||||
|
||||
# Create the new teams (this will create them in the database)#
|
||||
new_teams = []
|
||||
for i in range(teams_amount):
|
||||
section_letter = random.choice(available_sections)
|
||||
available_sections.remove(section_letter)
|
||||
team = Team.objects.create(section_letter=section_letter)
|
||||
new_teams.append(team)
|
||||
|
||||
teams_fixture = [{
|
||||
"model": "mainapp.team",
|
||||
"pk": team.pk,
|
||||
"fields": {"section_letter": team.section_letter}
|
||||
} for team in new_teams]
|
||||
|
||||
# Remove the teams from the database
|
||||
for team in new_teams:
|
||||
team.delete()
|
||||
|
||||
with open("src/mainapp/fixtures/teams_fixture.json", "w") as file:
|
||||
file.write(json.dumps(teams_fixture, indent=4))
|
||||
self.stdout.write(self.style.SUCCESS("Created teams_fixture.json."))
|
@ -1,5 +1,6 @@
|
||||
# Generated by Django 4.1.5 on 2023-05-07 21:57
|
||||
# Generated by Django 4.1.5 on 2023-01-26 14:24
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import mainapp.models
|
||||
@ -10,6 +11,7 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@ -19,11 +21,17 @@ class Migration(migrations.Migration):
|
||||
('peg_number', mainapp.models.ReusableAutoField(default=mainapp.models.ReusableAutoField.get_default, editable=False, primary_key=True, serialize=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Section',
|
||||
fields=[
|
||||
('character', models.CharField(max_length=1, primary_key=True, serialize=False, validators=[mainapp.models.validate_section_character])),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Team',
|
||||
fields=[
|
||||
('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)),
|
||||
('section', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.section')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -32,8 +40,8 @@ class Migration(migrations.Migration):
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=255)),
|
||||
('last_name', models.CharField(max_length=255)),
|
||||
('peg_number', models.PositiveIntegerField(null=True, unique=True)),
|
||||
('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, to='mainapp.team')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
25
src/mainapp/migrations/0002_member_peg_alter_member_team.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.1.5 on 2023-01-26 14:31
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import mainapp.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mainapp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='member',
|
||||
name='peg',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.peg'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='member',
|
||||
name='team',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mainapp.team', validators=[mainapp.models.validate_team_size]),
|
||||
),
|
||||
]
|
@ -1,14 +1,13 @@
|
||||
from string import ascii_uppercase
|
||||
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
# products/models.py
|
||||
from django.db import models
|
||||
|
||||
|
||||
class ReusableAutoField(models.PositiveIntegerField):
|
||||
"""A django auto field that can reuse deleted primary keys"""
|
||||
|
||||
|
||||
def get_next_available_id(self, model_cls, using=None):
|
||||
"""
|
||||
Returns the next available id for the given model class.
|
||||
@ -50,47 +49,23 @@ class Member(models.Model):
|
||||
|
||||
first_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')
|
||||
peg_number = models.PositiveIntegerField(null=True, editable=True, unique=True)
|
||||
# peg = models.OneToOneField("Peg", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# If the peg_number field is not set, we assign it the smallest
|
||||
# available positive integer, excluding any used values.
|
||||
if not self.peg_number:
|
||||
used_peg_numbers = Member.objects.exclude(id=self.id).exclude(peg_number=None).values_list("peg_number", flat=True)
|
||||
peg_numbers = set(range(1, Member.objects.count() + 1)) - set(used_peg_numbers)
|
||||
if peg_numbers:
|
||||
self.peg_number = min (peg_numbers)
|
||||
user = models.OneToOneField("auth.User", on_delete=models.CASCADE)
|
||||
team = models.ForeignKey("Team", on_delete=models.SET_NULL, null=True, blank=True, validators=(validate_team_size,))
|
||||
peg = models.OneToOneField("Peg", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.first_name} {self.last_name} (team {self.team.team_number}))"
|
||||
|
||||
@property
|
||||
def fullname(self) -> str:
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
|
||||
|
||||
BLOCKED_SECTION_LETTERS = ["i"] # lowercase only, sections cannot be any of these letters#
|
||||
|
||||
|
||||
class Team(models.Model):
|
||||
"""Represents a team"""
|
||||
|
||||
team_number = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False)
|
||||
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
|
||||
])
|
||||
section = models.OneToOneField("Section", on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"Team {self.team_number}"
|
||||
|
||||
@property
|
||||
def members(self) -> list[Member]:
|
||||
Member.objects.filter(team=self)
|
||||
|
||||
|
||||
def validate_section_character(value):
|
||||
"""Validates the section character"""
|
||||
@ -102,17 +77,17 @@ def validate_section_character(value):
|
||||
raise ValidationError(f"The character <{value}> is a prohibited character.")
|
||||
|
||||
|
||||
# class Section(models.Model):
|
||||
# """Represents a section of the scoreboard"""
|
||||
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, ))
|
||||
# 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 clean(self):
|
||||
self.character = self.character.upper()
|
||||
|
||||
# def __str__(self):
|
||||
# return f"Section {self.character}"
|
||||
def __str__(self):
|
||||
return f"Section {self.character}"
|
||||
|
||||
|
||||
#class Scoreboard(models.Model):
|
||||
|
@ -1,11 +0,0 @@
|
||||
import names
|
||||
from string import ascii_lowercase
|
||||
|
||||
created_teams = []
|
||||
for char in ascii_lowercase:
|
||||
created_teams.append({
|
||||
"identifier": char.upper(),
|
||||
"members": [{
|
||||
"name": names.get_full_name()
|
||||
} for i in range(9)]
|
||||
})
|
@ -1,56 +1,63 @@
|
||||
{% extends "base.html" %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<title>Home</title>
|
||||
|
||||
{% block content %}
|
||||
<section class="py-5 text-center container">
|
||||
<div class="row py-lg-5">
|
||||
<div class="col-lg-6 col-md-8 mx-auto">
|
||||
<h1 class="fw-light text-white">Results System</h1>
|
||||
<p class="lead text-white">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi faucibus elementum diam nec semper. Nullam vestibulum enim eu nisi condimentum, vitae suscipit risus imperdiet.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Styles -->
|
||||
{% load static %}
|
||||
<link rel="stylesheet" href="{% static 'css/index.css' %}">
|
||||
</head>
|
||||
|
||||
<div class="album py-5 bg-light">
|
||||
<div class="container">
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
|
||||
|
||||
<!-- Teams & Members -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="{% url 'teams' %}">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Teams / Members</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<body>
|
||||
<main>
|
||||
|
||||
<!-- Scoreboard -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="{% url 'scoreboard' %}">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Scoreboard</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Results -->
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="{% url 'results' %}">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Results</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<section class="py-5 text-center container">
|
||||
<div class="row py-lg-5">
|
||||
<div class="col-lg-6 col-md-8 mx-auto">
|
||||
<h1 class="fw-light text-white">Results System</h1>
|
||||
<p class="lead text-white">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi faucibus elementum diam nec semper. Nullam vestibulum enim eu nisi condimentum, vitae suscipit risus imperdiet.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
</section>
|
||||
|
||||
<div class="album py-5 bg-light">
|
||||
<div class="container">
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="teams.html">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Teams / Members</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="scoreboard.html">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Scoreboard</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card shadow-sm">
|
||||
<a href="results.html">
|
||||
<svg class="bd-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="#55595c"/><text x="50%" y="50%" fill="#eceeef" dy=".3em">Thumbnail</text></svg>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Results</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,11 +1,9 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Results |
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="bg-dark-subtle p-4">
|
||||
<a href="{% url 'index' %}" class="btn btn-primary">Back</a>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<title>Results</title>
|
||||
|
@ -1,11 +1,49 @@
|
||||
{% extends "base.html" %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<title>Scoreboard</title>
|
||||
<style>
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% block title %}
|
||||
Scoreboard |
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="bg-dark-subtle p-4">
|
||||
<a href="{% url 'index' %}" class="btn btn-primary">Back</a>
|
||||
<body>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col">
|
||||
<table class="table table-responsive table-striped table-hover table-bordered border-primary" contenteditable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">First</th>
|
||||
<th scope="col">Last</th>
|
||||
<th scope="col">Handle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td>Mark</td>
|
||||
<td>Otto</td>
|
||||
<td>@mdo</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2</th>
|
||||
<td>Jacob</td>
|
||||
<td>Thornton</td>
|
||||
<td>@fat</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">3</th>
|
||||
<td colspan="2">Larry the Bird</td>
|
||||
<td>@twitter</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</body>
|
@ -1,160 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Teams & Members |
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="bg-body p-4">
|
||||
<a href="{% url 'index' %}" class="btn btn-company px-4">Back</a>
|
||||
</div>
|
||||
|
||||
<div class="container my-4 p-4 pb-0 bg-body rounded">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12 col-xl-4">
|
||||
<h3 class="mb-3 mb-xl-0">Teams & Members</h3>
|
||||
</div>
|
||||
<div class="col-sm-3 col-md-6 col-xl-4 mb-3 mb-sm-0">
|
||||
<div class="input-group justify-content-xl-end">
|
||||
<button class="btn border-secondary-subtle btn-outline-company" id="addMember" data-bs-toggle="tooltip" data-bs-title="Add Member">
|
||||
<i class="bi bi-person"></i>
|
||||
</button>
|
||||
<button class="btn border-secondary-subtle btn-outline-company" id="addTeam" data-bs-toggle="tooltip" data-bs-title="Add Team">
|
||||
<i class="bi bi-people"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-md-6 col-xl-4">
|
||||
<div class="input-group dropdown">
|
||||
<button type="button" data-bs-toggle="dropdown" class="btn btn-outline-company border-secondary-subtle">
|
||||
<i class="bi bi-sort-up"></i>
|
||||
</button>
|
||||
<input type="search" class="form-control border-secondary-subtle shadow-none" placeholder="Search Members" id="search">
|
||||
<button type="button" class="btn btn-outline-company border-secondary-subtle rounded-end" id="searchButton"><i class="bi bi-search"></i></button>
|
||||
<form id="sortForm">
|
||||
<ul class="dropdown-menu py-3 text-body-secondary bg-body-tertiary border border-light-subtle shadow-sm justify-self-center mt-2" onclick="event.stopPropagation()">
|
||||
<li class="px-4">
|
||||
<h3 class="h6 mb-3">Sort groups by</h3>
|
||||
<div>
|
||||
<div class="d-inline-block form-check">
|
||||
<input type="radio" class="form-check-input" checked value="team_number" name="sortTeams" id="sortTeamsName">
|
||||
<label for="sortTeamsName" class="form-check-label">Teams</label>
|
||||
</div>
|
||||
<div class="d-inline-block form-check ms-4">
|
||||
<input type="radio" class="form-check-input" value="section_letter" name="sortTeams" id="sortTeamsSection">
|
||||
<label for="sortTeamsSection" class="form-check-label">Sections</label>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown-divider my-3 mx-4 bg-light-subtle"></li>
|
||||
<li class="px-4">
|
||||
<h3 class="h6 mb-3">Sort members by</h3>
|
||||
<div>
|
||||
<div class="d-inline-block form-check">
|
||||
<input type="radio" class="form-check-input" value="first_name" name="sortMembers" id="sortMembersName">
|
||||
<label for="sortMembersName" class="form-check-label">Name</label>
|
||||
</div>
|
||||
<div class="d-inline-block form-check ms-4">
|
||||
<input type="radio" class="form-check-input" checked value="peg_number" name="sortMembers" id="sortMembersPeg">
|
||||
<label for="sortMembersPeg" class="form-check-label">Peg Number</label>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" id="teamsContainer"></div>
|
||||
<div class="col-12 text-center pb-4" id="teamsLoadingSpinner">
|
||||
<div class="spinner-border spinner-border-lg" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pb-4" id="teamsNotFound">
|
||||
<div class="alert alert-danger m-0" role="alert">
|
||||
No teams found under that search
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Edit Team Modal -->
|
||||
<div class="modal fade" id="editTeamModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title fs-5" id="editTeamTitle">Team Number Here</h3>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editTeamForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<label for="editTeamNumber" class="form-label">Team Number</label>
|
||||
<input type="number" name="editTeamNumber" class="form-control" id="editTeamNumber">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="editTeamSection" class="form-label">Section Letter</label>
|
||||
<input type="text" name="editTeamSection" class="form-control" id="editTeamSection">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="saveEditTeamModal">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Member Modal -->
|
||||
<div class="modal fade" id="editMemberModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title fs-5" id="editMemberName">Member Name Here</h3>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editMemberForm">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="editMemberFirstName" class="form-label">Forename</label>
|
||||
<input type="text" name="editMemberFirstName" id="editMemberFirstName" class="form-control" required minlength="1" maxlength="30">
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="editMemberLastName" class="form-label">Surname</label>
|
||||
<input type="text" name="editMemberLastName" id="editMemberLastName" class="form-control" required minlength="1" maxlength="30">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="editMemberTeam" class="form-label">Team</label>
|
||||
<select name="editMemberTeam" id="editMemberTeam" class="form-select" required></select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="editMemberPeg" class="form-label">Peg Number</label>
|
||||
<input type="number" name="editMemberPeg" id="editMemberPeg" class="form-control" min="1" max="9999">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" id="saveEditModal">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
{% load static %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
const getTeamsUrl = "{% url 'get-teams' %}";
|
||||
const updateMemberUrl = "{% url 'update-member' %}";
|
||||
const csrfMiddlewareToken = "{{ csrf_token }}";
|
||||
</script>
|
||||
<script src="{% static 'js/teams.js' %}"></script>
|
||||
{% endblock scripts %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||
<title>Teams</title>
|
@ -3,11 +3,8 @@ from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'),
|
||||
path('results/', views.results, name='results'),
|
||||
path('scoreboard/', views.scoreboard, name='scoreboard'),
|
||||
path('teams/', views.teams, name='teams'),
|
||||
|
||||
path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg'),
|
||||
path('get-teams/', views.get_teams, name='get-teams'),
|
||||
path('update-member/', views.update_member, name='update-member')
|
||||
path('results.html', views.results, name='results'),
|
||||
path('scoreboard.html', views.scoreboard, name='scoreboard'),
|
||||
path('teams.html', views.teams, name='teams'),
|
||||
path('bulk-peg/', views.bulk_create_pegs, name='bulk-peg')
|
||||
]
|
@ -1,13 +1,6 @@
|
||||
"""Views for the main app."""
|
||||
|
||||
from functools import reduce
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Q, Case, When, Value, IntegerField
|
||||
|
||||
from .models import Peg, Team, Member
|
||||
|
||||
from .models import Peg
|
||||
|
||||
def index(request):
|
||||
return render(request, 'index.html')
|
||||
@ -30,87 +23,3 @@ def bulk_create_pegs(request):
|
||||
|
||||
# return to previous page
|
||||
return redirect(request.META.get('HTTP_REFERER'))
|
||||
|
||||
def get_teams(request):
|
||||
"""Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams.
|
||||
|
||||
Args:
|
||||
request: the web request object.
|
||||
Returns:
|
||||
JsonResponse: dictionary of teams like so {'teams': [{}, {}, {}]}.
|
||||
"""
|
||||
|
||||
if not request.POST:
|
||||
return
|
||||
|
||||
search = request.POST.get("search")
|
||||
sort_teams = request.POST.get("sortTeams") or "team_number"
|
||||
sort_members = request.POST.get("sortMembers") or "peg_number"
|
||||
|
||||
teams = Team.objects.order_by(sort_teams).all()
|
||||
|
||||
# Filter out teams that don't contain members being searched for
|
||||
if search:
|
||||
search_terms = search.split()
|
||||
members = Member.objects.filter(
|
||||
reduce(
|
||||
lambda x, y: x | y,
|
||||
[
|
||||
Q(first_name__icontains=term) | Q(last_name__icontains=term)
|
||||
for term in search_terms
|
||||
]
|
||||
)
|
||||
)
|
||||
teams = teams.filter(members__in=members).distinct()
|
||||
|
||||
# Create a dictionary for the data that is JSON safe
|
||||
response_data = {"teams": []}
|
||||
for team in teams:
|
||||
team_data = {
|
||||
"team_number": team.team_number,
|
||||
"section_letter": team.section_letter if team.section_letter else None,
|
||||
"members": [
|
||||
{
|
||||
"first": member.first_name,
|
||||
"last": member.last_name,
|
||||
"id": member.id,
|
||||
"team": team.team_number,
|
||||
"peg": member.peg_number,
|
||||
"section": team.section_letter if team.section_letter else None
|
||||
}
|
||||
for member in team.members.order_by(sort_members).all()
|
||||
]
|
||||
}
|
||||
response_data["teams"].append(team_data)
|
||||
|
||||
response_data["sortTeams"] = sort_teams
|
||||
response_data["sortMembers"] = sort_members
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
||||
def update_member(request):
|
||||
"""Update a member. Returns a JsonResponse with the updated teams."""
|
||||
|
||||
if not request.POST:
|
||||
return
|
||||
|
||||
# Get the updated values
|
||||
member_id = request.POST.get("memberId")
|
||||
first = request.POST.get("first")
|
||||
last = request.POST.get("last")
|
||||
team_number = request.POST.get("teamNumber")
|
||||
peg_number = request.POST.get("pegNumber")
|
||||
|
||||
# Get the member and team
|
||||
member = Member.objects.get(id=member_id)
|
||||
team = Team.objects.get(team_number=team_number)
|
||||
|
||||
# Update the member
|
||||
member.first_name = first
|
||||
member.last_name = last
|
||||
member.team = team
|
||||
member.peg_number = peg_number
|
||||
|
||||
member.save()
|
||||
|
||||
return get_teams(request)
|
||||
|
6
src/static/css/bootstrap.min.css
vendored
@ -1,121 +0,0 @@
|
||||
|
||||
.ul-cols-2 {
|
||||
column-count: 2;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
.ul-cols-2 li {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.spinner-border-lg {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.fluid-hover-zoom {
|
||||
transition:
|
||||
.3s transform cubic-bezier(.155,1.105,.295,1.12),
|
||||
.3s box-shadow,
|
||||
.3s -webkit-transform cubic-bezier(.155,1.105,.295,1.12);
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.fluid-hover-zoom:hover {
|
||||
transform: scale(1.035);
|
||||
}
|
||||
|
||||
.no-transform {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.shadow-on-hover:not(:hover) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.team-column {
|
||||
opacity: 0;
|
||||
transition: 5s opacity ease-in;
|
||||
}
|
||||
|
||||
.fixed-badge {
|
||||
min-width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.font-montserrat {
|
||||
font-family: Montserrat;
|
||||
}
|
||||
|
||||
.font-raleway {
|
||||
font-family: Raleway;
|
||||
}
|
||||
|
||||
.font-ssp {
|
||||
font-family: "Source Sans Pro";
|
||||
}
|
||||
|
||||
.pencil-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.force-contents-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.md-shadow-on-hover:hover {
|
||||
box-shadow: rgba(0, 0, 0, 0.15) 0px 8px 16px 0px !important;
|
||||
}
|
||||
|
||||
.text-company {
|
||||
color: #04385c;
|
||||
}
|
||||
|
||||
.company-bg {
|
||||
background-color: #04385c !important;
|
||||
}
|
||||
|
||||
.underline-company {
|
||||
text-decoration: underline solid #04385c;
|
||||
}
|
||||
|
||||
.border-company {
|
||||
border-color: #04385c !important;
|
||||
}
|
||||
|
||||
.btn-company {
|
||||
color: white;
|
||||
background-color: #04385c;
|
||||
}
|
||||
|
||||
.btn-company:hover {
|
||||
color: white;
|
||||
background-color: #002d51;
|
||||
}
|
||||
|
||||
.btn-outline-company {
|
||||
border: 1px solid #04385c;
|
||||
}
|
||||
|
||||
.btn-outline-company:hover {
|
||||
color: white;
|
||||
background-color: #04385c;
|
||||
border: 1px solid #04385c;
|
||||
}
|
||||
|
||||
|
||||
.hover-only-bg:not(:hover) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
input[type='radio']:checked {
|
||||
background-color: #04385c;
|
||||
border: 1px solid #04385c;
|
||||
box-shadow: none;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/* body {
|
||||
body {
|
||||
background-color: #0d5979;
|
||||
} */
|
||||
}
|
||||
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
|
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-0-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"/>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-8.012 4.158c1.858 0 2.96-1.582 2.96-3.99V7.84c0-2.426-1.079-3.996-2.936-3.996-1.864 0-2.965 1.588-2.965 3.996v.328c0 2.42 1.09 3.99 2.941 3.99Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 479 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-0-circle" viewBox="0 0 16 16">
|
||||
<path d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99ZM8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"/>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 511 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-0-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"/>
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm5.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 518 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-0-square" viewBox="0 0 16 16">
|
||||
<path d="M7.988 12.158c-1.851 0-2.941-1.57-2.941-3.99V7.84c0-2.408 1.101-3.996 2.965-3.996 1.857 0 2.935 1.57 2.935 3.996v.328c0 2.408-1.101 3.99-2.959 3.99ZM8 4.951c-1.008 0-1.629 1.09-1.629 2.895v.31c0 1.81.627 2.895 1.629 2.895s1.623-1.09 1.623-2.895v-.31c0-1.8-.621-2.895-1.623-2.895Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 585 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-1-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM9.283 4.002H7.971L6.072 5.385v1.271l1.834-1.318h.065V12h1.312V4.002Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 257 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-1-circle" viewBox="0 0 16 16">
|
||||
<path d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM9.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383h1.312Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 287 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-1-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm7.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383h1.312Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 294 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-1-square" viewBox="0 0 16 16">
|
||||
<path d="M9.283 4.002V12H7.971V5.338h-.065L6.072 6.656V5.385l1.899-1.383h1.312Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 376 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-123" viewBox="0 0 16 16">
|
||||
<path d="M2.873 11.297V4.142H1.699L0 5.379v1.137l1.64-1.18h.06v5.961h1.174Zm3.213-5.09v-.063c0-.618.44-1.169 1.196-1.169.676 0 1.174.44 1.174 1.106 0 .624-.42 1.101-.807 1.526L4.99 10.553v.744h4.78v-.99H6.643v-.069L8.41 8.252c.65-.724 1.237-1.332 1.237-2.27C9.646 4.849 8.723 4 7.308 4c-1.573 0-2.36 1.064-2.36 2.15v.057h1.138Zm6.559 1.883h.786c.823 0 1.374.481 1.379 1.179.01.707-.55 1.216-1.421 1.21-.77-.005-1.326-.419-1.379-.953h-1.095c.042 1.053.938 1.918 2.464 1.918 1.478 0 2.642-.839 2.62-2.144-.02-1.143-.922-1.651-1.551-1.714v-.063c.535-.09 1.347-.66 1.326-1.678-.026-1.053-.933-1.855-2.359-1.845-1.5.005-2.317.88-2.348 1.898h1.116c.032-.498.498-.944 1.206-.944.703 0 1.206.435 1.206 1.07.005.64-.504 1.106-1.2 1.106h-.75v.96Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 870 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-2-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM6.646 6.24c0-.691.493-1.306 1.336-1.306.756 0 1.313.492 1.313 1.236 0 .697-.469 1.23-.902 1.705l-2.971 3.293V12h5.344v-1.107H7.268v-.077l1.974-2.22.096-.107c.688-.763 1.287-1.428 1.287-2.43 0-1.266-1.031-2.215-2.613-2.215-1.758 0-2.637 1.19-2.637 2.402v.065h1.271v-.07Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 458 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-2-circle" viewBox="0 0 16 16">
|
||||
<path d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM6.646 6.24v.07H5.375v-.064c0-1.213.879-2.402 2.637-2.402 1.582 0 2.613.949 2.613 2.215 0 1.002-.6 1.667-1.287 2.43l-.096.107-1.974 2.22v.077h3.498V12H5.422v-.832l2.97-3.293c.434-.475.903-1.008.903-1.705 0-.744-.557-1.236-1.313-1.236-.843 0-1.336.615-1.336 1.306Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 480 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-2-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm4.646 6.24v.07H5.375v-.064c0-1.213.879-2.402 2.637-2.402 1.582 0 2.613.949 2.613 2.215 0 1.002-.6 1.667-1.287 2.43l-.096.107-1.974 2.22v.077h3.498V12H5.422v-.832l2.97-3.293c.434-.475.903-1.008.903-1.705 0-.744-.557-1.236-1.313-1.236-.843 0-1.336.615-1.336 1.306Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 487 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-2-square" viewBox="0 0 16 16">
|
||||
<path d="M6.646 6.24v.07H5.375v-.064c0-1.213.879-2.402 2.637-2.402 1.582 0 2.613.949 2.613 2.215 0 1.002-.6 1.667-1.287 2.43l-.096.107-1.974 2.22v.077h3.498V12H5.422v-.832l2.97-3.293c.434-.475.903-1.008.903-1.705 0-.744-.557-1.236-1.313-1.236-.843 0-1.336.615-1.336 1.306Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 569 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-3-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-8.082.414c.92 0 1.535.54 1.541 1.318.012.791-.615 1.36-1.588 1.354-.861-.006-1.482-.469-1.54-1.066H5.104c.047 1.177 1.05 2.144 2.754 2.144 1.653 0 2.954-.937 2.93-2.396-.023-1.278-1.031-1.846-1.734-1.916v-.07c.597-.1 1.505-.739 1.482-1.876-.03-1.177-1.043-2.074-2.637-2.062-1.675.006-2.59.984-2.625 2.12h1.248c.036-.556.557-1.054 1.348-1.054.785 0 1.348.486 1.348 1.195.006.715-.563 1.237-1.342 1.237h-.838v1.072h.879Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 608 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-3-circle" viewBox="0 0 16 16">
|
||||
<path d="M7.918 8.414h-.879V7.342h.838c.78 0 1.348-.522 1.342-1.237 0-.709-.563-1.195-1.348-1.195-.79 0-1.312.498-1.348 1.055H5.275c.036-1.137.95-2.115 2.625-2.121 1.594-.012 2.608.885 2.637 2.062.023 1.137-.885 1.776-1.482 1.875v.07c.703.07 1.71.64 1.734 1.917.024 1.459-1.277 2.396-2.93 2.396-1.705 0-2.707-.967-2.754-2.144H6.33c.059.597.68 1.06 1.541 1.066.973.006 1.6-.563 1.588-1.354-.006-.779-.621-1.318-1.541-1.318Z"/>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 645 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-3-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm5.918 8.414h-.879V7.342h.838c.78 0 1.348-.522 1.342-1.237 0-.709-.563-1.195-1.348-1.195-.79 0-1.312.498-1.348 1.055H5.275c.036-1.137.95-2.115 2.625-2.121 1.594-.012 2.608.885 2.637 2.062.023 1.137-.885 1.776-1.482 1.875v.07c.703.07 1.71.64 1.734 1.917.024 1.459-1.277 2.396-2.93 2.396-1.705 0-2.707-.967-2.754-2.144H6.33c.059.597.68 1.06 1.541 1.066.973.006 1.6-.563 1.588-1.354-.006-.779-.621-1.318-1.541-1.318Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 637 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-3-square" viewBox="0 0 16 16">
|
||||
<path d="M7.918 8.414h-.879V7.342h.838c.78 0 1.348-.522 1.342-1.237 0-.709-.563-1.195-1.348-1.195-.79 0-1.312.498-1.348 1.055H5.275c.036-1.137.95-2.115 2.625-2.121 1.594-.012 2.608.885 2.637 2.062.023 1.137-.885 1.776-1.482 1.875v.07c.703.07 1.71.64 1.734 1.917.024 1.459-1.277 2.396-2.93 2.396-1.705 0-2.707-.967-2.754-2.144H6.33c.059.597.68 1.06 1.541 1.066.973.006 1.6-.563 1.588-1.354-.006-.779-.621-1.318-1.541-1.318Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 719 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-4-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM7.519 5.057c-.886 1.418-1.772 2.838-2.542 4.265v1.12H8.85V12h1.26v-1.559h1.007V9.334H10.11V4.002H8.176c-.218.352-.438.703-.657 1.055ZM6.225 9.281v.053H8.85V5.063h-.065c-.867 1.33-1.787 2.806-2.56 4.218Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 391 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-4-circle" viewBox="0 0 16 16">
|
||||
<path d="M7.519 5.057c.22-.352.439-.703.657-1.055h1.933v5.332h1.008v1.107H10.11V12H8.85v-1.559H4.978V9.322c.77-1.427 1.656-2.847 2.542-4.265ZM6.225 9.281v.053H8.85V5.063h-.065c-.867 1.33-1.787 2.806-2.56 4.218Z"/>
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 433 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-4-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M6.225 9.281v.053H8.85V5.063h-.065c-.867 1.33-1.787 2.806-2.56 4.218Z"/>
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm5.519 5.057c.22-.352.439-.703.657-1.055h1.933v5.332h1.008v1.107H10.11V12H8.85v-1.559H4.978V9.322c.77-1.427 1.656-2.847 2.542-4.265Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 440 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-4-square" viewBox="0 0 16 16">
|
||||
<path d="M7.519 5.057c.22-.352.439-.703.657-1.055h1.933v5.332h1.008v1.107H10.11V12H8.85v-1.559H4.978V9.322c.77-1.427 1.656-2.847 2.542-4.265ZM6.225 9.281v.053H8.85V5.063h-.065c-.867 1.33-1.787 2.806-2.56 4.218Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 507 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-5-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0Zm-8.006 4.158c1.74 0 2.924-1.119 2.924-2.806 0-1.641-1.178-2.584-2.56-2.584-.897 0-1.442.421-1.612.68h-.064l.193-2.344h3.621V4.002H5.791L5.445 8.63h1.149c.193-.358.668-.809 1.435-.809.85 0 1.582.604 1.582 1.57 0 1.085-.779 1.682-1.57 1.682-.697 0-1.389-.31-1.53-1.031H5.276c.065 1.213 1.149 2.115 2.72 2.115Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 496 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-5-circle" viewBox="0 0 16 16">
|
||||
<path d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm15 0A8 8 0 1 0 0 8a8 8 0 0 0 16 0Zm-8.006 4.158c-1.57 0-2.654-.902-2.719-2.115h1.237c.14.72.832 1.031 1.529 1.031.791 0 1.57-.597 1.57-1.681 0-.967-.732-1.57-1.582-1.57-.767 0-1.242.45-1.435.808H5.445L5.791 4h4.705v1.103H6.875l-.193 2.343h.064c.17-.258.715-.68 1.611-.68 1.383 0 2.561.944 2.561 2.585 0 1.687-1.184 2.806-2.924 2.806Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 516 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-5-square-fill" viewBox="0 0 16 16">
|
||||
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Zm5.994 12.158c-1.57 0-2.654-.902-2.719-2.115h1.237c.14.72.832 1.031 1.529 1.031.791 0 1.57-.597 1.57-1.681 0-.967-.732-1.57-1.582-1.57-.767 0-1.242.45-1.435.808H5.445L5.791 4h4.705v1.103H6.875l-.193 2.343h.064c.17-.258.715-.68 1.611-.68 1.383 0 2.561.944 2.561 2.585 0 1.687-1.184 2.806-2.924 2.806Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 523 B |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-5-square" viewBox="0 0 16 16">
|
||||
<path d="M7.994 12.158c-1.57 0-2.654-.902-2.719-2.115h1.237c.14.72.832 1.031 1.529 1.031.791 0 1.57-.597 1.57-1.681 0-.967-.732-1.57-1.582-1.57-.767 0-1.242.45-1.435.808H5.445L5.791 4h4.705v1.103H6.875l-.193 2.343h.064c.17-.258.715-.68 1.611-.68 1.383 0 2.561.944 2.561 2.585 0 1.687-1.184 2.806-2.924 2.806Z"/>
|
||||
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2Zm15 0a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 605 B |
@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-6-circle-fill" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0ZM8.21 3.855c-1.868 0-3.116 1.395-3.116 4.407 0 1.183.228 2.039.597 2.642.569.926 1.477 1.254 2.409 1.254 1.629 0 2.847-1.013 2.847-2.783 0-1.676-1.254-2.555-2.508-2.555-1.125 0-1.752.61-1.98 1.155h-.082c-.012-1.946.727-3.036 1.805-3.036.802 0 1.213.457 1.312.815h1.29c-.06-.908-.962-1.899-2.573-1.899Zm-.099 4.008c-.92 0-1.564.65-1.564 1.576 0 1.032.703 1.635 1.558 1.635.868 0 1.553-.533 1.553-1.629 0-1.06-.744-1.582-1.547-1.582Z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 619 B |