Skip to content

Commit e9e6693

Browse files
#A05. As Admin, I would like to manually trigger an event that divides teams/judges evenly. (#124)
1 parent a28a555 commit e9e6693

26 files changed

Lines changed: 792 additions & 13 deletions

File tree

accounts/lists.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@
1717
('', 'Select Learning Stage'),
1818
('programme_preliminaries', 'Programme Preliminaries'),
1919
('programming_paradigms', 'Programming Paradigms'),
20-
('html_fundamentals', 'HTML Fundamentals'),
21-
('css_fundamentals', 'CSS Fundamentals'),
20+
('html_essentials', 'HTML Essentials'),
21+
('css_essentials', 'CSS Essentials'),
2222
('user_centric_frontend_development', 'User Centric Frontend Development'),
23-
('javascript_fundamentals', 'Javascript Fundamentals'),
23+
('comparative_programming_languages_essentials', 'Comparative Programming Languages Essentials'),
24+
('javascript_essentials', 'Javascript Essentials'),
2425
('interactive_frontend_development', 'Interactive Frontend Development'),
25-
('python_fundamentals', 'Python Fundamentals'),
26+
('python_essentials', 'Python essentials'),
2627
('practical_python', 'Practical Python'),
2728
('data_centric_development', 'Data Centric Development'),
28-
('full_stack_frameworks with django', 'Full Stack Frameworks with Django'),
29+
('backend_development','Backend Development'),
30+
('full_stack_frameworks_with_django', 'Full Stack Frameworks with Django'),
2931
('alumni', 'Alumni'),
3032
('staff', 'Staff'),
3133
)

accounts/models.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.contrib.auth.models import AbstractUser
33

44
from .lists import USER_TYPES_CHOICES, LMS_MODULES_CHOICES
5+
from teams.lists import LMS_LEVELS
56

67

78
class Organisation(models.Model):
@@ -44,13 +45,20 @@ class CustomUser(AbstractUser):
4445
default=Organisation.DEFAULT_PK
4546
)
4647

48+
class Meta:
49+
verbose_name = 'User'
50+
verbose_name_plural = 'Users'
51+
4752
def __str__(self):
4853
""" Return Class object to string via the user email value """
4954
return self.username
5055

5156
def human_readable_current_lms_module(self):
5257
return self.current_lms_module.replace('_', ' ')
5358

54-
class Meta:
55-
verbose_name = 'User'
56-
verbose_name_plural = 'Users'
59+
def to_team_member(self):
60+
return {
61+
'userid': self.id,
62+
'name': self.username,
63+
'level': LMS_LEVELS[self.current_lms_module]
64+
}

hackathon/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ class Hackathon(models.Model):
6767
default='not_yet_started',
6868
choices=JUDGING_STATUS_CHOICES
6969
)
70-
7170

7271
def __str__(self):
7372
return self.display_name
@@ -132,6 +131,11 @@ class HackTeam(models.Model):
132131

133132
def __str__(self):
134133
return self.display_name
134+
135+
class Meta:
136+
verbose_name = 'Hack Team'
137+
verbose_name_plural = 'Hack Teams'
138+
unique_together = ['display_name', 'hackathon']
135139

136140

137141
class HackProject(models.Model):

hackathon/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
check_projects_scores,
1111
view_hackathon
1212
)
13+
from teams.views import change_teams
1314

1415
urlpatterns = [
1516
path('', HackathonListView.as_view(), name="hackathon-list"),
1617
path("<int:hack_id>/team/<int:team_id>/judging/", judging, name="judging"),
1718
path("<int:hack_id>/final_score", check_projects_scores, name="final_score"),
19+
path("<int:hackathon_id>/change_teams/", change_teams, name="change_teams"),
1820
path("create_hackathon", create_hackathon, name='create_hackathon'),
1921
path("<int:hackathon_id>/", view_hackathon,
2022
name='view_hackathon'),
@@ -24,4 +26,3 @@
2426
name="delete_hackathon"),
2527
path('enroll/', enroll_toggle, name='enroll_toggle'),
2628
]
27-

hackathon/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def create_hackathon(request):
182182
form.save()
183183
messages.success(
184184
request, 'Thanks for submitting a new Hackathon event!')
185-
return redirect("hackathon:hackathon-list-scores")
185+
return redirect("hackathon:hackathon-list")
186186

187187

188188
@login_required

main/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"profiles",
3737
"resources",
3838
"submissions",
39+
"teams",
3940
]
4041

4142
MIDDLEWARE = [

main/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
path("hackathon/", include(("hackathon.urls", "hackathon"),
1212
namespace='hackathon')),
1313
path("submission/", include("submissions.urls")),
14+
path("teams/", include("teams.urls")),
1415
]

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ asgiref==3.2.10
22
certifi==2020.6.20
33
chardet==3.0.4
44
defusedxml==0.6.0
5-
Django==3.1.1
5+
Django==3.1.3
66
django-allauth==0.42.0
77
django-crispy-forms==1.9.2
88
dj-database-url==0.5.0

static/css/style.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,19 @@ h1.table_titles{
329329
margin: 0.8rem 0 0;
330330
}
331331

332+
/* Distribute teams page */
333+
ol.team-drop-area {
334+
min-height: 50px;
335+
}
336+
337+
form.distribute-teams-form, form.clear-teams-form {
338+
display: inline-block;
339+
}
340+
341+
.team-score-container {
342+
float:right
343+
}
344+
332345
.hackathon-sub-details::after {
333346
content: '';
334347
display: block;

static/js/teams.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* */
2+
let teams = JSON.parse(document.getElementById('_teams').textContent);
3+
let leftover_participants = JSON.parse(document.getElementById('_leftover_participants').textContent);
4+
5+
addNewTeam();
6+
distributeTeams();
7+
8+
function allowDrop(ev) {
9+
ev.preventDefault();
10+
}
11+
12+
function drag(ev) {
13+
ev.dataTransfer.setData('text', ev.target.id);
14+
}
15+
16+
function drop(ev) {
17+
/* Drop event which triggers functions to update the team score and the
18+
team object used to create or edit the teams */
19+
let targetElement;
20+
ev.preventDefault();
21+
let data = ev.dataTransfer.getData('text');
22+
let movingElement = document.getElementById(data);
23+
let movingElementParent = movingElement.parentElement;
24+
if(ev.target.nodeName == 'LI') {
25+
targetElement = ev.target.parentElement.id;
26+
ev.target.parentElement.appendChild(movingElement);
27+
} else {
28+
targetElement = ev.target.id;
29+
ev.target.appendChild(movingElement);
30+
}
31+
32+
changeTeamData(movingElement, movingElementParent.id, targetElement);
33+
changeTeamScores(movingElement, movingElementParent.id, targetElement);
34+
}
35+
36+
function changeTeamScores(movedElement, movedElementParentId, targetElementId){
37+
/* Deducts the participant's experience level from the
38+
the overall score for the team the participant was previously part of
39+
and adds it to the new team's score */
40+
let movedLevel = parseInt(movedElement.dataset.level);
41+
if (targetElementId != 'leftover_participants'){
42+
let teamScoreSpan = document.getElementById(targetElementId +'_score');
43+
teamScoreSpan.innerText = parseInt(teamScoreSpan.innerText) + movedLevel;
44+
}
45+
46+
if(movedElementParentId.includes('team')) {
47+
let removeScoreSpan = document.getElementById(movedElementParentId +'_score');
48+
removeScoreSpan.innerText = parseInt(removeScoreSpan.innerText) - movedLevel;
49+
}
50+
}
51+
52+
function changeTeamData(movedElement, movedElementParentId, targetElementId){
53+
/* Removes the participant from the team the participant was previously
54+
part of and adds it to the new team in the team data object which is used
55+
to create or edit the teams */
56+
let team = movedElementParentId.includes('team')
57+
? teams[movedElementParentId]
58+
: leftover_participants;
59+
let targetTeam = targetElementId.includes('team')
60+
? teams[targetElementId]
61+
: leftover_participants;
62+
let userid = movedElement.dataset.userid;
63+
let user = team.filter(x => x.userid == userid)[0];
64+
targetTeam.push(user)
65+
66+
if(movedElementParentId.includes('team')){
67+
teams[movedElementParentId] = teams[movedElementParentId]
68+
.filter(x => x.userid != userid);
69+
} else {
70+
leftover_participants = leftover_participants
71+
.filter(x => x.userid != userid);
72+
}
73+
$('input[name="teams"]').val(JSON.stringify(teams));
74+
}
75+
76+
function addNewTeam(){
77+
$('.add-team').click(function(){
78+
let numTeams = $('.team').length;
79+
let teamName = `team_${numTeams+1}`;
80+
let teamDisplayName = `Team ${numTeams+1}`;
81+
let teamTemplate = `<section class="card shadow mb-3 mt-3">
82+
<div class="card-body">
83+
<h5 class="p-orange card-title">${teamDisplayName}
84+
<span style="float:right">Team Score: <span id="${teamName}_score">0</span>
85+
</span></h5>
86+
<ol class="team team-drop-area" id="${teamName}"
87+
ondrop="drop(event)" ondragover="allowDrop(event)"></ol>
88+
</div>
89+
</section>`;
90+
$('#team-list').append($(teamTemplate));
91+
teams[teamName] = [];
92+
});
93+
}
94+
95+
function distributeTeams(){
96+
$('.distribute-teams-form').on('submit', function(event){
97+
if(leftover_participants.length > 0) {
98+
let confirm_msg = 'Some participants have not been assigned to a team. Do you still want to proceed?';
99+
let confirmation = window.confirmconfirm_msg(confirm_msg);
100+
if(!confirmation){
101+
event.preventDefault();
102+
}
103+
}
104+
});
105+
}

0 commit comments

Comments
 (0)