feat: expose "current location" for assets
based on most recent movement log, as in farmOS
This commit is contained in:
parent
41870ee2e2
commit
759eb906b9
8 changed files with 124 additions and 19 deletions
|
|
@ -36,6 +36,21 @@ class WuttaFarmAppHandler(base.AppHandler):
|
|||
default_auth_handler_spec = "wuttafarm.auth:WuttaFarmAuthHandler"
|
||||
default_install_handler_spec = "wuttafarm.install:WuttaFarmInstallHandler"
|
||||
|
||||
def get_asset_handler(self):
|
||||
"""
|
||||
Get the configured asset handler.
|
||||
|
||||
:rtype: :class:`~wuttafarm.assets.AssetHandler`
|
||||
"""
|
||||
if "asset" not in self.handlers:
|
||||
spec = self.config.get(
|
||||
f"{self.appname}.asset_handler",
|
||||
default="wuttafarm.assets:AssetHandler",
|
||||
)
|
||||
factory = self.load_object(spec)
|
||||
self.handlers["asset"] = factory(self.config)
|
||||
return self.handlers["asset"]
|
||||
|
||||
def get_farmos_handler(self):
|
||||
"""
|
||||
Get the configured farmOS integration handler.
|
||||
|
|
|
|||
49
src/wuttafarm/assets.py
Normal file
49
src/wuttafarm/assets.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# -*- 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/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Asset handler
|
||||
"""
|
||||
|
||||
from wuttjamaican.app import GenericHandler
|
||||
|
||||
|
||||
class AssetHandler(GenericHandler):
|
||||
"""
|
||||
Base class and default implementation for the asset
|
||||
:term:`handler`.
|
||||
"""
|
||||
|
||||
def get_locations(self, asset):
|
||||
model = self.app.model
|
||||
session = self.app.get_session(asset)
|
||||
|
||||
loclog = (
|
||||
session.query(model.Log)
|
||||
.join(model.LogAsset)
|
||||
.filter(model.LogAsset.asset == asset)
|
||||
.filter(model.Log.is_movement == True)
|
||||
.order_by(model.Log.timestamp.desc())
|
||||
.first()
|
||||
)
|
||||
if loclog:
|
||||
return loclog.locations
|
||||
return []
|
||||
|
|
@ -364,7 +364,7 @@ class AssetParentRefs(WuttaSet):
|
|||
return AssetParentRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class LogAssetRefs(WuttaSet):
|
||||
class AssetRefs(WuttaSet):
|
||||
"""
|
||||
Schema type for Assets field (on a Log record)
|
||||
"""
|
||||
|
|
@ -376,9 +376,9 @@ class LogAssetRefs(WuttaSet):
|
|||
return {asset.uuid for asset in appstruct}
|
||||
|
||||
def widget_maker(self, **kwargs):
|
||||
from wuttafarm.web.forms.widgets import LogAssetRefsWidget
|
||||
from wuttafarm.web.forms.widgets import AssetRefsWidget
|
||||
|
||||
return LogAssetRefsWidget(self.request, **kwargs)
|
||||
return AssetRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class LogQuantityRefs(WuttaSet):
|
||||
|
|
@ -398,7 +398,7 @@ class LogQuantityRefs(WuttaSet):
|
|||
return LogQuantityRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class LogOwnerRefs(WuttaSet):
|
||||
class OwnerRefs(WuttaSet):
|
||||
"""
|
||||
Schema type for Owners field (on a Log record)
|
||||
"""
|
||||
|
|
@ -410,9 +410,9 @@ class LogOwnerRefs(WuttaSet):
|
|||
return {user.uuid for user in appstruct}
|
||||
|
||||
def widget_maker(self, **kwargs):
|
||||
from wuttafarm.web.forms.widgets import LogOwnerRefsWidget
|
||||
from wuttafarm.web.forms.widgets import OwnerRefsWidget
|
||||
|
||||
return LogOwnerRefsWidget(self.request, **kwargs)
|
||||
return OwnerRefsWidget(self.request, **kwargs)
|
||||
|
||||
|
||||
class Notes(colander.String):
|
||||
|
|
|
|||
|
|
@ -423,9 +423,9 @@ class AssetParentRefsWidget(WuttaCheckboxChoiceWidget):
|
|||
return super().serialize(field, cstruct, **kw)
|
||||
|
||||
|
||||
class LogAssetRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
class AssetRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
"""
|
||||
Widget for Assets field (on a Log record)
|
||||
Widget for Assets field (of various kinds).
|
||||
"""
|
||||
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
|
|
@ -486,9 +486,9 @@ class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
|
|||
return super().serialize(field, cstruct, **kw)
|
||||
|
||||
|
||||
class LogOwnerRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
class OwnerRefsWidget(WuttaCheckboxChoiceWidget):
|
||||
"""
|
||||
Widget for Owners field (on a Log record)
|
||||
Widget for Owners field (on an Asset or Log record)
|
||||
"""
|
||||
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class AnimalAssetView(AssetMasterView):
|
|||
farmos_bundle = "animal"
|
||||
|
||||
labels = {
|
||||
"animal_type": "Species/Breed",
|
||||
"animal_type": "Species / Breed",
|
||||
"is_sterile": "Sterile",
|
||||
}
|
||||
|
||||
|
|
@ -243,6 +243,8 @@ class AnimalAssetView(AssetMasterView):
|
|||
"is_sterile",
|
||||
"notes",
|
||||
"asset_type",
|
||||
"owners",
|
||||
"locations",
|
||||
"archived",
|
||||
"drupal_id",
|
||||
"farmos_uuid",
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from wuttaweb.db import Session
|
|||
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.db.model import Asset, Log
|
||||
from wuttafarm.web.forms.schema import AssetParentRefs
|
||||
from wuttafarm.web.forms.schema import AssetParentRefs, OwnerRefs, AssetRefs
|
||||
from wuttafarm.web.forms.widgets import ImageWidget
|
||||
from wuttafarm.util import get_log_type_enum
|
||||
from wuttafarm.web.util import get_farmos_client_for_user
|
||||
|
|
@ -140,6 +140,10 @@ class AssetMasterView(WuttaFarmMasterView):
|
|||
g.set_label("owners", "Owner")
|
||||
g.set_renderer("owners", self.render_owners_for_grid)
|
||||
|
||||
# locations
|
||||
g.set_label("locations", "Location")
|
||||
g.set_renderer("locations", self.render_locations_for_grid)
|
||||
|
||||
# archived
|
||||
g.set_renderer("archived", "boolean")
|
||||
g.set_sorter("archived", model.Asset.archived)
|
||||
|
|
@ -170,6 +174,21 @@ class AssetMasterView(WuttaFarmMasterView):
|
|||
|
||||
return ", ".join([user.username for user in asset.owners])
|
||||
|
||||
def render_locations_for_grid(self, asset, field, value):
|
||||
asset_handler = self.app.get_asset_handler()
|
||||
locations = asset_handler.get_locations(asset)
|
||||
|
||||
if self.farmos_style_grid_links:
|
||||
links = []
|
||||
for loc in locations:
|
||||
url = self.request.route_url(
|
||||
f"{loc.asset_type}_assets.view", uuid=loc.uuid
|
||||
)
|
||||
links.append(tags.link_to(str(loc), url))
|
||||
return ", ".join(links)
|
||||
|
||||
return ", ".join([str(loc) for loc in locations])
|
||||
|
||||
def grid_row_class(self, asset, data, i):
|
||||
""" """
|
||||
if asset.archived:
|
||||
|
|
@ -179,6 +198,7 @@ class AssetMasterView(WuttaFarmMasterView):
|
|||
def configure_form(self, form):
|
||||
f = form
|
||||
super().configure_form(f)
|
||||
asset_handler = self.app.get_asset_handler()
|
||||
asset = form.model_instance
|
||||
|
||||
# asset_type
|
||||
|
|
@ -191,12 +211,30 @@ class AssetMasterView(WuttaFarmMasterView):
|
|||
)
|
||||
f.set_readonly("asset_type")
|
||||
|
||||
# owners
|
||||
if self.creating or self.editing:
|
||||
f.remove("owners") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("owners", OwnerRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("owners", asset.owners)
|
||||
|
||||
# locations
|
||||
if self.creating or self.editing:
|
||||
# nb. this is a calculated field
|
||||
f.remove("locations")
|
||||
else:
|
||||
f.set_label("locations", "Current Location")
|
||||
f.set_node("locations", AssetRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("locations", asset_handler.get_locations(asset))
|
||||
|
||||
# parents
|
||||
if self.creating or self.editing:
|
||||
f.remove("parents") # TODO: add support for this
|
||||
else:
|
||||
f.set_node("parents", AssetParentRefs(self.request))
|
||||
f.set_default("parents", [p.parent_uuid for p in asset.asset._parents])
|
||||
f.set_default("parents", [p.uuid for p in asset.parents])
|
||||
|
||||
# notes
|
||||
f.set_widget("notes", "notes")
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class AssetMasterView(FarmOSMasterView):
|
|||
labels = {
|
||||
"name": "Asset Name",
|
||||
"asset_type_name": "Asset Type",
|
||||
"owners": "Owner",
|
||||
"locations": "Location",
|
||||
"thumbnail_url": "Thumbnail URL",
|
||||
"image_url": "Image URL",
|
||||
|
|
@ -104,6 +103,7 @@ class AssetMasterView(FarmOSMasterView):
|
|||
g.set_filter("name", StringFilter)
|
||||
|
||||
# owners
|
||||
g.set_label("owners", "Owner")
|
||||
g.set_renderer("owners", self.render_owners_for_grid)
|
||||
|
||||
# locations
|
||||
|
|
@ -239,6 +239,7 @@ class AssetMasterView(FarmOSMasterView):
|
|||
if self.creating or self.editing:
|
||||
f.remove("locations")
|
||||
else:
|
||||
f.set_label("locations", "Current Location")
|
||||
f.set_node("locations", FarmOSLocationRefs(self.request))
|
||||
|
||||
# owners
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
|||
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.db.model import LogType, Log
|
||||
from wuttafarm.web.forms.schema import LogAssetRefs, LogQuantityRefs, LogOwnerRefs
|
||||
from wuttafarm.web.forms.schema import AssetRefs, LogQuantityRefs, OwnerRefs
|
||||
from wuttafarm.util import get_log_type_enum
|
||||
|
||||
|
||||
|
|
@ -259,7 +259,7 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
if self.creating or self.editing:
|
||||
f.remove("assets") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("assets", LogAssetRefs(self.request))
|
||||
f.set_node("assets", AssetRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("assets", log.assets)
|
||||
|
||||
|
|
@ -267,7 +267,7 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
if self.creating or self.editing:
|
||||
f.remove("groups") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("groups", LogAssetRefs(self.request))
|
||||
f.set_node("groups", AssetRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("groups", log.groups)
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
if self.creating or self.editing:
|
||||
f.remove("locations") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("locations", LogAssetRefs(self.request))
|
||||
f.set_node("locations", AssetRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("locations", log.locations)
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
if self.creating or self.editing:
|
||||
f.remove("owners") # TODO: need to support this
|
||||
else:
|
||||
f.set_node("owners", LogOwnerRefs(self.request))
|
||||
f.set_node("owners", OwnerRefs(self.request))
|
||||
# nb. must explicity declare value for non-standard field
|
||||
f.set_default("owners", log.owners)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue