Skip to content
Merged
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
2 changes: 1 addition & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CustomUserAdmin(BaseUserAdmin):
'username', 'first_name', 'last_name',
'full_name', 'slack_display_name',
'current_lms_module', 'organisation',
'timezone', 'user_type', 'is_external')}),
'timezone', 'user_type', 'dropoffs', 'dropped_off_hackathon', 'is_external')}),
('Permissions', {'fields': (
'is_active', 'is_staff', 'is_superuser',
'profile_is_public', 'email_is_public',
Expand Down
18 changes: 18 additions & 0 deletions accounts/migrations/0022_auto_20250214_1439.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2025-02-14 14:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0021_auto_20250205_1138'),
]

operations = [
migrations.AlterField(
model_name='customuser',
name='current_course',
field=models.CharField(choices=[('', 'Select Current Course'), ('L3', 'The Level 3 Diploma in Software Development (L3)'), ('5P', 'The 5 project Diploma in Software Development Course (5P)'), ('4P', 'The 4 project Diploma in Software Development Course (4P)'), ('FSBC', 'The 16 Week Full Stack Developer Bootcamp (BC)'), ('DATABC', 'The 16 Week Data-Analytics Bootcamp (DBC)'), ('external', 'Other/External')], default='', max_length=50),
),
]
14 changes: 14 additions & 0 deletions accounts/migrations/0023_merge_20250217_0913.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 3.1.13 on 2025-02-17 09:13

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('accounts', '0022_auto_20250214_1439'),
('accounts', '0022_auto_20250206_1702'),
]

operations = [
]
18 changes: 18 additions & 0 deletions accounts/migrations/0024_customuser_dropoffs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2025-02-17 12:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounts', '0023_merge_20250217_0913'),
]

operations = [
migrations.AddField(
model_name='customuser',
name='dropoffs',
field=models.IntegerField(default=0, help_text='Number of times a user has dropped off from a hackathon'),
),
]
20 changes: 20 additions & 0 deletions accounts/migrations/0025_customuser_dropped_off_hackathon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.1.13 on 2025-02-19 14:54

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('hackathon', '0056_hackathon_badge_url'),
('accounts', '0024_customuser_dropoffs'),
]

operations = [
migrations.AddField(
model_name='customuser',
name='dropped_off_hackathon',
field=models.ForeignKey(blank=True, help_text='The hackathon that the user dropped off from', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dropped_off_users', to='hackathon.hackathon'),
),
]
15 changes: 15 additions & 0 deletions accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ class CustomUser(AbstractUser):
choices=TIMEZONE_CHOICES,
)

dropoffs = models.IntegerField(
default=0,
help_text=("Number of times a user has dropped off from a hackathon")
)

dropped_off_hackathon = models.ForeignKey(
'hackathon.Hackathon',
on_delete=models.SET_NULL,
related_name='dropped_off_users',
null=True,
blank=True,
help_text=("The hackathon that the user dropped off from")
)

class Meta:
verbose_name = 'User'
verbose_name_plural = 'Users'
Expand All @@ -141,6 +155,7 @@ def to_team_member(self):
'timezone': self.timezone_to_offset(),
'num_hackathons': teams.count(),
'participant_label': self.participant_label(),
'dropoffs': self.dropoffs,
}

def timezone_to_offset(self):
Expand Down
2 changes: 1 addition & 1 deletion hackadmin/templates/includes/remove_participant.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<form class="delete" action="/hackadmin/{{hackathon.id}}/remove_participant/" method="POST">
<form class="remove_hackathon_participant" action="/hackadmin/{{hackathon.id}}/remove_participant/" method="POST">
{% csrf_token %}
<input type="hidden" value="{{remove_from_hackathon}}" name="remove_from_hackathon">
<input type="hidden" value="{{team.id}}" name="team_id">
Expand Down
5 changes: 5 additions & 0 deletions hackadmin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ def remove_participant(request, hackathon_id):
id=request.POST.get('team_id'))
team.participants.remove(participant)

if request.POST.get('dropoffs'):
participant.dropoffs += 1
participant.dropped_off_hackathon = get_object_or_404(Hackathon, id=hackathon_id)
participant.save()

messages.success(request, 'Participant successfully removed')
return redirect(reverse('hackadmin:hackathon_participants',
kwargs={'hackathon_id': hackathon_id}))
Expand Down
12 changes: 11 additions & 1 deletion hackathon/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ class HackathonForm(forms.ModelForm):
),
required=True
)
badge_url = forms.URLField(
label="Badge URL",
required=False,
widget=forms.TextInput(
attrs={
'placeholder': 'Add badge url if available',
'type': 'url'
}
)
)
start_date = forms.DateTimeField(
label="Start Date",
input_formats=['%d/%m/%Y %H:%M'],
Expand Down Expand Up @@ -110,7 +120,7 @@ class Meta:
fields = ['display_name', 'description', 'theme', 'start_date',
'end_date', 'status', 'organisation', 'score_categories',
'team_size', 'tag_line', 'is_public', 'max_participants',
'allow_external_registrations', 'registration_form'
'allow_external_registrations', 'registration_form', 'badge_url'
]

def __init__(self, *args, **kwargs):
Expand Down
18 changes: 18 additions & 0 deletions hackathon/migrations/0056_hackathon_badge_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.13 on 2025-02-14 14:39

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('hackathon', '0055_remove_event_isreadonly'),
]

operations = [
migrations.AddField(
model_name='hackathon',
name='badge_url',
field=models.URLField(blank=True, default='', help_text='Link to the badge image.'),
),
]
5 changes: 5 additions & 0 deletions hackathon/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ class Hackathon(models.Model):
blank=True,
help_text=("Link to the Google Form for registrations.")
)
badge_url = models.URLField(
default="",
blank=True,
help_text=("Link to the badge image.")
)

def __str__(self):
return self.display_name
Expand Down
3 changes: 3 additions & 0 deletions hackathon/templates/hackathon/create-event.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ <h1>Create Hackathon</h1>
<div class="col-12 col-md-12 mb-3">
{{ form.is_public|as_crispy_field }}
</div>
<div class="col-12 col-md-12 mb-3">
{{ form.badge_url|as_crispy_field }}
</div>
<div class="col-12 col-md-6 mb-3">
{{ form.allow_external_registrations|as_crispy_field }}
</div>
Expand Down
15 changes: 14 additions & 1 deletion hackathon/templates/hackathon/hackathon_view.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,15 @@

{% include 'hackathon/includes/judge_team_display.html' %}
{% else %}
{% include 'hackathon/includes/enrollpart.html' %}
{% with is_blocked=request.user|user_is_blocked:hackathon %}
{% if not is_blocked %}
{% include 'hackathon/includes/enrollpart.html' %}
{% else %}
<p class="text-center card body mt-2 p-3">Because you dropped off from the last hackathon you will not be allowed to register for this hackathon. If you believe this is a mistake,
please contact the community team on <a href="https://code-institute-room.slack.com/archives/CDAFARB71">Slack.</a><br>
<em> Make sure to read the <a href="{% url 'codeofconduct' %}">Hackathon Code of Conduct.</a> </em> </p>
{% endif %}
{% endwith %}
{% endif %}
</div>
</div>
Expand Down Expand Up @@ -122,6 +130,11 @@
<p class="hackathon-sub-details"><i class="fas fa-clock"></i> End: {{ hackathon.end_date }}</p>

<p class="hackathon-sub-details"><i class="fas fa-school"></i> Organisation: {{ hackathon.organisation }}</p>
{% if hackathon.badge_url %}
<p class="hackathon-sub-details"><img src="{{ hackathon.badge_url }}" alt="hackathon badge" style="height: 240px;"></p>
{% else %}
<p class="hackathon-sub-details"><img src="https://res.cloudinary.com/djdefbnij/image/upload/v1739874144/CommunityLogo_V3.ai_4_c74vwn.png" alt="hackathon badge" style="height: 240px;"></p>
{% endif %}
</div>
</div>

Expand Down
7 changes: 6 additions & 1 deletion hackathon/templates/hackathon/includes/hackathon_card.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{% load static %}
{% load account_tags %}
{% load custom_tags %}

<article class="card hack-card">
<div class="card-body">
Expand Down Expand Up @@ -69,7 +70,11 @@ <h5 class="p-orange card-title"><a href="/hackathon/{{hackathon.id}}/" class="ci
{% endif %}

{% if request.user.is_authenticated and hackathon.status == 'registration_open' and not request.user.user_type|is_types:authorised_types_on_card and request.user not in hackathon.participants.all %}
<div class="enroll-hackathon-card">{% include 'hackathon/includes/enroll_card.html' %}</div>
{% with is_blocked=request.user|user_is_blocked:hackathon %}
{% if not is_blocked %}
<div class="enroll-hackathon-card">{% include 'hackathon/includes/enroll_card.html' %}</div>
{% endif %}
{% endwith %}
{% endif %}
{% endwith %}

Expand Down
17 changes: 17 additions & 0 deletions hackathon/templatetags/custom_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import re

from django.template import Library
from django.db.models import Q
import datetime
from hackathon.models import Hackathon

ANCHOR_PATTERN = r'href[=][\"]?.+?(?=[\"])[\"]'

Expand Down Expand Up @@ -132,3 +134,18 @@ def remove_hrefs(text):
TODO: Add functionality to only remove specific links and show others
"""
return re.sub(ANCHOR_PATTERN, '', text)


@register.filter
def user_is_blocked(user, hackathon):
""" Checks if the user is blocked from the this hackathon"""
if not user.dropped_off_hackathon:
return False
else:
orgs = [1]
orgs.append(user.organisation.id)
hackathons = Hackathon.objects.filter((Q(organisation__in=orgs) | Q(is_public=True)) & Q(end_date__lte=hackathon.end_date)).exclude(status__in=['deleted', 'draft']).order_by('-end_date')
if user.dropped_off_hackathon in hackathons[:2]:
return True
return False

85 changes: 62 additions & 23 deletions profiles/templates/profiles/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,45 @@
<div class="container">
<div class="row">
<div class="col-12 col-md-3">
<div class="card">
<div class="p-3">
<div class="card">
<div class="p-3">
{% if is_owner %}
<button class="btn btn-secondary btn-sm edit-image"
data-image-type="profile_image"
data-toggle="modal" data-target="#uploadImageModal">
<i class="fas fa-pen mr-2"></i>Edit</button>
{% endif %}
{% if user.profile_image %}
<img class="card-img-top profile-image" src="{{user.profile_image}}" alt="Profile image">
{% else %}
<!-- Greasemann, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons -->
<img class="card-img-top profile-image" src="{% static 'img/profiles/profile.png' %}" alt="Profile image">
{% endif %}
</div>
<div class="text-center">
{% if is_owner %}
<button class="btn btn-secondary btn-sm edit-image"
data-image-type="profile_image"
data-toggle="modal" data-target="#uploadImageModal">
<i class="fas fa-pen mr-2"></i>Edit</button>
<p class="mt-2">Welcome: <span class="dark-text">{{ user.slack_display_name }}</span></p>
<p><a class="btn btn-ci" href="{% url 'edit_profile' %}"><i class="fas fa-user-edit p-1"></i>Edit Profile</a></p>
{% if not slack_enabled %}
<p><a class="btn btn-ci" href="{% url 'account_change_password' %}"><i class="fas fa-key p-1"></i></i>Change Password</a></p>
{% endif %}
{% if user.profile_image %}
<img class="card-img-top profile-image" src="{{user.profile_image}}" alt="Profile image">
{% else %}
<!-- Greasemann, CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0>, via Wikimedia Commons -->
<img class="card-img-top profile-image" src="{% static 'img/profiles/profile.png' %}" alt="Profile image">
<p class="mt-2"><span class="dark-text">{{ user.slack_display_name }}</span></p>
{% endif %}
</div>
</div>
<div class="text-center">
{% if is_owner %}
<p class="mt-2">Welcome: <span class="dark-text">{{ user.slack_display_name }}</span></p>
<p><a class="btn btn-ci" href="{% url 'edit_profile' %}"><i class="fas fa-user-edit p-1"></i>Edit Profile</a></p>
{% if not slack_enabled %}
<p><a class="btn btn-ci" href="{% url 'account_change_password' %}"><i class="fas fa-key p-1"></i></i>Change Password</a></p>
{% endif %}
{% else %}
<p class="mt-2"><span class="dark-text">{{ user.slack_display_name }}</span></p>
{% endif %}
<div class="card mt-3">
<div class="p-3">
<label class="w-100">Hackathon Badges</label>
{% for team in user.get_participated_teams %}
{% if team.hackathon.badge_url %}

<img class="card-img-top profile-image hover_badge mini-badge" src="{{ team.hackathon.badge_url }}" alt="Hackathon badge" style="height: 75px; width: 75px;" data-toggle="modal" data-target="#badgeModal" data-badge-url="{{ team.hackathon.badge_url }}">
{% endif %}
{% endfor %}
</div>
</div>
</div>
</div>
<!-- User Information -->
<div class="col-12 col-md-9">
<div class="card">
Expand Down Expand Up @@ -107,14 +118,26 @@
{% else %}
<p id="userHackathons">You haven't participated in any hackathons yet. Why not sign up for the next one?</p>
{% endif %}

<label for="dropoffs">Hackathon Drop offs</label>
<p id="dropoffs">
{% if user.dropoffs %}
{{ user.dropoffs }}
{% if user.dropoffs >= 1 %}
- <em>Dropping off from a hackathon greatly impacts the team, we understand life happens but please try to only register if you can commit the minimum 3 - 5 hours per day.</em>
{% endif %}
{% else %}
Thank you for not dropping off. Keep up the good work!
{% endif %}
</p>
<label for="website">Website</label>
<p id="website" class="profile-website-url">
{% if user.website_url %}
<a href="{{ user.website_url }}" target="_blank" class="profile-url">
{{ user.website_url }}
</a>
{% else %}N/A{% endif %}
{% else %}
N/A
{% endif %}
</p>

{% if is_owner or request.user.user_type|is_types:authorised_types or user.email_is_public %}
Expand All @@ -140,6 +163,22 @@
</div>
</div>

<!-- Modal for displaying enlarged badge -->
<div class="modal fade" id="badgeModal" tabindex="-1" role="dialog" aria-labelledby="badgeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body text-center">
<img id="badgeModalImage" src="" alt="Hackathon badge" class="img-fluid">
</div>
</div>
</div>
</div>

{% include 'upload_image.html' %}

{% endblock %}
Expand Down
Loading