From da9b559752bee70a157a66f973f9da8ed3284f79 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 16 Feb 2026 14:53:36 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20add=20basic=20support=20for=20WuttaFarm?= =?UTF-8?q?=20=E2=86=92=20farmOS=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit typical CLI export tool, but also the export happens automatically when create or edit of record happens in wuttafarm supported models: - AnimalType - AnimalAsset - GroupAsset - LandAsset - StructureAsset --- pyproject.toml | 1 + src/wuttafarm/app.py | 32 ++ src/wuttafarm/cli/__init__.py | 1 + src/wuttafarm/cli/export_farmos.py | 41 +++ src/wuttafarm/emails.py | 6 + src/wuttafarm/farmos/importing/__init__.py | 26 ++ src/wuttafarm/farmos/importing/model.py | 365 ++++++++++++++++++++ src/wuttafarm/farmos/importing/wuttafarm.py | 263 ++++++++++++++ src/wuttafarm/web/views/animals.py | 5 +- src/wuttafarm/web/views/assets.py | 10 + src/wuttafarm/web/views/common.py | 10 + src/wuttafarm/web/views/land.py | 5 +- src/wuttafarm/web/views/master.py | 4 + src/wuttafarm/web/views/structures.py | 5 +- 14 files changed, 765 insertions(+), 9 deletions(-) create mode 100644 src/wuttafarm/cli/export_farmos.py create mode 100644 src/wuttafarm/farmos/importing/__init__.py create mode 100644 src/wuttafarm/farmos/importing/model.py create mode 100644 src/wuttafarm/farmos/importing/wuttafarm.py diff --git a/pyproject.toml b/pyproject.toml index 073879b..51dcb61 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ wuttafarm = "wuttafarm.app:WuttaFarmAppProvider" "wuttafarm" = "wuttafarm.web.menus:WuttaFarmMenuHandler" [project.entry-points."wuttasync.importing"] +"export.to_farmos.from_wuttafarm" = "wuttafarm.farmos.importing.wuttafarm:FromWuttaFarmToFarmOS" "import.to_wuttafarm.from_farmos" = "wuttafarm.importing.farmos:FromFarmOSToWuttaFarm" diff --git a/src/wuttafarm/app.py b/src/wuttafarm/app.py index 72e9f00..087c48a 100644 --- a/src/wuttafarm/app.py +++ b/src/wuttafarm/app.py @@ -85,6 +85,38 @@ class WuttaFarmAppHandler(base.AppHandler): handler = self.get_farmos_handler() return handler.is_farmos_4x(*args, **kwargs) + def export_to_farmos(self, obj, require=True): + """ + Export the given object to farmOS, using configured handler. + + This should ensure the given object is also *updated* with the + farmOS UUID and Drupal ID, when new record is created in + farmOS. + + :param obj: Any data object in WuttaFarm, e.g. AnimalAsset + instance. + + :param require: If true, this will *require* the export + handler to support objects of the given type. If false, + then nothing will happen / export is silently skipped when + there is no such exporter. + """ + handler = self.app.get_import_handler("export.to_farmos.from_wuttafarm") + + model_name = type(obj).__name__ + if model_name not in handler.importers: + if require: + raise ValueError(f"no exporter found for {model_name}") + return + + # nb. begin txn to establish the API client + # TODO: should probably use current user oauth2 token instead + # of always making a new one here, which is what happens IIUC + handler.begin_target_transaction() + importer = handler.get_importer(model_name, caches_target=False) + normal = importer.normalize_source_object(obj) + importer.process_data(source_data=[normal]) + class WuttaFarmAppProvider(base.AppProvider): """ diff --git a/src/wuttafarm/cli/__init__.py b/src/wuttafarm/cli/__init__.py index 7f6c2bb..cd06344 100644 --- a/src/wuttafarm/cli/__init__.py +++ b/src/wuttafarm/cli/__init__.py @@ -26,5 +26,6 @@ WuttaFarm CLI from .base import wuttafarm_typer # nb. must bring in all modules for discovery to work +from . import export_farmos from . import import_farmos from . import install diff --git a/src/wuttafarm/cli/export_farmos.py b/src/wuttafarm/cli/export_farmos.py new file mode 100644 index 0000000..18a21dd --- /dev/null +++ b/src/wuttafarm/cli/export_farmos.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaFarm --Web app to integrate with and extend farmOS +# Copyright © 2026 Lance Edgar +# +# This file is part of WuttaFarm. +# +# WuttaFarm is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaFarm. If not, see . +# +################################################################################ +""" +See also: :ref:`wuttafarm-export-farmos` +""" + +import typer + +from wuttasync.cli import import_command, ImportCommandHandler + +from wuttafarm.cli import wuttafarm_typer + + +@wuttafarm_typer.command() +@import_command +def export_farmos(ctx: typer.Context, **kwargs): + """ + Export data from WuttaFarm to farmOS API + """ + config = ctx.parent.wutta_config + handler = ImportCommandHandler(config, key="export.to_farmos.from_wuttafarm") + handler.run(ctx) diff --git a/src/wuttafarm/emails.py b/src/wuttafarm/emails.py index 55b1612..05416ab 100644 --- a/src/wuttafarm/emails.py +++ b/src/wuttafarm/emails.py @@ -26,6 +26,12 @@ Email sending config for WuttaFarm from wuttasync.emails import ImportExportWarning +class export_to_farmos_from_wuttafarm_warning(ImportExportWarning): + """ + Diff warning for WuttaFarm → farmOS export. + """ + + class import_to_wuttafarm_from_farmos_warning(ImportExportWarning): """ Diff warning for farmOS → WuttaFarm import. diff --git a/src/wuttafarm/farmos/importing/__init__.py b/src/wuttafarm/farmos/importing/__init__.py new file mode 100644 index 0000000..a4b17eb --- /dev/null +++ b/src/wuttafarm/farmos/importing/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaFarm --Web app to integrate with and extend farmOS +# Copyright © 2026 Lance Edgar +# +# This file is part of WuttaFarm. +# +# WuttaFarm is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaFarm. If not, see . +# +################################################################################ +""" +Importing data *into* farmOS +""" + +from . import model diff --git a/src/wuttafarm/farmos/importing/model.py b/src/wuttafarm/farmos/importing/model.py new file mode 100644 index 0000000..6c3f5a0 --- /dev/null +++ b/src/wuttafarm/farmos/importing/model.py @@ -0,0 +1,365 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaFarm --Web app to integrate with and extend farmOS +# Copyright © 2026 Lance Edgar +# +# This file is part of WuttaFarm. +# +# WuttaFarm is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaFarm. If not, see . +# +################################################################################ +""" +Importer models targeting farmOS +""" + +import datetime +from uuid import UUID + +import requests + +from wuttasync.importing import Importer + + +class ToFarmOS(Importer): + """ + Base class for data importer targeting the farmOS API. + """ + + key = "uuid" + caches_target = True + + def format_datetime(self, dt): + """ + Convert a WuttaFarm datetime object to the format required for + pushing to the farmOS API. + """ + if dt is None: + return None + dt = self.app.localtime(dt) + return dt.timestamp() + + def normalize_datetime(self, dt): + """ + Convert a farmOS datetime value to naive UTC used by + WuttaFarm. + + :param dt: Date/time string value "as-is" from the farmOS API. + + :returns: Equivalent naive UTC ``datetime`` + """ + if dt is None: + return None + dt = datetime.datetime.fromisoformat(dt) + return self.app.make_utc(dt) + + +class ToFarmOSAsset(ToFarmOS): + """ + Base class for asset data importer targeting the farmOS API. + """ + + farmos_asset_type = None + + def get_target_objects(self, **kwargs): + assets = self.farmos_client.asset.get(self.farmos_asset_type) + return assets["data"] + + def get_target_object(self, key): + + # fetch from cache, if applicable + if self.caches_target: + return super().get_target_object(key) + + # okay now must fetch via API + if self.get_keys() != ["uuid"]: + raise ValueError("must use uuid key for this to work") + uuid = key[0] + + try: + asset = self.farmos_client.asset.get_id(self.farmos_asset_type, str(uuid)) + except requests.HTTPError as exc: + if exc.response.status_code == 404: + return None + raise + return asset["data"] + + def create_target_object(self, key, source_data): + if source_data.get("__ignoreme__"): + return None + if self.dry_run: + return source_data + + payload = self.get_asset_payload(source_data) + result = self.farmos_client.asset.send(self.farmos_asset_type, payload) + normal = self.normalize_target_object(result["data"]) + normal["_new_object"] = result["data"] + return normal + + def update_target_object(self, asset, source_data, target_data=None): + if self.dry_run: + return asset + + payload = self.get_asset_payload(source_data) + payload["id"] = str(source_data["uuid"]) + result = self.farmos_client.asset.send(self.farmos_asset_type, payload) + return self.normalize_target_object(result["data"]) + + def normalize_target_object(self, asset): + + if notes := asset["attributes"]["notes"]: + notes = notes["value"] + + return { + "uuid": UUID(asset["id"]), + "asset_name": asset["attributes"]["name"], + "is_location": asset["attributes"]["is_location"], + "is_fixed": asset["attributes"]["is_fixed"], + "notes": notes, + "archived": asset["attributes"]["archived"], + } + + def get_asset_payload(self, source_data): + + attrs = {} + if "asset_name" in self.fields: + attrs["name"] = source_data["asset_name"] + if "is_location" in self.fields: + attrs["is_location"] = source_data["is_location"] + if "is_fixed" in self.fields: + attrs["is_fixed"] = source_data["is_fixed"] + if "notes" in self.fields: + attrs["notes"] = {"value": source_data["notes"]} + if "archived" in self.fields: + attrs["archived"] = source_data["archived"] + + payload = {"attributes": attrs} + + return payload + + +class AnimalAssetImporter(ToFarmOSAsset): + + model_title = "AnimalAsset" + farmos_asset_type = "animal" + + supported_fields = [ + "uuid", + "asset_name", + "animal_type_uuid", + "sex", + "is_sterile", + "birthdate", + "notes", + "archived", + ] + + def normalize_target_object(self, animal): + data = super().normalize_target_object(animal) + data.update( + { + "animal_type_uuid": UUID( + animal["relationships"]["animal_type"]["data"]["id"] + ), + "sex": animal["attributes"]["sex"], + "is_sterile": animal["attributes"]["is_sterile"], + "birthdate": self.normalize_datetime(animal["attributes"]["birthdate"]), + } + ) + return data + + def get_asset_payload(self, source_data): + payload = super().get_asset_payload(source_data) + + attrs = {} + if "sex" in self.fields: + attrs["sex"] = source_data["sex"] + if "is_sterile" in self.fields: + attrs["is_sterile"] = source_data["is_sterile"] + if "birthdate" in self.fields: + attrs["birthdate"] = self.format_datetime(source_data["birthdate"]) + + rels = {} + if "animal_type_uuid" in self.fields: + rels["animal_type"] = { + "data": { + "id": str(source_data["animal_type_uuid"]), + "type": "taxonomy_term--animal_type", + } + } + + payload["attributes"].update(attrs) + if rels: + payload.setdefault("relationships", {}).update(rels) + + return payload + + +class AnimalTypeImporter(ToFarmOS): + + model_title = "AnimalType" + + supported_fields = [ + "uuid", + "name", + ] + + def get_target_objects(self, **kwargs): + result = self.farmos_client.resource.get("taxonomy_term", "animal_type") + return result["data"] + + def get_target_object(self, key): + + # fetch from cache, if applicable + if self.caches_target: + return super().get_target_object(key) + + # okay now must fetch via API + if self.get_keys() != ["uuid"]: + raise ValueError("must use uuid key for this to work") + uuid = key[0] + + try: + result = self.farmos_client.resource.get_id( + "taxonomy_term", "animal_type", str(uuid) + ) + except requests.HTTPError as exc: + if exc.response.status_code == 404: + return None + raise + return result["data"] + + def normalize_target_object(self, obj): + return { + "uuid": UUID(obj["id"]), + "name": obj["attributes"]["name"], + } + + def get_type_payload(self, source_data): + return { + "attributes": { + "name": source_data["name"], + } + } + + def create_target_object(self, key, source_data): + if source_data.get("__ignoreme__"): + return None + if self.dry_run: + return source_data + + payload = self.get_type_payload(source_data) + result = self.farmos_client.resource.send( + "taxonomy_term", "animal_type", payload + ) + normal = self.normalize_target_object(result["data"]) + normal["_new_object"] = result["data"] + return normal + + def update_target_object(self, asset, source_data, target_data=None): + if self.dry_run: + return asset + + payload = self.get_type_payload(source_data) + payload["id"] = str(source_data["uuid"]) + result = self.farmos_client.resource.send( + "taxonomy_term", "animal_type", payload + ) + return self.normalize_target_object(result["data"]) + + +class GroupAssetImporter(ToFarmOSAsset): + + model_title = "GroupAsset" + farmos_asset_type = "group" + + supported_fields = [ + "uuid", + "asset_name", + "notes", + "archived", + ] + + +class LandAssetImporter(ToFarmOSAsset): + + model_title = "LandAsset" + farmos_asset_type = "land" + + supported_fields = [ + "uuid", + "asset_name", + "land_type_id", + "is_location", + "is_fixed", + "notes", + "archived", + ] + + def normalize_target_object(self, land): + data = super().normalize_target_object(land) + data.update( + { + "land_type_id": land["attributes"]["land_type"], + } + ) + return data + + def get_asset_payload(self, source_data): + payload = super().get_asset_payload(source_data) + + attrs = {} + if "land_type_id" in self.fields: + attrs["land_type"] = source_data["land_type_id"] + + if attrs: + payload["attributes"].update(attrs) + + return payload + + +class StructureAssetImporter(ToFarmOSAsset): + + model_title = "StructureAsset" + farmos_asset_type = "structure" + + supported_fields = [ + "uuid", + "asset_name", + "structure_type_id", + "is_location", + "is_fixed", + "notes", + "archived", + ] + + def normalize_target_object(self, structure): + data = super().normalize_target_object(structure) + data.update( + { + "structure_type_id": structure["attributes"]["structure_type"], + } + ) + return data + + def get_asset_payload(self, source_data): + payload = super().get_asset_payload(source_data) + + attrs = {} + if "structure_type_id" in self.fields: + attrs["structure_type"] = source_data["structure_type_id"] + + if attrs: + payload["attributes"].update(attrs) + + return payload diff --git a/src/wuttafarm/farmos/importing/wuttafarm.py b/src/wuttafarm/farmos/importing/wuttafarm.py new file mode 100644 index 0000000..8ef8a77 --- /dev/null +++ b/src/wuttafarm/farmos/importing/wuttafarm.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaFarm --Web app to integrate with and extend farmOS +# Copyright © 2026 Lance Edgar +# +# This file is part of WuttaFarm. +# +# WuttaFarm is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaFarm. If not, see . +# +################################################################################ +""" +WuttaFarm → farmOS data export +""" + +from oauthlib.oauth2 import BackendApplicationClient +from requests_oauthlib import OAuth2Session + +from wuttasync.importing import ImportHandler, FromWuttaHandler, FromWutta, Orientation + +from wuttafarm.db import model +from wuttafarm.farmos import importing as farmos_importing + + +class FromWuttaFarmHandler(FromWuttaHandler): + """ + Base class for import handler targeting WuttaFarm + """ + + source_key = "wuttafarm" + + +class ToFarmOSHandler(ImportHandler): + """ + Base class for export handlers using CSV file(s) as data target. + """ + + target_key = "farmos" + generic_target_title = "farmOS" + + # TODO: a lot of duplication to cleanup here; see FromFarmOSHandler + + def begin_target_transaction(self): + """ + Establish the farmOS API client. + """ + token = self.get_farmos_oauth2_token() + self.farmos_client = self.app.get_farmos_client(token=token) + self.farmos_4x = self.app.is_farmos_4x(self.farmos_client) + + def get_farmos_oauth2_token(self): + + client_id = self.config.get( + "farmos.oauth2.importing.client_id", default="wuttafarm" + ) + client_secret = self.config.require("farmos.oauth2.importing.client_secret") + scope = self.config.get("farmos.oauth2.importing.scope", default="farm_manager") + + client = BackendApplicationClient(client_id=client_id) + oauth = OAuth2Session(client=client) + + return oauth.fetch_token( + token_url=self.app.get_farmos_url("/oauth/token"), + include_client_id=True, + client_secret=client_secret, + scope=scope, + ) + + def get_importer_kwargs(self, key, **kwargs): + kwargs = super().get_importer_kwargs(key, **kwargs) + kwargs["farmos_client"] = self.farmos_client + kwargs["farmos_4x"] = self.farmos_4x + return kwargs + + +class FromWuttaFarmToFarmOS(FromWuttaFarmHandler, ToFarmOSHandler): + """ + Handler for WuttaFarm → farmOS API export. + """ + + orientation = Orientation.EXPORT + + def define_importers(self): + """ """ + importers = super().define_importers() + importers["LandAsset"] = LandAssetImporter + importers["StructureAsset"] = StructureAssetImporter + importers["AnimalType"] = AnimalTypeImporter + importers["AnimalAsset"] = AnimalAssetImporter + importers["GroupAsset"] = GroupAssetImporter + return importers + + +class FromWuttaFarm(FromWutta): + + drupal_internal_id_field = "drupal_internal__id" + + def create_target_object(self, key, source_data): + obj = super().create_target_object(key, source_data) + if obj is None: + return None + + if not self.dry_run: + + # set farmOS, Drupal key fields in WuttaFarm + api_object = obj["_new_object"] + wf_object = source_data["_src_object"] + wf_object.farmos_uuid = obj["uuid"] + wf_object.drupal_id = api_object["attributes"][ + self.drupal_internal_id_field + ] + + return obj + + +class AnimalAssetImporter(FromWuttaFarm, farmos_importing.model.AnimalAssetImporter): + """ + WuttaFarm → farmOS API exporter for Animal Assets + """ + + source_model_class = model.AnimalAsset + + supported_fields = [ + "uuid", + "asset_name", + "animal_type_uuid", + "sex", + "is_sterile", + "birthdate", + "notes", + "archived", + ] + + def normalize_source_object(self, animal): + return { + "uuid": animal.farmos_uuid or self.app.make_true_uuid(), + "asset_name": animal.asset_name, + "animal_type_uuid": animal.animal_type.farmos_uuid, + "sex": animal.sex, + "is_sterile": animal.is_sterile, + "birthdate": animal.birthdate, + "notes": animal.notes, + "archived": animal.archived, + "_src_object": animal, + } + + +class AnimalTypeImporter(FromWuttaFarm, farmos_importing.model.AnimalTypeImporter): + """ + WuttaFarm → farmOS API exporter for Animal Types + """ + + source_model_class = model.AnimalType + + supported_fields = [ + "uuid", + "name", + ] + + drupal_internal_id_field = "drupal_internal__tid" + + def normalize_source_object(self, animal_type): + return { + "uuid": animal_type.farmos_uuid or self.app.make_true_uuid(), + "name": animal_type.name, + "_src_object": animal_type, + } + + +class GroupAssetImporter(FromWuttaFarm, farmos_importing.model.GroupAssetImporter): + """ + WuttaFarm → farmOS API exporter for Group Assets + """ + + source_model_class = model.GroupAsset + + supported_fields = [ + "uuid", + "asset_name", + "notes", + "archived", + ] + + def normalize_source_object(self, group): + return { + "uuid": group.farmos_uuid or self.app.make_true_uuid(), + "asset_name": group.asset_name, + "notes": group.notes, + "archived": group.archived, + "_src_object": group, + } + + +class LandAssetImporter(FromWuttaFarm, farmos_importing.model.LandAssetImporter): + """ + WuttaFarm → farmOS API exporter for Land Assets + """ + + source_model_class = model.LandAsset + + supported_fields = [ + "uuid", + "asset_name", + "land_type_id", + "is_location", + "is_fixed", + "notes", + "archived", + ] + + def normalize_source_object(self, land): + return { + "uuid": land.farmos_uuid or self.app.make_true_uuid(), + "asset_name": land.asset_name, + "land_type_id": land.land_type.drupal_id, + "is_location": land.is_location, + "is_fixed": land.is_fixed, + "notes": land.notes, + "archived": land.archived, + "_src_object": land, + } + + +class StructureAssetImporter( + FromWuttaFarm, farmos_importing.model.StructureAssetImporter +): + """ + WuttaFarm → farmOS API exporter for Structure Assets + """ + + source_model_class = model.StructureAsset + + supported_fields = [ + "uuid", + "asset_name", + "structure_type_id", + "is_location", + "is_fixed", + "notes", + "archived", + ] + + def normalize_source_object(self, structure): + return { + "uuid": structure.farmos_uuid or self.app.make_true_uuid(), + "asset_name": structure.asset_name, + "structure_type_id": structure.structure_type.drupal_id, + "is_location": structure.is_location, + "is_fixed": structure.is_fixed, + "notes": structure.notes, + "archived": structure.archived, + "_src_object": structure, + } diff --git a/src/wuttafarm/web/views/animals.py b/src/wuttafarm/web/views/animals.py index fbad4ce..bae7dde 100644 --- a/src/wuttafarm/web/views/animals.py +++ b/src/wuttafarm/web/views/animals.py @@ -26,13 +26,12 @@ Master view for Animals from wuttaweb.forms.schema import WuttaDictEnum from wuttafarm.db.model import AnimalType, AnimalAsset -from wuttafarm.web.views import WuttaFarmMasterView -from wuttafarm.web.views.assets import AssetMasterView +from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView from wuttafarm.web.forms.schema import AnimalTypeRef from wuttafarm.web.forms.widgets import ImageWidget -class AnimalTypeView(WuttaFarmMasterView): +class AnimalTypeView(AssetTypeMasterView): """ Master view for Animal Types """ diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index f0ebefe..dffaae7 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -118,6 +118,16 @@ class AssetView(WuttaFarmMasterView): return None +class AssetTypeMasterView(WuttaFarmMasterView): + """ + Base class for "Asset Type" master views. + + A bit of a misnmer perhaps, this is *not* for the actual AssetType + model, but rather the "secondary" types, e.g. AnimalType, + LandType etc. + """ + + class AssetMasterView(WuttaFarmMasterView): """ Base class for Asset master views diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py index 8b030f5..121e631 100644 --- a/src/wuttafarm/web/views/common.py +++ b/src/wuttafarm/web/views/common.py @@ -54,9 +54,13 @@ class CommonView(base.CommonView): "activity_logs.list", "activity_logs.view", "activity_logs.versions", + "animal_types.create", + "animal_types.edit", "animal_types.list", "animal_types.view", "animal_types.versions", + "animal_assets.create", + "animal_assets.edit", "animal_assets.list", "animal_assets.view", "animal_assets.versions", @@ -86,9 +90,13 @@ class CommonView(base.CommonView): "farmos_structures.view", "farmos_users.list", "farmos_users.view", + "group_asests.create", + "group_asests.edit", "group_asests.list", "group_asests.view", "group_asests.versions", + "land_assets.create", + "land_assets.edit", "land_assets.list", "land_assets.view", "land_assets.versions", @@ -101,6 +109,8 @@ class CommonView(base.CommonView): "structure_types.list", "structure_types.view", "structure_types.versions", + "structure_assets.create", + "structure_assets.edit", "structure_assets.list", "structure_assets.view", "structure_assets.versions", diff --git a/src/wuttafarm/web/views/land.py b/src/wuttafarm/web/views/land.py index ce577c9..aad15e7 100644 --- a/src/wuttafarm/web/views/land.py +++ b/src/wuttafarm/web/views/land.py @@ -26,12 +26,11 @@ Master view for Land Types from webhelpers2.html import HTML, tags from wuttafarm.db.model.land import LandType, LandAsset -from wuttafarm.web.views import WuttaFarmMasterView -from wuttafarm.web.views.assets import AssetMasterView +from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView from wuttafarm.web.forms.schema import LandTypeRef -class LandTypeView(WuttaFarmMasterView): +class LandTypeView(AssetTypeMasterView): """ Master view for Land Types """ diff --git a/src/wuttafarm/web/views/master.py b/src/wuttafarm/web/views/master.py index 98856f6..0e25a30 100644 --- a/src/wuttafarm/web/views/master.py +++ b/src/wuttafarm/web/views/master.py @@ -96,3 +96,7 @@ class WuttaFarmMasterView(MasterView): f.remove("drupal_id") else: f.set_readonly("drupal_id") + + def persist(self, obj, session=None): + super().persist(obj, session) + self.app.export_to_farmos(obj, require=False) diff --git a/src/wuttafarm/web/views/structures.py b/src/wuttafarm/web/views/structures.py index 5745658..aa9bf31 100644 --- a/src/wuttafarm/web/views/structures.py +++ b/src/wuttafarm/web/views/structures.py @@ -23,14 +23,13 @@ Master view for Structures """ -from wuttafarm.web.views import WuttaFarmMasterView -from wuttafarm.web.views.assets import AssetMasterView +from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView from wuttafarm.db.model import StructureType, StructureAsset from wuttafarm.web.forms.schema import StructureTypeRef from wuttafarm.web.forms.widgets import ImageWidget -class StructureTypeView(WuttaFarmMasterView): +class StructureTypeView(AssetTypeMasterView): """ Master view for Structure Types """