and make the Eggs quick form save to wuttafarm app DB first, then export to farmOS, if app is running as mirror
355 lines
11 KiB
Python
355 lines
11 KiB
Python
# -*- 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/>.
|
|
#
|
|
################################################################################
|
|
"""
|
|
Quick Form for "Eggs"
|
|
"""
|
|
|
|
import json
|
|
|
|
import colander
|
|
from deform.widget import SelectWidget
|
|
|
|
from farmOS.subrequests import Action, Subrequest, SubrequestsBlueprint, Format
|
|
|
|
from wuttaweb.forms.schema import WuttaDateTime
|
|
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
|
|
|
from wuttafarm.web.views.quick import QuickFormView
|
|
|
|
|
|
class EggsQuickForm(QuickFormView):
|
|
"""
|
|
Use this form to record an egg harvest. A harvest log will be
|
|
created with standard details filled in.
|
|
"""
|
|
|
|
form_title = "Eggs"
|
|
route_slug = "eggs"
|
|
url_slug = "eggs"
|
|
|
|
_layer_assets = None
|
|
|
|
# TODO: make this configurable?
|
|
unit_name = "egg(s)"
|
|
|
|
def make_quick_form(self):
|
|
f = self.make_form(
|
|
fields=[
|
|
"timestamp",
|
|
"count",
|
|
"asset",
|
|
"notes",
|
|
],
|
|
labels={
|
|
"timestamp": "Date",
|
|
"count": "Quantity",
|
|
"asset": "Layer Asset",
|
|
},
|
|
)
|
|
|
|
# timestamp
|
|
f.set_node("timestamp", WuttaDateTime())
|
|
f.set_widget("timestamp", WuttaDateTimeWidget(self.request))
|
|
f.set_default("timestamp", self.app.make_utc())
|
|
|
|
# count
|
|
f.set_node("count", colander.Integer())
|
|
|
|
# asset
|
|
assets = self.get_layer_assets()
|
|
values = [(a["uuid"], a["name"]) for a in assets]
|
|
f.set_widget("asset", SelectWidget(values=values))
|
|
if len(assets) == 1:
|
|
f.set_default("asset", assets[0]["uuid"])
|
|
|
|
# notes
|
|
f.set_widget("notes", "notes")
|
|
f.set_required("notes", False)
|
|
|
|
return f
|
|
|
|
def get_layer_assets(self):
|
|
if self._layer_assets is not None:
|
|
return self._layer_assets
|
|
|
|
if self.app.is_farmos_wrapper():
|
|
assets = self.get_layer_assets_from_farmos()
|
|
else:
|
|
assets = self.get_layer_assets_from_wuttafarm()
|
|
|
|
assets.sort(key=lambda a: a["name"])
|
|
self._layer_assets = assets
|
|
return assets
|
|
|
|
def get_layer_assets_from_wuttafarm(self):
|
|
model = self.app.model
|
|
session = self.Session()
|
|
assets = []
|
|
|
|
def normalize(asset):
|
|
asset_type = asset.__wutta_hint__["farmos_asset_type"]
|
|
return {
|
|
"uuid": str(asset.farmos_uuid),
|
|
"name": asset.asset_name,
|
|
"type": f"asset--{asset_type}",
|
|
}
|
|
|
|
query = (
|
|
session.query(model.AnimalAsset)
|
|
.join(model.Asset)
|
|
.filter(model.AnimalAsset.produces_eggs == True)
|
|
.order_by(model.Asset.asset_name)
|
|
)
|
|
assets.extend([normalize(a) for a in query])
|
|
|
|
query = (
|
|
session.query(model.GroupAsset)
|
|
.join(model.Asset)
|
|
.filter(model.GroupAsset.produces_eggs == True)
|
|
.order_by(model.Asset.asset_name)
|
|
)
|
|
assets.extend([normalize(a) for a in query])
|
|
|
|
return assets
|
|
|
|
def get_layer_assets_from_farmos(self):
|
|
assets = []
|
|
params = {
|
|
"filter[produces_eggs]": 1,
|
|
"sort": "name",
|
|
}
|
|
|
|
def normalize(asset):
|
|
return {
|
|
"uuid": asset["id"],
|
|
"name": asset["attributes"]["name"],
|
|
"type": asset["type"],
|
|
}
|
|
|
|
result = self.farmos_client.asset.get("animal", params=params)
|
|
assets.extend([normalize(a) for a in result["data"]])
|
|
|
|
result = self.farmos_client.asset.get("group", params=params)
|
|
assets.extend([normalize(a) for a in result["data"]])
|
|
|
|
return assets
|
|
|
|
def save_quick_form(self, form):
|
|
|
|
if self.app.is_farmos_wrapper():
|
|
return self.save_to_farmos(form)
|
|
|
|
return self.save_to_wuttafarm(form)
|
|
|
|
def save_to_farmos(self, form):
|
|
data = form.validated
|
|
|
|
assets = self.get_layer_assets()
|
|
assets = {a["uuid"]: a for a in assets}
|
|
asset = assets[data["asset"]]
|
|
|
|
# TODO: make this configurable?
|
|
unit_name = self.unit_name
|
|
|
|
unit = {"data": {"type": "taxonomy_term--unit"}}
|
|
new_unit = None
|
|
|
|
result = self.farmos_client.resource.get(
|
|
"taxonomy_term",
|
|
"unit",
|
|
params={
|
|
"filter[name]": unit_name,
|
|
},
|
|
)
|
|
if result["data"]:
|
|
unit["data"]["id"] = result["data"][0]["id"]
|
|
else:
|
|
payload = dict(unit)
|
|
payload["data"]["attributes"] = {"name": unit_name}
|
|
new_unit = Subrequest(
|
|
action=Action.create,
|
|
requestId="create-unit",
|
|
endpoint="api/taxonomy_term/unit",
|
|
body=payload,
|
|
)
|
|
unit["data"]["id"] = "{{create-unit.body@$.data.id}}"
|
|
|
|
quantity = {
|
|
"data": {
|
|
"type": "quantity--standard",
|
|
"attributes": {
|
|
"measure": "count",
|
|
"value": {
|
|
"numerator": data["count"],
|
|
"denominator": 1,
|
|
},
|
|
},
|
|
"relationships": {
|
|
"units": unit,
|
|
},
|
|
},
|
|
}
|
|
|
|
kw = {}
|
|
if new_unit:
|
|
kw["waitFor"] = ["create-unit"]
|
|
new_quantity = Subrequest(
|
|
action=Action.create,
|
|
requestId="create-quantity",
|
|
endpoint="api/quantity/standard",
|
|
body=quantity,
|
|
**kw,
|
|
)
|
|
|
|
notes = None
|
|
if data["notes"]:
|
|
notes = {"value": data["notes"]}
|
|
|
|
log = {
|
|
"data": {
|
|
"type": "log--harvest",
|
|
"attributes": {
|
|
"name": f"Collected {data['count']} {unit_name}",
|
|
"timestamp": self.app.localtime(data["timestamp"]).timestamp(),
|
|
"notes": notes,
|
|
"quick": ["eggs"],
|
|
},
|
|
"relationships": {
|
|
"asset": {
|
|
"data": [
|
|
{
|
|
"id": asset["uuid"],
|
|
"type": asset["type"],
|
|
},
|
|
],
|
|
},
|
|
"quantity": {
|
|
"data": [
|
|
{
|
|
"id": "{{create-quantity.body@$.data.id}}",
|
|
"type": "quantity--standard",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
new_log = Subrequest(
|
|
action=Action.create,
|
|
requestId="create-log",
|
|
waitFor=["create-quantity"],
|
|
endpoint="api/log/harvest",
|
|
body=log,
|
|
)
|
|
|
|
blueprints = [new_quantity, new_log]
|
|
if new_unit:
|
|
blueprints.insert(0, new_unit)
|
|
blueprint = SubrequestsBlueprint.parse_obj(blueprints)
|
|
response = self.farmos_client.subrequests.send(blueprint, format=Format.json)
|
|
|
|
log = json.loads(response["create-log#body{0}"]["body"])
|
|
|
|
if self.app.is_farmos_mirror():
|
|
if new_unit:
|
|
unit = json.loads(response["create-unit"]["body"])
|
|
self.app.auto_sync_from_farmos(
|
|
unit["data"], "Unit", client=self.farmos_client
|
|
)
|
|
quantity = json.loads(response["create-quantity"]["body"])
|
|
self.app.auto_sync_from_farmos(
|
|
quantity["data"], "StandardQuantity", client=self.farmos_client
|
|
)
|
|
self.app.auto_sync_from_farmos(
|
|
log["data"], "HarvestLog", client=self.farmos_client
|
|
)
|
|
|
|
return log
|
|
|
|
def save_to_wuttafarm(self, form):
|
|
model = self.app.model
|
|
session = self.Session()
|
|
data = form.validated
|
|
|
|
asset = (
|
|
session.query(model.Asset)
|
|
.filter(model.Asset.farmos_uuid == data["asset"])
|
|
.one()
|
|
)
|
|
|
|
# TODO: make this configurable?
|
|
unit_name = self.unit_name
|
|
|
|
new_unit = False
|
|
unit = session.query(model.Unit).filter(model.Unit.name == unit_name).first()
|
|
if not unit:
|
|
unit = model.Unit(name=unit_name)
|
|
session.add(unit)
|
|
new_unit = True
|
|
|
|
quantity = model.StandardQuantity(
|
|
quantity_type_id="standard",
|
|
measure_id="count",
|
|
value_numerator=data["count"],
|
|
value_denominator=1,
|
|
units=unit,
|
|
)
|
|
session.add(quantity)
|
|
|
|
log = model.HarvestLog(
|
|
log_type="harvest",
|
|
message=f"Collected {data['count']} {unit_name}",
|
|
timestamp=self.app.make_utc(data["timestamp"]),
|
|
notes=data["notes"] or None,
|
|
quick="eggs",
|
|
status="done",
|
|
)
|
|
session.add(log)
|
|
log.assets.append(asset)
|
|
log.quantities.append(quantity.quantity)
|
|
log.owners.append(self.request.user)
|
|
session.flush()
|
|
|
|
if self.app.is_farmos_mirror():
|
|
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 redirect_after_save(self, log):
|
|
model = self.app.model
|
|
|
|
if isinstance(log, model.HarvestLog):
|
|
return self.redirect(
|
|
self.request.route_url("logs_harvest.view", uuid=log.uuid)
|
|
)
|
|
|
|
return self.redirect(
|
|
self.request.route_url("farmos_logs_harvest.view", uuid=log["data"]["id"])
|
|
)
|
|
|
|
|
|
def includeme(config):
|
|
EggsQuickForm.defaults(config)
|