diff --git a/CHANGELOG.md b/CHANGELOG.md
index eabe9f0..5d6142c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,27 +5,6 @@ All notable changes to WuttaFarm will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## v0.2.0 (2026-02-08)
-
-### Feat
-
-- add view for farmOS activity logs
-- add view for farmOS log types
-- add view for farmOS structure types
-- add view for farmOS land types
-- add view for farmOS land assets
-- add view for farmOS groups
-- add view for farmOS asset types
-- add view for farmOS structures
-- add view for farmOS animal types
-- add view for farmOS users
-
-### Fix
-
-- add pyramid_exclog dependency
-- add menu option, "Go to farmOS"
-- ensure Buefy version matches what we use for custom css
-
## v0.1.5 (2026-02-07)
### Fix
diff --git a/pyproject.toml b/pyproject.toml
index 48faefd..d2c6ef1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
[project]
name = "WuttaFarm"
-version = "0.2.0"
+version = "0.1.5"
description = "Web app to integrate with and extend farmOS"
readme = "README.md"
authors = [
@@ -31,7 +31,6 @@ license = {text = "GNU General Public License v3"}
dependencies = [
"farmOS",
"psycopg2",
- "pyramid_exclog",
"WuttaWeb[continuum]>=0.27.3",
]
diff --git a/src/wuttafarm/web/app.py b/src/wuttafarm/web/app.py
index 5c59434..83c9817 100644
--- a/src/wuttafarm/web/app.py
+++ b/src/wuttafarm/web/app.py
@@ -49,16 +49,11 @@ def main(global_config, **settings):
app = wutta_config.get_app()
path = app.resource_path("wuttafarm.web.static:css/wuttafarm-buefy.css")
if os.path.exists(path):
-
# TODO: this is not robust enough, probably..but works for me/now
wutta_config.setdefault(
"wuttaweb.liburl.buefy_css", "/wuttafarm/css/wuttafarm-buefy.css"
)
- # nb. ensure buefy version matches what we use for custom css
- wutta_config.setdefault("wuttaweb.libver.buefy", "0.9.29")
- wutta_config.setdefault("wuttaweb.libver.buefy_css", "0.9.29")
-
# bring in the rest of WuttaFarm
pyramid_config.include("wuttafarm.web.static")
pyramid_config.include("wuttafarm.web.subscribers")
diff --git a/src/wuttafarm/web/forms/schema.py b/src/wuttafarm/web/forms/schema.py
deleted file mode 100644
index a38588a..0000000
--- a/src/wuttafarm/web/forms/schema.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Custom form widgets for WuttaFarm
-"""
-
-import json
-
-import colander
-
-
-class AnimalTypeType(colander.SchemaType):
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, node, appstruct):
- if appstruct is colander.null:
- return colander.null
-
- return json.dumps(appstruct)
-
- def widget_maker(self, **kwargs): # pylint: disable=empty-docstring
- """ """
- from wuttafarm.web.forms.widgets import AnimalTypeWidget
-
- return AnimalTypeWidget(self.request, **kwargs)
-
-
-class StructureType(colander.SchemaType):
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, node, appstruct):
- if appstruct is colander.null:
- return colander.null
-
- return json.dumps(appstruct)
-
- def widget_maker(self, **kwargs): # pylint: disable=empty-docstring
- """ """
- from wuttafarm.web.forms.widgets import StructureWidget
-
- return StructureWidget(self.request, **kwargs)
-
-
-class UsersType(colander.SchemaType):
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, node, appstruct):
- if appstruct is colander.null:
- return colander.null
-
- return json.dumps(appstruct)
-
- def widget_maker(self, **kwargs): # pylint: disable=empty-docstring
- """ """
- from wuttafarm.web.forms.widgets import UsersWidget
-
- return UsersWidget(self.request, **kwargs)
diff --git a/src/wuttafarm/web/forms/widgets.py b/src/wuttafarm/web/forms/widgets.py
index 0ffb055..047a8cd 100644
--- a/src/wuttafarm/web/forms/widgets.py
+++ b/src/wuttafarm/web/forms/widgets.py
@@ -23,21 +23,18 @@
Custom form widgets for WuttaFarm
"""
-import json
-
import colander
from deform.widget import Widget
from webhelpers2.html import HTML, tags
-class ImageWidget(Widget):
- """
- Widget to display an image URL for a record.
+class AnimalImage(Widget):
"""
+ Widget to display an image URL for an animal.
- def __init__(self, alt_text, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.alt_text = alt_text
+ TODO: this should be refactored to a more general name, once more
+ types of images need to be supported.
+ """
def serialize(self, field, cstruct, **kw):
readonly = kw.get("readonly", self.readonly)
@@ -45,86 +42,6 @@ class ImageWidget(Widget):
if cstruct in (colander.null, None):
return HTML.tag("span")
- return tags.image(cstruct, self.alt_text, **kw)
-
- return super().serialize(field, cstruct, **kw)
-
-
-class AnimalTypeWidget(Widget):
- """
- Widget to display an "animal type" field.
- """
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, field, cstruct, **kw):
- readonly = kw.get("readonly", self.readonly)
- if readonly:
- if cstruct in (colander.null, None):
- return HTML.tag("span")
-
- animal_type = json.loads(cstruct)
- return tags.link_to(
- animal_type["name"],
- self.request.route_url(
- "farmos_animal_types.view", uuid=animal_type["uuid"]
- ),
- )
-
- return super().serialize(field, cstruct, **kw)
-
-
-class StructureWidget(Widget):
- """
- Widget to display a "structure" field.
- """
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, field, cstruct, **kw):
- readonly = kw.get("readonly", self.readonly)
- if readonly:
- if cstruct in (colander.null, None):
- return HTML.tag("span")
-
- structure = json.loads(cstruct)
- return tags.link_to(
- structure["name"],
- self.request.route_url(
- "farmos_structures.view", uuid=structure["uuid"]
- ),
- )
-
- return super().serialize(field, cstruct, **kw)
-
-
-class UsersWidget(Widget):
- """
- Widget to display the list of owners for an asset etc.
- """
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
-
- def serialize(self, field, cstruct, **kw):
- readonly = kw.get("readonly", self.readonly)
- if readonly:
- if cstruct in (colander.null, None):
- return HTML.tag("span")
-
- items = []
- for user in json.loads(cstruct):
- link = tags.link_to(
- user["display_name"],
- self.request.route_url("farmos_users.view", uuid=user["uuid"]),
- )
- items.append(HTML.tag("li", c=link))
-
- return HTML.tag("ul", c=items)
+ return tags.image(cstruct, "animal image", **kw)
return super().serialize(field, cstruct, **kw)
diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py
index ab6f440..e999944 100644
--- a/src/wuttafarm/web/menus.py
+++ b/src/wuttafarm/web/menus.py
@@ -38,75 +38,14 @@ class WuttaFarmMenuHandler(base.MenuHandler):
]
def make_farmos_menu(self, request):
- config = request.wutta_config
- app = config.get_app()
return {
"title": "farmOS",
"type": "menu",
"items": [
- {
- "title": "Go to farmOS",
- "url": app.get_farmos_url(),
- "target": "_blank",
- },
- {"type": "sep"},
{
"title": "Animals",
"route": "farmos_animals",
"perm": "farmos_animals.list",
},
- {
- "title": "Groups",
- "route": "farmos_groups",
- "perm": "farmos_groups.list",
- },
- {
- "title": "Structures",
- "route": "farmos_structures",
- "perm": "farmos_structures.list",
- },
- {
- "title": "Land",
- "route": "farmos_land_assets",
- "perm": "farmos_land_assets.list",
- },
- {"type": "sep"},
- {
- "title": "Activity Logs",
- "route": "farmos_logs_activity",
- "perm": "farmos_logs_activity.list",
- },
- {"type": "sep"},
- {
- "title": "Animal Types",
- "route": "farmos_animal_types",
- "perm": "farmos_animal_types.list",
- },
- {
- "title": "Structure Types",
- "route": "farmos_structure_types",
- "perm": "farmos_structure_types.list",
- },
- {
- "title": "Land Types",
- "route": "farmos_land_types",
- "perm": "farmos_land_types.list",
- },
- {
- "title": "Asset Types",
- "route": "farmos_asset_types",
- "perm": "farmos_asset_types.list",
- },
- {
- "title": "Log Types",
- "route": "farmos_log_types",
- "perm": "farmos_log_types.list",
- },
- {"type": "sep"},
- {
- "title": "Users",
- "route": "farmos_users",
- "perm": "farmos_users.list",
- },
],
}
diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py
index f46c018..bb8a9c9 100644
--- a/src/wuttafarm/web/views/common.py
+++ b/src/wuttafarm/web/views/common.py
@@ -48,28 +48,8 @@ class CommonView(base.CommonView):
site_admin = session.query(model.Role).filter_by(name="Site Admin").first()
if site_admin:
site_admin_perms = [
- "farmos_animal_types.list",
- "farmos_animal_types.view",
"farmos_animals.list",
"farmos_animals.view",
- "farmos_asset_types.list",
- "farmos_asset_types.view",
- "farmos_groups.list",
- "farmos_groups.view",
- "farmos_land_assets.list",
- "farmos_land_assets.view",
- "farmos_land_types.list",
- "farmos_land_types.view",
- "farmos_log_types.list",
- "farmos_log_types.view",
- "farmos_logs_activity.list",
- "farmos_logs_activity.view",
- "farmos_structure_types.list",
- "farmos_structure_types.view",
- "farmos_structures.list",
- "farmos_structures.view",
- "farmos_users.list",
- "farmos_users.view",
]
for perm in site_admin_perms:
auth.grant_permission(site_admin, perm)
diff --git a/src/wuttafarm/web/views/farmos/__init__.py b/src/wuttafarm/web/views/farmos/__init__.py
index deacd7d..df789b3 100644
--- a/src/wuttafarm/web/views/farmos/__init__.py
+++ b/src/wuttafarm/web/views/farmos/__init__.py
@@ -27,14 +27,4 @@ from .master import FarmOSMasterView
def includeme(config):
- config.include("wuttafarm.web.views.farmos.users")
- config.include("wuttafarm.web.views.farmos.asset_types")
- config.include("wuttafarm.web.views.farmos.land_types")
- config.include("wuttafarm.web.views.farmos.land_assets")
- config.include("wuttafarm.web.views.farmos.structure_types")
- config.include("wuttafarm.web.views.farmos.structures")
- config.include("wuttafarm.web.views.farmos.animal_types")
config.include("wuttafarm.web.views.farmos.animals")
- config.include("wuttafarm.web.views.farmos.groups")
- config.include("wuttafarm.web.views.farmos.log_types")
- config.include("wuttafarm.web.views.farmos.logs_activity")
diff --git a/src/wuttafarm/web/views/farmos/animal_types.py b/src/wuttafarm/web/views/farmos/animal_types.py
deleted file mode 100644
index a974242..0000000
--- a/src/wuttafarm/web/views/farmos/animal_types.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS animal types
-"""
-
-import datetime
-
-import colander
-
-from wuttaweb.forms.schema import WuttaDateTime
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class AnimalTypeView(FarmOSMasterView):
- """
- Master view for Animal Types in farmOS.
- """
-
- model_name = "farmos_animal_type"
- model_title = "farmOS Animal Type"
- model_title_plural = "farmOS Animal Types"
-
- route_prefix = "farmos_animal_types"
- url_prefix = "/farmOS/animal-types"
-
- farmos_refurl_path = "/admin/structure/taxonomy/manage/animal_type/overview"
-
- grid_columns = [
- "name",
- "description",
- "changed",
- ]
-
- sort_defaults = "name"
-
- form_fields = [
- "name",
- "description",
- "changed",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- animal_types = self.farmos_client.resource.get("taxonomy_term", "animal_type")
- return [self.normalize_animal_type(t) for t in animal_types["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
- g.set_searchable("name")
-
- # changed
- g.set_renderer("changed", "datetime")
-
- def get_instance(self):
- animal_type = self.farmos_client.resource.get_id(
- "taxonomy_term", "animal_type", self.request.matchdict["uuid"]
- )
- return self.normalize_animal_type(animal_type["data"])
-
- def get_instance_title(self, animal_type):
- return animal_type["name"]
-
- def normalize_animal_type(self, animal_type):
-
- if changed := animal_type["attributes"]["changed"]:
- changed = datetime.datetime.fromisoformat(changed)
- changed = self.app.localtime(changed)
-
- if description := animal_type["attributes"]["description"]:
- description = description["value"]
-
- return {
- "uuid": animal_type["id"],
- "drupal_internal_id": animal_type["attributes"]["drupal_internal__tid"],
- "name": animal_type["attributes"]["name"],
- "description": description or colander.null,
- "changed": changed,
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # description
- f.set_widget("description", "notes")
-
- # changed
- f.set_node("changed", WuttaDateTime())
-
- def get_xref_buttons(self, animal_type):
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(
- f"/taxonomy/term/{animal_type['drupal_internal_id']}"
- ),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- AnimalTypeView = kwargs.get("AnimalTypeView", base["AnimalTypeView"])
- AnimalTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/animals.py b/src/wuttafarm/web/views/farmos/animals.py
index 8eca5af..aa00412 100644
--- a/src/wuttafarm/web/views/farmos/animals.py
+++ b/src/wuttafarm/web/views/farmos/animals.py
@@ -28,9 +28,7 @@ import datetime
import colander
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.forms.widgets import AnimalImage
class AnimalView(FarmOSMasterView):
@@ -48,9 +46,11 @@ class AnimalView(FarmOSMasterView):
farmos_refurl_path = "/assets/animal"
labels = {
- "animal_type": "Species / Breed",
"is_castrated": "Castrated",
- "location": "Current Location",
+ "location_name": "Current Location",
+ "raw_image_url": "Raw Image URL",
+ "large_image_url": "Large Image URL",
+ "thumbnail_image_url": "Thumbnail Image URL",
}
grid_columns = [
@@ -65,13 +65,13 @@ class AnimalView(FarmOSMasterView):
form_fields = [
"name",
- "animal_type",
+ "animal_type_name",
"birthdate",
"sex",
"is_castrated",
"status",
"owners",
- "location",
+ "location_name",
"notes",
"raw_image_url",
"large_image_url",
@@ -111,10 +111,7 @@ class AnimalView(FarmOSMasterView):
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"],
- }
+ data["animal_type_name"] = animal_type["data"]["attributes"]["name"]
# add location
if location := relationships.get("location"):
@@ -122,24 +119,20 @@ class AnimalView(FarmOSMasterView):
location = self.farmos_client.resource.get_id(
"asset", "structure", location["data"][0]["id"]
)
- data["location"] = {
- "uuid": location["data"]["id"],
- "name": location["data"]["attributes"]["name"],
- }
+ data["location_name"] = location["data"]["attributes"]["name"]
# add owners
if owner := relationships.get("owner"):
- data["owners"] = []
+ 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"],
- }
+ owners.append(
+ self.farmos_client.resource.get_id(
+ "user", "user", owner_data["id"]
+ )
)
+ data["owners"] = ", ".join(
+ [o["data"]["attributes"]["display_name"] for o in owners]
+ )
# add image urls
if image := relationships.get("image"):
@@ -174,6 +167,7 @@ class AnimalView(FarmOSMasterView):
"uuid": animal["id"],
"drupal_internal_id": animal["attributes"]["drupal_internal__id"],
"name": animal["attributes"]["name"],
+ "species_breed": "", # TODO
"birthdate": birthdate,
"sex": animal["attributes"]["sex"],
"is_castrated": animal["attributes"]["is_castrated"],
@@ -187,24 +181,15 @@ class AnimalView(FarmOSMasterView):
super().configure_form(f)
animal = f.model_instance
- # animal_type
- f.set_node("animal_type", AnimalTypeType(self.request))
-
# is_castrated
f.set_node("is_castrated", colander.Boolean())
- # location
- f.set_node("location", StructureType(self.request))
-
- # owners
- f.set_node("owners", UsersType(self.request))
-
# notes
f.set_widget("notes", "notes")
# image
if url := animal.get("large_image_url"):
- f.set_widget("image", ImageWidget("animal image"))
+ f.set_widget("image", AnimalImage())
f.set_default("image", url)
def get_xref_buttons(self, animal):
diff --git a/src/wuttafarm/web/views/farmos/asset_types.py b/src/wuttafarm/web/views/farmos/asset_types.py
deleted file mode 100644
index 75eebbe..0000000
--- a/src/wuttafarm/web/views/farmos/asset_types.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS asset types
-"""
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class AssetTypeView(FarmOSMasterView):
- """
- View for farmOS asset types
- """
-
- model_name = "farmos_asset_type"
- model_title = "farmOS Asset Type"
- model_title_plural = "farmOS Asset Types"
-
- route_prefix = "farmos_asset_types"
- url_prefix = "/farmOS/asset-types"
-
- grid_columns = [
- "label",
- "description",
- ]
-
- sort_defaults = "label"
-
- form_fields = [
- "label",
- "description",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- asset_types = self.farmos_client.resource.get("asset_type", "asset_type")
- return [self.normalize_asset_type(t) for t in asset_types["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # label
- g.set_link("label")
- g.set_searchable("label")
-
- # description
- g.set_searchable("description")
-
- def get_instance(self):
- asset_type = self.farmos_client.resource.get_id(
- "asset_type", "asset_type", self.request.matchdict["uuid"]
- )
- return self.normalize_asset_type(asset_type["data"])
-
- def get_instance_title(self, asset_type):
- return asset_type["label"]
-
- def normalize_asset_type(self, asset_type):
- return {
- "uuid": asset_type["id"],
- "drupal_internal_id": asset_type["attributes"]["drupal_internal__id"],
- "label": asset_type["attributes"]["label"],
- "description": asset_type["attributes"]["description"],
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # description
- f.set_widget("description", "notes")
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- AssetTypeView = kwargs.get("AssetTypeView", base["AssetTypeView"])
- AssetTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/groups.py b/src/wuttafarm/web/views/farmos/groups.py
deleted file mode 100644
index 4664a6b..0000000
--- a/src/wuttafarm/web/views/farmos/groups.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS Groups
-"""
-
-import datetime
-
-import colander
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-from wuttaweb.forms.schema import WuttaDateTime
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
-
-
-class GroupView(FarmOSMasterView):
- """
- View for farmOS Groups
- """
-
- model_name = "farmos_group"
- model_title = "farmOS Group"
- model_title_plural = "farmOS Groups"
-
- route_prefix = "farmos_groups"
- url_prefix = "/farmOS/groups"
-
- farmos_refurl_path = "/assets/group"
-
- grid_columns = [
- "name",
- "is_fixed",
- "is_location",
- "status",
- "changed",
- ]
-
- sort_defaults = "name"
-
- form_fields = [
- "name",
- "is_fixed",
- "is_location",
- "status",
- "notes",
- "created",
- "changed",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- groups = self.farmos_client.resource.get("asset", "group")
- return [self.normalize_group(a) for a in groups["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
- g.set_searchable("name")
-
- # is_fixed
- g.set_renderer("is_fixed", "boolean")
-
- # is_location
- g.set_renderer("is_location", "boolean")
-
- # changed
- g.set_renderer("changed", "datetime")
-
- def get_instance(self):
-
- group = self.farmos_client.resource.get_id(
- "asset", "group", self.request.matchdict["uuid"]
- )
-
- return self.normalize_group(group["data"])
-
- def get_instance_title(self, group):
- return group["name"]
-
- def normalize_group(self, group):
-
- if created := group["attributes"].get("created"):
- created = datetime.datetime.fromisoformat(created)
- created = self.app.localtime(created)
-
- if changed := group["attributes"].get("changed"):
- changed = datetime.datetime.fromisoformat(changed)
- changed = self.app.localtime(changed)
-
- return {
- "uuid": group["id"],
- "drupal_internal_id": group["attributes"]["drupal_internal__id"],
- "name": group["attributes"]["name"],
- "created": created,
- "changed": changed,
- "is_fixed": group["attributes"]["is_fixed"],
- "is_location": group["attributes"]["is_location"],
- "status": group["attributes"]["status"],
- "notes": group["attributes"]["notes"]["value"],
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # is_fixed
- f.set_node("is_fixed", colander.Boolean())
-
- # is_location
- f.set_node("is_location", colander.Boolean())
-
- # notes
- f.set_widget("notes", "notes")
-
- # created
- f.set_node("created", WuttaDateTime())
- f.set_widget("created", WuttaDateTimeWidget(self.request))
-
- # changed
- f.set_node("changed", WuttaDateTime())
- f.set_widget("changed", WuttaDateTimeWidget(self.request))
-
- def get_xref_buttons(self, group):
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(f"/asset/{group['drupal_internal_id']}"),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- GroupView = kwargs.get("GroupView", base["GroupView"])
- GroupView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/land_assets.py b/src/wuttafarm/web/views/farmos/land_assets.py
deleted file mode 100644
index a496cc5..0000000
--- a/src/wuttafarm/web/views/farmos/land_assets.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS Land Assets
-"""
-
-import datetime
-
-import colander
-
-from wuttaweb.forms.schema import WuttaDateTime
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class LandAssetView(FarmOSMasterView):
- """
- View for farmOS Land Assets
- """
-
- model_name = "farmos_land_asset"
- model_title = "farmOS Land Asset"
- model_title_plural = "farmOS Land Assets"
-
- route_prefix = "farmos_land_assets"
- url_prefix = "/farmOS/land"
-
- farmos_refurl_path = "/assets/land"
-
- grid_columns = [
- "name",
- "is_fixed",
- "is_location",
- "status",
- "changed",
- ]
-
- sort_defaults = "name"
-
- form_fields = [
- "name",
- "is_fixed",
- "is_location",
- "status",
- "notes",
- "created",
- "changed",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- land_assets = self.farmos_client.resource.get("asset", "land")
- return [self.normalize_land_asset(l) for l in land_assets["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
- g.set_searchable("name")
-
- # is_fixed
- g.set_renderer("is_fixed", "boolean")
-
- # is_location
- g.set_renderer("is_location", "boolean")
-
- # created
- g.set_renderer("created", "datetime")
-
- # changed
- g.set_renderer("changed", "datetime")
-
- def get_instance(self):
- land_asset = self.farmos_client.resource.get_id(
- "asset", "land", self.request.matchdict["uuid"]
- )
- return self.normalize_land_asset(land_asset["data"])
-
- def get_instance_title(self, land_asset):
- return land_asset["name"]
-
- def normalize_land_asset(self, land):
-
- if created := land["attributes"].get("created"):
- created = datetime.datetime.fromisoformat(created)
- created = self.app.localtime(created)
-
- if changed := land["attributes"].get("changed"):
- changed = datetime.datetime.fromisoformat(changed)
- changed = self.app.localtime(changed)
-
- if notes := land["attributes"]["notes"]:
- notes = notes["value"]
-
- return {
- "uuid": land["id"],
- "drupal_internal_id": land["attributes"]["drupal_internal__id"],
- "name": land["attributes"]["name"],
- "created": created,
- "changed": changed,
- "is_fixed": land["attributes"]["is_fixed"],
- "is_location": land["attributes"]["is_location"],
- "status": land["attributes"]["status"],
- "notes": notes or colander.null,
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # is_fixed
- f.set_node("is_fixed", colander.Boolean())
-
- # is_location
- f.set_node("is_location", colander.Boolean())
-
- # notes
- f.set_widget("notes", "notes")
-
- # created
- f.set_node("created", WuttaDateTime())
- f.set_widget("created", WuttaDateTimeWidget(self.request))
-
- # changed
- f.set_node("changed", WuttaDateTime())
- f.set_widget("changed", WuttaDateTimeWidget(self.request))
-
- def get_xref_buttons(self, land):
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(f"/asset/{land['drupal_internal_id']}"),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- LandAssetView = kwargs.get("LandAssetView", base["LandAssetView"])
- LandAssetView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/land_types.py b/src/wuttafarm/web/views/farmos/land_types.py
deleted file mode 100644
index aadece8..0000000
--- a/src/wuttafarm/web/views/farmos/land_types.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS land types
-"""
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class LandTypeView(FarmOSMasterView):
- """
- Master view for Land Types in farmOS.
- """
-
- model_name = "farmos_land_type"
- model_title = "farmOS Land Type"
- model_title_plural = "farmOS Land Types"
-
- route_prefix = "farmos_land_types"
- url_prefix = "/farmOS/land-types"
-
- grid_columns = [
- "label",
- ]
-
- sort_defaults = "label"
-
- form_fields = [
- "label",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- land_types = self.farmos_client.resource.get("land_type", "land_type")
- return [self.normalize_land_type(t) for t in land_types["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # label
- g.set_link("label")
- g.set_searchable("label")
-
- def get_instance(self):
- land_type = self.farmos_client.resource.get_id(
- "land_type", "land_type", self.request.matchdict["uuid"]
- )
- return self.normalize_land_type(land_type["data"])
-
- def get_instance_title(self, land_type):
- return land_type["label"]
-
- def normalize_land_type(self, land_type):
- return {
- "uuid": land_type["id"],
- "drupal_internal_id": land_type["attributes"]["drupal_internal__id"],
- "label": land_type["attributes"]["label"],
- }
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- LandTypeView = kwargs.get("LandTypeView", base["LandTypeView"])
- LandTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/log_types.py b/src/wuttafarm/web/views/farmos/log_types.py
deleted file mode 100644
index 6e72f8f..0000000
--- a/src/wuttafarm/web/views/farmos/log_types.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS log types
-"""
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class LogTypeView(FarmOSMasterView):
- """
- Master view for Log Types in farmOS.
- """
-
- model_name = "farmos_log_type"
- model_title = "farmOS Log Type"
- model_title_plural = "farmOS Log Types"
-
- route_prefix = "farmos_log_types"
- url_prefix = "/farmOS/log-types"
-
- grid_columns = [
- "label",
- "description",
- ]
-
- sort_defaults = "label"
-
- form_fields = [
- "label",
- "description",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- log_types = self.farmos_client.resource.get("log_type", "log_type")
- return [self.normalize_log_type(t) for t in log_types["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # label
- g.set_link("label")
- g.set_searchable("label")
-
- def get_instance(self):
- log_type = self.farmos_client.resource.get_id(
- "log_type", "log_type", self.request.matchdict["uuid"]
- )
- return self.normalize_log_type(log_type["data"])
-
- def get_instance_title(self, log_type):
- return log_type["label"]
-
- def normalize_log_type(self, log_type):
- return {
- "uuid": log_type["id"],
- "drupal_internal_id": log_type["attributes"]["drupal_internal__id"],
- "label": log_type["attributes"]["label"],
- "description": log_type["attributes"]["description"],
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # description
- f.set_widget("description", "notes")
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"])
- LogTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/logs_activity.py b/src/wuttafarm/web/views/farmos/logs_activity.py
deleted file mode 100644
index 61b4e85..0000000
--- a/src/wuttafarm/web/views/farmos/logs_activity.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS activity logs
-"""
-
-import datetime
-
-import colander
-
-from wuttaweb.forms.schema import WuttaDateTime
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class ActivityLogView(FarmOSMasterView):
- """
- View for farmOS activity logs
- """
-
- model_name = "farmos_activity_log"
- model_title = "farmOS Activity Log"
- model_title_plural = "farmOS Activity Logs"
-
- route_prefix = "farmos_logs_activity"
- url_prefix = "/farmOS/logs/activity"
-
- farmos_refurl_path = "/logs/activity"
-
- grid_columns = [
- "name",
- "timestamp",
- "status",
- ]
-
- sort_defaults = ("timestamp", "desc")
-
- form_fields = [
- "name",
- "timestamp",
- "status",
- "notes",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- logs = self.farmos_client.log.get("activity")
- return [self.normalize_log(t) for t in logs["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
- g.set_searchable("name")
-
- # timestamp
- g.set_renderer("timestamp", "datetime")
-
- def get_instance(self):
- log = self.farmos_client.log.get_id("activity", self.request.matchdict["uuid"])
- return self.normalize_log(log["data"])
-
- def get_instance_title(self, log):
- return log["name"]
-
- def normalize_log(self, log):
-
- if timestamp := log["attributes"]["timestamp"]:
- timestamp = datetime.datetime.fromisoformat(timestamp)
- timestamp = self.app.localtime(timestamp)
-
- if notes := log["attributes"]["notes"]:
- notes = notes["value"]
-
- return {
- "uuid": log["id"],
- "drupal_internal_id": log["attributes"]["drupal_internal__id"],
- "name": log["attributes"]["name"],
- "timestamp": timestamp,
- "status": log["attributes"]["status"],
- "notes": notes or colander.null,
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # timestamp
- f.set_node("timestamp", WuttaDateTime())
- f.set_widget("timestamp", WuttaDateTimeWidget(self.request))
-
- # notes
- f.set_widget("notes", "notes")
-
- def get_xref_buttons(self, log):
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(f"/log/{log['drupal_internal_id']}"),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- ActivityLogView = kwargs.get("ActivityLogView", base["ActivityLogView"])
- ActivityLogView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/master.py b/src/wuttafarm/web/views/farmos/master.py
index 59003d0..eed04d1 100644
--- a/src/wuttafarm/web/views/farmos/master.py
+++ b/src/wuttafarm/web/views/farmos/master.py
@@ -45,12 +45,6 @@ class FarmOSMasterView(MasterView):
farmos_refurl_path = None
- labels = {
- "raw_image_url": "Raw Image URL",
- "large_image_url": "Large Image URL",
- "thumbnail_image_url": "Thumbnail Image URL",
- }
-
def __init__(self, request, context=None):
super().__init__(request, context=context)
self.farmos_client = self.get_farmos_client()
diff --git a/src/wuttafarm/web/views/farmos/structure_types.py b/src/wuttafarm/web/views/farmos/structure_types.py
deleted file mode 100644
index 3fe4741..0000000
--- a/src/wuttafarm/web/views/farmos/structure_types.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS structure types
-"""
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class StructureTypeView(FarmOSMasterView):
- """
- Master view for Structure Types in farmOS.
- """
-
- model_name = "farmos_structure_type"
- model_title = "farmOS Structure Type"
- model_title_plural = "farmOS Structure Types"
-
- route_prefix = "farmos_structure_types"
- url_prefix = "/farmOS/structure-types"
-
- grid_columns = [
- "label",
- ]
-
- sort_defaults = "label"
-
- form_fields = [
- "label",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- structure_types = self.farmos_client.resource.get(
- "structure_type", "structure_type"
- )
- return [self.normalize_structure_type(t) for t in structure_types["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # label
- g.set_link("label")
- g.set_searchable("label")
-
- def get_instance(self):
- structure_type = self.farmos_client.resource.get_id(
- "structure_type", "structure_type", self.request.matchdict["uuid"]
- )
- return self.normalize_structure_type(structure_type["data"])
-
- def get_instance_title(self, structure_type):
- return structure_type["label"]
-
- def normalize_structure_type(self, structure_type):
- return {
- "uuid": structure_type["id"],
- "drupal_internal_id": structure_type["attributes"]["drupal_internal__id"],
- "label": structure_type["attributes"]["label"],
- }
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- StructureTypeView = kwargs.get("StructureTypeView", base["StructureTypeView"])
- StructureTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/structures.py b/src/wuttafarm/web/views/farmos/structures.py
deleted file mode 100644
index bbc4f1f..0000000
--- a/src/wuttafarm/web/views/farmos/structures.py
+++ /dev/null
@@ -1,209 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS Structures
-"""
-
-import datetime
-
-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.widgets import ImageWidget
-
-
-class StructureView(FarmOSMasterView):
- """
- View for farmOS Structures
- """
-
- model_name = "farmos_structure"
- model_title = "farmOS Structure"
- model_title_plural = "farmOS Structures"
-
- route_prefix = "farmos_structures"
- url_prefix = "/farmOS/structures"
-
- farmos_refurl_path = "/assets/structure"
-
- grid_columns = [
- "name",
- "status",
- "created",
- "changed",
- ]
-
- sort_defaults = "name"
-
- form_fields = [
- "name",
- "status",
- "structure_type",
- "is_location",
- "is_fixed",
- "notes",
- "created",
- "changed",
- "raw_image_url",
- "large_image_url",
- "thumbnail_image_url",
- "image",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- structures = self.farmos_client.resource.get("asset", "structure")
- return [self.normalize_structure(s) for s in structures["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
- g.set_searchable("name")
-
- # created
- g.set_renderer("created", "datetime")
-
- # changed
- g.set_renderer("changed", "datetime")
-
- def get_instance(self):
- structure = self.farmos_client.resource.get_id(
- "asset", "structure", self.request.matchdict["uuid"]
- )
-
- data = self.normalize_structure(structure["data"])
-
- if relationships := structure["data"].get("relationships"):
-
- # 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, structure):
- return structure["name"]
-
- def normalize_structure(self, structure):
-
- if created := structure["attributes"].get("created"):
- created = datetime.datetime.fromisoformat(created)
- created = self.app.localtime(created)
-
- if changed := structure["attributes"].get("changed"):
- changed = datetime.datetime.fromisoformat(changed)
- changed = self.app.localtime(changed)
-
- return {
- "uuid": structure["id"],
- "drupal_internal_id": structure["attributes"]["drupal_internal__id"],
- "name": structure["attributes"]["name"],
- "structure_type": structure["attributes"]["structure_type"],
- "is_fixed": structure["attributes"]["is_fixed"],
- "is_location": structure["attributes"]["is_location"],
- "notes": structure["attributes"]["notes"] or colander.null,
- "status": structure["attributes"]["status"],
- "created": created,
- "changed": changed,
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
- structure = f.model_instance
-
- # is_fixed
- f.set_node("is_fixed", colander.Boolean())
-
- # is_location
- f.set_node("is_location", colander.Boolean())
-
- # notes
- f.set_widget("notes", "notes")
-
- # created
- f.set_node("created", WuttaDateTime())
- f.set_widget("created", WuttaDateTimeWidget(self.request))
-
- # changed
- f.set_node("changed", WuttaDateTime())
- f.set_widget("changed", WuttaDateTimeWidget(self.request))
-
- # image
- if url := structure.get("large_image_url"):
- f.set_widget("image", ImageWidget("structure image"))
- f.set_default("image", url)
-
- def get_xref_buttons(self, structure):
- drupal_id = structure["drupal_internal_id"]
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(f"/asset/{drupal_id}"),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- StructureView = kwargs.get("StructureView", base["StructureView"])
- StructureView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/users.py b/src/wuttafarm/web/views/farmos/users.py
deleted file mode 100644
index 317bfe3..0000000
--- a/src/wuttafarm/web/views/farmos/users.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS Users
-"""
-
-import datetime
-
-import colander
-
-from wuttaweb.forms.schema import WuttaDateTime
-
-from wuttafarm.web.views.farmos import FarmOSMasterView
-
-
-class UserView(FarmOSMasterView):
- """
- Master view for Farm Animals
- """
-
- model_name = "farmos_user"
- model_title = "farmOS User"
- model_title_plural = "farmOS Users"
-
- route_prefix = "farmos_users"
- url_prefix = "/farmOS/users"
-
- farmos_refurl_path = "/people"
-
- grid_columns = [
- "display_name",
- ]
-
- sort_defaults = "display_name"
-
- form_fields = [
- "display_name",
- "name",
- "mail",
- "timezone",
- "created",
- "changed",
- ]
-
- def get_grid_data(self, columns=None, session=None):
- users = self.farmos_client.resource.get("user", "user")
- return [self.normalize_user(u) for u in users["data"]]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # display_name
- g.set_link("display_name")
- g.set_searchable("display_name")
-
- def get_instance(self):
- user = self.farmos_client.resource.get_id(
- "user", "user", self.request.matchdict["uuid"]
- )
- return self.normalize_user(user["data"])
-
- def get_instance_title(self, user):
- return user["display_name"]
-
- def normalize_user(self, user):
-
- if created := user["attributes"].get("created"):
- created = datetime.datetime.fromisoformat(created)
- created = self.app.localtime(created)
-
- if changed := user["attributes"].get("changed"):
- changed = datetime.datetime.fromisoformat(changed)
- changed = self.app.localtime(changed)
-
- return {
- "uuid": user["id"],
- "drupal_internal_id": user["attributes"].get("drupal_internal__uid"),
- "display_name": user["attributes"]["display_name"],
- "name": user["attributes"].get("name") or colander.null,
- "mail": user["attributes"].get("mail") or colander.null,
- "timezone": user["attributes"].get("timezone") or colander.null,
- "created": created,
- "changed": changed,
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
- user = f.model_instance
-
- # created
- f.set_node("created", WuttaDateTime())
-
- # changed
- f.set_node("changed", WuttaDateTime())
-
- def get_xref_buttons(self, user):
- if drupal_id := user["drupal_internal_id"]:
- return [
- self.make_button(
- "View in farmOS",
- primary=True,
- url=self.app.get_farmos_url(f"/user/{drupal_id}"),
- target="_blank",
- icon_left="external-link-alt",
- ),
- ]
- return None
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- UserView = kwargs.get("UserView", base["UserView"])
- UserView.defaults(config)
-
-
-def includeme(config):
- defaults(config)