Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
458a911
Initial pathway rework
KavyanshKhaitan2 Sep 18, 2026
77f2abc
Remove pathway groups (claude)
KavyanshKhaitan2 Sep 19, 2026
059cf1e
Refactor the code and fix a few stuff
KavyanshKhaitan2 Sep 20, 2026
b71f8de
Merge pull request #32 from hackclub/main
KavyanshKhaitan2 Sep 20, 2026
8dc2c09
Merge pull request #33 from hackclub/main
KavyanshKhaitan2 Sep 21, 2026
c0f1864
Merge pull request #35 from hackclub/main
KavyanshKhaitan2 Sep 21, 2026
e6696c1
Merge pull request #37 from hackclub/main
KavyanshKhaitan2 Sep 22, 2026
f964b33
Fix env example Hackatime redirect URI secure scheme
Heliodex Sep 22, 2026
94c3476
Merge most recent migrations
Heliodex Sep 22, 2026
cc4db3d
Fix empty state when no pathways are available
Heliodex Sep 22, 2026
97124a6
Fix chart data errors on admin dashboard
Heliodex Sep 22, 2026
bafad49
Improved typing and ignores for Pyrefly
Heliodex Sep 22, 2026
f2a1ea2
Fixes for linter issues
Heliodex Sep 22, 2026
09c8ab6
More removals for unused #ignore directives
Heliodex Sep 22, 2026
dd4fa37
Format files with Ruff
Heliodex Sep 22, 2026
77b2aaf
Fix trailing comma issues checked with Ruff
Heliodex Sep 22, 2026
5c49bbe
Define some model ID types to remove some unresolved-attribute ignore
Heliodex Sep 22, 2026
1013033
Install django-stubs for better type checking
Heliodex Sep 22, 2026
b290f0b
Update ignore directive comments to work with django-stubs
Heliodex Sep 22, 2026
cfa2709
Further improve model typing for user profiles, remove more type ignore
Heliodex Sep 22, 2026
6b8d0ad
Add minimise button to windows on dashboard and improve window controls
Heliodex Sep 22, 2026
c8328cd
Improve window button padding, replace maximise button with restore down
Heliodex Sep 22, 2026
a95accc
Active states for window buttons, taskbar programs, and start menu boton
Heliodex Sep 22, 2026
388117a
Replace tiny twisted logo start menu icon with cropped letter T
Heliodex Sep 22, 2026
abc6d0e
Fix vertical overflow when pressing taskbar page buttons
Heliodex Sep 22, 2026
d9cfbd0
Regen Tailwind theme to remove unused icons
Heliodex Sep 22, 2026
2360fd6
Remove unused fonts
Heliodex Sep 22, 2026
b2e56c7
Remove unused cotton.button component
Heliodex Sep 22, 2026
20f6c84
Remove placeholder round-square image
Heliodex Sep 22, 2026
d5459ea
Some elimination of broken/unused CSS classes & variables
Heliodex Sep 22, 2026
85a1f65
Fix some previously broken style classes
Heliodex Sep 22, 2026
f90c715
Desktop icon hover/active feedback effect
Heliodex Sep 22, 2026
7c48aa3
Fix play/pause button behaviour for music widget on 1st load
Heliodex Sep 22, 2026
925dbd1
Move window to front when clicking anywhere, not just title bar
Heliodex Sep 22, 2026
10e3e36
Replace string concatenation with interpolation
Heliodex Sep 22, 2026
870d015
Build basic test harness for future tests
Heliodex Sep 23, 2026
9fffb45
GitHub Actions runner for tests
Heliodex Sep 23, 2026
3e233f3
Fix linter issues with type casting and ignore comments
Heliodex Sep 23, 2026
b953d27
Tests for Ari, Hackatime auth, and database models
Heliodex Sep 23, 2026
3cbfcbc
Tests for admin panel access, image uploading for projects & ships, and
Heliodex Sep 23, 2026
8b3958a
Cleanup and test running fixes
Heliodex Sep 23, 2026
974ad33
Partial implementation of shop
KavyanshKhaitan2 Sep 23, 2026
a29752a
Merge pull request #38 from hackclub/main
KavyanshKhaitan2 Sep 23, 2026
2b107e5
Journal creation, update, and Hackatime tracking tests
Heliodex Sep 23, 2026
5d90e59
Merge leaf node migrations
Heliodex Sep 23, 2026
6e88312
Add something :idkman:
KavyanshKhaitan2 Sep 23, 2026
f4cba70
More edge case tests for existing test files
Heliodex Sep 23, 2026
1d48409
Test bad image content rejection with image construction
Heliodex Sep 23, 2026
81680e3
Improve uploaded file model CDN response typing
Heliodex Sep 23, 2026
01d9e7e
Add missing str metamethods
Heliodex Sep 23, 2026
5452f42
More typing improvements
Heliodex Sep 23, 2026
456f510
Formatting fixes
Heliodex Sep 23, 2026
088b323
Fix Ari ingest URL
Heliodex Sep 23, 2026
a35d4bf
Add missing ID field to ShopRegion
Heliodex Sep 23, 2026
716841e
Improve handling for possibly None variables
Heliodex Sep 23, 2026
d631910
Prevent journalling negative Hackatime time
Heliodex Sep 23, 2026
20c21d9
Fix comparison for pathway unlocking
Heliodex Sep 23, 2026
b019c47
Validate images uploaded to site
Heliodex Sep 23, 2026
cb47c91
Tests for communication with Ari
Heliodex Sep 23, 2026
be7ca01
Auth callback tests
Heliodex Sep 23, 2026
5ea9c0b
Tests for integration with Hackatime and HCA
Heliodex Sep 23, 2026
5c5b81f
Test pathway display for unlocking and grouping
Heliodex Sep 23, 2026
42ad16b
Test updating and reading project settings
Heliodex Sep 23, 2026
a21e576
Import fixes
Heliodex Sep 23, 2026
985f582
Remove commented template code
Heliodex Sep 23, 2026
7d9f8ab
Unused variable fix
Heliodex Sep 23, 2026
e234fee
Unused return value fix
Heliodex Sep 23, 2026
bd34210
More robust checking for Ari request payloads
Heliodex Sep 23, 2026
d50612f
Hackatime login & state auth checking
Heliodex Sep 23, 2026
c71d75c
Prevent editing journals for shipped projects
Heliodex Sep 23, 2026
ed5a436
Fix datepicker start/end date values when not defined
Heliodex Sep 23, 2026
9fa4cc4
Fix some markup issues, remove debug logging
Heliodex Sep 23, 2026
1462eb4
Linting & formatting fixes
Heliodex Sep 23, 2026
9cbe357
Handle all request failures in country lookup
Heliodex Sep 23, 2026
167e9c7
Explicitly support None arguments to minutes formatter
Heliodex Sep 23, 2026
0a473d3
Return 404 Not Found upon unknown shop regions
Heliodex Sep 23, 2026
137aa83
Check pathway minimum minutes is a whole number
Heliodex Sep 23, 2026
b0d0f6f
Ensure validity of new pathway start/end times
Heliodex Sep 23, 2026
d2161aa
Raise 400 Bad Request errors on bad project data
Heliodex Sep 23, 2026
ea708e1
Admin superuser permissions tests
Heliodex Sep 23, 2026
5ff14c4
Tests for example R2 bucket uploading
Heliodex Sep 23, 2026
9ed7143
Test pathway unlocking
Heliodex Sep 23, 2026
f46eb65
Admin updating and permission tests
Heliodex Sep 23, 2026
bc3ce57
Template tag tests for converting time and divide operation
Heliodex Sep 23, 2026
b68821d
Projcet creation and other client workflow tests
Heliodex Sep 23, 2026
3a36793
Middleware cookie tests
Heliodex Sep 23, 2026
620af42
Profile country checking tests
Heliodex Sep 23, 2026
10fc5c8
Tests for Slack bot logging
Heliodex Sep 23, 2026
3d17d36
Add testing instructions to README
KavyanshKhaitan2 Sep 24, 2026
fba6eb6
Update README
KavyanshKhaitan2 Sep 24, 2026
6e2bfdf
You can now create/delete/update shop items from admin dash
KavyanshKhaitan2 Sep 24, 2026
5b849ad
Improve Twisted taskbar start menu icon
Heliodex Sep 24, 2026
9ccbb09
Merge branch 'pathway_rework' of https://github.com/hackclub/twisted …
Heliodex Sep 24, 2026
a9b4ab1
Add selection box to Shop desktop icon
Heliodex Sep 24, 2026
863d95b
Do work
KavyanshKhaitan2 Sep 24, 2026
035bef1
Fixes for template conditionals
Heliodex Sep 24, 2026
360eece
Reload projects list after creating a project
Heliodex Sep 24, 2026
b569e83
Improved form validation when creating/modifying pathways
Heliodex Sep 24, 2026
3331f7c
404 correctly for invalid pathway item listings
Heliodex Sep 24, 2026
c624f72
Improved error logging for Ari project status querying
Heliodex Sep 24, 2026
928752a
Check for whether outbound Ari submissions can be sent
Heliodex Sep 24, 2026
7db08ea
Check project ship status when showing Hackatime project link warning
Heliodex Sep 24, 2026
6ae4715
Fix admin messages toast alert component
Heliodex Sep 24, 2026
18cf161
Improve pathway update field validation
Heliodex Sep 24, 2026
0e30032
Fix missing import
Heliodex Sep 24, 2026
999822a
More tests for admin workflows
Heliodex Sep 24, 2026
5c2c558
Tests for project submission and sending/uploading to Ari
Heliodex Sep 24, 2026
83321d8
Type casting fix
Heliodex Sep 24, 2026
c232bdf
Improve typing, fix linter issues, and add defaults to POST request
Heliodex Sep 24, 2026
1ee32cc
Merge branch 'pathway_rework' of https://github.com/hackclub/twisted …
Heliodex Sep 24, 2026
1953cc3
Improve shop item querying and typing
Heliodex Sep 24, 2026
803bb73
Merge branch 'main' into pathway_rework
KavyanshKhaitan2 Sep 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: tests

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Django tests
runs-on: ubuntu-latest
timeout-minutes: 15

services:
postgres:
image: postgres:16
env:
POSTGRES_DB: twisted_test
POSTGRES_USER: twisted_test
POSTGRES_PASSWORD: twisted_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U twisted_test -d twisted_test"
--health-interval 10s
--health-timeout 5s
--health-retries 5

env:
DJANGO_SETTINGS_MODULE: mysite.test_settings
POSTGRES_DB: twisted_test
POSTGRES_HOST: 127.0.0.1
POSTGRES_PASSWORD: twisted_test
POSTGRES_PORT: 5432
POSTGRES_USER: twisted_test

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Set up uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Install dependencies
run: uv sync --frozen --dev

- name: Run Django checks
run: |
uv run python twisted/manage.py check --settings=mysite.test_settings
uv run python twisted/manage.py makemigrations --check --dry-run --settings=mysite.test_settings

- name: Run tests with coverage
run: |
uv run coverage run twisted/manage.py test twisted_site common --settings=mysite.test_settings --verbosity 2
uv run coverage report
uv run coverage xml -o coverage.xml

- name: Upload coverage report
if: always()
uses: actions/upload-artifact@v4
with:
name: python-coverage
path: coverage.xml
if-no-files-found: ignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ build/
dist/
wheels/
*.egg-info
.coverage
coverage.xml
htmlcov/

# Virtual environments
.venv
Expand Down
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@ wanna help dev? follow these steps!
2. [install uv](https://docs.astral.sh/uv/getting-started/installation/)
3. copy twisted/.env.example to twisted/.env

All instructions below assume you've already cd'd into the `twisted` dir

### launching
1. run `cd twisted`
2. run `uv run manage.py migrate`
3. run `docker compose up db`
4. run `uv run manage.py tailwind dev` in a new terminal
5.
## Thanks
Thats all! Thanks to Heliodex for making this Hack Club ysws possible, if your interested in participating, your're more than welcome to do that :)
1. run `uv run manage.py migrate`
2. run `docker compose up db`
3. run `uv run manage.py tailwind dev` in a new terminal

### getting an admin account
1. log into your account on the web
2. run `uv run manage.py shell_plus` or `uv run manage.py shell` depending on what you fancy
3. get your profile by running `profile = Profile.objects.get()` (assuming only one person has signed up)
4. run `profile.is_staff = True`
5. run `perms = ProfileStaffPermissions.objects.create(superuser = True, view_users = True, view_pathways = True, manage_pathways = True, manage_fulfillments = True, manage_shop = True, view_review = True, manage_review = True, manage_announcements = True, view_auditlogs = True)` (this creates a perms object with all perms)
6. run `profile.staff_permissions = perms` (this associates the perms object with your profile)
7. run `profile.save()` (this saves the changes to the DB)

### testing
run `uv run manage.py test --settings mysite.test_settings`
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies = [
"django-tailwind[cookiecutter,honcho,reload]>=4.5.0",
"gunicorn>=26.0.0",
"psycopg[binary]>=3.3.4",
"pillow>=11.3.0",
"python-dotenv>=1.2.2",
"requests>=2.34.2",
"slack-bolt>=1.30.0",
Expand All @@ -38,6 +39,7 @@ ignore = ["A002", "C901", "CPY001", "D100", "D101", "D102", "D103", "D104", "D10

[tool.ruff.lint.per-file-ignores]
"*/migrations/*.py" = ["RUF012", "ALL"]
"*/tests/*.py" = ["PT009", "PT027", "S106"]

[tool.pyright]
venvPath = "."
Expand All @@ -47,3 +49,24 @@ executionEnvironments = [{ root = "twisted" }]
[tool.pyrefly]
infer-with-first-use = false
search-path = ["twisted"]
# Migrations are generated and excluded from ruff too (see per-file-ignores above).
project-excludes = ["**/migrations/**"]

[tool.coverage.run]
branch = true
source = ["twisted"]
omit = [
"*/migrations/*",
"*/tests/*",
"twisted/manage.py",
]

[tool.coverage.report]
show_missing = true
skip_covered = true

[dependency-groups]
dev = [
"coverage[toml]>=7.9.0",
"django-stubs>=6.1.1",
]
2 changes: 1 addition & 1 deletion twisted/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ HCA_REDIRECT_URI = http://localhost:8000/oauth/callback

HACKATIME_CLIENT_ID =
HACKATIME_CLIENT_SECRET =
HACKATIME_REDIRECT_URI = https://localhost:8000/oauth/hackatime_callback
HACKATIME_REDIRECT_URI = http://localhost:8000/oauth/hackatime_callback

SLACK_TOKEN = xoxb-***
SLACK_LOG_CHANNEL = C***
Expand Down
4 changes: 3 additions & 1 deletion twisted/mysite/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ def __call__(self, request: HttpRequest) -> HttpResponse:
else:
timezone.deactivate()
except (zoneinfo.ZoneInfoNotFoundError, ValueError):
logger.warning("Invalid django_timezone cookie value: %r", request.COOKIES.get("django_timezone"))
logger.warning(
"Invalid django_timezone cookie value: %r", request.COOKIES.get("django_timezone"),
)
timezone.deactivate()

return self.get_response(request)
1 change: 1 addition & 0 deletions twisted/mysite/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.humanize",
"django.contrib.postgres",
# Your apps
"common",
"twisted_site",
Expand Down
64 changes: 64 additions & 0 deletions twisted/mysite/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ruff: noqa: E402
# pyright: reportConstantRedefinition=false
"""Deterministic settings for the automated test suite."""

import os

_TEST_ENVIRONMENT = {
"ALLOWED_HOSTS": "testserver,localhost,127.0.0.1",
"ARI_INGEST_ENDPOINT": "https://example.invalid/ari/",
"ARI_SIGNING_SECRET": "test-ari-signing-secret",
"ARI_WEBHOOK_SECRET": "test-ari-webhook-secret",
"CSRF_TRUSTED_ORIGINS": "http://testserver",
"DEBUG": "false",
"DEBUG_REVIEW": "false",
"DEFAULT_PFP": "https://example.invalid/avatar.png",
"HACKATIME_CLIENT_ID": "test-hackatime-client-id",
"HACKATIME_CLIENT_SECRET": "test-hackatime-client-secret",
"HACKATIME_REDIRECT_URI": "http://testserver/oauth/hackatime_callback",
"HCA_CLIENT_ID": "test-hca-client-id",
"HCA_CLIENT_SECRET": "test-hca-client-secret",
"HCA_REDIRECT_URI": "http://testserver/oauth/callback",
"LOGIN_ENABLED": "true",
"POSTGRES_DB": "django",
"POSTGRES_HOST": "127.0.0.1",
"POSTGRES_PASSWORD": "django",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "django",
"R2_ACCESS_KEY": "test-r2-access-key",
"R2_BUCKET": "test-r2-bucket",
"R2_ENDPOINT": "https://example.invalid/r2",
"R2_PUBLIC_URL": "https://example.invalid/uploads",
"R2_SECRET_KEY": "test-r2-secret-key",
"SECRET_KEY": "test-secret-key-that-is-only-used-by-the-automated-test-suite",
"SLACK_LOG_CHANNEL": "C_TEST",
"SLACK_TOKEN": "xoxb-test-token",
}

for _name, _value in _TEST_ENVIRONMENT.items():
_ = os.environ.setdefault(_name, _value)

from mysite.settings import * # noqa: F403

DEBUG = False
DEBUG_REVIEW = False
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False

MIDDLEWARE = [
middleware
for middleware in MIDDLEWARE # noqa: F405
if middleware != "whitenoise.middleware.WhiteNoiseMiddleware"
]

PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]

STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/button-down.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/button-left.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/button-right.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/button-up.png
Binary file not shown.
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/checkmark.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/close.png
Binary file not shown.
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/help.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/maximize.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/minimize.png
Binary file not shown.
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/radio-border.png
Binary file not shown.
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/radio-dot.png
Binary file not shown.
Binary file removed twisted/tailwindcsstheme/static/icon/restore.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
63 changes: 11 additions & 52 deletions twisted/tailwindcsstheme/static_src/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@
font-display: swap;
}

@font-face {
font-family: "Piedra";
src: url("/static/fonts/Piedra-Regular.ttf") format("truetype");
font-weight: 100;
font-style: normal;
font-display: swap;
}

:root {
/* Accent: primary buttons, links, focus ring, active states */
--color-accent: var(--color-red-500);
Expand Down Expand Up @@ -67,15 +59,10 @@
--color-twisted-200: #e0a7ff;
--color-twisted-400: #b96cff;
--color-twisted-600: #8c3ed1;
--color-twisted-800: #61268f;
--color-twisted-999: #3a1750;

--color-absolute-white: #ffffff;
--color-fair-gray: #b8adbf;
--color-dark-black: #120817;

--color-surface: #2e153c;
--color-dark: #1b0d25;
}

@theme {
Expand All @@ -94,9 +81,6 @@
--color-card: #1b0d25;
--color-card-foreground: #faf7ff;

--color-popover: #48245c;
--color-popover-foreground: #ffffff;

--color-primary: #b96cff;
--color-primary-foreground: #190b24;

Expand All @@ -110,10 +94,8 @@
--color-muted-foreground: #c9b8d3;

--color-subtle: #806a8d;
--color-subtle-foreground: #e2d8e8;

--color-border: #654078;
--color-input: #75488a;
--color-ring: #c27aff;

--color-success: #4ade80;
Expand All @@ -124,6 +106,17 @@

--color-destructive: #ff6b7a;
--color-destructive-foreground: #2a080d;

/* "OS" palette — must live in @theme for Tailwind to generate its utilities */
--color-twisted-200: #e0a7ff;
--color-twisted-400: #b96cff;
--color-twisted-600: #8c3ed1;
--color-twisted-800: #61268f;
--color-twisted-999: #3a1750;

--color-absolute-white: #ffffff;
--color-dark-black: #120817;
--color-surface: #2e153c;
}

button {
Expand Down Expand Up @@ -162,33 +155,6 @@ button {
image-rendering: pixelated;
}

.window .maximized {
position: fixed !important;
inset: 0 0 32px 0 !important;
width: auto !important;
height: auto !important;
min-width: 0 !important;
min-height: 0 !important;
margin: 0 !important;
transform: none !important;
}

progress {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
display: block;
height: 100%;
border: none;
box-sizing: border-box;
background-color: var(--color-primary);
}

progress::-webkit-progress-bar {
background-color: var(--color-primary);
height: 100%;
}

::-webkit-scrollbar {
width: 10px;
height: 10px;
Expand All @@ -212,13 +178,6 @@ progress::-webkit-progress-bar {
background: #1e072f;
}

@utility no-scrollbar {
@apply [scrollbar-width:none] [&::-webkit-scrollbar]:hidden;
}

::-webkit-scrollbar-corner {
background: #1e072f;
}
.resize-handle {
position: absolute;
z-index: 10;
Expand Down
Loading
Loading