Compare commits

...

5 commits

11 changed files with 118 additions and 18 deletions

View file

@ -94,3 +94,9 @@ class FarmOSHandler(GenericHandler):
return f"{base}/{path}" return f"{base}/{path}"
return base return base
def get_oauth2_client_id(self):
return self.config.get("farmos.oauth2.client_id", default="farm")
def get_oauth2_scope(self):
return self.config.get("farmos.oauth2.scope", default="farm_manager")

37
src/wuttafarm/util.py Normal file
View file

@ -0,0 +1,37 @@
# -*- 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/>.
#
################################################################################
"""
misc. utilities
"""
from collections import OrderedDict
def get_log_type_enum(config, session=None):
app = config.get_app()
model = app.model
log_types = OrderedDict()
with app.short_session(session=session) as sess:
query = sess.query(model.LogType).order_by(model.LogType.name)
for log_type in query:
log_types[log_type.drupal_id] = log_type.name
return log_types

View file

@ -72,7 +72,7 @@ class WuttaFarmMenuHandler(base.MenuHandler):
{ {
"title": "Eggs", "title": "Eggs",
"route": "quick.eggs", "route": "quick.eggs",
# "perm": "assets.list", "perm": "quick.eggs",
}, },
], ],
} }

View file

@ -14,6 +14,28 @@
</b-input> </b-input>
</b-field> </b-field>
<b-field grouped>
<b-field label="OAuth2 Client ID">
<b-input name="farmos.oauth2.client_id"
v-model="simpleSettings['farmos.oauth2.client_id']"
@input="settingsNeedSaved = true">
</b-input>
</b-field>
<b-field label="OAuth2 Scope">
<b-input name="farmos.oauth2.scope"
v-model="simpleSettings['farmos.oauth2.scope']"
@input="settingsNeedSaved = true">
</b-input>
</b-field>
</b-field>
<b-field label="OAuth2 Redirect URI">
<wutta-copyable-text text="${url('farmos_oauth_callback')}" />
</b-field>
<b-field label="farmOS Integration Mode"> <b-field label="farmOS Integration Mode">
<b-select name="${app.appname}.farmos_integration_mode" <b-select name="${app.appname}.farmos_integration_mode"
v-model="simpleSettings['${app.appname}.farmos_integration_mode']" v-model="simpleSettings['${app.appname}.farmos_integration_mode']"

View file

@ -32,6 +32,7 @@ from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import Asset, Log 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
from wuttafarm.util import get_log_type_enum
def get_asset_type_enum(config): def get_asset_type_enum(config):
@ -301,7 +302,12 @@ class AssetMasterView(WuttaFarmMasterView):
def configure_row_grid(self, grid): def configure_row_grid(self, grid):
g = grid g = grid
super().configure_row_grid(g) super().configure_row_grid(g)
enum = self.app.enum
model = self.app.model model = self.app.model
session = self.Session()
# status
g.set_enum("status", enum.LOG_STATUS)
# drupal_id # drupal_id
g.set_label("drupal_id", "ID", column_only=True) g.set_label("drupal_id", "ID", column_only=True)
@ -318,6 +324,7 @@ class AssetMasterView(WuttaFarmMasterView):
# log_type # log_type
g.set_sorter("log_type", model.Log.log_type) g.set_sorter("log_type", model.Log.log_type)
g.set_filter("log_type", model.Log.log_type) g.set_filter("log_type", model.Log.log_type)
g.set_enum("log_type", get_log_type_enum(self.config, session=session))
def get_row_action_url_view(self, log, i): def get_row_action_url_view(self, log, i):
return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid) return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid)

View file

@ -55,9 +55,10 @@ class AuthView(base.AuthView):
return None return None
def get_farmos_oauth2_session(self): def get_farmos_oauth2_session(self):
farmos = self.app.get_farmos_handler()
return OAuth2Session( return OAuth2Session(
client_id="farm", client_id=farmos.get_oauth2_client_id(),
scope="farm_manager", scope=farmos.get_oauth2_scope(),
redirect_uri=self.request.route_url("farmos_oauth_callback"), redirect_uri=self.request.route_url("farmos_oauth_callback"),
) )

View file

@ -87,10 +87,20 @@ class CommonView(base.CommonView):
"farmos_logs_medical.view", "farmos_logs_medical.view",
"farmos_logs_observation.list", "farmos_logs_observation.list",
"farmos_logs_observation.view", "farmos_logs_observation.view",
"farmos_plant_assets.list",
"farmos_plant_assets.view",
"farmos_plant_types.list",
"farmos_plant_types.view",
"farmos_quantities_standard.list",
"farmos_quantities_standard.view",
"farmos_quantity_types.list",
"farmos_quantity_types.view",
"farmos_structure_assets.list", "farmos_structure_assets.list",
"farmos_structure_assets.view", "farmos_structure_assets.view",
"farmos_structure_types.list", "farmos_structure_types.list",
"farmos_structure_types.view", "farmos_structure_types.view",
"farmos_units.list",
"farmos_units.view",
"farmos_users.list", "farmos_users.list",
"farmos_users.view", "farmos_users.view",
"group_assets.create", "group_assets.create",
@ -121,6 +131,7 @@ class CommonView(base.CommonView):
"logs_observation.list", "logs_observation.list",
"logs_observation.view", "logs_observation.view",
"logs_observation.versions", "logs_observation.versions",
"quick.eggs",
"structure_types.list", "structure_types.list",
"structure_types.view", "structure_types.view",
"structure_types.versions", "structure_types.versions",

View file

@ -34,17 +34,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
from wuttafarm.web.views import WuttaFarmMasterView from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import LogType, Log from wuttafarm.db.model import LogType, Log
from wuttafarm.web.forms.schema import LogAssetRefs from wuttafarm.web.forms.schema import LogAssetRefs
from wuttafarm.util import get_log_type_enum
def get_log_type_enum(config):
app = config.get_app()
model = app.model
session = Session()
log_types = OrderedDict()
query = session.query(model.LogType).order_by(model.LogType.name)
for log_type in query:
log_types[log_type.drupal_id] = log_type.name
return log_types
class LogTypeView(WuttaFarmMasterView): class LogTypeView(WuttaFarmMasterView):
@ -142,6 +132,7 @@ class LogView(WuttaFarmMasterView):
def configure_grid(self, grid): def configure_grid(self, grid):
g = grid g = grid
super().configure_grid(g) super().configure_grid(g)
session = self.Session()
# drupal_id # drupal_id
g.set_label("drupal_id", "ID", column_only=True) g.set_label("drupal_id", "ID", column_only=True)
@ -154,7 +145,7 @@ class LogView(WuttaFarmMasterView):
g.set_link("message") g.set_link("message")
# log_type # log_type
g.set_enum("log_type", get_log_type_enum(self.config)) g.set_enum("log_type", get_log_type_enum(self.config, session=session))
# assets # assets
g.set_renderer("assets", self.render_assets_for_grid) g.set_renderer("assets", self.render_assets_for_grid)
@ -224,8 +215,10 @@ class LogMasterView(WuttaFarmMasterView):
g = grid g = grid
super().configure_grid(g) super().configure_grid(g)
model = self.app.model model = self.app.model
enum = self.app.enum
# status # status
g.set_enum("status", enum.LOG_STATUS)
g.set_sorter("status", model.Log.status) g.set_sorter("status", model.Log.status)
g.set_filter("status", model.Log.status) g.set_filter("status", model.Log.status)
@ -255,6 +248,7 @@ class LogMasterView(WuttaFarmMasterView):
f = form f = form
super().configure_form(f) super().configure_form(f)
enum = self.app.enum enum = self.app.enum
session = self.Session()
log = f.model_instance log = f.model_instance
# timestamp # timestamp
@ -280,7 +274,9 @@ class LogMasterView(WuttaFarmMasterView):
else: else:
f.set_node( f.set_node(
"log_type", "log_type",
WuttaDictEnum(self.request, get_log_type_enum(self.config)), WuttaDictEnum(
self.request, get_log_type_enum(self.config, session=session)
),
) )
f.set_readonly("log_type") f.set_readonly("log_type")

View file

@ -27,4 +27,9 @@ from .base import QuickFormView
def includeme(config): def includeme(config):
# perm group
config.add_wutta_permission_group("quick", "Quick Forms", overwrite=False)
# quick form views
config.include("wuttafarm.web.views.quick.eggs") config.include("wuttafarm.web.views.quick.eggs")

View file

@ -151,6 +151,10 @@ class QuickFormView(View):
def _defaults(cls, config): def _defaults(cls, config):
route_slug = cls.get_route_slug() route_slug = cls.get_route_slug()
url_slug = cls.get_url_slug() url_slug = cls.get_url_slug()
form_title = cls.get_form_title()
config.add_wutta_permission("quick", f"quick.{route_slug}", form_title)
config.add_route(f"quick.{route_slug}", f"/quick/{url_slug}") config.add_route(f"quick.{route_slug}", f"/quick/{url_slug}")
config.add_view(cls, route_name=f"quick.{route_slug}") config.add_view(
cls, route_name=f"quick.{route_slug}", permission=f"quick.{route_slug}"
)

View file

@ -57,10 +57,21 @@ class AppInfoView(base.AppInfoView):
return info return info
def configure_get_simple_settings(self): # pylint: disable=empty-docstring def configure_get_simple_settings(self): # pylint: disable=empty-docstring
farmos = self.app.get_farmos_handler()
simple_settings = super().configure_get_simple_settings() simple_settings = super().configure_get_simple_settings()
simple_settings.extend( simple_settings.extend(
[ [
{"name": "farmos.url.base"}, {
"name": "farmos.url.base",
},
{
"name": "farmos.oauth2.client_id",
"default": farmos.get_oauth2_client_id(),
},
{
"name": "farmos.oauth2.scope",
"default": farmos.get_oauth2_scope(),
},
{ {
"name": f"{self.app.appname}.farmos_integration_mode", "name": f"{self.app.appname}.farmos_integration_mode",
"default": self.app.get_farmos_integration_mode(), "default": self.app.get_farmos_integration_mode(),