diff --git a/CHANGELOG.md b/CHANGELOG.md index 579fc2a..4ee6b10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,20 +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 diff --git a/pyproject.toml b/pyproject.toml index b702d8c..c1a5cc0 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.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.3", ] 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/views/assets.py b/src/wuttafarm/web/views/assets.py index 1ada778..35c3b21 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -372,23 +372,21 @@ class AssetMasterView(WuttaFarmMasterView): # 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"]: + geometry = result["data"]["attributes"]["intrinsic_geometry"] - context["map_center"] = [geometry["lon"], geometry["lat"]] + context["map_center"] = [geometry["lon"], geometry["lat"]] - context["map_bounds"] = [ - [geometry["left"], geometry["bottom"]], - [geometry["right"], geometry["top"]], - ] + 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] + 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 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