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

This commit is contained in:
Lance Edgar 2026-02-10 19:42:02 -06:00
parent 5189c12f43
commit 6204db8ae3
8 changed files with 363 additions and 5 deletions

View file

@ -0,0 +1,119 @@
"""add Log Types
Revision ID: e0d9f72575d6
Revises: d7479d7161a8
Create Date: 2026-02-10 19:35:06.631814
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = "e0d9f72575d6"
down_revision: Union[str, None] = "d7479d7161a8"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# log_type
op.create_table(
"log_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_internal_id", sa.String(length=50), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_log_type")),
sa.UniqueConstraint(
"drupal_internal_id", name=op.f("uq_log_type_drupal_internal_id")
),
sa.UniqueConstraint("farmos_uuid", name=op.f("uq_log_type_farmos_uuid")),
sa.UniqueConstraint("name", name=op.f("uq_log_type_name")),
)
op.create_table(
"log_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_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_log_type_version")
),
)
op.create_index(
op.f("ix_log_type_version_end_transaction_id"),
"log_type_version",
["end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_log_type_version_operation_type"),
"log_type_version",
["operation_type"],
unique=False,
)
op.create_index(
"ix_log_type_version_pk_transaction_id",
"log_type_version",
["uuid", sa.literal_column("transaction_id DESC")],
unique=False,
)
op.create_index(
"ix_log_type_version_pk_validity",
"log_type_version",
["uuid", "transaction_id", "end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_log_type_version_transaction_id"),
"log_type_version",
["transaction_id"],
unique=False,
)
def downgrade() -> None:
# log_type
op.drop_index(
op.f("ix_log_type_version_transaction_id"), table_name="log_type_version"
)
op.drop_index("ix_log_type_version_pk_validity", table_name="log_type_version")
op.drop_index(
"ix_log_type_version_pk_transaction_id", table_name="log_type_version"
)
op.drop_index(
op.f("ix_log_type_version_operation_type"), table_name="log_type_version"
)
op.drop_index(
op.f("ix_log_type_version_end_transaction_id"), table_name="log_type_version"
)
op.drop_table("log_type_version")
op.drop_table("log_type")

View file

@ -34,3 +34,4 @@ from .assets import AssetType
from .land import LandType from .land import LandType
from .structures import StructureType from .structures import StructureType
from .animals import AnimalType from .animals import AnimalType
from .logs import LogType

View file

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

View file

@ -94,6 +94,7 @@ class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler):
importers["LandType"] = LandTypeImporter importers["LandType"] = LandTypeImporter
importers["StructureType"] = StructureTypeImporter importers["StructureType"] = StructureTypeImporter
importers["AnimalType"] = AnimalTypeImporter importers["AnimalType"] = AnimalTypeImporter
importers["LogType"] = LogTypeImporter
return importers return importers
@ -214,6 +215,35 @@ class LandTypeImporter(FromFarmOS, ToWutta):
} }
class LogTypeImporter(FromFarmOS, ToWutta):
"""
farmOS API WuttaFarm importer for Log Types
"""
model_class = model.LogType
supported_fields = [
"farmos_uuid",
"drupal_internal_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_internal_id": log_type["attributes"]["drupal_internal__id"],
"name": log_type["attributes"]["label"],
"description": log_type["attributes"]["description"],
}
class StructureTypeImporter(FromFarmOS, ToWutta): class StructureTypeImporter(FromFarmOS, ToWutta):
""" """
farmOS API WuttaFarm importer for Structure Types farmOS API WuttaFarm importer for Structure Types

View file

@ -34,6 +34,7 @@ class WuttaFarmMenuHandler(base.MenuHandler):
def make_menus(self, request, **kwargs): def make_menus(self, request, **kwargs):
return [ return [
self.make_asset_menu(request), self.make_asset_menu(request),
self.make_log_menu(request),
self.make_farmos_menu(request), self.make_farmos_menu(request),
self.make_admin_menu(request, include_people=True), self.make_admin_menu(request, include_people=True),
] ]
@ -48,16 +49,16 @@ class WuttaFarmMenuHandler(base.MenuHandler):
"route": "animal_types", "route": "animal_types",
"perm": "animal_types.list", "perm": "animal_types.list",
}, },
{
"title": "Land Types",
"route": "land_types",
"perm": "land_types.list",
},
{ {
"title": "Structure Types", "title": "Structure Types",
"route": "structure_types", "route": "structure_types",
"perm": "structure_types.list", "perm": "structure_types.list",
}, },
{
"title": "Land Types",
"route": "land_types",
"perm": "land_types.list",
},
{ {
"title": "Asset Types", "title": "Asset Types",
"route": "asset_types", "route": "asset_types",
@ -66,6 +67,19 @@ class WuttaFarmMenuHandler(base.MenuHandler):
], ],
} }
def make_log_menu(self, request):
return {
"title": "Logs",
"type": "menu",
"items": [
{
"title": "Log Types",
"route": "log_types",
"perm": "log_types.list",
},
],
}
def make_farmos_menu(self, request): def make_farmos_menu(self, request):
config = request.wutta_config config = request.wutta_config
app = config.get_app() app = config.get_app()

View file

@ -45,6 +45,7 @@ def includeme(config):
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.structure_types")
config.include("wuttafarm.web.views.animal_types") config.include("wuttafarm.web.views.animal_types")
config.include("wuttafarm.web.views.log_types")
# views for farmOS # views for farmOS
config.include("wuttafarm.web.views.farmos") config.include("wuttafarm.web.views.farmos")

View file

@ -87,6 +87,27 @@ class LogTypeView(FarmOSMasterView):
# description # description
f.set_widget("description", "notes") f.set_widget("description", "notes")
def get_xref_buttons(self, log_type):
model = self.app.model
session = self.Session()
buttons = []
if wf_log_type := (
session.query(model.LogType)
.filter(model.LogType.farmos_uuid == log_type["uuid"])
.first()
):
buttons.append(
self.make_button(
f"View {self.app.get_title()} record",
primary=True,
url=self.request.route_url("log_types.view", uuid=wf_log_type.uuid),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()

View file

@ -0,0 +1,90 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaFarm --Web app to integrate with and extend farmOS
# Copyright © 2026 Lance Edgar
#
# This file is part of WuttaFarm.
#
# WuttaFarm is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# WuttaFarm. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Master view for Log Types
"""
from wuttafarm.db.model.logs import LogType
from wuttafarm.web.views import WuttaFarmMasterView
class LogTypeView(WuttaFarmMasterView):
"""
Master view for Log Types
"""
model_class = LogType
route_prefix = "log_types"
url_prefix = "/log-types"
grid_columns = [
"name",
"description",
]
sort_defaults = "name"
filter_defaults = {
"name": {"active": True, "verb": "contains"},
}
form_fields = [
"name",
"description",
"farmos_uuid",
"drupal_internal_id",
]
def configure_grid(self, grid):
g = grid
super().configure_grid(g)
# name
g.set_link("name")
def get_xref_buttons(self, log_type):
buttons = super().get_xref_buttons(log_type)
if log_type.farmos_uuid:
buttons.append(
self.make_button(
"View farmOS record",
primary=True,
url=self.request.route_url(
"farmos_log_types.view", uuid=log_type.farmos_uuid
),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs):
base = globals()
LogTypeView = kwargs.get("LogTypeView", base["LogTypeView"])
LogTypeView.defaults(config)
def includeme(config):
defaults(config)