├── .circleci └── config.yml ├── .editorconfig ├── .flake8 ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── Procfile ├── README.md ├── USER_GUIDE.md ├── app.json ├── app ├── __init__.py ├── dashboard_modules.py ├── emails.py ├── hackathon_variables.py ├── heroku_settings.py ├── heroku_wsgi.py ├── jet_dashboard.py ├── log.py ├── mixins.py ├── settings.py ├── slack.py ├── static │ ├── css │ │ ├── main.css │ │ ├── reset.css │ │ └── tabs.css │ ├── favicon.ico │ ├── favicon.png │ ├── img │ │ ├── devpost.svg │ │ ├── sort_asc.png │ │ ├── sort_both.png │ │ └── sort_desc.png │ ├── lib │ │ ├── barrating.min.js │ │ ├── bars-square.css │ │ ├── c3.min.css │ │ ├── c3.min.js │ │ ├── material.css │ │ ├── qr-scanner-worker.min.js │ │ ├── qr-scanner.min.js │ │ ├── snackbar.css.map │ │ ├── snackbar.min.css │ │ ├── snackbar.min.js │ │ ├── snackbar.min.js.map │ │ └── typeahead.min.js │ └── logo.png ├── templates │ ├── 403.html │ ├── 404.html │ ├── 500.html │ ├── base.html │ ├── base_email.html │ ├── base_table.html │ ├── base_tabs.html │ ├── code_conduct.html │ ├── include │ │ ├── bootstrap_form.html │ │ └── deadline_countdown.html │ ├── mails │ │ └── include │ │ │ ├── closing.html │ │ │ ├── email_button.html │ │ │ └── email_image.html │ ├── modules │ │ ├── reviewers.html │ │ └── stats.html │ └── test_email │ │ ├── test_message.html │ │ └── test_subject.txt ├── urls.py ├── utils.py ├── views.py └── wsgi.py ├── applications ├── __init__.py ├── admin.py ├── apps.py ├── emails.py ├── forms.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── expire_applications.py │ │ └── print_applications.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_reimb_refactor.py │ ├── 0003_phone.py │ ├── 0004_empty_resume.py │ ├── 0005_reimb_amount.py │ ├── 0006_origin_simplified.py │ ├── 0007_team_user.py │ ├── 0008_update_apps.py │ ├── 0009_default_reimb.py │ ├── 0010_draft_app.py │ ├── 0011_tshirt_mlh.py │ ├── 0012_auto_20181205_1136.py │ ├── 0013_tshirts.py │ ├── 0014_gender_other.py │ ├── 0014_tshirt_fix.py │ ├── 0015_merge_20190218_1144.py │ ├── 0016_auto_20190611_1011.py │ ├── 0017_application_contacted_by.py │ ├── 0018_auto_20190725_0437.py │ ├── 0019_auto_20200321_1814.py │ ├── 0020_new_applications_20200402_1036.py │ ├── 0021_new_application_and_blacklist_20200520_1452.py │ ├── 0022_auto_20200521_0418.py │ ├── 0023_auto_20211002_0408.py │ └── __init__.py ├── models.py ├── signals.py ├── static │ ├── countries.json │ ├── css │ │ ├── profile.css │ │ └── vote.css │ ├── degrees.json │ ├── js │ │ └── form_modifiers.js │ ├── schools.json │ └── schools2.json ├── templates │ ├── application.html │ ├── cancel.html │ ├── convert_mentor.html │ ├── dashboard.html │ ├── include │ │ ├── application_form.html │ │ ├── applications_closed.html │ │ ├── cancel.html │ │ └── status.html │ ├── mails │ │ ├── confirmation_message.html │ │ ├── confirmation_subject.txt │ │ ├── include │ │ │ ├── arrival_departure_info.html │ │ │ └── cancel.html │ │ ├── invitation_hacker_message.html │ │ ├── invitation_hacker_subject.txt │ │ ├── invitation_mentor_message.html │ │ ├── invitation_mentor_subject.txt │ │ ├── invitation_volunteer_message.html │ │ ├── invitation_volunteer_subject.txt │ │ ├── last_reminder_message.html │ │ └── last_reminder_subject.txt │ └── sponsor_submitted.html ├── urls.py ├── validators.py └── views.py ├── checkin ├── __init__.py ├── admin.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── add_volunteers.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_to_new_applications_20200402_1036.py │ ├── 0003_new_applications_20200520_1452.py │ ├── 0004_alter_checkin_user.py │ └── __init__.py ├── models.py ├── static │ ├── css │ │ └── checkin.css │ ├── js │ │ ├── checkin.js │ │ ├── checkin_add.js │ │ └── form_modifiers.js │ └── lib │ │ ├── instascan.min.js │ │ └── typeahead.min.js ├── tables.py ├── templates │ ├── checkin │ │ ├── hacker.html │ │ ├── list.html │ │ └── ranking.html │ └── include │ │ └── input.html ├── urls.py └── views.py ├── documentation └── UML.png ├── files └── .keep ├── fixtures └── initial_data.json ├── manage.py ├── management.sh.template ├── organizers ├── __init__.py ├── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_to_new_applications_20200402_1036.py │ ├── 0003_application_comment_to_all_applications_20200520_1452.py │ ├── 0004_alter_vote_user.py │ └── __init__.py ├── models.py ├── tables.py ├── templates │ ├── application_detail.html │ ├── applications_list.html │ ├── blacklist_list.html │ ├── dubious_list.html │ ├── include │ │ ├── field.html │ │ └── number10.html │ ├── invite_list.html │ └── other_application_detail.html ├── urls.py └── views.py ├── reimbursement ├── __init__.py ├── admin.py ├── apps.py ├── emails.py ├── forms.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── expire_reimbursements.py ├── migrations │ ├── 0001_reimb_refactor.py │ ├── 0002_reimbursement_venmo_user.py │ ├── 0003_rename_user.py │ ├── 0004_origin_simplified.py │ ├── 0005_address.py │ ├── 0006_alter_reimbursement_reimbursed_by.py │ └── __init__.py ├── models.py ├── signals.py ├── tables.py ├── templates │ ├── include │ │ ├── r_status.html │ │ ├── reimbursement_form.html │ │ └── waitlisted_reimbursement.html │ ├── mails │ │ ├── include │ │ │ └── confirm_reimbursement.html │ │ ├── no_reimbursement_message.html │ │ ├── no_reimbursement_subject.txt │ │ ├── reimbursement_message.html │ │ ├── reimbursement_subject.txt │ │ ├── reject_receipt_message.html │ │ └── reject_receipt_subject.txt │ ├── reimbursement_detail.html │ ├── reimbursement_hacker.html │ ├── reimbursement_send_table.html │ └── reimbursements_table.html ├── urls.py └── views.py ├── requirements.txt ├── restart.sh.template ├── server.sh.template ├── stats ├── __init__.py ├── apps.py ├── tables.py ├── templates │ ├── application_stats.html │ ├── c3_base.html │ ├── c3_table_base.html │ ├── checkin_stats.html │ ├── mentor_stats.html │ ├── ranking.html │ ├── reimbursement_stats.html │ ├── sponsor_stats.html │ ├── users_stats.html │ └── volunteer_stats.html ├── urls.py └── views.py ├── teams ├── __init__.py ├── admin.py ├── forms.py ├── migrations │ ├── 0001_teams.py │ └── __init__.py ├── models.py ├── templates │ └── team.html ├── urls.py └── views.py └── user ├── __init__.py ├── admin.py ├── apps.py ├── emails.py ├── forms.py ├── migrations ├── 0001_initial.py ├── 0002_auto_20171101_1602.py ├── 0003_rename_user.py ├── 0004_created_time.py ├── 0005_makeemailfield.py ├── 0006_user_is_hardware_admin.py ├── 0007_user_mlh_id.py ├── 0008_user_can_review_dubious.py ├── 0009_auto_20200426_0854.py ├── 0009_user_types_20200321_0441.py ├── 0010_blacklist_and_user_type_permisions_20200520_1452.py ├── 0011_token.py └── __init__.py ├── mixins.py ├── models.py ├── providers.py ├── signals.py ├── templates ├── callback.html ├── confirm_delete.html ├── login.html ├── mails │ ├── password_reset_message.html │ ├── password_reset_subject.txt │ ├── sponsor_link_message.html │ ├── sponsor_link_subject.txt │ ├── verify_email_message.html │ └── verify_email_subject.txt ├── password_reset_complete.html ├── password_reset_confirm.html ├── password_reset_done.html ├── password_reset_form.html ├── profile.html ├── signup.html └── verify_email_required.html ├── tokens.py ├── urls.py └── views.py /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | workflows: 3 | version: 2 4 | test: 5 | jobs: 6 | - build-3.7 7 | - build-3.8 8 | - build-3.9 9 | 10 | jobs: 11 | build-3.7: &test-template 12 | docker: 13 | - image: circleci/python:3.7 14 | steps: 15 | - checkout 16 | - restore_cache: 17 | key: deps2-{{ .Environment.CIRCLE_JOB}}-{{ .Branch }}-{{ checksum "requirements.txt" }} 18 | - run: 19 | name: Create virtualenv 20 | command: | 21 | if [ "${CIRCLE_JOB}" == "build-2.7" ]; then 22 | virtualenv env 23 | else 24 | python3 -m venv env 25 | fi 26 | . env/bin/activate 27 | pip install -r requirements.txt 28 | - save_cache: 29 | key: deps2-{{ .Environment.CIRCLE_JOB}}-{{ .Branch }}-{{ checksum "requirements.txt" }} 30 | paths: 31 | - "env" 32 | - run: 33 | name: Running tests 34 | command: | 35 | . env/bin/activate 36 | python manage.py test 37 | - run: 38 | name: Linting code 39 | command: | 40 | . env/bin/activate 41 | flake8 42 | - store_artifacts: 43 | path: test-reports/ 44 | destination: python_app 45 | build-3.6: 46 | <<: *test-template 47 | docker: 48 | - image: circleci/python:3.6-jessie 49 | build-3.5: 50 | <<: *test-template 51 | docker: 52 | - image: circleci/python:3.5-jessie 53 | build-2.7: 54 | <<: *test-template 55 | docker: 56 | - image: circleci/python:2.7-jessie 57 | build-3.8: 58 | <<: *test-template 59 | docker: 60 | - image: circleci/python:3.8 61 | build-3.9: 62 | <<: *test-template 63 | docker: 64 | - image: circleci/python:3.9 65 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | charset = utf-8 12 | 13 | # Docstrings and comments use max_line_length = 79 14 | [*.py] 15 | max_line_length = 119 16 | 17 | # Use 2 spaces for the HTML files 18 | [*.html] 19 | indent_size = 2 20 | 21 | # The JSON files contain newlines inconsistently 22 | [*.json] 23 | indent_size = 2 24 | insert_final_newline = ignore 25 | 26 | # Minified JavaScript files shouldn't be changed 27 | [**.min.js] 28 | indent_style = ignore 29 | insert_final_newline = ignore 30 | 31 | # Makefiles always use tabs for indentation 32 | [Makefile] 33 | indent_style = tab 34 | 35 | # Batch files use tabs for indentation 36 | [*.bat] 37 | indent_style = tab 38 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = build,.git,.tox,./django/utils/six.py,./django/conf/app_template/*,./tests/.env,./env,./app/settings.py,./*/migrations,./app/hackathon_variables.py, ./venv/* 3 | ignore = W601,F403,W504,F405 4 | max-line-length = 119 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # BUG REPORT 6 | 7 | 8 | 9 | ## What happened 10 | 11 | 12 | 13 | 14 | ## What you expected to happen 15 | 16 | 17 | ## Anything else we need to know? 18 | 19 | 20 | 21 | 22 | # FEATURE REQUEST 23 | 24 | ## What do you want to happen 25 | 26 | ## Anything else we need to know? 27 | 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Patch Notes 6 | 7 | 8 | 9 | ## Which issue(s) this PR fixes (optional) 10 | 11 | 12 | Fixes # 13 | References # 14 | 15 | ## Additional Notes (optional) 16 | 17 | 18 | 19 | ## Some questions 20 | - [ ] I have read the contributing guidelines 21 | - [ ] I abide by this repository Code of Conduct 22 | - [ ] I understand that my PR won't be merged until GitHub gives a "green light" 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | env/ 3 | venv/ 4 | *.swp 5 | *.pyc 6 | /staticfiles 7 | fetch.sh 8 | app/db_settings.py 9 | db.json 10 | *.sqlite3 11 | client_secret.json 12 | insert_missing.sh 13 | check_invites.sh 14 | app/prod_settings.py 15 | tmp/ 16 | .vscode 17 | *.csv 18 | 19 | .DS_Store 20 | __pycache__/ 21 | 22 | files/* 23 | !files/.keep 24 | 25 | *.log 26 | cache/ 27 | *.sh 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.5" 5 | - "3.5-dev" # 3.5 development branch 6 | - "3.6" 7 | - "3.6-dev" # 3.6 development branch 8 | install: "pip install -r requirements.txt" 9 | script: 10 | - ./manage.py test 11 | - flake8 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hackers at UPC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: python manage.py migrate 2 | web: gunicorn app.heroku_wsgi 3 | -------------------------------------------------------------------------------- /USER_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Open registration 2 | 3 | You will only be able to register if applications are open. 4 | 5 | To open registration change the `HACKATHON_APP_DEADLINE` variable in [app/hackathon_variables.py](app/hackathon_variables.py) 6 | 7 | # Review applications 8 | 9 | 1. Register using your hackathon email (ex: `*@hackupc.com`). 10 | 2. Confirm your email (you should have gotten a confirmation email) 11 | 3. Open registration platform again. 12 | 13 | ## INTERNAL: How to make a normal user into a volunteer/reviewer 14 | 15 | 1. Log in your admin account created in the first step from set up (see [README](README.md)). 16 | 2. Access `Admin` tab (see top bar) 17 | 3. Go to `User` page (see side bar) 18 | 4. Find user you need to change. 19 | 5. Go to `Permissions` tab 20 | 6. Check the Organizer (for reviews), Volunteer (for checkin) or Director (for invites) boxes. 21 | 7. Save user 22 | 23 | # Review mechanism 24 | 25 | This is a suggested mechanism for reviewing applications. This is used by default. You can change the score system and weight in the code if need to change. 26 | 27 | The criteria are also suggestions, feel free to change them at any time. 28 | 29 | ## Score calculation 30 | The above specified scores can be summed up in 2 (1-10) separate values: 31 | 32 | **TECHNICAL SKILLS**. How the hacker is prepared from a technical point of view to participate in this hackathon. This may include: 33 | - Projects he has worked on question 34 | - Information such as github, CV, linkedin… 35 | - Background: What has the hacker done before (internships…) 36 | 37 | **PERSONAL SKILLS**. How the hacker is prepared from a personal point of view to participate in this hackathon. This may include: 38 | - Level of dedication to fill the application (are answers to “most excited about HackCU” and “projects you have collaborated with” well developed/well explained?) 39 | - Correctness/politeness (i.e. no “I want free food” stuff) 40 | - General valoration in: interest in learning, collaborating... 41 | 42 | If hacker personally known, reviewer should skip application. 43 | 44 | We process this after standardizing votes for each reviewer so that all votes weight similarly. This way we can save votes from people being too kind or too bad. 45 | 46 | ### Final vote for application 47 | `vote = TECH_SKILLS*0.2+PERSONAL_SKILLS*0.8` 48 | 49 | 50 | ## Batch of invites algorithm 51 | 52 | Choose N depending on number of confirmed and invited. From there, order by score. Choose N first ones and invite. Separate depending if they need travel reimbursement or not (110 reimbursement maximum). Also review independently Spain and UPC applicants to make sure that they get in. 53 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HackAssistant Registration", 3 | "description": "Hackathon registration server", 4 | "env": { 5 | "DOMAIN": { 6 | "description": "Custom domain where app will be running (ignore if deploying using Heroku default domain)", 7 | "required": false 8 | }, 9 | "SECRET": { 10 | "description": "A secret key for verifying the integrity of signed cookies.", 11 | "generator": "secret" 12 | }, 13 | "SG_KEY": { 14 | "description": "Sendgrid Key. Get it here: https://app.sendgrid.com/settings/api_keys" 15 | }, 16 | "DROPBOX_OAUTH2_TOKEN": { 17 | "description": "DropBox Oauth2 token. Get it here: https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account/", 18 | "required": false 19 | }, 20 | "SL_TOKEN": { 21 | "description": "Slack token to invite hackers automatically on confirmation. https://api.slack.com/custom-integrations/legacy-tokens", 22 | "required": false 23 | }, 24 | "SL_TEAM": { 25 | "description": "Slack team name (xxx on xxx.slack.com)", 26 | "required": false 27 | }, 28 | "PROD_MODE": { 29 | "description": "Disables Django debug mode.", 30 | "value":"true" 31 | }, 32 | "MLH_CLIENT_SECRET": { 33 | "description": "Enables MyMLH as a sign up option. Format for credentials needs to follow `CLIENT_ID@SECRET` from your MyMLH app.", 34 | "required": false 35 | } 36 | }, 37 | "scripts": { 38 | "postdeploy": "python manage.py migrate" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/__init__.py -------------------------------------------------------------------------------- /app/dashboard_modules.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.db.models import Count 3 | from jet.dashboard.modules import DashboardModule 4 | 5 | from applications.models import HackerApplication, STATUS 6 | from user.models import User 7 | 8 | 9 | class BestReviewerForm(forms.Form): 10 | limit = forms.IntegerField(label='Reviewers shown', min_value=1) 11 | 12 | 13 | class BestReviewers(DashboardModule): 14 | title = 'Best reviewers' 15 | template = 'modules/reviewers.html' 16 | limit = 10 17 | settings_form = BestReviewerForm 18 | 19 | def settings_dict(self): 20 | return { 21 | 'limit': self.limit 22 | } 23 | 24 | def load_settings(self, settings): 25 | self.limit = settings.get('limit', self.limit) 26 | 27 | def init_with_context(self, context): 28 | self.children = User.objects.annotate( 29 | vote_count=Count('vote__calculated_vote')).exclude(vote_count=0).order_by('-vote_count')[:self.limit] 30 | 31 | 32 | class AppsStatsForm(forms.Form): 33 | status = forms.ChoiceField(label='Status', 34 | choices=STATUS + [('__all__', 'All')]) 35 | 36 | 37 | class AppsStats(DashboardModule): 38 | title = 'Stats' 39 | template = 'modules/stats.html' 40 | status = '__all__' 41 | settings_form = AppsStatsForm 42 | 43 | def settings_dict(self): 44 | return { 45 | 'status': self.status 46 | } 47 | 48 | def load_settings(self, settings): 49 | self.status = settings.get('status', self.status) 50 | 51 | def init_with_context(self, context): 52 | qs = HackerApplication.objects.all() 53 | 54 | if self.status != '__all__': 55 | qs = qs.filter(status=self.status) 56 | 57 | self.tshirts = qs.values('tshirt_size') \ 58 | .annotate(count=Count('tshirt_size')) 59 | self.diets = qs.values('diet').annotate(count=Count('diet')) 60 | -------------------------------------------------------------------------------- /app/emails.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.core.mail import EmailMultiAlternatives, EmailMessage 3 | from django.template import TemplateDoesNotExist 4 | from django.template.loader import render_to_string 5 | 6 | from app import utils 7 | 8 | FROM_EMAIL = settings.HACKATHON_NAME + ' Team <' + settings.HACKATHON_CONTACT_EMAIL + '>' 9 | 10 | 11 | def render_mail(template_prefix, recipient_email, substitutions, 12 | from_email=FROM_EMAIL, action_required=False): 13 | """ 14 | Renders an e-mail to `email`. `template_prefix` identifies the 15 | e-mail that is to be sent, e.g. "account/email/email_confirmation" 16 | """ 17 | substitutions.update(utils.get_substitutions_templates()) 18 | subject = render_to_string('{0}_subject.txt'.format(template_prefix), 19 | context=substitutions) 20 | # remove superfluous line breaks 21 | subject = " ".join(subject.splitlines()).strip() 22 | prefix = '[' + settings.HACKATHON_NAME + ']' 23 | if action_required: 24 | prefix = '[ACTION REQUIRED]' 25 | subject = prefix + ' ' + subject 26 | substitutions.update({'subject': subject}) 27 | 28 | bodies = {} 29 | for ext in ['html', 'txt']: 30 | try: 31 | template_name = '{0}_message.{1}'.format(template_prefix, ext) 32 | bodies[ext] = render_to_string(template_name, 33 | substitutions).strip() 34 | except TemplateDoesNotExist: 35 | if ext == 'txt' and not bodies: 36 | # We need at least one body 37 | raise 38 | if 'txt' in bodies: 39 | msg = EmailMultiAlternatives(subject, 40 | bodies['txt'], 41 | from_email, 42 | [recipient_email]) 43 | if 'html' in bodies: 44 | msg.attach_alternative(bodies['html'], 'text/html') 45 | else: 46 | msg = EmailMessage(subject, 47 | bodies['html'], 48 | from_email, 49 | [recipient_email]) 50 | msg.content_subtype = 'html' # Main content is now text/html 51 | return msg 52 | 53 | 54 | def send_email(template_prefix, recipient_email, substitutions, 55 | from_email=FROM_EMAIL): 56 | msg = render_mail(template_prefix, recipient_email, substitutions, 57 | from_email) 58 | msg.send() 59 | -------------------------------------------------------------------------------- /app/heroku_settings.py: -------------------------------------------------------------------------------- 1 | from app.settings import * 2 | 3 | MIDDLEWARE.append('whitenoise.middleware.WhiteNoiseMiddleware') 4 | 5 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 6 | -------------------------------------------------------------------------------- /app/heroku_wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for testP project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.heroku_settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /app/jet_dashboard.py: -------------------------------------------------------------------------------- 1 | from app import dashboard_modules 2 | from django.utils.translation import ugettext_lazy as _ 3 | from jet.dashboard import modules 4 | from jet.dashboard.dashboard import Dashboard 5 | 6 | 7 | class CustomIndexDashboard(Dashboard): 8 | columns = 3 9 | 10 | def init_with_context(self, context): 11 | self.available_children.append(modules.LinkList) 12 | self.available_children.append(modules.ModelList) 13 | self.available_children.append(modules.RecentActions) 14 | self.available_children.append(dashboard_modules.BestReviewers) 15 | self.available_children.append(dashboard_modules.AppsStats) 16 | self.children.append(modules.ModelList( 17 | _('Models'), 18 | column=1, 19 | order=0 20 | )) 21 | self.children.append(dashboard_modules.BestReviewers( 22 | _('Reviewer Leaderboard'), 23 | column=0, order=0) 24 | ) 25 | -------------------------------------------------------------------------------- /app/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from copy import copy 3 | 4 | from django.core.mail import EmailMultiAlternatives 5 | from django.views.debug import ExceptionReporter 6 | 7 | from app import settings 8 | 9 | 10 | class HackathonDevEmailHandler(logging.Handler): 11 | """An exception log handler that emails log entries to site hackathon devs. 12 | 13 | If the request is passed as the first argument to the log record, 14 | request data will be provided in the email report. 15 | This is replicated from Django log to use the default backend properly. 16 | 17 | See: https://docs.djangoproject.com/en/1.11/howto/error-reporting/ 18 | """ 19 | 20 | def emit(self, record): 21 | 22 | try: 23 | request = record.request 24 | except Exception: 25 | request = None 26 | 27 | subject = '%s: %s' % ( 28 | record.levelname, 29 | record.getMessage() 30 | ) 31 | subject = subject.replace('\n', '\\n').replace('\r', '\\r') 32 | 33 | # Since we add a nicely formatted traceback on our own, create a copy 34 | # of the log record without the exception data. 35 | no_exc_record = copy(record) 36 | no_exc_record.exc_info = None 37 | no_exc_record.exc_text = None 38 | 39 | if record.exc_info: 40 | exc_info = record.exc_info 41 | else: 42 | exc_info = (None, record.getMessage(), None) 43 | if settings.HACKATHON_DEV_EMAILS: 44 | reporter = ExceptionReporter(request, is_email=True, *exc_info) 45 | message = "%s\n\n%s" % (self.format(no_exc_record), reporter.get_traceback_text()) 46 | html_message = reporter.get_traceback_html() 47 | msg = EmailMultiAlternatives(subject, 48 | message, 49 | 'server@' + settings.HACKATHON_DOMAIN, 50 | settings.HACKATHON_DEV_EMAILS) 51 | msg.attach_alternative(html_message, 'text/html') 52 | msg.send(fail_silently=True) 53 | -------------------------------------------------------------------------------- /app/mixins.py: -------------------------------------------------------------------------------- 1 | from django.forms import model_to_dict 2 | 3 | 4 | class TabsViewMixin(object): 5 | def get_current_tabs(self): 6 | return [] 7 | 8 | def get_back_url(self): 9 | return None 10 | 11 | def get_context_data(self, **kwargs): 12 | c = super(TabsViewMixin, self).get_context_data(**kwargs) 13 | c.update({'tabs': self.get_current_tabs(), 'back': self.get_back_url(), 'form_method': 'post'}) 14 | return c 15 | 16 | 17 | class OverwriteOnlyModelFormMixin(object): 18 | ''' 19 | Delete POST keys that were not actually found in the POST dict 20 | to prevent accidental overwriting of fields due to missing POST data. 21 | Based on: 22 | https://yuji.wordpress.com/2013/03/12/django-prevent-modelform-from-updating-values-if-user-did-not-submit-them/ 23 | ''' 24 | 25 | def clean(self): 26 | cleaned_data = super(OverwriteOnlyModelFormMixin, self).clean() 27 | c_cl_data = cleaned_data.copy() 28 | for field in c_cl_data.keys(): 29 | if self.prefix is not None: 30 | post_key = '-'.join((self.prefix, field)) 31 | else: 32 | post_key = field 33 | 34 | if post_key not in list(self.data.keys()) + list(self.files.keys()): 35 | # value was not posted, thus it should not overwrite any data. 36 | del cleaned_data[field] 37 | 38 | # only overwrite keys that were actually submitted via POST. 39 | model_data = model_to_dict(self.instance) 40 | model_data.update(cleaned_data) 41 | return model_data 42 | 43 | 44 | class BootstrapFormMixin: 45 | 46 | # example: {'TITLE': {'fields': [{'name': 'FIELD_NAME', 'space': GRID_NUMBER},], 'description': 'DESCRIPTION'},} 47 | # UPPER LETTERS MUST BE CHANGED 48 | bootstrap_field_info = {} 49 | read_only = [] 50 | 51 | def get_bootstrap_field_info(self): 52 | return self.bootstrap_field_info 53 | 54 | def set_read_only(self): 55 | for field in self.fields.values(): 56 | field.disabled = True 57 | 58 | @property 59 | def is_read_only(self): 60 | for field in self.fields.values(): 61 | if not field.disabled: 62 | return False 63 | return True 64 | 65 | @property 66 | def get_fields(self): 67 | result = self.get_bootstrap_field_info() 68 | for list_fields in result.values(): 69 | sum = 0 70 | for field in list_fields.get('fields'): 71 | if sum + field.get('space') > 12: 72 | sum = field.get('space') 73 | field['new_row'] = True 74 | else: 75 | sum += field.get('space') 76 | field['new_row'] = False 77 | name = field.get('name') 78 | field.update({'field': self.fields.get(name).get_bound_field(self, name)}) 79 | return result 80 | -------------------------------------------------------------------------------- /app/slack.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from django.conf import settings 3 | 4 | 5 | class SlackInvitationException(BaseException): 6 | pass 7 | 8 | 9 | BASE_URL = 'https://{}.slack.com/api/users.admin.invite' 10 | 11 | 12 | # Inspired by the code in https://github.com/giginet/django-slack-invitation 13 | 14 | def send_slack_invite(email, active=True): 15 | token = settings.SLACK.get('token', None) 16 | team = settings.SLACK.get('team', None) 17 | 18 | if not token or not team: 19 | raise SlackInvitationException( 20 | "Not configured slack, team = %s and token = %s" % (team, token)) 21 | 22 | r = requests.post(BASE_URL.format(team), data={ 23 | 'email': email, 24 | 'token': token, 25 | 'set_active': active 26 | }) 27 | response_object = r.json() 28 | if r.status_code == 200 and response_object['ok']: 29 | return True 30 | else: 31 | raise SlackInvitationException(response_object['error']) 32 | -------------------------------------------------------------------------------- /app/static/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /app/static/css/tabs.css: -------------------------------------------------------------------------------- 1 | .nav-tabs > li > a { 2 | -webkit-border-radius: 0; 3 | -moz-border-radius: 0; 4 | border-radius: 0; 5 | background: #C2BEBE; 6 | box-shadow: inset 0 -2px 2px rgba(0, 0, 0, 0.25); 7 | margin: 0; 8 | font-style: normal; 9 | line-height: normal; 10 | z-index: -1; 11 | font-size: 15px; 12 | color: black; 13 | border: 1px solid #838383; 14 | border-bottom: 0; 15 | padding-left: 30px; 16 | padding-right: 30px; 17 | -webkit-transition: all .1s linear; 18 | -moz-transition: all .1s linear; 19 | -o-transition: all .1s linear; 20 | transition: all .1s linear; 21 | padding-bottom: 9px; 22 | } 23 | 24 | .nav-tabs > li > a:hover { 25 | -webkit-border-radius: 0; 26 | -moz-border-radius: 0; 27 | border-radius: 0; 28 | background: #d4d1d1; 29 | border: 1px solid #838383; 30 | border-bottom: 0; 31 | } 32 | 33 | .nav-tabs { 34 | border: 0; 35 | margin: 0; 36 | } 37 | 38 | .nav-tabs > li.active_tab > a { 39 | box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.25); 40 | color: black; 41 | border-left: none !important; 42 | border-right: none !important; 43 | padding-bottom: 10px; 44 | } 45 | 46 | .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:active, .nav-tabs > li.active > a:focus { 47 | box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.25); 48 | color: black; 49 | 50 | } 51 | 52 | .nav-tabs > li { 53 | padding-bottom: 2px; 54 | margin-bottom: -3px; 55 | z-index: 1; 56 | 57 | } 58 | 59 | @media (max-width: 768px) { 60 | .nav-tabs > li > a { 61 | font-size: 12px; 62 | font-weight: normal; 63 | padding-left: 10px; 64 | padding-right: 10px; 65 | } 66 | } 67 | 68 | .active_tab { 69 | background-color: white; 70 | } 71 | 72 | .active_tab > a { 73 | background: none !important; 74 | box-shadow: none !important; 75 | } 76 | -------------------------------------------------------------------------------- /app/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/favicon.ico -------------------------------------------------------------------------------- /app/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/favicon.png -------------------------------------------------------------------------------- /app/static/img/devpost.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/static/img/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/img/sort_asc.png -------------------------------------------------------------------------------- /app/static/img/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/img/sort_both.png -------------------------------------------------------------------------------- /app/static/img/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/img/sort_desc.png -------------------------------------------------------------------------------- /app/static/lib/bars-square.css: -------------------------------------------------------------------------------- 1 | .br-theme-bars-square .br-widget { 2 | height: 15px; 3 | white-space: nowrap; 4 | } 5 | .br-theme-bars-square .br-widget a { 6 | display: block; 7 | width: 30px; 8 | height: 30px; 9 | float: left; 10 | border: 2px solid #bbcefb; 11 | background-color: white; 12 | margin: 2px; 13 | text-decoration: none; 14 | font-size: 14px; 15 | font-weight: 400; 16 | line-height: 2; 17 | text-align: center; 18 | color: #bbcefb; 19 | font-weight: 600; 20 | } 21 | .br-theme-bars-square .br-widget a.br-active, 22 | .br-theme-bars-square .br-widget a.br-selected { 23 | border: 2px solid #4278F5; 24 | color: #4278F5; 25 | } 26 | .br-theme-bars-square .br-widget .br-current-rating { 27 | clear: both; 28 | width: 330px; 29 | text-align: center; 30 | font-weight: 600; 31 | display: block; 32 | padding: .5em 0; 33 | color: #646464; 34 | } 35 | .br-theme-bars-square .br-readonly a { 36 | cursor: default; 37 | } 38 | .br-theme-bars-square .br-readonly a.br-active, 39 | .br-theme-bars-square .br-readonly a.br-selected { 40 | border: 2px solid #729bf8; 41 | color: #729bf8; 42 | } 43 | @media print { 44 | .br-theme-bars-square .br-widget a { 45 | border: 2px solid #b3b3b3; 46 | color: #b3b3b3; 47 | } 48 | .br-theme-bars-square .br-widget a.br-active, 49 | .br-theme-bars-square .br-widget a.br-selected { 50 | border: 2px solid black; 51 | color: black; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/static/lib/c3.min.css: -------------------------------------------------------------------------------- 1 | .c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:1;fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #ccc}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#fff}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:none}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max{fill:#777}.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}.c3-chart-arc.c3-target g path{opacity:1}.c3-chart-arc.c3-target.c3-focused g path{opacity:1} -------------------------------------------------------------------------------- /app/static/lib/material.css: -------------------------------------------------------------------------------- 1 | .snackbar { 2 | background-color: #323232; 3 | color: #FFFFFF; 4 | font-size: 14px; 5 | border-radius: 2px; 6 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 7 | height: 0; 8 | -moz-transition: -moz-transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s, padding 0s linear 0.2s, height 0s linear 0.2s; 9 | -webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s, padding 0s linear 0.2s, height 0s linear 0.2s; 10 | transition: transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s, padding 0s linear 0.2s, height 0s linear 0.2s; 11 | -moz-transform: translateY(200%); 12 | -webkit-transform: translateY(200%); 13 | transform: translateY(200%); 14 | } 15 | .snackbar.snackbar-opened { 16 | padding: 14px 15px; 17 | margin-bottom: 20px; 18 | height: auto; 19 | -moz-transition: -moz-transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s; 20 | -webkit-transition: -webkit-transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s; 21 | transition: transform 0.2s ease-in-out, opacity 0.2s ease-in, height 0s linear 0.2s, height 0s linear 0.2s; 22 | -moz-transform: none; 23 | -webkit-transform: none; 24 | transform: none; 25 | } 26 | .snackbar.toast { 27 | border-radius: 200px; 28 | } 29 | -------------------------------------------------------------------------------- /app/static/lib/snackbar.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["/src/snackbar.less"],"names":[],"mappings":"AACA;EACI,eAAA;EACA,UAAA;EACA,SAAA;EACA,cAAA;;AAEJ;EACI,gBAAA;EACA,WAAA;EACA,gBAAA;EACA,gBAAA;EACA,eAAA;EACA,UAAA;;AAGJ,SAAS;EACL,YAAA;EACA,UAAA;;AAoBJ,QAjB0B;EACtB;IACI,oBAAA;IACA,UAAA;IACA,WAAA;;EAHJ,mBAII;IACI,eAAA;;EALR,mBAOI,mCAAmC,YAAW;IAC1C,gBAAA;;EARR,mBAUI;IACI,gBAAA;IACA,gBAAA"} -------------------------------------------------------------------------------- /app/static/lib/snackbar.min.css: -------------------------------------------------------------------------------- 1 | #snackbar-container{position:fixed;left:20px;bottom:0;z-index:99999}.snackbar{overflow:hidden;clear:both;min-width:288px;max-width:568px;cursor:pointer;opacity:0}.snackbar.snackbar-opened{height:auto;opacity:1}@media (max-width:767px){#snackbar-container{left:0!important;right:0;width:100%}#snackbar-container .snackbar{min-width:100%}#snackbar-container [class="snackbar snackbar-opened"]~.snackbar.toast{margin-top:20px}#snackbar-container [class="snackbar snackbar-opened"]{border-radius:0;margin-bottom:0}} -------------------------------------------------------------------------------- /app/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/app/static/logo.png -------------------------------------------------------------------------------- /app/templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load static %} 4 | 5 | 6 | {% block head_title %}Forbidden {% endblock %} 7 | 8 | {% block body %} 9 | 14 |
15 |

YOU SHALL NOT PASS!

16 |

403 Access Forbidden

17 |

Please log in with a more powerful account to access this page

18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /app/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load static %} 4 | 5 | 6 | {% block head_title %}¯\_(ツ)_/¯ | Page not found {% endblock %} 7 | 8 | {% block body %} 9 | 14 |
15 |

¯\_(ツ)_/¯

16 |

404 Page not found

17 |

This specified file was not found on this website. Please check the URL for mistakes and 18 | try 19 | again.

20 |
21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /app/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% load static %} 4 | 5 | 6 | 7 | {% block head_title %}500 Oops {% endblock %} 8 | 9 | {% block body %} 10 | 15 |
16 |

500 Oops

17 |

Server Error

18 |

It seems like you found an error. Please reach us out 19 | at {{ h_contact_email|urlize }} if the problem persists.

20 | {% if h_repo %} 21 |

22 | Or you can open an issue 23 | here. 24 |

25 | {% endif %} 26 |
27 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /app/templates/base_table.html: -------------------------------------------------------------------------------- 1 | {% extends "base_tabs.html" %} 2 | {% load humanize %} 3 | {% load django_tables2 %} 4 | {% load bootstrap3 %} 5 | {% load static %} 6 | {% block panel %} 7 | 8 | {% if filter %} 9 | 20 |
21 | {% bootstrap_form filter.form %} 22 | 23 |
24 | {% endif %} 25 | {% block extra_panel %} 26 | {% endblock %} 27 | {% endblock %} 28 | {% block out_panel %} 29 |
30 | {% csrf_token %} 31 | {% render_table table 'django_tables2/bootstrap-responsive.html' %} 32 | {% block table_footer %} 33 | {% endblock %} 34 |
35 | {% endblock %} 36 | {% block extra_scripts %} 37 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /app/templates/include/bootstrap_form.html: -------------------------------------------------------------------------------- 1 | {% load bootstrap3 %} 2 | {% if messages %} 3 |
4 | {% for message in messages %} 5 | 11 | {% endfor %} 12 |
13 | {% endif %} 14 | {% for title, zone in form.get_fields.items %} 15 |
16 |

{{ title }}

17 | {% if zone.description %} 18 |

{{ zone.description }}

19 | {% endif %} 20 |
21 | {% for field in zone.fields %} 22 | {% if field.new_row %} 23 |
24 |
25 | {% endif %} 26 |
27 | {% bootstrap_field field.field %} 28 |
29 | {% endfor %} 30 |
31 |
32 | {% endfor %} 33 | -------------------------------------------------------------------------------- /app/templates/include/deadline_countdown.html: -------------------------------------------------------------------------------- 1 | {% with dl=timeleft|default:h_app_timeleft in_id=id|default:'secondsLeftDeadline' %} 2 | {% if dl %} 3 | 4 | 5 | 30 | 31 | {% endif %} 32 | {% endwith %} 33 | -------------------------------------------------------------------------------- /app/templates/mails/include/closing.html: -------------------------------------------------------------------------------- 1 |

2 | Have any other questions? Email us at {{ h_contact_email|urlize }} 3 | {% if h_fb or h_tw %} 4 | or message us on 5 | {% if h_fb %} 6 | Facebook 7 | {% if h_tw %} 8 | and 9 | Twitter 10 | {% endif %} 11 | {% elif h_tw %} 12 | Twitter 13 | {% endif %} 14 | . 15 | {% endif %} 16 |

17 |

We hope to see you at {{ h_name }}!

18 |

Best,

19 |

{{ h_name }} Team

20 | -------------------------------------------------------------------------------- /app/templates/mails/include/email_button.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 22 | 23 | 24 |
8 | 10 | 11 | 12 | 18 | 19 | 20 |
13 | 15 | {{ text }} 16 | 17 |
21 |
-------------------------------------------------------------------------------- /app/templates/mails/include/email_image.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /app/templates/modules/reviewers.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | 3 | -------------------------------------------------------------------------------- /app/templates/modules/stats.html: -------------------------------------------------------------------------------- 1 | {% load humanize %} 2 | 3 |

T-Shirts

4 | 5 | 6 | 7 | {% for tshirt in module.tshirts %} 8 | 9 | {% endfor %} 10 | 11 | 12 | 13 | 14 | {% for tshirt in module.tshirts %} 15 | 16 | {% endfor %} 17 | 18 | 19 |
{{ tshirt.hacker__tshirt_size }}
{{ tshirt.count }}
20 |

Diet

21 | 22 | 23 | 24 | {% for diet in module.diets %} 25 | 26 | {% endfor %} 27 | 28 | 29 | 30 | 31 | {% for diet in module.diets %} 32 | 33 | {% endfor %} 34 | 35 | 36 |
{{ diet.hacker__diet }}
{{ diet.count }}
37 |

Status

38 | 39 | 40 | 41 | {% for status in module.count_status %} 42 | 43 | {% endfor %} 44 | 45 | 46 | 47 | 48 | {% for status in module.count_status %} 49 | 50 | {% endfor %} 51 | 52 | 53 |
{{ status.status }}
{{ status.count }}
54 |

Reimbursement money

55 |

Pending tickets: {{ module.amount__sent.total }} €

56 |

Accepted: {{ module.amount__accepted.total }} €

57 |

Total projection: {{ module.amount__total.total }} €

58 | -------------------------------------------------------------------------------- /app/templates/test_email/test_message.html: -------------------------------------------------------------------------------- 1 | {% include 'base_email.html' %} -------------------------------------------------------------------------------- /app/templates/test_email/test_subject.txt: -------------------------------------------------------------------------------- 1 | THIS IS A TEST 2 | -------------------------------------------------------------------------------- /app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import url, include 3 | from django.conf.urls.static import static 4 | from django.contrib import admin 5 | from django.views.generic import RedirectView 6 | 7 | from app import views 8 | 9 | urlpatterns = [ 10 | url(r'^admin/', admin.site.urls), 11 | url(r'^user/', include('user.urls')), 12 | # url(r'^jet/', include('jet.urls', 'jet')), # Django JET URLS 13 | # url(r'^jet/dashboard/', include('jet.dashboard.urls', 'jet-dashboard')), # Django JET dashboard URLS 14 | url(r'^applications/', include('organizers.urls')), 15 | url(r'^', include('applications.urls')), 16 | url(r'^$', views.root_view, name='root'), 17 | url(r'^favicon.ico', RedirectView.as_view(url=static('favicon.ico'))), 18 | url(r'^checkin/', include('checkin.urls')), 19 | url(r'^teams/', include('teams.urls')), 20 | url(r'^stats/', include('stats.urls')), 21 | url(r'code_conduct/$', views.code_conduct, name='code_conduct'), 22 | url(r'^files/(?P.*)$', views.protectedMedia, name="protect_media"), 23 | 24 | ] 25 | 26 | if settings.REIMBURSEMENT_ENABLED: 27 | urlpatterns.append(url(r'^reimbursement/', include('reimbursement.urls'))) 28 | 29 | if settings.HARDWARE_ENABLED: 30 | urlpatterns.append(url(r'^hardware/', include('hardware.urls'))) 31 | 32 | if settings.DEBUG: 33 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 34 | -------------------------------------------------------------------------------- /app/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for testP project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /applications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/applications/__init__.py -------------------------------------------------------------------------------- /applications/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class ApplicationsConfig(AppConfig): 7 | name = 'applications' 8 | 9 | def ready(self): 10 | super(ApplicationsConfig, self).ready() 11 | from applications.signals import create_draft_application, clean_draft_application, \ 12 | auto_delete_file_on_change, auto_delete_file_on_delete 13 | create_draft_application 14 | clean_draft_application 15 | auto_delete_file_on_change 16 | auto_delete_file_on_delete 17 | -------------------------------------------------------------------------------- /applications/emails.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.core import mail 3 | 4 | from app import emails 5 | from app.utils import reverse 6 | 7 | 8 | def create_invite_email(application, request): 9 | c = { 10 | 'name': application.user.get_full_name, 11 | 'reimb': getattr(application.user, 'reimbursement', None), 12 | 'confirm_url': str(reverse('confirm_app', request=request, kwargs={'id': application.uuid_str})), 13 | 'cancel_url': str(reverse('cancel_app', request=request, kwargs={'id': application.uuid_str})), 14 | } 15 | if application.user.is_hacker(): 16 | return emails.render_mail('mails/invitation_hacker', application.user.email, c) 17 | if application.user.is_mentor(): 18 | return emails.render_mail('mails/invitation_mentor', application.user.email, c) 19 | return emails.render_mail('mails/invitation_volunteer', application.user.email, c) 20 | 21 | 22 | def create_confirmation_email(application, request): 23 | c = { 24 | 'name': application.user.get_full_name, 25 | 'token': application.uuid_str, 26 | 'qr_url': 'http://chart.googleapis.com/chart?cht=qr&chs=350x350&chl=%s' 27 | % application.uuid_str, 28 | 'cancel_url': str(reverse('cancel_app', request=request, kwargs={'id': application.uuid_str})), 29 | 'is_hacker': application.user.is_hacker(), 30 | 'is_sponsor': application.user.is_sponsor(), 31 | } 32 | return emails.render_mail('mails/confirmation', 33 | application.user.email, c) 34 | 35 | 36 | def create_lastreminder_email(application): 37 | c = { 38 | 'name': application.user.get_full_name, 39 | 'type': application.user.get_type_display(), 40 | # We need to make sure to redirect HTTP to HTTPS in production 41 | 'confirm_url': 'http://%s%s' % (settings.HACKATHON_DOMAIN, 42 | reverse('confirm_app', kwargs={'id': application.uuid_str})), 43 | 'cancel_url': 'http://%s%s' % (settings.HACKATHON_DOMAIN, 44 | reverse('cancel_app', kwargs={'id': application.uuid_str})), 45 | 'is_hacker': application.user.is_hacker(), 46 | 'is_sponsor': application.user.is_sponsor(), 47 | } 48 | return emails.render_mail('mails/last_reminder', 49 | application.user.email, c, action_required=True) 50 | 51 | 52 | def send_batch_emails(emails): 53 | connection = mail.get_connection() 54 | connection.send_messages(emails) 55 | -------------------------------------------------------------------------------- /applications/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/applications/management/__init__.py -------------------------------------------------------------------------------- /applications/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/applications/management/commands/__init__.py -------------------------------------------------------------------------------- /applications/management/commands/expire_applications.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | 3 | from django.core import mail 4 | from django.core.management.base import BaseCommand 5 | from django.utils import timezone 6 | 7 | from app.settings import APPLICATION_EXPIRATION_TYPES 8 | from applications import models, emails 9 | from applications.views import VIEW_APPLICATION_TYPE 10 | 11 | 12 | class Command(BaseCommand): 13 | help = 'Checks invites that have expired and sends reminders 24 before' 14 | 15 | def handle(self, *args, **options): 16 | fourdaysago = timezone.now() - timedelta(days=4) 17 | msgs = [] 18 | self.stdout.write('Checking reminders...') 19 | for type_app, do in APPLICATION_EXPIRATION_TYPES.items(): 20 | if do: 21 | self.stdout.write('Remindering %s applications' % type_app) 22 | reminders = VIEW_APPLICATION_TYPE[type_app].objects.filter( 23 | status_update_date__lte=fourdaysago, status=models.APP_INVITED) 24 | self.stdout.write('Checking reminders...%s found' % reminders.count()) 25 | for app in reminders: 26 | app.last_reminder() 27 | msgs.append(emails.create_lastreminder_email(app)) 28 | else: 29 | self.stdout.write('Skiping %s applications' % type_app) 30 | self.stdout.write('Sending reminders...') 31 | connection = mail.get_connection() 32 | connection.send_messages(msgs) 33 | self.stdout.write(self.style.SUCCESS( 34 | 'Sending reminders... Successfully sent %s reminders' % len(msgs))) 35 | 36 | onedayago = timezone.now() - timedelta(days=1) 37 | self.stdout.write('Checking expired...') 38 | for type_app, do in APPLICATION_EXPIRATION_TYPES.items(): 39 | if do: 40 | self.stdout.write('Expiring %s applications' % type_app) 41 | expired = VIEW_APPLICATION_TYPE[type_app].objects.filter( 42 | status_update_date__lte=onedayago, status=models.APP_LAST_REMIDER) 43 | self.stdout.write('Checking expired...%s found' % expired.count()) 44 | self.stdout.write('Setting expired...') 45 | count = len([app.expire() for app in expired]) 46 | self.stdout.write(self.style.SUCCESS( 47 | 'Setting expired... Successfully expired %s applications' % count)) 48 | else: 49 | self.stdout.write('Skiping %s applications' % type_app) 50 | -------------------------------------------------------------------------------- /applications/management/commands/print_applications.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | from applications import models 4 | 5 | 6 | class Command(BaseCommand): 7 | help = 'Prints applications filtered by state as CSV' 8 | 9 | def add_arguments(self, parser): 10 | parser.add_argument('-s', 11 | dest='state', 12 | default=False, 13 | help='filter by state') 14 | 15 | def handle(self, *args, **options): 16 | applications = models.HackerApplication.objects.all() 17 | 18 | if options['state']: 19 | applications = applications.filter(status=options['state']) 20 | 21 | self.stdout.write(','.join(['name', 'university', 'origin', 'email'])) 22 | for app in applications: 23 | res = [app.user.name, app.university, app.origin, app.user.email] 24 | self.stdout.write(','.join(res)) 25 | -------------------------------------------------------------------------------- /applications/migrations/0002_reimb_refactor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-08 17:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='status', 18 | field=models.CharField(choices=[('P', 'Pending'), ('R', 'Wait listed'), ('I', 'Invited'), ('LR', 'Last reminder'), ('C', 'Confirmed'), ('X', 'Cancelled'), ('A', 'Attended'), ('E', 'Expired')], default='P', max_length=2), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0003_phone.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-16 14:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0002_reimb_refactor'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='application', 17 | name='authorized_privacy', 18 | ), 19 | migrations.AddField( 20 | model_name='application', 21 | name='phone_number', 22 | field=models.CharField(blank=True, max_length=300, null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /applications/migrations/0004_empty_resume.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-17 16:35 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0003_phone'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='resume', 18 | field=models.FileField(blank=True, null=True, upload_to='resumes'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0005_reimb_amount.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-19 23:16 3 | from __future__ import unicode_literals 4 | 5 | import django.core.validators 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('applications', '0004_empty_resume'), 13 | ] 14 | 15 | operations = [ 16 | migrations.RenameField( 17 | model_name='application', 18 | old_name='scholarship', 19 | new_name='reimb', 20 | ), 21 | migrations.AddField( 22 | model_name='application', 23 | name='reimb_amount', 24 | field=models.FloatField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0, 'Negative? Really? Please put a positive value')]), 25 | ), 26 | migrations.AlterField( 27 | model_name='application', 28 | name='phone_number', 29 | field=models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(message="Phone number must be entered in the format: '+#########'. Up to 15 digits allowed.", regex='^\\+?1?\\d{9,15}$')]), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /applications/migrations/0006_origin_simplified.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-12-05 18:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0005_reimb_amount'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='application', 17 | old_name='origin_city', 18 | new_name='origin', 19 | ), 20 | migrations.RemoveField( 21 | model_name='application', 22 | name='origin_country', 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /applications/migrations/0007_team_user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-12-13 17:00 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0006_origin_simplified'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='application', 17 | name='team', 18 | ), 19 | migrations.RemoveField( 20 | model_name='application', 21 | name='teammates', 22 | ), 23 | migrations.AlterField( 24 | model_name='application', 25 | name='gender', 26 | field=models.CharField(choices=[('NA', 'Prefer not to answer'), ('M', 'Male'), ('F', 'Female'), ('NB', 'Non-binary')], default='NA', max_length=20), 27 | ), 28 | migrations.AlterField( 29 | model_name='application', 30 | name='status', 31 | field=models.CharField(choices=[('P', 'Under review'), ('R', 'Wait listed'), ('I', 'Invited'), ('LR', 'Last reminder'), ('C', 'Confirmed'), ('X', 'Cancelled'), ('A', 'Attended'), ('E', 'Expired')], default='P', max_length=2), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /applications/migrations/0008_update_apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-01-26 06:10 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0007_team_user'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='graduation_year', 18 | field=models.IntegerField(choices=[(2017, '2017'), (2018, '2018'), (2019, '2019'), (2020, '2020'), (2021, '2021'), (2022, '2022'), (2023, '2023')], default=2017), 19 | ), 20 | migrations.AlterField( 21 | model_name='application', 22 | name='tshirt_size', 23 | field=models.CharField(choices=[('XS', 'XS'), ('S', 'S'), ('M', 'M'), ('L', 'L'), ('XL', 'XL'), ('XXL', 'XXL')], default='M', max_length=3), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /applications/migrations/0009_default_reimb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-01-28 01:34 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0008_update_apps'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='reimb', 18 | field=models.BooleanField(default=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0010_draft_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-11-15 01:54 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('user', '0006_user_is_hardware_admin'), 14 | ('applications', '0009_default_reimb'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='DraftApplication', 20 | fields=[ 21 | ('content', models.CharField(max_length=1000)), 22 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /applications/migrations/0011_tshirt_mlh.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-11-16 00:42 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0010_draft_app'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='tshirt_size', 18 | field=models.CharField(choices=[("Women's - XXS", "Women's - XXS"), ("Women's - XS", "Women's - XS"), ("Women's - S", "Women's - S"), ("Women's - M", "Women's - M"), ("Women's - L", "Women's - L"), ("Women's - XL", "Women's - XL"), ("Women's - XXL", "Women's - XXL"), ('Unisex - XXS', 'Unisex - XXS'), ('Unisex - XS', 'Unisex - XS'), ('Unisex - S', 'Unisex - S'), ('Unisex - M', 'Unisex - M'), ('Unisex - L', 'Unisex - L'), ('Unisex - XL', 'Unisex - XL'), ('Unisex - XXL', 'Unisex - XXL')], default='Unisex - M', max_length=3), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0012_auto_20181205_1136.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-12-05 18:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0011_tshirt_mlh'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='draftapplication', 17 | name='content', 18 | field=models.CharField(max_length=7000), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0013_tshirts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-12-05 18:58 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0012_auto_20181205_1136'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='graduation_year', 18 | field=models.IntegerField(choices=[(2018, '2018'), (2019, '2019'), (2020, '2020'), (2021, '2021'), (2022, '2022'), (2023, '2023'), (2024, '2024')], default=2018), 19 | ), 20 | migrations.AlterField( 21 | model_name='application', 22 | name='tshirt_size', 23 | field=models.CharField(choices=[('W-XSS', "Women's - XXS"), ('W-XS', "Women's - XS"), ('W-S', "Women's - S"), ('W-M', "Women's - M"), ('W-L', "Women's - L"), ('W-XL', "Women's - XL"), ('W-XXL', "Women's - XXL"), ('XXS', 'Unisex - XXS'), ('XS', 'Unisex - XS'), ('S', 'Unisex - S'), ('M', 'Unisex - M'), ('L', 'Unisex - L'), ('XL', 'Unisex - XL'), ('XXL', 'Unisex - XXL')], default='M', max_length=3), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /applications/migrations/0014_gender_other.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-02-11 22:15 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0013_tshirts'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='application', 17 | name='other_gender', 18 | field=models.CharField(blank=True, max_length=50, null=True), 19 | ), 20 | migrations.AlterField( 21 | model_name='application', 22 | name='gender', 23 | field=models.CharField(choices=[('NA', 'Prefer not to answer'), ('M', 'Male'), ('F', 'Female'), ('NB', 'Non-binary'), ('X', 'Prefer to self-describe')], default='NA', max_length=23), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /applications/migrations/0014_tshirt_fix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-02-14 20:50 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0013_tshirts'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='application', 17 | name='tshirt_size', 18 | field=models.CharField(choices=[('W-XSS', "Women's - XXS"), ('W-XS', "Women's - XS"), ('W-S', "Women's - S"), ('W-M', "Women's - M"), ('W-L', "Women's - L"), ('W-XL', "Women's - XL"), ('W-XXL', "Women's - XXL"), ('XXS', 'Unisex - XXS'), ('XS', 'Unisex - XS'), ('S', 'Unisex - S'), ('M', 'Unisex - M'), ('L', 'Unisex - L'), ('XL', 'Unisex - XL'), ('XXL', 'Unisex - XXL')], default='M', max_length=5), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /applications/migrations/0015_merge_20190218_1144.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-02-18 18:44 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0014_tshirt_fix'), 12 | ('applications', '0014_gender_other'), 13 | ] 14 | 15 | operations = [ 16 | ] 17 | -------------------------------------------------------------------------------- /applications/migrations/0016_auto_20190611_1011.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-06-11 17:11 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('applications', '0015_merge_20190218_1144'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='application', 20 | name='assigned', 21 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assigned_dubious_applications', to=settings.AUTH_USER_MODEL), 22 | ), 23 | migrations.AddField( 24 | model_name='application', 25 | name='contacted', 26 | field=models.BooleanField(default=False), 27 | ), 28 | migrations.AlterField( 29 | model_name='application', 30 | name='first_timer', 31 | field=models.BooleanField(default=False), 32 | ), 33 | migrations.AlterField( 34 | model_name='application', 35 | name='status', 36 | field=models.CharField(choices=[('P', 'Under review'), ('R', 'Wait listed'), ('I', 'Invited'), ('LR', 'Last reminder'), ('C', 'Confirmed'), ('X', 'Cancelled'), ('A', 'Attended'), ('E', 'Expired'), ('D', 'Dubious')], default='P', max_length=2), 37 | ), 38 | ] 39 | -------------------------------------------------------------------------------- /applications/migrations/0017_application_contacted_by.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-06-25 16:25 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ('applications', '0016_auto_20190611_1011'), 15 | ] 16 | 17 | operations = [ 18 | migrations.AddField( 19 | model_name='application', 20 | name='contacted_by', 21 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dubious_applications', to=settings.AUTH_USER_MODEL), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /applications/migrations/0018_auto_20190725_0437.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-07-25 11:37 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('applications', '0017_application_contacted_by'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RemoveField( 18 | model_name='application', 19 | name='assigned', 20 | ), 21 | migrations.AlterField( 22 | model_name='application', 23 | name='contacted_by', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='contacted_by', to=settings.AUTH_USER_MODEL), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /applications/migrations/0019_auto_20200321_1814.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-03-22 01:14 3 | from __future__ import unicode_literals 4 | 5 | import applications.validators 6 | from django.db import migrations, models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('applications', '0018_auto_20190725_0437'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='application', 18 | name='resume', 19 | field=models.FileField(blank=True, null=True, upload_to='resumes', validators=[applications.validators.validate_file_extension]), 20 | ), 21 | migrations.AlterField( 22 | model_name='application', 23 | name='status', 24 | field=models.CharField(choices=[('P', 'Under review'), ('R', 'Wait listed'), ('I', 'Invited'), ('LR', 'Last reminder'), ('C', 'Confirmed'), ('X', 'Cancelled'), ('A', 'Attended'), ('E', 'Expired'), ('D', 'Dubious'), ('IV', 'Invalid')], default='P', max_length=2), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /applications/migrations/0022_auto_20200521_0418.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-05-21 11:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('applications', '0021_new_application_and_blacklist_20200520_1452'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='volunteerapplication', 17 | name='fav_movie', 18 | field=models.CharField(blank=True, max_length=60, null=True), 19 | ), 20 | migrations.AlterField( 21 | model_name='volunteerapplication', 22 | name='friends', 23 | field=models.CharField(blank=True, max_length=100, null=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /applications/migrations/0023_auto_20211002_0408.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2021-10-02 11:08 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('applications', '0022_auto_20200521_0418'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='hackerapplication', 18 | name='blacklisted_by', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blacklisted_by', to=settings.AUTH_USER_MODEL), 20 | ), 21 | migrations.AlterField( 22 | model_name='hackerapplication', 23 | name='contacted_by', 24 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contacted_by', to=settings.AUTH_USER_MODEL), 25 | ), 26 | migrations.AlterField( 27 | model_name='hackerapplication', 28 | name='invited_by', 29 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='hackerapplication_invited_applications', to=settings.AUTH_USER_MODEL), 30 | ), 31 | migrations.AlterField( 32 | model_name='mentorapplication', 33 | name='invited_by', 34 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mentorapplication_invited_applications', to=settings.AUTH_USER_MODEL), 35 | ), 36 | migrations.AlterField( 37 | model_name='sponsorapplication', 38 | name='user', 39 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sponsorapplication_application', to=settings.AUTH_USER_MODEL), 40 | ), 41 | migrations.AlterField( 42 | model_name='volunteerapplication', 43 | name='invited_by', 44 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='volunteerapplication_invited_applications', to=settings.AUTH_USER_MODEL), 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /applications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/applications/migrations/__init__.py -------------------------------------------------------------------------------- /applications/signals.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.db.models.signals import post_save, post_delete, pre_save 4 | from django.dispatch import receiver 5 | from django.forms import model_to_dict 6 | 7 | from applications import models 8 | 9 | 10 | # Delete DraftApplication when application submitted 11 | @receiver(post_save, sender=models.HackerApplication) 12 | @receiver(post_save, sender=models.MentorApplication) 13 | @receiver(post_save, sender=models.VolunteerApplication) 14 | def clean_draft_application(sender, instance, created, *args, **kwargs): 15 | if not created: 16 | return None 17 | # Delete draft as its no longer needed 18 | models.DraftApplication.objects.filter(user=instance.user).delete() 19 | 20 | 21 | # Create DraftApplication when application deleted 22 | @receiver(post_delete, sender=models.HackerApplication) 23 | @receiver(post_delete, sender=models.MentorApplication) 24 | @receiver(post_delete, sender=models.VolunteerApplication) 25 | def create_draft_application(sender, instance, *args, **kwargs): 26 | dict = model_to_dict(instance) 27 | for key in ['user', 'invited_by', 'submission_date', 'status_update_date', 'status', 'resume']: 28 | dict.pop(key, None) 29 | d = models.DraftApplication() 30 | d.user = instance.user 31 | d.save_dict(dict) 32 | d.save() 33 | 34 | 35 | # Delete resume file when application deleted 36 | @receiver(post_delete, sender=models.HackerApplication) 37 | @receiver(post_delete, sender=models.MentorApplication) 38 | def auto_delete_file_on_delete(sender, instance, **kwargs): 39 | if instance.resume: 40 | if os.path.isfile(instance.resume.path): 41 | os.remove(instance.resume.path) 42 | 43 | 44 | # Delete resume file when resume changed 45 | @receiver(pre_save, sender=models.HackerApplication) 46 | @receiver(pre_save, sender=models.MentorApplication) 47 | def auto_delete_file_on_change(sender, instance, **kwargs): 48 | if not instance.pk: 49 | return False 50 | 51 | try: 52 | old_resume = sender.objects.get(pk=instance.pk).resume 53 | except sender.DoesNotExist: 54 | return False 55 | if old_resume: 56 | new_resume = instance.resume 57 | if not old_resume == new_resume: 58 | if os.path.isfile(old_resume.path): 59 | os.remove(old_resume.path) 60 | -------------------------------------------------------------------------------- /applications/static/css/profile.css: -------------------------------------------------------------------------------- 1 | 2 | a.list-group-item { 3 | -webkit-border-radius: 0 !important; 4 | -moz-border-radius: 0 !important;; 5 | border-radius: 0 !important; 6 | border-left: solid 2px #a94442 !important; 7 | } 8 | 9 | .list-group { 10 | -webkit-border-radius: 0; 11 | -moz-border-radius: 0; 12 | border-radius: 0; 13 | width: auto; 14 | margin: 0; 15 | } 16 | 17 | .border-less { 18 | border: 0; 19 | } 20 | 21 | a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:active { 22 | border-top: 1px solid #ddd; 23 | border-bottom: 1px solid #ddd; 24 | border-right: 1px solid #ddd; 25 | border-left-width: 5px !important; 26 | padding-left: 12px; 27 | background-color: white; 28 | color: black; 29 | } 30 | 31 | 32 | 33 | .no-padding { 34 | padding: 0 !important; 35 | } 36 | 37 | .list-group-item.disabled, .list-group-item.disabled:hover { 38 | border-left-color: #777 !important; 39 | color: #777; 40 | } 41 | 42 | .list-group-item.finished, .list-group-item.finished:hover { 43 | border-left-color: green !important; 44 | } 45 | 46 | .margin-left { 47 | border-left: 1px solid #eee; 48 | } 49 | 50 | .margin-right { 51 | border-right: 1px solid #eee; 52 | } 53 | 54 | @media (max-width: 768px) { 55 | 56 | .margin-left { 57 | border-left: 0; 58 | border-top: 1px solid #eee; 59 | } 60 | 61 | .margin-right { 62 | border-right: 0; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /applications/static/css/vote.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | resize: none; 3 | } 4 | -------------------------------------------------------------------------------- /applications/static/degrees.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Biomedical Science", 3 | "Clinical Science", 4 | "Commerce", 5 | "Computer Applications", 6 | "Computer Information Systems", 7 | "Construction Technology", 8 | "Divinity", 9 | "Economics", 10 | "Education", 11 | "Engineering", 12 | "Fine Arts", 13 | "Information Systems", 14 | "Music", 15 | "Pharmacy", 16 | "Philosophy", 17 | "Social Work", 18 | "Technology", 19 | "Accountancy", 20 | "American Studies", 21 | "American Indian Studies", 22 | "Applied Psychology", 23 | "Anthropology", 24 | "Child Advocacy", 25 | "Clinical Psychology", 26 | "Forensic Psychology", 27 | "Organizational Psychology", 28 | "Aerospace Engineering", 29 | "Actuarial", 30 | "Agriculture", 31 | "Architecture", 32 | "Architectural Engineering", 33 | "Biology", 34 | "Biomedical Engineering", 35 | "Business Administration", 36 | "Business and Technology", 37 | "Chemical Engineering", 38 | "Chemistry", 39 | "Civil Engineering", 40 | "Clinical Laboratory Science", 41 | "Cognitive Science", 42 | "Computer Engineering", 43 | "Computer Science", 44 | "Construction Engineering", 45 | "Construction Management", 46 | "Criminal Justice", 47 | "Criminology", 48 | "Diagnostic Radiography", 49 | "Electrical Engineering", 50 | "Engineering Physics", 51 | "Engineering Science", 52 | "Engineering Technology", 53 | "English Literature", 54 | "Environmental Engineering", 55 | "Environmental Science", 56 | "Environmental Studies", 57 | "Food Science", 58 | "Foreign Service", 59 | "Forensic Science", 60 | "Forestry", 61 | "History", 62 | "Hospitality Management", 63 | "Human Resources Management", 64 | "Industrial Engineering", 65 | "Information Technology", 66 | "Integrated Science", 67 | "International Relations", 68 | "Journalism", 69 | "Legal Management", 70 | "Management", 71 | "Manufacturing Engineering", 72 | "Marketing", 73 | "Mathematics", 74 | "Mechanical Engineering", 75 | "Medical Technology", 76 | "Meteorology", 77 | "Microbiology", 78 | "Mining Engineering", 79 | "Molecular Biology", 80 | "Neuroscience", 81 | "Nursing", 82 | "Nutrition science", 83 | "Software Engineering", 84 | "Petroleum Engineering", 85 | "Podiatry", 86 | "Pharmacology", 87 | "Physical Therapy", 88 | "Physics", 89 | "Plant Science", 90 | "Politics", 91 | "Psychology", 92 | "Public Safety", 93 | "Quantity Surveying Engineering", 94 | "Radiologic Technology", 95 | "Real-Time Interactive Simulation", 96 | "Religion", 97 | "Respiratory Therapy", 98 | "Risk Management and Insurance", 99 | "Science Education", 100 | "Sports Management", 101 | "Systems Engineering", 102 | "Jazz Studies", 103 | "Composition", 104 | "Performance", 105 | "Theory", 106 | "Music Education", 107 | "Veterinary Technology", 108 | "EECS", 109 | "Other" 110 | ] -------------------------------------------------------------------------------- /applications/static/js/form_modifiers.js: -------------------------------------------------------------------------------- 1 | function make_field_typeahead(field_id, path_to_json) { 2 | $.ajax({ 3 | type: "GET", 4 | url: path_to_json, 5 | dataType: "json" 6 | }).done(function (res) { 7 | $("#id_" + field_id).typeahead({source: res}); 8 | }).fail(function (jqXHR, textStatus, errorThrown) { 9 | console.error("AJAX call failed: " + textStatus + ", " + errorThrown); 10 | }); 11 | } 12 | 13 | 14 | function conditional_field(field_to_hide, field_to_track, f_eval_to_show) { 15 | var parent = field_to_hide.parent('div'); 16 | field_to_track.on('change', function () { 17 | if (f_eval_to_show()) { 18 | parent.fadeIn(); 19 | } else { 20 | parent.fadeOut(); 21 | field_to_hide.val(''); 22 | } 23 | }); 24 | if (!f_eval_to_show()) { 25 | parent.hide() 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /applications/templates/application.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | 3 | {% load bootstrap3 %} 4 | {% block head_title %}Application{% endblock %} 5 | 6 | {% block panel %} 7 | 8 | 9 | {% if h_app_timeleft and application.can_be_edit %} 10 |
11 |

Time until applications close: {% include 'include/deadline_countdown.html' %}

13 |
14 | {% endif %} 15 | 16 | {% if application.can_be_edit %} 17 |

It is still possible to modify your application.

18 | {% else %} 19 |

Your application has been reviewed already. Editing has been disabled to make sure all reviewers get the 20 | same data. If you would like to change something important, please email us at {{ h_contact_email|urlize }}.

21 | {% endif %} 22 | {% include 'include/application_form.html' %} 23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /applications/templates/cancel.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | {% load static %} 3 | {% block head_title %}Cancel application{% endblock %} 4 | {% block panel %} 5 | 6 | {% if error %} 7 | 8 | {{ error }}.Please contact us if you think this is an error: {{ h_contact_email|urlize }}. 9 | Back to your profile 10 | 11 | {% else %} 12 | 13 |

Remember! After cancelling your application you will lose the right to attend the hackathon as a 14 | participant. Are you sure you want to cancel your application?

15 |
16 | {% csrf_token %} 17 | 18 | 19 |
20 | {% endif %} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /applications/templates/convert_mentor.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | 3 | {% load bootstrap3 %} 4 | {% block head_title %}Application{% endblock %} 5 | 6 | {% block panel %} 7 | 8 |
9 |

Please read this!

10 |

You can apply to come at {{ h_name }} as mentor. Be aware that if you choose to be a mentor, 11 | your hacker application will be removed. This action can't be reverted

12 |
13 | {% csrf_token %} 14 | 15 | Go back 16 |
17 |
18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /applications/templates/include/applications_closed.html: -------------------------------------------------------------------------------- 1 |

Applications have been closed. We are looking forward to welcome you on the next edition.

2 | -------------------------------------------------------------------------------- /applications/templates/include/cancel.html: -------------------------------------------------------------------------------- 1 | {% if application.can_be_cancelled %} 2 | 3 |

If you cannot make it, please let us know by clicking "Cancel". We want as many people as possible to experience 4 | the 5 | hackathon!

6 | Cancel 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /applications/templates/include/status.html: -------------------------------------------------------------------------------- 1 | {% if application %} 2 |
3 | Status: {{ application.get_soft_status_display }} 4 |
5 |
6 | {% endif %} 7 | -------------------------------------------------------------------------------- /applications/templates/mails/confirmation_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Confirmation of attendance for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

Thank you {{ name }} for confirming your attendance to {{ h_name }}!

6 |

We are really excited to host this event and have you with us.

7 | 8 |

In order to make registration go smoother, please show us this QR code during the process so we can easily find 9 | you:

10 | {% include 'mails/include/email_image.html' with url=qr_url %} 11 | {% include 'mails/include/cancel.html' %} 12 | {% include 'mails/include/arrival_departure_info.html' %} 13 | {% include 'mails/include/closing.html' %} 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /applications/templates/mails/confirmation_subject.txt: -------------------------------------------------------------------------------- 1 | Ticket to {{ h_name }} 2 | -------------------------------------------------------------------------------- /applications/templates/mails/include/arrival_departure_info.html: -------------------------------------------------------------------------------- 1 | {% if h_arrive %}

When to arrive {{ h_arrive }}

{% endif %} 2 | {% if h_leave %}

When to leave {{ h_leave }}

{% endif %} 3 | -------------------------------------------------------------------------------- /applications/templates/mails/include/cancel.html: -------------------------------------------------------------------------------- 1 |

If you cannot make it, please let us know by clicking "Cancel".

2 | {% if is_hacker %} 3 |

We want as many people as possible to experience the hackathon!

4 | {% endif %} 5 |

6 | Cancel 7 |

8 | -------------------------------------------------------------------------------- /applications/templates/mails/invitation_hacker_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Acceptance email for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

6 | Congratulations {{ name }}, you have been officially invited to {{ h_name }}! 7 |

8 |

9 | Respond to this invitation by clicking on the "Confirm" link below. 10 | Please confirm your spot within 5 days of receiving this email. Otherwise we will give your spot to another 11 | hacker on the waitlist. 12 |

13 | {% include 'mails/include/email_button.html' with text='Confirm' url=confirm_url %} 14 | {% include 'mails/include/cancel.html' %} 15 | {% if reimb.waitlisted %} 16 | {% include 'include/waitlisted_reimbursement.html' %} 17 | {% elif reimb %} 18 |

19 | Your reimbursement petition is being reviewed by our staff. Check your email in the next few days for an 20 | update. 21 |

22 | {% endif %} 23 | {% include 'mails/include/arrival_departure_info.html' %} 24 | {% include 'mails/include/closing.html' %} 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /applications/templates/mails/invitation_hacker_subject.txt: -------------------------------------------------------------------------------- 1 | Congrats! You have been accepted! -------------------------------------------------------------------------------- /applications/templates/mails/invitation_mentor_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Acceptance email for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

Hi {{ name }}!

6 |

7 | First of all, thanks for applying to our mentorship program. We are glad to inform you that you have been chosen 8 | to be a mentor during this new {{ h_name }} edition! 9 |

10 |

11 | When the event is closer, we'll contact you again and we'll invite you to our Slack group, used for organizing 12 | and hackers communication 13 |

14 |

15 | Respond to this invitation by clicking on the "Confirm" link below. 16 |

17 | {% include 'mails/include/email_button.html' with text='Confirm' url=confirm_url %} 18 | {% include 'mails/include/cancel.html' %} 19 | {% include 'mails/include/arrival_departure_info.html' %} 20 | {% include 'mails/include/closing.html' %} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /applications/templates/mails/invitation_mentor_subject.txt: -------------------------------------------------------------------------------- 1 | Congrats! You have been accepted! -------------------------------------------------------------------------------- /applications/templates/mails/invitation_volunteer_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Acceptance email for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

Hi {{ name }}!

6 |

7 | Thanks for applying as a volunteer for HackUPC 2021! Volunteering is one of the most important roles and we need people like you. 8 |

9 |

10 | Therefore, we invite you to help us achieving our goals during this hackathon. 11 |

12 |

13 | Don't forget to help us sharing HackUPC with your friends so that they can enjoy as well (sharing is caring). 14 |

15 |

16 | Respond to this invitation by clicking on the "Confirm" link below. 17 |

18 | {% include 'mails/include/email_button.html' with text='Confirm' url=confirm_url %} 19 | {% include 'mails/include/cancel.html' %} 20 | {% if reimb.waitlisted %} 21 | {% include 'include/waitlisted_reimbursement.html' %} 22 | {% elif reimb %} 23 |

24 | Your reimbursement petition is being reviewed by our staff. Check your email in the next few days for an 25 | update. 26 |

27 | {% endif %} 28 | {% include 'mails/include/arrival_departure_info.html' %} 29 | {% include 'mails/include/closing.html' %} 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /applications/templates/mails/invitation_volunteer_subject.txt: -------------------------------------------------------------------------------- 1 | Congrats! You have been accepted! -------------------------------------------------------------------------------- /applications/templates/mails/last_reminder_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %} Last reminder email for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

6 | Hi {{ name }}, 7 |

8 |

9 | We are still waiting for your answer! You have been officially accepted to {{ h_name }} 10 | but you still haven't either confirmed or canceled your spot. 11 | 12 | You have 24h to answer, after that, your application will expire and we will give your spot to another 13 | {{ type }}. 14 |

15 | {% include 'mails/include/email_button.html' with text='Confirm' url=confirm_url %} 16 | 17 | {% include 'mails/include/cancel.html' %} 18 | {% include 'mails/include/closing.html' %} 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /applications/templates/mails/last_reminder_subject.txt: -------------------------------------------------------------------------------- 1 | Confirm your attendance at {{h_name}}! 2 | -------------------------------------------------------------------------------- /applications/templates/sponsor_submitted.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | 3 | {% load bootstrap3 %} 4 | {% block head_title %}Application{% endblock %} 5 | 6 | {% block panel %} 7 | 8 |
9 |

Thank you!

10 |

You have registered to {{ h_name }}.

11 |
12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /applications/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | 4 | from applications import views 5 | 6 | urlpatterns = [ 7 | url(r'^applications/(?P[\w-]+)/confirm$', login_required(views.ConfirmApplication.as_view()), 8 | name='confirm_app'), 9 | url(r'^applications/(?P[\w-]+)/cancel$', login_required(views.CancelApplication.as_view()), 10 | name='cancel_app'), 11 | url(r'^dashboard/$', views.HackerDashboard.as_view(), name='dashboard'), 12 | url(r'^application/$', views.HackerApplication.as_view(), name='application'), 13 | url(r'^application/change_to_mentor/$', views.ConvertHackerToMentor.as_view(), name='change_to_mentor'), 14 | url(r'^application/draft/$', views.save_draft, name='save_draft'), 15 | url(r'^sponsor/(?P[0-9A-Za-z_\-]+)/(?P[\w-]+)/$', 16 | views.SponsorApplicationView.as_view(), name='sponsor_app'), 17 | url(r'^sponsor/dashboard/$', views.SponsorDashboard.as_view(), name='sponsor_dashboard'), 18 | ] 19 | -------------------------------------------------------------------------------- /applications/validators.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.core.exceptions import ValidationError 3 | from django.conf import settings 4 | 5 | 6 | def validate_file_extension(value): 7 | (_, ext) = os.path.splitext(value.name) 8 | valid_extensions = getattr(settings, 'SUPPORTED_RESUME_EXTENSIONS', None) 9 | if valid_extensions and not ext.lower() in valid_extensions: 10 | raise ValidationError('Unsupported file extension.') 11 | -------------------------------------------------------------------------------- /checkin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/checkin/__init__.py -------------------------------------------------------------------------------- /checkin/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from checkin import models 4 | 5 | 6 | # Register your models here. 7 | 8 | class CheckinAdmin(admin.ModelAdmin): 9 | list_display = ( 10 | 'user', 'type', 'application', 'update_time' 11 | ) 12 | search_fields = ('user__email', 'user__name', 'hacker__user__name', 'hacker__user__email', 'volunteer__user__name', 13 | 'volunteer__user__email', 'mentor__user__name', 'mentor__user__email', 'sponsor__user__name', 14 | 'sponsor__user__email') 15 | date_hierarchy = 'update_time' 16 | list_filter = ('user', ) 17 | actions = ['delete_selected', ] 18 | 19 | 20 | admin.site.register(models.CheckIn, admin_class=CheckinAdmin) 21 | -------------------------------------------------------------------------------- /checkin/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/checkin/management/__init__.py -------------------------------------------------------------------------------- /checkin/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/checkin/management/commands/__init__.py -------------------------------------------------------------------------------- /checkin/management/commands/add_volunteers.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import csv 4 | import sys 5 | 6 | from django.contrib.auth import authenticate 7 | from django.contrib.auth import get_user_model 8 | from django.core.management.base import BaseCommand 9 | 10 | 11 | User = get_user_model() 12 | 13 | 14 | class Command(BaseCommand): 15 | """ 16 | Format CSV: name,email,password 17 | """ 18 | help = 'Creates volunteers accounts' 19 | 20 | def add_arguments(self, parser): 21 | parser.add_argument('csv_filename', 22 | default=False, 23 | help='csv filename') 24 | 25 | def handle(self, *args, **options): 26 | 27 | with open(options['csv_filename']) as csv_f: 28 | for row in csv.reader(csv_f): 29 | name = row[0] 30 | email = row[1] 31 | password = row[2] 32 | try: 33 | user = User.objects.filter(email=email).first() 34 | 35 | if not user: 36 | print('Creating user {0}.'.format(email)) 37 | user = User.objects.create_user(name=name, email=email) 38 | user.set_password(password) 39 | else: 40 | print('Updating permissions for user {0}.'.format(email)) 41 | user.set_volunteer() 42 | user.save() 43 | assert authenticate(email=email, password=password) 44 | 45 | print('User {0} successfully created.'.format(email)) 46 | 47 | except Exception: 48 | print('There was a problem creating the user: {0}. Error: {1}.' 49 | .format(email, sys.exc_info()[1])) 50 | -------------------------------------------------------------------------------- /checkin/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-01 23:02 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ('applications', '0001_initial'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='CheckIn', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('update_time', models.DateTimeField()), 25 | ('application', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='applications.Application')), 26 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 27 | ], 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /checkin/migrations/0002_to_new_applications_20200402_1036.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-04-02 17:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('checkin', '0001_initial'), 13 | ('applications', '0020_new_applications_20200402_1036'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='checkin', 19 | name='application', 20 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='applications.HackerApplication'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /checkin/migrations/0003_new_applications_20200520_1452.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-05-20 21:52 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('applications', '0021_new_application_and_blacklist_20200520_1452'), 13 | ('checkin', '0002_to_new_applications_20200402_1036'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RemoveField( 18 | model_name='checkin', 19 | name='application', 20 | ), 21 | migrations.AddField( 22 | model_name='checkin', 23 | name='hacker', 24 | field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.HackerApplication'), 25 | ), 26 | migrations.AddField( 27 | model_name='checkin', 28 | name='mentor', 29 | field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.MentorApplication'), 30 | ), 31 | migrations.AddField( 32 | model_name='checkin', 33 | name='sponsor', 34 | field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.SponsorApplication'), 35 | ), 36 | migrations.AddField( 37 | model_name='checkin', 38 | name='volunteer', 39 | field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.VolunteerApplication'), 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /checkin/migrations/0004_alter_checkin_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2021-10-02 11:08 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('checkin', '0003_new_applications_20200520_1452'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='checkin', 18 | name='user', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /checkin/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/checkin/migrations/__init__.py -------------------------------------------------------------------------------- /checkin/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.db import models 4 | from django.utils import timezone 5 | 6 | from applications.models import APP_CONFIRMED, APP_ATTENDED 7 | from user.models import User 8 | 9 | 10 | class CheckIn(models.Model): 11 | hacker = models.OneToOneField('applications.HackerApplication', null=True, on_delete=models.CASCADE) 12 | mentor = models.OneToOneField('applications.MentorApplication', null=True, on_delete=models.CASCADE) 13 | volunteer = models.OneToOneField('applications.VolunteerApplication', null=True, on_delete=models.CASCADE) 14 | sponsor = models.OneToOneField('applications.SponsorApplication', null=True, on_delete=models.CASCADE) 15 | user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) 16 | update_time = models.DateTimeField() 17 | 18 | @property 19 | def application(self): 20 | if self.hacker_id: 21 | return self.hacker 22 | if self.mentor_id: 23 | return self.mentor 24 | if self.volunteer_id: 25 | return self.volunteer 26 | if self.sponsor_id: 27 | return self.sponsor 28 | return None 29 | 30 | def set_application(self, app): 31 | if app.user.is_hacker(): 32 | self.hacker = app 33 | elif app.user.is_volunteer(): 34 | self.volunteer = app 35 | elif app.user.is_mentor(): 36 | self.mentor = app 37 | elif app.user.is_sponsor(): 38 | self.sponsor = app 39 | else: 40 | raise ValueError 41 | 42 | def save(self, force_insert=False, force_update=False, using=None, 43 | update_fields=None): 44 | if (self.hacker and self.sponsor is None and self.volunteer is None and self.mentor is None) or \ 45 | (self.hacker is None and self.sponsor and self.volunteer is None and self.mentor is None) or \ 46 | (self.hacker is None and self.sponsor is None and self.volunteer and self.mentor is None) or \ 47 | (self.hacker is None and self.sponsor is None and self.volunteer is None and self.mentor): 48 | self.update_time = timezone.now() 49 | super(CheckIn, self).save(force_insert, force_update, using, 50 | update_fields) 51 | self.application.status = APP_ATTENDED 52 | else: 53 | raise ValueError 54 | 55 | def delete(self, using=None, keep_parents=False): 56 | app = self.application 57 | app.status = APP_CONFIRMED 58 | app.save() 59 | return super(CheckIn, self).delete(using, keep_parents) 60 | 61 | def type(self): 62 | return self.application.user.get_type_display() 63 | -------------------------------------------------------------------------------- /checkin/static/css/checkin.css: -------------------------------------------------------------------------------- 1 | .button-qr { 2 | vertical-align: middle; 3 | padding: 5px 7px 3px; 4 | border: thin solid #ddd; 5 | cursor: pointer; 6 | float: left; 7 | font-size: 24px; 8 | border-radius: 4px; 9 | } 10 | 11 | .button-qr-success { 12 | color: #3c763d; 13 | border-color: #3c763d; 14 | } 15 | 16 | .input-qr { 17 | display: block; 18 | overflow: hidden; 19 | padding-left: 10px; 20 | } 21 | -------------------------------------------------------------------------------- /checkin/static/js/form_modifiers.js: -------------------------------------------------------------------------------- 1 | function make_field_typeahead(field_id, path_to_json) { 2 | $.ajax({ 3 | type: "GET", 4 | url: path_to_json, 5 | dataType: "json" 6 | }).done(function (res) { 7 | $("#id_" + field_id).typeahead({source: res}); 8 | }).fail(function (jqXHR, textStatus, errorThrown) { 9 | console.error("AJAX call failed: " + textStatus + ", " + errorThrown); 10 | }); 11 | } 12 | 13 | 14 | function conditional_field(field_to_hide, field_to_track, f_eval_to_show, parent_num = 1) { 15 | var parent = field_to_hide; 16 | for(var i=0; i < parent_num; i++){ 17 | parent = parent.parent(); 18 | } 19 | field_to_track.on('change', function () { 20 | if (f_eval_to_show()) { 21 | parent.fadeIn(); 22 | } else { 23 | parent.fadeOut(); 24 | field_to_hide.val(''); 25 | } 26 | }); 27 | if (!f_eval_to_show()) { 28 | parent.hide() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /checkin/tables.py: -------------------------------------------------------------------------------- 1 | import django_filters 2 | import django_tables2 as tables 3 | from django.db.models import Q 4 | 5 | from applications.models import BaseApplication, SponsorApplication 6 | 7 | 8 | class ApplicationCheckinFilter(django_filters.FilterSet): 9 | search = django_filters.CharFilter(method='search_filter', label='Search') 10 | 11 | def search_filter(self, queryset, name, value): 12 | return queryset.filter(Q(user__email__icontains=value) | Q(user__name__icontains=value) | 13 | Q(uuid__icontains=value.replace('-', ''))) 14 | 15 | class Meta: 16 | model = BaseApplication 17 | fields = ['search', ] 18 | 19 | 20 | class ApplicationsCheckInTable(tables.Table): 21 | detail = tables.TemplateColumn( 22 | "Check-in ", 23 | verbose_name='Actions', ) 24 | 25 | class Meta: 26 | model = BaseApplication 27 | attrs = {'class': 'table table-hover'} 28 | template = 'django_tables2/bootstrap-responsive.html' 29 | fields = ['user.name', 'user.email'] 30 | empty_text = 'All users checked in! Yay!' 31 | 32 | 33 | class SponsorApplicationCheckinFilter(django_filters.FilterSet): 34 | search = django_filters.CharFilter(method='search_filter', label='Search') 35 | 36 | def search_filter(self, queryset, name, value): 37 | return queryset.filter(Q(user__email__icontains=value) | Q(user__name__icontains=value) | 38 | Q(name__icontains=value)) 39 | 40 | class Meta: 41 | model = SponsorApplication 42 | fields = ['search', ] 43 | 44 | 45 | class SponsorApplicationsCheckInTable(tables.Table): 46 | company = tables.Column(verbose_name='Company', accessor='user.name') 47 | detail = tables.TemplateColumn( 48 | "Check-in ", 49 | verbose_name='Actions', ) 50 | 51 | class Meta: 52 | model = SponsorApplication 53 | attrs = {'class': 'table table-hover'} 54 | template = 'django_tables2/bootstrap-responsive.html' 55 | fields = ['name', 'user.email'] 56 | empty_text = 'All users checked in! Yay!' 57 | -------------------------------------------------------------------------------- /checkin/templates/checkin/hacker.html: -------------------------------------------------------------------------------- 1 | {% extends "base_tabs.html" %} 2 | {% block head_title %}Check In {{ app.user.name }}{% endblock %} 3 | {% block panel %} 4 | 5 |
6 | {% include 'include/field.html' with desc='Lenny face' value=app.lennyface %} 7 | {% include 'include/field.html' with desc='Type' value=app.user.get_type_display %} 8 | {% if app.user.is_sponsor %} 9 | {% include 'include/field.html' with desc='Name' value=app.name %} 10 | {% include 'include/field.html' with desc='Company' value=app.user.name %} 11 | {% else %} 12 | {% include 'include/field.html' with desc='Name' value=app.user.name %} 13 | {% endif %} 14 | 15 | {% include 'include/field.html' with desc='Email' value=app.user.email %} 16 | {% include 'include/field.html' with desc='Shirt Size' value=app.tshirt_size %} 17 | {% include 'include/field.html' with desc='Diet' value=app.diet %} 18 | {% include 'include/field.html' with desc='Other' value=app.other_diet %} 19 | {% include 'include/field.html' with desc='Status' value=app.get_status_display %} 20 |
21 | 22 | {% endblock %} 23 | {% block cols-panel %}col-md-6 col-md-offset-3{% endblock %} 24 | {% block out_panel %} 25 | 52 | {% endblock %} 53 | -------------------------------------------------------------------------------- /checkin/templates/checkin/list.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | 3 | {% load static %} 4 | {% load bootstrap3 %} 5 | 6 | {% block extra_head %} 7 | 8 | 9 | 10 | {% endblock %} 11 | 12 | {% block head_title %}Check-in{% endblock %} 13 | 14 | {% block panel %} 15 |
16 | 20 |
21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /checkin/templates/checkin/ranking.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% load humanize %} 3 | {% load static %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}Volunteers Ranking{% endblock %} 6 | {% block extra_panel %} 7 |

So you think you have checked in tons of hackers, huh?

8 |

Drop your self esteem and look at the ranking.

9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /checkin/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from checkin import views 3 | 4 | urlpatterns = [ 5 | url(r'^hacker/all/$', views.CheckInList.as_view(), name='check_in_list'), 6 | url(r'(?P[a-z_\-]{1,10})/(?P[\w-]+)$', views.CheckInHackerView.as_view(), 7 | name='check_in_hacker'), 8 | url(r'^volunteer/all/$', views.CheckinVolunteerList.as_view(), name='check_in_volunteer_list'), 9 | url(r'^mentor/all/$', views.CheckinMentorList.as_view(), name='check_in_mentor_list'), 10 | url(r'^sponsor/all/$', views.CheckinSponsorList.as_view(), name='check_in_sponsor_list'), 11 | ] 12 | -------------------------------------------------------------------------------- /documentation/UML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/documentation/UML.png -------------------------------------------------------------------------------- /files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/files/.keep -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | django 16 | except ImportError: 17 | raise ImportError( 18 | "Couldn't import Django. Are you sure it's installed and " 19 | "available on your PYTHONPATH environment variable? Did you " 20 | "forget to activate a virtual environment?" 21 | ) 22 | raise 23 | execute_from_command_line(sys.argv) 24 | -------------------------------------------------------------------------------- /management.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PROD_MODE="True" 3 | # Set up your postgres password 4 | export PG_PWD="password" 5 | # If using Sendgrid set api key here 6 | export SG_KEY="API_KEY" 7 | # Domain where running 8 | export DOMAIN="my.hackupc.com" 9 | 10 | ./env/bin/python manage.py expire_applications && ./env/bin/python manage.py expire_reimbursements 11 | -------------------------------------------------------------------------------- /organizers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/organizers/__init__.py -------------------------------------------------------------------------------- /organizers/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from organizers import models 5 | 6 | 7 | class CommentAdmin(admin.ModelAdmin): 8 | list_display = ('application', 'author', 'text') 9 | list_per_page = 200 10 | actions = ['delete_selected', ] 11 | date_hierarchy = 'created_at' 12 | 13 | 14 | class VoteAdmin(admin.ModelAdmin): 15 | list_display = ('application', 'user', 'tech', 'personal', 'calculated_vote') 16 | list_per_page = 200 17 | list_filter = ('user', 'application') 18 | search_fields = ('application__user__name', 'application__user__email', 'user__name', 'user__email') 19 | actions = ['delete_selected', ] 20 | 21 | 22 | admin.site.register(models.ApplicationComment, admin_class=CommentAdmin) 23 | admin.site.register(models.Vote, admin_class=VoteAdmin) 24 | -------------------------------------------------------------------------------- /organizers/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-01 23:02 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ('applications', '0001_initial'), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='ApplicationComment', 23 | fields=[ 24 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 25 | ('text', models.CharField(max_length=500)), 26 | ('created_at', models.DateTimeField(default=django.utils.timezone.now)), 27 | ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applications.Application')), 28 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Vote', 33 | fields=[ 34 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('tech', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9'), (10, '10')], null=True)), 36 | ('personal', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9'), (10, '10')], null=True)), 37 | ('calculated_vote', models.FloatField(null=True)), 38 | ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applications.Application')), 39 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 40 | ], 41 | ), 42 | migrations.AlterUniqueTogether( 43 | name='vote', 44 | unique_together=set([('application', 'user')]), 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /organizers/migrations/0002_to_new_applications_20200402_1036.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-04-02 17:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('organizers', '0001_initial'), 13 | ('applications', '0020_new_applications_20200402_1036'), 14 | ] 15 | 16 | operations = [ 17 | migrations.AlterField( 18 | model_name='applicationcomment', 19 | name='application', 20 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applications.HackerApplication'), 21 | ), 22 | migrations.AlterField( 23 | model_name='vote', 24 | name='application', 25 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applications.HackerApplication'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /organizers/migrations/0003_application_comment_to_all_applications_20200520_1452.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-05-20 21:52 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('applications', '0021_new_application_and_blacklist_20200520_1452'), 13 | ('organizers', '0002_to_new_applications_20200402_1036'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RemoveField( 18 | model_name='applicationcomment', 19 | name='application', 20 | ), 21 | migrations.AddField( 22 | model_name='applicationcomment', 23 | name='hacker', 24 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.HackerApplication'), 25 | ), 26 | migrations.AddField( 27 | model_name='applicationcomment', 28 | name='mentor', 29 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.MentorApplication'), 30 | ), 31 | migrations.AddField( 32 | model_name='applicationcomment', 33 | name='sponsor', 34 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.SponsorApplication'), 35 | ), 36 | migrations.AddField( 37 | model_name='applicationcomment', 38 | name='volunteer', 39 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='applications.VolunteerApplication'), 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /organizers/migrations/0004_alter_vote_user.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2021-10-02 11:08 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('organizers', '0003_application_comment_to_all_applications_20200520_1452'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='vote', 18 | name='user', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /organizers/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/organizers/migrations/__init__.py -------------------------------------------------------------------------------- /organizers/templates/applications_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% load django_tables2 %} 3 | {% block head_title %}All application{% endblock %} 4 | {% block extra_head %} 5 | 13 | {% endblock %} 14 | 15 | {% block table_footer %} 16 | {% if not otherApplication and user.is_director %} 17 | 22 | {% elif otherApplication and emailCopy %} 23 |
24 |
25 | 26 |
27 |
28 | Copy mails 29 |
30 |
31 | {% elif createUser %} 32 |
33 | Create new Sponsor 34 |
35 | {% endif %} 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /organizers/templates/blacklist_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% load django_tables2 %} 3 | {% block head_title %}All application{% endblock %} 4 | 5 | {% block table_footer %} 6 | {% if user.has_blacklist_access %} 7 | 11 | {% endif %} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /organizers/templates/dubious_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% load django_tables2 %} 3 | {% block head_title %}All application{% endblock %} 4 | 5 | {% block table_footer %} 6 | {% if user.has_dubious_access %} 7 | 11 | {% endif %} 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /organizers/templates/include/field.html: -------------------------------------------------------------------------------- 1 | {% if value %} 2 |
{{ desc }} {% if showlength %} ({{ value | length}}) {% endif %}
3 |
{{ value }}
4 | {% endif %} 5 | -------------------------------------------------------------------------------- /organizers/templates/include/number10.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | 9 | 10 | -------------------------------------------------------------------------------- /organizers/templates/invite_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | 3 | {% block head_title %}Invite {% if teams %}teams{% else %}applications{% endif %} {% endblock %} 4 | 5 | 6 | {% block table_title %}Invite {% if teams %}teams{% else %}applications{% endif %}{% endblock %} 7 | 8 | 9 | {% block table_footer %} 10 | 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /organizers/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from organizers import views 4 | 5 | urlpatterns = [ 6 | url(r'^hacker/review/$', views.ReviewApplicationView.as_view(), name='review'), 7 | url(r'^hacker/(?P[\w-]+)$', views.ApplicationDetailView.as_view(), name="app_detail"), 8 | url(r'^hacker/all/$', views.ApplicationsListView.as_view(), name="app_list"), 9 | url(r'^hacker/all/invite/$', views.InviteListView.as_view(), name="invite_list"), 10 | url(r'^hacker/all/invite/teams/$', views.InviteTeamListView.as_view(), name="invite_teams_list"), 11 | url(r'^hacker/dubious/$', views.DubiousApplicationsListView.as_view(), name="dubious"), 12 | url(r'^volunteer/all/$', views.VolunteerApplicationsListView.as_view(), name="volunteer_list"), 13 | url(r'^volunteer/(?P[\w-]+)$', views.ReviewVolunteerApplicationView.as_view(), name="volunteer_detail"), 14 | url(r'^sponsor/all/$', views.SponsorApplicationsListView.as_view(), name="sponsor_list"), 15 | url(r'^sponsor/(?P[\w-]+)$', views.ReviewSponsorApplicationView.as_view(), name="sponsor_detail"), 16 | url(r'^mentor/all/$', views.MentorApplicationsListView.as_view(), name="mentor_list"), 17 | url(r'^mentor/(?P[\w-]+)$', views.ReviewMentorApplicationView.as_view(), name="mentor_detail"), 18 | url(r'^user/sponsor/all/$', views.SponsorUserListView.as_view(), name="sponsor_user_list"), 19 | url(r'^hacker/blacklist/$', views.BlacklistApplicationsListView.as_view(), name="blacklist"), 20 | ] 21 | -------------------------------------------------------------------------------- /reimbursement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/reimbursement/__init__.py -------------------------------------------------------------------------------- /reimbursement/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class ReimbursementConfig(AppConfig): 7 | name = 'reimbursement' 8 | 9 | def ready(self): 10 | super(ReimbursementConfig, self).ready() 11 | from .signals import reimbursement_create 12 | reimbursement_create 13 | -------------------------------------------------------------------------------- /reimbursement/emails.py: -------------------------------------------------------------------------------- 1 | from app import emails 2 | from app.utils import reverse 3 | 4 | 5 | def create_reimbursement_email(reimb, request): 6 | app = reimb.hacker.application 7 | c = _get_context(app, reimb, request) 8 | return emails.render_mail('mails/reimbursement', reimb.hacker.email, c) 9 | 10 | 11 | def create_reject_receipt_email(reimb, request): 12 | app = reimb.hacker.application 13 | c = _get_context(app, reimb, request) 14 | return emails.render_mail('mails/reject_receipt', reimb.hacker.email, c, from_email=reimb.reimbursed_by.email) 15 | 16 | 17 | def create_no_reimbursement_email(reimb, request): 18 | app = reimb.hacker.application 19 | c = _get_context(app, reimb, request) 20 | return emails.render_mail('mails/no_reimbursement', reimb.hacker.email, c) 21 | 22 | 23 | def _get_context(app, reimb, request): 24 | return { 25 | 'app': app, 26 | 'reimb': reimb, 27 | 'confirm_url': str(reverse('confirm_app', kwargs={'id': app.uuid_str}, request=request)), 28 | 'form_url': str(reverse('reimbursement_dashboard', request=request)), 29 | 'cancel_url': str(reverse('cancel_app', kwargs={'id': app.uuid_str}, request=request)) 30 | } 31 | -------------------------------------------------------------------------------- /reimbursement/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/reimbursement/management/__init__.py -------------------------------------------------------------------------------- /reimbursement/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/reimbursement/management/commands/__init__.py -------------------------------------------------------------------------------- /reimbursement/management/commands/expire_reimbursements.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.utils import timezone 3 | 4 | from reimbursement import models 5 | 6 | 7 | class Command(BaseCommand): 8 | help = 'Checks reimbursements that have expired' 9 | 10 | def handle(self, *args, **options): 11 | self.stdout.write('Checking expired reimbursements...') 12 | reimbs = models.Reimbursement.objects.filter( 13 | expiration_time__lte=timezone.now(), status=models.RE_PEND_TICKET) 14 | self.stdout.write('Checking expired reimbursements...%s found' % reimbs.count()) 15 | for reimb in reimbs: 16 | reimb.expire() 17 | -------------------------------------------------------------------------------- /reimbursement/migrations/0001_reimb_refactor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-08 17:43 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ('user', '0002_auto_20171101_1602'), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='Reimbursement', 23 | fields=[ 24 | ('assigned_money', models.FloatField()), 25 | ('reimbursement_money', models.FloatField(blank=True, null=True)), 26 | ('public_comment', models.CharField(blank=True, max_length=300, null=True)), 27 | ('paypal_email', models.EmailField(blank=True, max_length=254, null=True)), 28 | ('receipt', models.FileField(blank=True, null=True, upload_to=b'')), 29 | ('multiple_hackers', models.BooleanField(default=False)), 30 | ('friend_emails', models.CharField(blank=True, max_length=400, null=True)), 31 | ('origin_city', models.CharField(max_length=300)), 32 | ('origin_country', models.CharField(max_length=300)), 33 | ('hacker', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)), 34 | ('expiration_time', models.DateTimeField(blank=True, null=True)), 35 | ('update_time', models.DateTimeField(default=django.utils.timezone.now)), 36 | ('creation_time', models.DateTimeField(default=django.utils.timezone.now)), 37 | ('status', models.CharField(choices=[('D', 'Pending review'), ('W', 'Wait listed'), ('PT', 'Pending ticket submission'), ('PA', 'Pending ticket approval'), ('A', 'Ticket approved'), ('FS', 'Friend submission')], default='D', max_length=2)), 38 | ('friend_submission', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='friend_submissions', to='reimbursement.Reimbursement')), 39 | ('reimbursed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reimbursements_made', to=settings.AUTH_USER_MODEL)), 40 | ], 41 | ), 42 | ] 43 | -------------------------------------------------------------------------------- /reimbursement/migrations/0002_reimbursement_venmo_user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-11 05:06 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('reimbursement', '0001_reimb_refactor'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='reimbursement', 17 | name='venmo_user', 18 | field=models.CharField(max_length=40,null=True, blank=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /reimbursement/migrations/0003_rename_user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-16 05:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('reimbursement', '0002_reimbursement_venmo_user'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='reimbursement', 17 | name='receipt', 18 | field=models.FileField(blank=True, null=True, upload_to='receipt'), 19 | ), 20 | migrations.AlterField( 21 | model_name='reimbursement', 22 | name='status', 23 | field=models.CharField(choices=[('D', 'Pending review'), ('W', 'Wait listed'), ('PT', 'Pending receipt submission'), ('PA', 'Pending receipt approval'), ('A', 'Receipt approved'), ('FS', 'Friend submission')], default='D', max_length=2), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /reimbursement/migrations/0004_origin_simplified.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-12-05 18:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('reimbursement', '0003_rename_user'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='reimbursement', 17 | old_name='origin_city', 18 | new_name='origin', 19 | ), 20 | migrations.RemoveField( 21 | model_name='reimbursement', 22 | name='origin_country', 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /reimbursement/migrations/0005_address.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-01-29 18:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('reimbursement', '0004_origin_simplified'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='reimbursement', 17 | name='address', 18 | field=models.CharField(blank=True, max_length=300, null=True), 19 | ), 20 | migrations.AlterField( 21 | model_name='reimbursement', 22 | name='status', 23 | field=models.CharField(choices=[('D', 'Pending review'), ('W', 'Wait listed'), ('PT', 'Pending receipt submission'), ('PA', 'Pending receipt approval'), ('A', 'Receipt approved'), ('X', 'Expired'), ('FS', 'Friend submission')], default='D', max_length=2), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /reimbursement/migrations/0006_alter_reimbursement_reimbursed_by.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2021-10-02 11:08 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('reimbursement', '0005_address'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='reimbursement', 18 | name='reimbursed_by', 19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reimbursements_made', to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /reimbursement/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/reimbursement/migrations/__init__.py -------------------------------------------------------------------------------- /reimbursement/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save 2 | from django.dispatch import receiver 3 | from django.utils import timezone 4 | 5 | from applications.models import HackerApplication 6 | from reimbursement.models import Reimbursement, RE_EXPIRED, RE_PEND_TICKET 7 | 8 | 9 | @receiver(post_save, sender=HackerApplication) 10 | def reimbursement_create(sender, instance, created, *args, **kwargs): 11 | exists = Reimbursement.objects.filter(hacker=instance.user).exists() 12 | if not instance.reimb and exists: 13 | Reimbursement.objects.get(hacker=instance.user).delete() 14 | return 15 | if not instance.reimb: 16 | return 17 | if exists: 18 | reimb = Reimbursement.objects.get(hacker=instance.user) 19 | else: 20 | reimb = Reimbursement() 21 | reimb.generate_draft(instance) 22 | 23 | 24 | @receiver(post_save, sender=Reimbursement) 25 | def reimbursement_unexpire(sender, instance, created, *args, **kwargs): 26 | if instance.status == RE_EXPIRED and instance.expiration_time and instance.expiration_time > timezone.now(): 27 | instance.status = RE_PEND_TICKET 28 | instance.save() 29 | -------------------------------------------------------------------------------- /reimbursement/tables.py: -------------------------------------------------------------------------------- 1 | import django_filters 2 | import django_tables2 as tables 3 | from django import forms 4 | from django.db.models import Q 5 | 6 | from reimbursement.models import Reimbursement, RE_STATUS 7 | 8 | 9 | class ReimbursementFilter(django_filters.FilterSet): 10 | search = django_filters.CharFilter(method='search_filter', label='Search') 11 | status = django_filters.MultipleChoiceFilter('status', label='Status', choices=RE_STATUS, 12 | widget=forms.CheckboxSelectMultiple) 13 | 14 | def search_filter(self, queryset, name, value): 15 | return queryset.filter( 16 | Q(hacker__email__icontains=value) | Q(hacker__name__icontains=value) | Q(origin__icontains=value) 17 | ) 18 | 19 | class Meta: 20 | model = Reimbursement 21 | fields = ['search', 'status'] 22 | 23 | 24 | class ReimbursementTable(tables.Table): 25 | detail = tables.TemplateColumn( 26 | "Detail ", 27 | verbose_name='Actions', orderable=False) 28 | 29 | class Meta: 30 | model = Reimbursement 31 | attrs = {'class': 'table table-hover'} 32 | template = 'django_tables2/bootstrap-responsive.html' 33 | fields = ['hacker.name', 'hacker.email', 'assigned_money', 'reimbursement_money', 'status', 'origin', ] 34 | empty_text = 'No reimbursement match criteria' 35 | 36 | 37 | class SendReimbursementFilter(django_filters.FilterSet): 38 | search = django_filters.CharFilter(method='search_filter', label='Search') 39 | 40 | def search_filter(self, queryset, name, value): 41 | return queryset.filter( 42 | Q(hacker__email__icontains=value) | Q(hacker__name__icontains=value) | Q(origin__icontains=value) 43 | ) 44 | 45 | class Meta: 46 | model = Reimbursement 47 | fields = ['search', ] 48 | 49 | 50 | class SendReimbursementTable(tables.Table): 51 | detail = tables.TemplateColumn( 52 | "Open ", 53 | verbose_name='Actions', orderable=False) 54 | 55 | selected = tables.CheckBoxColumn(accessor="pk", verbose_name='Select') 56 | assigned_money = tables.TemplateColumn( 57 | " ", 58 | verbose_name='Assigned money', orderable=False) 59 | 60 | class Meta: 61 | model = Reimbursement 62 | attrs = {'class': 'table table-hover'} 63 | template = 'django_tables2/bootstrap-responsive.html' 64 | fields = ['selected', 'assigned_money', 'hacker.email', 'origin', ] 65 | 66 | empty_text = 'No reimbursement match criteria' 67 | -------------------------------------------------------------------------------- /reimbursement/templates/include/r_status.html: -------------------------------------------------------------------------------- 1 | {% if reimbursement %} 2 | 5 |
6 | {% endif %} 7 | -------------------------------------------------------------------------------- /reimbursement/templates/include/reimbursement_form.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 9 | 10 | 11 | 12 | 13 | 14 | 39 | -------------------------------------------------------------------------------- /reimbursement/templates/include/waitlisted_reimbursement.html: -------------------------------------------------------------------------------- 1 |

Unfortunately, we don't have enough budget to cover your travel expenses. We would like to have enough money for 2 | everyone but our budget is limited. We added you to our travel reimbursement wait list. 3 | Hope you can still make it! If you can't, please cancel your application invite.

4 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/include/confirm_reimbursement.html: -------------------------------------------------------------------------------- 1 | {% if app.is_invited %} 2 | 3 |

4 | In order to receive your travel reimbursement, remember to confirm your attendance as soon as possible. Here you 5 | can 6 | do it: 7 |

8 |

9 | Confirm 10 |

11 | {% endif %} 12 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/no_reimbursement_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Travel reimbursement update for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

Dear {{ app.user.get_full_name }},

6 |

Unfortunately, we aren’t able to offer you travel reimbursement for this edition of {{ h_name }}. 7 | We still hope you can join us at {{ h_name }}!

8 |

Please remember to confirm your spot if you will be joining us. If your plans 9 | have changed, please let us know by cancelling your invite here. 10 |

11 |

12 | If you need an invite letter for your visa, answer this email with your full name, university name and passport 13 | number, 14 | as shown in your passport. 15 |

16 | {% include 'mails/include/closing.html' %} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/no_reimbursement_subject.txt: -------------------------------------------------------------------------------- 1 | Reimbursement update 2 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/reimbursement_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Travel reimbursement award for {{ h_name }}{% endblock %} 3 | 4 | {% block content %} 5 |

Congrats {{ app.user.get_full_name }}, you have been granted travel reimbursement for this edition of {{ h_name }}. We will 6 | reimburse you {{ h_currency }}{{ reimb.assigned_money }} as you are coming from {{ reimb.origin }}. 7 | You have {{ h_r_days }} days to submit a receipt of your tickets. 8 |

9 | {% include 'mails/include/email_button.html' with text='Submit receipt' url=form_url %} 10 | {% include 'mails/include/confirm_reimbursement.html' %} 11 |

12 | If you need an invite letter for your visa, answer this email with your full name, university name and passport number, 13 | as shown in your passport. 14 |

15 | {% include 'mails/include/closing.html' %} 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/reimbursement_subject.txt: -------------------------------------------------------------------------------- 1 | Reimbursement granted -------------------------------------------------------------------------------- /reimbursement/templates/mails/reject_receipt_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Receipt is rejected{% endblock %} 3 | 4 | {% block content %} 5 |

Hey {{ app.user.get_full_name }},

6 |
7 |

The receipt you submitted is invalid.

8 |

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

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

12 | Please submit a valid receipt to be reimbursed. I have extended the deadline so you can submit a valid receipt. 13 | You have {{ h_r_days }} more days to submit a valid receipt. 14 |

15 | {% include 'mails/include/confirm_reimbursement.html' %} 16 | 17 |

Best,


18 |

19 | {{ reimb.reimbursed_by.get_full_name }}
20 | Organizer, {{ h_name }} 21 |

22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /reimbursement/templates/mails/reject_receipt_subject.txt: -------------------------------------------------------------------------------- 1 | Receipt rejected 2 | -------------------------------------------------------------------------------- /reimbursement/templates/reimbursement_send_table.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% block head_title %}Send reimbursements{% endblock %} 3 | 4 | {% block table_title %}Reimbursements eligible{% endblock %} 5 | {% block table_footer %} 6 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /reimbursement/templates/reimbursements_table.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% block head_title %}All Reimbursements{% endblock %} 3 | 4 | {% block table_title %}All Reimbursements{% endblock %} 5 | {% block table_footer %} 6 | {% if user.is_director %} 7 | 10 | {% endif %} 11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /reimbursement/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from reimbursement import views 4 | 5 | urlpatterns = [ 6 | url(r'^dash_board/$', views.ReimbursementHacker.as_view(), name='reimbursement_dashboard'), 7 | url(r'^hacker/review/$', views.ReceiptReview.as_view(), name='receipt_review'), 8 | url(r'^hacker/all/send/$', views.SendReimbursementListView.as_view(), name='send_reimbursement'), 9 | url(r'^hacker/all/$', views.ReimbursementListView.as_view(), name='reimbursement_list'), 10 | url(r'^hacker/(?P[\w-]+)$', views.ReimbursementDetail.as_view(), name='reimbursement_detail'), 11 | ] 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.0 2 | cachetools==3.1.1 3 | certifi==2019.9.11 4 | chardet==3.0.4 5 | defusedxml==0.6.0 6 | dj-database-url==0.5.0 7 | Django==3.2.7 8 | django-bootstrap3==15.0.0 9 | django-filter==2.4.0 10 | django-multiselectfield==0.1.12 11 | django-storages @ git+https://github.com/jschneier/django-storages.git@b116e3a235323144cda6d3cc5a5cb27baf076ee2 12 | django-tables2==2.4.0 13 | dropbox==8.7.1 14 | et-xmlfile==1.0.1 15 | flake8==3.9.2 16 | google-api-python-client==1.7.9 17 | google-auth==1.6.3 18 | google-auth-httplib2==0.0.3 19 | gunicorn==19.6.0 20 | httplib2>=0.19.0 21 | idna==2.7 22 | jdcal==1.4.1 23 | mccabe==0.6.1 24 | oauth2client==4.0.0 25 | oauthlib==2.0.2 26 | odfpy==1.4.0 27 | openpyxl==2.6.4 28 | packaging==16.8 29 | psycopg2-binary~=2.8.3 30 | pyasn1==0.4.8 31 | pyasn1-modules==0.2.1 32 | pycodestyle==2.7.0 33 | pyparsing==2.4.7 34 | python-http-client==2.2.1 35 | python-openid==2.2.5 36 | pytz==2017.2 37 | PyYAML>=5.4 38 | requests==2.26.0 39 | requests-oauthlib==0.8.0 40 | rsa>=4.7 41 | sendgrid==3.6.3 42 | sendgrid-django==4.0.4 43 | six==1.10.0 44 | tablib==0.12.1 45 | unicodecsv==0.14.1 46 | uritemplate==3.0.0 47 | urllib3>=1.26.5 48 | uuid==1.30 49 | whitenoise==5.3.0 50 | xlrd==1.2.0 51 | xlwt==1.3.0 52 | -------------------------------------------------------------------------------- /restart.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export PROD_MODE="True" 3 | # Set up your postgres password 4 | export PG_PWD="password" 5 | # Domain where running 6 | export DOMAIN="my.hackupc.com" 7 | 8 | 9 | echo "checking updates..." 10 | ./env/bin/pip install -r requirements.txt 11 | echo "checking updates...done" 12 | echo "migrating db..." 13 | ./env/bin/python manage.py migrate 14 | echo "migrating db...done" 15 | echo "collecting static..." 16 | ./env/bin/python manage.py collectstatic --no-input 17 | echo "collecting static...done" 18 | echo "removing all pyc..." 19 | find . -name \*.pyc -delete 20 | echo "removing all pyc...done" 21 | echo "Deploy completed. The game is on!" 22 | -------------------------------------------------------------------------------- /server.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export PROD_MODE="True" 3 | # You can create it by running os.urandom(24) in Python 4 | export SECRET="SECRET" 5 | # Set up your postgres password 6 | export PG_PWD="password" 7 | # If using Sendgrid set api key here 8 | export SG_KEY="API_KEY" 9 | # Optional: Slack token and team 10 | export SL_TOKEN="TOKEN" 11 | export SL_TEAM="test-team" 12 | # Domain where running 13 | export DOMAIN="my.hackupc.com" 14 | 15 | ./env/bin/gunicorn --workers 3 --log-file=gunicorn.log --bind unix:backend.sock app.wsgi:application 16 | -------------------------------------------------------------------------------- /stats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/stats/__init__.py -------------------------------------------------------------------------------- /stats/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class StatsConfig(AppConfig): 5 | name = 'stats' 6 | -------------------------------------------------------------------------------- /stats/tables.py: -------------------------------------------------------------------------------- 1 | import django_tables2 as tables 2 | 3 | from user.models import User 4 | 5 | 6 | class CheckinRankingListTable(tables.Table): 7 | class Meta: 8 | model = User 9 | attrs = {'class': 'table table-hover'} 10 | template = 'django_tables2/bootstrap-responsive.html' 11 | fields = ['name', 'email', 'checkin_count'] 12 | empty_text = 'No checked in user yet... Why? :\'(' 13 | order_by = '-checkin_count' 14 | 15 | 16 | class OrganizerRankingListTable(tables.Table): 17 | counter = tables.TemplateColumn('{{ row_counter|add:1 }}', verbose_name='Position') 18 | 19 | class Meta: 20 | model = User 21 | attrs = {'class': 'table table-hover'} 22 | template = 'django_tables2/bootstrap-responsive.html' 23 | fields = ['counter', 'email', 'vote_count', 'skip_count', 'total_count'] 24 | empty_text = 'No organizers voted yet... Why? :\'(' 25 | order_by = '-total_count' 26 | -------------------------------------------------------------------------------- /stats/templates/c3_base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | {% load static %} 3 | 4 | 5 | {% block extra_head %} 6 | 7 | {% endblock %} 8 | 9 | {% block extra_scripts %} 10 | 11 | 12 | {% block c3script %}{% endblock %} 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /stats/templates/c3_table_base.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_table.html' %} 2 | {% load static %} 3 | 4 | 5 | {% block extra_head %} 6 | 7 | {% endblock %} 8 | 9 | {% block extra_scripts %} 10 | 11 | 12 | {% block c3script %}{% endblock %} 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /stats/templates/ranking.html: -------------------------------------------------------------------------------- 1 | {% extends "base_table.html" %} 2 | {% load humanize %} 3 | {% load static %} 4 | {% block head_title %}Organizer stats{% endblock %} 5 | {% block extra_panel %} 6 |

Ranking

7 |

So you think you have reviewed tons, huh?

8 |

Drop your self esteem and look at the ranking.

9 | {% endblock %} 10 | 11 | 12 | -------------------------------------------------------------------------------- /stats/templates/reimbursement_stats.html: -------------------------------------------------------------------------------- 1 | {% extends 'c3_base.html' %} 2 | 3 | {% block head_title %}Reimbursement stats{% endblock %} 4 | {% block panel %} 5 |

Reimbursement stats

6 | Last updated: 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |

Status

16 |
17 |

Reimbursement count:

18 |
19 |
20 |

Applications

21 |
22 |
23 |
24 | {% endblock %} 25 | {% block c3script %} 26 | 90 | {% endblock %} 91 | -------------------------------------------------------------------------------- /stats/templates/users_stats.html: -------------------------------------------------------------------------------- 1 | {% extends 'c3_base.html' %} 2 | 3 | {% block head_title %}Users stats{% endblock %} 4 | {% block panel %} 5 |

Users stats

6 | Last updated: 7 |
8 |
9 |

All

10 |
11 |

Users count:

12 |
13 |
14 | {% endblock %} 15 | {% block c3script %} 16 | 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /stats/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import url 3 | from django.views.decorators.cache import cache_page 4 | 5 | from stats import views 6 | 7 | urlpatterns = [ 8 | url(r'^api/apps/$', cache_page(5 * 60)(views.app_stats_api), name='api_app_stats'), 9 | url(r'^api/volunteer/$', cache_page(5 * 60)(views.volunteer_stats_api), name='api_volunteer_stats'), 10 | url(r'^api/mentor/$', cache_page(5 * 60)(views.mentor_stats_api), name='api_mentor_stats'), 11 | url(r'^api/sponsor/$', cache_page(5 * 60)(views.sponsor_stats_api), name='api_sponsor_stats'), 12 | url(r'^api/reimb/$', cache_page(5 * 60)(views.reimb_stats_api), name='api_reimb_stats'), 13 | url(r'^api/users/$', cache_page(5 * 60)(views.users_stats_api), name='api_users_stats'), 14 | url(r'^api/checkin/$', cache_page(5 * 60)(views.checkin_stats_api), name='api_checkin_stats'), 15 | url(r'^apps/$', views.AppStats.as_view(), name='app_stats'), 16 | url(r'^volunt_apps/$', views.VolunteerStats.as_view(), name='volunteer_stats'), 17 | url(r'^ment_apps/$', views.MentorStats.as_view(), name='mentor_stats'), 18 | url(r'^spon_apps/$', views.SponsorStats.as_view(), name='sponsor_stats'), 19 | url(r'^users/$', views.UsersStats.as_view(), name='users_stats'), 20 | url(r'^check_in/$', views.CheckinStats.as_view(), name='checkin_stats'), 21 | url(r'^organizers/$', views.OrganizerStats.as_view(), name='organizer_stats'), 22 | ] 23 | 24 | 25 | if getattr(settings, 'REIMBURSEMENT_ENABLED', False): 26 | urlpatterns.append(url(r'^reimb/$', views.ReimbStats.as_view(), name='reimb_stats')) 27 | -------------------------------------------------------------------------------- /teams/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/teams/__init__.py -------------------------------------------------------------------------------- /teams/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from teams import models 5 | 6 | 7 | class TeamAdmin(admin.ModelAdmin): 8 | list_display = ('team_code', 'user',) 9 | list_per_page = 200 10 | list_filter = ('team_code', 'user') 11 | search_fields = ('team_code', 'user') 12 | actions = ['delete_selected', ] 13 | 14 | 15 | admin.site.register(models.Team, admin_class=TeamAdmin) 16 | -------------------------------------------------------------------------------- /teams/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.conf import settings 3 | 4 | from teams.models import Team 5 | 6 | 7 | class JoinTeamForm(forms.ModelForm): 8 | def clean_team_code(self): 9 | team_code = self.cleaned_data['team_code'] 10 | teammates = Team.objects.filter(team_code=team_code).count() 11 | if teammates == 0: 12 | raise forms.ValidationError("No team exists with the current code. Did you want to create a team instead?") 13 | max_teammates = getattr(settings, 'HACKATHON_MAX_TEAMMATES', 4) 14 | if teammates == max_teammates: 15 | raise forms.ValidationError("Full team. Max teammates is %d" % max_teammates) 16 | return team_code 17 | 18 | class Meta: 19 | model = Team 20 | exclude = ['user', ] 21 | labels = { 22 | 'team_code': 'Your team code' 23 | } 24 | help_texts = { 25 | 'team_code': 'Paste here the team code that your teammate has sent you' 26 | } 27 | -------------------------------------------------------------------------------- /teams/migrations/0001_teams.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-12-13 19:59 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import teams.models 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='Team', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('team_code', models.CharField(default=teams.models.generate_team_id, max_length=13)), 25 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 26 | ], 27 | ), 28 | migrations.AlterUniqueTogether( 29 | name='team', 30 | unique_together=set([('team_code', 'user')]), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /teams/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/teams/migrations/__init__.py -------------------------------------------------------------------------------- /teams/models.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from django.db import models 4 | 5 | from user.models import User 6 | 7 | TEAM_ID_LENGTH = 13 8 | 9 | 10 | def generate_team_id(): 11 | s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" 12 | passlen = TEAM_ID_LENGTH 13 | return "".join(random.sample(s, passlen)) 14 | 15 | 16 | class Team(models.Model): 17 | team_code = models.CharField(default=generate_team_id, max_length=TEAM_ID_LENGTH) 18 | user = models.OneToOneField(User, on_delete=models.CASCADE) 19 | 20 | class Meta: 21 | unique_together = (("team_code", "user"),) 22 | -------------------------------------------------------------------------------- /teams/templates/team.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | 3 | {% load bootstrap3 %} 4 | {% block head_title %}Team{% endblock %} 5 | 6 | {% block panel %} 7 | {% if team %} 8 |

Team code

9 |

{{ team.team_code }}

10 |

Send this to whoever you want to join this team. Remember that teams have a maximum size of {{ h_max_team }} 11 | hackers

12 |
13 |

Teammates ({{ teammates|length }}/{{ h_max_team }})

14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {% for hacker in teammates %} 26 | 27 | 28 | 29 | 32 | 33 | {% endfor %} 34 | 35 |
NameEmailFinished application?
{{ hacker.name }} {{ hacker.email }} 31 |
36 |
37 |
38 |
39 | {% csrf_token %} 40 | 42 |
43 | {% elif not app.can_join_team %} 44 | 45 | 46 | {% elif not h_app_closed and not team %} 47 |

Do you have a team already? Join it below. Otherwise you can create a new team.

48 |
49 | {% csrf_token %} 50 | {% bootstrap_form form %} 51 |
52 | 53 |
54 |
55 | 57 |
58 |
59 | {% endif %} 60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /teams/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from teams import views 4 | 5 | urlpatterns = [ 6 | url(r'^$', views.HackerTeam.as_view(), name='teams'), 7 | ] 8 | -------------------------------------------------------------------------------- /teams/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib import messages 2 | from django.http import HttpResponseRedirect 3 | from django.shortcuts import render 4 | 5 | from app.utils import hacker_tabs, reverse 6 | from app.views import TabsView 7 | from teams import forms 8 | from teams import models 9 | from user.mixins import IsHackerMixin 10 | 11 | 12 | class HackerTeam(IsHackerMixin, TabsView): 13 | template_name = 'team.html' 14 | 15 | def get_current_tabs(self): 16 | return hacker_tabs(self.request.user) 17 | 18 | def get_context_data(self, **kwargs): 19 | 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) 22 | teammates = [] 23 | 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 | instance = models.Team() 31 | instance.team_code = '' 32 | form = forms.JoinTeamForm(instance=instance) 33 | c.update({'team': team, 'teammates': teammates, 'app': app, 'form': form}) 34 | return c 35 | 36 | def post(self, request, *args, **kwargs): 37 | 38 | if request.POST.get('create', None): 39 | team = models.Team() 40 | team.user = request.user 41 | team.save() 42 | return HttpResponseRedirect(reverse('teams')) 43 | if request.POST.get('leave', None): 44 | team = getattr(request.user, 'team', None) 45 | if team: 46 | team.delete() 47 | return HttpResponseRedirect(reverse('teams')) 48 | else: 49 | form = forms.JoinTeamForm(request.POST, request.FILES) 50 | if form.is_valid(): 51 | team = form.save(commit=False) 52 | team.user = request.user 53 | team.save() 54 | 55 | messages.success(request, 'Team joined successfully!') 56 | 57 | return HttpResponseRedirect(reverse('teams')) 58 | else: 59 | c = self.get_context_data() 60 | c.update({'form': form}) 61 | return render(request, self.template_name, c) 62 | -------------------------------------------------------------------------------- /user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/user/__init__.py -------------------------------------------------------------------------------- /user/admin.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.contrib import admin 3 | # Register your models here. 4 | from django.contrib.admin.forms import AdminPasswordChangeForm 5 | from django.contrib.auth.models import Group 6 | 7 | from user import models 8 | from user.forms import UserChangeForm 9 | 10 | 11 | class UserAdmin(admin.ModelAdmin): 12 | form = UserChangeForm 13 | change_password_form = AdminPasswordChangeForm 14 | 15 | display_fields = ['email', 'name', 'type', 'admin_is_organizer', 'admin_is_volunteer_accepted', 16 | 'is_director', 'have_application'] 17 | filter_fields = ['is_director', 'is_admin', 'email_verified', 'type'] 18 | permission_fields = ['is_director', 'is_admin', 'email_verified', 'can_review_dubious', 'can_review_blacklist', 19 | 'can_review_volunteers', 'can_review_mentors', 'can_review_sponsors'] 20 | 21 | if settings.HARDWARE_ENABLED: 22 | display_fields.append('is_hardware_admin') 23 | filter_fields.append('is_hardware_admin') 24 | permission_fields.insert(4, 'is_hardware_admin') 25 | 26 | # The fields to be used in displaying the User model. 27 | # These override the definitions on the base UserAdmin 28 | # that reference specific fields on auth.User. 29 | list_display = tuple(display_fields) 30 | list_filter = tuple(filter_fields) 31 | permission_fields = tuple(permission_fields) 32 | 33 | fieldsets = ( 34 | (None, {'fields': ('email', 'type', 'password')}), 35 | ('Personal info', {'fields': ('name',)}), 36 | ('Permissions', {'fields': permission_fields}), 37 | ('Important dates', {'fields': ('last_login',)}), 38 | ) 39 | add_fieldsets = ( 40 | (None, { 41 | 'classes': ('wide',), 42 | 'fields': ('email', 'name', 'password1', 'password2',)} 43 | ), 44 | ) 45 | search_fields = ('email',) 46 | ordering = ('created_time',) 47 | date_hierarchy = 'created_time' 48 | filter_horizontal = () 49 | 50 | def get_fieldsets(self, request, obj=None): 51 | if not obj: 52 | return self.add_fieldsets 53 | return super(UserAdmin, self).get_fieldsets(request, obj) 54 | 55 | 56 | class BlacklistUserAdmin(admin.ModelAdmin): 57 | list_display = ('email', 'name', 'date_of_ban') 58 | list_per_page = 20 59 | list_filter = ('email', 'name') 60 | search_fields = ('email', 'name') 61 | actions = ['delete_selected', ] 62 | 63 | 64 | admin.site.register(models.User, admin_class=UserAdmin) 65 | admin.site.register(models.BlacklistUser, admin_class=BlacklistUserAdmin) 66 | admin.site.unregister(Group) 67 | -------------------------------------------------------------------------------- /user/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class UserConfig(AppConfig): 7 | name = 'user' 8 | 9 | def ready(self): 10 | super(UserConfig, self).ready() 11 | from .signals import user_organizer, user_verify_email 12 | user_organizer 13 | user_verify_email 14 | -------------------------------------------------------------------------------- /user/emails.py: -------------------------------------------------------------------------------- 1 | from app import emails 2 | 3 | 4 | def create_verify_email(user, activate_url): 5 | c = { 6 | 'user': user, 7 | 'activate_url': activate_url 8 | } 9 | return emails.render_mail('mails/verify_email', 10 | user.email, c) 11 | 12 | 13 | def create_password_reset_email(user, reset_url): 14 | c = { 15 | 'user': user, 16 | 'reset_url': reset_url 17 | } 18 | return emails.render_mail('mails/password_reset', 19 | user.email, c) 20 | 21 | 22 | def create_sponsor_link_email(user, user_sponsor_url, app_sponsor_url, sponsor_name): 23 | c = { 24 | 'user': user, 25 | 'user_sponsor_url': user_sponsor_url, 26 | 'app_sponsor_url': app_sponsor_url, 27 | 'sponsor_name': sponsor_name, 28 | } 29 | return emails.render_mail('mails/sponsor_link', 30 | user.email, c) 31 | -------------------------------------------------------------------------------- /user/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-10-24 05:23 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='User', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('password', models.CharField(max_length=128, verbose_name='password')), 21 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 22 | ('email', models.CharField(max_length=255, unique=True, verbose_name='email')), 23 | ('nickname', models.CharField(max_length=255, verbose_name='Nickname')), 24 | ('email_verified', models.BooleanField(default=False)), 25 | ('is_active', models.BooleanField(default=True)), 26 | ('is_volunteer', models.BooleanField(default=False)), 27 | ('is_organizer', models.BooleanField(default=False)), 28 | ('is_director', models.BooleanField(default=False)), 29 | ], 30 | options={ 31 | 'abstract': False, 32 | }, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /user/migrations/0002_auto_20171101_1602.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-01 23:02 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='user', 17 | name='nickname', 18 | field=models.CharField(max_length=255, verbose_name='Preferred name'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /user/migrations/0003_rename_user.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-16 05:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0002_auto_20171101_1602'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='user', 17 | old_name='nickname', 18 | new_name='name', 19 | ), 20 | migrations.AddField( 21 | model_name='user', 22 | name='is_admin', 23 | field=models.BooleanField(default=False), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /user/migrations/0004_created_time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2017-11-20 02:52 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('user', '0003_rename_user'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='user', 18 | name='created_time', 19 | field=models.DateTimeField(default=django.utils.timezone.now), 20 | ), 21 | migrations.AlterField( 22 | model_name='user', 23 | name='name', 24 | field=models.CharField(max_length=255, verbose_name='Full name'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /user/migrations/0005_makeemailfield.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-01-06 23:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0004_created_time'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='user', 17 | name='email', 18 | field=models.EmailField(max_length=255, unique=True, verbose_name='email'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /user/migrations/0006_user_is_hardware_admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-09-17 19:28 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0005_makeemailfield'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='is_hardware_admin', 18 | field=models.BooleanField(default=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /user/migrations/0007_user_mlh_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2018-12-05 18:36 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0006_user_is_hardware_admin'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='mlh_id', 18 | field=models.IntegerField(blank=True, null=True, unique=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /user/migrations/0008_user_can_review_dubious.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.4 on 2019-07-25 11:37 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0007_user_mlh_id'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='can_review_dubious', 18 | field=models.BooleanField(default=False), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /user/migrations/0009_auto_20200426_0854.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.23 on 2020-04-26 15:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0008_user_can_review_dubious'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='BlacklistUser', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('email', models.EmailField(max_length=255, unique=True, verbose_name='email')), 20 | ('name', models.CharField(max_length=255, verbose_name='Full name')), 21 | ('motive_of_ban', models.CharField(blank=True, max_length=300, null=True)), 22 | ('date_of_ban', models.DateTimeField()), 23 | ], 24 | ), 25 | migrations.AddField( 26 | model_name='user', 27 | name='can_review_blacklist', 28 | field=models.BooleanField(default=False), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /user/migrations/0009_user_types_20200321_0441.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-03-21 11:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0008_user_can_review_dubious'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='type', 18 | field=models.CharField( 19 | choices=[('H', 'Hacker'), ('M', 'Mentor'), ('S', 'Sponsor'), ('V', 'Volunteer'), ('O', 'Organizer')], 20 | default='H', max_length=2), 21 | ), 22 | migrations.RunSQL(["UPDATE user_user SET type='V' where is_volunteer;"], 23 | ["UPDATE user_user SET is_volunteer = 0 where type='V';"]), 24 | migrations.RunSQL(["UPDATE user_user SET type='O' where is_organizer;"], 25 | ["UPDATE user_user SET is_organizer = 1 where type='O';"]), 26 | migrations.RemoveField( 27 | model_name='user', 28 | name='is_organizer', 29 | ), 30 | migrations.RemoveField( 31 | model_name='user', 32 | name='is_volunteer', 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /user/migrations/0010_blacklist_and_user_type_permisions_20200520_1452.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-05-20 21:52 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('user', '0009_auto_20200426_0854'), 12 | ('user', '0009_user_types_20200321_0441') 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='user', 18 | name='can_review_mentors', 19 | field=models.BooleanField(default=False), 20 | ), 21 | migrations.AddField( 22 | model_name='user', 23 | name='can_review_sponsors', 24 | field=models.BooleanField(default=False), 25 | ), 26 | migrations.AddField( 27 | model_name='user', 28 | name='can_review_volunteers', 29 | field=models.BooleanField(default=False), 30 | ), 31 | migrations.AddField( 32 | model_name='user', 33 | name='max_applications', 34 | field=models.IntegerField(default=1), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /user/migrations/0011_token.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.28 on 2020-05-21 11:18 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import uuid 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('user', '0010_blacklist_and_user_type_permisions_20200520_1452'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Token', 20 | fields=[ 21 | ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), 22 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='token', serialize=False, to=settings.AUTH_USER_MODEL)), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /user/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HackAssistant/registration/5d3163328bfc12be11dc8533edd4a9f101017a40/user/migrations/__init__.py -------------------------------------------------------------------------------- /user/providers.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from django.conf import settings 3 | 4 | from app.utils import reverse 5 | 6 | 7 | def auth_mlh(auth_code, request): 8 | conf = settings.OAUTH_PROVIDERS.get('mlh', {}) 9 | 10 | # If logic not configured, exit 11 | if not conf or not conf.get('id', False): 12 | raise ValueError('Bad configuration, this should never show up') 13 | 14 | # Get Auth code from GET request 15 | conf['code'] = auth_code 16 | if not conf['code']: 17 | raise ValueError('Invalid URL') 18 | 19 | # Get Bearer token 20 | conf['redirect_url'] = reverse('callback', request=request, kwargs={'provider': 'mlh'}) 21 | token_url = '{token_url}?client_id={id}&client_secret={secret}&code={code}&' \ 22 | 'redirect_uri={redirect_url}&grant_type=authorization_code'.format(**conf).replace('\n', '') 23 | resp = requests.post( 24 | token_url 25 | ) 26 | if resp.json().get('error', None): 27 | raise ValueError('Authentification failed, try again please!') 28 | 29 | # Get access token 30 | return resp.json().get('access_token', None) 31 | 32 | 33 | def get_mlh_user(access_token): 34 | conf = settings.OAUTH_PROVIDERS.get('mlh', {}) 35 | conf['access_token'] = access_token 36 | mlhuser = requests.get('{user_url}?access_token={access_token}'.format(**conf)).json() 37 | if mlhuser.get('status', None).lower() != 'ok': 38 | raise ValueError('Authentification failed, try again please!') 39 | return mlhuser.get('data', {}) 40 | -------------------------------------------------------------------------------- /user/signals.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from django.conf import settings 4 | from django.db.models.signals import post_save, pre_save 5 | from django.dispatch import receiver 6 | 7 | from user import tokens, models 8 | from user.models import User 9 | 10 | REGEX_PATTERN = getattr(settings, 'REGEX_HACKATHON_ORGANIZER_EMAIL', None) 11 | DEV_EMAILS = getattr(settings, 'HACKATHON_DEV_EMAILS', None) 12 | 13 | 14 | # Make user organizer or admin if fits regex 15 | @receiver(post_save, sender=User) 16 | def user_organizer(sender, instance, created, *args, **kwargs): 17 | if not created: 18 | return None 19 | 20 | if REGEX_PATTERN and re.match(REGEX_PATTERN, instance.email): 21 | instance.type = models.USR_ORGANIZER 22 | instance.save() 23 | 24 | if DEV_EMAILS and instance.email in DEV_EMAILS: 25 | instance.is_admin = True 26 | instance.save() 27 | 28 | 29 | # Send user verification 30 | @receiver(post_save, sender=User) 31 | def user_verify_email(sender, instance, created, *args, **kwargs): 32 | if created and not instance.email_verified: 33 | msg = tokens.generate_verify_email(instance) 34 | msg.send() 35 | 36 | 37 | @receiver(pre_save, sender=User) 38 | def change_type(sender, instance, *args, **kwargs): 39 | try: 40 | old_user = sender.objects.get(pk=instance.id) 41 | except User.DoesNotExist: 42 | old_user = None 43 | if old_user and old_user.application and old_user.type != instance.type: 44 | if old_user.is_volunteer(): 45 | instance.volunteerapplication_application.delete() 46 | if old_user.is_mentor(): 47 | instance.mentorapplication_application.delete() 48 | if old_user.is_hacker(): 49 | instance.hackerapplication_application.delete() 50 | elif old_user and old_user.is_sponsor() and old_user.sponsorapplication_application: 51 | instance.sponsorapplication_application.all().delete() 52 | -------------------------------------------------------------------------------- /user/templates/callback.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Set passoword" %}{% endblock %} 6 | 7 | {% block content %} 8 | {% if not error %} 9 |

Set password for account

10 |

Set a password for your account for extra security. 11 | You will be able to use it to log in with your email ({{ email }}).

12 |
13 | {% csrf_token %} 14 | {% bootstrap_form form layout="horizontal" %} 15 | 16 |
17 | {% else %} 18 |

Invalid request

19 | 20 |

21 | {{ error }} Please reach out to {{ h_contact_email }} if you think this is an error. 22 |

23 | < Go back 25 | {% endif %} 26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /user/templates/confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_tabs.html' %} 2 | 3 | {% load bootstrap3 %} 4 | {% block head_title %}Application{% endblock %} 5 | 6 | {% block panel %} 7 | < back 8 |
9 |

Please read this!

10 |

Your whole account is going to be deleted. That means all your progress will be lost and you will be 11 | cancelling your attendance at {{ h_name }}. This action can't be reverted. Click below in order to 12 | delete your account.

13 |
14 | {% csrf_token %} 15 | 16 | 17 |
18 |
19 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /user/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Sign In" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

Sign in

10 | 11 |
12 | {% csrf_token %} 13 | {% bootstrap_form form %} 14 | 15 |

Forgot your password?

16 | 17 | {% if h_oauth_providers.mlh and h_oauth_providers.mlh.id and h_oauth_providers.mlh.auth_url %} 18 | 19 | 21 | Sign in with MyMLH 22 | 23 | {% endif %} 24 | {% if not h_app_closed %} 25 | {% trans "...or sign up if you don't have an account" %} 27 | {% endif %} 28 | 29 |
30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /user/templates/mails/password_reset_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Password reset{% endblock %} 3 | {% block content %} 4 |

5 | To initiate the password reset process for your {{ user.get_full_name }} {{ h_name }} Account, 6 | click below: 7 |

8 | 9 | {% include 'mails/include/email_button.html' with url=reset_url text='Reset password' %} 10 |

11 | If clicking the link above doesn't work, please copy and paste the URL in a new browser 12 | window instead. 13 |

14 |

{{ reset_url|urlize }}

15 | 16 | {% include 'mails/include/closing.html' %} 17 | 18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /user/templates/mails/password_reset_subject.txt: -------------------------------------------------------------------------------- 1 | Password reset 2 | -------------------------------------------------------------------------------- /user/templates/mails/sponsor_link_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}New Sponsor: {{ sponsor_name }}{% endblock %} 3 | {% block content %} 4 |

5 | Hi {{ user.name }}, 6 | thanks for creating a new Sponsor User! Here you have a few useful links: 7 | Password reset generator link for {{ sponsor_name }}: 8 |

9 | {% include 'mails/include/email_button.html' with url=user_sponsor_url text='Reset password' %} 10 |

11 | If clicking the link above doesn't work, please copy and paste the URL in a new browser 12 | window instead. 13 |

14 |

{{ user_sponsor_url|urlize }}

15 |

16 | Application link for {{ sponsor_name }}: 17 |

18 | {% include 'mails/include/email_button.html' with url=app_sponsor_url text='Application' %} 19 |

20 | If clicking the link above doesn't work, please copy and paste the URL in a new browser 21 | window instead. 22 |

23 |

{{ app_sponsor_url|urlize }}

24 | 25 | {% include 'mails/include/closing.html' %} 26 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /user/templates/mails/sponsor_link_subject.txt: -------------------------------------------------------------------------------- 1 | New Sponsor Links 2 | -------------------------------------------------------------------------------- /user/templates/mails/verify_email_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'base_email.html' %} 2 | {% block preheader %}Verify your email address{% endblock %} 3 | 4 | {% block content %} 5 |

Hey {{ user.get_full_name }},

6 |

You have 5 days to verify your email address for your account at {{ h_name }}

7 | {% include 'mails/include/email_button.html' with text='Verify' url=activate_url %} 8 | 9 | {% include 'mails/include/closing.html' %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /user/templates/mails/verify_email_subject.txt: -------------------------------------------------------------------------------- 1 | Please Confirm Your E-mail Address 2 | -------------------------------------------------------------------------------- /user/templates/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Change password" %}{% endblock %} 6 | 7 | {% block content %} 8 |

Password reset complete

9 | 10 |

11 | Your password has been set. You may go ahead and log in now. 12 |

13 | 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /user/templates/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Change password" %}{% endblock %} 6 | 7 | {% block content %} 8 | {% if validlink %} 9 |

Change password

10 |
11 | {% csrf_token %} 12 | {% bootstrap_form form layout="horizontal" %} 13 | 14 |
15 | {% else %} 16 |

Invalid

17 | 18 |

19 | The password reset link was invalid, possibly because it has already been used. 20 | Please request a new password reset. 21 |

22 | {% endif %} 23 | 24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /user/templates/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Password reset complete" %}{% endblock %} 6 | 7 | {% block content %} 8 |

Password reset complete

9 | 10 |

11 | We've emailed you instructions for setting your password, if an account exists with the email you entered. 12 | You should receive them shortly. 13 |

14 |

15 | If you don't receive an email, please make sure you've entered the address you registered with, 16 | and check your spam folder. 17 |

18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /user/templates/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Forgot password" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

Forgot password

10 | 11 |
12 | {% csrf_token %} 13 | {% bootstrap_form form layout="horizontal" %} 14 | 15 | 16 | 17 |
18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /user/templates/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "base_tabs.html" %} 2 | {% load humanize %} 3 | {% load bootstrap3 %} 4 | {% load static %} 5 | {% block head_title %}Profile{% endblock %} 6 | {% block extra_head %} 7 | 8 | 9 | 10 | {% endblock %} 11 | {% block panel %} 12 |

Profile

13 |
14 |

Personal data

15 |
16 |

If you haven't applied yet you can change your user and apply as hacker, mentor or volunteer

17 |
18 | {% csrf_token %} 19 | {% bootstrap_form_errors form %} 20 | {% for field in form %} 21 | {% bootstrap_field field %} 22 | {% endfor %} 23 | 24 |
25 |

Delete account

26 |
27 |

You can delete your account with all the personal data from your user and applications. This action cannot be reverted.

28 | Delete account 29 |
30 | {% endblock %} 31 | 32 | {% block out_panel %} 33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /user/templates/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Sign Up" %}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% if h_app_closed %} 10 |

Applications are closed

11 | {% include 'include/applications_closed.html' %} 12 | {% trans "Log in " %} 14 | {% else %} 15 |

Sign up

16 | {% if h_app_timeleft %} 17 |

Application deadline:{% include 'include/deadline_countdown.html' %}

18 | {% endif %} 19 | 20 |
21 | {% csrf_token %} 22 | {% bootstrap_form form %} 23 | 24 | {% if h_oauth_providers.mlh and h_oauth_providers.mlh.id and h_oauth_providers.mlh.auth_url %} 25 | 26 | 27 | Sign up with MLH 28 | 29 | {% endif %} 30 |
31 | {% trans "...or log in if you already have an account " %} 33 | 34 | {% endif %} 35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /user/templates/verify_email_required.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% load i18n %} 4 | {% load bootstrap3 %} 5 | {% block head_title %}{% trans "Verify your email" %}{% endblock %} 6 | 7 | {% block content %} 8 |

Verify your email

9 | 10 |

11 | You should have received an email at {{ user.email }}. Check your spam folder. If you haven't received an email, 12 | you can ask to send it again here. 13 |

14 | 15 | 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /user/tokens.py: -------------------------------------------------------------------------------- 1 | import six 2 | from django.conf import settings 3 | from django.contrib.auth.tokens import PasswordResetTokenGenerator 4 | from django.urls import reverse 5 | from django.utils.encoding import force_bytes 6 | from django.utils.http import urlsafe_base64_encode 7 | 8 | from app.utils import reverse as r_reverse 9 | from user.emails import create_verify_email, create_password_reset_email, create_sponsor_link_email 10 | from user.models import Token 11 | 12 | 13 | class AccountActivationTokenGenerator(PasswordResetTokenGenerator): 14 | def _make_hash_value(self, user, timestamp): 15 | return ( 16 | six.text_type(user.pk) + six.text_type(timestamp) + 17 | six.text_type(user.email_verified) 18 | ) 19 | 20 | 21 | account_activation_token = AccountActivationTokenGenerator() 22 | 23 | password_reset_token = PasswordResetTokenGenerator() 24 | 25 | 26 | def generate_verify_email(user): 27 | token = account_activation_token.make_token(user) 28 | uuid = urlsafe_base64_encode(force_bytes(user.pk)) 29 | activate_url = 'http://' + settings.HACKATHON_DOMAIN + \ 30 | reverse('activate', kwargs={'uid': uuid, 'token': token}) 31 | return create_verify_email(user, activate_url) 32 | 33 | 34 | def generate_pw_reset_email(user, request): 35 | token = password_reset_token.make_token(user) 36 | uuid = urlsafe_base64_encode(force_bytes(user.pk)) 37 | reset_url = r_reverse('password_reset_confirm', kwargs={'uid': uuid, 'token': token}, request=request) 38 | return create_password_reset_email(user, reset_url) 39 | 40 | 41 | def generate_sponsor_link_email(user, request): 42 | token = password_reset_token.make_token(user) 43 | t = Token() 44 | t.user = user 45 | app_token = t.uuid_str() 46 | t.save() 47 | uuid = urlsafe_base64_encode(force_bytes(user.pk)) 48 | user_sponsor_url = r_reverse('password_reset_confirm', kwargs={'uid': uuid, 'token': token}, request=request) 49 | app_sponsor_url = r_reverse('sponsor_app', kwargs={'uid': uuid, 'token': app_token}, request=request) 50 | print(user_sponsor_url) 51 | print(app_sponsor_url) 52 | return create_sponsor_link_email(request.user, user_sponsor_url, app_sponsor_url, user.name) 53 | -------------------------------------------------------------------------------- /user/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.urls import reverse_lazy 3 | from django.views.generic.base import RedirectView 4 | 5 | from user import views 6 | 7 | urlpatterns = [ 8 | url(r'^login/$', views.login, name='account_login'), 9 | url(r'^callback/(?P[0-9A-Za-z_\-]+)/$', views.callback, name='callback'), 10 | url(r'^signup/$', RedirectView.as_view(url=reverse_lazy('account_signup_typed', kwargs={'u_type': 'hacker'})), 11 | name='account_signup'), 12 | url(r'^register/sponsor/$', views.SponsorRegister.as_view(), name='sponsor_signup'), 13 | url(r'^signup/(?P[a-z_\-]{1,10})/$', views.signup, name='account_signup_typed'), 14 | url(r'^logout/$', views.logout, name='account_logout'), 15 | url(r'^activate/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$', 16 | views.activate, name='activate'), 17 | url(r'^password/$', views.set_password, name='set_password'), 18 | url(r'^password_reset/$', views.password_reset, name='password_reset'), 19 | url(r'^password_reset/done/$', views.password_reset_done, name='password_reset_done'), 20 | url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,40})/$', 21 | views.password_reset_confirm, name='password_reset_confirm'), 22 | url(r'^reset/done/$', views.password_reset_complete, name='password_reset_complete'), 23 | url(r'^verify/$', views.verify_email_required, name='verify_email_required'), 24 | url(r'^verify/send$', views.send_email_verification, name='send_email_verification'), 25 | url(r'^profile/$', views.UserProfile.as_view(), name='user_profile'), 26 | url(r'^profile/delete/$', views.DeleteAccount.as_view(), name='user_profile_delete'), 27 | ] 28 | --------------------------------------------------------------------------------