Skip to content

Commit c01d337

Browse files
Merge remote-tracking branch 'upstream/master' into extract-celery-code
2 parents 5d3425e + 3f0809d commit c01d337

File tree

13 files changed

+178
-98
lines changed

13 files changed

+178
-98
lines changed

.github/workflows/ci-testing-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ jobs:
342342
unit-test-webserver-02:
343343
needs: changes
344344
if: ${{ needs.changes.outputs.webserver == 'true' || github.event_name == 'push' || github.event.inputs.force_all_builds == 'true' }}
345-
timeout-minutes: 25 # if this timeout gets too small, then split the tests
345+
timeout-minutes: 35 # if this timeout gets too small, then split the tests
346346
name: "[unit] webserver 02"
347347
runs-on: ${{ matrix.os }}
348348
strategy:

.vscode/settings.template.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This is a template. Clone and replace extension ".template.json" by ".json"
22
{
33
"autoDocstring.docstringFormat": "pep257",
4+
45
"editor.tabSize": 2,
56
"editor.insertSpaces": true,
67
"editor.detectIndentation": false,

services/dask-sidecar/src/simcore_service_dask_sidecar/computational_sidecar/core.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pathlib import Path
88
from pprint import pformat
99
from types import TracebackType
10-
from typing import cast
10+
from typing import Final, cast
1111
from uuid import uuid4
1212

1313
from aiodocker import Docker
@@ -32,7 +32,6 @@
3232
from ..utils.dask import TaskPublisher
3333
from ..utils.files import (
3434
check_need_unzipping,
35-
log_partial_file_content,
3635
pull_file_from_remote,
3736
push_file_to_remote,
3837
)
@@ -49,8 +48,8 @@
4948
from .task_shared_volume import TaskSharedVolumes
5049

5150
_logger = logging.getLogger(__name__)
52-
_CONTAINER_WAIT_TIME_SECS = 2
53-
_MAX_LOGGED_FILE_CHARS = 40
51+
CONTAINER_WAIT_TIME_SECS = 2
52+
_TASK_PROCESSING_PROGRESS_WEIGHT: Final[float] = 0.99
5453

5554

5655
@dataclass(kw_only=True, frozen=True, slots=True)
@@ -148,17 +147,11 @@ async def _retrieve_output_data(
148147
upload_tasks = []
149148
for output_params in output_data.values():
150149
if isinstance(output_params, FileUrl):
151-
assert (
150+
assert ( # nosec
152151
output_params.file_mapping
153152
), f"{output_params.model_dump_json(indent=1)} expected resolved in TaskOutputData.from_task_output"
154153

155154
src_path = task_volumes.outputs_folder / output_params.file_mapping
156-
await log_partial_file_content(
157-
src_path,
158-
logger=_logger,
159-
log_level=logging.DEBUG,
160-
max_chars=_MAX_LOGGED_FILE_CHARS,
161-
)
162155
upload_tasks.append(
163156
push_file_to_remote(
164157
src_path,
@@ -274,7 +267,7 @@ async def run(self, command: list[str]) -> TaskOutputData:
274267
)
275268
# wait until the container finished, either success or fail or timeout
276269
while (container_data := await container.show())["State"]["Running"]:
277-
await asyncio.sleep(_CONTAINER_WAIT_TIME_SECS)
270+
await asyncio.sleep(CONTAINER_WAIT_TIME_SECS)
278271
if container_data["State"]["ExitCode"] > os.EX_OK:
279272
raise ServiceRuntimeError(
280273
service_key=self.task_parameters.image,

services/dask-sidecar/src/simcore_service_dask_sidecar/utils/files.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from settings_library.s3 import S3Settings
2121
from yarl import URL
2222

23-
_logger = logging.getLogger(__name__)
23+
logger = logging.getLogger(__name__)
2424

2525
HTTP_FILE_SYSTEM_SCHEMES: Final = ["http", "https"]
2626
S3_FILE_SYSTEM_SCHEMES: Final = ["s3", "s3a"]
@@ -208,7 +208,7 @@ async def pull_file_from_remote(
208208
await log_publishing_cb(
209209
f"Uncompressing '{download_dst_path.name}'...", logging.INFO
210210
)
211-
_logger.debug(
211+
logger.debug(
212212
"%s is a zip file and will be now uncompressed", download_dst_path
213213
)
214214
with repro_zipfile.ReproducibleZipFile(download_dst_path, "r") as zip_obj:
@@ -258,7 +258,7 @@ async def _push_file_to_remote(
258258
log_publishing_cb: LogPublishingCB,
259259
s3_settings: S3Settings | None,
260260
) -> None:
261-
_logger.debug("Uploading %s to %s...", file_to_upload, dst_url)
261+
logger.debug("Uploading %s to %s...", file_to_upload, dst_url)
262262
assert dst_url.path # nosec
263263

264264
storage_kwargs: S3FsSettingsDict | dict[str, Any] = {}
@@ -306,7 +306,7 @@ async def push_file_to_remote(
306306
await asyncio.get_event_loop().run_in_executor(
307307
None, zfp.write, src_path, src_path.name
308308
)
309-
_logger.debug("%s created.", archive_file_path)
309+
logger.debug("%s created.", archive_file_path)
310310
assert archive_file_path.exists() # nosec
311311
file_to_upload = archive_file_path
312312
await log_publishing_cb(
@@ -319,7 +319,7 @@ async def push_file_to_remote(
319319
)
320320

321321
if dst_url.scheme in HTTP_FILE_SYSTEM_SCHEMES:
322-
_logger.debug("destination is a http presigned link")
322+
logger.debug("destination is a http presigned link")
323323
await _push_file_to_http_link(file_to_upload, dst_url, log_publishing_cb)
324324
else:
325325
await _push_file_to_remote(
@@ -330,22 +330,3 @@ async def push_file_to_remote(
330330
f"Upload of '{src_path.name}' to '{dst_url.path.strip('/')}' complete",
331331
logging.INFO,
332332
)
333-
334-
335-
async def log_partial_file_content(
336-
src_path: Path, *, logger: logging.Logger, log_level: int, max_chars: int
337-
) -> None:
338-
if max_chars < 0:
339-
msg = "max_chars must be non-negative"
340-
raise ValueError(msg)
341-
if max_chars == 0:
342-
return
343-
if not src_path.exists():
344-
logger.log(log_level, "file does not exist: %s", src_path)
345-
return
346-
async with aiofiles.open(src_path, encoding="utf-8") as f:
347-
content = await f.read(max_chars + 1)
348-
if len(content) > max_chars:
349-
logger.log(log_level, "file content (truncated): %s...", content[:max_chars])
350-
else:
351-
logger.log(log_level, "file content: %s", content)

services/dask-sidecar/tests/unit/test_utils_files.py

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import asyncio
66
import hashlib
7-
import logging
87
import mimetypes
98
import zipfile
109
from collections.abc import AsyncIterable
@@ -22,7 +21,6 @@
2221
from settings_library.s3 import S3Settings
2322
from simcore_service_dask_sidecar.utils.files import (
2423
_s3fs_settings_from_s3_settings,
25-
log_partial_file_content,
2624
pull_file_from_remote,
2725
push_file_to_remote,
2826
)
@@ -513,48 +511,3 @@ async def test_push_file_to_remote_creates_reproducible_zip_archive(
513511
assert dst_path2.exists()
514512

515513
assert _compute_hash(dst_path1) == _compute_hash(dst_path2)
516-
517-
518-
async def test_log_partial_file_content(
519-
tmp_path: Path, caplog: pytest.LogCaptureFixture
520-
):
521-
# Create a file with known content
522-
file_content = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
523-
file_path = tmp_path / "testfile.txt"
524-
file_path.write_text(file_content)
525-
logger = logging.getLogger("pytest.utils.files")
526-
527-
# Case 1: file longer than max_chars
528-
with caplog.at_level(logging.DEBUG, logger=logger.name):
529-
await log_partial_file_content(
530-
file_path, logger=logger, log_level=logging.DEBUG, max_chars=10
531-
)
532-
assert any(
533-
"file content (truncated): abcdefghij..." in record.getMessage()
534-
for record in caplog.records
535-
)
536-
537-
# Case 2: file shorter than max_chars
538-
caplog.clear()
539-
short_content = "short"
540-
short_file = tmp_path / "short.txt"
541-
short_file.write_text(short_content)
542-
with caplog.at_level(logging.DEBUG, logger=logger.name):
543-
await log_partial_file_content(
544-
short_file, logger=logger, log_level=logging.DEBUG, max_chars=10
545-
)
546-
assert any(
547-
"file content: short" in record.getMessage() for record in caplog.records
548-
)
549-
550-
# Case 3: file does not exist
551-
caplog.clear()
552-
non_existent = tmp_path / "doesnotexist.txt"
553-
with caplog.at_level(logging.DEBUG, logger=logger.name):
554-
await log_partial_file_content(
555-
non_existent, logger=logger, log_level=logging.DEBUG, max_chars=10
556-
)
557-
assert any(
558-
f"file does not exist: {non_existent}" in record.getMessage()
559-
for record in caplog.records
560-
)

services/static-webserver/client/source/class/osparc/dashboard/GroupedCardContainer.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ qx.Class.define("osparc.dashboard.GroupedCardContainer", {
2323

2424
this._setLayout(new qx.ui.layout.VBox());
2525

26-
const showAllButton = this.__showAllButton = new qx.ui.form.Button().set({
26+
const showAllButton = this.__expandButton = new qx.ui.form.Button().set({
2727
margin: 10,
2828
marginBottom: 5
2929
});
@@ -70,7 +70,7 @@ qx.Class.define("osparc.dashboard.GroupedCardContainer", {
7070
},
7171

7272
members: {
73-
__showAllButton: null,
73+
__expandButton: null,
7474
__contentContainer: null,
7575

7676
_createChildControlImpl: function(id) {
@@ -116,7 +116,7 @@ qx.Class.define("osparc.dashboard.GroupedCardContainer", {
116116
__createContentContainer: function() {
117117
let contentContainer = null;
118118
const expanded = this.isExpanded();
119-
const showAllBtn = this.__showAllButton;
119+
const showAllBtn = this.__expandButton;
120120
if (expanded) {
121121
contentContainer = new osparc.dashboard.CardContainer();
122122
showAllBtn.show();
@@ -174,6 +174,10 @@ qx.Class.define("osparc.dashboard.GroupedCardContainer", {
174174
return this.__contentContainer;
175175
},
176176

177+
getExpandButton: function() {
178+
return this.__expandButton;
179+
},
180+
177181
// overridden
178182
add: function(child, idx) {
179183
if (osparc.dashboard.CardContainer.isValidCard(child)) {

services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
324324
this._addToLayout(resourcesContainer);
325325
},
326326

327-
__groupByChanged: function(groupBy) {
327+
_groupByChanged: function(groupBy) {
328328
// if cards are grouped they need to be in grid mode
329329
this._resourcesContainer.setMode("grid");
330330
this.__viewModeLayout.setVisibility(groupBy ? "excluded" : "visible");
@@ -356,27 +356,34 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", {
356356

357357
const dontGroup = new qx.ui.menu.RadioButton(this.tr("None"));
358358
osparc.utils.Utils.setIdToWidget(dontGroup, "groupByNone");
359-
dontGroup.addListener("execute", () => this.__groupByChanged(null));
359+
dontGroup.addListener("execute", () => this._groupByChanged(null));
360360

361361
groupByMenu.add(dontGroup);
362362
groupOptions.add(dontGroup);
363363

364364
if (this._resourceType === "template") {
365-
const tagByGroup = new qx.ui.menu.RadioButton(this.tr("Tags"));
366-
tagByGroup.addListener("execute", () => this.__groupByChanged("tags"));
367-
groupByMenu.add(tagByGroup);
368-
groupOptions.add(tagByGroup);
365+
const groupByTag = new qx.ui.menu.RadioButton(this.tr("Tags"));
366+
groupByTag.addListener("execute", () => this._groupByChanged("tags"));
367+
groupByMenu.add(groupByTag);
368+
groupOptions.add(groupByTag);
369369
if (
370370
osparc.product.Utils.isProduct("s4l") ||
371371
osparc.product.Utils.isProduct("s4lacad") ||
372372
osparc.product.Utils.isProduct("s4llite")
373373
) {
374-
tagByGroup.execute();
374+
groupByTag.execute();
375375
}
376+
} else if (this._resourceType === "service" && osparc.product.Utils.groupServices()) {
377+
const groupByFeatured = new qx.ui.menu.RadioButton(this.tr("Featured"));
378+
groupByFeatured.addListener("execute", () => this._groupByChanged("groupedServices"));
379+
groupByMenu.add(groupByFeatured);
380+
groupOptions.add(groupByFeatured);
381+
groupByFeatured.execute();
382+
groupByButton.exclude(); // don't let users change the grouping
376383
}
377384

378385
const groupByShared = new qx.ui.menu.RadioButton(this.tr("Shared with"));
379-
groupByShared.addListener("execute", () => this.__groupByChanged("shared"));
386+
groupByShared.addListener("execute", () => this._groupByChanged("shared"));
380387
groupByMenu.add(groupByShared);
381388
groupOptions.add(groupByShared);
382389

services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
5858
},
5959

6060
groupBy: {
61-
check: [null, "tags", "shared"],
61+
check: [null, "tags", "shared", "groupedServices"],
6262
init: null,
6363
nullable: true
6464
}
@@ -309,8 +309,11 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
309309
case "shared":
310310
groupTitle = "Not Shared";
311311
break;
312+
case "groupedServices":
313+
groupTitle = "Misc";
314+
break;
312315
}
313-
const noGroupContainer = this.__createGroupContainer("no-group", groupTitle, "transparent");
316+
const noGroupContainer = this.__createGroupContainer("no-group", groupTitle, "text");
314317
this.__groupedContainers.add(noGroupContainer);
315318
this._add(this.__groupedContainers);
316319
} else {
@@ -525,12 +528,55 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", {
525528
}
526529
},
527530

531+
__groupByGroupedServices: function(cards, resourceData) {
532+
const groupedServicesConfig = osparc.store.Products.getInstance().getGroupedServicesUiConfig();
533+
if (groupedServicesConfig == null) {
534+
return;
535+
}
536+
537+
// create group containers for each category
538+
groupedServicesConfig["categories"].forEach(category => {
539+
if (this.__getGroupContainer(category["id"]) === null) {
540+
const groupContainer = this.__createGroupContainer(category["id"], category["title"], category["color"]);
541+
groupContainer.setHeaderIcon("@FontAwesome5Solid/tag/24");
542+
this.__groupedContainers.add(groupContainer);
543+
}
544+
});
545+
546+
// get the right container
547+
let container = null;
548+
const serviceKey = resourceData["key"];
549+
if (serviceKey) {
550+
const groupInfo = groupedServicesConfig["services"].find(serviceInfo => serviceInfo["serviceKey"] === serviceKey);
551+
if (groupInfo) {
552+
container = this.__getGroupContainer(groupInfo["category"]);
553+
}
554+
}
555+
if (container === null) {
556+
container = this.__getGroupContainer("no-group");
557+
container.setHeaderIcon("@FontAwesome5Solid/tag/24");
558+
}
559+
560+
// create the card and add it to the container
561+
const card = this.__createCard(resourceData);
562+
this.__addCardToContainer(card, container);
563+
cards.push(card);
564+
565+
this.__moveNoGroupToLast();
566+
this.__groupedContainersList.forEach(groupedContainer => {
567+
groupedContainer.setExpanded(true);
568+
groupedContainer.getExpandButton().exclude();
569+
});
570+
},
571+
528572
__resourceToCards: function(resourceData) {
529573
const cardsCreated = [];
530574
if (this.getGroupBy() === "tags") {
531575
this.__groupByTags(cardsCreated, resourceData);
532576
} else if (this.getGroupBy() === "shared") {
533577
this.__groupByShareWith(cardsCreated, resourceData);
578+
} else if (this.getGroupBy() === "groupedServices") {
579+
this.__groupByGroupedServices(cardsCreated, resourceData);
534580
} else {
535581
const card = this.__createCard(resourceData);
536582
this.__addCardToContainer(card, this.__nonGroupedContainer);

services/static-webserver/client/source/class/osparc/desktop/preferences/window/ShowAPIKey.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ qx.Class.define("osparc.desktop.preferences.window.ShowAPIKey", {
7676
const hBox = this.__createEntry(title);
7777
if (label) {
7878
// partially hide the key and secret
79-
hBox.getChildren()[1].setValue(label.substring(1, 8) + "****")
79+
hBox.getChildren()[1].setValue(label.substring(0, 8) + "****")
8080
}
8181
return hBox;
8282
},

0 commit comments

Comments
 (0)