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

This commit is contained in:
Lance Edgar 2026-02-08 21:05:40 -06:00
parent 09042747a0
commit 4a517bf7bf
23 changed files with 786 additions and 12 deletions

View file

@ -0,0 +1,6 @@
``wuttafarm.cli.base``
======================
.. automodule:: wuttafarm.cli.base
:members:

View file

@ -0,0 +1,6 @@
``wuttafarm.cli.import_farmos``
===============================
.. automodule:: wuttafarm.cli.import_farmos
:members:

View file

@ -0,0 +1,6 @@
``wuttafarm.cli.install``
=========================
.. automodule:: wuttafarm.cli.install
:members:

View file

@ -0,0 +1,6 @@
``wuttafarm.importing.farmos``
==============================
.. automodule:: wuttafarm.importing.farmos
:members:

View file

@ -0,0 +1,6 @@
``wuttafarm.importing``
=======================
.. automodule:: wuttafarm.importing
:members:

View file

@ -8,9 +8,6 @@ and extend `farmOS`_.
.. _WuttaWeb: https://wuttaproject.org .. _WuttaWeb: https://wuttaproject.org
.. _farmOS: https://farmos.org .. _farmOS: https://farmos.org
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
It is just an experiment so far; the ideas I hope to play with It is just an experiment so far; the ideas I hope to play with
include: include:
@ -19,6 +16,9 @@ include:
- possibly add more schema / extra features - possibly add more schema / extra features
- possibly sync data back to farmOS - possibly sync data back to farmOS
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@ -37,11 +37,16 @@ include:
api/wuttafarm.app api/wuttafarm.app
api/wuttafarm.auth api/wuttafarm.auth
api/wuttafarm.cli api/wuttafarm.cli
api/wuttafarm.cli.base
api/wuttafarm.cli.import_farmos
api/wuttafarm.cli.install
api/wuttafarm.config api/wuttafarm.config
api/wuttafarm.db api/wuttafarm.db
api/wuttafarm.db.model api/wuttafarm.db.model
api/wuttafarm.farmos api/wuttafarm.farmos
api/wuttafarm.farmos.handler api/wuttafarm.farmos.handler
api/wuttafarm.importing
api/wuttafarm.importing.farmos
api/wuttafarm.install api/wuttafarm.install
api/wuttafarm.web api/wuttafarm.web
api/wuttafarm.web.app api/wuttafarm.web.app

View file

@ -33,6 +33,7 @@ dependencies = [
"psycopg2", "psycopg2",
"pyramid_exclog", "pyramid_exclog",
"uvicorn[standard]", "uvicorn[standard]",
"WuttaSync",
"WuttaWeb[continuum]>=0.27.4", "WuttaWeb[continuum]>=0.27.4",
] ]
@ -47,12 +48,18 @@ docs = ["Sphinx", "furo"]
[project.entry-points."paste.app_factory"] [project.entry-points."paste.app_factory"]
"main" = "wuttafarm.web.app:main" "main" = "wuttafarm.web.app:main"
[project.entry-points."wutta.app.providers"]
wuttafarm = "wuttafarm.app:WuttaFarmAppProvider"
[project.entry-points."wutta.config.extensions"] [project.entry-points."wutta.config.extensions"]
"wuttafarm" = "wuttafarm.config:WuttaFarmConfig" "wuttafarm" = "wuttafarm.config:WuttaFarmConfig"
[project.entry-points."wutta.web.menus"] [project.entry-points."wutta.web.menus"]
"wuttafarm" = "wuttafarm.web.menus:WuttaFarmMenuHandler" "wuttafarm" = "wuttafarm.web.menus:WuttaFarmMenuHandler"
[project.entry-points."wuttasync.importing"]
"import.to_wuttafarm.from_farmos" = "wuttafarm.importing.farmos:FromFarmOSToWuttaFarm"
[project.urls] [project.urls]
Homepage = "https://forgejo.wuttaproject.org/wutta/wuttafarm" Homepage = "https://forgejo.wuttaproject.org/wutta/wuttafarm"

View file

@ -64,3 +64,11 @@ class WuttaFarmAppHandler(base.AppHandler):
""" """
handler = self.get_farmos_handler() handler = self.get_farmos_handler()
return handler.get_farmos_client(*args, **kwargs) return handler.get_farmos_client(*args, **kwargs)
class WuttaFarmAppProvider(base.AppProvider):
"""
The :term:`app provider` for WuttaFarm.
"""
email_modules = ["wuttafarm.emails"]

View file

@ -0,0 +1,30 @@
# -*- 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/>.
#
################################################################################
"""
WuttaFarm CLI
"""
from .base import wuttafarm_typer
# nb. must bring in all modules for discovery to work
from . import import_farmos
from . import install

31
src/wuttafarm/cli/base.py Normal file
View file

@ -0,0 +1,31 @@
# -*- 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/>.
#
################################################################################
"""
WuttaFarm CLI - base Typer instance
"""
from wuttjamaican.cli import make_typer
wuttafarm_typer = make_typer(
name="wuttafarm", help="WuttaFarm -- Web app to integrate with and extend farmOS"
)

View file

@ -0,0 +1,41 @@
# -*- 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/>.
#
################################################################################
"""
See also: :ref:`wuttafarm-import-farmos`
"""
import typer
from wuttasync.cli import import_command, ImportCommandHandler
from wuttafarm.cli import wuttafarm_typer
@wuttafarm_typer.command()
@import_command
def import_farmos(ctx: typer.Context, **kwargs):
"""
Import data from farmOS API to WuttaFarm
"""
config = ctx.parent.wutta_config
handler = ImportCommandHandler(config, key="import.to_wuttafarm.from_farmos")
handler.run(ctx)

View file

@ -25,12 +25,7 @@ WuttaFarm CLI
import typer import typer
from wuttjamaican.cli import make_typer from wuttafarm.cli import wuttafarm_typer
wuttafarm_typer = make_typer(
name="wuttafarm", help="WuttaFarm -- Web app to integrate with and extend farmOS"
)
@wuttafarm_typer.command() @wuttafarm_typer.command()

View file

@ -0,0 +1,120 @@
"""add Animal Types
Revision ID: 2b6385d0fa17
Revises:
Create Date: 2026-02-08 14:55:42.236918
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
import wuttjamaican.db.util
# revision identifiers, used by Alembic.
revision: str = "2b6385d0fa17"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = ("wuttafarm",)
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# animal_type
op.create_table(
"animal_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("changed", sa.DateTime(), nullable=True),
sa.Column("farmos_uuid", wuttjamaican.db.util.UUID(), nullable=True),
sa.Column("drupal_internal_id", sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_animal_type")),
sa.UniqueConstraint(
"drupal_internal_id", name=op.f("uq_animal_type_drupal_internal_id")
),
sa.UniqueConstraint("farmos_uuid", name=op.f("uq_animal_type_farmos_uuid")),
sa.UniqueConstraint("name", name=op.f("uq_animal_type_name")),
)
op.create_table(
"animal_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.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_animal_type_version")
),
)
op.create_index(
op.f("ix_animal_type_version_end_transaction_id"),
"animal_type_version",
["end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_animal_type_version_operation_type"),
"animal_type_version",
["operation_type"],
unique=False,
)
op.create_index(
"ix_animal_type_version_pk_transaction_id",
"animal_type_version",
["uuid", sa.literal_column("transaction_id DESC")],
unique=False,
)
op.create_index(
"ix_animal_type_version_pk_validity",
"animal_type_version",
["uuid", "transaction_id", "end_transaction_id"],
unique=False,
)
op.create_index(
op.f("ix_animal_type_version_transaction_id"),
"animal_type_version",
["transaction_id"],
unique=False,
)
def downgrade() -> None:
# animal_type
op.drop_index(
op.f("ix_animal_type_version_transaction_id"), table_name="animal_type_version"
)
op.drop_index(
"ix_animal_type_version_pk_validity", table_name="animal_type_version"
)
op.drop_index(
"ix_animal_type_version_pk_transaction_id", table_name="animal_type_version"
)
op.drop_index(
op.f("ix_animal_type_version_operation_type"), table_name="animal_type_version"
)
op.drop_index(
op.f("ix_animal_type_version_end_transaction_id"),
table_name="animal_type_version",
)
op.drop_table("animal_type_version")
op.drop_table("animal_type")

View file

@ -26,4 +26,5 @@ WuttaFarm data models
# bring in all of wutta # bring in all of wutta
from wuttjamaican.db.model import * from wuttjamaican.db.model import *
# TODO: import other/custom models here... # wuttafarm models
from .animals import AnimalType

View file

@ -0,0 +1,94 @@
# -*- 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 Animal Types
"""
import sqlalchemy as sa
from sqlalchemy import orm
from wuttjamaican.db import model
class AnimalType(model.Base):
"""
Represents an "animal type" (taxonomy term) from farmOS
"""
__tablename__ = "animal_type"
__versioned__ = {
"exclude": [
"changed",
],
}
__wutta_hint__ = {
"model_title": "Animal Type",
"model_title_plural": "Animal Types",
}
uuid = model.uuid_column()
name = sa.Column(
sa.String(length=100),
nullable=False,
unique=True,
doc="""
Name of the animal type.
""",
)
description = sa.Column(
sa.String(length=255),
nullable=True,
doc="""
Optional description for the animal type.
""",
)
changed = sa.Column(
sa.DateTime(),
nullable=True,
doc="""
When the animal type was last changed, according to farmOS.
""",
)
farmos_uuid = sa.Column(
model.UUID(),
nullable=True,
unique=True,
doc="""
UUID for the animal type within farmOS.
""",
)
drupal_internal_id = sa.Column(
sa.Integer(),
nullable=True,
unique=True,
doc="""
Drupal internal ID for the animal type.
""",
)
def __str__(self):
return self.name or ""

32
src/wuttafarm/emails.py Normal file
View file

@ -0,0 +1,32 @@
# -*- 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/>.
#
################################################################################
"""
Email sending config for WuttaFarm
"""
from wuttasync.emails import ImportExportWarning
class import_to_wuttafarm_from_farmos_warning(ImportExportWarning):
"""
Diff warning for farmOS WuttaFarm import.
"""

View file

@ -0,0 +1,24 @@
# -*- 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/>.
#
################################################################################
"""
Importing data to WuttaFarm
"""

View file

@ -0,0 +1,154 @@
# -*- 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/>.
#
################################################################################
"""
Data import for farmOS -> WuttaFarm
"""
import datetime
from uuid import UUID
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session
from wuttasync.importing import ImportHandler, ToWuttaHandler, Importer, ToWutta
from wuttafarm.db import model
class FromFarmOSHandler(ImportHandler):
"""
Base class for import handler using farmOS API as data source.
"""
source_key = "farmos"
generic_source_title = "farmOS"
def begin_source_transaction(self):
"""
Establish the farmOS API client.
"""
token = self.get_farmos_oauth2_token()
self.farmos_client = self.app.get_farmos_client(token=token)
def get_farmos_oauth2_token(self):
client_id = self.config.get(
"farmos.oauth2.importing.client_id", default="wuttafarm"
)
client_secret = self.config.require("farmos.oauth2.importing.client_secret")
scope = self.config.get("farmos.oauth2.importing.scope", default="farm_manager")
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
return oauth.fetch_token(
token_url=self.app.get_farmos_url("/oauth/token"),
include_client_id=True,
client_secret=client_secret,
scope=scope,
)
def get_importer_kwargs(self, key, **kwargs):
kwargs = super().get_importer_kwargs(key, **kwargs)
kwargs["farmos_client"] = self.farmos_client
return kwargs
class ToWuttaFarmHandler(ToWuttaHandler):
"""
Base class for import handler targeting WuttaFarm
"""
target_key = "wuttafarm"
class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler):
"""
Handler for farmOS WuttaFarm import.
"""
def define_importers(self):
""" """
importers = super().define_importers()
importers["AnimalType"] = AnimalTypeImporter
return importers
class FromFarmOS(Importer):
"""
Base class for importers using farmOS API as data source.
"""
key = "farmos_uuid"
def get_supported_fields(self):
"""
Auto-remove the ``uuid`` field, since we use ``farmos_uuid``
instead for the importer key.
"""
fields = list(super().get_supported_fields())
if "uuid" in fields:
fields.remove("uuid")
return fields
def normalize_datetime(self, dt):
"""
Convert a farmOS datetime value to naive UTC used by
WuttaFarm.
:param dt: Date/time string value "as-is" from the farmOS API.
:returns: Equivalent naive UTC ``datetime``
"""
dt = datetime.datetime.fromisoformat(dt)
return self.app.make_utc(dt)
class AnimalTypeImporter(FromFarmOS, ToWutta):
"""
farmOS API WuttaFarm importer for Animal Types
"""
model_class = model.AnimalType
supported_fields = [
"farmos_uuid",
"drupal_internal_id",
"name",
"description",
"changed",
]
def get_source_objects(self):
""" """
animal_types = self.farmos_client.resource.get("taxonomy_term", "animal_type")
return animal_types["data"]
def normalize_source_object(self, animal_type):
""" """
return {
"farmos_uuid": UUID(animal_type["id"]),
"drupal_internal_id": animal_type["attributes"]["drupal_internal__tid"],
"name": animal_type["attributes"]["name"],
"description": animal_type["attributes"]["description"],
"changed": self.normalize_datetime(animal_type["attributes"]["changed"]),
}

View file

@ -33,10 +33,24 @@ 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_farmos_menu(request), self.make_farmos_menu(request),
self.make_admin_menu(request, include_people=True), self.make_admin_menu(request, include_people=True),
] ]
def make_asset_menu(self, request):
return {
"title": "Assets",
"type": "menu",
"items": [
{
"title": "Animal Types",
"route": "animal_types",
"perm": "animal_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

@ -25,6 +25,8 @@ WuttaFarm Views
from wuttaweb.views import essential from wuttaweb.views import essential
from .master import WuttaFarmMasterView
def includeme(config): def includeme(config):
@ -37,5 +39,8 @@ def includeme(config):
} }
) )
# native table views
config.include("wuttafarm.web.views.animal_types")
# views for farmOS # views for farmOS
config.include("wuttafarm.web.views.farmos") config.include("wuttafarm.web.views.farmos")

View file

@ -0,0 +1,99 @@
# -*- 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 Animal Types
"""
from wuttafarm.db.model.animals import AnimalType
from wuttafarm.web.views import WuttaFarmMasterView
class AnimalTypeView(WuttaFarmMasterView):
"""
Master view for Animal Types
"""
model_class = AnimalType
route_prefix = "animal_types"
url_prefix = "/animal-types"
farmos_refurl_path = "/admin/structure/taxonomy/manage/animal_type/overview"
grid_columns = [
"name",
"description",
"changed",
]
sort_defaults = "name"
filter_defaults = {
"name": {"active": True, "verb": "contains"},
}
form_fields = [
"name",
"description",
"changed",
"farmos_uuid",
"drupal_internal_id",
]
def configure_grid(self, grid):
g = grid
super().configure_grid(g)
# name
g.set_link("name")
def get_farmos_url(self, animal_type):
return self.app.get_farmos_url(
f"/taxonomy/term/{animal_type.drupal_internal_id}"
)
def get_xref_buttons(self, animal_type):
buttons = super().get_xref_buttons(animal_type)
if animal_type.farmos_uuid:
buttons.append(
self.make_button(
"View farmOS record",
primary=True,
url=self.request.route_url(
"farmos_animal_types.view", uuid=animal_type.farmos_uuid
),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs):
base = globals()
AnimalTypeView = kwargs.get("AnimalTypeView", base["AnimalTypeView"])
AnimalTypeView.defaults(config)
def includeme(config):
defaults(config)

View file

@ -113,7 +113,10 @@ class AnimalTypeView(FarmOSMasterView):
f.set_node("changed", WuttaDateTime()) f.set_node("changed", WuttaDateTime())
def get_xref_buttons(self, animal_type): def get_xref_buttons(self, animal_type):
return [ model = self.app.model
session = self.Session()
buttons = [
self.make_button( self.make_button(
"View in farmOS", "View in farmOS",
primary=True, primary=True,
@ -122,9 +125,27 @@ class AnimalTypeView(FarmOSMasterView):
), ),
target="_blank", target="_blank",
icon_left="external-link-alt", icon_left="external-link-alt",
), )
] ]
if wf_animal_type := (
session.query(model.AnimalType)
.filter(model.AnimalType.farmos_uuid == animal_type["uuid"])
.first()
):
buttons.append(
self.make_button(
f"View {self.app.get_title()} record",
primary=True,
url=self.request.route_url(
"animal_types.view", uuid=wf_animal_type.uuid
),
icon_left="eye",
)
)
return buttons
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()

View file

@ -0,0 +1,63 @@
# -*- 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/>.
#
################################################################################
"""
Base class for WuttaFarm master views
"""
from wuttaweb.views import MasterView
class WuttaFarmMasterView(MasterView):
"""
Base class for WuttaFarm master views
"""
farmos_refurl_path = None
labels = {
"farmos_uuid": "farmOS UUID",
"drupal_internal_id": "Drupal Internal ID",
}
def get_farmos_url(self, obj):
return None
def get_template_context(self, context):
if self.listing and self.farmos_refurl_path:
context["farmos_refurl"] = self.app.get_farmos_url(self.farmos_refurl_path)
return context
def get_xref_buttons(self, obj):
url = self.get_farmos_url(obj)
if url:
return [
self.make_button(
"View in farmOS",
primary=True,
url=url,
target="_blank",
icon_left="external-link-alt",
)
]
return []