From 15835b4c2d335d374b259db731ae8568b05b675a Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Sat, 22 Nov 2025 18:09:24 +0500 Subject: [PATCH] bugungi kun --- core/apps/shared/admin/__init__.py | 5 +- core/apps/shared/admin/district.py | 2 +- core/apps/shared/admin/doctor.py | 11 ++ core/apps/shared/admin/pharmacy.py | 10 ++ core/apps/shared/admin/place.py | 2 +- core/apps/shared/admin/plan.py | 10 ++ core/apps/shared/admin/region.py | 2 +- .../migrations/0005_doctor_pharmacy_plan.py | 75 ++++++++++++ core/apps/shared/models/__init__.py | 5 +- core/apps/shared/models/doctor.py | 29 +++++ core/apps/shared/models/pharmacy.py | 26 ++++ core/apps/shared/models/plan.py | 17 +++ core/apps/shared/serializers/doctor.py | 98 +++++++++++++++ core/apps/shared/urls.py | 10 ++ core/apps/shared/views/district.py | 2 +- core/apps/shared/views/doctor.py | 112 ++++++++++++++++++ 16 files changed, 410 insertions(+), 6 deletions(-) create mode 100644 core/apps/shared/admin/doctor.py create mode 100644 core/apps/shared/admin/pharmacy.py create mode 100644 core/apps/shared/admin/plan.py create mode 100644 core/apps/shared/migrations/0005_doctor_pharmacy_plan.py create mode 100644 core/apps/shared/models/doctor.py create mode 100644 core/apps/shared/models/pharmacy.py create mode 100644 core/apps/shared/models/plan.py create mode 100644 core/apps/shared/serializers/doctor.py create mode 100644 core/apps/shared/views/doctor.py diff --git a/core/apps/shared/admin/__init__.py b/core/apps/shared/admin/__init__.py index 2ff5e06..c6d16de 100644 --- a/core/apps/shared/admin/__init__.py +++ b/core/apps/shared/admin/__init__.py @@ -1,3 +1,6 @@ from .region import * from .district import * -from .place import * \ No newline at end of file +from .place import * +from .pharmacy import * +from .doctor import * +from .plan import * \ No newline at end of file diff --git a/core/apps/shared/admin/district.py b/core/apps/shared/admin/district.py index 7ab1ee7..5782fbc 100644 --- a/core/apps/shared/admin/district.py +++ b/core/apps/shared/admin/district.py @@ -6,7 +6,7 @@ from core.apps.shared.models import District @admin.register(District) class DistrictAdmin(admin.ModelAdmin): - list_display = ['name', 'user', 'created_at'] + list_display = ['id','name', 'user', 'created_at'] search_fields = ['name'] \ No newline at end of file diff --git a/core/apps/shared/admin/doctor.py b/core/apps/shared/admin/doctor.py new file mode 100644 index 0000000..4c99ca2 --- /dev/null +++ b/core/apps/shared/admin/doctor.py @@ -0,0 +1,11 @@ +from django.contrib import admin + +# shared +from core.apps.shared.models import Doctor + + +@admin.register(Doctor) +class DoctorAdmin(admin.ModelAdmin): + list_display = ['id', 'first_name', 'last_name', 'phone_number'] + search_fields = list_display + diff --git a/core/apps/shared/admin/pharmacy.py b/core/apps/shared/admin/pharmacy.py new file mode 100644 index 0000000..9faf912 --- /dev/null +++ b/core/apps/shared/admin/pharmacy.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +# shared +from core.apps.shared.models import Pharmacy + + +@admin.register(Pharmacy) +class PharmacyAdmin(admin.ModelAdmin): + list_display = ['id', 'name', 'inn', 'owner_phone', 'responsible_phone'] + search_fields = list_display \ No newline at end of file diff --git a/core/apps/shared/admin/place.py b/core/apps/shared/admin/place.py index dd101cf..c560772 100644 --- a/core/apps/shared/admin/place.py +++ b/core/apps/shared/admin/place.py @@ -6,5 +6,5 @@ from core.apps.shared.models import Place @admin.register(Place) class PlaceAdmin(admin.ModelAdmin): - list_display = ['name', 'user', 'district'] + list_display = ['id', 'name', 'user', 'district'] search_fields = ['name'] \ No newline at end of file diff --git a/core/apps/shared/admin/plan.py b/core/apps/shared/admin/plan.py new file mode 100644 index 0000000..5d6f129 --- /dev/null +++ b/core/apps/shared/admin/plan.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +# shared +from core.apps.shared.models import Plan + + +@admin.register(Plan) +class PlanAdmin(admin.ModelAdmin): + list_display = ['id', 'title', 'date'] + search_fields = list_display \ No newline at end of file diff --git a/core/apps/shared/admin/region.py b/core/apps/shared/admin/region.py index edb1cb6..04ab0c7 100644 --- a/core/apps/shared/admin/region.py +++ b/core/apps/shared/admin/region.py @@ -6,5 +6,5 @@ from core.apps.shared.models import Region @admin.register(Region) class RegionAdmin(admin.ModelAdmin): - list_display = ['name', 'users_count'] + list_display = ['id', 'name', 'users_count'] search_fields = ['name'] \ No newline at end of file diff --git a/core/apps/shared/migrations/0005_doctor_pharmacy_plan.py b/core/apps/shared/migrations/0005_doctor_pharmacy_plan.py new file mode 100644 index 0000000..32049b8 --- /dev/null +++ b/core/apps/shared/migrations/0005_doctor_pharmacy_plan.py @@ -0,0 +1,75 @@ +# Generated by Django 5.2 on 2025-11-22 12:27 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shared', '0004_place'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Doctor', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('first_name', models.CharField(max_length=200)), + ('last_name', models.CharField(max_length=200)), + ('phone_number', models.CharField(max_length=15)), + ('work_place', models.CharField(max_length=200)), + ('sphere', models.CharField(max_length=200)), + ('description', models.TextField()), + ('longitude', models.FloatField(default=0.0)), + ('latitude', models.FloatField(default=0.0)), + ('extra_location', models.JSONField()), + ('district', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doctors', to='shared.district')), + ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doctors', to='shared.place')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='doctors', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Pharmacy', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ('inn', models.CharField(max_length=200)), + ('owner_phone', models.CharField(max_length=15)), + ('responsible_phone', models.CharField(max_length=15)), + ('longitude', models.FloatField(default=0.0)), + ('latitude', models.FloatField(default=0.0)), + ('extra_location', models.JSONField()), + ('district', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pharmacies', to='shared.district')), + ('place', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pharmacies', to='shared.place')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pharmacies', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Plan', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('title', models.CharField(max_length=200)), + ('description', models.TextField()), + ('date', models.DateField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plans', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/core/apps/shared/models/__init__.py b/core/apps/shared/models/__init__.py index 4873267..9e83596 100644 --- a/core/apps/shared/models/__init__.py +++ b/core/apps/shared/models/__init__.py @@ -1,4 +1,7 @@ from .base import * from .region import * from .district import * -from .place import * \ No newline at end of file +from .place import * +from .doctor import * +from .pharmacy import * +from .plan import * \ No newline at end of file diff --git a/core/apps/shared/models/doctor.py b/core/apps/shared/models/doctor.py new file mode 100644 index 0000000..4b7063d --- /dev/null +++ b/core/apps/shared/models/doctor.py @@ -0,0 +1,29 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel, District, Place +# accounts +from core.apps.accounts.models import User + + +class Doctor(BaseModel): + # information + first_name = models.CharField(max_length=200) + last_name = models.CharField(max_length=200) + phone_number = models.CharField(max_length=15) + work_place = models.CharField(max_length=200) + sphere = models.CharField(max_length=200) + description = models.TextField() + # relations + district = models.ForeignKey(District, on_delete=models.CASCADE, related_name='doctors') + place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name='doctors') + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='doctors') + # location + longitude = models.FloatField(default=0.00) + latitude = models.FloatField(default=0.00) + extra_location = models.JSONField() + + def __str__(self): + return f"{self.first_name} {self.last_name} - {self.work_place}" + + \ No newline at end of file diff --git a/core/apps/shared/models/pharmacy.py b/core/apps/shared/models/pharmacy.py new file mode 100644 index 0000000..62fff52 --- /dev/null +++ b/core/apps/shared/models/pharmacy.py @@ -0,0 +1,26 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel, District, Place +# accounts +from core.apps.accounts.models import User + + +class Pharmacy(BaseModel): + # informations + name = models.CharField(max_length=200) + inn = models.CharField(max_length=200) + owner_phone = models.CharField(max_length=15) + responsible_phone = models.CharField(max_length=15) + # relations + district = models.ForeignKey(District, on_delete=models.CASCADE, related_name='pharmacies') + place = models.ForeignKey(Place, on_delete=models.CASCADE, related_name='pharmacies') + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='pharmacies') + # location + longitude = models.FloatField(default=0.00) + latitude = models.FloatField(default=0.00) + extra_location = models.JSONField() + + def __str__(self): + return f"{self.name} - {self.inn}" + \ No newline at end of file diff --git a/core/apps/shared/models/plan.py b/core/apps/shared/models/plan.py new file mode 100644 index 0000000..bfe7eed --- /dev/null +++ b/core/apps/shared/models/plan.py @@ -0,0 +1,17 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel +# accounts +from core.apps.accounts.models import User + + +class Plan(BaseModel): + title = models.CharField(max_length=200) + description = models.TextField() + date = models.DateField() + # relations + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='plans') + + def __str__(self): + return self.title \ No newline at end of file diff --git a/core/apps/shared/serializers/doctor.py b/core/apps/shared/serializers/doctor.py new file mode 100644 index 0000000..ef655bd --- /dev/null +++ b/core/apps/shared/serializers/doctor.py @@ -0,0 +1,98 @@ +# django +from django.db import transaction + +# rest framework +from rest_framework import serializers + +# shared +from core.apps.shared.models import Doctor, District, Place +# accounts +from core.apps.accounts.models import User + + +class DoctorListSerializer(serializers.ModelSerializer): + class Meta: + model = Doctor + fields = [ + 'id', + 'first_name', + 'last_name', + 'phone_number', + 'work_place', + 'description', + 'district', + 'place', + 'longitude', + 'latitude', + 'extra_location', + 'created_at', + ] + + def get_district(self, obj): + return { + 'id': obj.district.id, + 'name': obj.district.name, + } + + def get_place(self, obj): + return { + 'id': obj.place.id, + 'name': obj.place.name, + } + + +class DoctorCreateUpdateSerializer(serializers.Serializer): + first_name = serializers.CharField(required=False) + last_name = serializers.CharField(required=False) + phone_number = serializers.CharField(required=False) + work_place = serializers.CharField(required=False) + description = serializers.CharField(required=False) + district = serializers.IntegerField(required=False) + place = serializers.IntegerField(required=False) + longitude = serializers.FloatField(required=False) + latitude = serializers.FloatField(required=False) + extra_location = serializers.JSONField(required=False) + + def validate(self, data): + if data.get('district'): + district = District.objects.filter(id=data['district']).first() + if not district: + raise serializers.ValidationError({"district": "District topilamadi"}) + data['district_obj'] = district + if data.get('place'): + place = Place.objects.filter(id=data['place']).first() + if not place: + raise serializers.ValidationError({"place": "Place topilamadi"}) + data['place_obj'] = place + return data + + def create(self, validated_data): + with transaction.atomic(): + return Doctor.objects.create( + first_name=validated_data.get('first_name'), + last_name=validated_data.get('last_name'), + phone_number=validated_data.get('phone_number'), + work_place=validated_data.get('work_place'), + description=validated_data.get('description'), + district=validated_data.get('district_obj'), + place=validated_data.get('place_obj'), + longitude=validated_data.get('longitude'), + latitude=validated_data.get('latitude'), + extra_location=validated_data.get('extra_location'), + user=self.context.get('user'), + ) + + def update(self, instance, validated_data): + with transaction.atomic(): + instance.first_name = validated_data.get('first_name', instance.first_name) + instance.last_name = validated_data.get('last_name', instance.last_name) + instance.phone_number = validated_data.get('phone_number', instance.phone_number) + instance.work_place = validated_data.get('work_place', instance.work_place) + instance.description = validated_data.get('description', instance.description) + instance.district = validated_data.get('district_obj', instance.district) + instance.place = validated_data.get('place_obj', instance.place) + instance.longitude = validated_data.get('longitude', instance.longitude) + instance.latitude = validated_data.get('latitude', instance.latitude) + instance.extra_location = validated_data.get('extra_location', instance.extra_location) + instance.save() + return instance \ No newline at end of file diff --git a/core/apps/shared/urls.py b/core/apps/shared/urls.py index fa15bdc..ab30d05 100644 --- a/core/apps/shared/urls.py +++ b/core/apps/shared/urls.py @@ -6,6 +6,8 @@ from core.apps.shared.views import region as region_view from core.apps.shared.views import district as dis_view # shared place view from core.apps.shared.views import place as pl_view +# shared doctor view +from core.apps.shared.views import doctor as dc_view urlpatterns = [ @@ -31,4 +33,12 @@ urlpatterns = [ path('/', pl_view.PlaceDeleteUpdateApiView.as_view(), name='place-update-delete-api'), ] )), + # doctor + path('doctor/', include( + [ + path('list/', dc_view.DoctorListApiView.as_view(), name='doctor-list-api'), + path('create/', dc_view.DoctorCreateApiView.as_view(), name='doctor-create-api'), + path('/', dc_view.DoctorDeleteUpdateApiView.as_view(), name='doctor-update-delete-api'), + ] + )), ] \ No newline at end of file diff --git a/core/apps/shared/views/district.py b/core/apps/shared/views/district.py index a68f2f0..cddcb4f 100644 --- a/core/apps/shared/views/district.py +++ b/core/apps/shared/views/district.py @@ -55,7 +55,7 @@ class DistrictCreateApiView(generics.CreateAPIView, ResponseMixin): name = serializer.validated_data.get('name') obj = District.objects.create(name=name, user=request.user) return self.success_response( - data=district_serializers.DistrictSerializer(obj), + data=district_serializers.DistrictSerializer(obj).data, message='malumot qoshildi', status_code=201 ) diff --git a/core/apps/shared/views/doctor.py b/core/apps/shared/views/doctor.py new file mode 100644 index 0000000..f98c9e4 --- /dev/null +++ b/core/apps/shared/views/doctor.py @@ -0,0 +1,112 @@ +# django +from django.shortcuts import get_object_or_404 + +# rest framework +from rest_framework import generics, permissions + +# drf yasg +from drf_yasg.utils import swagger_auto_schema + +# shared +from core.apps.shared.models import Doctor +from core.apps.shared.serializers.doctor import DoctorCreateUpdateSerializer, DoctorListSerializer +from core.apps.shared.serializers.base import SuccessResponseSerializer, BaseResponseSerializer +from core.apps.shared.utils.response_mixin import ResponseMixin + + + +class DoctorListApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = DoctorListSerializer + queryset = Doctor.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 200: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def get(self, request): + try: + query = self.queryset.filter(user=request.user) + serializer = self.serializer_class(query, many=True) + return self.success_response(data=serializer.data, message='malumotlar fetch qilindi') + except Exception as e: + return self.error_response( + data=str(e), message='xatolik' + ) + + +class DoctorCreateApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = DoctorCreateUpdateSerializer + queryset = Doctor.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 201: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def post(self, request): + try: + serializer = self.serializer_class(data=request.data, context={'user': request.user}) + if serializer.is_valid(): + obj = serializer.save() + return self.success_response( + data=DoctorListSerializer(obj).data, + message="malumot qoshildi" + ) + return self.failure_response( + data=serializer.data, + message="malumot qoshilmadi" + ) + except Exception as e: + return self.error_response(data=str(e), message='xatolar') + + + +class DoctorDeleteUpdateApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = DoctorCreateUpdateSerializer + queryset = Doctor.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 200: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def patch(self, request, id): + try: + obj = get_object_or_404(Doctor, id=id, user=request.user) + serializer = self.serializer_class(data=request.data, instance=obj) + if serializer.is_valid(): + serializer.save() + return self.success_response( + message='Malumot tahrilandi' + ) + return self.failure_response( + data=serializer.errors, + message='Malumot tahrirlanmadi' + ) + except Exception as e: + return self.error_response(data=str(e), message='xatolik') + + @swagger_auto_schema( + responses={ + 204: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def delete(self, request, id): + try: + obj = get_object_or_404(Doctor, id=id, user=request.user) + obj.delete() + return self.success_response(message='Malumot ochirildi', status_code=204) + except Exception as e: + return self.error_response(data=str(e), message='xatolik')