From d9ed0de775a2e9d7111f5d8b520546febc1c21ab Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 22 Mar 2026 13:58:04 -0500 Subject: [PATCH 1/4] fix: split numerator/denominator for quantity values --- src/wuttafarm/web/views/logs.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py index 2a4e6e0..4924b31 100644 --- a/src/wuttafarm/web/views/logs.py +++ b/src/wuttafarm/web/views/logs.py @@ -23,6 +23,8 @@ Base views for Logs """ +import decimal +import time from collections import OrderedDict import colander @@ -398,11 +400,12 @@ class LogMasterView(WuttaFarmMasterView): units = session.get(model.Unit, new_qty["units"]["uuid"]) assert units if new_qty["uuid"].startswith("new_"): + num, denom = decimal.Decimal(new_qty["value"]).as_integer_ratio() qty = self.app.make_true_quantity( new_qty["quantity_type"]["drupal_id"], measure_id=new_qty["measure"], - value_numerator=int(new_qty["value"]), - value_denominator=1, + value_numerator=num, + value_denominator=denom, units=units, ) # nb. must ensure "typed" quantity record persists! @@ -441,18 +444,25 @@ class LogMasterView(WuttaFarmMasterView): if old_mtype.uuid.hex not in desired: quantity.material_types.remove(old_mtype) - def auto_sync_to_farmos(self, client, log): + def auto_sync_to_farmos(self, client, uuid): model = self.app.model - session = self.Session() + model_class = self.get_model_class() - # nb. ensure quantities have uuid keys - session.flush() + with self.app.short_session(commit=True) as session: + if user := session.query(model.User).filter_by(username="farmos").first(): + session.info["continuum_user_id"] = user.uuid - for qty in log.quantities: - qty = self.app.get_true_quantity(qty) - self.app.auto_sync_to_farmos(qty, client=client) + log = None + while not log: + log = session.get(model_class, uuid) + if not log: + time.sleep(0.1) - self.app.auto_sync_to_farmos(log, client=client) + for qty in log.quantities: + qty = self.app.get_true_quantity(qty) + self.app.auto_sync_to_farmos(qty, client=client) + + self.app.auto_sync_to_farmos(log, client=client) def get_farmos_url(self, log): return self.app.get_farmos_url(f"/log/{log.drupal_id}") From 066fcb857f8b8a46db36bace28d06b755acd0933 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 22 Mar 2026 14:24:20 -0500 Subject: [PATCH 2/4] fix: set material types when creating new log w/ material quantity --- src/wuttafarm/web/menus.py | 10 +++++----- src/wuttafarm/web/templates/wuttafarm-components.mako | 11 ++++++++--- src/wuttafarm/web/views/logs.py | 2 ++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py index 2756738..62d3d9c 100644 --- a/src/wuttafarm/web/menus.py +++ b/src/wuttafarm/web/menus.py @@ -138,6 +138,11 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "land_types", "perm": "land_types.list", }, + { + "title": "Material Types", + "route": "material_types", + "perm": "material_types.list", + }, { "title": "Plant Types", "route": "plant_types", @@ -218,11 +223,6 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "log_types", "perm": "log_types.list", }, - { - "title": "Material Types", - "route": "material_types", - "perm": "material_types.list", - }, { "title": "Measures", "route": "measures", diff --git a/src/wuttafarm/web/templates/wuttafarm-components.mako b/src/wuttafarm/web/templates/wuttafarm-components.mako index 890568f..b7c81c7 100644 --- a/src/wuttafarm/web/templates/wuttafarm-components.mako +++ b/src/wuttafarm/web/templates/wuttafarm-components.mako @@ -847,12 +847,18 @@ } const measureMap = {} - for (let m of this.measures) { + for (const m of this.measures) { measureMap[m.drupal_id] = m.name } + const quantityTypeMap = {} + for (const qt of this.quantityTypes) { + quantityTypeMap[qt.drupal_id] = qt.name + } + return { measureMap, + quantityTypeMap, quantityType: this.defaultQuantityType, editShowDialog: false, editNew: true, @@ -893,8 +899,7 @@ this.newQuantity.quantity_type = { drupal_id: this.quantityType, - ## TODO: add support for other quantity types - name: "Standard", + name: this.quantityTypeMap[this.quantityType], } this.newQuantity.measure = null diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py index 4924b31..df2b01f 100644 --- a/src/wuttafarm/web/views/logs.py +++ b/src/wuttafarm/web/views/logs.py @@ -408,6 +408,8 @@ class LogMasterView(WuttaFarmMasterView): value_denominator=denom, units=units, ) + if qty.quantity_type_id == "material": + self.set_material_types(qty, new_qty["material_types"]) # nb. must ensure "typed" quantity record persists! session.add(qty) # but must add "generic" quantity record to log From 0ab511d841e46dd2ab192b0171cee6b99eec33bb Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 4 May 2026 21:43:16 -0500 Subject: [PATCH 3/4] docs: add "intro" doc to better describe integration modes --- docs/index.rst | 1 + docs/narr/intro.rst | 123 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 docs/narr/intro.rst diff --git a/docs/index.rst b/docs/index.rst index be04bee..89d3d70 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ include: :maxdepth: 2 :caption: Documentation: + narr/intro narr/install narr/auth narr/features diff --git a/docs/narr/intro.rst b/docs/narr/intro.rst new file mode 100644 index 0000000..4466d25 --- /dev/null +++ b/docs/narr/intro.rst @@ -0,0 +1,123 @@ + +============== + Introduction +============== + +This page hopefully conveys the what and why of WuttaFarm as a project. + +WuttaFarm can serve as a "supplement" or "alternative" to `farmOS +`_, depending on your needs. It has 3 distinct +"modes" of operation: + +* :ref:`wrapper (API only) ` +* :ref:`mirror (2-way sync) ` +* :ref:`standalone ` + + +Rationale +--------- + +This project exists primarily to scratch a personal itch, but the hope +of course is that others will find it useful also. This is partly why +the app has different integration modes. + +Realistically this is not (yet?) meant to be a "finished" app per se. +In that sense it is akin to farmOS itself, i.e. "part app, part +framework" which can be extended where needed. + +It purposefully does not try to re-invent every wheel provided by +farmOS. But it does re-invent "certain types" of wheels, to some +degree, depending on which mode is used. + +It should also be mentioned, the 3 "modes" are essentially just +abstractions for sake of convenience. The framework allows one to +customize however you want regardless of the "mode" in effect. + +The target audience I suppose, is folks like me who prefer Python over +PHP, and can customize away without having to touch Drupal. + + +.. _wrapper-mode: + +Wrapper Mode (API only) +----------------------- + +This is the default integration mode for the app. In wrapper mode, +WuttaFarm UI will only expose views which display/modify farmOS data +*directly* via its JSONAPI. The app will not expose any views which +operate on its "native" tables (for assets, logs etc.). + +Any data model extensions needed must of course be managed on the +farmOS side, since the native tables are not used for this mode. + +This mode is simplest (for farmOS integration) since every user action +goes immeditely through the API. There is no "sync" or caching +involved. + +There is one known issue with this mode, and it's kind of a bummer: + +When viewing the "list" page for any record type (e.g. Animal Assets), +due to a quirk with the JSONAPI, you can only see the "first 50" +results in the listing. However you can still filter/sort the +records, to hopefully find what you need. And you can still view +*any* record if you have its URL (which you can normally figure out +from the farmOS UUID value). + + +.. _mirror-mode: + +Mirror Mode (2-way sync) +------------------------ + +This mode exposes the native table views in addition to direct API +views. It's sort of a combination of the wrapper and standalone +modes, with an extra daemon process to handle some of the sync tasks. + +(It's also the most complex mode to setup; more "moving parts" which +means potentially more to go wrong and require troubleshooting.) + +Again this depends on your actual use case, but a "complete" setup for +this mode would mean: + +* changes made in WuttaFarm are synced to farmOS via JSONAPI +* changes made in farmOS are synced to WuttaFarm via webhook+daemon + +The latter includes changes made via the farmOS proper web app, and/or +changes made via the "direct API" views within WuttaFarm (or anything +else that invokes the farmOS API). This requires the extra daemon +process (running alongside the WuttaFarm web app process), as well as +the `webhooks module +`_ on +the farmOS side. + +It's possible to extend the data model on the WuttaFarm side with or +without doing so on the farmOS side, and vice versa. Although +regardless you may have to take both into consideration for your +overall sync needs. + +There is support for "full" data import/export between systems in both +directions, WuttaFarm <=> farmOS. Limited of course by the common +schema they share etc. This can be leveraged easily to include +nightly "data diff" checks, ensuring all stays in sync. + + + +.. _standalone-mode: + +Standalone Mode +--------------- + +This mode exposes *only* the native table views, and none of the +"direct API" views. This requires no farmOS at all. + +This lets you customize the data model as needed, using SQLAlchemy and +Alembic, as does the mirror mode. However no "sync" is required so +you don't have to consider the farmOS data model. + +While the wrapper mode is simplest for farmOS integration, this +standalone mode is even simpler, technically speaking. It should work +well if you just need the basic record-keeping aspects. + +The main downside with this mode is there is not (yet?) any +mapping/geometry support, nor is there support for image/file +attachments. From e27dad573b84fe262a8267ca498412476d28f1af Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 4 May 2026 21:44:24 -0500 Subject: [PATCH 4/4] =?UTF-8?q?bump:=20version=200.11.2=20=E2=86=92=200.11?= =?UTF-8?q?.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 579fc2a..781a26c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ 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.11.3 (2026-05-04) + +### Fix + +- set material types when creating new log w/ material quantity +- split numerator/denominator for quantity values + ## v0.11.2 (2026-03-21) ### Fix diff --git a/pyproject.toml b/pyproject.toml index b702d8c..4012f79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "WuttaFarm" -version = "0.11.2" +version = "0.11.3" description = "Web app to integrate with and extend farmOS" readme = "README.md" authors = [