supervisely 6.73.214__py3-none-any.whl → 6.73.216__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.

Potentially problematic release.


This version of supervisely might be problematic. Click here for more details.

Files changed (52) hide show
  1. supervisely/app/fastapi/templating.py +1 -1
  2. supervisely/app/widgets/report_thumbnail/report_thumbnail.py +17 -5
  3. supervisely/app/widgets/team_files_selector/team_files_selector.py +3 -0
  4. supervisely/nn/artifacts/__init__.py +1 -0
  5. supervisely/nn/artifacts/rtdetr.py +32 -0
  6. supervisely/nn/benchmark/comparison/__init__.py +0 -0
  7. supervisely/nn/benchmark/comparison/detection_visualization/__init__.py +0 -0
  8. supervisely/nn/benchmark/comparison/detection_visualization/text_templates.py +437 -0
  9. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/__init__.py +27 -0
  10. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/avg_precision_by_class.py +125 -0
  11. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/calibration_score.py +224 -0
  12. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/explore_predicttions.py +112 -0
  13. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/localization_accuracy.py +161 -0
  14. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/outcome_counts.py +336 -0
  15. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/overview.py +249 -0
  16. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/pr_curve.py +142 -0
  17. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/precision_recal_f1.py +300 -0
  18. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/speedtest.py +308 -0
  19. supervisely/nn/benchmark/comparison/detection_visualization/vis_metrics/vis_metric.py +19 -0
  20. supervisely/nn/benchmark/comparison/detection_visualization/visualizer.py +298 -0
  21. supervisely/nn/benchmark/comparison/model_comparison.py +84 -0
  22. supervisely/nn/benchmark/evaluation/coco/metric_provider.py +9 -7
  23. supervisely/nn/benchmark/visualization/evaluation_result.py +266 -0
  24. supervisely/nn/benchmark/visualization/renderer.py +100 -0
  25. supervisely/nn/benchmark/visualization/report_template.html +46 -0
  26. supervisely/nn/benchmark/visualization/visualizer.py +1 -1
  27. supervisely/nn/benchmark/visualization/widgets/__init__.py +17 -0
  28. supervisely/nn/benchmark/visualization/widgets/chart/__init__.py +0 -0
  29. supervisely/nn/benchmark/visualization/widgets/chart/chart.py +72 -0
  30. supervisely/nn/benchmark/visualization/widgets/chart/template.html +16 -0
  31. supervisely/nn/benchmark/visualization/widgets/collapse/__init__.py +0 -0
  32. supervisely/nn/benchmark/visualization/widgets/collapse/collapse.py +33 -0
  33. supervisely/nn/benchmark/visualization/widgets/container/__init__.py +0 -0
  34. supervisely/nn/benchmark/visualization/widgets/container/container.py +54 -0
  35. supervisely/nn/benchmark/visualization/widgets/gallery/__init__.py +0 -0
  36. supervisely/nn/benchmark/visualization/widgets/gallery/gallery.py +125 -0
  37. supervisely/nn/benchmark/visualization/widgets/gallery/template.html +49 -0
  38. supervisely/nn/benchmark/visualization/widgets/markdown/__init__.py +0 -0
  39. supervisely/nn/benchmark/visualization/widgets/markdown/markdown.py +53 -0
  40. supervisely/nn/benchmark/visualization/widgets/notification/__init__.py +0 -0
  41. supervisely/nn/benchmark/visualization/widgets/notification/notification.py +38 -0
  42. supervisely/nn/benchmark/visualization/widgets/sidebar/__init__.py +0 -0
  43. supervisely/nn/benchmark/visualization/widgets/sidebar/sidebar.py +67 -0
  44. supervisely/nn/benchmark/visualization/widgets/table/__init__.py +0 -0
  45. supervisely/nn/benchmark/visualization/widgets/table/table.py +116 -0
  46. supervisely/nn/benchmark/visualization/widgets/widget.py +22 -0
  47. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/METADATA +1 -1
  48. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/RECORD +52 -12
  49. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/LICENSE +0 -0
  50. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/WHEEL +0 -0
  51. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/entry_points.txt +0 -0
  52. {supervisely-6.73.214.dist-info → supervisely-6.73.216.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,33 @@
1
+ from typing import Dict, List
2
+
3
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
4
+
5
+
6
+ class CollapseWidget(BaseWidget):
7
+ def __init__(self, widgets: List[BaseWidget]) -> None:
8
+ super().__init__()
9
+ self.widgets = widgets
10
+
11
+ def save_data(self, basepath: str) -> None:
12
+ for widget in self.widgets:
13
+ widget.save_data(basepath)
14
+
15
+ def get_state(self) -> Dict:
16
+ return {}
17
+
18
+ def to_html(self) -> str:
19
+ items = "\n".join(
20
+ [
21
+ f"""
22
+ <el-collapse-item title="{widget.title}">
23
+ {widget.to_html()}
24
+ </el-collapse-item>
25
+ """
26
+ for widget in self.widgets
27
+ ]
28
+ )
29
+ return f"""
30
+ <el-collapse class='mb-6'>
31
+ {items}
32
+ </el-collapse>
33
+ """
@@ -0,0 +1,54 @@
1
+ from typing import Dict, List, Literal, Optional, Union
2
+
3
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
4
+
5
+
6
+ class ContainerWidget(BaseWidget):
7
+ def __init__(
8
+ self,
9
+ widgets: List[BaseWidget],
10
+ name: str = "container",
11
+ title: str = None,
12
+ grid: Optional[bool] = False,
13
+ grid_cols: Union[int, Literal["auto"]] = 2,
14
+ grid_rows: Union[int, Literal["auto"]] = "auto",
15
+ ):
16
+ super().__init__(name, title)
17
+ self.widgets = widgets
18
+ self.grid = grid
19
+ self.grid_cols = grid_cols
20
+ self.grid_rows = grid_rows
21
+
22
+ def to_html(self) -> str:
23
+ if self.grid:
24
+ if any([v != "auto" and type(v) != int for v in [self.grid_cols, self.grid_rows]]):
25
+ raise ValueError("grid_cols and grid_rows must be either 'auto' or an integer")
26
+ s = f"""
27
+ <div
28
+ id="{ self.id }"
29
+ style="
30
+ display: grid;
31
+ grid-template-columns: repeat({self.grid_cols}, 1fr);
32
+ grid-template-rows: repeat({self.grid_rows}, 1fr);
33
+ grid-column-gap: 16px;
34
+ grid-row-gap: 16px;
35
+ margin-bottom: 25px;
36
+ "
37
+ >
38
+ """
39
+ else:
40
+ s = "<div>"
41
+ for widget in self.widgets:
42
+ s += "<div>" + widget.to_html() + "</div>"
43
+ s += "</div>"
44
+ return s
45
+
46
+ def save_data(self, basepath: str) -> None:
47
+ for widget in self.widgets:
48
+ widget.save_data(basepath)
49
+
50
+ def get_state(self) -> Dict:
51
+ state = {}
52
+ for widget in self.widgets:
53
+ state.update(widget.get_state())
54
+ return state
@@ -0,0 +1,125 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import List, Optional, Union
4
+
5
+ from jinja2 import Template
6
+
7
+ from supervisely.api.annotation_api import AnnotationInfo
8
+ from supervisely.api.image_api import ImageInfo
9
+ from supervisely.app.widgets import GridGalleryV2
10
+ from supervisely.io.fs import ensure_base_path
11
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
12
+ from supervisely.project.project_meta import ProjectMeta
13
+
14
+
15
+ class GalleryWidget(BaseWidget):
16
+ def __init__(
17
+ self,
18
+ name: str,
19
+ filters: Optional[List] = None,
20
+ is_modal: Optional[bool] = False,
21
+ columns_number: int = 3,
22
+ ):
23
+ super().__init__(name)
24
+ self.reference = self.id
25
+ self.is_modal = is_modal
26
+ self.click_handled = False
27
+ self.click_gallery_id = None
28
+ self.click_data = None
29
+ self.click_gallery_items_limit = None
30
+ self.image_left_header = False
31
+ self._project_meta = None
32
+ self.show_all_button = False
33
+ self.columns_number = columns_number
34
+
35
+ filters = filters # or [{"confidence": [0.6, 1]}]
36
+ self._gallery = GridGalleryV2(
37
+ columns_number=columns_number,
38
+ annotations_opacity=0.4,
39
+ border_width=4,
40
+ enable_zoom=False,
41
+ default_tag_filters=filters,
42
+ show_zoom_slider=False,
43
+ )
44
+
45
+ def set_project_meta(self, project_meta: ProjectMeta):
46
+ self._project_meta = project_meta
47
+
48
+ def set_images(
49
+ self,
50
+ image_infos: List[ImageInfo],
51
+ ann_infos: List[AnnotationInfo],
52
+ project_metas: List[ProjectMeta] = None,
53
+ skip_tags_filtering: Optional[List[Union[bool, List[str]]]] = None,
54
+ ):
55
+ """One time operation"""
56
+ if project_metas is None:
57
+ project_metas = [self._project_meta] * self.columns_number
58
+
59
+ if skip_tags_filtering is None:
60
+ skip_tags_filtering = [False] * self.columns_number
61
+
62
+ for idx, (image, ann) in enumerate(zip(image_infos, ann_infos)):
63
+ image_name = image.name
64
+ image_url = image.full_storage_url
65
+ self._gallery.append(
66
+ title=image_name,
67
+ image_url=image_url,
68
+ annotation_info=ann,
69
+ column_index=idx % self.columns_number,
70
+ project_meta=project_metas[idx % self.columns_number],
71
+ ignore_tags_filtering=skip_tags_filtering[idx % self.columns_number],
72
+ )
73
+
74
+ def _get_init_data(self):
75
+ res = {}
76
+ self._gallery._update_filters()
77
+ res.update(self._gallery.get_json_state())
78
+ res.update(self._gallery.get_json_data()["content"])
79
+ res["layoutData"] = res.pop("annotations")
80
+ res["projectMeta"] = self._project_meta.to_json()
81
+ return res
82
+
83
+ def save_data(self, basepath: str) -> None:
84
+ # init data
85
+ basepath = basepath.rstrip("/")
86
+ ensure_base_path(basepath + self.data_source)
87
+
88
+ with open(basepath + self.data_source, "w") as f:
89
+ json.dump(self._get_init_data(), f)
90
+
91
+ # click data
92
+ if self.click_data is not None:
93
+ ensure_base_path(basepath + self.click_data_source)
94
+ with open(basepath + self.click_data_source, "w") as f:
95
+ json.dump(self.click_data, f)
96
+
97
+ def get_state(self) -> None:
98
+ return {}
99
+
100
+ def to_html(self) -> str:
101
+ template_str = Path(__file__).parent / "template.html"
102
+ return Template(template_str.read_text()).render(self._get_template_data())
103
+
104
+ def add_on_click(self, gallery_id, click_data, gallery_items_limit):
105
+ self.click_handled = True
106
+ self.click_gallery_id = gallery_id
107
+ self.click_data = click_data
108
+ self.click_gallery_items_limit = gallery_items_limit
109
+
110
+ def add_image_left_header(self, html: str):
111
+ self.image_left_header = html
112
+
113
+ def _get_template_data(self):
114
+ return {
115
+ "widget_id": self.id,
116
+ "reference": self.reference, # TODO: same as id?
117
+ "init_data_source": self.data_source,
118
+ "is_modal": str(self.is_modal).lower(),
119
+ "click_handled": self.click_handled,
120
+ "show_all_button": self.show_all_button,
121
+ "click_data_source": self.click_data_source,
122
+ "click_gallery_id": self.click_gallery_id,
123
+ "click_gallery_items_limit": self.click_gallery_items_limit,
124
+ "image_left_header": self.image_left_header,
125
+ }
@@ -0,0 +1,49 @@
1
+ <div style="position: relative; max-height: 600px; overflow: hidden;">
2
+ <sly-iw-gallery ref="{{ widget_id }}" iw-widget-id="{{ widget_id }}" :options="{'isModalWindow': {{ is_modal }}}"
3
+ :actions="{
4
+ 'init': {
5
+ 'dataSource': '{{ init_data_source }}',
6
+ },
7
+ {% if click_handled %}
8
+ 'chart-click': {
9
+ 'key': 'explore',
10
+ 'dataSource': '{{ click_data_source }}',
11
+ 'galleryId': '{{ click_gallery_id }}',
12
+ 'limit': {{ click_gallery_items_limit }},
13
+ },
14
+ {% endif %}
15
+ }" :command="command" :data="data">
16
+ {% if image_left_header %}
17
+ <span slot="image-left-header">
18
+ {{ image_left_header }}
19
+ </span>
20
+ {% endif %}
21
+ </sly-iw-gallery>
22
+
23
+
24
+ {% if show_all_button %}
25
+ <!-- Gradient overlay -->
26
+ <div
27
+ style="position: absolute; z-index: 100; bottom: 20px; width: 100%; height: 100px; background: linear-gradient(to top, rgba(255, 255, 255, 1), transparent);">
28
+ </div>
29
+ <div
30
+ style="position: absolute; z-index: 100; bottom: 0; width: 100%; height: 20px; background: rgba(255, 255, 255, 1);">
31
+ </div>
32
+
33
+ <!-- 'Explore more' button -->
34
+ <div style="position: absolute; bottom: 20px; z-index: 101; display: flex; justify-content: center; width: 100%">
35
+ <el-button iw-widget-id="btn-1" type="primary" @click="command({
36
+ method: 'update-gallery',
37
+ payload: {
38
+ data: {
39
+ 'key': 'explore',
40
+ 'limit': {{ click_gallery_items_limit}},
41
+ 'dataSource': '{{ click_data_source }}',
42
+ },
43
+ 'galleryId': '{{ click_gallery_id }}',
44
+ },
45
+ internalCommand: true
46
+ })">Explore more</el-button>
47
+ </div>
48
+ {% endif %}
49
+ </div>
@@ -0,0 +1,53 @@
1
+ from typing import Dict
2
+
3
+ from supervisely.io.fs import ensure_base_path
4
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
5
+
6
+
7
+ class MarkdownWidget(BaseWidget):
8
+ def __init__(
9
+ self,
10
+ name: str,
11
+ title: str,
12
+ text: str = None,
13
+ ) -> None:
14
+ super().__init__(name, title)
15
+ self.text = text
16
+ self.data_source = f"/data/{self.name}_{self.id}.md"
17
+ self.is_info_block = False
18
+ self.width_fit_content = False
19
+
20
+ def save_data(self, basepath: str) -> None:
21
+ # init data
22
+ basepath = basepath.rstrip("/")
23
+ ensure_base_path(basepath + self.data_source)
24
+
25
+ with open(basepath + self.data_source, "w") as f:
26
+ f.write(self.text)
27
+
28
+ def get_state(self) -> Dict:
29
+ return {}
30
+
31
+ def to_html(self) -> str:
32
+ style_class = "markdown-no-border"
33
+ if self.is_info_block:
34
+ style_class += " overview-info-block"
35
+ if self.width_fit_content:
36
+ style_class += " markdown-fit-content"
37
+
38
+ return f"""
39
+ <div style="margin-top: 10px;">
40
+ <sly-iw-markdown
41
+ id="{ self.id }"
42
+ class="{ style_class }"
43
+ iw-widget-id="{ self.id }"
44
+ :actions="{{
45
+ 'init': {{
46
+ 'dataSource': '{ self.data_source }',
47
+ }},
48
+ }}"
49
+ :command="command"
50
+ :data="data"
51
+ />
52
+ </div>
53
+ """
@@ -0,0 +1,38 @@
1
+ from typing import Dict
2
+
3
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
4
+
5
+
6
+ class NotificationWidget(BaseWidget):
7
+
8
+ def __init__(
9
+ self,
10
+ name: str,
11
+ title: str,
12
+ desc: str = None,
13
+ ) -> None:
14
+ super().__init__(name, title=title)
15
+ self.desc = desc
16
+
17
+ def save_data(self, basepath: str) -> None:
18
+ return
19
+
20
+ def get_state(self) -> Dict:
21
+ return {}
22
+
23
+ def to_html(self) -> str:
24
+ return f"""
25
+ <div style="margin-top: 20px; margin-bottom: 20px;">
26
+ <sly-iw-notification
27
+ iw-widget-id="{ self.id }"
28
+ >
29
+ <span slot="title">
30
+ { self.title }
31
+ </span>
32
+
33
+ <span slot="description">
34
+ { self.desc }
35
+ </span>
36
+ </sly-iw-notification>
37
+ </div>
38
+ """
@@ -0,0 +1,67 @@
1
+ from typing import Dict, List
2
+
3
+ from jinja2 import Template
4
+
5
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
6
+
7
+
8
+ class SidebarWidget(BaseWidget):
9
+
10
+ def __init__(self, widgets: List[BaseWidget], anchors: List[str]) -> None:
11
+ self.widgets = widgets
12
+ self.anchors = anchors
13
+
14
+ def sidebar_template_str(self, widget: BaseWidget):
15
+ button_style = f"{{fontWeight: data.scrollIntoView === '{widget.id}' ? 'bold' : 'normal'}}"
16
+
17
+ return f"""
18
+ <div>
19
+ <el-button type="text" @click="data.scrollIntoView='{widget.id}'" :style="{button_style}">
20
+ {widget.title}
21
+ </el-button>
22
+ </div>
23
+ """
24
+
25
+ @property
26
+ def html_str(self):
27
+ anchored_widgets = sorted(
28
+ [widget for widget in self.widgets if widget.id in self.anchors],
29
+ key=lambda x: self.anchors.index(x.id),
30
+ )
31
+
32
+ sidebar_content = "".join(self.sidebar_template_str(widget) for widget in anchored_widgets)
33
+ main_content = "".join(
34
+ f"""
35
+ <div style="margin-top: 20px;">
36
+ {widget.to_html()}
37
+ </div>
38
+ """
39
+ for widget in self.widgets
40
+ )
41
+ template = f"""
42
+ <sly-iw-sidebar
43
+ :options="{{ height: 'calc(100vh - 130px)', clearMainPanelPaddings: true, leftSided: false, disableResize: true, sidebarWidth: 300 }}"
44
+ >
45
+ <div slot="sidebar">
46
+ {sidebar_content}
47
+ </div>
48
+ <div style="padding-right: 35px;">
49
+ {main_content}
50
+ </div>
51
+ </sly-iw-sidebar>
52
+ """
53
+
54
+ return template
55
+
56
+ def to_html(self) -> str:
57
+ return Template(self.html_str).render()
58
+
59
+ def save_data(self, basepath: str) -> None:
60
+ for widget in self.widgets:
61
+ widget.save_data(basepath)
62
+
63
+ def get_state(self) -> Dict:
64
+ state = {}
65
+ for widget in self.widgets:
66
+ state.update(widget.get_state())
67
+ return state
@@ -0,0 +1,116 @@
1
+ import json
2
+ from typing import Any, Dict, Optional, Union
3
+
4
+ from jinja2 import Template
5
+
6
+ from supervisely.io.fs import ensure_base_path
7
+ from supervisely.nn.benchmark.visualization.widgets.widget import BaseWidget
8
+
9
+
10
+ class TableWidget(BaseWidget):
11
+ def __init__(
12
+ self,
13
+ name: str,
14
+ data: Dict = None,
15
+ click_data: Any = None,
16
+ click_gellery_id: str = "",
17
+ fix_columns: int = 0,
18
+ show_header_controls: bool = True,
19
+ main_column: Optional[str] = None,
20
+ width: Optional[Union[int, str]] = None,
21
+ page_size: int = 10,
22
+ ) -> None:
23
+ super().__init__(name=name)
24
+ self.data = data
25
+ self.fix_columns = fix_columns
26
+ self.show_header_controls = show_header_controls
27
+ self.main_column = main_column
28
+ self.click_data = click_data
29
+ self.click_gellery_id = click_gellery_id
30
+
31
+ if isinstance(width, int):
32
+ width = f"width: {width}px"
33
+ elif isinstance(width, str):
34
+ width = f"width: {width.rstrip('%')}%"
35
+ self.width = width
36
+ self.page_size = page_size
37
+
38
+ self.clickable = self.click_data is not None
39
+
40
+ def save_data(self, basepath: str) -> None:
41
+ # init data
42
+ basepath = basepath.rstrip("/")
43
+ ensure_base_path(basepath + self.data_source)
44
+
45
+ with open(basepath + self.data_source, "w") as f:
46
+ json.dump(self.data, f)
47
+
48
+ # click data
49
+ if self.click_data is not None:
50
+ ensure_base_path(basepath + self.click_data_source)
51
+ with open(basepath + self.click_data_source, "w") as f:
52
+ json.dump(self.click_data, f)
53
+
54
+ def get_state(self) -> Dict:
55
+ return {}
56
+
57
+ def get_render_data(self) -> Dict:
58
+ return {
59
+ "widget_id": self.id,
60
+ "fixColumns": self.fix_columns,
61
+ "showHeaderControls": self.show_header_controls,
62
+ "init_data_source": self.data_source,
63
+ "table_click_data": self.click_data_source,
64
+ "table_gallery_id": self.click_gellery_id,
65
+ "mainColumn": self.main_column,
66
+ "clickable": self.clickable,
67
+ "width": self.width or "",
68
+ "data": "data",
69
+ "command": "command",
70
+ "page_size": self.page_size,
71
+ }
72
+
73
+ def to_html(self) -> str:
74
+ template_str = """
75
+ <div style="margin-top: 20px; margin-bottom: 30px; {{ width }}">
76
+ <sly-iw-table
77
+ iw-widget-id="{{ widget_id }}"
78
+ {% if clickable %}
79
+ style="cursor: pointer;"
80
+ {% endif %}
81
+ :options="{
82
+ isRowClickable: '{{ clickable }}' === 'True',
83
+ fixColumns: {{ fixColumns }},
84
+ showHeaderControls: '{{ showHeaderControls }}' === 'True',
85
+ }"
86
+ :actions="{
87
+ 'init': {
88
+ 'dataSource': '{{ init_data_source }}',
89
+ 'perPage': {{ page_size }},
90
+ },
91
+ {% if clickable %}
92
+ 'chart-click': {
93
+ 'dataSource': '{{ table_click_data }}',
94
+ 'galleryId': '{{ table_gallery_id }}',
95
+ 'getKey':(payload)=>payload.row[0],
96
+ },
97
+ {% endif %}
98
+ }"
99
+ :command="{{ command }}"
100
+ :data="{{ data }}"
101
+ >
102
+ <span
103
+ slot="custom-cell-content"
104
+ slot-scope='{ row, column, cellValue }'
105
+ >
106
+ <div
107
+ v-if="column === '{{ mainColumn }}'"
108
+ class="fflex"
109
+ >
110
+ <b>{{ '{{cellValue}}' }}</b>
111
+ </div>
112
+ </span>
113
+ </sly-iw-table>
114
+ </div>
115
+ """
116
+ return Template(template_str).render(self.get_render_data())
@@ -0,0 +1,22 @@
1
+ from typing import Optional
2
+
3
+ from supervisely._utils import camel_to_snake, rand_str
4
+
5
+
6
+ class BaseWidget:
7
+ def __init__(self, name: str = None, title: Optional[str] = None) -> None:
8
+ self.type = camel_to_snake(self.__class__.__name__)
9
+ self.id = f"{self.type}_{rand_str(5)}"
10
+ self.name = name
11
+ self.title = title
12
+ self.data_source = f"/data/{self.name}_{self.id}.json"
13
+ self.click_data_source = f"/data/{self.name}_{self.id}_click_data.json"
14
+
15
+ def save_data(self, basepath: str) -> None:
16
+ raise NotImplementedError
17
+
18
+ def get_state(self) -> None:
19
+ raise NotImplementedError
20
+
21
+ def to_html(self) -> str:
22
+ raise NotImplementedError
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: supervisely
3
- Version: 6.73.214
3
+ Version: 6.73.216
4
4
  Summary: Supervisely Python SDK.
5
5
  Home-page: https://github.com/supervisely/supervisely
6
6
  Author: Supervisely