feat: add schema, import support for Log.quantities
This commit is contained in:
parent
1d877545ae
commit
7d2ae48067
7 changed files with 204 additions and 6 deletions
|
|
@ -0,0 +1,118 @@
|
||||||
|
"""add LogQuantity
|
||||||
|
|
||||||
|
Revision ID: 9e875e5cbdc1
|
||||||
|
Revises: 74d32b4ec210
|
||||||
|
Create Date: 2026-02-28 21:55:31.876087
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import wuttjamaican.db.util
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "9e875e5cbdc1"
|
||||||
|
down_revision: Union[str, None] = "74d32b4ec210"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# log_quantity
|
||||||
|
op.create_table(
|
||||||
|
"log_quantity",
|
||||||
|
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.Column("log_uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.Column("quantity_uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["log_uuid"], ["log.uuid"], name=op.f("fk_log_quantity_log_uuid_log")
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["quantity_uuid"],
|
||||||
|
["quantity.uuid"],
|
||||||
|
name=op.f("fk_log_quantity_quantity_uuid_quantity"),
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_quantity")),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"log_quantity_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(
|
||||||
|
"quantity_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_quantity_version")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_log_quantity_version_end_transaction_id"),
|
||||||
|
"log_quantity_version",
|
||||||
|
["end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_log_quantity_version_operation_type"),
|
||||||
|
"log_quantity_version",
|
||||||
|
["operation_type"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_log_quantity_version_pk_transaction_id",
|
||||||
|
"log_quantity_version",
|
||||||
|
["uuid", sa.literal_column("transaction_id DESC")],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_log_quantity_version_pk_validity",
|
||||||
|
"log_quantity_version",
|
||||||
|
["uuid", "transaction_id", "end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_log_quantity_version_transaction_id"),
|
||||||
|
"log_quantity_version",
|
||||||
|
["transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
|
||||||
|
# log_quantity
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_log_quantity_version_transaction_id"),
|
||||||
|
table_name="log_quantity_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_log_quantity_version_pk_validity", table_name="log_quantity_version"
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_log_quantity_version_pk_transaction_id", table_name="log_quantity_version"
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_log_quantity_version_operation_type"),
|
||||||
|
table_name="log_quantity_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_log_quantity_version_end_transaction_id"),
|
||||||
|
table_name="log_quantity_version",
|
||||||
|
)
|
||||||
|
op.drop_table("log_quantity_version")
|
||||||
|
op.drop_table("log_quantity")
|
||||||
|
|
@ -38,7 +38,7 @@ 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 .asset_plant import PlantType, PlantAsset, PlantAssetPlantType
|
from .asset_plant import PlantType, PlantAsset, PlantAssetPlantType
|
||||||
from .log import LogType, Log, LogAsset
|
from .log import LogType, Log, LogAsset, LogGroup, LogLocation, LogQuantity, LogOwner
|
||||||
from .log_activity import ActivityLog
|
from .log_activity import ActivityLog
|
||||||
from .log_harvest import HarvestLog
|
from .log_harvest import HarvestLog
|
||||||
from .log_medical import MedicalLog
|
from .log_medical import MedicalLog
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,19 @@ class Log(model.Base):
|
||||||
creator=lambda asset: LogLocation(asset=asset),
|
creator=lambda asset: LogLocation(asset=asset),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_quantities = orm.relationship(
|
||||||
|
"LogQuantity",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
|
cascade_backrefs=False,
|
||||||
|
back_populates="log",
|
||||||
|
)
|
||||||
|
|
||||||
|
quantities = association_proxy(
|
||||||
|
"_quantities",
|
||||||
|
"quantity",
|
||||||
|
creator=lambda quantity: LogQuantity(quantity=quantity),
|
||||||
|
)
|
||||||
|
|
||||||
_owners = orm.relationship(
|
_owners = orm.relationship(
|
||||||
"LogOwner",
|
"LogOwner",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
|
|
@ -247,6 +260,7 @@ def add_log_proxies(subclass):
|
||||||
Log.make_proxy(subclass, "log", "assets")
|
Log.make_proxy(subclass, "log", "assets")
|
||||||
Log.make_proxy(subclass, "log", "groups")
|
Log.make_proxy(subclass, "log", "groups")
|
||||||
Log.make_proxy(subclass, "log", "locations")
|
Log.make_proxy(subclass, "log", "locations")
|
||||||
|
Log.make_proxy(subclass, "log", "quantities")
|
||||||
Log.make_proxy(subclass, "log", "owners")
|
Log.make_proxy(subclass, "log", "owners")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -322,6 +336,30 @@ class LogLocation(model.Base):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LogQuantity(model.Base):
|
||||||
|
"""
|
||||||
|
Represents a "log's quantity relationship" from farmOS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "log_quantity"
|
||||||
|
__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="_quantities",
|
||||||
|
)
|
||||||
|
|
||||||
|
quantity_uuid = model.uuid_fk_column("quantity.uuid", nullable=False)
|
||||||
|
quantity = orm.relationship(
|
||||||
|
"Quantity",
|
||||||
|
foreign_keys=quantity_uuid,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LogOwner(model.Base):
|
class LogOwner(model.Base):
|
||||||
"""
|
"""
|
||||||
Represents a "log's owner relationship" from farmOS.
|
Represents a "log's owner relationship" from farmOS.
|
||||||
|
|
|
||||||
|
|
@ -982,6 +982,7 @@ class LogImporterBase(FromFarmOS, ToWutta):
|
||||||
"assets",
|
"assets",
|
||||||
"groups",
|
"groups",
|
||||||
"locations",
|
"locations",
|
||||||
|
"quantities",
|
||||||
"owners",
|
"owners",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -1019,6 +1020,9 @@ class LogImporterBase(FromFarmOS, ToWutta):
|
||||||
for asset in data["locations"]
|
for asset in data["locations"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if "quantities" in self.fields:
|
||||||
|
data["quantities"] = [UUID(uuid) for uuid in data["quantity_uuids"]]
|
||||||
|
|
||||||
if "owners" in self.fields:
|
if "owners" in self.fields:
|
||||||
data["owners"] = [UUID(uuid) for uuid in data["owner_uuids"]]
|
data["owners"] = [UUID(uuid) for uuid in data["owner_uuids"]]
|
||||||
|
|
||||||
|
|
@ -1042,6 +1046,9 @@ class LogImporterBase(FromFarmOS, ToWutta):
|
||||||
(asset.asset_type, asset.farmos_uuid) for asset in log.locations
|
(asset.asset_type, asset.farmos_uuid) for asset in log.locations
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if "quantities" in self.fields:
|
||||||
|
data["quantities"] = [qty.farmos_uuid for qty in log.quantities]
|
||||||
|
|
||||||
if "owners" in self.fields:
|
if "owners" in self.fields:
|
||||||
data["owners"] = [user.farmos_uuid for user in log.owners]
|
data["owners"] = [user.farmos_uuid for user in log.owners]
|
||||||
|
|
||||||
|
|
@ -1129,6 +1136,31 @@ class LogImporterBase(FromFarmOS, ToWutta):
|
||||||
)
|
)
|
||||||
log.locations.remove(asset)
|
log.locations.remove(asset)
|
||||||
|
|
||||||
|
if "quantities" in self.fields:
|
||||||
|
if (
|
||||||
|
not target_data
|
||||||
|
or target_data["quantities"] != source_data["quantities"]
|
||||||
|
):
|
||||||
|
|
||||||
|
for farmos_uuid in source_data["quantities"]:
|
||||||
|
if not target_data or farmos_uuid not in target_data["quantities"]:
|
||||||
|
qty = (
|
||||||
|
self.target_session.query(model.Quantity)
|
||||||
|
.filter(model.Quantity.farmos_uuid == farmos_uuid)
|
||||||
|
.one()
|
||||||
|
)
|
||||||
|
log.quantities.append(qty)
|
||||||
|
|
||||||
|
if target_data:
|
||||||
|
for farmos_uuid in target_data["quantities"]:
|
||||||
|
if farmos_uuid not in source_data["quantities"]:
|
||||||
|
qty = (
|
||||||
|
self.target_session.query(model.Quantity)
|
||||||
|
.filter(model.Quantity.farmos_uuid == farmos_uuid)
|
||||||
|
.one()
|
||||||
|
)
|
||||||
|
log.quantities.remove(qty)
|
||||||
|
|
||||||
if "owners" in self.fields:
|
if "owners" in self.fields:
|
||||||
if not target_data or target_data["owners"] != source_data["owners"]:
|
if not target_data or target_data["owners"] != source_data["owners"]:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ class HarvestLogView(LogMasterView):
|
||||||
"name",
|
"name",
|
||||||
"assets",
|
"assets",
|
||||||
"quantities",
|
"quantities",
|
||||||
"is_group_assignment",
|
|
||||||
"owners",
|
"owners",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Base views for Logs
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
from webhelpers2.html import tags
|
from webhelpers2.html import tags, HTML
|
||||||
|
|
||||||
from wuttaweb.forms.schema import WuttaDictEnum
|
from wuttaweb.forms.schema import WuttaDictEnum
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
|
|
@ -100,6 +100,7 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
labels = {
|
labels = {
|
||||||
"message": "Log Name",
|
"message": "Log Name",
|
||||||
"locations": "Location",
|
"locations": "Location",
|
||||||
|
"quantities": "Quantity",
|
||||||
}
|
}
|
||||||
|
|
||||||
grid_columns = [
|
grid_columns = [
|
||||||
|
|
@ -109,7 +110,7 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
"message",
|
"message",
|
||||||
"assets",
|
"assets",
|
||||||
"locations",
|
"locations",
|
||||||
"quantity",
|
"quantities",
|
||||||
"is_group_assignment",
|
"is_group_assignment",
|
||||||
"owners",
|
"owners",
|
||||||
]
|
]
|
||||||
|
|
@ -189,6 +190,9 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
# locations
|
# locations
|
||||||
g.set_renderer("locations", self.render_assets_for_grid)
|
g.set_renderer("locations", self.render_assets_for_grid)
|
||||||
|
|
||||||
|
# quantities
|
||||||
|
g.set_renderer("quantities", self.render_quantities_for_grid)
|
||||||
|
|
||||||
# is_group_assignment
|
# is_group_assignment
|
||||||
g.set_renderer("is_group_assignment", "boolean")
|
g.set_renderer("is_group_assignment", "boolean")
|
||||||
g.set_sorter("is_group_assignment", model.Log.is_group_assignment)
|
g.set_sorter("is_group_assignment", model.Log.is_group_assignment)
|
||||||
|
|
@ -212,6 +216,13 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
|
|
||||||
return ", ".join([str(a) for a in assets])
|
return ", ".join([str(a) for a in assets])
|
||||||
|
|
||||||
|
def render_quantities_for_grid(self, log, field, value):
|
||||||
|
quantities = getattr(log, field) or []
|
||||||
|
items = []
|
||||||
|
for qty in quantities:
|
||||||
|
items.append(HTML.tag("li", c=qty.render_as_text(self.config)))
|
||||||
|
return HTML.tag("ul", c=items)
|
||||||
|
|
||||||
def render_owners_for_grid(self, log, field, value):
|
def render_owners_for_grid(self, log, field, value):
|
||||||
|
|
||||||
if self.farmos_style_grid_links:
|
if self.farmos_style_grid_links:
|
||||||
|
|
@ -377,7 +388,7 @@ class AllLogView(LogMasterView):
|
||||||
"log_type",
|
"log_type",
|
||||||
"assets",
|
"assets",
|
||||||
"locations",
|
"locations",
|
||||||
"quantity",
|
"quantities",
|
||||||
"groups",
|
"groups",
|
||||||
"is_group_assignment",
|
"is_group_assignment",
|
||||||
"owners",
|
"owners",
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class HarvestLogView(LogMasterView):
|
||||||
"timestamp",
|
"timestamp",
|
||||||
"message",
|
"message",
|
||||||
"assets",
|
"assets",
|
||||||
"quantity",
|
"quantities",
|
||||||
"owners",
|
"owners",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue