diff --git a/src/wuttafarm/web/forms/widgets.py b/src/wuttafarm/web/forms/widgets.py index 24c33eb..7c807fa 100644 --- a/src/wuttafarm/web/forms/widgets.py +++ b/src/wuttafarm/web/forms/widgets.py @@ -189,7 +189,7 @@ class StructureWidget(Widget): return tags.link_to( structure["name"], self.request.route_url( - "farmos_structures.view", uuid=structure["uuid"] + "farmos_structure_assets.view", uuid=structure["uuid"] ), ) diff --git a/src/wuttafarm/web/grids.py b/src/wuttafarm/web/grids.py index 198d591..5e5e87c 100644 --- a/src/wuttafarm/web/grids.py +++ b/src/wuttafarm/web/grids.py @@ -135,12 +135,20 @@ class SimpleSorter: class ResourceData: - def __init__(self, config, farmos_client, content_type, normalizer=None): + def __init__( + self, + config, + farmos_client, + content_type, + include=None, + normalizer=None, + ): self.config = config self.farmos_client = farmos_client self.entity, self.bundle = content_type.split("--") self.filters = [] self.sorters = [] + self.include = include self.normalizer = normalizer self._data = None @@ -189,12 +197,17 @@ class ResourceData: # params["page[offset]"] = start # params["page[limit]"] = stop - start + if self.include: + params["include"] = self.include + result = self.farmos_client.resource.get( self.entity, self.bundle, params=params ) data = result["data"] + included = {obj["id"]: obj for obj in result.get("included", [])} + if self.normalizer: - data = [self.normalizer(d) for d in data] + data = [self.normalizer(d, included) for d in data] self._data = data return self._data diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py index c79acec..be59006 100644 --- a/src/wuttafarm/web/menus.py +++ b/src/wuttafarm/web/menus.py @@ -202,13 +202,13 @@ class WuttaFarmMenuHandler(base.MenuHandler): {"type": "sep"}, { "title": "Animal Assets", - "route": "farmos_assets_animal", - "perm": "farmos_assets_animal.list", + "route": "farmos_animal_assets", + "perm": "farmos_animal_assets.list", }, { "title": "Group Assets", - "route": "farmos_groups", - "perm": "farmos_groups.list", + "route": "farmos_group_assets", + "perm": "farmos_group_assets.list", }, { "title": "Land Assets", @@ -217,13 +217,13 @@ class WuttaFarmMenuHandler(base.MenuHandler): }, { "title": "Plant Assets", - "route": "farmos_asset_plant", - "perm": "farmos_asset_plant.list", + "route": "farmos_plant_assets", + "perm": "farmos_plant_assets.list", }, { "title": "Structure Assets", - "route": "farmos_structures", - "perm": "farmos_structures.list", + "route": "farmos_structure_assets", + "perm": "farmos_structure_assets.list", }, {"type": "sep"}, { @@ -311,13 +311,13 @@ class WuttaFarmMenuHandler(base.MenuHandler): "items": [ { "title": "Animal", - "route": "farmos_assets_animal", - "perm": "farmos_assets_animal.list", + "route": "farmos_animal_assets", + "perm": "farmos_animal_assets.list", }, { "title": "Group", - "route": "farmos_groups", - "perm": "farmos_groups.list", + "route": "farmos_group_assets", + "perm": "farmos_group_assets.list", }, { "title": "Land", @@ -326,13 +326,13 @@ class WuttaFarmMenuHandler(base.MenuHandler): }, { "title": "Plant", - "route": "farmos_asset_plant", - "perm": "farmos_asset_plant.list", + "route": "farmos_plant_assets", + "perm": "farmos_plant_assets.list", }, { "title": "Structure", - "route": "farmos_structures", - "perm": "farmos_structures.list", + "route": "farmos_structure_assets", + "perm": "farmos_structure_assets.list", }, {"type": "sep"}, { diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index 85835f9..b78f149 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -278,29 +278,14 @@ class AssetMasterView(WuttaFarmMasterView): buttons = super().get_xref_buttons(asset) if asset.farmos_uuid: - - # TODO - route = None - if asset.asset_type == "animal": - route = "farmos_assets_animal.view" - elif asset.asset_type == "group": - route = "farmos_groups.view" - elif asset.asset_type == "land": - route = "farmos_land_assets.view" - elif asset.asset_type == "plant": - route = "farmos_asset_plant.view" - elif asset.asset_type == "structure": - route = "farmos_structures.view" - - if route: - buttons.append( - self.make_button( - "View farmOS record", - primary=True, - url=self.request.route_url(route, uuid=asset.farmos_uuid), - icon_left="eye", - ) + asset_type = self.get_model_class().__wutta_hint__["farmos_asset_type"] + route = f"farmos_{asset_type}_assets.view" + url = self.request.route_url(route, uuid=asset.farmos_uuid) + buttons.append( + self.make_button( + "View farmOS record", primary=True, url=url, icon_left="eye" ) + ) return buttons diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py index 2efd5c6..f15e92b 100644 --- a/src/wuttafarm/web/views/common.py +++ b/src/wuttafarm/web/views/common.py @@ -65,14 +65,14 @@ class CommonView(base.CommonView): "asset_types.list", "asset_types.view", "asset_types.versions", + "farmos_animal_assets.list", + "farmos_animal_assets.view", "farmos_animal_types.list", "farmos_animal_types.view", - "farmos_assets_animal.list", - "farmos_assets_animal.view", "farmos_asset_types.list", "farmos_asset_types.view", - "farmos_groups.list", - "farmos_groups.view", + "farmos_group_assets.list", + "farmos_group_assets.view", "farmos_land_assets.list", "farmos_land_assets.view", "farmos_land_types.list", @@ -87,10 +87,10 @@ class CommonView(base.CommonView): "farmos_logs_medical.view", "farmos_logs_observation.list", "farmos_logs_observation.view", + "farmos_structure_assets.list", + "farmos_structure_assets.view", "farmos_structure_types.list", "farmos_structure_types.view", - "farmos_structures.list", - "farmos_structures.view", "farmos_users.list", "farmos_users.view", "group_assets.create", diff --git a/src/wuttafarm/web/views/farmos/animals.py b/src/wuttafarm/web/views/farmos/animals.py index ce5cd40..3f329f0 100644 --- a/src/wuttafarm/web/views/farmos/animals.py +++ b/src/wuttafarm/web/views/farmos/animals.py @@ -26,6 +26,7 @@ Master view for Farm Animals import datetime import colander +from webhelpers2.html import tags from wuttaweb.forms.schema import WuttaDateTime from wuttaweb.forms.widgets import WuttaDateTimeWidget @@ -45,11 +46,11 @@ class AnimalView(AssetMasterView): Master view for Farm Animals """ - model_name = "farmos_animal_asset" + model_name = "farmos_animal_assets" model_title = "farmOS Animal Asset" model_title_plural = "farmOS Animal Assets" - route_prefix = "farmos_assets_animal" + route_prefix = "farmos_animal_assets" url_prefix = "/farmOS/assets/animal" farmos_asset_type = "animal" @@ -57,6 +58,7 @@ class AnimalView(AssetMasterView): labels = { "animal_type": "Species / Breed", + "animal_type_name": "Species / Breed", "is_sterile": "Sterile", } @@ -64,9 +66,13 @@ class AnimalView(AssetMasterView): "drupal_id", "name", "produces_eggs", + "animal_type_name", "birthdate", "is_sterile", "sex", + "groups", + "owners", + "locations", "archived", ] @@ -78,6 +84,7 @@ class AnimalView(AssetMasterView): "sex", "is_sterile", "archived", + "groups", "owners", "location", "notes", @@ -87,6 +94,10 @@ class AnimalView(AssetMasterView): "image", ] + def get_grid_data(self, **kwargs): + kwargs.setdefault("include", "animal_type,group,owner,location") + return super().get_grid_data(**kwargs) + def configure_grid(self, grid): g = grid super().configure_grid(g) @@ -97,6 +108,11 @@ class AnimalView(AssetMasterView): g.set_sorter("produces_eggs", SimpleSorter("produces_eggs")) g.set_filter("produces_eggs", NullableBooleanFilter) + # animal_type_name + g.set_renderer("animal_type_name", self.render_animal_type_for_grid) + 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")) @@ -106,11 +122,27 @@ class AnimalView(AssetMasterView): g.set_sorter("sex", SimpleSorter("sex")) g.set_filter("sex", StringFilter) + # groups + g.set_label("groups", "Group Membership") + g.set_renderer("groups", self.render_groups_for_grid) + # 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) + + def render_groups_for_grid(self, animal, field, value): + links = [] + for group in animal["group_objects"]: + url = self.request.route_url("farmos_group_assets.view", uuid=group["uuid"]) + links.append(tags.link_to(group["name"], url)) + return ", ".join(links) + def get_instance(self): data = super().get_instance() @@ -118,21 +150,21 @@ class AnimalView(AssetMasterView): if relationships := self.raw_json["data"].get("relationships"): # add 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"], - } + 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): - - normal = super().normalize_asset(animal) + def normalize_asset(self, animal, included): + normal = super().normalize_asset(animal, included) birthdate = animal["attributes"]["birthdate"] if birthdate: @@ -145,8 +177,36 @@ class AnimalView(AssetMasterView): else: sterile = animal["attributes"]["is_castrated"] + animal_type = None + animal_type_name = None + group_objects = [] + group_names = [] + if relationships := animal.get("relationships"): + + if animal_type := relationships.get("animal_type"): + if animal_type := included.get(animal_type["data"]["id"]): + animal_type = { + "uuid": animal_type["id"], + "name": animal_type["attributes"]["name"], + } + animal_type_name = animal_type["name"] + + if groups := relationships.get("group"): + for group in groups["data"]: + if group := included.get(group["id"]): + group = { + "uuid": group["id"], + "name": group["attributes"]["name"], + } + group_objects.append(group) + group_names.append(group["name"]) + normal.update( { + "animal_type": animal_type, + "animal_type_name": animal_type_name, + "group_objects": group_objects, + "group_names": group_names, "birthdate": birthdate, "sex": animal["attributes"]["sex"] or colander.null, "is_sterile": sterile, diff --git a/src/wuttafarm/web/views/farmos/assets.py b/src/wuttafarm/web/views/farmos/assets.py index 06f9563..1a61d42 100644 --- a/src/wuttafarm/web/views/farmos/assets.py +++ b/src/wuttafarm/web/views/farmos/assets.py @@ -24,6 +24,7 @@ Base class for Asset master views """ import colander +from webhelpers2.html import tags from wuttafarm.web.views.farmos import FarmOSMasterView from wuttafarm.web.forms.schema import UsersType, StructureType @@ -48,12 +49,15 @@ class AssetMasterView(FarmOSMasterView): labels = { "name": "Asset Name", - "location": "Current Location", + "owners": "Owner", + "locations": "Location", } grid_columns = [ "drupal_id", "name", + "owners", + "locations", "archived", ] @@ -64,12 +68,14 @@ class AssetMasterView(FarmOSMasterView): "archived": {"active": True, "verb": "is_false"}, } - def get_grid_data(self, columns=None, session=None): + def get_grid_data(self, columns=None, session=None, **kwargs): + kwargs.setdefault("include", "owner,location") + kwargs.setdefault("normalizer", self.normalize_asset) return ResourceData( self.config, self.farmos_client, f"asset--{self.farmos_asset_type}", - normalizer=self.normalize_asset, + **kwargs, ) def configure_grid(self, grid): @@ -86,11 +92,33 @@ class AssetMasterView(FarmOSMasterView): g.set_sorter("name", SimpleSorter("name")) g.set_filter("name", StringFilter) + # owners + g.set_renderer("owners", self.render_owners_for_grid) + + # locations + g.set_renderer("locations", self.render_locations_for_grid) + # archived g.set_renderer("archived", "boolean") g.set_sorter("archived", SimpleSorter("archived")) g.set_filter("archived", BooleanFilter) + def render_owners_for_grid(self, asset, field, value): + links = [] + for user in value: + url = self.request.route_url("farmos_users.view", uuid=user["uuid"]) + links.append(tags.link_to(user["name"], url)) + return ", ".join(links) + + def render_locations_for_grid(self, asset, field, value): + links = [] + for location in value: + asset_type = location["type"].split("--")[1] + route = f"farmos_{asset_type}_assets.view" + url = self.request.route_url(route, uuid=location["uuid"]) + links.append(tags.link_to(location["name"], url)) + return ", ".join(links) + def grid_row_class(self, asset, data, i): """ """ if asset["archived"]: @@ -104,7 +132,7 @@ class AssetMasterView(FarmOSMasterView): self.raw_json = asset # instance data - data = self.normalize_asset(asset["data"]) + data = self.normalize_asset(asset["data"], {}) if relationships := asset["data"].get("relationships"): @@ -155,7 +183,7 @@ class AssetMasterView(FarmOSMasterView): def get_instance_title(self, asset): return asset["name"] - def normalize_asset(self, asset): + def normalize_asset(self, asset, included): if notes := asset["attributes"]["notes"]: notes = notes["value"] @@ -165,12 +193,43 @@ class AssetMasterView(FarmOSMasterView): else: archived = asset["attributes"]["status"] == "archived" + owner_objects = [] + owner_names = [] + location_objects = [] + location_names = [] + if relationships := asset.get("relationships"): + + if owners := relationships.get("owner"): + for user in owners["data"]: + if user := included.get(user["id"]): + user = { + "uuid": user["id"], + "name": user["attributes"]["name"], + } + owner_objects.append(user) + owner_names.append(user["name"]) + + if locations := relationships.get("location"): + for location in locations["data"]: + if location := included.get(location["id"]): + location = { + "uuid": location["id"], + "type": location["type"], + "name": location["attributes"]["name"], + } + location_objects.append(location) + location_names.append(location["name"]) + return { "uuid": asset["id"], "drupal_id": asset["attributes"]["drupal_internal__id"], "name": asset["attributes"]["name"], "location": colander.null, # TODO "notes": notes or colander.null, + "owners": owner_objects, + "owner_names": owner_names, + "locations": location_objects, + "location_names": location_names, "archived": archived, } diff --git a/src/wuttafarm/web/views/farmos/groups.py b/src/wuttafarm/web/views/farmos/groups.py index ddb7278..8794965 100644 --- a/src/wuttafarm/web/views/farmos/groups.py +++ b/src/wuttafarm/web/views/farmos/groups.py @@ -41,7 +41,7 @@ class GroupView(FarmOSMasterView): model_title = "farmOS Group" model_title_plural = "farmOS Groups" - route_prefix = "farmos_groups" + route_prefix = "farmos_group_assets" url_prefix = "/farmOS/groups" farmos_refurl_path = "/assets/group" diff --git a/src/wuttafarm/web/views/farmos/plants.py b/src/wuttafarm/web/views/farmos/plants.py index 95a2dab..57bf2d4 100644 --- a/src/wuttafarm/web/views/farmos/plants.py +++ b/src/wuttafarm/web/views/farmos/plants.py @@ -80,11 +80,11 @@ class PlantAssetView(FarmOSMasterView): Master view for farmOS Plant Assets """ - model_name = "farmos_asset_plant" + model_name = "farmos_plant_assets" model_title = "farmOS Plant Asset" model_title_plural = "farmOS Plant Assets" - route_prefix = "farmos_asset_plant" + route_prefix = "farmos_plant_assets" url_prefix = "/farmOS/assets/plant" farmos_refurl_path = "/assets/plant" diff --git a/src/wuttafarm/web/views/farmos/structures.py b/src/wuttafarm/web/views/farmos/structures.py index 550f432..b6dc97b 100644 --- a/src/wuttafarm/web/views/farmos/structures.py +++ b/src/wuttafarm/web/views/farmos/structures.py @@ -39,11 +39,11 @@ class StructureView(FarmOSMasterView): View for farmOS Structures """ - model_name = "farmos_structure" + model_name = "farmos_structure_asset" model_title = "farmOS Structure" model_title_plural = "farmOS Structures" - route_prefix = "farmos_structures" + route_prefix = "farmos_structure_assets" url_prefix = "/farmOS/structures" farmos_refurl_path = "/assets/structure"