Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mittab/apps/tab/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class TeamAdmin(admin.ModelAdmin):
admin.site.register(models.TabSettings)
admin.site.register(models.Room)
admin.site.register(models.RoomTag)
admin.site.register(models.RankingGroup)
admin.site.register(models.Bye)
admin.site.register(models.NoShow)
admin.site.register(models.BreakingTeam)
Expand Down
43 changes: 38 additions & 5 deletions mittab/apps/tab/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,10 @@ class Media:


class TeamForm(forms.ModelForm):
debaters = forms.ModelMultipleChoiceField(queryset=Debater.objects.all(),
required=False)
debaters = forms.ModelMultipleChoiceField(
queryset=Debater.objects.all(),
required=False
)

def clean_debaters(self):
data = self.cleaned_data["debaters"]
Expand Down Expand Up @@ -233,9 +235,6 @@ class Meta:


class DebaterForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DebaterForm, self).__init__(*args, **kwargs)

class Meta:
model = Debater
exclude = ["tiebreaker"]
Expand Down Expand Up @@ -763,6 +762,40 @@ def __init__(self, *args, **kwargs):
self.fields.pop("judges")
self.fields.pop("rooms")


class RankingGroupForm(forms.ModelForm):
teams = forms.ModelMultipleChoiceField(
queryset=Team.objects.all(),
required=False,
)
debaters = forms.ModelMultipleChoiceField(
queryset=Debater.objects.all(),
required=False,
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields["teams"].initial = self.instance.teams.all()
self.fields["debaters"].initial = self.instance.debaters.all()

def save(self, commit=True):
ranking_group = super().save(commit=commit)
ranking_group.teams.set(self.cleaned_data.get("teams", []))
ranking_group.debaters.set(self.cleaned_data.get("debaters", []))
return ranking_group

class Meta:
model = RankingGroup
fields = ("name", "teams", "debaters")


class MiniRankingGroupForm(RankingGroupForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields.pop("teams")
self.fields.pop("debaters")

class BackupForm(forms.Form):
backup_name = forms.CharField(
max_length=255,
Expand Down
22 changes: 22 additions & 0 deletions mittab/apps/tab/migrations/0032_rankinggroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 3.2.25 on 2025-11-10 02:30

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tab', '0031_remove_noshow_lenient_late'),
]

operations = [
migrations.CreateModel(
name='RankingGroup',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True)),
('debaters', models.ManyToManyField(blank=True, related_name='ranking_groups', to='tab.Debater')),
('teams', models.ManyToManyField(blank=True, related_name='ranking_groups', to='tab.Team')),
],
),
]
19 changes: 19 additions & 0 deletions mittab/apps/tab/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ def with_preloaded_relations_for_tab_card(cls):
"debaters__roundstats_set__round",
"debaters__team_set",
"debaters__team_set__no_shows",
"ranking_groups",
)

@classmethod
Expand Down Expand Up @@ -232,6 +233,7 @@ def with_preloaded_relations_for_tabbing(cls):
"debaters__roundstats_set__round",
"debaters__team_set",
"debaters__team_set__no_shows",
"ranking_groups",
)

def set_unique_team_code(self):
Expand Down Expand Up @@ -648,3 +650,20 @@ class RoomTag(models.Model):

def __str__(self):
return self.tag


class RankingGroup(models.Model):
name = models.CharField(max_length=255, unique=True)
teams = models.ManyToManyField(
"Team",
blank=True,
related_name="ranking_groups",
)
debaters = models.ManyToManyField(
"Debater",
blank=True,
related_name="ranking_groups",
)

def __str__(self):
return self.name
31 changes: 28 additions & 3 deletions mittab/apps/tab/views/debater_views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.shortcuts import render
from django.utils.text import slugify

from mittab.apps.tab.forms import DebaterForm
from mittab.apps.tab.helpers import redirect_and_flash_error, \
Expand All @@ -12,8 +13,16 @@

def view_debaters(request):
# Get a list of (id,debater_name) tuples
c_debaters = [(debater.pk, debater.display, 0, "")
for debater in Debater.objects.all()]
c_debaters = [
(
debater.pk,
debater.display,
0,
"",
list(debater.ranking_groups.order_by("name").values_list("name", flat=True)),
)
for debater in Debater.objects.prefetch_related("ranking_groups").all()
]
return render(
request, "common/list_data.html", {
"item_type": "debater",
Expand All @@ -25,7 +34,7 @@ def view_debaters(request):
def view_debater(request, debater_id):
debater_id = int(debater_id)
try:
debater = Debater.objects.get(pk=debater_id)
debater = Debater.objects.prefetch_related("ranking_groups").get(pk=debater_id)
except Debater.DoesNotExist:
return redirect_and_flash_error(request, "No such debater")
if request.method == "POST":
Expand Down Expand Up @@ -132,9 +141,25 @@ def rank_debaters(request):
request
)

ranking_group_tables = []
ranking_groups = RankingGroup.objects.prefetch_related("debaters").order_by("name")
for ranking_group in ranking_groups:
debater_ids = {debater.id for debater in ranking_group.debaters.all()}
grouped_debaters = [
debater_entry for debater_entry in debaters
if debater_entry[0].id in debater_ids
]
if grouped_debaters:
ranking_group_tables.append({
"title": f"{ranking_group.name} Rankings",
"anchor": f"debater-ranking-group-{slugify(ranking_group.name)}",
"debaters": grouped_debaters,
})

return render(
request, "tab/rank_debaters_component.html", {
"debaters": debaters,
"nov_debaters": nov_debaters,
"debater_ranking_groups": ranking_group_tables,
"title": "Speaker Rankings"
})
28 changes: 24 additions & 4 deletions mittab/apps/tab/views/team_views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import permission_required
from django.shortcuts import render
from django.utils.text import slugify

from mittab.apps.tab.forms import TeamForm, TeamEntryForm, ScratchForm
from mittab.libs.cacheing import cache_logic
Expand All @@ -25,9 +26,16 @@ def flags(team):
result |= TabFlags.TEAM_NOT_CHECKED_IN
return result

c_teams = [(team.id, team.display_backend, flags(team),
TabFlags.flags_to_symbols(flags(team)))
for team in Team.objects.all()]
c_teams = [
(
team.id,
team.display_backend,
flags(team),
TabFlags.flags_to_symbols(flags(team)),
list(team.ranking_groups.order_by("name").values_list("name", flat=True)),
)
for team in Team.objects.prefetch_related("ranking_groups").all()
]
all_flags = [[TabFlags.TEAM_CHECKED_IN, TabFlags.TEAM_NOT_CHECKED_IN]]
filters, symbol_text = TabFlags.get_filters_and_symbols(all_flags)
return render(
Expand Down Expand Up @@ -384,9 +392,21 @@ def rank_teams(request):
public=False
)

ranking_group_tables = []
ranking_groups = RankingGroup.objects.prefetch_related("teams").order_by("name")
for ranking_group in ranking_groups:
team_ids = {team.id for team in ranking_group.teams.all()}
grouped_teams = [team for team in teams if team[0].id in team_ids]
if grouped_teams:
ranking_group_tables.append({
"title": f"{ranking_group.name} Rankings",
"anchor": f"team-ranking-group-{slugify(ranking_group.name)}",
"teams": grouped_teams,
})

return render(request, "tab/rank_teams_component.html", {
"varsity": teams,
"novice": nov_teams,
"team_ranking_groups": ranking_group_tables,
"title": "Team Rankings"
})

63 changes: 61 additions & 2 deletions mittab/apps/tab/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@

from mittab.apps.tab.archive import ArchiveExporter
from mittab.apps.tab.views.debater_views import get_speaker_rankings
from mittab.apps.tab.forms import MiniRoomTagForm, RoomTagForm, SchoolForm, RoomForm, \
UploadDataForm, ScratchForm, SettingsForm
from mittab.apps.tab.forms import (
MiniRankingGroupForm,
MiniRoomTagForm,
RankingGroupForm,
RoomTagForm,
SchoolForm,
RoomForm,
UploadDataForm,
ScratchForm,
SettingsForm,
)
from mittab.apps.tab.helpers import redirect_and_flash_error, \
redirect_and_flash_success
from mittab.apps.tab.models import *
Expand Down Expand Up @@ -571,6 +580,56 @@ def manage_room_tags(request):
{"room_tags": room_tags,
"form": form})


def ranking_group(request, group_id=None):
group = None
if group_id is not None:
group = RankingGroup.objects.filter(pk=group_id).first()

if request.method == "POST":
if request.POST.get("_method") == "DELETE":
if group is not None:
group.delete()
return redirect_and_flash_success(
request, "Ranking group deleted successfully"
)
return redirect_and_flash_error(request, "Ranking group does not exist")

form = RankingGroupForm(request.POST, instance=group)
if not form.is_valid():
return redirect_and_flash_error(request, "Error saving ranking group.")

ranking_group_instance = form.save()
path = reverse("manage_ranking_groups")
message = (
f"Ranking group {ranking_group_instance.name} "
f"{'updated' if group else 'created'} successfully"
)
return redirect_and_flash_success(request, message, path=path)

form = RankingGroupForm(instance=group)
return render(
request,
"common/data_entry.html",
{
"form": form,
"links": [],
"title": f"Viewing Ranking Group: {group.name}" if group else "Create Ranking Group",
},
)


def manage_ranking_groups(request):
if request.method == "POST":
return ranking_group(request)
form = MiniRankingGroupForm(request.POST or None)
ranking_groups = RankingGroup.objects.all().order_by("name")
return render(
request,
"pairing/manage_ranking_groups.html",
{"ranking_groups": ranking_groups, "form": form},
)

def batch_checkin(request):
round_numbers = list([i + 1 for i in range(TabSettings.get("tot_rounds"))])
all_round_numbers = [0] + round_numbers
Expand Down
8 changes: 7 additions & 1 deletion mittab/templates/common/_index_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@
</h4>
<div class="collapse show" id="{{ html_id }}-content">
<div class="index-section-content">
{% if model_name == "Teams" %}
<div class="text-center mb-2">
<a href="{% url 'manage_ranking_groups' %}" class="btn btn-sm btn-light">Manage Ranking Groups</a>
</div>
{% endif %}
{% if model_name == "Rooms" %}
<div class="text-center mb-2">
<a href="{% url 'manage_room_tags' %}" class="btn btn-sm btn-light">Manage Room Tags</a>
</div>
{% elif view_path_prefix == "judge" %}
{% endif %}
{% if view_path_prefix == "judge" %}
<div class="text-center mb-2">
<a class="btn btn-sm btn-light" href="{% url 'download_judge_codes' %}">
Download Judge Codes
Expand Down
19 changes: 15 additions & 4 deletions mittab/templates/common/list_data.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,21 @@
</li>
{% endfor %}
{% else %}
{% for id, name, flags, symbols in item_list %}
<li class="searchable filterable {% if forloop.counter0|divisibleby:2 %}even{%else%}odd{%endif%}" data-filters="{{flags}}">
<a class="no_decoration" href="/{{item_type}}/{{id}}">{{name}} {{symbols}}</a>
</li>
{% for item in item_list %}
{% with id=item.0 name=item.1 flags=item.2 symbols=item.3 %}
<li class="searchable filterable {% if forloop.counter0|divisibleby:2 %}even{%else%}odd{%endif%}" data-filters="{{flags}}">
<a class="no_decoration" href="/{{item_type}}/{{id}}">
{{name}} {{symbols}}
{% if item|length > 4 and item.4 %}
<span class="ranking-badges ml-2">
{% for badge in item.4 %}
<span class="badge badge-secondary badge-pill mr-1">{{ badge }}</span>
{% endfor %}
</span>
{% endif %}
</a>
</li>
{% endwith %}
{% endfor %}
{% endif %}
</ul>
Expand Down
Loading