├── .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 |
24 | {% endif %}
25 | {% block extra_panel %}
26 | {% endblock %}
27 | {% endblock %}
28 | {% block out_panel %}
29 |
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 |
6 | {{ message|capfirst }}
7 |
8 | ×
9 |
10 |
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 |
8 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/templates/mails/include/email_image.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/templates/modules/reviewers.html:
--------------------------------------------------------------------------------
1 | {% load humanize %}
2 |
3 |
4 | {% for reviewer in module.children %}
5 |
6 | {{ forloop.counter|ordinal }} - {{ reviewer.username }}
7 |
8 | {{ reviewer.vote_count }}
9 |
10 |
11 |
12 | {% empty %}
13 |
14 | Nothing to show
15 |
16 | {% endfor %}
17 |
--------------------------------------------------------------------------------
/app/templates/modules/stats.html:
--------------------------------------------------------------------------------
1 | {% load humanize %}
2 |
3 | T-Shirts
4 |
5 |
6 |
7 | {% for tshirt in module.tshirts %}
8 | {{ tshirt.hacker__tshirt_size }}
9 | {% endfor %}
10 |
11 |
12 |
13 |
14 | {% for tshirt in module.tshirts %}
15 | {{ tshirt.count }}
16 | {% endfor %}
17 |
18 |
19 |
20 | Diet
21 |
22 |
23 |
24 | {% for diet in module.diets %}
25 | {{ diet.hacker__diet }}
26 | {% endfor %}
27 |
28 |
29 |
30 |
31 | {% for diet in module.diets %}
32 | {{ diet.count }}
33 | {% endfor %}
34 |
35 |
36 |
37 | Status
38 |
39 |
40 |
41 | {% for status in module.count_status %}
42 | {{ status.status }}
43 | {% endfor %}
44 |
45 |
46 |
47 |
48 | {% for status in module.count_status %}
49 | {{ status.count }}
50 | {% endfor %}
51 |
52 |
53 |
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 |
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 |
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 |
31 | {% elif createUser %}
32 |
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 |
3 | {% for int, str in max_vote.items %}
4 | {{ int }}
5 | {% endfor %}
6 |
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 |
3 | Status: {{ reimbursement.get_status_display }}
4 |
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 |
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 | Name
20 | Email
21 | Finished application?
22 |
23 |
24 |
25 | {% for hacker in teammates %}
26 |
27 | {{ hacker.name }}
28 | {{ hacker.email }}
29 |
31 |
32 |
33 | {% endfor %}
34 |
35 |
36 |
37 |
38 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------