From 31ca87a411e8450bb66e8083b4a6b652969752a8 Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 00:28:43 +0000 Subject: [PATCH 1/6] add a CI job to publish Docker artifact so the registry-client can run the backend in order to run the UI tests --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99989ee..9bfad31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,40 @@ jobs: heroku_email: ${{ secrets.HEROKU_EMAIL }} remote_branch: master + + publish-docker-image: + name: Publish Docker image to ghcr.io + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' + needs: + - unittest + - precommit + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./compose/production/django/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + bump: if: "github.event_name == 'push' && github.ref == 'refs/heads/main'" runs-on: ubuntu-latest From 38ebc71c5fc2e13d89ce1c9f5e8eed26e2d0f752 Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 00:54:11 +0000 Subject: [PATCH 2/6] format code and exclude migrations from pre-commit --- .pre-commit-config.yaml | 7 ++- Makefile | 14 ++--- tmh_registry/registry/admin.py | 16 ++++-- tmh_registry/registry/api/serializers.py | 65 ++++++++++++++++++------ tmh_registry/registry/api/viewsets.py | 58 ++++++++++++--------- tmh_registry/registry/models.py | 45 +++++++++------- tmh_registry/registry/urls.py | 28 +++++++--- 7 files changed, 155 insertions(+), 78 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af19496..8ff61c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: repo: local - hooks: - - exclude: (/settings/) + - exclude: (/settings/|/migrations/) id: black args: - --config=.pre-commit/pyproject.toml @@ -52,12 +52,13 @@ repos: - --config=.pre-commit/setup.cfg id: flake8 language_version: python3 + exclude: (/settings/|/migrations/) repo: https://github.com/PyCQA/flake8 rev: 7.0.0 - hooks: - entry: pylint --rcfile=.pre-commit/.pylintrc - exclude: (/settings/) + exclude: (/settings/|/migrations/) files: \.py$ id: system language: system @@ -66,6 +67,7 @@ repos: - hooks: - id: commitizen + exclude: (/settings/|/migrations/) stages: - commit-msg args: @@ -76,5 +78,6 @@ repos: - hooks: - id: bandit args: [ --verbose, -ll, . ] + exclude: (/settings/|/migrations/) repo: https://github.com/PyCQA/bandit rev: 1.7.7 diff --git a/Makefile b/Makefile index 39b8c83..e5d32aa 100644 --- a/Makefile +++ b/Makefile @@ -19,24 +19,24 @@ pre-commit: pre-commit run --all-files build: - docker-compose -f ${COMPOSE_ENV}.yml build + docker compose -f ${COMPOSE_ENV}.yml build run: build - docker-compose -f ${COMPOSE_ENV}.yml up -d + docker compose -f ${COMPOSE_ENV}.yml up -d migrate: - docker-compose -f ${COMPOSE_ENV}.yml exec -T django bash -c "python manage.py makemigrations && python manage.py migrate" + docker compose -f ${COMPOSE_ENV}.yml exec -T django bash -c "python manage.py makemigrations && python manage.py migrate" test: - docker-compose -f ${COMPOSE_ENV}.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest ${target} --disable-pytest-warnings; + docker compose -f ${COMPOSE_ENV}.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest ${target} --disable-pytest-warnings; test-one: - docker-compose -f ${COMPOSE_ENV}.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest ${file} -k ${test_name} --disable-pytest-warnings; + docker compose -f ${COMPOSE_ENV}.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest ${file} -k ${test_name} --disable-pytest-warnings; cover: - docker-compose -f ${COMPOSE_ENV}.yml exec -T django coverage report + docker compose -f ${COMPOSE_ENV}.yml exec -T django coverage report teardown: - docker-compose -f ${COMPOSE_ENV}.yml down -v + docker compose -f ${COMPOSE_ENV}.yml down -v recreate: teardown run diff --git a/tmh_registry/registry/admin.py b/tmh_registry/registry/admin.py index 3fbaf17..aa491df 100644 --- a/tmh_registry/registry/admin.py +++ b/tmh_registry/registry/admin.py @@ -5,17 +5,18 @@ from import_export.admin import ExportMixin from tmh_registry.registry.models import ( + Announcement, Discharge, Episode, FollowUp, Hospital, + HospitalRegionMapping, Patient, PatientHospitalMapping, PreferredHospital, - Zone, Region, - HospitalRegionMapping, - RegionZoneMapping, Announcement, + RegionZoneMapping, + Zone, ) @@ -43,33 +44,42 @@ class PatientAdmin(ExportMixin, admin.ModelAdmin): class PatientHospitalMappingAdmin(ExportMixin, admin.ModelAdmin): model = PatientHospitalMapping + @admin.register(PreferredHospital) class PreferredHospitalAdmin(ExportMixin, admin.ModelAdmin): model = PreferredHospital + @admin.register(Episode) class EpisodeAdmin(ExportMixin, admin.ModelAdmin): model = Episode + @admin.register(Zone) class ZoneAdmin(ExportMixin, admin.ModelAdmin): model = Zone + @admin.register(Region) class RegionAdmin(ExportMixin, admin.ModelAdmin): model = Region + @admin.register(HospitalRegionMapping) class HospitalRegionMappingAdmin(ExportMixin, admin.ModelAdmin): model = HospitalRegionMapping + @admin.register(RegionZoneMapping) class RegionZoneMappingAdmin(ExportMixin, admin.ModelAdmin): model = RegionZoneMapping + @admin.register(Announcement) class AnnouncementAdmin(ExportMixin, admin.ModelAdmin): model = Announcement + + """ To allow CSV export on User model """ diff --git a/tmh_registry/registry/api/serializers.py b/tmh_registry/registry/api/serializers.py index 08926c6..bf79b74 100644 --- a/tmh_registry/registry/api/serializers.py +++ b/tmh_registry/registry/api/serializers.py @@ -1,20 +1,30 @@ from drf_yasg.utils import swagger_serializer_method from rest_framework.exceptions import ValidationError -from rest_framework.fields import BooleanField, CharField, IntegerField, DateField +from rest_framework.fields import ( + BooleanField, + CharField, + DateField, + IntegerField, +) from rest_framework.relations import PrimaryKeyRelatedField -from rest_framework.serializers import ModelSerializer, SerializerMethodField, Serializer +from rest_framework.serializers import ( + ModelSerializer, + Serializer, + SerializerMethodField, +) from ...common.utils.functions import get_text_choice_value_from_label from ...users.api.serializers import MedicalPersonnelSerializer from ...users.models import MedicalPersonnel from ..models import ( + Announcement, Discharge, Episode, FollowUp, Hospital, Patient, PatientHospitalMapping, - PreferredHospital, Announcement, + PreferredHospital, ) @@ -240,6 +250,7 @@ def to_representation(self, instance): return data + class PreferredHospitalReadSerializer(ModelSerializer): hospital = SerializerMethodField() @@ -251,13 +262,16 @@ def get_hospital(self, obj): request = self.context.get("request") if request and request.user: try: - medical_personnel = MedicalPersonnel.objects.get(user=request.user) + medical_personnel = MedicalPersonnel.objects.get( + user=request.user + ) if obj.medical_personnel == medical_personnel: return {"id": obj.hospital.id} except MedicalPersonnel.DoesNotExist: pass return None + class PatientHospitalMappingWriteSerializer(ModelSerializer): patient_id = PrimaryKeyRelatedField(queryset=Patient.objects.all()) hospital_id = PrimaryKeyRelatedField(queryset=Hospital.objects.all()) @@ -646,21 +660,36 @@ def create(self, validated_data): return follow_up + class SurgeonEpisodeSummarySerializer(Serializer): episode_count = IntegerField() last_episode_date = DateField(allow_null=True) class OwnedEpisodeSerializer(ModelSerializer): - patient_name = CharField(source='patient_hospital_mapping.patient.full_name', read_only=True) - patient_id = CharField(source='patient_hospital_mapping.patient.id', read_only=True) - hospital_id = CharField(source='patient_hospital_mapping.hospital.id', read_only=True) + patient_name = CharField( + source="patient_hospital_mapping.patient.full_name", read_only=True + ) + patient_id = CharField( + source="patient_hospital_mapping.patient.id", read_only=True + ) + hospital_id = CharField( + source="patient_hospital_mapping.hospital.id", read_only=True + ) follow_up_dates = SerializerMethodField() discharge = SerializerMethodField() class Meta: model = Episode - fields = ['id', 'surgery_date', 'patient_name', 'discharge', 'follow_up_dates', 'patient_id', 'hospital_id'] + fields = [ + "id", + "surgery_date", + "patient_name", + "discharge", + "follow_up_dates", + "patient_id", + "hospital_id", + ] def get_follow_up_dates(self, obj): # Return only the dates of follow-up objects as a list of strings @@ -668,7 +697,7 @@ def get_follow_up_dates(self, obj): def get_discharge(self, obj): # Return the discharge date if available, otherwise None - discharge = getattr(obj, 'discharge', None) + discharge = getattr(obj, "discharge", None) return discharge.date if discharge else None @@ -680,10 +709,10 @@ class UnlinkedPatientSerializer(ModelSerializer): class Meta: model = Patient - fields = ['id', 'full_name', 'hospital_id', 'patient_hospital_id'] + fields = ["id", "full_name", "hospital_id", "patient_hospital_id"] def get_hospital_id(self, obj): - request = self.context.get('request') + request = self.context.get("request") if not request: return None @@ -696,7 +725,7 @@ def get_hospital_id(self, obj): return preferred_hospital.id def get_patient_hospital_id(self, obj): - request = self.context.get('request') + request = self.context.get("request") if not request: return None @@ -707,13 +736,19 @@ def get_patient_hospital_id(self, obj): return None mapping = PatientHospitalMapping.objects.filter( - patient=obj, - hospital=preferred_hospital + patient=obj, hospital=preferred_hospital ).first() return mapping.patient_hospital_id if mapping else None + class AnnouncementSerializer(ModelSerializer): class Meta: model = Announcement - fields = ["id", "announcement_text", "display_from", "display_until", "created_at"] + fields = [ + "id", + "announcement_text", + "display_from", + "display_until", + "created_at", + ] diff --git a/tmh_registry/registry/api/viewsets.py b/tmh_registry/registry/api/viewsets.py index 3ab9f3e..32b5bc5 100644 --- a/tmh_registry/registry/api/viewsets.py +++ b/tmh_registry/registry/api/viewsets.py @@ -1,7 +1,8 @@ from django.core.exceptions import ObjectDoesNotExist -from django.db.models import CharField, Q, Count, Max, OuterRef, Exists +from django.db.models import CharField, Count, Exists, Max, OuterRef, Q from django.db.models.functions import Cast from django.utils.decorators import method_decorator +from django.utils.timezone import now, timedelta from django_filters import ( # pylint: disable=E0401 CharFilter, NumberFilter, @@ -17,11 +18,11 @@ from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from rest_framework.views import APIView from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet -from django.utils.timezone import now, timedelta +from ...users.models import MedicalPersonnel from ..models import ( + Announcement, Discharge, Episode, FollowUp, @@ -29,9 +30,9 @@ Patient, PatientHospitalMapping, PreferredHospital, - Announcement, ) from .serializers import ( + AnnouncementSerializer, CreatePatientSerializer, DischargeReadSerializer, DischargeWriteSerializer, @@ -40,24 +41,26 @@ FollowUpReadSerializer, FollowUpWriteSerializer, HospitalSerializer, + OwnedEpisodeSerializer, PatientHospitalMappingReadSerializer, PatientHospitalMappingWriteSerializer, PreferredHospitalReadSerializer, ReadPatientSerializer, - SurgeonEpisodeSummarySerializer, OwnedEpisodeSerializer, UnlinkedPatientSerializer, AnnouncementSerializer, + SurgeonEpisodeSummarySerializer, + UnlinkedPatientSerializer, ) -from ...users.models import MedicalPersonnel class HospitalViewSet(viewsets.ReadOnlyModelViewSet): queryset = Hospital.objects.all() serializer_class = HospitalSerializer + class PreferredHospitalViewSet(viewsets.GenericViewSet): serializer_class = PreferredHospitalReadSerializer permission_classes = [IsAuthenticated] - @action(detail=False, methods=['get']) + @action(detail=False, methods=["get"]) def retrieve_for_current_user(self, request, *args, **kwargs): user = request.user try: @@ -65,12 +68,15 @@ def retrieve_for_current_user(self, request, *args, **kwargs): except MedicalPersonnel.DoesNotExist: return Response({}, status=200) try: - preferred_hospital = PreferredHospital.objects.get(medical_personnel=medical_personnel) + preferred_hospital = PreferredHospital.objects.get( + medical_personnel=medical_personnel + ) except PreferredHospital.DoesNotExist: return Response({}, status=200) serializer = self.get_serializer(preferred_hospital) return Response(serializer.data) + class SurgeonEpisodeSummaryViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = SurgeonEpisodeSummarySerializer permission_classes = [IsAuthenticated] @@ -88,13 +94,13 @@ def list(self, request, *args, **kwargs): queryset = self.get_queryset() stats = queryset.aggregate( - episode_count=Count('id'), - last_episode_date=Max('surgery_date') + episode_count=Count("id"), last_episode_date=Max("surgery_date") ) serializer = self.get_serializer(stats) return Response(serializer.data) + class OwnedEpisodesViewSet(viewsets.ReadOnlyModelViewSet): pagination_class = None serializer_class = OwnedEpisodeSerializer @@ -108,13 +114,13 @@ def get_queryset(self): return Episode.objects.none() episodes = ( - Episode.objects - .filter(surgeons=medical_personnel) + Episode.objects.filter(surgeons=medical_personnel) .select_related("patient_hospital_mapping__patient") - .prefetch_related( "discharge", "followup_set") + .prefetch_related("discharge", "followup_set") ) return episodes + class PatientFilterSet(FilterSet): hospital_id = NumberFilter( method="filter_hospital", @@ -307,7 +313,6 @@ def discharge(self, request, pk=None): ), }, ) - @action(detail=False, methods=["get"]) def stats(self, request): period = request.query_params.get("period") # e.g., "7d", "30d" @@ -403,25 +408,28 @@ def get_queryset(self): # Subquery: check if any Episode exists for a given patient in the preferred hospital has_episode_subquery = Episode.objects.filter( - patient_hospital_mapping__patient=OuterRef('pk'), - patient_hospital_mapping__hospital=preferred_hospital + patient_hospital_mapping__patient=OuterRef("pk"), + patient_hospital_mapping__hospital=preferred_hospital, ) - patients = Patient.objects.filter( - hospital_mappings__hospital=preferred_hospital - ).annotate( - has_episode=Exists(has_episode_subquery) - ).filter( - has_episode=False - ).distinct().prefetch_related('hospital_mappings') + patients = ( + Patient.objects.filter( + hospital_mappings__hospital=preferred_hospital + ) + .annotate(has_episode=Exists(has_episode_subquery)) + .filter(has_episode=False) + .distinct() + .prefetch_related("hospital_mappings") + ) return patients def get_serializer_context(self): context = super().get_serializer_context() - context['request'] = self.request + context["request"] = self.request return context + class AnnouncementViewSet(ReadOnlyModelViewSet): serializer_class = AnnouncementSerializer @@ -429,5 +437,5 @@ def get_queryset(self): current_time = now() return Announcement.objects.filter( Q(display_from__lte=current_time) | Q(display_from__isnull=True), - Q(display_until__gte=current_time) | Q(display_until__isnull=True) + Q(display_until__gte=current_time) | Q(display_until__isnull=True), ).order_by("-created_at") diff --git a/tmh_registry/registry/models.py b/tmh_registry/registry/models.py index d5e49f1..cf4c6c6 100644 --- a/tmh_registry/registry/models.py +++ b/tmh_registry/registry/models.py @@ -78,23 +78,25 @@ class Meta: def __str__(self): return f"Patient {self.patient.full_name} ({self.patient_hospital_id}) - Hospital {self.hospital.name}" + class PreferredHospital(Model): medical_personnel = OneToOneField( MedicalPersonnel, on_delete=CASCADE, related_name="preferred_hospital" ) hospital = ForeignKey( - Hospital, on_delete=CASCADE, related_name="preferred_by_medical_personnel" + Hospital, + on_delete=CASCADE, + related_name="preferred_by_medical_personnel", ) class Meta: - unique_together = ( - ("medical_personnel", "hospital"), - ) + unique_together = (("medical_personnel", "hospital"),) verbose_name_plural = "Medical Personnel Preferred Hospitals" def __str__(self): return f"{self.medical_personnel.user.first_name} {self.medical_personnel.user.last_name} ({self.medical_personnel.user.username}) - Hospital {self.hospital.name}" + class Episode(Model): class EpisodeChoices(TextChoices): INGUINAL = ("INGUINAL", "Inguinal Mesh Hernia Repair") @@ -227,52 +229,55 @@ def __str__(self): class Meta: verbose_name_plural = "Follow Ups" + class Zone(Model): name = CharField(max_length=255, unique=True) + def __str__(self): return self.name + class Meta: verbose_name_plural = "Zones" + class Region(Model): name = CharField(max_length=255, unique=True) + def __str__(self): return self.name + class Meta: verbose_name_plural = "Regions" + class HospitalRegionMapping(Model): hospital = OneToOneField( - Hospital, - on_delete=CASCADE, - related_name="region_mapping" + Hospital, on_delete=CASCADE, related_name="region_mapping" ) region = ForeignKey( - Region, - on_delete=CASCADE, - related_name="hospital_mappings" + Region, on_delete=CASCADE, related_name="hospital_mappings" ) + def __str__(self): return f"{self.hospital.name} - {self.region.name}" + class Meta: verbose_name_plural = "Hospital-Region Mappings" + class RegionZoneMapping(Model): region = OneToOneField( - Region, - on_delete=CASCADE, - related_name="zone_mapping" - ) - zone = ForeignKey( - Zone, - on_delete=CASCADE, - related_name="region_mappings" + Region, on_delete=CASCADE, related_name="zone_mapping" ) + zone = ForeignKey(Zone, on_delete=CASCADE, related_name="region_mappings") + def __str__(self): return f"{self.region.name} - {self.zone.name}" + class Meta: verbose_name_plural = "Region-Zone Mappings" + class Announcement(Model): announcement_text = TextField() display_from = DateTimeField(null=True, blank=True) @@ -280,4 +285,6 @@ class Announcement(Model): created_at = DateTimeField(auto_now_add=True) def __str__(self): - return f"Announcement ({self.created_at}): {self.announcement_text[:50]}" + return ( + f"Announcement ({self.created_at}): {self.announcement_text[:50]}" + ) diff --git a/tmh_registry/registry/urls.py b/tmh_registry/registry/urls.py index 7d0c051..95bbf4b 100644 --- a/tmh_registry/registry/urls.py +++ b/tmh_registry/registry/urls.py @@ -2,17 +2,17 @@ from rest_framework.routers import DefaultRouter from .api.viewsets import ( + AnnouncementViewSet, DischargeViewset, EpisodeViewset, FollowUpViewset, HospitalViewSet, + OwnedEpisodesViewSet, PatientHospitalMappingViewset, PatientViewSet, PreferredHospitalViewSet, SurgeonEpisodeSummaryViewSet, - OwnedEpisodesViewSet, UnlinkedPatientsViewSet, - AnnouncementViewSet ) router = DefaultRouter() @@ -22,11 +22,25 @@ router.register(r"episodes", EpisodeViewset) router.register(r"discharges", DischargeViewset) router.register(r"follow-ups", FollowUpViewset) -router.register(r"preferred-hospital", PreferredHospitalViewSet, basename='preferred-hospital') -router.register(r'surgeon-episode-summary', SurgeonEpisodeSummaryViewSet, basename='surgeon-episode-summary') -router.register(r"owned-episodes", OwnedEpisodesViewSet, basename='owned-episodes') -router.register(r'unlinked-patients', UnlinkedPatientsViewSet, basename='unlinked-patients') -router.register(r"announcements", AnnouncementViewSet, basename="announcements") +router.register( + r"preferred-hospital", + PreferredHospitalViewSet, + basename="preferred-hospital", +) +router.register( + r"surgeon-episode-summary", + SurgeonEpisodeSummaryViewSet, + basename="surgeon-episode-summary", +) +router.register( + r"owned-episodes", OwnedEpisodesViewSet, basename="owned-episodes" +) +router.register( + r"unlinked-patients", UnlinkedPatientsViewSet, basename="unlinked-patients" +) +router.register( + r"announcements", AnnouncementViewSet, basename="announcements" +) urlpatterns = [ path("", include(router.urls)), From a2f044ab0291a41fff6d2dbbb7b47aa033fda570 Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 00:54:23 +0000 Subject: [PATCH 3/6] fix CI docker command --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bfad31..402facb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,17 +44,17 @@ jobs: - name: Checkout Code Repository uses: actions/checkout@v2 - name: Build the Stack - run: docker-compose -f local.yml build + run: docker compose -f local.yml build - name: Make DB Migrations - run: docker-compose -f local.yml run --rm django python manage.py migrate + run: docker compose -f local.yml run --rm django python manage.py migrate - name: Run the Stack - run: docker-compose -f local.yml up -d + run: docker compose -f local.yml up -d - name: Run Django Tests - run: docker-compose -f local.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest --disable-pytest-warnings; + run: docker compose -f local.yml exec -T django coverage run --rcfile=.pre-commit/setup.cfg -m pytest --disable-pytest-warnings; - name: Print Coverage - run: docker-compose -f local.yml exec -T django coverage report + run: docker compose -f local.yml exec -T django coverage report - name: Tear down the Stack - run: docker-compose -f local.yml down + run: docker compose -f local.yml down deploy: runs-on: ubuntu-latest From ce332fedee626add14eb5547ec5a8aec1562eae6 Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 01:06:43 +0000 Subject: [PATCH 4/6] upgrade the dependencies --- local.yml | 2 -- requirements/local.txt | 40 ++++++++++++++++++++-------------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/local.yml b/local.yml index d491847..b2b9087 100644 --- a/local.yml +++ b/local.yml @@ -1,5 +1,3 @@ -version: '3' - volumes: local_postgres_data: {} local_postgres_data_backups: {} diff --git a/requirements/local.txt b/requirements/local.txt index 9219228..0eeb2ae 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -7,29 +7,29 @@ watchgod==0.7 # https://github.com/samuelcolvin/watchgod # Testing # ------------------------------------------------------------------------------ -mypy==0.790 # https://github.com/python/mypy -django-stubs==1.8.0 # https://github.com/typeddjango/django-stubs -pytest==6.1.2 # https://github.com/pytest-dev/pytest -pytest-sugar==0.9.4 # https://github.com/Frozenball/pytest-sugar -mock==4.0.3 # https://pypi.org/project/mock/ -freezegun==1.1.0 # https://pypi.org/project/freezegun/0.1.11/ -faker==8.5.1 # https://github.com/joke2k/faker -parameterized==0.8.1 # https://github.com/wolever/parameterized +mypy==1.5.1 # https://github.com/python/mypy +django-stubs==4.2.3 # https://github.com/typeddjango/django-stubs +pytest==7.4.3 # https://github.com/pytest-dev/pytest +pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar +mock==5.1.0 # https://pypi.org/project/mock/ +freezegun==1.2.2 # https://pypi.org/project/freezegun/ +faker==19.3.1 # https://github.com/joke2k/faker +parameterized==0.9.0 # https://github.com/wolever/parameterized # Code quality # ------------------------------------------------------------------------------ -flake8==3.8.4 # https://github.com/PyCQA/flake8 -flake8-isort==4.0.0 # https://github.com/gforcada/flake8-isort -coverage==5.5 # https://github.com/nedbat/coveragepy -black==20.8b1 # https://github.com/ambv/black -pylint-django==2.5.4 # https://github.com/PyCQA/pylint-django -pre-commit==2.10.1 # https://github.com/pre-commit/pre-commit -isort==4.3.21 # https://github.com/PyCQA/isort +flake8==6.1.0 # https://github.com/PyCQA/flake8 +flake8-isort==6.0.0 # https://github.com/gforcada/flake8-isort +coverage==7.3.2 # https://github.com/nedbat/coveragepy +black==23.9.1 # https://github.com/psycopg/black +pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django +pre-commit==3.5.0 # https://github.com/pre-commit/pre-commit +isort==5.12.0 # https://github.com/PyCQA/isort # Django # ------------------------------------------------------------------------------ -factory-boy==3.2.0 # https://github.com/FactoryBoy/factory_boy -django-debug-toolbar==3.1.1 # https://github.com/jazzband/django-debug-toolbar -django-extensions==3.1.3 # https://github.com/django-extensions/django-extensions -django-coverage-plugin==2.0.0 # https://github.com/nedbat/django_coverage_plugin -pytest-django==4.1.0 # https://github.com/pytest-dev/pytest-django +factory-boy==3.3.0 # https://github.com/FactoryBoy/factory_boy +django-debug-toolbar==4.2.0 # https://github.com/jazzband/django-debug-toolbar +django-extensions==3.2.3 # https://github.com/django-extensions/django-extensions +django-coverage-plugin==3.1.0 # https://github.com/nedbat/django_coverage_plugin +pytest-django==4.7.0 # https://github.com/pytest-dev/pytest-django From 0a3c3d0ba97c9f6918acf0a35f97973b21243110 Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 01:15:01 +0000 Subject: [PATCH 5/6] upgrade django --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 3deaf87..e5e1552 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -9,7 +9,7 @@ uvicorn[standard]==0.18.0 # https://github.com/encode/uvicorn # Django # ------------------------------------------------------------------------------ django-storages[boto3]==1.11.1 # https://github.com/jschneier/django-storages -django==3.1.3 # https://www.djangoproject.com/ +django==3.2.25 # https://www.djangoproject.com/ django-environ==0.4.5 # https://github.com/joke2k/django-environ django-model-utils==4.1.1 # https://github.com/jazzband/django-model-utils django-crispy-forms==1.12.0 # https://github.com/django-crispy-forms/django-crispy-forms From 124e021c071c95d2e05805d1ae09aeeb637a355a Mon Sep 17 00:00:00 2001 From: Sabi Date: Tue, 9 Dec 2025 01:17:47 +0000 Subject: [PATCH 6/6] fix new formatting rules --- config/api_router.py | 1 - tmh_registry/common/error_handling.py | 1 - tmh_registry/common/tests/test_error_handling.py | 1 - tmh_registry/registry/admin.py | 1 - tmh_registry/registry/models.py | 1 - .../registry/tests/api/viewsets/episodes/test_get_detail.py | 1 - .../registry/tests/api/viewsets/episodes/test_get_discharge.py | 1 - .../registry/tests/api/viewsets/episodes/test_get_follow_ups.py | 1 - tmh_registry/registry/tests/api/viewsets/episodes/test_post.py | 1 - tmh_registry/registry/tests/api/viewsets/test_discharges.py | 1 - tmh_registry/registry/tests/api/viewsets/test_follow_ups.py | 1 - .../tests/api/viewsets/test_patient_hospital_mappings.py | 1 - tmh_registry/users/admin.py | 1 - tmh_registry/users/api/permissions.py | 1 - tmh_registry/users/api/views.py | 1 - tmh_registry/users/api/viewsets.py | 1 - tmh_registry/users/tests/api/test_serializers.py | 1 - tmh_registry/users/tests/api/test_viewsets.py | 1 - tmh_registry/users/tests/test_views.py | 1 - tmh_registry/users/urls.py | 1 - 20 files changed, 20 deletions(-) diff --git a/config/api_router.py b/config/api_router.py index ffab400..cf0e037 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -1,6 +1,5 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter - from tmh_registry.users.api.viewsets import UserViewSet router = DefaultRouter() diff --git a/tmh_registry/common/error_handling.py b/tmh_registry/common/error_handling.py index f8e3bb7..478f780 100644 --- a/tmh_registry/common/error_handling.py +++ b/tmh_registry/common/error_handling.py @@ -22,7 +22,6 @@ HTTP_405_METHOD_NOT_ALLOWED, HTTP_500_INTERNAL_SERVER_ERROR, ) - from tmh_registry.common.exceptions import ProjectAPIException logger = getLogger(__name__) diff --git a/tmh_registry/common/tests/test_error_handling.py b/tmh_registry/common/tests/test_error_handling.py index e1f62e2..7c52f78 100644 --- a/tmh_registry/common/tests/test_error_handling.py +++ b/tmh_registry/common/tests/test_error_handling.py @@ -20,7 +20,6 @@ HTTP_404_NOT_FOUND, HTTP_405_METHOD_NOT_ALLOWED, ) - from tmh_registry.common.error_handling import ( error_handler, get_exception_handling_dict, diff --git a/tmh_registry/registry/admin.py b/tmh_registry/registry/admin.py index aa491df..f739dce 100644 --- a/tmh_registry/registry/admin.py +++ b/tmh_registry/registry/admin.py @@ -3,7 +3,6 @@ from django.contrib.auth.models import User from import_export import resources from import_export.admin import ExportMixin - from tmh_registry.registry.models import ( Announcement, Discharge, diff --git a/tmh_registry/registry/models.py b/tmh_registry/registry/models.py index cf4c6c6..19587a8 100644 --- a/tmh_registry/registry/models.py +++ b/tmh_registry/registry/models.py @@ -14,7 +14,6 @@ TextChoices, TextField, ) - from tmh_registry.common.models import TimeStampMixin from tmh_registry.users.models import MedicalPersonnel diff --git a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_detail.py b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_detail.py index a81f544..12e3b9c 100644 --- a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_detail.py +++ b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_detail.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND from rest_framework.test import APIClient - from tmh_registry.users.factories import MedicalPersonnelFactory from .....factories import EpisodeFactory diff --git a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_discharge.py b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_discharge.py index 6054886..dc9afa1 100644 --- a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_discharge.py +++ b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_discharge.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND from rest_framework.test import APIClient - from tmh_registry.users.factories import MedicalPersonnelFactory from .....factories import DischargeFactory, EpisodeFactory diff --git a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_follow_ups.py b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_follow_ups.py index 8a3bfc0..589dac0 100644 --- a/tmh_registry/registry/tests/api/viewsets/episodes/test_get_follow_ups.py +++ b/tmh_registry/registry/tests/api/viewsets/episodes/test_get_follow_ups.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND from rest_framework.test import APIClient - from tmh_registry.users.factories import MedicalPersonnelFactory from .....factories import EpisodeFactory, FollowUpFactory diff --git a/tmh_registry/registry/tests/api/viewsets/episodes/test_post.py b/tmh_registry/registry/tests/api/viewsets/episodes/test_post.py index 96cea81..6bfc95d 100644 --- a/tmh_registry/registry/tests/api/viewsets/episodes/test_post.py +++ b/tmh_registry/registry/tests/api/viewsets/episodes/test_post.py @@ -5,7 +5,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST from rest_framework.test import APIClient - from tmh_registry.common.utils.functions import ( get_text_choice_value_from_label, ) diff --git a/tmh_registry/registry/tests/api/viewsets/test_discharges.py b/tmh_registry/registry/tests/api/viewsets/test_discharges.py index 4c4e56a..80bf2df 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_discharges.py +++ b/tmh_registry/registry/tests/api/viewsets/test_discharges.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST from rest_framework.test import APIClient - from tmh_registry.registry.factories import DischargeFactory, EpisodeFactory from tmh_registry.users.factories import MedicalPersonnelFactory diff --git a/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py b/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py index fcb65e9..a8d66da 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py +++ b/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST from rest_framework.test import APIClient - from tmh_registry.common.utils.functions import ( get_text_choice_value_from_label, ) diff --git a/tmh_registry/registry/tests/api/viewsets/test_patient_hospital_mappings.py b/tmh_registry/registry/tests/api/viewsets/test_patient_hospital_mappings.py index 3731d95..9fb3f55 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_patient_hospital_mappings.py +++ b/tmh_registry/registry/tests/api/viewsets/test_patient_hospital_mappings.py @@ -2,7 +2,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST from rest_framework.test import APIClient - from tmh_registry.users.factories import MedicalPersonnelFactory from ....factories import ( diff --git a/tmh_registry/users/admin.py b/tmh_registry/users/admin.py index 47e6401..9783f50 100644 --- a/tmh_registry/users/admin.py +++ b/tmh_registry/users/admin.py @@ -1,6 +1,5 @@ from django.contrib import admin from import_export.admin import ExportMixin - from tmh_registry.users.models import MedicalPersonnel diff --git a/tmh_registry/users/api/permissions.py b/tmh_registry/users/api/permissions.py index 8ecac95..c8069fc 100644 --- a/tmh_registry/users/api/permissions.py +++ b/tmh_registry/users/api/permissions.py @@ -1,7 +1,6 @@ import builtins from rest_framework import permissions - from tmh_registry.users.models import MedicalPersonnel diff --git a/tmh_registry/users/api/views.py b/tmh_registry/users/api/views.py index 4abe8ee..e1e4138 100644 --- a/tmh_registry/users/api/views.py +++ b/tmh_registry/users/api/views.py @@ -12,7 +12,6 @@ from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView - from tmh_registry.users.api.serializers import ( ChangePasswordSerializer, SignInResponseSerializer, diff --git a/tmh_registry/users/api/viewsets.py b/tmh_registry/users/api/viewsets.py index c7736b6..9277e74 100644 --- a/tmh_registry/users/api/viewsets.py +++ b/tmh_registry/users/api/viewsets.py @@ -9,7 +9,6 @@ ) from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet - from tmh_registry.users.api.serializers import ( MedicalPersonnelSerializer, UserReadSerializer, diff --git a/tmh_registry/users/tests/api/test_serializers.py b/tmh_registry/users/tests/api/test_serializers.py index cf06a99..9d42311 100644 --- a/tmh_registry/users/tests/api/test_serializers.py +++ b/tmh_registry/users/tests/api/test_serializers.py @@ -4,7 +4,6 @@ from rest_framework.authtoken.models import Token from rest_framework.status import HTTP_403_FORBIDDEN from rest_framework.test import APIClient - from tmh_registry.users.api.serializers import UserSerializer from tmh_registry.users.factories import UserFactory diff --git a/tmh_registry/users/tests/api/test_viewsets.py b/tmh_registry/users/tests/api/test_viewsets.py index 4996c3f..6c7c3ba 100644 --- a/tmh_registry/users/tests/api/test_viewsets.py +++ b/tmh_registry/users/tests/api/test_viewsets.py @@ -3,7 +3,6 @@ from mock import MagicMock from pytest import mark from rest_framework import status - from tmh_registry.users.api.serializers import ( UserReadSerializer, UserSerializer, diff --git a/tmh_registry/users/tests/test_views.py b/tmh_registry/users/tests/test_views.py index c5b7d75..5870184 100644 --- a/tmh_registry/users/tests/test_views.py +++ b/tmh_registry/users/tests/test_views.py @@ -6,7 +6,6 @@ from pytest import mark from rest_framework import status from rest_framework.exceptions import ValidationError - from tmh_registry.users.api.views import SignInView diff --git a/tmh_registry/users/urls.py b/tmh_registry/users/urls.py index dac9736..a7cefb2 100644 --- a/tmh_registry/users/urls.py +++ b/tmh_registry/users/urls.py @@ -1,6 +1,5 @@ from django.urls import include, path from rest_framework.routers import DefaultRouter - from tmh_registry.users.api.views import ChangePasswordView, SignInView from tmh_registry.users.api.viewsets import MedicalPersonnelViewSet