feat: add more assets (plant) and logs (harvest, medical, observation)
This commit is contained in:
parent
b061959b18
commit
2e0ec73317
31 changed files with 2847 additions and 206 deletions
|
|
@ -0,0 +1,596 @@
|
|||
"""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")
|
||||
|
|
@ -35,5 +35,9 @@ from .asset_land import LandType, LandAsset
|
|||
from .asset_structure import StructureType, StructureAsset
|
||||
from .asset_animal import AnimalType, AnimalAsset
|
||||
from .asset_group import GroupAsset
|
||||
from .log import LogType, Log
|
||||
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
|
||||
|
|
|
|||
132
src/wuttafarm/db/model/asset_plant.py
Normal file
132
src/wuttafarm/db/model/asset_plant.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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.
|
||||
""",
|
||||
)
|
||||
|
|
@ -92,7 +92,7 @@ class Log(model.Base):
|
|||
__versioned__ = {}
|
||||
__wutta_hint__ = {
|
||||
"model_title": "Log",
|
||||
"model_title_plural": "Logs",
|
||||
"model_title_plural": "All Logs",
|
||||
}
|
||||
|
||||
uuid = model.uuid_column()
|
||||
|
|
@ -153,6 +153,8 @@ class Log(model.Base):
|
|||
""",
|
||||
)
|
||||
|
||||
_assets = orm.relationship("LogAsset", back_populates="log")
|
||||
|
||||
def __str__(self):
|
||||
return self.message or ""
|
||||
|
||||
|
|
@ -177,3 +179,27 @@ def add_log_proxies(subclass):
|
|||
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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ from wuttafarm.db.model.log import LogMixin, add_log_proxies
|
|||
|
||||
class ActivityLog(LogMixin, model.Base):
|
||||
"""
|
||||
Represents an activity log from farmOS
|
||||
Represents an Activity Log from farmOS
|
||||
"""
|
||||
|
||||
__tablename__ = "log_activity"
|
||||
|
|
|
|||
45
src/wuttafarm/db/model/log_harvest.py
Normal file
45
src/wuttafarm/db/model/log_harvest.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
45
src/wuttafarm/db/model/log_medical.py
Normal file
45
src/wuttafarm/db/model/log_medical.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
45
src/wuttafarm/db/model/log_observation.py
Normal file
45
src/wuttafarm/db/model/log_observation.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
|
|
@ -34,3 +34,12 @@ ANIMAL_SEX = OrderedDict(
|
|||
("F", "Female"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
LOG_STATUS = OrderedDict(
|
||||
[
|
||||
("pending", "Pending"),
|
||||
("done", "Done"),
|
||||
("abandoned", "Abandoned"),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -363,3 +363,113 @@ 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"
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ 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
|
||||
|
||||
|
||||
|
|
@ -261,3 +265,62 @@ 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
|
||||
|
|
|
|||
|
|
@ -104,8 +104,13 @@ 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,6 +149,9 @@ 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())
|
||||
|
|
@ -174,6 +182,12 @@ 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)
|
||||
|
|
@ -283,71 +297,6 @@ class AssetImporterBase(FromFarmOS, ToWutta):
|
|||
return asset
|
||||
|
||||
|
||||
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_source_objects(self):
|
||||
""" """
|
||||
log_type = self.get_farmos_log_type()
|
||||
result = self.farmos_client.log.get(log_type)
|
||||
return result["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"],
|
||||
"log_type": self.get_farmos_log_type(),
|
||||
"message": log["attributes"]["name"],
|
||||
"timestamp": self.normalize_datetime(log["attributes"]["timestamp"]),
|
||||
"notes": notes,
|
||||
"status": log["attributes"]["status"],
|
||||
}
|
||||
|
||||
|
||||
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",
|
||||
]
|
||||
|
||||
|
||||
class AnimalAssetImporter(AssetImporterBase):
|
||||
"""
|
||||
farmOS API → WuttaFarm importer for Animals
|
||||
|
|
@ -604,12 +553,12 @@ class LandTypeImporter(FromFarmOS, ToWutta):
|
|||
}
|
||||
|
||||
|
||||
class LogTypeImporter(FromFarmOS, ToWutta):
|
||||
class PlantTypeImporter(FromFarmOS, ToWutta):
|
||||
"""
|
||||
farmOS API → WuttaFarm importer for Log Types
|
||||
farmOS API → WuttaFarm importer for Plant Types
|
||||
"""
|
||||
|
||||
model_class = model.LogType
|
||||
model_class = model.PlantType
|
||||
|
||||
supported_fields = [
|
||||
"farmos_uuid",
|
||||
|
|
@ -620,19 +569,112 @@ class LogTypeImporter(FromFarmOS, ToWutta):
|
|||
|
||||
def get_source_objects(self):
|
||||
""" """
|
||||
log_types = self.farmos_client.resource.get("log_type")
|
||||
return log_types["data"]
|
||||
result = self.farmos_client.resource.get("taxonomy_term", "plant_type")
|
||||
return result["data"]
|
||||
|
||||
def normalize_source_object(self, log_type):
|
||||
def normalize_source_object(self, plant_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"],
|
||||
"farmos_uuid": UUID(plant_type["id"]),
|
||||
"drupal_id": plant_type["attributes"]["drupal_internal__tid"],
|
||||
"name": plant_type["attributes"]["name"],
|
||||
"description": plant_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
|
||||
|
|
@ -768,3 +810,229 @@ 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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -74,6 +74,25 @@ 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
|
||||
|
|
@ -99,6 +118,23 @@ 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):
|
||||
|
|
@ -177,3 +213,20 @@ 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)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,67 @@ 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.
|
||||
|
|
@ -166,3 +227,34 @@ 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)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ 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",
|
||||
|
|
@ -80,6 +85,11 @@ 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",
|
||||
|
|
@ -99,9 +109,29 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
|||
"type": "menu",
|
||||
"items": [
|
||||
{
|
||||
"title": "Activity Logs",
|
||||
"route": "activity_logs",
|
||||
"perm": "activity_logs.list",
|
||||
"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",
|
||||
},
|
||||
{"type": "sep"},
|
||||
{
|
||||
|
|
@ -135,6 +165,11 @@ 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",
|
||||
|
|
@ -151,12 +186,32 @@ 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",
|
||||
|
|
|
|||
|
|
@ -47,8 +47,12 @@ 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.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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
from wuttafarm.db.model import Asset, Log
|
||||
from wuttafarm.web.forms.schema import AssetParentRefs
|
||||
from wuttafarm.web.forms.widgets import ImageWidget
|
||||
|
||||
|
|
@ -140,6 +140,28 @@ 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)
|
||||
|
||||
|
|
@ -265,6 +287,8 @@ 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"
|
||||
|
||||
|
|
@ -280,6 +304,39 @@ 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()
|
||||
|
|
|
|||
|
|
@ -36,5 +36,9 @@ 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")
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ 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"],
|
||||
|
|
@ -124,7 +127,7 @@ class GroupView(FarmOSMasterView):
|
|||
"is_fixed": group["attributes"]["is_fixed"],
|
||||
"is_location": group["attributes"]["is_location"],
|
||||
"archived": archived,
|
||||
"notes": group["attributes"]["notes"]["value"],
|
||||
"notes": notes or colander.null,
|
||||
}
|
||||
|
||||
def configure_form(self, form):
|
||||
|
|
|
|||
142
src/wuttafarm/web/views/farmos/logs.py
Normal file
142
src/wuttafarm/web/views/farmos/logs.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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
|
||||
|
|
@ -20,20 +20,13 @@
|
|||
#
|
||||
################################################################################
|
||||
"""
|
||||
View for farmOS activity logs
|
||||
View for farmOS Activity Logs
|
||||
"""
|
||||
|
||||
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.views.farmos.logs import LogMasterView
|
||||
|
||||
|
||||
class ActivityLogView(FarmOSMasterView):
|
||||
class ActivityLogView(LogMasterView):
|
||||
"""
|
||||
View for farmOS activity logs
|
||||
"""
|
||||
|
|
@ -45,105 +38,9 @@ class ActivityLogView(FarmOSMasterView):
|
|||
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()
|
||||
|
|
|
|||
53
src/wuttafarm/web/views/farmos/logs_harvest.py
Normal file
53
src/wuttafarm/web/views/farmos/logs_harvest.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
53
src/wuttafarm/web/views/farmos/logs_medical.py
Normal file
53
src/wuttafarm/web/views/farmos/logs_medical.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
53
src/wuttafarm/web/views/farmos/logs_observation.py
Normal file
53
src/wuttafarm/web/views/farmos/logs_observation.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
362
src/wuttafarm/web/views/farmos/plants.py
Normal file
362
src/wuttafarm/web/views/farmos/plants.py
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
|
|
@ -25,12 +25,15 @@ 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
|
||||
from wuttafarm.db.model import LogType, Log
|
||||
from wuttafarm.web.forms.schema import LogAssetRefs
|
||||
|
||||
|
||||
def get_log_type_enum(config):
|
||||
|
|
@ -96,6 +99,77 @@ class LogTypeView(WuttaFarmMasterView):
|
|||
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
|
||||
|
|
@ -165,13 +239,34 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
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:
|
||||
|
|
@ -183,23 +278,44 @@ class LogMasterView(WuttaFarmMasterView):
|
|||
)
|
||||
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:
|
||||
|
||||
# TODO
|
||||
route = None
|
||||
if log.log_type == "activity":
|
||||
route = "farmos_logs_activity.view"
|
||||
|
||||
if route:
|
||||
log_type = self.get_farmos_log_type()
|
||||
route = f"farmos_logs_{log_type}.view"
|
||||
buttons.append(
|
||||
self.make_button(
|
||||
"View farmOS record",
|
||||
|
|
@ -218,6 +334,9 @@ def defaults(config, **kwargs):
|
|||
LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"])
|
||||
LogTypeView.defaults(config)
|
||||
|
||||
LogView = kwargs.get("LogView", base["LogView"])
|
||||
LogView.defaults(config)
|
||||
|
||||
|
||||
def includeme(config):
|
||||
defaults(config)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class ActivityLogView(LogMasterView):
|
|||
"""
|
||||
|
||||
model_class = ActivityLog
|
||||
route_prefix = "activity_logs"
|
||||
route_prefix = "logs_activity"
|
||||
url_prefix = "/logs/activity"
|
||||
|
||||
farmos_refurl_path = "/logs/activity"
|
||||
|
|
|
|||
50
src/wuttafarm/web/views/logs_harvest.py
Normal file
50
src/wuttafarm/web/views/logs_harvest.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
50
src/wuttafarm/web/views/logs_medical.py
Normal file
50
src/wuttafarm/web/views/logs_medical.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
50
src/wuttafarm/web/views/logs_observation.py
Normal file
50
src/wuttafarm/web/views/logs_observation.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
201
src/wuttafarm/web/views/plants.py
Normal file
201
src/wuttafarm/web/views/plants.py
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaFarm --Web app to integrate with and extend farmOS
|
||||
# Copyright © 2026 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaFarm.
|
||||
#
|
||||
# WuttaFarm is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue