Skip to content

Commit 156d8f4

Browse files
Fixing team assignment and adding extra info to the team display (#266)
1 parent 6738a6a commit 156d8f4

19 files changed

Lines changed: 266 additions & 109 deletions

File tree

accounts/models.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
from datetime import datetime
12
from enum import Enum
3+
import pytz
24

35
from django.db import models
46
from django.contrib.auth.models import AbstractUser
@@ -116,12 +118,33 @@ def human_readable_current_lms_module(self):
116118
return self.current_lms_module.replace('_', ' ')
117119

118120
def to_team_member(self):
121+
teams = self.participated_hackteams.filter(
122+
hackathon__status='finished')
119123
return {
120124
'userid': self.id,
121125
'name': self.slack_display_name or self.email,
122-
'level': LMS_LEVELS.get(self.current_lms_module) or 1
126+
'level': LMS_LEVELS.get(self.current_lms_module) or 1,
127+
'timezone': self.timezone_to_offset(),
128+
'num_hackathons': teams.count(),
129+
'participant_label': self.participant_label(),
123130
}
124131

132+
def timezone_to_offset(self):
133+
if not self.timezone:
134+
return
135+
offset = datetime.now(pytz.timezone(self.timezone)).strftime('%z')
136+
return f'UTC{offset[:-2]}'
137+
138+
def participant_label(self):
139+
teams = self.participated_hackteams.filter(
140+
hackathon__status='finished')
141+
if teams.count() == 0:
142+
return 'Hackathon Newbie'
143+
elif teams.count() < 2:
144+
return 'Hackathon Enthusiast'
145+
else:
146+
return 'Hackathon Veteran'
147+
125148
@property
126149
def user_type(self):
127150
""" Return the user's main designation.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 3.1.13 on 2022-01-13 13:50
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11+
('hackathon', '0045_hackprojectscorecategory_is_active'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='hackteam',
17+
name='participants',
18+
field=models.ManyToManyField(related_name='participated_hackteams', to=settings.AUTH_USER_MODEL),
19+
),
20+
]

hackathon/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class HackTeam(models.Model):
169169
# Issue is that a user could join more than one team on the same Hackathon.
170170
# Could use a custom save method to prevent it.
171171
participants = models.ManyToManyField(User,
172-
related_name="hackteam")
172+
related_name="participated_hackteams")
173173
# A team has one mentor, a mentor has numerous teams: One to Many.
174174
mentor = models.ForeignKey(User,
175175
null=True,

hackathon/views.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def check_projects_scores(request, hackathon_id):
197197

198198
hack_awards_formset = HackAwardFormSet(
199199
form_kwargs={'hackathon_id': hackathon_id},
200-
queryset=HackAward.objects.filter(hackathon=hackathon))
200+
queryset=hackathon.awards.all())
201201

202202
return render(request, 'hackathon/final_score.html', {
203203
'hackathon': hackathon.display_name,
@@ -373,7 +373,6 @@ def view_hackathon_public(request, hackathon_id):
373373
""" A limited view of the hackathon page for the public """
374374
hackathon = get_object_or_404(Hackathon, pk=hackathon_id)
375375
redirect_url = (request.META.get('HTTP_REFERER') or reverse('home'))
376-
print(f"REFERER: {request.META.get('HTTP_REFERER')}")
377376
if hackathon.status == 'deleted':
378377
messages.error(request, 'This hackathon does not exist.')
379378
return redirect(redirect_url)

home/templatetags/home_tags.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ def convert_rating(rating):
1111
half_star = '<i class="fas fa-star-half-alt"></i>'
1212

1313
rating_split = [int(rating) for rating in str(rating).split('.')]
14-
print(rating_split[0] * star)
1514
return rating_split[0] * star + int(bool(rating_split[1])) * half_star
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{% extends "base.html" %}
2+
{% load static %}
3+
{% load teams_tags %}
4+
{% load account_tags %}
5+
6+
{% block extra_meta %}
7+
8+
<meta property='og:title' content='{{showcase.hack_project.display_name}}'>
9+
{% if showcase.get_image %}
10+
<meta property='og:image' content='{{showcase.image_url}}'>
11+
{% endif %}
12+
<meta property='og:description' content='{{showcase.hack_project.description}}'>
13+
<meta property='og:url' content='{{showcase.url}}'>
14+
<meta property='og:author' content='Team {{showcase.hack_project.hackteam.display_name}}'>
15+
16+
{% endblock %}
17+
18+
{% block css %}
19+
<link rel="stylesheet" href="{% static 'hackathon/css/hackathon.css' %}" />
20+
{% endblock %}
21+
22+
{% block content %}
23+
<div class="row">
24+
<div class="col teams-page">
25+
26+
<div class="container-fluid">
27+
{% if team.header_image %}
28+
<div class="row teams-header"
29+
style="background-image:url('{{ team.header_image }}')">
30+
{% else %}
31+
<div class="row teams-header"
32+
style="background-image:url({% static 'img/ci-hackathon--horizontal.png' %})">
33+
{% endif %}
34+
<div class="team-name">
35+
<h2>Team: <i>{{team}}</i></h2>
36+
</div>
37+
</div>
38+
39+
<div class="row mb-2 mt-5 ml-4">
40+
<div class="col">
41+
42+
<h3>About the team</h3>
43+
<div class="share-linkedin">
44+
<script src="https://platform.linkedin.com/in.js" type="text/javascript">lang: en_US</script>
45+
<script type="IN/Share" data-url="{{showcase.url}}"></script>
46+
</div>
47+
</div>
48+
</div>
49+
50+
<div class="row mt-3 team-members-display">
51+
<div class="col my-3 text-center">
52+
{% for member in showcase.showcase_participants.all %}
53+
{% include 'includes/showcase_member.html' %}
54+
{% endfor %}
55+
56+
{% for anon in anon_members %}
57+
{% include 'includes/anon_member.html' %}
58+
{% endfor %}
59+
</div>
60+
</div>
61+
62+
{% if team.project %}
63+
{% include 'includes/project.html' %}
64+
{% else %}
65+
{% include 'includes/empty_project.html' %}
66+
{% endif %}
67+
</div>
68+
69+
</div>
70+
</div>
71+
{% endblock %}

showcase/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def view_showcases(request):
1313
""" Shows the project showcase page """
1414
showcase_settings = ShowcaseSiteSettings.objects.first()
1515
if not showcase_settings:
16-
return render(request, 'showcase.html', {
16+
return render(request, 'showcases.html', {
1717
'top_results': None,
1818
'all_showcases': None,
1919
})
@@ -40,7 +40,7 @@ def view_showcases(request):
4040
page = request.GET.get('page')
4141
paginated_showcases = paginator.get_page(page)
4242

43-
return render(request, 'showcase.html', {
43+
return render(request, 'showcases.html', {
4444
'top_results': top_results,
4545
'all_showcases': paginated_showcases,
4646
})
@@ -58,7 +58,7 @@ def view_showcase(request, showcase_id):
5858
anon_members = range(len(team.participants.all())
5959
- len(showcase.showcase_participants.all()))
6060

61-
return render(request, 'team.html', {
61+
return render(request, 'view_showcase.html', {
6262
'team': team,
6363
'rename_team_form': None,
6464
'showcase': showcase,

static/css/style.css

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
--img-highlighter: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,0.6) 100%);
1818
--contact-bg: linear-gradient(48deg, rgb(229, 71, 37) 50%, rgb(255, 49, 134));
1919
--star-rating: #f2d230;
20+
--s-teal-half-opacity: rgba(35, 187, 187, 0.5);
21+
--bronze: #e99644;
22+
--bronze-font: #5c3105;
23+
--silver: #dddddd;
24+
--silver-font: #535353;
25+
--gold: #ffd700;
26+
--gold-font: #635403;
2027
}
2128

2229
html,
@@ -488,6 +495,43 @@ form.distribute-teams-form, form.clear-teams-form {
488495
background: var(--s-grey);
489496
}
490497

498+
.team-drop-area li span {
499+
color: var(--white);
500+
}
501+
502+
.team-drop-area li span:not(.member-name), .team-member-label {
503+
font-size: 0.9em;
504+
}
505+
506+
.team-member-label {
507+
display: block;
508+
color: var(--white);
509+
}
510+
511+
.team-drop-area li span.newbie,
512+
.team-member-label span.newbie {
513+
color: var(--bronze-font);
514+
background: var(--bronze);
515+
}
516+
517+
.team-drop-area li span.enthusiast,
518+
.team-member-label span.enthusiast {
519+
color: var(--silver-font);
520+
background: var(--silver);
521+
}
522+
523+
.team-drop-area li span.veteran,
524+
.team-member-label span.veteran {
525+
color: var(--gold-font);
526+
background: var(--gold);
527+
}
528+
529+
.team-drop-area li span.member-name {
530+
color: var(--p-grey);
531+
min-width: 250px;
532+
display: inline-block;
533+
}
534+
491535
/* Profile image */
492536
.profile-image {
493537
border-radius: 50%;
@@ -548,7 +592,7 @@ form.distribute-teams-form, form.clear-teams-form {
548592
}
549593

550594
.team-members-display div.col {
551-
text-overflow: ellipsis;
595+
text-overflow: scroll;
552596
white-space: nowrap;
553597
overflow-y: auto;
554598
margin: 0rem 2rem;
@@ -819,7 +863,10 @@ img.showcase-image-edit {
819863
#carousel-wrapper {
820864
margin-top: 30px;
821865
}
822-
866+
.team-drop-area li span.member-name {
867+
display: inline;
868+
min-width: none;
869+
}
823870
}
824871

825872
@media (max-width:993px) {
@@ -828,3 +875,16 @@ img.showcase-image-edit {
828875
font-size: 1.1rem;
829876
}
830877
}
878+
879+
@media (max-width:650px){
880+
.team-menu-dropdown-container {
881+
position: relative;
882+
right: 0;
883+
}
884+
.teams-dropdown {
885+
transform: translate3d(0px, 38px, 0) !important;
886+
}
887+
.dropdown-menu.teams-dropdown {
888+
width: 100%;
889+
}
890+
}

static/js/teams.js

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ let teams = JSON.parse(document.getElementById('_teams').textContent);
33
let leftover_participants = JSON.parse(document.getElementById('_leftover_participants').textContent);
44

55
addNewTeam();
6-
distributeTeams();
6+
confirmBeforeAction('.clear-teams-form', 'submit', 'Do you really want to clear all teams and re-distribute them?');
7+
confirmBeforeAction('.distribute-teams-form', 'submit', 'Some participants have not been assigned to a team. Do you still want to proceed?');
78

89
function allowDrop(ev) {
910
ev.preventDefault();
@@ -24,11 +25,20 @@ function drop(ev) {
2425
if(ev.target.nodeName == 'LI') {
2526
targetElement = ev.target.parentElement.id;
2627
ev.target.parentElement.appendChild(movingElement);
27-
} else {
28+
} else if(ev.target.nodeName == 'SPAN'){
29+
targetElement = ev.target.parentElement.parentElement.id;
30+
ev.target.parentElement.parentElement.appendChild(movingElement);
31+
} else if(ev.target.nodeName == 'H5') {
32+
targetElement = ev.target.nextElementSibling.id;
33+
ev.target.nextElementSibling.appendChild(movingElement);
34+
} else if(ev.target.nodeName == 'DIV') {
35+
targetElement = ev.target.children[1].id;
36+
ev.target.children[1].appendChild(movingElement);
37+
}
38+
else {
2839
targetElement = ev.target.id;
2940
ev.target.appendChild(movingElement);
3041
}
31-
3242
changeTeamData(movingElement, movingElementParent.id, targetElement);
3343
changeTeamScores(movingElement, movingElementParent.id, targetElement);
3444
}
@@ -41,35 +51,34 @@ function changeTeamScores(movedElement, movedElementParentId, targetElementId){
4151
if (targetElementId != 'leftover_participants'){
4252
let teamScoreSpan = document.getElementById(targetElementId +'_score');
4353
teamScoreSpan.innerText = parseInt(teamScoreSpan.innerText) + movedLevel;
44-
}
45-
46-
if(movedElementParentId.includes('team')) {
4754
let removeScoreSpan = document.getElementById(movedElementParentId +'_score');
48-
removeScoreSpan.innerText = parseInt(removeScoreSpan.innerText) - movedLevel;
55+
if(removeScoreSpan != null) {
56+
removeScoreSpan.innerText = parseInt(removeScoreSpan.innerText) - movedLevel;
57+
}
4958
}
5059
}
5160

5261
function changeTeamData(movedElement, movedElementParentId, targetElementId){
5362
/* Removes the participant from the team the participant was previously
5463
part of and adds it to the new team in the team data object which is used
5564
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;
65+
if (movedElementParentId == targetElementId) {
66+
return;
67+
}
68+
let team = movedElementParentId.includes('leftover_participants')
69+
? leftover_participants
70+
: teams[movedElementParentId];
71+
let targetTeam = targetElementId.includes('leftover_participants')
72+
? leftover_participants
73+
: teams[targetElementId];
6274
let userid = movedElement.dataset.userid;
6375
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);
76+
if(movedElementParentId.includes('leftover_participants')){
77+
leftover_participants = leftover_participants.filter(x => x.userid != userid);
6978
} else {
70-
leftover_participants = leftover_participants
71-
.filter(x => x.userid != userid);
79+
teams[movedElementParentId] = teams[movedElementParentId].filter(x => x.userid != userid);
7280
}
81+
targetTeam.push(user);
7382
$('input[name="teams"]').val(JSON.stringify(teams));
7483
}
7584

@@ -92,11 +101,10 @@ function addNewTeam(){
92101
});
93102
}
94103

95-
function distributeTeams(){
96-
$('.distribute-teams-form').on('submit', function(event){
104+
function confirmBeforeAction(className, action, msg){
105+
$(className).on(action, function(event){
97106
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);
107+
let confirmation = window.confirm(msg);
100108
if(!confirmation){
101109
event.preventDefault();
102110
}

0 commit comments

Comments
 (0)