From 6f7bb4652ad58775275eafa487cfd6e50fe3815d Mon Sep 17 00:00:00 2001 From: enric4000 Date: Fri, 6 Feb 2026 21:17:32 +0100 Subject: [PATCH 01/23] Edited Hackathon variables to be up to date with server, opened mentor DeadLine and fixed html header --- app/hackathon_variables.py | 105 +++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 45 deletions(-) diff --git a/app/hackathon_variables.py b/app/hackathon_variables.py index 493b10f8b..66f089726 100644 --- a/app/hackathon_variables.py +++ b/app/hackathon_variables.py @@ -4,61 +4,72 @@ from django.utils import timezone -HACKATHON_NAME = 'HackUPC' +HACKATHON_NAME = "HackUPC" # What's the name for the application -HACKATHON_APPLICATION_NAME = 'My HackUPC' +HACKATHON_APPLICATION_NAME = "My HackUPC" # Hackathon timezone -TIME_ZONE = 'CET' +TIME_ZONE = "CET" # This description will be used on the html and sharing meta tags -HACKATHON_DESCRIPTION = 'Join us for BarcelonaTech\'s hackathon. 36h. May 3 - 5.' +HACKATHON_DESCRIPTION = "Join us for BarcelonaTech's hackathon. 36h. April 24 - 26." # Domain where application is deployed, can be set by env variable -HACKATHON_DOMAIN = os.environ.get('DOMAIN', None) -HEROKU_APP_NAME = os.environ.get('HEROKU_APP_NAME', None) +HACKATHON_DOMAIN = os.environ.get("DOMAIN", None) +HEROKU_APP_NAME = os.environ.get("HEROKU_APP_NAME", None) if HEROKU_APP_NAME and not HACKATHON_DOMAIN: - HACKATHON_DOMAIN = '%s.herokuapp.com' % HEROKU_APP_NAME + HACKATHON_DOMAIN = "%s.herokuapp.com" % HEROKU_APP_NAME elif not HACKATHON_DOMAIN: - HACKATHON_DOMAIN = 'localhost:8000' + HACKATHON_DOMAIN = "localhost:8000" # Hackathon contact email: where should all hackers contact you. It will also be used as a sender for all emails -HACKATHON_CONTACT_EMAIL = 'contact@hackupc.com' +HACKATHON_CONTACT_EMAIL = "contact@hackupc.com" # Hackathon logo url, will be used on all emails -HACKATHON_LOGO_URL = 'https://my.hackupc.com/static/logo.png' +HACKATHON_LOGO_URL = "https://my.hackupc.com/static/logo.png" -HACKATHON_OG_IMAGE = 'https://hackupc.com/ogimage.png?v=2021' +HACKATHON_OG_IMAGE = "https://hackupc.com/ogimage.png?v=2021" # (OPTIONAL) Track visits on your website -HACKATHON_GOOGLE_ANALYTICS = 'UA-69542332-2' +HACKATHON_GOOGLE_ANALYTICS = "UA-69542332-2" # (OPTIONAL) Hackathon Twitter user -HACKATHON_TWITTER_ACCOUNT = 'hackupc' +HACKATHON_TWITTER_ACCOUNT = "hackupc" # (OPTIONAL) Hackathon Facebook page -HACKATHON_FACEBOOK_PAGE = 'hackupc' +HACKATHON_FACEBOOK_PAGE = "hackupc" # (OPTIONAL) Hackathon YouTube channel -HACKATHON_YOUTUBE_PAGE = 'UCiiRorGg59Xd5Sjj9bjIt-g' +HACKATHON_YOUTUBE_PAGE = "UCiiRorGg59Xd5Sjj9bjIt-g" # (OPTIONAL) Hackathon Instagram user -HACKATHON_INSTAGRAM_ACCOUNT = 'hackupc' +HACKATHON_INSTAGRAM_ACCOUNT = "hackupc" # (OPTIONAL) Hackathon Medium user -HACKATHON_MEDIUM_ACCOUNT = 'hackupc' +HACKATHON_MEDIUM_ACCOUNT = "hackupc" # (OPTIONAL) Github Repo for this project (so meta) -HACKATHON_GITHUB_REPO = 'https://github.com/hackupc/myhackupc/' +HACKATHON_GITHUB_REPO = "https://github.com/hackupc/myhackupc/" # (OPTIONAL) Applications deadline -HACKATHON_APP_DEADLINE = timezone.datetime(2030, 4, 24, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)) -VOLUNTEER_APP_DEADLINE = timezone.datetime(2030, 4, 18, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)) -MENTOR_APP_DEADLINE = timezone.datetime(2030, 3, 25, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)) +HACKATHON_APP_DEADLINE = timezone.datetime( + 2026, 4, 1, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) +VOLUNTEER_APP_DEADLINE = timezone.datetime( + 2026, 3, 27, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) +MENTOR_APP_DEADLINE = timezone.datetime( + 2026, 3, 27, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) + # (OPTIONAL) Online checkin activated -ONLINE_CHECKIN = timezone.datetime(2020, 5, 3, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)) +ONLINE_CHECKIN = timezone.datetime( + 2020, 5, 3, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) # (OPTIONAL) When to arrive at the hackathon -HACKATHON_ARRIVE = '' +HACKATHON_ARRIVE = "" # (OPTIONAL) When to arrive at the hackathon -HACKATHON_LEAVE = '' +HACKATHON_LEAVE = "" # (OPTIONAL) Hackathon live page -HACKATHON_LIVE_PAGE = 'https://live.hackupc.com' +HACKATHON_LIVE_PAGE = "https://live.hackupc.com" # (OPTIONAL) Regex to automatically match organizers emails and set them as organizers when signing up -REGEX_HACKATHON_ORGANIZER_EMAIL = '^.*@hackupc\.com$' +REGEX_HACKATHON_ORGANIZER_EMAIL = "^.*@hackupc\.com$" # (OPTIONAL) Send 500 errors to email while on production mode -HACKATHON_DEV_EMAILS = ['devs@hackupc.com', ] +HACKATHON_DEV_EMAILS = [ + "devs@hackupc.com", +] # Baggage configuration BAGGAGE_ENABLED = True @@ -67,10 +78,14 @@ # Reimbursement configuration REIMBURSEMENT_ENABLED = True DEFAULT_REIMBURSEMENT_AMOUNT = 100 -CURRENCY = '€' -REIMBURSEMENT_EXPIRY_DATE = timezone.datetime(2025, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)) -REIMBURSEMENT_REQUIREMENTS = 'You have to submit a project and demo it during the event in order to get reimbursed' -REIMBURSEMENT_DEADLINE = timezone.datetime(2025, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)) +CURRENCY = "€" +REIMBURSEMENT_EXPIRY_DATE = timezone.datetime( + 2025, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) +REIMBURSEMENT_REQUIREMENTS = "You have to submit a project and demo it during the event in order to get reimbursed" +REIMBURSEMENT_DEADLINE = timezone.datetime( + 2025, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) # (OPTIONAL) Max team members. Defaults to 4 TEAMS_ENABLED = True @@ -82,9 +97,9 @@ # (OPTIONAL) Slack credentials # Highly recommended to create a separate user account to extract the token from SLACK = { - 'team': os.environ.get('SL_TEAM', 'test'), + "team": os.environ.get("SL_TEAM", "test"), # Get it here: https://api.slack.com/custom-integrations/legacy-tokens - 'token': os.environ.get('SL_TOKEN', None) + "token": os.environ.get("SL_TOKEN", None), } # (OPTIONAL) Logged in cookie @@ -95,16 +110,16 @@ # Hardware configuration # Hardware request time length (in minutes) HARDWARE_ENABLED = True -#Hardware request time length (in minutes) +# Hardware request time length (in minutes) HARDWARE_REQUEST_TIME = 15 SLACK_BOT = { - 'id': os.environ.get('SL_BOT_ID', None), - 'token': os.environ.get('SL_BOT_TOKEN', None), - 'channel': os.environ.get('SL_BOT_CHANNEL', None), - 'director1': os.environ.get('SL_BOT_DIRECTOR1', None), - 'director2': os.environ.get('SL_BOT_DIRECTOR2', None) + "id": os.environ.get("SL_BOT_ID", None), + "token": os.environ.get("SL_BOT_TOKEN", None), + "channel": os.environ.get("SL_BOT_CHANNEL", None), + "director1": os.environ.get("SL_BOT_DIRECTOR1", None), + "director2": os.environ.get("SL_BOT_DIRECTOR2", None), } # Enable judging tab JUDGING_ENABLED = False @@ -119,7 +134,7 @@ # Enable blacklist separate pipeline (disabled by default) BLACKLIST_ENABLED = True -SUPPORTED_RESUME_EXTENSIONS = ['.pdf'] +SUPPORTED_RESUME_EXTENSIONS = [".pdf"] # Mentor/Volunteer applications can expire if they are invited, set to False to not MENTOR_EXPIRES = False @@ -129,9 +144,9 @@ HYBRID_HACKATHON = False N_MAX_LIVE_HACKERS = 600 -SERVER_EMAIL = 'HackUPC Team ' +SERVER_EMAIL = "HackUPC Team " -CODE_CONDUCT_LINK = 'https://legal.hackersatupc.org/hackupc/code_of_conduct' -LEGAL_LINK = 'https://legal.hackersatupc.org/hackupc/legal_notice' -PRIVACY_LINK = 'https://legal.hackersatupc.org/hackupc/privacy_and_cookies' -TERMS_LINK = 'https://legal.hackersatupc.org/hackupc/terms_and_conditions' +CODE_CONDUCT_LINK = "https://legal.hackersatupc.org/hackupc/code_of_conduct" +LEGAL_LINK = "https://legal.hackersatupc.org/hackupc/legal_notice" +PRIVACY_LINK = "https://legal.hackersatupc.org/hackupc/privacy_and_cookies" +TERMS_LINK = "https://legal.hackersatupc.org/hackupc/terms_and_conditions" From 5a110eaa1e120b3e0a06ad0d1ae01caed55469c5 Mon Sep 17 00:00:00 2001 From: enric4000 Date: Mon, 9 Feb 2026 19:06:56 +0100 Subject: [PATCH 02/23] Made Volunteer pages readOnly --- app/templates/base.html | 6 +- .../templates/other_application_detail.html | 71 ++++++++++--------- organizers/views.py | 40 ++++++----- 3 files changed, 60 insertions(+), 57 deletions(-) diff --git a/app/templates/base.html b/app/templates/base.html index 1a6d4b2be..de0228562 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -111,10 +111,8 @@ {% endif %} {% if request.user.is_organizer %} - {% if request.user.has_volunteer_access %} -
  • Volunteer
  • - {% endif %} +
  • Volunteer
  • {% if request.user.has_mentor_access %}
  • Mentor
  • diff --git a/organizers/templates/other_application_detail.html b/organizers/templates/other_application_detail.html index 8a2bf232b..0978fe018 100644 --- a/organizers/templates/other_application_detail.html +++ b/organizers/templates/other_application_detail.html @@ -20,7 +20,7 @@

    Personal

    {% include 'include/field.html' with desc='Name' value=app.name %} {% elif app.user.is_volunteer %} {% include 'include/field.html' with desc='Name' value=app.user.name %} - {% include 'include/field.html' with desc='Is over 18?' value=app.under_age|yesno %} + {% include 'include/field.html' with desc='Is underage?' value=app.under_age|yesno %} {% include 'include/field.html' with desc='Studies and course/graduation' value=app.studies_and_course %} {% else %} {% include 'include/field.html' with desc='Name' value=app.user.name %} @@ -35,7 +35,7 @@

    Personal

    {% include 'include/field.html' with desc='Gender' value=app.get_gender_display %} {% include 'include/field.html' with desc='Other gender' value=app.other_gender %} {% include 'include/field.html' with desc='In BCN Apr-May' value=app.lennyface|yesno %} -
    +

    Validation

    @@ -45,16 +45,18 @@

    Validation

    {% csrf_token %} - + {% if user.has_volunteer_access %} + + {% endif %}

    -
    +

    Volunteering

    @@ -188,7 +190,7 @@

    {{ comment.text }}

    {% csrf_token %} + rows="3" required maxlength="500"> @@ -198,28 +200,29 @@

    {{ comment.text }}

    {% block out_panel %} {% if app and not app.user.is_sponsor %} - - + {% if app and app.user.is_volunteer and not user.has_volunteer_access %} + {% else %} + + {% endif %} {% endif %} {% endblock %} diff --git a/organizers/views.py b/organizers/views.py index 01c6a18d0..723c2377b 100644 --- a/organizers/views.py +++ b/organizers/views.py @@ -85,7 +85,9 @@ def add_vote(application, user, tech_rat, pers_rat): v.save() votes_count = application.vote_set.count() if votes_count >= 5 and not application.cv_flagged: - AcceptedResume.objects.update_or_create(application=application, defaults={'accepted': True}) + AcceptedResume.objects.update_or_create( + application=application, defaults={"accepted": True} + ) return v @@ -359,12 +361,12 @@ def post(self, request, *args, **kwargs): id_ = request.POST.get("app_id") application = models.HackerApplication.objects.get(pk=id_) - comment_text = request.POST.get('comment_text', None) - motive_of_ban = request.POST.get('motive_of_ban', None) - dubious_type = request.POST.get('dubious_type', None) - dubious_comment_text = request.POST.get('dubious_comment_text', None) + comment_text = request.POST.get("comment_text", None) + motive_of_ban = request.POST.get("motive_of_ban", None) + dubious_type = request.POST.get("dubious_type", None) + dubious_comment_text = request.POST.get("dubious_comment_text", None) - if request.POST.get('add_comment'): + if request.POST.get("add_comment"): add_comment(application, request.user, comment_text) elif request.POST.get("invite") and request.user.is_director: self.invite_application(application) @@ -376,7 +378,7 @@ def post(self, request, *args, **kwargs): self.waitlist_application(application) elif request.POST.get("slack") and request.user.is_organizer: self.slack_invite(application) - elif request.POST.get('set_dubious') and request.user.is_organizer: + elif request.POST.get("set_dubious") and request.user.is_organizer: application.set_dubious(request.user, dubious_type, dubious_comment_text) elif request.POST.get("set_flagged_cv") and request.user.is_organizer: application.set_flagged_cv() @@ -510,8 +512,8 @@ def post(self, request, *args, **kwargs): tech_vote = request.POST.get("tech_rat", None) pers_vote = request.POST.get("pers_rat", None) comment_text = request.POST.get("comment_text", None) - dubious_type = request.POST.get('dubious_type', None) - dubious_comment_text = request.POST.get('dubious_comment_text', None) + dubious_type = request.POST.get("dubious_type", None) + dubious_comment_text = request.POST.get("dubious_comment_text", None) application = models.HackerApplication.objects.get( pk=request.POST.get("app_id") @@ -525,7 +527,9 @@ def post(self, request, *args, **kwargs): "/applications/hacker/review/" + application.uuid_str ) elif request.POST.get("set_dubious"): - application.set_dubious(request.user, dubious_type, dubious_comment_text) + application.set_dubious( + request.user, dubious_type, dubious_comment_text + ) elif request.POST.get("unset_dubious"): application.unset_dubious() elif request.POST.get("set_flagged_cv") and request.user.is_organizer: @@ -615,8 +619,8 @@ def post(self, request, *args, **kwargs): tech_vote = request.POST.get("tech_rat", None) pers_vote = request.POST.get("pers_rat", None) comment_text = request.POST.get("comment_text", None) - dubious_type = request.POST.get('dubious_type', None) - dubious_comment_text = request.POST.get('dubious_comment_text', None) + dubious_type = request.POST.get("dubious_type", None) + dubious_comment_text = request.POST.get("dubious_comment_text", None) application = models.HackerApplication.objects.get( pk=request.POST.get("app_id") @@ -630,7 +634,9 @@ def post(self, request, *args, **kwargs): "/applications/hacker/review/" + application.uuid_str ) elif request.POST.get("set_dubious"): - application.set_dubious(request.user, dubious_type, dubious_comment_text) + application.set_dubious( + request.user, dubious_type, dubious_comment_text + ) elif request.POST.get("unset_dubious"): application.unset_dubious() elif request.POST.get("set_flagged_cv") and request.user.is_organizer: @@ -846,9 +852,7 @@ def get_context_data(self, **kwargs): return context -class VolunteerApplicationsListView( - HaveVolunteerPermissionMixin, _OtherApplicationsListView -): +class VolunteerApplicationsListView(IsOrganizerMixin, _OtherApplicationsListView): table_class = VolunteerListTable filterset_class = VolunteerFilter @@ -912,9 +916,7 @@ def get_current_tabs(self): return mentor_tabs(self.request.user) -class ReviewVolunteerApplicationView( - TabsViewMixin, HaveVolunteerPermissionMixin, TemplateView -): +class ReviewVolunteerApplicationView(IsOrganizerMixin, TabsViewMixin, TemplateView): template_name = "other_application_detail.html" def get_application(self, kwargs): From dfc20d1ec97c6b8c3cbcd7777997ede0cd4680a8 Mon Sep 17 00:00:00 2001 From: enric4000 Date: Mon, 9 Feb 2026 19:09:15 +0100 Subject: [PATCH 03/23] Removed unused variable --- organizers/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/organizers/views.py b/organizers/views.py index 723c2377b..84e4ff239 100644 --- a/organizers/views.py +++ b/organizers/views.py @@ -61,7 +61,6 @@ IsOrganizerMixin, IsDirectorMixin, HaveDubiousPermissionMixin, - HaveVolunteerPermissionMixin, HaveSponsorPermissionMixin, HaveMentorPermissionMixin, IsBlacklistAdminMixin, From 595974760d66752b743ec828a2260a9a64519477 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 12:51:02 +0100 Subject: [PATCH 04/23] mails --- .../mails/devpost_upload_message.html | 21 +++++++++++++ .../mails/devpost_upload_subject.txt | 1 + .../mails/reject_receipt_message.html | 2 +- .../mails/ticket_accepted_message.html | 31 +++++++++++++++++++ .../mails/ticket_accepted_subject.txt | 1 + .../mails/travel_tickets_upload_message.html | 22 +++++++++++++ .../mails/travel_tickets_upload_subject.txt | 1 + 7 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 reimbursement/templates/mails/devpost_upload_message.html create mode 100644 reimbursement/templates/mails/devpost_upload_subject.txt create mode 100644 reimbursement/templates/mails/ticket_accepted_message.html create mode 100644 reimbursement/templates/mails/ticket_accepted_subject.txt create mode 100644 reimbursement/templates/mails/travel_tickets_upload_message.html create mode 100644 reimbursement/templates/mails/travel_tickets_upload_subject.txt diff --git a/reimbursement/templates/mails/devpost_upload_message.html b/reimbursement/templates/mails/devpost_upload_message.html new file mode 100644 index 000000000..da62d5e7e --- /dev/null +++ b/reimbursement/templates/mails/devpost_upload_message.html @@ -0,0 +1,21 @@ +{% extends 'base_email.html' %} +{% block preheader %}Upload your Devpost link{% endblock %} + +{% block content %} +

    Hey {{ app.user.get_full_name }},

    +
    +

    HackUPC has officially come to an end, and we hope you enjoyed the event as much as we did!

    +

    The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us + to validate your submission and verify that the project is valid and that it complies with the requirements that + were previously communicated.

    + +

    Upload your link here: https://my.hackupc.com/reimbursement/dash_board/. +

    + +{% include 'mails/include/email_button.html' with text='Upload Devpost link' url=form_url %} + +

    Thank you for your collaboration!!

    + +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} \ No newline at end of file diff --git a/reimbursement/templates/mails/devpost_upload_subject.txt b/reimbursement/templates/mails/devpost_upload_subject.txt new file mode 100644 index 000000000..f99021acc --- /dev/null +++ b/reimbursement/templates/mails/devpost_upload_subject.txt @@ -0,0 +1 @@ +[ACTION REQUIRED] Upload your Devpost link diff --git a/reimbursement/templates/mails/reject_receipt_message.html b/reimbursement/templates/mails/reject_receipt_message.html index b5461b9a0..066c62e08 100644 --- a/reimbursement/templates/mails/reject_receipt_message.html +++ b/reimbursement/templates/mails/reject_receipt_message.html @@ -5,7 +5,7 @@

    Hey {{ app.user.get_full_name }},


    The receipt you submitted is invalid.

    -

    Comment on receipt submitted: {{ reimb.public_comment }}

    +

    Rejected reason: {{ reimb.public_comment }}

    {% include 'mails/include/email_button.html' with text='Submit receipt' url=form_url %}

    diff --git a/reimbursement/templates/mails/ticket_accepted_message.html b/reimbursement/templates/mails/ticket_accepted_message.html new file mode 100644 index 000000000..12d2f87d7 --- /dev/null +++ b/reimbursement/templates/mails/ticket_accepted_message.html @@ -0,0 +1,31 @@ +{% extends 'base_email.html' %} +{% block preheader %}Your ticket has been accepted{% endblock %} + +{% block content %} +

    Hey {{ app.user.get_full_name }},

    +
    +

    If you are receiving this mail is because your travel ticket has been accepted.

    +

    However, the process requires you to follow the below instructions during the event to consider you eligible for a + reimbursement.

    + +

    Regarding the project:

    +
      +
    • The participant must demo a valid project to the HackUPC judges.
    • +
    • The Devpost username must be similar to the participant’s real name. Usernames that do not reasonably resemble + the real name (e.g. TheBest) will not be accepted.
    • +
    • The completeness of the GitHub repository will be taken into account, including aspects such as regular commits + throughout the weekend.
    • +
    + +

    Regarding the attendance:

    +
      +
    • It must be demonstrable that the participant attended the event and stayed at the venue for a minimum period of + participation. Attendance to meals, activities, workshops, and other official event activities may be taken into + account to verify participation.
    • +
    + +

    The money will be transferred to the participant using PayPal in the following 60 days after the event. The paypal + account MUST exist.

    + +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} \ No newline at end of file diff --git a/reimbursement/templates/mails/ticket_accepted_subject.txt b/reimbursement/templates/mails/ticket_accepted_subject.txt new file mode 100644 index 000000000..4498c9a44 --- /dev/null +++ b/reimbursement/templates/mails/ticket_accepted_subject.txt @@ -0,0 +1 @@ +[IMPORTANT INFORMATION ] Your ticket has been accepted diff --git a/reimbursement/templates/mails/travel_tickets_upload_message.html b/reimbursement/templates/mails/travel_tickets_upload_message.html new file mode 100644 index 000000000..3b84a1383 --- /dev/null +++ b/reimbursement/templates/mails/travel_tickets_upload_message.html @@ -0,0 +1,22 @@ +{% extends 'base_email.html' %} +{% block preheader %}Upload your travel tickets{% endblock %} + +{% block content %} +

    Hey {{ app.user.get_full_name }},

    +
    +

    For us to accept your travel reimbursement request, you need to upload VALID travel tickets in https://my.hackupc.com/reimbursement/dash_board/. +

    +

    A ticket is considered valid if and only if:

    +
      +
    • The receipts are in the name of the participant requesting the reimbursement.
    • +
    • It includes a detailed price breakdown, the purchase date, and the travel dates, clearly indicating which costs + correspond to the travel itself and which relate to additional services (e.g. extra luggage).
    • +
    • The travel dates are within one week before or one week after the event.
    • +
    • The ticket is uploaded before the start of the event.
    • +
    + +{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} + +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} \ No newline at end of file diff --git a/reimbursement/templates/mails/travel_tickets_upload_subject.txt b/reimbursement/templates/mails/travel_tickets_upload_subject.txt new file mode 100644 index 000000000..b96ef8343 --- /dev/null +++ b/reimbursement/templates/mails/travel_tickets_upload_subject.txt @@ -0,0 +1 @@ +[ACTION REQUIRED] Upload your travel tickets From a2e8ebb0e85ea2835e290028fabf7ffa4584a78b Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 18:43:16 +0100 Subject: [PATCH 05/23] mails autoamtics --- app/hackathon_variables.py | 9 +++- applications/views.py | 6 +++ cronjob | 2 + reimbursement/emails.py | 49 ++++++++++++++++--- .../commands/send_devpost_emails.py | 38 ++++++++++++++ .../migrations/0015_auto_20260219_1731.py | 30 ++++++++++++ reimbursement/models.py | 8 ++- reimbursement/views.py | 4 +- 8 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 reimbursement/management/commands/send_devpost_emails.py create mode 100644 reimbursement/migrations/0015_auto_20260219_1731.py diff --git a/app/hackathon_variables.py b/app/hackathon_variables.py index 66f089726..92a280615 100644 --- a/app/hackathon_variables.py +++ b/app/hackathon_variables.py @@ -60,6 +60,11 @@ # (OPTIONAL) When to arrive at the hackathon HACKATHON_LEAVE = "" +# (OPTIONAL) When the event ends (to send Devpost link emails) +HACKATHON_EVENT_END = timezone.datetime( + 2026, 4, 26, 20, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) +) + # (OPTIONAL) Hackathon live page HACKATHON_LIVE_PAGE = "https://live.hackupc.com" @@ -80,11 +85,11 @@ DEFAULT_REIMBURSEMENT_AMOUNT = 100 CURRENCY = "€" REIMBURSEMENT_EXPIRY_DATE = timezone.datetime( - 2025, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) + 2026, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) ) REIMBURSEMENT_REQUIREMENTS = "You have to submit a project and demo it during the event in order to get reimbursed" REIMBURSEMENT_DEADLINE = timezone.datetime( - 2025, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) + 2026, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) ) # (OPTIONAL) Max team members. Defaults to 4 diff --git a/applications/views.py b/applications/views.py index b61dd5936..63877e8af 100644 --- a/applications/views.py +++ b/applications/views.py @@ -80,6 +80,12 @@ def get(self, request, *args, **kwargs): if msg: msg.send() + if application.user.is_hacker() and application.reimb: + from reimbursement import emails as reimb_emails + reimb_msg = reimb_emails.create_travel_tickets_upload_email( + application.user.reimbursement, request + ) + reimb_msg.send() try: slack.send_slack_invite(request.user.email) # Ignore if we can't send, it's only optional diff --git a/cronjob b/cronjob index eb4a12213..9e7e140db 100644 --- a/cronjob +++ b/cronjob @@ -2,3 +2,5 @@ 6 */2 * * * root /usr/local/bin/python /code/manage.py expire_applications # Run expire reimbursements job at 6 minutes past the hour, every 2 hours 36 */2 * * * root /usr/local/bin/python /code/manage.py expire_reimbursements +# Run send devpost emails job every Sunday at 20:00 +0 20 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails diff --git a/reimbursement/emails.py b/reimbursement/emails.py index 8a2dd1055..59085cc7a 100644 --- a/reimbursement/emails.py +++ b/reimbursement/emails.py @@ -25,15 +25,50 @@ def create_no_reimbursement_email(reimb, request): return emails.render_mail("mails/no_reimbursement", reimb.hacker.email, c) +def create_travel_tickets_upload_email(reimb, request): + app = reimb.hacker.application + c = _get_context(app, reimb, request) + return emails.render_mail( + "mails/travel_tickets_upload", reimb.hacker.email, c, action_required=True + ) + + +def create_ticket_accepted_email(reimb, request): + app = reimb.hacker.application + c = _get_context(app, reimb, request) + return emails.render_mail("mails/ticket_accepted", reimb.hacker.email, c) + + +def create_devpost_upload_email(reimb, request): + app = reimb.hacker.application + c = _get_context(app, reimb, request) + return emails.render_mail( + "mails/devpost_upload", reimb.hacker.email, c, action_required=True + ) + + +def create_project_invalidated_email(reimb, request): + app = reimb.hacker.application + c = _get_context(app, reimb, request) + return emails.render_mail("mails/project_invalidated", reimb.hacker.email, c) + + def _get_context(app, reimb, request): + from django.conf import settings + + confirm_url = reverse("confirm_app", kwargs={"id": app.uuid_str}, request=request) + form_url = reverse("reimbursement_dashboard", request=request) + cancel_url = reverse("cancel_app", kwargs={"id": app.uuid_str}, request=request) + if not request: + base_url = "https://" + settings.HACKATHON_DOMAIN + confirm_url = base_url + confirm_url + form_url = base_url + form_url + cancel_url = base_url + cancel_url + return { "app": app, "reimb": reimb, - "confirm_url": str( - reverse("confirm_app", kwargs={"id": app.uuid_str}, request=request) - ), - "form_url": str(reverse("reimbursement_dashboard", request=request)), - "cancel_url": str( - reverse("cancel_app", kwargs={"id": app.uuid_str}, request=request) - ), + "confirm_url": str(confirm_url), + "form_url": str(form_url), + "cancel_url": str(cancel_url), } diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py new file mode 100644 index 000000000..dd66def47 --- /dev/null +++ b/reimbursement/management/commands/send_devpost_emails.py @@ -0,0 +1,38 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone +from django.conf import settings +from reimbursement.models import Reimbursement, RE_APPROVED +from reimbursement import emails + +class Command(BaseCommand): + help = 'Sends automatic emails to upload Devpost link to approved reimbursements' + + def handle(self, *args, **options): + # Check if the event has ended + if timezone.now() < settings.HACKATHON_EVENT_END: + self.stdout.write(self.style.WARNING('Event has not ended yet. Skipping Devpost emails.')) + return + + # We target reimbursements that have the receipt approved (RE_APPROVED), + # hasn't uploaded a devpost link yet (devpost=''), + # and we haven't sent the reminder email yet (devpost_email_sent=False). + reimbursements = Reimbursement.objects.filter( + status=RE_APPROVED, + devpost='', + devpost_email_sent=False + ) + + self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.') + + sent_count = 0 + for reimb in reimbursements: + try: + msg = emails.create_devpost_upload_email(reimb, None) + msg.send() + reimb.devpost_email_sent = True + reimb.save() + sent_count += 1 + except Exception as e: + self.stderr.write(f'Error sending email to {reimb.hacker.email}: {str(e)}') + + self.stdout.write(self.style.SUCCESS(f'Successfully sent {sent_count} emails.')) diff --git a/reimbursement/migrations/0015_auto_20260219_1731.py b/reimbursement/migrations/0015_auto_20260219_1731.py new file mode 100644 index 000000000..b8ddb4405 --- /dev/null +++ b/reimbursement/migrations/0015_auto_20260219_1731.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2.23 on 2026-02-19 17:31 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('reimbursement', '0014_alter_reimbursement_expiration_time'), + ] + + operations = [ + migrations.AddField( + model_name='reimbursement', + name='devpost_email_sent', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='reimbursement', + name='expiration_time', + field=models.DateTimeField(default=datetime.datetime(2026, 5, 2, 16, 0, tzinfo=utc)), + ), + migrations.AlterField( + model_name='reimbursement', + name='status', + field=models.CharField(choices=[('W', 'Wait listed'), ('PT', 'Pending receipt submission'), ('PA', 'Pending receipt approval'), ('A', 'Receipt approved'), ('X', 'Expired'), ('FS', 'Friend submission'), ('F', 'Reimbursement Approved'), ('PD', 'Pending demo validation'), ('I', 'Invalid')], default='PT', max_length=2), + ), + ] diff --git a/reimbursement/models.py b/reimbursement/models.py index c89fa6575..0fd238db8 100644 --- a/reimbursement/models.py +++ b/reimbursement/models.py @@ -19,6 +19,7 @@ RE_FINALIZED = "F" RE_EXPIRED = "X" RE_FRIEND_SUBMISSION = "FS" +RE_INVALID = "I" RE_STATUS = [ (RE_WAITLISTED, "Wait listed"), @@ -29,6 +30,7 @@ (RE_FRIEND_SUBMISSION, "Friend submission"), (RE_FINALIZED, "Reimbursement Approved"), (RE_PEND_DEMO_VAL, "Pending demo validation"), + (RE_INVALID, "Invalid"), ] @@ -65,6 +67,7 @@ class Reimbursement(models.Model): public_comment = models.CharField(max_length=300, null=True, blank=True) devpost = models.URLField(blank=True, null=True, default="") + devpost_email_sent = models.BooleanField(default=False) # User controlled paypal_email = models.EmailField(null=True, blank=True) @@ -157,9 +160,10 @@ def validate(self, user): self.reimbursed_by = user self.save() - def invalidate(self, user): + def invalidate(self, user, reason): if self.status == RE_PEND_DEMO_VAL: - self.status = RE_WAITLISTED + self.status = RE_INVALID + self.public_comment = reason self.status_update_date = timezone.now() self.reimbursed_by = user self.save() diff --git a/reimbursement/views.py b/reimbursement/views.py index 993e93357..890c32f32 100644 --- a/reimbursement/views.py +++ b/reimbursement/views.py @@ -170,7 +170,8 @@ def post(self, request, *args, **kwargs): elif "invalidate" in request.POST: id_ = kwargs.get("id", None) reimb = models.Reimbursement.objects.get(pk=id_) - reimb.invalidate(request.user) + reimb.invalidate(request.user, request.POST.get("invalidate_reason")) + emails.create_project_invalidated_email(reimb, request).send() messages.success(request, "Reimbursement invalidated") else: id_ = request.POST.get("id", None) @@ -183,6 +184,7 @@ def post(self, request, *args, **kwargs): if a_form.is_valid(): a_form.save(commit=False) a_form.instance.accept_receipt(request.user) + emails.create_ticket_accepted_email(a_form.instance, request).send() a_form.save() messages.success(request, "Receipt accepted") else: From 81dba97b04504490ec2f3ed542bc4aa3c2af838f Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 19:56:47 +0100 Subject: [PATCH 06/23] fix --- reimbursement/management/commands/send_devpost_emails.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py index dd66def47..12c7b101c 100644 --- a/reimbursement/management/commands/send_devpost_emails.py +++ b/reimbursement/management/commands/send_devpost_emails.py @@ -4,6 +4,7 @@ from reimbursement.models import Reimbursement, RE_APPROVED from reimbursement import emails + class Command(BaseCommand): help = 'Sends automatic emails to upload Devpost link to approved reimbursements' @@ -21,9 +22,9 @@ def handle(self, *args, **options): devpost='', devpost_email_sent=False ) - + self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.') - + sent_count = 0 for reimb in reimbursements: try: @@ -34,5 +35,5 @@ def handle(self, *args, **options): sent_count += 1 except Exception as e: self.stderr.write(f'Error sending email to {reimb.hacker.email}: {str(e)}') - + self.stdout.write(self.style.SUCCESS(f'Successfully sent {sent_count} emails.')) From f437349a5a14f1404002c6107d2e03d3f3b4dede Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 19:59:17 +0100 Subject: [PATCH 07/23] fix --- reimbursement/management/commands/send_devpost_emails.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py index 12c7b101c..2f24f6bd6 100644 --- a/reimbursement/management/commands/send_devpost_emails.py +++ b/reimbursement/management/commands/send_devpost_emails.py @@ -24,7 +24,7 @@ def handle(self, *args, **options): ) self.stdout.write(f'Found {reimbursements.count()} reimbursements pending Devpost link.') - + sent_count = 0 for reimb in reimbursements: try: From 9fba62db170cec5c830b00a503c82130280999bc Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 21:02:07 +0100 Subject: [PATCH 08/23] fix alguns errors --- reimbursement/templates/mails/project_invalidated_subject.txt | 1 + reimbursement/views.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 reimbursement/templates/mails/project_invalidated_subject.txt diff --git a/reimbursement/templates/mails/project_invalidated_subject.txt b/reimbursement/templates/mails/project_invalidated_subject.txt new file mode 100644 index 000000000..eafe9d603 --- /dev/null +++ b/reimbursement/templates/mails/project_invalidated_subject.txt @@ -0,0 +1 @@ +[IMPORTANT INFORMATION] Your reimbursement has been invalidated diff --git a/reimbursement/views.py b/reimbursement/views.py index 890c32f32..e1e847a1a 100644 --- a/reimbursement/views.py +++ b/reimbursement/views.py @@ -249,6 +249,7 @@ def post(self, request, *args, **kwargs): if a_form.is_valid(): a_form.save(commit=False) a_form.instance.accept_receipt(request.user) + emails.create_ticket_accepted_email(a_form.instance, request).send() a_form.save() messages.success(request, "Receipt accepted") else: From 2e608118b10a4788a41ad36e0dd630ccdb82efcc Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 19 Feb 2026 23:36:49 +0100 Subject: [PATCH 09/23] imatges wallet --- app/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/utils.py b/app/utils.py index bbee6463e..b0ce14b98 100644 --- a/app/utils.py +++ b/app/utils.py @@ -235,7 +235,7 @@ def generateGTicketUrl(qrValue: str): }, "logo": { "sourceUri": { - "uri": "https://i.ibb.co/b5TQV4md/hackupc2025.png", + "uri": "https://i.ibb.co/g2GGdMB/Copy-of-Isotipo-B-6.png", }, "contentDescription": { "defaultValue": { @@ -253,7 +253,7 @@ def generateGTicketUrl(qrValue: str): "imageModulesData": [ { "mainImage": { - "sourceUri": {"uri": "https://hackupc.com/ogimage.png"}, + "sourceUri": {"uri": "https://hackupc.com/og_image2026.png"}, "contentDescription": { "defaultValue": {"language": "en-US", "value": "Event picture"} }, From d56573e19a68c49a7125a41c5a2ce2b820f31494 Mon Sep 17 00:00:00 2001 From: EncryptEx <41539618+EncryptEx@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:00:25 +0100 Subject: [PATCH 10/23] style: updated wallet card for 2026 edition --- app/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/utils.py b/app/utils.py index b0ce14b98..b5dd4fdda 100644 --- a/app/utils.py +++ b/app/utils.py @@ -300,10 +300,10 @@ def generateGTicketUrl(qrValue: str): }, "dateTime": {"start": "2026-04-24T16:00", "end": "2026-04-26T17:00"}, "reviewStatus": "UNDER_REVIEW", - "hexBackgroundColor": "#231F20", + "hexBackgroundColor": "#5B5340", "heroImage": { "sourceUri": { - "uri": "https://i.ibb.co/LL5T8d0/walletv2.png", + "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png", }, "contentDescription": { "defaultValue": { From 37e90dba7e3e45bf3364f78f49f660daf3c179c8 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Fri, 20 Feb 2026 13:02:21 +0100 Subject: [PATCH 11/23] wallet final --- app/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/utils.py b/app/utils.py index b0ce14b98..a356f9fdf 100644 --- a/app/utils.py +++ b/app/utils.py @@ -300,10 +300,10 @@ def generateGTicketUrl(qrValue: str): }, "dateTime": {"start": "2026-04-24T16:00", "end": "2026-04-26T17:00"}, "reviewStatus": "UNDER_REVIEW", - "hexBackgroundColor": "#231F20", + "hexBackgroundColor": "#AADEFE", "heroImage": { "sourceUri": { - "uri": "https://i.ibb.co/LL5T8d0/walletv2.png", + "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png", }, "contentDescription": { "defaultValue": { From e02e424144db0cd885d2a45676314d032184948e Mon Sep 17 00:00:00 2001 From: EncryptEx <41539618+EncryptEx@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:17:16 +0100 Subject: [PATCH 12/23] style: fix alt on banner --- app/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils.py b/app/utils.py index a356f9fdf..a686d9ee6 100644 --- a/app/utils.py +++ b/app/utils.py @@ -308,7 +308,7 @@ def generateGTicketUrl(qrValue: str): "contentDescription": { "defaultValue": { "language": "en-US", - "value": "HackUPC galactic banner", + "value": "HackUPC 2026 Banner", }, }, }, From 7758f3e681001d2b958f09a0c23fad4bc05e915f Mon Sep 17 00:00:00 2001 From: EncryptEx <41539618+EncryptEx@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:44:53 +0100 Subject: [PATCH 13/23] final version --- app/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/utils.py b/app/utils.py index a686d9ee6..d784b52cb 100644 --- a/app/utils.py +++ b/app/utils.py @@ -303,7 +303,7 @@ def generateGTicketUrl(qrValue: str): "hexBackgroundColor": "#AADEFE", "heroImage": { "sourceUri": { - "uri": "https://i.ibb.co/6Rkp3P6M/og-image2026.png", + "uri": "https://i.ibb.co/nN4GxfFt/imatge-wallet.png", }, "contentDescription": { "defaultValue": { From db59b85216a9808709cbd9f2038cae3fc5e9cea3 Mon Sep 17 00:00:00 2001 From: Yaoyao Date: Sun, 22 Feb 2026 13:04:43 +0100 Subject: [PATCH 14/23] fix punts --- applications/templates/include/application_form.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/templates/include/application_form.html b/applications/templates/include/application_form.html index 950b970c1..3f9d70b99 100644 --- a/applications/templates/include/application_form.html +++ b/applications/templates/include/application_form.html @@ -19,7 +19,7 @@

    ❗ Before filling out the application, read this article about - How To Do a Good Application.. + How To Do a Good Application.

    {% endif %} From 68d309ab8c9e71c99787fff4960fa210af9a3d24 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Wed, 25 Feb 2026 17:17:43 +0100 Subject: [PATCH 15/23] update hackaton_variables --- app/hackathon_variables.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/hackathon_variables.py b/app/hackathon_variables.py index 92a280615..c1e5d521b 100644 --- a/app/hackathon_variables.py +++ b/app/hackathon_variables.py @@ -84,13 +84,9 @@ REIMBURSEMENT_ENABLED = True DEFAULT_REIMBURSEMENT_AMOUNT = 100 CURRENCY = "€" -REIMBURSEMENT_EXPIRY_DATE = timezone.datetime( - 2026, 5, 2, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE) -) -REIMBURSEMENT_REQUIREMENTS = "You have to submit a project and demo it during the event in order to get reimbursed" -REIMBURSEMENT_DEADLINE = timezone.datetime( - 2026, 5, 5, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE) -) +REIMBURSEMENT_EXPIRY_DATE = timezone.datetime(2026, 4, 4, 17, 00, tzinfo=timezone.pytz.timezone(TIME_ZONE)) +REIMBURSEMENT_REQUIREMENTS = 'You have to submit a project and demo it during the event in order to get reimbursed' +REIMBURSEMENT_DEADLINE = timezone.datetime(2026, 4, 4, 23, 59, tzinfo=timezone.pytz.timezone(TIME_ZONE)) # (OPTIONAL) Max team members. Defaults to 4 TEAMS_ENABLED = True From 1f54ecd51f5f3e284244212c5155c886e950157c Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Wed, 25 Feb 2026 17:27:21 +0100 Subject: [PATCH 16/23] actualitzacions de plantilles --- .../mails/devpost_upload_message.html | 6 ++---- .../mails/devpost_upload_subject.txt | 2 +- .../mails/ticket_accepted_message.html | 12 +++++------ .../mails/travel_tickets_upload_message.html | 20 +++++++++---------- .../mails/travel_tickets_upload_subject.txt | 2 +- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/reimbursement/templates/mails/devpost_upload_message.html b/reimbursement/templates/mails/devpost_upload_message.html index da62d5e7e..f7d81aca1 100644 --- a/reimbursement/templates/mails/devpost_upload_message.html +++ b/reimbursement/templates/mails/devpost_upload_message.html @@ -5,13 +5,11 @@

    Hey {{ app.user.get_full_name }},


    HackUPC has officially come to an end, and we hope you enjoyed the event as much as we did!

    -

    The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us +

    The next step to get your travel reimbursement is to upload the link to your project on Devpost. This will allow us to validate your submission and verify that the project is valid and that it complies with the requirements that were previously communicated.

    -

    Upload your link here: https://my.hackupc.com/reimbursement/dash_board/. -

    +

    Upload your link here:

    {% include 'mails/include/email_button.html' with text='Upload Devpost link' url=form_url %} diff --git a/reimbursement/templates/mails/devpost_upload_subject.txt b/reimbursement/templates/mails/devpost_upload_subject.txt index f99021acc..991155d8f 100644 --- a/reimbursement/templates/mails/devpost_upload_subject.txt +++ b/reimbursement/templates/mails/devpost_upload_subject.txt @@ -1 +1 @@ -[ACTION REQUIRED] Upload your Devpost link +Upload your Devpost link diff --git a/reimbursement/templates/mails/ticket_accepted_message.html b/reimbursement/templates/mails/ticket_accepted_message.html index 12d2f87d7..775dc320e 100644 --- a/reimbursement/templates/mails/ticket_accepted_message.html +++ b/reimbursement/templates/mails/ticket_accepted_message.html @@ -10,22 +10,22 @@

    Regarding the project:

      -
    • The participant must demo a valid project to the HackUPC judges.
    • -
    • The Devpost username must be similar to the participant’s real name. Usernames that do not reasonably resemble +
    • The participant must demo a valid project to the HackUPC judges.
    • +
    • The Devpost username must be similar to the participant’s real name. Usernames that do not reasonably resemble the real name (e.g. TheBest) will not be accepted.
    • -
    • The completeness of the GitHub repository will be taken into account, including aspects such as regular commits +
    • The completeness of the GitHub repository will be taken into account, including aspects such as regular commits throughout the weekend.

    Regarding the attendance:

      -
    • It must be demonstrable that the participant attended the event and stayed at the venue for a minimum period of - participation. Attendance to meals, activities, workshops, and other official event activities may be taken into +
    • It must be demonstrable that the participant attended the event and stayed at the venue for a minimum period of + participation. Attendance to meals, activities, workshops, and other official event activities may be taken into account to verify participation.

    The money will be transferred to the participant using PayPal in the following 60 days after the event. The paypal - account MUST exist.

    + account MUST exist.

    {% include 'mails/include/closing.html' with travel="true" %} {% endblock %} \ No newline at end of file diff --git a/reimbursement/templates/mails/travel_tickets_upload_message.html b/reimbursement/templates/mails/travel_tickets_upload_message.html index 3b84a1383..8cf424da1 100644 --- a/reimbursement/templates/mails/travel_tickets_upload_message.html +++ b/reimbursement/templates/mails/travel_tickets_upload_message.html @@ -4,19 +4,19 @@ {% block content %}

    Hey {{ app.user.get_full_name }},


    -

    For us to accept your travel reimbursement request, you need to upload VALID travel tickets in https://my.hackupc.com/reimbursement/dash_board/. -

    -

    A ticket is considered valid if and only if:

    +

    For us to accept your travel reimbursement request, you need to upload VALID travel tickets here:

    + +{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} + +

    A ticket is considered valid if and only if:

      -
    • The receipts are in the name of the participant requesting the reimbursement.
    • -
    • It includes a detailed price breakdown, the purchase date, and the travel dates, clearly indicating which costs +
    • The receipts are in the name of the participant requesting the reimbursement.
    • +
    • It includes a detailed price breakdown, the purchase date, and the travel dates, clearly indicating which costs correspond to the travel itself and which relate to additional services (e.g. extra luggage).
    • -
    • The travel dates are within one week before or one week after the event.
    • -
    • The ticket is uploaded before the start of the event.
    • +
    • The travel dates are within one week before or one week after the eventevent, between April 21st and May 5th + (both included)..
    • +
    • The ticket is uploaded before the start of the event.
    -{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} - {% include 'mails/include/closing.html' with travel="true" %} {% endblock %} \ No newline at end of file diff --git a/reimbursement/templates/mails/travel_tickets_upload_subject.txt b/reimbursement/templates/mails/travel_tickets_upload_subject.txt index b96ef8343..b74d979d8 100644 --- a/reimbursement/templates/mails/travel_tickets_upload_subject.txt +++ b/reimbursement/templates/mails/travel_tickets_upload_subject.txt @@ -1 +1 @@ -[ACTION REQUIRED] Upload your travel tickets +Upload your travel tickets From 0df616bfc3eccd19591fea6609376a496fef2a87 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Wed, 25 Feb 2026 17:43:36 +0100 Subject: [PATCH 17/23] afegir paypal --- .../templates/mails/travel_tickets_upload_message.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reimbursement/templates/mails/travel_tickets_upload_message.html b/reimbursement/templates/mails/travel_tickets_upload_message.html index 8cf424da1..d799a3a8d 100644 --- a/reimbursement/templates/mails/travel_tickets_upload_message.html +++ b/reimbursement/templates/mails/travel_tickets_upload_message.html @@ -18,5 +18,7 @@
  • The ticket is uploaded before the start of the event.
  • +

    Remember! The paypal account MUST exist.

    + {% include 'mails/include/closing.html' with travel="true" %} {% endblock %} \ No newline at end of file From efa602ed1c403de4ff72f4949fd7e4a61689f097 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Wed, 25 Feb 2026 17:47:50 +0100 Subject: [PATCH 18/23] cronjob diumenges a les 20 --- cronjob | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjob b/cronjob index 9e7e140db..574ab4244 100644 --- a/cronjob +++ b/cronjob @@ -2,5 +2,5 @@ 6 */2 * * * root /usr/local/bin/python /code/manage.py expire_applications # Run expire reimbursements job at 6 minutes past the hour, every 2 hours 36 */2 * * * root /usr/local/bin/python /code/manage.py expire_reimbursements -# Run send devpost emails job every Sunday at 20:00 -0 20 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails +# Run send devpost emails job every Sunday at 20:00 server va 1 hora abans +0 19 * * 0 root /usr/local/bin/python /code/manage.py send_devpost_emails From bf49141bf2bc3134bdbe65d501e6e74132c8c0b7 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Thu, 26 Feb 2026 17:43:59 +0100 Subject: [PATCH 19/23] mail actualitzat --- .../mails/travel_tickets_upload_message.html | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/reimbursement/templates/mails/travel_tickets_upload_message.html b/reimbursement/templates/mails/travel_tickets_upload_message.html index d799a3a8d..4f33175fb 100644 --- a/reimbursement/templates/mails/travel_tickets_upload_message.html +++ b/reimbursement/templates/mails/travel_tickets_upload_message.html @@ -1,24 +1,34 @@ {% extends 'base_email.html' %} -{% block preheader %}Upload your travel tickets{% endblock %} +{% block preheader %}Upload your travel ticket{% endblock %} {% block content %} -

    Hey {{ app.user.get_full_name }},

    +

    Hey {{ app.user.get_full_name }},


    -

    For us to accept your travel reimbursement request, you need to upload VALID travel tickets here:

    -{% include 'mails/include/email_button.html' with text='Upload tickets' url=form_url %} +

    Congratulations on being accepted to HackUPC 2026! 🎉

    -

    A ticket is considered valid if and only if:

    +

    We noticed that you selected the travel reimbursement option. To proceed, please upload a valid travel ticket to your MyHackUPC dashboard as soon as possible.

    + +{% include 'mails/include/email_button.html' with text='Upload ticket' url=form_url %} + +

    A ticket will be considered valid only if:

      -
    • The receipts are in the name of the participant requesting the reimbursement.
    • -
    • It includes a detailed price breakdown, the purchase date, and the travel dates, clearly indicating which costs - correspond to the travel itself and which relate to additional services (e.g. extra luggage).
    • -
    • The travel dates are within one week before or one week after the eventevent, between April 21st and May 5th - (both included)..
    • -
    • The ticket is uploaded before the start of the event.
    • +
    • We only accept bus, plane or train tickets.
    • +
    • The receipt is issued in the name of the participant requesting the reimbursement.
    • +
    • It includes a detailed price breakdown, the purchase date, the travel dates, and a clear distinction between the basic fare and any additional services (e.g., extra luggage, seat selection, insurance). Any additional service will be discounted.
    • +
    • The travel dates are within one week before or after the event (between April 21st and May 5th, inclusive).
    • +
    • The ticket is uploaded before the start of the event.
    • +
    • You have a valid PayPal account to receive the reimbursement.
    -

    Remember! The paypal account MUST exist.

    +
    + +

    Reimbursement Policy

    +

    The reimbursed amount will be the minimum of:

    +
      +
    • 50% of your travel expenses (calculated based on the basic fare only), and
    • +
    • The maximum amount assigned to your destination, determined by the organizing team based on budget and demand.
    • +
    {% include 'mails/include/closing.html' with travel="true" %} -{% endblock %} \ No newline at end of file +{% endblock %} From 294f0b47a227d24f2f474d0e220214cafbb17727 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Sat, 28 Feb 2026 14:02:19 +0100 Subject: [PATCH 20/23] mail_devpost --- reimbursement/management/commands/send_devpost_emails.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py index 2f24f6bd6..2a441d19c 100644 --- a/reimbursement/management/commands/send_devpost_emails.py +++ b/reimbursement/management/commands/send_devpost_emails.py @@ -1,7 +1,7 @@ from django.core.management.base import BaseCommand from django.utils import timezone from django.conf import settings -from reimbursement.models import Reimbursement, RE_APPROVED +from reimbursement.models import Reimbursement, RE_PEND_DEMO_VAL from reimbursement import emails @@ -18,8 +18,7 @@ def handle(self, *args, **options): # hasn't uploaded a devpost link yet (devpost=''), # and we haven't sent the reminder email yet (devpost_email_sent=False). reimbursements = Reimbursement.objects.filter( - status=RE_APPROVED, - devpost='', + status=RE_PEND_DEMO_VAL, devpost_email_sent=False ) From 2bef24fc01091495343daeedbb94a1e878f1c6d2 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Sat, 28 Feb 2026 14:20:53 +0100 Subject: [PATCH 21/23] mail_devpostx2 --- reimbursement/management/commands/send_devpost_emails.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reimbursement/management/commands/send_devpost_emails.py b/reimbursement/management/commands/send_devpost_emails.py index 2a441d19c..2f24f6bd6 100644 --- a/reimbursement/management/commands/send_devpost_emails.py +++ b/reimbursement/management/commands/send_devpost_emails.py @@ -1,7 +1,7 @@ from django.core.management.base import BaseCommand from django.utils import timezone from django.conf import settings -from reimbursement.models import Reimbursement, RE_PEND_DEMO_VAL +from reimbursement.models import Reimbursement, RE_APPROVED from reimbursement import emails @@ -18,7 +18,8 @@ def handle(self, *args, **options): # hasn't uploaded a devpost link yet (devpost=''), # and we haven't sent the reminder email yet (devpost_email_sent=False). reimbursements = Reimbursement.objects.filter( - status=RE_PEND_DEMO_VAL, + status=RE_APPROVED, + devpost='', devpost_email_sent=False ) From 30c762598b6e4db43960c597475bf6fa7dbbefd4 Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Sat, 28 Feb 2026 16:16:52 +0100 Subject: [PATCH 22/23] nous mails --- reimbursement/emails.py | 6 ++++++ .../mails/devpost_approved_message.html | 14 ++++++++++++++ .../mails/devpost_approved_subject.txt | 1 + .../mails/project_invalidated_message.html | 17 +++++++++++++++++ .../mails/project_invalidated_subject.txt | 2 +- reimbursement/views.py | 1 + 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 reimbursement/templates/mails/devpost_approved_message.html create mode 100644 reimbursement/templates/mails/devpost_approved_subject.txt create mode 100644 reimbursement/templates/mails/project_invalidated_message.html diff --git a/reimbursement/emails.py b/reimbursement/emails.py index 59085cc7a..a425b4da3 100644 --- a/reimbursement/emails.py +++ b/reimbursement/emails.py @@ -53,6 +53,12 @@ def create_project_invalidated_email(reimb, request): return emails.render_mail("mails/project_invalidated", reimb.hacker.email, c) +def create_devpost_approved_email(reimb, request): + app = reimb.hacker.application + c = _get_context(app, reimb, request) + return emails.render_mail("mails/devpost_approved", reimb.hacker.email, c) + + def _get_context(app, reimb, request): from django.conf import settings diff --git a/reimbursement/templates/mails/devpost_approved_message.html b/reimbursement/templates/mails/devpost_approved_message.html new file mode 100644 index 000000000..83613f611 --- /dev/null +++ b/reimbursement/templates/mails/devpost_approved_message.html @@ -0,0 +1,14 @@ +{% extends 'base_email.html' %} +{% block preheader %}Your travel reimbursement has been approved!{% endblock %} + +{% block content %} +

    Congratulations {{ app.user.get_full_name }}!

    +
    +

    Your travel reimbursement request has been approved by our Finance Team. You will receive the reimbursed amount within two months after the event. Please note that minor delays may occur due to the high volume of travel reimbursement requests this year.

    + +

    You have been assigned: {{ reimb.assigned_money }} €

    + +

    The money will be transferred to your PayPal account. Make sure the PayPal account you provided is correct and active.

    + +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} diff --git a/reimbursement/templates/mails/devpost_approved_subject.txt b/reimbursement/templates/mails/devpost_approved_subject.txt new file mode 100644 index 000000000..92c21d31b --- /dev/null +++ b/reimbursement/templates/mails/devpost_approved_subject.txt @@ -0,0 +1 @@ +Travel Reimbursement – Approved! \ No newline at end of file diff --git a/reimbursement/templates/mails/project_invalidated_message.html b/reimbursement/templates/mails/project_invalidated_message.html new file mode 100644 index 000000000..19c08743d --- /dev/null +++ b/reimbursement/templates/mails/project_invalidated_message.html @@ -0,0 +1,17 @@ +{% extends 'base_email.html' %} +{% block preheader %}Your travel reimbursement request – Not Approved{% endblock %} + +{% block content %} +

    Dear {{ app.user.get_full_name }},

    +
    +

    After reviewing your travel reimbursement request, we regret to inform you that your application cannot be approved. This may be due to one of the following reasons:

    + +
      +
    • You did not meet the minimum required attendance time at the venue.
    • +
    • The project or Devpost submission provided was invalid or did not meet the established criteria.
    • +
    + +

    If you believe this decision has been made in error or would like further clarification, please feel free to contact us.

    + +{% include 'mails/include/closing.html' with travel="true" %} +{% endblock %} diff --git a/reimbursement/templates/mails/project_invalidated_subject.txt b/reimbursement/templates/mails/project_invalidated_subject.txt index eafe9d603..080f7d800 100644 --- a/reimbursement/templates/mails/project_invalidated_subject.txt +++ b/reimbursement/templates/mails/project_invalidated_subject.txt @@ -1 +1 @@ -[IMPORTANT INFORMATION] Your reimbursement has been invalidated +Travel Reimbursement Request – Not Approved diff --git a/reimbursement/views.py b/reimbursement/views.py index e1e847a1a..99a63af11 100644 --- a/reimbursement/views.py +++ b/reimbursement/views.py @@ -145,6 +145,7 @@ def post(self, request, *args, **kwargs): if form.is_valid(): form.save(commit=False) reimb.validate(request.user) + emails.create_devpost_approved_email(reimb, request).send() form.save() messages.success(self.request, "Reimbursement successfully validated!") else: From d866a7d5a0e1bb5ebedb62a3ec8e7ba9938db5fe Mon Sep 17 00:00:00 2001 From: polmf <99polmf@gmail.com> Date: Sat, 28 Feb 2026 16:33:10 +0100 Subject: [PATCH 23/23] correcio mails --- reimbursement/templates/mails/devpost_approved_message.html | 2 +- reimbursement/templates/mails/project_invalidated_message.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reimbursement/templates/mails/devpost_approved_message.html b/reimbursement/templates/mails/devpost_approved_message.html index 83613f611..50497be18 100644 --- a/reimbursement/templates/mails/devpost_approved_message.html +++ b/reimbursement/templates/mails/devpost_approved_message.html @@ -6,7 +6,7 @@

    Your travel reimbursement request has been approved by our Finance Team. You will receive the reimbursed amount within two months after the event. Please note that minor delays may occur due to the high volume of travel reimbursement requests this year.

    -

    You have been assigned: {{ reimb.assigned_money }} €

    +

    You have been assigned: {{ h_currency }}{{ reimb.reimbursement_money }}

    The money will be transferred to your PayPal account. Make sure the PayPal account you provided is correct and active.

    diff --git a/reimbursement/templates/mails/project_invalidated_message.html b/reimbursement/templates/mails/project_invalidated_message.html index 19c08743d..73c52b8a7 100644 --- a/reimbursement/templates/mails/project_invalidated_message.html +++ b/reimbursement/templates/mails/project_invalidated_message.html @@ -12,6 +12,6 @@

    If you believe this decision has been made in error or would like further clarification, please feel free to contact us.

    +

    Best regards,

    -{% include 'mails/include/closing.html' with travel="true" %} {% endblock %}