initial commit

This commit is contained in:
lauren 2021-08-14 20:13:07 -04:00
parent c557977d8e
commit a867f38153
43 changed files with 1375 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
db.sqlite3
secret.py
__pycache__/
*.pyc

9
Pipfile Normal file
View File

@ -0,0 +1,9 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
Django = "~=3.2"
psycopg2-binary = "~=2.8.6"
social-auth-app-django = "~=4.0.0"

272
Pipfile.lock generated Normal file
View File

@ -0,0 +1,272 @@
{
"_meta": {
"hash": {
"sha256": "b4008672fb38a3e89baf1655aa6b39685d638b0b7f6012ef5fb8da7b48ed33c2"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"asgiref": {
"hashes": [
"sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
"sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.1"
},
"certifi": {
"hashes": [
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2021.5.30"
},
"cffi": {
"hashes": [
"sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d",
"sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771",
"sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872",
"sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c",
"sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc",
"sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762",
"sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202",
"sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5",
"sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548",
"sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a",
"sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f",
"sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20",
"sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218",
"sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c",
"sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e",
"sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56",
"sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224",
"sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a",
"sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2",
"sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a",
"sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819",
"sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346",
"sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b",
"sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e",
"sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534",
"sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb",
"sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0",
"sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156",
"sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd",
"sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87",
"sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc",
"sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195",
"sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33",
"sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f",
"sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d",
"sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd",
"sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728",
"sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7",
"sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca",
"sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99",
"sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf",
"sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e",
"sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c",
"sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5",
"sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"
],
"version": "==1.14.6"
},
"charset-normalizer": {
"hashes": [
"sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b",
"sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"
],
"markers": "python_version >= '3'",
"version": "==2.0.4"
},
"cryptography": {
"hashes": [
"sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d",
"sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959",
"sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6",
"sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873",
"sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2",
"sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713",
"sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1",
"sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177",
"sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250",
"sha256:b01fd6f2737816cb1e08ed4807ae194404790eac7ad030b34f2ce72b332f5586",
"sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3",
"sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca",
"sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d",
"sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.7"
},
"defusedxml": {
"hashes": [
"sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
"sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.7.1"
},
"django": {
"hashes": [
"sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13",
"sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022"
],
"index": "pypi",
"version": "==3.2.6"
},
"idna": {
"hashes": [
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"markers": "python_version >= '3'",
"version": "==3.2"
},
"oauthlib": {
"hashes": [
"sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc",
"sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"
],
"markers": "python_version >= '3.6'",
"version": "==3.1.1"
},
"psycopg2-binary": {
"hashes": [
"sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
"sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67",
"sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0",
"sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6",
"sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db",
"sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94",
"sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52",
"sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056",
"sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b",
"sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd",
"sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550",
"sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679",
"sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83",
"sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77",
"sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2",
"sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77",
"sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2",
"sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd",
"sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859",
"sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1",
"sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25",
"sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152",
"sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf",
"sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f",
"sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729",
"sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71",
"sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66",
"sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4",
"sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449",
"sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
"sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a",
"sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
"sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
"sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
"sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"
],
"index": "pypi",
"version": "==2.8.6"
},
"pycparser": {
"hashes": [
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pyjwt": {
"hashes": [
"sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
"sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"
],
"markers": "python_version >= '3.6'",
"version": "==2.1.0"
},
"python3-openid": {
"hashes": [
"sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf",
"sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"
],
"version": "==3.2.0"
},
"pytz": {
"hashes": [
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2021.1"
},
"requests": {
"hashes": [
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==2.26.0"
},
"requests-oauthlib": {
"hashes": [
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
"sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
],
"version": "==1.3.0"
},
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
"social-auth-app-django": {
"hashes": [
"sha256:2c69e57df0b30c9c1823519c5f1992cbe4f3f98fdc7d95c840e091a752708840",
"sha256:567ad0e028311541d7dfed51d3bf2c60440a6fd236d5d4d06c5a618b3d6c57c5",
"sha256:df5212370bd250108987c4748419a1a1d0cec750878856c2644c36aaa0fd3e58"
],
"index": "pypi",
"version": "==4.0.0"
},
"social-auth-core": {
"hashes": [
"sha256:5ab43b3b15dce5f059db69cc3082c216574739f0edbc98629c8c6e8769c67eb4",
"sha256:983b53167ac56e7ba4909db555602a6e7a98c97ca47183bb222eb85ba627bf2b"
],
"markers": "python_version >= '3.6'",
"version": "==4.1.0"
},
"sqlparse": {
"hashes": [
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
],
"markers": "python_version >= '3.5'",
"version": "==0.4.1"
},
"urllib3": {
"hashes": [
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.6"
}
},
"develop": {}
}

0
hunt/__init__.py Normal file
View File

0
hunt/apps/__init__.py Normal file
View File

View File

6
hunt/apps/auth/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AuthConfig(AppConfig):
name = "hunt.apps.auth"
label = "authentication"

View File

36
hunt/apps/auth/oauth.py Normal file
View File

@ -0,0 +1,36 @@
from typing import Any, Dict, List
from social_core.backends.oauth import BaseOAuth2
from social_core.pipeline.user import get_username as social_get_username
def get_username(strategy, details, *args, user=None, **kwargs):
result = social_get_username(strategy, details, user=user, *args, **kwargs)
return result
class IonOauth2(BaseOAuth2):
name = "ion"
AUTHORIZATION_URL = "https://ion.tjhsst.edu/oauth/authorize"
ACCESS_TOKEN_URL = "https://ion.tjhsst.edu/oauth/token"
ACCESS_TOKEN_METHOD = "POST"
EXTRA_DATA = [("refresh_token", "refresh_token", True), ("expires_in", "expires")]
def get_scope(self) -> List[str]:
return ["read"]
def get_user_details(self, response: Dict[str, Any]) -> Dict[str, Any]:
profile = self.get_json(
"https://ion.tjhsst.edu/api/profile", params={"access_token": response["access_token"]},
)
# fields used to populate/update User model
data = {
key: profile[key]
for key in ("id", "first_name", "last_name", "is_student", "graduation_year")
}
data["username"] = profile["ion_username"]
data["email"] = profile["tj_email"]
return data
def get_user_id(self, details: Dict[str, Any], response: Any) -> int:
return details["id"]

11
hunt/apps/auth/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = "auth"
urlpatterns = [
path("", views.index_view, name="index"),
path("login/", views.login_view, name="login"),
path("logout/", views.logout_view, name="logout"),
]

18
hunt/apps/auth/views.py Normal file
View File

@ -0,0 +1,18 @@
from django.contrib.auth import logout
from django.shortcuts import redirect, render
def index_view(request):
if request.user.is_authenticated:
return redirect("main:index")
else:
return redirect("auth:login")
def login_view(request):
return render(request, "auth/login.html")
def logout_view(request):
logout(request)
return redirect("auth:index")

View File

6
hunt/apps/main/admin.py Normal file
View File

@ -0,0 +1,6 @@
from django.contrib import admin
from .models import Challenge, Class
admin.site.register(Challenge)
admin.site.register(Class)

6
hunt/apps/main/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class MainConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'hunt.apps.main'

View File

@ -0,0 +1,35 @@
# Generated by Django 3.2.6 on 2021-08-14 22:22
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Challenge',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('description', models.CharField(max_length=500)),
('flag', models.CharField(max_length=50)),
('points', models.IntegerField()),
('exclusive', models.BooleanField(default=False)),
('locked', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='Class',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('year', models.CharField(choices=[('2022', 'Seniors'), ('2023', 'Juniors'), ('2024', 'Sophomores'), ('2025', 'Freshmen')], max_length=20, unique=True)),
('last_updated', models.DateTimeField(auto_now=True)),
('challenges_completed', models.ManyToManyField(related_name='classes_completed', to='main.Challenge')),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-08-14 23:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='class',
name='challenges_completed',
field=models.ManyToManyField(blank=True, related_name='classes_completed', to='main.Challenge'),
),
]

View File

34
hunt/apps/main/models.py Normal file
View File

@ -0,0 +1,34 @@
from django.db import models
from django.db.models.fields.related import ManyToManyField
class Challenge(models.Model):
id = models.AutoField(primary_key=True, null=False, blank=False)
name = models.CharField(max_length=100, null=False, blank=False)
description = models.CharField(max_length=500, null=False, blank=False)
flag = models.CharField(max_length=50, null=False, blank=False)
points = models.IntegerField(null=False, blank=False)
exclusive = models.BooleanField(default=False)
locked = models.BooleanField(default=False)
def __str__(self):
return "{} ({})".format(self.name, self.id)
class Class(models.Model):
YEAR_CHOICES = (('2022', 'Seniors'), ('2023', 'Juniors'), ('2024', 'Sophomores'), ('2025', 'Freshmen'))
id = models.AutoField(primary_key=True, null=False, blank=False)
year = models.CharField(max_length=20, choices=YEAR_CHOICES, null=False, blank=False, unique=True)
challenges_completed = models.ManyToManyField(Challenge, related_name="classes_completed", blank=True)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.year
def get_points(self):
sum = 0
for c in self.challenges_completed.all():
sum += c.points
return sum

11
hunt/apps/main/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.urls import path
from . import views
app_name = 'main'
urlpatterns = [
path("", views.index, name="index"),
path("overview/", views.overview, name="overview"),
path("validate/", views.validate_flag, name="validate_flag"),
]

56
hunt/apps/main/views.py Normal file
View File

@ -0,0 +1,56 @@
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http.response import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from .models import Challenge, Class
@login_required
def index(request):
if request.user.is_participant():
"""
Challenges fall into one of three statuses with respect to the user:
- available (user can complete)
- completed (completed by users's class)
- locked (can only be completed by one class and has been completed)
"""
challenges_completed_by_class = set(Class.objects.get(year=str(request.user.graduation_year)).challenges_completed.all())
challenges_dict = dict()
for c in Challenge.objects.all():
if c in challenges_completed_by_class:
challenges_dict[c.id] = [c, "completed"]
elif c.locked:
challenges_dict[c.id] = [c, "locked"]
else:
challenges_dict[c.id] = [c, "available"]
return render(request, 'main/index.html', context={"challenges_dict": challenges_dict})
else:
return redirect(reverse("main:overview"))
@login_required
def overview(request):
data = sorted([(c.year, c.get_points()) for c in Class.objects.all()])
return render(request, 'main/overview.html', context={'data': data})
@login_required
def validate_flag(request):
if request.is_ajax() and request.user.is_participant():
challenge = get_object_or_404(Challenge, id=int(request.POST.get("challenge_id")))
flag = request.POST.get("flag")
if flag == challenge.flag:
if not challenge.locked:
request.user.challenges_done.add(challenge)
if challenge.exclusive:
challenge.locked = True
challenge.save()
hoco_class = Class.objects.get(year=str(request.user.graduation_year))
hoco_class.challenges_completed.add(challenge)
hoco_class.save()
response = {"result": "success"}
else:
response = {"result": "failure"}
return JsonResponse(response)
else:
return PermissionDenied

View File

8
hunt/apps/users/admin.py Normal file
View File

@ -0,0 +1,8 @@
from django.contrib import admin
from .models import Group, User
# Register your models here.
admin.site.register(User)
admin.site.register(Group)

6
hunt/apps/users/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class UserConfig(AppConfig):
name = "hunt.apps.users"
label = "users"

View File

@ -0,0 +1,51 @@
# Generated by Django 3.2.6 on 2021-08-14 22:22
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import hunt.apps.users.models
class Migration(migrations.Migration):
initial = True
dependencies = [
('main', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('id', models.AutoField(primary_key=True, serialize=False)),
('username', models.CharField(max_length=32, unique=True)),
('first_name', models.CharField(max_length=35)),
('last_name', models.CharField(max_length=70)),
('is_student', models.BooleanField(default=False)),
('graduation_year', models.IntegerField(null=True)),
('email', models.EmailField(max_length=50)),
('is_superuser', models.BooleanField(default=False)),
('date_joined', models.DateTimeField(auto_now_add=True)),
('challenges_done', models.ManyToManyField(related_name='users_that_completed', to='main.Challenge')),
('hoco_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_set', to='main.class')),
],
options={
'abstract': False,
},
managers=[
('objects', hunt.apps.users.models.UserManager()),
],
),
migrations.CreateModel(
name='Group',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('is_service', models.BooleanField(default=False)),
('name', models.CharField(max_length=32)),
('users', models.ManyToManyField(related_name='unix_groups', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-08-14 22:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_staff',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 3.2.6 on 2021-08-14 23:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0002_user_is_staff'),
]
operations = [
migrations.RenameField(
model_name='user',
old_name='is_staff',
new_name='_is_staff',
),
migrations.RemoveField(
model_name='user',
name='hoco_class',
),
]

View File

86
hunt/apps/users/models.py Normal file
View File

@ -0,0 +1,86 @@
import logging
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import UserManager as DjangoUserManager
from django.db import models
from ..main.models import Challenge, Class
logger = logging.getLogger(__name__)
class UserManager(DjangoUserManager):
pass
class User(AbstractBaseUser):
objects = UserManager()
USERNAME_FIELD = "username"
EMAIL_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name", "is_student", "graduation_year"]
id = models.AutoField(primary_key=True)
username = models.CharField(unique=True, max_length=32, null=False, blank=False)
first_name = models.CharField(max_length=35, null=False, blank=False)
last_name = models.CharField(max_length=70, null=False, blank=False)
is_student = models.BooleanField(default=False, null=False)
graduation_year = models.IntegerField(null=True)
email = models.EmailField(max_length=50, null=False, blank=False)
is_superuser = models.BooleanField(default=False, null=False)
_is_staff = models.BooleanField(default=False, null=False)
challenges_done = models.ManyToManyField(Challenge, related_name="users_that_completed")
date_joined = models.DateTimeField(auto_now_add=True)
def has_perm(self, perm, obj=None) -> bool: # pylint: disable=unused-argument
return self.is_superuser
def has_module_perms(self, app_label) -> bool: # pylint: disable=unused-argument
return self.is_superuser
@property
def is_staff(self) -> bool:
return self._is_staff or self.is_superuser
@is_staff.setter
def is_staff(self, staff: bool) -> None:
self._is_staff = staff
@property
def full_name(self) -> str:
return self.first_name + " " + self.last_name
@property
def short_name(self) -> str:
return self.first_name
def get_full_name(self) -> str:
return self.full_name
def get_short_name(self) -> str:
return self.short_name
def get_social_auth(self):
return self.social_auth.get(provider="ion")
def __str__(self):
return self.username
def __repr__(self):
return "<User: {} ({})>".format(self.username, self.id)
def is_participant(self):
return self.is_student and not self.is_superuser
class Group(models.Model):
id = models.AutoField(primary_key=True)
is_service = models.BooleanField(default=False)
name = models.CharField(max_length=32)
users = models.ManyToManyField(User, related_name="unix_groups")
def __str__(self):
return self.name

177
hunt/settings/__init__.py Normal file
View File

@ -0,0 +1,177 @@
"""
Django settings for hunt project.
Generated by 'django-admin startproject' using Django 3.2.6.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-=6^bbhfem^#pl*@w29%mo$z#r5_#2d5-m@9q0&9egz@qtcfbah'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'hunt.sites.tjhsst.edu', 'hunt.tjhsst.edu']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'social_django',
'hunt.apps.main',
'hunt.apps.auth',
'hunt.apps.users',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'hunt.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'hunt.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
AUTHENTICATION_BACKENDS = ('hunt.apps.auth.oauth.IonOauth2',)
SOCIAL_AUTH_USER_FIELDS = [
'username',
'first_name',
'last_name',
'email',
'id',
'is_student',
'graduation_year',
]
SOCIAL_AUTH_URL_NAMESPACE = "social"
SOCIAL_AUTH_PIPELINE = (
"social_core.pipeline.social_auth.social_details",
"social_core.pipeline.social_auth.social_uid",
"social_core.pipeline.social_auth.auth_allowed",
"social_core.pipeline.social_auth.social_user",
"hunt.apps.auth.oauth.get_username",
"social_core.pipeline.social_auth.associate_by_email",
"social_core.pipeline.user.create_user",
"social_core.pipeline.social_auth.associate_user",
"social_core.pipeline.social_auth.load_extra_data",
)
AUTH_USER_MODEL = "users.User"
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Login
LOGIN_URL = "auth:login"
LOGIN_REDIRECT_URL = "auth:index"
LOGOUT_REDIRECT_URL = "auth:index"
SESSION_SAVE_EVERY_REQUEST = True
SOCIAL_AUTH_ALWAYS_ASSOCIATE = True
SOCIAL_AUTH_LOGIN_ERROR_URL = "/"
SOCIAL_AUTH_RAISE_EXCEPTIONS = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / "serve"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
try:
from .secret import * # noqa
except ImportError:
pass

View File

@ -0,0 +1,5 @@
DEBUG = False
SECRET_KEY = 'supersecret'
SOCIAL_AUTH_ION_KEY = ''
SOCIAL_AUTH_ION_SECRET = ''

83
hunt/static/css/base.css Normal file
View File

@ -0,0 +1,83 @@
body {
margin: 0px;
font-family: "Open Sans", "Helvetica Neue", sans-serif;
font-size: 15px;
background-color: white;
text-align: center;
}
.header {
background-color: rgb(24, 82, 103);
text-align: left;
margin: 0px;
}
.header_img {
width: 50px;
margin-left: 10px;
margin-top: 5px;
padding: 6px;
display: inline-block;
vertical-align: middle;
}
.header_title {
color:#ffffff;
padding-top: 10px;
font-size: 25px;
display: inline-block;
vertical-align: middle;
}
.header_nav {
padding: 10px;
color: #ffffff;
font-size: 19px;
text-decoration: none;
display: inline-block;
}
.header_nav:hover {
text-decoration: none;
color: #a2a2a3;
}
.dropdown-btn:hover {
text-decoration: none;
color: #a2a2a3;
}
.drop-item:hover {
text-decoration: none;
color: #a2a2a3;
}
.drop-item {
font-size: 16px;
padding-top: 5px;
color: #ffffff;
}
.dropdown-btn {
color: #ffffff;
}
.dropdown-menu {
padding: 10px;
margin-top: 9px;
background-color:rgba(24, 82, 103, .9);
}
.nav_section {
text-align: right;
}
@media screen and (max-width: 48em) {
.header {
text-align: center;
}
.nav_section {
text-align: center;
}
}

24
hunt/static/css/index.css Normal file
View File

@ -0,0 +1,24 @@
.box {
padding: 5px;
margin: 5px;
}
.available {
background-color: lightgray;
}
.completed {
background-color: lightgreen;
}
.locked {
background-color: lightsalmon;
}
.centered-div {
text-align: center;
}
.left-div {
text-align: left;
}

43
hunt/static/css/login.css Normal file
View File

@ -0,0 +1,43 @@
.btn.btn-ion {
text-decoration: none;
color: #484848;
display: inline-block;
line-height: 18px;
padding: 7px 10px;
margin: 2px 0;
font-size: 13px;
font-weight: bold;
text-shadow: 0 1px 0 rgba(255,255,255,.9);
white-space: nowrap;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #f7f7f4;
background: -moz-linear-gradient(top,#f7f7f4 0%,#eaeaea 100%);
background: -webkit-linear-gradient(top,#f7f7f4 0%,#eaeaea 100%);
background: linear-gradient(to bottom,#f7f7f4 0%,#eaeaea 100%);
border: 1px solid #ddd;
border-bottom-color: #c5c5c5;
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,.05);
-moz-box-shadow: 0 1px 3px rgba(0,0,0,.05);
-pie-box-shadow: none;
box-shadow: 0 1px 3px rgba(0,0,0,.05);
vertical-align: middle;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-appearance: none;
-moz-appearance: none;
-o-appearance: none;
appearance: none;
}
.login-box {
text-align: center;
padding: 15px;
border: 1px solid rgb(24, 82, 103);
margin: 15px auto;
max-width: 438px;
}

View File

@ -0,0 +1,73 @@
#hoco-scores {
display: none;
margin: 15px auto;
width: 580px;
text-align: center;
position: absolute;
left: calc(50% - 290px);
top: 100px;
}
.box {
display: inline-block;
position: relative;
background-color: #545454;
color: white;
padding: 15px 15px 20px;
margin: 5px;
width: 125px;
height: 125px;
overflow: hidden;
}
.class {
font-size: 1.5em;
}
.score {
font-size: 2em;
}
@media screen and (max-width: 640px) {
.center-wrapper {
margin-top: 40px;
}
#hoco-scores {
width: 284px;
zoom: 0.7;
left: calc(50% - 142px);
}
}
.corner-ribbon {
width: 95px;
background: #e43;
position: absolute;
top: 5px;
left: -30px;
text-align: center;
line-height: 25px;
letter-spacing: 1px;
color: #f0f0f0;
transform: rotate(-45deg);
-webkit-transform: rotate(-45deg);
}
.gold {
background-color: #C98910;
}
.silver {
background-color: #A8A8A8;
}
.bronze {
background-color: #965A38;
}
@media screen and (max-height: 900px) {
.center {
top: 440px;
}
}

BIN
hunt/static/img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% load static %}
{% block head %}
<link rel='stylesheet' href="{% static 'css/login.css' %}">
{% endblock %}
{% block main %}
<div class='header' style='text-align:left;'>
<img class='header_img' src="{% static 'img/favicon.png' %}"/>
<h1 class='header_title'>Hoco Hunt 2021</h1>
</div>
<div class='login-box'>
<p><b>Login in with your Ion account</b> to access the Hoco Hunt 2021.</p>
<a href="{% url 'social:begin' 'ion' %}{% if request.GET.next %}?next={{ request.GET.next|urlencode }}{% endif %}" class='btn btn-ion'>Log in with Ion</a>
</div>
{% endblock %}

27
hunt/templates/base.html Normal file
View File

@ -0,0 +1,27 @@
{% load static %}
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<script src='https://kit.fontawesome.com/74f5f903f7.js' crossorigin='anonymous'></script>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous">
</script>
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
<title>Hoco Hunt 2021</title>
<link rel='icon' href="{% static 'img/favicon.png' %}" type='image/png'>
<link rel='stylesheet' href="{% static 'css/base.css' %}">
{% block head %}{% endblock %}
</head>
<body>
{% block root %}
{% block main %}
{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,40 @@
{% extends 'base.html' %}
{% load static %}
{% block root %}
<div id='root'>
<div class='row header'>
<div class='col-lg-5 col-md-6 col-sm-12 col-xs-12'>
<a href="{% url 'main:index' %}">
<img class='header_img' src="{% static 'img/favicon.png' %}"/>
<h1 class='header_title'>Hoco Hunt 2021</h1>
</a>
</div>
<div class='col-lg-2 col-md-1'></div>
<div class='col-lg-5 col-md-5 col-sm-12 col-xs-12 nav_section'>
<div class='dropdown header_nav'>
<button class='btn dropdown-toggle dropdown-btn' data-toggle='dropdown'>
<i class='fas fa-user'></i>
<span class='caret'></span>
</button>
<ul class='dropdown-menu dropdown-menu-right'>
{% if user.is_authenticated %}
<li><h3 class='drop-item'>{{ user.username }}</h3></li>
<li><a class='drop-item' href="{% url 'main:overview' %}"><h3 class='drop-item'>Overview</h3></a></li>
{% if user.is_participant %}
<li><a class='drop-item' href="{% url 'main:index' %}"><h3 class='drop-item'>Challenges</h3></a></li>
{% endif %}
<li><a class='drop-item' href="{% url 'auth:logout' %}"><h3 class='drop-item'>Logout</h3></a></li>
{% else %}
<li><a class='drop-item' href="{% url 'auth:login' %}"><h3 class='drop-item'>Login</h3></a></li>
{% endif %}
</ul>
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js' integrity='sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1' crossorigin='anonymous'></script>
<script src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js' integrity='sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM' crossorigin='anonymous'></script>
{% block main %}{% endblock %}
</div>
{% endblock %}

View File

@ -0,0 +1,59 @@
{% extends 'base_with_nav.html' %}
{% load static %}
{% block head %}
<link rel='stylesheet' href="{% static 'css/index.css' %}">
<script>
function checkFlag(challenge_id) {
$.ajax({
type : 'POST',
url: "{% url 'main:validate_flag' %}",
data: {
csrfmiddlewaretoken: '{{ csrf_token }}',
dataType: 'json',
challenge_id: challenge_id,
flag: $("#" + challenge_id).val(),
},
success: function(data) {
if (data.result == "success") {
$("#box" + challenge_id).removeClass("available").addClass("completed");
} else {
$("#" + challenge_id).css("background-color", "red");
}
},
failure: function() {
$("#" + challenge_id).css("background-color", "red");
}
});
}
</script>
{% endblock %}
{% block main %}
<div class='row'>
{% for challenge, status in challenges_dict.items %}
<div class='col-lg-3 col-md-4 col-sm-6 col-xs-12'>
<div id="box{{ status.0.id }}" class="box {% if status.1 == 'available' %}available{% elif status.1 == 'completed' %}completed{% else %}locked{% endif %}">
<div class="centered-div">
<h4>{{ status.0.name }}</h4>
<p>Points: {{ status.0.points }}</p>
</div>
<div class="left-div">
{% if status.0.exclusive %}
<p>This challenge's points are only available to the first class to complete it.</p>
{% elif status.1 == 'locked' %}
<p>This challenge was exclusive and has already been completed by another class.</p>
{% endif %}
<p>{{ status.0.description }}</p>
</div>
{% if status.1 == 'available' %}
<hr>
<input type="text" id="{{ status.0.id }}" placeholder="Enter the flag here"/>
<input type="submit" value="Submit" onclick="checkFlag({{ status.0.id }})" />
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,67 @@
{% extends 'base_with_nav.html' %}
{% load static %}
{% block head %}
<link rel='stylesheet' href="{% static 'css/overview.css' %}">
<script>
$(document).ready(function() {
giveRibbons();
$("#hoco-scores").fadeIn();
});
function giveRibbons() {
var arr = [];
$("#hoco-scores .box").each(function() {
var score = parseFloat($(this).find(".score").text());
arr.push([$(this), score]);
});
arr.sort(function(a, b) {
return b[1] - a[1];
});
var place = 0;
$.each(arr, function(k, v) {
if (k > 0 && arr[k - 1][1] != v[1]) {
place += 1;
}
if (place == 0) {
v[0].find(".corner-ribbon").addClass("gold").text("1st");
}
else if (place == 1) {
v[0].find(".corner-ribbon").addClass("silver").text("2nd");
}
else if (place == 2) {
v[0].find(".corner-ribbon").addClass("bronze").text("3rd");
}
});
}
</script>
{% endblock %}
{% block main %}
<div id="hoco-scores">
<div class="box">
<div class="class">{{ data.0.0 }}</div>
<div class="corner-ribbon"></div>
<div class="score" id="score-senior">{{ data.0.1 }}</div>
points
</div>
<div class="box">
<div class="class">{{ data.1.0 }}</div>
<div class="corner-ribbon"></div>
<div class="score" id="score-junior">{{ data.1.1 }}</div>
points
</div>
<div class="box">
<div class="class">{{ data.2.0 }}</div>
<div class="corner-ribbon"></div>
<div class="score" id="score-sophomore">{{ data.2.1 }}</div>
points
</div>
<div class="box">
<div class="class">{{ data.3.0 }}</div>
<div class="corner-ribbon"></div>
<div class="score" id="score-freshman">{{ data.3.1 }}</div>
points
</div>
</div>
{% endblock %}

9
hunt/urls.py Normal file
View File

@ -0,0 +1,9 @@
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path("", include("social_django.urls", namespace="social")),
path("", include("hunt.apps.auth.urls", namespace="auth")),
path("main/", include("hunt.apps.main.urls", namespace="main")),
]

16
hunt/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for hunt project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hunt.settings')
application = get_wsgi_application()

22
manage.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hunt.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()