diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py index d4e7715..ab6f440 100644 --- a/src/wuttafarm/web/menus.py +++ b/src/wuttafarm/web/menus.py @@ -71,6 +71,12 @@ class WuttaFarmMenuHandler(base.MenuHandler): "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", diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py index 9f4100b..f46c018 100644 --- a/src/wuttafarm/web/views/common.py +++ b/src/wuttafarm/web/views/common.py @@ -62,6 +62,8 @@ class CommonView(base.CommonView): "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", diff --git a/src/wuttafarm/web/views/farmos/__init__.py b/src/wuttafarm/web/views/farmos/__init__.py index cc389db..deacd7d 100644 --- a/src/wuttafarm/web/views/farmos/__init__.py +++ b/src/wuttafarm/web/views/farmos/__init__.py @@ -37,3 +37,4 @@ def includeme(config): 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/logs_activity.py b/src/wuttafarm/web/views/farmos/logs_activity.py new file mode 100644 index 0000000..61b4e85 --- /dev/null +++ b/src/wuttafarm/web/views/farmos/logs_activity.py @@ -0,0 +1,136 @@ +# -*- 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)