UPDATE
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
from django.utils import timezone
|
||||
|
||||
from .models import AccessFeature, AccessRule, FarmAccessProfile
|
||||
|
||||
|
||||
def _manager_id_set(manager):
|
||||
return {obj.id for obj in manager.all()}
|
||||
|
||||
|
||||
def rule_matches_farm(rule, farm, product_ids=None, sensor_catalog_ids=None):
|
||||
if not rule.is_active:
|
||||
return False
|
||||
|
||||
subscription_plan_ids = _manager_id_set(rule.subscription_plans)
|
||||
if subscription_plan_ids:
|
||||
if farm.subscription_plan_id is None or farm.subscription_plan_id not in subscription_plan_ids:
|
||||
return False
|
||||
|
||||
farm_type_ids = _manager_id_set(rule.farm_types)
|
||||
if farm_type_ids and farm.farm_type_id not in farm_type_ids:
|
||||
return False
|
||||
|
||||
product_rule_ids = _manager_id_set(rule.products)
|
||||
if product_rule_ids:
|
||||
product_ids = product_ids if product_ids is not None else set(farm.products.values_list("id", flat=True))
|
||||
if not product_ids or product_rule_ids.isdisjoint(product_ids):
|
||||
return False
|
||||
|
||||
sensor_catalog_rule_ids = _manager_id_set(rule.sensor_catalogs)
|
||||
if sensor_catalog_rule_ids:
|
||||
sensor_catalog_ids = (
|
||||
sensor_catalog_ids
|
||||
if sensor_catalog_ids is not None
|
||||
else set(farm.sensors.exclude(sensor_catalog_id__isnull=True).values_list("sensor_catalog_id", flat=True))
|
||||
)
|
||||
if not sensor_catalog_ids or sensor_catalog_rule_ids.isdisjoint(sensor_catalog_ids):
|
||||
return False
|
||||
|
||||
sensor_catalog_rule_names = set(rule.metadata.get("sensor_catalog_names", [])) if isinstance(rule.metadata, dict) else set()
|
||||
if sensor_catalog_rule_names:
|
||||
farm_sensor_catalog_names = set(
|
||||
farm.sensors.exclude(sensor_catalog__name__isnull=True).values_list("sensor_catalog__name", flat=True)
|
||||
)
|
||||
if not farm_sensor_catalog_names or sensor_catalog_rule_names.isdisjoint(farm_sensor_catalog_names):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def build_farm_access_profile(farm):
|
||||
features = AccessFeature.objects.all().order_by("feature_type", "code")
|
||||
resolved = {
|
||||
feature.code: {
|
||||
"enabled": feature.default_enabled,
|
||||
"type": feature.feature_type,
|
||||
"name": feature.name,
|
||||
"description": feature.description,
|
||||
"metadata": feature.metadata,
|
||||
"source": "default",
|
||||
}
|
||||
for feature in features
|
||||
}
|
||||
|
||||
product_ids = set(farm.products.values_list("id", flat=True))
|
||||
sensor_catalog_ids = set(farm.sensors.exclude(sensor_catalog_id__isnull=True).values_list("sensor_catalog_id", flat=True))
|
||||
|
||||
rules = (
|
||||
AccessRule.objects.filter(is_active=True, features__isnull=False)
|
||||
.distinct()
|
||||
.prefetch_related("features", "subscription_plans", "farm_types", "products", "sensor_catalogs")
|
||||
.order_by("priority", "id")
|
||||
)
|
||||
|
||||
matched_rules = []
|
||||
for rule in rules:
|
||||
if not rule_matches_farm(rule, farm, product_ids=product_ids, sensor_catalog_ids=sensor_catalog_ids):
|
||||
continue
|
||||
|
||||
matched_rules.append(
|
||||
{
|
||||
"code": rule.code,
|
||||
"name": rule.name,
|
||||
"effect": rule.effect,
|
||||
"priority": rule.priority,
|
||||
}
|
||||
)
|
||||
is_enabled = rule.effect == AccessRule.ALLOW
|
||||
for feature in rule.features.all():
|
||||
resolved[feature.code] = {
|
||||
"enabled": is_enabled,
|
||||
"type": feature.feature_type,
|
||||
"name": feature.name,
|
||||
"description": feature.description,
|
||||
"metadata": feature.metadata,
|
||||
"source": rule.code,
|
||||
}
|
||||
|
||||
grouped = {}
|
||||
for code, payload in resolved.items():
|
||||
grouped.setdefault(f"{payload['type']}s", {})[code] = payload
|
||||
|
||||
profile, _created = FarmAccessProfile.objects.update_or_create(
|
||||
farm=farm,
|
||||
defaults={
|
||||
"cached_features": resolved,
|
||||
"cached_groups": grouped,
|
||||
"matched_rules": matched_rules,
|
||||
"last_resolved_at": timezone.now(),
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
"farm_uuid": str(farm.farm_uuid),
|
||||
"subscription_plan": {
|
||||
"uuid": str(farm.subscription_plan.uuid),
|
||||
"code": farm.subscription_plan.code,
|
||||
"name": farm.subscription_plan.name,
|
||||
}
|
||||
if farm.subscription_plan_id
|
||||
else None,
|
||||
"features": profile.cached_features,
|
||||
"groups": profile.cached_groups,
|
||||
"matched_rules": profile.matched_rules,
|
||||
"resolved_from_profile": True,
|
||||
}
|
||||
|
||||
|
||||
def is_feature_enabled_for_farm(farm, feature_code):
|
||||
profile = getattr(farm, "access_profile", None)
|
||||
if profile and isinstance(profile.cached_features, dict):
|
||||
feature_payload = profile.cached_features.get(feature_code)
|
||||
if feature_payload is not None:
|
||||
return bool(feature_payload.get("enabled"))
|
||||
|
||||
profile_data = build_farm_access_profile(farm)
|
||||
feature_payload = profile_data["features"].get(feature_code)
|
||||
if feature_payload is None:
|
||||
return False
|
||||
return bool(feature_payload.get("enabled"))
|
||||
Reference in New Issue
Block a user