-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/user models #61
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
Merged
Merged
Changes from 11 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
fc79f61
Add Icon Button Component
XanderVertegaal c7d3134
Add copy button
XanderVertegaal c55719c
Avoid unnecessary requests
XanderVertegaal 5440a8a
Compare route params
XanderVertegaal d42d1e2
Add base field to form
XanderVertegaal 7b4796f
Add base field to Problem
XanderVertegaal e7da8fc
Use Problem.field in problem views
XanderVertegaal 07297b3
Use update_or_create for sentences
XanderVertegaal aadfd32
Use base field in frontend
XanderVertegaal 60a2e5a
Simplify Annotate component template
XanderVertegaal bcb27e9
Keep queryParams after form update
XanderVertegaal a68c25a
Fix failing tests
XanderVertegaal 1e089b0
Merge branch 'feature/user-problems' into feature/derived-problems
XanderVertegaal 658a1fe
Add base field to serializer
XanderVertegaal 8c23335
Add Replace ViewMode with AppMode in ProblemDetailsComponent
XanderVertegaal d0e0d34
Remove unused import
XanderVertegaal c06aa05
Merge branch 'feature/user-problems' into feature/derived-problems
XanderVertegaal e49e3d6
Add role field to User model
XanderVertegaal d627060
Add role to serializer
XanderVertegaal 26a629b
Implement frontend add/edit checks
XanderVertegaal 1bd7eea
Route guard
XanderVertegaal b3d717f
Role-based user icons
XanderVertegaal 63d8684
Add backend permission checks
XanderVertegaal fde5405
Tweak admin and serializer
XanderVertegaal a5c44c5
Wait in CanEditOrAddGuard
XanderVertegaal 7e75de2
Fix route guard
XanderVertegaal aff8c7b
Fix frontend tests
XanderVertegaal e940059
Fix backend tests
XanderVertegaal 7897f67
Merge branch 'feature/user-problems' into feature/derived-problems
XanderVertegaal 0428b12
Outfactor util
XanderVertegaal 5006b4c
Set base in serializer
XanderVertegaal 873b040
Reinstate IconButton for non-navigational buttons
XanderVertegaal b4ec78d
Remove unused imports
XanderVertegaal bea3b3e
Silence Python type warning
XanderVertegaal 2ef9935
Return base correctly
XanderVertegaal f43bd3f
Avoid duplicate results upon filtering
XanderVertegaal 7188e7f
Outfactor general utility function
XanderVertegaal 5b3cbeb
Merge branch 'feature/derived-problems' into feature/user-models
XanderVertegaal ecbb706
Remove User.role; use Django permission system instead
XanderVertegaal 5b5c941
Add data migration for user groups
XanderVertegaal bb034f7
Document user permissions
XanderVertegaal 31bac00
Use new permission checks in frontend
XanderVertegaal 045a612
Major Tom
XanderVertegaal b169462
Fix DRF Permission classes
XanderVertegaal 856832e
Reuse permissions in tests
XanderVertegaal 27f7a0f
Fix failing tests
XanderVertegaal 5acbcb0
Use appropriate casing
XanderVertegaal 49fadb4
Fix frontend tests as well
XanderVertegaal 0b2b8c2
Reinstate illegally removed whitelines
XanderVertegaal e0733f9
Use built-in client for tests
XanderVertegaal 65e2bc7
Remove unused imports
XanderVertegaal 140a505
Reinstate proper indentation
XanderVertegaal c666067
Outfactor user2icon
XanderVertegaal 3f69dfb
Reduce excessive nesting in newProblem$ observable
XanderVertegaal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,45 @@ | ||
| from django.contrib import admin | ||
| from django.contrib.auth import admin as auth_admin | ||
| from . import models | ||
|
|
||
|
|
||
| @admin.register(models.User) | ||
| class UserAdmin(auth_admin.UserAdmin): | ||
| pass | ||
| from typing import Any | ||
| from django.contrib import admin | ||
| from django.contrib.auth import admin as auth_admin | ||
| from django.http import HttpRequest | ||
XanderVertegaal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| from . import models | ||
| from django.utils.translation import gettext_lazy as _ | ||
XanderVertegaal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| @admin.register(models.User) | ||
| class UserAdmin(auth_admin.UserAdmin): | ||
| list_display = ("username", "first_name", "email", "last_name", "role", "is_staff") | ||
|
|
||
| def get_fieldsets( | ||
| self, request: HttpRequest, obj: models.User | None = None | ||
| ) -> list[tuple[str | None, dict[str, Any]]]: | ||
| user = request.user | ||
| if user.is_superuser: | ||
| admin_fieldset = super().get_fieldsets(request, obj) | ||
| if len(admin_fieldset) >= 3: | ||
| # Add the 'role' field to the admin fieldset for superusers. | ||
| admin_fieldset[2][1]["fields"] = ( | ||
| "is_active", | ||
| "is_staff", | ||
| "is_superuser", | ||
| "role", | ||
| "groups", | ||
| "user_permissions", | ||
| ) | ||
| return admin_fieldset | ||
|
|
||
| # Non-superusers should not be able to edit password, is_superuser and groups through Django Admin. | ||
| return [ | ||
| (None, {"fields": ("username",)}), | ||
| (_("Personal info"), {"fields": ("first_name", "last_name", "email")}), | ||
| ( | ||
| _("Permissions"), | ||
| { | ||
| "fields": ( | ||
| "is_active", | ||
| "is_staff", | ||
| "role", | ||
| ), | ||
| }, | ||
| ), | ||
| ] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Generated by Django 4.2.20 on 2025-11-04 10:51 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("user", "0003_sitedomain"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name="user", | ||
| name="role", | ||
| field=models.CharField( | ||
| choices=[ | ||
| ("annotator", "Annotator"), | ||
| ("master_annotator", "Master Annotator"), | ||
| ("visitor", "Visitor"), | ||
| ], | ||
| default="visitor", | ||
| help_text="Visitors can browse problems and parses. Annotators can annotate non-locked problems. Master Annotators can manage users, change problem lock status, and review annotations.", | ||
| max_length=20, | ||
| ), | ||
| ), | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,33 @@ | ||
| import django.contrib.auth.models as django_auth_models | ||
|
|
||
|
|
||
| class User(django_auth_models.AbstractUser): | ||
| """ | ||
| Core user model used for authentication. | ||
| """ | ||
|
|
||
| # Only extend this model with information that is relevant for | ||
| # authentication; for things like settings and preferences, add | ||
| # a UserProfile model. | ||
|
|
||
| class Meta: | ||
| db_table = "auth_user" | ||
| import django.contrib.auth.models as django_auth_models | ||
| from django.db import models | ||
|
|
||
|
|
||
| class User(django_auth_models.AbstractUser): | ||
| """ | ||
| Core user model used for authentication. | ||
| """ | ||
|
|
||
| # Only extend this model with information that is relevant for | ||
| # authentication; for things like settings and preferences, add | ||
| # a UserProfile model. | ||
|
|
||
| class Meta: | ||
| db_table = "auth_user" | ||
|
|
||
| class Role(models.TextChoices): | ||
| ANNOTATOR = "annotator", "Annotator" | ||
| MASTER_ANNOTATOR = "master_annotator", "Master Annotator" | ||
| VISITOR = "visitor", "Visitor" | ||
|
|
||
XanderVertegaal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| role = models.CharField( | ||
| max_length=20, | ||
| choices=Role.choices, | ||
| default=Role.VISITOR, | ||
| help_text="Visitors can browse problems and parses. Annotators can annotate non-locked problems. Master Annotators can manage users, change problem lock status, and review annotations.", | ||
| ) | ||
| @property | ||
| def can_edit_or_add_problem(self) -> bool: | ||
| """ | ||
| Determines whether the user can edit or add problems. | ||
| """ | ||
| return self.is_superuser or self.role in [self.Role.MASTER_ANNOTATOR] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,23 @@ | ||
| from dj_rest_auth.serializers import UserDetailsSerializer | ||
| from rest_framework import serializers | ||
|
|
||
|
|
||
| class CustomUserDetailsSerializer(UserDetailsSerializer): | ||
|
|
||
| class Meta(UserDetailsSerializer.Meta): | ||
| is_staff = serializers.BooleanField(read_only=True) | ||
| fields = ( | ||
| "id", | ||
| "username", | ||
| "email", | ||
| "first_name", | ||
| "last_name", | ||
| "is_staff", | ||
| ) | ||
| read_only_fields = ["is_staff", "id", "email"] | ||
| from dj_rest_auth.serializers import UserDetailsSerializer | ||
| from rest_framework import serializers | ||
|
|
||
|
|
||
| class CustomUserDetailsSerializer(UserDetailsSerializer): | ||
| firstName = serializers.CharField(source='first_name') | ||
| lastName = serializers.CharField(source='last_name') | ||
| isStaff = serializers.BooleanField(read_only=True, source='is_staff') | ||
| canEditOrAddProblem = serializers.BooleanField(read_only=True, source='can_edit_or_add_problem') | ||
|
|
||
| class Meta(UserDetailsSerializer.Meta): | ||
|
|
||
| fields = ( | ||
| "id", | ||
| "username", | ||
| "email", | ||
| "firstName", | ||
| "lastName", | ||
| "isStaff", | ||
| "role", | ||
| "canEditOrAddProblem", | ||
| ) | ||
| read_only_fields = ["isStaff", "id", "email"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,37 +1,39 @@ | ||
| from unittest.mock import ANY | ||
|
|
||
|
|
||
| def test_user_details(user_client, user_data): | ||
| details = user_client.get("/users/user/") | ||
| assert details.status_code == 200 | ||
| assert details.data == { | ||
| "id": ANY, | ||
| "username": user_data["username"], | ||
| "email": user_data["email"], | ||
| "first_name": user_data["first_name"], | ||
| "last_name": user_data["last_name"], | ||
| "is_staff": False, | ||
| } | ||
|
|
||
|
|
||
| def test_user_updates(user_client, user_data): | ||
| route = "/users/user/" | ||
| details = lambda: user_client.get(route).data | ||
| assert details()["username"] == user_data["username"] | ||
|
|
||
| # update username should succeed | ||
| response = user_client.patch( | ||
| route, | ||
| {"username": "NewName"}, | ||
| content_type="application/json", | ||
| ) | ||
| assert response.status_code == 200 | ||
| assert details()["username"] == "NewName" | ||
|
|
||
| # is_staff is readonly, so nothing should happen | ||
| response = user_client.patch( | ||
| route, | ||
| {"is_staff": True}, | ||
| content_type="application/json", | ||
| ) | ||
| assert not details()["is_staff"] | ||
| from unittest.mock import ANY | ||
|
|
||
|
|
||
| def test_user_details(user_client, user_data): | ||
| details = user_client.get("/users/user/") | ||
| assert details.status_code == 200 | ||
| assert details.data == { | ||
| "id": ANY, | ||
| "username": user_data["username"], | ||
| "email": user_data["email"], | ||
| "firstName": user_data["first_name"], | ||
| "lastName": user_data["last_name"], | ||
| "isStaff": False, | ||
| "role": "visitor", | ||
| "canEditOrAddProblem": False, | ||
| } | ||
|
|
||
|
|
||
| def test_user_updates(user_client, user_data): | ||
| route = "/users/user/" | ||
| details = lambda: user_client.get(route).data | ||
| assert details()["username"] == user_data["username"] | ||
|
|
||
| # update username should succeed | ||
| response = user_client.patch( | ||
| route, | ||
| {"username": "NewName"}, | ||
| content_type="application/json", | ||
| ) | ||
| assert response.status_code == 200 | ||
| assert details()["username"] == "NewName" | ||
|
|
||
| # isStaff is readonly, so nothing should happen | ||
| response = user_client.patch( | ||
| route, | ||
| {"isStaff": True}, | ||
| content_type="application/json", | ||
| ) | ||
| assert not details()["isStaff"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.