diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf9487..d096239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +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.5.0 (2026-02-18) - -### Feat - -- add `produces_eggs` flag for animal, group assets -- add more assets (plant) and logs (harvest, medical, observation) -- refactor log models, views to use generic/common base - -### Fix - -- rename db model modules, for better convention -- add override for requests cert validation - ## v0.4.1 (2026-02-17) ### Fix diff --git a/pyproject.toml b/pyproject.toml index a474302..44bea43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "WuttaFarm" -version = "0.5.0" +version = "0.4.1" description = "Web app to integrate with and extend farmOS" readme = "README.md" authors = [ diff --git a/src/wuttafarm/db/alembic/versions/11e0e46f48a6_add_plant_assets_and_more_logs.py b/src/wuttafarm/db/alembic/versions/11e0e46f48a6_add_plant_assets_and_more_logs.py deleted file mode 100644 index 7df55c4..0000000 --- a/src/wuttafarm/db/alembic/versions/11e0e46f48a6_add_plant_assets_and_more_logs.py +++ /dev/null @@ -1,596 +0,0 @@ -"""add Plant Assets and more Logs - -Revision ID: 11e0e46f48a6 -Revises: dd6351e69233 -Create Date: 2026-02-18 18:11:46.536930 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import wuttjamaican.db.util - - -# revision identifiers, used by Alembic. -revision: str = "11e0e46f48a6" -down_revision: Union[str, None] = "dd6351e69233" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - - # plant_type - op.create_table( - "plant_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_plant_type")), - sa.UniqueConstraint("drupal_id", name=op.f("uq_plant_type_drupal_id")), - sa.UniqueConstraint("farmos_uuid", name=op.f("uq_plant_type_farmos_uuid")), - sa.UniqueConstraint("name", name=op.f("uq_plant_type_name")), - ) - op.create_table( - "plant_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_plant_type_version") - ), - ) - op.create_index( - op.f("ix_plant_type_version_end_transaction_id"), - "plant_type_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_plant_type_version_operation_type"), - "plant_type_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_plant_type_version_pk_transaction_id", - "plant_type_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_plant_type_version_pk_validity", - "plant_type_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_plant_type_version_transaction_id"), - "plant_type_version", - ["transaction_id"], - unique=False, - ) - - # asset_plant - op.create_table( - "asset_plant", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["uuid"], ["asset.uuid"], name=op.f("fk_asset_plant_uuid_asset") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_plant")), - ) - op.create_table( - "asset_plant_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_plant_version") - ), - ) - op.create_index( - op.f("ix_asset_plant_version_end_transaction_id"), - "asset_plant_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_asset_plant_version_operation_type"), - "asset_plant_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_asset_plant_version_pk_transaction_id", - "asset_plant_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_asset_plant_version_pk_validity", - "asset_plant_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_asset_plant_version_transaction_id"), - "asset_plant_version", - ["transaction_id"], - unique=False, - ) - - # asset_plant_plant_type - op.create_table( - "asset_plant_plant_type", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.Column("plant_asset_uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.Column("plant_type_uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["plant_asset_uuid"], - ["asset_plant.uuid"], - name=op.f("fk_asset_plant_plant_type_plant_asset_uuid_asset_plant"), - ), - sa.ForeignKeyConstraint( - ["plant_type_uuid"], - ["plant_type.uuid"], - name=op.f("fk_asset_plant_plant_type_plant_type_uuid_plant_type"), - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_asset_plant_plant_type")), - ) - op.create_table( - "asset_plant_plant_type_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( - "plant_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_plant_plant_type_version") - ), - ) - op.create_index( - op.f("ix_asset_plant_plant_type_version_end_transaction_id"), - "asset_plant_plant_type_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_asset_plant_plant_type_version_operation_type"), - "asset_plant_plant_type_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_asset_plant_plant_type_version_pk_transaction_id", - "asset_plant_plant_type_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_asset_plant_plant_type_version_pk_validity", - "asset_plant_plant_type_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_asset_plant_plant_type_version_transaction_id"), - "asset_plant_plant_type_version", - ["transaction_id"], - unique=False, - ) - - # log_asset - op.create_table( - "log_asset", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.Column("log_uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.Column("asset_uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["asset_uuid"], ["asset.uuid"], name=op.f("fk_log_asset_asset_uuid_asset") - ), - sa.ForeignKeyConstraint( - ["log_uuid"], ["log.uuid"], name=op.f("fk_log_asset_log_uuid_log") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_asset")), - ) - op.create_table( - "log_asset_version", - sa.Column( - "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False - ), - sa.Column( - "log_uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=True - ), - sa.Column( - "asset_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_log_asset_version") - ), - ) - op.create_index( - op.f("ix_log_asset_version_end_transaction_id"), - "log_asset_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_asset_version_operation_type"), - "log_asset_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_log_asset_version_pk_transaction_id", - "log_asset_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_log_asset_version_pk_validity", - "log_asset_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_asset_version_transaction_id"), - "log_asset_version", - ["transaction_id"], - unique=False, - ) - - # log_harvest - op.create_table( - "log_harvest", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["uuid"], ["log.uuid"], name=op.f("fk_log_harvest_uuid_log") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_harvest")), - ) - op.create_table( - "log_harvest_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_log_harvest_version") - ), - ) - op.create_index( - op.f("ix_log_harvest_version_end_transaction_id"), - "log_harvest_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_harvest_version_operation_type"), - "log_harvest_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_log_harvest_version_pk_transaction_id", - "log_harvest_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_log_harvest_version_pk_validity", - "log_harvest_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_harvest_version_transaction_id"), - "log_harvest_version", - ["transaction_id"], - unique=False, - ) - - # log_medical - op.create_table( - "log_medical", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["uuid"], ["log.uuid"], name=op.f("fk_log_medical_uuid_log") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_medical")), - ) - op.create_table( - "log_medical_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_log_medical_version") - ), - ) - op.create_index( - op.f("ix_log_medical_version_end_transaction_id"), - "log_medical_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_medical_version_operation_type"), - "log_medical_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_log_medical_version_pk_transaction_id", - "log_medical_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_log_medical_version_pk_validity", - "log_medical_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_medical_version_transaction_id"), - "log_medical_version", - ["transaction_id"], - unique=False, - ) - - # log_observation - op.create_table( - "log_observation", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.ForeignKeyConstraint( - ["uuid"], ["log.uuid"], name=op.f("fk_log_observation_uuid_log") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_observation")), - ) - op.create_table( - "log_observation_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_log_observation_version") - ), - ) - op.create_index( - op.f("ix_log_observation_version_end_transaction_id"), - "log_observation_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_observation_version_operation_type"), - "log_observation_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_log_observation_version_pk_transaction_id", - "log_observation_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_log_observation_version_pk_validity", - "log_observation_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_observation_version_transaction_id"), - "log_observation_version", - ["transaction_id"], - unique=False, - ) - - -def downgrade() -> None: - - # log_observation - op.drop_index( - op.f("ix_log_observation_version_transaction_id"), - table_name="log_observation_version", - ) - op.drop_index( - "ix_log_observation_version_pk_validity", table_name="log_observation_version" - ) - op.drop_index( - "ix_log_observation_version_pk_transaction_id", - table_name="log_observation_version", - ) - op.drop_index( - op.f("ix_log_observation_version_operation_type"), - table_name="log_observation_version", - ) - op.drop_index( - op.f("ix_log_observation_version_end_transaction_id"), - table_name="log_observation_version", - ) - op.drop_table("log_observation_version") - op.drop_table("log_observation") - - # log_medical - op.drop_index( - op.f("ix_log_medical_version_transaction_id"), table_name="log_medical_version" - ) - op.drop_index( - "ix_log_medical_version_pk_validity", table_name="log_medical_version" - ) - op.drop_index( - "ix_log_medical_version_pk_transaction_id", table_name="log_medical_version" - ) - op.drop_index( - op.f("ix_log_medical_version_operation_type"), table_name="log_medical_version" - ) - op.drop_index( - op.f("ix_log_medical_version_end_transaction_id"), - table_name="log_medical_version", - ) - op.drop_table("log_medical_version") - op.drop_table("log_medical") - - # log_harvest - op.drop_index( - op.f("ix_log_harvest_version_transaction_id"), table_name="log_harvest_version" - ) - op.drop_index( - "ix_log_harvest_version_pk_validity", table_name="log_harvest_version" - ) - op.drop_index( - "ix_log_harvest_version_pk_transaction_id", table_name="log_harvest_version" - ) - op.drop_index( - op.f("ix_log_harvest_version_operation_type"), table_name="log_harvest_version" - ) - op.drop_index( - op.f("ix_log_harvest_version_end_transaction_id"), - table_name="log_harvest_version", - ) - op.drop_table("log_harvest_version") - op.drop_table("log_harvest") - - # log_asset - op.drop_index( - op.f("ix_log_asset_version_transaction_id"), table_name="log_asset_version" - ) - op.drop_index("ix_log_asset_version_pk_validity", table_name="log_asset_version") - op.drop_index( - "ix_log_asset_version_pk_transaction_id", table_name="log_asset_version" - ) - op.drop_index( - op.f("ix_log_asset_version_operation_type"), table_name="log_asset_version" - ) - op.drop_index( - op.f("ix_log_asset_version_end_transaction_id"), table_name="log_asset_version" - ) - op.drop_table("log_asset_version") - op.drop_table("log_asset") - - # asset_plant_plant_type - op.drop_index( - op.f("ix_asset_plant_plant_type_version_transaction_id"), - table_name="asset_plant_plant_type_version", - ) - op.drop_index( - "ix_asset_plant_plant_type_version_pk_validity", - table_name="asset_plant_plant_type_version", - ) - op.drop_index( - "ix_asset_plant_plant_type_version_pk_transaction_id", - table_name="asset_plant_plant_type_version", - ) - op.drop_index( - op.f("ix_asset_plant_plant_type_version_operation_type"), - table_name="asset_plant_plant_type_version", - ) - op.drop_index( - op.f("ix_asset_plant_plant_type_version_end_transaction_id"), - table_name="asset_plant_plant_type_version", - ) - op.drop_table("asset_plant_plant_type_version") - op.drop_table("asset_plant_plant_type") - - # asset_plant - op.drop_index( - op.f("ix_asset_plant_version_transaction_id"), table_name="asset_plant_version" - ) - op.drop_index( - "ix_asset_plant_version_pk_validity", table_name="asset_plant_version" - ) - op.drop_index( - "ix_asset_plant_version_pk_transaction_id", table_name="asset_plant_version" - ) - op.drop_index( - op.f("ix_asset_plant_version_operation_type"), table_name="asset_plant_version" - ) - op.drop_index( - op.f("ix_asset_plant_version_end_transaction_id"), - table_name="asset_plant_version", - ) - op.drop_table("asset_plant_version") - op.drop_table("asset_plant") - - # plant_type - op.drop_index( - op.f("ix_plant_type_version_transaction_id"), table_name="plant_type_version" - ) - op.drop_index("ix_plant_type_version_pk_validity", table_name="plant_type_version") - op.drop_index( - "ix_plant_type_version_pk_transaction_id", table_name="plant_type_version" - ) - op.drop_index( - op.f("ix_plant_type_version_operation_type"), table_name="plant_type_version" - ) - op.drop_index( - op.f("ix_plant_type_version_end_transaction_id"), - table_name="plant_type_version", - ) - op.drop_table("plant_type_version") - op.drop_table("plant_type") diff --git a/src/wuttafarm/db/alembic/versions/82a03f4ef1a4_add_produces_eggs_via_eggmixin.py b/src/wuttafarm/db/alembic/versions/82a03f4ef1a4_add_produces_eggs_via_eggmixin.py deleted file mode 100644 index 9bed92c..0000000 --- a/src/wuttafarm/db/alembic/versions/82a03f4ef1a4_add_produces_eggs_via_eggmixin.py +++ /dev/null @@ -1,52 +0,0 @@ -"""add produces_eggs via EggMixin - -Revision ID: 82a03f4ef1a4 -Revises: 11e0e46f48a6 -Create Date: 2026-02-18 18:45:36.015144 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import wuttjamaican.db.util - - -# revision identifiers, used by Alembic. -revision: str = "82a03f4ef1a4" -down_revision: Union[str, None] = "11e0e46f48a6" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - - # asset_animal - op.add_column( - "asset_animal", sa.Column("produces_eggs", sa.Boolean(), nullable=True) - ) - op.add_column( - "asset_animal_version", - sa.Column("produces_eggs", sa.Boolean(), autoincrement=False, nullable=True), - ) - - # asset_group - op.add_column( - "asset_group", sa.Column("produces_eggs", sa.Boolean(), nullable=True) - ) - op.add_column( - "asset_group_version", - sa.Column("produces_eggs", sa.Boolean(), autoincrement=False, nullable=True), - ) - - -def downgrade() -> None: - - # asset_group - op.drop_column("asset_group_version", "produces_eggs") - op.drop_column("asset_group", "produces_eggs") - - # asset_animal - op.drop_column("asset_animal_version", "produces_eggs") - op.drop_column("asset_animal", "produces_eggs") diff --git a/src/wuttafarm/db/alembic/versions/dd6351e69233_add_generic_log_base.py b/src/wuttafarm/db/alembic/versions/dd6351e69233_add_generic_log_base.py deleted file mode 100644 index 0b82da9..0000000 --- a/src/wuttafarm/db/alembic/versions/dd6351e69233_add_generic_log_base.py +++ /dev/null @@ -1,206 +0,0 @@ -"""add generic log base - -Revision ID: dd6351e69233 -Revises: b8cd4a8f981f -Create Date: 2026-02-18 12:09:05.200134 - -""" - -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import wuttjamaican.db.util -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision: str = "dd6351e69233" -down_revision: Union[str, None] = "b8cd4a8f981f" -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - - # log - op.create_table( - "log", - sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False), - sa.Column("log_type", sa.String(length=100), nullable=False), - sa.Column("message", sa.String(length=255), nullable=False), - sa.Column("timestamp", sa.DateTime(), nullable=False), - sa.Column("status", sa.String(length=20), nullable=False), - sa.Column("notes", sa.Text(), nullable=True), - sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True), - sa.Column("drupal_id", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["log_type"], ["log_type.drupal_id"], name=op.f("fk_log_log_type_log_type") - ), - sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log")), - sa.UniqueConstraint("drupal_id", name=op.f("uq_log_drupal_id")), - sa.UniqueConstraint("farmos_uuid", name=op.f("uq_log_farmos_uuid")), - ) - op.create_table( - "log_version", - sa.Column( - "uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False - ), - sa.Column( - "log_type", sa.String(length=100), autoincrement=False, nullable=True - ), - sa.Column("message", sa.String(length=255), autoincrement=False, nullable=True), - sa.Column("timestamp", sa.DateTime(), autoincrement=False, nullable=True), - sa.Column("status", sa.String(length=20), autoincrement=False, nullable=True), - sa.Column("notes", sa.Text(), 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_log_version")), - ) - op.create_index( - op.f("ix_log_version_end_transaction_id"), - "log_version", - ["end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_version_operation_type"), - "log_version", - ["operation_type"], - unique=False, - ) - op.create_index( - "ix_log_version_pk_transaction_id", - "log_version", - ["uuid", sa.literal_column("transaction_id DESC")], - unique=False, - ) - op.create_index( - "ix_log_version_pk_validity", - "log_version", - ["uuid", "transaction_id", "end_transaction_id"], - unique=False, - ) - op.create_index( - op.f("ix_log_version_transaction_id"), - "log_version", - ["transaction_id"], - unique=False, - ) - - # log_activity - op.drop_column("log_activity_version", "status") - op.drop_column("log_activity_version", "farmos_uuid") - op.drop_column("log_activity_version", "timestamp") - op.drop_column("log_activity_version", "message") - op.drop_column("log_activity_version", "drupal_id") - op.drop_column("log_activity_version", "notes") - op.drop_constraint( - op.f("uq_log_activity_drupal_id"), "log_activity", type_="unique" - ) - op.drop_constraint( - op.f("uq_log_activity_farmos_uuid"), "log_activity", type_="unique" - ) - op.create_foreign_key( - op.f("fk_log_activity_uuid_log"), "log_activity", "log", ["uuid"], ["uuid"] - ) - op.drop_column("log_activity", "status") - op.drop_column("log_activity", "farmos_uuid") - op.drop_column("log_activity", "timestamp") - op.drop_column("log_activity", "message") - op.drop_column("log_activity", "drupal_id") - op.drop_column("log_activity", "notes") - - -def downgrade() -> None: - - # log_activity - op.add_column( - "log_activity", - sa.Column("notes", sa.TEXT(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity", - sa.Column("drupal_id", sa.INTEGER(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity", - sa.Column( - "message", sa.VARCHAR(length=255), autoincrement=False, nullable=False - ), - ) - op.add_column( - "log_activity", - sa.Column( - "timestamp", postgresql.TIMESTAMP(), autoincrement=False, nullable=False - ), - ) - op.add_column( - "log_activity", - sa.Column("farmos_uuid", sa.UUID(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity", - sa.Column("status", sa.VARCHAR(length=20), autoincrement=False, nullable=False), - ) - op.drop_constraint( - op.f("fk_log_activity_uuid_log"), "log_activity", type_="foreignkey" - ) - op.create_unique_constraint( - op.f("uq_log_activity_farmos_uuid"), - "log_activity", - ["farmos_uuid"], - postgresql_nulls_not_distinct=False, - ) - op.create_unique_constraint( - op.f("uq_log_activity_drupal_id"), - "log_activity", - ["drupal_id"], - postgresql_nulls_not_distinct=False, - ) - op.add_column( - "log_activity_version", - sa.Column("notes", sa.TEXT(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity_version", - sa.Column("drupal_id", sa.INTEGER(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity_version", - sa.Column( - "message", sa.VARCHAR(length=255), autoincrement=False, nullable=True - ), - ) - op.add_column( - "log_activity_version", - sa.Column( - "timestamp", postgresql.TIMESTAMP(), autoincrement=False, nullable=True - ), - ) - op.add_column( - "log_activity_version", - sa.Column("farmos_uuid", sa.UUID(), autoincrement=False, nullable=True), - ) - op.add_column( - "log_activity_version", - sa.Column("status", sa.VARCHAR(length=20), autoincrement=False, nullable=True), - ) - - # log - op.drop_index(op.f("ix_log_version_transaction_id"), table_name="log_version") - op.drop_index("ix_log_version_pk_validity", table_name="log_version") - op.drop_index("ix_log_version_pk_transaction_id", table_name="log_version") - op.drop_index(op.f("ix_log_version_operation_type"), table_name="log_version") - op.drop_index(op.f("ix_log_version_end_transaction_id"), table_name="log_version") - op.drop_table("log_version") - op.drop_table("log") diff --git a/src/wuttafarm/db/model/__init__.py b/src/wuttafarm/db/model/__init__.py index f9eb790..367bc1c 100644 --- a/src/wuttafarm/db/model/__init__.py +++ b/src/wuttafarm/db/model/__init__.py @@ -30,14 +30,9 @@ from wuttjamaican.db.model import * from .users import WuttaFarmUser # wuttafarm proper models -from .asset import AssetType, Asset, AssetParent -from .asset_land import LandType, LandAsset -from .asset_structure import StructureType, StructureAsset -from .asset_animal import AnimalType, AnimalAsset -from .asset_group import GroupAsset -from .asset_plant import PlantType, PlantAsset, PlantAssetPlantType -from .log import LogType, Log, LogAsset -from .log_activity import ActivityLog -from .log_harvest import HarvestLog -from .log_medical import MedicalLog -from .log_observation import ObservationLog +from .assets import AssetType, Asset, AssetParent +from .land import LandType, LandAsset +from .structures import StructureType, StructureAsset +from .animals import AnimalType, AnimalAsset +from .groups import GroupAsset +from .logs import LogType, ActivityLog diff --git a/src/wuttafarm/db/model/asset_animal.py b/src/wuttafarm/db/model/animals.py similarity index 96% rename from src/wuttafarm/db/model/asset_animal.py rename to src/wuttafarm/db/model/animals.py index 768b0f9..8c0df35 100644 --- a/src/wuttafarm/db/model/asset_animal.py +++ b/src/wuttafarm/db/model/animals.py @@ -28,7 +28,7 @@ from sqlalchemy import orm from wuttjamaican.db import model -from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies, EggMixin +from wuttafarm.db.model.assets import AssetMixin, add_asset_proxies class AnimalType(model.Base): @@ -84,7 +84,7 @@ class AnimalType(model.Base): return self.name or "" -class AnimalAsset(AssetMixin, EggMixin, model.Base): +class AnimalAsset(AssetMixin, model.Base): """ Represents an animal asset from farmOS """ diff --git a/src/wuttafarm/db/model/asset_plant.py b/src/wuttafarm/db/model/asset_plant.py deleted file mode 100644 index 5f10e7c..0000000 --- a/src/wuttafarm/db/model/asset_plant.py +++ /dev/null @@ -1,132 +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 Plant Assets -""" - -import sqlalchemy as sa -from sqlalchemy import orm - -from wuttjamaican.db import model - -from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies - - -class PlantType(model.Base): - """ - Represents a "plant type" (taxonomy term) from farmOS - """ - - __tablename__ = "plant_type" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Plant Type", - "model_title_plural": "Plant Types", - } - - uuid = model.uuid_column() - - name = sa.Column( - sa.String(length=100), - nullable=False, - unique=True, - doc=""" - Name of the plant type. - """, - ) - - description = sa.Column( - sa.String(length=255), - nullable=True, - doc=""" - Optional description for the plant type. - """, - ) - - farmos_uuid = sa.Column( - model.UUID(), - nullable=True, - unique=True, - doc=""" - UUID for the plant type within farmOS. - """, - ) - - drupal_id = sa.Column( - sa.Integer(), - nullable=True, - unique=True, - doc=""" - Drupal internal ID for the plant type. - """, - ) - - def __str__(self): - return self.name or "" - - -class PlantAsset(AssetMixin, model.Base): - """ - Represents a plant asset from farmOS - """ - - __tablename__ = "asset_plant" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Plant Asset", - "model_title_plural": "Plant Assets", - "farmos_asset_type": "plant", - } - - _plant_types = orm.relationship( - "PlantAssetPlantType", - back_populates="plant_asset", - ) - - -add_asset_proxies(PlantAsset) - - -class PlantAssetPlantType(model.Base): - """ - Associates one or more plant types with a plant asset. - """ - - __tablename__ = "asset_plant_plant_type" - __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="_plant_types", - ) - - plant_type_uuid = model.uuid_fk_column("plant_type.uuid", nullable=False) - plant_type = orm.relationship( - PlantType, - doc=""" - Reference to the plant type. - """, - ) diff --git a/src/wuttafarm/db/model/asset.py b/src/wuttafarm/db/model/assets.py similarity index 95% rename from src/wuttafarm/db/model/asset.py rename to src/wuttafarm/db/model/assets.py index 90372e2..531fd62 100644 --- a/src/wuttafarm/db/model/asset.py +++ b/src/wuttafarm/db/model/assets.py @@ -215,18 +215,6 @@ def add_asset_proxies(subclass): Asset.make_proxy(subclass, "asset", "archived") -class EggMixin: - - produces_eggs = sa.Column( - sa.Boolean(), - nullable=True, - doc=""" - Whether the group asset produces eggs (i.e. it should be - available in the egg harvest form). - """, - ) - - class AssetParent(model.Base): """ Represents an "asset's parent relationship" from farmOS. diff --git a/src/wuttafarm/db/model/asset_group.py b/src/wuttafarm/db/model/groups.py similarity index 91% rename from src/wuttafarm/db/model/asset_group.py rename to src/wuttafarm/db/model/groups.py index ad4d184..84453a7 100644 --- a/src/wuttafarm/db/model/asset_group.py +++ b/src/wuttafarm/db/model/groups.py @@ -25,10 +25,10 @@ Model definition for Groups from wuttjamaican.db import model -from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies, EggMixin +from wuttafarm.db.model.assets import AssetMixin, add_asset_proxies -class GroupAsset(AssetMixin, EggMixin, model.Base): +class GroupAsset(AssetMixin, model.Base): """ Represents a group asset from farmOS """ diff --git a/src/wuttafarm/db/model/asset_land.py b/src/wuttafarm/db/model/land.py similarity index 97% rename from src/wuttafarm/db/model/asset_land.py rename to src/wuttafarm/db/model/land.py index bbd7bf0..1221c63 100644 --- a/src/wuttafarm/db/model/asset_land.py +++ b/src/wuttafarm/db/model/land.py @@ -28,7 +28,7 @@ from sqlalchemy import orm from wuttjamaican.db import model -from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies +from wuttafarm.db.model.assets import AssetMixin, add_asset_proxies class LandType(model.Base): diff --git a/src/wuttafarm/db/model/log_activity.py b/src/wuttafarm/db/model/log_activity.py deleted file mode 100644 index 2f5f6e5..0000000 --- a/src/wuttafarm/db/model/log_activity.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 Activity Logs -""" - -from wuttjamaican.db import model - -from wuttafarm.db.model.log import LogMixin, add_log_proxies - - -class ActivityLog(LogMixin, model.Base): - """ - Represents an Activity Log from farmOS - """ - - __tablename__ = "log_activity" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Activity Log", - "model_title_plural": "Activity Logs", - "farmos_log_type": "activity", - } - - -add_log_proxies(ActivityLog) diff --git a/src/wuttafarm/db/model/log_harvest.py b/src/wuttafarm/db/model/log_harvest.py deleted file mode 100644 index 35c3105..0000000 --- a/src/wuttafarm/db/model/log_harvest.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 Harvest Logs -""" - -from wuttjamaican.db import model - -from wuttafarm.db.model.log import LogMixin, add_log_proxies - - -class HarvestLog(LogMixin, model.Base): - """ - Represents a Harvest Log from farmOS - """ - - __tablename__ = "log_harvest" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Harvest Log", - "model_title_plural": "Harvest Logs", - "farmos_log_type": "harvest", - } - - -add_log_proxies(HarvestLog) diff --git a/src/wuttafarm/db/model/log_medical.py b/src/wuttafarm/db/model/log_medical.py deleted file mode 100644 index 439ee3b..0000000 --- a/src/wuttafarm/db/model/log_medical.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 Medical Logs -""" - -from wuttjamaican.db import model - -from wuttafarm.db.model.log import LogMixin, add_log_proxies - - -class MedicalLog(LogMixin, model.Base): - """ - Represents a Medical Log from farmOS - """ - - __tablename__ = "log_medical" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Medical Log", - "model_title_plural": "Medical Logs", - "farmos_log_type": "medical", - } - - -add_log_proxies(MedicalLog) diff --git a/src/wuttafarm/db/model/log_observation.py b/src/wuttafarm/db/model/log_observation.py deleted file mode 100644 index ab89c3f..0000000 --- a/src/wuttafarm/db/model/log_observation.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 Observation Logs -""" - -from wuttjamaican.db import model - -from wuttafarm.db.model.log import LogMixin, add_log_proxies - - -class ObservationLog(LogMixin, model.Base): - """ - Represents a Observation Log from farmOS - """ - - __tablename__ = "log_observation" - __versioned__ = {} - __wutta_hint__ = { - "model_title": "Observation Log", - "model_title_plural": "Observation Logs", - "farmos_log_type": "observation", - } - - -add_log_proxies(ObservationLog) diff --git a/src/wuttafarm/db/model/log.py b/src/wuttafarm/db/model/logs.py similarity index 67% rename from src/wuttafarm/db/model/log.py rename to src/wuttafarm/db/model/logs.py index a86c447..76f7715 100644 --- a/src/wuttafarm/db/model/log.py +++ b/src/wuttafarm/db/model/logs.py @@ -20,12 +20,11 @@ # ################################################################################ """ -Model definition for Logs +Model definition for Log Types """ import sqlalchemy as sa from sqlalchemy import orm -from sqlalchemy.ext.declarative import declared_attr from wuttjamaican.db import model @@ -83,26 +82,20 @@ class LogType(model.Base): return self.name or "" -class Log(model.Base): +class ActivityLog(model.Base): """ - Represents a base log record from farmOS + Represents an activity log from farmOS """ - __tablename__ = "log" + __tablename__ = "log_activity" __versioned__ = {} __wutta_hint__ = { - "model_title": "Log", - "model_title_plural": "All Logs", + "model_title": "Activity Log", + "model_title_plural": "Activity Logs", } uuid = model.uuid_column() - log_type = sa.Column( - sa.String(length=100), - sa.ForeignKey("log_type.drupal_id"), - nullable=False, - ) - message = sa.Column( sa.String(length=255), nullable=False, @@ -153,53 +146,5 @@ class Log(model.Base): """, ) - _assets = orm.relationship("LogAsset", back_populates="log") - def __str__(self): return self.message or "" - - -class LogMixin: - - uuid = model.uuid_fk_column("log.uuid", nullable=False, primary_key=True) - - @declared_attr - def log(cls): - return orm.relationship(Log) - - def __str__(self): - return self.message or "" - - -def add_log_proxies(subclass): - Log.make_proxy(subclass, "log", "farmos_uuid") - Log.make_proxy(subclass, "log", "drupal_id") - Log.make_proxy(subclass, "log", "log_type") - Log.make_proxy(subclass, "log", "message") - Log.make_proxy(subclass, "log", "timestamp") - Log.make_proxy(subclass, "log", "status") - Log.make_proxy(subclass, "log", "notes") - - -class LogAsset(model.Base): - """ - Represents a "log's asset relationship" from farmOS. - """ - - __tablename__ = "log_asset" - __versioned__ = {} - - uuid = model.uuid_column() - - log_uuid = model.uuid_fk_column("log.uuid", nullable=False) - log = orm.relationship( - Log, - foreign_keys=log_uuid, - back_populates="_assets", - ) - - asset_uuid = model.uuid_fk_column("asset.uuid", nullable=False) - asset = orm.relationship( - "Asset", - foreign_keys=asset_uuid, - ) diff --git a/src/wuttafarm/db/model/asset_structure.py b/src/wuttafarm/db/model/structures.py similarity index 97% rename from src/wuttafarm/db/model/asset_structure.py rename to src/wuttafarm/db/model/structures.py index 7f4fc23..8c5371c 100644 --- a/src/wuttafarm/db/model/asset_structure.py +++ b/src/wuttafarm/db/model/structures.py @@ -28,7 +28,7 @@ from sqlalchemy import orm from wuttjamaican.db import model -from wuttafarm.db.model.asset import AssetMixin, add_asset_proxies +from wuttafarm.db.model.assets import AssetMixin, add_asset_proxies class StructureType(model.Base): diff --git a/src/wuttafarm/enum.py b/src/wuttafarm/enum.py index 03181b9..41bf597 100644 --- a/src/wuttafarm/enum.py +++ b/src/wuttafarm/enum.py @@ -34,12 +34,3 @@ ANIMAL_SEX = OrderedDict( ("F", "Female"), ] ) - - -LOG_STATUS = OrderedDict( - [ - ("pending", "Pending"), - ("done", "Done"), - ("abandoned", "Abandoned"), - ] -) diff --git a/src/wuttafarm/farmos/importing/model.py b/src/wuttafarm/farmos/importing/model.py index d20c068..6c3f5a0 100644 --- a/src/wuttafarm/farmos/importing/model.py +++ b/src/wuttafarm/farmos/importing/model.py @@ -125,7 +125,6 @@ class ToFarmOSAsset(ToFarmOS): "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"], } @@ -139,8 +138,6 @@ class ToFarmOSAsset(ToFarmOS): attrs["is_location"] = source_data["is_location"] if "is_fixed" in self.fields: attrs["is_fixed"] = source_data["is_fixed"] - if "produces_eggs" in self.fields: - attrs["produces_eggs"] = source_data["produces_eggs"] if "notes" in self.fields: attrs["notes"] = {"value": source_data["notes"]} if "archived" in self.fields: @@ -162,7 +159,6 @@ class AnimalAssetImporter(ToFarmOSAsset): "animal_type_uuid", "sex", "is_sterile", - "produces_eggs", "birthdate", "notes", "archived", @@ -290,7 +286,6 @@ class GroupAssetImporter(ToFarmOSAsset): supported_fields = [ "uuid", "asset_name", - "produces_eggs", "notes", "archived", ] @@ -368,113 +363,3 @@ class StructureAssetImporter(ToFarmOSAsset): payload["attributes"].update(attrs) return payload - - -############################## -# log importers -############################## - - -class ToFarmOSLog(ToFarmOS): - """ - Base class for log data importer targeting the farmOS API. - """ - - farmos_log_type = None - - supported_fields = [ - "uuid", - "name", - "notes", - ] - - def get_target_objects(self, **kwargs): - result = self.farmos_client.log.get(self.farmos_log_type) - return result["data"] - - def get_target_object(self, key): - - # fetch from cache, if applicable - if self.caches_target: - return super().get_target_object(key) - - # okay now must fetch via API - if self.get_keys() != ["uuid"]: - raise ValueError("must use uuid key for this to work") - uuid = key[0] - - try: - log = self.farmos_client.log.get_id(self.farmos_log_type, str(uuid)) - except requests.HTTPError as exc: - if exc.response.status_code == 404: - return None - raise - return log["data"] - - def create_target_object(self, key, source_data): - if source_data.get("__ignoreme__"): - return None - if self.dry_run: - return source_data - - payload = self.get_log_payload(source_data) - result = self.farmos_client.log.send(self.farmos_log_type, payload) - normal = self.normalize_target_object(result["data"]) - normal["_new_object"] = result["data"] - return normal - - def update_target_object(self, asset, source_data, target_data=None): - if self.dry_run: - return asset - - payload = self.get_log_payload(source_data) - payload["id"] = str(source_data["uuid"]) - result = self.farmos_client.log.send(self.farmos_log_type, payload) - return self.normalize_target_object(result["data"]) - - def normalize_target_object(self, log): - - if notes := log["attributes"]["notes"]: - notes = notes["value"] - - return { - "uuid": UUID(log["id"]), - "name": log["attributes"]["name"], - "notes": notes, - } - - def get_log_payload(self, source_data): - - attrs = {} - if "name" in self.fields: - attrs["name"] = source_data["name"] - if "notes" in self.fields: - attrs["notes"] = {"value": source_data["notes"]} - - payload = {"attributes": attrs} - - return payload - - -class ActivityLogImporter(ToFarmOSLog): - - model_title = "ActivityLog" - farmos_log_type = "activity" - - -class HarvestLogImporter(ToFarmOSLog): - - model_title = "HarvestLog" - farmos_log_type = "harvest" - - -class MedicalLogImporter(ToFarmOSLog): - - model_title = "MedicalLog" - farmos_log_type = "medical" - - -class ObservationLogImporter(ToFarmOSLog): - - model_title = "ObservationLog" - farmos_log_type = "observation" diff --git a/src/wuttafarm/farmos/importing/wuttafarm.py b/src/wuttafarm/farmos/importing/wuttafarm.py index ffd78b7..8ef8a77 100644 --- a/src/wuttafarm/farmos/importing/wuttafarm.py +++ b/src/wuttafarm/farmos/importing/wuttafarm.py @@ -98,10 +98,6 @@ class FromWuttaFarmToFarmOS(FromWuttaFarmHandler, ToFarmOSHandler): importers["AnimalType"] = AnimalTypeImporter importers["AnimalAsset"] = AnimalAssetImporter importers["GroupAsset"] = GroupAssetImporter - importers["ActivityLog"] = ActivityLogImporter - importers["HarvestLog"] = HarvestLogImporter - importers["MedicalLog"] = MedicalLogImporter - importers["ObservationLog"] = ObservationLogImporter return importers @@ -140,7 +136,6 @@ class AnimalAssetImporter(FromWuttaFarm, farmos_importing.model.AnimalAssetImpor "animal_type_uuid", "sex", "is_sterile", - "produces_eggs", "birthdate", "notes", "archived", @@ -153,7 +148,6 @@ class AnimalAssetImporter(FromWuttaFarm, farmos_importing.model.AnimalAssetImpor "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, @@ -193,7 +187,6 @@ class GroupAssetImporter(FromWuttaFarm, farmos_importing.model.GroupAssetImporte supported_fields = [ "uuid", "asset_name", - "produces_eggs", "notes", "archived", ] @@ -202,7 +195,6 @@ class GroupAssetImporter(FromWuttaFarm, farmos_importing.model.GroupAssetImporte 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, @@ -269,62 +261,3 @@ class StructureAssetImporter( "archived": structure.archived, "_src_object": structure, } - - -############################## -# log importers -############################## - - -class FromWuttaFarmLog(FromWuttaFarm): - """ - Base class for WuttaFarm -> farmOS log importers - """ - - supported_fields = [ - "uuid", - "name", - "notes", - ] - - def normalize_source_object(self, log): - return { - "uuid": log.farmos_uuid or self.app.make_true_uuid(), - "name": log.message, - "notes": log.notes, - "_src_object": log, - } - - -class ActivityLogImporter(FromWuttaFarmLog, farmos_importing.model.ActivityLogImporter): - """ - WuttaFarm → farmOS API exporter for Activity Logs - """ - - source_model_class = model.ActivityLog - - -class HarvestLogImporter(FromWuttaFarmLog, farmos_importing.model.HarvestLogImporter): - """ - WuttaFarm → farmOS API exporter for Harvest Logs - """ - - source_model_class = model.HarvestLog - - -class MedicalLogImporter(FromWuttaFarmLog, farmos_importing.model.MedicalLogImporter): - """ - WuttaFarm → farmOS API exporter for Medical Logs - """ - - source_model_class = model.MedicalLog - - -class ObservationLogImporter( - FromWuttaFarmLog, farmos_importing.model.ObservationLogImporter -): - """ - WuttaFarm → farmOS API exporter for Observation Logs - """ - - source_model_class = model.ObservationLog diff --git a/src/wuttafarm/importing/farmos.py b/src/wuttafarm/importing/farmos.py index d1cac19..b07d06d 100644 --- a/src/wuttafarm/importing/farmos.py +++ b/src/wuttafarm/importing/farmos.py @@ -104,13 +104,8 @@ class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler): importers["AnimalType"] = AnimalTypeImporter importers["AnimalAsset"] = AnimalAssetImporter importers["GroupAsset"] = GroupAssetImporter - importers["PlantType"] = PlantTypeImporter - importers["PlantAsset"] = PlantAssetImporter importers["LogType"] = LogTypeImporter importers["ActivityLog"] = ActivityLogImporter - importers["HarvestLog"] = HarvestLogImporter - importers["MedicalLog"] = MedicalLogImporter - importers["ObservationLog"] = ObservationLogImporter return importers @@ -144,14 +139,48 @@ class FromFarmOS(Importer): return self.app.make_utc(dt) +class ActivityLogImporter(FromFarmOS, ToWutta): + """ + farmOS API → WuttaFarm importer for Activity Logs + """ + + model_class = model.ActivityLog + + supported_fields = [ + "farmos_uuid", + "drupal_id", + "message", + "timestamp", + "notes", + "status", + ] + + def get_source_objects(self): + """ """ + logs = self.farmos_client.log.get("activity") + return logs["data"] + + def normalize_source_object(self, log): + """ """ + + if notes := log["attributes"]["notes"]: + notes = notes["value"] + + return { + "farmos_uuid": UUID(log["id"]), + "drupal_id": log["attributes"]["drupal_internal__id"], + "message": log["attributes"]["name"], + "timestamp": self.normalize_datetime(log["attributes"]["timestamp"]), + "notes": notes, + "status": log["attributes"]["status"], + } + + class AssetImporterBase(FromFarmOS, ToWutta): """ Base class for farmOS API → WuttaFarm asset importers """ - def get_farmos_asset_type(self): - return self.model_class.__wutta_hint__["farmos_asset_type"] - def get_simple_fields(self): """ """ fields = list(super().get_simple_fields()) @@ -182,12 +211,6 @@ class AssetImporterBase(FromFarmOS, ToWutta): ) return fields - def get_source_objects(self): - """ """ - asset_type = self.get_farmos_asset_type() - result = self.farmos_client.asset.get(asset_type) - return result["data"] - def normalize_source_data(self, **kwargs): """ """ data = super().normalize_source_data(**kwargs) @@ -312,7 +335,6 @@ class AnimalAssetImporter(AssetImporterBase): "animal_type_uuid", "sex", "is_sterile", - "produces_eggs", "birthdate", "notes", "archived", @@ -372,7 +394,6 @@ class AnimalAssetImporter(AssetImporterBase): "animal_type_uuid": animal_type_uuid, "sex": animal["attributes"]["sex"], "is_sterile": sterile, - "produces_eggs": animal["attributes"]["produces_eggs"], "birthdate": birthdate, } ) @@ -451,7 +472,6 @@ class GroupAssetImporter(AssetImporterBase): "asset_name", "is_location", "is_fixed", - "produces_eggs", "notes", "archived", "image_url", @@ -470,7 +490,6 @@ class GroupAssetImporter(AssetImporterBase): data.update( { "asset_type": "group", - "produces_eggs": group["attributes"]["produces_eggs"], } ) return data @@ -557,12 +576,12 @@ class LandTypeImporter(FromFarmOS, ToWutta): } -class PlantTypeImporter(FromFarmOS, ToWutta): +class LogTypeImporter(FromFarmOS, ToWutta): """ - farmOS API → WuttaFarm importer for Plant Types + farmOS API → WuttaFarm importer for Log Types """ - model_class = model.PlantType + model_class = model.LogType supported_fields = [ "farmos_uuid", @@ -573,112 +592,19 @@ class PlantTypeImporter(FromFarmOS, ToWutta): def get_source_objects(self): """ """ - result = self.farmos_client.resource.get("taxonomy_term", "plant_type") - return result["data"] + log_types = self.farmos_client.resource.get("log_type") + return log_types["data"] - def normalize_source_object(self, plant_type): + def normalize_source_object(self, log_type): """ """ return { - "farmos_uuid": UUID(plant_type["id"]), - "drupal_id": plant_type["attributes"]["drupal_internal__tid"], - "name": plant_type["attributes"]["name"], - "description": plant_type["attributes"]["description"], + "farmos_uuid": UUID(log_type["id"]), + "drupal_id": log_type["attributes"]["drupal_internal__id"], + "name": log_type["attributes"]["label"], + "description": log_type["attributes"]["description"], } -class PlantAssetImporter(AssetImporterBase): - """ - farmOS API → WuttaFarm importer for Plant Assets - """ - - model_class = model.PlantAsset - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "asset_type", - "asset_name", - "plant_types", - "notes", - "archived", - "image_url", - "thumbnail_url", - ] - - def setup(self): - super().setup() - model = self.app.model - - self.plant_types_by_farmos_uuid = {} - for plant_type in self.target_session.query(model.PlantType): - if plant_type.farmos_uuid: - self.plant_types_by_farmos_uuid[plant_type.farmos_uuid] = plant_type - - def normalize_source_object(self, plant): - """ """ - plant_types = None - if relationships := plant.get("relationships"): - - if plant_type := relationships.get("plant_type"): - plant_types = [] - for plant_type in plant_type["data"]: - if wf_plant_type := self.plant_types_by_farmos_uuid.get( - UUID(plant_type["id"]) - ): - plant_types.append(wf_plant_type.uuid) - else: - log.warning("plant type not found: %s", plant_type["id"]) - - data = self.normalize_asset(plant) - data.update( - { - "asset_type": "plant", - "plant_types": plant_types, - } - ) - return data - - def normalize_target_object(self, plant): - data = super().normalize_target_object(plant) - - if "plant_types" in self.fields: - data["plant_types"] = [t.plant_type_uuid for t in plant._plant_types] - - return data - - def update_target_object(self, plant, source_data, target_data=None): - model = self.app.model - plant = super().update_target_object(plant, source_data, target_data) - - if "plant_types" in self.fields: - if ( - not target_data - or target_data["plant_types"] != source_data["plant_types"] - ): - - for uuid in source_data["plant_types"]: - if not target_data or uuid not in target_data["plant_types"]: - self.target_session.flush() - plant._plant_types.append( - model.PlantAssetPlantType(plant_type_uuid=uuid) - ) - - if target_data: - for uuid in target_data["plant_types"]: - if uuid not in source_data["plant_types"]: - plant_type = ( - self.target_session.query(model.PlantAssetPlantType) - .filter(model.PlantAssetPlantType.plant_asset == plant) - .filter( - model.PlantAssetPlantType.plant_type_uuid == uuid - ) - .one() - ) - self.target_session.delete(plant_type) - - return plant - - class StructureAssetImporter(AssetImporterBase): """ farmOS API → WuttaFarm importer for Structure Assets @@ -814,229 +740,3 @@ class UserImporter(FromFarmOS, ToWutta): if not user.farmos_uuid: return False return True - - -############################## -# log importers -############################## - - -class LogTypeImporter(FromFarmOS, ToWutta): - """ - farmOS API → WuttaFarm importer for Log Types - """ - - model_class = model.LogType - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "name", - "description", - ] - - def get_source_objects(self): - """ """ - log_types = self.farmos_client.resource.get("log_type") - return log_types["data"] - - def normalize_source_object(self, log_type): - """ """ - return { - "farmos_uuid": UUID(log_type["id"]), - "drupal_id": log_type["attributes"]["drupal_internal__id"], - "name": log_type["attributes"]["label"], - "description": log_type["attributes"]["description"], - } - - -class LogImporterBase(FromFarmOS, ToWutta): - """ - Base class for farmOS API → WuttaFarm log importers - """ - - def get_farmos_log_type(self): - return self.model_class.__wutta_hint__["farmos_log_type"] - - def get_simple_fields(self): - """ """ - fields = list(super().get_simple_fields()) - # nb. must explicitly declare proxy fields - fields.extend( - [ - "farmos_uuid", - "drupal_id", - "log_type", - "message", - "timestamp", - "notes", - "status", - ] - ) - return fields - - def get_supported_fields(self): - """ """ - fields = list(super().get_supported_fields()) - fields.extend( - [ - "assets", - ] - ) - return fields - - def get_source_objects(self): - """ """ - log_type = self.get_farmos_log_type() - result = self.farmos_client.log.get(log_type) - return result["data"] - - def get_asset_type(self, asset): - return asset["type"].split("--")[1] - - def normalize_source_object(self, log): - """ """ - if notes := log["attributes"]["notes"]: - notes = notes["value"] - - assets = None - if "assets" in self.fields: - assets = [] - for asset in log["relationships"]["asset"]["data"]: - assets.append((self.get_asset_type(asset), UUID(asset["id"]))) - - return { - "farmos_uuid": UUID(log["id"]), - "drupal_id": log["attributes"]["drupal_internal__id"], - "log_type": self.get_farmos_log_type(), - "message": log["attributes"]["name"], - "timestamp": self.normalize_datetime(log["attributes"]["timestamp"]), - "notes": notes, - "status": log["attributes"]["status"], - "assets": assets, - } - - def normalize_target_object(self, log): - data = super().normalize_target_object(log) - - if "assets" in self.fields: - data["assets"] = [ - (a.asset.asset_type, a.asset.farmos_uuid) for a in log.log._assets - ] - - return data - - def update_target_object(self, log, source_data, target_data=None): - model = self.app.model - log = super().update_target_object(log, source_data, target_data) - - if "assets" in self.fields: - if not target_data or target_data["assets"] != source_data["assets"]: - - for key in source_data["assets"]: - asset_type, farmos_uuid = key - if not target_data or key not in target_data["assets"]: - self.target_session.flush() - asset = ( - self.target_session.query(model.Asset) - .filter(model.Asset.asset_type == asset_type) - .filter(model.Asset.farmos_uuid == farmos_uuid) - .one() - ) - log.log._assets.append(model.LogAsset(asset=asset)) - - if target_data: - for key in target_data["assets"]: - asset_type, farmos_uuid = key - if key not in source_data["assets"]: - asset = ( - self.target_session.query(model.Asset) - .filter(model.Asset.asset_type == asset_type) - .filter(model.Asset.farmos_uuid == farmos_uuid) - .one() - ) - asset = ( - self.target_session.query(model.LogAsset) - .filter(model.LogAsset.log == log) - .filter(model.LogAsset.asset == asset) - .one() - ) - self.target_session.delete(asset) - - return log - - -class ActivityLogImporter(LogImporterBase): - """ - farmOS API → WuttaFarm importer for Activity Logs - """ - - model_class = model.ActivityLog - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "log_type", - "message", - "timestamp", - "notes", - "status", - "assets", - ] - - -class HarvestLogImporter(LogImporterBase): - """ - farmOS API → WuttaFarm importer for Harvest Logs - """ - - model_class = model.HarvestLog - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "log_type", - "message", - "timestamp", - "notes", - "status", - "assets", - ] - - -class MedicalLogImporter(LogImporterBase): - """ - farmOS API → WuttaFarm importer for Medical Logs - """ - - model_class = model.MedicalLog - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "log_type", - "message", - "timestamp", - "notes", - "status", - "assets", - ] - - -class ObservationLogImporter(LogImporterBase): - """ - farmOS API → WuttaFarm importer for Observation Logs - """ - - model_class = model.ObservationLog - - supported_fields = [ - "farmos_uuid", - "drupal_id", - "log_type", - "message", - "timestamp", - "notes", - "status", - "assets", - ] diff --git a/src/wuttafarm/web/forms/schema.py b/src/wuttafarm/web/forms/schema.py index 123f662..95b3e9d 100644 --- a/src/wuttafarm/web/forms/schema.py +++ b/src/wuttafarm/web/forms/schema.py @@ -74,25 +74,6 @@ class AnimalTypeType(colander.SchemaType): return AnimalTypeWidget(self.request, **kwargs) -class FarmOSPlantTypes(colander.SchemaType): - - def __init__(self, request, *args, **kwargs): - super().__init__(*args, **kwargs) - self.request = request - - def serialize(self, node, appstruct): - if appstruct is colander.null: - return colander.null - - return json.dumps(appstruct) - - def widget_maker(self, **kwargs): # pylint: disable=empty-docstring - """ """ - from wuttafarm.web.forms.widgets import FarmOSPlantTypesWidget - - return FarmOSPlantTypesWidget(self.request, **kwargs) - - class LandTypeRef(ObjectRef): """ Custom schema type for a @@ -118,23 +99,6 @@ class LandTypeRef(ObjectRef): return self.request.route_url("land_types.view", uuid=land_type.uuid) -class PlantTypeRefs(WuttaSet): - """ - Schema type for Plant Types field (on a Plant Asset). - """ - - 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 PlantTypeRefsWidget - - return PlantTypeRefsWidget(self.request, **kwargs) - - class StructureType(colander.SchemaType): def __init__(self, request, *args, **kwargs): @@ -213,20 +177,3 @@ class AssetParentRefs(WuttaSet): from wuttafarm.web.forms.widgets import AssetParentRefsWidget return AssetParentRefsWidget(self.request, **kwargs) - - -class LogAssetRefs(WuttaSet): - """ - Schema type for Assets field (on a Log record) - """ - - 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 LogAssetRefsWidget - - return LogAssetRefsWidget(self.request, **kwargs) diff --git a/src/wuttafarm/web/forms/widgets.py b/src/wuttafarm/web/forms/widgets.py index d5bf5c2..f812ccf 100644 --- a/src/wuttafarm/web/forms/widgets.py +++ b/src/wuttafarm/web/forms/widgets.py @@ -81,67 +81,6 @@ class AnimalTypeWidget(Widget): return super().serialize(field, cstruct, **kw) -class FarmOSPlantTypesWidget(Widget): - """ - Widget to display a farmOS "plant types" field. - """ - - def __init__(self, request, *args, **kwargs): - super().__init__(*args, **kwargs) - self.request = request - - def serialize(self, field, cstruct, **kw): - """ """ - readonly = kw.get("readonly", self.readonly) - if readonly: - if cstruct in (colander.null, None): - return HTML.tag("span") - - links = [] - for plant_type in json.loads(cstruct): - link = tags.link_to( - plant_type["name"], - self.request.route_url( - "farmos_plant_types.view", uuid=plant_type["uuid"] - ), - ) - links.append(HTML.tag("li", c=link)) - return HTML.tag("ul", c=links) - - return super().serialize(field, cstruct, **kw) - - -class PlantTypeRefsWidget(WuttaCheckboxChoiceWidget): - """ - Widget for Plant Types field (on a Plant Asset). - """ - - def serialize(self, field, cstruct, **kw): - """ """ - model = self.app.model - session = Session() - - readonly = kw.get("readonly", self.readonly) - if readonly: - plant_types = [] - for uuid in json.loads(cstruct): - plant_type = session.get(model.PlantType, uuid) - plant_types.append( - HTML.tag( - "li", - c=tags.link_to( - str(plant_type), - self.request.route_url( - "plant_types.view", uuid=plant_type.uuid - ), - ), - ) - ) - return HTML.tag("ul", c=plant_types) - - return super().serialize(field, cstruct, **kw) - - class StructureWidget(Widget): """ Widget to display a "structure" field. @@ -227,34 +166,3 @@ class AssetParentRefsWidget(WuttaCheckboxChoiceWidget): return HTML.tag("ul", c=parents) return super().serialize(field, cstruct, **kw) - - -class LogAssetRefsWidget(WuttaCheckboxChoiceWidget): - """ - Widget for Assets field (on a Log record) - """ - - def serialize(self, field, cstruct, **kw): - """ """ - model = self.app.model - session = Session() - - readonly = kw.get("readonly", self.readonly) - if readonly: - assets = [] - for uuid in json.loads(cstruct): - asset = session.get(model.Asset, uuid) - assets.append( - HTML.tag( - "li", - c=tags.link_to( - str(asset), - self.request.route_url( - f"{asset.asset_type}_assets.view", uuid=asset.uuid - ), - ), - ) - ) - return HTML.tag("ul", c=assets) - - return super().serialize(field, cstruct, **kw) diff --git a/src/wuttafarm/web/menus.py b/src/wuttafarm/web/menus.py index d52a6ca..bdd2fbf 100644 --- a/src/wuttafarm/web/menus.py +++ b/src/wuttafarm/web/menus.py @@ -64,11 +64,6 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "land_assets", "perm": "land_assets.list", }, - { - "title": "Plant", - "route": "plant_assets", - "perm": "plant_assets.list", - }, { "title": "Structure", "route": "structure_assets", @@ -85,11 +80,6 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "land_types", "perm": "land_types.list", }, - { - "title": "Plant Types", - "route": "plant_types", - "perm": "plant_types.list", - }, { "title": "Structure Types", "route": "structure_types", @@ -109,29 +99,9 @@ class WuttaFarmMenuHandler(base.MenuHandler): "type": "menu", "items": [ { - "title": "All Logs", - "route": "log", - "perm": "log.list", - }, - { - "title": "Activity", - "route": "logs_activity", - "perm": "logs_activity.list", - }, - { - "title": "Harvest", - "route": "logs_harvest", - "perm": "logs_harvest.list", - }, - { - "title": "Medical", - "route": "logs_medical", - "perm": "logs_medical.list", - }, - { - "title": "Observation", - "route": "logs_observation", - "perm": "logs_observation.list", + "title": "Activity Logs", + "route": "activity_logs", + "perm": "activity_logs.list", }, {"type": "sep"}, { @@ -165,11 +135,6 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "farmos_groups", "perm": "farmos_groups.list", }, - { - "title": "Plants", - "route": "farmos_asset_plant", - "perm": "farmos_asset_plant.list", - }, { "title": "Structures", "route": "farmos_structures", @@ -186,32 +151,12 @@ class WuttaFarmMenuHandler(base.MenuHandler): "route": "farmos_logs_activity", "perm": "farmos_logs_activity.list", }, - { - "title": "Harvest Logs", - "route": "farmos_logs_harvest", - "perm": "farmos_logs_harvest.list", - }, - { - "title": "Medical Logs", - "route": "farmos_logs_medical", - "perm": "farmos_logs_medical.list", - }, - { - "title": "Observation Logs", - "route": "farmos_logs_observation", - "perm": "farmos_logs_observation.list", - }, {"type": "sep"}, { "title": "Animal Types", "route": "farmos_animal_types", "perm": "farmos_animal_types.list", }, - { - "title": "Plant Types", - "route": "farmos_plant_types", - "perm": "farmos_plant_types.list", - }, { "title": "Structure Types", "route": "farmos_structure_types", diff --git a/src/wuttafarm/web/views/__init__.py b/src/wuttafarm/web/views/__init__.py index bb710a2..e44c16e 100644 --- a/src/wuttafarm/web/views/__init__.py +++ b/src/wuttafarm/web/views/__init__.py @@ -47,12 +47,8 @@ def includeme(config): config.include("wuttafarm.web.views.structures") config.include("wuttafarm.web.views.animals") config.include("wuttafarm.web.views.groups") - config.include("wuttafarm.web.views.plants") - config.include("wuttafarm.web.views.logs") + config.include("wuttafarm.web.views.log_types") 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") # views for farmOS config.include("wuttafarm.web.views.farmos") diff --git a/src/wuttafarm/web/views/animals.py b/src/wuttafarm/web/views/animals.py index 72a05ee..7016e36 100644 --- a/src/wuttafarm/web/views/animals.py +++ b/src/wuttafarm/web/views/animals.py @@ -157,7 +157,6 @@ class AnimalAssetView(AssetMasterView): "birthdate", "is_sterile", "sex", - "produces_eggs", "archived", ] @@ -167,7 +166,6 @@ class AnimalAssetView(AssetMasterView): "birthdate", "sex", "is_sterile", - "produces_eggs", "notes", "asset_type", "archived", diff --git a/src/wuttafarm/web/views/asset_types.py b/src/wuttafarm/web/views/asset_types.py index b9f560a..775fa3a 100644 --- a/src/wuttafarm/web/views/asset_types.py +++ b/src/wuttafarm/web/views/asset_types.py @@ -23,7 +23,7 @@ Master view for Asset Types """ -from wuttafarm.db.model import AssetType +from wuttafarm.db.model.assets import AssetType from wuttafarm.web.views import WuttaFarmMasterView diff --git a/src/wuttafarm/web/views/assets.py b/src/wuttafarm/web/views/assets.py index b918839..dffaae7 100644 --- a/src/wuttafarm/web/views/assets.py +++ b/src/wuttafarm/web/views/assets.py @@ -29,7 +29,7 @@ from wuttaweb.forms.schema import WuttaDictEnum from wuttaweb.db import Session from wuttafarm.web.views import WuttaFarmMasterView -from wuttafarm.db.model import Asset, Log +from wuttafarm.db.model import Asset from wuttafarm.web.forms.schema import AssetParentRefs from wuttafarm.web.forms.widgets import ImageWidget @@ -140,28 +140,6 @@ class AssetMasterView(WuttaFarmMasterView): "archived": {"active": True, "verb": "is_false"}, } - has_rows = True - row_model_class = Log - rows_viewable = True - - row_labels = { - "message": "Log Name", - } - - row_grid_columns = [ - "status", - "drupal_id", - "timestamp", - "message", - "log_type", - "assets", - "location", - "quantity", - "is_group_assignment", - ] - - rows_sort_defaults = ("timestamp", "desc") - def get_fallback_templates(self, template): templates = super().get_fallback_templates(template) @@ -287,8 +265,6 @@ class AssetMasterView(WuttaFarmMasterView): route = "farmos_groups.view" elif asset.asset_type == "land": route = "farmos_land_assets.view" - elif asset.asset_type == "plant": - route = "farmos_asset_plant.view" elif asset.asset_type == "structure": route = "farmos_structures.view" @@ -304,39 +280,6 @@ class AssetMasterView(WuttaFarmMasterView): return buttons - def get_row_grid_data(self, asset): - model = self.app.model - session = self.Session() - return ( - session.query(model.Log) - .outerjoin(model.LogAsset) - .filter(model.LogAsset.asset_uuid == asset.uuid) - ) - - def configure_row_grid(self, grid): - g = grid - super().configure_row_grid(g) - model = self.app.model - - # drupal_id - g.set_label("drupal_id", "ID", column_only=True) - - # message - g.set_link("message") - g.set_sorter("message", model.Log.message) - g.set_filter("message", model.Log.message) - - # timestamp - g.set_sorter("timestamp", model.Log.timestamp) - g.set_filter("timestamp", model.Log.timestamp) - - # log_type - g.set_sorter("log_type", model.Log.log_type) - g.set_filter("log_type", model.Log.log_type) - - def get_row_action_url_view(self, log, i): - return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid) - def defaults(config, **kwargs): base = globals() diff --git a/src/wuttafarm/web/views/farmos/__init__.py b/src/wuttafarm/web/views/farmos/__init__.py index bda5d03..deacd7d 100644 --- a/src/wuttafarm/web/views/farmos/__init__.py +++ b/src/wuttafarm/web/views/farmos/__init__.py @@ -36,9 +36,5 @@ def includeme(config): 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.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") diff --git a/src/wuttafarm/web/views/farmos/groups.py b/src/wuttafarm/web/views/farmos/groups.py index ddb7278..c6748c4 100644 --- a/src/wuttafarm/web/views/farmos/groups.py +++ b/src/wuttafarm/web/views/farmos/groups.py @@ -115,9 +115,6 @@ class GroupView(FarmOSMasterView): else: archived = group["attributes"]["status"] == "archived" - if notes := group["attributes"]["notes"]: - notes = notes["value"] - return { "uuid": group["id"], "drupal_id": group["attributes"]["drupal_internal__id"], @@ -127,7 +124,7 @@ class GroupView(FarmOSMasterView): "is_fixed": group["attributes"]["is_fixed"], "is_location": group["attributes"]["is_location"], "archived": archived, - "notes": notes or colander.null, + "notes": group["attributes"]["notes"]["value"], } def configure_form(self, form): diff --git a/src/wuttafarm/web/views/farmos/logs.py b/src/wuttafarm/web/views/farmos/logs.py deleted file mode 100644 index a3e804f..0000000 --- a/src/wuttafarm/web/views/farmos/logs.py +++ /dev/null @@ -1,142 +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 Harvest Logs -""" - -import datetime - -import colander - -from wuttaweb.forms.schema import WuttaDateTime -from wuttaweb.forms.widgets import WuttaDateTimeWidget - -from wuttafarm.web.views.farmos import FarmOSMasterView - - -class LogMasterView(FarmOSMasterView): - """ - Base class for farmOS Log master views - """ - - farmos_log_type = None - - grid_columns = [ - "name", - "timestamp", - "status", - ] - - sort_defaults = ("timestamp", "desc") - - form_fields = [ - "name", - "timestamp", - "status", - "notes", - ] - - def get_grid_data(self, columns=None, session=None): - result = self.farmos_client.log.get(self.farmos_log_type) - return [self.normalize_log(l) for l in result["data"]] - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # name - g.set_link("name") - g.set_searchable("name") - - # timestamp - g.set_renderer("timestamp", "datetime") - - def get_instance(self): - log = self.farmos_client.log.get_id( - self.farmos_log_type, self.request.matchdict["uuid"] - ) - self.raw_json = log - return self.normalize_log(log["data"]) - - def get_instance_title(self, log): - return log["name"] - - def normalize_log(self, log): - - if timestamp := log["attributes"]["timestamp"]: - timestamp = datetime.datetime.fromisoformat(timestamp) - timestamp = self.app.localtime(timestamp) - - if notes := log["attributes"]["notes"]: - notes = notes["value"] - - return { - "uuid": log["id"], - "drupal_id": log["attributes"]["drupal_internal__id"], - "name": log["attributes"]["name"], - "timestamp": timestamp, - "status": log["attributes"]["status"], - "notes": notes or colander.null, - } - - def configure_form(self, form): - f = form - super().configure_form(f) - - # timestamp - f.set_node("timestamp", WuttaDateTime()) - f.set_widget("timestamp", WuttaDateTimeWidget(self.request)) - - # notes - f.set_widget("notes", "notes") - - def get_xref_buttons(self, log): - model = self.app.model - session = self.Session() - - buttons = [ - self.make_button( - "View in farmOS", - primary=True, - url=self.app.get_farmos_url(f"/log/{log['drupal_id']}"), - target="_blank", - icon_left="external-link-alt", - ), - ] - - if wf_log := ( - session.query(model.Log) - .filter(model.Log.farmos_uuid == log["uuid"]) - .first() - ): - buttons.append( - self.make_button( - f"View {self.app.get_title()} record", - primary=True, - url=self.request.route_url( - f"logs_{self.farmos_log_type}.view", uuid=wf_log.uuid - ), - icon_left="eye", - ) - ) - - return buttons diff --git a/src/wuttafarm/web/views/farmos/logs_activity.py b/src/wuttafarm/web/views/farmos/logs_activity.py index 972ca31..e966810 100644 --- a/src/wuttafarm/web/views/farmos/logs_activity.py +++ b/src/wuttafarm/web/views/farmos/logs_activity.py @@ -20,13 +20,20 @@ # ################################################################################ """ -View for farmOS Activity Logs +View for farmOS activity logs """ -from wuttafarm.web.views.farmos.logs import LogMasterView +import datetime + +import colander + +from wuttaweb.forms.schema import WuttaDateTime +from wuttaweb.forms.widgets import WuttaDateTimeWidget + +from wuttafarm.web.views.farmos import FarmOSMasterView -class ActivityLogView(LogMasterView): +class ActivityLogView(FarmOSMasterView): """ View for farmOS activity logs """ @@ -38,9 +45,105 @@ class ActivityLogView(LogMasterView): route_prefix = "farmos_logs_activity" url_prefix = "/farmOS/logs/activity" - farmos_log_type = "activity" farmos_refurl_path = "/logs/activity" + grid_columns = [ + "name", + "timestamp", + "status", + ] + + sort_defaults = ("timestamp", "desc") + + form_fields = [ + "name", + "timestamp", + "status", + "notes", + ] + + def get_grid_data(self, columns=None, session=None): + logs = self.farmos_client.log.get("activity") + return [self.normalize_log(t) for t in logs["data"]] + + def configure_grid(self, grid): + g = grid + super().configure_grid(g) + + # name + g.set_link("name") + g.set_searchable("name") + + # timestamp + g.set_renderer("timestamp", "datetime") + + def get_instance(self): + log = self.farmos_client.log.get_id("activity", self.request.matchdict["uuid"]) + self.raw_json = log + return self.normalize_log(log["data"]) + + def get_instance_title(self, log): + return log["name"] + + def normalize_log(self, log): + + if timestamp := log["attributes"]["timestamp"]: + timestamp = datetime.datetime.fromisoformat(timestamp) + timestamp = self.app.localtime(timestamp) + + if notes := log["attributes"]["notes"]: + notes = notes["value"] + + return { + "uuid": log["id"], + "drupal_id": log["attributes"]["drupal_internal__id"], + "name": log["attributes"]["name"], + "timestamp": timestamp, + "status": log["attributes"]["status"], + "notes": notes or colander.null, + } + + def configure_form(self, form): + f = form + super().configure_form(f) + + # timestamp + f.set_node("timestamp", WuttaDateTime()) + f.set_widget("timestamp", WuttaDateTimeWidget(self.request)) + + # notes + f.set_widget("notes", "notes") + + def get_xref_buttons(self, log): + model = self.app.model + session = self.Session() + + buttons = [ + self.make_button( + "View in farmOS", + primary=True, + url=self.app.get_farmos_url(f"/log/{log['drupal_id']}"), + target="_blank", + icon_left="external-link-alt", + ), + ] + + if wf_log := ( + session.query(model.ActivityLog) + .filter(model.ActivityLog.farmos_uuid == log["uuid"]) + .first() + ): + buttons.append( + self.make_button( + f"View {self.app.get_title()} record", + primary=True, + url=self.request.route_url("activity_logs.view", uuid=wf_log.uuid), + icon_left="eye", + ) + ) + + return buttons + def defaults(config, **kwargs): base = globals() diff --git a/src/wuttafarm/web/views/farmos/logs_harvest.py b/src/wuttafarm/web/views/farmos/logs_harvest.py deleted file mode 100644 index 0f39a5a..0000000 --- a/src/wuttafarm/web/views/farmos/logs_harvest.py +++ /dev/null @@ -1,53 +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 Harvest Logs -""" - -from wuttafarm.web.views.farmos.logs import LogMasterView - - -class HarvestLogView(LogMasterView): - """ - View for farmOS harvest logs - """ - - model_name = "farmos_harvest_log" - model_title = "farmOS Harvest Log" - model_title_plural = "farmOS Harvest Logs" - - route_prefix = "farmos_logs_harvest" - url_prefix = "/farmOS/logs/harvest" - - farmos_log_type = "harvest" - farmos_refurl_path = "/logs/harvest" - - -def defaults(config, **kwargs): - base = globals() - - HarvestLogView = kwargs.get("HarvestLogView", base["HarvestLogView"]) - HarvestLogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/farmos/logs_medical.py b/src/wuttafarm/web/views/farmos/logs_medical.py deleted file mode 100644 index 95a88c5..0000000 --- a/src/wuttafarm/web/views/farmos/logs_medical.py +++ /dev/null @@ -1,53 +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 Medical Logs -""" - -from wuttafarm.web.views.farmos.logs import LogMasterView - - -class MedicalLogView(LogMasterView): - """ - View for farmOS medical logs - """ - - model_name = "farmos_medical_log" - model_title = "farmOS Medical Log" - model_title_plural = "farmOS Medical Logs" - - route_prefix = "farmos_logs_medical" - url_prefix = "/farmOS/logs/medical" - - farmos_log_type = "medical" - farmos_refurl_path = "/logs/medical" - - -def defaults(config, **kwargs): - base = globals() - - MedicalLogView = kwargs.get("MedicalLogView", base["MedicalLogView"]) - MedicalLogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/farmos/logs_observation.py b/src/wuttafarm/web/views/farmos/logs_observation.py deleted file mode 100644 index ab27b5a..0000000 --- a/src/wuttafarm/web/views/farmos/logs_observation.py +++ /dev/null @@ -1,53 +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 Observation Logs -""" - -from wuttafarm.web.views.farmos.logs import LogMasterView - - -class ObservationLogView(LogMasterView): - """ - View for farmOS observation logs - """ - - model_name = "farmos_observation_log" - model_title = "farmOS Observation Log" - model_title_plural = "farmOS Observation Logs" - - route_prefix = "farmos_logs_observation" - url_prefix = "/farmOS/logs/observation" - - farmos_log_type = "observation" - farmos_refurl_path = "/logs/observation" - - -def defaults(config, **kwargs): - base = globals() - - ObservationLogView = kwargs.get("ObservationLogView", base["ObservationLogView"]) - ObservationLogView.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 deleted file mode 100644 index f02801f..0000000 --- a/src/wuttafarm/web/views/farmos/plants.py +++ /dev/null @@ -1,362 +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 Farm Plants -""" - -import datetime - -import colander - -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 UsersType, StructureType, FarmOSPlantTypes -from wuttafarm.web.forms.widgets import ImageWidget - - -class PlantTypeView(FarmOSMasterView): - """ - Master view for Plant Types in farmOS. - """ - - model_name = "farmos_plant_type" - model_title = "farmOS Plant Type" - model_title_plural = "farmOS Plant Types" - - route_prefix = "farmos_plant_types" - url_prefix = "/farmOS/plant-types" - - farmos_refurl_path = "/admin/structure/taxonomy/manage/plant_type/overview" - - grid_columns = [ - "name", - "description", - "changed", - ] - - sort_defaults = "name" - - form_fields = [ - "name", - "description", - "changed", - ] - - def get_grid_data(self, columns=None, session=None): - result = self.farmos_client.resource.get("taxonomy_term", "plant_type") - return [self.normalize_plant_type(t) for t in result["data"]] - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # name - g.set_link("name") - g.set_searchable("name") - - # changed - g.set_renderer("changed", "datetime") - - def get_instance(self): - plant_type = self.farmos_client.resource.get_id( - "taxonomy_term", "plant_type", self.request.matchdict["uuid"] - ) - self.raw_json = plant_type - return self.normalize_plant_type(plant_type["data"]) - - def get_instance_title(self, plant_type): - return plant_type["name"] - - def normalize_plant_type(self, plant_type): - - if changed := plant_type["attributes"]["changed"]: - changed = datetime.datetime.fromisoformat(changed) - changed = self.app.localtime(changed) - - if description := plant_type["attributes"]["description"]: - description = description["value"] - - return { - "uuid": plant_type["id"], - "drupal_id": plant_type["attributes"]["drupal_internal__tid"], - "name": plant_type["attributes"]["name"], - "description": description or colander.null, - "changed": changed, - } - - def configure_form(self, form): - f = form - super().configure_form(f) - - # description - f.set_widget("description", "notes") - - # changed - f.set_node("changed", WuttaDateTime()) - - def get_xref_buttons(self, plant_type): - model = self.app.model - session = self.Session() - - buttons = [ - self.make_button( - "View in farmOS", - primary=True, - url=self.app.get_farmos_url( - f"/taxonomy/term/{plant_type['drupal_id']}" - ), - target="_blank", - icon_left="external-link-alt", - ) - ] - - if wf_plant_type := ( - session.query(model.PlantType) - .filter(model.PlantType.farmos_uuid == plant_type["uuid"]) - .first() - ): - buttons.append( - self.make_button( - f"View {self.app.get_title()} record", - primary=True, - url=self.request.route_url( - "plant_types.view", uuid=wf_plant_type.uuid - ), - icon_left="eye", - ) - ) - - return buttons - - -class PlantAssetView(FarmOSMasterView): - """ - Master view for farmOS Plant Assets - """ - - model_name = "farmos_asset_plant" - model_title = "farmOS Plant Asset" - model_title_plural = "farmOS Plant Assets" - - route_prefix = "farmos_asset_plant" - url_prefix = "/farmOS/assets/plant" - - farmos_refurl_path = "/assets/plant" - - grid_columns = [ - "name", - "archived", - ] - - sort_defaults = "name" - - form_fields = [ - "name", - "plant_types", - "archived", - "owners", - "location", - "notes", - "raw_image_url", - "large_image_url", - "thumbnail_image_url", - "image", - ] - - def get_grid_data(self, columns=None, session=None): - result = self.farmos_client.asset.get("plant") - return [self.normalize_plant(a) for a in result["data"]] - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # name - g.set_link("name") - g.set_searchable("name") - - # archived - g.set_renderer("archived", "boolean") - - def get_instance(self): - - plant = self.farmos_client.resource.get_id( - "asset", "plant", self.request.matchdict["uuid"] - ) - self.raw_json = plant - - # instance data - data = self.normalize_plant(plant["data"]) - - if relationships := plant["data"].get("relationships"): - - # add plant types - if plant_type := relationships.get("plant_type"): - if plant_type["data"]: - data["plant_types"] = [] - for plant_type in plant_type["data"]: - plant_type = self.farmos_client.resource.get_id( - "taxonomy_term", "plant_type", plant_type["id"] - ) - data["plant_types"].append( - { - "uuid": plant_type["data"]["id"], - "name": plant_type["data"]["attributes"]["name"], - } - ) - - # add location - if location := relationships.get("location"): - if location["data"]: - location = self.farmos_client.resource.get_id( - "asset", "structure", location["data"][0]["id"] - ) - data["location"] = { - "uuid": location["data"]["id"], - "name": location["data"]["attributes"]["name"], - } - - # add owners - if owner := relationships.get("owner"): - data["owners"] = [] - for owner_data in owner["data"]: - owner = self.farmos_client.resource.get_id( - "user", "user", owner_data["id"] - ) - data["owners"].append( - { - "uuid": owner["data"]["id"], - "display_name": owner["data"]["attributes"]["display_name"], - } - ) - - # add image urls - if image := relationships.get("image"): - if image["data"]: - image = self.farmos_client.resource.get_id( - "file", "file", image["data"][0]["id"] - ) - data["raw_image_url"] = self.app.get_farmos_url( - image["data"]["attributes"]["uri"]["url"] - ) - # nb. other styles available: medium, wide - data["large_image_url"] = image["data"]["attributes"][ - "image_style_uri" - ]["large"] - data["thumbnail_image_url"] = image["data"]["attributes"][ - "image_style_uri" - ]["thumbnail"] - - return data - - def get_instance_title(self, plant): - return plant["name"] - - def normalize_plant(self, 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": plant["id"], - "drupal_id": plant["attributes"]["drupal_internal__id"], - "name": plant["attributes"]["name"], - "location": colander.null, # TODO - "archived": archived, - "notes": notes or colander.null, - } - - def configure_form(self, form): - f = form - super().configure_form(f) - plant = f.model_instance - - # plant_types - f.set_node("plant_types", FarmOSPlantTypes(self.request)) - - # location - f.set_node("location", StructureType(self.request)) - - # owners - f.set_node("owners", UsersType(self.request)) - - # notes - f.set_widget("notes", "notes") - - # archived - f.set_node("archived", colander.Boolean()) - - # image - if url := plant.get("large_image_url"): - f.set_widget("image", ImageWidget("plant image")) - f.set_default("image", url) - - def get_xref_buttons(self, plant): - model = self.app.model - session = self.Session() - - buttons = [ - self.make_button( - "View in farmOS", - primary=True, - url=self.app.get_farmos_url(f"/asset/{plant['drupal_id']}"), - target="_blank", - icon_left="external-link-alt", - ), - ] - - if wf_plant := ( - session.query(model.Asset) - .filter(model.Asset.farmos_uuid == plant["uuid"]) - .first() - ): - buttons.append( - self.make_button( - f"View {self.app.get_title()} record", - primary=True, - url=self.request.route_url("plant_assets.view", uuid=wf_plant.uuid), - icon_left="eye", - ) - ) - - return buttons - - -def defaults(config, **kwargs): - base = globals() - - PlantTypeView = kwargs.get("PlantTypeView", base["PlantTypeView"]) - PlantTypeView.defaults(config) - - PlantAssetView = kwargs.get("PlantAssetView", base["PlantAssetView"]) - PlantAssetView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/groups.py b/src/wuttafarm/web/views/groups.py index 4b26463..21d7ed4 100644 --- a/src/wuttafarm/web/views/groups.py +++ b/src/wuttafarm/web/views/groups.py @@ -24,7 +24,7 @@ Master view for Groups """ from wuttafarm.web.views.assets import AssetMasterView -from wuttafarm.db.model import GroupAsset +from wuttafarm.db.model.groups import GroupAsset class GroupView(AssetMasterView): @@ -42,7 +42,6 @@ class GroupView(AssetMasterView): "thumbnail", "drupal_id", "asset_name", - "produces_eggs", "archived", ] @@ -50,7 +49,6 @@ class GroupView(AssetMasterView): "asset_name", "notes", "asset_type", - "produces_eggs", "archived", "farmos_uuid", "drupal_id", diff --git a/src/wuttafarm/web/views/land.py b/src/wuttafarm/web/views/land.py index 22827a0..aad15e7 100644 --- a/src/wuttafarm/web/views/land.py +++ b/src/wuttafarm/web/views/land.py @@ -25,7 +25,7 @@ Master view for Land Types from webhelpers2.html import HTML, tags -from wuttafarm.db.model import LandType, LandAsset +from wuttafarm.db.model.land import LandType, LandAsset from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView from wuttafarm.web.forms.schema import LandTypeRef diff --git a/src/wuttafarm/web/views/log_types.py b/src/wuttafarm/web/views/log_types.py new file mode 100644 index 0000000..13ea35f --- /dev/null +++ b/src/wuttafarm/web/views/log_types.py @@ -0,0 +1,90 @@ +# -*- 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 Log Types +""" + +from wuttafarm.db.model.logs import LogType +from wuttafarm.web.views import WuttaFarmMasterView + + +class LogTypeView(WuttaFarmMasterView): + """ + Master view for Log Types + """ + + model_class = LogType + route_prefix = "log_types" + url_prefix = "/log-types" + + grid_columns = [ + "name", + "description", + ] + + sort_defaults = "name" + + filter_defaults = { + "name": {"active": True, "verb": "contains"}, + } + + form_fields = [ + "name", + "description", + "farmos_uuid", + "drupal_id", + ] + + def configure_grid(self, grid): + g = grid + super().configure_grid(g) + + # name + g.set_link("name") + + def get_xref_buttons(self, log_type): + buttons = super().get_xref_buttons(log_type) + + if log_type.farmos_uuid: + buttons.append( + self.make_button( + "View farmOS record", + primary=True, + url=self.request.route_url( + "farmos_log_types.view", uuid=log_type.farmos_uuid + ), + icon_left="eye", + ) + ) + + return buttons + + +def defaults(config, **kwargs): + base = globals() + + LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"]) + LogTypeView.defaults(config) + + +def includeme(config): + defaults(config) diff --git a/src/wuttafarm/web/views/logs.py b/src/wuttafarm/web/views/logs.py deleted file mode 100644 index cf77967..0000000 --- a/src/wuttafarm/web/views/logs.py +++ /dev/null @@ -1,342 +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 views for Logs -""" - -from collections import OrderedDict - -import colander - -from wuttaweb.forms.schema import WuttaDictEnum -from wuttaweb.db import Session -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 LogAssetRefs - - -def get_log_type_enum(config): - app = config.get_app() - model = app.model - session = Session() - log_types = OrderedDict() - query = session.query(model.LogType).order_by(model.LogType.name) - for log_type in query: - log_types[log_type.drupal_id] = log_type.name - return log_types - - -class LogTypeView(WuttaFarmMasterView): - """ - Master view for Log Types - """ - - model_class = LogType - route_prefix = "log_types" - url_prefix = "/log-types" - - grid_columns = [ - "name", - "description", - ] - - sort_defaults = "name" - - filter_defaults = { - "name": {"active": True, "verb": "contains"}, - } - - form_fields = [ - "name", - "description", - "farmos_uuid", - "drupal_id", - ] - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # name - g.set_link("name") - - def get_xref_buttons(self, log_type): - buttons = super().get_xref_buttons(log_type) - - if log_type.farmos_uuid: - buttons.append( - self.make_button( - "View farmOS record", - primary=True, - url=self.request.route_url( - "farmos_log_types.view", uuid=log_type.farmos_uuid - ), - icon_left="eye", - ) - ) - - return buttons - - -class LogView(WuttaFarmMasterView): - """ - Master view for All Logs - """ - - model_class = Log - route_prefix = "log" - url_prefix = "/logs" - - farmos_refurl_path = "/logs" - - viewable = False - creatable = False - editable = False - deletable = False - model_is_versioned = False - - labels = { - "message": "Log Name", - } - - grid_columns = [ - "status", - "drupal_id", - "timestamp", - "message", - "log_type", - "assets", - "location", - "quantity", - "groups", - "is_group_assignment", - ] - - sort_defaults = ("timestamp", "desc") - - filter_defaults = { - "message": {"active": True, "verb": "contains"}, - } - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # drupal_id - g.set_label("drupal_id", "ID", column_only=True) - - # timestamp - g.set_renderer("timestamp", "date") - g.set_link("timestamp") - - # message - g.set_link("message") - - # log_type - g.set_enum("log_type", get_log_type_enum(self.config)) - - # assets - g.set_renderer("assets", self.render_assets_for_grid) - - # view action links to final log record - def log_url(log, i): - return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid) - - g.add_action("view", icon="eye", url=log_url) - - def render_assets_for_grid(self, log, field, value): - assets = [str(a.asset) for a in log._assets] - return ", ".join(assets) - - -class LogMasterView(WuttaFarmMasterView): - """ - Base class for Asset master views - """ - - grid_columns = [ - "status", - "drupal_id", - "timestamp", - "message", - "assets", - "location", - "quantity", - "is_group_assignment", - ] - - sort_defaults = ("timestamp", "desc") - - filter_defaults = { - "message": {"active": True, "verb": "contains"}, - } - - form_fields = [ - "message", - "timestamp", - "assets", - "location", - "quantity", - "notes", - "status", - "log_type", - "owners", - "is_group_assignment", - "farmos_uuid", - "drupal_id", - ] - - def get_query(self, session=None): - """ """ - model = self.app.model - model_class = self.get_model_class() - session = session or self.Session() - return session.query(model_class).join(model.Log) - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - model = self.app.model - - # status - g.set_sorter("status", model.Log.status) - g.set_filter("status", model.Log.status) - - # drupal_id - g.set_label("drupal_id", "ID", column_only=True) - g.set_sorter("drupal_id", model.Log.drupal_id) - g.set_filter("drupal_id", model.Log.drupal_id) - - # timestamp - g.set_renderer("timestamp", "date") - g.set_link("timestamp") - g.set_sorter("timestamp", model.Log.timestamp) - g.set_filter("timestamp", model.Log.timestamp) - - # message - g.set_link("message") - g.set_sorter("message", model.Log.message) - g.set_filter("message", model.Log.message) - - # assets - g.set_renderer("assets", self.render_assets_for_grid) - - def render_assets_for_grid(self, log, field, value): - return ", ".join([a.asset.asset_name for a in log.log._assets]) - - def configure_form(self, form): - f = form - super().configure_form(f) - enum = self.app.enum - log = f.model_instance - - # timestamp - # TODO: the widget should be automatic (assn proxy field) - f.set_widget("timestamp", WuttaDateTimeWidget(self.request)) - if self.creating: - f.set_default("timestamp", self.app.make_utc()) - - # assets - if self.creating or self.editing: - f.remove("assets") # TODO: need to support this - else: - f.set_node("assets", LogAssetRefs(self.request)) - f.set_default("assets", [a.asset_uuid for a in log.log._assets]) - - # location - if self.creating or self.editing: - f.remove("location") # TODO: need to support this - - # log_type - if self.creating: - f.remove("log_type") - else: - f.set_node( - "log_type", - WuttaDictEnum(self.request, get_log_type_enum(self.config)), - ) - f.set_readonly("log_type") - - # quantity - if self.creating or self.editing: - f.remove("quantity") # TODO: need to support this - - # notes - f.set_widget("notes", "notes") - - # owners - if self.creating or self.editing: - f.remove("owners") # TODO: need to support this - - # status - f.set_node("status", WuttaDictEnum(self.request, enum.LOG_STATUS)) - - # is_group_assignment - f.set_node("is_group_assignment", colander.Boolean()) - - def objectify(self, form): - log = super().objectify(form) - - if self.creating: - model_class = self.get_model_class() - log.log_type = self.get_farmos_log_type() - - return log - - def get_farmos_url(self, log): - return self.app.get_farmos_url(f"/log/{log.drupal_id}") - - def get_farmos_log_type(self): - return self.model_class.__wutta_hint__["farmos_log_type"] - - def get_xref_buttons(self, log): - buttons = super().get_xref_buttons(log) - - if log.farmos_uuid: - log_type = self.get_farmos_log_type() - route = f"farmos_logs_{log_type}.view" - buttons.append( - self.make_button( - "View farmOS record", - primary=True, - url=self.request.route_url(route, uuid=log.farmos_uuid), - icon_left="eye", - ) - ) - - return buttons - - -def defaults(config, **kwargs): - base = globals() - - LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"]) - LogTypeView.defaults(config) - - LogView = kwargs.get("LogView", base["LogView"]) - LogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/logs_activity.py b/src/wuttafarm/web/views/logs_activity.py index dda3ca7..a2b2154 100644 --- a/src/wuttafarm/web/views/logs_activity.py +++ b/src/wuttafarm/web/views/logs_activity.py @@ -23,21 +23,76 @@ Master view for Activity Logs """ -from wuttafarm.web.views.logs import LogMasterView -from wuttafarm.db.model import ActivityLog +from wuttafarm.db.model.logs import ActivityLog +from wuttafarm.web.views import WuttaFarmMasterView -class ActivityLogView(LogMasterView): +class ActivityLogView(WuttaFarmMasterView): """ Master view for Activity Logs """ model_class = ActivityLog - route_prefix = "logs_activity" + route_prefix = "activity_logs" url_prefix = "/logs/activity" farmos_refurl_path = "/logs/activity" + grid_columns = [ + "message", + "timestamp", + "status", + ] + + sort_defaults = ("timestamp", "desc") + + filter_defaults = { + "message": {"active": True, "verb": "contains"}, + } + + form_fields = [ + "message", + "timestamp", + "status", + "notes", + "farmos_uuid", + "drupal_id", + ] + + def configure_grid(self, grid): + g = grid + super().configure_grid(g) + + # message + g.set_link("message") + + def configure_form(self, form): + f = form + super().configure_form(f) + + # notes + f.set_widget("notes", "notes") + + def get_farmos_url(self, log): + return self.app.get_farmos_url(f"/log/{log.drupal_id}") + + def get_xref_buttons(self, log): + buttons = super().get_xref_buttons(log) + + if log.farmos_uuid: + buttons.append( + self.make_button( + "View farmOS record", + primary=True, + url=self.request.route_url( + "farmos_logs_activity.view", uuid=log.farmos_uuid + ), + icon_left="eye", + ) + ) + + return buttons + def defaults(config, **kwargs): base = globals() diff --git a/src/wuttafarm/web/views/logs_harvest.py b/src/wuttafarm/web/views/logs_harvest.py deleted file mode 100644 index 825c864..0000000 --- a/src/wuttafarm/web/views/logs_harvest.py +++ /dev/null @@ -1,50 +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 Harvest Logs -""" - -from wuttafarm.web.views.logs import LogMasterView -from wuttafarm.db.model import HarvestLog - - -class HarvestLogView(LogMasterView): - """ - Master view for Harvest Logs - """ - - model_class = HarvestLog - route_prefix = "logs_harvest" - url_prefix = "/logs/harvest" - - farmos_refurl_path = "/logs/harvest" - - -def defaults(config, **kwargs): - base = globals() - - HarvestLogView = kwargs.get("HarvestLogView", base["HarvestLogView"]) - HarvestLogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/logs_medical.py b/src/wuttafarm/web/views/logs_medical.py deleted file mode 100644 index d582db9..0000000 --- a/src/wuttafarm/web/views/logs_medical.py +++ /dev/null @@ -1,50 +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 Medical Logs -""" - -from wuttafarm.web.views.logs import LogMasterView -from wuttafarm.db.model import MedicalLog - - -class MedicalLogView(LogMasterView): - """ - Master view for Medical Logs - """ - - model_class = MedicalLog - route_prefix = "logs_medical" - url_prefix = "/logs/medical" - - farmos_refurl_path = "/logs/medical" - - -def defaults(config, **kwargs): - base = globals() - - MedicalLogView = kwargs.get("MedicalLogView", base["MedicalLogView"]) - MedicalLogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/logs_observation.py b/src/wuttafarm/web/views/logs_observation.py deleted file mode 100644 index a4b9e8e..0000000 --- a/src/wuttafarm/web/views/logs_observation.py +++ /dev/null @@ -1,50 +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 Observation Logs -""" - -from wuttafarm.web.views.logs import LogMasterView -from wuttafarm.db.model import ObservationLog - - -class ObservationLogView(LogMasterView): - """ - Master view for Observation Logs - """ - - model_class = ObservationLog - route_prefix = "logs_observation" - url_prefix = "/logs/observation" - - farmos_refurl_path = "/logs/observation" - - -def defaults(config, **kwargs): - base = globals() - - ObservationLogView = kwargs.get("ObservationLogView", base["ObservationLogView"]) - ObservationLogView.defaults(config) - - -def includeme(config): - defaults(config) diff --git a/src/wuttafarm/web/views/plants.py b/src/wuttafarm/web/views/plants.py deleted file mode 100644 index d92949a..0000000 --- a/src/wuttafarm/web/views/plants.py +++ /dev/null @@ -1,201 +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 wuttaweb.forms.schema import WuttaDictEnum - -from wuttafarm.db.model import PlantType, PlantAsset -from wuttafarm.web.views.assets import AssetTypeMasterView, AssetMasterView -from wuttafarm.web.forms.schema import PlantTypeRefs -from wuttafarm.web.forms.widgets import ImageWidget - - -class PlantTypeView(AssetTypeMasterView): - """ - Master view for Plant Types - """ - - model_class = PlantType - route_prefix = "plant_types" - url_prefix = "/plant-types" - - farmos_refurl_path = "/admin/structure/taxonomy/manage/plant_type/overview" - - grid_columns = [ - "name", - "description", - ] - - sort_defaults = "name" - - filter_defaults = { - "name": {"active": True, "verb": "contains"}, - } - - form_fields = [ - "name", - "description", - "farmos_uuid", - "drupal_id", - ] - - 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, plant_type): - return self.app.get_farmos_url(f"/taxonomy/term/{plant_type.drupal_id}") - - def get_xref_buttons(self, plant_type): - buttons = super().get_xref_buttons(plant_type) - - if plant_type.farmos_uuid: - buttons.append( - self.make_button( - "View farmOS record", - primary=True, - url=self.request.route_url( - "farmos_plant_types.view", uuid=plant_type.farmos_uuid - ), - icon_left="eye", - ) - ) - - return buttons - - def get_row_grid_data(self, plant_type): - model = self.app.model - session = self.Session() - return ( - session.query(model.PlantAsset) - .join(model.Asset) - .outerjoin(model.PlantAssetPlantType) - .filter(model.PlantAssetPlantType.plant_type == plant_type) - ) - - 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) - - -class PlantAssetView(AssetMasterView): - """ - Master view for Plant Assets - """ - - model_class = PlantAsset - route_prefix = "plant_assets" - url_prefix = "/assets/plant" - - farmos_refurl_path = "/assets/plant" - - labels = { - "plant_types": "Crop/Variety", - } - - grid_columns = [ - "thumbnail", - "drupal_id", - "asset_name", - "plant_types", - "season", - "archived", - ] - - form_fields = [ - "asset_name", - "plant_types", - "season", - "notes", - "asset_type", - "archived", - "farmos_uuid", - "drupal_id", - "thumbnail_url", - "image_url", - "thumbnail", - "image", - ] - - def configure_grid(self, grid): - g = grid - super().configure_grid(g) - - # plant_types - g.set_renderer("plant_types", self.render_grid_plant_types) - - def render_grid_plant_types(self, plant, field, value): - return ", ".join([t.plant_type.name for t in plant._plant_types]) - - def configure_form(self, form): - f = form - super().configure_form(f) - enum = self.app.enum - plant = f.model_instance - - # plant_types - f.set_node("plant_types", PlantTypeRefs(self.request)) - f.set_default("plant_types", [t.plant_type_uuid for t in plant._plant_types]) - - -def defaults(config, **kwargs): - base = globals() - - PlantTypeView = kwargs.get("PlantTypeView", base["PlantTypeView"]) - PlantTypeView.defaults(config) - - PlantAssetView = kwargs.get("PlantAssetView", base["PlantAssetView"]) - PlantAssetView.defaults(config) - - -def includeme(config): - defaults(config)