fix: update sterile, archived flags per farmOS 4.x

3.x should still work okay too though
This commit is contained in:
Lance Edgar 2026-02-14 14:37:41 -06:00
parent 35068c0cb1
commit 985d224cb8
8 changed files with 156 additions and 26 deletions

View file

@ -65,6 +65,24 @@ class WuttaFarmAppHandler(base.AppHandler):
handler = self.get_farmos_handler()
return handler.get_farmos_client(*args, **kwargs)
def is_farmos_3x(self, *args, **kwargs):
"""
Check if the farmOS version is 3.x. This is a convenience
wrapper around
:meth:`~wuttafarm.farmos.handler.FarmOSHandler.is_farmos_3x()`.
"""
handler = self.get_farmos_handler()
return handler.is_farmos_3x(*args, **kwargs)
def is_farmos_4x(self, *args, **kwargs):
"""
Check if the farmOS version is 4.x. This is a convenience
wrapper around
:meth:`~wuttafarm.farmos.handler.FarmOSHandler.is_farmos_4x()`.
"""
handler = self.get_farmos_handler()
return handler.is_farmos_4x(*args, **kwargs)
class WuttaFarmAppProvider(base.AppProvider):
"""

View file

@ -42,6 +42,35 @@ class FarmOSHandler(GenericHandler):
hostname = self.get_farmos_url()
return farmOS(hostname, **kwargs)
def get_farmos_version(self, client=None, *args, **kwargs):
"""
Returns the farmOS version in use.
"""
if not client:
client = self.get_farmos_client(*args, **kwargs)
info = client.info()
return info["meta"]["farm"]["version"]
def is_farmos_3x(self, client=None, *args, **kwargs):
"""
Check if the farmOS version is 3.x.
"""
if not client:
client = self.get_farmos_client(*args, **kwargs)
version = self.get_farmos_version(client)
return version[0] == "3"
def is_farmos_4x(self, client=None, *args, **kwargs):
"""
Check if the farmOS version is 4.x.
"""
if not client:
client = self.get_farmos_client(*args, **kwargs)
version = self.get_farmos_version(client)
return version[0] == "4"
def get_farmos_url(self, path=None, require=True):
"""
Returns the base URL for farmOS, or one with ``path`` appended.

View file

@ -52,6 +52,7 @@ class FromFarmOSHandler(ImportHandler):
"""
token = self.get_farmos_oauth2_token()
self.farmos_client = self.app.get_farmos_client(token=token)
self.farmos_4x = self.app.is_farmos_4x(self.farmos_client)
def get_farmos_oauth2_token(self):
@ -74,6 +75,7 @@ class FromFarmOSHandler(ImportHandler):
def get_importer_kwargs(self, key, **kwargs):
kwargs = super().get_importer_kwargs(key, **kwargs)
kwargs["farmos_client"] = self.farmos_client
kwargs["farmos_4x"] = self.farmos_4x
return kwargs
@ -241,18 +243,28 @@ class AnimalImporter(FromFarmOS, ToWutta):
birthdate = self.app.localtime(birthdate)
birthdate = self.app.make_utc(birthdate)
if self.farmos_4x:
sterile = animal["attributes"]["is_sterile"]
else:
sterile = animal["attributes"]["is_castrated"]
if notes := animal["attributes"]["notes"]:
notes = notes["value"]
if self.farmos_4x:
active = not animal["attributes"]["archived"]
else:
active = animal["attributes"]["status"] == "active"
return {
"farmos_uuid": UUID(animal["id"]),
"drupal_id": animal["attributes"]["drupal_internal__id"],
"name": animal["attributes"]["name"],
"animal_type_uuid": animal_type.uuid,
"sex": animal["attributes"]["sex"],
"is_sterile": animal["attributes"]["is_castrated"],
"is_sterile": sterile,
"birthdate": birthdate,
"active": animal["attributes"]["status"] == "active",
"active": active,
"notes": notes,
"image_url": image_url,
}
@ -345,13 +357,18 @@ class GroupImporter(FromFarmOS, ToWutta):
if notes := group["attributes"]["notes"]:
notes = notes["value"]
if self.farmos_4x:
active = not group["attributes"]["archived"]
else:
active = group["attributes"]["status"] == "active"
return {
"farmos_uuid": UUID(group["id"]),
"drupal_id": group["attributes"]["drupal_internal__id"],
"name": group["attributes"]["name"],
"is_location": group["attributes"]["is_location"],
"is_fixed": group["attributes"]["is_fixed"],
"active": group["attributes"]["status"] == "active",
"active": active,
"notes": notes,
}
@ -400,6 +417,11 @@ class LandAssetImporter(FromFarmOS, ToWutta):
if notes := land["attributes"]["notes"]:
notes = notes["value"]
if self.farmos_4x:
active = not land["attributes"]["archived"]
else:
active = land["attributes"]["status"] == "active"
return {
"farmos_uuid": UUID(land["id"]),
"drupal_id": land["attributes"]["drupal_internal__id"],
@ -407,7 +429,7 @@ class LandAssetImporter(FromFarmOS, ToWutta):
"land_type_uuid": land_type.uuid,
"is_location": land["attributes"]["is_location"],
"is_fixed": land["attributes"]["is_fixed"],
"active": land["attributes"]["status"] == "active",
"active": active,
"notes": notes,
}
@ -527,6 +549,11 @@ class StructureImporter(FromFarmOS, ToWutta):
):
image_url = image_style["large"]
if self.farmos_4x:
active = not structure["attributes"]["archived"]
else:
active = structure["attributes"]["status"] == "active"
return {
"farmos_uuid": UUID(structure["id"]),
"drupal_id": structure["attributes"]["drupal_internal__id"],
@ -534,7 +561,7 @@ class StructureImporter(FromFarmOS, ToWutta):
"structure_type_uuid": structure_type.uuid,
"is_location": structure["attributes"]["is_location"],
"is_fixed": structure["attributes"]["is_fixed"],
"active": structure["attributes"]["status"] == "active",
"active": active,
"notes": notes,
"image_url": image_url,
}

View file

@ -51,7 +51,6 @@ class AnimalView(FarmOSMasterView):
labels = {
"animal_type": "Species / Breed",
"is_castrated": "Castrated",
"location": "Current Location",
}
@ -59,8 +58,8 @@ class AnimalView(FarmOSMasterView):
"name",
"birthdate",
"sex",
"is_castrated",
"status",
"is_sterile",
"archived",
]
sort_defaults = "name"
@ -70,8 +69,8 @@ class AnimalView(FarmOSMasterView):
"animal_type",
"birthdate",
"sex",
"is_castrated",
"status",
"is_sterile",
"archived",
"owners",
"location",
"notes",
@ -96,6 +95,12 @@ class AnimalView(FarmOSMasterView):
# birthdate
g.set_renderer("birthdate", "date")
# is_sterile
g.set_renderer("is_sterile", "boolean")
# archived
g.set_renderer("archived", "boolean")
def get_instance(self):
animal = self.farmos_client.resource.get_id(
@ -173,16 +178,30 @@ class AnimalView(FarmOSMasterView):
birthdate = datetime.datetime.fromisoformat(birthdate)
birthdate = self.app.localtime(birthdate)
sterile = None
if self.farmos_4x:
sterile = animal["attributes"]["is_sterile"]
else:
sterile = animal["attributes"]["is_castrated"]
if notes := animal["attributes"]["notes"]:
notes = notes["value"]
if self.farmos_4x:
archived = animal["attributes"]["archived"]
else:
archived = animal["attributes"]["status"] == "archived"
return {
"uuid": animal["id"],
"drupal_id": animal["attributes"]["drupal_internal__id"],
"name": animal["attributes"]["name"],
"birthdate": birthdate,
"sex": animal["attributes"]["sex"],
"is_castrated": animal["attributes"]["is_castrated"],
"location": "", # TODO
"status": animal["attributes"]["status"],
"notes": animal["attributes"]["notes"]["value"],
"sex": animal["attributes"]["sex"] or colander.null,
"is_sterile": sterile,
"location": colander.null, # TODO
"archived": archived,
"notes": notes or colander.null,
}
def configure_form(self, form):
@ -197,8 +216,8 @@ class AnimalView(FarmOSMasterView):
f.set_node("birthdate", WuttaDateTime())
f.set_widget("birthdate", WuttaDateTimeWidget(self.request))
# is_castrated
f.set_node("is_castrated", colander.Boolean())
# is_sterile
f.set_node("is_sterile", colander.Boolean())
# location
f.set_node("location", StructureType(self.request))
@ -209,6 +228,9 @@ class AnimalView(FarmOSMasterView):
# notes
f.set_widget("notes", "notes")
# archived
f.set_node("archived", colander.Boolean())
# image
if url := animal.get("large_image_url"):
f.set_widget("image", ImageWidget("animal image"))

View file

@ -50,7 +50,7 @@ class GroupView(FarmOSMasterView):
"name",
"is_fixed",
"is_location",
"status",
"archived",
"changed",
]
@ -60,7 +60,7 @@ class GroupView(FarmOSMasterView):
"name",
"is_fixed",
"is_location",
"status",
"archived",
"notes",
"created",
"changed",
@ -87,6 +87,9 @@ class GroupView(FarmOSMasterView):
# changed
g.set_renderer("changed", "datetime")
# archived
g.set_renderer("archived", "boolean")
def get_instance(self):
group = self.farmos_client.resource.get_id(
"asset", "group", self.request.matchdict["uuid"]
@ -107,6 +110,11 @@ class GroupView(FarmOSMasterView):
changed = datetime.datetime.fromisoformat(changed)
changed = self.app.localtime(changed)
if self.farmos_4x:
archived = group["attributes"]["archived"]
else:
archived = group["attributes"]["status"] == "archived"
return {
"uuid": group["id"],
"drupal_id": group["attributes"]["drupal_internal__id"],
@ -115,7 +123,7 @@ class GroupView(FarmOSMasterView):
"changed": changed,
"is_fixed": group["attributes"]["is_fixed"],
"is_location": group["attributes"]["is_location"],
"status": group["attributes"]["status"],
"archived": archived,
"notes": group["attributes"]["notes"]["value"],
}
@ -140,6 +148,9 @@ class GroupView(FarmOSMasterView):
f.set_node("changed", WuttaDateTime())
f.set_widget("changed", WuttaDateTimeWidget(self.request))
# archived
f.set_node("archived", colander.Boolean())
def get_xref_buttons(self, group):
model = self.app.model
session = self.Session()

View file

@ -52,7 +52,7 @@ class LandAssetView(FarmOSMasterView):
"land_type",
"is_fixed",
"is_location",
"status",
"archived",
"changed",
]
@ -63,7 +63,7 @@ class LandAssetView(FarmOSMasterView):
"land_type",
"is_fixed",
"is_location",
"status",
"archived",
"notes",
"created",
"changed",
@ -93,6 +93,9 @@ class LandAssetView(FarmOSMasterView):
# changed
g.set_renderer("changed", "datetime")
# archived
g.set_renderer("archived", "boolean")
def get_instance(self):
land_asset = self.farmos_client.resource.get_id(
"asset", "land", self.request.matchdict["uuid"]
@ -116,6 +119,11 @@ class LandAssetView(FarmOSMasterView):
if notes := land["attributes"]["notes"]:
notes = notes["value"]
if self.farmos_4x:
archived = land["attributes"]["archived"]
else:
archived = land["attributes"]["status"] == "archived"
return {
"uuid": land["id"],
"drupal_id": land["attributes"]["drupal_internal__id"],
@ -125,7 +133,7 @@ class LandAssetView(FarmOSMasterView):
"changed": changed,
"is_fixed": land["attributes"]["is_fixed"],
"is_location": land["attributes"]["is_location"],
"status": land["attributes"]["status"],
"archived": archived,
"notes": notes or colander.null,
}
@ -150,6 +158,9 @@ class LandAssetView(FarmOSMasterView):
f.set_node("changed", WuttaDateTime())
f.set_widget("changed", WuttaDateTimeWidget(self.request))
# archived
f.set_node("archived", colander.Boolean())
def get_xref_buttons(self, land):
return [
self.make_button(

View file

@ -58,6 +58,7 @@ class FarmOSMasterView(MasterView):
def __init__(self, request, context=None):
super().__init__(request, context=context)
self.farmos_client = self.get_farmos_client()
self.farmos_4x = self.app.is_farmos_4x(self.farmos_client)
self.raw_json = None
def get_farmos_client(self):

View file

@ -50,7 +50,7 @@ class StructureView(FarmOSMasterView):
grid_columns = [
"name",
"status",
"archived",
"created",
"changed",
]
@ -59,7 +59,7 @@ class StructureView(FarmOSMasterView):
form_fields = [
"name",
"status",
"archived",
"structure_type",
"is_location",
"is_fixed",
@ -90,6 +90,9 @@ class StructureView(FarmOSMasterView):
# changed
g.set_renderer("changed", "datetime")
# archived
g.set_renderer("archived", "boolean")
def get_instance(self):
structure = self.farmos_client.resource.get_id(
"asset", "structure", self.request.matchdict["uuid"]
@ -145,6 +148,11 @@ class StructureView(FarmOSMasterView):
changed = datetime.datetime.fromisoformat(changed)
changed = self.app.localtime(changed)
if self.farmos_4x:
archived = structure["attributes"]["archived"]
else:
archived = structure["attributes"]["status"] == "archived"
return {
"uuid": structure["id"],
"drupal_id": structure["attributes"]["drupal_internal__id"],
@ -153,7 +161,7 @@ class StructureView(FarmOSMasterView):
"is_fixed": structure["attributes"]["is_fixed"],
"is_location": structure["attributes"]["is_location"],
"notes": structure["attributes"]["notes"] or colander.null,
"status": structure["attributes"]["status"],
"archived": archived,
"created": created,
"changed": changed,
}
@ -180,6 +188,9 @@ class StructureView(FarmOSMasterView):
f.set_node("changed", WuttaDateTime())
f.set_widget("changed", WuttaDateTimeWidget(self.request))
# archived
f.set_node("archived", colander.Boolean())
# image
if url := structure.get("large_image_url"):
f.set_widget("image", ImageWidget("structure image"))