623 lines
18 KiB
Python
623 lines
18 KiB
Python
"""Views for the main app."""
|
|
|
|
import json
|
|
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, Min
|
|
from django.db.utils import IntegrityError
|
|
from django.views import View
|
|
from django.views.decorators.http import require_GET, require_POST
|
|
from django.http import HttpRequest, HttpResponseNotFound
|
|
from django.core import serializers
|
|
from django.forms.models import model_to_dict
|
|
|
|
from .models import Venue, Waters
|
|
# from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator
|
|
|
|
|
|
def index(request):
|
|
|
|
venues = Venue.objects.all()
|
|
context = {"venues": venues, "venue_types": Venue.VENUE_TYPES}
|
|
|
|
return render(request, 'index.html', context)
|
|
|
|
def results(request):
|
|
return render(request, 'results.html')
|
|
|
|
def scoreboard(request):
|
|
return render(request, 'scoreboard.html')
|
|
|
|
def teams(request):
|
|
return render(request, 'teams.html')
|
|
|
|
def venues(request):
|
|
|
|
venues = Venue.objects.all()
|
|
context = {"venues": venues, "venue_types": Venue.VENUE_TYPES}
|
|
|
|
return render(request, 'venues.html', context)
|
|
|
|
def venue_details(request, venue_id):
|
|
|
|
try:
|
|
venue = Venue.objects.get(id=venue_id)
|
|
except Venue.DoesNotExist:
|
|
return HttpResponseNotFound("<h1>404 - Venue not found</h1>")
|
|
|
|
context = {"venue": venue}
|
|
|
|
return render(request, 'venue_details.html', context)
|
|
|
|
def create_venue(request):
|
|
|
|
if request.method != "POST":
|
|
return JsonResponse({"error", "Method not allowed"}, status=403)
|
|
|
|
test = request.POST
|
|
|
|
attributes = {
|
|
name: request.POST.get(name) for name in
|
|
[field.name for field in Venue._meta.get_fields()]
|
|
}
|
|
venue_id = request.POST.get("id")
|
|
|
|
if venue_id:
|
|
venue = Venue.objects.get(id=venue_id)
|
|
for k, v in attributes.items():
|
|
setattr(venue, k, v)
|
|
|
|
venue.save()
|
|
return JsonResponse({"success": "successful update"}, status=200)
|
|
|
|
del attributes["id"]
|
|
Venue.objects.create(**attributes)
|
|
return JsonResponse({"success": "successful creation"}, status=200)
|
|
|
|
def get_venue_details(request, venue_id: int):
|
|
|
|
try:
|
|
venue = Venue.objects.get(pk=venue_id)
|
|
except Venue.DoesNotExist:
|
|
return JsonResponse({"error": "Venue not found"}, status=404)
|
|
|
|
json_venue = model_to_dict(venue)
|
|
|
|
return JsonResponse({"data": json_venue})
|
|
|
|
def get_venue_waters(request, venue_id: int):
|
|
|
|
try:
|
|
venue = Venue.objects.get(pk=venue_id)
|
|
except Venue.DoesNotExist:
|
|
return JsonResponse({"error": "Venue not found"}, status=404)
|
|
|
|
waters = Waters.objects.filter(venue=venue)
|
|
waters_data = [
|
|
{
|
|
"name": water.name,
|
|
"description": water.description,
|
|
"pegs_min": water.pegs_min,
|
|
"pegs_max": water.pegs_max,
|
|
"water_type": water.water_type,
|
|
"fish_type": water.fish_type
|
|
}
|
|
for water in waters
|
|
]
|
|
return JsonResponse({"waters", waters_data})
|
|
|
|
# def bulk_create_pegs(request):
|
|
# """Bulk create pegs"""
|
|
|
|
# number_of_pegs = request.POST.get("pegAmount")
|
|
# for i in range(int(number_of_pegs)):
|
|
# Peg.objects.create()
|
|
|
|
# # return to previous page
|
|
# return redirect(request.META.get('HTTP_REFERER'))
|
|
|
|
def name_sort_key(section):
|
|
if len(section.name) == 1:
|
|
return (0, section.name)
|
|
else:
|
|
return (1, section.name[:-1], section.name[-1])
|
|
|
|
|
|
class ManageAnglersView(View):
|
|
"""View for the Manage Anglers page."""
|
|
|
|
template_name = "anglers.html"
|
|
|
|
def get(self, request: HttpRequest, *args, **kwargs) -> HttpRequest:
|
|
"""Handle GET requests to the Manage Anglers page.
|
|
|
|
Args:
|
|
request (HttpRequest): The HttpRequest object, contains GET data.
|
|
|
|
Returns:
|
|
HttpRequest: A render of the Manage Anglers page.
|
|
"""
|
|
|
|
anglers = Member.objects.order_by("first_name", "last_name")
|
|
context = {"anglers": anglers}
|
|
|
|
return render(request, self.template_name, context)
|
|
|
|
def post(self, request: HttpRequest, *args, **kwargs) -> JsonResponse:
|
|
"""Handle POST requests to the Manage Anglers page.
|
|
|
|
Args:
|
|
request (HttpRequest): The HttpRequest object, contains POST data.
|
|
|
|
Returns:
|
|
JsonResponse: Contains the result of the action.
|
|
"""
|
|
|
|
tasks = request.POST.getlist("tasks[]")
|
|
|
|
data = {}
|
|
for task in tasks:
|
|
data.update(self.handle_task(request, task))
|
|
|
|
return JsonResponse(data)
|
|
|
|
def handle_task(self, request, task: str) -> dict[str, str]:
|
|
"""Handle a task.
|
|
|
|
Args:
|
|
request (HttpRequest): HttpRequest object, contains POST data.
|
|
task (str): The task to handle.
|
|
|
|
Raises:
|
|
ValueError: The task is invalid.
|
|
|
|
Returns:
|
|
dict[str, str]: The result of the task.
|
|
"""
|
|
|
|
# Format is {key = ACTION-TASK_NAME: value = HANDLER_FUNCTION}
|
|
task_handlers = {
|
|
"update-team": self.update_team,
|
|
"update-section": self.update_section,
|
|
"update-angler": self.update_angler,
|
|
"get-teams": self.get_teams,
|
|
"get-sections": self.get_sections,
|
|
"get-anglers": self.get_anglers,
|
|
"delete-team": self.delete_team,
|
|
"delete-section": self.delete_section,
|
|
"delete-angler": self.delete_angler,
|
|
"get-nextTeamNumber": self.get_next_team_number,
|
|
"get-nextPegNumber": self.get_next_peg_number,
|
|
}
|
|
|
|
handler = task_handlers.get(task)
|
|
if not handler:
|
|
raise ValueError(f"Invalid task: {task}")
|
|
|
|
return handler(request)
|
|
|
|
def update_team(self, request) -> dict[str]:
|
|
"""Update a team, returns a dictionary of the new team's data."""
|
|
|
|
result = {"form_errors": {}, "team": None}
|
|
team_id = request.POST.get("id")
|
|
team_number = request.POST.get("number")
|
|
|
|
if not (team_id and team_number):
|
|
raise ValueError("Team ID or Team Number is missing or empty")
|
|
|
|
if team_id == "-1":
|
|
team = Team(number=team_number)
|
|
else:
|
|
team = Team.objects.get(id=team_id)
|
|
team.number = team_number
|
|
|
|
try:
|
|
team.save()
|
|
result["team"] = {"id": team.id, "number": team.number}
|
|
except IntegrityError:
|
|
result["form_errors"]["#teamNumber"] = "A Team with this number already exists"
|
|
|
|
return result
|
|
|
|
def update_section(self, request) -> dict[str]:
|
|
"""Update a section, returns a dictionary of the new section's data."""
|
|
|
|
result = {"form_errors": {}, "section": None}
|
|
section_id = request.POST.get("id")
|
|
section_character = request.POST.get("character")
|
|
|
|
if not (section_id and section_character):
|
|
raise ValueError("Section ID or Section Character is missing or empty")
|
|
|
|
if section_id == "-1":
|
|
section = Section(character=section_character)
|
|
else:
|
|
section = Section.objects.get(id=section_id)
|
|
section.character = section_character
|
|
|
|
try:
|
|
section.save()
|
|
result["section"] = {"id": section.id, "character": section.character}
|
|
except IntegrityError:
|
|
result["form_errors"]["#editSectionNameError"] = "A Section with this character already exists"
|
|
|
|
return result
|
|
|
|
def update_angler(self, request) -> dict[str]:
|
|
"""Update an Angler, returns a dictionary of the new angler's data."""
|
|
|
|
result = {"form_errors": {}, "angler": None}
|
|
angler_id = request.POST.get("angler_id")
|
|
forename = request.POST.get("forename")
|
|
surname = request.POST.get("surname")
|
|
peg_number = request.POST.get("peg_number")
|
|
team_id = request.POST.get("team_id")
|
|
section_id = request.POST.get("section_id")
|
|
|
|
if not angler_id:
|
|
raise ValueError("Invalid angler ID")
|
|
|
|
team = Team.objects.get(id=team_id)
|
|
section = Section.objects.get(id=section_id)
|
|
|
|
if angler_id == "-1":
|
|
angler = Member(
|
|
first_name=forename,
|
|
last_name=surname,
|
|
peg_number=peg_number,
|
|
team=team,
|
|
section=section
|
|
)
|
|
else:
|
|
angler = Member.objects.get(id=angler_id)
|
|
angler.first_name = forename
|
|
angler.last_name = surname
|
|
angler.peg_number = peg_number
|
|
angler.team = team
|
|
angler.section = section
|
|
|
|
try:
|
|
angler.save()
|
|
except IntegrityError:
|
|
result["form_errors"]["#anglerPeg"] = "An Angler with this peg number already exists"
|
|
|
|
result["angler"] = {
|
|
"id": angler.id,
|
|
"forename": forename,
|
|
"surname": surname,
|
|
"peg_number": peg_number,
|
|
"team_id": team_id,
|
|
"section_id": section_id,
|
|
"team_number": angler.team.number,
|
|
"section_character": angler.section.character
|
|
}
|
|
|
|
return result
|
|
|
|
def get_teams(self, request) -> dict[str]:
|
|
"""Returns a dictionary of all teams."""
|
|
|
|
search = request.POST.get("search")
|
|
teams = Team.objects.order_by("number").all()
|
|
|
|
# Search works by exluding teams that do not contain members with the search term in their names.
|
|
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()
|
|
|
|
return {"teams": [{"id": team.id, "number": team.number} for team in teams]}
|
|
|
|
def get_sections(self, request) -> dict[str]:
|
|
"""Returns a dictionary of all sections."""
|
|
|
|
search = request.POST.get("search")
|
|
sections = Section.objects.order_by("character").all()
|
|
|
|
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
|
|
]))
|
|
sections = sections.filter(members__in=members).distinct()
|
|
|
|
return {"sections": [{"id": section.id, "character": section.character} for section in sections]}
|
|
|
|
def get_anglers(self, request) -> dict[str]:
|
|
"""Returns a dictionary of all anglers."""
|
|
|
|
search = request.POST.get("search")
|
|
anglers = Member.objects.order_by("first_name").all()
|
|
order_by = "peg_number" if request.POST.get("sortAnglers") == "pegs" else "first_name"
|
|
|
|
if search:
|
|
search_terms = search.split()
|
|
anglers = anglers.filter(reduce(lambda x, y: x & y, [
|
|
Q(first_name__icontains=term) | Q(last_name__icontains=term)
|
|
for term in search_terms
|
|
])).distinct()
|
|
|
|
return {
|
|
"anglers": [
|
|
{
|
|
"id": angler.id,
|
|
"first_name": angler.first_name,
|
|
"last_name": angler.last_name,
|
|
"peg_number": angler.peg_number,
|
|
"team_id": angler.team.id,
|
|
"section_id": angler.section.id,
|
|
"team_number": angler.team.number,
|
|
"section_character": angler.section.character
|
|
}
|
|
for angler in anglers.order_by(order_by).all()
|
|
]
|
|
}
|
|
|
|
def delete_team(self, request) -> dict:
|
|
"""Deletes a team."""
|
|
|
|
team_id = request.POST.get("team_id")
|
|
if not team_id:
|
|
raise ValueError("Invalid team ID")
|
|
|
|
teams = Team.objects.get(id=team_id)
|
|
teams.delete()
|
|
|
|
return {}
|
|
|
|
def delete_section(self, request) -> dict:
|
|
"""Deletes a section."""
|
|
|
|
section_id = request.POST.get("section_id")
|
|
if not section_id:
|
|
raise ValueError("Invalid section ID")
|
|
|
|
sections = Section.objects.get(id=section_id)
|
|
sections.delete()
|
|
|
|
return {}
|
|
|
|
def delete_angler(self, request) -> dict:
|
|
"""Delete an angler."""
|
|
|
|
angler_id = request.POST.get("angler_id")
|
|
if not angler_id:
|
|
raise ValueError("Invalid angler ID")
|
|
|
|
angler = Member.objects.get(id=angler_id)
|
|
angler.delete()
|
|
|
|
return {}
|
|
|
|
def get_next_team_number(self, request) -> dict[str, int]:
|
|
"""Returns the next available team number."""
|
|
|
|
next_team_number = 1
|
|
|
|
while Team.objects.filter(number=next_team_number).exists():
|
|
next_team_number += 1
|
|
|
|
return {"nextTeamNumber": next_team_number}
|
|
|
|
def get_next_peg_number(self, request) -> dict[str, int]:
|
|
"""Returns the next available peg number."""
|
|
|
|
next_peg_number = 1
|
|
|
|
while Member.objects.filter(peg_number=next_peg_number).exists():
|
|
next_peg_number += 1
|
|
|
|
return {"nextPegNumber": next_peg_number}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_angler_page_data(request, **kwargs):
|
|
"""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_groups = request.POST.get("sortGroups") or "team"
|
|
sort_members = request.POST.get("sortMembers") or "peg_number"
|
|
|
|
teams = Team.objects.order_by("number").all()
|
|
sections = Section.objects.order_by("character").all()
|
|
|
|
if search:
|
|
search_terms = search.split()
|
|
members = Member.objects.filter(
|
|
reduce(
|
|
lambda x, y: x & y, ## changed to AND from OR to fix bug with whitespace searches
|
|
[
|
|
Q(first_name__icontains=term) | Q(last_name__icontains=term)
|
|
for term in search_terms
|
|
]
|
|
)
|
|
)
|
|
teams = teams.filter(members__in=members).distinct()
|
|
sections = sections.filter(members__in=members).distinct()
|
|
|
|
response_data = {
|
|
"teams": [
|
|
{"id": team.id, "number": team.number}
|
|
for team in teams
|
|
],
|
|
"sections": [
|
|
{"id": sec.id, "character": sec.character}
|
|
for sec in sections
|
|
],
|
|
"anglers": [
|
|
{
|
|
"id": member.id,
|
|
"first": member.first_name,
|
|
"last": member.last_name,
|
|
"peg": member.peg_number,
|
|
"team_id": member.team.id if member.team else None,
|
|
"section_id": member.section.id if member.section else None
|
|
}
|
|
for member in Member.objects.order_by(sort_members).all()
|
|
]
|
|
}
|
|
|
|
response_data["sortGroups"] = sort_groups
|
|
response_data["sortMembers"] = sort_members
|
|
|
|
for key, value in kwargs.items():
|
|
response_data[key] = value
|
|
|
|
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(name=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_angler_page_data(request)
|
|
|
|
def update_section(request):
|
|
"""Update a section, returns JsonResponse with updated teams data."""
|
|
|
|
if not request.POST:
|
|
return
|
|
|
|
section_id = request.POST.get("sectionId")
|
|
section_name = request.POST.get("sectionName")
|
|
|
|
validator = SectionValidator()
|
|
if not validator.is_valid(section_name):
|
|
json_response = get_angler_page_data(request, form_errors={
|
|
"editSectionName": "This is an invalid section"
|
|
})
|
|
return json_response
|
|
|
|
if section_id == "-1":
|
|
section = Section(character=section_name)
|
|
else:
|
|
section = Section.objects.get(id=section_id)
|
|
section.character = section_name
|
|
|
|
try:
|
|
section.save()
|
|
except IntegrityError:
|
|
json_response = get_angler_page_data(request, form_errors={
|
|
"editSectionName": "A Section with this character already exists"
|
|
})
|
|
return json_response
|
|
|
|
return get_angler_page_data(request) # returns jsonresponse with new details
|
|
|
|
def update_team(request):
|
|
"""Update a team, returns a JsonResponse with updated teams data."""
|
|
|
|
if not request.POST:
|
|
return
|
|
|
|
team_id = request.POST.get("id")
|
|
team_number = request.POST.get("number")
|
|
|
|
try:
|
|
if team_id == "-1":
|
|
team = Team.objects.create(number=team_number)
|
|
else:
|
|
team = Team.objects.get(id=team_id)
|
|
team.number = team_number
|
|
team.save()
|
|
except IntegrityError as error:
|
|
json_response = get_angler_page_data(request, form_errors={
|
|
"editTeamNumber": "A Team with this number already exists"
|
|
})
|
|
return json_response
|
|
|
|
return get_angler_page_data(request)
|
|
|
|
def get_next_peg() -> int:
|
|
pass
|
|
|
|
def get_next_section() -> str:
|
|
|
|
section_name = SectionManager.get_max_section()
|
|
return SectionManager.find_next_section(section_name)
|
|
|
|
|
|
def get_next_team() -> int:
|
|
|
|
field = ReusableAutoField
|
|
field.model = Team
|
|
|
|
return field().get_default()
|
|
|
|
|
|
def get_next_identifier(request):
|
|
"""Get the next available identifer (peg, section character, etc.) for an object."""
|
|
|
|
if not request.POST:
|
|
return
|
|
|
|
item = request.POST.get("item")
|
|
|
|
match item:
|
|
case "member_peg":
|
|
result = get_next_peg()
|
|
|
|
case "section_name":
|
|
result = get_next_section()
|
|
|
|
case "team_number":
|
|
result = get_next_team()
|
|
|
|
case _:
|
|
raise ValueError(f"Bad identifier item: {item}")
|
|
|
|
return JsonResponse({"identifier": result})
|