mirror of
				https://github.com/leaders-of-digital-9-task/backend.git
				synced 2025-10-31 15:57:25 +03:00 
			
		
		
		
	added auth, dicom crud
This commit is contained in:
		
							parent
							
								
									8d9d0697da
								
							
						
					
					
						commit
						4f15a29b07
					
				|  | @ -3,4 +3,5 @@ | ||||||
|   <component name="JavaScriptSettings"> |   <component name="JavaScriptSettings"> | ||||||
|     <option name="languageLevel" value="ES6" /> |     <option name="languageLevel" value="ES6" /> | ||||||
|   </component> |   </component> | ||||||
|  |   <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (lct_backend)" project-jdk-type="Python SDK" /> | ||||||
| </project> | </project> | ||||||
|  |  | ||||||
|  | @ -1,15 +1,30 @@ | ||||||
| from django.conf import settings | from dicom.api.views import ListCreateDicomApi, RetrieveUpdateDeleteDicomApi | ||||||
| from rest_framework.routers import DefaultRouter, SimpleRouter | from django.urls import include, path | ||||||
|  | from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView | ||||||
|  | from users.api.views import RegisterView | ||||||
| 
 | 
 | ||||||
| from image_markuper.users.api.views import UserViewSet | urlpatterns = [ | ||||||
| 
 |     path( | ||||||
| if settings.DEBUG: |         "auth/", | ||||||
|     router = DefaultRouter() |         include( | ||||||
| else: |             [ | ||||||
|     router = SimpleRouter() |                 path("token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), | ||||||
| 
 |                 path("refresh/", TokenRefreshView.as_view(), name="token_refresh"), | ||||||
| router.register("users", UserViewSet) |                 path("register/", RegisterView.as_view(), name="user_register"), | ||||||
| 
 |             ] | ||||||
| 
 |         ), | ||||||
| app_name = "api" |     ), | ||||||
| urlpatterns = router.urls |     path( | ||||||
|  |         "dicom/", | ||||||
|  |         include( | ||||||
|  |             [ | ||||||
|  |                 path("", ListCreateDicomApi.as_view(), name="list_create_dicom"), | ||||||
|  |                 path( | ||||||
|  |                     "<str:slug>", | ||||||
|  |                     RetrieveUpdateDeleteDicomApi.as_view(), | ||||||
|  |                     name="get_update_delete_dicom", | ||||||
|  |                 ), | ||||||
|  |             ] | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent | ||||||
| APPS_DIR = ROOT_DIR / "image_markuper" | APPS_DIR = ROOT_DIR / "image_markuper" | ||||||
| env = environ.Env() | env = environ.Env() | ||||||
| 
 | 
 | ||||||
| READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False) | READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=True) | ||||||
| if READ_DOT_ENV_FILE: | if READ_DOT_ENV_FILE: | ||||||
|     # OS environment variables take precedence over variables from .env |     # OS environment variables take precedence over variables from .env | ||||||
|     env.read_env(str(ROOT_DIR / ".env")) |     env.read_env(str(ROOT_DIR / ".env")) | ||||||
|  | @ -80,6 +80,7 @@ THIRD_PARTY_APPS = [ | ||||||
| 
 | 
 | ||||||
| LOCAL_APPS = [ | LOCAL_APPS = [ | ||||||
|     "image_markuper.users", |     "image_markuper.users", | ||||||
|  |     "image_markuper.dicom" | ||||||
|     # Your stuff: custom apps go here |     # Your stuff: custom apps go here | ||||||
| ] | ] | ||||||
| # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps | # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps | ||||||
|  | @ -302,8 +303,7 @@ SOCIALACCOUNT_FORMS = {"signup": "image_markuper.users.forms.UserSocialSignupFor | ||||||
| # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ | # django-rest-framework - https://www.django-rest-framework.org/api-guide/settings/ | ||||||
| REST_FRAMEWORK = { | REST_FRAMEWORK = { | ||||||
|     "DEFAULT_AUTHENTICATION_CLASSES": ( |     "DEFAULT_AUTHENTICATION_CLASSES": ( | ||||||
|         "rest_framework.authentication.SessionAuthentication", |         "rest_framework_simplejwt.authentication.JWTAuthentication", | ||||||
|         "rest_framework.authentication.TokenAuthentication", |  | ||||||
|     ), |     ), | ||||||
|     "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), |     "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), | ||||||
|     "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", |     "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", | ||||||
|  | @ -311,18 +311,19 @@ REST_FRAMEWORK = { | ||||||
| 
 | 
 | ||||||
| # django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup | # django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup | ||||||
| CORS_URLS_REGEX = r"^/api/.*$" | CORS_URLS_REGEX = r"^/api/.*$" | ||||||
| 
 |  | ||||||
| # By Default swagger ui is available only to admin user(s). You can change permission classes to change that | # By Default swagger ui is available only to admin user(s). You can change permission classes to change that | ||||||
| # See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings | # See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings | ||||||
| SPECTACULAR_SETTINGS = { | SPECTACULAR_SETTINGS = { | ||||||
|     "TITLE": "Image markuper API", |     "TITLE": "Image markuper API", | ||||||
|     "DESCRIPTION": "Documentation of API endpoints of Image markuper", |     "DESCRIPTION": "Documentation of API endpoints of Image markuper", | ||||||
|     "VERSION": "1.0.0", |     "VERSION": "1.0.0", | ||||||
|     "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"], |     "SCHEMA_PATH_PREFIX": r"/api/", | ||||||
|  |     "SERVE_INCLUDE_SCHEMA": False, | ||||||
|  |     "SERVE_PERMISSIONS": ["rest_framework.permissions.AllowAny"], | ||||||
|     "SERVERS": [ |     "SERVERS": [ | ||||||
|         {"url": "http://127.0.0.1:8000", "description": "Local Development server"}, |         {"url": "https://dev.akarpov.ru", "description": "Development server"}, | ||||||
|         {"url": "https://akarpov.ru", "description": "Production server"}, |         {"url": "https//127.0.0.1:8000", "description": "Development server"}, | ||||||
|     ], |     ], | ||||||
| } | } | ||||||
| # Your stuff... | 
 | ||||||
| # ------------------------------------------------------------------------------ | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | from datetime import timedelta | ||||||
|  | 
 | ||||||
| from .base import *  # noqa | from .base import *  # noqa | ||||||
| from .base import env | from .base import env | ||||||
| 
 | 
 | ||||||
|  | @ -11,7 +13,7 @@ SECRET_KEY = env( | ||||||
|     default="XgO9NMQfMY5CNeVNh98WrKRXiQZnvtPzHJrF9ROPhAFLVEG1FvDD2ZRKTdJKVu8p", |     default="XgO9NMQfMY5CNeVNh98WrKRXiQZnvtPzHJrF9ROPhAFLVEG1FvDD2ZRKTdJKVu8p", | ||||||
| ) | ) | ||||||
| # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts | # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts | ||||||
| ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"] | ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "dev.akarpov.ru"] | ||||||
| 
 | 
 | ||||||
| # CACHES | # CACHES | ||||||
| # ------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------ | ||||||
|  | @ -64,5 +66,9 @@ INSTALLED_APPS += ["django_extensions"]  # noqa F405 | ||||||
| 
 | 
 | ||||||
| # https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates | # https://docs.celeryq.dev/en/stable/userguide/configuration.html#task-eager-propagates | ||||||
| CELERY_TASK_EAGER_PROPAGATES = True | CELERY_TASK_EAGER_PROPAGATES = True | ||||||
| # Your stuff... | 
 | ||||||
| # ------------------------------------------------------------------------------ | SIMPLE_JWT = { | ||||||
|  |     "ACCESS_TOKEN_LIFETIME": timedelta(minutes=10000), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CORS_ALLOW_ALL_ORIGINS = True | ||||||
|  |  | ||||||
|  | @ -2,21 +2,14 @@ from django.conf import settings | ||||||
| from django.conf.urls.static import static | from django.conf.urls.static import static | ||||||
| from django.contrib import admin | from django.contrib import admin | ||||||
| from django.urls import include, path | from django.urls import include, path | ||||||
| from django.views import defaults as default_views | from drf_spectacular.views import ( | ||||||
| from django.views.generic import TemplateView |     SpectacularAPIView, | ||||||
| from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView |     SpectacularRedocView, | ||||||
| from rest_framework.authtoken.views import obtain_auth_token |     SpectacularSwaggerView, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| urlpatterns = [ | urlpatterns = [ | ||||||
|     path("", TemplateView.as_view(template_name="pages/home.html"), name="home"), |     path(settings.ADMIN_URL, admin.site.urls) | ||||||
|     path( |  | ||||||
|         "about/", TemplateView.as_view(template_name="pages/about.html"), name="about" |  | ||||||
|     ), |  | ||||||
|     # Django Admin, use {% url 'admin:index' %} |  | ||||||
|     path(settings.ADMIN_URL, admin.site.urls), |  | ||||||
|     # User management |  | ||||||
|     path("users/", include("image_markuper.users.urls", namespace="users")), |  | ||||||
|     path("accounts/", include("allauth.urls")), |  | ||||||
|     # Your stuff: custom urls includes go here |     # Your stuff: custom urls includes go here | ||||||
| ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) | ||||||
| 
 | 
 | ||||||
|  | @ -24,37 +17,23 @@ urlpatterns = [ | ||||||
| urlpatterns += [ | urlpatterns += [ | ||||||
|     # API base url |     # API base url | ||||||
|     path("api/", include("config.api_router")), |     path("api/", include("config.api_router")), | ||||||
|     # DRF auth token |     path("api_schema/", SpectacularAPIView.as_view(), name="api-schema"), | ||||||
|     path("auth-token/", obtain_auth_token), |     path("api_rschema/", SpectacularAPIView.as_view(), name="api-redoc-schema"), | ||||||
|     path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"), |  | ||||||
|     path( |     path( | ||||||
|         "api/docs/", |         "api/docs/", | ||||||
|         SpectacularSwaggerView.as_view(url_name="api-schema"), |         SpectacularSwaggerView.as_view(url_name="api-schema"), | ||||||
|         name="api-docs", |         name="home", | ||||||
|  |     ), | ||||||
|  |     path( | ||||||
|  |         "api/redoc/", | ||||||
|  |         SpectacularRedocView.as_view(url_name="api-redoc-schema"), | ||||||
|  |         name="home", | ||||||
|     ), |     ), | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| if settings.DEBUG: | if settings.DEBUG: | ||||||
|     # This allows the error pages to be debugged during development, just visit |     # This allows the error pages to be debugged during development, just visit | ||||||
|     # these url in browser to see how these error pages look like. |     # these url in browser to see how these error pages look like. | ||||||
|     urlpatterns += [ |  | ||||||
|         path( |  | ||||||
|             "400/", |  | ||||||
|             default_views.bad_request, |  | ||||||
|             kwargs={"exception": Exception("Bad Request!")}, |  | ||||||
|         ), |  | ||||||
|         path( |  | ||||||
|             "403/", |  | ||||||
|             default_views.permission_denied, |  | ||||||
|             kwargs={"exception": Exception("Permission Denied")}, |  | ||||||
|         ), |  | ||||||
|         path( |  | ||||||
|             "404/", |  | ||||||
|             default_views.page_not_found, |  | ||||||
|             kwargs={"exception": Exception("Page not Found")}, |  | ||||||
|         ), |  | ||||||
|         path("500/", default_views.server_error), |  | ||||||
|     ] |  | ||||||
|     if "debug_toolbar" in settings.INSTALLED_APPS: |     if "debug_toolbar" in settings.INSTALLED_APPS: | ||||||
|         import debug_toolbar |         import debug_toolbar | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								image_markuper/dicom/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								image_markuper/dicom/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								image_markuper/dicom/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								image_markuper/dicom/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								image_markuper/dicom/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								image_markuper/dicom/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										24
									
								
								image_markuper/dicom/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								image_markuper/dicom/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | from dicom.models import Dicom | ||||||
|  | from rest_framework import serializers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ListDicomSerializer(serializers.ModelSerializer): | ||||||
|  |     url = serializers.HyperlinkedIdentityField( | ||||||
|  |         view_name="get_update_delete_dicom", lookup_field="slug" | ||||||
|  |     ) | ||||||
|  |     file = serializers.FileField() | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = Dicom | ||||||
|  |         fields = ["file", "uploaded", "url"] | ||||||
|  | 
 | ||||||
|  |     def create(self, validated_data): | ||||||
|  |         return Dicom.objects.create(**validated_data, user=self.context["request"].user) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DicomSerializer(serializers.ModelSerializer): | ||||||
|  |     file = serializers.FileField() | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = Dicom | ||||||
|  |         fields = ["file", "uploaded"] | ||||||
							
								
								
									
										36
									
								
								image_markuper/dicom/api/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								image_markuper/dicom/api/views.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | from drf_spectacular.utils import extend_schema | ||||||
|  | from rest_framework import generics | ||||||
|  | from rest_framework.parsers import FormParser, MultiPartParser | ||||||
|  | 
 | ||||||
|  | from ..models import Dicom | ||||||
|  | from .serializers import DicomSerializer, ListDicomSerializer | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ListCreateDicomApi(generics.ListCreateAPIView): | ||||||
|  |     serializer_class = ListDicomSerializer | ||||||
|  |     parser_classes = [MultiPartParser, FormParser] | ||||||
|  | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         return Dicom.objects.filter(user=self.request.user) | ||||||
|  | 
 | ||||||
|  |     @extend_schema( | ||||||
|  |         operation_id="upload_file", | ||||||
|  |         request={ | ||||||
|  |             "multipart/form-data": { | ||||||
|  |                 "type": "object", | ||||||
|  |                 "properties": {"file": {"type": "string", "format": "binary"}}, | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |     ) | ||||||
|  |     def post(self, request, *args, **kwargs): | ||||||
|  |         return self.create(request, *args, **kwargs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RetrieveUpdateDeleteDicomApi(generics.RetrieveUpdateDestroyAPIView): | ||||||
|  |     def get_queryset(self): | ||||||
|  |         return Dicom.objects.filter(user=self.request.user) | ||||||
|  | 
 | ||||||
|  |     serializer_class = DicomSerializer | ||||||
|  |     parser_classes = [MultiPartParser, FormParser] | ||||||
|  | 
 | ||||||
|  |     lookup_field = "slug" | ||||||
							
								
								
									
										12
									
								
								image_markuper/dicom/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								image_markuper/dicom/apps.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | from django.apps import AppConfig | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DicomConfig(AppConfig): | ||||||
|  |     default_auto_field = "django.db.models.BigAutoField" | ||||||
|  |     name = "dicom" | ||||||
|  | 
 | ||||||
|  |     def ready(self): | ||||||
|  |         try: | ||||||
|  |             import image_markuper.dicom.signals  # noqa F401 | ||||||
|  |         except ImportError: | ||||||
|  |             pass | ||||||
							
								
								
									
										28
									
								
								image_markuper/dicom/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								image_markuper/dicom/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | # 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 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     initial = True | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     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)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								image_markuper/dicom/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								image_markuper/dicom/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										16
									
								
								image_markuper/dicom/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								image_markuper/dicom/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | from django.contrib.auth import get_user_model | ||||||
|  | from django.db import models | ||||||
|  | from utils.files import media_upload_path | ||||||
|  | 
 | ||||||
|  | User = get_user_model() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Dicom(models.Model): | ||||||
|  |     user = models.ForeignKey(User, related_name="files", on_delete=models.CASCADE) | ||||||
|  |     slug = models.SlugField() | ||||||
|  | 
 | ||||||
|  |     file = models.FileField(upload_to=media_upload_path) | ||||||
|  |     uploaded = models.DateTimeField(auto_now_add=True) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.file.name | ||||||
							
								
								
									
										12
									
								
								image_markuper/dicom/signals.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								image_markuper/dicom/signals.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | from dicom.models import Dicom | ||||||
|  | from django.db.models.signals import pre_save | ||||||
|  | from django.dispatch import receiver | ||||||
|  | from utils.generators import generate_charset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @receiver(pre_save, sender=Dicom) | ||||||
|  | def create_dicom(sender, instance: Dicom, **kwargs): | ||||||
|  |     slug = generate_charset(5) | ||||||
|  |     while Dicom.objects.filter(slug=slug): | ||||||
|  |         slug = generate_charset(5) | ||||||
|  |     instance.slug = slug | ||||||
|  | @ -15,7 +15,6 @@ class UserAdmin(auth_admin.UserAdmin): | ||||||
|     add_form = UserAdminCreationForm |     add_form = UserAdminCreationForm | ||||||
|     fieldsets = ( |     fieldsets = ( | ||||||
|         (None, {"fields": ("username", "password")}), |         (None, {"fields": ("username", "password")}), | ||||||
|         (_("Personal info"), {"fields": ("name", "email")}), |  | ||||||
|         ( |         ( | ||||||
|             _("Permissions"), |             _("Permissions"), | ||||||
|             { |             { | ||||||
|  | @ -30,5 +29,5 @@ class UserAdmin(auth_admin.UserAdmin): | ||||||
|         ), |         ), | ||||||
|         (_("Important dates"), {"fields": ("last_login", "date_joined")}), |         (_("Important dates"), {"fields": ("last_login", "date_joined")}), | ||||||
|     ) |     ) | ||||||
|     list_display = ["username", "name", "is_superuser"] |     list_display = ["username", "is_superuser"] | ||||||
|     search_fields = ["name"] |     search_fields = ["username"] | ||||||
|  |  | ||||||
|  | @ -7,8 +7,28 @@ User = get_user_model() | ||||||
| class UserSerializer(serializers.ModelSerializer): | class UserSerializer(serializers.ModelSerializer): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = User |         model = User | ||||||
|         fields = ["username", "name", "url"] |         fields = ["username", "url"] | ||||||
| 
 | 
 | ||||||
|         extra_kwargs = { |         extra_kwargs = { | ||||||
|             "url": {"view_name": "api:user-detail", "lookup_field": "username"} |             "url": {"view_name": "api:user-detail", "lookup_field": "username"} | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RegisterSerializer(serializers.ModelSerializer): | ||||||
|  |     class Meta: | ||||||
|  |         model = User | ||||||
|  |         fields = ( | ||||||
|  |             "username", | ||||||
|  |             "password", | ||||||
|  |         ) | ||||||
|  |         extra_kwargs = {"password": {"write_only": True}} | ||||||
|  | 
 | ||||||
|  |     def create(self, validated_data): | ||||||
|  |         user = User.objects.create( | ||||||
|  |             username=validated_data["username"], | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         user.set_password(validated_data["password"]) | ||||||
|  |         user.save() | ||||||
|  | 
 | ||||||
|  |         return user | ||||||
|  |  | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from rest_framework import status | from rest_framework import generics, status | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin | from rest_framework.mixins import ListModelMixin, RetrieveModelMixin, UpdateModelMixin | ||||||
|  | from rest_framework.permissions import AllowAny | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.viewsets import GenericViewSet | from rest_framework.viewsets import GenericViewSet | ||||||
| 
 | 
 | ||||||
| from .serializers import UserSerializer | from .serializers import RegisterSerializer, UserSerializer | ||||||
| 
 | 
 | ||||||
| User = get_user_model() | User = get_user_model() | ||||||
| 
 | 
 | ||||||
|  | @ -16,10 +17,15 @@ class UserViewSet(RetrieveModelMixin, ListModelMixin, UpdateModelMixin, GenericV | ||||||
|     lookup_field = "username" |     lookup_field = "username" | ||||||
| 
 | 
 | ||||||
|     def get_queryset(self, *args, **kwargs): |     def get_queryset(self, *args, **kwargs): | ||||||
|         assert isinstance(self.request.user.id, int) |  | ||||||
|         return self.queryset.filter(id=self.request.user.id) |         return self.queryset.filter(id=self.request.user.id) | ||||||
| 
 | 
 | ||||||
|     @action(detail=False) |     @action(detail=False) | ||||||
|     def me(self, request): |     def me(self, request): | ||||||
|         serializer = UserSerializer(request.user, context={"request": request}) |         serializer = UserSerializer(request.user, context={"request": request}) | ||||||
|         return Response(status=status.HTTP_200_OK, data=serializer.data) |         return Response(status=status.HTTP_200_OK, data=serializer.data) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RegisterView(generics.CreateAPIView): | ||||||
|  |     queryset = User.objects.all() | ||||||
|  |     permission_classes = (AllowAny,) | ||||||
|  |     serializer_class = RegisterSerializer | ||||||
|  |  | ||||||
|  | @ -1,7 +1,5 @@ | ||||||
| from django.contrib.auth.models import AbstractUser | from django.contrib.auth.models import AbstractUser | ||||||
| from django.db.models import CharField |  | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext_lazy as _ |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class User(AbstractUser): | class User(AbstractUser): | ||||||
|  | @ -11,10 +9,9 @@ class User(AbstractUser): | ||||||
|     check forms.SignupForm and forms.SocialSignupForms accordingly. |     check forms.SignupForm and forms.SocialSignupForms accordingly. | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     #: First and last name do not cover name patterns around the globe |  | ||||||
|     name = CharField(_("Name of User"), blank=True, max_length=255) |  | ||||||
|     first_name = None  # type: ignore |     first_name = None  # type: ignore | ||||||
|     last_name = None  # type: ignore |     last_name = None  # type: ignore | ||||||
|  |     email = None  # type: ignore | ||||||
| 
 | 
 | ||||||
|     def get_absolute_url(self): |     def get_absolute_url(self): | ||||||
|         """Get url for user's detail view. |         """Get url for user's detail view. | ||||||
|  |  | ||||||
|  | @ -25,9 +25,6 @@ class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): | ||||||
|     success_message = _("Information successfully updated") |     success_message = _("Information successfully updated") | ||||||
| 
 | 
 | ||||||
|     def get_success_url(self): |     def get_success_url(self): | ||||||
|         assert ( |  | ||||||
|             self.request.user.is_authenticated |  | ||||||
|         )  # for mypy to know that the user is authenticated |  | ||||||
|         return self.request.user.get_absolute_url() |         return self.request.user.get_absolute_url() | ||||||
| 
 | 
 | ||||||
|     def get_object(self): |     def get_object(self): | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								image_markuper/utils/files.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								image_markuper/utils/files.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | from utils.generators import generate_charset | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def media_upload_path(instance, filename): | ||||||
|  |     return os.path.join(f"uploads/dicom/{generate_charset(7)}/", filename) | ||||||
							
								
								
									
										7
									
								
								image_markuper/utils/generators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								image_markuper/utils/generators.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | import random | ||||||
|  | import string | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def generate_charset(length: int) -> str: | ||||||
|  |     """Generate a random string of characters of a given length.""" | ||||||
|  |     return "".join(random.choice(string.ascii_letters) for _ in range(length)) | ||||||
|  | @ -1,67 +0,0 @@ | ||||||
| import os |  | ||||||
| from collections.abc import Sequence |  | ||||||
| from pathlib import Path |  | ||||||
| 
 |  | ||||||
| import pytest |  | ||||||
| 
 |  | ||||||
| ROOT_DIR_PATH = Path(__file__).parent.resolve() |  | ||||||
| PRODUCTION_DOTENVS_DIR_PATH = ROOT_DIR_PATH / ".envs" / ".production" |  | ||||||
| PRODUCTION_DOTENV_FILE_PATHS = [ |  | ||||||
|     PRODUCTION_DOTENVS_DIR_PATH / ".django", |  | ||||||
|     PRODUCTION_DOTENVS_DIR_PATH / ".postgres", |  | ||||||
| ] |  | ||||||
| DOTENV_FILE_PATH = ROOT_DIR_PATH / ".env" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def merge( |  | ||||||
|     output_file_path: str, merged_file_paths: Sequence[str], append_linesep: bool = True |  | ||||||
| ) -> None: |  | ||||||
|     with open(output_file_path, "w") as output_file: |  | ||||||
|         for merged_file_path in merged_file_paths: |  | ||||||
|             with open(merged_file_path) as merged_file: |  | ||||||
|                 merged_file_content = merged_file.read() |  | ||||||
|                 output_file.write(merged_file_content) |  | ||||||
|                 if append_linesep: |  | ||||||
|                     output_file.write(os.linesep) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def main(): |  | ||||||
|     merge(DOTENV_FILE_PATH, PRODUCTION_DOTENV_FILE_PATHS) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @pytest.mark.parametrize("merged_file_count", range(3)) |  | ||||||
| @pytest.mark.parametrize("append_linesep", [True, False]) |  | ||||||
| def test_merge(tmpdir_factory, merged_file_count: int, append_linesep: bool): |  | ||||||
|     tmp_dir_path = Path(str(tmpdir_factory.getbasetemp())) |  | ||||||
| 
 |  | ||||||
|     output_file_path = tmp_dir_path / ".env" |  | ||||||
| 
 |  | ||||||
|     expected_output_file_content = "" |  | ||||||
|     merged_file_paths = [] |  | ||||||
|     for i in range(merged_file_count): |  | ||||||
|         merged_file_ord = i + 1 |  | ||||||
| 
 |  | ||||||
|         merged_filename = f".service{merged_file_ord}" |  | ||||||
|         merged_file_path = tmp_dir_path / merged_filename |  | ||||||
| 
 |  | ||||||
|         merged_file_content = merged_filename * merged_file_ord |  | ||||||
| 
 |  | ||||||
|         with open(merged_file_path, "w+") as file: |  | ||||||
|             file.write(merged_file_content) |  | ||||||
| 
 |  | ||||||
|         expected_output_file_content += merged_file_content |  | ||||||
|         if append_linesep: |  | ||||||
|             expected_output_file_content += os.linesep |  | ||||||
| 
 |  | ||||||
|         merged_file_paths.append(merged_file_path) |  | ||||||
| 
 |  | ||||||
|     merge(output_file_path, merged_file_paths, append_linesep) |  | ||||||
| 
 |  | ||||||
|     with open(output_file_path) as output_file: |  | ||||||
|         actual_output_file_content = output_file.read() |  | ||||||
| 
 |  | ||||||
|     assert actual_output_file_content == expected_output_file_content |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     main() |  | ||||||
|  | @ -21,5 +21,6 @@ django-redis==5.2.0  # https://github.com/jazzband/django-redis | ||||||
| # Django REST Framework | # Django REST Framework | ||||||
| djangorestframework==3.14.0  # https://github.com/encode/django-rest-framework | djangorestframework==3.14.0  # https://github.com/encode/django-rest-framework | ||||||
| django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers | django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers | ||||||
|  | djangorestframework-simplejwt==5.2.2 | ||||||
| # DRF-spectacular for api documentation | # DRF-spectacular for api documentation | ||||||
| drf-spectacular==0.24.2  # https://github.com/tfranzel/drf-spectacular | drf-spectacular==0.24.2  # https://github.com/tfranzel/drf-spectacular | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user