diff --git a/CHANGELOG.md b/CHANGELOG.md index 579fc2a..6c85559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,30 +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.11.2 (2026-03-21) - -### Fix - -- use separate thread to sync changes to farmOS -- avoid error if asset has no geometry - -## v0.11.1 (2026-03-21) - -### Fix - -- improve behavior when deleting mirrored record from farmOS -- use correct uuid when processing webhook to delete record - -## v0.11.0 (2026-03-15) - -### Feat - -- show basic map for "fixed" assets - -### Fix - -- include LogQuantity changes when viewing Log revision - ## v0.10.0 (2026-03-11) ### Feat diff --git a/pyproject.toml b/pyproject.toml index b702d8c..7fdd859 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "WuttaFarm" -version = "0.11.2" +version = "0.10.0" description = "Web app to integrate with and extend farmOS" readme = "README.md" authors = [ @@ -34,7 +34,7 @@ dependencies = [ "pyramid_exclog", "uvicorn[standard]", "WuttaSync", - "WuttaWeb[continuum]>=0.30.1", + "WuttaWeb[continuum]>=0.29.2", ] diff --git a/src/wuttafarm/cli/process_webhooks.py b/src/wuttafarm/cli/process_webhooks.py index 9d66a70..9731247 100644 --- a/src/wuttafarm/cli/process_webhooks.py +++ b/src/wuttafarm/cli/process_webhooks.py @@ -94,7 +94,8 @@ class ChangeProcessor: return # delete corresponding record from our app - if obj := importer.get_target_object((change.farmos_uuid,)): + obj = importer.get_target_object((change.uuid,)) + if obj: importer.delete_target_object(obj) # TODO: this should live elsewhere diff --git a/src/wuttafarm/web/templates/assets/master/view.mako b/src/wuttafarm/web/templates/assets/master/view.mako index 5b7b822..dac5a1c 100644 --- a/src/wuttafarm/web/templates/assets/master/view.mako +++ b/src/wuttafarm/web/templates/assets/master/view.mako @@ -10,74 +10,5 @@ % endif -
- - ## main form -
- ${parent.page_content()} -
- - ## location map - % if map_polygon: -
- % endif - -
- - - -<%def name="modify_vue_vars()"> - ${parent.modify_vue_vars()} - % if map_polygon: - - % endif + ${parent.page_content()} diff --git a/src/wuttafarm/web/templates/base.mako b/src/wuttafarm/web/templates/base.mako index caa5c67..b28b52f 100644 --- a/src/wuttafarm/web/templates/base.mako +++ b/src/wuttafarm/web/templates/base.mako @@ -1,16 +1,6 @@ <%inherit file="wuttaweb:templates/base.mako" /> <%namespace file="/wuttafarm-components.mako" import="make_wuttafarm_components" /> -<%def name="head_tags()"> - ${parent.head_tags()} - - ## TODO: this likely does not belong in the base template, and should be - ## included per template where actually needed. but this is easier for now. - - - - - <%def name="index_title_controls()"> ${parent.index_title_controls()} diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index 1ada778..64f4dbc 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -23,7 +23,6 @@ Master view for Assets """ -import re from collections import OrderedDict from webhelpers2.html import tags @@ -360,38 +359,6 @@ class AssetMasterView(WuttaFarmMasterView): return buttons - def get_template_context(self, context): - context = super().get_template_context(context) - - if self.viewing: - asset = context["instance"] - - # add location geometry if applicable - if asset.is_fixed and asset.farmos_uuid and not self.app.is_standalone(): - - # TODO: eventually sync GIS data, avoid this API call? - client = get_farmos_client_for_user(self.request) - result = client.asset.get_id(asset.asset_type, asset.farmos_uuid) - if geometry := result["data"]["attributes"]["intrinsic_geometry"]: - - context["map_center"] = [geometry["lon"], geometry["lat"]] - - context["map_bounds"] = [ - [geometry["left"], geometry["bottom"]], - [geometry["right"], geometry["top"]], - ] - - if match := re.match( - r"^POLYGON \(\((?P[^\)]+)\)\)$", geometry["value"] - ): - points = match.group("points").split(", ") - points = [ - [float(pt) for pt in pair.split(" ")] for pair in points - ] - context["map_polygon"] = [points] - - return context - def get_version_joins(self): """ We override this to declare the relationship between the diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py index 2a4e6e0..3d91ba1 100644 --- a/src/wuttafarm/web/views/logs.py +++ b/src/wuttafarm/web/views/logs.py @@ -491,7 +491,6 @@ class LogMasterView(WuttaFarmMasterView): return super().get_version_joins() + [ model.Log, (model.LogAsset, "log_uuid", "uuid"), - (model.LogQuantity, "log_uuid", "uuid"), ] diff --git a/src/wuttafarm/web/views/master.py b/src/wuttafarm/web/views/master.py index c828b96..d9fe986 100644 --- a/src/wuttafarm/web/views/master.py +++ b/src/wuttafarm/web/views/master.py @@ -23,10 +23,6 @@ Base class for WuttaFarm master views """ -import threading -import time - -import requests from webhelpers2.html import tags from wuttaweb.views import MasterView @@ -111,36 +107,17 @@ class WuttaFarmMasterView(MasterView): f.set_readonly("drupal_id") def persist(self, obj, session=None): - session = session or self.Session() # save per usual super().persist(obj, session) # maybe also sync change to farmOS if self.app.is_farmos_mirror(): - if self.creating: - session.flush() # need the new uuid client = get_farmos_client_for_user(self.request) - thread = threading.Thread( - target=self.auto_sync_to_farmos, args=(client, obj.uuid) - ) - thread.start() + self.auto_sync_to_farmos(client, obj) - def auto_sync_to_farmos(self, client, uuid): - model = self.app.model - model_class = self.get_model_class() - - 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 - - obj = None - while not obj: - obj = session.get(model_class, uuid) - if not obj: - time.sleep(0.1) - - self.app.auto_sync_to_farmos(obj, client=client, require=False) + def auto_sync_to_farmos(self, client, obj): + self.app.auto_sync_to_farmos(obj, client=client, require=False) def get_farmos_entity_type(self): if self.farmos_entity_type: @@ -168,24 +145,10 @@ class WuttaFarmMasterView(MasterView): # maybe delete from farmOS also if farmos_uuid: + entity_type = self.get_farmos_entity_type() + bundle = self.get_farmos_bundle() client = get_farmos_client_for_user(self.request) - # nb. must use separate thread to avoid some kind of race - # condition (?) - seems as though maybe a "boomerang" - # effect is happening; this seems to help anyway - thread = threading.Thread( - target=self.delete_from_farmos, args=(client, farmos_uuid) - ) - thread.start() - - def delete_from_farmos(self, client, farmos_uuid): - entity_type = self.get_farmos_entity_type() - bundle = self.get_farmos_bundle() - try: client.resource.delete(entity_type, bundle, farmos_uuid) - except requests.HTTPError as exc: - # ignore if record not found in farmOS - if exc.response.status_code != 404: - raise class TaxonomyMasterView(WuttaFarmMasterView): diff --git a/src/wuttafarm/web/views/quick/eggs.py b/src/wuttafarm/web/views/quick/eggs.py index fded73c..8aae46e 100644 --- a/src/wuttafarm/web/views/quick/eggs.py +++ b/src/wuttafarm/web/views/quick/eggs.py @@ -24,8 +24,6 @@ Quick Form for "Eggs" """ import json -import threading -import time import colander from deform.widget import SelectWidget @@ -333,43 +331,13 @@ class EggsQuickForm(QuickFormView): session.flush() if self.app.is_farmos_mirror(): - thread = threading.Thread( - target=self.auto_sync_to_farmos, - args=(log.uuid, quantity.uuid, new_unit.uuid if new_unit else None), - ) - thread.start() + if new_unit: + self.app.auto_sync_to_farmos(unit, client=self.farmos_client) + self.app.auto_sync_to_farmos(quantity, client=self.farmos_client) + self.app.auto_sync_to_farmos(log, client=self.farmos_client) return log - def auto_sync_to_farmos(self, log_uuid, quantity_uuid, new_unit_uuid): - model = self.app.model - - 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 - - if new_unit_uuid: - new_unit = None - while not new_unit: - new_unit = session.get(model.Unit, new_unit_uuid) - if not new_unit: - time.sleep(0.1) - self.app.auto_sync_to_farmos(unit, client=self.farmos_client) - - quantity = None - while not quantity: - quantity = session.get(model.StandardQuantity, quantity_uuid) - if not quantity: - time.sleep(0.1) - self.app.auto_sync_to_farmos(quantity, client=self.farmos_client) - - log = None - while not log: - log = session.get(model.HarvestLog, log_uuid) - if not log: - time.sleep(0.1) - self.app.auto_sync_to_farmos(log, client=self.farmos_client) - def redirect_after_save(self, log): model = self.app.model