feat: add basic CRUD for direct API views: animal types, animal assets
This commit is contained in:
parent
c976d94bdd
commit
ad6ac13d50
6 changed files with 351 additions and 97 deletions
|
|
@ -58,10 +58,50 @@ class AnimalTypeRef(ObjectRef):
|
||||||
class FarmOSRef(colander.SchemaType):
|
class FarmOSRef(colander.SchemaType):
|
||||||
|
|
||||||
def __init__(self, request, route_prefix, *args, **kwargs):
|
def __init__(self, request, route_prefix, *args, **kwargs):
|
||||||
|
self.values = kwargs.pop("values", None)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.request = request
|
self.request = request
|
||||||
self.route_prefix = route_prefix
|
self.route_prefix = route_prefix
|
||||||
|
|
||||||
|
def get_values(self):
|
||||||
|
if callable(self.values):
|
||||||
|
self.values = self.values()
|
||||||
|
return self.values
|
||||||
|
|
||||||
|
def serialize(self, node, appstruct):
|
||||||
|
if appstruct is colander.null:
|
||||||
|
return colander.null
|
||||||
|
|
||||||
|
# nb. keep a ref to this for later use
|
||||||
|
node.model_instance = appstruct
|
||||||
|
|
||||||
|
# serialize to PK as string
|
||||||
|
return appstruct["uuid"]
|
||||||
|
|
||||||
|
def deserialize(self, node, cstruct):
|
||||||
|
if not cstruct:
|
||||||
|
return colander.null
|
||||||
|
|
||||||
|
# nb. deserialize to PK string, not dict
|
||||||
|
return cstruct
|
||||||
|
|
||||||
|
def widget_maker(self, **kwargs):
|
||||||
|
from wuttafarm.web.forms.widgets import FarmOSRefWidget
|
||||||
|
|
||||||
|
if not kwargs.get("readonly"):
|
||||||
|
if "values" not in kwargs:
|
||||||
|
if values := self.get_values():
|
||||||
|
kwargs["values"] = values
|
||||||
|
|
||||||
|
return FarmOSRefWidget(self.request, self.route_prefix, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FarmOSRefs(WuttaSet):
|
||||||
|
|
||||||
|
def __init__(self, request, route_prefix, *args, **kwargs):
|
||||||
|
super().__init__(request, *args, **kwargs)
|
||||||
|
self.route_prefix = route_prefix
|
||||||
|
|
||||||
def serialize(self, node, appstruct):
|
def serialize(self, node, appstruct):
|
||||||
if appstruct is colander.null:
|
if appstruct is colander.null:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
|
@ -69,10 +109,23 @@ class FarmOSRef(colander.SchemaType):
|
||||||
return json.dumps(appstruct)
|
return json.dumps(appstruct)
|
||||||
|
|
||||||
def widget_maker(self, **kwargs):
|
def widget_maker(self, **kwargs):
|
||||||
""" """
|
from wuttafarm.web.forms.widgets import FarmOSRefsWidget
|
||||||
from wuttafarm.web.forms.widgets import FarmOSRefWidget
|
|
||||||
|
|
||||||
return FarmOSRefWidget(self.request, self.route_prefix, **kwargs)
|
return FarmOSRefsWidget(self.request, self.route_prefix, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FarmOSLocationRefs(WuttaSet):
|
||||||
|
|
||||||
|
def serialize(self, node, appstruct):
|
||||||
|
if appstruct is colander.null:
|
||||||
|
return colander.null
|
||||||
|
|
||||||
|
return json.dumps(appstruct)
|
||||||
|
|
||||||
|
def widget_maker(self, **kwargs):
|
||||||
|
from wuttafarm.web.forms.widgets import FarmOSLocationRefsWidget
|
||||||
|
|
||||||
|
return FarmOSLocationRefsWidget(self.request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AnimalTypeType(colander.SchemaType):
|
class AnimalTypeType(colander.SchemaType):
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Custom form widgets for WuttaFarm
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
from deform.widget import Widget
|
from deform.widget import Widget, SelectWidget
|
||||||
from webhelpers2.html import HTML, tags
|
from webhelpers2.html import HTML, tags
|
||||||
|
|
||||||
from wuttaweb.forms.widgets import WuttaCheckboxChoiceWidget
|
from wuttaweb.forms.widgets import WuttaCheckboxChoiceWidget
|
||||||
|
|
@ -54,7 +54,7 @@ class ImageWidget(Widget):
|
||||||
return super().serialize(field, cstruct, **kw)
|
return super().serialize(field, cstruct, **kw)
|
||||||
|
|
||||||
|
|
||||||
class FarmOSRefWidget(Widget):
|
class FarmOSRefWidget(SelectWidget):
|
||||||
"""
|
"""
|
||||||
Generic widget to display "any reference field" - as a link to
|
Generic widget to display "any reference field" - as a link to
|
||||||
view the farmOS record it references. Only used by the farmOS
|
view the farmOS record it references. Only used by the farmOS
|
||||||
|
|
@ -72,7 +72,12 @@ class FarmOSRefWidget(Widget):
|
||||||
if cstruct in (colander.null, None):
|
if cstruct in (colander.null, None):
|
||||||
return HTML.tag("span")
|
return HTML.tag("span")
|
||||||
|
|
||||||
|
try:
|
||||||
obj = json.loads(cstruct)
|
obj = json.loads(cstruct)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
name = dict(self.values)[cstruct]
|
||||||
|
obj = {"uuid": cstruct, "name": name}
|
||||||
|
|
||||||
return tags.link_to(
|
return tags.link_to(
|
||||||
obj["name"],
|
obj["name"],
|
||||||
self.request.route_url(f"{self.route_prefix}.view", uuid=obj["uuid"]),
|
self.request.route_url(f"{self.route_prefix}.view", uuid=obj["uuid"]),
|
||||||
|
|
@ -81,6 +86,59 @@ class FarmOSRefWidget(Widget):
|
||||||
return super().serialize(field, cstruct, **kw)
|
return super().serialize(field, cstruct, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class FarmOSRefsWidget(Widget):
|
||||||
|
|
||||||
|
def __init__(self, request, route_prefix, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.request = request
|
||||||
|
self.route_prefix = route_prefix
|
||||||
|
|
||||||
|
def serialize(self, field, cstruct, **kw):
|
||||||
|
readonly = kw.get("readonly", self.readonly)
|
||||||
|
if readonly:
|
||||||
|
if cstruct in (colander.null, None):
|
||||||
|
return HTML.tag("span")
|
||||||
|
|
||||||
|
links = []
|
||||||
|
for obj in json.loads(cstruct):
|
||||||
|
url = self.request.route_url(
|
||||||
|
f"{self.route_prefix}.view", uuid=obj["uuid"]
|
||||||
|
)
|
||||||
|
links.append(HTML.tag("li", c=tags.link_to(obj["name"], url)))
|
||||||
|
|
||||||
|
return HTML.tag("ul", c=links)
|
||||||
|
|
||||||
|
return super().serialize(field, cstruct, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
class FarmOSLocationRefsWidget(Widget):
|
||||||
|
"""
|
||||||
|
Widget to display a "Locations" field for an asset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def serialize(self, field, cstruct, **kw):
|
||||||
|
readonly = kw.get("readonly", self.readonly)
|
||||||
|
if readonly:
|
||||||
|
if cstruct in (colander.null, None):
|
||||||
|
return HTML.tag("span")
|
||||||
|
|
||||||
|
locations = []
|
||||||
|
for location in json.loads(cstruct):
|
||||||
|
asset_type = location["type"].split("--")[1]
|
||||||
|
url = self.request.route_url(
|
||||||
|
f"farmos_{asset_type}_assets.view", uuid=location["uuid"]
|
||||||
|
)
|
||||||
|
locations.append(HTML.tag("li", c=tags.link_to(location["name"], url)))
|
||||||
|
|
||||||
|
return HTML.tag("ul", c=locations)
|
||||||
|
|
||||||
|
return super().serialize(field, cstruct, **kw)
|
||||||
|
|
||||||
|
|
||||||
class AnimalTypeWidget(Widget):
|
class AnimalTypeWidget(Widget):
|
||||||
"""
|
"""
|
||||||
Widget to display an "animal type" field.
|
Widget to display an "animal type" field.
|
||||||
|
|
|
||||||
|
|
@ -167,9 +167,9 @@ class AnimalAssetView(AssetMasterView):
|
||||||
"asset_name",
|
"asset_name",
|
||||||
"animal_type",
|
"animal_type",
|
||||||
"birthdate",
|
"birthdate",
|
||||||
|
"produces_eggs",
|
||||||
"sex",
|
"sex",
|
||||||
"is_sterile",
|
"is_sterile",
|
||||||
"produces_eggs",
|
|
||||||
"notes",
|
"notes",
|
||||||
"asset_type",
|
"asset_type",
|
||||||
"archived",
|
"archived",
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import datetime
|
||||||
import colander
|
import colander
|
||||||
from webhelpers2.html import tags
|
from webhelpers2.html import tags
|
||||||
|
|
||||||
from wuttaweb.forms.schema import WuttaDateTime
|
from wuttaweb.forms.schema import WuttaDateTime, WuttaDictEnum
|
||||||
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
||||||
|
|
||||||
from wuttafarm.web.views.farmos.assets import AssetMasterView
|
from wuttafarm.web.views.farmos.assets import AssetMasterView
|
||||||
|
|
@ -39,7 +39,7 @@ from wuttafarm.web.grids import (
|
||||||
NullableBooleanFilter,
|
NullableBooleanFilter,
|
||||||
DateTimeFilter,
|
DateTimeFilter,
|
||||||
)
|
)
|
||||||
from wuttafarm.web.forms.schema import AnimalTypeType
|
from wuttafarm.web.forms.schema import FarmOSRef
|
||||||
|
|
||||||
|
|
||||||
class AnimalView(AssetMasterView):
|
class AnimalView(AssetMasterView):
|
||||||
|
|
@ -85,20 +85,23 @@ class AnimalView(AssetMasterView):
|
||||||
"produces_eggs",
|
"produces_eggs",
|
||||||
"sex",
|
"sex",
|
||||||
"is_sterile",
|
"is_sterile",
|
||||||
"archived",
|
"notes",
|
||||||
|
"asset_type_name",
|
||||||
"groups",
|
"groups",
|
||||||
"owners",
|
"owners",
|
||||||
"location",
|
"locations",
|
||||||
"notes",
|
"archived",
|
||||||
"raw_image_url",
|
"thumbnail_url",
|
||||||
"large_image_url",
|
"image_url",
|
||||||
"thumbnail_image_url",
|
"thumbnail",
|
||||||
"image",
|
"image",
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_grid_data(self, **kwargs):
|
def get_farmos_api_includes(self):
|
||||||
kwargs.setdefault("include", "image,animal_type,group,owner,location")
|
includes = super().get_farmos_api_includes()
|
||||||
return super().get_grid_data(**kwargs)
|
includes.add("animal_type")
|
||||||
|
includes.add("group")
|
||||||
|
return includes
|
||||||
|
|
||||||
def configure_grid(self, grid):
|
def configure_grid(self, grid):
|
||||||
g = grid
|
g = grid
|
||||||
|
|
@ -188,19 +191,17 @@ class AnimalView(AssetMasterView):
|
||||||
else:
|
else:
|
||||||
sterile = animal["attributes"]["is_castrated"]
|
sterile = animal["attributes"]["is_castrated"]
|
||||||
|
|
||||||
animal_type = None
|
animal_type_object = None
|
||||||
animal_type_name = None
|
|
||||||
group_objects = []
|
group_objects = []
|
||||||
group_names = []
|
group_names = []
|
||||||
if relationships := animal.get("relationships"):
|
if relationships := animal.get("relationships"):
|
||||||
|
|
||||||
if animal_type := relationships.get("animal_type"):
|
if animal_type := relationships.get("animal_type"):
|
||||||
if animal_type := included.get(animal_type["data"]["id"]):
|
if animal_type := included.get(animal_type["data"]["id"]):
|
||||||
animal_type = {
|
animal_type_object = {
|
||||||
"uuid": animal_type["id"],
|
"uuid": animal_type["id"],
|
||||||
"name": animal_type["attributes"]["name"],
|
"name": animal_type["attributes"]["name"],
|
||||||
}
|
}
|
||||||
animal_type_name = animal_type["name"]
|
|
||||||
|
|
||||||
if groups := relationships.get("group"):
|
if groups := relationships.get("group"):
|
||||||
for group in groups["data"]:
|
for group in groups["data"]:
|
||||||
|
|
@ -214,8 +215,9 @@ class AnimalView(AssetMasterView):
|
||||||
|
|
||||||
normal.update(
|
normal.update(
|
||||||
{
|
{
|
||||||
"animal_type": animal_type,
|
"animal_type": animal_type_object,
|
||||||
"animal_type_name": animal_type_name,
|
"animal_type_uuid": animal_type_object["uuid"],
|
||||||
|
"animal_type_name": animal_type_object["name"],
|
||||||
"group_objects": group_objects,
|
"group_objects": group_objects,
|
||||||
"group_names": group_names,
|
"group_names": group_names,
|
||||||
"birthdate": birthdate,
|
"birthdate": birthdate,
|
||||||
|
|
@ -227,23 +229,78 @@ class AnimalView(AssetMasterView):
|
||||||
|
|
||||||
return normal
|
return normal
|
||||||
|
|
||||||
|
def get_animal_types(self):
|
||||||
|
animal_types = []
|
||||||
|
result = self.farmos_client.resource.get(
|
||||||
|
"taxonomy_term", "animal_type", params={"sort": "name"}
|
||||||
|
)
|
||||||
|
for animal_type in result["data"]:
|
||||||
|
animal_types.append((animal_type["id"], animal_type["attributes"]["name"]))
|
||||||
|
return animal_types
|
||||||
|
|
||||||
def configure_form(self, form):
|
def configure_form(self, form):
|
||||||
f = form
|
f = form
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
|
enum = self.app.enum
|
||||||
|
animal = f.model_instance
|
||||||
|
|
||||||
# animal_type
|
# animal_type
|
||||||
f.set_node("animal_type", AnimalTypeType(self.request))
|
f.set_node(
|
||||||
|
"animal_type",
|
||||||
# birthdate
|
FarmOSRef(
|
||||||
f.set_node("birthdate", WuttaDateTime())
|
self.request, "farmos_animal_types", values=self.get_animal_types
|
||||||
f.set_widget("birthdate", WuttaDateTimeWidget(self.request))
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# produces_eggs
|
# produces_eggs
|
||||||
f.set_node("produces_eggs", colander.Boolean())
|
f.set_node("produces_eggs", colander.Boolean())
|
||||||
|
|
||||||
|
# birthdate
|
||||||
|
f.set_node("birthdate", WuttaDateTime())
|
||||||
|
f.set_widget("birthdate", WuttaDateTimeWidget(self.request))
|
||||||
|
f.set_required("birthdate", False)
|
||||||
|
|
||||||
|
# sex
|
||||||
|
if not (self.creating or self.editing) and not animal["sex"]:
|
||||||
|
pass # TODO: dict enum widget does not handle null values well
|
||||||
|
else:
|
||||||
|
f.set_node("sex", WuttaDictEnum(self.request, enum.ANIMAL_SEX))
|
||||||
|
f.set_required("sex", False)
|
||||||
|
|
||||||
# is_sterile
|
# is_sterile
|
||||||
f.set_node("is_sterile", colander.Boolean())
|
f.set_node("is_sterile", colander.Boolean())
|
||||||
|
|
||||||
|
# groups
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("groups") # TODO
|
||||||
|
|
||||||
|
def get_api_payload(self, animal):
|
||||||
|
payload = super().get_api_payload(animal)
|
||||||
|
|
||||||
|
birthdate = None
|
||||||
|
if animal["birthdate"]:
|
||||||
|
birthdate = self.app.localtime(animal["birthdate"]).timestamp()
|
||||||
|
|
||||||
|
attrs = {
|
||||||
|
"sex": animal["sex"] or None,
|
||||||
|
"is_sterile": animal["is_sterile"],
|
||||||
|
"produces_eggs": animal["produces_eggs"],
|
||||||
|
"birthdate": birthdate,
|
||||||
|
}
|
||||||
|
|
||||||
|
rels = {
|
||||||
|
"animal_type": {
|
||||||
|
"data": {
|
||||||
|
"id": animal["animal_type"],
|
||||||
|
"type": "taxonomy_term--animal_type",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload["attributes"].update(attrs)
|
||||||
|
payload.setdefault("relationships", {}).update(rels)
|
||||||
|
return payload
|
||||||
|
|
||||||
def get_xref_buttons(self, animal):
|
def get_xref_buttons(self, animal):
|
||||||
buttons = super().get_xref_buttons(animal)
|
buttons = super().get_xref_buttons(animal)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import colander
|
||||||
from webhelpers2.html import tags
|
from webhelpers2.html import tags
|
||||||
|
|
||||||
from wuttafarm.web.views.farmos import FarmOSMasterView
|
from wuttafarm.web.views.farmos import FarmOSMasterView
|
||||||
from wuttafarm.web.forms.schema import UsersType, StructureType
|
from wuttafarm.web.forms.schema import FarmOSRefs, FarmOSLocationRefs
|
||||||
from wuttafarm.web.forms.widgets import ImageWidget
|
from wuttafarm.web.forms.widgets import ImageWidget
|
||||||
from wuttafarm.web.grids import (
|
from wuttafarm.web.grids import (
|
||||||
ResourceData,
|
ResourceData,
|
||||||
|
|
@ -44,13 +44,19 @@ class AssetMasterView(FarmOSMasterView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
farmos_asset_type = None
|
farmos_asset_type = None
|
||||||
|
creatable = True
|
||||||
|
editable = True
|
||||||
|
deletable = True
|
||||||
filterable = True
|
filterable = True
|
||||||
sort_on_backend = True
|
sort_on_backend = True
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
"name": "Asset Name",
|
"name": "Asset Name",
|
||||||
|
"asset_type_name": "Asset Type",
|
||||||
"owners": "Owner",
|
"owners": "Owner",
|
||||||
"locations": "Location",
|
"locations": "Location",
|
||||||
|
"thumbnail_url": "Thumbnail URL",
|
||||||
|
"image_url": "Image URL",
|
||||||
}
|
}
|
||||||
|
|
||||||
grid_columns = [
|
grid_columns = [
|
||||||
|
|
@ -69,14 +75,13 @@ class AssetMasterView(FarmOSMasterView):
|
||||||
"archived": {"active": True, "verb": "is_false"},
|
"archived": {"active": True, "verb": "is_false"},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_grid_data(self, columns=None, session=None, **kwargs):
|
def get_grid_data(self, **kwargs):
|
||||||
kwargs.setdefault("include", "image,owner,location")
|
|
||||||
kwargs.setdefault("normalizer", self.normalize_asset)
|
|
||||||
return ResourceData(
|
return ResourceData(
|
||||||
self.config,
|
self.config,
|
||||||
self.farmos_client,
|
self.farmos_client,
|
||||||
f"asset--{self.farmos_asset_type}",
|
f"asset--{self.farmos_asset_type}",
|
||||||
**kwargs,
|
include=",".join(self.get_farmos_api_includes()),
|
||||||
|
normalizer=self.normalize_asset,
|
||||||
)
|
)
|
||||||
|
|
||||||
def configure_grid(self, grid):
|
def configure_grid(self, grid):
|
||||||
|
|
@ -142,60 +147,19 @@ class AssetMasterView(FarmOSMasterView):
|
||||||
return "has-background-warning"
|
return "has-background-warning"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_farmos_api_includes(self):
|
||||||
|
return {"asset_type", "location", "owner", "image"}
|
||||||
|
|
||||||
def get_instance(self):
|
def get_instance(self):
|
||||||
asset = self.farmos_client.resource.get_id(
|
asset = self.farmos_client.resource.get_id(
|
||||||
"asset", self.farmos_asset_type, self.request.matchdict["uuid"]
|
"asset",
|
||||||
|
self.farmos_asset_type,
|
||||||
|
self.request.matchdict["uuid"],
|
||||||
|
params={"include": ",".join(self.get_farmos_api_includes())},
|
||||||
)
|
)
|
||||||
self.raw_json = asset
|
self.raw_json = asset
|
||||||
|
included = {obj["id"]: obj for obj in asset.get("included", [])}
|
||||||
# instance data
|
return self.normalize_asset(asset["data"], included)
|
||||||
data = self.normalize_asset(asset["data"], {})
|
|
||||||
|
|
||||||
if relationships := asset["data"].get("relationships"):
|
|
||||||
|
|
||||||
# add location
|
|
||||||
if location := relationships.get("location"):
|
|
||||||
if location["data"]:
|
|
||||||
location = self.farmos_client.resource.get_id(
|
|
||||||
"asset", "structure", location["data"][0]["id"]
|
|
||||||
)
|
|
||||||
data["location"] = {
|
|
||||||
"uuid": location["data"]["id"],
|
|
||||||
"name": location["data"]["attributes"]["name"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# add owners
|
|
||||||
if owner := relationships.get("owner"):
|
|
||||||
data["owners"] = []
|
|
||||||
for owner_data in owner["data"]:
|
|
||||||
owner = self.farmos_client.resource.get_id(
|
|
||||||
"user", "user", owner_data["id"]
|
|
||||||
)
|
|
||||||
data["owners"].append(
|
|
||||||
{
|
|
||||||
"uuid": owner["data"]["id"],
|
|
||||||
"display_name": owner["data"]["attributes"]["display_name"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# add image urls
|
|
||||||
if image := relationships.get("image"):
|
|
||||||
if image["data"]:
|
|
||||||
image = self.farmos_client.resource.get_id(
|
|
||||||
"file", "file", image["data"][0]["id"]
|
|
||||||
)
|
|
||||||
data["raw_image_url"] = self.app.get_farmos_url(
|
|
||||||
image["data"]["attributes"]["uri"]["url"]
|
|
||||||
)
|
|
||||||
# nb. other styles available: medium, wide
|
|
||||||
data["large_image_url"] = image["data"]["attributes"][
|
|
||||||
"image_style_uri"
|
|
||||||
]["large"]
|
|
||||||
data["thumbnail_image_url"] = image["data"]["attributes"][
|
|
||||||
"image_style_uri"
|
|
||||||
]["thumbnail"]
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get_instance_title(self, asset):
|
def get_instance_title(self, asset):
|
||||||
return asset["name"]
|
return asset["name"]
|
||||||
|
|
@ -210,13 +174,24 @@ class AssetMasterView(FarmOSMasterView):
|
||||||
else:
|
else:
|
||||||
archived = asset["attributes"]["status"] == "archived"
|
archived = asset["attributes"]["status"] == "archived"
|
||||||
|
|
||||||
|
asset_type_object = {}
|
||||||
|
asset_type_name = None
|
||||||
owner_objects = []
|
owner_objects = []
|
||||||
owner_names = []
|
owner_names = []
|
||||||
location_objects = []
|
location_objects = []
|
||||||
location_names = []
|
location_names = []
|
||||||
thumbnail_url = None
|
thumbnail_url = None
|
||||||
|
image_url = None
|
||||||
if relationships := asset.get("relationships"):
|
if relationships := asset.get("relationships"):
|
||||||
|
|
||||||
|
if asset_type := relationships.get("asset_type"):
|
||||||
|
if asset_type := included.get(asset_type["data"]["id"]):
|
||||||
|
asset_type_object = {
|
||||||
|
"uuid": asset_type["id"],
|
||||||
|
"name": asset_type["attributes"]["label"],
|
||||||
|
}
|
||||||
|
asset_type_name = asset_type_object["name"]
|
||||||
|
|
||||||
if owners := relationships.get("owner"):
|
if owners := relationships.get("owner"):
|
||||||
for user in owners["data"]:
|
for user in owners["data"]:
|
||||||
if user := included.get(user["id"]):
|
if user := included.get(user["id"]):
|
||||||
|
|
@ -244,42 +219,102 @@ class AssetMasterView(FarmOSMasterView):
|
||||||
thumbnail_url = image["attributes"]["image_style_uri"][
|
thumbnail_url = image["attributes"]["image_style_uri"][
|
||||||
"thumbnail"
|
"thumbnail"
|
||||||
]
|
]
|
||||||
|
image_url = image["attributes"]["image_style_uri"]["large"]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"uuid": asset["id"],
|
"uuid": asset["id"],
|
||||||
"drupal_id": asset["attributes"]["drupal_internal__id"],
|
"drupal_id": asset["attributes"]["drupal_internal__id"],
|
||||||
"name": asset["attributes"]["name"],
|
"name": asset["attributes"]["name"],
|
||||||
"location": colander.null, # TODO
|
"asset_type": asset_type_object,
|
||||||
|
"asset_type_name": asset_type_name,
|
||||||
"notes": notes or colander.null,
|
"notes": notes or colander.null,
|
||||||
"owners": owner_objects,
|
"owners": owner_objects,
|
||||||
"owner_names": owner_names,
|
"owner_names": owner_names,
|
||||||
"locations": location_objects,
|
"locations": location_objects,
|
||||||
"location_names": location_names,
|
"location_names": location_names,
|
||||||
"archived": archived,
|
"archived": archived,
|
||||||
"thumbnail_url": thumbnail_url,
|
"thumbnail_url": thumbnail_url or colander.null,
|
||||||
|
"image_url": image_url or colander.null,
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure_form(self, form):
|
def configure_form(self, form):
|
||||||
f = form
|
f = form
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
animal = f.model_instance
|
asset = f.model_instance
|
||||||
|
|
||||||
# location
|
# asset_type_name
|
||||||
f.set_node("location", StructureType(self.request))
|
if self.creating or self.editing:
|
||||||
|
f.remove("asset_type_name")
|
||||||
|
|
||||||
|
# locations
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("locations")
|
||||||
|
else:
|
||||||
|
f.set_node("locations", FarmOSLocationRefs(self.request))
|
||||||
|
|
||||||
# owners
|
# owners
|
||||||
f.set_node("owners", UsersType(self.request))
|
if self.creating or self.editing:
|
||||||
|
f.remove("owners") # TODO
|
||||||
|
else:
|
||||||
|
f.set_node("owners", FarmOSRefs(self.request, "farmos_users"))
|
||||||
|
|
||||||
# notes
|
# notes
|
||||||
f.set_widget("notes", "notes")
|
f.set_widget("notes", "notes")
|
||||||
|
f.set_required("notes", False)
|
||||||
|
|
||||||
# archived
|
# archived
|
||||||
f.set_node("archived", colander.Boolean())
|
f.set_node("archived", colander.Boolean())
|
||||||
|
|
||||||
|
# thumbnail_url
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("thumbnail_url")
|
||||||
|
|
||||||
|
# image_url
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("image_url")
|
||||||
|
|
||||||
|
# thumbnail
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("thumbnail")
|
||||||
|
elif asset.get("thumbnail_url"):
|
||||||
|
f.set_widget("thumbnail", ImageWidget("asset thumbnail"))
|
||||||
|
f.set_default("thumbnail", asset["thumbnail_url"])
|
||||||
|
|
||||||
# image
|
# image
|
||||||
if url := animal.get("large_image_url"):
|
if self.creating or self.editing:
|
||||||
f.set_widget("image", ImageWidget("animal image"))
|
f.remove("image")
|
||||||
f.set_default("image", url)
|
elif asset.get("image_url"):
|
||||||
|
f.set_widget("image", ImageWidget("asset image"))
|
||||||
|
f.set_default("image", asset["image_url"])
|
||||||
|
|
||||||
|
def persist(self, asset, session=None):
|
||||||
|
payload = self.get_api_payload(asset)
|
||||||
|
if self.editing:
|
||||||
|
payload["id"] = asset["uuid"]
|
||||||
|
|
||||||
|
result = self.farmos_client.asset.send(self.farmos_asset_type, payload)
|
||||||
|
|
||||||
|
if self.creating:
|
||||||
|
asset["uuid"] = result["data"]["id"]
|
||||||
|
|
||||||
|
def get_api_payload(self, asset):
|
||||||
|
|
||||||
|
attrs = {
|
||||||
|
"name": asset["name"],
|
||||||
|
"notes": {"value": asset["notes"] or None},
|
||||||
|
"archived": asset["archived"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if "is_location" in asset:
|
||||||
|
attrs["is_location"] = asset["is_location"]
|
||||||
|
|
||||||
|
if "is_fixed" in asset:
|
||||||
|
attrs["is_fixed"] = asset["is_fixed"]
|
||||||
|
|
||||||
|
return {"attributes": attrs}
|
||||||
|
|
||||||
|
def delete_instance(self, asset):
|
||||||
|
self.farmos_client.asset.delete(self.farmos_asset_type, asset["uuid"])
|
||||||
|
|
||||||
def get_xref_buttons(self, asset):
|
def get_xref_buttons(self, asset):
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,16 @@ import markdown
|
||||||
|
|
||||||
from wuttaweb.views import MasterView
|
from wuttaweb.views import MasterView
|
||||||
from wuttaweb.forms.schema import WuttaDateTime
|
from wuttaweb.forms.schema import WuttaDateTime
|
||||||
|
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
||||||
|
|
||||||
from wuttafarm.web.util import save_farmos_oauth2_token, use_farmos_style_grid_links
|
from wuttafarm.web.util import save_farmos_oauth2_token, use_farmos_style_grid_links
|
||||||
from wuttafarm.web.grids import ResourceData, StringFilter, SimpleSorter
|
from wuttafarm.web.grids import (
|
||||||
|
ResourceData,
|
||||||
|
StringFilter,
|
||||||
|
NullableStringFilter,
|
||||||
|
DateTimeFilter,
|
||||||
|
SimpleSorter,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FarmOSMasterView(MasterView):
|
class FarmOSMasterView(MasterView):
|
||||||
|
|
@ -114,6 +121,9 @@ class TaxonomyMasterView(FarmOSMasterView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
farmos_taxonomy_type = None
|
farmos_taxonomy_type = None
|
||||||
|
creatable = True
|
||||||
|
editable = True
|
||||||
|
deletable = True
|
||||||
filterable = True
|
filterable = True
|
||||||
sort_on_backend = True
|
sort_on_backend = True
|
||||||
|
|
||||||
|
|
@ -143,7 +153,7 @@ class TaxonomyMasterView(FarmOSMasterView):
|
||||||
normalizer=self.normalize_taxonomy_term,
|
normalizer=self.normalize_taxonomy_term,
|
||||||
)
|
)
|
||||||
|
|
||||||
def normalize_taxonomy_term(self, term):
|
def normalize_taxonomy_term(self, term, included):
|
||||||
|
|
||||||
if changed := term["attributes"]["changed"]:
|
if changed := term["attributes"]["changed"]:
|
||||||
changed = datetime.datetime.fromisoformat(changed)
|
changed = datetime.datetime.fromisoformat(changed)
|
||||||
|
|
@ -169,15 +179,21 @@ class TaxonomyMasterView(FarmOSMasterView):
|
||||||
g.set_sorter("name", SimpleSorter("name"))
|
g.set_sorter("name", SimpleSorter("name"))
|
||||||
g.set_filter("name", StringFilter)
|
g.set_filter("name", StringFilter)
|
||||||
|
|
||||||
|
# description
|
||||||
|
g.set_sorter("description", SimpleSorter("description.value"))
|
||||||
|
g.set_filter("description", NullableStringFilter, path="description.value")
|
||||||
|
|
||||||
# changed
|
# changed
|
||||||
g.set_renderer("changed", "datetime")
|
g.set_renderer("changed", "datetime")
|
||||||
|
g.set_sorter("changed", SimpleSorter("changed"))
|
||||||
|
g.set_filter("changed", DateTimeFilter)
|
||||||
|
|
||||||
def get_instance(self):
|
def get_instance(self):
|
||||||
result = self.farmos_client.resource.get_id(
|
result = self.farmos_client.resource.get_id(
|
||||||
"taxonomy_term", self.farmos_taxonomy_type, self.request.matchdict["uuid"]
|
"taxonomy_term", self.farmos_taxonomy_type, self.request.matchdict["uuid"]
|
||||||
)
|
)
|
||||||
self.raw_json = result
|
self.raw_json = result
|
||||||
return self.normalize_taxonomy_term(result["data"])
|
return self.normalize_taxonomy_term(result["data"], {})
|
||||||
|
|
||||||
def get_instance_title(self, term):
|
def get_instance_title(self, term):
|
||||||
return term["name"]
|
return term["name"]
|
||||||
|
|
@ -188,9 +204,44 @@ class TaxonomyMasterView(FarmOSMasterView):
|
||||||
|
|
||||||
# description
|
# description
|
||||||
f.set_widget("description", "notes")
|
f.set_widget("description", "notes")
|
||||||
|
f.set_required("description", False)
|
||||||
|
|
||||||
# changed
|
# changed
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("changed")
|
||||||
|
else:
|
||||||
f.set_node("changed", WuttaDateTime())
|
f.set_node("changed", WuttaDateTime())
|
||||||
|
f.set_widget("changed", WuttaDateTimeWidget(self.request))
|
||||||
|
|
||||||
|
def get_api_payload(self, term):
|
||||||
|
|
||||||
|
attrs = {
|
||||||
|
"name": term["name"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if description := term["description"]:
|
||||||
|
attrs["description"] = {"value": description}
|
||||||
|
else:
|
||||||
|
attrs["description"] = None
|
||||||
|
|
||||||
|
return {"attributes": attrs}
|
||||||
|
|
||||||
|
def persist(self, term, session=None):
|
||||||
|
payload = self.get_api_payload(term)
|
||||||
|
if self.editing:
|
||||||
|
payload["id"] = term["uuid"]
|
||||||
|
|
||||||
|
result = self.farmos_client.resource.send(
|
||||||
|
"taxonomy_term", self.farmos_taxonomy_type, payload
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.creating:
|
||||||
|
term["uuid"] = result["data"]["id"]
|
||||||
|
|
||||||
|
def delete_instance(self, term):
|
||||||
|
self.farmos_client.resource.delete(
|
||||||
|
"taxonomy_term", self.farmos_taxonomy_type, term["uuid"]
|
||||||
|
)
|
||||||
|
|
||||||
def get_xref_buttons(self, term):
|
def get_xref_buttons(self, term):
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue