Tidy up server specified interface
This commit is contained in:
parent
9f2cde2275
commit
ca21ab03c6
5 changed files with 49 additions and 36 deletions
|
|
@ -13,7 +13,11 @@ from pydantic import BaseModel
|
||||||
|
|
||||||
import labthings_fastapi as lt
|
import labthings_fastapi as lt
|
||||||
|
|
||||||
from openflexure_microscope_server.ui import PropertyControl, property_control_for
|
from openflexure_microscope_server.ui import (
|
||||||
|
UI_ELEMENT_RESPONSE,
|
||||||
|
UIElementList,
|
||||||
|
property_control_for,
|
||||||
|
)
|
||||||
|
|
||||||
SettingsType = TypeVar("SettingsType", bound=BaseModel)
|
SettingsType = TypeVar("SettingsType", bound=BaseModel)
|
||||||
BackgroundType = TypeVar("BackgroundType", bound=BaseModel)
|
BackgroundType = TypeVar("BackgroundType", bound=BaseModel)
|
||||||
|
|
@ -72,9 +76,9 @@ class BackgroundDetectAlgorithm(lt.Thing):
|
||||||
"Each background detect algorithm must implement an set_background method."
|
"Each background detect algorithm must implement an set_background method."
|
||||||
)
|
)
|
||||||
|
|
||||||
@lt.property
|
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||||
def settings_ui(self) -> list[PropertyControl]:
|
def settings_ui(self) -> UIElementList:
|
||||||
"""A list of PropertyControl objects to create the settings in the UI."""
|
"""Return the UI for the background detect settings."""
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"Each background detect algorithm must implement an settings_ui method."
|
"Each background detect algorithm must implement an settings_ui method."
|
||||||
)
|
)
|
||||||
|
|
@ -113,15 +117,19 @@ class ColourChannelDetectLUV(BackgroundDetectAlgorithm):
|
||||||
image to be labeled as containing sample.
|
image to be labeled as containing sample.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@lt.property
|
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||||
def settings_ui(self) -> list[PropertyControl]:
|
def settings_ui(self) -> UIElementList:
|
||||||
"""A list of PropertyControl objects to create the settings in the UI."""
|
"""Return the UI for the background detect settings."""
|
||||||
return [
|
return UIElementList(
|
||||||
property_control_for(self, "channel_tolerance", label="Channel Tolerance"),
|
[
|
||||||
property_control_for(
|
property_control_for(
|
||||||
self, "min_sample_coverage", label="Sample Coverage Required (%)"
|
self, "channel_tolerance", label="Channel Tolerance"
|
||||||
),
|
),
|
||||||
]
|
property_control_for(
|
||||||
|
self, "min_sample_coverage", label="Sample Coverage Required (%)"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@lt.property
|
@lt.property
|
||||||
def ready(self) -> bool:
|
def ready(self) -> bool:
|
||||||
|
|
@ -237,15 +245,19 @@ class ChannelDeviationLUV(BackgroundDetectAlgorithm):
|
||||||
image to be labeled as containing sample.
|
image to be labeled as containing sample.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@lt.property
|
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||||
def settings_ui(self) -> list[PropertyControl]:
|
def settings_ui(self) -> UIElementList:
|
||||||
"""A list of PropertyControl objects to create the settings in the UI."""
|
"""Return the UI for the background detect settings."""
|
||||||
return [
|
return UIElementList(
|
||||||
property_control_for(self, "channel_tolerance", label="Channel Tolerance"),
|
[
|
||||||
property_control_for(
|
property_control_for(
|
||||||
self, "min_sample_coverage", label="Sample Coverage Required (%)"
|
self, "channel_tolerance", label="Channel Tolerance"
|
||||||
),
|
),
|
||||||
]
|
property_control_for(
|
||||||
|
self, "min_sample_coverage", label="Sample Coverage Required (%)"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@lt.property
|
@lt.property
|
||||||
def ready(self) -> bool:
|
def ready(self) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -566,7 +566,7 @@ class HistoScanWorkflow(RectGridWorkflow[HistoScanSettingsModel]):
|
||||||
[
|
[
|
||||||
Accordion(
|
Accordion(
|
||||||
title="Background Detect",
|
title="Background Detect",
|
||||||
children=self._background_detector.settings_ui,
|
children=self._background_detector.settings_ui(),
|
||||||
),
|
),
|
||||||
property_control_for(
|
property_control_for(
|
||||||
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
||||||
|
|
@ -581,7 +581,7 @@ class HistoScanWorkflow(RectGridWorkflow[HistoScanSettingsModel]):
|
||||||
),
|
),
|
||||||
property_control_for(
|
property_control_for(
|
||||||
self, "stack_dz", label="Stack dz (steps)", step=5
|
self, "stack_dz", label="Stack dz (steps)", step=5
|
||||||
).model_dump(),
|
),
|
||||||
property_control_for(
|
property_control_for(
|
||||||
self, "autofocus_dz", label="Autofocus Range (steps)", step=200
|
self, "autofocus_dz", label="Autofocus Range (steps)", step=200
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class SafeHTMLParser(HTMLParser):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.output = ""
|
self.output = ""
|
||||||
|
|
||||||
def handle_starttag(self, tag: str, attrs: dict[str, str]) -> None:
|
def handle_starttag(self, tag: str, attrs: list[tuple[str, Optional[str]]]) -> None:
|
||||||
"""Append a start tag found in the HTML if it is in the allowed list.
|
"""Append a start tag found in the HTML if it is in the allowed list.
|
||||||
|
|
||||||
This is called by the base class when a start tag is found. We only append
|
This is called by the base class when a start tag is found. We only append
|
||||||
|
|
@ -52,15 +52,16 @@ class SafeHTMLParser(HTMLParser):
|
||||||
if tag == "a":
|
if tag == "a":
|
||||||
href = None
|
href = None
|
||||||
for attr, value in attrs:
|
for attr, value in attrs:
|
||||||
if attr == "href":
|
if attr == "href" and value is not None:
|
||||||
href = value.strip()
|
href = value.strip()
|
||||||
|
|
||||||
if href:
|
if href:
|
||||||
safe_href = escape(href, quote=True)
|
safe_href = escape(href, quote=True)
|
||||||
# Always in a new tab
|
# Always in a new tab
|
||||||
self.output.append(
|
self.output += (
|
||||||
f'<a href="{safe_href}" target="_blank" rel="noopener noreferrer">'
|
f'<a href="{safe_href}" target="_blank" rel="noopener noreferrer">'
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.output += "<a>"
|
self.output += "<a>"
|
||||||
else:
|
else:
|
||||||
|
|
@ -71,7 +72,9 @@ class SafeHTMLParser(HTMLParser):
|
||||||
if tag in ALLOWED_TAGS:
|
if tag in ALLOWED_TAGS:
|
||||||
self.output += f"</{tag}>"
|
self.output += f"</{tag}>"
|
||||||
|
|
||||||
def handle_startendtag(self, tag: str, _attrs: dict[str, str]) -> None:
|
def handle_startendtag(
|
||||||
|
self, tag: str, _attrs: list[tuple[str, Optional[str]]]
|
||||||
|
) -> None:
|
||||||
"""Append a self-closing tag if it is a new line."""
|
"""Append a self-closing tag if it is a new line."""
|
||||||
if tag == "br":
|
if tag == "br":
|
||||||
self.output += "<br/>"
|
self.output += "<br/>"
|
||||||
|
|
@ -251,7 +254,7 @@ class Accordion(BaseModel):
|
||||||
|
|
||||||
element_type: Literal["accordion"] = "accordion"
|
element_type: Literal["accordion"] = "accordion"
|
||||||
title: str
|
title: str
|
||||||
children: list["UIElementModels"]
|
children: "UIElementList"
|
||||||
|
|
||||||
|
|
||||||
class Container(BaseModel):
|
class Container(BaseModel):
|
||||||
|
|
@ -262,7 +265,7 @@ class Container(BaseModel):
|
||||||
|
|
||||||
element_type: Literal["container"] = "container"
|
element_type: Literal["container"] = "container"
|
||||||
css_class: str
|
css_class: str
|
||||||
children: list["UIElementModels"]
|
children: "UIElementList"
|
||||||
|
|
||||||
|
|
||||||
UIElementModels = HTMLBlock | PropertyControl | ActionButton | Accordion | Container
|
UIElementModels = HTMLBlock | PropertyControl | ActionButton | Accordion | Container
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-for="(element, index) in elements" :key="index" class="uk-margin">
|
<div v-for="(element, index) in elements" :key="index" class="uk-margin">
|
||||||
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div v-if="element.element_type === 'html_block'" v-html="element.html"></div>
|
<div v-if="element.element_type === 'html_block'" v-html="element.html"></div>
|
||||||
|
<!-- eslint-enable -->
|
||||||
<server-specified-property-control
|
<server-specified-property-control
|
||||||
v-else-if="element.element_type === 'property_control'"
|
v-else-if="element.element_type === 'property_control'"
|
||||||
:property-data="element"
|
:property-data="element"
|
||||||
|
|
@ -12,11 +14,9 @@
|
||||||
<simple-accordion v-else-if="element.element_type === 'accordion'" :title="element.title">
|
<simple-accordion v-else-if="element.element_type === 'accordion'" :title="element.title">
|
||||||
<server-specified-interface :elements="element.children" />
|
<server-specified-interface :elements="element.children" />
|
||||||
</simple-accordion>
|
</simple-accordion>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
|
||||||
<div v-else-if="element.element_type === 'container'" :class="element.css_class">
|
<div v-else-if="element.element_type === 'container'" :class="element.css_class">
|
||||||
<server-specified-interface :elements="element.children" />
|
<server-specified-interface :elements="element.children" />
|
||||||
</div>
|
</div>
|
||||||
<!-- eslint-enable -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,10 +111,8 @@ export default {
|
||||||
);
|
);
|
||||||
if (this.backgroundDetectorName) {
|
if (this.backgroundDetectorName) {
|
||||||
this.ready = await this.readThingProperty(this.backgroundDetectorName, "ready");
|
this.ready = await this.readThingProperty(this.backgroundDetectorName, "ready");
|
||||||
this.backgroundDetectorSettings = await this.readThingProperty(
|
this.backgroundDetectorSettings =
|
||||||
this.backgroundDetectorName,
|
(await this.getThingEndpoint(this.backgroundDetectorName, "settings_ui")) || [];
|
||||||
"settings_ui",
|
|
||||||
);
|
|
||||||
this.backgroundDetectorDisplayName = await this.readThingProperty(
|
this.backgroundDetectorDisplayName = await this.readThingProperty(
|
||||||
this.backgroundDetectorName,
|
this.backgroundDetectorName,
|
||||||
"display_name",
|
"display_name",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue