feat: add basic support for WuttaFarm → farmOS export
typical CLI export tool, but also the export happens automatically when create or edit of record happens in wuttafarm supported models: - AnimalType - AnimalAsset - GroupAsset - LandAsset - StructureAsset
This commit is contained in:
parent
6677fe1e23
commit
da9b559752
14 changed files with 765 additions and 9 deletions
|
|
@ -58,6 +58,7 @@ wuttafarm = "wuttafarm.app:WuttaFarmAppProvider"
|
|||
"wuttafarm" = "wuttafarm.web.menus:WuttaFarmMenuHandler"
|
||||
|
||||
[project.entry-points."wuttasync.importing"]
|
||||
"export.to_farmos.from_wuttafarm" = "wuttafarm.farmos.importing.wuttafarm:FromWuttaFarmToFarmOS"
|
||||
"import.to_wuttafarm.from_farmos" = "wuttafarm.importing.farmos:FromFarmOSToWuttaFarm"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,38 @@ class WuttaFarmAppHandler(base.AppHandler):
|
|||
handler = self.get_farmos_handler()
|
||||
return handler.is_farmos_4x(*args, **kwargs)
|
||||
|
||||
def export_to_farmos(self, obj, require=True):
|
||||
"""
|
||||
Export the given object to farmOS, using configured handler.
|
||||
|
||||
This should ensure the given object is also *updated* with the
|
||||
farmOS UUID and Drupal ID, when new record is created in
|
||||
farmOS.
|
||||
|
||||
:param obj: Any data object in WuttaFarm, e.g. AnimalAsset
|
||||
instance.
|
||||
|
||||
:param require: If true, this will *require* the export
|
||||
handler to support objects of the given type. If false,
|
||||
then nothing will happen / export is silently skipped when
|
||||
there is no such exporter.
|
||||
"""
|
||||
handler = self.app.get_import_handler("export.to_farmos.from_wuttafarm")
|
||||
|
||||
model_name = type(obj).__name__
|
||||
if model_name not in handler.importers:
|
||||
if require:
|
||||
raise ValueError(f"no exporter found for {model_name}")
|
||||
return
|
||||
|
||||
# nb. begin txn to establish the API client
|
||||
# TODO: should probably use current user oauth2 token instead
|
||||
# of always making a new one here, which is what happens IIUC
|
||||
handler.begin_target_transaction()
|
||||
importer = handler.get_importer(model_name, caches_target=False)
|
||||
normal = importer.normalize_source_object(obj)
|
||||
importer.process_data(source_data=[normal])
|
||||
|
||||
|
||||
class WuttaFarmAppProvider(base.AppProvider):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -26,5 +26,6 @@ WuttaFarm CLI
|
|||
from .base import wuttafarm_typer
|
||||
|
||||
# nb. must bring in all modules for discovery to work
|
||||
from . import export_farmos
|
||||
from . import import_farmos
|
||||
from . import install
|
||||
|
|
|
|||
41
src/wuttafarm/cli/export_farmos.py
Normal file
41
src/wuttafarm/cli/export_farmos.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
See also: :ref:`wuttafarm-export-farmos`
|
||||
"""
|
||||
|
||||
import typer
|
||||
|
||||
from wuttasync.cli import import_command, ImportCommandHandler
|
||||
|
||||
from wuttafarm.cli import wuttafarm_typer
|
||||
|
||||
|
||||
@wuttafarm_typer.command()
|
||||
@import_command
|
||||
def export_farmos(ctx: typer.Context, **kwargs):
|
||||
"""
|
||||
Export data from WuttaFarm to farmOS API
|
||||
"""
|
||||
config = ctx.parent.wutta_config
|
||||
handler = ImportCommandHandler(config, key="export.to_farmos.from_wuttafarm")
|
||||
handler.run(ctx)
|
||||
|
|
@ -26,6 +26,12 @@ Email sending config for WuttaFarm
|
|||
from wuttasync.emails import ImportExportWarning
|
||||
|
||||
|
||||
class export_to_farmos_from_wuttafarm_warning(ImportExportWarning):
|
||||
"""
|
||||
Diff warning for WuttaFarm → farmOS export.
|
||||
"""
|
||||
|
||||
|
||||
class import_to_wuttafarm_from_farmos_warning(ImportExportWarning):
|
||||
"""
|
||||
Diff warning for farmOS → WuttaFarm import.
|
||||
|
|
|
|||
26
src/wuttafarm/farmos/importing/__init__.py
Normal file
26
src/wuttafarm/farmos/importing/__init__.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Importing data *into* farmOS
|
||||
"""
|
||||
|
||||
from . import model
|
||||
365
src/wuttafarm/farmos/importing/model.py
Normal file
365
src/wuttafarm/farmos/importing/model.py
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Importer models targeting farmOS
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from uuid import UUID
|
||||
|
||||
import requests
|
||||
|
||||
from wuttasync.importing import Importer
|
||||
|
||||
|
||||
class ToFarmOS(Importer):
|
||||
"""
|
||||
Base class for data importer targeting the farmOS API.
|
||||
"""
|
||||
|
||||
key = "uuid"
|
||||
caches_target = True
|
||||
|
||||
def format_datetime(self, dt):
|
||||
"""
|
||||
Convert a WuttaFarm datetime object to the format required for
|
||||
pushing to the farmOS API.
|
||||
"""
|
||||
if dt is None:
|
||||
return None
|
||||
dt = self.app.localtime(dt)
|
||||
return dt.timestamp()
|
||||
|
||||
def normalize_datetime(self, dt):
|
||||
"""
|
||||
Convert a farmOS datetime value to naive UTC used by
|
||||
WuttaFarm.
|
||||
|
||||
:param dt: Date/time string value "as-is" from the farmOS API.
|
||||
|
||||
:returns: Equivalent naive UTC ``datetime``
|
||||
"""
|
||||
if dt is None:
|
||||
return None
|
||||
dt = datetime.datetime.fromisoformat(dt)
|
||||
return self.app.make_utc(dt)
|
||||
|
||||
|
||||
class ToFarmOSAsset(ToFarmOS):
|
||||
"""
|
||||
Base class for asset data importer targeting the farmOS API.
|
||||
"""
|
||||
|
||||
farmos_asset_type = None
|
||||
|
||||
def get_target_objects(self, **kwargs):
|
||||
assets = self.farmos_client.asset.get(self.farmos_asset_type)
|
||||
return assets["data"]
|
||||
|
||||
def get_target_object(self, key):
|
||||
|
||||
# fetch from cache, if applicable
|
||||
if self.caches_target:
|
||||
return super().get_target_object(key)
|
||||
|
||||
# okay now must fetch via API
|
||||
if self.get_keys() != ["uuid"]:
|
||||
raise ValueError("must use uuid key for this to work")
|
||||
uuid = key[0]
|
||||
|
||||
try:
|
||||
asset = self.farmos_client.asset.get_id(self.farmos_asset_type, str(uuid))
|
||||
except requests.HTTPError as exc:
|
||||
if exc.response.status_code == 404:
|
||||
return None
|
||||
raise
|
||||
return asset["data"]
|
||||
|
||||
def create_target_object(self, key, source_data):
|
||||
if source_data.get("__ignoreme__"):
|
||||
return None
|
||||
if self.dry_run:
|
||||
return source_data
|
||||
|
||||
payload = self.get_asset_payload(source_data)
|
||||
result = self.farmos_client.asset.send(self.farmos_asset_type, payload)
|
||||
normal = self.normalize_target_object(result["data"])
|
||||
normal["_new_object"] = result["data"]
|
||||
return normal
|
||||
|
||||
def update_target_object(self, asset, source_data, target_data=None):
|
||||
if self.dry_run:
|
||||
return asset
|
||||
|
||||
payload = self.get_asset_payload(source_data)
|
||||
payload["id"] = str(source_data["uuid"])
|
||||
result = self.farmos_client.asset.send(self.farmos_asset_type, payload)
|
||||
return self.normalize_target_object(result["data"])
|
||||
|
||||
def normalize_target_object(self, asset):
|
||||
|
||||
if notes := asset["attributes"]["notes"]:
|
||||
notes = notes["value"]
|
||||
|
||||
return {
|
||||
"uuid": UUID(asset["id"]),
|
||||
"asset_name": asset["attributes"]["name"],
|
||||
"is_location": asset["attributes"]["is_location"],
|
||||
"is_fixed": asset["attributes"]["is_fixed"],
|
||||
"notes": notes,
|
||||
"archived": asset["attributes"]["archived"],
|
||||
}
|
||||
|
||||
def get_asset_payload(self, source_data):
|
||||
|
||||
attrs = {}
|
||||
if "asset_name" in self.fields:
|
||||
attrs["name"] = source_data["asset_name"]
|
||||
if "is_location" in self.fields:
|
||||
attrs["is_location"] = source_data["is_location"]
|
||||
if "is_fixed" in self.fields:
|
||||
attrs["is_fixed"] = source_data["is_fixed"]
|
||||
if "notes" in self.fields:
|
||||
attrs["notes"] = {"value": source_data["notes"]}
|
||||
if "archived" in self.fields:
|
||||
attrs["archived"] = source_data["archived"]
|
||||
|
||||
payload = {"attributes": attrs}
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
class AnimalAssetImporter(ToFarmOSAsset):
|
||||
|
||||
model_title = "AnimalAsset"
|
||||
farmos_asset_type = "animal"
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"animal_type_uuid",
|
||||
"sex",
|
||||
"is_sterile",
|
||||
"birthdate",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_target_object(self, animal):
|
||||
data = super().normalize_target_object(animal)
|
||||
data.update(
|
||||
{
|
||||
"animal_type_uuid": UUID(
|
||||
animal["relationships"]["animal_type"]["data"]["id"]
|
||||
),
|
||||
"sex": animal["attributes"]["sex"],
|
||||
"is_sterile": animal["attributes"]["is_sterile"],
|
||||
"birthdate": self.normalize_datetime(animal["attributes"]["birthdate"]),
|
||||
}
|
||||
)
|
||||
return data
|
||||
|
||||
def get_asset_payload(self, source_data):
|
||||
payload = super().get_asset_payload(source_data)
|
||||
|
||||
attrs = {}
|
||||
if "sex" in self.fields:
|
||||
attrs["sex"] = source_data["sex"]
|
||||
if "is_sterile" in self.fields:
|
||||
attrs["is_sterile"] = source_data["is_sterile"]
|
||||
if "birthdate" in self.fields:
|
||||
attrs["birthdate"] = self.format_datetime(source_data["birthdate"])
|
||||
|
||||
rels = {}
|
||||
if "animal_type_uuid" in self.fields:
|
||||
rels["animal_type"] = {
|
||||
"data": {
|
||||
"id": str(source_data["animal_type_uuid"]),
|
||||
"type": "taxonomy_term--animal_type",
|
||||
}
|
||||
}
|
||||
|
||||
payload["attributes"].update(attrs)
|
||||
if rels:
|
||||
payload.setdefault("relationships", {}).update(rels)
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
class AnimalTypeImporter(ToFarmOS):
|
||||
|
||||
model_title = "AnimalType"
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"name",
|
||||
]
|
||||
|
||||
def get_target_objects(self, **kwargs):
|
||||
result = self.farmos_client.resource.get("taxonomy_term", "animal_type")
|
||||
return result["data"]
|
||||
|
||||
def get_target_object(self, key):
|
||||
|
||||
# fetch from cache, if applicable
|
||||
if self.caches_target:
|
||||
return super().get_target_object(key)
|
||||
|
||||
# okay now must fetch via API
|
||||
if self.get_keys() != ["uuid"]:
|
||||
raise ValueError("must use uuid key for this to work")
|
||||
uuid = key[0]
|
||||
|
||||
try:
|
||||
result = self.farmos_client.resource.get_id(
|
||||
"taxonomy_term", "animal_type", str(uuid)
|
||||
)
|
||||
except requests.HTTPError as exc:
|
||||
if exc.response.status_code == 404:
|
||||
return None
|
||||
raise
|
||||
return result["data"]
|
||||
|
||||
def normalize_target_object(self, obj):
|
||||
return {
|
||||
"uuid": UUID(obj["id"]),
|
||||
"name": obj["attributes"]["name"],
|
||||
}
|
||||
|
||||
def get_type_payload(self, source_data):
|
||||
return {
|
||||
"attributes": {
|
||||
"name": source_data["name"],
|
||||
}
|
||||
}
|
||||
|
||||
def create_target_object(self, key, source_data):
|
||||
if source_data.get("__ignoreme__"):
|
||||
return None
|
||||
if self.dry_run:
|
||||
return source_data
|
||||
|
||||
payload = self.get_type_payload(source_data)
|
||||
result = self.farmos_client.resource.send(
|
||||
"taxonomy_term", "animal_type", payload
|
||||
)
|
||||
normal = self.normalize_target_object(result["data"])
|
||||
normal["_new_object"] = result["data"]
|
||||
return normal
|
||||
|
||||
def update_target_object(self, asset, source_data, target_data=None):
|
||||
if self.dry_run:
|
||||
return asset
|
||||
|
||||
payload = self.get_type_payload(source_data)
|
||||
payload["id"] = str(source_data["uuid"])
|
||||
result = self.farmos_client.resource.send(
|
||||
"taxonomy_term", "animal_type", payload
|
||||
)
|
||||
return self.normalize_target_object(result["data"])
|
||||
|
||||
|
||||
class GroupAssetImporter(ToFarmOSAsset):
|
||||
|
||||
model_title = "GroupAsset"
|
||||
farmos_asset_type = "group"
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
|
||||
class LandAssetImporter(ToFarmOSAsset):
|
||||
|
||||
model_title = "LandAsset"
|
||||
farmos_asset_type = "land"
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"land_type_id",
|
||||
"is_location",
|
||||
"is_fixed",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_target_object(self, land):
|
||||
data = super().normalize_target_object(land)
|
||||
data.update(
|
||||
{
|
||||
"land_type_id": land["attributes"]["land_type"],
|
||||
}
|
||||
)
|
||||
return data
|
||||
|
||||
def get_asset_payload(self, source_data):
|
||||
payload = super().get_asset_payload(source_data)
|
||||
|
||||
attrs = {}
|
||||
if "land_type_id" in self.fields:
|
||||
attrs["land_type"] = source_data["land_type_id"]
|
||||
|
||||
if attrs:
|
||||
payload["attributes"].update(attrs)
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
class StructureAssetImporter(ToFarmOSAsset):
|
||||
|
||||
model_title = "StructureAsset"
|
||||
farmos_asset_type = "structure"
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"structure_type_id",
|
||||
"is_location",
|
||||
"is_fixed",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_target_object(self, structure):
|
||||
data = super().normalize_target_object(structure)
|
||||
data.update(
|
||||
{
|
||||
"structure_type_id": structure["attributes"]["structure_type"],
|
||||
}
|
||||
)
|
||||
return data
|
||||
|
||||
def get_asset_payload(self, source_data):
|
||||
payload = super().get_asset_payload(source_data)
|
||||
|
||||
attrs = {}
|
||||
if "structure_type_id" in self.fields:
|
||||
attrs["structure_type"] = source_data["structure_type_id"]
|
||||
|
||||
if attrs:
|
||||
payload["attributes"].update(attrs)
|
||||
|
||||
return payload
|
||||
263
src/wuttafarm/farmos/importing/wuttafarm.py
Normal file
263
src/wuttafarm/farmos/importing/wuttafarm.py
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
# -*- 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
WuttaFarm → farmOS data export
|
||||
"""
|
||||
|
||||
from oauthlib.oauth2 import BackendApplicationClient
|
||||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
from wuttasync.importing import ImportHandler, FromWuttaHandler, FromWutta, Orientation
|
||||
|
||||
from wuttafarm.db import model
|
||||
from wuttafarm.farmos import importing as farmos_importing
|
||||
|
||||
|
||||
class FromWuttaFarmHandler(FromWuttaHandler):
|
||||
"""
|
||||
Base class for import handler targeting WuttaFarm
|
||||
"""
|
||||
|
||||
source_key = "wuttafarm"
|
||||
|
||||
|
||||
class ToFarmOSHandler(ImportHandler):
|
||||
"""
|
||||
Base class for export handlers using CSV file(s) as data target.
|
||||
"""
|
||||
|
||||
target_key = "farmos"
|
||||
generic_target_title = "farmOS"
|
||||
|
||||
# TODO: a lot of duplication to cleanup here; see FromFarmOSHandler
|
||||
|
||||
def begin_target_transaction(self):
|
||||
"""
|
||||
Establish the farmOS API client.
|
||||
"""
|
||||
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):
|
||||
|
||||
client_id = self.config.get(
|
||||
"farmos.oauth2.importing.client_id", default="wuttafarm"
|
||||
)
|
||||
client_secret = self.config.require("farmos.oauth2.importing.client_secret")
|
||||
scope = self.config.get("farmos.oauth2.importing.scope", default="farm_manager")
|
||||
|
||||
client = BackendApplicationClient(client_id=client_id)
|
||||
oauth = OAuth2Session(client=client)
|
||||
|
||||
return oauth.fetch_token(
|
||||
token_url=self.app.get_farmos_url("/oauth/token"),
|
||||
include_client_id=True,
|
||||
client_secret=client_secret,
|
||||
scope=scope,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
|
||||
class FromWuttaFarmToFarmOS(FromWuttaFarmHandler, ToFarmOSHandler):
|
||||
"""
|
||||
Handler for WuttaFarm → farmOS API export.
|
||||
"""
|
||||
|
||||
orientation = Orientation.EXPORT
|
||||
|
||||
def define_importers(self):
|
||||
""" """
|
||||
importers = super().define_importers()
|
||||
importers["LandAsset"] = LandAssetImporter
|
||||
importers["StructureAsset"] = StructureAssetImporter
|
||||
importers["AnimalType"] = AnimalTypeImporter
|
||||
importers["AnimalAsset"] = AnimalAssetImporter
|
||||
importers["GroupAsset"] = GroupAssetImporter
|
||||
return importers
|
||||
|
||||
|
||||
class FromWuttaFarm(FromWutta):
|
||||
|
||||
drupal_internal_id_field = "drupal_internal__id"
|
||||
|
||||
def create_target_object(self, key, source_data):
|
||||
obj = super().create_target_object(key, source_data)
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if not self.dry_run:
|
||||
|
||||
# set farmOS, Drupal key fields in WuttaFarm
|
||||
api_object = obj["_new_object"]
|
||||
wf_object = source_data["_src_object"]
|
||||
wf_object.farmos_uuid = obj["uuid"]
|
||||
wf_object.drupal_id = api_object["attributes"][
|
||||
self.drupal_internal_id_field
|
||||
]
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class AnimalAssetImporter(FromWuttaFarm, farmos_importing.model.AnimalAssetImporter):
|
||||
"""
|
||||
WuttaFarm → farmOS API exporter for Animal Assets
|
||||
"""
|
||||
|
||||
source_model_class = model.AnimalAsset
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"animal_type_uuid",
|
||||
"sex",
|
||||
"is_sterile",
|
||||
"birthdate",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_source_object(self, animal):
|
||||
return {
|
||||
"uuid": animal.farmos_uuid or self.app.make_true_uuid(),
|
||||
"asset_name": animal.asset_name,
|
||||
"animal_type_uuid": animal.animal_type.farmos_uuid,
|
||||
"sex": animal.sex,
|
||||
"is_sterile": animal.is_sterile,
|
||||
"birthdate": animal.birthdate,
|
||||
"notes": animal.notes,
|
||||
"archived": animal.archived,
|
||||
"_src_object": animal,
|
||||
}
|
||||
|
||||
|
||||
class AnimalTypeImporter(FromWuttaFarm, farmos_importing.model.AnimalTypeImporter):
|
||||
"""
|
||||
WuttaFarm → farmOS API exporter for Animal Types
|
||||
"""
|
||||
|
||||
source_model_class = model.AnimalType
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"name",
|
||||
]
|
||||
|
||||
drupal_internal_id_field = "drupal_internal__tid"
|
||||
|
||||
def normalize_source_object(self, animal_type):
|
||||
return {
|
||||
"uuid": animal_type.farmos_uuid or self.app.make_true_uuid(),
|
||||
"name": animal_type.name,
|
||||
"_src_object": animal_type,
|
||||
}
|
||||
|
||||
|
||||
class GroupAssetImporter(FromWuttaFarm, farmos_importing.model.GroupAssetImporter):
|
||||
"""
|
||||
WuttaFarm → farmOS API exporter for Group Assets
|
||||
"""
|
||||
|
||||
source_model_class = model.GroupAsset
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_source_object(self, group):
|
||||
return {
|
||||
"uuid": group.farmos_uuid or self.app.make_true_uuid(),
|
||||
"asset_name": group.asset_name,
|
||||
"notes": group.notes,
|
||||
"archived": group.archived,
|
||||
"_src_object": group,
|
||||
}
|
||||
|
||||
|
||||
class LandAssetImporter(FromWuttaFarm, farmos_importing.model.LandAssetImporter):
|
||||
"""
|
||||
WuttaFarm → farmOS API exporter for Land Assets
|
||||
"""
|
||||
|
||||
source_model_class = model.LandAsset
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"land_type_id",
|
||||
"is_location",
|
||||
"is_fixed",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_source_object(self, land):
|
||||
return {
|
||||
"uuid": land.farmos_uuid or self.app.make_true_uuid(),
|
||||
"asset_name": land.asset_name,
|
||||
"land_type_id": land.land_type.drupal_id,
|
||||
"is_location": land.is_location,
|
||||
"is_fixed": land.is_fixed,
|
||||
"notes": land.notes,
|
||||
"archived": land.archived,
|
||||
"_src_object": land,
|
||||
}
|
||||
|
||||
|
||||
class StructureAssetImporter(
|
||||
FromWuttaFarm, farmos_importing.model.StructureAssetImporter
|
||||
):
|
||||
"""
|
||||
WuttaFarm → farmOS API exporter for Structure Assets
|
||||
"""
|
||||
|
||||
source_model_class = model.StructureAsset
|
||||
|
||||
supported_fields = [
|
||||
"uuid",
|
||||
"asset_name",
|
||||
"structure_type_id",
|
||||
"is_location",
|
||||
"is_fixed",
|
||||
"notes",
|
||||
"archived",
|
||||
]
|
||||
|
||||
def normalize_source_object(self, structure):
|
||||
return {
|
||||
"uuid": structure.farmos_uuid or self.app.make_true_uuid(),
|
||||
"asset_name": structure.asset_name,
|
||||
"structure_type_id": structure.structure_type.drupal_id,
|
||||
"is_location": structure.is_location,
|
||||
"is_fixed": structure.is_fixed,
|
||||
"notes": structure.notes,
|
||||
"archived": structure.archived,
|
||||
"_src_object": structure,
|
||||
}
|
||||
|
|
@ -26,13 +26,12 @@ Master view for Animals
|
|||
from wuttaweb.forms.schema import WuttaDictEnum
|
||||
|
||||
from wuttafarm.db.model import AnimalType, AnimalAsset
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.web.views.assets import AssetMasterView
|
||||
from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView
|
||||
from wuttafarm.web.forms.schema import AnimalTypeRef
|
||||
from wuttafarm.web.forms.widgets import ImageWidget
|
||||
|
||||
|
||||
class AnimalTypeView(WuttaFarmMasterView):
|
||||
class AnimalTypeView(AssetTypeMasterView):
|
||||
"""
|
||||
Master view for Animal Types
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -118,6 +118,16 @@ class AssetView(WuttaFarmMasterView):
|
|||
return None
|
||||
|
||||
|
||||
class AssetTypeMasterView(WuttaFarmMasterView):
|
||||
"""
|
||||
Base class for "Asset Type" master views.
|
||||
|
||||
A bit of a misnmer perhaps, this is *not* for the actual AssetType
|
||||
model, but rather the "secondary" types, e.g. AnimalType,
|
||||
LandType etc.
|
||||
"""
|
||||
|
||||
|
||||
class AssetMasterView(WuttaFarmMasterView):
|
||||
"""
|
||||
Base class for Asset master views
|
||||
|
|
|
|||
|
|
@ -54,9 +54,13 @@ class CommonView(base.CommonView):
|
|||
"activity_logs.list",
|
||||
"activity_logs.view",
|
||||
"activity_logs.versions",
|
||||
"animal_types.create",
|
||||
"animal_types.edit",
|
||||
"animal_types.list",
|
||||
"animal_types.view",
|
||||
"animal_types.versions",
|
||||
"animal_assets.create",
|
||||
"animal_assets.edit",
|
||||
"animal_assets.list",
|
||||
"animal_assets.view",
|
||||
"animal_assets.versions",
|
||||
|
|
@ -86,9 +90,13 @@ class CommonView(base.CommonView):
|
|||
"farmos_structures.view",
|
||||
"farmos_users.list",
|
||||
"farmos_users.view",
|
||||
"group_asests.create",
|
||||
"group_asests.edit",
|
||||
"group_asests.list",
|
||||
"group_asests.view",
|
||||
"group_asests.versions",
|
||||
"land_assets.create",
|
||||
"land_assets.edit",
|
||||
"land_assets.list",
|
||||
"land_assets.view",
|
||||
"land_assets.versions",
|
||||
|
|
@ -101,6 +109,8 @@ class CommonView(base.CommonView):
|
|||
"structure_types.list",
|
||||
"structure_types.view",
|
||||
"structure_types.versions",
|
||||
"structure_assets.create",
|
||||
"structure_assets.edit",
|
||||
"structure_assets.list",
|
||||
"structure_assets.view",
|
||||
"structure_assets.versions",
|
||||
|
|
|
|||
|
|
@ -26,12 +26,11 @@ Master view for Land Types
|
|||
from webhelpers2.html import HTML, tags
|
||||
|
||||
from wuttafarm.db.model.land import LandType, LandAsset
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.web.views.assets import AssetMasterView
|
||||
from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView
|
||||
from wuttafarm.web.forms.schema import LandTypeRef
|
||||
|
||||
|
||||
class LandTypeView(WuttaFarmMasterView):
|
||||
class LandTypeView(AssetTypeMasterView):
|
||||
"""
|
||||
Master view for Land Types
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -96,3 +96,7 @@ class WuttaFarmMasterView(MasterView):
|
|||
f.remove("drupal_id")
|
||||
else:
|
||||
f.set_readonly("drupal_id")
|
||||
|
||||
def persist(self, obj, session=None):
|
||||
super().persist(obj, session)
|
||||
self.app.export_to_farmos(obj, require=False)
|
||||
|
|
|
|||
|
|
@ -23,14 +23,13 @@
|
|||
Master view for Structures
|
||||
"""
|
||||
|
||||
from wuttafarm.web.views import WuttaFarmMasterView
|
||||
from wuttafarm.web.views.assets import AssetMasterView
|
||||
from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView
|
||||
from wuttafarm.db.model import StructureType, StructureAsset
|
||||
from wuttafarm.web.forms.schema import StructureTypeRef
|
||||
from wuttafarm.web.forms.widgets import ImageWidget
|
||||
|
||||
|
||||
class StructureTypeView(WuttaFarmMasterView):
|
||||
class StructureTypeView(AssetTypeMasterView):
|
||||
"""
|
||||
Master view for Structure Types
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue