Skip to content

Commit 56cb96a

Browse files
committed
Merge branch 'flag_cv' of https://github.com/hackupc/myhackupc into flag_cv
2 parents 3d5b070 + 828a7e9 commit 56cb96a

File tree

6 files changed

+136
-81
lines changed

6 files changed

+136
-81
lines changed

applications/models/hacker.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,16 @@ def annotate_vote(cls, qs):
8888
return qs.annotate(vote_avg=Avg("vote__calculated_vote"))
8989

9090
def invalidate(self):
91+
"""
92+
Marks the application as invalid, but only if its current status is "dubious".
93+
Also, if the user has a team, it deletes it.
94+
"""
9195
if self.status != APP_DUBIOUS:
92-
raise ValidationError(
93-
"Applications can only be marked as invalid if they are dubious first"
94-
)
95-
self.status = APP_INVALID
96+
raise ValidationError('Applications can only be marked as invalid if they are dubious first')
97+
self.status = APP_INVALID
98+
team = getattr(self.user, 'team', None)
99+
if team:
100+
team.delete()
96101
self.save()
97102

98103
def set_dubious(self):
@@ -124,6 +129,10 @@ def set_contacted(self, user):
124129
self.save()
125130

126131
def confirm_blacklist(self, user, motive_of_ban):
132+
"""
133+
Confirms the application as blacklisted, but only if its current status is "APP_BLACKLISTED".
134+
Also, if the user has a team, it deletes it.
135+
"""
127136
if self.status != APP_BLACKLISTED:
128137
raise ValidationError(
129138
"Applications can only be confirmed as blacklisted if they are blacklisted first"
@@ -136,6 +145,9 @@ def confirm_blacklist(self, user, motive_of_ban):
136145
self.user, motive_of_ban
137146
)
138147
blacklist_user.save()
148+
team = getattr(self.user, 'team', None)
149+
if team:
150+
team.delete()
139151
self.save()
140152

141153
def set_blacklist(self):

meals/templates/meal_checkin.html

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,36 @@ <h1 style="font-size: larger">{{ meal_name }}</h1>
4545

4646
<video id="qr-video" style="width: 100%; height: 100%; "></video>
4747
<script nonce="{{ request.csp_nonce }}">
48-
// set timer bool
49-
isReady = true;
48+
let isReady = true;
49+
let lastScannedQR = null;
50+
let lastScanTime = 0;
5051

51-
// Load qr video and add function to execute when scans a qr
5252
document.addEventListener('DOMContentLoaded', () => {
5353
let scanner = new Scanner('qr-video', (content) => {
54-
if (content.data && isReady) {
55-
SuperScan('{% url 'cool_meals_api' %}', content.data, scanner)
54+
const now = Date.now();
55+
56+
if (now - lastScanTime > 10000) {
57+
lastScannedQR = null;
58+
}
59+
60+
if (content.data && isReady && content.data !== lastScannedQR) {
61+
lastScannedQR = content.data;
62+
lastScanTime = now;
63+
SuperScan('{% url 'cool_meals_api' %}', content.data, scanner);
5664
isReady = false;
57-
setTimeout(()=>{
65+
setTimeout(() => {
5866
isReady = true;
59-
}, 1000)
67+
}, 1000);
68+
} else if (content.data === lastScannedQR) {
69+
console.log("Duplicate QR detected (ignored): " + content.data);
6070
}
61-
console.log("SCANED: "+ content.data);
71+
console.log("SCANNED: " + content.data);
6272
}, {
6373
popup: false,
64-
})
74+
});
6575
scanner.show();
66-
// $('.scan-again').click(() => {
67-
// scanner.show()
68-
// document.getElementById('no').style.display = "none";
69-
//document.getElementById('success').style.display = "none";
70-
// })
71-
})
76+
});
7277

73-
74-
// Core function to send data to meals API
7578
function SuperScan(theUrl, content, scanner) {
7679
$.ajax({
7780
type: "POST",
@@ -89,37 +92,22 @@ <h1 style="font-size: larger">{{ meal_name }}</h1>
8992
document.getElementById('deny').innerHTML = "Something went wrong.";
9093
}
9194

92-
if (data.success == true) { // 200 ok means can eat
93-
// show success alert
94-
95+
if (data.success == true) {
9596
$('#success').fadeOut("fast");
9697
$('#success').fadeIn("fast");
97-
// Display user's diet
98-
99-
// console.log(data.responseText.diet);
10098
document.getElementById('succ').innerHTML = "User allowed to entry <br> Diet: <b>" + data.diet + "</b>";
101-
// hide no pass alert
10299
document.getElementById('no').style.display = "none";
103-
104-
} else { // else, (may be 400). Has already eatern.
105-
// hide success alert
100+
} else {
106101
document.getElementById('success').style.display = "none";
107-
108102
document.getElementById('deny').innerHTML = data.error;
109-
// show no pass alert
110103
$('#no').fadeOut("fast");
111104
$('#no').fadeIn("fast");
112-
// hide alert after time desired.
113-
114105
}
115106
}
116-
117-
118107
});
119108
}
120-
121-
122109
</script>
110+
123111
</div>
124112

125113
</div>

teams/forms.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,48 @@
66

77

88
class JoinTeamForm(forms.ModelForm):
9+
"""
10+
Form for joining an existing team.
11+
Validates that the team exists and that it is not full.
12+
If the team does not exist, it raises a validation error.
13+
If the team is full, it raises a validation error.
14+
"""
15+
916
def clean_team_code(self):
10-
team_code = self.cleaned_data['team_code']
17+
team_code = self.cleaned_data["team_code"]
1118
teammates = Team.objects.filter(team_code=team_code).count()
1219
if teammates == 0:
13-
raise forms.ValidationError("No team exists with the current code. Did you want to create a team instead?")
14-
max_teammates = getattr(settings, 'HACKATHON_MAX_TEAMMATES', 4)
20+
raise forms.ValidationError(
21+
"No team exists with the current code. Did you want to create a team instead?"
22+
)
23+
max_teammates = getattr(settings, "HACKATHON_MAX_TEAMMATES", 4)
1524
if teammates == max_teammates:
16-
raise forms.ValidationError("Full team. Max teammates is %d" % max_teammates)
17-
if Team.objects.filter(team_code=team_code).exclude(
18-
user__hackerapplication_application__status__in=[APP_PENDING, APP_LAST_REMIDER, APP_DUBIOUS]).exists():
19-
raise forms.ValidationError("Some members of this team are accepted or cancelled. You can't join their "
20-
"team here but don't worry you still can join them on the event.")
25+
raise forms.ValidationError(
26+
"Full team. Max teammates is %d" % max_teammates
27+
)
28+
if (
29+
Team.objects.filter(team_code=team_code)
30+
.exclude(
31+
user__hackerapplication_application__status__in=[
32+
APP_PENDING,
33+
APP_LAST_REMIDER,
34+
APP_DUBIOUS,
35+
]
36+
)
37+
.exists()
38+
):
39+
raise forms.ValidationError(
40+
"Some members of this team are accepted or cancelled. You can't join their "
41+
"team here but don't worry you still can join them on the event."
42+
)
2143
return team_code
2244

2345
class Meta:
2446
model = Team
25-
exclude = ['user', ]
26-
labels = {
27-
'team_code': 'Your team code'
28-
}
47+
exclude = [
48+
"user",
49+
]
50+
labels = {"team_code": "Your team code"}
2951
help_texts = {
30-
'team_code': 'Paste here the team code that your teammate has sent you'
52+
"team_code": "Paste here the team code that your teammate has sent you"
3153
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 3.2.23 on 2025-09-26 17:46
2+
3+
from django.db import migrations, models
4+
import teams.models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('teams', '0001_teams'),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name='team',
16+
name='team_code',
17+
field=models.CharField(default=teams.models.generate_team_id, max_length=36),
18+
),
19+
]

teams/models.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
import random
2-
1+
import uuid
32
from django.db import models
4-
53
from user.models import User
64

7-
TEAM_ID_LENGTH = 13
5+
MAX_LENGTH_TEAM_CODE = 36
86

97

108
def generate_team_id():
11-
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
12-
while True:
13-
team_id = "".join(random.sample(s, TEAM_ID_LENGTH))
14-
if not Team.objects.filter(team_code=team_id).exists():
15-
return team_id
9+
return str(uuid.uuid4())
1610

1711

1812
class Team(models.Model):
19-
team_code = models.CharField(default=generate_team_id, max_length=TEAM_ID_LENGTH)
13+
"""
14+
This model represents a team of hackers. For each hacker in a team, there is one entry
15+
in this table with the same team_code. The user field is a foreign key to the user
16+
"""
17+
18+
team_code = models.CharField(
19+
default=generate_team_id, max_length=MAX_LENGTH_TEAM_CODE
20+
)
2021
user = models.OneToOneField(User, on_delete=models.CASCADE)
2122

2223
class Meta:

teams/views.py

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,52 +10,65 @@
1010

1111

1212
class HackerTeam(IsHackerMixin, TabsView):
13-
template_name = 'team.html'
13+
"""
14+
View for hackers to manage their team.
15+
Hackers can create a new team, join an existing team or leave their current team,
16+
by making a POST request with theappropriate action.
17+
"""
18+
19+
template_name = "team.html"
1420

1521
def get_current_tabs(self):
1622
return hacker_tabs(self.request.user)
1723

1824
def get_context_data(self, **kwargs):
1925
c = super(HackerTeam, self).get_context_data(**kwargs)
20-
team = getattr(self.request.user, 'team', None)
21-
app = getattr(self.request.user, 'hackerapplication_application', None)
26+
team = getattr(self.request.user, "team", None)
27+
app = getattr(self.request.user, "hackerapplication_application", None)
2228
teammates = []
2329
if team:
24-
teammates = models.Team.objects.filter(team_code=team.team_code) \
25-
.values('user__name', 'user__email', 'user__hackerapplication_application')
26-
teammates = list(map(lambda x:
27-
{'name': x['user__name'], 'email': x['user__email'],
28-
'app': x['user__hackerapplication_application']},
29-
teammates))
30+
teammates = models.Team.objects.filter(team_code=team.team_code).values(
31+
"user__name", "user__email", "user__hackerapplication_application"
32+
)
33+
teammates = list(
34+
map(
35+
lambda x: {
36+
"name": x["user__name"],
37+
"email": x["user__email"],
38+
"app": x["user__hackerapplication_application"],
39+
},
40+
teammates,
41+
)
42+
)
3043
instance = models.Team()
31-
instance.team_code = ''
44+
instance.team_code = ""
3245
form = forms.JoinTeamForm(instance=instance)
33-
c.update({'team': team, 'teammates': teammates, 'app': app, 'form': form})
46+
c.update({"team": team, "teammates": teammates, "app": app, "form": form})
3447
return c
3548

3649
def post(self, request, *args, **kwargs):
3750

38-
if request.POST.get('create', None):
51+
if request.POST.get("create", None):
3952
team = models.Team()
4053
team.user = request.user
4154
team.save()
42-
return HttpResponseRedirect(reverse('teams'))
43-
if request.POST.get('leave', None):
44-
team = getattr(request.user, 'team', None)
55+
return HttpResponseRedirect(reverse("teams"))
56+
if request.POST.get("leave", None):
57+
team = getattr(request.user, "team", None)
4558
if team:
4659
team.delete()
47-
return HttpResponseRedirect(reverse('teams'))
60+
return HttpResponseRedirect(reverse("teams"))
4861
else:
4962
form = forms.JoinTeamForm(request.POST, request.FILES)
5063
if form.is_valid():
5164
team = form.save(commit=False)
5265
team.user = request.user
5366
team.save()
5467

55-
messages.success(request, 'Team joined successfully!')
68+
messages.success(request, "Team joined successfully!")
5669

57-
return HttpResponseRedirect(reverse('teams'))
70+
return HttpResponseRedirect(reverse("teams"))
5871
else:
5972
c = self.get_context_data()
60-
c.update({'form': form})
73+
c.update({"form": form})
6174
return render(request, self.template_name, c)

0 commit comments

Comments
 (0)