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_structure import StructureType, StructureAsset
|
||||||
from .asset_animal import AnimalType, AnimalAsset
|
from .asset_animal import AnimalType, AnimalAsset
|
||||||
from .asset_group import GroupAsset
|
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_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__ = {}
|
__versioned__ = {}
|
||||||
__wutta_hint__ = {
|
__wutta_hint__ = {
|
||||||
"model_title": "Log",
|
"model_title": "Log",
|
||||||
"model_title_plural": "Logs",
|
"model_title_plural": "All Logs",
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid = model.uuid_column()
|
uuid = model.uuid_column()
|
||||||
|
|
@ -153,6 +153,8 @@ class Log(model.Base):
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_assets = orm.relationship("LogAsset", back_populates="log")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.message or ""
|
return self.message or ""
|
||||||
|
|
||||||
|
|
@ -177,3 +179,27 @@ def add_log_proxies(subclass):
|
||||||
Log.make_proxy(subclass, "log", "timestamp")
|
Log.make_proxy(subclass, "log", "timestamp")
|
||||||
Log.make_proxy(subclass, "log", "status")
|
Log.make_proxy(subclass, "log", "status")
|
||||||
Log.make_proxy(subclass, "log", "notes")
|
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):
|
class ActivityLog(LogMixin, model.Base):
|
||||||
"""
|
"""
|
||||||
Represents an activity log from farmOS
|
Represents an Activity Log from farmOS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "log_activity"
|
__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"),
|
("F", "Female"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LOG_STATUS = OrderedDict(
|
||||||
|
[
|
||||||
|
("pending", "Pending"),
|
||||||
|
("done", "Done"),
|
||||||
|
("abandoned", "Abandoned"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -363,3 +363,113 @@ class StructureAssetImporter(ToFarmOSAsset):
|
||||||
payload["attributes"].update(attrs)
|
payload["attributes"].update(attrs)
|
||||||
|
|
||||||
return payload
|
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["AnimalType"] = AnimalTypeImporter
|
||||||
importers["AnimalAsset"] = AnimalAssetImporter
|
importers["AnimalAsset"] = AnimalAssetImporter
|
||||||
importers["GroupAsset"] = GroupAssetImporter
|
importers["GroupAsset"] = GroupAssetImporter
|
||||||
|
importers["ActivityLog"] = ActivityLogImporter
|
||||||
|
importers["HarvestLog"] = HarvestLogImporter
|
||||||
|
importers["MedicalLog"] = MedicalLogImporter
|
||||||
|
importers["ObservationLog"] = ObservationLogImporter
|
||||||
return importers
|
return importers
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -261,3 +265,62 @@ class StructureAssetImporter(
|
||||||
"archived": structure.archived,
|
"archived": structure.archived,
|
||||||
"_src_object": structure,
|
"_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["AnimalType"] = AnimalTypeImporter
|
||||||
importers["AnimalAsset"] = AnimalAssetImporter
|
importers["AnimalAsset"] = AnimalAssetImporter
|
||||||
importers["GroupAsset"] = GroupAssetImporter
|
importers["GroupAsset"] = GroupAssetImporter
|
||||||
|
importers["PlantType"] = PlantTypeImporter
|
||||||
|
importers["PlantAsset"] = PlantAssetImporter
|
||||||
importers["LogType"] = LogTypeImporter
|
importers["LogType"] = LogTypeImporter
|
||||||
importers["ActivityLog"] = ActivityLogImporter
|
importers["ActivityLog"] = ActivityLogImporter
|
||||||
|
importers["HarvestLog"] = HarvestLogImporter
|
||||||
|
importers["MedicalLog"] = MedicalLogImporter
|
||||||
|
importers["ObservationLog"] = ObservationLogImporter
|
||||||
return importers
|
return importers
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,6 +149,9 @@ class AssetImporterBase(FromFarmOS, ToWutta):
|
||||||
Base class for farmOS API → WuttaFarm asset importers
|
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):
|
def get_simple_fields(self):
|
||||||
""" """
|
""" """
|
||||||
fields = list(super().get_simple_fields())
|
fields = list(super().get_simple_fields())
|
||||||
|
|
@ -174,6 +182,12 @@ class AssetImporterBase(FromFarmOS, ToWutta):
|
||||||
)
|
)
|
||||||
return fields
|
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):
|
def normalize_source_data(self, **kwargs):
|
||||||
""" """
|
""" """
|
||||||
data = super().normalize_source_data(**kwargs)
|
data = super().normalize_source_data(**kwargs)
|
||||||
|
|
@ -283,71 +297,6 @@ class AssetImporterBase(FromFarmOS, ToWutta):
|
||||||
return asset
|
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):
|
class AnimalAssetImporter(AssetImporterBase):
|
||||||
"""
|
"""
|
||||||
farmOS API → WuttaFarm importer for Animals
|
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 = [
|
supported_fields = [
|
||||||
"farmos_uuid",
|
"farmos_uuid",
|
||||||
|
|
@ -620,19 +569,112 @@ class LogTypeImporter(FromFarmOS, ToWutta):
|
||||||
|
|
||||||
def get_source_objects(self):
|
def get_source_objects(self):
|
||||||
""" """
|
""" """
|
||||||
log_types = self.farmos_client.resource.get("log_type")
|
result = self.farmos_client.resource.get("taxonomy_term", "plant_type")
|
||||||
return log_types["data"]
|
return result["data"]
|
||||||
|
|
||||||
def normalize_source_object(self, log_type):
|
def normalize_source_object(self, plant_type):
|
||||||
""" """
|
""" """
|
||||||
return {
|
return {
|
||||||
"farmos_uuid": UUID(log_type["id"]),
|
"farmos_uuid": UUID(plant_type["id"]),
|
||||||
"drupal_id": log_type["attributes"]["drupal_internal__id"],
|
"drupal_id": plant_type["attributes"]["drupal_internal__tid"],
|
||||||
"name": log_type["attributes"]["label"],
|
"name": plant_type["attributes"]["name"],
|
||||||
"description": log_type["attributes"]["description"],
|
"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):
|
class StructureAssetImporter(AssetImporterBase):
|
||||||
"""
|
"""
|
||||||
farmOS API → WuttaFarm importer for Structure Assets
|
farmOS API → WuttaFarm importer for Structure Assets
|
||||||
|
|
@ -768,3 +810,229 @@ class UserImporter(FromFarmOS, ToWutta):
|
||||||
if not user.farmos_uuid:
|
if not user.farmos_uuid:
|
||||||
return False
|
return False
|
||||||
return True
|
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)
|
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):
|
class LandTypeRef(ObjectRef):
|
||||||
"""
|
"""
|
||||||
Custom schema type for a
|
Custom schema type for a
|
||||||
|
|
@ -99,6 +118,23 @@ class LandTypeRef(ObjectRef):
|
||||||
return self.request.route_url("land_types.view", uuid=land_type.uuid)
|
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):
|
class StructureType(colander.SchemaType):
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
|
@ -177,3 +213,20 @@ class AssetParentRefs(WuttaSet):
|
||||||
from wuttafarm.web.forms.widgets import AssetParentRefsWidget
|
from wuttafarm.web.forms.widgets import AssetParentRefsWidget
|
||||||
|
|
||||||
return AssetParentRefsWidget(self.request, **kwargs)
|
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)
|
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):
|
class StructureWidget(Widget):
|
||||||
"""
|
"""
|
||||||
Widget to display a "structure" field.
|
Widget to display a "structure" field.
|
||||||
|
|
@ -166,3 +227,34 @@ class AssetParentRefsWidget(WuttaCheckboxChoiceWidget):
|
||||||
return HTML.tag("ul", c=parents)
|
return HTML.tag("ul", c=parents)
|
||||||
|
|
||||||
return super().serialize(field, cstruct, **kw)
|
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",
|
"route": "land_assets",
|
||||||
"perm": "land_assets.list",
|
"perm": "land_assets.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Plant",
|
||||||
|
"route": "plant_assets",
|
||||||
|
"perm": "plant_assets.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Structure",
|
"title": "Structure",
|
||||||
"route": "structure_assets",
|
"route": "structure_assets",
|
||||||
|
|
@ -80,6 +85,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "land_types",
|
"route": "land_types",
|
||||||
"perm": "land_types.list",
|
"perm": "land_types.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Plant Types",
|
||||||
|
"route": "plant_types",
|
||||||
|
"perm": "plant_types.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Structure Types",
|
"title": "Structure Types",
|
||||||
"route": "structure_types",
|
"route": "structure_types",
|
||||||
|
|
@ -99,9 +109,29 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"type": "menu",
|
"type": "menu",
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"title": "Activity Logs",
|
"title": "All Logs",
|
||||||
"route": "activity_logs",
|
"route": "log",
|
||||||
"perm": "activity_logs.list",
|
"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"},
|
{"type": "sep"},
|
||||||
{
|
{
|
||||||
|
|
@ -135,6 +165,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "farmos_groups",
|
"route": "farmos_groups",
|
||||||
"perm": "farmos_groups.list",
|
"perm": "farmos_groups.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Plants",
|
||||||
|
"route": "farmos_asset_plant",
|
||||||
|
"perm": "farmos_asset_plant.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Structures",
|
"title": "Structures",
|
||||||
"route": "farmos_structures",
|
"route": "farmos_structures",
|
||||||
|
|
@ -151,12 +186,32 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "farmos_logs_activity",
|
"route": "farmos_logs_activity",
|
||||||
"perm": "farmos_logs_activity.list",
|
"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"},
|
{"type": "sep"},
|
||||||
{
|
{
|
||||||
"title": "Animal Types",
|
"title": "Animal Types",
|
||||||
"route": "farmos_animal_types",
|
"route": "farmos_animal_types",
|
||||||
"perm": "farmos_animal_types.list",
|
"perm": "farmos_animal_types.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Plant Types",
|
||||||
|
"route": "farmos_plant_types",
|
||||||
|
"perm": "farmos_plant_types.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Structure Types",
|
"title": "Structure Types",
|
||||||
"route": "farmos_structure_types",
|
"route": "farmos_structure_types",
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,12 @@ def includeme(config):
|
||||||
config.include("wuttafarm.web.views.structures")
|
config.include("wuttafarm.web.views.structures")
|
||||||
config.include("wuttafarm.web.views.animals")
|
config.include("wuttafarm.web.views.animals")
|
||||||
config.include("wuttafarm.web.views.groups")
|
config.include("wuttafarm.web.views.groups")
|
||||||
|
config.include("wuttafarm.web.views.plants")
|
||||||
config.include("wuttafarm.web.views.logs")
|
config.include("wuttafarm.web.views.logs")
|
||||||
config.include("wuttafarm.web.views.logs_activity")
|
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
|
# views for farmOS
|
||||||
config.include("wuttafarm.web.views.farmos")
|
config.include("wuttafarm.web.views.farmos")
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ from wuttaweb.forms.schema import WuttaDictEnum
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
|
|
||||||
from wuttafarm.web.views import WuttaFarmMasterView
|
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.schema import AssetParentRefs
|
||||||
from wuttafarm.web.forms.widgets import ImageWidget
|
from wuttafarm.web.forms.widgets import ImageWidget
|
||||||
|
|
||||||
|
|
@ -140,6 +140,28 @@ class AssetMasterView(WuttaFarmMasterView):
|
||||||
"archived": {"active": True, "verb": "is_false"},
|
"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):
|
def get_fallback_templates(self, template):
|
||||||
templates = super().get_fallback_templates(template)
|
templates = super().get_fallback_templates(template)
|
||||||
|
|
||||||
|
|
@ -265,6 +287,8 @@ class AssetMasterView(WuttaFarmMasterView):
|
||||||
route = "farmos_groups.view"
|
route = "farmos_groups.view"
|
||||||
elif asset.asset_type == "land":
|
elif asset.asset_type == "land":
|
||||||
route = "farmos_land_assets.view"
|
route = "farmos_land_assets.view"
|
||||||
|
elif asset.asset_type == "plant":
|
||||||
|
route = "farmos_asset_plant.view"
|
||||||
elif asset.asset_type == "structure":
|
elif asset.asset_type == "structure":
|
||||||
route = "farmos_structures.view"
|
route = "farmos_structures.view"
|
||||||
|
|
||||||
|
|
@ -280,6 +304,39 @@ class AssetMasterView(WuttaFarmMasterView):
|
||||||
|
|
||||||
return buttons
|
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):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
base = globals()
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,9 @@ def includeme(config):
|
||||||
config.include("wuttafarm.web.views.farmos.animal_types")
|
config.include("wuttafarm.web.views.farmos.animal_types")
|
||||||
config.include("wuttafarm.web.views.farmos.animals")
|
config.include("wuttafarm.web.views.farmos.animals")
|
||||||
config.include("wuttafarm.web.views.farmos.groups")
|
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.log_types")
|
||||||
config.include("wuttafarm.web.views.farmos.logs_activity")
|
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:
|
else:
|
||||||
archived = group["attributes"]["status"] == "archived"
|
archived = group["attributes"]["status"] == "archived"
|
||||||
|
|
||||||
|
if notes := group["attributes"]["notes"]:
|
||||||
|
notes = notes["value"]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"uuid": group["id"],
|
"uuid": group["id"],
|
||||||
"drupal_id": group["attributes"]["drupal_internal__id"],
|
"drupal_id": group["attributes"]["drupal_internal__id"],
|
||||||
|
|
@ -124,7 +127,7 @@ class GroupView(FarmOSMasterView):
|
||||||
"is_fixed": group["attributes"]["is_fixed"],
|
"is_fixed": group["attributes"]["is_fixed"],
|
||||||
"is_location": group["attributes"]["is_location"],
|
"is_location": group["attributes"]["is_location"],
|
||||||
"archived": archived,
|
"archived": archived,
|
||||||
"notes": group["attributes"]["notes"]["value"],
|
"notes": notes or colander.null,
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure_form(self, form):
|
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
|
from wuttafarm.web.views.farmos.logs import LogMasterView
|
||||||
|
|
||||||
import colander
|
|
||||||
|
|
||||||
from wuttaweb.forms.schema import WuttaDateTime
|
|
||||||
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
|
||||||
|
|
||||||
from wuttafarm.web.views.farmos import FarmOSMasterView
|
|
||||||
|
|
||||||
|
|
||||||
class ActivityLogView(FarmOSMasterView):
|
class ActivityLogView(LogMasterView):
|
||||||
"""
|
"""
|
||||||
View for farmOS activity logs
|
View for farmOS activity logs
|
||||||
"""
|
"""
|
||||||
|
|
@ -45,105 +38,9 @@ class ActivityLogView(FarmOSMasterView):
|
||||||
route_prefix = "farmos_logs_activity"
|
route_prefix = "farmos_logs_activity"
|
||||||
url_prefix = "/farmOS/logs/activity"
|
url_prefix = "/farmOS/logs/activity"
|
||||||
|
|
||||||
|
farmos_log_type = "activity"
|
||||||
farmos_refurl_path = "/logs/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):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
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
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import colander
|
||||||
|
|
||||||
from wuttaweb.forms.schema import WuttaDictEnum
|
from wuttaweb.forms.schema import WuttaDictEnum
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
||||||
|
|
||||||
from wuttafarm.web.views import WuttaFarmMasterView
|
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):
|
def get_log_type_enum(config):
|
||||||
|
|
@ -96,6 +99,77 @@ class LogTypeView(WuttaFarmMasterView):
|
||||||
return buttons
|
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):
|
class LogMasterView(WuttaFarmMasterView):
|
||||||
"""
|
"""
|
||||||
Base class for Asset master views
|
Base class for Asset master views
|
||||||
|
|
@ -165,13 +239,34 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
g.set_sorter("message", model.Log.message)
|
g.set_sorter("message", model.Log.message)
|
||||||
g.set_filter("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):
|
def configure_form(self, form):
|
||||||
f = form
|
f = form
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
|
enum = self.app.enum
|
||||||
|
log = f.model_instance
|
||||||
|
|
||||||
# timestamp
|
# timestamp
|
||||||
# TODO: the widget should be automatic (assn proxy field)
|
# TODO: the widget should be automatic (assn proxy field)
|
||||||
f.set_widget("timestamp", WuttaDateTimeWidget(self.request))
|
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
|
# log_type
|
||||||
if self.creating:
|
if self.creating:
|
||||||
|
|
@ -183,23 +278,44 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
)
|
)
|
||||||
f.set_readonly("log_type")
|
f.set_readonly("log_type")
|
||||||
|
|
||||||
|
# quantity
|
||||||
|
if self.creating or self.editing:
|
||||||
|
f.remove("quantity") # TODO: need to support this
|
||||||
|
|
||||||
# notes
|
# notes
|
||||||
f.set_widget("notes", "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):
|
def get_farmos_url(self, log):
|
||||||
return self.app.get_farmos_url(f"/log/{log.drupal_id}")
|
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):
|
def get_xref_buttons(self, log):
|
||||||
buttons = super().get_xref_buttons(log)
|
buttons = super().get_xref_buttons(log)
|
||||||
|
|
||||||
if log.farmos_uuid:
|
if log.farmos_uuid:
|
||||||
|
log_type = self.get_farmos_log_type()
|
||||||
# TODO
|
route = f"farmos_logs_{log_type}.view"
|
||||||
route = None
|
|
||||||
if log.log_type == "activity":
|
|
||||||
route = "farmos_logs_activity.view"
|
|
||||||
|
|
||||||
if route:
|
|
||||||
buttons.append(
|
buttons.append(
|
||||||
self.make_button(
|
self.make_button(
|
||||||
"View farmOS record",
|
"View farmOS record",
|
||||||
|
|
@ -218,6 +334,9 @@ def defaults(config, **kwargs):
|
||||||
LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"])
|
LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"])
|
||||||
LogTypeView.defaults(config)
|
LogTypeView.defaults(config)
|
||||||
|
|
||||||
|
LogView = kwargs.get("LogView", base["LogView"])
|
||||||
|
LogView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
defaults(config)
|
defaults(config)
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class ActivityLogView(LogMasterView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
model_class = ActivityLog
|
model_class = ActivityLog
|
||||||
route_prefix = "activity_logs"
|
route_prefix = "logs_activity"
|
||||||
url_prefix = "/logs/activity"
|
url_prefix = "/logs/activity"
|
||||||
|
|
||||||
farmos_refurl_path = "/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