Skip to content

Commit a7f9dc9

Browse files
committed
Fix some pylint issues, improve initadmin, add env documentation, replace decouple
1 parent 1019660 commit a7f9dc9

File tree

8 files changed

+126
-32
lines changed

8 files changed

+126
-32
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ jobs:
2020
pip install -r requirements.txt
2121
2222
- name: Run Pylint
23-
run: pylint . --fail-under 7.0
23+
run: pylint . --fail-under 8.0

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ For more detailed instructions, see the [documentation](docs/).
3131
- Clone this repository `git clone https://github.com/tgrants/grade-check.git`
3232
- Set environment variables
3333
- Copy template `cp .env.example .env`
34-
- Edit it `nano .env`
35-
- if `DJANGO_SECRET_KEY` is left empty, it will be generated when the app is run for the first time
34+
- Edit it `nano .env` (see the [environment variable documentation](docs/enviornment-variables.md))
3635
- Install [Docker](https://docs.docker.com/engine/install/debian/)
3736
- Get docker compose `sudo apt install docker-compose`
3837
- Enable docker `sudo systemctl enable docker`
@@ -45,10 +44,6 @@ For more detailed instructions, see the [documentation](docs/).
4544

4645
## Updating
4746

48-
> [!WARNING]
49-
>
50-
> Migrating your data to a new version is currently not supported, but will be in the future.
51-
5247
- Read the [changelogs](https://github.com/tgrants/grade-check/releases)
5348
- Pull the latest changes `git pull`
5449
- Make sure `.env` has the same values as `.env.example`

core/admin.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
from django.contrib import admin
2-
from django.urls import path
31
from django import forms
2+
from django.contrib import admin, messages
3+
from django.contrib.admin.models import LogEntry, ADDITION
44
from django.contrib.auth.models import User
5+
from django.contrib.contenttypes.models import ContentType
56
from django.shortcuts import render, redirect
6-
from django.contrib import messages
7+
from django.urls import path
78

8-
from django.contrib.admin.models import LogEntry, ADDITION
9-
from django.contrib.contenttypes.models import ContentType
10-
from django.utils.encoding import force_str
119

1210
class BulkUserUploadForm(forms.Form):
1311
data = forms.CharField(
Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,66 @@
1-
from django.core.management.base import BaseCommand
1+
"""
2+
Management command to create a default Django superuser.
3+
4+
This command creates a superuser if none exist, using either provided command-line
5+
arguments, environment variables, or fallback defaults. Intended for easy setup
6+
in development or initial deployments.
7+
"""
8+
9+
import os
10+
11+
from django.core.management import color_style
12+
from django.core.management.base import BaseCommand, CommandError
213
from django.contrib.auth import get_user_model
314

15+
416
class Command(BaseCommand):
17+
"""
18+
Django management command to create a default superuser.
19+
20+
Optional arguments:
21+
--username: specify the superuser username (default 'admin' or env var)
22+
--email: specify the superuser email address (default 'admin@email.invalid' or env var)
23+
--password: specify the superuser password (default 'gradecheck' or env var)
24+
25+
If any user exists already, user creation will be skipped.
26+
"""
27+
528
help = 'Creates a default superuser'
629

30+
def add_arguments(self, parser):
31+
parser.add_argument(
32+
'--username',
33+
type = str,
34+
help = 'Specify the superuser username',
35+
default = os.getenv('DJANGO_SUPERUSER_USERNAME', 'admin')
36+
)
37+
parser.add_argument(
38+
'--email',
39+
type=str,
40+
help='Specify the superuser email',
41+
default=os.getenv('DJANGO_SUPERUSER_EMAIL', 'admin@email.invalid')
42+
)
43+
parser.add_argument(
44+
'--password',
45+
type=str,
46+
help='Specify the superuser password',
47+
default=os.getenv('DJANGO_SUPERUSER_PASSWORD', 'gradecheck')
48+
)
49+
750
def handle(self, *args, **options):
8-
User = get_user_model()
9-
if User.objects.exists():
10-
self.stdout.write(self.style.WARNING('Users already exist - skipping default user creation'))
51+
user_model = get_user_model()
52+
style = color_style()
53+
54+
if user_model.objects.exists():
55+
self.stdout.write(style.SUCCESS('Skipping default user creation')) # pylint: disable=no-member
1156
return
12-
User.objects.create_superuser(
13-
username='admin',
14-
email='admin@email.invalid',
15-
password='gradecheck'
16-
)
17-
self.stdout.write(self.style.SUCCESS('Default superuser created'))
57+
58+
username = options['username']
59+
email = options['email']
60+
password = options['password']
61+
62+
try:
63+
user_model.objects.create_superuser(username=username, email=email, password=password)
64+
self.stdout.write(style.SUCCESS(f'Default superuser "{username}" created')) # pylint: disable=no-member
65+
except Exception as e:
66+
raise CommandError(f'Failed to create superuser: {e}') from e

core/urls.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
99
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
1010
path('profile/', views.profile_view, name='profile'),
11-
path('password_change/', auth_views.PasswordChangeView.as_view(template_name='password_change.html'), name='password_change'),
12-
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'), name='password_change_done'),
11+
path(
12+
'password_change/',
13+
auth_views.PasswordChangeView.as_view(template_name='password_change.html'),
14+
name='password_change'
15+
),
16+
path(
17+
'password_change/done/',
18+
auth_views.PasswordChangeDoneView.as_view(template_name='password_change_done.html'),
19+
name='password_change_done'
20+
),
1321
]

docs/enviornment-variables.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Environment variables
2+
3+
## Spreadsheet
4+
5+
- `SPREADSHEET_ID`
6+
- The ID of the spreadsheet you want to share
7+
- You can extract it from the URL
8+
- Example value: `1tAD7xkj3GdSL1xnRlA4UJXCKO_71pzwiVKY8Fkr-XY4`
9+
- `SHEET_NAME`
10+
- The name of the page you want to share
11+
- Located at the bottom of the spreadsheet
12+
- Example value: `Sheet1`
13+
- `CACHE_TIMEOUT`
14+
- Time in seconds before the sheet cache expires
15+
- Caching is done to reduce the amount of requests to the API
16+
- Example value: `300` (5 minutes)
17+
18+
## Django
19+
20+
- `DJANGO_SECRET_KEY`
21+
- Secret key used for cryptographic signing
22+
- Default value: generated automatically when the app is run for the first time
23+
- Example value: `uWZOXv1Tl10ZXpxEUe-xPB10RlYz05YRTpjpvnQQHqTGOg2C5TZxvuEiKKOphYbJUHs`
24+
25+
## Postgres
26+
27+
- `POSTGRES_DB`
28+
- Name for the postgres database
29+
- Example value: `gcdb`
30+
- `POSTGRES_USER`
31+
- Username for the postgres user
32+
- Example value: `gcuser`
33+
- `POSTGRES_PASSWORD`
34+
- Password for the postgres user
35+
- Example value: `gcpass`
36+
- `POSTGRES_HOST`
37+
- Host address for the postgres database
38+
- Default value: `localhost`
39+
40+
## Default admin
41+
42+
- `DEFAULT_ADMIN_USERNAME`
43+
- Username for the initial admin user
44+
- Default value: `admin`
45+
- `DEFAULT_ADMIN_PASSWORD`
46+
- Password for the initial admin user
47+
- Default value: `gradecheck`

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@ google-auth-oauthlib
55
gunicorn
66
psycopg2-binary
77
pylint
8-
python-decouple
98
python-dotenv

sheets/utils.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import os
2-
import pickle
3-
from datetime import datetime, timedelta
2+
43
from django.core.cache import cache
5-
from decouple import config
64

75
from google.oauth2.service_account import Credentials
86
from googleapiclient.discovery import build
97

10-
SPREADSHEET_ID = config('SPREADSHEET_ID')
11-
SHEET_NAME = config('SHEET_NAME')
8+
SPREADSHEET_ID = os.getenv("SPREADSHEET_ID")
9+
SHEET_NAME = os.getenv("SHEET_NAME")
1210
RANGE_NAME = SHEET_NAME
1311

1412
SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']
1513
SERVICE_ACCOUNT_FILE = os.path.join(os.path.dirname(__file__), '..', 'credentials.json')
1614

1715
CACHE_KEY = 'google_sheet_data'
18-
CACHE_TIMEOUT = float(config('CACHE_TIMEOUT'))
16+
CACHE_TIMEOUT = float(os.getenv("CACHE_TIMEOUT"))
1917

2018

2119
def get_sheet_service():

0 commit comments

Comments
 (0)