Source code for wuttafarm.web.views.farmos.animals

# -*- 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/>.
#
################################################################################
"""
Master view for Farm Animals
"""

import datetime

import colander
from webhelpers2.html import tags

from wuttaweb.forms.schema import WuttaDateTime, WuttaDictEnum
from wuttaweb.forms.widgets import WuttaDateTimeWidget

from wuttafarm.web.views.farmos.assets import AssetMasterView
from wuttafarm.web.grids import (
    SimpleSorter,
    StringFilter,
    BooleanFilter,
    NullableBooleanFilter,
    DateTimeFilter,
)
from wuttafarm.web.forms.schema import FarmOSRef


[docs] class AnimalView(AssetMasterView): """ Master view for Farm Animals """ model_name = "farmos_animal_assets" model_title = "farmOS Animal Asset" model_title_plural = "farmOS Animal Assets" route_prefix = "farmos_animal_assets" url_prefix = "/farmOS/assets/animal" farmos_asset_type = "animal" farmos_refurl_path = "/assets/animal" labels = { "animal_type": "Species / Breed", "animal_type_name": "Species / Breed", "is_sterile": "Sterile", } grid_columns = [ "thumbnail", "drupal_id", "name", "produces_eggs", "animal_type_name", "birthdate", "is_sterile", "sex", "groups", "owners", "locations", "archived", ] form_fields = [ "name", "animal_type", "birthdate", "produces_eggs", "sex", "is_sterile", "notes", "asset_type_name", "owners", "locations", "groups", "archived", "thumbnail_url", "image_url", "thumbnail", "image", ] def get_farmos_api_includes(self): includes = super().get_farmos_api_includes() includes.update(["animal_type"]) return includes
[docs] def configure_grid(self, grid): g = grid super().configure_grid(g) enum = self.app.enum # produces_eggs g.set_renderer("produces_eggs", "boolean") g.set_sorter("produces_eggs", SimpleSorter("produces_eggs")) g.set_filter("produces_eggs", NullableBooleanFilter) # animal_type_name if self.farmos_style_grid_links: g.set_renderer("animal_type_name", self.render_animal_type_for_grid) else: g.set_link("animal_type_name") g.set_sorter("animal_type_name", SimpleSorter("animal_type.name")) g.set_filter("animal_type_name", StringFilter, path="animal_type.name") # birthdate g.set_renderer("birthdate", "date") g.set_sorter("birthdate", SimpleSorter("birthdate")) g.set_filter("birthdate", DateTimeFilter) # sex g.set_enum("sex", enum.ANIMAL_SEX) g.set_sorter("sex", SimpleSorter("sex")) g.set_filter("sex", StringFilter) # is_sterile g.set_renderer("is_sterile", "boolean") g.set_sorter("is_sterile", SimpleSorter("is_sterile")) g.set_filter("is_sterile", BooleanFilter)
def render_animal_type_for_grid(self, animal, field, value): uuid = animal["animal_type"]["uuid"] url = self.request.route_url("farmos_animal_types.view", uuid=uuid) return tags.link_to(value, url)
[docs] def get_instance(self): data = super().get_instance() if relationships := self.raw_json["data"].get("relationships"): # add animal type if not data.get("animal_type"): if animal_type := relationships.get("animal_type"): if animal_type["data"]: animal_type = self.farmos_client.resource.get_id( "taxonomy_term", "animal_type", animal_type["data"]["id"] ) data["animal_type"] = { "uuid": animal_type["data"]["id"], "name": animal_type["data"]["attributes"]["name"], } return data
def normalize_asset(self, animal, included): normal = super().normalize_asset(animal, included) birthdate = animal["attributes"]["birthdate"] if birthdate: birthdate = datetime.datetime.fromisoformat(birthdate) birthdate = self.app.localtime(birthdate) sterile = None if self.farmos_4x: sterile = animal["attributes"]["is_sterile"] else: sterile = animal["attributes"]["is_castrated"] animal_type_object = None if relationships := animal.get("relationships"): if animal_type := relationships.get("animal_type"): if animal_type := included.get(animal_type["data"]["id"]): animal_type_object = { "uuid": animal_type["id"], "name": animal_type["attributes"]["name"], } normal.update( { "animal_type": animal_type_object, "animal_type_uuid": animal_type_object["uuid"], "animal_type_name": animal_type_object["name"], "birthdate": birthdate, "sex": animal["attributes"]["sex"] or colander.null, "is_sterile": sterile, "produces_eggs": animal["attributes"].get("produces_eggs"), } ) return normal def get_animal_types(self): animal_types = [] result = self.farmos_client.resource.get( "taxonomy_term", "animal_type", params={"sort": "name"} ) for animal_type in result["data"]: animal_types.append((animal_type["id"], animal_type["attributes"]["name"])) return animal_types
[docs] def configure_form(self, form): f = form super().configure_form(f) enum = self.app.enum animal = f.model_instance # animal_type f.set_node( "animal_type", FarmOSRef( self.request, "farmos_animal_types", values=self.get_animal_types ), ) # produces_eggs f.set_node("produces_eggs", colander.Boolean()) # birthdate f.set_node("birthdate", WuttaDateTime()) f.set_widget("birthdate", WuttaDateTimeWidget(self.request)) f.set_required("birthdate", False) # sex if not (self.creating or self.editing) and not animal["sex"]: pass # TODO: dict enum widget does not handle null values well else: f.set_node("sex", WuttaDictEnum(self.request, enum.ANIMAL_SEX)) f.set_required("sex", False) # is_sterile f.set_node("is_sterile", colander.Boolean())
def get_api_payload(self, animal): payload = super().get_api_payload(animal) birthdate = None if animal["birthdate"]: birthdate = self.app.localtime(animal["birthdate"]).timestamp() attrs = { "sex": animal["sex"] or None, "is_sterile": animal["is_sterile"], "produces_eggs": animal["produces_eggs"], "birthdate": birthdate, } rels = { "animal_type": { "data": { "id": animal["animal_type"], "type": "taxonomy_term--animal_type", } } } payload["attributes"].update(attrs) payload.setdefault("relationships", {}).update(rels) return payload
[docs] def get_xref_buttons(self, animal): buttons = super().get_xref_buttons(animal) 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", ) ) return buttons
def defaults(config, **kwargs): base = globals() AnimalView = kwargs.get("AnimalView", base["AnimalView"]) AnimalView.defaults(config) def includeme(config): defaults(config)