Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for SR2024 #62

Merged
merged 21 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading