Skip to content

Commit

Permalink
Merge pull request #62 from srobo/sr2024
Browse files Browse the repository at this point in the history
Updates for SR2024
  • Loading branch information
raccube authored Apr 7, 2024
2 parents d1334cd + 956a996 commit 9523420
Show file tree
Hide file tree
Showing 77 changed files with 862 additions and 780 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
run: cp helpdesk/helpdesk/configuration.dev.py helpdesk/helpdesk/configuration.py
- name: Static type checking
run: make type
- name: Formatting
run: make format-check
- name: Lint
run: make lint
- name: Unit tests
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
helpdesk/helpdesk/configuration.py
helpdesk/db.sqlite
helpdesk/static
teams.csv

### Django ###
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
.PHONY: all clean lint type test test-cov
.PHONY: all clean format format-check lint type test test-cov

CMD:=
PYMODULE:=helpdesk
MANAGEPY:=$(CMD) ./$(PYMODULE)/manage.py
APPS:=helpdesk accounts display teams tickets
SPHINX_ARGS:=docs/ docs/_build -nWE

all: type test lint
all: type test format lint

format:
find $(PYMODULE) -name "*.html" | xargs $(CMD) djhtml
$(CMD) ruff format $(PYMODULE)

format-check:
find $(PYMODULE) -name "*.html" | xargs $(CMD) djhtml --check
$(CMD) ruff format --check $(PYMODULE)

lint:
$(CMD) ruff check $(PYMODULE)
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ The `Makefile` contains commands that can be used to run tests and linting:
- `make test` - Run unit tests
- `make type` - Type checking

## SR2023 Deployment
## Deployment

This system was deployed for the helpdesk at the SR2023 competition as an experiment and received positive feedback. It was deployed on a 512MB 1 core machine on [Fly](https://fly.io) with a separate Postgres database server of the same specifications.
This system is deployed using our [Ansible](https://github.com/srobo/ansible/) configuration.

### Login with Google

Credentials are configured through the Django admin. OAuth credentials need to be configured as below:

- User type: Internal (this ensures it's only SR accounts which can be used)
- Scopes: `.../auth/userinfo.email`, `.../auth/userinfo.profile`, `openid`
- Redirect URIs: `https://studentrobotics.org/helpdesk/auth/google/login/callback/`
- Authorised JavaScript origins: `https://studentrobotics.org`

A [project](https://console.cloud.google.com/home/dashboard?project=helpdesk-419320) exists for this in our Google Cloud account.
3 changes: 1 addition & 2 deletions helpdesk/accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@


class SignupForm(UserCreationForm):

signup_code = forms.CharField(
label="Volunteer Signup Code",
help_text="This code verifies that you are a volunteer. It can be found in the break room.",
Expand All @@ -20,7 +19,7 @@ class SignupForm(UserCreationForm):
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
field_classes = {"username": UsernameField}

def clean_signup_code(self) -> None:
signup_code = self.cleaned_data.get("signup_code")
Expand Down
13 changes: 7 additions & 6 deletions helpdesk/accounts/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@


class ProfileMiddleware:

EXCLUDED_PATHS: set[tuple[str | None, str]] = {
(None, "account_logout"),
("accounts", "onboarding"),
Expand All @@ -19,11 +18,13 @@ def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None:

def __call__(self, request: HttpRequest) -> HttpResponse:
profile_complete = getattr(request.user, "onboarded_at", None)
if all([
request.user.is_authenticated,
not profile_complete,
self._request_requires_profile(request),
]):
if all(
[
request.user.is_authenticated,
not profile_complete,
self._request_requires_profile(request),
]
):
return redirect("accounts:onboarding")
return self.get_response(request)

Expand Down
11 changes: 8 additions & 3 deletions helpdesk/accounts/migrations/0001_create_user_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ class Migration(migrations.Migration):
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login",
blank=True,
null=True,
verbose_name="last login",
),
),
(
Expand Down Expand Up @@ -61,7 +63,9 @@ class Migration(migrations.Migration):
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address",
blank=True,
max_length=254,
verbose_name="email address",
),
),
(
Expand All @@ -83,7 +87,8 @@ class Migration(migrations.Migration):
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined",
default=django.utils.timezone.now,
verbose_name="date joined",
),
),
],
Expand Down
19 changes: 9 additions & 10 deletions helpdesk/accounts/migrations/0003_auto_20230318_1405.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@


class Migration(migrations.Migration):

dependencies = [
('accounts', '0002_add_default_ticket_queue'),
("accounts", "0002_add_default_ticket_queue"),
]

operations = [
migrations.RemoveField(
model_name='user',
name='name',
model_name="user",
name="name",
),
migrations.AddField(
model_name='user',
name='first_name',
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
model_name="user",
name="first_name",
field=models.CharField(blank=True, max_length=150, verbose_name="first name"),
),
migrations.AddField(
model_name='user',
name='last_name',
field=models.CharField(blank=True, max_length=150, verbose_name='last name'),
model_name="user",
name="last_name",
field=models.CharField(blank=True, max_length=150, verbose_name="last name"),
),
]
7 changes: 3 additions & 4 deletions helpdesk/accounts/migrations/0005_user_onboarded_at.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@


class Migration(migrations.Migration):

dependencies = [
('accounts', '0004_allow_default_ticket_queue_to_be_blank'),
("accounts", "0004_allow_default_ticket_queue_to_be_blank"),
]

operations = [
migrations.AddField(
model_name='user',
name='onboarded_at',
model_name="user",
name="onboarded_at",
field=models.DateTimeField(blank=True, null=True),
),
]
1 change: 0 additions & 1 deletion helpdesk/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@


class User(AbstractUser):

# Helpdesk Specific Fields
default_ticket_queue = models.ForeignKey(
"tickets.TicketQueue",
Expand Down
14 changes: 6 additions & 8 deletions helpdesk/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@


class ProfileUpdateView(LoginRequiredMixin, UpdateView):

model = User
fields = ["first_name", "last_name", "default_ticket_queue"]

Expand All @@ -28,8 +27,8 @@ def get_object(self, queryset: models.QuerySet[User] | None = None) -> User:
def get_form(self, form_class: type[BaseModelForm] | None = None) -> BaseModelForm:
form = super().get_form(form_class)
# Modify generated form to require a name.
form.fields['first_name'].required = True
form.fields['first_name'].label = "Given name"
form.fields["first_name"].required = True
form.fields["first_name"].label = "Given name"
return form

def get_success_url(self) -> str:
Expand All @@ -38,7 +37,6 @@ def get_success_url(self) -> str:


class OnboardingView(LoginRequiredMixin, UpdateView):

model = User
fields = ["first_name", "last_name", "default_ticket_queue"]
template_name = "accounts/onboarding.html"
Expand All @@ -57,8 +55,8 @@ def get_object(self, queryset: models.QuerySet[User] | None = None) -> User:
def get_form(self, form_class: type[BaseModelForm] | None = None) -> BaseModelForm:
form = super().get_form(form_class)
# Modify generated form to require a name.
form.fields['first_name'].required = True
form.fields['first_name'].label = "Given name"
form.fields["first_name"].required = True
form.fields["first_name"].label = "Given name"
return form

def form_valid(self, form: BaseModelForm) -> HttpResponse:
Expand All @@ -72,5 +70,5 @@ def get_success_url(self) -> str:

class SignupView(CreateView):
form_class = SignupForm
success_url = reverse_lazy('account_login')
template_name = 'accounts/signup.html'
success_url = reverse_lazy("account_login")
template_name = "accounts/signup.html"
4 changes: 2 additions & 2 deletions helpdesk/display/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@


class DisplayConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'display'
default_auto_field = "django.db.models.BigAutoField"
name = "display"
1 change: 0 additions & 1 deletion helpdesk/display/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@


class HelpdeskDisplayView(TemplateView):

template_name = "display/helpdesk.html"

def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
Expand Down
2 changes: 0 additions & 2 deletions helpdesk/helpdesk/account_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class AccountAdapter(DefaultAccountAdapter):

def is_open_for_signup(self, request: HttpRequest) -> bool:
"""
Checks whether or not the site is open for signups.
Expand All @@ -17,4 +16,3 @@ def is_open_for_signup(self, request: HttpRequest) -> bool:
if request.path.rstrip("/") == reverse("account_signup").rstrip("/"):
return False
return True

2 changes: 0 additions & 2 deletions helpdesk/helpdesk/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@


class CommentSubmitForm(forms.Form):

comment = forms.CharField(widget=forms.Textarea(attrs={"rows": "5"}))

70 changes: 50 additions & 20 deletions helpdesk/helpdesk/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@
ADMINS = getattr(configuration, "ADMINS", [])
BASE_PATH = getattr(configuration, "BASE_PATH", "")
if BASE_PATH:
BASE_PATH = (
BASE_PATH.strip("/") + "/"
) # Enforce trailing slash only # pragma: nocover
BASE_PATH = BASE_PATH.strip("/") + "/" # Enforce trailing slash only # pragma: nocover
DEBUG = getattr(configuration, "DEBUG", False)
EMAIL = getattr(configuration, "EMAIL", {})
SYSTEM_TITLE = getattr(configuration, "SYSTEM_TITLE", "Helpdesk")
Expand Down Expand Up @@ -101,12 +99,12 @@
"crispy_forms",
"crispy_bulma",
"django_filters",
'django_tables2',
'django_tables2_bulma_template',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
"django_tables2",
"django_tables2_bulma_template",
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.google",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
Expand All @@ -123,12 +121,13 @@
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"allauth.account.middleware.AccountMiddleware",
"accounts.middleware.ProfileMiddleware",
]

AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
]

CORS_ALLOW_ALL_ORIGINS = True
Expand Down Expand Up @@ -193,7 +192,7 @@

# Authentication URLs
LOGIN_URL = f"/{BASE_PATH}auth/login/"
LOGOUT_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = LOGIN_URL

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
Expand All @@ -209,16 +208,16 @@

# Django AllAuth

ACCOUNT_ADAPTER = 'helpdesk.account_adapter.AccountAdapter'
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https'
ACCOUNT_ADAPTER = "helpdesk.account_adapter.AccountAdapter"
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': [
'profile',
'email',
"google": {
"SCOPE": [
"profile",
"email",
],
'AUTH_PARAMS': {
'access_type': 'online',
"AUTH_PARAMS": {
"access_type": "online",
},
},
}
Expand All @@ -227,3 +226,34 @@

SRCOMP_HTTP_BASE_URL = getattr(configuration, "SRCOMP_HTTP_BASE_URL", None)
VOLUNTEER_SIGNUP_CODE = getattr(configuration, "VOLUNTEER_SIGNUP_CODE")


LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
# Send logs with at least INFO level to the console.
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "verbose",
},
},
"formatters": {
"verbose": {
"format": "[%(asctime)s][%(process)d][%(levelname)s][%(name)s] %(message)s",
},
},
"loggers": {
"django.request": {
"handlers": ["console"],
"level": "ERROR",
"propagate": False,
},
"django.security": {
"handlers": ["console"],
"level": "WARNING",
"propagate": False,
},
},
}
Loading

0 comments on commit 9523420

Please sign in to comment.