From aba29912babdb9e06cb9358121fcc192cbc89cc3 Mon Sep 17 00:00:00 2001 From: Stefan Dworschak Date: Wed, 20 Jan 2021 13:15:45 +0000 Subject: [PATCH] #A09. As Admin, I would like to trigger an event to award the winning project(s) based on the judges scores. (#134) --- hackathon/fixtures/hackathons.json | 80 ++++++++- hackathon/forms.py | 34 +++- hackathon/lists.py | 6 +- ...0026_hackprojectscorecategory_hackathon.py | 19 ++ .../migrations/0027_auto_20210119_0045.py | 19 ++ .../migrations/0028_auto_20210119_2352.py | 22 +++ .../migrations/0032_merge_20210120_1141.py | 14 ++ hackathon/models.py | 14 +- .../templates/hackathon/create-event.html | 3 + .../templates/hackathon/final_score.html | 129 +++++++++++--- .../templates/hackathon/hackathon_list.html | 2 +- .../templates/hackathon/hackathon_view.html | 1 + .../hackathon/includes/hackathon_card.html | 23 ++- hackathon/templatetags/my_tags.py | 32 ++++ hackathon/views.py | 164 ++++++++++++++---- left_to_do.txt | 4 + static/css/style.css | 17 +- test.json | 0 18 files changed, 518 insertions(+), 65 deletions(-) create mode 100644 hackathon/migrations/0026_hackprojectscorecategory_hackathon.py create mode 100644 hackathon/migrations/0027_auto_20210119_0045.py create mode 100644 hackathon/migrations/0028_auto_20210119_2352.py create mode 100644 hackathon/migrations/0032_merge_20210120_1141.py create mode 100644 left_to_do.txt create mode 100644 test.json diff --git a/hackathon/fixtures/hackathons.json b/hackathon/fixtures/hackathons.json index 11aa219a..d797ca0a 100644 --- a/hackathon/fixtures/hackathons.json +++ b/hackathon/fixtures/hackathons.json @@ -275,5 +275,83 @@ "min_score":1, "max_score":10 } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 1, + "fields": { + "created": "2021-01-18T23:22:27.172Z", + "updated": "2021-01-18T23:22:27.172Z", + "created_by": 1, + "display_name": "Best Project", + "description": "The project with the best score overall.", + "hackathon": 4, + "winning_project": null + } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 2, + "fields": { + "created": "2021-01-18T23:23:11.556Z", + "updated": "2021-01-18T23:23:11.556Z", + "created_by": 1, + "display_name": "Best Project (1st Runners Up)", + "description": "The project with the second best score overall.", + "hackathon": 4, + "winning_project": null + } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 3, + "fields": { + "created": "2021-01-18T23:23:35.718Z", + "updated": "2021-01-18T23:23:35.718Z", + "created_by": 1, + "display_name": "Best Project (2nd Runners Up)", + "description": "The project with the third best score overall.", + "hackathon": 4, + "winning_project": null + } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 4, + "fields": { + "created": "2021-01-18T23:25:31.644Z", + "updated": "2021-01-18T23:25:31.644Z", + "created_by": 1, + "display_name": "Most Innovative Project", + "description": "The project using the most innovative technology, approach or architecture.", + "hackathon": 4, + "winning_project": null + } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 5, + "fields": { + "created": "2021-01-18T23:26:34.100Z", + "updated": "2021-01-18T23:26:34.100Z", + "created_by": 1, + "display_name": "Best Commercial Application", + "description": "The project which shows be the best commercial application and is the closest to be market ready.", + "hackathon": 4, + "winning_project": null + } + }, + { + "model": "hackathon.hackawardcategory", + "pk": 6, + "fields": { + "created": "2021-01-18T23:27:06.554Z", + "updated": "2021-01-18T23:27:06.554Z", + "created_by": 1, + "display_name": "Most Creative Project", + "description": "The project with the most creative idea.", + "hackathon": 4, + "winning_project": null + } } -] \ No newline at end of file +] diff --git a/hackathon/forms.py b/hackathon/forms.py index b6bf8f84..09e3e57f 100644 --- a/hackathon/forms.py +++ b/hackathon/forms.py @@ -1,7 +1,9 @@ from django import forms +from django.forms import BaseModelFormSet from accounts.models import Organisation -from .models import Hackathon, HackProject +from .models import Hackathon, HackProject, HackAwardCategory,\ + HackProjectScoreCategory from .lists import STATUS_TYPES_CHOICES, JUDGING_STATUS_CHOICES class HackathonForm(forms.ModelForm): @@ -24,7 +26,7 @@ class HackathonForm(forms.ModelForm): max_length=3000, widget=forms.Textarea( attrs={ - 'rows': 3, + 'rows': 4, 'placeholder': 'Tell us more about this event...' } ) @@ -78,11 +80,18 @@ class HackathonForm(forms.ModelForm): label="Organisation", queryset=Organisation.objects.order_by('display_name'), ) + score_categories = forms.ModelMultipleChoiceField( + queryset=HackProjectScoreCategory.objects.all(), + widget=forms.SelectMultiple(attrs={ + 'size': '5' + }) + ) class Meta: model = Hackathon fields = ['display_name', 'description', 'theme', 'start_date', 'end_date', 'status', 'judging_status', 'organisation', + 'score_categories', ] def __init__(self, *args, **kwargs): @@ -104,3 +113,24 @@ class Meta: 'start_date': forms.HiddenInput(), 'end_date': forms.HiddenInput() } + + +class HackAwardCategoryForm(forms.ModelForm): + + display_name = forms.CharField( + label='Award Category Name', + widget=forms.TextInput( + attrs={ + 'readonly': True + } + ), + required=True + ) + + class Meta: + model = HackAwardCategory + fields = ('id', 'display_name', 'winning_project') + + def __init__(self, *args, **kwargs): + super(HackAwardCategoryForm, self).__init__(*args, **kwargs) + self.fields['display_name'].widget.attrs['readonly'] = True diff --git a/hackathon/lists.py b/hackathon/lists.py index da8070b3..0e9cb016 100644 --- a/hackathon/lists.py +++ b/hackathon/lists.py @@ -16,4 +16,8 @@ ('not_yet_started', "Hasn't started"), ('open', "Open"), ('closed', "Closed"), -) \ No newline at end of file +) + +AWARD_CATEGORIES = ['Best Project', 'Best Project (1st Runners Up)', + 'Best Project (2nd Runners Up)', 'Most Innovative Project', + 'Best Commercial Application', 'Most Creative Project'] diff --git a/hackathon/migrations/0026_hackprojectscorecategory_hackathon.py b/hackathon/migrations/0026_hackprojectscorecategory_hackathon.py new file mode 100644 index 00000000..d24fd239 --- /dev/null +++ b/hackathon/migrations/0026_hackprojectscorecategory_hackathon.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.3 on 2021-01-18 23:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hackathon', '0025_auto_20210111_2242'), + ] + + operations = [ + migrations.AddField( + model_name='hackprojectscorecategory', + name='hackathon', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hackprojectscorecategories', to='hackathon.hackathon'), + ), + ] diff --git a/hackathon/migrations/0027_auto_20210119_0045.py b/hackathon/migrations/0027_auto_20210119_0045.py new file mode 100644 index 00000000..801005ca --- /dev/null +++ b/hackathon/migrations/0027_auto_20210119_0045.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.3 on 2021-01-19 00:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hackathon', '0026_hackprojectscorecategory_hackathon'), + ] + + operations = [ + migrations.AlterField( + model_name='hackawardcategory', + name='winning_project', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hackathon.hackproject'), + ), + ] diff --git a/hackathon/migrations/0028_auto_20210119_2352.py b/hackathon/migrations/0028_auto_20210119_2352.py new file mode 100644 index 00000000..80f4038f --- /dev/null +++ b/hackathon/migrations/0028_auto_20210119_2352.py @@ -0,0 +1,22 @@ +# Generated by Django 3.1.3 on 2021-01-19 23:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hackathon', '0027_auto_20210119_0045'), + ] + + operations = [ + migrations.RemoveField( + model_name='hackprojectscorecategory', + name='hackathon', + ), + migrations.AddField( + model_name='hackathon', + name='score_categories', + field=models.ManyToManyField(blank=True, related_name='hackathon_score_categories', to='hackathon.HackProjectScoreCategory'), + ), + ] diff --git a/hackathon/migrations/0032_merge_20210120_1141.py b/hackathon/migrations/0032_merge_20210120_1141.py new file mode 100644 index 00000000..53862f9e --- /dev/null +++ b/hackathon/migrations/0032_merge_20210120_1141.py @@ -0,0 +1,14 @@ +# Generated by Django 3.1.3 on 2021-01-20 11:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('hackathon', '0028_auto_20210119_2352'), + ('hackathon', '0031_merge_20210118_1524'), + ] + + operations = [ + ] diff --git a/hackathon/models.py b/hackathon/models.py index 9957c677..973c804b 100644 --- a/hackathon/models.py +++ b/hackathon/models.py @@ -44,6 +44,12 @@ class Hackathon(models.Model): participants = models.ManyToManyField(User, blank=True, related_name='hackathon_participants') + # Hackathons can have multiple score categories and score categories + # Can belong to multiple hackahtons: Many to Many + score_categories = models.ManyToManyField( + 'HackProjectScoreCategory', + blank=True, + related_name='hackathon_score_categories') # One organiser could organise more than one Hackathon: One To Many organiser = models.ForeignKey(User, null=True, @@ -90,10 +96,10 @@ class HackAwardCategory(models.Model): on_delete=models.CASCADE, related_name="awards") # One category can have one winner: One to One - winning_project = models.OneToOneField("HackProject", - null=True, - blank=True, - on_delete=models.SET_NULL) + winning_project = models.ForeignKey("HackProject", + null=True, + blank=True, + on_delete=models.SET_NULL) def __str__(self): return self.display_name diff --git a/hackathon/templates/hackathon/create-event.html b/hackathon/templates/hackathon/create-event.html index 3bc8a0d3..ed58307e 100644 --- a/hackathon/templates/hackathon/create-event.html +++ b/hackathon/templates/hackathon/create-event.html @@ -49,6 +49,9 @@

Create Hackathon

{{ form.end_date|as_crispy_field }}
+
+ {{ form.score_categories|as_crispy_field }} +
Cancel diff --git a/hackathon/templates/hackathon/final_score.html b/hackathon/templates/hackathon/final_score.html index 2f3aedf5..d21e410f 100644 --- a/hackathon/templates/hackathon/final_score.html +++ b/hackathon/templates/hackathon/final_score.html @@ -1,26 +1,109 @@ {% extends "base.html" %} {% load static %} +{% load my_tags %} {% block content %} -

{{ hackathon }}
Final Scores

- {% if sorted_teams_scores %} - - - - - - - - - {% for team in sorted_teams_scores %} - - - - - {% endfor %} - -
Project Score
{{ team.0 }}{{ team.1 }}
- {% else %} - There are no final scores for this Hackathon. - {% endif %} - {% endblock %} - \ No newline at end of file + +{% if hack_awards_formset.get_queryset|length == 0 %} + +{% endif %} + +

{{ hackathon }} Scores

+{% if sorted_teams_scores %} + + + + + {% for judge in sorted_teams_scores.0.scores.keys %} + + {% endfor %} + + + + + {% for project in sorted_teams_scores %} + + + {% for judge in project.scores %} + {% with judge_score=project.scores|get_value_from_dict:judge %} + + {% endwith %} + {% endfor %} + + + {% endfor %} + +
Team / Project {{ judge }} Total Score
{{ project.team_name}} - {{ project.project_name}}{{ judge_score.Total }}{{ project.total_score }}
+ +{% if category_scores %} + + + + + {% with categories=category_scores.values|to_list|first %} + {% for category in categories.keys %} + + {% endfor %} + {% endwith %} + + + + {% with teams=category_scores.keys|sort_list %} + {% for team in teams %} + + + {% with team_scores=category_scores|get_value_from_dict:team %} + + {% for category, score in team_scores.items %} + + {% endfor %} + {% endwith %} + + {% endfor %} + {% endwith %} + +
+ Team / Project + + {{category}} +
{{team}} + {{score.place|place_identifier}} +
+{% endif %} + +{% if hack_awards_formset.get_queryset|length > 0 %} +
+
+ +
+ {% csrf_token %} + {{ hack_awards_formset.management_form }} + {% for form in hack_awards_formset %} +
+
+ {{ form.id|as_crispy_field }} + {{ form.display_name|as_crispy_field }} +
+
+ {{ form.winning_project|as_crispy_field }} +
+
+ {% endfor %} +
+
+ +
+
+
+ +
+
+{% endif %} + +{% else %} +No scores added yet. +{% endif %} + +{% endblock %} diff --git a/hackathon/templates/hackathon/hackathon_list.html b/hackathon/templates/hackathon/hackathon_list.html index 3249bc98..fb63721f 100644 --- a/hackathon/templates/hackathon/hackathon_list.html +++ b/hackathon/templates/hackathon/hackathon_list.html @@ -9,7 +9,7 @@ {% if hackathons %}

Hackathons

{% for hackathon in hackathons %} - {% if hackathon.organisation == 1 or hackathon.organisation == user.organisation %} + {% if hackathon.organisation.id == 1 or hackathon.organisation == user.organisation or user.is_staff %} {% if hackathon.status != 'draft' or user == hackathon.created_by %} {% if not hackathon.status == 'deleted' %} {% include 'hackathon/includes/hackathon_card.html' %} diff --git a/hackathon/templates/hackathon/hackathon_view.html b/hackathon/templates/hackathon/hackathon_view.html index f2bb8a3a..75d4c8f5 100644 --- a/hackathon/templates/hackathon/hackathon_view.html +++ b/hackathon/templates/hackathon/hackathon_view.html @@ -28,6 +28,7 @@ {% if hackathon.teams.all|length == 0 %}Distribute{% else %}Change{% endif %} Teams Change Hackathon Status + View Scores {% endif %}
diff --git a/hackathon/templates/hackathon/includes/hackathon_card.html b/hackathon/templates/hackathon/includes/hackathon_card.html index 3c96bfcc..31d033a8 100644 --- a/hackathon/templates/hackathon/includes/hackathon_card.html +++ b/hackathon/templates/hackathon/includes/hackathon_card.html @@ -2,12 +2,33 @@
{{ hackathon.display_name }} {% if user.user_type != 'participant' %}{{hackathon.status}} {% endif %}

{{ hackathon.start_date }} - {{ hackathon.end_date }}

-

Organised for {{ hackathon.organisation }}

+

Open to all {{ hackathon.organisation }} students

+ {% if hackathon.end_date|date:"jS F Y H:i" <= today|date:"jS F Y H:i" or hackathon.status == 'finished' %} + {% if request.user in hackathon.participants.all %} +

You participated in this hackathon

+ {% else %} +

Registration closed for this hackathon

+ {% endif %} + {% else %} + {% if hackathon.status == 'registration_open' and request.user not in hackathon.participants.all %} +

Registrations open!

+ {% elif hackathon.status == 'registration_open' and request.user in hackathon.participants.all %} +

You are enrolled in this hackathon!

+ {% elif hackathon.status == 'hack_in_progress' or hackathon.status == 'judging' %} +

You are participating in this hackathon

+ {% else %} +

Registrations starting soon!

+ {% endif %} + {% endif %} + {% if user.user_type != 'participant' %}

Participants: {{ hackathon.participants.all|length }} / Teams: {{ hackathon.teams.all|length }}

{% endif %} Read More + {% if hackathon.status == 'registration_open' and not request.user.is_staff and request.user not in hackathon.participants.all %} +
{% include 'hackathon/includes/enrollpart.html' %}
+ {% endif %} {% if user.is_authenticated and user.is_superuser %} diff --git a/hackathon/templatetags/my_tags.py b/hackathon/templatetags/my_tags.py index b55ae7bf..7bfeff84 100644 --- a/hackathon/templatetags/my_tags.py +++ b/hackathon/templatetags/my_tags.py @@ -1,6 +1,8 @@ # range snippet from: https://www.djangosnippets.org/snippets/1357/ # adjusted to current project needs based on https://docs.djangoproject.com/en/3.1/howto/custom-template-tags/ +from operator import itemgetter + from django.template import Library import datetime from django.conf import settings @@ -34,3 +36,33 @@ def event_ended(date_event): Date can be updated if organisers want to put a deadline to enrol Help provided in Stack overflow: https://stackoverflow.com/questions/64605335/comparing-dates-using-a-comparator-inside-django-template/64605785#64605785''' return date_event.date() >= datetime.date.today() + + +@register.filter +def get_value_from_dict(data, key): + """ Retrieves a value from a dict based on a given key """ + if key: + return data.get(key) + + +@register.filter +def to_list(data): + return list(data) + + +@register.filter +def sort_list(data): + return sorted(list(data)) + + +@register.filter +def place_identifier(num): + num_str = str(num) + if num_str[-1] == '1': + return num_str + 'st' + elif num_str[-1] == '2': + return num_str + 'nd' + elif num_str[-1] == '3': + return num_str + 'rd' + else: + return num_str + 'th' diff --git a/hackathon/views.py b/hackathon/views.py index a265ce02..2646d686 100644 --- a/hackathon/views.py +++ b/hackathon/views.py @@ -1,6 +1,9 @@ +from copy import deepcopy from datetime import datetime +from operator import itemgetter from dateutil.parser import parse +from django.forms import modelformset_factory from django.views.generic import ListView, DetailView from django.shortcuts import render, get_object_or_404, redirect, reverse from django.contrib import messages @@ -10,7 +13,16 @@ from django.utils import timezone from .models import Hackathon, HackTeam, HackProject, HackProjectScore, HackProjectScoreCategory, HackAwardCategory -from .forms import HackathonForm, ChangeHackathonStatusForm +from .forms import HackathonForm, ChangeHackathonStatusForm,\ + HackAwardCategoryForm +from .lists import AWARD_CATEGORIES + +DEFAULT_SCORES = { + 'team_name': '', + 'project_name': '', + 'scores': {}, + 'total_score': 0, +} class HackathonListView(ListView): @@ -86,7 +98,8 @@ def judging(request, hack_id, team_id): hack_project_score_category=score_category, ) team_score.save() - return check_projects_scores(request, hack_id) + return redirect(reverse('hackathon:view_hackathon', + kwargs={'hackathon_id': hack_id})) context = { 'hackathon': hackathon, @@ -109,37 +122,113 @@ def check_projects_scores(request, hack_id): and render final_score.html with the score table. """ - score_categories = HackProjectScoreCategory.objects.all() hackathon = get_object_or_404(Hackathon, pk=hack_id) - judges = hackathon.judges.count() - number_categories = score_categories.count() - projects = list(HackTeam.objects.filter( - hackathon=hackathon).values_list('project', flat=True).distinct()) - - # Number of objects that should be in the HackProjectScore for each project - number_of_objects = judges * number_categories - - for project in projects: - if HackProjectScore.objects.filter(project=project).count() != number_of_objects: - return render(request, 'hackathon/final_score.html', {"hackathon": hackathon.display_name}) + HackAwardCategoryFormSet = modelformset_factory( + HackAwardCategory, fields=('id', 'display_name', 'winning_project'), + form=HackAwardCategoryForm, extra=0) - # Calculate total score for each team - scores_list = [] - for project in projects: - total_score = sum(list(HackProjectScore.objects.filter( - project=project).values_list("score", flat=True))) - scores_list.append(total_score) - - # Pair the teams with the total score - team_scores = {} - for i, score in enumerate(scores_list): - team_scores[str(HackProject.objects.get(id=projects[i]))] = score - - # Sort the teams by score - sorted_teams_scores = sorted( - team_scores.items(), key=lambda x: x[1], reverse=True) + if request.method == 'POST': + hack_awards_formset = HackAwardCategoryFormSet( + request.POST, + queryset=HackAwardCategory.objects.filter(hackathon=hackathon)) + if hack_awards_formset.is_valid(): + hack_awards_formset.save() + else: + print(hack_awards_formset.errors) + pass + return redirect(reverse('hackathon:final_score', kwargs={'hack_id': hack_id})) - return render(request, 'hackathon/final_score.html', {'sorted_teams_scores': sorted_teams_scores, "hackathon": hackathon.display_name}) + else: + team_scores = {} + category_scores_per_team = {} + score_categories = HackProjectScoreCategory.objects.all() + hackathon_projects = [team.project.id for team in hackathon.teams.all() + if team.project] + scores = HackProjectScore.objects.filter( + project_id__in=hackathon_projects).all() + judges = hackathon.judges.all() + + # Creating scores data structure + # { + # team_name: { + # team_name: 'Team', + # project_name: 'Project', + # scores: { + # judge_1: { + # 'Score Category 1': 1, + # ... + # 'Score Category n': 1, + # 'Total': 2 + # }, + # judge_n: { + # ... + # } + # }, + # total_score: 5 + # } + # } + for score in scores: + judge_name = score.judge.slack_display_name + team_name = score.project.hackteam.display_name + project_name = score.project.display_name + score_category = score.hack_project_score_category.category + team_scores.setdefault(team_name, deepcopy(DEFAULT_SCORES)) + team_scores[team_name]['team_name'] = team_name + team_scores[team_name]['project_name'] = project_name + team_scores[team_name]['scores'].setdefault(judge_name, {}) + team_scores[team_name]['scores'][judge_name].setdefault('Total', 0) + team_scores[team_name]['scores'][judge_name][score_category] = ( + score.score) + team_scores[team_name]['total_score'] += score.score + team_scores[team_name]['scores'][judge_name][ + 'Total'] += score.score + + category_scores_per_team.setdefault(score_category, {}) + category_scores_per_team[score_category].setdefault(team_name, 0) + category_scores_per_team[score_category][team_name] += score.score + + # Fill empty totals for any judges who have not submitted scores + for team, team_score in team_scores.items(): + for judge in judges: + if judge.slack_display_name not in team_score['scores']: + team_score['scores'][judge.slack_display_name] = { + 'Total': 0} + + sorted_team_scores = sorted(team_scores.values(), + key=itemgetter('total_score'), + reverse=True) + + # Ordering scores per category + category_scores_per_category = {} + for category, scores in category_scores_per_team.items(): + category_scores_per_category[category] = sorted( + [{"team_name": team_name, "score": score} + for team_name, score in scores.items()], + key=itemgetter('score'), + reverse=True) + + # Grouping back into teams + category_scores = {team.display_name : {} for team in hackathon.teams.all()} + for category, teams in category_scores_per_category.items(): + max_score = max([team['score'] for team in teams]) + place = 1 + for team in teams: + category_scores[team['team_name']].setdefault(category, {}) + category_scores[team['team_name']][category]['score'] = team['score'] + if team['score'] < max_score: + place += 1 + category_scores[team['team_name']][category]['place'] = place + + hack_awards_formset = HackAwardCategoryFormSet( + queryset=HackAwardCategory.objects.filter(hackathon=hackathon)) + + return render(request, 'hackathon/final_score.html', { + 'sorted_teams_scores': sorted_team_scores, + 'category_scores': category_scores, + 'hackathon': hackathon.display_name, + 'judges': judges, + 'hack_awards_formset': hack_awards_formset, + }) def create_hackathon(request): @@ -151,7 +240,9 @@ def create_hackathon(request): return redirect("hackathon:hackathon-list") template = "hackathon/create-event.html" - form = HackathonForm(initial={'organisation': 1}) + form = HackathonForm(initial={ + 'organisation': 1, + 'score_categories':HackProjectScoreCategory.objects.all()[:5]}) return render(request, template, {"form": form}) @@ -179,6 +270,17 @@ def create_hackathon(request): form.instance.created_by = request.user form.instance.organiser = request.user form.save() + # Taking the first 3 award categories and creating them for the + # newly created hackathon + for award_category in AWARD_CATEGORIES[:3]: + hack_award = HackAwardCategory.objects.filter( + display_name=award_category).first() + if not hack_award: + continue + hack_award.pk = None + hack_award.created_by = request.user + hack_award.hackathon = form.instance + hack_award.save() messages.success( request, 'Thanks for submitting a new Hackathon event!') return redirect("hackathon:hackathon-list") diff --git a/left_to_do.txt b/left_to_do.txt new file mode 100644 index 00000000..992815ea --- /dev/null +++ b/left_to_do.txt @@ -0,0 +1,4 @@ +- Discuss if we want to exclude judges who have not scored all teams from the scoring +- Add view to edit awards for hackathon / add it to create form +- Add field and upload for hackathon image +- Add notification to notify participants of winners \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css index 29925b43..be44f8f2 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -70,6 +70,8 @@ main { color: var(--p-orange); border: 3px solid var(--p-orange); transition: all .4s; + padding: 4px 10px; + border-radius: 5px; } .btn-ci:hover { @@ -227,6 +229,15 @@ html .all-auth-container { color: var(--p-orange); } +.enroll-hackathon-card, .enroll-hackathon-card form { + width: fit-content; + display: inline-block; +} +.enroll-hackathon-card form button#enroll-part { + margin: 0 !important; + font-size: .75rem; +} + #accordion-wrapper>h1, #carousel-wrapper>h1 { font-size: 2rem; @@ -276,7 +287,7 @@ html .all-auth-container { } /* Final Score page */ -tbody >:first-child { +.final_score_table tbody >:first-child { background-color: var(--p-orange); color: var(--white); } @@ -304,6 +315,10 @@ h1.table_titles{ padding: 2vh } +.card .card-body form.hack-award-submission { + max-width: none; +} + .bg-p-blue { background-color: var(--p-blue); } diff --git a/test.json b/test.json new file mode 100644 index 00000000..e69de29b