diff --git a/config/api_router.py b/config/api_router.py index ae44f3f..958acd5 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -1,10 +1,10 @@ from dicom.api.views import ( CreateCircleApi, - CreatePolygonApi, + CreateroiApi, ListCreateDicomApi, RetrieveUpdateDeleteCircleApi, RetrieveUpdateDeleteDicomApi, - RetrieveUpdateDeletePolygonApi, + RetrieveUpdateDeleteroiApi, SmartFileUploadApi, ) from django.urls import include, path @@ -34,9 +34,9 @@ urlpatterns = [ name="get_update_delete_dicom", ), path( - "/polygon", - CreatePolygonApi.as_view(), - name="create_polygon", + "/Roi", + CreateroiApi.as_view(), + name="create_roi", ), path( "/circle", @@ -51,9 +51,9 @@ urlpatterns = [ include( [ path( - "polygon/", - RetrieveUpdateDeletePolygonApi.as_view(), - name="get_update_delete_polygon", + "Roi/", + RetrieveUpdateDeleteroiApi.as_view(), + name="get_update_delete_roi", ), path( "circle/", diff --git a/config/settings/base.py b/config/settings/base.py index 0a4c53b..f136cf5 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -66,14 +66,7 @@ DJANGO_APPS = [ "django.forms", ] THIRD_PARTY_APPS = [ - "crispy_forms", - "crispy_bootstrap5", - "allauth", - "allauth.account", - "allauth.socialaccount", - "django_celery_beat", "rest_framework", - "rest_framework.authtoken", "corsheaders", "drf_spectacular", ] @@ -189,7 +182,6 @@ TEMPLATES = [ "django.template.context_processors.static", "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", - "image_markuper.users.context_processors.allauth_settings", ], }, } @@ -284,23 +276,6 @@ CELERY_TASK_TIME_LIMIT = 5 * 60 CELERY_TASK_SOFT_TIME_LIMIT = 60 # https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-scheduler CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" -# django-allauth -# ------------------------------------------------------------------------------ -ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True) -# https://django-allauth.readthedocs.io/en/latest/configuration.html -ACCOUNT_AUTHENTICATION_METHOD = "username" -# https://django-allauth.readthedocs.io/en/latest/configuration.html -ACCOUNT_EMAIL_REQUIRED = True -# https://django-allauth.readthedocs.io/en/latest/configuration.html -ACCOUNT_EMAIL_VERIFICATION = "mandatory" -# https://django-allauth.readthedocs.io/en/latest/configuration.html -ACCOUNT_ADAPTER = "image_markuper.users.adapters.AccountAdapter" -# https://django-allauth.readthedocs.io/en/latest/forms.html -ACCOUNT_FORMS = {"signup": "image_markuper.users.forms.UserSignupForm"} -# https://django-allauth.readthedocs.io/en/latest/configuration.html -SOCIALACCOUNT_ADAPTER = "image_markuper.users.adapters.SocialAccountAdapter" -# https://django-allauth.readthedocs.io/en/latest/forms.html -SOCIALACCOUNT_FORMS = {"signup": "image_markuper.users.forms.UserSocialSignupForm"} # django-rest-framework # ------------------------------------------------------------------------------- diff --git a/image_markuper/dicom/admin.py b/image_markuper/dicom/admin.py index ac2c093..72ec33b 100644 --- a/image_markuper/dicom/admin.py +++ b/image_markuper/dicom/admin.py @@ -1,6 +1,6 @@ -from dicom.models import Circle, Dicom, Polygon +from dicom.models import Circle, Dicom, Roi from django.contrib import admin admin.site.register(Dicom) admin.site.register(Circle) -admin.site.register(Polygon) +admin.site.register(Roi) diff --git a/image_markuper/dicom/api/serializers.py b/image_markuper/dicom/api/serializers.py index 6bc70c9..d104649 100644 --- a/image_markuper/dicom/api/serializers.py +++ b/image_markuper/dicom/api/serializers.py @@ -1,4 +1,4 @@ -from dicom.models import Circle, Coordinate, Dicom, Polygon +from dicom.models import Circle, Coordinate, Dicom, Roi from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from rest_framework.generics import get_object_or_404 @@ -34,7 +34,7 @@ class ListDicomSerializer(serializers.ModelSerializer): class BaseShapeSerializer(serializers.Serializer): - type = serializers.ChoiceField(choices=["circle", "polygon"]) + type = serializers.ChoiceField(choices=["circle", "Roi"]) image_number = serializers.IntegerField() coordinates = CoordinateSerializer(many=True) @@ -52,11 +52,11 @@ class DicomSerializer(serializers.ModelSerializer): fields = ["file", "uploaded", "pathology_type", "shapes"] -class PolygonSerializer(serializers.ModelSerializer): +class RoiSerializer(serializers.ModelSerializer): coordinates = CoordinateSerializer(many=True) class Meta: - model = Polygon + model = Roi fields = ["id", "image_number", "coordinates"] extra_kwargs = {"id": {"read_only": True}} @@ -66,12 +66,12 @@ class PolygonSerializer(serializers.ModelSerializer): dicom = get_object_or_404( Dicom, slug=self.context["request"].parser_context["kwargs"]["slug"] ) - polygon = Polygon.objects.create( + roi = Roi.objects.create( dicom=dicom, image_number=validated_data["image_number"] ) - create_coordinate(validated_data["coordinates"], polygon) - return polygon + create_coordinate(validated_data["coordinates"], roi) + return roi def update(self, obj: Circle, validated_data): Coordinate.objects.filter(shape=obj).delete() @@ -114,5 +114,5 @@ class CircleSerializer(serializers.ModelSerializer): return obj -class SmartFileUploadSerializer(serializers.ModelSerializer): +class SmartFileUploadSerializer(serializers.Serializer): file = serializers.FileField() diff --git a/image_markuper/dicom/api/views.py b/image_markuper/dicom/api/views.py index 7876861..a8e4db6 100644 --- a/image_markuper/dicom/api/views.py +++ b/image_markuper/dicom/api/views.py @@ -1,18 +1,18 @@ from drf_spectacular.utils import extend_schema from rest_framework import generics, status from rest_framework.exceptions import ValidationError -from rest_framework.generics import get_object_or_404 +from rest_framework.generics import GenericAPIView, get_object_or_404 from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.response import Response -from rest_framework.views import APIView -from ..models import Circle, Dicom, Polygon +from ..models import Circle, Dicom, Roi from ..services import process_files from .serializers import ( CircleSerializer, DicomSerializer, ListDicomSerializer, - PolygonSerializer, + RoiSerializer, + SmartFileUploadSerializer, ) @@ -34,21 +34,19 @@ class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView): lookup_field = "slug" -class CreatePolygonApi(generics.CreateAPIView): - serializer_class = PolygonSerializer +class CreateroiApi(generics.CreateAPIView): + serializer_class = RoiSerializer class CreateCircleApi(generics.CreateAPIView): serializer_class = CircleSerializer -class RetrieveUpdateDeletePolygonApi(generics.RetrieveUpdateDestroyAPIView): - serializer_class = PolygonSerializer +class RetrieveUpdateDeleteroiApi(generics.RetrieveUpdateDestroyAPIView): + serializer_class = RoiSerializer def get_object(self): - return get_object_or_404( - Polygon, id=self.request.parser_context["kwargs"]["id"] - ) + return get_object_or_404(Roi, id=self.request.parser_context["kwargs"]["id"]) @extend_schema(description="Note: coordinated are dropped on update") def put(self, request, *args, **kwargs): @@ -74,9 +72,11 @@ class RetrieveUpdateDeleteCircleApi(generics.RetrieveUpdateDestroyAPIView): return self.update(request, *args, **kwargs) -class SmartFileUploadApi(APIView): +class SmartFileUploadApi(GenericAPIView): parser_classes = [MultiPartParser, FormParser] + serializer_class = SmartFileUploadSerializer + @extend_schema(responses={201: DicomSerializer(many=True)}) def post(self, request): if "file" not in request.data: raise ValidationError("no files") @@ -85,3 +85,7 @@ class SmartFileUploadApi(APIView): DicomSerializer(d_list.files.all(), many=True).data, status=status.HTTP_201_CREATED, ) + + +class UpdateDicomLayerApi(GenericAPIView): + serializer_class = SmartFileUploadSerializer diff --git a/image_markuper/dicom/migrations/0001_initial.py b/image_markuper/dicom/migrations/0001_initial.py index 5c9370b..729616f 100644 --- a/image_markuper/dicom/migrations/0001_initial.py +++ b/image_markuper/dicom/migrations/0001_initial.py @@ -1,28 +1,10 @@ -# Generated by Django 4.0.8 on 2022-10-26 15:01 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import utils.files +from django.db import migrations class Migration(migrations.Migration): initial = True - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] + dependencies = [] - operations = [ - migrations.CreateModel( - name='Dicom', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField()), - ('file', models.FileField(upload_to=utils.files.media_upload_path)), - ('uploaded', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to=settings.AUTH_USER_MODEL)), - ], - ), - ] + operations = [] diff --git a/image_markuper/dicom/models/__init__.py b/image_markuper/dicom/models/__init__.py index d099365..0a6c40b 100644 --- a/image_markuper/dicom/models/__init__.py +++ b/image_markuper/dicom/models/__init__.py @@ -1,3 +1,3 @@ # flake8: noqa from .base import Dicom, ListOfDicom -from .blocks import BaseShape, Circle, Coordinate, Polygon +from .blocks import BaseShape, Circle, Coordinate, Roi diff --git a/image_markuper/dicom/models/blocks.py b/image_markuper/dicom/models/blocks.py index 3f93f34..d51abac 100644 --- a/image_markuper/dicom/models/blocks.py +++ b/image_markuper/dicom/models/blocks.py @@ -47,14 +47,14 @@ class Circle(BaseShape): return f"circle on {self.dicom.file.name}" -class Polygon(BaseShape): +class Roi(BaseShape): def serialize_self(self): return { "id": self.id, - "type": "polygon", + "type": "Roi", "image_number": self.image_number, "coordinates": self.coordinates, } def __str__(self): - return f"polygon on {self.dicom.file.name}" + return f"Roi on {self.dicom.file.name}" diff --git a/image_markuper/users/adapters.py b/image_markuper/users/adapters.py deleted file mode 100644 index 0d206fa..0000000 --- a/image_markuper/users/adapters.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Any - -from allauth.account.adapter import DefaultAccountAdapter -from allauth.socialaccount.adapter import DefaultSocialAccountAdapter -from django.conf import settings -from django.http import HttpRequest - - -class AccountAdapter(DefaultAccountAdapter): - def is_open_for_signup(self, request: HttpRequest): - return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) - - -class SocialAccountAdapter(DefaultSocialAccountAdapter): - def is_open_for_signup(self, request: HttpRequest, sociallogin: Any): - return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) diff --git a/image_markuper/users/admin.py b/image_markuper/users/admin.py index d2d6698..3da2800 100644 --- a/image_markuper/users/admin.py +++ b/image_markuper/users/admin.py @@ -1,33 +1,7 @@ from django.contrib import admin -from django.contrib.auth import admin as auth_admin from django.contrib.auth import get_user_model -from django.utils.translation import gettext_lazy as _ - -from image_markuper.users.forms import UserAdminChangeForm, UserAdminCreationForm User = get_user_model() -@admin.register(User) -class UserAdmin(auth_admin.UserAdmin): - - form = UserAdminChangeForm - add_form = UserAdminCreationForm - fieldsets = ( - (None, {"fields": ("username", "password")}), - ( - _("Permissions"), - { - "fields": ( - "is_active", - "is_staff", - "is_superuser", - "groups", - "user_permissions", - ), - }, - ), - (_("Important dates"), {"fields": ("last_login", "date_joined")}), - ) - list_display = ["username", "is_superuser"] - search_fields = ["username"] +admin.site.register(User) diff --git a/image_markuper/users/apps.py b/image_markuper/users/apps.py index 7a1e4ec..71bce63 100644 --- a/image_markuper/users/apps.py +++ b/image_markuper/users/apps.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ class UsersConfig(AppConfig): - name = "image_markuper.users" + name = "users" verbose_name = _("Users") def ready(self): diff --git a/image_markuper/users/context_processors.py b/image_markuper/users/context_processors.py deleted file mode 100644 index e2633ae..0000000 --- a/image_markuper/users/context_processors.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.conf import settings - - -def allauth_settings(request): - """Expose some settings from django-allauth in templates.""" - return { - "ACCOUNT_ALLOW_REGISTRATION": settings.ACCOUNT_ALLOW_REGISTRATION, - } diff --git a/image_markuper/users/forms.py b/image_markuper/users/forms.py deleted file mode 100644 index 6e1dd9d..0000000 --- a/image_markuper/users/forms.py +++ /dev/null @@ -1,42 +0,0 @@ -from allauth.account.forms import SignupForm -from allauth.socialaccount.forms import SignupForm as SocialSignupForm -from django.contrib.auth import forms as admin_forms -from django.contrib.auth import get_user_model -from django.utils.translation import gettext_lazy as _ - -User = get_user_model() - - -class UserAdminChangeForm(admin_forms.UserChangeForm): - class Meta(admin_forms.UserChangeForm.Meta): - model = User - - -class UserAdminCreationForm(admin_forms.UserCreationForm): - """ - Form for User Creation in the Admin Area. - To change user signup, see UserSignupForm and UserSocialSignupForm. - """ - - class Meta(admin_forms.UserCreationForm.Meta): - model = User - - error_messages = { - "username": {"unique": _("This username has already been taken.")} - } - - -class UserSignupForm(SignupForm): - """ - Form that will be rendered on a user sign up section/screen. - Default fields will be added automatically. - Check UserSocialSignupForm for accounts created from social. - """ - - -class UserSocialSignupForm(SocialSignupForm): - """ - Renders the form when user has signed up using social accounts. - Default fields will be added automatically. - See UserSignupForm otherwise. - """ diff --git a/image_markuper/users/migrations/0001_initial.py b/image_markuper/users/migrations/0001_initial.py index acd1851..729616f 100644 --- a/image_markuper/users/migrations/0001_initial.py +++ b/image_markuper/users/migrations/0001_initial.py @@ -1,124 +1,10 @@ -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.utils.timezone +from django.db import migrations class Migration(migrations.Migration): initial = True - dependencies = [ - ("auth", "0012_alter_user_first_name_max_length"), - ] + dependencies = [] - operations = [ - migrations.CreateModel( - name="User", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("password", models.CharField(max_length=128, verbose_name="password")), - ( - "last_login", - models.DateTimeField( - blank=True, null=True, verbose_name="last login" - ), - ), - ( - "is_superuser", - models.BooleanField( - default=False, - help_text="Designates that this user has all permissions without explicitly assigning them.", - verbose_name="superuser status", - ), - ), - ( - "username", - models.CharField( - error_messages={ - "unique": "A user with that username already exists." - }, - help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", - max_length=150, - unique=True, - validators=[ - django.contrib.auth.validators.UnicodeUsernameValidator() - ], - verbose_name="username", - ), - ), - ( - "email", - models.EmailField( - blank=True, max_length=254, verbose_name="email address" - ), - ), - ( - "is_staff", - models.BooleanField( - default=False, - help_text="Designates whether the user can log into this admin site.", - verbose_name="staff status", - ), - ), - ( - "is_active", - models.BooleanField( - default=True, - help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", - verbose_name="active", - ), - ), - ( - "date_joined", - models.DateTimeField( - default=django.utils.timezone.now, verbose_name="date joined" - ), - ), - ( - "name", - models.CharField( - blank=True, max_length=255, verbose_name="Name of User" - ), - ), - ( - "groups", - models.ManyToManyField( - blank=True, - help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", - related_name="user_set", - related_query_name="user", - to="auth.Group", - verbose_name="groups", - ), - ), - ( - "user_permissions", - models.ManyToManyField( - blank=True, - help_text="Specific permissions for this user.", - related_name="user_set", - related_query_name="user", - to="auth.Permission", - verbose_name="user permissions", - ), - ), - ], - options={ - "verbose_name": "user", - "verbose_name_plural": "users", - "abstract": False, - }, - managers=[ - ("objects", django.contrib.auth.models.UserManager()), - ], - ), - ] + operations = [] diff --git a/image_markuper/users/tasks.py b/image_markuper/users/tasks.py deleted file mode 100644 index c99341c..0000000 --- a/image_markuper/users/tasks.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.contrib.auth import get_user_model - -from config import celery_app - -User = get_user_model() - - -@celery_app.task() -def get_users_count(): - """A pointless Celery task to demonstrate usage.""" - return User.objects.count() diff --git a/image_markuper/users/tests/__init__.py b/image_markuper/users/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/image_markuper/users/tests/factories.py b/image_markuper/users/tests/factories.py deleted file mode 100644 index e304762..0000000 --- a/image_markuper/users/tests/factories.py +++ /dev/null @@ -1,33 +0,0 @@ -from collections.abc import Sequence -from typing import Any - -from django.contrib.auth import get_user_model -from factory import Faker, post_generation -from factory.django import DjangoModelFactory - - -class UserFactory(DjangoModelFactory): - - username = Faker("user_name") - email = Faker("email") - name = Faker("name") - - @post_generation - def password(self, create: bool, extracted: Sequence[Any], **kwargs): - password = ( - extracted - if extracted - else Faker( - "password", - length=42, - special_chars=True, - digits=True, - upper_case=True, - lower_case=True, - ).evaluate(None, None, extra={"locale": None}) - ) - self.set_password(password) - - class Meta: - model = get_user_model() - django_get_or_create = ["username"] diff --git a/image_markuper/users/tests/test_admin.py b/image_markuper/users/tests/test_admin.py deleted file mode 100644 index 665d444..0000000 --- a/image_markuper/users/tests/test_admin.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.urls import reverse - -from image_markuper.users.models import User - - -class TestUserAdmin: - def test_changelist(self, admin_client): - url = reverse("admin:users_user_changelist") - response = admin_client.get(url) - assert response.status_code == 200 - - def test_search(self, admin_client): - url = reverse("admin:users_user_changelist") - response = admin_client.get(url, data={"q": "test"}) - assert response.status_code == 200 - - def test_add(self, admin_client): - url = reverse("admin:users_user_add") - response = admin_client.get(url) - assert response.status_code == 200 - - response = admin_client.post( - url, - data={ - "username": "test", - "password1": "My_R@ndom-P@ssw0rd", - "password2": "My_R@ndom-P@ssw0rd", - }, - ) - assert response.status_code == 302 - assert User.objects.filter(username="test").exists() - - def test_view_user(self, admin_client): - user = User.objects.get(username="admin") - url = reverse("admin:users_user_change", kwargs={"object_id": user.pk}) - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/image_markuper/users/tests/test_drf_urls.py b/image_markuper/users/tests/test_drf_urls.py deleted file mode 100644 index 8d2a559..0000000 --- a/image_markuper/users/tests/test_drf_urls.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.urls import resolve, reverse - -from image_markuper.users.models import User - - -def test_user_detail(user: User): - assert ( - reverse("api:user-detail", kwargs={"username": user.username}) - == f"/api/users/{user.username}/" - ) - assert resolve(f"/api/users/{user.username}/").view_name == "api:user-detail" - - -def test_user_list(): - assert reverse("api:user-list") == "/api/users/" - assert resolve("/api/users/").view_name == "api:user-list" - - -def test_user_me(): - assert reverse("api:user-me") == "/api/users/me/" - assert resolve("/api/users/me/").view_name == "api:user-me" diff --git a/image_markuper/users/tests/test_drf_views.py b/image_markuper/users/tests/test_drf_views.py deleted file mode 100644 index 0ef5d75..0000000 --- a/image_markuper/users/tests/test_drf_views.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.test import RequestFactory - -from image_markuper.users.api.views import UserViewSet -from image_markuper.users.models import User - - -class TestUserViewSet: - def test_get_queryset(self, user: User, rf: RequestFactory): - view = UserViewSet() - request = rf.get("/fake-url/") - request.user = user - - view.request = request - - assert user in view.get_queryset() - - def test_me(self, user: User, rf: RequestFactory): - view = UserViewSet() - request = rf.get("/fake-url/") - request.user = user - - view.request = request - - response = view.me(request) - - assert response.data == { - "username": user.username, - "name": user.name, - "url": f"http://testserver/api/users/{user.username}/", - } diff --git a/image_markuper/users/tests/test_forms.py b/image_markuper/users/tests/test_forms.py deleted file mode 100644 index 1a98ca9..0000000 --- a/image_markuper/users/tests/test_forms.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Module for all Form Tests. -""" -from django.utils.translation import gettext_lazy as _ - -from image_markuper.users.forms import UserAdminCreationForm -from image_markuper.users.models import User - - -class TestUserAdminCreationForm: - """ - Test class for all tests related to the UserAdminCreationForm - """ - - def test_username_validation_error_msg(self, user: User): - """ - Tests UserAdminCreation Form's unique validator functions correctly by testing: - 1) A new user with an existing username cannot be added. - 2) Only 1 error is raised by the UserCreation Form - 3) The desired error message is raised - """ - - # The user already exists, - # hence cannot be created. - form = UserAdminCreationForm( - { - "username": user.username, - "password1": user.password, - "password2": user.password, - } - ) - - assert not form.is_valid() - assert len(form.errors) == 1 - assert "username" in form.errors - assert form.errors["username"][0] == _("This username has already been taken.") diff --git a/image_markuper/users/tests/test_models.py b/image_markuper/users/tests/test_models.py deleted file mode 100644 index fd27b98..0000000 --- a/image_markuper/users/tests/test_models.py +++ /dev/null @@ -1,5 +0,0 @@ -from image_markuper.users.models import User - - -def test_user_get_absolute_url(user: User): - assert user.get_absolute_url() == f"/users/{user.username}/" diff --git a/image_markuper/users/tests/test_swagger.py b/image_markuper/users/tests/test_swagger.py deleted file mode 100644 index f97658b..0000000 --- a/image_markuper/users/tests/test_swagger.py +++ /dev/null @@ -1,21 +0,0 @@ -import pytest -from django.urls import reverse - - -def test_swagger_accessible_by_admin(admin_client): - url = reverse("api-docs") - response = admin_client.get(url) - assert response.status_code == 200 - - -@pytest.mark.django_db -def test_swagger_ui_not_accessible_by_normal_user(client): - url = reverse("api-docs") - response = client.get(url) - assert response.status_code == 403 - - -def test_api_schema_generated_successfully(admin_client): - url = reverse("api-schema") - response = admin_client.get(url) - assert response.status_code == 200 diff --git a/image_markuper/users/tests/test_tasks.py b/image_markuper/users/tests/test_tasks.py deleted file mode 100644 index ddf5784..0000000 --- a/image_markuper/users/tests/test_tasks.py +++ /dev/null @@ -1,16 +0,0 @@ -import pytest -from celery.result import EagerResult - -from image_markuper.users.tasks import get_users_count -from image_markuper.users.tests.factories import UserFactory - -pytestmark = pytest.mark.django_db - - -def test_user_count(settings): - """A basic test to execute the get_users_count Celery task.""" - UserFactory.create_batch(3) - settings.CELERY_TASK_ALWAYS_EAGER = True - task_result = get_users_count.delay() - assert isinstance(task_result, EagerResult) - assert task_result.result == 3 diff --git a/image_markuper/users/tests/test_urls.py b/image_markuper/users/tests/test_urls.py deleted file mode 100644 index 0e1499f..0000000 --- a/image_markuper/users/tests/test_urls.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.urls import resolve, reverse - -from image_markuper.users.models import User - - -def test_detail(user: User): - assert ( - reverse("users:detail", kwargs={"username": user.username}) - == f"/users/{user.username}/" - ) - assert resolve(f"/users/{user.username}/").view_name == "users:detail" - - -def test_update(): - assert reverse("users:update") == "/users/~update/" - assert resolve("/users/~update/").view_name == "users:update" - - -def test_redirect(): - assert reverse("users:redirect") == "/users/~redirect/" - assert resolve("/users/~redirect/").view_name == "users:redirect" diff --git a/image_markuper/users/tests/test_views.py b/image_markuper/users/tests/test_views.py deleted file mode 100644 index 578bc93..0000000 --- a/image_markuper/users/tests/test_views.py +++ /dev/null @@ -1,103 +0,0 @@ -import pytest -from django.conf import settings -from django.contrib import messages -from django.contrib.auth.models import AnonymousUser -from django.contrib.messages.middleware import MessageMiddleware -from django.contrib.sessions.middleware import SessionMiddleware -from django.http import HttpRequest, HttpResponseRedirect -from django.test import RequestFactory -from django.urls import reverse - -from image_markuper.users.forms import UserAdminChangeForm -from image_markuper.users.models import User -from image_markuper.users.tests.factories import UserFactory -from image_markuper.users.views import ( - UserRedirectView, - UserUpdateView, - user_detail_view, -) - -pytestmark = pytest.mark.django_db - - -class TestUserUpdateView: - """ - TODO: - extracting view initialization code as class-scoped fixture - would be great if only pytest-django supported non-function-scoped - fixture db access -- this is a work-in-progress for now: - https://github.com/pytest-dev/pytest-django/pull/258 - """ - - def dummy_get_response(self, request: HttpRequest): - return None - - def test_get_success_url(self, user: User, rf: RequestFactory): - view = UserUpdateView() - request = rf.get("/fake-url/") - request.user = user - - view.request = request - - assert view.get_success_url() == f"/users/{user.username}/" - - def test_get_object(self, user: User, rf: RequestFactory): - view = UserUpdateView() - request = rf.get("/fake-url/") - request.user = user - - view.request = request - - assert view.get_object() == user - - def test_form_valid(self, user: User, rf: RequestFactory): - view = UserUpdateView() - request = rf.get("/fake-url/") - - # Add the session/message middleware to the request - SessionMiddleware(self.dummy_get_response).process_request(request) - MessageMiddleware(self.dummy_get_response).process_request(request) - request.user = user - - view.request = request - - # Initialize the form - form = UserAdminChangeForm() - form.cleaned_data = {} - form.instance = user - view.form_valid(form) - - messages_sent = [m.message for m in messages.get_messages(request)] - assert messages_sent == ["Information successfully updated"] - - -class TestUserRedirectView: - def test_get_redirect_url(self, user: User, rf: RequestFactory): - view = UserRedirectView() - request = rf.get("/fake-url") - request.user = user - - view.request = request - - assert view.get_redirect_url() == f"/users/{user.username}/" - - -class TestUserDetailView: - def test_authenticated(self, user: User, rf: RequestFactory): - request = rf.get("/fake-url/") - request.user = UserFactory() - - response = user_detail_view(request, username=user.username) - - assert response.status_code == 200 - - def test_not_authenticated(self, user: User, rf: RequestFactory): - request = rf.get("/fake-url/") - request.user = AnonymousUser() - - response = user_detail_view(request, username=user.username) - login_url = reverse(settings.LOGIN_URL) - - assert isinstance(response, HttpResponseRedirect) - assert response.status_code == 302 - assert response.url == f"{login_url}?next=/fake-url/" diff --git a/image_markuper/users/urls.py b/image_markuper/users/urls.py deleted file mode 100644 index 2282783..0000000 --- a/image_markuper/users/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.urls import path - -from image_markuper.users.views import ( - user_detail_view, - user_redirect_view, - user_update_view, -) - -app_name = "users" -urlpatterns = [ - path("~redirect/", view=user_redirect_view, name="redirect"), - path("~update/", view=user_update_view, name="update"), - path("/", view=user_detail_view, name="detail"), -] diff --git a/image_markuper/users/views.py b/image_markuper/users/views.py deleted file mode 100644 index cc31b1e..0000000 --- a/image_markuper/users/views.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.contrib.auth import get_user_model -from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.messages.views import SuccessMessageMixin -from django.urls import reverse -from django.utils.translation import gettext_lazy as _ -from django.views.generic import DetailView, RedirectView, UpdateView - -User = get_user_model() - - -class UserDetailView(LoginRequiredMixin, DetailView): - - model = User - slug_field = "username" - slug_url_kwarg = "username" - - -user_detail_view = UserDetailView.as_view() - - -class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): - - model = User - fields = ["name"] - success_message = _("Information successfully updated") - - def get_success_url(self): - return self.request.user.get_absolute_url() - - def get_object(self): - return self.request.user - - -user_update_view = UserUpdateView.as_view() - - -class UserRedirectView(LoginRequiredMixin, RedirectView): - - permanent = False - - def get_redirect_url(self): - return reverse("users:detail", kwargs={"username": self.request.user.username}) - - -user_redirect_view = UserRedirectView.as_view()