From a5d7f89fcb3df02bd2061b1e8d23c371040be671 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 26 Feb 2026 19:10:41 -0600 Subject: [PATCH] feat: improve mirror/deletion for assets, logs, animal types --- src/wuttafarm/db/model/asset.py | 7 +++- src/wuttafarm/db/model/log.py | 7 +++- src/wuttafarm/web/views/animals.py | 3 ++ src/wuttafarm/web/views/assets.py | 18 +-------- src/wuttafarm/web/views/groups.py | 1 + src/wuttafarm/web/views/land.py | 1 + src/wuttafarm/web/views/logs.py | 2 + src/wuttafarm/web/views/logs_activity.py | 1 + src/wuttafarm/web/views/logs_harvest.py | 1 + src/wuttafarm/web/views/logs_medical.py | 1 + src/wuttafarm/web/views/logs_observation.py | 1 + src/wuttafarm/web/views/master.py | 44 +++++++++++++++++++-- src/wuttafarm/web/views/plants.py | 1 + src/wuttafarm/web/views/structures.py | 1 + src/wuttafarm/web/views/units.py | 2 + 15 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/wuttafarm/db/model/asset.py b/src/wuttafarm/db/model/asset.py index 90372e2..3e4de6e 100644 --- a/src/wuttafarm/db/model/asset.py +++ b/src/wuttafarm/db/model/asset.py @@ -196,7 +196,12 @@ class AssetMixin: @declared_attr def asset(cls): - return orm.relationship(Asset) + return orm.relationship( + Asset, + single_parent=True, + cascade="all, delete-orphan", + cascade_backrefs=False, + ) def __str__(self): return self.asset_name or "" diff --git a/src/wuttafarm/db/model/log.py b/src/wuttafarm/db/model/log.py index a86c447..fd59478 100644 --- a/src/wuttafarm/db/model/log.py +++ b/src/wuttafarm/db/model/log.py @@ -165,7 +165,12 @@ class LogMixin: @declared_attr def log(cls): - return orm.relationship(Log) + return orm.relationship( + Log, + single_parent=True, + cascade="all, delete-orphan", + cascade_backrefs=False, + ) def __str__(self): return self.message or "" diff --git a/src/wuttafarm/web/views/animals.py b/src/wuttafarm/web/views/animals.py index 8f05584..241f1bb 100644 --- a/src/wuttafarm/web/views/animals.py +++ b/src/wuttafarm/web/views/animals.py @@ -42,6 +42,8 @@ class AnimalTypeView(AssetTypeMasterView): route_prefix = "animal_types" url_prefix = "/animal-types" + farmos_entity_type = "taxonomy_term" + farmos_bundle = "animal_type" farmos_refurl_path = "/admin/structure/taxonomy/manage/animal_type/overview" grid_columns = [ @@ -145,6 +147,7 @@ class AnimalAssetView(AssetMasterView): url_prefix = "/assets/animal" farmos_refurl_path = "/assets/animal" + farmos_bundle = "animal" labels = { "animal_type": "Species/Breed", diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index 2dade09..f5046f9 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -135,6 +135,8 @@ class AssetMasterView(WuttaFarmMasterView): Base class for Asset master views """ + farmos_entity_type = "asset" + sort_defaults = "asset_name" filter_defaults = { @@ -276,22 +278,6 @@ class AssetMasterView(WuttaFarmMasterView): model_class = self.get_model_class() return model_class.__wutta_hint__["farmos_asset_type"] - def delete_instance(self, obj): - - # save farmOS UUID if we need it - farmos_uuid = None - if self.app.is_farmos_mirror() and hasattr(obj, "farmos_uuid"): - farmos_uuid = obj.farmos_uuid - - # delete per usual - super().delete_instance(obj) - - # maybe delete from farmOS also - if farmos_uuid: - client = get_farmos_client_for_user(self.request) - asset_type = self.get_asset_type() - client.asset.delete(asset_type, farmos_uuid) - def get_farmos_url(self, asset): return self.app.get_farmos_url(f"/asset/{asset.drupal_id}") diff --git a/src/wuttafarm/web/views/groups.py b/src/wuttafarm/web/views/groups.py index 4b26463..61394fa 100644 --- a/src/wuttafarm/web/views/groups.py +++ b/src/wuttafarm/web/views/groups.py @@ -37,6 +37,7 @@ class GroupView(AssetMasterView): url_prefix = "/assets/group" farmos_refurl_path = "/assets/group" + farmos_bundle = "group" grid_columns = [ "thumbnail", diff --git a/src/wuttafarm/web/views/land.py b/src/wuttafarm/web/views/land.py index 22827a0..9523cb5 100644 --- a/src/wuttafarm/web/views/land.py +++ b/src/wuttafarm/web/views/land.py @@ -139,6 +139,7 @@ class LandAssetView(AssetMasterView): route_prefix = "land_assets" url_prefix = "/assets/land" + farmos_bundle = "land" farmos_refurl_path = "/assets/land" grid_columns = [ diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py index b393cec..9dc2bd4 100644 --- a/src/wuttafarm/web/views/logs.py +++ b/src/wuttafarm/web/views/logs.py @@ -166,6 +166,8 @@ class LogMasterView(WuttaFarmMasterView): Base class for Asset master views """ + farmos_entity_type = "log" + labels = { "message": "Log Name", "owners": "Owner", diff --git a/src/wuttafarm/web/views/logs_activity.py b/src/wuttafarm/web/views/logs_activity.py index dda3ca7..19f8782 100644 --- a/src/wuttafarm/web/views/logs_activity.py +++ b/src/wuttafarm/web/views/logs_activity.py @@ -36,6 +36,7 @@ class ActivityLogView(LogMasterView): route_prefix = "logs_activity" url_prefix = "/logs/activity" + farmos_bundle = "activity" farmos_refurl_path = "/logs/activity" diff --git a/src/wuttafarm/web/views/logs_harvest.py b/src/wuttafarm/web/views/logs_harvest.py index 825c864..1c9a6f2 100644 --- a/src/wuttafarm/web/views/logs_harvest.py +++ b/src/wuttafarm/web/views/logs_harvest.py @@ -36,6 +36,7 @@ class HarvestLogView(LogMasterView): route_prefix = "logs_harvest" url_prefix = "/logs/harvest" + farmos_bundle = "harvest" farmos_refurl_path = "/logs/harvest" diff --git a/src/wuttafarm/web/views/logs_medical.py b/src/wuttafarm/web/views/logs_medical.py index d582db9..c5769e8 100644 --- a/src/wuttafarm/web/views/logs_medical.py +++ b/src/wuttafarm/web/views/logs_medical.py @@ -36,6 +36,7 @@ class MedicalLogView(LogMasterView): route_prefix = "logs_medical" url_prefix = "/logs/medical" + farmos_bundle = "medical" farmos_refurl_path = "/logs/medical" diff --git a/src/wuttafarm/web/views/logs_observation.py b/src/wuttafarm/web/views/logs_observation.py index a4b9e8e..5b190d1 100644 --- a/src/wuttafarm/web/views/logs_observation.py +++ b/src/wuttafarm/web/views/logs_observation.py @@ -36,6 +36,7 @@ class ObservationLogView(LogMasterView): route_prefix = "logs_observation" url_prefix = "/logs/observation" + farmos_bundle = "observation" farmos_refurl_path = "/logs/observation" diff --git a/src/wuttafarm/web/views/master.py b/src/wuttafarm/web/views/master.py index 6ab0631..ec3c913 100644 --- a/src/wuttafarm/web/views/master.py +++ b/src/wuttafarm/web/views/master.py @@ -27,7 +27,7 @@ from webhelpers2.html import tags from wuttaweb.views import MasterView -from wuttafarm.web.util import use_farmos_style_grid_links +from wuttafarm.web.util import use_farmos_style_grid_links, get_farmos_client_for_user class WuttaFarmMasterView(MasterView): @@ -36,6 +36,8 @@ class WuttaFarmMasterView(MasterView): """ farmos_refurl_path = None + farmos_entity_type = None + farmos_bundle = None labels = { "farmos_uuid": "farmOS UUID", @@ -104,6 +106,42 @@ class WuttaFarmMasterView(MasterView): f.set_readonly("drupal_id") def persist(self, obj, session=None): + + # save per usual super().persist(obj, session) - token = self.request.session.get("farmos.oauth2.token") - self.app.auto_sync_to_farmos(obj, token=token, require=False) + + # maybe also sync change to farmOS + if self.app.is_farmos_mirror(): + token = self.request.session.get("farmos.oauth2.token") + self.app.auto_sync_to_farmos(obj, token=token, require=False) + + def get_farmos_entity_type(self): + if self.farmos_entity_type: + return self.farmos_entity_type + raise NotImplementedError( + f"must define {self.__class__.__name__}.farmos_entity_type" + ) + + def get_farmos_bundle(self): + if self.farmos_bundle: + return self.farmos_bundle + raise NotImplementedError( + f"must define {self.__class__.__name__}.farmos_bundle" + ) + + def delete_instance(self, obj): + + # save farmOS UUID if we need it + farmos_uuid = None + if hasattr(obj, "farmos_uuid") and self.app.is_farmos_mirror(): + farmos_uuid = obj.farmos_uuid + + # delete per usual + super().delete_instance(obj) + + # maybe delete from farmOS also + if farmos_uuid: + entity_type = self.get_farmos_entity_type() + bundle = self.get_farmos_bundle() + client = get_farmos_client_for_user(self.request) + client.resource.delete(entity_type, bundle, farmos_uuid) diff --git a/src/wuttafarm/web/views/plants.py b/src/wuttafarm/web/views/plants.py index 4bd32c6..4a343a6 100644 --- a/src/wuttafarm/web/views/plants.py +++ b/src/wuttafarm/web/views/plants.py @@ -41,6 +41,7 @@ class PlantTypeView(AssetTypeMasterView): url_prefix = "/plant-types" farmos_refurl_path = "/admin/structure/taxonomy/manage/plant_type/overview" + farmos_bundle = "plant" grid_columns = [ "name", diff --git a/src/wuttafarm/web/views/structures.py b/src/wuttafarm/web/views/structures.py index aa9bf31..11a21b9 100644 --- a/src/wuttafarm/web/views/structures.py +++ b/src/wuttafarm/web/views/structures.py @@ -138,6 +138,7 @@ class StructureAssetView(AssetMasterView): route_prefix = "structure_assets" url_prefix = "/asset/structures" + farmos_bundle = "structure" farmos_refurl_path = "/assets/structure" grid_columns = [ diff --git a/src/wuttafarm/web/views/units.py b/src/wuttafarm/web/views/units.py index 3b86426..a36a238 100644 --- a/src/wuttafarm/web/views/units.py +++ b/src/wuttafarm/web/views/units.py @@ -69,6 +69,8 @@ class UnitView(WuttaFarmMasterView): route_prefix = "units" url_prefix = "/units" + farmos_entity_type = "taxonomy_term" + farmos_bundle = "unit" farmos_refurl_path = "/admin/structure/taxonomy/manage/unit/overview" grid_columns = [