2023-11-09 01:10:58 +00:00

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})