From 9cfa91e091d8579e5333a410589a69b5ce068655 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 19 Feb 2026 19:56:44 -0600 Subject: [PATCH] fix: standardize a bit more for the farmOS Animal Assets view --- src/wuttafarm/web/menus.py | 8 +- src/wuttafarm/web/views/animals.py | 2 +- src/wuttafarm/web/views/assets.py | 2 +- src/wuttafarm/web/views/common.py | 4 +- src/wuttafarm/web/views/farmos/animals.py | 176 +++++----------------- src/wuttafarm/web/views/farmos/assets.py | 175 +++++++++++++++++++++ 6 files changed, 222 insertions(+), 145 deletions(-) create mode 100644 src/wuttafarm/web/views/farmos/assets.py diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py index 408fd2e..c79acec 100644 --- a/src/wuttafarm/web/menus.py +++ b/src/wuttafarm/web/menus.py @@ -202,8 +202,8 @@ class WuttaFarmMenuHandler(base.MenuHandler): {"type": "sep"}, { "title": "Animal Assets", - "route": "farmos_animals", - "perm": "farmos_animals.list", + "route": "farmos_assets_animal", + "perm": "farmos_assets_animal.list", }, { "title": "Group Assets", @@ -311,8 +311,8 @@ class WuttaFarmMenuHandler(base.MenuHandler): "items": [ { "title": "Animal", - "route": "farmos_animals", - "perm": "farmos_animals.list", + "route": "farmos_assets_animal", + "perm": "farmos_assets_animal.list", }, { "title": "Group", diff --git a/src/wuttafarm/web/views/animals.py b/src/wuttafarm/web/views/animals.py index 72a05ee..09162b2 100644 --- a/src/wuttafarm/web/views/animals.py +++ b/src/wuttafarm/web/views/animals.py @@ -153,11 +153,11 @@ class AnimalAssetView(AssetMasterView): "thumbnail", "drupal_id", "asset_name", + "produces_eggs", "animal_type", "birthdate", "is_sterile", "sex", - "produces_eggs", "archived", ] diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index b918839..85835f9 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -282,7 +282,7 @@ class AssetMasterView(WuttaFarmMasterView): # TODO route = None if asset.asset_type == "animal": - route = "farmos_animals.view" + route = "farmos_assets_animal.view" elif asset.asset_type == "group": route = "farmos_groups.view" elif asset.asset_type == "land": diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py index 44a9598..2efd5c6 100644 --- a/src/wuttafarm/web/views/common.py +++ b/src/wuttafarm/web/views/common.py @@ -67,8 +67,8 @@ class CommonView(base.CommonView): "asset_types.versions", "farmos_animal_types.list", "farmos_animal_types.view", - "farmos_animals.list", - "farmos_animals.view", + "farmos_assets_animal.list", + "farmos_assets_animal.view", "farmos_asset_types.list", "farmos_asset_types.view", "farmos_groups.list", diff --git a/src/wuttafarm/web/views/farmos/animals.py b/src/wuttafarm/web/views/farmos/animals.py index c9c2887..3a79c8c 100644 --- a/src/wuttafarm/web/views/farmos/animals.py +++ b/src/wuttafarm/web/views/farmos/animals.py @@ -30,28 +30,27 @@ import colander from wuttaweb.forms.schema import WuttaDateTime from wuttaweb.forms.widgets import WuttaDateTimeWidget -from wuttafarm.web.views.farmos import FarmOSMasterView -from wuttafarm.web.forms.schema import UsersType, AnimalTypeType, StructureType -from wuttafarm.web.forms.widgets import ImageWidget +from wuttafarm.web.views.farmos.assets import AssetMasterView +from wuttafarm.web.forms.schema import AnimalTypeType -class AnimalView(FarmOSMasterView): +class AnimalView(AssetMasterView): """ Master view for Farm Animals """ - model_name = "farmos_animal" - model_title = "farmOS Animal" - model_title_plural = "farmOS Animals" + model_name = "farmos_animal_asset" + model_title = "farmOS Animal Asset" + model_title_plural = "farmOS Animal Assets" - route_prefix = "farmos_animals" - url_prefix = "/farmOS/animals" + route_prefix = "farmos_assets_animal" + url_prefix = "/farmOS/assets/animal" + farmos_asset_type = "animal" farmos_refurl_path = "/assets/animal" labels = { "animal_type": "Species / Breed", - "location": "Current Location", } grid_columns = [ @@ -62,8 +61,6 @@ class AnimalView(FarmOSMasterView): "archived", ] - sort_defaults = "name" - form_fields = [ "name", "animal_type", @@ -80,38 +77,21 @@ class AnimalView(FarmOSMasterView): "image", ] - def get_grid_data(self, columns=None, session=None): - animals = self.farmos_client.resource.get("asset", "animal") - return [self.normalize_animal(a) for a in animals["data"]] - def configure_grid(self, grid): g = grid super().configure_grid(g) - # name - g.set_link("name") - g.set_searchable("name") - # birthdate g.set_renderer("birthdate", "date") # is_sterile g.set_renderer("is_sterile", "boolean") - # archived - g.set_renderer("archived", "boolean") - def get_instance(self): - animal = self.farmos_client.resource.get_id( - "asset", "animal", self.request.matchdict["uuid"] - ) - self.raw_json = animal + data = super().get_instance() - # instance data - data = self.normalize_animal(animal["data"]) - - if relationships := animal["data"].get("relationships"): + if relationships := self.raw_json["data"].get("relationships"): # add animal type if animal_type := relationships.get("animal_type"): @@ -124,54 +104,11 @@ class AnimalView(FarmOSMasterView): "name": animal_type["data"]["attributes"]["name"], } - # add location - if location := relationships.get("location"): - if location["data"]: - location = self.farmos_client.resource.get_id( - "asset", "structure", location["data"][0]["id"] - ) - data["location"] = { - "uuid": location["data"]["id"], - "name": location["data"]["attributes"]["name"], - } - - # add owners - if owner := relationships.get("owner"): - data["owners"] = [] - for owner_data in owner["data"]: - owner = self.farmos_client.resource.get_id( - "user", "user", owner_data["id"] - ) - data["owners"].append( - { - "uuid": owner["data"]["id"], - "display_name": owner["data"]["attributes"]["display_name"], - } - ) - - # add image urls - if image := relationships.get("image"): - if image["data"]: - image = self.farmos_client.resource.get_id( - "file", "file", image["data"][0]["id"] - ) - data["raw_image_url"] = self.app.get_farmos_url( - image["data"]["attributes"]["uri"]["url"] - ) - # nb. other styles available: medium, wide - data["large_image_url"] = image["data"]["attributes"][ - "image_style_uri" - ]["large"] - data["thumbnail_image_url"] = image["data"]["attributes"][ - "image_style_uri" - ]["thumbnail"] - return data - def get_instance_title(self, animal): - return animal["name"] + def normalize_asset(self, animal): - def normalize_animal(self, animal): + normal = super().normalize_asset(animal) birthdate = animal["attributes"]["birthdate"] if birthdate: @@ -184,30 +121,19 @@ class AnimalView(FarmOSMasterView): else: sterile = animal["attributes"]["is_castrated"] - if notes := animal["attributes"]["notes"]: - notes = notes["value"] + normal.update( + { + "birthdate": birthdate, + "sex": animal["attributes"]["sex"] or colander.null, + "is_sterile": sterile, + } + ) - if self.farmos_4x: - archived = animal["attributes"]["archived"] - else: - archived = animal["attributes"]["status"] == "archived" - - return { - "uuid": animal["id"], - "drupal_id": animal["attributes"]["drupal_internal__id"], - "name": animal["attributes"]["name"], - "birthdate": birthdate, - "sex": animal["attributes"]["sex"] or colander.null, - "is_sterile": sterile, - "location": colander.null, # TODO - "archived": archived, - "notes": notes or colander.null, - } + return normal def configure_form(self, form): f = form super().configure_form(f) - animal = f.model_instance # animal_type f.set_node("animal_type", AnimalTypeType(self.request)) @@ -219,52 +145,28 @@ class AnimalView(FarmOSMasterView): # is_sterile f.set_node("is_sterile", colander.Boolean()) - # location - f.set_node("location", StructureType(self.request)) - - # owners - f.set_node("owners", UsersType(self.request)) - - # notes - f.set_widget("notes", "notes") - - # archived - f.set_node("archived", colander.Boolean()) - - # image - if url := animal.get("large_image_url"): - f.set_widget("image", ImageWidget("animal image")) - f.set_default("image", url) - def get_xref_buttons(self, animal): - model = self.app.model - session = self.Session() + buttons = super().get_xref_buttons(animal) - buttons = [ - self.make_button( - "View in farmOS", - primary=True, - url=self.app.get_farmos_url(f"/asset/{animal['drupal_id']}"), - target="_blank", - icon_left="external-link-alt", - ), - ] + if self.app.is_farmos_mirror(): + model = self.app.model + session = self.Session() - if wf_animal := ( - session.query(model.Asset) - .filter(model.Asset.farmos_uuid == animal["uuid"]) - .first() - ): - buttons.append( - self.make_button( - f"View {self.app.get_title()} record", - primary=True, - url=self.request.route_url( - "animal_assets.view", uuid=wf_animal.uuid - ), - icon_left="eye", + if wf_animal := ( + session.query(model.Asset) + .filter(model.Asset.farmos_uuid == animal["uuid"]) + .first() + ): + buttons.append( + self.make_button( + f"View {self.app.get_title()} record", + primary=True, + url=self.request.route_url( + "animal_assets.view", uuid=wf_animal.uuid + ), + icon_left="eye", + ) ) - ) return buttons diff --git a/src/wuttafarm/web/views/farmos/assets.py b/src/wuttafarm/web/views/farmos/assets.py new file mode 100644 index 0000000..31f21c9 --- /dev/null +++ b/src/wuttafarm/web/views/farmos/assets.py @@ -0,0 +1,175 @@ +# -*- 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 . +# +################################################################################ +""" +Base class for Asset master views +""" + +import colander + +from wuttafarm.web.views.farmos import FarmOSMasterView +from wuttafarm.web.forms.schema import UsersType, StructureType +from wuttafarm.web.forms.widgets import ImageWidget + + +class AssetMasterView(FarmOSMasterView): + """ + Base class for Asset master views + """ + + farmos_asset_type = None + + labels = { + "name": "Asset Name", + "location": "Current Location", + } + + grid_columns = [ + "name", + "archived", + ] + + sort_defaults = "name" + + def get_grid_data(self, columns=None, session=None): + result = self.farmos_client.asset.get(self.farmos_asset_type) + return [self.normalize_asset(a) for a in result["data"]] + + def configure_grid(self, grid): + g = grid + super().configure_grid(g) + + # name + g.set_link("name") + g.set_searchable("name") + + # archived + g.set_renderer("archived", "boolean") + + def get_instance(self): + asset = self.farmos_client.resource.get_id( + "asset", self.farmos_asset_type, self.request.matchdict["uuid"] + ) + self.raw_json = asset + + # instance data + data = self.normalize_asset(asset["data"]) + + if relationships := asset["data"].get("relationships"): + + # add location + if location := relationships.get("location"): + if location["data"]: + location = self.farmos_client.resource.get_id( + "asset", "structure", location["data"][0]["id"] + ) + data["location"] = { + "uuid": location["data"]["id"], + "name": location["data"]["attributes"]["name"], + } + + # add owners + if owner := relationships.get("owner"): + data["owners"] = [] + for owner_data in owner["data"]: + owner = self.farmos_client.resource.get_id( + "user", "user", owner_data["id"] + ) + data["owners"].append( + { + "uuid": owner["data"]["id"], + "display_name": owner["data"]["attributes"]["display_name"], + } + ) + + # add image urls + if image := relationships.get("image"): + if image["data"]: + image = self.farmos_client.resource.get_id( + "file", "file", image["data"][0]["id"] + ) + data["raw_image_url"] = self.app.get_farmos_url( + image["data"]["attributes"]["uri"]["url"] + ) + # nb. other styles available: medium, wide + data["large_image_url"] = image["data"]["attributes"][ + "image_style_uri" + ]["large"] + data["thumbnail_image_url"] = image["data"]["attributes"][ + "image_style_uri" + ]["thumbnail"] + + return data + + def get_instance_title(self, asset): + return asset["name"] + + def normalize_asset(self, asset): + + if notes := asset["attributes"]["notes"]: + notes = notes["value"] + + if self.farmos_4x: + archived = asset["attributes"]["archived"] + else: + archived = asset["attributes"]["status"] == "archived" + + return { + "uuid": asset["id"], + "drupal_id": asset["attributes"]["drupal_internal__id"], + "name": asset["attributes"]["name"], + "location": colander.null, # TODO + "notes": notes or colander.null, + "archived": archived, + } + + def configure_form(self, form): + f = form + super().configure_form(f) + animal = f.model_instance + + # location + f.set_node("location", StructureType(self.request)) + + # owners + f.set_node("owners", UsersType(self.request)) + + # notes + f.set_widget("notes", "notes") + + # archived + f.set_node("archived", colander.Boolean()) + + # image + if url := animal.get("large_image_url"): + f.set_widget("image", ImageWidget("animal image")) + f.set_default("image", url) + + def get_xref_buttons(self, asset): + return [ + self.make_button( + "View in farmOS", + primary=True, + url=self.app.get_farmos_url(f"/asset/{asset['drupal_id']}"), + target="_blank", + icon_left="external-link-alt", + ), + ]