feat: add native table for Structure Types; import from farmOS API

This commit is contained in:
Lance Edgar 2026-02-10 19:31:21 -06:00
parent b573ae459e
commit 5189c12f43
8 changed files with 341 additions and 0 deletions

View file

@ -0,0 +1,121 @@
"""add Structure Types
Revision ID: d7479d7161a8
Revises: 9f2243df9566
Create Date: 2026-02-10 19:24:20.249826
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = "d7479d7161a8"
down_revision: Union[str, None] = "9f2243df9566"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# structure_type
op.create_table(
"structure_type",
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
sa.Column("name", sa.String(length=100), nullable=False),
sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("drupal_internal_id", sa.String(length=50), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_structure_type")),
sa.UniqueConstraint(
"drupal_internal_id", name=op.f("uq_structure_type_drupal_internal_id")
),
sa.UniqueConstraint("farmos_uuid", name=op.f("uq_structure_type_farmos_uuid")),
sa.UniqueConstraint("name", name=op.f("uq_structure_type_name")),
)
op.create_table(
"structure_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(
"farmos_uuid",
wuttjamaican.db.util.UUID(),
autoincrement=False,
nullable=True,
),
sa.Column(
"drupal_internal_id",
sa.String(length=50),
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_structure_type_version")
),
)
op.create_index(
op.f("ix_structure_type_version_end_transaction_id"),
"structure_type_version",
["end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_structure_type_version_operation_type"),
"structure_type_version",
["operation_type"],
unique=False,
)
op.create_index(
"ix_structure_type_version_pk_transaction_id",
"structure_type_version",
["uuid", sa.literal_column("transaction_id DESC")],
unique=False,
)
op.create_index(
"ix_structure_type_version_pk_validity",
"structure_type_version",
["uuid", "transaction_id", "end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_structure_type_version_transaction_id"),
"structure_type_version",
["transaction_id"],
unique=False,
)
def downgrade() -> None:
# structure_type
op.drop_index(
op.f("ix_structure_type_version_transaction_id"),
table_name="structure_type_version",
)
op.drop_index(
"ix_structure_type_version_pk_validity", table_name="structure_type_version"
)
op.drop_index(
"ix_structure_type_version_pk_transaction_id",
table_name="structure_type_version",
)
op.drop_index(
op.f("ix_structure_type_version_operation_type"),
table_name="structure_type_version",
)
op.drop_index(
op.f("ix_structure_type_version_end_transaction_id"),
table_name="structure_type_version",
)
op.drop_table("structure_type_version")
op.drop_table("structure_type")

View file

@ -32,4 +32,5 @@ from .users import WuttaFarmUser
# wuttafarm proper models # wuttafarm proper models
from .assets import AssetType from .assets import AssetType
from .land import LandType from .land import LandType
from .structures import StructureType
from .animals import AnimalType from .animals import AnimalType

View file

@ -0,0 +1,74 @@
# -*- 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 Structure Types
"""
import sqlalchemy as sa
from sqlalchemy import orm
from wuttjamaican.db import model
class StructureType(model.Base):
"""
Represents a "structure type" from farmOS
"""
__tablename__ = "structure_type"
__versioned__ = {}
__wutta_hint__ = {
"model_title": "Structure Type",
"model_title_plural": "Structure Types",
}
uuid = model.uuid_column()
name = sa.Column(
sa.String(length=100),
nullable=False,
unique=True,
doc="""
Name of the structure type.
""",
)
farmos_uuid = sa.Column(
model.UUID(),
nullable=True,
unique=True,
doc="""
UUID for the structure type within farmOS.
""",
)
drupal_internal_id = sa.Column(
sa.String(length=50),
nullable=True,
unique=True,
doc="""
Drupal internal ID for the structure type.
""",
)
def __str__(self):
return self.name or ""

View file

@ -92,6 +92,7 @@ class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler):
importers["User"] = UserImporter importers["User"] = UserImporter
importers["AssetType"] = AssetTypeImporter importers["AssetType"] = AssetTypeImporter
importers["LandType"] = LandTypeImporter importers["LandType"] = LandTypeImporter
importers["StructureType"] = StructureTypeImporter
importers["AnimalType"] = AnimalTypeImporter importers["AnimalType"] = AnimalTypeImporter
return importers return importers
@ -213,6 +214,33 @@ class LandTypeImporter(FromFarmOS, ToWutta):
} }
class StructureTypeImporter(FromFarmOS, ToWutta):
"""
farmOS API WuttaFarm importer for Structure Types
"""
model_class = model.StructureType
supported_fields = [
"farmos_uuid",
"drupal_internal_id",
"name",
]
def get_source_objects(self):
""" """
structure_types = self.farmos_client.resource.get("structure_type")
return structure_types["data"]
def normalize_source_object(self, structure_type):
""" """
return {
"farmos_uuid": UUID(structure_type["id"]),
"drupal_internal_id": structure_type["attributes"]["drupal_internal__id"],
"name": structure_type["attributes"]["label"],
}
class UserImporter(FromFarmOS, ToWutta): class UserImporter(FromFarmOS, ToWutta):
""" """
farmOS API WuttaFarm importer for Users farmOS API WuttaFarm importer for Users

View file

@ -53,6 +53,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "land_types", "route": "land_types",
"perm": "land_types.list", "perm": "land_types.list",
}, },
{
"title": "Structure Types",
"route": "structure_types",
"perm": "structure_types.list",
},
{ {
"title": "Asset Types", "title": "Asset Types",
"route": "asset_types", "route": "asset_types",

View file

@ -43,6 +43,7 @@ def includeme(config):
# native table views # native table views
config.include("wuttafarm.web.views.asset_types") config.include("wuttafarm.web.views.asset_types")
config.include("wuttafarm.web.views.land_types") config.include("wuttafarm.web.views.land_types")
config.include("wuttafarm.web.views.structure_types")
config.include("wuttafarm.web.views.animal_types") config.include("wuttafarm.web.views.animal_types")
# views for farmOS # views for farmOS

View file

@ -79,6 +79,29 @@ class StructureTypeView(FarmOSMasterView):
"label": structure_type["attributes"]["label"], "label": structure_type["attributes"]["label"],
} }
def get_xref_buttons(self, structure_type):
model = self.app.model
session = self.Session()
buttons = []
if wf_structure_type := (
session.query(model.StructureType)
.filter(model.StructureType.farmos_uuid == structure_type["uuid"])
.first()
):
buttons.append(
self.make_button(
f"View {self.app.get_title()} record",
primary=True,
url=self.request.route_url(
"structure_types.view", uuid=wf_structure_type.uuid
),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()

View file

@ -0,0 +1,88 @@
# -*- 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 Structure Types
"""
from wuttafarm.db.model.structures import StructureType
from wuttafarm.web.views import WuttaFarmMasterView
class StructureTypeView(WuttaFarmMasterView):
"""
Master view for Structure Types
"""
model_class = StructureType
route_prefix = "structure_types"
url_prefix = "/structure-types"
grid_columns = [
"name",
]
sort_defaults = "name"
filter_defaults = {
"name": {"active": True, "verb": "contains"},
}
form_fields = [
"name",
"farmos_uuid",
"drupal_internal_id",
]
def configure_grid(self, grid):
g = grid
super().configure_grid(g)
# name
g.set_link("name")
def get_xref_buttons(self, structure_type):
buttons = super().get_xref_buttons(structure_type)
if structure_type.farmos_uuid:
buttons.append(
self.make_button(
"View farmOS record",
primary=True,
url=self.request.route_url(
"farmos_structure_types.view", uuid=structure_type.farmos_uuid
),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs):
base = globals()
StructureTypeView = kwargs.get("StructureTypeView", base["StructureTypeView"])
StructureTypeView.defaults(config)
def includeme(config):
defaults(config)