diff --git a/anonymiser/management/commands/display_model_anonymisation.py b/anonymiser/management/commands/display_model_anonymisation.py index 888ec77..e53d8de 100644 --- a/anonymiser/management/commands/display_model_anonymisation.py +++ b/anonymiser/management/commands/display_model_anonymisation.py @@ -1,24 +1,16 @@ from typing import Any -from django.apps import apps from django.core.management.base import BaseCommand -from django.db.models import Model from django.template.loader import render_to_string -from anonymiser.registry import get_model_anonymiser +from anonymiser import registry class Command(BaseCommand): - def get_models(self) -> list[type[Model]]: - """Force alphabetical order of models.""" - return sorted(apps.get_models(), key=lambda m: m._meta.label) - def handle(self, *args: Any, **options: Any) -> None: - for model in self.get_models(): - if anonymiser := get_model_anonymiser(model): - data = anonymiser.get_model_field_summary() + model_fields = registry.get_all_model_fields() out = render_to_string( "display_model_anonymisation.md", - {"anonymised_models": data}, + {"model_fields": model_fields}, ) self.stdout.write(out) diff --git a/anonymiser/models.py b/anonymiser/models.py index c6cfdee..b42cfbf 100644 --- a/anonymiser/models.py +++ b/anonymiser/models.py @@ -175,8 +175,8 @@ class RedacterBase(_ModelBase): custom_field_redactions: dict[str, Any] = {} class FieldRedactionStratgy(StrEnum): - AUTO = "auto" - CUSTOM = "custom" + AUTO = "AUTO" + CUSTOM = "CUSTOM" NONE = "" def is_field_redaction_auto(self, field: models.Field) -> bool: diff --git a/anonymiser/registry.py b/anonymiser/registry.py index 304fc06..bed077d 100644 --- a/anonymiser/registry.py +++ b/anonymiser/registry.py @@ -1,9 +1,11 @@ import logging import threading +from collections import defaultdict +from django.apps import apps from django.db import models -from .models import ModelAnonymiser +from .models import ModelAnonymiser, ModelFieldSummary lock = threading.Lock() logger = logging.getLogger(__name__) @@ -47,3 +49,28 @@ def get_model_anonymiser( if anonymiser := _registry.get(model): return anonymiser() return None + + +def get_all_model_fields( + anonymised_only: bool = False, +) -> dict[str, list[ModelFieldSummary]]: + """ + Return all models and their fields as ModelFieldSummary. + + The return dict uses the `app.Model` string format as the dict key, + with a list of all fields as the value. This method includes all + models by default unless the `anonymised_only` + param is True. + + """ + models = sorted(apps.get_models(), key=lambda m: m._meta.label) + output = defaultdict(list) + for m in models: + anonymiser = get_model_anonymiser(m) + if anonymised_only and not anonymiser: + continue + for f in m._meta.get_fields(): + output[m._meta.label].append(ModelFieldSummary(m, f, anonymiser)) + # sort fields by type then name - easier to scan. + output[m._meta.label].sort(key=lambda d: f"{d.field_type}.{d.field_name}") + return dict(output) diff --git a/anonymiser/templates/display_model_anonymisation.md b/anonymiser/templates/display_model_anonymisation.md index 614e332..fd1cd30 100644 --- a/anonymiser/templates/display_model_anonymisation.md +++ b/anonymiser/templates/display_model_anonymisation.md @@ -1,11 +1,4 @@ -# Model Anonymisation Snapshot - -## Registered model anonymisers -Model | Anonymiser ---- | ---{% for model,anonymiser in model_anonymisers.items %} -{{ model }} | {{ anonymiser|default:"-" }} {% endfor %} - ## Model field anonymisation App | Model | Field | Type | Anonymise | Redacte ---- | --- | --- | --- | --- | ---{% for field in anonymised_models %} -{{ field.app }} | {{ field.model }} | {{ field.field_name }} | {{ field.field_type }} | {% if field.is_anonymised %}X{% else %}-{% endif %} | {{ field.redaction_strategy|default:"-" }}{% endfor %} +--- | --- | --- | --- | --- | ---{% for model,fields in anonymised_models.items %}{% for field in fields %} +{{ field.app }} | {{ field.model }} | {{ field.field_name }} | {{ field.field_type }} | {{ field.is_anonymised|default:"-" }} | {{ field.redaction_strategy|default:"-"|upper }}{% endfor %}{% endfor %} diff --git a/tests/test_models.py b/tests/test_models.py index 505ad0a..a821cac 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -34,14 +34,14 @@ def test_model_fields_summary(user_anonymiser: UserAnonymiser) -> None: def test_model_fields_data(user_anonymiser: UserAnonymiser) -> None: - fsd = ModelFieldSummary(User, User._meta.get_field("first_name"), user_anonymiser) - assert fsd.app == "tests" - assert fsd.model == "User" - assert fsd.field_name == "first_name" - assert fsd.field_type == "CharField" - assert fsd.is_anonymised is True - assert fsd.is_redacted is True - assert fsd.redaction_strategy == user_anonymiser.FieldRedactionStratgy.AUTO + mfs = ModelFieldSummary(User, User._meta.get_field("first_name"), user_anonymiser) + assert mfs.app == "tests" + assert mfs.model == "User" + assert mfs.field_name == "first_name" + assert mfs.field_type == "CharField" + assert mfs.is_anonymised is True + assert mfs.is_redacted is True + assert mfs.redaction_strategy == user_anonymiser.FieldRedactionStratgy.CUSTOM @pytest.mark.django_db