ScanList gets scans from GalleryThing, no ongoing scans shown.
This commit is contained in:
parent
4583151345
commit
a16d47ed61
5 changed files with 52 additions and 63 deletions
|
|
@ -335,14 +335,25 @@ class ScanDirectoryManager:
|
||||||
return [f.name for f in os.scandir(self._base_scan_dir) if f.is_dir()]
|
return [f.name for f in os.scandir(self._base_scan_dir) if f.is_dir()]
|
||||||
|
|
||||||
@requires_lock
|
@requires_lock
|
||||||
def all_scans_info(self, ongoing: Optional[str] = None) -> list[ScanInfo]:
|
def all_scans_info(
|
||||||
"""Return a lists of ScanInfo objects for each scan."""
|
self, *, ongoing: Optional[str] = None, include_ongoing: bool = True
|
||||||
|
) -> list[ScanInfo]:
|
||||||
|
"""Return a lists of ScanInfo objects for each scan.
|
||||||
|
|
||||||
|
:param ongoing: The name of the ongoing scan (or None if no scan is ongoing).
|
||||||
|
:param include_ongoing: True (dfault) to include the scan info of the ongoing
|
||||||
|
scan in the return. False to exclude it.
|
||||||
|
|
||||||
|
:return: A list of ScanInfo objects for each scan.
|
||||||
|
"""
|
||||||
all_info: list[ScanInfo] = []
|
all_info: list[ScanInfo] = []
|
||||||
for scan_name in self.all_scans:
|
for scan_name in self.all_scans:
|
||||||
# If the scan is ongoing send flag to skip reading the json data
|
scan_is_ongoing = scan_name == ongoing
|
||||||
skip_json = scan_name == ongoing
|
if scan_is_ongoing and not include_ongoing:
|
||||||
|
continue
|
||||||
scan_dir = ScanDirectory(scan_name, self.base_dir)
|
scan_dir = ScanDirectory(scan_name, self.base_dir)
|
||||||
info = scan_dir.scan_info(skip_json=skip_json)
|
# If the scan is ongoing send flag to skip reading the json data
|
||||||
|
info = scan_dir.scan_info(skip_json=scan_is_ongoing)
|
||||||
all_info.append(info)
|
all_info.append(info)
|
||||||
return all_info
|
return all_info
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ the Things that capture the Data. This thing just provides a unified way for the
|
||||||
front end to access the data.
|
front end to access the data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Mapping, Optional, Protocol, Self, runtime_checkable
|
from typing import Any, Mapping, Optional, Protocol, Self, cast, runtime_checkable
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
@ -42,10 +42,10 @@ class GalleryThing(lt.Thing):
|
||||||
|
|
||||||
all_ofm_things: Mapping[str, OFMThing] = lt.thing_slot()
|
all_ofm_things: Mapping[str, OFMThing] = lt.thing_slot()
|
||||||
|
|
||||||
_gallery_providing_things: Optional[Mapping[str, OFMThing]] = None
|
_gallery_providing_things: Optional[Mapping[str, GalleryCompatibleThing]] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gallery_providing_things(self) -> Mapping[str, OFMThing]:
|
def gallery_providing_things(self) -> Mapping[str, GalleryCompatibleThing]:
|
||||||
"""All Things that provide data to the gallery."""
|
"""All Things that provide data to the gallery."""
|
||||||
if self._gallery_providing_things is None:
|
if self._gallery_providing_things is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
|
|
@ -56,6 +56,7 @@ class GalleryThing(lt.Thing):
|
||||||
def __enter__(self) -> Self:
|
def __enter__(self) -> Self:
|
||||||
"""Check for all gallery providing things on server startup."""
|
"""Check for all gallery providing things on server startup."""
|
||||||
self._set_gallery_providers()
|
self._set_gallery_providers()
|
||||||
|
return self
|
||||||
|
|
||||||
def _set_gallery_providers(self) -> None:
|
def _set_gallery_providers(self) -> None:
|
||||||
"""Set the mapping of gallery providing things.
|
"""Set the mapping of gallery providing things.
|
||||||
|
|
@ -77,4 +78,22 @@ class GalleryThing(lt.Thing):
|
||||||
)
|
)
|
||||||
gallery_providers.pop(key)
|
gallery_providers.pop(key)
|
||||||
|
|
||||||
self._gallery_providing_things = gallery_providers
|
# Cast the type of each thing to "GalleryCompatibleThing" as other Things have
|
||||||
|
# been popped.
|
||||||
|
self._gallery_providing_things = cast(
|
||||||
|
Mapping[str, GalleryCompatibleThing], gallery_providers
|
||||||
|
)
|
||||||
|
|
||||||
|
@lt.property
|
||||||
|
def list_data(self) -> list[dict[str, Any]]:
|
||||||
|
"""List the data from all registered things.
|
||||||
|
|
||||||
|
Currently only works with `smart_scan` as the UI cards are not customisable.
|
||||||
|
|
||||||
|
This will change to an action (or another type of endpoint) at a
|
||||||
|
later date to enable filtering, and returning only a specific page.
|
||||||
|
"""
|
||||||
|
data_list = []
|
||||||
|
for thing in self.gallery_providing_things.values():
|
||||||
|
data_list += [model.model_dump() for model in thing.get_gallery_data()]
|
||||||
|
return data_list
|
||||||
|
|
|
||||||
|
|
@ -71,16 +71,6 @@ class ActiveScanData(scan_directories.BaseScanData):
|
||||||
self.scan_result = result
|
self.scan_result = result
|
||||||
|
|
||||||
|
|
||||||
class ScanListInfo(BaseModel):
|
|
||||||
"""The information to be sent to the Scan List tab."""
|
|
||||||
|
|
||||||
scans: list[scan_directories.ScanInfo]
|
|
||||||
"""The list of scans as ScanInfo objects"""
|
|
||||||
|
|
||||||
ongoing: Optional[str]
|
|
||||||
"""The name of the ongoing scan or None"""
|
|
||||||
|
|
||||||
|
|
||||||
class JPEGBlob(lt.blob.Blob):
|
class JPEGBlob(lt.blob.Blob):
|
||||||
"""A class representing a JPEG image as a LabThings FastAPI Blob."""
|
"""A class representing a JPEG image as a LabThings FastAPI Blob."""
|
||||||
|
|
||||||
|
|
@ -197,7 +187,9 @@ class SmartScanThing(OFMThing):
|
||||||
in any ongoing scans being read.
|
in any ongoing scans being read.
|
||||||
"""
|
"""
|
||||||
ongoing_name = None if self._ongoing_scan is None else self.ongoing_scan.name
|
ongoing_name = None if self._ongoing_scan is None else self.ongoing_scan.name
|
||||||
return self._scan_dir_manager.all_scans_info(ongoing=ongoing_name)
|
return self._scan_dir_manager.all_scans_info(
|
||||||
|
ongoing=ongoing_name, include_ongoing=False
|
||||||
|
)
|
||||||
|
|
||||||
# Note that the default detector name is set at init. This is over written if
|
# Note that the default detector name is set at init. This is over written if
|
||||||
# setting is loaded from disk.
|
# setting is loaded from disk.
|
||||||
|
|
@ -567,21 +559,6 @@ class SmartScanThing(OFMThing):
|
||||||
stitch_automatically: bool = lt.setting(default=True)
|
stitch_automatically: bool = lt.setting(default=True)
|
||||||
"""Whether to run a final stitch at the end of a successful scan."""
|
"""Whether to run a final stitch at the end of a successful scan."""
|
||||||
|
|
||||||
@lt.property
|
|
||||||
def scans(self) -> ScanListInfo:
|
|
||||||
"""All the available scans.
|
|
||||||
|
|
||||||
Each scan has a name (which can be used to access it), along with
|
|
||||||
its modified and created times (according to the filesystem) and
|
|
||||||
the number of items in the ``images`` folder. Note that image count
|
|
||||||
uses a regular expression, and changes to the naming scheme will
|
|
||||||
break it.
|
|
||||||
"""
|
|
||||||
return ScanListInfo(
|
|
||||||
scans=self.get_gallery_data(),
|
|
||||||
ongoing=None if self._ongoing_scan is None else self.ongoing_scan.name,
|
|
||||||
)
|
|
||||||
|
|
||||||
@lt.endpoint(
|
@lt.endpoint(
|
||||||
"get",
|
"get",
|
||||||
"get_stitch/{scan_name}",
|
"get_stitch/{scan_name}",
|
||||||
|
|
@ -646,7 +623,7 @@ class SmartScanThing(OFMThing):
|
||||||
def purge_empty_scans(self) -> None:
|
def purge_empty_scans(self) -> None:
|
||||||
"""Delete all scan folders containing no images at the top level."""
|
"""Delete all scan folders containing no images at the top level."""
|
||||||
# JSON is ignored as it's created before any images are captured
|
# JSON is ignored as it's created before any images are captured
|
||||||
for scan_info in self._get_all_scan_info():
|
for scan_info in self.get_gallery_data():
|
||||||
if scan_info.number_of_images == 0:
|
if scan_info.number_of_images == 0:
|
||||||
self._delete_scan(scan_info.name)
|
self._delete_scan(scan_info.name)
|
||||||
|
|
||||||
|
|
@ -761,6 +738,6 @@ class SmartScanThing(OFMThing):
|
||||||
"""
|
"""
|
||||||
if self._scan_lock.locked():
|
if self._scan_lock.locked():
|
||||||
raise RuntimeError("Can't stitch previous scans while a scan is ongoing")
|
raise RuntimeError("Can't stitch previous scans while a scan is ongoing")
|
||||||
for scan in self._get_all_scan_info():
|
for scan in self.get_gallery_data():
|
||||||
if scan.dzi is None:
|
if scan.dzi is None:
|
||||||
self.stitch_scan(scan_name=scan.name)
|
self.stitch_scan(scan_name=scan.name)
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="uk-card-title scan-card-title">{{ scanData.name }}</h3>
|
<h3 class="uk-card-title scan-card-title">{{ scanData.name }}</h3>
|
||||||
<h4 v-if="ongoing" class="ongoing-msg">Scan in progress</h4>
|
<div class="button-container">
|
||||||
<div v-if="!ongoing" class="button-container">
|
|
||||||
<div class="uk-button-group scan-card-buttons">
|
<div class="uk-button-group scan-card-buttons">
|
||||||
<action-button
|
<action-button
|
||||||
class="uk-width-1-2"
|
class="uk-width-1-2"
|
||||||
|
|
@ -59,10 +58,9 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ scanData.number_of_images }} images</li>
|
<li>{{ scanData.number_of_images }} images</li>
|
||||||
<li>Created: {{ formatDate(scanData.created) }}</li>
|
<li>Created: {{ formatDate(scanData.created) }}</li>
|
||||||
<li v-if="!ongoing">Duration: {{ formatDuration(scanData.duration) }}</li>
|
<li>Duration: {{ formatDuration(scanData.duration) }}</li>
|
||||||
<li v-if="ongoing">Duration: <i>Ongoing</i></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul v-if="!ongoing">
|
<ul>
|
||||||
<li v-if="scanData.number_of_images < 3" class="warning-msg">
|
<li v-if="scanData.number_of_images < 3" class="warning-msg">
|
||||||
Not enough images to stitch
|
Not enough images to stitch
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -102,10 +100,6 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
ongoing: {
|
|
||||||
type: Boolean,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ["viewer-requested", "update-requested"],
|
emits: ["viewer-requested", "update-requested"],
|
||||||
|
|
@ -148,9 +142,7 @@ export default {
|
||||||
},
|
},
|
||||||
requestViewer() {
|
requestViewer() {
|
||||||
// Notify parent that thumbnail was clicked
|
// Notify parent that thumbnail was clicked
|
||||||
if (!this.ongoing) {
|
this.$emit("viewer-requested", this.scanData);
|
||||||
this.$emit("viewer-requested", this.scanData);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async deleteScan() {
|
async deleteScan() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -204,9 +196,4 @@ ul {
|
||||||
.scan-card-title {
|
.scan-card-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ongoing-msg {
|
|
||||||
text-align: center;
|
|
||||||
padding: 2rem 0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@
|
||||||
<scan-card
|
<scan-card
|
||||||
:scan-data="scanData"
|
:scan-data="scanData"
|
||||||
:scans-uri="scansUri"
|
:scans-uri="scansUri"
|
||||||
:ongoing="isOngoing(scanData.name)"
|
|
||||||
@viewer-requested="showScan"
|
@viewer-requested="showScan"
|
||||||
@update-requested="updateScans"
|
@update-requested="updateScans"
|
||||||
/>
|
/>
|
||||||
|
|
@ -97,7 +96,6 @@ export default {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
scans: [],
|
scans: [],
|
||||||
ongoing: null,
|
|
||||||
selectedScan: null,
|
selectedScan: null,
|
||||||
osdViewer: null,
|
osdViewer: null,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
|
@ -108,7 +106,9 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useSettingsStore, ["baseUri", "ready"]),
|
...mapState(useSettingsStore, ["baseUri", "ready"]),
|
||||||
scansUri() {
|
scansUri() {
|
||||||
return this.thingPropertyUrl("smart_scan", "scans");
|
// The scans URI is currently used for creating endpoint URIs.
|
||||||
|
// The actual property does not exist. So allowUndefined=true
|
||||||
|
return this.thingPropertyUrl("smart_scan", "scans", true);
|
||||||
},
|
},
|
||||||
scansEmpty() {
|
scansEmpty() {
|
||||||
return this.scans.length == 0;
|
return this.scans.length == 0;
|
||||||
|
|
@ -179,9 +179,7 @@ export default {
|
||||||
},
|
},
|
||||||
async updateScans() {
|
async updateScans() {
|
||||||
try {
|
try {
|
||||||
let scans_information = await this.readThingProperty("smart_scan", "scans");
|
let scans = await this.readThingProperty("gallery", "list_data");
|
||||||
let scans = scans_information.scans;
|
|
||||||
this.ongoing = scans_information.ongoing;
|
|
||||||
if (!scans | (scans.length == 0)) {
|
if (!scans | (scans.length == 0)) {
|
||||||
this.scans = scans;
|
this.scans = scans;
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +196,6 @@ export default {
|
||||||
this.scans = [];
|
this.scans = [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isOngoing(name) {
|
|
||||||
return name === this.ongoing;
|
|
||||||
},
|
|
||||||
async deleteAllScans() {
|
async deleteAllScans() {
|
||||||
try {
|
try {
|
||||||
await this.modalConfirm(
|
await this.modalConfirm(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue