Skip to content

Commit b791440

Browse files
Merge branch 'master' into extract-celery-code
2 parents 6ea49fe + a21f916 commit b791440

File tree

27 files changed

+339
-95
lines changed

27 files changed

+339
-95
lines changed

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,5 @@ This document provides guidelines and best practices for using GitHub Copilot in
5151

5252
- [Python Coding Conventions](../docs/coding-conventions.md)
5353
- [Environment Variables Guide](../docs/env-vars.md)
54-
- [Steps to Upgrade Python](../docs/steps-to-upgrade-python.md)
5554
- [Pydantic Annotated fields](../docs/llm-prompts/pydantic-annotated-fields.md)
55+
- [Steps to Upgrade Python](../docs/steps-to-upgrade-python.md)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
mode: 'edit'
3+
description: 'Update user messages'
4+
---
5+
6+
This prompt guide is for updating user-facing messages in ${file} or ${selection}
7+
8+
## What is a User Message?
9+
10+
A user message is any string that will be displayed to end-users of the application.
11+
In our codebase, these messages are marked with the `user_message` function:
12+
13+
```python
14+
from common_library.user_messages import user_message
15+
16+
error_msg = user_message("Operation failed. Please try again later.")
17+
```
18+
19+
## Guidelines for Updating User Messages
20+
21+
When modifying user messages, follow these rules:
22+
23+
1. **Version Tracking**: Every modification to a user message must include an incremented `_version` parameter:
24+
25+
```python
26+
# Before modification
27+
user_message("Error: Unable to connect to the server.")
28+
29+
# After modification, add _version or increment it if it already exists
30+
user_message("Currently unable to establish connection to the server.", _version=1)
31+
```
32+
33+
2. **F-String Preservation**: When modifying messages that use f-strings, preserve all parameters and their formatting:
34+
35+
```python
36+
# Before
37+
user_message(f"Project {project_name} could not be loaded.")
38+
39+
# After (correct)
40+
user_message(f"Unable to load project {project_name}.", _version=1)
41+
42+
# After (incorrect - lost the parameter)
43+
user_message("Unable to load project.", _version=1)
44+
```
45+
46+
3. **Message Style**: Follow *strictly* the guidelines in `${workspaceFolder}/docs/user-messages-guidelines.md`
47+
48+
4. **Preserve Context**: Ensure the modified message conveys the same meaning and context as the original.
49+
50+
5. **Incremental Versioning**: If a message already has a version, increment it by 1:
51+
52+
```python
53+
# Before
54+
user_message("Session expired.", _version=2)
55+
56+
# After
57+
user_message("Your session has expired. Please log in again.", _version=3)
58+
```
59+
60+
## Examples
61+
62+
### Example 1: Simple Message Update
63+
64+
```python
65+
# Before
66+
error_dialog(user_message("Failed to save changes."))
67+
68+
# After
69+
error_dialog(user_message("Unable to save your changes. Please try again.", _version=1))
70+
```
71+
72+
### Example 2: F-string Message Update
73+
74+
```python
75+
# Before
76+
raise ValueError(user_message(f"Invalid input parameter: {param_name}"))
77+
78+
# After
79+
raise ValueError(user_message(f"The parameter '{param_name}' contains a value that is not allowed.", _version=1))
80+
```
81+
82+
### Example 3: Already Versioned Message
83+
84+
```python
85+
# Before
86+
return HttpErrorInfo(status.HTTP_404_NOT_FOUND, user_message("User not found.", _version=1))
87+
88+
# After
89+
return HttpErrorInfo(status.HTTP_404_NOT_FOUND, user_message("The requested user could not be found.", _version=2))
90+
```
91+
92+
Remember: The goal is to improve clarity and helpfulness for end-users while maintaining accurate versioning for tracking changes.

docs/messages-guidelines.md renamed to docs/user-messages-guidelines.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Error and Warning Message Guidelines
1+
# User Message Guidelines
22

3-
These guidelines ensure that messages are user-friendly, clear, and helpful while maintaining a professional tone. 🚀
3+
These guidelines ensure that error and warnings user-facing messages are user-friendly, clear, and helpful while maintaining a professional tone. 🚀
44

55
Some details:
66

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def user_message(msg: str, *, _version: int | None = None) -> str:
2+
"""Marks a message as user-facing
3+
4+
Arguments:
5+
msg -- human-friendly string that follows docs/user-messages-guidelines.md
6+
_version -- version number to track changes to messages; increment when modifying an existing message
7+
8+
Returns:
9+
The original message string, allowing it to be used inline in code
10+
"""
11+
return msg
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from common_library.user_messages import user_message
2+
3+
4+
def test_user_message() -> None:
5+
6+
assert user_message("This is a user message") == "This is a user message"

packages/service-library/src/servicelib/aiohttp/rest_middlewares.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from aiohttp.web_response import StreamResponse
1414
from common_library.error_codes import create_error_code
1515
from common_library.json_serialization import json_dumps, json_loads
16+
from common_library.user_messages import user_message
1617
from models_library.rest_error import ErrorGet, ErrorItemType, LogMessageType
1718

1819
from ..logging_errors import create_troubleshotting_log_kwargs
@@ -31,7 +32,7 @@
3132
from .web_exceptions_extension import get_http_error_class_or_none
3233

3334
DEFAULT_API_VERSION = "v0"
34-
_FMSG_INTERNAL_ERROR_USER_FRIENDLY = (
35+
_FMSG_INTERNAL_ERROR_USER_FRIENDLY = user_message(
3536
"We apologize for the inconvenience. "
3637
"The issue has been recorded, please report it if it persists."
3738
)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
5757
case "template":
5858
case "tutorial":
5959
case "hypertool":
60+
// when getting the latest study data, the debt information was lost
61+
if (osparc.study.Utils.isInDebt(this.__resourceData)) {
62+
const mainStore = osparc.store.Store.getInstance();
63+
this.__resourceData["debt"] = mainStore.getStudyDebt(this.__resourceData["uuid"]);
64+
}
6065
osparc.store.Services.getStudyServicesMetadata(latestResourceData)
6166
.finally(() => {
6267
this.__resourceModel = new osparc.data.model.Study(latestResourceData);
@@ -89,6 +94,7 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
8994
"updateService": "qx.event.type.Data",
9095
"updateHypertool": "qx.event.type.Data",
9196
"publishTemplate": "qx.event.type.Data",
97+
"closeWindow": "qx.event.type.Event",
9298
},
9399

94100

@@ -107,6 +113,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
107113
width: this.WIDTH,
108114
height: this.HEIGHT,
109115
});
116+
resourceDetails.addListener("closeWindow", () => {
117+
win.close();
118+
});
110119
return win;
111120
},
112121

@@ -479,6 +488,9 @@ qx.Class.define("osparc.dashboard.ResourceDetails", {
479488
const enabled = osparc.study.Utils.canBeOpened(resourceData);
480489
page.openButton.setEnabled(enabled);
481490
})
491+
billingSettings.addListener("closeWindow", () => {
492+
this.fireEvent("closeWindow");
493+
}, this);
482494
const billingScroll = new qx.ui.container.Scroll(billingSettings);
483495
page.addToContent(billingScroll);
484496
}

services/static-webserver/client/source/class/osparc/desktop/StudyEditor.js

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -179,20 +179,7 @@ qx.Class.define("osparc.desktop.StudyEditor", {
179179
if ("status" in err && err["status"]) {
180180
if (err["status"] == 402) {
181181
msg = err["message"];
182-
// The backend might have thrown a 402 because the wallet was negative
183-
const match = msg.match(/last transaction of\s([-]?\d+(\.\d+)?)\sresulted/);
184-
let debt = null;
185-
if ("debtAmount" in err) {
186-
// the study has some debt that needs to be paid
187-
debt = err["debtAmount"];
188-
} else if (match) {
189-
// the study has some debt that needs to be paid
190-
debt = parseFloat(match[1]); // Convert the captured string to a number
191-
}
192-
if (debt) {
193-
// if get here, it means that the 402 was thrown due to the debt
194-
osparc.store.Store.getInstance().setStudyDebt(study.getUuid(), debt);
195-
}
182+
osparc.study.Utils.extractDebtFromError(study.getUuid(), err);
196183
} else if (err["status"] == 409) { // max_open_studies_per_user
197184
msg = err["message"];
198185
} else if (err["status"] == 423) { // Locked

services/static-webserver/client/source/class/osparc/share/RequestServiceAccess.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,17 @@ qx.Class.define("osparc.share.RequestServiceAccess", {
5858

5959
// Populate the grid with the cantReadServicesData
6060
cantReadServicesData.forEach((cantReadServiceData, idx) => {
61-
const group = osparc.store.Groups.getInstance().getGroup(cantReadServiceData["owner"]);
62-
if (group) {
63-
const username = new qx.ui.basic.Label(group.getLabel()).set({
61+
const userGroupId = cantReadServiceData["owner"];
62+
if (userGroupId) {
63+
const username = new qx.ui.basic.Label().set({
6464
rich: true,
6565
selectable: true,
6666
});
6767
layout.add(username, {
6868
row: idx+1,
6969
column: 0
7070
});
71-
const email = new qx.ui.basic.Label(group.getEmail()).set({
71+
const email = new qx.ui.basic.Label().set({
7272
rich: true,
7373
selectable: true,
7474
});
@@ -85,6 +85,16 @@ qx.Class.define("osparc.share.RequestServiceAccess", {
8585
row: idx+1,
8686
column: 2
8787
});
88+
89+
osparc.store.Users.getInstance().getUser(userGroupId)
90+
.then(user => {
91+
username.setValue(user ? user.getLabel() : this.tr("Unknown user"));
92+
email.setValue(user ? user.getEmail() : "Unknown email");
93+
})
94+
.catch(() => {
95+
username.setValue(this.tr("Unknown user"));
96+
email.setValue("Unknown email");
97+
});
8898
}
8999
});
90100
}

services/static-webserver/client/source/class/osparc/store/Store.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ qx.Class.define("osparc.store.Store", {
253253
},
254254

255255
members: {
256+
__studiesInDebt: null,
257+
256258
// fetch resources that do not require log in
257259
preloadCalls: async function() {
258260
await osparc.data.Resources.get("config");
@@ -454,6 +456,16 @@ qx.Class.define("osparc.store.Store", {
454456
},
455457

456458
setStudyDebt: function(studyId, debt) {
459+
// init object if it does not exist
460+
if (this.__studiesInDebt === null) {
461+
this.__studiesInDebt = {};
462+
}
463+
if (debt) {
464+
this.__studiesInDebt[studyId] = debt;
465+
} else {
466+
delete this.__studiesInDebt[studyId];
467+
}
468+
457469
const studiesWStateCache = this.getStudies();
458470
const idx = studiesWStateCache.findIndex(studyWStateCache => studyWStateCache["uuid"] === studyId);
459471
if (idx !== -1) {
@@ -470,6 +482,17 @@ qx.Class.define("osparc.store.Store", {
470482
});
471483
},
472484

485+
getStudyDebt: function(studyId) {
486+
if (this.__studiesInDebt && studyId in this.__studiesInDebt) {
487+
return this.__studiesInDebt[studyId];
488+
}
489+
return null;
490+
},
491+
492+
isStudyInDebt: function(studyId) {
493+
return Boolean(this.getStudyDebt(studyId));
494+
},
495+
473496
trashStudy: function(studyId) {
474497
const params = {
475498
url: {

0 commit comments

Comments
 (0)