fusion-tools 2.4.2__tar.gz → 2.4.3__tar.gz
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.
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/PKG-INFO +1 -1
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/setup.py +1 -1
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/components/maps.py +2 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/components/segmentation.py +232 -84
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/utils/shapes.py +41 -6
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools.egg-info/PKG-INFO +1 -1
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/LICENSE +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/README.md +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/pyproject.toml +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/setup.cfg +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/__init__.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/components/__init__.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/components/tools.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/dataset.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/feature_extraction.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/handler.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/tileserver.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/utils/__init__.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/utils/omics.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/utils/stats.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/visualization/__init__.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/visualization/components.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools/visualization/vis_utils.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools.egg-info/SOURCES.txt +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools.egg-info/dependency_links.txt +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools.egg-info/requires.txt +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/src/fusion_tools.egg-info/top_level.txt +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_classification_model.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_custom_component.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_dataset_classes.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_extract_nested_prop.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_extract_recursive.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_feature_annotation.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_feature_extraction.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_generate_property_dict.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_get_gene_info.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_hra_viewer.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_image_overlay.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_model_train.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_multiframe_rgb.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_multiframeslidemap.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_process.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_property_plotter.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_read_anndata.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_remote_slide_annotations.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_remote_tileserver.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_segmentation.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_slide_annotations.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_spatial_aggregation.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_tile_server.py +0 -0
- {fusion_tools-2.4.2 → fusion_tools-2.4.3}/tests/test_visualization_component.py +0 -0
|
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
|
5
5
|
|
|
6
6
|
setuptools.setup(
|
|
7
7
|
name="fusion-tools",
|
|
8
|
-
version="2.4.
|
|
8
|
+
version="2.4.3",
|
|
9
9
|
author="Sam Border",
|
|
10
10
|
author_email="sam.border2256@gmail.com",
|
|
11
11
|
description="Modular visualization and analysis dashboard creation for high-resolution microscopy images",
|
|
@@ -366,6 +366,7 @@ class SlideMap(MapComponent):
|
|
|
366
366
|
var overlayVal = Number.Nan;
|
|
367
367
|
if (overlayProp) {
|
|
368
368
|
if (overlayProp.name) {
|
|
369
|
+
//TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
369
370
|
var overlaySubProps = overlayProp.name.split(" --> ");
|
|
370
371
|
var prop_dict = feature.properties;
|
|
371
372
|
for (let i = 0; i < overlaySubProps.length; i++) {
|
|
@@ -423,6 +424,7 @@ class SlideMap(MapComponent):
|
|
|
423
424
|
// Iterating through filterVals list
|
|
424
425
|
var filter = filterVals[i];
|
|
425
426
|
if (filter.name) {
|
|
427
|
+
//TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
426
428
|
var filterSubProps = filter.name.split(" --> ");
|
|
427
429
|
var prop_dict = feature.properties;
|
|
428
430
|
for (let j = 0; j < filterSubProps.length; j++) {
|
|
@@ -1240,6 +1240,10 @@ class BulkLabels(Tool):
|
|
|
1240
1240
|
n_clicks = 0,
|
|
1241
1241
|
id = {'type': 'bulk-labels-apply-labels','index': 0},
|
|
1242
1242
|
disabled = True
|
|
1243
|
+
),
|
|
1244
|
+
dbc.Tooltip(
|
|
1245
|
+
'Saves current label to labeling session but does not add label to structure-level data',
|
|
1246
|
+
target = {'type': 'bulk-labels-apply-labels','index': 0}
|
|
1243
1247
|
)
|
|
1244
1248
|
])
|
|
1245
1249
|
]),
|
|
@@ -1252,6 +1256,10 @@ class BulkLabels(Tool):
|
|
|
1252
1256
|
id = {'type': 'bulk-labels-download-labels','index': 0},
|
|
1253
1257
|
disabled = True
|
|
1254
1258
|
),
|
|
1259
|
+
dbc.Tooltip(
|
|
1260
|
+
'Downloads both individual labels as well as associated labeling metadata containing rationales for labels.',
|
|
1261
|
+
target = {'type': 'bulk-labels-download-labels','index': 0}
|
|
1262
|
+
),
|
|
1255
1263
|
dcc.Download(
|
|
1256
1264
|
id = {'type':'bulk-labels-download-data','index': 0}
|
|
1257
1265
|
),
|
|
@@ -1259,6 +1267,29 @@ class BulkLabels(Tool):
|
|
|
1259
1267
|
id = {'type': 'bulk-labels-download-metadata','index': 0}
|
|
1260
1268
|
)
|
|
1261
1269
|
])
|
|
1270
|
+
],style = {'marginTop': '10px'}),
|
|
1271
|
+
dbc.Row([
|
|
1272
|
+
dbc.Col([
|
|
1273
|
+
dbc.Button(
|
|
1274
|
+
'Add Labels to Structures',
|
|
1275
|
+
id = {'type': 'bulk-labels-add-labels-to-structures','index': 0},
|
|
1276
|
+
className = 'd-grid col-12 mx-auto',
|
|
1277
|
+
n_clicks = 0,
|
|
1278
|
+
disabled = True
|
|
1279
|
+
),
|
|
1280
|
+
dbc.Tooltip(
|
|
1281
|
+
'Adds current labels to structures for use in plotting, filtering, etc.',
|
|
1282
|
+
target = {'type': 'bulk-labels-add-labels-to-structures','index': 0}
|
|
1283
|
+
)
|
|
1284
|
+
])
|
|
1285
|
+
],style = {'marginTop': '10px'}),
|
|
1286
|
+
dbc.Row([
|
|
1287
|
+
dbc.Col([
|
|
1288
|
+
html.Div(
|
|
1289
|
+
id = {'type': 'bulk-labels-label-stats-div','index': 0},
|
|
1290
|
+
children = []
|
|
1291
|
+
)
|
|
1292
|
+
])
|
|
1262
1293
|
],style = {'marginTop': '10px'})
|
|
1263
1294
|
])
|
|
1264
1295
|
])
|
|
@@ -1307,7 +1338,8 @@ class BulkLabels(Tool):
|
|
|
1307
1338
|
Output({'type': 'bulk-labels-label-text','index': ALL},'disabled'),
|
|
1308
1339
|
Output({'type': 'bulk-labels-label-rationale','index': ALL},'disabled'),
|
|
1309
1340
|
Output({'type': 'bulk-labels-apply-labels','index': ALL},'disabled'),
|
|
1310
|
-
Output({'type': 'bulk-labels-download-labels','index': ALL},'disabled')
|
|
1341
|
+
Output({'type': 'bulk-labels-download-labels','index': ALL},'disabled'),
|
|
1342
|
+
Output({'type': 'bulk-labels-add-labels-to-structures','index': ALL},'disabled')
|
|
1311
1343
|
],
|
|
1312
1344
|
[
|
|
1313
1345
|
State({'type': 'bulk-labels-include-structures','index': ALL},'value'),
|
|
@@ -1381,6 +1413,8 @@ class BulkLabels(Tool):
|
|
|
1381
1413
|
],
|
|
1382
1414
|
[
|
|
1383
1415
|
Output({'type': 'bulk-labels-labels-store','index': ALL},'data'),
|
|
1416
|
+
Output({'type': 'bulk-labels-apply-labels','index': ALL},'color'),
|
|
1417
|
+
Output({'type': 'bulk-labels-label-stats-div','index': ALL},'children')
|
|
1384
1418
|
],
|
|
1385
1419
|
[
|
|
1386
1420
|
State({'type': 'bulk-labels-markers','index': ALL},'data'),
|
|
@@ -1413,7 +1447,9 @@ class BulkLabels(Tool):
|
|
|
1413
1447
|
Output({'type': 'bulk-labels-label-source','index': ALL},'children'),
|
|
1414
1448
|
Output({'type': 'bulk-labels-apply-labels','index': ALL},'disabled'),
|
|
1415
1449
|
Output({'type': 'bulk-labels-include-structures','index':ALL},'options'),
|
|
1416
|
-
Output({'type': 'bulk-labels-spatial-query-structures','index':ALL},'options')
|
|
1450
|
+
Output({'type': 'bulk-labels-spatial-query-structures','index':ALL},'options'),
|
|
1451
|
+
Output({'type': 'bulk-labels-apply-labels','index': ALL},'color'),
|
|
1452
|
+
Output({'type': 'map-marker-div','index': ALL},'children')
|
|
1417
1453
|
],
|
|
1418
1454
|
[
|
|
1419
1455
|
State({'type': 'feature-overlay','index':ALL},'name')
|
|
@@ -1457,6 +1493,33 @@ class BulkLabels(Tool):
|
|
|
1457
1493
|
]
|
|
1458
1494
|
)(self.update_structure_count)
|
|
1459
1495
|
|
|
1496
|
+
# Adding current labels to structure data
|
|
1497
|
+
self.blueprint.callback(
|
|
1498
|
+
[
|
|
1499
|
+
Input({'type': 'bulk-labels-add-labels-to-structures','index': ALL},'n_clicks')
|
|
1500
|
+
],
|
|
1501
|
+
[
|
|
1502
|
+
Output({'type': 'map-annotations-store','index': ALL},'data')
|
|
1503
|
+
],
|
|
1504
|
+
[
|
|
1505
|
+
State({'type': 'map-annotations-store','index': ALL},'data'),
|
|
1506
|
+
State({'type': 'bulk-labels-labels-store','index': ALL},'data')
|
|
1507
|
+
]
|
|
1508
|
+
)(self.add_labels_to_structures)
|
|
1509
|
+
|
|
1510
|
+
# Editing current labels by editing the label table
|
|
1511
|
+
self.blueprint.callback(
|
|
1512
|
+
[
|
|
1513
|
+
Input({'type': 'bulk-labels-label-table','index': ALL},'data'),
|
|
1514
|
+
],
|
|
1515
|
+
[
|
|
1516
|
+
Output({'type': 'bulk-labels-labels-store','index': ALL},'data')
|
|
1517
|
+
],
|
|
1518
|
+
[
|
|
1519
|
+
State({'type': 'bulk-labels-labels-store','index': ALL},'data'),
|
|
1520
|
+
]
|
|
1521
|
+
)(self.update_label_table)
|
|
1522
|
+
|
|
1460
1523
|
|
|
1461
1524
|
def update_slide(self, new_annotations: list):
|
|
1462
1525
|
|
|
@@ -1511,6 +1574,8 @@ class BulkLabels(Tool):
|
|
|
1511
1574
|
|
|
1512
1575
|
if query_type is None:
|
|
1513
1576
|
raise exceptions.PreventUpdate
|
|
1577
|
+
if not type(query_type)==str:
|
|
1578
|
+
raise exceptions.PreventUpdate
|
|
1514
1579
|
|
|
1515
1580
|
query_types = {
|
|
1516
1581
|
'within': '''
|
|
@@ -1770,7 +1835,7 @@ class BulkLabels(Tool):
|
|
|
1770
1835
|
(f['bbox'][1]+f['bbox'][3])/2
|
|
1771
1836
|
]
|
|
1772
1837
|
},
|
|
1773
|
-
'properties': f_data
|
|
1838
|
+
'properties': f_data | {'_id': f['properties']['_id']}
|
|
1774
1839
|
}
|
|
1775
1840
|
for f,f_data in zip(filtered_geojson['features'],filtered_ref_list)
|
|
1776
1841
|
]
|
|
@@ -1792,15 +1857,17 @@ class BulkLabels(Tool):
|
|
|
1792
1857
|
labels_rationale_disabled = False
|
|
1793
1858
|
labels_apply_button_disabled = False
|
|
1794
1859
|
labels_download_button_disabled = False
|
|
1860
|
+
labels_add_to_structures_button_disabled = False
|
|
1795
1861
|
else:
|
|
1796
1862
|
labels_type_disabled = True
|
|
1797
1863
|
labels_text_disabled = True
|
|
1798
1864
|
labels_rationale_disabled = True
|
|
1799
1865
|
labels_apply_button_disabled = True
|
|
1800
1866
|
labels_download_button_disabled = True
|
|
1867
|
+
labels_add_to_structures_button_disabled = True
|
|
1801
1868
|
|
|
1802
1869
|
|
|
1803
|
-
return [new_structures_div], [new_markers_div], [new_labels_source], [labels_type_disabled], [labels_text_disabled], [labels_rationale_disabled], [labels_apply_button_disabled], [labels_download_button_disabled]
|
|
1870
|
+
return [new_structures_div], [new_markers_div], [new_labels_source], [labels_type_disabled], [labels_text_disabled], [labels_rationale_disabled], [labels_apply_button_disabled], [labels_download_button_disabled], [labels_add_to_structures_button_disabled]
|
|
1804
1871
|
|
|
1805
1872
|
def update_label_properties(self, add_click:list, remove_click:list, property_info:list):
|
|
1806
1873
|
"""Adding/removing property filter dropdown
|
|
@@ -1913,36 +1980,13 @@ class BulkLabels(Tool):
|
|
|
1913
1980
|
)
|
|
1914
1981
|
)
|
|
1915
1982
|
|
|
1916
|
-
return values_selector
|
|
1917
|
-
|
|
1918
|
-
def parse_marker_div(self, parent_marker_div,slide_information):
|
|
1919
|
-
"""Parse div containing markers, extracting position and data from each
|
|
1920
|
-
|
|
1921
|
-
:param parent_marker_div: Div containing all current Markers
|
|
1922
|
-
:type parent_marker_div: list
|
|
1923
|
-
:return: Coordinates for all markers and associated data (source structure and index)
|
|
1924
|
-
:rtype: tuple
|
|
1925
|
-
"""
|
|
1926
|
-
|
|
1927
|
-
marker_coords = []
|
|
1928
|
-
marker_data = []
|
|
1929
|
-
for p in parent_marker_div:
|
|
1930
|
-
coords = p['props']['position']
|
|
1931
|
-
data = json.loads(p['props']['children'][-1]['props']['children'])
|
|
1932
|
-
|
|
1933
|
-
marker_coords.append(
|
|
1934
|
-
[coords[1]/slide_information['x_scale'],coords[0]/slide_information['y_scale']]
|
|
1935
|
-
)
|
|
1936
|
-
marker_data.append(data)
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
return marker_coords, marker_data
|
|
1983
|
+
return values_selector
|
|
1940
1984
|
|
|
1941
|
-
def search_labels(self,
|
|
1985
|
+
def search_labels(self, marker_features, current_labels, label_type, label_text, label_method):
|
|
1942
1986
|
"""Checking current labels and adding the new label based on label_method (inclusive, exclusive, or unlabeled).
|
|
1943
1987
|
|
|
1944
|
-
:param
|
|
1945
|
-
:type
|
|
1988
|
+
:param marker_features: GeoJSON Features for new markers
|
|
1989
|
+
:type marker_features: list
|
|
1946
1990
|
:param current_labels: List of all current labels (dicts with keys: centroid, labels)
|
|
1947
1991
|
:type current_labels: list
|
|
1948
1992
|
:param label_type: String corresponding to the type for the new label
|
|
@@ -1959,8 +2003,10 @@ class BulkLabels(Tool):
|
|
|
1959
2003
|
else:
|
|
1960
2004
|
current_labels['labels'] = []
|
|
1961
2005
|
current_centroids = []
|
|
1962
|
-
for m_idx, m in enumerate(
|
|
1963
|
-
|
|
2006
|
+
for m_idx, m in enumerate(marker_features):
|
|
2007
|
+
m_centroid = m['geometry']['coordinates']
|
|
2008
|
+
m_id = m['properties']['_id']
|
|
2009
|
+
if m_centroid in current_centroids:
|
|
1964
2010
|
cent_idx = current_centroids.index(m)
|
|
1965
2011
|
if label_method=='in':
|
|
1966
2012
|
# Adding inclusive label to current set of labels
|
|
@@ -1996,7 +2042,8 @@ class BulkLabels(Tool):
|
|
|
1996
2042
|
else:
|
|
1997
2043
|
current_labels['labels'].append(
|
|
1998
2044
|
{
|
|
1999
|
-
'centroid':
|
|
2045
|
+
'centroid': m_centroid,
|
|
2046
|
+
'_id': m_id,
|
|
2000
2047
|
'labels': [{
|
|
2001
2048
|
'type': label_type,
|
|
2002
2049
|
'value': label_text
|
|
@@ -2049,57 +2096,14 @@ class BulkLabels(Tool):
|
|
|
2049
2096
|
|
|
2050
2097
|
scaled_markers = geojson.utils.map_geometries(lambda g: geojson.utils.map_tuples(lambda c: (c[0]/slide_information['x_scale'],c[1]/slide_information['y_scale']),g),current_markers)
|
|
2051
2098
|
|
|
2052
|
-
|
|
2053
|
-
labeled_items = self.search_labels(marker_positions, current_data, label_type, label_text, label_method)
|
|
2054
|
-
|
|
2055
|
-
#TODO: Applying property to structures
|
|
2056
|
-
"""
|
|
2057
|
-
ann_names_order = [i['properties']['name'] for i in current_annotations]
|
|
2058
|
-
apply_label_dict = {'type': label_type, 'value': label_text}
|
|
2059
|
-
for m_d in marker_data:
|
|
2060
|
-
ann_idx = ann_names_order.index(m_d['name'])
|
|
2061
|
-
if label_method=='in':
|
|
2062
|
-
if 'fusion_labels' in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']:
|
|
2063
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'].append(
|
|
2064
|
-
apply_label_dict
|
|
2065
|
-
)
|
|
2066
|
-
else:
|
|
2067
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'] = [
|
|
2068
|
-
apply_label_dict
|
|
2069
|
-
]
|
|
2070
|
-
elif label_method=='over':
|
|
2071
|
-
if 'fusion_labels' in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']:
|
|
2072
|
-
if any([label_type==i['type'] for i in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels']]):
|
|
2073
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'] = [
|
|
2074
|
-
i if not i['type']==label_type else apply_label_dict
|
|
2075
|
-
for i in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels']
|
|
2076
|
-
]
|
|
2077
|
-
else:
|
|
2078
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'].append(
|
|
2079
|
-
apply_label_dict
|
|
2080
|
-
)
|
|
2081
|
-
else:
|
|
2082
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'] = [
|
|
2083
|
-
apply_label_dict
|
|
2084
|
-
]
|
|
2085
|
-
elif label_method=='un':
|
|
2086
|
-
if 'fusion_labels' in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels']:
|
|
2087
|
-
if not any([label_type==i['type'] for i in current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels']]):
|
|
2088
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'].append(
|
|
2089
|
-
apply_label_dict
|
|
2090
|
-
)
|
|
2091
|
-
else:
|
|
2092
|
-
current_annotations[ann_idx]['features'][m_d['feature_index']]['properties']['fusion_labels'] = [
|
|
2093
|
-
apply_label_dict
|
|
2094
|
-
]
|
|
2095
|
-
|
|
2096
|
-
"""
|
|
2099
|
+
labeled_items = self.search_labels(scaled_markers['features'], current_data, label_type, label_text, label_method)
|
|
2097
2100
|
|
|
2098
|
-
if 'labels' in current_data:
|
|
2101
|
+
if 'labels' in current_data and 'labels_metadata' in current_data:
|
|
2099
2102
|
current_data['labels'] = labeled_items['labels']
|
|
2100
2103
|
current_data['labels_metadata'].append(
|
|
2101
2104
|
{
|
|
2102
2105
|
'type': label_type,
|
|
2106
|
+
'label': label_text,
|
|
2103
2107
|
'rationale': label_rationale,
|
|
2104
2108
|
'source': label_source
|
|
2105
2109
|
}
|
|
@@ -2109,15 +2113,56 @@ class BulkLabels(Tool):
|
|
|
2109
2113
|
current_data['labels_metadata'] = [
|
|
2110
2114
|
{
|
|
2111
2115
|
'type': label_type,
|
|
2116
|
+
'label': label_text,
|
|
2112
2117
|
'rationale': label_rationale,
|
|
2113
2118
|
'source': label_source
|
|
2114
2119
|
}
|
|
2115
2120
|
]
|
|
2116
2121
|
|
|
2117
2122
|
new_data = json.dumps(current_data)
|
|
2118
|
-
#new_annotations = json.dumps(current_annotations)
|
|
2119
2123
|
|
|
2120
|
-
|
|
2124
|
+
# Creating a table for count of unique labels:
|
|
2125
|
+
labels = []
|
|
2126
|
+
for l in labeled_items['labels']:
|
|
2127
|
+
for m in l['labels']:
|
|
2128
|
+
# Accounting for multiple values for the same "type"
|
|
2129
|
+
l_dict = {
|
|
2130
|
+
'Label Type': m['type'],
|
|
2131
|
+
'Value': m['value']
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
labels.append(l_dict)
|
|
2135
|
+
|
|
2136
|
+
label_count_df = pd.DataFrame.from_records(labels).groupby(by='Label Type')
|
|
2137
|
+
label_counts = label_count_df.value_counts(['Value']).to_frame()
|
|
2138
|
+
label_counts.reset_index(inplace=True)
|
|
2139
|
+
label_counts.columns = ['Label Type','Label Value','Count']
|
|
2140
|
+
|
|
2141
|
+
label_count_table = dash_table.DataTable(
|
|
2142
|
+
columns = [{'name':i,'id':i,'selectable':True} for i in label_counts],
|
|
2143
|
+
data = label_counts.to_dict('records'),
|
|
2144
|
+
editable=True,
|
|
2145
|
+
row_deletable=True,
|
|
2146
|
+
sort_mode='multi',
|
|
2147
|
+
sort_action = 'native',
|
|
2148
|
+
page_current=0,
|
|
2149
|
+
page_size=5,
|
|
2150
|
+
style_cell = {
|
|
2151
|
+
'overflow':'hidden',
|
|
2152
|
+
'textOverflow':'ellipsis',
|
|
2153
|
+
'maxWidth':0
|
|
2154
|
+
},
|
|
2155
|
+
tooltip_data = [
|
|
2156
|
+
{
|
|
2157
|
+
column: {'value':str(value),'type':'markdown'}
|
|
2158
|
+
for column, value in row.items()
|
|
2159
|
+
} for row in label_counts.to_dict('records')
|
|
2160
|
+
],
|
|
2161
|
+
tooltip_duration = None,
|
|
2162
|
+
id = {'type': f'{self.component_prefix}-bulk-labels-label-table','index':0}
|
|
2163
|
+
)
|
|
2164
|
+
|
|
2165
|
+
return [new_data], ['success'], [label_count_table]
|
|
2121
2166
|
|
|
2122
2167
|
def refresh_labels(self, refresh_click, structure_options):
|
|
2123
2168
|
"""Clear current label components and start over
|
|
@@ -2143,6 +2188,8 @@ class BulkLabels(Tool):
|
|
|
2143
2188
|
rationale_disable = True
|
|
2144
2189
|
label_source = '`Label Source`'
|
|
2145
2190
|
apply_disable = True
|
|
2191
|
+
apply_style = 'primary'
|
|
2192
|
+
markers = []
|
|
2146
2193
|
|
|
2147
2194
|
include_options = [{'label': i, 'value': i} for i in structure_options]
|
|
2148
2195
|
if len(ctx.outputs_list[13])>0:
|
|
@@ -2150,7 +2197,7 @@ class BulkLabels(Tool):
|
|
|
2150
2197
|
else:
|
|
2151
2198
|
spatial_queries_structures = []
|
|
2152
2199
|
|
|
2153
|
-
return [include_structures], [spatial_queries], [add_property], [current_structures],[label_type],[type_disable],[label_text], [text_disable], [label_rationale], [rationale_disable], [label_source], [apply_disable], [include_options], spatial_queries_structures
|
|
2200
|
+
return [include_structures], [spatial_queries], [add_property], [current_structures],[label_type],[type_disable],[label_text], [text_disable], [label_rationale], [rationale_disable], [label_source], [apply_disable], [include_options], spatial_queries_structures, [apply_style], [markers]
|
|
2154
2201
|
else:
|
|
2155
2202
|
raise exceptions.PreventUpdate
|
|
2156
2203
|
|
|
@@ -2184,4 +2231,105 @@ class BulkLabels(Tool):
|
|
|
2184
2231
|
)
|
|
2185
2232
|
]
|
|
2186
2233
|
|
|
2187
|
-
return new_structure_count_children
|
|
2234
|
+
return new_structure_count_children
|
|
2235
|
+
|
|
2236
|
+
def add_labels_to_structures(self, button_click, current_annotations, current_labels):
|
|
2237
|
+
|
|
2238
|
+
if not any([i['value'] for i in ctx.triggered]):
|
|
2239
|
+
raise exceptions.PreventUpdate
|
|
2240
|
+
|
|
2241
|
+
current_annotations = json.loads(get_pattern_matching_value(current_annotations))
|
|
2242
|
+
current_labels = json.loads(get_pattern_matching_value(current_labels))
|
|
2243
|
+
|
|
2244
|
+
label_ids = [i['_id'] for i in current_labels['labels']]
|
|
2245
|
+
for c in current_annotations:
|
|
2246
|
+
feature_ids = [i['properties']['_id'] for i in c['features']]
|
|
2247
|
+
id_intersect = list(set(label_ids) & set(feature_ids))
|
|
2248
|
+
if len(id_intersect)>0:
|
|
2249
|
+
for id in id_intersect:
|
|
2250
|
+
feature_label = current_labels['labels'][label_ids.index(id)]
|
|
2251
|
+
print(feature_label)
|
|
2252
|
+
c['features'][feature_ids.index(id)]['properties'] = c['features'][feature_ids.index(id)]['properties'] | current_labels['labels'][label_ids.index(id)]
|
|
2253
|
+
|
|
2254
|
+
updated_annotations = json.dumps(current_annotations)
|
|
2255
|
+
|
|
2256
|
+
return [updated_annotations]
|
|
2257
|
+
|
|
2258
|
+
def update_label_table(self, table_data, current_labels):
|
|
2259
|
+
|
|
2260
|
+
table_data = get_pattern_matching_value(table_data)
|
|
2261
|
+
current_labels = json.loads(get_pattern_matching_value(current_labels))
|
|
2262
|
+
if not table_data is None:
|
|
2263
|
+
included_label_types = sorted(list(set([i['Label Type'] for i in table_data])))
|
|
2264
|
+
current_label_types = sorted(list(set([i['type'] for i in current_labels['labels_metadata']])))
|
|
2265
|
+
|
|
2266
|
+
included_label_values = []
|
|
2267
|
+
current_label_values = []
|
|
2268
|
+
for t in included_label_types:
|
|
2269
|
+
included_label_values.extend(sorted([i['Label Value'] for i in table_data if i['Label Type']==t]))
|
|
2270
|
+
current_label_values.extend(sorted([i['label'] for i in current_labels['labels_metadata'] if i['type']==t]))
|
|
2271
|
+
|
|
2272
|
+
# Removing not-included label from labels and label metadata
|
|
2273
|
+
if not included_label_types==current_label_types or not included_label_values==current_label_values:
|
|
2274
|
+
# If the len of included_label_types does not equal the len of current_label_types then there has been a type deletion
|
|
2275
|
+
deletion = False
|
|
2276
|
+
replation = False
|
|
2277
|
+
if not len(included_label_types)==len(current_label_types):
|
|
2278
|
+
# This would be a deletion of one label type/value
|
|
2279
|
+
#print(f'type deletion: {included_label_types}, {current_label_types}')
|
|
2280
|
+
deletion = True
|
|
2281
|
+
replation = False
|
|
2282
|
+
else:
|
|
2283
|
+
# If the len of the types are the same length then check if they contain the same values:
|
|
2284
|
+
if not included_label_types==current_label_types:
|
|
2285
|
+
# This is a type replation
|
|
2286
|
+
#print(f'type replation: {included_label_types}, {current_label_types}')
|
|
2287
|
+
deletion = False
|
|
2288
|
+
replation = {'type': [i for i in included_label_types if not i in current_label_types][0]}
|
|
2289
|
+
|
|
2290
|
+
else:
|
|
2291
|
+
# In this case there is most likely a value replation
|
|
2292
|
+
if not included_label_values==current_label_values:
|
|
2293
|
+
#print(f'value replation: {included_label_values}, {current_label_values}')
|
|
2294
|
+
deletion = False
|
|
2295
|
+
replation = {'value': [i for i in included_label_values if not i in current_label_values][0]}
|
|
2296
|
+
|
|
2297
|
+
for l_idx,l in enumerate(current_labels['labels']):
|
|
2298
|
+
for l_m_idx,l_m in enumerate(l['labels']):
|
|
2299
|
+
if not l_m['type'] in included_label_types:
|
|
2300
|
+
if deletion:
|
|
2301
|
+
del current_labels['labels'][l_idx]['labels'][l_m_idx]
|
|
2302
|
+
elif replation:
|
|
2303
|
+
current_labels['labels'][l_idx]['labels'][l_m_idx]['type'] = replation['type']
|
|
2304
|
+
else:
|
|
2305
|
+
if not l_m['value'] in included_label_values:
|
|
2306
|
+
if deletion:
|
|
2307
|
+
del current_labels['labels'][l_idx]['labels'][l_m_idx]
|
|
2308
|
+
elif replation:
|
|
2309
|
+
current_labels['labels'][l_idx]['labels'][l_m_idx]['value'] = replation['value']
|
|
2310
|
+
|
|
2311
|
+
if len(l['labels'])==0:
|
|
2312
|
+
del current_labels['labels'][l_idx]
|
|
2313
|
+
|
|
2314
|
+
if deletion:
|
|
2315
|
+
current_labels['labels_metadata'] = [i for i in current_labels['labels_metadata'] if i['label'] in included_label_values and i['type'] in included_label_types]
|
|
2316
|
+
elif replation:
|
|
2317
|
+
if 'type' in replation:
|
|
2318
|
+
current_labels['labels_metadata'] = [i if i['type'] in included_label_types else i | replation for i in current_labels['labels_metadata']]
|
|
2319
|
+
elif 'value' in replation:
|
|
2320
|
+
current_labels['labels_metadata'] = [i if i['label'] in included_label_values else i | {'label': replation['value']} for i in current_labels['labels_metadata']]
|
|
2321
|
+
|
|
2322
|
+
updated_labels = json.dumps(current_labels)
|
|
2323
|
+
else:
|
|
2324
|
+
raise exceptions.PreventUpdate
|
|
2325
|
+
else:
|
|
2326
|
+
updated_labels = json.dumps({})
|
|
2327
|
+
|
|
2328
|
+
return [updated_labels]
|
|
2329
|
+
|
|
2330
|
+
|
|
2331
|
+
|
|
2332
|
+
|
|
2333
|
+
|
|
2334
|
+
|
|
2335
|
+
|
|
@@ -740,6 +740,7 @@ def spatially_aggregate(child_geo:dict, parent_geos: list, separate: bool = True
|
|
|
740
740
|
'Sum': nested_sum
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
+
#TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
743
744
|
sub_keys = reversed(p.split(' --> '))
|
|
744
745
|
for p_idx,part in enumerate(sub_keys):
|
|
745
746
|
nested_dict = {part: nested_dict}
|
|
@@ -756,6 +757,7 @@ def spatially_aggregate(child_geo:dict, parent_geos: list, separate: bool = True
|
|
|
756
757
|
props = numeric_df.columns.tolist()
|
|
757
758
|
for p in props:
|
|
758
759
|
mean_dict = mean_props[p]
|
|
760
|
+
#TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
759
761
|
for part in reversed(p.split(' --> ')):
|
|
760
762
|
mean_dict = {part: mean_dict}
|
|
761
763
|
|
|
@@ -801,6 +803,7 @@ def spatially_aggregate(child_geo:dict, parent_geos: list, separate: bool = True
|
|
|
801
803
|
'Sum': nested_sum
|
|
802
804
|
}
|
|
803
805
|
|
|
806
|
+
#TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
804
807
|
sub_keys = reversed(p.split(' --> '))
|
|
805
808
|
for part in sub_keys:
|
|
806
809
|
nested_dict = {part: nested_dict}
|
|
@@ -819,6 +822,7 @@ def spatially_aggregate(child_geo:dict, parent_geos: list, separate: bool = True
|
|
|
819
822
|
props = numeric_df.columns.tolist()
|
|
820
823
|
for p in props:
|
|
821
824
|
mean_dict = mean_props[p]
|
|
825
|
+
#TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
822
826
|
for part in reversed(p.split(' --> ')):
|
|
823
827
|
mean_dict = {part: mean_dict}
|
|
824
828
|
|
|
@@ -860,18 +864,44 @@ def extract_nested_prop(main_prop_dict: dict, depth: int, path: tuple = (), valu
|
|
|
860
864
|
values_list.append({
|
|
861
865
|
' --> '.join(list(path+(keys,))): values
|
|
862
866
|
})
|
|
867
|
+
|
|
868
|
+
elif type(values)==list:
|
|
869
|
+
values_list.extend(extract_listed_prop,path+(keys,),[])
|
|
870
|
+
|
|
863
871
|
else:
|
|
864
|
-
# Skipping properties that are still nested
|
|
872
|
+
# Skipping properties that are still nested
|
|
865
873
|
continue
|
|
866
874
|
else:
|
|
867
875
|
if type(values)==dict:
|
|
868
876
|
extract_nested_prop(values, depth-1, path+ (keys,), values_list)
|
|
869
|
-
|
|
877
|
+
elif type(values)==list:
|
|
878
|
+
values_list.extend(extract_listed_prop,path+(keys,),[])
|
|
879
|
+
elif type(values) in [int,float,str]:
|
|
870
880
|
# Only adding properties to the list one time
|
|
871
881
|
if not any([' --> '.join(list(path+(keys,))) in list(i.keys()) for i in values_list]):
|
|
872
882
|
values_list.append({
|
|
873
883
|
' --> '.join(list(path+(keys,))): values
|
|
874
884
|
})
|
|
885
|
+
else:
|
|
886
|
+
# Skipping properties of some mysterious fifth type
|
|
887
|
+
continue
|
|
888
|
+
|
|
889
|
+
return values_list
|
|
890
|
+
|
|
891
|
+
def extract_listed_prop(main_list: list, path: tuple = (), values_list: list = []):
|
|
892
|
+
|
|
893
|
+
if len(main_list)>0:
|
|
894
|
+
for item_idx,list_item in enumerate(main_list):
|
|
895
|
+
item_key = f'Value {item_idx}'
|
|
896
|
+
if type(list_item) in [int,float,str]:
|
|
897
|
+
values_list.append({
|
|
898
|
+
' --+ '.join(list(path+(item_key,))): list_item
|
|
899
|
+
})
|
|
900
|
+
elif type(list_item)==dict:
|
|
901
|
+
nested_n = find_nested_levels({item_key:list_item})
|
|
902
|
+
values_list.extend(extract_nested_prop({item_key: list_item},nested_n,path+(item_key,),[]))
|
|
903
|
+
elif type(list_item)==list:
|
|
904
|
+
extract_listed_prop(list_item,path+(item_key,),[])
|
|
875
905
|
|
|
876
906
|
return values_list
|
|
877
907
|
|
|
@@ -916,8 +946,13 @@ def extract_geojson_properties(geo_list: list, reference_object: Union[str,None]
|
|
|
916
946
|
for p in f_props:
|
|
917
947
|
# Checking for sub-properties
|
|
918
948
|
sub_props = []
|
|
919
|
-
if type(f['properties'][p])
|
|
920
|
-
|
|
949
|
+
if type(f['properties'][p]) in [dict,list]:
|
|
950
|
+
# Pulling out nested properties (either dictionaries or lists or lists of dictionaries or dictionaries of lists, etc.)
|
|
951
|
+
if type(f['properties'][p]) ==dict:
|
|
952
|
+
nested_value = extract_nested_prop({p: f['properties'][p]}, nested_depth, (), [])
|
|
953
|
+
elif type(f['properties'][p])==list:
|
|
954
|
+
nested_value = extract_listed_prop(f['properties'][p],(p,),[])
|
|
955
|
+
|
|
921
956
|
if len(nested_value)>0:
|
|
922
957
|
for n in nested_value:
|
|
923
958
|
n_key = list(n.keys())[0]
|
|
@@ -950,7 +985,7 @@ def extract_geojson_properties(geo_list: list, reference_object: Union[str,None]
|
|
|
950
985
|
property_info[n_key]['unique'].append(n_value)
|
|
951
986
|
property_info[n_key]['distinct'] +=1
|
|
952
987
|
|
|
953
|
-
|
|
988
|
+
elif type(f['properties'][p]) in [int,float,str]:
|
|
954
989
|
f_sup_val = f['properties'][p]
|
|
955
990
|
|
|
956
991
|
if not p in property_info:
|
|
@@ -986,7 +1021,6 @@ def extract_geojson_properties(geo_list: list, reference_object: Union[str,None]
|
|
|
986
1021
|
|
|
987
1022
|
#TODO: After loading an experiment, reference the file here for additional properties
|
|
988
1023
|
|
|
989
|
-
|
|
990
1024
|
|
|
991
1025
|
geojson_properties = sorted(geojson_properties)
|
|
992
1026
|
|
|
@@ -1090,6 +1124,7 @@ def process_filters_queries(filter_list:list, spatial_list:list, structures:list
|
|
|
1090
1124
|
|
|
1091
1125
|
for f in filter_list:
|
|
1092
1126
|
if include:
|
|
1127
|
+
#TODO: Update this for different types of nested props (--+ = list, --# = external reference object)
|
|
1093
1128
|
filter_name_parts = f['name'].split(' --> ')
|
|
1094
1129
|
|
|
1095
1130
|
include = True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|