Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Facility Flag: Difference between revisions

From OHC Network Wiki
Content deleted Content added
OHC identity seed
 
Automated edit (via update-page on MediaWiki MCP Server)
 
Line 217: Line 217:
* Flag names are limited to entries registered in the <code>FACILITY</code> <code>FlagRegistry</code>. An unregistered name fails save validation with <code>FlagNotFoundError</code>, so clients cannot introduce arbitrary flag strings.
* Flag names are limited to entries registered in the <code>FACILITY</code> <code>FlagRegistry</code>. An unregistered name fails save validation with <code>FlagNotFoundError</code>, so clients cannot introduce arbitrary flag strings.
* The <code>{facility, flag}</code> uniqueness holds only over non-deleted rows, so soft-deleted history is preserved and a flag can be removed then re-added.
* The <code>{facility, flag}</code> uniqueness holds only over non-deleted rows, so soft-deleted history is preserved and a flag can be removed then re-added.

{{Navbox facility}}


{{Related}}
{{Related}}

Latest revision as of 09:32, 6 July 2026

referencefacilityCARE 3.0+

FacilityFlag binds a registered flag name to a single Facility to gate server-side behaviour. It is not a FHIR resource and has no Pydantic spec — no endpoint, serializer, or ...CreateSpec/...ReadSpec — so everything below comes from the Django storage model. Clients never write flags; they read them as a derived array on the Facility resource (see Resource specs).

Source:

Models

Model Purpose
FacilityFlag Associates a registered feature flag with a single Facility, gating behaviour per facility

FacilityFlag extends BaseFlag, which extends BaseModel — the Care EMR base providing external_id (UUID), created_date/modified_date audit timestamps, and soft-delete via deleted. BaseModel is leaner than EMRBaseModel (see Base model): no created_by/updated_by, no history, no meta JSON. BaseFlag adds the flag value and the per-entity machinery: cache invalidation and validation against the flag registry.

FacilityFlag fields

Field Type Required Default Notes
flag CharField(max_length=1024) yes
facility FK → facility.Facility yes
external_id UUIDField auto
created_date DateTimeField auto
modified_date DateTimeField auto
deleted BooleanField no

The model has no JSONFields, so there is no opaque JSON shape to document.

Flag type values

flag_type is fixed to FlagType.FACILITY. The full FlagType enum (from [https://github.com/ohcnetwork/care/blob/develop/care/utils/registries/feature_flag.py feature_flag.py]):

FlagType member Value Used by
USER "USER" UserFlag ([[References/User & Skills
FACILITY "FACILITY" FacilityFlag (this model)

The flag values themselves are not enumerated in source. They are registered at runtime via FlagRegistry.register(FlagType.FACILITY, "<name>"), and a name absent from the FACILITY registry raises FlagNotFoundError on save.

Class-level configuration

These class attributes wire FacilityFlag into the generic BaseFlag caching and validation logic. They are not database fields.

Attribute Value Notes
cache_key_template "facility_flag_cache:{entity_id}:{flag_name}" Per-flag existence cache key
all_flags_cache_key_template "facility_all_flags_cache:{entity_id}" Cache key for a facility's full flag list
flag_type FlagType.FACILITY Registry namespace used for flag-name validation
entity_field_name "facility" Tells BaseFlag which FK is the owning entity; backs the entity / entity_id properties

Module-level constants mirror these templates: FACILITY_FLAG_CACHE_KEY ("facility_flag_cache:{facility_id}:{flag_name}"), FACILITY_ALL_FLAGS_CACHE_KEY ("facility_all_flags_cache:{facility_id}"), and FACILITY_FLAG_CACHE_TTL (60 * 60 * 24, 1 day).

Constraints

UniqueConstraint(
    fields=["facility", "flag"],
    condition=Q(deleted=False),
    name="unique_facility_flag",
)

A facility holds a given flag at most once among non-deleted rows. The condition excludes soft-deleted rows, so a flag can be removed and re-added. Meta.verbose_name = "Facility Flag".

Methods & save behaviour

save() (inherited from BaseFlag)

Every save runs four steps in order:

  1. validate_flag(self.flag) calls FlagRegistry.validate_flag_name(FlagType.FACILITY, flag), which validates the flag type, then checks that the name is registered. An unknown type or unregistered name raises FlagNotFoundError (a subclass of Django ValidationError) and the row is not written.
  2. The per-flag cache key (facility_flag_cache:{facility_id}:{flag}) is deleted.
  3. The all-flags cache key (facility_all_flags_cache:{facility_id}) is deleted.
  4. The row is persisted via super().save().

Deletes use the BaseModel soft-delete: delete() sets deleted = True and saves update_fields=["deleted"]. It does not purge cache entries, so they expire by TTL or are rebuilt on the next miss.

Lookups

Method Returns Notes
check_facility_has_flag(facility_id, flag_name) bool Delegates to BaseFlag.check_entity_has_flag: validates the flag name, then cache.get_or_set on the per-flag key with an .exists() query; cached for FLAGS_CACHE_TTL (1 day)
get_all_flags(facility_id) tuple[FlagName] Delegates to BaseFlag.get_all_flags: cache.get_or_set on the all-flags key, materializing values_list("flag", flat=True) for the facility; cached for 1 day

Both wrap the generic BaseFlag methods using entity_field_name="facility". Facility.get_facility_flags() is a thin wrapper over FacilityFlag.get_all_flags(self.id).

Properties (inherited)

  • entity → the related Facility instance (getattr(self, "facility"))
  • entity_idfacility_id (getattr(self, "facility_id"))

Resource specs (API schema)

FacilityFlag has no Pydantic resource spec and no REST endpoint of its own. There is no FacilityFlagCreateSpec / FacilityFlagUpdateSpec / FacilityFlagListSpec / FacilityFlagRetrieveSpec, no serializer, and no viewset — confirmed by their absence from care/emr/resources/ and care/facility/api/. Flags are administered through Django admin or registration/management code, not the public API.

Flags reach API clients in exactly one place: the Facility resource exposes them as a derived read-only field.

Spec Field Role Behaviour
FacilityRetrieveSpec ([https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/facility/spec.py facility/spec.py]) flags: list[str] read · detail Populated in perform_extra_serialization via mapping["flags"] = obj.get_facility_flags(). Server-maintained and read-only; clients cannot set or mutate flags through the Facility write specs

The flags array is the materialized list of registered flag names attached to the facility, from FacilityFlag.get_all_flags (cache-backed, 1-day TTL). No value set is bound to it — entries are the runtime FACILITY registry names, not a coded concept. The User resource mirrors this, exposing flags via obj.get_all_flags() backed by UserFlag (see User).

FacilityFlag defines no secondary models. Its behaviour comes from two utilities in care/utils.

BaseFlag (abstract)

flag → CharField(max_length=1024)
(abstract base — no table of its own)
class attrs: cache_key_template, all_flags_cache_key_template, flag_type, entity_field_name

Provides save-time validation, cache invalidation, and the check_entity_has_flag / get_all_flags lookups. Subclasses set flag_type, entity_field_name, and the two cache-key templates. See [https://github.com/ohcnetwork/care/blob/develop/care/utils/models/base.py care/utils/models/base.py].

FlagRegistry

An in-process, class-level registry (_flags: dict[FlagType, dict[FlagName, bool]]) of valid flag names per FlagType. Key methods:

Method Notes
register(flag_type, flag_name) Adds a name to a type's registry, creating the type bucket if needed
register_wrapper(flag_type, flag_name) Class-decorator form of register
unregister(flag_type, flag_name) Removes a name; logs a warning if absent
validate_flag_type(flag_type) Raises FlagNotFoundError ("Invalid Flag Type") if the type bucket is missing
validate_flag_name(flag_type, flag_name) Validates type, then raises FlagNotFoundError ("Flag not registered") if the name is absent
get_all_flags(flag_type) list[FlagName] of registered names
get_all_flags_as_choices(flag_type) (value, value) tuples for Django choices

A flag must be registered before a FacilityFlag referencing it can be saved. See [https://github.com/ohcnetwork/care/blob/develop/care/utils/registries/feature_flag.py care/utils/registries/feature_flag.py].

API integration notes

  • There is no CRUD endpoint. Manage flags via Django admin or registration/management code; clients only observe them through the read-only flags: list[str] field on FacilityRetrieveSpec (see Resource specs).
  • In application code, read through check_facility_has_flag / get_all_flags (or Facility.get_facility_flags()), which are cache-backed. Do not query the table directly for hot-path checks.
  • Flag names are limited to entries registered in the FACILITY FlagRegistry. An unregistered name fails save validation with FlagNotFoundError, so clients cannot introduce arbitrary flag strings.
  • The {facility, flag} uniqueness holds only over non-deleted rows, so soft-deleted history is preserved and a flag can be removed then re-added.