Skip to content

Commit

Permalink
added caching and user flags
Browse files Browse the repository at this point in the history
  • Loading branch information
sainak committed Sep 10, 2024
1 parent 781d3d2 commit e5cc032
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 84 deletions.
19 changes: 8 additions & 11 deletions care/facility/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django import forms
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django import forms
from djangoql.admin import DjangoQLSearchMixin
from djqscsv import render_to_csv_response

Expand All @@ -13,12 +13,14 @@
PatientConsultation,
)
from care.facility.models.patient_sample import PatientSample
from care.utils.registries.feature_flag import FlagRegistry, FlagType

from .models import (
Building,
Disease,
Facility,
FacilityCapacity,
FacilityFlag,
FacilityInventoryItem,
FacilityInventoryItemTag,
FacilityInventoryUnit,
Expand All @@ -34,9 +36,8 @@
PatientInvestigationGroup,
PatientRegistration,
Room,
StaffRoomAllocation, FacilityFlag,
StaffRoomAllocation,
)
from .registries.feature_flag import FlagRegistry, FlagType


class BuildingAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -190,16 +191,11 @@ class FacilityUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin, ExportCsvMixin):
actions = ["export_as_csv"]


def get_facility_flags():
try:
return ((x,x) for x in FlagRegistry.get_all_flags(FlagType.FACILITY))
except:
return ()

class FacilityFlagAdmin(admin.ModelAdmin):

class AddHorribleTableEntryForm(forms.ModelForm):
flag = forms.ChoiceField(choices=get_facility_flags)
flag = forms.ChoiceField(
choices=lambda: FlagRegistry.get_all_flags_as_choices(FlagType.FACILITY)
)

class Meta:
fields = "__all__"
Expand Down Expand Up @@ -237,4 +233,5 @@ class Meta:
admin.site.register(PatientConsent)
admin.site.register(FileUpload)
admin.site.register(PatientConsultation)

admin.site.register(FacilityFlag, FacilityFlagAdmin)
5 changes: 5 additions & 0 deletions care/facility/api/serializers/facility.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ class FacilitySerializer(FacilityBasicInfoSerializer):
)
bed_count = serializers.SerializerMethodField()

facility_flags = serializers.SerializerMethodField()

def get_facility_flags(self, facility):
return facility.get_facility_flags()

class Meta:
model = Facility
fields = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Generated by Django 4.2.10 on 2024-09-06 12:20
# Generated by Django 4.2.15 on 2024-09-10 14:07

from django.db import migrations, models
import django.db.models.deletion
import uuid

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

Expand Down Expand Up @@ -47,7 +48,15 @@ class Migration(migrations.Migration):
),
],
options={
"abstract": False,
"verbose_name": "Facility Flag",
},
),
migrations.AddConstraint(
model_name="facilityflag",
constraint=models.UniqueConstraint(
condition=models.Q(("deleted", False)),
fields=("facility", "flag"),
name="unique_facility_flag",
),
),
]
4 changes: 4 additions & 0 deletions care/facility/models/facility.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from simple_history.models import HistoricalRecords

from care.facility.models import FacilityBaseModel, reverse_choices
from care.facility.models.feature_flag import FacilityFlag
from care.facility.models.mixins.permissions.facility import (
FacilityPermissionMixin,
FacilityRelatedPermissionMixin,
Expand Down Expand Up @@ -282,6 +283,9 @@ def get_features_display(self):
return []
return [FacilityFeature(f).label for f in self.features]

def get_facility_flags(self):
return FacilityFlag.get_all_flags(self.id)

CSV_MAPPING = {
"name": "Facility Name",
"facility_type": "Facility Type",
Expand Down
79 changes: 62 additions & 17 deletions care/facility/models/feature_flag.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,79 @@
from care.facility.registries.feature_flag import FlagRegistry, FlagType
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import models

from care.utils.models.base import BaseModel
from care.utils.registries.feature_flag import FlagName, FlagRegistry, FlagType

from django.db import models
FACILITY_FLAG_CACHE_KEY = "facility_flag_cache:{facility_id}:{flag_name}"
FACILITY_ALL_FLAGS_CACHE_KEY = "facility_all_flags_cache:{facility_id}"
FACILITY_FLAG_CACHE_TTL = 60 * 60 * 24 # 1 Day


class FacilityFlag(BaseModel):
facility = models.ForeignKey(
"Facility", on_delete=models.CASCADE, null=False, blank=False
"facility.Facility", on_delete=models.CASCADE, null=False, blank=False
)
flag = models.CharField(max_length=1024)

class Meta:
verbose_name = "Facility Flag"

def __str__(self):
return f"{self.facility.name} - {self.flag}"

@classmethod
def validate_flag(cls, flag_name):
def validate_flag(cls, flag_name: FlagName):
FlagRegistry.validate_flag_name(FlagType.FACILITY, flag_name)

def save(
self, *args, **kwargs
):
def save(self, *args, **kwargs):
self.validate_flag(self.flag)
# TODO : Add Unique Together

if (
not self.deleted
and self.__class__.objects.filter(
facility_id=self.facility_id, flag=self.flag
).exists()
):
raise ValidationError("Flag Already Exists")

cache.delete(
FACILITY_FLAG_CACHE_KEY.format(
facility_id=self.facility_id, flag_name=self.flag
)
)
cache.delete(FACILITY_ALL_FLAGS_CACHE_KEY.format(facility_id=self.facility_id))

return super().save(*args, **kwargs)

@classmethod
def check_facility_has_flag(cls , facility , flag_name):
def check_facility_has_flag(cls, facility_id: int, flag_name: FlagName) -> bool:
cls.validate_flag(flag_name)
# TODO : Add Caching , Invalidate on save actions
return cls.objects.filter(facility=facility, flag=flag_name).exists()
return cache.get_or_set(
FACILITY_FLAG_CACHE_KEY.format(
facility_id=facility_id, flag_name=flag_name
),
default=lambda: cls.objects.filter(
facility_id=facility_id, flag=flag_name
).exists(),
timeout=FACILITY_FLAG_CACHE_TTL,
)

@classmethod
def get_all_flags(cls, facility_id: int) -> tuple[FlagName]:
return cache.get_or_set(
FACILITY_ALL_FLAGS_CACHE_KEY.format(facility_id=facility_id),
default=lambda: tuple(
cls.objects.filter(facility_id=facility_id).values_list(
"flag", flat=True
)
),
timeout=FACILITY_FLAG_CACHE_TTL,
)

def __str__(self) -> str:
return f"Facility Flag: {self.facility.name} - {self.flag}"

class Meta:
verbose_name = "Facility Flag"
constraints = [
models.UniqueConstraint(
fields=["facility", "flag"],
condition=models.Q(deleted=False),
name="unique_facility_flag",
)
]
49 changes: 0 additions & 49 deletions care/facility/registries/feature_flag.py

This file was deleted.

27 changes: 25 additions & 2 deletions care/users/admin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
from django import forms
from django.contrib import admin
from django.contrib.auth import admin as auth_admin
from django.contrib.auth import get_user_model
from djqscsv import render_to_csv_response

from care.users.forms import UserChangeForm, UserCreationForm
from care.users.models import District, LocalBody, Skill, State, UserSkill, Ward
from care.users.models import (
District,
LocalBody,
Skill,
State,
UserFlag,
UserSkill,
Ward,
)
from care.utils.registries.feature_flag import FlagRegistry, FlagType

User = get_user_model()

Expand Down Expand Up @@ -72,6 +82,19 @@ class WardAdmin(admin.ModelAdmin):
autocomplete_fields = ["local_body"]


admin.site.register(Skill)
@admin.register(UserFlag)
class UserFlagAdmin(admin.ModelAdmin):
class AddHorribleTableEntryForm(forms.ModelForm):
flag = forms.ChoiceField(
choices=lambda: FlagRegistry.get_all_flags_as_choices(FlagType.USER)
)

class Meta:
fields = "__all__"
model = UserFlag

form = AddHorribleTableEntryForm


admin.site.register(Skill)
admin.site.register(UserSkill)
6 changes: 6 additions & 0 deletions care/users/api/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ class UserSerializer(SignUpSerializer):

date_of_birth = serializers.DateField(required=True)

user_flags = serializers.SerializerMethodField()

def get_user_flags(self, user) -> tuple[str]:
return user.get_all_flags()

class Meta:
model = User
fields = (
Expand Down Expand Up @@ -316,6 +321,7 @@ class Meta:
"pf_endpoint",
"pf_p256dh",
"pf_auth",
"user_flags",
)
read_only_fields = (
"is_superuser",
Expand Down
63 changes: 63 additions & 0 deletions care/users/migrations/0017_userflag_userflag_unique_user_flag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Generated by Django 4.2.15 on 2024-09-10 14:07

import uuid

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("users", "0016_upgrade_user_skills"),
]

operations = [
migrations.CreateModel(
name="UserFlag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"external_id",
models.UUIDField(db_index=True, default=uuid.uuid4, unique=True),
),
(
"created_date",
models.DateTimeField(auto_now_add=True, db_index=True, null=True),
),
(
"modified_date",
models.DateTimeField(auto_now=True, db_index=True, null=True),
),
("deleted", models.BooleanField(db_index=True, default=False)),
("flag", models.CharField(max_length=1024)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "User Flag",
},
),
migrations.AddConstraint(
model_name="userflag",
constraint=models.UniqueConstraint(
condition=models.Q(("deleted", False)),
fields=("user", "flag"),
name="unique_user_flag",
),
),
]
Loading

0 comments on commit e5cc032

Please sign in to comment.