diff --git a/CHANGELOG.md b/CHANGELOG.md
index 794220d..f1eedfc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,33 +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.9.0 (2026-03-10)
-
-### Feat
-
-- add schema, edit/sync support for Seeding Logs
-- add schema, edit/sync support for Equipment Assets
-- add schema, edit/sync support for Equipment Types
-- add schema, edit/sync support for Water Assets
-- add edit/sync support for Material Types + Material Quantities
-- add edit/sync support for Material Types
-- add edit/sync support for Log Quantities
-- add edit/sync support for `Log.groups`
-- add edit/sync support for `Log.locations`
-- expose Assets field when editing a Log record
-- add edit/sync support for Plant Seasons
-- add edit/sync support for asset parents
-
-### Fix
-
-- avoid error when material type is unknown
-- improve behavior when deleting a Standard Quantity
-- cleanup grid views for All, Standard Quantities
-- add ordinal for sorting Measures
-- expose `is_location` and `is_fixed` for editing on Animal Asset
-- allow "N/A" option for animal sex
-- fix Assets column for All Logs subgrid when viewing asset
-
## v0.8.0 (2026-03-04)
### Feat
diff --git a/pyproject.toml b/pyproject.toml
index 51737ce..1bb1dda 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
[project]
name = "WuttaFarm"
-version = "0.9.0"
+version = "0.8.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.29.2",
+ "WuttaWeb[continuum]>=0.29.0",
]
diff --git a/src/wuttafarm/app.py b/src/wuttafarm/app.py
index decd44f..cb9aed3 100644
--- a/src/wuttafarm/app.py
+++ b/src/wuttafarm/app.py
@@ -151,74 +151,6 @@ class WuttaFarmAppHandler(base.AppHandler):
factory = self.load_object(spec)
return factory(self.config, farmos_client)
- def get_quantity_types(self, session=None):
- """
- Returns a list of all known quantity types.
- """
- model = self.model
- with self.short_session(session=session) as sess:
- return (
- sess.query(model.QuantityType).order_by(model.QuantityType.name).all()
- )
-
- def get_measures(self, session=None):
- """
- Returns a list of all known measures.
- """
- model = self.model
- with self.short_session(session=session) as sess:
- return sess.query(model.Measure).order_by(model.Measure.ordinal).all()
-
- def get_units(self, session=None):
- """
- Returns a list of all known units.
- """
- model = self.model
- with self.short_session(session=session) as sess:
- return sess.query(model.Unit).order_by(model.Unit.name).all()
-
- def get_material_types(self, session=None):
- """
- Returns a list of all known material types.
- """
- model = self.model
- with self.short_session(session=session) as sess:
- return (
- sess.query(model.MaterialType).order_by(model.MaterialType.name).all()
- )
-
- def get_quantity_models(self):
- model = self.model
- return {
- "standard": model.StandardQuantity,
- "material": model.MaterialQuantity,
- }
-
- def get_true_quantity(self, quantity, require=True):
- model = self.model
- if not isinstance(quantity, model.Quantity):
- if require and not quantity:
- raise ValueError(f"quantity is not valid: {quantity}")
- return quantity
-
- session = self.get_session(quantity)
- models = self.get_quantity_models()
- if require and quantity.quantity_type_id not in models:
- raise ValueError(
- f"quantity has invalid quantity_type_id: {quantity.quantity_type_id}"
- )
-
- true_quantity = session.get(models[quantity.quantity_type_id], quantity.uuid)
- if require and not true_quantity:
- raise ValueError(f"quantity has no true/typed quantity record: {quantity}")
-
- return true_quantity
-
- def make_true_quantity(self, quantity_type_id, **kwargs):
- models = self.get_quantity_models()
- kwargs["quantity_type_id"] = quantity_type_id
- return models[quantity_type_id](**kwargs)
-
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
"""
Export the given object to farmOS, using configured handler.
diff --git a/src/wuttafarm/db/alembic/versions/1c89f3fbb521_add_materialtype.py b/src/wuttafarm/db/alembic/versions/1c89f3fbb521_add_materialtype.py
deleted file mode 100644
index 0d4c0f5..0000000
--- a/src/wuttafarm/db/alembic/versions/1c89f3fbb521_add_materialtype.py
+++ /dev/null
@@ -1,116 +0,0 @@
-"""add MaterialType
-
-Revision ID: 1c89f3fbb521
-Revises: 82a497e30a97
-Create Date: 2026-03-08 14:38:04.538621
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "1c89f3fbb521"
-down_revision: Union[str, None] = "82a497e30a97"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # material_type
- op.create_table(
- "material_type",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("name", sa.String(length=100), nullable=False),
- sa.Column("description", sa.String(length=255), nullable=True),
- sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True),
- sa.Column("drupal_id", sa.Integer(), nullable=True),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_material_type")),
- sa.UniqueConstraint("drupal_id", name=op.f("uq_material_type_drupal_id")),
- sa.UniqueConstraint("farmos_uuid", name=op.f("uq_material_type_farmos_uuid")),
- )
- op.create_table(
- "material_type_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column("name", sa.String(length=100), autoincrement=False, nullable=True),
- sa.Column(
- "description", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column(
- "farmos_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column("drupal_id", sa.Integer(), autoincrement=False, nullable=True),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_material_type_version")
- ),
- )
- op.create_index(
- op.f("ix_material_type_version_end_transaction_id"),
- "material_type_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_material_type_version_operation_type"),
- "material_type_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_material_type_version_pk_transaction_id",
- "material_type_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_material_type_version_pk_validity",
- "material_type_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_material_type_version_transaction_id"),
- "material_type_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # material_type
- op.drop_index(
- op.f("ix_material_type_version_transaction_id"),
- table_name="material_type_version",
- )
- op.drop_index(
- "ix_material_type_version_pk_validity", table_name="material_type_version"
- )
- op.drop_index(
- "ix_material_type_version_pk_transaction_id", table_name="material_type_version"
- )
- op.drop_index(
- op.f("ix_material_type_version_operation_type"),
- table_name="material_type_version",
- )
- op.drop_index(
- op.f("ix_material_type_version_end_transaction_id"),
- table_name="material_type_version",
- )
- op.drop_table("material_type_version")
- op.drop_table("material_type")
diff --git a/src/wuttafarm/db/alembic/versions/82a497e30a97_add_measure_ordinal.py b/src/wuttafarm/db/alembic/versions/82a497e30a97_add_measure_ordinal.py
deleted file mode 100644
index 2cd8057..0000000
--- a/src/wuttafarm/db/alembic/versions/82a497e30a97_add_measure_ordinal.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""add Measure.ordinal
-
-Revision ID: 82a497e30a97
-Revises: c5183b781d34
-Create Date: 2026-03-08 13:15:36.917747
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "82a497e30a97"
-down_revision: Union[str, None] = "c5183b781d34"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # measure
- op.add_column("measure", sa.Column("ordinal", sa.Integer(), nullable=True))
- op.add_column(
- "measure_version",
- sa.Column("ordinal", sa.Integer(), autoincrement=False, nullable=True),
- )
-
-
-def downgrade() -> None:
-
- # measure
- op.drop_column("measure_version", "ordinal")
- op.drop_column("measure", "ordinal")
diff --git a/src/wuttafarm/db/alembic/versions/9c53513f8862_add_materialquantity.py b/src/wuttafarm/db/alembic/versions/9c53513f8862_add_materialquantity.py
deleted file mode 100644
index 6f28989..0000000
--- a/src/wuttafarm/db/alembic/versions/9c53513f8862_add_materialquantity.py
+++ /dev/null
@@ -1,211 +0,0 @@
-"""add MaterialQuantity
-
-Revision ID: 9c53513f8862
-Revises: 1c89f3fbb521
-Create Date: 2026-03-08 18:14:05.587678
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "9c53513f8862"
-down_revision: Union[str, None] = "1c89f3fbb521"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # quantity_material
- op.create_table(
- "quantity_material",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["uuid"], ["quantity.uuid"], name=op.f("fk_quantity_material_uuid_quantity")
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_quantity_material")),
- )
- op.create_table(
- "quantity_material_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_quantity_material_version")
- ),
- )
- op.create_index(
- op.f("ix_quantity_material_version_end_transaction_id"),
- "quantity_material_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_quantity_material_version_operation_type"),
- "quantity_material_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_quantity_material_version_pk_transaction_id",
- "quantity_material_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_quantity_material_version_pk_validity",
- "quantity_material_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_quantity_material_version_transaction_id"),
- "quantity_material_version",
- ["transaction_id"],
- unique=False,
- )
-
- # quantity_material_material_type
- op.create_table(
- "quantity_material_material_type",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("quantity_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("material_type_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["material_type_uuid"],
- ["material_type.uuid"],
- name=op.f(
- "fk_quantity_material_material_type_material_type_uuid_material_type"
- ),
- ),
- sa.ForeignKeyConstraint(
- ["quantity_uuid"],
- ["quantity_material.uuid"],
- name=op.f(
- "fk_quantity_material_material_type_quantity_uuid_quantity_material"
- ),
- ),
- sa.PrimaryKeyConstraint(
- "uuid", name=op.f("pk_quantity_material_material_type")
- ),
- )
- op.create_table(
- "quantity_material_material_type_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "quantity_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "material_type_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid",
- "transaction_id",
- name=op.f("pk_quantity_material_material_type_version"),
- ),
- )
- op.create_index(
- op.f("ix_quantity_material_material_type_version_end_transaction_id"),
- "quantity_material_material_type_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_quantity_material_material_type_version_operation_type"),
- "quantity_material_material_type_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_quantity_material_material_type_version_pk_transaction_id",
- "quantity_material_material_type_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_quantity_material_material_type_version_pk_validity",
- "quantity_material_material_type_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_quantity_material_material_type_version_transaction_id"),
- "quantity_material_material_type_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # quantity_material_material_type
- op.drop_index(
- op.f("ix_quantity_material_material_type_version_transaction_id"),
- table_name="quantity_material_material_type_version",
- )
- op.drop_index(
- "ix_quantity_material_material_type_version_pk_validity",
- table_name="quantity_material_material_type_version",
- )
- op.drop_index(
- "ix_quantity_material_material_type_version_pk_transaction_id",
- table_name="quantity_material_material_type_version",
- )
- op.drop_index(
- op.f("ix_quantity_material_material_type_version_operation_type"),
- table_name="quantity_material_material_type_version",
- )
- op.drop_index(
- op.f("ix_quantity_material_material_type_version_end_transaction_id"),
- table_name="quantity_material_material_type_version",
- )
- op.drop_table("quantity_material_material_type_version")
- op.drop_table("quantity_material_material_type")
-
- # quantity_material
- op.drop_index(
- op.f("ix_quantity_material_version_transaction_id"),
- table_name="quantity_material_version",
- )
- op.drop_index(
- "ix_quantity_material_version_pk_validity",
- table_name="quantity_material_version",
- )
- op.drop_index(
- "ix_quantity_material_version_pk_transaction_id",
- table_name="quantity_material_version",
- )
- op.drop_index(
- op.f("ix_quantity_material_version_operation_type"),
- table_name="quantity_material_version",
- )
- op.drop_index(
- op.f("ix_quantity_material_version_end_transaction_id"),
- table_name="quantity_material_version",
- )
- op.drop_table("quantity_material_version")
- op.drop_table("quantity_material")
diff --git a/src/wuttafarm/db/alembic/versions/c5183b781d34_add_plant_seasons.py b/src/wuttafarm/db/alembic/versions/c5183b781d34_add_plant_seasons.py
deleted file mode 100644
index 406ad64..0000000
--- a/src/wuttafarm/db/alembic/versions/c5183b781d34_add_plant_seasons.py
+++ /dev/null
@@ -1,205 +0,0 @@
-"""add plant seasons
-
-Revision ID: c5183b781d34
-Revises: 5f474125a80e
-Create Date: 2026-03-06 20:18:40.160531
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "c5183b781d34"
-down_revision: Union[str, None] = "5f474125a80e"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # season
- op.create_table(
- "season",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("name", sa.String(length=100), nullable=False),
- sa.Column("description", sa.String(length=255), nullable=True),
- sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True),
- sa.Column("drupal_id", sa.Integer(), nullable=True),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_season")),
- sa.UniqueConstraint("drupal_id", name=op.f("uq_season_drupal_id")),
- sa.UniqueConstraint("farmos_uuid", name=op.f("uq_season_farmos_uuid")),
- sa.UniqueConstraint("name", name=op.f("uq_season_name")),
- )
- op.create_table(
- "season_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column("name", sa.String(length=100), autoincrement=False, nullable=True),
- sa.Column(
- "description", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column(
- "farmos_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column("drupal_id", sa.Integer(), autoincrement=False, nullable=True),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_season_version")
- ),
- )
- op.create_index(
- op.f("ix_season_version_end_transaction_id"),
- "season_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_season_version_operation_type"),
- "season_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_season_version_pk_transaction_id",
- "season_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_season_version_pk_validity",
- "season_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_season_version_transaction_id"),
- "season_version",
- ["transaction_id"],
- unique=False,
- )
-
- # asset_plant_season
- op.create_table(
- "asset_plant_season",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("plant_asset_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("season_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["plant_asset_uuid"],
- ["asset_plant.uuid"],
- name=op.f("fk_asset_plant_season_plant_asset_uuid_asset_plant"),
- ),
- sa.ForeignKeyConstraint(
- ["season_uuid"],
- ["season.uuid"],
- name=op.f("fk_asset_plant_season_season_uuid_season"),
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_plant_season")),
- )
- op.create_table(
- "asset_plant_season_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "plant_asset_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "season_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_asset_plant_season_version")
- ),
- )
- op.create_index(
- op.f("ix_asset_plant_season_version_end_transaction_id"),
- "asset_plant_season_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_plant_season_version_operation_type"),
- "asset_plant_season_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_asset_plant_season_version_pk_transaction_id",
- "asset_plant_season_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_asset_plant_season_version_pk_validity",
- "asset_plant_season_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_plant_season_version_transaction_id"),
- "asset_plant_season_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # asset_plant_season
- op.drop_index(
- op.f("ix_asset_plant_season_version_transaction_id"),
- table_name="asset_plant_season_version",
- )
- op.drop_index(
- "ix_asset_plant_season_version_pk_validity",
- table_name="asset_plant_season_version",
- )
- op.drop_index(
- "ix_asset_plant_season_version_pk_transaction_id",
- table_name="asset_plant_season_version",
- )
- op.drop_index(
- op.f("ix_asset_plant_season_version_operation_type"),
- table_name="asset_plant_season_version",
- )
- op.drop_index(
- op.f("ix_asset_plant_season_version_end_transaction_id"),
- table_name="asset_plant_season_version",
- )
- op.drop_table("asset_plant_season_version")
- op.drop_table("asset_plant_season")
-
- # season
- op.drop_index(op.f("ix_season_version_transaction_id"), table_name="season_version")
- op.drop_index("ix_season_version_pk_validity", table_name="season_version")
- op.drop_index("ix_season_version_pk_transaction_id", table_name="season_version")
- op.drop_index(op.f("ix_season_version_operation_type"), table_name="season_version")
- op.drop_index(
- op.f("ix_season_version_end_transaction_id"), table_name="season_version"
- )
- op.drop_table("season_version")
- op.drop_table("season")
diff --git a/src/wuttafarm/db/alembic/versions/dca5b48a5562_add_seedinglog.py b/src/wuttafarm/db/alembic/versions/dca5b48a5562_add_seedinglog.py
deleted file mode 100644
index 6a374b4..0000000
--- a/src/wuttafarm/db/alembic/versions/dca5b48a5562_add_seedinglog.py
+++ /dev/null
@@ -1,108 +0,0 @@
-"""add SeedingLog
-
-Revision ID: dca5b48a5562
-Revises: e9b8664e1f39
-Create Date: 2026-03-10 09:52:13.999777
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "dca5b48a5562"
-down_revision: Union[str, None] = "e9b8664e1f39"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # log_seeding
- op.create_table(
- "log_seeding",
- sa.Column("source", sa.String(length=255), nullable=True),
- sa.Column("purchase_date", sa.DateTime(), nullable=True),
- sa.Column("lot_number", sa.String(length=255), nullable=True),
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["uuid"], ["log.uuid"], name=op.f("fk_log_seeding_uuid_log")
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_seeding")),
- )
- op.create_table(
- "log_seeding_version",
- sa.Column("source", sa.String(length=255), autoincrement=False, nullable=True),
- sa.Column("purchase_date", sa.DateTime(), autoincrement=False, nullable=True),
- sa.Column(
- "lot_number", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_log_seeding_version")
- ),
- )
- op.create_index(
- op.f("ix_log_seeding_version_end_transaction_id"),
- "log_seeding_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_log_seeding_version_operation_type"),
- "log_seeding_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_log_seeding_version_pk_transaction_id",
- "log_seeding_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_log_seeding_version_pk_validity",
- "log_seeding_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_log_seeding_version_transaction_id"),
- "log_seeding_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # log_seeding
- op.drop_index(
- op.f("ix_log_seeding_version_transaction_id"), table_name="log_seeding_version"
- )
- op.drop_index(
- "ix_log_seeding_version_pk_validity", table_name="log_seeding_version"
- )
- op.drop_index(
- "ix_log_seeding_version_pk_transaction_id", table_name="log_seeding_version"
- )
- op.drop_index(
- op.f("ix_log_seeding_version_operation_type"), table_name="log_seeding_version"
- )
- op.drop_index(
- op.f("ix_log_seeding_version_end_transaction_id"),
- table_name="log_seeding_version",
- )
- op.drop_table("log_seeding_version")
- op.drop_table("log_seeding")
diff --git a/src/wuttafarm/db/alembic/versions/de1197d24485_add_waterasset.py b/src/wuttafarm/db/alembic/versions/de1197d24485_add_waterasset.py
deleted file mode 100644
index e123fc3..0000000
--- a/src/wuttafarm/db/alembic/versions/de1197d24485_add_waterasset.py
+++ /dev/null
@@ -1,100 +0,0 @@
-"""add WaterAsset
-
-Revision ID: de1197d24485
-Revises: 9c53513f8862
-Create Date: 2026-03-09 14:59:12.032318
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "de1197d24485"
-down_revision: Union[str, None] = "9c53513f8862"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # asset_water
- op.create_table(
- "asset_water",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["uuid"], ["asset.uuid"], name=op.f("fk_asset_water_uuid_asset")
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_water")),
- )
- op.create_table(
- "asset_water_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_asset_water_version")
- ),
- )
- op.create_index(
- op.f("ix_asset_water_version_end_transaction_id"),
- "asset_water_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_water_version_operation_type"),
- "asset_water_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_asset_water_version_pk_transaction_id",
- "asset_water_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_asset_water_version_pk_validity",
- "asset_water_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_water_version_transaction_id"),
- "asset_water_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # asset_water
- op.drop_index(
- op.f("ix_asset_water_version_transaction_id"), table_name="asset_water_version"
- )
- op.drop_index(
- "ix_asset_water_version_pk_validity", table_name="asset_water_version"
- )
- op.drop_index(
- "ix_asset_water_version_pk_transaction_id", table_name="asset_water_version"
- )
- op.drop_index(
- op.f("ix_asset_water_version_operation_type"), table_name="asset_water_version"
- )
- op.drop_index(
- op.f("ix_asset_water_version_end_transaction_id"),
- table_name="asset_water_version",
- )
- op.drop_table("asset_water_version")
- op.drop_table("asset_water")
diff --git a/src/wuttafarm/db/alembic/versions/e5b27eac471c_add_equipmenttype.py b/src/wuttafarm/db/alembic/versions/e5b27eac471c_add_equipmenttype.py
deleted file mode 100644
index a436725..0000000
--- a/src/wuttafarm/db/alembic/versions/e5b27eac471c_add_equipmenttype.py
+++ /dev/null
@@ -1,118 +0,0 @@
-"""add EquipmentType
-
-Revision ID: e5b27eac471c
-Revises: de1197d24485
-Create Date: 2026-03-09 15:45:35.047694
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "e5b27eac471c"
-down_revision: Union[str, None] = "de1197d24485"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # equipment_type
- op.create_table(
- "equipment_type",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("name", sa.String(length=100), nullable=False),
- sa.Column("description", sa.String(length=255), nullable=True),
- sa.Column("drupal_id", sa.Integer(), nullable=True),
- sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_equipment_type")),
- sa.UniqueConstraint("drupal_id", name=op.f("uq_equipment_type_drupal_id")),
- sa.UniqueConstraint("farmos_uuid", name=op.f("uq_equipment_type_farmos_uuid")),
- sa.UniqueConstraint("name", name=op.f("uq_equipment_type_name")),
- )
- op.create_table(
- "equipment_type_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column("name", sa.String(length=100), autoincrement=False, nullable=True),
- sa.Column(
- "description", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column("drupal_id", sa.Integer(), autoincrement=False, nullable=True),
- sa.Column(
- "farmos_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_equipment_type_version")
- ),
- )
- op.create_index(
- op.f("ix_equipment_type_version_end_transaction_id"),
- "equipment_type_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_equipment_type_version_operation_type"),
- "equipment_type_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_equipment_type_version_pk_transaction_id",
- "equipment_type_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_equipment_type_version_pk_validity",
- "equipment_type_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_equipment_type_version_transaction_id"),
- "equipment_type_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # equipment_type
- op.drop_index(
- op.f("ix_equipment_type_version_transaction_id"),
- table_name="equipment_type_version",
- )
- op.drop_index(
- "ix_equipment_type_version_pk_validity", table_name="equipment_type_version"
- )
- op.drop_index(
- "ix_equipment_type_version_pk_transaction_id",
- table_name="equipment_type_version",
- )
- op.drop_index(
- op.f("ix_equipment_type_version_operation_type"),
- table_name="equipment_type_version",
- )
- op.drop_index(
- op.f("ix_equipment_type_version_end_transaction_id"),
- table_name="equipment_type_version",
- )
- op.drop_table("equipment_type_version")
- op.drop_table("equipment_type")
diff --git a/src/wuttafarm/db/alembic/versions/e9b8664e1f39_add_equipmentasset.py b/src/wuttafarm/db/alembic/versions/e9b8664e1f39_add_equipmentasset.py
deleted file mode 100644
index 2a8ed15..0000000
--- a/src/wuttafarm/db/alembic/versions/e9b8664e1f39_add_equipmentasset.py
+++ /dev/null
@@ -1,218 +0,0 @@
-"""add EquipmentAsset
-
-Revision ID: e9b8664e1f39
-Revises: e5b27eac471c
-Create Date: 2026-03-09 18:05:54.917562
-
-"""
-
-from typing import Sequence, Union
-
-from alembic import op
-import sqlalchemy as sa
-import wuttjamaican.db.util
-
-
-# revision identifiers, used by Alembic.
-revision: str = "e9b8664e1f39"
-down_revision: Union[str, None] = "e5b27eac471c"
-branch_labels: Union[str, Sequence[str], None] = None
-depends_on: Union[str, Sequence[str], None] = None
-
-
-def upgrade() -> None:
-
- # asset_equipment
- op.create_table(
- "asset_equipment",
- sa.Column("manufacturer", sa.String(length=255), nullable=True),
- sa.Column("model", sa.String(length=255), nullable=True),
- sa.Column("serial_number", sa.String(length=255), nullable=True),
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["uuid"], ["asset.uuid"], name=op.f("fk_asset_equipment_uuid_asset")
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_equipment")),
- )
- op.create_table(
- "asset_equipment_version",
- sa.Column(
- "manufacturer", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column("model", sa.String(length=255), autoincrement=False, nullable=True),
- sa.Column(
- "serial_number", sa.String(length=255), autoincrement=False, nullable=True
- ),
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid", "transaction_id", name=op.f("pk_asset_equipment_version")
- ),
- )
- op.create_index(
- op.f("ix_asset_equipment_version_end_transaction_id"),
- "asset_equipment_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_equipment_version_operation_type"),
- "asset_equipment_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_asset_equipment_version_pk_transaction_id",
- "asset_equipment_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_asset_equipment_version_pk_validity",
- "asset_equipment_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_equipment_version_transaction_id"),
- "asset_equipment_version",
- ["transaction_id"],
- unique=False,
- )
-
- # asset_equipment_equipment_type
- op.create_table(
- "asset_equipment_equipment_type",
- sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("equipment_asset_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.Column("equipment_type_uuid", wuttjamaican.db.util.UUID(), nullable=False),
- sa.ForeignKeyConstraint(
- ["equipment_asset_uuid"],
- ["asset_equipment.uuid"],
- name=op.f(
- "fk_asset_equipment_equipment_type_equipment_asset_uuid_asset_equipment"
- ),
- ),
- sa.ForeignKeyConstraint(
- ["equipment_type_uuid"],
- ["equipment_type.uuid"],
- name=op.f(
- "fk_asset_equipment_equipment_type_equipment_type_uuid_equipment_type"
- ),
- ),
- sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_equipment_equipment_type")),
- )
- op.create_table(
- "asset_equipment_equipment_type_version",
- sa.Column(
- "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
- ),
- sa.Column(
- "equipment_asset_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "equipment_type_uuid",
- wuttjamaican.db.util.UUID(),
- autoincrement=False,
- nullable=True,
- ),
- sa.Column(
- "transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
- ),
- sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
- sa.Column("operation_type", sa.SmallInteger(), nullable=False),
- sa.PrimaryKeyConstraint(
- "uuid",
- "transaction_id",
- name=op.f("pk_asset_equipment_equipment_type_version"),
- ),
- )
- op.create_index(
- op.f("ix_asset_equipment_equipment_type_version_end_transaction_id"),
- "asset_equipment_equipment_type_version",
- ["end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_equipment_equipment_type_version_operation_type"),
- "asset_equipment_equipment_type_version",
- ["operation_type"],
- unique=False,
- )
- op.create_index(
- "ix_asset_equipment_equipment_type_version_pk_transaction_id",
- "asset_equipment_equipment_type_version",
- ["uuid", sa.literal_column("transaction_id DESC")],
- unique=False,
- )
- op.create_index(
- "ix_asset_equipment_equipment_type_version_pk_validity",
- "asset_equipment_equipment_type_version",
- ["uuid", "transaction_id", "end_transaction_id"],
- unique=False,
- )
- op.create_index(
- op.f("ix_asset_equipment_equipment_type_version_transaction_id"),
- "asset_equipment_equipment_type_version",
- ["transaction_id"],
- unique=False,
- )
-
-
-def downgrade() -> None:
-
- # asset_equipment_equipment_type
- op.drop_index(
- op.f("ix_asset_equipment_equipment_type_version_transaction_id"),
- table_name="asset_equipment_equipment_type_version",
- )
- op.drop_index(
- "ix_asset_equipment_equipment_type_version_pk_validity",
- table_name="asset_equipment_equipment_type_version",
- )
- op.drop_index(
- "ix_asset_equipment_equipment_type_version_pk_transaction_id",
- table_name="asset_equipment_equipment_type_version",
- )
- op.drop_index(
- op.f("ix_asset_equipment_equipment_type_version_operation_type"),
- table_name="asset_equipment_equipment_type_version",
- )
- op.drop_index(
- op.f("ix_asset_equipment_equipment_type_version_end_transaction_id"),
- table_name="asset_equipment_equipment_type_version",
- )
- op.drop_table("asset_equipment_equipment_type_version")
- op.drop_table("asset_equipment_equipment_type")
-
- # asset_equipment
- op.drop_index(
- op.f("ix_asset_equipment_version_transaction_id"),
- table_name="asset_equipment_version",
- )
- op.drop_index(
- "ix_asset_equipment_version_pk_validity", table_name="asset_equipment_version"
- )
- op.drop_index(
- "ix_asset_equipment_version_pk_transaction_id",
- table_name="asset_equipment_version",
- )
- op.drop_index(
- op.f("ix_asset_equipment_version_operation_type"),
- table_name="asset_equipment_version",
- )
- op.drop_index(
- op.f("ix_asset_equipment_version_end_transaction_id"),
- table_name="asset_equipment_version",
- )
- op.drop_table("asset_equipment_version")
- op.drop_table("asset_equipment")
diff --git a/src/wuttafarm/db/model/__init__.py b/src/wuttafarm/db/model/__init__.py
index d90272b..15514fb 100644
--- a/src/wuttafarm/db/model/__init__.py
+++ b/src/wuttafarm/db/model/__init__.py
@@ -31,31 +31,15 @@ from .users import WuttaFarmUser
# wuttafarm proper models
from .unit import Unit, Measure
-from .material_type import MaterialType
-from .quantities import (
- QuantityType,
- Quantity,
- StandardQuantity,
- MaterialQuantity,
- MaterialQuantityMaterialType,
-)
+from .quantities import QuantityType, Quantity, StandardQuantity
from .asset import AssetType, Asset, AssetParent
from .asset_land import LandType, LandAsset
from .asset_structure import StructureType, StructureAsset
-from .asset_equipment import EquipmentType, EquipmentAsset, EquipmentAssetEquipmentType
from .asset_animal import AnimalType, AnimalAsset
from .asset_group import GroupAsset
-from .asset_plant import (
- PlantType,
- Season,
- PlantAsset,
- PlantAssetPlantType,
- PlantAssetSeason,
-)
-from .asset_water import WaterAsset
+from .asset_plant import PlantType, PlantAsset, PlantAssetPlantType
from .log import LogType, Log, LogAsset, LogGroup, LogLocation, LogQuantity, LogOwner
from .log_activity import ActivityLog
from .log_harvest import HarvestLog
from .log_medical import MedicalLog
from .log_observation import ObservationLog
-from .log_seeding import SeedingLog
diff --git a/src/wuttafarm/db/model/asset_equipment.py b/src/wuttafarm/db/model/asset_equipment.py
deleted file mode 100644
index 51af9ee..0000000
--- a/src/wuttafarm/db/model/asset_equipment.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Model definition for Equipment
-"""
-
-import sqlalchemy as sa
-from sqlalchemy import orm
-from sqlalchemy.ext.associationproxy import association_proxy
-
-from wuttjamaican.db import model
-
-from wuttafarm.db.model.taxonomy import TaxonomyMixin
-from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies
-
-
-class EquipmentType(TaxonomyMixin, model.Base):
- """
- Represents an "equipment type" (taxonomy term) from farmOS
- """
-
- __tablename__ = "equipment_type"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Equipment Type",
- "model_title_plural": "Equipment Types",
- }
-
- _equipment_assets = orm.relationship(
- "EquipmentAssetEquipmentType",
- cascade_backrefs=False,
- back_populates="equipment_type",
- )
-
-
-class EquipmentAsset(AssetMixin, model.Base):
- """
- Represents an equipment asset from farmOS
- """
-
- __tablename__ = "asset_equipment"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Equipment Asset",
- "model_title_plural": "Equipment Assets",
- "farmos_asset_type": "equipment",
- }
-
- manufacturer = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Name of the manufacturer, if applicable.
- """,
- )
-
- model = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Model name for the equipment, if applicable.
- """,
- )
-
- serial_number = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Serial number for the equipment, if applicable.
- """,
- )
-
- _equipment_types = orm.relationship(
- "EquipmentAssetEquipmentType",
- cascade="all, delete-orphan",
- cascade_backrefs=False,
- back_populates="equipment_asset",
- )
-
- equipment_types = association_proxy(
- "_equipment_types",
- "equipment_type",
- creator=lambda pt: EquipmentAssetEquipmentType(equipment_type=pt),
- )
-
-
-add_asset_proxies(EquipmentAsset)
-
-
-class EquipmentAssetEquipmentType(model.Base):
- """
- Associates one or more equipment types with an equipment asset.
- """
-
- __tablename__ = "asset_equipment_equipment_type"
- __versioned__ = {}
-
- uuid = model.uuid_column()
-
- equipment_asset_uuid = model.uuid_fk_column("asset_equipment.uuid", nullable=False)
- equipment_asset = orm.relationship(
- EquipmentAsset,
- foreign_keys=equipment_asset_uuid,
- back_populates="_equipment_types",
- )
-
- equipment_type_uuid = model.uuid_fk_column("equipment_type.uuid", nullable=False)
- equipment_type = orm.relationship(
- EquipmentType,
- doc="""
- Reference to the equipment type.
- """,
- back_populates="_equipment_assets",
- )
diff --git a/src/wuttafarm/db/model/asset_plant.py b/src/wuttafarm/db/model/asset_plant.py
index fa1be03..62f7e9b 100644
--- a/src/wuttafarm/db/model/asset_plant.py
+++ b/src/wuttafarm/db/model/asset_plant.py
@@ -91,65 +91,6 @@ class PlantType(model.Base):
return self.name or ""
-class Season(model.Base):
- """
- Represents a "season" (taxonomy term) from farmOS
- """
-
- __tablename__ = "season"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Season",
- "model_title_plural": "Seasons",
- }
-
- uuid = model.uuid_column()
-
- name = sa.Column(
- sa.String(length=100),
- nullable=False,
- unique=True,
- doc="""
- Name of the season.
- """,
- )
-
- description = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Optional description for the season.
- """,
- )
-
- farmos_uuid = sa.Column(
- model.UUID(),
- nullable=True,
- unique=True,
- doc="""
- UUID for the season within farmOS.
- """,
- )
-
- drupal_id = sa.Column(
- sa.Integer(),
- nullable=True,
- unique=True,
- doc="""
- Drupal internal ID for the season.
- """,
- )
-
- _plant_assets = orm.relationship(
- "PlantAssetSeason",
- cascade_backrefs=False,
- back_populates="season",
- )
-
- def __str__(self):
- return self.name or ""
-
-
class PlantAsset(AssetMixin, model.Base):
"""
Represents a plant asset from farmOS
@@ -176,19 +117,6 @@ class PlantAsset(AssetMixin, model.Base):
creator=lambda pt: PlantAssetPlantType(plant_type=pt),
)
- _seasons = orm.relationship(
- "PlantAssetSeason",
- cascade="all, delete-orphan",
- cascade_backrefs=False,
- back_populates="plant_asset",
- )
-
- seasons = association_proxy(
- "_seasons",
- "season",
- creator=lambda s: PlantAssetSeason(season=s),
- )
-
add_asset_proxies(PlantAsset)
@@ -218,30 +146,3 @@ class PlantAssetPlantType(model.Base):
""",
back_populates="_plant_assets",
)
-
-
-class PlantAssetSeason(model.Base):
- """
- Associates one or more seasons with a plant asset.
- """
-
- __tablename__ = "asset_plant_season"
- __versioned__ = {}
-
- uuid = model.uuid_column()
-
- plant_asset_uuid = model.uuid_fk_column("asset_plant.uuid", nullable=False)
- plant_asset = orm.relationship(
- PlantAsset,
- foreign_keys=plant_asset_uuid,
- back_populates="_seasons",
- )
-
- season_uuid = model.uuid_fk_column("season.uuid", nullable=False)
- season = orm.relationship(
- Season,
- doc="""
- Reference to the season.
- """,
- back_populates="_plant_assets",
- )
diff --git a/src/wuttafarm/db/model/asset_water.py b/src/wuttafarm/db/model/asset_water.py
deleted file mode 100644
index 046c899..0000000
--- a/src/wuttafarm/db/model/asset_water.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Model definition for Water Assets
-"""
-
-from wuttjamaican.db import model
-
-from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies
-
-
-class WaterAsset(AssetMixin, model.Base):
- """
- Represents a water asset from farmOS
- """
-
- __tablename__ = "asset_water"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Water Asset",
- "model_title_plural": "Water Assets",
- "farmos_asset_type": "water",
- }
-
-
-add_asset_proxies(WaterAsset)
diff --git a/src/wuttafarm/db/model/log_seeding.py b/src/wuttafarm/db/model/log_seeding.py
deleted file mode 100644
index 7f68923..0000000
--- a/src/wuttafarm/db/model/log_seeding.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Model definition for Seeding Logs
-"""
-
-import sqlalchemy as sa
-
-from wuttjamaican.db import model
-
-from wuttafarm.db.model.log import LogMixin, add_log_proxies
-
-
-class SeedingLog(LogMixin, model.Base):
- """
- Represents a Seeding Log from farmOS
- """
-
- __tablename__ = "log_seeding"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Seeding Log",
- "model_title_plural": "Seeding Logs",
- "farmos_log_type": "seeding",
- }
-
- source = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Where the seed was obtained, if applicable.
- """,
- )
-
- purchase_date = sa.Column(
- sa.DateTime(),
- nullable=True,
- doc="""
- When the seed was purchased, if applicable.
- """,
- )
-
- lot_number = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Lot number for the seed, if applicable.
- """,
- )
-
-
-add_log_proxies(SeedingLog)
diff --git a/src/wuttafarm/db/model/material_type.py b/src/wuttafarm/db/model/material_type.py
deleted file mode 100644
index a124451..0000000
--- a/src/wuttafarm/db/model/material_type.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Model definition for Material Types
-"""
-
-import sqlalchemy as sa
-from sqlalchemy import orm
-from sqlalchemy.ext.associationproxy import association_proxy
-
-from wuttjamaican.db import model
-
-
-class MaterialType(model.Base):
- """
- Represents a "material type" (taxonomy term) from farmOS
- """
-
- __tablename__ = "material_type"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Material Type",
- "model_title_plural": "Material Types",
- }
-
- uuid = model.uuid_column()
-
- name = sa.Column(
- sa.String(length=100),
- nullable=False,
- doc="""
- Name of the material type.
- """,
- )
-
- description = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Optional description for the material type.
- """,
- )
-
- farmos_uuid = sa.Column(
- model.UUID(),
- nullable=True,
- unique=True,
- doc="""
- UUID for the material type within farmOS.
- """,
- )
-
- drupal_id = sa.Column(
- sa.Integer(),
- nullable=True,
- unique=True,
- doc="""
- Drupal internal ID for the material type.
- """,
- )
-
- _quantities = orm.relationship(
- "MaterialQuantityMaterialType",
- cascade="all, delete-orphan",
- cascade_backrefs=False,
- back_populates="material_type",
- )
-
- def _make_material_quantity(qty):
- from wuttafarm.db.model import MaterialQuantityMaterialType
-
- return MaterialQuantityMaterialType(quantity=qty)
-
- quantities = association_proxy(
- "_quantities",
- "quantity",
- creator=_make_material_quantity,
- )
-
- def __str__(self):
- return self.name or ""
diff --git a/src/wuttafarm/db/model/quantities.py b/src/wuttafarm/db/model/quantities.py
index 4fa92af..4bed6a0 100644
--- a/src/wuttafarm/db/model/quantities.py
+++ b/src/wuttafarm/db/model/quantities.py
@@ -181,13 +181,9 @@ class Quantity(model.Base):
creator=make_log_quantity,
)
- def get_value_decimal(self):
- # TODO: should actually return a decimal here?
- return self.value_numerator / self.value_denominator
-
def render_as_text(self, config=None):
measure = str(self.measure or self.measure_id or "")
- value = self.get_value_decimal()
+ value = self.value_numerator / self.value_denominator
if config:
app = config.get_app()
value = app.render_quantity(value)
@@ -204,15 +200,7 @@ class QuantityMixin:
@declared_attr
def quantity(cls):
- return orm.relationship(
- Quantity,
- single_parent=True,
- cascade="all, delete-orphan",
- cascade_backrefs=False,
- )
-
- def get_value_decimal(self):
- return self.quantity.get_value_decimal()
+ return orm.relationship(Quantity)
def render_as_text(self, config=None):
return self.quantity.render_as_text(config)
@@ -252,64 +240,3 @@ class StandardQuantity(QuantityMixin, model.Base):
add_quantity_proxies(StandardQuantity)
-
-
-class MaterialQuantity(QuantityMixin, model.Base):
- """
- Represents a Material Quantity from farmOS
- """
-
- __tablename__ = "quantity_material"
- __versioned__ = {}
- __wutta_hint__ = {
- "model_title": "Material Quantity",
- "model_title_plural": "Material Quantities",
- "farmos_quantity_type": "material",
- }
-
- _material_types = orm.relationship(
- "MaterialQuantityMaterialType",
- cascade="all, delete-orphan",
- cascade_backrefs=False,
- back_populates="quantity",
- )
-
- material_types = association_proxy(
- "_material_types",
- "material_type",
- creator=lambda mtype: MaterialQuantityMaterialType(material_type=mtype),
- )
-
- def render_as_text(self, config=None):
- text = super().render_as_text(config)
- mtypes = ", ".join([str(mt) for mt in self.material_types])
- return f"{mtypes} {text}"
-
-
-add_quantity_proxies(MaterialQuantity)
-
-
-class MaterialQuantityMaterialType(model.Base):
- """
- Represents a "material quantity's material type relationship" from
- farmOS.
- """
-
- __tablename__ = "quantity_material_material_type"
- __versioned__ = {}
-
- uuid = model.uuid_column()
-
- quantity_uuid = model.uuid_fk_column("quantity_material.uuid", nullable=False)
- quantity = orm.relationship(
- MaterialQuantity,
- foreign_keys=quantity_uuid,
- back_populates="_material_types",
- )
-
- material_type_uuid = model.uuid_fk_column("material_type.uuid", nullable=False)
- material_type = orm.relationship(
- "MaterialType",
- foreign_keys=material_type_uuid,
- back_populates="_quantities",
- )
diff --git a/src/wuttafarm/db/model/taxonomy.py b/src/wuttafarm/db/model/taxonomy.py
deleted file mode 100644
index 3d84197..0000000
--- a/src/wuttafarm/db/model/taxonomy.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Base logic for taxonomy term models
-"""
-
-import sqlalchemy as sa
-
-from wuttjamaican.db import model
-
-
-class TaxonomyMixin:
- """
- Mixin for taxonomy term models
- """
-
- uuid = model.uuid_column()
-
- name = sa.Column(
- sa.String(length=100),
- nullable=False,
- unique=True,
- doc="""
- Name for the taxonomy term.
- """,
- )
-
- description = sa.Column(
- sa.String(length=255),
- nullable=True,
- doc="""
- Optional description for the taxonomy term.
- """,
- )
-
- drupal_id = sa.Column(
- sa.Integer(),
- nullable=True,
- unique=True,
- doc="""
- Drupal internal ID for the taxonomy term.
- """,
- )
-
- farmos_uuid = sa.Column(
- model.UUID(),
- nullable=True,
- unique=True,
- doc="""
- UUID for the taxonomy term within farmOS.
- """,
- )
-
- def __str__(self):
- return self.name or ""
diff --git a/src/wuttafarm/db/model/unit.py b/src/wuttafarm/db/model/unit.py
index a376e2c..e9c6e70 100644
--- a/src/wuttafarm/db/model/unit.py
+++ b/src/wuttafarm/db/model/unit.py
@@ -42,14 +42,6 @@ class Measure(model.Base):
uuid = model.uuid_column()
- ordinal = sa.Column(
- sa.Integer(),
- nullable=True,
- doc="""
- Ordinal (sequence number) for the measure.
- """,
- )
-
name = sa.Column(
sa.String(length=100),
nullable=False,
diff --git a/src/wuttafarm/farmos/importing/model.py b/src/wuttafarm/farmos/importing/model.py
index 011a170..ad1cb38 100644
--- a/src/wuttafarm/farmos/importing/model.py
+++ b/src/wuttafarm/farmos/importing/model.py
@@ -71,15 +71,13 @@ class ToFarmOSTaxonomy(ToFarmOS):
supported_fields = [
"uuid",
"name",
- "description",
]
def get_target_objects(self, **kwargs):
- return list(
- self.farmos_client.resource.iterate(
- "taxonomy_term", self.farmos_taxonomy_type
- )
+ result = self.farmos_client.resource.get(
+ "taxonomy_term", self.farmos_taxonomy_type
)
+ return result["data"]
def get_target_object(self, key):
@@ -103,24 +101,17 @@ class ToFarmOSTaxonomy(ToFarmOS):
return result["data"]
def normalize_target_object(self, obj):
- if description := obj["attributes"]["description"]:
- description = description["value"]
return {
"uuid": UUID(obj["id"]),
"name": obj["attributes"]["name"],
- "description": description,
}
def get_term_payload(self, source_data):
-
- attrs = {}
- if "name" in self.fields:
- attrs["name"] = source_data["name"]
- if "description" in self.fields:
- attrs["description"] = {"value": source_data["description"]}
-
- payload = {"attributes": attrs}
- return payload
+ return {
+ "attributes": {
+ "name": source_data["name"],
+ }
+ }
def create_target_object(self, key, source_data):
if source_data.get("__ignoreme__"):
@@ -136,9 +127,9 @@ class ToFarmOSTaxonomy(ToFarmOS):
normal["_new_object"] = result["data"]
return normal
- def update_target_object(self, term, source_data, target_data=None):
+ def update_target_object(self, asset, source_data, target_data=None):
if self.dry_run:
- return term
+ return asset
payload = self.get_term_payload(source_data)
payload["id"] = str(source_data["uuid"])
@@ -155,12 +146,9 @@ class ToFarmOSAsset(ToFarmOS):
farmos_asset_type = None
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.normal = self.app.get_normalizer(self.farmos_client)
-
def get_target_objects(self, **kwargs):
- return list(self.farmos_client.asset.iterate(self.farmos_asset_type))
+ assets = self.farmos_client.asset.get(self.farmos_asset_type)
+ return assets["data"]
def get_target_object(self, key):
@@ -203,17 +191,18 @@ class ToFarmOSAsset(ToFarmOS):
return self.normalize_target_object(result["data"])
def normalize_target_object(self, asset):
- normal = self.normal.normalize_farmos_asset(asset)
+
+ if notes := asset["attributes"]["notes"]:
+ notes = notes["value"]
+
return {
- "uuid": UUID(normal["uuid"]),
- "asset_name": normal["asset_name"],
- "is_location": normal["is_location"],
- "is_fixed": normal["is_fixed"],
- # nb. this is only used for certain asset types
- "produces_eggs": normal["produces_eggs"],
- "parents": [(p["asset_type"], UUID(p["uuid"])) for p in normal["parents"]],
- "notes": normal["notes"],
- "archived": normal["archived"],
+ "uuid": UUID(asset["id"]),
+ "asset_name": asset["attributes"]["name"],
+ "is_location": asset["attributes"]["is_location"],
+ "is_fixed": asset["attributes"]["is_fixed"],
+ "produces_eggs": asset["attributes"].get("produces_eggs"),
+ "notes": notes,
+ "archived": asset["attributes"]["archived"],
}
def get_asset_payload(self, source_data):
@@ -232,18 +221,8 @@ class ToFarmOSAsset(ToFarmOS):
if "archived" in self.fields:
attrs["archived"] = source_data["archived"]
- rels = {}
- if "parents" in self.fields:
- rels["parent"] = {"data": []}
- for asset_type, uuid in source_data["parents"]:
- rels["parent"]["data"].append(
- {
- "id": str(uuid),
- "type": f"asset--{asset_type}",
- }
- )
+ payload = {"attributes": attrs}
- payload = {"attributes": attrs, "relationships": rels}
return payload
@@ -266,8 +245,6 @@ class AnimalAssetImporter(ToFarmOSAsset):
"is_sterile",
"produces_eggs",
"birthdate",
- "is_location",
- "is_fixed",
"notes",
"archived",
]
@@ -319,80 +296,6 @@ class AnimalTypeImporter(ToFarmOSTaxonomy):
farmos_taxonomy_type = "animal_type"
-class EquipmentTypeImporter(ToFarmOSTaxonomy):
-
- model_title = "EquipmentType"
- farmos_taxonomy_type = "equipment_type"
-
-
-class EquipmentAssetImporter(ToFarmOSAsset):
-
- model_title = "EquipmentAsset"
- farmos_asset_type = "equipment"
-
- supported_fields = [
- "uuid",
- "asset_name",
- "manufacturer",
- "model",
- "serial_number",
- "equipment_type_uuids",
- "is_location",
- "is_fixed",
- "notes",
- "archived",
- ]
-
- def normalize_target_object(self, equipment):
- data = super().normalize_target_object(equipment)
- data.update(
- {
- "manufacturer": equipment["attributes"]["manufacturer"],
- "model": equipment["attributes"]["model"],
- "serial_number": equipment["attributes"]["serial_number"],
- "equipment_type_uuids": [
- UUID(etype["id"])
- for etype in equipment["relationships"]["equipment_type"]["data"]
- ],
- }
- )
- return data
-
- def get_asset_payload(self, source_data):
- payload = super().get_asset_payload(source_data)
-
- attrs = {}
- if "manufacturer" in self.fields:
- attrs["manufacturer"] = source_data["manufacturer"]
- if "model" in self.fields:
- attrs["model"] = source_data["model"]
- if "serial_number" in self.fields:
- attrs["serial_number"] = source_data["serial_number"]
-
- rels = {}
- if "equipment_type_uuids" in self.fields:
- rels["equipment_type"] = {"data": []}
- for uuid in source_data["equipment_type_uuids"]:
- rels["equipment_type"]["data"].append(
- {
- "id": str(uuid),
- "type": "taxonomy_term--equipment_type",
- }
- )
-
- payload["attributes"].update(attrs)
- if rels:
- payload.setdefault("relationships", {}).update(rels)
-
- return payload
-
-
-class MaterialTypeImporter(ToFarmOSTaxonomy):
-
- model_title = "MaterialType"
- farmos_taxonomy_type = "material_type"
-
-
class GroupAssetImporter(ToFarmOSAsset):
model_title = "GroupAsset"
@@ -450,12 +353,6 @@ class PlantTypeImporter(ToFarmOSTaxonomy):
farmos_taxonomy_type = "plant_type"
-class SeasonImporter(ToFarmOSTaxonomy):
-
- model_title = "Season"
- farmos_taxonomy_type = "season"
-
-
class PlantAssetImporter(ToFarmOSAsset):
model_title = "PlantAsset"
@@ -465,7 +362,6 @@ class PlantAssetImporter(ToFarmOSAsset):
"uuid",
"asset_name",
"plant_type_uuids",
- "season_uuids",
"notes",
"archived",
]
@@ -477,9 +373,6 @@ class PlantAssetImporter(ToFarmOSAsset):
"plant_type_uuids": [
UUID(p["id"]) for p in plant["relationships"]["plant_type"]["data"]
],
- "season_uuids": [
- UUID(p["id"]) for p in plant["relationships"]["season"]["data"]
- ],
}
)
return data
@@ -505,15 +398,6 @@ class PlantAssetImporter(ToFarmOSAsset):
"type": "taxonomy_term--plant_type",
}
)
- if "season_uuids" in self.fields:
- rels["season"] = {"data": []}
- for uuid in source_data["season_uuids"]:
- rels["season"]["data"].append(
- {
- "id": str(uuid),
- "type": "taxonomy_term--season",
- }
- )
payload["attributes"].update(attrs)
if rels:
@@ -559,21 +443,6 @@ class StructureAssetImporter(ToFarmOSAsset):
return payload
-class WaterAssetImporter(ToFarmOSAsset):
-
- model_title = "WaterAsset"
- farmos_asset_type = "water"
-
- supported_fields = [
- "uuid",
- "asset_name",
- "is_location",
- "is_fixed",
- "notes",
- "archived",
- ]
-
-
##############################
# quantity importers
##############################
@@ -700,49 +569,6 @@ class ToFarmOSQuantity(ToFarmOS):
return payload
-class MaterialQuantityImporter(ToFarmOSQuantity):
-
- model_title = "MaterialQuantity"
- farmos_quantity_type = "material"
-
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "material_types",
- ]
- )
- return fields
-
- def normalize_target_object(self, quantity):
- data = super().normalize_target_object(quantity)
-
- if "material_types" in self.fields:
- data["material_types"] = [
- UUID(mtype["id"])
- for mtype in quantity["relationships"]["material_type"]["data"]
- ]
-
- return data
-
- def get_quantity_payload(self, source_data):
- payload = super().get_quantity_payload(source_data)
-
- rels = {}
- if "material_types" in self.fields:
- rels["material_type"] = {"data": []}
- for uuid in source_data["material_types"]:
- rels["material_type"]["data"].append(
- {
- "id": str(uuid),
- "type": "taxonomy_term--material_type",
- }
- )
-
- payload.setdefault("relationships", {}).update(rels)
- return payload
-
-
class StandardQuantityImporter(ToFarmOSQuantity):
model_title = "StandardQuantity"
@@ -771,8 +597,6 @@ class ToFarmOSLog(ToFarmOS):
"notes",
"quick",
"assets",
- "locations",
- "groups",
"quantities",
]
@@ -781,7 +605,8 @@ class ToFarmOSLog(ToFarmOS):
self.normal = self.app.get_normalizer(self.farmos_client)
def get_target_objects(self, **kwargs):
- return list(self.farmos_client.log.iterate(self.farmos_log_type))
+ result = self.farmos_client.log.get(self.farmos_log_type)
+ return result["data"]
def get_target_object(self, key):
@@ -835,10 +660,6 @@ class ToFarmOSLog(ToFarmOS):
"notes": normal["notes"],
"quick": normal["quick"],
"assets": [(a["asset_type"], UUID(a["uuid"])) for a in normal["assets"]],
- "locations": [
- (l["asset_type"], UUID(l["uuid"])) for l in normal["locations"]
- ],
- "groups": [(g["asset_type"], UUID(g["uuid"])) for g in normal["groups"]],
"quantities": [UUID(uuid) for uuid in normal["quantity_uuids"]],
}
@@ -871,26 +692,6 @@ class ToFarmOSLog(ToFarmOS):
}
)
rels["asset"] = {"data": assets}
- if "locations" in self.fields:
- locations = []
- for asset_type, uuid in source_data["locations"]:
- locations.append(
- {
- "type": f"asset--{asset_type}",
- "id": str(uuid),
- }
- )
- rels["location"] = {"data": locations}
- if "groups" in self.fields:
- groups = []
- for asset_type, uuid in source_data["groups"]:
- groups.append(
- {
- "type": f"asset--{asset_type}",
- "id": str(uuid),
- }
- )
- rels["group"] = {"data": groups}
if "quantities" in self.fields:
quantities = []
for uuid in source_data["quantities"]:
@@ -955,48 +756,3 @@ class ObservationLogImporter(ToFarmOSLog):
model_title = "ObservationLog"
farmos_log_type = "observation"
-
-
-class SeedingLogImporter(ToFarmOSLog):
-
- model_title = "SeedingLog"
- farmos_log_type = "seeding"
-
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "source",
- "purchase_date",
- "lot_number",
- ]
- )
- return fields
-
- def normalize_target_object(self, log):
- data = super().normalize_target_object(log)
- data.update(
- {
- "source": log["attributes"]["source"],
- "purchase_date": self.normalize_datetime(
- log["attributes"]["purchase_date"]
- ),
- "lot_number": log["attributes"]["lot_number"],
- }
- )
- return data
-
- def get_log_payload(self, source_data):
- payload = super().get_log_payload(source_data)
-
- attrs = {}
- if "source" in self.fields:
- attrs["source"] = source_data["source"]
- if "purchase_date" in self.fields:
- attrs["purchase_date"] = self.format_datetime(source_data["purchase_date"])
- if "lot_number" in self.fields:
- attrs["lot_number"] = source_data["lot_number"]
-
- if attrs:
- payload["attributes"].update(attrs)
- return payload
diff --git a/src/wuttafarm/farmos/importing/wuttafarm.py b/src/wuttafarm/farmos/importing/wuttafarm.py
index 746f761..8394e4c 100644
--- a/src/wuttafarm/farmos/importing/wuttafarm.py
+++ b/src/wuttafarm/farmos/importing/wuttafarm.py
@@ -98,24 +98,17 @@ class FromWuttaFarmToFarmOS(FromWuttaFarmHandler, ToFarmOSHandler):
importers = super().define_importers()
importers["LandAsset"] = LandAssetImporter
importers["StructureAsset"] = StructureAssetImporter
- importers["WaterAsset"] = WaterAssetImporter
- importers["EquipmentType"] = EquipmentTypeImporter
- importers["EquipmentAsset"] = EquipmentAssetImporter
importers["AnimalType"] = AnimalTypeImporter
importers["AnimalAsset"] = AnimalAssetImporter
importers["GroupAsset"] = GroupAssetImporter
importers["PlantType"] = PlantTypeImporter
- importers["Season"] = SeasonImporter
importers["PlantAsset"] = PlantAssetImporter
importers["Unit"] = UnitImporter
- importers["MaterialType"] = MaterialTypeImporter
- importers["MaterialQuantity"] = MaterialQuantityImporter
importers["StandardQuantity"] = StandardQuantityImporter
importers["ActivityLog"] = ActivityLogImporter
importers["HarvestLog"] = HarvestLogImporter
importers["MedicalLog"] = MedicalLogImporter
importers["ObservationLog"] = ObservationLogImporter
- importers["SeedingLog"] = SeedingLogImporter
return importers
@@ -141,158 +134,60 @@ class FromWuttaFarm(FromWutta):
return obj
-class FromWuttaFarmAsset(FromWuttaFarm):
- """
- Base class for WuttaFarm → farmOS API asset exporters
- """
-
- supported_fields = [
- "uuid",
- "asset_name",
- "is_location",
- "is_fixed",
- "parents",
- "notes",
- "archived",
- ]
-
- def normalize_source_object(self, asset):
- return {
- "uuid": asset.farmos_uuid or self.app.make_true_uuid(),
- "asset_name": asset.asset_name,
- "is_location": asset.is_location,
- "is_fixed": asset.is_fixed,
- "parents": [(p.asset_type, p.farmos_uuid) for p in asset.parents],
- "notes": asset.notes,
- "archived": asset.archived,
- "_src_object": asset,
- }
-
-
-class AnimalAssetImporter(
- FromWuttaFarmAsset, farmos_importing.model.AnimalAssetImporter
-):
+class AnimalAssetImporter(FromWuttaFarm, farmos_importing.model.AnimalAssetImporter):
"""
WuttaFarm → farmOS API exporter for Animal Assets
"""
source_model_class = model.AnimalAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "animal_type_uuid",
- "sex",
- "is_sterile",
- "produces_eggs",
- "birthdate",
- ]
- )
- return fields
-
- def normalize_source_object(self, animal):
- data = super().normalize_source_object(animal)
- data.update(
- {
- "animal_type_uuid": animal.animal_type.farmos_uuid,
- "sex": animal.sex,
- "is_sterile": animal.is_sterile,
- "produces_eggs": animal.produces_eggs,
- "birthdate": animal.birthdate,
- }
- )
- return data
-
-
-class FromWuttaFarmTaxonomy(FromWuttaFarm):
- """
- Base class for taxonomy term exporters
- """
-
supported_fields = [
"uuid",
- "name",
- "description",
+ "asset_name",
+ "animal_type_uuid",
+ "sex",
+ "is_sterile",
+ "produces_eggs",
+ "birthdate",
+ "notes",
+ "archived",
]
- drupal_internal_id_field = "drupal_internal__tid"
-
- def normalize_source_object(self, term):
+ def normalize_source_object(self, animal):
return {
- "uuid": term.farmos_uuid or self.app.make_true_uuid(),
- "name": term.name,
- "description": term.description,
- "_src_object": term,
+ "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,
+ "produces_eggs": animal.produces_eggs,
+ "birthdate": animal.birthdate,
+ "notes": animal.notes,
+ "archived": animal.archived,
+ "_src_object": animal,
}
-class EquipmentTypeImporter(
- FromWuttaFarmTaxonomy, farmos_importing.model.EquipmentTypeImporter
-):
- """
- WuttaFarm → farmOS API exporter for Equipment Types
- """
-
- source_model_class = model.EquipmentType
-
-
-class EquipmentAssetImporter(
- FromWuttaFarmAsset, farmos_importing.model.EquipmentAssetImporter
-):
- """
- WuttaFarm → farmOS API exporter for Equipment Assets
- """
-
- source_model_class = model.EquipmentAsset
-
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
-
- print(fields)
- fields.extend(
- [
- "manufacturer",
- "model",
- "serial_number",
- "equipment_type_uuids",
- ]
- )
- return fields
-
- def normalize_source_object(self, equipment):
- data = super().normalize_source_object(equipment)
- data.update(
- {
- "manufacturer": equipment.manufacturer,
- "model": equipment.model,
- "serial_number": equipment.serial_number,
- "equipment_type_uuids": [
- etype.farmos_uuid for etype in equipment.equipment_types
- ],
- }
- )
- return data
-
-
-class AnimalTypeImporter(
- FromWuttaFarmTaxonomy, farmos_importing.model.AnimalTypeImporter
-):
+class AnimalTypeImporter(FromWuttaFarm, farmos_importing.model.AnimalTypeImporter):
"""
WuttaFarm → farmOS API exporter for Animal Types
"""
source_model_class = model.AnimalType
+ supported_fields = [
+ "uuid",
+ "name",
+ ]
-class MaterialTypeImporter(
- FromWuttaFarmTaxonomy, farmos_importing.model.MaterialTypeImporter
-):
- """
- WuttaFarm → farmOS API exporter for Material Types
- """
+ drupal_internal_id_field = "drupal_internal__tid"
- source_model_class = model.MaterialType
+ 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 UnitImporter(FromWuttaFarm, farmos_importing.model.UnitImporter):
@@ -317,56 +212,60 @@ class UnitImporter(FromWuttaFarm, farmos_importing.model.UnitImporter):
}
-class GroupAssetImporter(FromWuttaFarmAsset, farmos_importing.model.GroupAssetImporter):
+class GroupAssetImporter(FromWuttaFarm, farmos_importing.model.GroupAssetImporter):
"""
WuttaFarm → farmOS API exporter for Group Assets
"""
source_model_class = model.GroupAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "produces_eggs",
- ]
- )
- return fields
+ supported_fields = [
+ "uuid",
+ "asset_name",
+ "produces_eggs",
+ "notes",
+ "archived",
+ ]
def normalize_source_object(self, group):
- data = super().normalize_source_object(group)
- data.update(
- {
- "produces_eggs": group.produces_eggs,
- }
- )
- return data
+ return {
+ "uuid": group.farmos_uuid or self.app.make_true_uuid(),
+ "asset_name": group.asset_name,
+ "produces_eggs": group.produces_eggs,
+ "notes": group.notes,
+ "archived": group.archived,
+ "_src_object": group,
+ }
-class LandAssetImporter(FromWuttaFarmAsset, farmos_importing.model.LandAssetImporter):
+class LandAssetImporter(FromWuttaFarm, farmos_importing.model.LandAssetImporter):
"""
WuttaFarm → farmOS API exporter for Land Assets
"""
source_model_class = model.LandAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "land_type_id",
- ]
- )
- return fields
+ supported_fields = [
+ "uuid",
+ "asset_name",
+ "land_type_id",
+ "is_location",
+ "is_fixed",
+ "notes",
+ "archived",
+ ]
def normalize_source_object(self, land):
- data = super().normalize_source_object(land)
- data.update(
- {
- "land_type_id": land.land_type.drupal_id,
- }
- )
- return data
+ 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 PlantTypeImporter(FromWuttaFarm, farmos_importing.model.PlantTypeImporter):
@@ -391,58 +290,34 @@ class PlantTypeImporter(FromWuttaFarm, farmos_importing.model.PlantTypeImporter)
}
-class SeasonImporter(FromWuttaFarm, farmos_importing.model.SeasonImporter):
- """
- WuttaFarm → farmOS API exporter for Seasons
- """
-
- source_model_class = model.Season
-
- supported_fields = [
- "uuid",
- "name",
- ]
-
- drupal_internal_id_field = "drupal_internal__tid"
-
- def normalize_source_object(self, season):
- return {
- "uuid": season.farmos_uuid or self.app.make_true_uuid(),
- "name": season.name,
- "_src_object": season,
- }
-
-
-class PlantAssetImporter(FromWuttaFarmAsset, farmos_importing.model.PlantAssetImporter):
+class PlantAssetImporter(FromWuttaFarm, farmos_importing.model.PlantAssetImporter):
"""
WuttaFarm → farmOS API exporter for Plant Assets
"""
source_model_class = model.PlantAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "plant_type_uuids",
- "season_uuids",
- ]
- )
- return fields
+ supported_fields = [
+ "uuid",
+ "asset_name",
+ "plant_type_uuids",
+ "notes",
+ "archived",
+ ]
def normalize_source_object(self, plant):
- data = super().normalize_source_object(plant)
- data.update(
- {
- "plant_type_uuids": [pt.farmos_uuid for pt in plant.plant_types],
- "season_uuids": [s.farmos_uuid for s in plant.seasons],
- }
- )
- return data
+ return {
+ "uuid": plant.farmos_uuid or self.app.make_true_uuid(),
+ "asset_name": plant.asset_name,
+ "plant_type_uuids": [t.plant_type.farmos_uuid for t in plant._plant_types],
+ "notes": plant.notes,
+ "archived": plant.archived,
+ "_src_object": plant,
+ }
class StructureAssetImporter(
- FromWuttaFarmAsset, farmos_importing.model.StructureAssetImporter
+ FromWuttaFarm, farmos_importing.model.StructureAssetImporter
):
"""
WuttaFarm → farmOS API exporter for Structure Assets
@@ -450,31 +325,27 @@ class StructureAssetImporter(
source_model_class = model.StructureAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "structure_type_id",
- ]
- )
- return fields
+ supported_fields = [
+ "uuid",
+ "asset_name",
+ "structure_type_id",
+ "is_location",
+ "is_fixed",
+ "notes",
+ "archived",
+ ]
def normalize_source_object(self, structure):
- data = super().normalize_source_object(structure)
- data.update(
- {
- "structure_type_id": structure.structure_type.drupal_id,
- }
- )
- return data
-
-
-class WaterAssetImporter(FromWuttaFarmAsset, farmos_importing.model.WaterAssetImporter):
- """
- WuttaFarm → farmOS API exporter for Water Assets
- """
-
- source_model_class = model.WaterAsset
+ 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,
+ }
##############################
@@ -510,24 +381,6 @@ class FromWuttaFarmQuantity(FromWuttaFarm):
}
-class MaterialQuantityImporter(
- FromWuttaFarmQuantity, farmos_importing.model.MaterialQuantityImporter
-):
- """
- WuttaFarm → farmOS API exporter for Material Quantities
- """
-
- source_model_class = model.MaterialQuantity
-
- def normalize_source_object(self, quantity):
- data = super().normalize_source_object(quantity)
-
- if "material_types" in self.fields:
- data["material_types"] = [mt.farmos_uuid for mt in quantity.material_types]
-
- return data
-
-
class StandardQuantityImporter(
FromWuttaFarmQuantity, farmos_importing.model.StandardQuantityImporter
):
@@ -558,8 +411,6 @@ class FromWuttaFarmLog(FromWuttaFarm):
"notes",
"quick",
"assets",
- "locations",
- "groups",
"quantities",
]
@@ -574,8 +425,6 @@ class FromWuttaFarmLog(FromWuttaFarm):
"notes": log.notes,
"quick": self.config.parse_list(log.quick) if log.quick else [],
"assets": [(a.asset_type, a.farmos_uuid) for a in log.assets],
- "locations": [(l.asset_type, l.farmos_uuid) for l in log.locations],
- "groups": [(g.asset_type, g.farmos_uuid) for g in log.groups],
"quantities": [qty.farmos_uuid for qty in log.quantities],
"_src_object": log,
}
@@ -631,22 +480,3 @@ class ObservationLogImporter(
"""
source_model_class = model.ObservationLog
-
-
-class SeedingLogImporter(FromWuttaFarmLog, farmos_importing.model.SeedingLogImporter):
- """
- WuttaFarm → farmOS API exporter for Seeding Logs
- """
-
- source_model_class = model.SeedingLog
-
- def normalize_source_object(self, log):
- data = super().normalize_source_object(log)
- data.update(
- {
- "source": log.source,
- "purchase_date": log.purchase_date,
- "lot_number": log.lot_number,
- }
- )
- return data
diff --git a/src/wuttafarm/importing/farmos.py b/src/wuttafarm/importing/farmos.py
index c739bad..6b21090 100644
--- a/src/wuttafarm/importing/farmos.py
+++ b/src/wuttafarm/importing/farmos.py
@@ -106,27 +106,20 @@ class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler):
importers["LandAsset"] = LandAssetImporter
importers["StructureType"] = StructureTypeImporter
importers["StructureAsset"] = StructureAssetImporter
- importers["WaterAsset"] = WaterAssetImporter
- importers["EquipmentType"] = EquipmentTypeImporter
- importers["EquipmentAsset"] = EquipmentAssetImporter
importers["AnimalType"] = AnimalTypeImporter
importers["AnimalAsset"] = AnimalAssetImporter
importers["GroupAsset"] = GroupAssetImporter
importers["PlantType"] = PlantTypeImporter
- importers["Season"] = SeasonImporter
importers["PlantAsset"] = PlantAssetImporter
importers["Measure"] = MeasureImporter
importers["Unit"] = UnitImporter
- importers["MaterialType"] = MaterialTypeImporter
importers["QuantityType"] = QuantityTypeImporter
importers["StandardQuantity"] = StandardQuantityImporter
- importers["MaterialQuantity"] = MaterialQuantityImporter
importers["LogType"] = LogTypeImporter
importers["ActivityLog"] = ActivityLogImporter
importers["HarvestLog"] = HarvestLogImporter
importers["MedicalLog"] = MedicalLogImporter
importers["ObservationLog"] = ObservationLogImporter
- importers["SeedingLog"] = SeedingLogImporter
return importers
@@ -156,8 +149,6 @@ class FromFarmOS(Importer):
:returns: Equivalent naive UTC ``datetime``
"""
- if not dt:
- return None
dt = datetime.datetime.fromisoformat(dt)
return self.app.make_utc(dt)
@@ -339,18 +330,21 @@ class AnimalAssetImporter(AssetImporterBase):
model_class = model.AnimalAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "animal_type_uuid",
- "sex",
- "is_sterile",
- "produces_eggs",
- "birthdate",
- ]
- )
- return fields
+ supported_fields = [
+ "farmos_uuid",
+ "drupal_id",
+ "asset_type",
+ "asset_name",
+ "animal_type_uuid",
+ "sex",
+ "is_sterile",
+ "produces_eggs",
+ "birthdate",
+ "notes",
+ "archived",
+ "image_url",
+ "thumbnail_url",
+ ]
def setup(self):
super().setup()
@@ -405,12 +399,12 @@ class AnimalAssetImporter(AssetImporterBase):
return data
-class TaxonomyImporterBase(FromFarmOS, ToWutta):
+class AnimalTypeImporter(FromFarmOS, ToWutta):
"""
- farmOS API → WuttaFarm importer for taxonomy terms
+ farmOS API → WuttaFarm importer for Animal Types
"""
- taxonomy_type = None
+ model_class = model.AnimalType
supported_fields = [
"farmos_uuid",
@@ -421,50 +415,19 @@ class TaxonomyImporterBase(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(
- self.farmos_client.resource.iterate("taxonomy_term", self.taxonomy_type)
- )
+ animal_types = self.farmos_client.resource.get("taxonomy_term", "animal_type")
+ return animal_types["data"]
- def normalize_source_object(self, term):
+ def normalize_source_object(self, animal_type):
""" """
- if description := term["attributes"]["description"]:
- description = description["value"]
-
return {
- "farmos_uuid": UUID(term["id"]),
- "drupal_id": term["attributes"]["drupal_internal__tid"],
- "name": term["attributes"]["name"],
- "description": description,
+ "farmos_uuid": UUID(animal_type["id"]),
+ "drupal_id": animal_type["attributes"]["drupal_internal__tid"],
+ "name": animal_type["attributes"]["name"],
+ "description": animal_type["attributes"]["description"],
}
-class AnimalTypeImporter(TaxonomyImporterBase):
- """
- farmOS API → WuttaFarm importer for Animal Types
- """
-
- model_class = model.AnimalType
- taxonomy_type = "animal_type"
-
-
-class MaterialTypeImporter(TaxonomyImporterBase):
- """
- farmOS API → WuttaFarm importer for Material Types
- """
-
- model_class = model.MaterialType
- taxonomy_type = "material_type"
-
-
-class EquipmentTypeImporter(TaxonomyImporterBase):
- """
- farmOS API → WuttaFarm importer for Equipment Types
- """
-
- model_class = model.EquipmentType
- taxonomy_type = "equipment_type"
-
-
class AssetTypeImporter(FromFarmOS, ToWutta):
"""
farmOS API → WuttaFarm importer for Asset Types
@@ -481,7 +444,8 @@ class AssetTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("asset_type"))
+ asset_types = self.farmos_client.resource.get("asset_type")
+ return asset_types["data"]
def normalize_source_object(self, asset_type):
""" """
@@ -493,111 +457,6 @@ class AssetTypeImporter(FromFarmOS, ToWutta):
}
-class EquipmentAssetImporter(AssetImporterBase):
- """
- farmOS API → WuttaFarm importer for Equipment Assets
- """
-
- model_class = model.EquipmentAsset
-
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "equipment_types",
- ]
- )
- return fields
-
- def setup(self):
- super().setup()
- model = self.app.model
-
- self.equipment_types_by_farmos_uuid = {}
- for equipment_type in self.target_session.query(model.EquipmentType):
- if equipment_type.farmos_uuid:
- self.equipment_types_by_farmos_uuid[equipment_type.farmos_uuid] = (
- equipment_type
- )
-
- def normalize_source_object(self, equipment):
- """ """
- data = super().normalize_source_object(equipment)
-
- equipment_types = []
- if relationships := equipment.get("relationships"):
-
- if equipment_type := relationships.get("equipment_type"):
- equipment_types = []
- for equipment_type in equipment_type["data"]:
- if wf_equipment_type := self.equipment_types_by_farmos_uuid.get(
- UUID(equipment_type["id"])
- ):
- equipment_types.append(wf_equipment_type.uuid)
- else:
- log.warning(
- "equipment type not found: %s", equipment_type["id"]
- )
-
- data.update(
- {
- "manufacturer": equipment["attributes"]["manufacturer"],
- "model": equipment["attributes"]["model"],
- "serial_number": equipment["attributes"]["serial_number"],
- "equipment_types": set(equipment_types),
- }
- )
- return data
-
- def normalize_target_object(self, equipment):
- data = super().normalize_target_object(equipment)
-
- if "equipment_types" in self.fields:
- data["equipment_types"] = set(
- [etype.uuid for etype in equipment.equipment_types]
- )
-
- return data
-
- def update_target_object(self, equipment, source_data, target_data=None):
- model = self.app.model
- equipment = super().update_target_object(equipment, source_data, target_data)
-
- if "equipment_types" in self.fields:
- if (
- not target_data
- or target_data["equipment_types"] != source_data["equipment_types"]
- ):
-
- for uuid in source_data["equipment_types"]:
- if not target_data or uuid not in target_data["equipment_types"]:
- self.target_session.flush()
- equipment._equipment_types.append(
- model.EquipmentAssetEquipmentType(equipment_type_uuid=uuid)
- )
-
- if target_data:
- for uuid in target_data["equipment_types"]:
- if uuid not in source_data["equipment_types"]:
- equipment_type = (
- self.target_session.query(
- model.EquipmentAssetEquipmentType
- )
- .filter(
- model.EquipmentAssetEquipmentType.equipment_asset
- == equipment
- )
- .filter(
- model.EquipmentAssetEquipmentType.equipment_type_uuid
- == uuid
- )
- .one()
- )
- self.target_session.delete(equipment_type)
-
- return equipment
-
-
class GroupAssetImporter(AssetImporterBase):
"""
farmOS API → WuttaFarm importer for Group Assets
@@ -605,14 +464,20 @@ class GroupAssetImporter(AssetImporterBase):
model_class = model.GroupAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "produces_eggs",
- ]
- )
- return fields
+ supported_fields = [
+ "farmos_uuid",
+ "drupal_id",
+ "asset_type",
+ "asset_name",
+ "is_location",
+ "is_fixed",
+ "produces_eggs",
+ "notes",
+ "archived",
+ "image_url",
+ "thumbnail_url",
+ "parents",
+ ]
def normalize_source_object(self, group):
""" """
@@ -632,14 +497,18 @@ class LandAssetImporter(AssetImporterBase):
model_class = model.LandAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "land_type_uuid",
- ]
- )
- return fields
+ supported_fields = [
+ "farmos_uuid",
+ "drupal_id",
+ "asset_type",
+ "asset_name",
+ "land_type_uuid",
+ "is_location",
+ "is_fixed",
+ "notes",
+ "archived",
+ "parents",
+ ]
def setup(self):
""" """
@@ -684,7 +553,8 @@ class LandTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("land_type"))
+ land_types = self.farmos_client.resource.get("land_type")
+ return land_types["data"]
def normalize_source_object(self, land_type):
""" """
@@ -711,7 +581,8 @@ class PlantTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("taxonomy_term", "plant_type"))
+ result = self.farmos_client.resource.get("taxonomy_term", "plant_type")
+ return result["data"]
def normalize_source_object(self, plant_type):
""" """
@@ -723,34 +594,6 @@ class PlantTypeImporter(FromFarmOS, ToWutta):
}
-class SeasonImporter(FromFarmOS, ToWutta):
- """
- farmOS API → WuttaFarm importer for Seasons
- """
-
- model_class = model.Season
-
- supported_fields = [
- "farmos_uuid",
- "drupal_id",
- "name",
- "description",
- ]
-
- def get_source_objects(self):
- """ """
- return list(self.farmos_client.resource.iterate("taxonomy_term", "season"))
-
- def normalize_source_object(self, season):
- """ """
- return {
- "farmos_uuid": UUID(season["id"]),
- "drupal_id": season["attributes"]["drupal_internal__tid"],
- "name": season["attributes"]["name"],
- "description": season["attributes"]["description"],
- }
-
-
class PlantAssetImporter(AssetImporterBase):
"""
farmOS API → WuttaFarm importer for Plant Assets
@@ -758,15 +601,17 @@ class PlantAssetImporter(AssetImporterBase):
model_class = model.PlantAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "plant_types",
- "seasons",
- ]
- )
- return fields
+ supported_fields = [
+ "farmos_uuid",
+ "drupal_id",
+ "asset_type",
+ "asset_name",
+ "plant_types",
+ "notes",
+ "archived",
+ "image_url",
+ "thumbnail_url",
+ ]
def setup(self):
super().setup()
@@ -777,17 +622,9 @@ class PlantAssetImporter(AssetImporterBase):
if plant_type.farmos_uuid:
self.plant_types_by_farmos_uuid[plant_type.farmos_uuid] = plant_type
- self.seasons_by_farmos_uuid = {}
- for season in self.target_session.query(model.Season):
- if season.farmos_uuid:
- self.seasons_by_farmos_uuid[season.farmos_uuid] = season
-
def normalize_source_object(self, plant):
""" """
- data = super().normalize_source_object(plant)
-
plant_types = []
- seasons = []
if relationships := plant.get("relationships"):
if plant_type := relationships.get("plant_type"):
@@ -800,18 +637,10 @@ class PlantAssetImporter(AssetImporterBase):
else:
log.warning("plant type not found: %s", plant_type["id"])
- if season := relationships.get("season"):
- seasons = []
- for season in season["data"]:
- if wf_season := self.seasons_by_farmos_uuid.get(UUID(season["id"])):
- seasons.append(wf_season.uuid)
- else:
- log.warning("season not found: %s", season["id"])
-
+ data = super().normalize_source_object(plant)
data.update(
{
"plant_types": set(plant_types),
- "seasons": set(seasons),
}
)
return data
@@ -822,9 +651,6 @@ class PlantAssetImporter(AssetImporterBase):
if "plant_types" in self.fields:
data["plant_types"] = set([pt.uuid for pt in plant.plant_types])
- if "seasons" in self.fields:
- data["seasons"] = set([s.uuid for s in plant.seasons])
-
return data
def update_target_object(self, plant, source_data, target_data=None):
@@ -857,25 +683,6 @@ class PlantAssetImporter(AssetImporterBase):
)
self.target_session.delete(plant_type)
- if "seasons" in self.fields:
- if not target_data or target_data["seasons"] != source_data["seasons"]:
-
- for uuid in source_data["seasons"]:
- if not target_data or uuid not in target_data["seasons"]:
- self.target_session.flush()
- plant._seasons.append(model.PlantAssetSeason(season_uuid=uuid))
-
- if target_data:
- for uuid in target_data["seasons"]:
- if uuid not in source_data["seasons"]:
- season = (
- self.target_session.query(model.PlantAssetSeason)
- .filter(model.PlantAssetSeason.plant_asset == plant)
- .filter(model.PlantAssetSeason.season_uuid == uuid)
- .one()
- )
- self.target_session.delete(season)
-
return plant
@@ -886,14 +693,20 @@ class StructureAssetImporter(AssetImporterBase):
model_class = model.StructureAsset
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "structure_type_uuid",
- ]
- )
- return fields
+ supported_fields = [
+ "farmos_uuid",
+ "drupal_id",
+ "asset_type",
+ "asset_name",
+ "structure_type_uuid",
+ "is_location",
+ "is_fixed",
+ "notes",
+ "archived",
+ "image_url",
+ "thumbnail_url",
+ "parents",
+ ]
def setup(self):
super().setup()
@@ -939,7 +752,8 @@ class StructureTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("structure_type"))
+ structure_types = self.farmos_client.resource.get("structure_type")
+ return structure_types["data"]
def normalize_source_object(self, structure_type):
""" """
@@ -950,14 +764,6 @@ class StructureTypeImporter(FromFarmOS, ToWutta):
}
-class WaterAssetImporter(AssetImporterBase):
- """
- farmOS API → WuttaFarm importer for Water Assets
- """
-
- model_class = model.WaterAsset
-
-
class UserImporter(FromFarmOS, ToWutta):
"""
farmOS API → WuttaFarm importer for Users
@@ -985,7 +791,8 @@ class UserImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("user"))
+ users = self.farmos_client.resource.get("user")
+ return users["data"]
def normalize_source_object(self, user):
""" """
@@ -1026,7 +833,6 @@ class MeasureImporter(FromFarmOS, ToWutta):
supported_fields = [
"drupal_id",
- "ordinal",
"name",
]
@@ -1037,15 +843,12 @@ class MeasureImporter(FromFarmOS, ToWutta):
)
response.raise_for_status()
data = response.json()
- self.ordinal = 0
return data["definitions"]["attributes"]["properties"]["measure"]["oneOf"]
def normalize_source_object(self, measure):
""" """
- self.ordinal += 1
return {
"drupal_id": measure["const"],
- "ordinal": self.ordinal,
"name": measure["title"],
}
@@ -1066,7 +869,8 @@ class UnitImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("taxonomy_term", "unit"))
+ result = self.farmos_client.resource.get("taxonomy_term", "unit")
+ return result["data"]
def normalize_source_object(self, unit):
""" """
@@ -1094,7 +898,8 @@ class QuantityTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("quantity_type"))
+ result = self.farmos_client.resource.get("quantity_type")
+ return result["data"]
def normalize_source_object(self, quantity_type):
""" """
@@ -1122,7 +927,8 @@ class LogTypeImporter(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
- return list(self.farmos_client.resource.iterate("log_type"))
+ log_types = self.farmos_client.resource.get("log_type")
+ return log_types["data"]
def normalize_source_object(self, log_type):
""" """
@@ -1420,41 +1226,6 @@ class ObservationLogImporter(LogImporterBase):
model_class = model.ObservationLog
-class SeedingLogImporter(LogImporterBase):
- """
- farmOS API → WuttaFarm importer for Seeding Logs
- """
-
- model_class = model.SeedingLog
-
- def get_simple_fields(self):
- """ """
- fields = list(super().get_simple_fields())
- # nb. must explicitly declare proxy fields
- fields.extend(
- [
- "source",
- "purchase_date",
- "lot_number",
- ]
- )
- return fields
-
- def normalize_source_object(self, log):
- """ """
- data = super().normalize_source_object(log)
- data.update(
- {
- "source": log["attributes"]["source"],
- "purchase_date": self.normalize_datetime(
- log["attributes"]["purchase_date"]
- ),
- "lot_number": log["attributes"]["lot_number"],
- }
- )
- return data
-
-
class QuantityImporterBase(FromFarmOS, ToWutta):
"""
Base class for farmOS API → WuttaFarm quantity importers
@@ -1500,7 +1271,8 @@ class QuantityImporterBase(FromFarmOS, ToWutta):
def get_source_objects(self):
""" """
quantity_type = self.get_farmos_quantity_type()
- return list(self.farmos_client.resource.iterate("quantity", quantity_type))
+ result = self.farmos_client.resource.get("quantity", quantity_type)
+ return result["data"]
def get_quantity_type_by_farmos_uuid(self, uuid):
if hasattr(self, "quantity_types_by_farmos_uuid"):
@@ -1583,76 +1355,3 @@ class StandardQuantityImporter(QuantityImporterBase):
"units_uuid",
"label",
]
-
-
-class MaterialQuantityImporter(QuantityImporterBase):
- """
- farmOS API → WuttaFarm importer for Material Quantities
- """
-
- model_class = model.MaterialQuantity
-
- def get_supported_fields(self):
- fields = list(super().get_supported_fields())
- fields.extend(
- [
- "material_types",
- ]
- )
- return fields
-
- def normalize_source_object(self, quantity):
- """ """
- data = super().normalize_source_object(quantity)
-
- if "material_types" in self.fields:
- data["material_types"] = [
- UUID(mtype["id"])
- for mtype in quantity["relationships"]["material_type"]["data"]
- ]
-
- return data
-
- def normalize_target_object(self, quantity):
- data = super().normalize_target_object(quantity)
-
- if "material_types" in self.fields:
- data["material_types"] = [
- mtype.farmos_uuid for mtype in quantity.material_types
- ]
-
- return data
-
- def update_target_object(self, quantity, source_data, target_data=None):
- model = self.app.model
- quantity = super().update_target_object(quantity, source_data, target_data)
-
- if "material_types" in self.fields:
- if (
- not target_data
- or target_data["material_types"] != source_data["material_types"]
- ):
-
- for farmos_uuid in source_data["material_types"]:
- if (
- not target_data
- or farmos_uuid not in target_data["material_types"]
- ):
- mtype = (
- self.target_session.query(model.MaterialType)
- .filter(model.MaterialType.farmos_uuid == farmos_uuid)
- .one()
- )
- quantity.material_types.append(mtype)
-
- if target_data:
- for farmos_uuid in target_data["material_types"]:
- if farmos_uuid not in source_data["material_types"]:
- mtype = (
- self.target_session.query(model.MaterialType)
- .filter(model.MaterialType.farmos_uuid == farmos_uuid)
- .one()
- )
- quantity.material_types.remove(mtype)
-
- return quantity
diff --git a/src/wuttafarm/normal.py b/src/wuttafarm/normal.py
index c47fcc3..4fc8796 100644
--- a/src/wuttafarm/normal.py
+++ b/src/wuttafarm/normal.py
@@ -84,41 +84,16 @@ class Normalizer(GenericHandler):
self._farmos_units = units
return self._farmos_units
- def normalize_datetime(self, value):
- if not value:
- return None
- value = datetime.datetime.fromisoformat(value)
- return self.app.localtime(value)
-
def normalize_farmos_asset(self, asset, included={}):
""" """
if notes := asset["attributes"]["notes"]:
notes = notes["value"]
- parent_objects = []
- parent_uuids = []
owner_objects = []
owner_uuids = []
if relationships := asset.get("relationships"):
- if parents := relationships.get("parent"):
- for parent in parents["data"]:
- parent_uuid = parent["id"]
- parent_uuids.append(parent_uuid)
- parent_object = {
- "uuid": parent_uuid,
- "type": parent["type"],
- "asset_type": parent["type"].split("--")[1],
- }
- if parent := included.get(parent_uuid):
- parent_object.update(
- {
- "name": parent["attributes"]["name"],
- }
- )
- parent_objects.append(parent_object)
-
if owners := relationships.get("owner"):
for user in owners["data"]:
user_uuid = user["id"]
@@ -131,11 +106,6 @@ class Normalizer(GenericHandler):
}
)
- # if self.farmos_4x:
- # archived = asset["attributes"]["archived"]
- # else:
- # archived = asset["attributes"]["status"] == "archived"
-
return {
"uuid": asset["id"],
"drupal_id": asset["attributes"]["drupal_internal__id"],
@@ -144,10 +114,6 @@ class Normalizer(GenericHandler):
"is_fixed": asset["attributes"]["is_fixed"],
"archived": asset["attributes"]["archived"],
"notes": notes,
- # nb. this is only used for certain asset types
- "produces_eggs": asset["attributes"].get("produces_eggs"),
- "parents": parent_objects,
- "parent_uuids": parent_uuids,
"owners": owner_objects,
"owner_uuids": owner_uuids,
}
@@ -155,7 +121,8 @@ class Normalizer(GenericHandler):
def normalize_farmos_log(self, log, included={}):
if timestamp := log["attributes"]["timestamp"]:
- timestamp = self.normalize_datetime(timestamp)
+ timestamp = datetime.datetime.fromisoformat(timestamp)
+ timestamp = self.app.localtime(timestamp)
if notes := log["attributes"]["notes"]:
notes = notes["value"]
@@ -265,29 +232,27 @@ class Normalizer(GenericHandler):
measure_id = attrs["measure"]
- quantity_object = {
- "uuid": quantity["id"],
- "drupal_id": attrs["drupal_internal__id"],
- "quantity_type_uuid": rels["quantity_type"]["data"]["id"],
- "quantity_type_id": rels["quantity_type"]["data"]["meta"][
- "drupal_internal__target_id"
- ],
- "measure_id": measure_id,
- "measure_name": self.get_farmos_measure_name(measure_id),
- "value_numerator": value["numerator"],
- "value_decimal": value["decimal"],
- "value_denominator": value["denominator"],
- "unit_uuid": unit_uuid,
- "unit_name": unit["attributes"]["name"],
- }
- if quantity_object["quantity_type_id"] == "material":
- quantity_object["material_types"] = [
- {"uuid": mtype["id"]}
- for mtype in quantity["relationships"]["material_type"][
- "data"
- ]
- ]
- quantity_objects.append(quantity_object)
+ quantity_objects.append(
+ {
+ "uuid": quantity["id"],
+ "drupal_id": attrs["drupal_internal__id"],
+ "quantity_type_uuid": rels["quantity_type"]["data"][
+ "id"
+ ],
+ "quantity_type_id": rels["quantity_type"]["data"][
+ "meta"
+ ]["drupal_internal__target_id"],
+ "measure_id": measure_id,
+ "measure_name": self.get_farmos_measure_name(
+ measure_id
+ ),
+ "value_numerator": value["numerator"],
+ "value_decimal": value["decimal"],
+ "value_denominator": value["denominator"],
+ "unit_uuid": unit_uuid,
+ "unit_name": unit["attributes"]["name"],
+ }
+ )
if owners := relationships.get("owner"):
for user in owners["data"]:
diff --git a/src/wuttafarm/web/forms/schema.py b/src/wuttafarm/web/forms/schema.py
index e24f0cf..6bf434e 100644
--- a/src/wuttafarm/web/forms/schema.py
+++ b/src/wuttafarm/web/forms/schema.py
@@ -28,7 +28,7 @@ import json
import colander
from wuttaweb.db import Session
-from wuttaweb.forms.schema import ObjectRef, WuttaSet, WuttaList
+from wuttaweb.forms.schema import ObjectRef, WuttaSet
from wuttaweb.forms.widgets import NotesWidget
@@ -164,9 +164,10 @@ class FarmOSRefs(WuttaSet):
self.route_prefix = route_prefix
def serialize(self, node, appstruct):
- if not appstruct:
+ if appstruct is colander.null:
return colander.null
- return appstruct
+
+ return json.dumps(appstruct)
def widget_maker(self, **kwargs):
from wuttafarm.web.forms.widgets import FarmOSRefsWidget
@@ -216,35 +217,6 @@ class FarmOSQuantityRefs(WuttaSet):
return FarmOSQuantityRefsWidget(**kwargs)
-class FarmOSTaxonomyTerms(colander.SchemaType):
- """
- Schema type which can represent multiple taxonomy terms.
- """
-
- route_prefix = None
-
- def __init__(self, request, route_prefix=None, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- if route_prefix:
- self.route_prefix = route_prefix
-
- def serialize(self, node, appstruct):
- if not appstruct:
- return colander.null
- return appstruct
-
- def widget_maker(self, **kwargs):
- from wuttafarm.web.forms.widgets import FarmOSTaxonomyTermsWidget
-
- return FarmOSTaxonomyTermsWidget(self.request, self.route_prefix, **kwargs)
-
-
-class FarmOSEquipmentTypeRefs(FarmOSTaxonomyTerms):
-
- route_prefix = "farmos_equipment_types"
-
-
class FarmOSPlantTypes(colander.SchemaType):
def __init__(self, request, *args, **kwargs):
@@ -289,35 +261,6 @@ class LandTypeRef(ObjectRef):
return self.request.route_url("land_types.view", uuid=land_type.uuid)
-class TaxonomyTermRefs(WuttaList):
- """
- Generic schema type for a field which can reference multiple
- taxonomy terms.
- """
-
- def serialize(self, node, appstruct):
- if not appstruct:
- return colander.null
-
- terms = []
- for term in appstruct:
- terms.append(
- {
- "uuid": str(term.uuid),
- "name": term.name,
- }
- )
- return terms
-
-
-class EquipmentTypeRefs(TaxonomyTermRefs):
-
- def widget_maker(self, **kwargs):
- from wuttafarm.web.forms.widgets import EquipmentTypeRefsWidget
-
- return EquipmentTypeRefsWidget(self.request, **kwargs)
-
-
class PlantTypeRefs(WuttaSet):
"""
Schema type for Plant Types field (on a Plant Asset).
@@ -345,62 +288,6 @@ class PlantTypeRefs(WuttaSet):
return PlantTypeRefsWidget(self.request, **kwargs)
-class MaterialTypeRefs(colander.List):
- """
- Schema type for Material Types field (on a Material Asset).
- """
-
- def __init__(self, request):
- super().__init__()
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
- def serialize(self, node, appstruct):
- if not appstruct:
- return colander.null
-
- mtypes = []
- for mtype in appstruct:
- mtypes.append(
- {
- "uuid": mtype.uuid.hex,
- "name": mtype.name,
- }
- )
- return mtypes
-
- def widget_maker(self, **kwargs):
- from wuttafarm.web.forms.widgets import MaterialTypeRefsWidget
-
- return MaterialTypeRefsWidget(self.request, **kwargs)
-
-
-class SeasonRefs(WuttaSet):
- """
- Schema type for Plant Types field (on a Plant Asset).
- """
-
- def serialize(self, node, appstruct):
- if not appstruct:
- return []
-
- return [season.uuid.hex for season in appstruct]
-
- def widget_maker(self, **kwargs):
- from wuttafarm.web.forms.widgets import SeasonRefsWidget
-
- model = self.app.model
- session = Session()
-
- if "values" not in kwargs:
- seasons = session.query(model.Season).order_by(model.Season.name).all()
- values = [(s.uuid.hex, str(s)) for s in seasons]
- kwargs["values"] = values
-
- return SeasonRefsWidget(self.request, **kwargs)
-
-
class StructureType(colander.SchemaType):
def __init__(self, request, *args, **kwargs):
@@ -485,100 +372,55 @@ class UsersType(colander.SchemaType):
return UsersWidget(self.request, **kwargs)
+class AssetParentRefs(WuttaSet):
+ """
+ Schema type for Parents field which references assets.
+ """
+
+ def serialize(self, node, appstruct):
+ if not appstruct:
+ appstruct = []
+ uuids = [u.hex for u in appstruct]
+ return json.dumps(uuids)
+
+ def widget_maker(self, **kwargs):
+ from wuttafarm.web.forms.widgets import AssetParentRefsWidget
+
+ return AssetParentRefsWidget(self.request, **kwargs)
+
+
class AssetRefs(WuttaSet):
"""
Schema type for Assets field (on a Log record)
"""
- def __init__(
- self, request, for_asset=None, is_group=None, is_location=None, **kwargs
- ):
- super().__init__(request, **kwargs)
- self.is_group = is_group
- self.is_location = is_location
- self.for_asset = for_asset
-
def serialize(self, node, appstruct):
if not appstruct:
return colander.null
- return {asset.uuid.hex for asset in appstruct}
+ return {asset.uuid for asset in appstruct}
def widget_maker(self, **kwargs):
from wuttafarm.web.forms.widgets import AssetRefsWidget
- model = self.app.model
- session = Session()
-
- if "values" not in kwargs:
- query = session.query(model.Asset)
- if self.is_group is not None:
- query = query.join(model.GroupAsset)
- if self.is_location is not None:
- query = query.filter(model.Asset.is_location == self.is_location)
- if self.for_asset:
- query = query.filter(model.Asset.uuid != self.for_asset.uuid)
- query = query.order_by(model.Asset.asset_name)
- values = [(asset.uuid.hex, str(asset)) for asset in query]
- kwargs["values"] = values
-
return AssetRefsWidget(self.request, **kwargs)
-class QuantityRefs(colander.List):
+class LogQuantityRefs(WuttaSet):
"""
Schema type for Quantities field (on a Log record)
"""
- def __init__(self, request):
- super().__init__()
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
def serialize(self, node, appstruct):
if not appstruct:
return colander.null
- quantities = []
- for qty in appstruct:
-
- quantity = {
- "uuid": qty.uuid.hex,
- "quantity_type": {
- "drupal_id": qty.quantity_type_id,
- "name": qty.quantity_type.name,
- },
- "measure": qty.measure_id,
- "value": qty.get_value_decimal(),
- "units": {
- "uuid": qty.units.uuid.hex,
- "name": qty.units.name,
- },
- "as_text": qty.render_as_text(self.config),
- # nb. always include this regardless of quantity type,
- # for sake of easier frontend logic
- "material_types": [],
- }
-
- if qty.quantity_type_id == "material":
- quantity["material_types"] = []
- for mtype in qty.material_types:
- quantity["material_types"].append(
- {
- "uuid": mtype.uuid.hex,
- "name": mtype.name,
- }
- )
-
- quantities.append(quantity)
-
- return quantities
+ return {qty.uuid for qty in appstruct}
def widget_maker(self, **kwargs):
- from wuttafarm.web.forms.widgets import QuantityRefsWidget
+ from wuttafarm.web.forms.widgets import LogQuantityRefsWidget
- return QuantityRefsWidget(self.request, **kwargs)
+ return LogQuantityRefsWidget(self.request, **kwargs)
class OwnerRefs(WuttaSet):
diff --git a/src/wuttafarm/web/forms/widgets.py b/src/wuttafarm/web/forms/widgets.py
index db79eae..0a14638 100644
--- a/src/wuttafarm/web/forms/widgets.py
+++ b/src/wuttafarm/web/forms/widgets.py
@@ -33,7 +33,6 @@ from wuttaweb.forms.widgets import WuttaCheckboxChoiceWidget, ObjectRefWidget
from wuttaweb.db import Session
from wuttafarm.web.util import render_quantity_objects
-from wuttafarm.db.model import EquipmentType
class ImageWidget(Widget):
@@ -125,7 +124,7 @@ class FarmOSRefsWidget(Widget):
return HTML.tag("span")
links = []
- for obj in cstruct:
+ for obj in json.loads(cstruct):
url = self.request.route_url(
f"{self.route_prefix}.view", uuid=obj["uuid"]
)
@@ -229,38 +228,6 @@ class FarmOSUnitRefWidget(Widget):
return super().serialize(field, cstruct, **kw)
-class FarmOSTaxonomyTermsWidget(Widget):
- """
- Widget to display a field which can reference multiple taxonomy
- terms.
- """
-
- 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 term in cstruct:
- link = tags.link_to(
- term["name"],
- self.request.route_url(
- f"{self.route_prefix}.view", uuid=term["uuid"]
- ),
- )
- links.append(HTML.tag("li", c=link))
- return HTML.tag("ul", c=links)
-
- return super().serialize(field, cstruct, **kw)
-
-
class FarmOSPlantTypesWidget(Widget):
"""
Widget to display a farmOS "plant types" field.
@@ -291,88 +258,6 @@ class FarmOSPlantTypesWidget(Widget):
return super().serialize(field, cstruct, **kw)
-class TaxonomyTermRefsWidget(Widget):
- """
- Generic (incomplete) widget for fields which can reference
- multiple taxonomy terms.
-
- This widget can handle typical read-only scenarios but the
- editable mode is not implemented.
- """
-
- route_prefix = None
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
- @classmethod
- def get_route_prefix(cls):
- return cls.route_prefix
-
- @classmethod
- def get_permission_prefix(cls):
- return cls.route_prefix
-
- def serialize(self, field, cstruct, **kw):
- """ """
- if not cstruct:
- cstruct = []
-
- if readonly := kw.get("readonly", self.readonly):
- items = []
- route_prefix = self.get_route_prefix()
- for term in cstruct:
- url = self.request.route_url(f"{route_prefix}.view", uuid=term["uuid"])
- link = tags.link_to(term["name"], url)
- items.append(HTML.tag("li", c=link))
- return HTML.tag("ul", c=items)
-
- tmpl_values = self.get_template_values(field, cstruct, kw)
- return field.renderer(self.template, **tmpl_values)
-
- def get_template_values(self, field, cstruct, kw):
- values = super().get_template_values(field, cstruct, kw)
- model = self.app.model
- session = Session()
-
- terms = []
- query = session.query(self.model_class).order_by(self.model_class.name)
- for term in query:
- terms.append(
- {
- "uuid": str(term.uuid),
- "name": term.name,
- }
- )
- values["terms"] = terms
-
- permission_prefix = self.get_permission_prefix()
- if self.request.has_perm(f"{permission_prefix}.create"):
- values["can_create"] = True
-
- return values
-
- def deserialize(self, field, pstruct):
- """ """
- if not pstruct:
- return colander.null
-
- return json.loads(pstruct)
-
-
-class EquipmentTypeRefsWidget(TaxonomyTermRefsWidget):
- """
- Widget for Equipment Types field.
- """
-
- model_class = EquipmentType
- route_prefix = "equipment_types"
- template = "equipmenttyperefs"
-
-
class PlantTypeRefsWidget(Widget):
"""
Widget for Plant Types field (on a Plant Asset).
@@ -447,144 +332,6 @@ class PlantTypeRefsWidget(Widget):
return set(pstruct.split(","))
-class MaterialTypeRefsWidget(Widget):
- """
- Widget for Material Types field (on a Material Asset).
- """
-
- template = "materialtyperefs"
- values = ()
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
- def serialize(self, field, cstruct, **kw):
- """ """
- model = self.app.model
- session = Session()
-
- if not cstruct:
- cstruct = []
-
- if readonly := kw.get("readonly", self.readonly):
- items = []
- for mtype in cstruct:
- items.append(
- HTML.tag(
- "li",
- c=tags.link_to(
- mtype["name"],
- self.request.route_url(
- "material_types.view", uuid=mtype["uuid"]
- ),
- ),
- )
- )
- return HTML.tag("ul", c=items)
-
- tmpl_values = self.get_template_values(field, cstruct, kw)
- return field.renderer(self.template, **tmpl_values)
-
- def get_template_values(self, field, cstruct, kw):
- """ """
- values = super().get_template_values(field, cstruct, kw)
- session = Session()
-
- material_types = []
- for mtype in self.app.get_material_types(session):
- material_types.append(
- {
- "uuid": mtype.uuid.hex,
- "name": mtype.name,
- }
- )
- values["material_types"] = json.dumps(material_types)
-
- return values
-
- def deserialize(self, field, pstruct):
- """ """
- if not pstruct:
- return []
-
- return json.loads(pstruct)
-
-
-class SeasonRefsWidget(Widget):
- """
- Widget for Seasons field (on a Plant Asset).
- """
-
- template = "seasonrefs"
- values = ()
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
- def serialize(self, field, cstruct, **kw):
- """ """
- model = self.app.model
- session = Session()
-
- if cstruct in (colander.null, None):
- cstruct = ()
-
- if readonly := kw.get("readonly", self.readonly):
- items = []
-
- seasons = (
- session.query(model.Season)
- .filter(model.Season.uuid.in_(cstruct))
- .order_by(model.Season.name)
- .all()
- )
-
- for season in seasons:
- items.append(
- HTML.tag(
- "li",
- c=tags.link_to(
- str(season),
- self.request.route_url("seasons.view", uuid=season.uuid),
- ),
- )
- )
-
- return HTML.tag("ul", c=items)
-
- values = kw.get("values", self.values)
- if not isinstance(values, sequence_types):
- raise TypeError("Values must be a sequence type (list, tuple, or range).")
-
- kw["values"] = _normalize_choices(values)
- tmpl_values = self.get_template_values(field, cstruct, kw)
- return field.renderer(self.template, **tmpl_values)
-
- def get_template_values(self, field, cstruct, kw):
- """ """
- values = super().get_template_values(field, cstruct, kw)
-
- values["js_values"] = json.dumps(values["values"])
-
- if self.request.has_perm("seasons.create"):
- values["can_create"] = True
-
- return values
-
- def deserialize(self, field, pstruct):
- """ """
- if not pstruct:
- return set()
-
- return set(pstruct.split(","))
-
-
class StructureWidget(Widget):
"""
Widget to display a "structure" field.
@@ -646,20 +393,42 @@ class UsersWidget(Widget):
##############################
-class AssetRefsWidget(Widget):
+class AssetParentRefsWidget(WuttaCheckboxChoiceWidget):
+ """
+ Widget for Parents field which references assets.
+ """
+
+ def serialize(self, field, cstruct, **kw):
+ """ """
+ model = self.app.model
+ session = Session()
+
+ readonly = kw.get("readonly", self.readonly)
+ if readonly:
+ parents = []
+ for uuid in json.loads(cstruct):
+ parent = session.get(model.Asset, uuid)
+ parents.append(
+ HTML.tag(
+ "li",
+ c=tags.link_to(
+ str(parent),
+ self.request.route_url(
+ f"{parent.asset_type}_assets.view", uuid=parent.uuid
+ ),
+ ),
+ )
+ )
+ return HTML.tag("ul", c=parents)
+
+ return super().serialize(field, cstruct, **kw)
+
+
+class AssetRefsWidget(WuttaCheckboxChoiceWidget):
"""
Widget for Assets field (of various kinds).
"""
- template = "assetrefs"
- values = ()
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
def serialize(self, field, cstruct, **kw):
""" """
model = self.app.model
@@ -683,43 +452,14 @@ class AssetRefsWidget(Widget):
)
return HTML.tag("ul", c=assets)
- values = kw.get("values", self.values)
- if not isinstance(values, sequence_types):
- raise TypeError("Values must be a sequence type (list, tuple, or range).")
-
- kw["values"] = _normalize_choices(values)
- tmpl_values = self.get_template_values(field, cstruct, kw)
- return field.renderer(self.template, **tmpl_values)
-
- def get_template_values(self, field, cstruct, kw):
- """ """
- values = super().get_template_values(field, cstruct, kw)
-
- values["js_values"] = json.dumps(values["values"])
-
- return values
-
- def deserialize(self, field, pstruct):
- """ """
- if not pstruct:
- return set()
-
- return set(pstruct.split(","))
+ return super().serialize(field, cstruct, **kw)
-class QuantityRefsWidget(Widget):
+class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
"""
Widget for Quantities field (on a Log record)
"""
- template = "quantityrefs"
-
- def __init__(self, request, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.request = request
- self.config = self.request.wutta_config
- self.app = self.config.get_app()
-
def serialize(self, field, cstruct, **kw):
""" """
model = self.app.model
@@ -727,78 +467,24 @@ class QuantityRefsWidget(Widget):
readonly = kw.get("readonly", self.readonly)
if readonly:
- if not cstruct:
- return ""
-
quantities = []
- for qty in cstruct:
- url = self.request.route_url(
- f"quantities_{qty['quantity_type']['drupal_id']}.view",
- uuid=qty["uuid"],
+ for uuid in cstruct or []:
+ qty = session.get(model.Quantity, uuid)
+ quantities.append(
+ HTML.tag(
+ "li",
+ c=tags.link_to(
+ qty.render_as_text(self.config),
+ # TODO
+ self.request.route_url(
+ "quantities_standard.view", uuid=qty.uuid
+ ),
+ ),
+ )
)
- quantities.append(HTML.tag("li", c=tags.link_to(qty["as_text"], url)))
-
return HTML.tag("ul", c=quantities)
- tmpl_values = self.get_template_values(field, cstruct, kw)
- return field.renderer(self.template, **tmpl_values)
-
- def get_template_values(self, field, cstruct, kw):
- model = self.app.model
- session = Session()
- values = super().get_template_values(field, cstruct, kw)
-
- qtypes = []
- for qtype in self.app.get_quantity_types(session):
- qtypes.append(
- {
- "uuid": qtype.uuid.hex,
- "drupal_id": qtype.drupal_id,
- "name": qtype.name,
- }
- )
- values["quantity_types"] = qtypes
-
- material_types = []
- for mtype in self.app.get_material_types(session):
- material_types.append(
- {
- "uuid": mtype.uuid.hex,
- "name": mtype.name,
- }
- )
- values["material_types"] = material_types
-
- measures = []
- for measure in self.app.get_measures(session):
- measures.append(
- {
- "uuid": measure.uuid.hex,
- "drupal_id": measure.drupal_id,
- "name": measure.name,
- }
- )
- values["measures"] = measures
-
- units = []
- for unit in self.app.get_units(session):
- units.append(
- {
- "uuid": unit.uuid.hex,
- "drupal_id": unit.drupal_id,
- "name": unit.name,
- }
- )
- values["units"] = units
-
- return values
-
- def deserialize(self, field, pstruct):
- """ """
- if not pstruct:
- return set()
-
- return json.loads(pstruct)
+ return super().serialize(field, cstruct, **kw)
class OwnerRefsWidget(WuttaCheckboxChoiceWidget):
diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py
index 2756738..fe7719e 100644
--- a/src/wuttafarm/web/menus.py
+++ b/src/wuttafarm/web/menus.py
@@ -92,11 +92,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "animal_assets",
"perm": "animal_assets.list",
},
- {
- "title": "Equipment",
- "route": "equipment_assets",
- "perm": "equipment_assets.list",
- },
{
"title": "Group",
"route": "group_assets",
@@ -117,22 +112,12 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "structure_assets",
"perm": "structure_assets.list",
},
- {
- "title": "Water",
- "route": "water_assets",
- "perm": "water_assets.list",
- },
{"type": "sep"},
{
"title": "Animal Types",
"route": "animal_types",
"perm": "animal_types.list",
},
- {
- "title": "Equipment Types",
- "route": "equipment_types",
- "perm": "equipment_types.list",
- },
{
"title": "Land Types",
"route": "land_types",
@@ -143,11 +128,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "plant_types",
"perm": "plant_types.list",
},
- {
- "title": "Seasons",
- "route": "seasons",
- "perm": "seasons.list",
- },
{
"title": "Structure Types",
"route": "structure_types",
@@ -191,22 +171,12 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "logs_observation",
"perm": "logs_observation.list",
},
- {
- "title": "Seeding",
- "route": "logs_seeding",
- "perm": "logs_seeding.list",
- },
{"type": "sep"},
{
"title": "All Quantities",
"route": "quantities",
"perm": "quantities.list",
},
- {
- "title": "Material Quantities",
- "route": "quantities_material",
- "perm": "quantities_material.list",
- },
{
"title": "Standard Quantities",
"route": "quantities_standard",
@@ -218,11 +188,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "log_types",
"perm": "log_types.list",
},
- {
- "title": "Material Types",
- "route": "material_types",
- "perm": "material_types.list",
- },
{
"title": "Measures",
"route": "measures",
@@ -259,11 +224,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_animal_assets",
"perm": "farmos_animal_assets.list",
},
- {
- "title": "Equipment Assets",
- "route": "farmos_equipment_assets",
- "perm": "farmos_equipment_assets.list",
- },
{
"title": "Group Assets",
"route": "farmos_group_assets",
@@ -284,11 +244,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_structure_assets",
"perm": "farmos_structure_assets.list",
},
- {
- "title": "Water Assets",
- "route": "farmos_water_assets",
- "perm": "farmos_water_assets.list",
- },
{"type": "sep"},
{
"title": "Activity Logs",
@@ -310,22 +265,12 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_logs_observation",
"perm": "farmos_logs_observation.list",
},
- {
- "title": "Seeding Logs",
- "route": "farmos_logs_seeding",
- "perm": "farmos_logs_seeding.list",
- },
{"type": "sep"},
{
"title": "Animal Types",
"route": "farmos_animal_types",
"perm": "farmos_animal_types.list",
},
- {
- "title": "Equipment Types",
- "route": "farmos_equipment_types",
- "perm": "farmos_equipment_types.list",
- },
{
"title": "Land Types",
"route": "farmos_land_types",
@@ -336,11 +281,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_plant_types",
"perm": "farmos_plant_types.list",
},
- {
- "title": "Seasons",
- "route": "farmos_seasons",
- "perm": "farmos_seasons.list",
- },
{
"title": "Structure Types",
"route": "farmos_structure_types",
@@ -357,21 +297,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_log_types",
"perm": "farmos_log_types.list",
},
- {
- "title": "Material Types",
- "route": "farmos_material_types",
- "perm": "farmos_material_types.list",
- },
{
"title": "Quantity Types",
"route": "farmos_quantity_types",
"perm": "farmos_quantity_types.list",
},
- {
- "title": "Material Quantities",
- "route": "farmos_quantities_material",
- "perm": "farmos_quantities_material.list",
- },
{
"title": "Standard Quantities",
"route": "farmos_quantities_standard",
@@ -403,11 +333,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_animal_assets",
"perm": "farmos_animal_assets.list",
},
- {
- "title": "Equipment",
- "route": "farmos_equipment_assets",
- "perm": "farmos_equipment_assets.list",
- },
{
"title": "Group",
"route": "farmos_group_assets",
@@ -428,22 +353,12 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_structure_assets",
"perm": "farmos_structure_assets.list",
},
- {
- "title": "Water",
- "route": "farmos_water_assets",
- "perm": "farmos_water_assets.list",
- },
{"type": "sep"},
{
"title": "Animal Types",
"route": "farmos_animal_types",
"perm": "farmos_animal_types.list",
},
- {
- "title": "Equipment Types",
- "route": "farmos_equipment_types",
- "perm": "farmos_equipment_types.list",
- },
{
"title": "Land Types",
"route": "farmos_land_types",
@@ -454,11 +369,6 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_plant_types",
"perm": "farmos_plant_types.list",
},
- {
- "title": "Seasons",
- "route": "farmos_seasons",
- "perm": "farmos_seasons.list",
- },
{
"title": "Structure Types",
"route": "farmos_structure_types",
@@ -500,32 +410,17 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "farmos_logs_observation",
"perm": "farmos_logs_observation.list",
},
- {
- "title": "Seeding",
- "route": "farmos_logs_seeding",
- "perm": "farmos_logs_seeding.list",
- },
{"type": "sep"},
{
"title": "Log Types",
"route": "farmos_log_types",
"perm": "farmos_log_types.list",
},
- {
- "title": "Material Types",
- "route": "farmos_material_types",
- "perm": "farmos_material_types.list",
- },
{
"title": "Quantity Types",
"route": "farmos_quantity_types",
"perm": "farmos_quantity_types.list",
},
- {
- "title": "Material Quantities",
- "route": "farmos_quantities_material",
- "perm": "farmos_quantities_material.list",
- },
{
"title": "Standard Quantities",
"route": "farmos_quantities_standard",
diff --git a/src/wuttafarm/web/templates/deform/assetrefs.pt b/src/wuttafarm/web/templates/deform/assetrefs.pt
deleted file mode 100644
index b2e9660..0000000
--- a/src/wuttafarm/web/templates/deform/assetrefs.pt
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/src/wuttafarm/web/templates/deform/equipmenttyperefs.pt b/src/wuttafarm/web/templates/deform/equipmenttyperefs.pt
deleted file mode 100644
index 4d48fd7..0000000
--- a/src/wuttafarm/web/templates/deform/equipmenttyperefs.pt
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/src/wuttafarm/web/templates/deform/materialtyperefs.pt b/src/wuttafarm/web/templates/deform/materialtyperefs.pt
deleted file mode 100644
index 44ac6e8..0000000
--- a/src/wuttafarm/web/templates/deform/materialtyperefs.pt
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/src/wuttafarm/web/templates/deform/quantityrefs.pt b/src/wuttafarm/web/templates/deform/quantityrefs.pt
deleted file mode 100644
index cc65f77..0000000
--- a/src/wuttafarm/web/templates/deform/quantityrefs.pt
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
diff --git a/src/wuttafarm/web/templates/deform/seasonrefs.pt b/src/wuttafarm/web/templates/deform/seasonrefs.pt
deleted file mode 100644
index 955241a..0000000
--- a/src/wuttafarm/web/templates/deform/seasonrefs.pt
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/src/wuttafarm/web/templates/wuttafarm-components.mako b/src/wuttafarm/web/templates/wuttafarm-components.mako
index 890568f..37b176e 100644
--- a/src/wuttafarm/web/templates/wuttafarm-components.mako
+++ b/src/wuttafarm/web/templates/wuttafarm-components.mako
@@ -1,330 +1,7 @@
<%def name="make_wuttafarm_components()">
- ${self.make_taxonomy_terms_picker_component()}
- ${self.make_equipment_types_picker_component()}
- ${self.make_assets_picker_component()}
${self.make_animal_type_picker_component()}
- ${self.make_material_types_picker_component()}
- ${self.make_quantity_editor_component()}
- ${self.make_quantities_editor_component()}
${self.make_plant_types_picker_component()}
- ${self.make_seasons_picker_component()}
-%def>
-
-<%def name="make_taxonomy_terms_picker_component()">
-
-
-%def>
-
-<%def name="make_equipment_types_picker_component()">
-
-
-%def>
-
-<%def name="make_assets_picker_component()">
-
-
%def>
<%def name="make_animal_type_picker_component()">
@@ -431,10 +108,7 @@
createSave() {
this.createSaving = true
- ## TODO
- % if not app.is_farmos_wrapper():
const url = "${url('animal_types.ajax_create')}"
- % endif
const params = {name: this.createName}
this.wuttaPOST(url, params, response => {
this.internalAnimalTypes.push([response.data.uuid, response.data.name])
@@ -454,503 +128,6 @@
%def>
-<%def name="make_material_types_picker_component()">
-
-
-%def>
-
-<%def name="make_quantity_editor_component()">
-
-
-%def>
-
-<%def name="make_quantities_editor_component()">
-
-
-%def>
-
<%def name="make_plant_types_picker_component()">
%def>
-
-<%def name="make_seasons_picker_component()">
-
-
-%def>
diff --git a/src/wuttafarm/web/util.py b/src/wuttafarm/web/util.py
index ec88525..977550a 100644
--- a/src/wuttafarm/web/util.py
+++ b/src/wuttafarm/web/util.py
@@ -78,12 +78,4 @@ def render_quantity_object(quantity):
measure = quantity["measure_name"]
value = quantity["value_decimal"]
unit = quantity["unit_name"]
- text = f"( {measure} ) {value} {unit}"
-
- if quantity["quantity_type_id"] == "material":
- materials = ", ".join(
- [mtype.get("name", "???") for mtype in quantity["material_types"]]
- )
- return f"{materials} {text}"
-
- return text
+ return f"( {measure} ) {value} {unit}"
diff --git a/src/wuttafarm/web/views/__init__.py b/src/wuttafarm/web/views/__init__.py
index e66f479..0d58a72 100644
--- a/src/wuttafarm/web/views/__init__.py
+++ b/src/wuttafarm/web/views/__init__.py
@@ -25,7 +25,7 @@ WuttaFarm Views
from wuttaweb.views import essential
-from .master import WuttaFarmMasterView, TaxonomyMasterView
+from .master import WuttaFarmMasterView
def includeme(config):
@@ -48,23 +48,19 @@ def includeme(config):
# native table views
if mode != enum.FARMOS_INTEGRATION_MODE_WRAPPER:
config.include("wuttafarm.web.views.units")
- config.include("wuttafarm.web.views.material_types")
config.include("wuttafarm.web.views.quantities")
config.include("wuttafarm.web.views.asset_types")
config.include("wuttafarm.web.views.assets")
config.include("wuttafarm.web.views.land")
config.include("wuttafarm.web.views.structures")
- config.include("wuttafarm.web.views.equipment")
config.include("wuttafarm.web.views.animals")
config.include("wuttafarm.web.views.groups")
config.include("wuttafarm.web.views.plants")
- config.include("wuttafarm.web.views.water")
config.include("wuttafarm.web.views.logs")
config.include("wuttafarm.web.views.logs_activity")
config.include("wuttafarm.web.views.logs_harvest")
config.include("wuttafarm.web.views.logs_medical")
config.include("wuttafarm.web.views.logs_observation")
- config.include("wuttafarm.web.views.logs_seeding")
# quick form views
# (nb. these work with all integration modes)
diff --git a/src/wuttafarm/web/views/animals.py b/src/wuttafarm/web/views/animals.py
index ad9f060..f4c97e2 100644
--- a/src/wuttafarm/web/views/animals.py
+++ b/src/wuttafarm/web/views/animals.py
@@ -23,12 +23,9 @@
Master view for Animals
"""
-from collections import OrderedDict
-
from webhelpers2.html import tags
from wuttaweb.forms.schema import WuttaDictEnum
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttaweb.util import get_form_data
from wuttafarm.db.model import AnimalType, AnimalAsset
@@ -237,6 +234,27 @@ class AnimalAssetView(AssetMasterView):
"archived",
]
+ form_fields = [
+ "asset_name",
+ "animal_type",
+ "birthdate",
+ "produces_eggs",
+ "sex",
+ "is_sterile",
+ "notes",
+ "asset_type",
+ "owners",
+ "locations",
+ "groups",
+ "archived",
+ "drupal_id",
+ "farmos_uuid",
+ "thumbnail_url",
+ "image_url",
+ "thumbnail",
+ "image",
+ ]
+
def configure_grid(self, grid):
g = grid
super().configure_grid(g)
@@ -270,32 +288,15 @@ class AnimalAssetView(AssetMasterView):
animal = f.model_instance
# animal_type
- f.fields.insert_after("asset_name", "animal_type")
f.set_node("animal_type", AnimalTypeRef(self.request))
- # birthdate
- f.fields.insert_after("animal_type", "birthdate")
- # TODO: why must we assign the widget here? pretty sure that
- # was not needed when we declared form_fields directly, i.e.
- # instead of adding birthdate field in this method
- f.set_widget("birthdate", WuttaDateTimeWidget(self.request))
-
- # produces_eggs
- f.fields.insert_after("birthdate", "produces_eggs")
-
# sex
- f.fields.insert_after("produces_eggs", "sex")
if not (self.creating or self.editing) and animal.sex is None:
pass # TODO: dict enum widget does not handle null values well
else:
- # nb. ensure empty option appears like we want
- sex_enum = OrderedDict([("", "N/A")] + list(enum.ANIMAL_SEX.items()))
- f.set_node("sex", WuttaDictEnum(self.request, sex_enum))
+ f.set_node("sex", WuttaDictEnum(self.request, enum.ANIMAL_SEX))
f.set_required("sex", False)
- # is_sterile
- f.fields.insert_after("sex", "is_sterile")
-
def defaults(config, **kwargs):
base = globals()
diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py
index 64f4dbc..b4e4d31 100644
--- a/src/wuttafarm/web/views/assets.py
+++ b/src/wuttafarm/web/views/assets.py
@@ -32,7 +32,7 @@ from wuttaweb.db import Session
from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import Asset, Log
-from wuttafarm.web.forms.schema import OwnerRefs, AssetRefs
+from wuttafarm.web.forms.schema import AssetParentRefs, OwnerRefs, AssetRefs
from wuttafarm.web.forms.widgets import ImageWidget
from wuttafarm.util import get_log_type_enum
from wuttafarm.web.util import get_farmos_client_for_user
@@ -77,25 +77,6 @@ class AssetMasterView(WuttaFarmMasterView):
"archived": {"active": True, "verb": "is_false"},
}
- form_fields = [
- "asset_name",
- "parents",
- "notes",
- "asset_type",
- "owners",
- "is_location",
- "is_fixed",
- "locations",
- "groups",
- "archived",
- "drupal_id",
- "farmos_uuid",
- "thumbnail_url",
- "image_url",
- "thumbnail",
- "image",
- ]
-
has_rows = True
row_model_class = Log
rows_viewable = True
@@ -280,11 +261,11 @@ class AssetMasterView(WuttaFarmMasterView):
f.set_default("groups", asset_handler.get_groups(asset))
# parents
- f.set_node("parents", AssetRefs(self.request, for_asset=asset))
- f.set_required("parents", False)
- if not self.creating:
- # nb. must explicity declare value for non-standard field
- f.set_default("parents", asset.parents)
+ if self.creating or self.editing:
+ f.remove("parents") # TODO: add support for this
+ else:
+ f.set_node("parents", AssetParentRefs(self.request))
+ f.set_default("parents", [p.uuid for p in asset.parents])
# notes
f.set_widget("notes", "notes")
@@ -312,29 +293,11 @@ class AssetMasterView(WuttaFarmMasterView):
f.set_default("image", asset.image_url)
def objectify(self, form):
- model = self.app.model
- session = self.Session()
asset = super().objectify(form)
- data = form.validated
if self.creating:
asset.asset_type = self.get_asset_type()
- current = [p.uuid for p in asset.parents]
- desired = data["parents"] or []
-
- for uuid in desired:
- if uuid not in current:
- parent = session.get(model.Asset, uuid)
- assert parent
- asset.parents.append(parent)
-
- for uuid in current:
- if uuid not in desired:
- parent = session.get(model.Asset, uuid)
- assert parent
- asset.parents.remove(parent)
-
return asset
def get_asset_type(self):
@@ -410,23 +373,6 @@ class AssetMasterView(WuttaFarmMasterView):
g.set_filter("log_type", model.Log.log_type)
g.set_enum("log_type", get_log_type_enum(self.config, session=session))
- # assets
- g.set_renderer("assets", self.render_assets_for_grid)
-
- def render_assets_for_grid(self, log, field, value):
- assets = getattr(log, field)
-
- if self.farmos_style_grid_links:
- links = []
- for asset in assets:
- url = self.request.route_url(
- f"{asset.asset_type}_assets.view", uuid=asset.uuid
- )
- links.append(tags.link_to(str(asset), url))
- return ", ".join(links)
-
- return ", ".join([str(a) for a in assets])
-
def get_row_action_url_view(self, log, i):
return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid)
diff --git a/src/wuttafarm/web/views/common.py b/src/wuttafarm/web/views/common.py
index 445f810..674d76e 100644
--- a/src/wuttafarm/web/views/common.py
+++ b/src/wuttafarm/web/views/common.py
@@ -95,8 +95,6 @@ class CommonView(base.CommonView):
"farmos_quantities_standard.view",
"farmos_quantity_types.list",
"farmos_quantity_types.view",
- "farmos_seasons.list",
- "farmos_seasons.view",
"farmos_structure_assets.list",
"farmos_structure_assets.view",
"farmos_structure_types.list",
@@ -134,12 +132,6 @@ class CommonView(base.CommonView):
"logs_observation.view",
"logs_observation.versions",
"quick.eggs",
- "plant_types.list",
- "plant_types.view",
- "plant_types.versions",
- "seasons.list",
- "seasons.view",
- "seasons.versions",
"structure_types.list",
"structure_types.view",
"structure_types.versions",
diff --git a/src/wuttafarm/web/views/equipment.py b/src/wuttafarm/web/views/equipment.py
deleted file mode 100644
index 8076556..0000000
--- a/src/wuttafarm/web/views/equipment.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for Plants
-"""
-
-from wuttafarm.db.model import EquipmentType, EquipmentAsset
-from wuttafarm.web.views import TaxonomyMasterView
-from wuttafarm.web.views.assets import AssetMasterView
-from wuttafarm.web.forms.schema import EquipmentTypeRefs
-
-
-class EquipmentTypeView(TaxonomyMasterView):
- """
- Master view for Equipment Types
- """
-
- model_class = EquipmentType
- route_prefix = "equipment_types"
- url_prefix = "/equipment-types"
-
- farmos_route_prefix = "farmos_equipment_types"
- farmos_entity_type = "taxonomy_term"
- farmos_bundle = "equipment_type"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/equipment_type/overview"
-
-
-class EquipmentAssetView(AssetMasterView):
- """
- Master view for Equipment Assets
- """
-
- model_class = EquipmentAsset
- route_prefix = "equipment_assets"
- url_prefix = "/assets/equipment"
-
- farmos_bundle = "equipment"
- farmos_refurl_path = "/assets/equipment"
-
- labels = {
- "equipment_types": "Equipment Type",
- }
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
- equipment = f.model_instance
-
- # equipment_types
- f.fields.insert_after("asset_name", "equipment_types")
- f.set_node("equipment_types", EquipmentTypeRefs(self.request))
- if not self.creating:
- # nb. must explcitly declare value for non-standard field
- f.set_default("equipment_types", equipment.equipment_types)
-
- # manufacturer
- f.fields.insert_after("equipment_types", "manufacturer")
-
- # model
- f.fields.insert_after("manufacturer", "model")
-
- # serial_number
- f.fields.insert_after("model", "serial_number")
-
- def objectify(self, form):
- equipment = super().objectify(form)
- data = form.validated
-
- self.set_equipment_types(equipment, data["equipment_types"])
-
- return equipment
-
- def set_equipment_types(self, equipment, desired):
- model = self.app.model
- session = self.Session()
- current = [str(etype.uuid) for etype in equipment.equipment_types]
-
- for etype in desired:
- if etype["uuid"] not in current:
- equipment_type = session.get(model.EquipmentType, etype["uuid"])
- assert equipment_type
- equipment.equipment_types.append(equipment_type)
-
- desired = [etype["uuid"] for etype in desired]
- for uuid in current:
- if uuid not in desired:
- equipment_type = session.get(model.EquipmentType, uuid)
- assert equipment_type
- equipment.equipment_types.remove(equipment_type)
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- EquipmentTypeView = kwargs.get("EquipmentTypeView", base["EquipmentTypeView"])
- EquipmentTypeView.defaults(config)
-
- EquipmentAssetView = kwargs.get("EquipmentAssetView", base["EquipmentAssetView"])
- EquipmentAssetView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/__init__.py b/src/wuttafarm/web/views/farmos/__init__.py
index 708b553..e59ac1f 100644
--- a/src/wuttafarm/web/views/farmos/__init__.py
+++ b/src/wuttafarm/web/views/farmos/__init__.py
@@ -28,7 +28,6 @@ from .master import FarmOSMasterView
def includeme(config):
config.include("wuttafarm.web.views.farmos.users")
- config.include("wuttafarm.web.views.farmos.materials")
config.include("wuttafarm.web.views.farmos.quantities")
config.include("wuttafarm.web.views.farmos.asset_types")
config.include("wuttafarm.web.views.farmos.units")
@@ -36,15 +35,12 @@ def includeme(config):
config.include("wuttafarm.web.views.farmos.land_assets")
config.include("wuttafarm.web.views.farmos.structure_types")
config.include("wuttafarm.web.views.farmos.structures")
- config.include("wuttafarm.web.views.farmos.equipment")
config.include("wuttafarm.web.views.farmos.animal_types")
config.include("wuttafarm.web.views.farmos.animals")
config.include("wuttafarm.web.views.farmos.groups")
config.include("wuttafarm.web.views.farmos.plants")
- config.include("wuttafarm.web.views.farmos.water")
config.include("wuttafarm.web.views.farmos.log_types")
config.include("wuttafarm.web.views.farmos.logs_activity")
config.include("wuttafarm.web.views.farmos.logs_harvest")
config.include("wuttafarm.web.views.farmos.logs_medical")
config.include("wuttafarm.web.views.farmos.logs_observation")
- config.include("wuttafarm.web.views.farmos.logs_seeding")
diff --git a/src/wuttafarm/web/views/farmos/animals.py b/src/wuttafarm/web/views/farmos/animals.py
index 6a6bc92..c99cc5a 100644
--- a/src/wuttafarm/web/views/farmos/animals.py
+++ b/src/wuttafarm/web/views/farmos/animals.py
@@ -39,7 +39,7 @@ from wuttafarm.web.grids import (
NullableBooleanFilter,
DateTimeFilter,
)
-from wuttafarm.web.forms.schema import FarmOSRef
+from wuttafarm.web.forms.schema import FarmOSRef, FarmOSAssetRefs
class AnimalView(AssetMasterView):
@@ -99,7 +99,8 @@ class AnimalView(AssetMasterView):
def get_farmos_api_includes(self):
includes = super().get_farmos_api_includes()
- includes.update(["animal_type"])
+ includes.add("animal_type")
+ includes.add("group")
return includes
def configure_grid(self, grid):
@@ -130,6 +131,10 @@ class AnimalView(AssetMasterView):
g.set_sorter("sex", SimpleSorter("sex"))
g.set_filter("sex", StringFilter)
+ # groups
+ g.set_label("groups", "Group Membership")
+ g.set_renderer("groups", self.render_groups_for_grid)
+
# is_sterile
g.set_renderer("is_sterile", "boolean")
g.set_sorter("is_sterile", SimpleSorter("is_sterile"))
@@ -140,6 +145,18 @@ class AnimalView(AssetMasterView):
url = self.request.route_url("farmos_animal_types.view", uuid=uuid)
return tags.link_to(value, url)
+ def render_groups_for_grid(self, animal, field, value):
+ groups = []
+ for group in animal["groups"]:
+ if self.farmos_style_grid_links:
+ url = self.request.route_url(
+ "farmos_group_assets.view", uuid=group["uuid"]
+ )
+ groups.append(tags.link_to(group["name"], url))
+ else:
+ groups.append(group["name"])
+ return ", ".join(groups)
+
def get_instance(self):
data = super().get_instance()
@@ -175,6 +192,8 @@ class AnimalView(AssetMasterView):
sterile = animal["attributes"]["is_castrated"]
animal_type_object = None
+ group_objects = []
+ group_names = []
if relationships := animal.get("relationships"):
if animal_type := relationships.get("animal_type"):
@@ -184,11 +203,24 @@ class AnimalView(AssetMasterView):
"name": animal_type["attributes"]["name"],
}
+ if groups := relationships.get("group"):
+ for group in groups["data"]:
+ if group := included.get(group["id"]):
+ group = {
+ "uuid": group["id"],
+ "name": group["attributes"]["name"],
+ "asset_type": "group",
+ }
+ group_objects.append(group)
+ group_names.append(group["name"])
+
normal.update(
{
"animal_type": animal_type_object,
"animal_type_uuid": animal_type_object["uuid"],
"animal_type_name": animal_type_object["name"],
+ "groups": group_objects,
+ "group_names": group_names,
"birthdate": birthdate,
"sex": animal["attributes"]["sex"] or colander.null,
"is_sterile": sterile,
@@ -239,6 +271,12 @@ class AnimalView(AssetMasterView):
# is_sterile
f.set_node("is_sterile", colander.Boolean())
+ # groups
+ if self.creating or self.editing:
+ f.remove("groups") # TODO
+ else:
+ f.set_node("groups", FarmOSAssetRefs(self.request))
+
def get_api_payload(self, animal):
payload = super().get_api_payload(animal)
diff --git a/src/wuttafarm/web/views/farmos/assets.py b/src/wuttafarm/web/views/farmos/assets.py
index 24dd145..11f744b 100644
--- a/src/wuttafarm/web/views/farmos/assets.py
+++ b/src/wuttafarm/web/views/farmos/assets.py
@@ -24,11 +24,10 @@ Base class for Asset master views
"""
import colander
-import requests
from webhelpers2.html import tags
from wuttafarm.web.views.farmos import FarmOSMasterView
-from wuttafarm.web.forms.schema import FarmOSRefs, FarmOSAssetRefs, FarmOSLocationRefs
+from wuttafarm.web.forms.schema import FarmOSRefs, FarmOSLocationRefs
from wuttafarm.web.forms.widgets import ImageWidget
from wuttafarm.web.grids import (
ResourceData,
@@ -76,23 +75,6 @@ class AssetMasterView(FarmOSMasterView):
"archived": {"active": True, "verb": "is_false"},
}
- form_fields = [
- "name",
- "notes",
- "asset_type_name",
- "owners",
- "is_location",
- "is_fixed",
- "locations",
- "groups",
- "archived",
- "drupal_id",
- "thumbnail_url",
- "image_url",
- "thumbnail",
- "image",
- ]
-
def get_grid_data(self, **kwargs):
return ResourceData(
self.config,
@@ -128,10 +110,6 @@ class AssetMasterView(FarmOSMasterView):
# locations
g.set_renderer("locations", self.render_locations_for_grid)
- # groups
- g.set_label("groups", "Group Membership")
- g.set_renderer("groups", self.render_assets_for_grid)
-
# archived
g.set_renderer("archived", "boolean")
g.set_sorter("archived", SimpleSorter("archived"))
@@ -142,20 +120,6 @@ class AssetMasterView(FarmOSMasterView):
return tags.image(url, f"thumbnail for {self.get_model_title()}")
return None
- def render_assets_for_grid(self, log, field, value):
- if not value:
- return ""
-
- assets = []
- for asset in value:
- if self.farmos_style_grid_links:
- route = f"farmos_{asset['asset_type']}_assets.view"
- url = self.request.route_url(route, uuid=asset["uuid"])
- assets.append(tags.link_to(asset["name"], url))
- else:
- assets.append(asset["name"])
- return ", ".join(assets)
-
def render_locations_for_grid(self, asset, field, value):
locations = []
for location in value:
@@ -175,19 +139,14 @@ class AssetMasterView(FarmOSMasterView):
return None
def get_farmos_api_includes(self):
- return {"asset_type", "location", "group", "owner", "image"}
+ return {"asset_type", "location", "owner", "image"}
def get_instance(self):
- try:
- result = self.farmos_client.asset.get_id(
- self.farmos_asset_type,
- self.request.matchdict["uuid"],
- params={"include": ",".join(self.get_farmos_api_includes())},
- )
- except requests.HTTPError as exc:
- if exc.response.status_code == 404:
- raise self.notfound()
- raise
+ result = self.farmos_client.asset.get_id(
+ self.farmos_asset_type,
+ self.request.matchdict["uuid"],
+ params={"include": ",".join(self.get_farmos_api_includes())},
+ )
self.raw_json = result
included = {obj["id"]: obj for obj in result.get("included", [])}
return self.normalize_asset(result["data"], included)
@@ -211,7 +170,6 @@ class AssetMasterView(FarmOSMasterView):
owner_names = []
location_objects = []
location_names = []
- group_objects = []
thumbnail_url = None
image_url = None
if relationships := asset.get("relationships"):
@@ -245,16 +203,6 @@ class AssetMasterView(FarmOSMasterView):
location_objects.append(location)
location_names.append(location["name"])
- if groups := relationships.get("group"):
- for group in groups["data"]:
- if group := included.get(group["id"]):
- group = {
- "uuid": group["id"],
- "name": group["attributes"]["name"],
- "asset_type": "group",
- }
- group_objects.append(group)
-
if images := relationships.get("image"):
for image in images["data"]:
if image := included.get(image["id"]):
@@ -269,14 +217,11 @@ class AssetMasterView(FarmOSMasterView):
"name": asset["attributes"]["name"],
"asset_type": asset_type_object,
"asset_type_name": asset_type_name,
- "is_location": asset["attributes"]["is_location"],
- "is_fixed": asset["attributes"]["is_fixed"],
"notes": notes or colander.null,
"owners": owner_objects,
"owner_names": owner_names,
"locations": location_objects,
"location_names": location_names,
- "groups": group_objects,
"archived": archived,
"thumbnail_url": thumbnail_url or colander.null,
"image_url": image_url or colander.null,
@@ -298,12 +243,6 @@ class AssetMasterView(FarmOSMasterView):
f.set_label("locations", "Current Location")
f.set_node("locations", FarmOSLocationRefs(self.request))
- # groups
- if self.creating or self.editing:
- f.remove("groups") # TODO
- else:
- f.set_node("groups", FarmOSAssetRefs(self.request))
-
# owners
if self.creating or self.editing:
f.remove("owners") # TODO
@@ -314,16 +253,6 @@ class AssetMasterView(FarmOSMasterView):
f.set_widget("notes", "notes")
f.set_required("notes", False)
- # is_location
- f.set_node("is_location", colander.Boolean())
-
- # is_fixed
- f.set_node("is_fixed", colander.Boolean())
-
- # groups
- if self.creating or self.editing:
- f.remove("groups") # TODO: add support for this?
-
# archived
f.set_node("archived", colander.Boolean())
diff --git a/src/wuttafarm/web/views/farmos/equipment.py b/src/wuttafarm/web/views/farmos/equipment.py
deleted file mode 100644
index 6855747..0000000
--- a/src/wuttafarm/web/views/farmos/equipment.py
+++ /dev/null
@@ -1,211 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for farmOS Equipment
-"""
-
-from webhelpers2.html import tags
-
-from wuttafarm.web.views.farmos.master import TaxonomyMasterView
-from wuttafarm.web.views.farmos.assets import AssetMasterView
-from wuttafarm.web.forms.schema import FarmOSEquipmentTypeRefs
-
-
-class EquipmentTypeView(TaxonomyMasterView):
- """
- Master view for Equipment Types in farmOS.
- """
-
- model_name = "farmos_equipment_type"
- model_title = "farmOS Equipment Type"
- model_title_plural = "farmOS Equipment Types"
-
- route_prefix = "farmos_equipment_types"
- url_prefix = "/farmOS/equipment-types"
-
- farmos_taxonomy_type = "equipment_type"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/equipment_type/overview"
-
- def get_xref_buttons(self, equipment_type):
- buttons = super().get_xref_buttons(equipment_type)
- model = self.app.model
- session = self.Session()
-
- if wf_equipment_type := (
- session.query(model.EquipmentType)
- .filter(model.EquipmentType.farmos_uuid == equipment_type["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url(
- "equipment_types.view", uuid=wf_equipment_type.uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
-class EquipmentAssetView(AssetMasterView):
- """
- Master view for farmOS Equipment Assets
- """
-
- model_name = "farmos_equipment_assets"
- model_title = "farmOS Equipment Asset"
- model_title_plural = "farmOS Equipment Assets"
-
- route_prefix = "farmos_equipment_assets"
- url_prefix = "/farmOS/assets/equipment"
-
- farmos_asset_type = "equipment"
- farmos_refurl_path = "/assets/equipment"
-
- labels = {
- "equipment_types": "Equipment Type",
- }
-
- grid_columns = [
- "thumbnail",
- "drupal_id",
- "name",
- "equipment_types",
- "manufacturer",
- "model",
- "serial_number",
- "groups",
- "owners",
- "archived",
- ]
-
- def get_farmos_api_includes(self):
- includes = super().get_farmos_api_includes()
- includes.update(["equipment_type"])
- return includes
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # equipment_types
- g.set_renderer("equipment_types", self.render_equipment_types_for_grid)
-
- def render_equipment_types_for_grid(self, equipment, field, value):
- if self.farmos_style_grid_links:
- links = []
- for etype in value:
- url = self.request.route_url(
- f"farmos_equipment_types.view", uuid=etype["uuid"]
- )
- links.append(tags.link_to(etype["name"], url))
- return ", ".join(links)
-
- return ", ".join([etype["name"] for etype in value])
-
- def normalize_asset(self, equipment, included):
- data = super().normalize_asset(equipment, included)
-
- equipment_type_objects = []
- rels = equipment["relationships"]
- for etype in rels["equipment_type"]["data"]:
- uuid = etype["id"]
- equipment_type = {
- "uuid": uuid,
- "type": etype["type"],
- }
- if etype := included.get(uuid):
- equipment_type.update(
- {
- "name": etype["attributes"]["name"],
- }
- )
- equipment_type_objects.append(equipment_type)
-
- data.update(
- {
- "manufacturer": equipment["attributes"]["manufacturer"],
- "model": equipment["attributes"]["model"],
- "serial_number": equipment["attributes"]["serial_number"],
- "equipment_types": equipment_type_objects,
- }
- )
-
- return data
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # equipment_types
- f.fields.insert_after("name", "equipment_types")
- f.set_node("equipment_types", FarmOSEquipmentTypeRefs(self.request))
-
- # manufacturer
- f.fields.insert_after("equipment_types", "manufacturer")
-
- # model
- f.fields.insert_after("manufacturer", "model")
-
- # serial_number
- f.fields.insert_after("model", "serial_number")
-
- def get_xref_buttons(self, equipment):
- model = self.app.model
- session = self.Session()
-
- buttons = super().get_xref_buttons(equipment)
-
- if wf_equipment := (
- session.query(model.Asset)
- .filter(model.Asset.farmos_uuid == equipment["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url(
- "equipment_assets.view", uuid=wf_equipment.uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- EquipmentTypeView = kwargs.get("EquipmentTypeView", base["EquipmentTypeView"])
- EquipmentTypeView.defaults(config)
-
- EquipmentAssetView = kwargs.get("EquipmentAssetView", base["EquipmentAssetView"])
- EquipmentAssetView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/logs.py b/src/wuttafarm/web/views/farmos/logs.py
index 74fb941..cb7a87b 100644
--- a/src/wuttafarm/web/views/farmos/logs.py
+++ b/src/wuttafarm/web/views/farmos/logs.py
@@ -45,7 +45,7 @@ from wuttafarm.web.forms.schema import (
LogQuick,
Notes,
)
-from wuttafarm.web.util import render_quantity_objects, render_quantity_object
+from wuttafarm.web.util import render_quantity_objects
class LogMasterView(FarmOSMasterView):
@@ -199,20 +199,7 @@ class LogMasterView(FarmOSMasterView):
)
self.raw_json = result
included = {obj["id"]: obj for obj in result.get("included", [])}
- instance = self.normalize_log(result["data"], included)
-
- for qty in instance["quantities"]:
-
- if qty["quantity_type_id"] == "material":
- for mtype in qty["material_types"]:
- result = self.farmos_client.resource.get_id(
- "taxonomy_term", "material_type", mtype["uuid"]
- )
- mtype["name"] = result["data"]["attributes"]["name"]
-
- qty["as_text"] = render_quantity_object(qty)
-
- return instance
+ return self.normalize_log(result["data"], included)
def get_instance_title(self, log):
return log["name"]
diff --git a/src/wuttafarm/web/views/farmos/logs_seeding.py b/src/wuttafarm/web/views/farmos/logs_seeding.py
deleted file mode 100644
index ed967cc..0000000
--- a/src/wuttafarm/web/views/farmos/logs_seeding.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS Seeding Logs
-"""
-
-from wuttaweb.forms.schema import WuttaDateTime
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
-
-from wuttafarm.web.views.farmos.logs import LogMasterView
-from wuttafarm.web.grids import SimpleSorter, StringFilter
-
-
-class SeedingLogView(LogMasterView):
- """
- View for farmOS seeding logs
- """
-
- model_name = "farmos_seeding_log"
- model_title = "farmOS Seeding Log"
- model_title_plural = "farmOS Seeding Logs"
-
- route_prefix = "farmos_logs_seeding"
- url_prefix = "/farmOS/logs/seeding"
-
- farmos_log_type = "seeding"
- farmos_refurl_path = "/logs/seeding"
-
- grid_columns = [
- "status",
- "drupal_id",
- "timestamp",
- "name",
- "assets",
- "locations",
- "purchase_date",
- "source",
- "is_group_assignment",
- "owners",
- ]
-
- def normalize_log(self, log, included):
- data = super().normalize_log(log, included)
- data.update(
- {
- "source": log["attributes"]["source"],
- "purchase_date": self.normal.normalize_datetime(
- log["attributes"]["purchase_date"]
- ),
- "lot_number": log["attributes"]["lot_number"],
- }
- )
- return data
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # purchase_date
- g.set_renderer("purchase_date", "date")
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # source
- f.fields.insert_after("timestamp", "source")
-
- # purchase_date
- f.fields.insert_after("source", "purchase_date")
- f.set_node("purchase_date", WuttaDateTime())
- f.set_widget("purchase_date", WuttaDateTimeWidget(self.request))
-
- # lot_number
- f.fields.insert_after("purchase_date", "lot_number")
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- SeedingLogView = kwargs.get("SeedingLogView", base["SeedingLogView"])
- SeedingLogView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/materials.py b/src/wuttafarm/web/views/farmos/materials.py
deleted file mode 100644
index e56557d..0000000
--- a/src/wuttafarm/web/views/farmos/materials.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-View for farmOS materials
-"""
-
-from wuttafarm.web.views.farmos.master import TaxonomyMasterView
-
-
-class MaterialTypeView(TaxonomyMasterView):
- """
- Master view for Material Types in farmOS.
- """
-
- model_name = "farmos_material_type"
- model_title = "farmOS Material Type"
- model_title_plural = "farmOS Material Types"
-
- route_prefix = "farmos_material_types"
- url_prefix = "/farmOS/material-types"
-
- farmos_taxonomy_type = "material_type"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/material_type/overview"
-
- def get_xref_buttons(self, material_type):
- buttons = super().get_xref_buttons(material_type)
- model = self.app.model
- session = self.Session()
-
- if wf_material_type := (
- session.query(model.MaterialType)
- .filter(model.MaterialType.farmos_uuid == material_type["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url(
- "material_types.view", uuid=wf_material_type.uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- MaterialTypeView = kwargs.get("MaterialTypeView", base["MaterialTypeView"])
- MaterialTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/plants.py b/src/wuttafarm/web/views/farmos/plants.py
index 40768c4..57bf2d4 100644
--- a/src/wuttafarm/web/views/farmos/plants.py
+++ b/src/wuttafarm/web/views/farmos/plants.py
@@ -32,12 +32,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttafarm.web.views.farmos.master import TaxonomyMasterView
from wuttafarm.web.views.farmos import FarmOSMasterView
-from wuttafarm.web.forms.schema import (
- UsersType,
- StructureType,
- FarmOSPlantTypes,
- FarmOSRefs,
-)
+from wuttafarm.web.forms.schema import UsersType, StructureType, FarmOSPlantTypes
from wuttafarm.web.forms.widgets import ImageWidget
@@ -80,43 +75,6 @@ class PlantTypeView(TaxonomyMasterView):
return buttons
-class SeasonView(TaxonomyMasterView):
- """
- Master view for Seasons in farmOS.
- """
-
- model_name = "farmos_season"
- model_title = "farmOS Season"
- model_title_plural = "farmOS Seasons"
-
- route_prefix = "farmos_seasons"
- url_prefix = "/farmOS/seasons"
-
- farmos_taxonomy_type = "season"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/season/overview"
-
- def get_xref_buttons(self, season):
- buttons = super().get_xref_buttons(season)
- model = self.app.model
- session = self.Session()
-
- if wf_season := (
- session.query(model.Season)
- .filter(model.Season.farmos_uuid == season["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url("seasons.view", uuid=wf_season.uuid),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
class PlantAssetView(FarmOSMasterView):
"""
Master view for farmOS Plant Assets
@@ -131,10 +89,6 @@ class PlantAssetView(FarmOSMasterView):
farmos_refurl_path = "/assets/plant"
- labels = {
- "seasons": "Season",
- }
-
grid_columns = [
"name",
"archived",
@@ -145,7 +99,6 @@ class PlantAssetView(FarmOSMasterView):
form_fields = [
"name",
"plant_types",
- "seasons",
"archived",
"owners",
"location",
@@ -198,21 +151,6 @@ class PlantAssetView(FarmOSMasterView):
}
)
- # add seasons
- if seasons := relationships.get("season"):
- if seasons["data"]:
- data["seasons"] = []
- for season in seasons["data"]:
- season = self.farmos_client.resource.get_id(
- "taxonomy_term", "season", season["id"]
- )
- data["seasons"].append(
- {
- "uuid": season["data"]["id"],
- "name": season["data"]["attributes"]["name"],
- }
- )
-
# add location
if location := relationships.get("location"):
if location["data"]:
@@ -261,14 +199,22 @@ class PlantAssetView(FarmOSMasterView):
return plant["name"]
def normalize_plant(self, plant):
- normal = self.normal.normalize_farmos_asset(plant)
+
+ if notes := plant["attributes"]["notes"]:
+ notes = notes["value"]
+
+ if self.farmos_4x:
+ archived = plant["attributes"]["archived"]
+ else:
+ archived = plant["attributes"]["status"] == "archived"
+
return {
- "uuid": normal["uuid"],
- "drupal_id": normal["drupal_id"],
- "name": normal["asset_name"],
+ "uuid": plant["id"],
+ "drupal_id": plant["attributes"]["drupal_internal__id"],
+ "name": plant["attributes"]["name"],
"location": colander.null, # TODO
- "archived": normal["archived"],
- "notes": normal["notes"] or colander.null,
+ "archived": archived,
+ "notes": notes or colander.null,
}
def configure_form(self, form):
@@ -279,9 +225,6 @@ class PlantAssetView(FarmOSMasterView):
# plant_types
f.set_node("plant_types", FarmOSPlantTypes(self.request))
- # seasons
- f.set_node("seasons", FarmOSRefs(self.request, "farmos_seasons"))
-
# location
f.set_node("location", StructureType(self.request))
@@ -336,9 +279,6 @@ def defaults(config, **kwargs):
PlantTypeView = kwargs.get("PlantTypeView", base["PlantTypeView"])
PlantTypeView.defaults(config)
- SeasonView = kwargs.get("SeasonView", base["SeasonView"])
- SeasonView.defaults(config)
-
PlantAssetView = kwargs.get("PlantAssetView", base["PlantAssetView"])
PlantAssetView.defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/quantities.py b/src/wuttafarm/web/views/farmos/quantities.py
index bd2e519..a388559 100644
--- a/src/wuttafarm/web/views/farmos/quantities.py
+++ b/src/wuttafarm/web/views/farmos/quantities.py
@@ -26,13 +26,12 @@ View for farmOS Quantity Types
import datetime
import colander
-import requests
from wuttaweb.forms.schema import WuttaDateTime
from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttafarm.web.views.farmos import FarmOSMasterView
-from wuttafarm.web.forms.schema import FarmOSUnitRef, FarmOSRefs
+from wuttafarm.web.forms.schema import FarmOSUnitRef
from wuttafarm.web.grids import ResourceData
@@ -143,7 +142,6 @@ class QuantityMasterView(FarmOSMasterView):
sort_defaults = ("drupal_id", "desc")
form_fields = [
- "quantity_type_name",
"measure",
"value",
"units",
@@ -173,7 +171,6 @@ class QuantityMasterView(FarmOSMasterView):
# as_text
g.set_renderer("as_text", self.render_as_text_for_grid)
- g.set_link("as_text")
# measure
g.set_renderer("measure", self.render_measure_for_grid)
@@ -206,26 +203,14 @@ class QuantityMasterView(FarmOSMasterView):
return qty["value"]["decimal"]
def get_instance(self):
- # TODO: this pattern should be repeated for other views
- try:
- result = self.farmos_client.resource.get_id(
- "quantity",
- self.farmos_quantity_type,
- self.request.matchdict["uuid"],
- params={"include": self.get_farmos_api_includes()},
- )
- except requests.HTTPError as exc:
- if exc.response.status_code == 404:
- raise self.notfound()
- raise
+ quantity = self.farmos_client.resource.get_id(
+ "quantity", self.farmos_quantity_type, self.request.matchdict["uuid"]
+ )
+ self.raw_json = quantity
- self.raw_json = result
+ data = self.normalize_quantity(quantity["data"])
- included = {obj["id"]: obj for obj in result.get("included", [])}
- assert included
- data = self.normalize_quantity(result["data"], included)
-
- if relationships := result["data"].get("relationships"):
+ if relationships := quantity["data"].get("relationships"):
# add units
if units := relationships.get("units"):
@@ -293,11 +278,6 @@ class QuantityMasterView(FarmOSMasterView):
f = form
super().configure_form(f)
- # quantity_type_name
- f.set_label("quantity_type_name", "Quantity Type")
- f.set_readonly("quantity_type_name")
- f.set_default("quantity_type_name", self.farmos_quantity_type.capitalize())
-
# created
f.set_node("created", WuttaDateTime(self.request))
f.set_widget("created", WuttaDateTimeWidget(self.request))
@@ -323,7 +303,6 @@ class StandardQuantityView(QuantityMasterView):
url_prefix = "/farmOS/quantities/standard"
farmos_quantity_type = "standard"
- farmos_refurl_path = "/log-quantities/standard"
def get_xref_buttons(self, standard_quantity):
model = self.app.model
@@ -350,90 +329,6 @@ class StandardQuantityView(QuantityMasterView):
return buttons
-class MaterialQuantityView(QuantityMasterView):
- """
- View for farmOS Material Quantities
- """
-
- model_name = "farmos_material_quantity"
- model_title = "farmOS Material Quantity"
- model_title_plural = "farmOS Material Quantities"
-
- route_prefix = "farmos_quantities_material"
- url_prefix = "/farmOS/quantities/material"
-
- farmos_quantity_type = "material"
- farmos_refurl_path = "/log-quantities/material"
-
- def get_farmos_api_includes(self):
- includes = super().get_farmos_api_includes()
- includes.update({"material_type"})
- return includes
-
- def normalize_quantity(self, quantity, included={}):
- normal = super().normalize_quantity(quantity, included)
-
- material_type_objects = []
- material_type_uuids = []
- if relationships := quantity["relationships"]:
-
- if material_types := relationships["material_type"]["data"]:
- for mtype in material_types:
- uuid = mtype["id"]
- material_type_uuids.append(uuid)
- material_type = {
- "uuid": uuid,
- "type": mtype["type"],
- }
- if mtype := included.get(uuid):
- material_type.update(
- {
- "name": mtype["attributes"]["name"],
- }
- )
- material_type_objects.append(material_type)
-
- normal.update(
- {
- "material_types": material_type_objects,
- "material_type_uuids": material_type_uuids,
- }
- )
- return normal
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # material_types
- f.fields.insert_before("measure", "material_types")
- f.set_node("material_types", FarmOSRefs(self.request, "farmos_material_types"))
-
- def get_xref_buttons(self, material_quantity):
- model = self.app.model
- session = self.Session()
- buttons = []
-
- if wf_material_quantity := (
- session.query(model.MaterialQuantity)
- .join(model.Quantity)
- .filter(model.Quantity.farmos_uuid == material_quantity["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url(
- "quantities_material.view", uuid=wf_material_quantity.uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
def defaults(config, **kwargs):
base = globals()
@@ -445,11 +340,6 @@ def defaults(config, **kwargs):
)
StandardQuantityView.defaults(config)
- MaterialQuantityView = kwargs.get(
- "MaterialQuantityView", base["MaterialQuantityView"]
- )
- MaterialQuantityView.defaults(config)
-
def includeme(config):
defaults(config)
diff --git a/src/wuttafarm/web/views/farmos/water.py b/src/wuttafarm/web/views/farmos/water.py
deleted file mode 100644
index 129f22e..0000000
--- a/src/wuttafarm/web/views/farmos/water.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for Water Assets in farmOS
-"""
-
-from wuttafarm.web.views.farmos.assets import AssetMasterView
-
-
-class WaterAssetView(AssetMasterView):
- """
- Master view for farmOS Water Assets
- """
-
- model_name = "farmos_water_assets"
- model_title = "farmOS Water Asset"
- model_title_plural = "farmOS Water Assets"
-
- route_prefix = "farmos_water_assets"
- url_prefix = "/farmOS/assets/water"
-
- farmos_asset_type = "water"
- farmos_refurl_path = "/assets/water"
-
- grid_columns = [
- "thumbnail",
- "drupal_id",
- "name",
- "archived",
- ]
-
- def get_xref_buttons(self, water):
- model = self.app.model
- session = self.Session()
- buttons = super().get_xref_buttons(water)
-
- if wf_water := (
- session.query(model.Asset)
- .filter(model.Asset.farmos_uuid == water["uuid"])
- .first()
- ):
- buttons.append(
- self.make_button(
- f"View {self.app.get_title()} record",
- primary=True,
- url=self.request.route_url("water_assets.view", uuid=wf_water.uuid),
- icon_left="eye",
- )
- )
-
- return buttons
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- WaterAssetView = kwargs.get("WaterAssetView", base["WaterAssetView"])
- WaterAssetView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/groups.py b/src/wuttafarm/web/views/groups.py
index c8cc7f7..4331280 100644
--- a/src/wuttafarm/web/views/groups.py
+++ b/src/wuttafarm/web/views/groups.py
@@ -47,11 +47,15 @@ class GroupView(AssetMasterView):
"archived",
]
- def configure_form(self, f):
- super().configure_form(f)
-
- # produces_eggs
- f.fields.insert_after("asset_type", "produces_eggs")
+ form_fields = [
+ "asset_name",
+ "notes",
+ "asset_type",
+ "produces_eggs",
+ "archived",
+ "drupal_id",
+ "farmos_uuid",
+ ]
def defaults(config, **kwargs):
diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py
index 3d91ba1..9c983b7 100644
--- a/src/wuttafarm/web/views/logs.py
+++ b/src/wuttafarm/web/views/logs.py
@@ -34,7 +34,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import LogType, Log
-from wuttafarm.web.forms.schema import AssetRefs, QuantityRefs, OwnerRefs
+from wuttafarm.web.forms.schema import AssetRefs, LogQuantityRefs, OwnerRefs
from wuttafarm.util import get_log_type_enum
@@ -256,21 +256,26 @@ class LogMasterView(WuttaFarmMasterView):
f.set_default("timestamp", self.app.make_utc())
# assets
- f.set_node("assets", AssetRefs(self.request))
- f.set_required("assets", False)
- if not self.creating:
+ if self.creating or self.editing:
+ f.remove("assets") # TODO: need to support this
+ else:
+ f.set_node("assets", AssetRefs(self.request))
# nb. must explicity declare value for non-standard field
f.set_default("assets", log.assets)
# groups
- f.set_node("groups", AssetRefs(self.request, is_group=True))
- if not self.creating:
+ if self.creating or self.editing:
+ f.remove("groups") # TODO: need to support this
+ else:
+ f.set_node("groups", AssetRefs(self.request))
# nb. must explicity declare value for non-standard field
f.set_default("groups", log.groups)
# locations
- f.set_node("locations", AssetRefs(self.request, is_location=True))
- if not self.creating:
+ if self.creating or self.editing:
+ f.remove("locations") # TODO: need to support this
+ else:
+ f.set_node("locations", AssetRefs(self.request))
# nb. must explicity declare value for non-standard field
f.set_default("locations", log.locations)
@@ -287,12 +292,12 @@ class LogMasterView(WuttaFarmMasterView):
f.set_readonly("log_type")
# quantities
- f.set_node("quantities", QuantityRefs(self.request))
- if not self.creating:
+ if self.creating or self.editing:
+ f.remove("quantities") # TODO: need to support this
+ else:
+ f.set_node("quantities", LogQuantityRefs(self.request))
# nb. must explicity declare value for non-standard field
- f.set_default(
- "quantities", [self.app.get_true_quantity(q) for q in log.quantities]
- )
+ f.set_default("quantities", log.quantities)
# notes
f.set_widget("notes", "notes")
@@ -319,141 +324,13 @@ class LogMasterView(WuttaFarmMasterView):
def objectify(self, form):
log = super().objectify(form)
- data = form.validated
if self.creating:
-
- # log_type
+ model_class = self.get_model_class()
log.log_type = self.get_farmos_log_type()
- # owner
- log.owners = [self.request.user]
-
- self.set_assets(log, data["assets"])
- self.set_locations(log, data["locations"])
- self.set_groups(log, data["groups"])
- self.set_quantities(log, data["quantities"])
-
return log
- def set_assets(self, log, desired):
- model = self.app.model
- session = self.Session()
- current = [a.uuid for a in log.assets]
-
- for uuid in desired:
- if uuid not in current:
- asset = session.get(model.Asset, uuid)
- assert asset
- log.assets.append(asset)
-
- for uuid in current:
- if uuid not in desired:
- asset = session.get(model.Asset, uuid)
- assert asset
- log.assets.remove(asset)
-
- def set_locations(self, log, desired):
- model = self.app.model
- session = self.Session()
- current = [l.uuid for l in log.locations]
-
- for uuid in desired:
- if uuid not in current:
- location = session.get(model.Asset, uuid)
- assert location
- log.locations.append(location)
-
- for uuid in current:
- if uuid not in desired:
- location = session.get(model.Asset, uuid)
- assert location
- log.locations.remove(location)
-
- def set_groups(self, log, desired):
- model = self.app.model
- session = self.Session()
- current = [g.uuid for g in log.groups]
-
- for uuid in desired:
- if uuid not in current:
- group = session.get(model.Asset, uuid)
- assert group
- log.groups.append(group)
-
- for uuid in current:
- if uuid not in desired:
- group = session.get(model.Asset, uuid)
- assert group
- log.groups.remove(group)
-
- def set_quantities(self, log, desired):
- model = self.app.model
- session = self.Session()
-
- current = {
- qty.uuid.hex: self.app.get_true_quantity(qty) for qty in log.quantities
- }
- for new_qty in desired:
- units = session.get(model.Unit, new_qty["units"]["uuid"])
- assert units
- if new_qty["uuid"].startswith("new_"):
- qty = self.app.make_true_quantity(
- new_qty["quantity_type"]["drupal_id"],
- measure_id=new_qty["measure"],
- value_numerator=int(new_qty["value"]),
- value_denominator=1,
- units=units,
- )
- # nb. must ensure "typed" quantity record persists!
- session.add(qty)
- # but must add "generic" quantity record to log
- log.quantities.append(qty.quantity)
- else:
- old_qty = current[new_qty["uuid"]]
- old_qty.measure_id = new_qty["measure"]
- old_qty.value_numerator = int(new_qty["value"])
- old_qty.value_denominator = 1
- old_qty.units = units
- if old_qty.quantity_type_id == "material":
- self.set_material_types(old_qty, new_qty["material_types"])
-
- desired = [qty["uuid"] for qty in desired]
- for old_qty in list(log.quantities):
- # nb. "old_qty" may be newly-created, w/ no uuid yet
- # (this logic may break if session gets flushed early!)
- if old_qty.uuid and old_qty.uuid.hex not in desired:
- log.quantities.remove(old_qty)
-
- def set_material_types(self, quantity, desired):
- model = self.app.model
- session = self.Session()
- current = {mtype.uuid: mtype for mtype in quantity.material_types}
-
- for new_mtype in desired:
- mtype = session.get(model.MaterialType, new_mtype["uuid"])
- assert mtype
- if mtype.uuid not in current:
- quantity.material_types.append(mtype)
-
- desired = [mtype["uuid"] for mtype in desired]
- for old_mtype in current.values():
- if old_mtype.uuid.hex not in desired:
- quantity.material_types.remove(old_mtype)
-
- def auto_sync_to_farmos(self, client, log):
- model = self.app.model
- session = self.Session()
-
- # nb. ensure quantities have uuid keys
- session.flush()
-
- for qty in log.quantities:
- qty = self.app.get_true_quantity(qty)
- self.app.auto_sync_to_farmos(qty, client=client)
-
- self.app.auto_sync_to_farmos(log, client=client)
-
def get_farmos_url(self, log):
return self.app.get_farmos_url(f"/log/{log.drupal_id}")
diff --git a/src/wuttafarm/web/views/logs_seeding.py b/src/wuttafarm/web/views/logs_seeding.py
deleted file mode 100644
index 8946aff..0000000
--- a/src/wuttafarm/web/views/logs_seeding.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for Seeding Logs
-"""
-
-from wuttaweb.forms.widgets import WuttaDateTimeWidget
-
-from wuttafarm.web.views.logs import LogMasterView
-from wuttafarm.db.model import SeedingLog
-
-
-class SeedingLogView(LogMasterView):
- """
- Master view for Seeding Logs
- """
-
- model_class = SeedingLog
- route_prefix = "logs_seeding"
- url_prefix = "/logs/seeding"
-
- farmos_bundle = "seeding"
- farmos_refurl_path = "/logs/seeding"
-
- grid_columns = [
- "status",
- "drupal_id",
- "timestamp",
- "message",
- "assets",
- "locations",
- "purchase_date",
- "source",
- "is_group_assignment",
- "owners",
- ]
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # source
- f.fields.insert_after("timestamp", "source")
-
- # purchase_date
- f.fields.insert_after("source", "purchase_date")
- f.set_widget("purchase_date", WuttaDateTimeWidget(self.request))
-
- # lot_number
- f.fields.insert_after("purchase_date", "lot_number")
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- SeedingLogView = kwargs.get("SeedingLogView", base["SeedingLogView"])
- SeedingLogView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/master.py b/src/wuttafarm/web/views/master.py
index d9fe986..747cdc5 100644
--- a/src/wuttafarm/web/views/master.py
+++ b/src/wuttafarm/web/views/master.py
@@ -26,7 +26,6 @@ Base class for WuttaFarm master views
from webhelpers2.html import tags
from wuttaweb.views import MasterView
-from wuttaweb.util import get_form_data
from wuttafarm.web.util import use_farmos_style_grid_links, get_farmos_client_for_user
@@ -114,10 +113,7 @@ class WuttaFarmMasterView(MasterView):
# maybe also sync change to farmOS
if self.app.is_farmos_mirror():
client = get_farmos_client_for_user(self.request)
- self.auto_sync_to_farmos(client, obj)
-
- def auto_sync_to_farmos(self, client, obj):
- self.app.auto_sync_to_farmos(obj, client=client, require=False)
+ self.app.auto_sync_to_farmos(obj, client=client, require=False)
def get_farmos_entity_type(self):
if self.farmos_entity_type:
@@ -149,112 +145,3 @@ class WuttaFarmMasterView(MasterView):
bundle = self.get_farmos_bundle()
client = get_farmos_client_for_user(self.request)
client.resource.delete(entity_type, bundle, farmos_uuid)
-
-
-class TaxonomyMasterView(WuttaFarmMasterView):
- """
- Base class for master views serving taxonomy terms.
- """
-
- farmos_entity_type = "taxonomy_term"
-
- grid_columns = [
- "name",
- "description",
- ]
-
- sort_defaults = "name"
-
- filter_defaults = {
- "name": {"active": True, "verb": "contains"},
- }
-
- form_fields = [
- "name",
- "description",
- "drupal_id",
- "farmos_uuid",
- ]
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
-
- # description
- f.set_widget("description", "notes")
-
- def get_farmos_url(self, obj):
- return self.app.get_farmos_url(f"/taxonomy/term/{obj.drupal_id}")
-
- def get_xref_buttons(self, term):
- buttons = super().get_xref_buttons(term)
-
- if term.farmos_uuid:
- buttons.append(
- self.make_button(
- "View farmOS record",
- primary=True,
- url=self.request.route_url(
- f"{self.farmos_route_prefix}.view", uuid=term.farmos_uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
- def ajax_create(self):
- """
- AJAX view to create a new taxonomy term.
- """
- model = self.app.model
- session = self.Session()
- data = get_form_data(self.request)
-
- name = data.get("name")
- if not name:
- return {"error": "Name is required"}
-
- term = self.model_class(name=name)
- session.add(term)
- session.flush()
-
- if self.app.is_farmos_mirror():
- client = get_farmos_client_for_user(self.request)
- self.app.auto_sync_to_farmos(term, client=client)
-
- return {
- "uuid": term.uuid.hex,
- "name": term.name,
- "farmos_uuid": term.farmos_uuid.hex,
- "drupal_id": term.drupal_id,
- }
-
- @classmethod
- def defaults(cls, config):
- """ """
- cls._defaults(config)
- cls._taxonomy_defaults(config)
-
- @classmethod
- def _taxonomy_defaults(cls, config):
- route_prefix = cls.get_route_prefix()
- permission_prefix = cls.get_permission_prefix()
- url_prefix = cls.get_url_prefix()
-
- # ajax_create
- config.add_route(f"{route_prefix}.ajax_create", f"{url_prefix}/ajax/new")
- config.add_view(
- cls,
- attr="ajax_create",
- route_name=f"{route_prefix}.ajax_create",
- permission=f"{permission_prefix}.create",
- renderer="json",
- )
diff --git a/src/wuttafarm/web/views/material_types.py b/src/wuttafarm/web/views/material_types.py
deleted file mode 100644
index d2118a7..0000000
--- a/src/wuttafarm/web/views/material_types.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for Material Types
-"""
-
-from wuttafarm.web.views import TaxonomyMasterView
-from wuttafarm.db.model import MaterialType
-
-
-class MaterialTypeView(TaxonomyMasterView):
- """
- Master view for Material Types
- """
-
- model_class = MaterialType
- route_prefix = "material_types"
- url_prefix = "/material-types"
-
- farmos_route_prefix = "farmos_material_types"
- farmos_bundle = "material_type"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/material_type/overview"
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- MaterialTypeView = kwargs.get("MaterialTypeView", base["MaterialTypeView"])
- MaterialTypeView.defaults(config)
-
-
-def includeme(config):
- defaults(config)
diff --git a/src/wuttafarm/web/views/plants.py b/src/wuttafarm/web/views/plants.py
index 16bd3c0..a114e07 100644
--- a/src/wuttafarm/web/views/plants.py
+++ b/src/wuttafarm/web/views/plants.py
@@ -28,9 +28,9 @@ from webhelpers2.html import tags
from wuttaweb.forms.schema import WuttaDictEnum
from wuttaweb.util import get_form_data
-from wuttafarm.db.model import PlantType, Season, PlantAsset
+from wuttafarm.db.model import PlantType, PlantAsset
from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView
-from wuttafarm.web.forms.schema import PlantTypeRefs, SeasonRefs
+from wuttafarm.web.forms.schema import PlantTypeRefs
from wuttafarm.web.forms.widgets import ImageWidget
from wuttafarm.web.util import get_farmos_client_for_user
@@ -195,166 +195,6 @@ class PlantTypeView(AssetTypeMasterView):
)
-class SeasonView(AssetTypeMasterView):
- """
- Master view for Seasons
- """
-
- model_class = Season
- route_prefix = "seasons"
- url_prefix = "/seasons"
-
- farmos_entity_type = "taxonomy_term"
- farmos_bundle = "season"
- farmos_refurl_path = "/admin/structure/taxonomy/manage/season/overview"
-
- grid_columns = [
- "name",
- "description",
- ]
-
- sort_defaults = "name"
-
- filter_defaults = {
- "name": {"active": True, "verb": "contains"},
- }
-
- form_fields = [
- "name",
- "description",
- "drupal_id",
- "farmos_uuid",
- ]
-
- has_rows = True
- row_model_class = PlantAsset
- rows_viewable = True
-
- row_grid_columns = [
- "asset_name",
- "archived",
- ]
-
- rows_sort_defaults = "asset_name"
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # name
- g.set_link("name")
-
- def get_farmos_url(self, season):
- return self.app.get_farmos_url(f"/taxonomy/term/{season.drupal_id}")
-
- def get_xref_buttons(self, season):
- buttons = super().get_xref_buttons(season)
-
- if season.farmos_uuid:
- buttons.append(
- self.make_button(
- "View farmOS record",
- primary=True,
- url=self.request.route_url(
- "farmos_seasons.view", uuid=season.farmos_uuid
- ),
- icon_left="eye",
- )
- )
-
- return buttons
-
- def delete(self):
- season = self.get_instance()
-
- if season._plant_assets:
- self.request.session.flash(
- "Cannot delete season which is still referenced by plant assets.",
- "warning",
- )
- url = self.get_action_url("view", season)
- return self.redirect(self.request.get_referrer(default=url))
-
- return super().delete()
-
- def get_row_grid_data(self, season):
- model = self.app.model
- session = self.Session()
- return (
- session.query(model.PlantAsset)
- .join(model.Asset)
- .outerjoin(model.PlantAssetSeason)
- .filter(model.PlantAssetSeason.season == season)
- )
-
- def configure_row_grid(self, grid):
- g = grid
- super().configure_row_grid(g)
- model = self.app.model
-
- # asset_name
- g.set_link("asset_name")
- g.set_sorter("asset_name", model.Asset.asset_name)
- g.set_filter("asset_name", model.Asset.asset_name)
-
- # archived
- g.set_renderer("archived", "boolean")
- g.set_sorter("archived", model.Asset.archived)
- g.set_filter("archived", model.Asset.archived)
-
- def get_row_action_url_view(self, plant, i):
- return self.request.route_url("plant_assets.view", uuid=plant.uuid)
-
- def ajax_create(self):
- """
- AJAX view to create a new season.
- """
- model = self.app.model
- session = self.Session()
- data = get_form_data(self.request)
-
- name = data.get("name")
- if not name:
- return {"error": "Name is required"}
-
- season = model.Season(name=name)
- session.add(season)
- session.flush()
-
- if self.app.is_farmos_mirror():
- client = get_farmos_client_for_user(self.request)
- self.app.auto_sync_to_farmos(season, client=client)
-
- return {
- "uuid": season.uuid.hex,
- "name": season.name,
- "farmos_uuid": season.farmos_uuid.hex,
- "drupal_id": season.drupal_id,
- }
-
- @classmethod
- def defaults(cls, config):
- """ """
- cls._defaults(config)
- cls._season_defaults(config)
-
- @classmethod
- def _season_defaults(cls, config):
- route_prefix = cls.get_route_prefix()
- permission_prefix = cls.get_permission_prefix()
- url_prefix = cls.get_url_prefix()
-
- # ajax_create
- config.add_route(f"{route_prefix}.ajax_create", f"{url_prefix}/ajax/new")
- config.add_view(
- cls,
- attr="ajax_create",
- route_name=f"{route_prefix}.ajax_create",
- permission=f"{permission_prefix}.create",
- renderer="json",
- )
-
-
class PlantAssetView(AssetMasterView):
"""
Master view for Plant Assets
@@ -369,7 +209,6 @@ class PlantAssetView(AssetMasterView):
labels = {
"plant_types": "Crop/Variety",
- "seasons": "Season",
}
grid_columns = [
@@ -381,6 +220,21 @@ class PlantAssetView(AssetMasterView):
"archived",
]
+ form_fields = [
+ "asset_name",
+ "plant_types",
+ "season",
+ "notes",
+ "asset_type",
+ "archived",
+ "drupal_id",
+ "farmos_uuid",
+ "thumbnail_url",
+ "image_url",
+ "thumbnail",
+ "image",
+ ]
+
def configure_grid(self, grid):
g = grid
super().configure_grid(g)
@@ -408,33 +262,23 @@ class PlantAssetView(AssetMasterView):
plant = f.model_instance
# plant_types
- f.fields.insert_after("asset_name", "plant_types")
f.set_node("plant_types", PlantTypeRefs(self.request))
if not self.creating:
# nb. must explcitly declare value for non-standard field
f.set_default("plant_types", [pt.uuid for pt in plant.plant_types])
# season
- f.fields.insert_after("plant_types", "seasons")
- f.set_node("seasons", SeasonRefs(self.request))
- f.set_required("seasons", False)
- if not self.creating:
- # nb. must explcitly declare value for non-standard field
- f.set_default("seasons", plant.seasons)
+ if self.creating or self.editing:
+ f.remove("season") # TODO: add support for this
def objectify(self, form):
+ model = self.app.model
+ session = self.Session()
plant = super().objectify(form)
data = form.validated
- self.set_plant_types(plant, data["plant_types"])
- self.set_seasons(plant, data["seasons"])
-
- return plant
-
- def set_plant_types(self, plant, desired):
- model = self.app.model
- session = self.Session()
current = [pt.uuid for pt in plant.plant_types]
+ desired = data["plant_types"]
for uuid in desired:
if uuid not in current:
@@ -448,22 +292,7 @@ class PlantAssetView(AssetMasterView):
assert plant_type
plant.plant_types.remove(plant_type)
- def set_seasons(self, plant, desired):
- model = self.app.model
- session = self.Session()
- current = [s.uuid for s in plant.seasons]
-
- for uuid in desired:
- if uuid not in current:
- season = session.get(model.Season, uuid)
- assert season
- plant.seasons.append(season)
-
- for uuid in current:
- if uuid not in desired:
- season = session.get(model.Season, uuid)
- assert season
- plant.seasons.remove(season)
+ return plant
def defaults(config, **kwargs):
@@ -472,9 +301,6 @@ def defaults(config, **kwargs):
PlantTypeView = kwargs.get("PlantTypeView", base["PlantTypeView"])
PlantTypeView.defaults(config)
- SeasonView = kwargs.get("SeasonView", base["SeasonView"])
- SeasonView.defaults(config)
-
PlantAssetView = kwargs.get("PlantAssetView", base["PlantAssetView"])
PlantAssetView.defaults(config)
diff --git a/src/wuttafarm/web/views/quantities.py b/src/wuttafarm/web/views/quantities.py
index 9a91941..d4112cf 100644
--- a/src/wuttafarm/web/views/quantities.py
+++ b/src/wuttafarm/web/views/quantities.py
@@ -25,19 +25,11 @@ Master view for Quantities
from collections import OrderedDict
-from webhelpers2.html import tags
-
from wuttaweb.db import Session
from wuttafarm.web.views import WuttaFarmMasterView
-from wuttafarm.db.model import (
- QuantityType,
- Quantity,
- StandardQuantity,
- MaterialQuantity,
-)
-from wuttafarm.web.forms.schema import UnitRef, LogRef, MaterialTypeRefs
-from wuttafarm.util import get_log_type_enum
+from wuttafarm.db.model import QuantityType, Quantity, StandardQuantity
+from wuttafarm.web.forms.schema import UnitRef, LogRef
def get_quantity_type_enum(config):
@@ -108,28 +100,17 @@ class QuantityMasterView(WuttaFarmMasterView):
Base class for Quantity master views
"""
- farmos_entity_type = "quantity"
-
- labels = {
- "log_id": "Log ID",
- }
-
grid_columns = [
"drupal_id",
- "log_id",
- "log_status",
- "log_timestamp",
- "log_type",
- "log_name",
- "log_assets",
+ "as_text",
+ "quantity_type",
"measure",
"value",
"units",
"label",
- "quantity_type",
]
- sort_defaults = ("log_timestamp", "desc")
+ sort_defaults = ("drupal_id", "desc")
form_fields = [
"quantity_type",
@@ -148,15 +129,10 @@ class QuantityMasterView(WuttaFarmMasterView):
model = self.app.model
model_class = self.get_model_class()
session = session or self.Session()
-
query = session.query(model_class)
if model_class is not model.Quantity:
query = query.join(model.Quantity)
-
query = query.join(model.Measure).join(model.Unit)
-
- query = query.outerjoin(model.LogQuantity).outerjoin(model.Log)
-
return query
def configure_grid(self, grid):
@@ -164,39 +140,14 @@ class QuantityMasterView(WuttaFarmMasterView):
super().configure_grid(g)
model = self.app.model
model_class = self.get_model_class()
- session = self.Session()
# drupal_id
g.set_label("drupal_id", "ID", column_only=True)
g.set_sorter("drupal_id", model.Quantity.drupal_id)
- # log_id
- g.set_renderer("log_id", self.render_log_id_for_grid)
- g.set_sorter("log_id", model.Log.drupal_id)
-
- # log_status
- g.set_renderer("log_status", self.render_log_status_for_grid)
- g.set_sorter("log_status", model.Log.status)
-
- # log_timestamp
- g.set_renderer("log_timestamp", self.render_log_timestamp_for_grid)
- g.set_sorter("log_timestamp", model.Log.timestamp)
-
- # log_type
- self.log_type_enum = get_log_type_enum(self.config, session)
- g.set_renderer("log_type", self.render_log_type_for_grid)
- g.set_sorter("log_type", model.Log.log_type)
-
- # log_name
- g.set_renderer("log_name", self.render_log_name_for_grid)
- g.set_sorter("log_name", model.Log.message)
- if not self.farmos_style_grid_links:
- g.set_link("log_name")
-
- # log_assets
- g.set_renderer("log_assets", self.render_log_assets_for_grid)
- if not self.farmos_style_grid_links:
- g.set_link("log_assets")
+ # as_text
+ g.set_renderer("as_text", self.render_as_text_for_grid)
+ g.set_link("as_text")
# quantity_type
if model_class is not model.Quantity:
@@ -226,47 +177,8 @@ class QuantityMasterView(WuttaFarmMasterView):
g.add_action("view", icon="eye", url=quantity_url)
- def render_log_id_for_grid(self, quantity, field, value):
- if log := quantity.log:
- return log.drupal_id
- return None
-
- def render_log_status_for_grid(self, quantity, field, value):
- enum = self.app.enum
- if log := quantity.log:
- return enum.LOG_STATUS.get(log.status, log.status)
- return None
-
- def render_log_timestamp_for_grid(self, quantity, field, value):
- if log := quantity.log:
- return self.app.render_date(log.timestamp)
- return None
-
- def render_log_type_for_grid(self, quantity, field, value):
- if log := quantity.log:
- return self.log_type_enum.get(log.log_type, log.log_type)
- return None
-
- def render_log_name_for_grid(self, quantity, field, value):
- if log := quantity.log:
- if self.farmos_style_grid_links:
- url = self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid)
- return tags.link_to(log.message, url)
- return log.message
- return None
-
- def render_log_assets_for_grid(self, quantity, field, value):
- if log := quantity.log:
- if self.farmos_style_grid_links:
- links = []
- for asset in log.assets:
- url = self.request.route_url(
- f"{asset.asset_type}_assets.view", uuid=asset.uuid
- )
- links.append(tags.link_to(str(asset), url))
- return ", ".join(links)
- return [str(a) for a in log.assets]
- return None
+ def render_as_text_for_grid(self, quantity, field, value):
+ return quantity.render_as_text(self.config)
def render_value_for_grid(self, quantity, field, value):
value = quantity.value_numerator / quantity.value_denominator
@@ -359,8 +271,6 @@ class AllQuantityView(QuantityMasterView):
deletable = False
model_is_versioned = False
- farmos_refurl_path = "/log-quantities"
-
class StandardQuantityView(QuantityMasterView):
"""
@@ -371,77 +281,6 @@ class StandardQuantityView(QuantityMasterView):
route_prefix = "quantities_standard"
url_prefix = "/quantities/standard"
- farmos_bundle = "standard"
- farmos_refurl_path = "/log-quantities/standard"
-
-
-class MaterialQuantityView(QuantityMasterView):
- """
- Master view for Material Quantities
- """
-
- model_class = MaterialQuantity
- route_prefix = "quantities_material"
- url_prefix = "/quantities/material"
-
- farmos_bundle = "material"
- farmos_refurl_path = "/log-quantities/material"
-
- def configure_grid(self, grid):
- g = grid
- super().configure_grid(g)
-
- # material_types
- g.columns.append("material_types")
- g.set_label("material_types", "Material Type", column_only=True)
- g.set_renderer("material_types", self.render_material_types_for_grid)
-
- def render_material_types_for_grid(self, quantity, field, value):
- if self.farmos_style_grid_links:
- links = []
- for mtype in quantity.material_types:
- url = self.request.route_url("material_types.view", uuid=mtype.uuid)
- links.append(tags.link_to(str(mtype), url))
- return ", ".join(links)
-
- return ", ".join([str(mtype) for mtype in quantity.material_types])
-
- def configure_form(self, form):
- f = form
- super().configure_form(f)
- quantity = form.model_instance
-
- # material_types
- f.fields.insert_after("quantity_type", "material_types")
- f.set_node("material_types", MaterialTypeRefs(self.request))
- if not self.creating:
- f.set_default("material_types", quantity.material_types)
-
- def objectify(self, form):
- quantity = super().objectify(form)
- data = form.validated
-
- self.set_material_types(quantity, data["material_types"])
-
- return quantity
-
- def set_material_types(self, quantity, desired):
- model = self.app.model
- session = self.Session()
-
- current = {mt.uuid.hex: mt for mt in quantity.material_types}
-
- for mtype in desired:
- if mtype["uuid"] not in current:
- mtype = session.get(model.MaterialType, mtype["uuid"])
- assert mtype
- quantity.material_types.append(mtype)
-
- desired = [mtype["uuid"] for mtype in desired]
- for uuid, mtype in current.items():
- if uuid not in desired:
- quantity.material_types.remove(mtype)
-
def defaults(config, **kwargs):
base = globals()
@@ -457,11 +296,6 @@ def defaults(config, **kwargs):
)
StandardQuantityView.defaults(config)
- MaterialQuantityView = kwargs.get(
- "MaterialQuantityView", base["MaterialQuantityView"]
- )
- MaterialQuantityView.defaults(config)
-
def includeme(config):
defaults(config)
diff --git a/src/wuttafarm/web/views/units.py b/src/wuttafarm/web/views/units.py
index 13cff36..fe8dafe 100644
--- a/src/wuttafarm/web/views/units.py
+++ b/src/wuttafarm/web/views/units.py
@@ -37,19 +37,17 @@ class MeasureView(WuttaFarmMasterView):
url_prefix = "/measures"
grid_columns = [
- "ordinal",
"name",
"drupal_id",
]
- sort_defaults = "ordinal"
+ sort_defaults = "name"
filter_defaults = {
"name": {"active": True, "verb": "contains"},
}
form_fields = [
- "ordinal",
"name",
"drupal_id",
]
diff --git a/src/wuttafarm/web/views/water.py b/src/wuttafarm/web/views/water.py
deleted file mode 100644
index c0d551e..0000000
--- a/src/wuttafarm/web/views/water.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Master view for Water Assets
-"""
-
-from wuttafarm.db.model import WaterAsset
-from wuttafarm.web.views.assets import AssetMasterView
-
-
-class WaterAssetView(AssetMasterView):
- """
- Master view for Plant Assets
- """
-
- model_class = WaterAsset
- route_prefix = "water_assets"
- url_prefix = "/assets/water"
-
- farmos_bundle = "water"
- farmos_refurl_path = "/assets/water"
-
- grid_columns = [
- "thumbnail",
- "drupal_id",
- "asset_name",
- "parents",
- "archived",
- ]
-
-
-def defaults(config, **kwargs):
- base = globals()
-
- WaterAssetView = kwargs.get("WaterAssetView", base["WaterAssetView"])
- WaterAssetView.defaults(config)
-
-
-def includeme(config):
- defaults(config)