Skip to content

Commit

Permalink
Merge branch 'master' into buy-credits
Browse files Browse the repository at this point in the history
 Conflicts:
	services/static-webserver/client/source/class/osparc/desktop/credits/BuyCreditsStepper.js
  • Loading branch information
ignapas committed Feb 22, 2024
2 parents 8fe3866 + 226c183 commit 2f1b3ec
Show file tree
Hide file tree
Showing 41 changed files with 510 additions and 222 deletions.
23 changes: 23 additions & 0 deletions packages/models-library/src/models_library/errors_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from pydantic.errors import PydanticErrorMixin


class OsparcErrorMixin(PydanticErrorMixin):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "code"):
cls.code = cls._get_full_class_name()
return super().__new__(cls, *args, **kwargs)

@classmethod
def _get_full_class_name(cls) -> str:
relevant_classes = [
c.__name__
for c in cls.__mro__[:-1]
if c.__name__
not in (
"PydanticErrorMixin",
"OsparcErrorMixin",
"Exception",
"BaseException",
)
]
return ".".join(reversed(relevant_classes))
14 changes: 6 additions & 8 deletions packages/models-library/tests/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@
from pydantic import BaseModel, ValidationError, conint


class B(BaseModel):
y: list[int]


class A(BaseModel):
x: conint(ge=2)
b: B
def test_pydantic_error_dict():
class B(BaseModel):
y: list[int]

class A(BaseModel):
x: conint(ge=2)
b: B

def test_pydantic_error_dict():
with pytest.raises(ValidationError) as exc_info:
A(x=-1, b={"y": [0, "wrong"]})

Expand Down
136 changes: 136 additions & 0 deletions packages/models-library/tests/test_errors_classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# pylint: disable=protected-access
# pylint: disable=redefined-outer-name
# pylint: disable=unused-argument
# pylint: disable=unused-variable
# pylint: disable=no-member


from datetime import datetime
from typing import Any

import pytest
from models_library.errors_classes import OsparcErrorMixin


def test_get_full_class_name():
class A(OsparcErrorMixin):
...

class B1(A):
...

class B2(A):
...

class C(B2):
...

class B12(B1, ValueError):
...

assert B1._get_full_class_name() == "A.B1"
assert C._get_full_class_name() == "A.B2.C"
assert A._get_full_class_name() == "A"

# diamond inheritance (not usual but supported)
assert B12._get_full_class_name() == "ValueError.A.B1.B12"


def test_error_codes_and_msg_template():
class MyBaseError(OsparcErrorMixin, Exception):
def __init__(self, **ctx: Any) -> None:
super().__init__(**ctx) # Do not forget this for base exceptions!

class MyValueError(MyBaseError, ValueError):
msg_template = "Wrong value {value}"

error = MyValueError(value=42)

assert error.code == "ValueError.MyBaseError.MyValueError"
assert f"{error}" == "Wrong value 42"

class MyTypeError(MyBaseError, TypeError):
code = "i_want_this"
msg_template = "Wrong type {type}"

error = MyTypeError(type="int")

assert error.code == "i_want_this"
assert f"{error}" == "Wrong type int"


def test_error_msg_template_override():
class MyError(OsparcErrorMixin, Exception):
msg_template = "Wrong value {value}"

error_override_msg = MyError(msg_template="I want this message")
assert str(error_override_msg) == "I want this message"

error = MyError(value=42)
assert hasattr(error, "value")
assert str(error) == f"Wrong value {error.value}"


def test_error_msg_template_nicer_override():
class MyError(OsparcErrorMixin, Exception):
msg_template = "Wrong value {value}"

def __init__(self, msg=None, **ctx: Any) -> None:
super().__init__(**ctx)
# positional argument msg (if defined) overrides the msg_template
if msg:
self.msg_template = msg

error_override_msg = MyError("I want this message")
assert str(error_override_msg) == "I want this message"

error = MyError(value=42)
assert hasattr(error, "value")
assert str(error) == f"Wrong value {error.value}"


def test_error_with_constructor():
class MyError(OsparcErrorMixin, ValueError):
msg_template = "Wrong value {value}"

# handy e.g. autocompletion
def __init__(self, *, my_value: int = 42, **extra):
super().__init__(**extra)
self.value = my_value

error = MyError(my_value=33, something_else="yes")
assert error.value == 33
assert str(error) == "Wrong value 33"
assert not hasattr(error, "my_value")

# the autocompletion does not see this
assert error.something_else == "yes"


@pytest.mark.parametrize(
"str_format,ctx,expected",
[
pytest.param("{value:10}", {"value": "Python"}, "Python ", id="left-align"),
pytest.param(
"{value:>10}", {"value": "Python"}, " Python", id="right-align"
),
pytest.param(
"{value:^10}", {"value": "Python"}, " Python ", id="center-align"
),
pytest.param("{v:.2f}", {"v": 3.1415926}, "3.14", id="decimals"),
pytest.param(
"{dt:%Y-%m-%d %H:%M}",
{"dt": datetime(2020, 5, 17, 18, 45)},
"2020-05-17 18:45",
id="datetime",
),
],
)
def test_msg_template_with_different_formats(
str_format: str, ctx: dict[str, Any], expected: str
):
class MyError(OsparcErrorMixin, ValueError):
msg_template = str_format

error = MyError(**ctx)
assert str(error) == expected
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ qx.Class.define("osparc.dashboard.StudyThumbnailExplorer", {
__getThumbnailSuggestions: function() {
const thumbnailSuggestions = new osparc.editor.ThumbnailSuggestions().set({
minHeight: this.self().THUMBNAIL_SLIDER_HEIGHT,
maxHeight: this.self().THUMBNAIL_SLIDER_HEIGHT,
maxHeight: this.self().THUMBNAIL_SLIDER_HEIGHT + 2,
backgroundColor: "transparent",
padding: [3, 0]
padding: 3
});
return thumbnailSuggestions;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ qx.Class.define("osparc.desktop.preferences.pages.TagsPage", {

__createComponents: function() {
this.__addTagButton = new qx.ui.form.Button().set({
appearance: "strong-button",
appearance: "form-button-outlined",
label: this.tr("New Tag"),
icon: "@FontAwesome5Solid/plus/14"
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,15 @@ qx.Class.define("osparc.editor.ThumbnailEditor", {
switch (id) {
case "url-field":
control = new qx.ui.form.TextField().set({
font: "text-14",
backgroundColor: "background-main",
appearance: "form-input",
placeholder: this.tr("url")
});
this.bind("url", control, "value");
this._add(control);
break;
case "thumbnails-layout": {
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
const label = new qx.ui.basic.Label(this.tr("or pick one from the list of services:"));
const label = new qx.ui.basic.Label(this.tr("or pick one from the list below:"));
control.add(label);
this._add(control, {
flex: 1
Expand All @@ -96,6 +95,10 @@ qx.Class.define("osparc.editor.ThumbnailEditor", {
}
case "scroll-thumbnails": {
control = new osparc.editor.ThumbnailSuggestions();
control.set({
padding: 2,
backgroundColor: "transparent"
})
const thumbnailsLayout = this.getChildControl("thumbnails-layout");
thumbnailsLayout.add(control);
break;
Expand All @@ -109,13 +112,19 @@ qx.Class.define("osparc.editor.ThumbnailEditor", {
case "cancel-btn": {
const buttons = this.getChildControl("buttons-layout");
control = new qx.ui.form.Button(this.tr("Cancel"));
control.set({
appearance: "form-button-text"
});
control.addListener("execute", () => this.fireEvent("cancel"), this);
buttons.add(control);
break;
}
case "save-btn": {
const buttons = this.getChildControl("buttons-layout");
control = new qx.ui.form.Button(this.tr("Save"));
control.set({
appearance: "form-button"
});
control.addListener("execute", () => {
const urlField = this.getChildControl("url-field");
const validUrl = this.self().sanitizeUrl(urlField.getValue());
Expand All @@ -136,7 +145,7 @@ qx.Class.define("osparc.editor.ThumbnailEditor", {
thumbnailSuggestions.setSuggestions(suggestions);
thumbnailSuggestions.addListener("thumbnailTapped", e => {
const thumbnailData = e.getData();
this.setUrl(thumbnailData["source"]);
this.setUrl(thumbnailData.source);
});
this.getChildControl("thumbnails-layout").setVisibility(suggestions.length ? "visible" : "excluded");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ qx.Class.define("osparc.editor.ThumbnailSuggestions", {

this.set({
alignX: "center",
maxHeight: 170
height: 118,
maxHeight: 118
});
this.setButtonsWidth(30);

Expand All @@ -49,17 +50,31 @@ qx.Class.define("osparc.editor.ThumbnailSuggestions", {
},

statics: {
defaultTemplates: [
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/Thumbnail.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/bright_coulomb.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/dynamic_hertz.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/electric_heaviside.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/energetic_ampere.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/glowing_tesla.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/illuminated_volta.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/luminous_ohm.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/magnetic_lorentz.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/radiant_maxwell.png",
"https://raw.githubusercontent.com/ZurichMedTech/s4l-assets/main/app/full/project_thumbnails/vibrant_faraday.png"
],
extractThumbnailSuggestions: function(study) {
const suggestions = new Set([]);
const wb = study.getWorkbench();
const nodes = wb.getWorkbenchInitData() ? wb.getWorkbenchInitData() : wb.getNodes();
Object.values(nodes).forEach(node => {
const srvMetadata = osparc.service.Utils.getMetaData(node["key"], node["version"]);
const srvMetadata = osparc.service.Utils.getMetaData(node.getKey(), node.getVersion());
if (srvMetadata && srvMetadata["thumbnail"] && !osparc.data.model.Node.isFrontend(node)) {
suggestions.add(srvMetadata["thumbnail"]);
}
});
return Array.from(suggestions);
const amendedArray = [...suggestions, ...this.defaultTemplates]
return Array.from(amendedArray);
}
},

Expand Down Expand Up @@ -122,25 +137,34 @@ qx.Class.define("osparc.editor.ThumbnailSuggestions", {
},

thumbnailTapped: function(thumbnail) {
this.getChildren().forEach(thumbnailImg => osparc.utils.Utils.hideBorder(thumbnailImg));
this.getChildren().forEach(thumbnailImg => {
osparc.utils.Utils.updateBorderColor(thumbnailImg, qx.theme.manager.Color.getInstance().resolve("box-shadow"));
osparc.utils.Utils.addBackground(thumbnailImg, qx.theme.manager.Color.getInstance().resolve("fab-background"));
});
const color = qx.theme.manager.Color.getInstance().resolve("background-selected-dark");
osparc.utils.Utils.addBorder(thumbnail, 1, color);
const bgColor = qx.theme.manager.Color.getInstance().resolve("background-selected");
osparc.utils.Utils.updateBorderColor(thumbnail, color);
osparc.utils.Utils.addBackground(thumbnail, bgColor);
this.fireDataEvent("thumbnailTapped", {
type: thumbnail.thumbnailType,
source: thumbnail.thumbnailFileUrl
type: thumbnail.thumbnailType || "templateThumbnail",
source: thumbnail.thumbnailFileUrl || thumbnail.getSource()
});
},

setSuggestions: function(suggestions) {
this.removeAll();
suggestions.forEach(suggestion => {
const maxHeight = this.getMaxHeight();
const thumbnail = new osparc.ui.basic.Thumbnail(suggestion["thumbnailUrl"], maxHeight, parseInt(maxHeight*2/3));
thumbnail.thumbnailType = suggestion["type"];
thumbnail.thumbnailFileUrl = suggestion["fileUrl"];
thumbnail.setMarginLeft(1); // give some extra space to the selection border
thumbnail.addListener("mouseover", () => thumbnail.set({decorator: "selected-light"}), this);
thumbnail.addListener("mouseout", () => thumbnail.set({decorator: "fab-button"}), this);
const thumbnail = new osparc.ui.basic.Thumbnail(suggestion.thumbnailUrl || suggestion, maxHeight, parseInt(maxHeight*2/3));
thumbnail.set({
minWidth: 97,
margin: 0,
decorator: "thumbnail"
});
thumbnail.thumbnailType = suggestion.type || "templateThumbnail";
thumbnail.thumbnailFileUrl = suggestion.fileUrl || suggestion;
thumbnail.addListener("mouseover", () => thumbnail.set({decorator: "thumbnail-selected"}), this);
thumbnail.addListener("mouseout", () => thumbnail.set({decorator: "thumbnail"}), this);
thumbnail.addListener("tap", () => {
this.thumbnailTapped(thumbnail);
}, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ qx.Class.define("osparc.form.tag.TagManager", {
});

const addTagButton = this.__addTagButton = new qx.ui.form.Button().set({
appearance: "strong-button",
appearance: "form-button-outlined",
label: this.tr("New Tag"),
icon: "@FontAwesome5Solid/plus/14",
alignX: "center",
Expand Down
Loading

0 comments on commit 2f1b3ec

Please sign in to comment.