From 628764a338e940a650bc26d265afa4090d95214e Mon Sep 17 00:00:00 2001 From: Corban-Lee <77944149+XordK@users.noreply.github.com> Date: Thu, 11 May 2023 15:43:33 +0100 Subject: [PATCH] Adding Teams & Sections is functional-ish --- src/mainapp/models.py | 108 +++++++++++++++----------- src/mainapp/templates/index.html | 2 +- src/mainapp/templates/teams.html | 92 +++++++++++++--------- src/mainapp/urls.py | 5 +- src/mainapp/views.py | 104 ++++++++++++++++++++++++- src/static/img/logo.webp | Bin 0 -> 28508 bytes src/static/js/teams.js | 126 ++++++++++++++++++++++++++++--- 7 files changed, 343 insertions(+), 94 deletions(-) create mode 100644 src/static/img/logo.webp diff --git a/src/mainapp/models.py b/src/mainapp/models.py index 14a4ffd..a74288c 100644 --- a/src/mainapp/models.py +++ b/src/mainapp/models.py @@ -1,5 +1,7 @@ """Models for the mainapp.""" +from string import ascii_uppercase + from django.db import models from django.core.exceptions import ValidationError @@ -96,12 +98,65 @@ class SectionValidator: return value + +class SectionManager(models.Manager): + + @staticmethod + def get_max_section(): + max_section = None + max_number = -1 + + # Iterate through all sections in the database + for section in Section.objects.all(): + section_name = section.name + section_number = 0 + + # Calculate the section number based on the section name + for i, char in enumerate(section_name): + section_number += (ord(char) - ord('A') + 1) * (26 ** (len(section_name) - i - 1)) + + # Check if this section has a higher number than the current maximum + if section_number > max_number: + max_number = section_number + max_section = section_name + + return max_section + + @staticmethod + def find_next_section(current_section): + if not current_section: + return 'A' + + # Split current section name into a list of characters + chars = list(current_section) + + # Increment the last character + chars[-1] = chr(ord(chars[-1]) + 1) + + # Check if the last character is "Z", and carry over to the next character if necessary + for i in range(len(chars) - 1, -1, -1): + if chars[i] > 'Z': + chars[i] = 'A' + if i == 0: + # If the first character needs to be incremented, add a new character "A" + chars.insert(0, 'A') + else: + # Increment the previous character + chars[i - 1] = chr(ord(chars[i - 1]) + 1) + else: + break + + # Join the characters back into a string and return the result + return ''.join(chars) + class Section(models.Model): """Represents a fishing area. Members can be assigned to a section, but no 2 teammates can be in the same or adjacent section.""" name = models.CharField(max_length=3, unique=True, null=False) + objects = SectionManager() + def clean(self): super().clean() @@ -118,52 +173,6 @@ class Section(models.Model): def get_members(self): return Member.objects.filter(section=self) - @property - def section_after(self): - """Returns the next section after this one, or none if not found.""" - sections = [sec for sec in Section.objects.all()] - sections.sort(key=lambda sec: sec.name) - try: - return sections[sections.index(self) + 1] - except IndexError: - return None - - @property - def section_before(self): - """Returns the last section before this one, or none if not found.""" - sections = [sec for sec in Section.objects.all()] - sections.sort(key=lambda sec: sec.name) - try: - return sections[sections.index(self) - 1] - except IndexError: - return None - - def is_joinable(self, member, /) -> bool: - """Returns boolean if a member is able to join this section.""" - - if not member.team: - return True - - teammates = [teammate for teammate in Member.objects.filter(team=member.team)] - - section_after = self.section_after.get_members if self.section_after else [] - section_before = self.section_before.get_members if self.section_before else [] - - for teammate in teammates: - if teammate in section_after: - return False - - if teammate in section_before: - return False - - return True - - return any( - teammate in section_after or teammate in section_before - for teammate in teammates - ) - - class Member(models.Model): """Represents a member of a team""" @@ -192,11 +201,18 @@ class Member(models.Model): def fullname(self) -> str: return f"{self.first_name} {self.last_name}" + +class TeamManager(models.Manager): + pass + + class Team(models.Model): """Represents a team""" name = ReusableAutoField(primary_key=True, default=ReusableAutoField.get_default, editable=False) + objects = TeamManager() + def __str__(self): return f"Team {self.name}" diff --git a/src/mainapp/templates/index.html b/src/mainapp/templates/index.html index caf0414..d586ac6 100644 --- a/src/mainapp/templates/index.html +++ b/src/mainapp/templates/index.html @@ -24,7 +24,7 @@ -->

- Member Management + Angler Management

diff --git a/src/mainapp/templates/teams.html b/src/mainapp/templates/teams.html index 0cc482e..ee4b7aa 100644 --- a/src/mainapp/templates/teams.html +++ b/src/mainapp/templates/teams.html @@ -1,7 +1,8 @@ {% extends "base.html" %} +{% load static %} {% block title %} - Member Management | + Manage Anglers | {% endblock title %} {% block style %} @@ -10,21 +11,22 @@ {% endblock style %} {% block content %} -

+
Back +

- Member Management + Manage Anglers

- - +
{% endblock content %} -{% load static %} {% block scripts %} @@ -217,6 +236,9 @@ diff --git a/src/mainapp/urls.py b/src/mainapp/urls.py index 691cdb6..7da889d 100644 --- a/src/mainapp/urls.py +++ b/src/mainapp/urls.py @@ -9,5 +9,8 @@ urlpatterns = [ # 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('update-member/', views.update_member, name='update-member'), + path('update-section/', views.update_section, name='update-section'), + path('update-team/', views.update_team, name='update-team'), + path("get-next-identifier/", views.get_next_identifier, name='get-next-identifier') ] \ No newline at end of file diff --git a/src/mainapp/views.py b/src/mainapp/views.py index bba9a74..cdecbb9 100644 --- a/src/mainapp/views.py +++ b/src/mainapp/views.py @@ -5,8 +5,9 @@ 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 django.db.utils import IntegrityError -from .models import Team, Member, Section +from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator def index(request): @@ -37,7 +38,7 @@ def name_sort_key(section): else: return (1, section.name[:-1], section.name[-1]) -def get_teams(request): +def get_teams(request, **kwargs): """Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams. Args: @@ -95,6 +96,9 @@ def get_teams(request): 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): @@ -123,3 +127,99 @@ def update_member(request): member.save() return get_teams(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_teams(request, form_errors={ + "editSectionName": "This is an invalid section" + }) + return json_response + + if section_id == "-1": + section = Section(name=section_name) + else: + section = Section.objects.get(id=section_id) + section.name = section_name + + try: + section.save() + except IntegrityError: + json_response = get_teams(request, form_errors={ + "editSectionName": "A Section with this character already exists" + }) + return json_response + + return get_teams(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("teamId") + team_number = request.POST.get("teamNumber") + + try: + if team_id == "-1": + team = Team.objects.create(name=team_number) + else: + team = Team.objects.get(id=team_id) + team.name = team_number + team.save() + except IntegrityError as error: + json_response = get_teams(request, form_errors={ + "editTeamNumber": "A Team with this number already exists" + }) + return json_response + + return get_teams(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}) diff --git a/src/static/img/logo.webp b/src/static/img/logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..5497afacc9aee0b327824d66bef55734304de630 GIT binary patch literal 28508 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 { } }); + $("#editSectionNameButton").on("click", () => { + + $.ajax({ + url: getNextIdentifierUrl, + type: "post", + data: { + "csrfmiddlewaretoken": csrfMiddlewareToken, + "item": "section_name" + }, + error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, + success: (result) => { + $("#editSectionName").val(result.identifier) + } + }); + + }); + + $("#editTeamNumberButton").on("click", () => { + + $.ajax({ + url: getNextIdentifierUrl, + type: "post", + data: { + "csrfmiddlewaretoken": csrfMiddlewareToken, + "item": "team_number" + }, + error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, + success: (result) => { + $("#editTeamNumber").val(result.identifier) + } + }); + }); + $("select").select2({theme: "bootstrap-5", minimumResultsForSearch: -1}) // load the teams with default filters @@ -180,6 +213,10 @@ function loadTeams(groups, highlightText="", groupType) { $("#addMember").on("click", () => { openEditMemberModal(-1); }); + + $("#addSection").on("click", () => { + openEditSectionModal(-1); + }); } function fetchAndLoadTeams(search="", sortGroups="", sortMembers="") { @@ -213,19 +250,88 @@ function teamsLoading(show=true) { $("#teamsLoadingSpinner").hide(); } -function openEditTeamModal(teamNumber) { +function openEditSectionModal(sectionId) { - // // Team data - // const team = $(`.team[data-number='${teamNumber}']`); - // const section = team.data("section"); + $("#editSectionNameError").hide(); + $("#editSectionModal").modal("show"); - // // Load data to form - // $("#editTeamTitle").text(`Team ${teamNumber}`); + $("#saveEditSectionModal").off("click").on("click", () => { - // $("#editTeamNumber").val(teamNumber); - // $('#editTeamSection').val(section); + if(!$("#editSectionName").val().match(/^[A-Za-z]+$/)) { + $("#editSectionName").focus(); + $("#editSectionNameError").text("Invalid section character").show(); + return; + } + + + teamsLoading(true); + const [search, sortGroups, sortMembers] = getFilters(); + + $.ajax({ + url: updateSectionUrl, + type: "post", + data: { + "csrfmiddlewaretoken": csrfMiddlewareToken, + "sectionId": sectionId, + "sectionName": $("#editSectionName").val(), + "search": search, + "sortGroups": sortGroups, + "sortMembers": sortMembers + }, + error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, + success: (result) => { + + teamsLoading(false); + loadTeams(result.groups, search, result.sortGroups); + + if (!result.form_errors) { + $("#editSectionModal").modal("hide"); + } + else { + $("#editSectionName").focus(); + $("#editSectionNameError").text(result.form_errors.editSectionName).show(); + } + } + }); + }); +} + +function openEditTeamModal(teamId) { $("#editTeamModal").modal("show"); + + $("#saveEditTeamModal").off("click").on("click", () => { + teamsLoading(true); + + const [search, sortGroups, sortMembers] = getFilters() + + $.ajax({ + url: updateTeamUrl, + type: "post", + data: { + "csrfmiddlewaretoken": csrfMiddlewareToken, + "teamId": teamId, + "teamNumber": $("#editTeamNumber").val(), + "search": search, + "sortGroups": sortGroups, + "sortMembers": sortMembers + }, + error: (xhr, textStatus, errorThrown) => { alert(xhr + " " + textStatus + " " + errorThrown); }, + success: (result) => { + + teamsLoading(false); + loadTeams(result.groups, search, result.sortGroups); + + if (!result.form_errors) { + $("#editTeamModal").modal("hide"); + } + else { + $("#editTeamNumber").focus(); + $("#editTeamNumberError").text(result.form_errors.editTeamNumber).show(); + } + } + }); + }); } function openEditMemberModal(memberId) { @@ -271,7 +377,9 @@ function openEditMemberModal(memberId) { $("#editMemberModal").modal("show"); // Update the submit button - $("#saveEditModal").off("click").on("click", () => { saveEditMemberModal(memberId); }); + $("#saveEditModal").off("click").on("click", () => { + alert("you pressed save member"); + }); } function saveEditMemberModal(memberId) {