supervisely 6.73.373__py3-none-any.whl → 6.73.374__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ from supervisely.app.widgets_context import JinjaWidgets
13
13
  class RunAppButton(Widget):
14
14
  def __init__(
15
15
  self,
16
+ team_id: int,
16
17
  workspace_id: int,
17
18
  module_id: int,
18
19
  payload: dict = None,
@@ -26,11 +27,14 @@ class RunAppButton(Widget):
26
27
  icon_gap: Optional[int] = 5,
27
28
  available_in_offline: Optional[bool] = False,
28
29
  visible_by_vue_field: Optional[str] = "",
30
+ check_existing_task_cb: Optional[str] = "null",
29
31
  widget_id: Optional[str] = None,
30
32
  ):
31
33
  """
32
34
  Button the runs an app on Supervisely instance.
33
35
 
36
+ :param team_id: Team ID.
37
+ :type team_id: int
34
38
  :param workspace_id: Workspace ID.
35
39
  :type workspace_id: int
36
40
  :param module_id: Module ID.
@@ -53,6 +57,8 @@ class RunAppButton(Widget):
53
57
  :type available_in_offline: bool, optional
54
58
  :param visible_by_vue_field: Vue field that controls the button visibility. If set to "isStaticVersion", the button will be visible only in offline session.
55
59
  :type visible_by_vue_field: str, optional
60
+ :param check_existing_task_cb: Sets the callback function for checking existing tasks. Function should be a string (docstring) of JavaScript code.
61
+ :type check_existing_task_cb: str, optional
56
62
  :param widget_id: Widget ID.
57
63
  :type widget_id: str, optional
58
64
 
@@ -88,12 +94,13 @@ class RunAppButton(Widget):
88
94
 
89
95
  self._available_in_offline = available_in_offline
90
96
  self._visible_by_vue_field = visible_by_vue_field
91
-
92
97
  self._loading = False
93
98
  self._disabled = False
99
+ self._team_id = team_id
94
100
  self._workspace_id = workspace_id
95
101
  self._module_id = module_id
96
102
  self._payload = payload
103
+ self._check_existing_task_cb = check_existing_task_cb
97
104
 
98
105
  super().__init__(widget_id=widget_id, file_path=__file__)
99
106
 
@@ -114,6 +121,7 @@ class RunAppButton(Widget):
114
121
  - available_in_offline: If True, the button will be available in offline session.
115
122
  """
116
123
  return {
124
+ "check_existing_task_cb": self._check_existing_task_cb,
117
125
  "options": {
118
126
  "text": self._text,
119
127
  "button_type": self._button_type,
@@ -123,12 +131,13 @@ class RunAppButton(Widget):
123
131
  "disabled": self._disabled,
124
132
  "icon": self._icon,
125
133
  "available_in_offline": self._available_in_offline,
126
- }
134
+ },
127
135
  }
128
136
 
129
137
  def get_json_state(self) -> None:
130
138
  """Button widget doesn't have state, so this method returns None."""
131
139
  return {
140
+ "team_id": self._team_id,
132
141
  "workspace_id": self._workspace_id,
133
142
  "module_id": self._module_id,
134
143
  "payload": self._payload,
@@ -298,3 +307,14 @@ class RunAppButton(Widget):
298
307
  """
299
308
  self._disabled = value
300
309
  DataJson()[self.widget_id]["options"]["disabled"] = self._disabled
310
+
311
+ def set_check_existing_task_cb(self, function: str) -> None:
312
+ """Sets the callback function for checking existing tasks.
313
+ Function should be a string (docstring) of JavaScript code.
314
+
315
+ :param function: Callback function for checking existing tasks.
316
+ :type function: str
317
+ """
318
+ self._check_existing_task_cb = function
319
+ DataJson()[self.widget_id]["check_existing_task_cb"] = self._check_existing_task_cb
320
+ DataJson().send_changes()
@@ -1,46 +1,106 @@
1
- Vue.component('sly-run-app-button', {
2
- props: {
3
- 'publicApiInstance': { 'type': Object },
4
- 'workspaceId': { 'type': Number }, 'moduleId': { 'type': Number }, 'payload': { 'type': Object }, 'options': { 'type': Object }
5
- },
6
- data() {
7
- return {
8
- loading: false,
9
- };
10
- },
11
- methods: {
12
- async runApp() {
13
- try {
14
- this.loading = true;
15
-
16
- const tasks = await this.publicApiInstance.post('tasks.run.app', {
17
- params: this.payload,
18
- workspaceId: this.workspaceId,
19
- moduleId: this.moduleId,
20
- nodeId: null,
21
- }).then(response => response.data);
22
-
23
- const task = tasks[0];
24
- const origin = new URL(this.publicApiInstance.defaults.baseURL).origin;
25
- window.open(`${origin}/apps/${task.appId}/sessions/${task.taskId}`, '_blank');
26
- } finally {
27
- this.loading = false;
28
- }
29
- }
30
- },
31
- template: `
32
- <el-button
33
- :class="{'available-in-offline': options.available_in_offline}"
34
- @click="runApp"
35
- v-loading="options.loading || loading"
36
- :type="options.button_type"
37
- :plain="options.plain"
38
- :size="options.button_size"
39
- :disabled="options.disabled"
40
-
41
- >
42
- <span v-html="options.icon"></span>
43
- <span v-html="options.text"></span>
44
- </el-button>
45
- `,
1
+ Vue.component("sly-run-app-button", {
2
+ props: {
3
+ publicApiInstance: { type: Function },
4
+ workspaceId: { type: Number },
5
+ moduleId: { type: Number },
6
+ payload: { type: Object },
7
+ options: { type: Object, default: () => ({}) },
8
+ groupId: { type: Number },
9
+ checkExistingTaskCb: { type: Function, default: null },
10
+ },
11
+ data() {
12
+ return {
13
+ loading: false,
14
+ };
15
+ },
16
+ methods: {
17
+ async runApp() {
18
+ try {
19
+ this.loading = true;
20
+
21
+ if (this.checkExistingTaskCb) {
22
+ let checkExistingTaskCb =
23
+ typeof this.checkExistingTaskCb === "function"
24
+ ? this.checkExistingTaskCb
25
+ : null;
26
+
27
+ if (typeof this.checkExistingTaskCb === "string") {
28
+ console.log("checkExistingTaskCb", this.checkExistingTaskCb);
29
+ try {
30
+ checkExistingTaskCb = new Function(
31
+ "task",
32
+ this.checkExistingTaskCb
33
+ );
34
+ } catch (err) {
35
+ console.log("Error parsing checkExistingTaskCb string:", err);
36
+ }
37
+ }
38
+
39
+ console.log("Before check checkExistingTaskCb", checkExistingTaskCb);
40
+ if (checkExistingTaskCb) {
41
+ const allEntities = await this.publicApiInstance
42
+ .post("apps.list", {
43
+ withShared: true,
44
+ onlyRunning: true,
45
+ groupId: this.groupId,
46
+ filter: [
47
+ { field: "moduleId", operator: "=", value: this.moduleId },
48
+ ],
49
+ })
50
+ .then((res) => res.data?.entities || []);
51
+
52
+ const existTasks = allEntities.flatMap(
53
+ (entity) => entity.tasks || []
54
+ );
55
+
56
+ if (existTasks.length) {
57
+ const foundTask = existTasks.find((t) => checkExistingTaskCb(t));
58
+ console.log("foundTask", foundTask);
59
+
60
+ if (foundTask) {
61
+ window.open(
62
+ `/apps/${foundTask.meta.app.id}/sessions/${foundTask.id}`,
63
+ "_blank"
64
+ );
65
+ return;
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ const tasks = await this.publicApiInstance
72
+ .post("tasks.run.app", {
73
+ params: this.payload,
74
+ workspaceId: this.workspaceId,
75
+ moduleId: this.moduleId,
76
+ nodeId: null,
77
+ })
78
+ .then((response) => response.data);
79
+
80
+ const task = tasks[0];
81
+ const origin = new URL(this.publicApiInstance.defaults.baseURL).origin;
82
+ window.open(
83
+ `${origin}/apps/${task.appId}/sessions/${task.taskId}`,
84
+ "_blank"
85
+ );
86
+ } finally {
87
+ this.loading = false;
88
+ }
89
+ },
90
+ },
91
+ template: `
92
+ <el-button
93
+ :class="{'available-in-offline': options.available_in_offline}"
94
+ @click="runApp"
95
+ v-loading="options.loading || loading"
96
+ :type="options.button_type"
97
+ :plain="options.plain"
98
+ :size="options.button_size"
99
+ :disabled="options.disabled"
100
+
101
+ >
102
+ <span v-html="options.icon"></span>
103
+ <span v-html="options.text"></span>
104
+ </el-button>
105
+ `,
46
106
  });
@@ -1,10 +1,5 @@
1
- <sly-run-app-button
2
- :public-api-instance="publicApiInstance"
3
- :workspace-id="state.{{{widget.widget_id}}}.workspace_id"
4
- :module-id="state.{{{widget.widget_id}}}.module_id"
5
- :payload="state.{{{widget.widget_id}}}.payload"
6
- :options="data.{{{widget.widget_id}}}.options"
7
- {% if widget._visible_by_vue_field %}
8
- v-show="{{{widget._visible_by_vue_field}}}"
9
- {% endif %}
10
- ></sly-run-app-button>
1
+ <sly-run-app-button :public-api-instance="publicApiInstance" :group-id="state.{{{widget.widget_id}}}.team_id"
2
+ :workspace-id="state.{{{widget.widget_id}}}.workspace_id" :module-id="state.{{{widget.widget_id}}}.module_id"
3
+ :payload="state.{{{widget.widget_id}}}.payload" :options="data.{{{widget.widget_id}}}.options" {% if
4
+ widget._visible_by_vue_field %} v-show="{{{widget._visible_by_vue_field}}}" {% endif %}
5
+ :check-existing-task-cb="data.{{{widget.widget_id}}}.check_existing_task_cb"></sly-run-app-button>
@@ -36,6 +36,7 @@ class TrainingLogs:
36
36
  self.display_widgets = []
37
37
  self.app_options = app_options
38
38
  api = Api.from_env()
39
+ team_id = sly_env.team_id()
39
40
 
40
41
  # GUI Components
41
42
  self.validator_text = Text("")
@@ -73,6 +74,7 @@ class TrainingLogs:
73
74
  module_info = gui_utils.get_module_info_by_name(api, app_name)
74
75
  if module_info is not None:
75
76
  self.tensorboard_offline_button = RunAppButton(
77
+ team_id=team_id,
76
78
  workspace_id=workspace_id,
77
79
  module_id=module_info["id"],
78
80
  payload={},
@@ -81,9 +83,10 @@ class TrainingLogs:
81
83
  plain=True,
82
84
  icon="zmdi zmdi-chart",
83
85
  available_in_offline=True,
84
- visible_by_vue_field="isStaticVersion",
86
+ visible_by_vue_field=None,
85
87
  )
86
88
  self.tensorboard_offline_button.disable()
89
+ self.tensorboard_offline_button.hide()
87
90
  self.display_widgets.extend([self.tensorboard_offline_button])
88
91
  else:
89
92
  logger.warning(
@@ -136,3 +136,26 @@ def get_module_info_by_name(api: Api, app_name: str) -> Union[Dict, None]:
136
136
  if module["name"] == app_name:
137
137
  app_info = api.app.get_info(module["id"])
138
138
  return app_info
139
+
140
+
141
+ def generate_task_check_function_js(folder: str) -> str:
142
+ """
143
+ Returns JavaScript function code for checking existing tasks.
144
+
145
+ :param folder: Remote folder to check.
146
+ :type folder: str
147
+ :return: JavaScript function code for checking existing tasks.
148
+ :rtype: str
149
+ """
150
+ escaped_folder = folder.replace("'", "\\'")
151
+ js_code = f"""
152
+ if (!task || !task.meta || !task.meta.params || !task.meta.params.state) {{
153
+ return false;
154
+ }}
155
+ const taskFolder = task.meta.params.state.slyFolder;
156
+ if (!taskFolder || typeof taskFolder !== 'string') {{
157
+ return false;
158
+ }}
159
+ return taskFolder === '{escaped_folder}';
160
+ """
161
+ return js_code
@@ -10,6 +10,7 @@ import subprocess
10
10
  from datetime import datetime
11
11
  from os import getcwd, listdir, walk
12
12
  from os.path import basename, dirname, exists, expanduser, isdir, isfile, join
13
+ from time import sleep
13
14
  from typing import Any, Dict, List, Literal, Optional, Union
14
15
  from urllib.request import urlopen
15
16
 
@@ -57,6 +58,7 @@ from supervisely.nn.inference import RuntimeType, SessionJSON
57
58
  from supervisely.nn.inference.inference import Inference
58
59
  from supervisely.nn.task_type import TaskType
59
60
  from supervisely.nn.training.gui.gui import TrainGUI
61
+ from supervisely.nn.training.gui.utils import generate_task_check_function_js
60
62
  from supervisely.nn.training.loggers import setup_train_logger, train_logger
61
63
  from supervisely.nn.utils import ModelSource, _get_model_name
62
64
  from supervisely.output import set_directory
@@ -1949,11 +1951,15 @@ class TrainApp:
1949
1951
  self.progress_bar_main.hide()
1950
1952
 
1951
1953
  file_info = self._api.file.get_info_by_path(self.team_id, join(remote_dir, "open_app.lnk"))
1954
+
1952
1955
  # Set offline tensorboard button payload
1953
1956
  if is_production():
1954
- self.gui.training_logs.tensorboard_offline_button.payload = {
1955
- "state": {"slyFolder": f"{join(remote_dir, 'logs')}"}
1956
- }
1957
+ remote_log_dir = join(remote_dir, "logs")
1958
+ tb_btn_payload = {"state": {"slyFolder": remote_log_dir}}
1959
+ self.gui.training_logs.tensorboard_offline_button.payload = tb_btn_payload
1960
+ self.gui.training_logs.tensorboard_offline_button.set_check_existing_task_cb(
1961
+ generate_task_check_function_js(remote_log_dir)
1962
+ )
1957
1963
  self.gui.training_logs.tensorboard_offline_button.enable()
1958
1964
 
1959
1965
  return remote_dir, file_info
@@ -2608,6 +2614,10 @@ class TrainApp:
2608
2614
  self.gui.training_process.start_button.loading = False
2609
2615
 
2610
2616
  # Shutdown the app after training is finished
2617
+
2618
+ self.gui.training_logs.tensorboard_button.hide()
2619
+ self.gui.training_logs.tensorboard_offline_button.show()
2620
+ sleep(1) # wait for the button to be shown
2611
2621
  self.app.shutdown()
2612
2622
  except Exception as e:
2613
2623
  message = f"Error occurred during finalizing and uploading training artifacts. {check_logs_text}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.373
3
+ Version: 6.73.374
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely
@@ -427,9 +427,9 @@ supervisely/app/widgets/report_thumbnail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
427
427
  supervisely/app/widgets/report_thumbnail/report_thumbnail.py,sha256=JYEoNi9tXslx9nlxdFcklxk-4ONtb_F46H5FRxZD1p0,2163
428
428
  supervisely/app/widgets/report_thumbnail/template.html,sha256=7dGngqwO_xxWZLOre0f6Vml3B_qXWu0l1P96L3opJ84,429
429
429
  supervisely/app/widgets/run_app_button/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
430
- supervisely/app/widgets/run_app_button/run_app_button.py,sha256=CGX37FLyl7eWtVdWRu-00zgebEmPRS2yfnEkSowH_F8,10011
431
- supervisely/app/widgets/run_app_button/script.js,sha256=SIBDV5CepA34aFuLUPNMsAZiqLqkUq8kP9PQfLp-iAM,1325
432
- supervisely/app/widgets/run_app_button/template.html,sha256=-SX7zZ53ehyfQMg_az3B9dl5_UC3TdvHL9yXNNgsowk,403
430
+ supervisely/app/widgets/run_app_button/run_app_button.py,sha256=cr07jabP1vF-rvH0_swVTuzpWXsvI9tHy_oQbJrHxSU,11044
431
+ supervisely/app/widgets/run_app_button/script.js,sha256=6rlluiW1hbHN7swtNSoFX9Gmrz2U-nRNOwoUpnSBmiw,2736
432
+ supervisely/app/widgets/run_app_button/template.html,sha256=rFIYPC9hl2b9H_LR1Kr_1RCpzfAAd-FBcmTmt75YXnE,511
433
433
  supervisely/app/widgets/scatter_chart/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
434
434
  supervisely/app/widgets/scatter_chart/scatter_chart.py,sha256=nX7fZbWsDiMjOek5FvIHgKbnBwQ-ol_V3XvnoAnz59c,4832
435
435
  supervisely/app/widgets/select/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -994,7 +994,7 @@ supervisely/nn/tracking/__init__.py,sha256=Ld1ed7ZZQZPkhX-5Xr-UbHZx5zLCm2-tInHnP
994
994
  supervisely/nn/tracking/boxmot.py,sha256=H9cQjYGL9nX_TLrfKDChhljTIiE9lffcgbwWCf_4PJU,4277
995
995
  supervisely/nn/tracking/tracking.py,sha256=WNrNm02B1pspA3d_AmzSJ-54RZTqWV2NZiC7FHe88bo,857
996
996
  supervisely/nn/training/__init__.py,sha256=gY4PCykJ-42MWKsqb9kl-skemKa8yB6t_fb5kzqR66U,111
997
- supervisely/nn/training/train_app.py,sha256=D2Fuy1SzoHTqeMWrdLLVLqeZN5Eu6M_CzU85y78na6I,116077
997
+ supervisely/nn/training/train_app.py,sha256=-NbFxj0MMPGJ8ysQcIxYd3j7oKutEcOYL9-9oO-bCiU,116587
998
998
  supervisely/nn/training/gui/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
999
999
  supervisely/nn/training/gui/classes_selector.py,sha256=Bpp-RFDQqcZ0kLJmS6ZnExkdscWwRusvF4vbWjEsKlQ,3926
1000
1000
  supervisely/nn/training/gui/gui.py,sha256=Z68uMPNkOyb70rpxfVDfJuGSzcoOhrqqDog8PABF2JQ,43312
@@ -1004,9 +1004,9 @@ supervisely/nn/training/gui/model_selector.py,sha256=I6KRKyylpwUEC3CApEnzDKkWe5x
1004
1004
  supervisely/nn/training/gui/tags_selector.py,sha256=ZirVXm5NNuIDjXO3wuHDnpYTWgFVLn-W7voBudh5bP0,3772
1005
1005
  supervisely/nn/training/gui/train_val_splits_selector.py,sha256=V8aMe0l-CaoHxP0y6GeCBGDMd7L3epQ3KqeFsMY43rs,11191
1006
1006
  supervisely/nn/training/gui/training_artifacts.py,sha256=c0GH70ZByvnL413KWHjSKcSX8V5DStXM5sjFVZafSZo,10519
1007
- supervisely/nn/training/gui/training_logs.py,sha256=fXj1Cszu7Qi4p8tNdBPqwet08I4xcphB5jbTPjCDmVk,4851
1007
+ supervisely/nn/training/gui/training_logs.py,sha256=GgEQMj9p98Z3p2b_-3BkHOhY7WQYELxctsRKmkbg3JY,4966
1008
1008
  supervisely/nn/training/gui/training_process.py,sha256=2F65cuu5ypKWkdaO4uVpNLMkwXjM8dpprd7Km5aedds,3192
1009
- supervisely/nn/training/gui/utils.py,sha256=EO629fSOwxOsiXL7mr1EXTiKKMIg7xgVm3jtCnF4r7Q,4268
1009
+ supervisely/nn/training/gui/utils.py,sha256=cEOsYItxgGTGKxFAvn7zQcTpwHgcGRO_UNGBn8idMUI,4983
1010
1010
  supervisely/nn/training/loggers/__init__.py,sha256=DOqR-4NJv25C4Y1HCWggvGNM5mgo1CbwQOdvROOL-60,777
1011
1011
  supervisely/nn/training/loggers/base_train_logger.py,sha256=Gf_TKwSfQdSVG6P3wAeWf5t2_EJWJqOPqt_TsJ5jpBY,1914
1012
1012
  supervisely/nn/training/loggers/tensorboard_logger.py,sha256=s6dNFHIaucRTRMEdDM8mHT7v8bUdl13oDK78kPeCW8U,1116
@@ -1097,9 +1097,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
1097
1097
  supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
1098
1098
  supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
1099
1099
  supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
1100
- supervisely-6.73.373.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1101
- supervisely-6.73.373.dist-info/METADATA,sha256=9QccODidDNZTM4MfriUbOZMtOFQ7d8SzadddcA2TMZU,35154
1102
- supervisely-6.73.373.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1103
- supervisely-6.73.373.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1104
- supervisely-6.73.373.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1105
- supervisely-6.73.373.dist-info/RECORD,,
1100
+ supervisely-6.73.374.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1101
+ supervisely-6.73.374.dist-info/METADATA,sha256=CZQRzlek6lQZ0EeIc-BYmKKPqE-p-nVIGMMm7eh4Aws,35154
1102
+ supervisely-6.73.374.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1103
+ supervisely-6.73.374.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
1104
+ supervisely-6.73.374.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
1105
+ supervisely-6.73.374.dist-info/RECORD,,