fix: standardize a bit more for the farmOS Animal Assets view

This commit is contained in:
Lance Edgar 2026-02-19 19:56:44 -06:00
parent 87101d6b04
commit 9cfa91e091
6 changed files with 222 additions and 145 deletions

View file

@ -202,8 +202,8 @@ class WuttaFarmMenuHandler(base.MenuHandler):
{"type": "sep"}, {"type": "sep"},
{ {
"title": "Animal Assets", "title": "Animal Assets",
"route": "farmos_animals", "route": "farmos_assets_animal",
"perm": "farmos_animals.list", "perm": "farmos_assets_animal.list",
}, },
{ {
"title": "Group Assets", "title": "Group Assets",
@ -311,8 +311,8 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"items": [ "items": [
{ {
"title": "Animal", "title": "Animal",
"route": "farmos_animals", "route": "farmos_assets_animal",
"perm": "farmos_animals.list", "perm": "farmos_assets_animal.list",
}, },
{ {
"title": "Group", "title": "Group",

View file

@ -153,11 +153,11 @@ class AnimalAssetView(AssetMasterView):
"thumbnail", "thumbnail",
"drupal_id", "drupal_id",
"asset_name", "asset_name",
"produces_eggs",
"animal_type", "animal_type",
"birthdate", "birthdate",
"is_sterile", "is_sterile",
"sex", "sex",
"produces_eggs",
"archived", "archived",
] ]

View file

@ -282,7 +282,7 @@ class AssetMasterView(WuttaFarmMasterView):
# TODO # TODO
route = None route = None
if asset.asset_type == "animal": if asset.asset_type == "animal":
route = "farmos_animals.view" route = "farmos_assets_animal.view"
elif asset.asset_type == "group": elif asset.asset_type == "group":
route = "farmos_groups.view" route = "farmos_groups.view"
elif asset.asset_type == "land": elif asset.asset_type == "land":

View file

@ -67,8 +67,8 @@ class CommonView(base.CommonView):
"asset_types.versions", "asset_types.versions",
"farmos_animal_types.list", "farmos_animal_types.list",
"farmos_animal_types.view", "farmos_animal_types.view",
"farmos_animals.list", "farmos_assets_animal.list",
"farmos_animals.view", "farmos_assets_animal.view",
"farmos_asset_types.list", "farmos_asset_types.list",
"farmos_asset_types.view", "farmos_asset_types.view",
"farmos_groups.list", "farmos_groups.list",

View file

@ -30,28 +30,27 @@ import colander
from wuttaweb.forms.schema import WuttaDateTime from wuttaweb.forms.schema import WuttaDateTime
from wuttaweb.forms.widgets import WuttaDateTimeWidget from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttafarm.web.views.farmos import FarmOSMasterView from wuttafarm.web.views.farmos.assets import AssetMasterView
from wuttafarm.web.forms.schema import UsersType, AnimalTypeType, StructureType from wuttafarm.web.forms.schema import AnimalTypeType
from wuttafarm.web.forms.widgets import ImageWidget
class AnimalView(FarmOSMasterView): class AnimalView(AssetMasterView):
""" """
Master view for Farm Animals Master view for Farm Animals
""" """
model_name = "farmos_animal" model_name = "farmos_animal_asset"
model_title = "farmOS Animal" model_title = "farmOS Animal Asset"
model_title_plural = "farmOS Animals" model_title_plural = "farmOS Animal Assets"
route_prefix = "farmos_animals" route_prefix = "farmos_assets_animal"
url_prefix = "/farmOS/animals" url_prefix = "/farmOS/assets/animal"
farmos_asset_type = "animal"
farmos_refurl_path = "/assets/animal" farmos_refurl_path = "/assets/animal"
labels = { labels = {
"animal_type": "Species / Breed", "animal_type": "Species / Breed",
"location": "Current Location",
} }
grid_columns = [ grid_columns = [
@ -62,8 +61,6 @@ class AnimalView(FarmOSMasterView):
"archived", "archived",
] ]
sort_defaults = "name"
form_fields = [ form_fields = [
"name", "name",
"animal_type", "animal_type",
@ -80,38 +77,21 @@ class AnimalView(FarmOSMasterView):
"image", "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): def configure_grid(self, grid):
g = grid g = grid
super().configure_grid(g) super().configure_grid(g)
# name
g.set_link("name")
g.set_searchable("name")
# birthdate # birthdate
g.set_renderer("birthdate", "date") g.set_renderer("birthdate", "date")
# is_sterile # is_sterile
g.set_renderer("is_sterile", "boolean") g.set_renderer("is_sterile", "boolean")
# archived
g.set_renderer("archived", "boolean")
def get_instance(self): def get_instance(self):
animal = self.farmos_client.resource.get_id( data = super().get_instance()
"asset", "animal", self.request.matchdict["uuid"]
)
self.raw_json = animal
# instance data if relationships := self.raw_json["data"].get("relationships"):
data = self.normalize_animal(animal["data"])
if relationships := animal["data"].get("relationships"):
# add animal type # add animal type
if animal_type := relationships.get("animal_type"): if animal_type := relationships.get("animal_type"):
@ -124,54 +104,11 @@ class AnimalView(FarmOSMasterView):
"name": animal_type["data"]["attributes"]["name"], "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 return data
def get_instance_title(self, animal): def normalize_asset(self, animal):
return animal["name"]
def normalize_animal(self, animal): normal = super().normalize_asset(animal)
birthdate = animal["attributes"]["birthdate"] birthdate = animal["attributes"]["birthdate"]
if birthdate: if birthdate:
@ -184,30 +121,19 @@ class AnimalView(FarmOSMasterView):
else: else:
sterile = animal["attributes"]["is_castrated"] sterile = animal["attributes"]["is_castrated"]
if notes := animal["attributes"]["notes"]: normal.update(
notes = notes["value"] {
"birthdate": birthdate,
"sex": animal["attributes"]["sex"] or colander.null,
"is_sterile": sterile,
}
)
if self.farmos_4x: return normal
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,
}
def configure_form(self, form): def configure_form(self, form):
f = form f = form
super().configure_form(f) super().configure_form(f)
animal = f.model_instance
# animal_type # animal_type
f.set_node("animal_type", AnimalTypeType(self.request)) f.set_node("animal_type", AnimalTypeType(self.request))
@ -219,52 +145,28 @@ class AnimalView(FarmOSMasterView):
# is_sterile # is_sterile
f.set_node("is_sterile", colander.Boolean()) 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): def get_xref_buttons(self, animal):
model = self.app.model buttons = super().get_xref_buttons(animal)
session = self.Session()
buttons = [ if self.app.is_farmos_mirror():
self.make_button( model = self.app.model
"View in farmOS", session = self.Session()
primary=True,
url=self.app.get_farmos_url(f"/asset/{animal['drupal_id']}"),
target="_blank",
icon_left="external-link-alt",
),
]
if wf_animal := ( if wf_animal := (
session.query(model.Asset) session.query(model.Asset)
.filter(model.Asset.farmos_uuid == animal["uuid"]) .filter(model.Asset.farmos_uuid == animal["uuid"])
.first() .first()
): ):
buttons.append( buttons.append(
self.make_button( self.make_button(
f"View {self.app.get_title()} record", f"View {self.app.get_title()} record",
primary=True, primary=True,
url=self.request.route_url( url=self.request.route_url(
"animal_assets.view", uuid=wf_animal.uuid "animal_assets.view", uuid=wf_animal.uuid
), ),
icon_left="eye", icon_left="eye",
)
) )
)
return buttons return buttons

View file

@ -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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
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",
),
]