batoolset 0.1.0__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.
- batoolset/GUI/__init__.py +0 -0
- batoolset/GUI/list_gui.py +513 -0
- batoolset/GUI/multi_image_display.py +989 -0
- batoolset/GUI/open.py +325 -0
- batoolset/GUI/overlay_hints.py +45 -0
- batoolset/GUI/paint2.py +1345 -0
- batoolset/GUI/scrollablepaint.py +1152 -0
- batoolset/IJ_ROIs/__init__.py +0 -0
- batoolset/IJ_ROIs/tools.py +40 -0
- batoolset/PATH/__init__.py +0 -0
- batoolset/PATH/tools.py +79 -0
- batoolset/SQLite_tools/__init__.py +0 -0
- batoolset/SQLite_tools/tools.py +840 -0
- batoolset/__init__.py +0 -0
- batoolset/binary/__init__.py +0 -0
- batoolset/binary/tools.py +123 -0
- batoolset/binarytools/__init__.py +0 -0
- batoolset/binarytools/cell_center_detector.py +290 -0
- batoolset/clipboard_tools/__init__.py +0 -0
- batoolset/clipboard_tools/tools.py +7 -0
- batoolset/colors/__init__.py +0 -0
- batoolset/colors/colorgen.py +193 -0
- batoolset/colors/tools.py +22 -0
- batoolset/command_line/__init__.py +0 -0
- batoolset/command_line/tools.py +18 -0
- batoolset/dialogs/__init__.py +0 -0
- batoolset/dialogs/opensave.py +228 -0
- batoolset/dicts/__init__.py +0 -0
- batoolset/dicts/tools.py +299 -0
- batoolset/dims/__init__.py +0 -0
- batoolset/dims/tools.py +83 -0
- batoolset/draw/__init__.py +0 -0
- batoolset/draw/shapes/Position.py +183 -0
- batoolset/draw/shapes/__init__.py +0 -0
- batoolset/draw/shapes/automated_alignment_drawing_refactored.py +346 -0
- batoolset/draw/shapes/circle2d.py +99 -0
- batoolset/draw/shapes/compute_optimal_size_all_merged.py +317 -0
- batoolset/draw/shapes/ellipse2d.py +336 -0
- batoolset/draw/shapes/freehand2d.py +235 -0
- batoolset/draw/shapes/group.py +1830 -0
- batoolset/draw/shapes/image2d.py +2469 -0
- batoolset/draw/shapes/line2d.py +575 -0
- batoolset/draw/shapes/point2d.py +257 -0
- batoolset/draw/shapes/polygon2d.py +357 -0
- batoolset/draw/shapes/polyline2d.py +235 -0
- batoolset/draw/shapes/rect2d.py +388 -0
- batoolset/draw/shapes/rectangle2d.py +601 -0
- batoolset/draw/shapes/scalebar.py +304 -0
- batoolset/draw/shapes/serializablefont.py +126 -0
- batoolset/draw/shapes/serializableguiparams.py +10 -0
- batoolset/draw/shapes/square2d.py +103 -0
- batoolset/draw/shapes/textorientation.py +45 -0
- batoolset/draw/shapes/tools.py +467 -0
- batoolset/draw/shapes/transform2D.py +350 -0
- batoolset/draw/shapes/txt2d.py +550 -0
- batoolset/draw/widgets/__init__.py +0 -0
- batoolset/draw/widgets/col.py +343 -0
- batoolset/draw/widgets/image_group.py +248 -0
- batoolset/draw/widgets/image_organizer.py +96 -0
- batoolset/draw/widgets/paint.py +184 -0
- batoolset/draw/widgets/vectorial.py +257 -0
- batoolset/figure/__init__.py +0 -0
- batoolset/figure/alignment.py +814 -0
- batoolset/figure/column.py +1117 -0
- batoolset/figure/fig_tools.py +285 -0
- batoolset/figure/minimal_debugger_scale_etc.py +139 -0
- batoolset/figure/panel.py +697 -0
- batoolset/figure/row.py +852 -0
- batoolset/figure/user_selection.py +287 -0
- batoolset/files/__init__.py +0 -0
- batoolset/files/tools.py +1321 -0
- batoolset/images/__init__.py +0 -0
- batoolset/images/tools.py +81 -0
- batoolset/img.py +5216 -0
- batoolset/linux/__init__.py +0 -0
- batoolset/linux/tools.py +72 -0
- batoolset/lists/__init__.py +0 -0
- batoolset/lists/tools.py +436 -0
- batoolset/luts/__init__.py +0 -0
- batoolset/luts/lut_minimal_test.py +1207 -0
- batoolset/matplolib/__init__.py +0 -0
- batoolset/matplolib/tools.py +44 -0
- batoolset/np_tools/__init__.py +0 -0
- batoolset/np_tools/tools.py +252 -0
- batoolset/np_tools/trash.py +15 -0
- batoolset/pandas_tools/__init__.py +0 -0
- batoolset/pandas_tools/tools.py +880 -0
- batoolset/partials/__init__.py +0 -0
- batoolset/partials/tools.py +83 -0
- batoolset/plot_tools/__init__.py +0 -0
- batoolset/plot_tools/tools.py +70 -0
- batoolset/pyqt/__init__.py +0 -0
- batoolset/pyqt/tools.py +691 -0
- batoolset/re_tools/__init__.py +0 -0
- batoolset/re_tools/tools.py +35 -0
- batoolset/serialization/__init__.py +0 -0
- batoolset/serialization/tools.py +1192 -0
- batoolset/sets/__init__.py +0 -0
- batoolset/sets/tools.py +109 -0
- batoolset/settings/__init__.py +0 -0
- batoolset/settings/global_settings.py +134 -0
- batoolset/strings/__init__.py +0 -0
- batoolset/strings/tools.py +225 -0
- batoolset/ta/__init__.py +0 -0
- batoolset/ta/database/__init__.py +0 -0
- batoolset/ta/database/advanced_sql_plotter.py +1170 -0
- batoolset/ta/database/my_qt_table.py +79 -0
- batoolset/ta/database/preview_and_edit_sqlite_db.py +405 -0
- batoolset/ta/database/sql.py +2141 -0
- batoolset/ta/database/sql_column_name_dialog.py +122 -0
- batoolset/ta/measurements/TAmeasures.py +2226 -0
- batoolset/ta/measurements/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/__init__.py +0 -0
- batoolset/ta/measurements/measurements3D/get_point_on_surface_if_centroid_is_bad.py +175 -0
- batoolset/ta/measurements/measurements3D/height_map_to_projection.py +82 -0
- batoolset/ta/measurements/measurements3D/measures3D.py +208 -0
- batoolset/ta/measurements/measurements3D/triangulate.py +331 -0
- batoolset/ta/measurements/nematic.py +562 -0
- batoolset/ta/measurements/nematic_measurements.py +107 -0
- batoolset/ta/segmentation/__init__.py +0 -0
- batoolset/ta/segmentation/neo_wshed.py +314 -0
- batoolset/ta/selections/__init__.py +0 -0
- batoolset/ta/selections/selection.py +329 -0
- batoolset/ta/tracking/__init__.py +0 -0
- batoolset/ta/tracking/last_tracking_based_on_matching_or_on_translation_from_mermaid_warp.py +653 -0
- batoolset/ta/tracking/local_to_track_correspondance.py +138 -0
- batoolset/ta/tracking/rapid_detection_of_vertices.py +517 -0
- batoolset/ta/tracking/registration.py +108 -0
- batoolset/ta/tracking/swap_detector_and_fixer.py +296 -0
- batoolset/ta/tracking/track_correction.py +238 -0
- batoolset/ta/tracking/tracking_error_detector_and_fixer.py +3099 -0
- batoolset/ta/tracking/tracking_yet_another_approach_pyramidal_registration_n_neo_swapping_correction.py +725 -0
- batoolset/ta/utils/TA_tools.py +43 -0
- batoolset/ta/utils/__init__.py +0 -0
- batoolset/ta/utils/rps_tools.py +1783 -0
- batoolset/tests/__init__.py +0 -0
- batoolset/tests/rps_tools.py +51 -0
- batoolset/tests/tools.py +10 -0
- batoolset/tools/__init__.py +0 -0
- batoolset/tools/early_stopper_class.py +9 -0
- batoolset/tools/guided_data_recoverer.py +110 -0
- batoolset/tools/logger.py +100 -0
- batoolset/tools/mytimer.py +31 -0
- batoolset/tools/prompt_color.py +160 -0
- batoolset/tools/qthandler.py +118 -0
- batoolset/uitools/__init__.py +0 -0
- batoolset/uitools/blinker.py +56 -0
- batoolset/unsafe/__init__.py +0 -0
- batoolset/unsafe/tools.py +115 -0
- batoolset/utils/__init__.py +0 -0
- batoolset/utils/commontools.py +158 -0
- batoolset/utils/loadlist.py +728 -0
- batoolset/version.py +10 -0
- batoolset/xml/__init__.py +0 -0
- batoolset/xml/tools.py +65 -0
- batoolset/zip_tools/__init__.py +0 -0
- batoolset/zip_tools/tools.py +76 -0
- batoolset-0.1.0.dist-info/LICENSE +24 -0
- batoolset-0.1.0.dist-info/METADATA +45 -0
- batoolset-0.1.0.dist-info/RECORD +162 -0
- batoolset-0.1.0.dist-info/WHEEL +5 -0
- batoolset-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2141 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import traceback
|
|
3
|
+
import math
|
|
4
|
+
import pandas
|
|
5
|
+
from prettytable import PrettyTable
|
|
6
|
+
import batoolset.SQLite_tools.tools
|
|
7
|
+
from batoolset.img import Img, has_metadata, _create_dir
|
|
8
|
+
from batoolset.ta.selections.selection import convert_coords_to_IDs
|
|
9
|
+
from batoolset.tools.early_stopper_class import early_stop
|
|
10
|
+
from batoolset.tools.logger import TA_logger # logging
|
|
11
|
+
from batoolset.utils.loadlist import smart_TA_list,loadlist
|
|
12
|
+
from batoolset.files.tools import smart_name_parser
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import pandas as pd
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
logger = TA_logger()
|
|
18
|
+
|
|
19
|
+
# " need header"
|
|
20
|
+
def create_table_and_append_data(db_path, table_name, columns, data, column_types=None, temporary=False):
|
|
21
|
+
"""
|
|
22
|
+
Creates an SQL table in a database and fills it with data.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
db_path (str): The path to the database file.
|
|
26
|
+
table_name (str): The name of the table to create.
|
|
27
|
+
columns (list): A list of column names.
|
|
28
|
+
data (dict): A dictionary containing the data to populate the table, where the keys represent column names and the values represent column data.
|
|
29
|
+
column_types (list, optional): A list of column types. Defaults to None.
|
|
30
|
+
temporary (bool, optional): Specifies whether the created table is temporary. Defaults to False.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
Exception: If an error occurs while creating the table or filling it with data, or when closing the database connection.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
# >>> columns = ['id', 'name', 'age']
|
|
37
|
+
# >>> data = {
|
|
38
|
+
# ... 'id': [1, 2, 3],
|
|
39
|
+
# ... 'name': ['John', 'Jane', 'Alice'],
|
|
40
|
+
# ... 'age': [25, 32, 41]
|
|
41
|
+
# ... }
|
|
42
|
+
# >>> create_table_and_append_data("mydb.db", "persons", columns, data)
|
|
43
|
+
#
|
|
44
|
+
# >>> columns = ['id', 'fruit']
|
|
45
|
+
# >>> data = {
|
|
46
|
+
# ... 'id': [1, 2, 3],
|
|
47
|
+
# ... 'fruit': ['apple', 'banana', 'cherry']
|
|
48
|
+
# ... }
|
|
49
|
+
# >>> column_types = ['INTEGER', 'TEXT']
|
|
50
|
+
# >>> create_table_and_append_data("mydb.db", "fruits", columns, data, column_types=column_types, temporary=True)
|
|
51
|
+
|
|
52
|
+
# >>> columns = ['id', 'fruit']
|
|
53
|
+
# >>> data = np.array( [['1', 'apple'], ['2', 'banana'], ['3', 'cherry']])
|
|
54
|
+
# >>> column_types = ['INTEGER', 'TEXT']
|
|
55
|
+
# >>> create_table_and_append_data("mydb.db", "fruits", columns, data, column_types=column_types, temporary=False)
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
db = None
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
db = TAsql(db_path)
|
|
62
|
+
|
|
63
|
+
# Drop the table if it already exists
|
|
64
|
+
db.drop_table(table_name)
|
|
65
|
+
|
|
66
|
+
# Prepare the column content for the CREATE TABLE statement
|
|
67
|
+
col_content = columns
|
|
68
|
+
if isinstance(col_content, list):
|
|
69
|
+
if column_types is not None:
|
|
70
|
+
# Combine the column names and types
|
|
71
|
+
concat = list(zip(columns, column_types))
|
|
72
|
+
concat = [str(name) + ' ' + str(type) for name, type in concat]
|
|
73
|
+
col_content = _list_to_string(concat, add_quotes=False)
|
|
74
|
+
else:
|
|
75
|
+
col_content = _list_to_string(col_content, add_quotes=False)
|
|
76
|
+
|
|
77
|
+
# Create the table with the specified columns and column types
|
|
78
|
+
db.cur.execute(
|
|
79
|
+
'CREATE' + (' TEMPORARY' if temporary else '') + ' TABLE ' + table_name + ' (' + col_content + ')')
|
|
80
|
+
db.fill_table(table_name, data)
|
|
81
|
+
|
|
82
|
+
except:
|
|
83
|
+
traceback.print_exc()
|
|
84
|
+
logger.error('Something went wrong...')
|
|
85
|
+
|
|
86
|
+
finally:
|
|
87
|
+
if db is not None:
|
|
88
|
+
try:
|
|
89
|
+
db.close()
|
|
90
|
+
except:
|
|
91
|
+
traceback.print_exc()
|
|
92
|
+
|
|
93
|
+
def get_table_columns(path_to_db, table_name):
|
|
94
|
+
"""
|
|
95
|
+
Given a table name, prints the names of all the columns in the table.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
path_to_db (str): The path to the SQLite database file.
|
|
99
|
+
table_name (str): The name of the table.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
None
|
|
103
|
+
|
|
104
|
+
Examples:
|
|
105
|
+
>>> get_table_columns('/media/teamPrudhomme/EqpPrudhomme2/Benoit_pr_Benjamin/coccinelles/latest images_20230614/raw images/N4N4_29_M_1a/ladybug_seg.db', 'elytras_shape')[:2]
|
|
106
|
+
['areas_without_holes', 'areas_with_holes']
|
|
107
|
+
"""
|
|
108
|
+
return batoolset.SQLite_tools.tools.get_table_columns(path_to_db, table_name)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def remove_dupes_from_table_and_overwrite_table(db_path, table_name):
|
|
112
|
+
"""
|
|
113
|
+
Removes duplicates from a table and overwrites the table with the deduplicated data.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
db_path (str): The path to the database file.
|
|
117
|
+
table_name (str): The name of the table to deduplicate.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
list: The query results.
|
|
121
|
+
|
|
122
|
+
# Examples:
|
|
123
|
+
# >>> remove_dupes_from_table_and_overwrite_table('database.db', 'my_table')
|
|
124
|
+
# [1, 2, 3, 4]
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
query_results = []
|
|
128
|
+
db = None
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
db = TAsql(db_path)
|
|
132
|
+
|
|
133
|
+
headers, data_rows = db.run_SQL_command_and_get_results('SELECT DISTINCT * FROM ' + table_name, return_header=True)
|
|
134
|
+
|
|
135
|
+
if not isinstance(data_rows, np.ndarray):
|
|
136
|
+
data_rows = np.asarray(data_rows, dtype=object)
|
|
137
|
+
data_rows = data_rows.T
|
|
138
|
+
finally_formatted_table = {}
|
|
139
|
+
|
|
140
|
+
for iii, header in enumerate(headers):
|
|
141
|
+
try:
|
|
142
|
+
finally_formatted_table[header] = data_rows[iii].tolist()
|
|
143
|
+
except:
|
|
144
|
+
logger.warning('no data to be added --> the column will be empty')
|
|
145
|
+
finally_formatted_table[header] = []
|
|
146
|
+
|
|
147
|
+
db.drop_table(table_name)
|
|
148
|
+
db.create_and_append_table(table_name, finally_formatted_table)
|
|
149
|
+
|
|
150
|
+
except:
|
|
151
|
+
traceback.print_exc()
|
|
152
|
+
logger.error('Something went wrong...')
|
|
153
|
+
|
|
154
|
+
finally:
|
|
155
|
+
if db is not None:
|
|
156
|
+
try:
|
|
157
|
+
db.close()
|
|
158
|
+
except:
|
|
159
|
+
traceback.print_exc()
|
|
160
|
+
|
|
161
|
+
return query_results
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# maybe put this some other place --> this saves a table data and header as csv
|
|
165
|
+
|
|
166
|
+
def save_data_to_csv(output_file_name, header, data):
|
|
167
|
+
"""
|
|
168
|
+
Saves data to a CSV file.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
output_file_name (str): The name of the output CSV file.
|
|
172
|
+
header (list): The list of column headers.
|
|
173
|
+
data (list): The list of data rows.
|
|
174
|
+
|
|
175
|
+
# Examples:
|
|
176
|
+
# >>> header = ['Name', 'Age', 'Gender']
|
|
177
|
+
# >>> data = [['John', 25, 'Male'], ['Jane', 30, 'Female']]
|
|
178
|
+
# >>> save_data_to_csv('output.csv', header, data)
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
_create_dir(output_file_name) # Create directory if it doesn't exist
|
|
182
|
+
|
|
183
|
+
if header is not None and header: # Check if header is provided and not empty
|
|
184
|
+
db_df = pd.DataFrame(data=data if data else None, columns=header) # Create a DataFrame with the data and header
|
|
185
|
+
db_df.to_csv(output_file_name, index=False) # Save the DataFrame to a CSV file without index
|
|
186
|
+
else:
|
|
187
|
+
open(output_file_name, 'w').close() # Create an empty file if no header is provided
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def prepend_to_content(content, thing_to_prepend, auto_convert_tuple_to_list=True):
|
|
191
|
+
"""
|
|
192
|
+
Prepends a value to each element in a nested list or a single list.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
content (list or tuple): The content to prepend the value to.
|
|
196
|
+
thing_to_prepend (any): The value to prepend.
|
|
197
|
+
auto_convert_tuple_to_list (bool): Flag to automatically convert tuples to lists.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
list or tuple: The modified content with the value prepended.
|
|
201
|
+
|
|
202
|
+
Examples:
|
|
203
|
+
>>> content = [['Apple', 'Banana'], ['Orange', 'Mango']]
|
|
204
|
+
>>> prepend_to_content(content, 'Fruit')
|
|
205
|
+
[['Fruit', 'Apple', 'Banana'], ['Fruit', 'Orange', 'Mango']]
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
if isinstance(content, list) and content: # Check if content is a non-empty list
|
|
209
|
+
if isinstance(content[0], (list, tuple)): # Check if content is a nested list or tuple
|
|
210
|
+
for iii, cur_content in enumerate(content):
|
|
211
|
+
if auto_convert_tuple_to_list and isinstance(cur_content, tuple):
|
|
212
|
+
cur_content = list(cur_content) # Convert tuple to list if auto conversion is enabled
|
|
213
|
+
cur_content.insert(0, thing_to_prepend) # Prepend the value to each element
|
|
214
|
+
content[iii] = cur_content # Update the modified element in the content list
|
|
215
|
+
else:
|
|
216
|
+
if auto_convert_tuple_to_list and isinstance(content, tuple):
|
|
217
|
+
content = list(content) # Convert tuple to list if auto conversion is enabled
|
|
218
|
+
content.insert(0, thing_to_prepend) # Prepend the value to the content list
|
|
219
|
+
return content
|
|
220
|
+
|
|
221
|
+
def query_db_and_get_results(db_path, SQL_command_to_run):
|
|
222
|
+
"""
|
|
223
|
+
Executes an SQL command on a database and retrieves the query results.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
db_path (str): The path to the database.
|
|
227
|
+
SQL_command_to_run (str): The SQL command to execute.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
list: The results of the SQL query.
|
|
231
|
+
|
|
232
|
+
# Examples:
|
|
233
|
+
# >>> query_db_and_get_results('database.db', 'SELECT * FROM customers')
|
|
234
|
+
# [['John', 'Doe', 'john.doe@example.com'], ['Jane', 'Smith', 'jane.smith@example.com']]
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
from batoolset.ta.database.sql import TAsql # Import the required module
|
|
238
|
+
query_results = [] # Initialize an empty list to store the query results
|
|
239
|
+
db = None
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
db = TAsql(db_path) # Create a TAsql object with the specified database path
|
|
243
|
+
query_results = db.run_SQL_command_and_get_results(SQL_command_to_run, return_header=False)
|
|
244
|
+
# Execute the SQL command and retrieve the results
|
|
245
|
+
except:
|
|
246
|
+
traceback.print_exc() # Print the traceback in case of an exception
|
|
247
|
+
logger.error('Something went wrong...') # Log an error message
|
|
248
|
+
finally:
|
|
249
|
+
if db is not None:
|
|
250
|
+
try:
|
|
251
|
+
db.close() # Close the database connection
|
|
252
|
+
except:
|
|
253
|
+
traceback.print_exc() # Print the traceback in case of an exception
|
|
254
|
+
return query_results # Return the query results
|
|
255
|
+
|
|
256
|
+
def table_exists_in_db(db_path, table_name):
|
|
257
|
+
"""
|
|
258
|
+
Checks if a table exists in a database.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
db_path (str): The path to the database.
|
|
262
|
+
table_name (str): The name of the table to check.
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
bool: True if the table exists, False otherwise.
|
|
266
|
+
|
|
267
|
+
# Examples:
|
|
268
|
+
# >>> table_exists_in_db('database.db', 'customers')
|
|
269
|
+
# True
|
|
270
|
+
"""
|
|
271
|
+
from batoolset.ta.database.sql import TAsql
|
|
272
|
+
db = None
|
|
273
|
+
exists = False
|
|
274
|
+
try:
|
|
275
|
+
db = TAsql(db_path)
|
|
276
|
+
exists = db.exists(table_name)
|
|
277
|
+
except:
|
|
278
|
+
traceback.print_exc()
|
|
279
|
+
logger.error('Something went wrong...')
|
|
280
|
+
finally:
|
|
281
|
+
if db is not None:
|
|
282
|
+
try:
|
|
283
|
+
db.close()
|
|
284
|
+
except:
|
|
285
|
+
traceback.print_exc()
|
|
286
|
+
return exists
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# so maybe xlsx file is a good way of storing data in the end... --> TODO maybe too...
|
|
290
|
+
def table_to_xlsx_with_sheets(db_path, output_xlsx_file):
|
|
291
|
+
"""
|
|
292
|
+
Converts SQL tables in a database to an Excel file with separate sheets for each table.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
db_path (str): The path to the database.
|
|
296
|
+
output_xlsx_file (str): The path to the output Excel file.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
None
|
|
300
|
+
|
|
301
|
+
Examples:
|
|
302
|
+
table_to_xlsx_with_sheets('database.db', 'output.xlsx')
|
|
303
|
+
"""
|
|
304
|
+
# TODO maybe truncate tab name to 31 characters to avoid issues in Excel
|
|
305
|
+
|
|
306
|
+
# read all the tables and store them to the xlsx db
|
|
307
|
+
try:
|
|
308
|
+
db = TAsql(db_path)
|
|
309
|
+
tables = db.get_tables()
|
|
310
|
+
|
|
311
|
+
if db is not None:
|
|
312
|
+
try:
|
|
313
|
+
db.close()
|
|
314
|
+
except:
|
|
315
|
+
traceback.print_exc()
|
|
316
|
+
|
|
317
|
+
conn = sqlite3.connect(db_path)
|
|
318
|
+
with pd.ExcelWriter(output_xlsx_file) as writer:
|
|
319
|
+
for table in tables:
|
|
320
|
+
df = pandas.read_sql_query('SELECT * from '+table, conn)
|
|
321
|
+
df.to_excel(writer, sheet_name=table, index=False)
|
|
322
|
+
|
|
323
|
+
conn.close()
|
|
324
|
+
except:
|
|
325
|
+
traceback.print_exc()
|
|
326
|
+
logger.error('Something went wrong...')
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class TAsql:
|
|
331
|
+
def __init__(self, filename_or_connection=None, add_useful_missing_SQL_commands=True):
|
|
332
|
+
"""
|
|
333
|
+
Initializes a TAsql object.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
filename_or_connection (str or sqlite3.Connection): The filename or connection to the SQLite database.
|
|
337
|
+
add_useful_missing_SQL_commands (bool): Whether to add useful missing SQL commands. Default is True.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
None
|
|
341
|
+
"""
|
|
342
|
+
self.db_name = filename_or_connection
|
|
343
|
+
if isinstance(filename_or_connection, sqlite3.Connection):
|
|
344
|
+
self.con = filename_or_connection
|
|
345
|
+
self.db_name = None
|
|
346
|
+
logger.debug('Opened database from connection')
|
|
347
|
+
elif filename_or_connection is None:
|
|
348
|
+
# if no file name is specified, create an in-memory database
|
|
349
|
+
self.con = sqlite3.connect(":memory:")
|
|
350
|
+
self.db_name = ':memory:'
|
|
351
|
+
else:
|
|
352
|
+
if not os.path.exists(self.db_name):
|
|
353
|
+
parent_dir = os.path.dirname(self.db_name)
|
|
354
|
+
if not os.path.exists(parent_dir) and parent_dir != '':
|
|
355
|
+
# if parent folder does not exist, create it so that the db can be created too
|
|
356
|
+
os.makedirs(parent_dir, exist_ok=True)
|
|
357
|
+
self.con = sqlite3.connect(self.db_name)
|
|
358
|
+
logger.debug('Opened database: ' + str(self.db_name))
|
|
359
|
+
|
|
360
|
+
if add_useful_missing_SQL_commands:
|
|
361
|
+
self.add_useful_missing_SQL_commands()
|
|
362
|
+
self.cur = self.con.cursor()
|
|
363
|
+
|
|
364
|
+
def add_useful_missing_SQL_commands(self):
|
|
365
|
+
"""
|
|
366
|
+
Adds useful missing SQL commands to the SQLite database connection.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
None
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
None
|
|
373
|
+
"""
|
|
374
|
+
self.con.create_function("sin", 1, math.sin)
|
|
375
|
+
self.con.create_function("atan2", 2, math.atan2)
|
|
376
|
+
self.con.create_function("sqrt", 1, math.sqrt)
|
|
377
|
+
|
|
378
|
+
def drop_table(self, table_name):
|
|
379
|
+
"""
|
|
380
|
+
Drops a table from the SQLite database.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
table_name (str): The name of the table to drop.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
None
|
|
387
|
+
"""
|
|
388
|
+
self.cur.execute('DROP TABLE IF EXISTS ' + table_name)
|
|
389
|
+
|
|
390
|
+
def create_and_append_table(self, table_name, datas, temporary=False):
|
|
391
|
+
"""
|
|
392
|
+
Creates a new table in the SQLite database and appends data to it.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
table_name (str): The name of the table to create.
|
|
396
|
+
datas (dict): A dictionary containing the column names as keys and the data as values.
|
|
397
|
+
temporary (bool, optional): Specifies whether the table is temporary. Defaults to False.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
None
|
|
401
|
+
"""
|
|
402
|
+
# Create the table with the specified column names and types
|
|
403
|
+
self.create_table(table_name, list(datas.keys()), column_types=get_types_from_data(datas), temporary=temporary)
|
|
404
|
+
|
|
405
|
+
# Fill the table with data
|
|
406
|
+
self.fill_table(table_name, datas)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def create_table(self, table_name, columns, column_types=None, temporary=False):
|
|
410
|
+
"""
|
|
411
|
+
Creates a new table in the SQLite database with the specified columns and column types.
|
|
412
|
+
|
|
413
|
+
Args:
|
|
414
|
+
table_name (str): The name of the table to create.
|
|
415
|
+
columns (list): A list of column names.
|
|
416
|
+
column_types (list, optional): A list of column types corresponding to the columns. Defaults to None.
|
|
417
|
+
temporary (bool, optional): Specifies whether the table is temporary. Defaults to False.
|
|
418
|
+
|
|
419
|
+
Returns:
|
|
420
|
+
None
|
|
421
|
+
"""
|
|
422
|
+
# TODO if types are specified --> need add it
|
|
423
|
+
|
|
424
|
+
# Drop the table if it already exists
|
|
425
|
+
self.drop_table(table_name)
|
|
426
|
+
|
|
427
|
+
# Prepare the column content for the CREATE TABLE statement
|
|
428
|
+
col_content = columns
|
|
429
|
+
if isinstance(col_content, list):
|
|
430
|
+
if column_types is not None:
|
|
431
|
+
# Combine the column names and types
|
|
432
|
+
concat = list(zip(columns, column_types))
|
|
433
|
+
concat = [str(name) + ' ' + str(type) for name, type in concat]
|
|
434
|
+
col_content = _list_to_string(concat, add_quotes=False)
|
|
435
|
+
else:
|
|
436
|
+
col_content = _list_to_string(col_content, add_quotes=False)
|
|
437
|
+
|
|
438
|
+
# Create the table with the specified columns and column types
|
|
439
|
+
self.cur.execute('CREATE' + (' TEMPORARY' if temporary else '') + ' TABLE ' + table_name + ' (' + col_content + ')')
|
|
440
|
+
|
|
441
|
+
def fill_table(self, table_name, datas):
|
|
442
|
+
"""
|
|
443
|
+
Fills the specified table in the SQLite database with the provided data.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
table_name (str): The name of the table to fill.
|
|
447
|
+
datas (dict or list): The data to insert into the table. If a dictionary is provided, the values will be inserted as rows in the table.
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
None
|
|
451
|
+
"""
|
|
452
|
+
if isinstance(datas, dict):
|
|
453
|
+
# Convert the dictionary values to a list
|
|
454
|
+
datas = list(datas.values())
|
|
455
|
+
|
|
456
|
+
# Reorder the data into a more friendly format
|
|
457
|
+
datas = np.array(datas, dtype=object).T.tolist()
|
|
458
|
+
|
|
459
|
+
# TODO this is totally inefficient --> change the code some day to directly handle np.ndarray some day
|
|
460
|
+
if isinstance(datas, np.ndarray):
|
|
461
|
+
datas = datas.tolist()
|
|
462
|
+
|
|
463
|
+
# Iterate over the data and insert into the table
|
|
464
|
+
for data in datas:
|
|
465
|
+
# Convert the data list to a string
|
|
466
|
+
list_as_string = _list_to_string(data) # bug is here
|
|
467
|
+
|
|
468
|
+
# Generate the INSERT INTO command
|
|
469
|
+
COMMAND = "INSERT INTO " + table_name + " VALUES (" + list_as_string + ")"
|
|
470
|
+
|
|
471
|
+
# Check if the command has empty values and insert a row of NULL values
|
|
472
|
+
if COMMAND.endswith('()'):
|
|
473
|
+
print('empty values creating an empty row filled with NULL')
|
|
474
|
+
self.cur.execute("INSERT INTO " + table_name + " DEFAULT VALUES")
|
|
475
|
+
else:
|
|
476
|
+
self.cur.execute(COMMAND)
|
|
477
|
+
|
|
478
|
+
# Commit the changes to the database
|
|
479
|
+
self.con.commit()
|
|
480
|
+
|
|
481
|
+
def exists(self, table_name, attached_table=None):
|
|
482
|
+
"""
|
|
483
|
+
Checks if a table exists in the SQLite database.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
table_name (str): The name of the table to check.
|
|
487
|
+
attached_table (str, optional): The name of the attached table. Defaults to None.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
bool: True if the table exists, False otherwise.
|
|
491
|
+
"""
|
|
492
|
+
if table_name is None:
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
# Check if the table exists in the main database
|
|
496
|
+
if not '.' in table_name:
|
|
497
|
+
self.cur.execute("SELECT COUNT(name) FROM " + ('' if attached_table is None else str(
|
|
498
|
+
attached_table) + '.') + "sqlite_master WHERE type='table' AND name='" + table_name + "';")
|
|
499
|
+
else:
|
|
500
|
+
# Split the table name to detect temporary tables
|
|
501
|
+
master, table = table_name.split('.')
|
|
502
|
+
self.cur.execute("SELECT COUNT(name) FROM " + str(
|
|
503
|
+
master) + '.' + "sqlite_master WHERE type='table' AND name='" + table + "';")
|
|
504
|
+
|
|
505
|
+
# If the count is 1, then the table exists
|
|
506
|
+
if self.cur.fetchone()[0] == 1:
|
|
507
|
+
return True
|
|
508
|
+
|
|
509
|
+
return False
|
|
510
|
+
|
|
511
|
+
def save_query_to_csv_file(self, sql_command, output_file_name):
|
|
512
|
+
"""
|
|
513
|
+
Executes an SQL query and saves the results to a CSV file.
|
|
514
|
+
|
|
515
|
+
Args:
|
|
516
|
+
sql_command (str): The SQL command to execute.
|
|
517
|
+
output_file_name (str): The name of the output CSV file.
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
None
|
|
521
|
+
"""
|
|
522
|
+
import pandas as pd
|
|
523
|
+
|
|
524
|
+
# Execute the SQL query and store the results in a pandas DataFrame
|
|
525
|
+
db_df = pd.read_sql_query(sql_command, self.con)
|
|
526
|
+
|
|
527
|
+
# Save the DataFrame to a CSV file
|
|
528
|
+
db_df.to_csv(output_file_name, index=False)
|
|
529
|
+
|
|
530
|
+
def get_tables(self, force_lower_case=False):
|
|
531
|
+
"""
|
|
532
|
+
Retrieves the list of table names in the database.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
force_lower_case (bool): If True, forces the table names to be returned in lowercase.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
list: The list of table names in the database.
|
|
539
|
+
"""
|
|
540
|
+
try:
|
|
541
|
+
# Execute an SQL query to retrieve the table names from the database
|
|
542
|
+
self.cur.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
|
543
|
+
query_result = self.cur.fetchall()
|
|
544
|
+
# Unpack the query result to extract the table names
|
|
545
|
+
names = self._unpack(query_result)
|
|
546
|
+
if force_lower_case:
|
|
547
|
+
# Convert table names to lowercase if specified
|
|
548
|
+
names = _to_lower(names)
|
|
549
|
+
return names
|
|
550
|
+
except:
|
|
551
|
+
# Something went wrong, assume no tables exist
|
|
552
|
+
return None
|
|
553
|
+
|
|
554
|
+
def close(self):
|
|
555
|
+
"""
|
|
556
|
+
Closes the database connection.
|
|
557
|
+
|
|
558
|
+
This method commits any pending changes and closes the connection to the database.
|
|
559
|
+
"""
|
|
560
|
+
try:
|
|
561
|
+
# Commit any pending changes before closing
|
|
562
|
+
self.con.commit()
|
|
563
|
+
except:
|
|
564
|
+
# Ignore any exceptions that occur during commit
|
|
565
|
+
pass
|
|
566
|
+
|
|
567
|
+
logger.debug('Closing database: ' + str(self.db_name))
|
|
568
|
+
# Close the database connection
|
|
569
|
+
self.con.close()
|
|
570
|
+
|
|
571
|
+
def get_table_header(self, tablename):
|
|
572
|
+
"""
|
|
573
|
+
Retrieves the column names of a table.
|
|
574
|
+
|
|
575
|
+
Args:
|
|
576
|
+
tablename (str): The name of the table.
|
|
577
|
+
|
|
578
|
+
Returns:
|
|
579
|
+
list: The list of column names.
|
|
580
|
+
"""
|
|
581
|
+
# Retrieve the column names using the get_table_column_names_and_types method
|
|
582
|
+
header = self.get_table_column_names_and_types(table_name=tablename, return_colnames_only=True)
|
|
583
|
+
|
|
584
|
+
# Return the column names
|
|
585
|
+
return header
|
|
586
|
+
|
|
587
|
+
def attach_table(self, dbName, nickName):
|
|
588
|
+
"""
|
|
589
|
+
Attaches a new table to the current database.
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
dbName (str): The filename of the table to attach.
|
|
593
|
+
nickName (str): The nickname for the attached table.
|
|
594
|
+
|
|
595
|
+
Returns:
|
|
596
|
+
None
|
|
597
|
+
"""
|
|
598
|
+
if dbName is not None:
|
|
599
|
+
# Replace backslashes with forward slashes in the filename
|
|
600
|
+
dbName = dbName.replace('\\\\', '/').replace('\\', '/')
|
|
601
|
+
|
|
602
|
+
# Execute the command to attach the table
|
|
603
|
+
self.execute_command("ATTACH DATABASE '" + dbName + "' AS '" + nickName + "'")
|
|
604
|
+
|
|
605
|
+
def detach_table(self, nickName):
|
|
606
|
+
"""
|
|
607
|
+
Detaches a table from the current database.
|
|
608
|
+
|
|
609
|
+
Args:
|
|
610
|
+
nickName (str): The nickname of the table to detach.
|
|
611
|
+
|
|
612
|
+
Returns:
|
|
613
|
+
None
|
|
614
|
+
"""
|
|
615
|
+
if nickName is not None:
|
|
616
|
+
# Execute the command to detach the table
|
|
617
|
+
self.execute_command("DETACH DATABASE '" + nickName + "'")
|
|
618
|
+
|
|
619
|
+
def execute_command(self, SQL_command, warn_on_error=True):
|
|
620
|
+
"""
|
|
621
|
+
Executes an SQL command on the database.
|
|
622
|
+
|
|
623
|
+
Args:
|
|
624
|
+
SQL_command (str): The SQL command to execute.
|
|
625
|
+
warn_on_error (bool): If True, prints a warning message on error.
|
|
626
|
+
|
|
627
|
+
Returns:
|
|
628
|
+
None
|
|
629
|
+
"""
|
|
630
|
+
if SQL_command is None:
|
|
631
|
+
return
|
|
632
|
+
try:
|
|
633
|
+
# Execute the SQL command
|
|
634
|
+
self.cur.execute(SQL_command)
|
|
635
|
+
self.cur.fetchall()
|
|
636
|
+
self.con.commit()
|
|
637
|
+
except:
|
|
638
|
+
if warn_on_error:
|
|
639
|
+
traceback.print_exc()
|
|
640
|
+
logger.error(
|
|
641
|
+
'error executing the following command:\n"' + str(SQL_command) + '"' + '\ntable name:' + str(
|
|
642
|
+
self.db_name))
|
|
643
|
+
|
|
644
|
+
def EXCEPT(self, table_name, *columns_to_exclude): # concatTableName=False,
|
|
645
|
+
"""
|
|
646
|
+
Generates a list of column names for a table excluding specified columns.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
table_name (str): The name of the table.
|
|
650
|
+
*columns_to_exclude (str): Columns to exclude from the generated list.
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
str: A comma-separated string of column names excluding the specified columns.
|
|
654
|
+
"""
|
|
655
|
+
columns_to_exclude = [col.lower() for col in columns_to_exclude]
|
|
656
|
+
columns = self.get_table_column_names_and_types(table_name, return_colnames_only=True)
|
|
657
|
+
columns = [col.lower() for col in columns]
|
|
658
|
+
columns = [col for col in columns if col not in columns_to_exclude]
|
|
659
|
+
|
|
660
|
+
# Generate the comma-separated string of column names
|
|
661
|
+
return ', '.join(columns)
|
|
662
|
+
|
|
663
|
+
def run_SQL_command_and_get_results(self, SQL_command, return_header=False, warn_on_error=True):
|
|
664
|
+
"""
|
|
665
|
+
Executes an SQL command and retrieves the results.
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
SQL_command (str): The SQL command to execute.
|
|
669
|
+
return_header (bool): If True, returns the header along with the results.
|
|
670
|
+
warn_on_error (bool): If True, prints a warning message on error.
|
|
671
|
+
|
|
672
|
+
Returns:
|
|
673
|
+
tuple: A tuple containing the results and optionally the header if return_header is True.
|
|
674
|
+
"""
|
|
675
|
+
if SQL_command is None:
|
|
676
|
+
if return_header:
|
|
677
|
+
return None, None
|
|
678
|
+
return None
|
|
679
|
+
|
|
680
|
+
if SQL_command.count(';') > 1:
|
|
681
|
+
# Split the command and execute the last part
|
|
682
|
+
SQL_commands = SQL_command.strip().split(';')
|
|
683
|
+
SQL_commands = [sql_command for sql_command in SQL_commands if sql_command.strip() != '']
|
|
684
|
+
|
|
685
|
+
if len(SQL_commands) > 1:
|
|
686
|
+
last_command = SQL_commands[-1]
|
|
687
|
+
for command in SQL_commands[:-1]:
|
|
688
|
+
self.run_SQL_command_and_get_results(command, return_header=False, warn_on_error=warn_on_error)
|
|
689
|
+
|
|
690
|
+
# Execute the last command and retrieve the results
|
|
691
|
+
return self.run_SQL_command_and_get_results(last_command, return_header=return_header,
|
|
692
|
+
warn_on_error=warn_on_error)
|
|
693
|
+
|
|
694
|
+
try:
|
|
695
|
+
self.cur.execute(SQL_command)
|
|
696
|
+
query_result = self.cur.fetchall()
|
|
697
|
+
|
|
698
|
+
if return_header:
|
|
699
|
+
headers = self._unpack(self.cur.description)
|
|
700
|
+
return headers, query_result
|
|
701
|
+
|
|
702
|
+
return query_result
|
|
703
|
+
except:
|
|
704
|
+
# Command failed (e.g., table does not exist)
|
|
705
|
+
if warn_on_error:
|
|
706
|
+
traceback.print_exc()
|
|
707
|
+
|
|
708
|
+
if return_header:
|
|
709
|
+
return None, None
|
|
710
|
+
return None
|
|
711
|
+
|
|
712
|
+
def clean(self):
|
|
713
|
+
"""
|
|
714
|
+
Cleans the database by removing unnecessary data.
|
|
715
|
+
|
|
716
|
+
This operation can lead to a strong size reduction in the database.
|
|
717
|
+
|
|
718
|
+
Returns:
|
|
719
|
+
None
|
|
720
|
+
"""
|
|
721
|
+
logger.debug('Cleaning the database: ' + str(self.db_name))
|
|
722
|
+
self.run_SQL_command_and_get_results("VACUUM;")
|
|
723
|
+
|
|
724
|
+
def get_column(self, table_name, column_name, sort=None):
|
|
725
|
+
"""
|
|
726
|
+
Retrieves the values of a specific column from a table.
|
|
727
|
+
|
|
728
|
+
Args:
|
|
729
|
+
table_name (str): The name of the table.
|
|
730
|
+
column_name (str): The name of the column.
|
|
731
|
+
sort (str): The sorting order of the results. Can be 'ASC' for ascending or 'DESC' for descending.
|
|
732
|
+
|
|
733
|
+
Returns:
|
|
734
|
+
list: The values of the specified column.
|
|
735
|
+
"""
|
|
736
|
+
if not self.exists(table_name) or column_name is None:
|
|
737
|
+
return None
|
|
738
|
+
try:
|
|
739
|
+
results = self.run_SQL_command_and_get_results('SELECT "' + column_name + '" FROM "' + table_name + '"' + (
|
|
740
|
+
'' if sort is None else 'ORDER BY "' + column_name + '" ' + sort), return_header=False,
|
|
741
|
+
warn_on_error=False)
|
|
742
|
+
results = self._unpack(results)
|
|
743
|
+
return results
|
|
744
|
+
except:
|
|
745
|
+
return None
|
|
746
|
+
|
|
747
|
+
def _unpack(self, lst):
|
|
748
|
+
"""
|
|
749
|
+
Unpacks a list of tuples by extracting the first element from each tuple.
|
|
750
|
+
|
|
751
|
+
Args:
|
|
752
|
+
lst (list): The list of tuples to unpack.
|
|
753
|
+
|
|
754
|
+
Returns:
|
|
755
|
+
list: The unpacked list containing the first element of each tuple.
|
|
756
|
+
"""
|
|
757
|
+
out = [elem[0] for elem in lst]
|
|
758
|
+
return out
|
|
759
|
+
|
|
760
|
+
def get_table_column_names_and_types(self, table_name, return_colnames_only=False, attached_table=None):
|
|
761
|
+
"""
|
|
762
|
+
Retrieves the column names and types of a table.
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
table_name (str): The name of the table.
|
|
766
|
+
return_colnames_only (bool): If True, returns only the column names. If False, returns a dictionary
|
|
767
|
+
mapping column names to their corresponding types.
|
|
768
|
+
attached_table (str): The name of the attached table, if applicable.
|
|
769
|
+
|
|
770
|
+
Returns:
|
|
771
|
+
list or dict: The column names and types of the table, depending on the value of return_colnames_only.
|
|
772
|
+
"""
|
|
773
|
+
if not self.exists(table_name, attached_table=attached_table):
|
|
774
|
+
return None
|
|
775
|
+
|
|
776
|
+
if not '.' in table_name:
|
|
777
|
+
cols_and_types = self.run_SQL_command_and_get_results('PRAGMA ' + ('' if attached_table is None else str(
|
|
778
|
+
attached_table) + '.') + 'table_info("' + table_name + '");') # does not return attached ones --> need another code
|
|
779
|
+
else:
|
|
780
|
+
master, table = table_name.split('.')
|
|
781
|
+
cols_and_types = self.run_SQL_command_and_get_results(
|
|
782
|
+
'PRAGMA ' + master + '.' + 'table_info("' + table + '");') # does not return attached ones --> need another code
|
|
783
|
+
|
|
784
|
+
if cols_and_types is None:
|
|
785
|
+
return None
|
|
786
|
+
|
|
787
|
+
cols_and_types = {col[1]: col[2] for col in cols_and_types}
|
|
788
|
+
|
|
789
|
+
if return_colnames_only:
|
|
790
|
+
return list(cols_and_types.keys())
|
|
791
|
+
else:
|
|
792
|
+
return cols_and_types
|
|
793
|
+
|
|
794
|
+
def get_min_max(self, table_name, column_name, freq=None, ignore_None_and_string=True, force_numeric=False):
|
|
795
|
+
"""
|
|
796
|
+
Retrieves the minimum and maximum values of a column in a table.
|
|
797
|
+
|
|
798
|
+
Args:
|
|
799
|
+
table_name (str): The name of the table.
|
|
800
|
+
column_name (str): The name of the column.
|
|
801
|
+
freq (float or list or tuple): The frequency or range of frequencies at which to sample the data. If None,
|
|
802
|
+
the minimum and maximum values are returned. If a single float value is provided, it represents both the
|
|
803
|
+
lower and upper frequency bounds. If a list or tuple of two float values is provided, they represent
|
|
804
|
+
the lower and upper frequency bounds, respectively.
|
|
805
|
+
ignore_None_and_string (bool): If True, ignores None and string values when calculating the minimum and maximum.
|
|
806
|
+
force_numeric (bool): If True, converts values to numeric before calculating the minimum and maximum.
|
|
807
|
+
|
|
808
|
+
Returns:
|
|
809
|
+
tuple: A tuple containing the minimum and maximum values of the column.
|
|
810
|
+
"""
|
|
811
|
+
if table_name is None or column_name is None:
|
|
812
|
+
return None, None
|
|
813
|
+
|
|
814
|
+
sorted_data = self.get_column(table_name, column_name, sort='ASC')
|
|
815
|
+
if sorted_data is None:
|
|
816
|
+
return None, None
|
|
817
|
+
|
|
818
|
+
# Call sort_col_numpy function with the appropriate parameters
|
|
819
|
+
return sort_col_numpy(sorted_data, freq=freq, ignore_None_and_string=ignore_None_and_string,
|
|
820
|
+
force_numeric=force_numeric, sort=False)
|
|
821
|
+
|
|
822
|
+
def getNbRows(self, tableName):
|
|
823
|
+
"""
|
|
824
|
+
Counts the number of rows in a table.
|
|
825
|
+
|
|
826
|
+
Args:
|
|
827
|
+
tableName (str): The name of the table.
|
|
828
|
+
|
|
829
|
+
Returns:
|
|
830
|
+
int: The number of rows in the table.
|
|
831
|
+
"""
|
|
832
|
+
if not self.exists(tableName):
|
|
833
|
+
return 0
|
|
834
|
+
|
|
835
|
+
SQLQuery = "SELECT COUNT(*) from '" + tableName + "';"
|
|
836
|
+
value = self.run_SQL_command_and_get_results(SQLQuery)
|
|
837
|
+
|
|
838
|
+
try:
|
|
839
|
+
return value[0][0]
|
|
840
|
+
except:
|
|
841
|
+
print('error')
|
|
842
|
+
|
|
843
|
+
return 0
|
|
844
|
+
|
|
845
|
+
def add_column(self, table_name, column_name, col_type=None, default_value='NULL'):
|
|
846
|
+
"""
|
|
847
|
+
Adds a column to a table.
|
|
848
|
+
|
|
849
|
+
Args:
|
|
850
|
+
table_name (str): The name of the table.
|
|
851
|
+
column_name (str): The name of the column to be added.
|
|
852
|
+
col_type (str, optional): The data type of the column. Defaults to None.
|
|
853
|
+
default_value (str, optional): The default value for the column. Defaults to 'NULL'.
|
|
854
|
+
"""
|
|
855
|
+
if not self.exists(table_name):
|
|
856
|
+
logger.error('Table ' + str(table_name) + ' does not exist.')
|
|
857
|
+
return
|
|
858
|
+
|
|
859
|
+
self.execute_command('ALTER TABLE ' + table_name + ' ADD COLUMN "' + column_name + '"' +
|
|
860
|
+
('' if col_type is None else (' ' + str(col_type) + ' ')) +
|
|
861
|
+
('' if default_value is None else ' DEFAULT ' + str(default_value)) + ';')
|
|
862
|
+
|
|
863
|
+
def remove_column(self, table_name, column_name):
|
|
864
|
+
"""
|
|
865
|
+
Removes a column from a table.
|
|
866
|
+
|
|
867
|
+
Args:
|
|
868
|
+
table_name (str): The name of the table.
|
|
869
|
+
column_name (str): The name of the column to be removed.
|
|
870
|
+
"""
|
|
871
|
+
if not self.exists(table_name):
|
|
872
|
+
logger.error('Table ' + str(table_name) + ' does not exist.')
|
|
873
|
+
return
|
|
874
|
+
try:
|
|
875
|
+
self.execute_command('ALTER TABLE ' + table_name + ' DROP COLUMN "' + column_name + '"')
|
|
876
|
+
except:
|
|
877
|
+
# If the column does not exist, there's nothing to drop, but it's not an error.
|
|
878
|
+
logger.warning(
|
|
879
|
+
'Column does not exist and could not be dropped: ' + str(table_name) + ' ' + str(column_name))
|
|
880
|
+
pass
|
|
881
|
+
|
|
882
|
+
def print_query(self, SQL_command):
|
|
883
|
+
"""
|
|
884
|
+
Prints the results of a SQL query in a formatted table.
|
|
885
|
+
|
|
886
|
+
Args:
|
|
887
|
+
SQL_command (str): The SQL query to execute and print the results.
|
|
888
|
+
"""
|
|
889
|
+
# Retrieve the header and data from the SQL query
|
|
890
|
+
header, data = self.run_SQL_command_and_get_results(SQL_command, return_header=True)
|
|
891
|
+
|
|
892
|
+
# Create a PrettyTable instance
|
|
893
|
+
x = PrettyTable(header)
|
|
894
|
+
|
|
895
|
+
# Add each row of data to the PrettyTable
|
|
896
|
+
for row in data:
|
|
897
|
+
x.add_row(row)
|
|
898
|
+
|
|
899
|
+
# Print the PrettyTable
|
|
900
|
+
print(x)
|
|
901
|
+
|
|
902
|
+
def isTableEmpty(self, tableName):
|
|
903
|
+
"""
|
|
904
|
+
Checks if a table is empty or does not exist.
|
|
905
|
+
|
|
906
|
+
Args:
|
|
907
|
+
tableName (str): The name of the table.
|
|
908
|
+
|
|
909
|
+
Returns:
|
|
910
|
+
bool: True if the table is empty or does not exist, False otherwise.
|
|
911
|
+
"""
|
|
912
|
+
if not self.exists(tableName):
|
|
913
|
+
return True
|
|
914
|
+
|
|
915
|
+
if self.getNbRows(tableName) == 0:
|
|
916
|
+
return True
|
|
917
|
+
|
|
918
|
+
return False
|
|
919
|
+
|
|
920
|
+
def create_filtered_query(self, SQL_command, filtering_elements_dict=None):
|
|
921
|
+
"""
|
|
922
|
+
Creates a filtered query based on the provided SQL command and filtering elements.
|
|
923
|
+
|
|
924
|
+
Args:
|
|
925
|
+
SQL_command (str): The original SQL command to filter.
|
|
926
|
+
filtering_elements_dict (dict): A dictionary where the keys are column names and the values are lists of elements
|
|
927
|
+
to filter.
|
|
928
|
+
|
|
929
|
+
Returns:
|
|
930
|
+
str: The filtered SQL query.
|
|
931
|
+
|
|
932
|
+
Examples:
|
|
933
|
+
filtering_elements_dict = {'area': [120, 130], 'local_ID': [33]}
|
|
934
|
+
create_filtered_query(SQL_command, filtering_elements_dict)
|
|
935
|
+
This will return: "SELECT * FROM (SQL_command) WHERE area IN (120, 130) AND local_ID IN (33)"
|
|
936
|
+
"""
|
|
937
|
+
if filtering_elements_dict is None:
|
|
938
|
+
# Empty filter, return the default command
|
|
939
|
+
return SQL_command
|
|
940
|
+
|
|
941
|
+
if not filtering_elements_dict:
|
|
942
|
+
return SQL_command
|
|
943
|
+
|
|
944
|
+
initial_command = 'SELECT * FROM (' + SQL_command + ') WHERE '
|
|
945
|
+
extra_command = ''
|
|
946
|
+
|
|
947
|
+
for kkk, (k, v) in enumerate(filtering_elements_dict.items()):
|
|
948
|
+
filters = ', '.join(str(f) for f in v)
|
|
949
|
+
extra_command += ' ' + (' AND ' if kkk > 0 else '') + k + ' IN (%s)' % filters
|
|
950
|
+
|
|
951
|
+
query = initial_command + extra_command
|
|
952
|
+
return query
|
|
953
|
+
|
|
954
|
+
def filter_by_clone(self, table_name, cell_label):
|
|
955
|
+
"""
|
|
956
|
+
Filters a database based on a clone table.
|
|
957
|
+
|
|
958
|
+
Args:
|
|
959
|
+
table_name (str): The name of the clone table in the database.
|
|
960
|
+
cell_label: The cell label image used to filter the database.
|
|
961
|
+
|
|
962
|
+
Returns:
|
|
963
|
+
list: The filtered local IDs.
|
|
964
|
+
|
|
965
|
+
Examples:
|
|
966
|
+
filter_by_clone('tracked_clone', cell_label)
|
|
967
|
+
This will return a list of filtered local IDs based on the provided clone table and cell label image.
|
|
968
|
+
"""
|
|
969
|
+
coords = []
|
|
970
|
+
try:
|
|
971
|
+
coords = self.run_SQL_command_and_get_results('SELECT * FROM ' + table_name, return_header=False)
|
|
972
|
+
except:
|
|
973
|
+
traceback.print_exc()
|
|
974
|
+
|
|
975
|
+
local_ids = convert_coords_to_IDs(cell_label, coords, forbidden_colors=[0], return_image=False,
|
|
976
|
+
new_color_to_give_to_cells_if_return_image=None)
|
|
977
|
+
return local_ids
|
|
978
|
+
def any_to_numeric(values):
|
|
979
|
+
"""
|
|
980
|
+
Converts values to numeric types if possible.
|
|
981
|
+
|
|
982
|
+
Args:
|
|
983
|
+
values (list): The list of values to convert.
|
|
984
|
+
|
|
985
|
+
Returns:
|
|
986
|
+
list: The converted values.
|
|
987
|
+
|
|
988
|
+
Examples:
|
|
989
|
+
values = ['123', '3.14', '456', '7.89']
|
|
990
|
+
converted_values = any_to_numeric(values)
|
|
991
|
+
This will convert the values in the list to their respective numeric types.
|
|
992
|
+
"""
|
|
993
|
+
if values is None:
|
|
994
|
+
return None
|
|
995
|
+
for iii, v in enumerate(values):
|
|
996
|
+
if isinstance(v, str):
|
|
997
|
+
if '.' in v:
|
|
998
|
+
try:
|
|
999
|
+
values[iii] = float(v)
|
|
1000
|
+
except ValueError:
|
|
1001
|
+
values[iii] = None
|
|
1002
|
+
else:
|
|
1003
|
+
try:
|
|
1004
|
+
values[iii] = int(v)
|
|
1005
|
+
except ValueError:
|
|
1006
|
+
values[iii] = None
|
|
1007
|
+
return values
|
|
1008
|
+
|
|
1009
|
+
def get_numeric_value(v):
|
|
1010
|
+
"""
|
|
1011
|
+
Converts a value to a numeric type if possible.
|
|
1012
|
+
|
|
1013
|
+
Args:
|
|
1014
|
+
v: The value to convert.
|
|
1015
|
+
|
|
1016
|
+
Returns:
|
|
1017
|
+
The converted numeric value if conversion is successful, None otherwise.
|
|
1018
|
+
|
|
1019
|
+
Examples:
|
|
1020
|
+
value = '3.14'
|
|
1021
|
+
converted_value = get_numeric_value(value)
|
|
1022
|
+
This will convert the value to a float type.
|
|
1023
|
+
"""
|
|
1024
|
+
if isinstance(v, str):
|
|
1025
|
+
if '.' in v:
|
|
1026
|
+
try:
|
|
1027
|
+
return float(v)
|
|
1028
|
+
except ValueError:
|
|
1029
|
+
return None
|
|
1030
|
+
else:
|
|
1031
|
+
try:
|
|
1032
|
+
return int(v)
|
|
1033
|
+
except ValueError:
|
|
1034
|
+
return None
|
|
1035
|
+
return v
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
import numpy as np
|
|
1039
|
+
|
|
1040
|
+
def sort_col_numpy(unsorted_data, freq=None, ignore_None_and_string=True, force_numeric=False, sort=True):
|
|
1041
|
+
"""
|
|
1042
|
+
Sorts a column of data using NumPy and computes the minimum and maximum values.
|
|
1043
|
+
|
|
1044
|
+
Args:
|
|
1045
|
+
unsorted_data: The column of data to sort.
|
|
1046
|
+
freq: The frequency range to consider for computing the minimum and maximum values.
|
|
1047
|
+
ignore_None_and_string: Flag to ignore None and string values during sorting.
|
|
1048
|
+
force_numeric: Flag to force conversion of values to numeric types.
|
|
1049
|
+
sort: Flag to indicate whether to perform sorting.
|
|
1050
|
+
|
|
1051
|
+
Returns:
|
|
1052
|
+
The minimum and maximum values of the sorted column.
|
|
1053
|
+
|
|
1054
|
+
Examples:
|
|
1055
|
+
data = [3, 1, 2]
|
|
1056
|
+
min_val, max_val = sort_col_numpy(data)
|
|
1057
|
+
This will sort the data and compute the minimum and maximum values.
|
|
1058
|
+
"""
|
|
1059
|
+
if unsorted_data is None:
|
|
1060
|
+
return None, None
|
|
1061
|
+
|
|
1062
|
+
sorted_data = unsorted_data
|
|
1063
|
+
|
|
1064
|
+
if force_numeric:
|
|
1065
|
+
sorted_data = any_to_numeric(sorted_data)
|
|
1066
|
+
|
|
1067
|
+
if ignore_None_and_string:
|
|
1068
|
+
sorted_data = [val for val in sorted_data if val is not None or isinstance(val, str)]
|
|
1069
|
+
|
|
1070
|
+
if sort:
|
|
1071
|
+
sorted_data = np.sort(unsorted_data)
|
|
1072
|
+
|
|
1073
|
+
length = len(sorted_data)
|
|
1074
|
+
min_val = sorted_data[0]
|
|
1075
|
+
max_val = sorted_data[-1]
|
|
1076
|
+
|
|
1077
|
+
if freq is None or freq == 0.:
|
|
1078
|
+
return min_val, max_val
|
|
1079
|
+
else:
|
|
1080
|
+
if isinstance(freq, list) or isinstance(freq, tuple):
|
|
1081
|
+
if len(freq) == 1:
|
|
1082
|
+
lower = freq[0]
|
|
1083
|
+
upper = freq[0]
|
|
1084
|
+
else:
|
|
1085
|
+
lower = freq[0]
|
|
1086
|
+
upper = freq[1]
|
|
1087
|
+
else:
|
|
1088
|
+
lower = freq
|
|
1089
|
+
upper = freq
|
|
1090
|
+
|
|
1091
|
+
if lower > 0.:
|
|
1092
|
+
idx = round(lower * length)
|
|
1093
|
+
try:
|
|
1094
|
+
min_val = sorted_data[idx]
|
|
1095
|
+
except:
|
|
1096
|
+
pass
|
|
1097
|
+
|
|
1098
|
+
if upper > 0.:
|
|
1099
|
+
idx = round(upper * length)
|
|
1100
|
+
try:
|
|
1101
|
+
max_val = sorted_data[-idx]
|
|
1102
|
+
except:
|
|
1103
|
+
pass
|
|
1104
|
+
|
|
1105
|
+
return min_val, max_val
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
def update_db_properties_using_image_properties(input_file, ta_path):
|
|
1110
|
+
"""
|
|
1111
|
+
Update the database properties using the image properties.
|
|
1112
|
+
|
|
1113
|
+
Args:
|
|
1114
|
+
input_file (str): The path to the input image file.
|
|
1115
|
+
ta_path (str): The path to the database directory.
|
|
1116
|
+
|
|
1117
|
+
Raises:
|
|
1118
|
+
Exception: If an error occurs while reading properties from the image or writing them to the database.
|
|
1119
|
+
"""
|
|
1120
|
+
try:
|
|
1121
|
+
tmp = Img(input_file) # Create an Img object from the input image file
|
|
1122
|
+
if has_metadata(tmp): # Check if the image has metadata
|
|
1123
|
+
db_path = os.path.join(ta_path, 'pyTA.db') # Construct the path to the database file
|
|
1124
|
+
db = TAsql(db_path) # Create a TAsql object with the database path
|
|
1125
|
+
try:
|
|
1126
|
+
# Initialize variables for storing the image properties
|
|
1127
|
+
voxel_size_x = None
|
|
1128
|
+
voxel_size_y = None
|
|
1129
|
+
voxel_size_z = None
|
|
1130
|
+
voxel_z_over_x_ratio = None
|
|
1131
|
+
time = None
|
|
1132
|
+
creation_time = None
|
|
1133
|
+
|
|
1134
|
+
# Extract the image properties from the metadata
|
|
1135
|
+
if 'vx' in tmp.metadata:
|
|
1136
|
+
voxel_size_x = tmp.metadata['vx']
|
|
1137
|
+
if 'vy' in tmp.metadata:
|
|
1138
|
+
voxel_size_y = tmp.metadata['vy']
|
|
1139
|
+
if 'vz' in tmp.metadata:
|
|
1140
|
+
voxel_size_z = tmp.metadata['vz']
|
|
1141
|
+
if 'AR' in tmp.metadata:
|
|
1142
|
+
voxel_z_over_x_ratio = tmp.metadata['AR']
|
|
1143
|
+
if 'time' in tmp.metadata:
|
|
1144
|
+
time = tmp.metadata['time']
|
|
1145
|
+
if 'creation_time' in tmp.metadata:
|
|
1146
|
+
creation_time = tmp.metadata['creation_time']
|
|
1147
|
+
|
|
1148
|
+
# Create a dictionary with the image properties
|
|
1149
|
+
neo_data = {'voxel_size_x': voxel_size_x,
|
|
1150
|
+
'voxel_size_y': voxel_size_y,
|
|
1151
|
+
'voxel_size_z': voxel_size_z,
|
|
1152
|
+
'voxel_z_over_x_ratio': voxel_z_over_x_ratio,
|
|
1153
|
+
'time': time,
|
|
1154
|
+
'creation_time': creation_time}
|
|
1155
|
+
|
|
1156
|
+
if db.exists('properties'):
|
|
1157
|
+
# The 'properties' table exists in the database, update the existing data
|
|
1158
|
+
header, cols = db.run_SQL_command_and_get_results('SELECT * FROM properties', return_header=True)
|
|
1159
|
+
cols = cols[0]
|
|
1160
|
+
data = _to_dict(header, cols) # Convert the retrieved data to a dictionary
|
|
1161
|
+
for key in list(neo_data.keys()):
|
|
1162
|
+
if neo_data[key] is None:
|
|
1163
|
+
if key not in data:
|
|
1164
|
+
data[key] = neo_data[key]
|
|
1165
|
+
else:
|
|
1166
|
+
data[str(key)] = neo_data[key]
|
|
1167
|
+
else:
|
|
1168
|
+
# The 'properties' table does not exist, use the new data as it is
|
|
1169
|
+
data = neo_data
|
|
1170
|
+
|
|
1171
|
+
data = {k: [v] for k, v in data.items()} # Convert the data dictionary to a dictionary of lists
|
|
1172
|
+
db.create_and_append_table('properties', data) # Create or append the 'properties' table with the data
|
|
1173
|
+
except:
|
|
1174
|
+
traceback.print_exc()
|
|
1175
|
+
print('An error occurred while reading properties from the image or writing them to the database')
|
|
1176
|
+
finally:
|
|
1177
|
+
try:
|
|
1178
|
+
db.close() # Close the database connection
|
|
1179
|
+
except:
|
|
1180
|
+
pass
|
|
1181
|
+
del tmp # Delete the temporary Img object
|
|
1182
|
+
except:
|
|
1183
|
+
traceback.print_exc()
|
|
1184
|
+
print('Error could not save image properties to the TA database')
|
|
1185
|
+
|
|
1186
|
+
def _to_dict(header, col):
|
|
1187
|
+
"""
|
|
1188
|
+
Convert the header and column data into a dictionary.
|
|
1189
|
+
|
|
1190
|
+
Args:
|
|
1191
|
+
header (list): The header containing the column names.
|
|
1192
|
+
col (list): The column data.
|
|
1193
|
+
|
|
1194
|
+
Returns:
|
|
1195
|
+
dict: A dictionary where the column names are the keys and the column data are the values.
|
|
1196
|
+
"""
|
|
1197
|
+
dct = {}
|
|
1198
|
+
for iii, he in enumerate(header):
|
|
1199
|
+
dct[he] = col[iii] # Map the column name to its corresponding data value
|
|
1200
|
+
return dct
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
def _list_to_string(lst, add_quotes=True):
|
|
1204
|
+
"""
|
|
1205
|
+
Convert a list or dictionary to a string representation.
|
|
1206
|
+
|
|
1207
|
+
Args:
|
|
1208
|
+
lst (list or dict): The list or dictionary to be converted.
|
|
1209
|
+
add_quotes (bool, optional): Indicates whether quotes should be added around string values (default: True).
|
|
1210
|
+
|
|
1211
|
+
Returns:
|
|
1212
|
+
str: The string representation of the list or dictionary.
|
|
1213
|
+
|
|
1214
|
+
Examples:
|
|
1215
|
+
>>> lst = [1, 2, 3]
|
|
1216
|
+
>>> _list_to_string(lst)
|
|
1217
|
+
"'1', '2', '3'"
|
|
1218
|
+
|
|
1219
|
+
>>> lst = ['apple', 'banana', 'cherry']
|
|
1220
|
+
>>> _list_to_string(lst)
|
|
1221
|
+
"'apple', 'banana', 'cherry'"
|
|
1222
|
+
|
|
1223
|
+
>>> dct = {'Name': 'John', 'Age': 25, 'Country': 'USA'}
|
|
1224
|
+
>>> _list_to_string(dct)
|
|
1225
|
+
'Name John, Age 25, Country USA'
|
|
1226
|
+
"""
|
|
1227
|
+
if isinstance(lst, list):
|
|
1228
|
+
return ', '.join(
|
|
1229
|
+
map(
|
|
1230
|
+
lambda x: "'" + str(x) + "'" if x is not None and add_quotes else str(x) if x is not None and not add_quotes else 'Null',
|
|
1231
|
+
lst
|
|
1232
|
+
)
|
|
1233
|
+
)
|
|
1234
|
+
elif isinstance(lst, dict):
|
|
1235
|
+
return ', '.join(
|
|
1236
|
+
(str(x) + ' ' + str(y)) if y is not None else str(x) + ' ' + 'Null' for x, y in lst.items()
|
|
1237
|
+
)
|
|
1238
|
+
else:
|
|
1239
|
+
return list
|
|
1240
|
+
|
|
1241
|
+
# TODO get master db --> TODO --> required for global plots --> easy way of doing things in fact, I love it
|
|
1242
|
+
|
|
1243
|
+
# maybe allow unknown
|
|
1244
|
+
def get_type(value):
|
|
1245
|
+
"""
|
|
1246
|
+
Determine the data type of a value.
|
|
1247
|
+
|
|
1248
|
+
Args:
|
|
1249
|
+
value (any): The value to determine the data type.
|
|
1250
|
+
|
|
1251
|
+
Returns:
|
|
1252
|
+
str: The data type of the value.
|
|
1253
|
+
|
|
1254
|
+
Examples:
|
|
1255
|
+
>>> get_type(42)
|
|
1256
|
+
'INTEGER'
|
|
1257
|
+
|
|
1258
|
+
>>> get_type(3.14)
|
|
1259
|
+
'FLOAT'
|
|
1260
|
+
|
|
1261
|
+
>>> get_type('Hello, world!')
|
|
1262
|
+
'TEXT'
|
|
1263
|
+
|
|
1264
|
+
>>> get_type(True)
|
|
1265
|
+
'BOOLEAN'
|
|
1266
|
+
"""
|
|
1267
|
+
if isinstance(value, bool):
|
|
1268
|
+
return 'BOOLEAN'
|
|
1269
|
+
if isinstance(value, str):
|
|
1270
|
+
return 'TEXT'
|
|
1271
|
+
if isinstance(value, int):
|
|
1272
|
+
return 'INTEGER'
|
|
1273
|
+
if isinstance(value, float):
|
|
1274
|
+
return 'FLOAT'
|
|
1275
|
+
if isinstance(value, list):
|
|
1276
|
+
return 'TEXT'
|
|
1277
|
+
if isinstance(value, np.int64) or isinstance(value, np.uint64) \
|
|
1278
|
+
or isinstance(value, np.int32) or isinstance(value, np.uint32) \
|
|
1279
|
+
or isinstance(value, np.int8) or isinstance(value, np.uint8):
|
|
1280
|
+
return 'INTEGER'
|
|
1281
|
+
if isinstance(value, Img):
|
|
1282
|
+
return 'INTEGER'
|
|
1283
|
+
if isinstance(value, tuple) or isinstance(value, np.ndarray):
|
|
1284
|
+
return 'TEXT'
|
|
1285
|
+
|
|
1286
|
+
print('error type not supported:', type(value), value)
|
|
1287
|
+
return 'TEXT'
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def get_types_from_data(one_row_of_data):
|
|
1291
|
+
"""
|
|
1292
|
+
Get the data types from a row of data.
|
|
1293
|
+
|
|
1294
|
+
Args:
|
|
1295
|
+
one_row_of_data (dict or list): A row of data represented as a dictionary or list.
|
|
1296
|
+
|
|
1297
|
+
Returns:
|
|
1298
|
+
list: A list of data types corresponding to the values in the row of data.
|
|
1299
|
+
|
|
1300
|
+
Examples:
|
|
1301
|
+
>>> get_types_from_data({'name': 'John', 'age': 30, 'height': 180.5})
|
|
1302
|
+
['TEXT', 'INTEGER', 'FLOAT']
|
|
1303
|
+
|
|
1304
|
+
"""
|
|
1305
|
+
types = []
|
|
1306
|
+
if isinstance(one_row_of_data, dict):
|
|
1307
|
+
for k, v in one_row_of_data.items():
|
|
1308
|
+
if v is None:
|
|
1309
|
+
types.append('TEXT')
|
|
1310
|
+
continue
|
|
1311
|
+
|
|
1312
|
+
if not isinstance(v, list):
|
|
1313
|
+
types.append(get_type(v))
|
|
1314
|
+
else:
|
|
1315
|
+
success = False
|
|
1316
|
+
for vv in v:
|
|
1317
|
+
if vv is not None:
|
|
1318
|
+
types.append(get_type(vv))
|
|
1319
|
+
success = True
|
|
1320
|
+
break
|
|
1321
|
+
if not success:
|
|
1322
|
+
types.append('TEXT')
|
|
1323
|
+
else:
|
|
1324
|
+
for data in one_row_of_data:
|
|
1325
|
+
types.append(get_type(data))
|
|
1326
|
+
|
|
1327
|
+
return types
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
def _to_lower(lst):
|
|
1331
|
+
"""
|
|
1332
|
+
Convert a list of elements to lowercase strings.
|
|
1333
|
+
|
|
1334
|
+
Args:
|
|
1335
|
+
lst (list): A list of elements.
|
|
1336
|
+
|
|
1337
|
+
Returns:
|
|
1338
|
+
list: A new list with elements converted to lowercase strings.
|
|
1339
|
+
|
|
1340
|
+
Examples:
|
|
1341
|
+
>>> _to_lower(['Apple', 'Banana', 'Orange'])
|
|
1342
|
+
['apple', 'banana', 'orange']
|
|
1343
|
+
|
|
1344
|
+
>>> _to_lower(['Hello', 'WORLD'])
|
|
1345
|
+
['hello', 'world']
|
|
1346
|
+
"""
|
|
1347
|
+
return [str(elm).lower() for elm in lst]
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
# NB this will fail when some command returns for some of the samples a null output --> think if there can be a workaround
|
|
1351
|
+
# MEGA TODO also I should always return the header and make sure it's identical to the previous one, because if it's not the case the aggregation is not possible --> then maybe set the row to None and later replace it with the appropriate nb of Null for example !!! --> think about that
|
|
1352
|
+
# TODO --> maybe also allow to save the master db --> pb is that it is never created the request is just made on the fly
|
|
1353
|
+
def combine_single_file_queries(lst, sql_command, table_names=None, db_name='pyTA.db', return_header=False, prepend_frame_nb=True, prepend_file_name=True, output_filename=None ): # , save_master_db=False
|
|
1354
|
+
"""
|
|
1355
|
+
Combine the results of single file queries into a master data set.
|
|
1356
|
+
|
|
1357
|
+
Args:
|
|
1358
|
+
lst (list): List of file paths to query.
|
|
1359
|
+
sql_command (str): SQL command to execute for each file.
|
|
1360
|
+
table_names (str or list, optional): Names of tables to include in the query. Defaults to None.
|
|
1361
|
+
db_name (str, optional): Name of the database file. Defaults to 'pyTA.db'.
|
|
1362
|
+
return_header (bool, optional): Flag to indicate whether to return the header. Defaults to False.
|
|
1363
|
+
prepend_frame_nb (bool, optional): Flag to indicate whether to prepend the frame number to the data. Defaults to True.
|
|
1364
|
+
prepend_file_name (bool, optional): Flag to indicate whether to prepend the file name to the data. Defaults to True.
|
|
1365
|
+
output_filename (str, optional): Name of the output file to save the combined data. Defaults to None.
|
|
1366
|
+
|
|
1367
|
+
Returns:
|
|
1368
|
+
tuple or list: If `output_filename` is None and `return_header` is False, returns the combined data as a list.
|
|
1369
|
+
If `output_filename` is None and `return_header` is True, returns the header and combined data as a tuple.
|
|
1370
|
+
If `output_filename` is provided, saves the combined data to the specified file and returns None.
|
|
1371
|
+
|
|
1372
|
+
# Examples:
|
|
1373
|
+
# >>> lst = ['file1.db', 'file2.db', 'file3.db']
|
|
1374
|
+
# >>> sql_command = 'SELECT * FROM data'
|
|
1375
|
+
# >>> combine_single_file_queries(lst, sql_command, table_names='my_table', output_filename='combined_data.csv')
|
|
1376
|
+
#
|
|
1377
|
+
# >>> lst = ['file1.db', 'file2.db', 'file3.db']
|
|
1378
|
+
# >>> sql_command = 'SELECT * FROM data WHERE value > 10'
|
|
1379
|
+
# >>> header, data = combine_single_file_queries(lst, sql_command, return_header=True)
|
|
1380
|
+
# >>> print(header)
|
|
1381
|
+
# >>> print(data)
|
|
1382
|
+
"""
|
|
1383
|
+
|
|
1384
|
+
if lst is not None and lst and isinstance(lst, list):
|
|
1385
|
+
# merged output
|
|
1386
|
+
master_data = []
|
|
1387
|
+
header = None
|
|
1388
|
+
for iii, file in enumerate(lst):
|
|
1389
|
+
# Parse the database file path
|
|
1390
|
+
db_path = smart_name_parser(file, db_name)
|
|
1391
|
+
# Create a TAsql instance for the database
|
|
1392
|
+
db = TAsql(db_path)
|
|
1393
|
+
try:
|
|
1394
|
+
final_command = sql_command
|
|
1395
|
+
|
|
1396
|
+
if table_names is not None:
|
|
1397
|
+
if isinstance(table_names, list):
|
|
1398
|
+
# Iterate over the table names and check if they exist in the database
|
|
1399
|
+
for table_name in table_names:
|
|
1400
|
+
if db.exists(table_name):
|
|
1401
|
+
final_command += table_name
|
|
1402
|
+
break
|
|
1403
|
+
else:
|
|
1404
|
+
final_command += table_names
|
|
1405
|
+
|
|
1406
|
+
# Execute the SQL command and get the results
|
|
1407
|
+
out = db.run_SQL_command_and_get_results(final_command, return_header=iii == 0)
|
|
1408
|
+
if isinstance(out, tuple):
|
|
1409
|
+
header, data = out
|
|
1410
|
+
if prepend_file_name:
|
|
1411
|
+
header = prepend_to_content(header, 'filename')
|
|
1412
|
+
if prepend_frame_nb:
|
|
1413
|
+
header = prepend_to_content(header, 'frame_nb')
|
|
1414
|
+
else:
|
|
1415
|
+
data = out
|
|
1416
|
+
if prepend_file_name:
|
|
1417
|
+
data = prepend_to_content(data, file)
|
|
1418
|
+
if prepend_frame_nb:
|
|
1419
|
+
data = prepend_to_content(data, iii)
|
|
1420
|
+
|
|
1421
|
+
# Append the data to the master data list
|
|
1422
|
+
master_data.extend(data)
|
|
1423
|
+
except:
|
|
1424
|
+
traceback.print_exc()
|
|
1425
|
+
finally:
|
|
1426
|
+
# if save_master_db:
|
|
1427
|
+
# if isinstance(save_master_db, bool):
|
|
1428
|
+
# save_master_db = smart_name_parser(output_filename, 'full_no_ext')+'_master.db'
|
|
1429
|
+
#
|
|
1430
|
+
# Close the database connection
|
|
1431
|
+
db.close()
|
|
1432
|
+
|
|
1433
|
+
if not master_data:
|
|
1434
|
+
master_data = None
|
|
1435
|
+
if output_filename is not None:
|
|
1436
|
+
# Save the combined data to the output file
|
|
1437
|
+
save_data_to_csv(output_filename, header, master_data)
|
|
1438
|
+
else:
|
|
1439
|
+
if return_header:
|
|
1440
|
+
return header, master_data
|
|
1441
|
+
return master_data
|
|
1442
|
+
else:
|
|
1443
|
+
logger.error('No input list -> nothing to do...')
|
|
1444
|
+
if return_header:
|
|
1445
|
+
return None, None
|
|
1446
|
+
return None
|
|
1447
|
+
|
|
1448
|
+
|
|
1449
|
+
# TODO KEEP MEGA URGENT do force_track_cells_db_update for bonds and vertices when the tracking will be implemented.
|
|
1450
|
+
# TODO --> find a way to handle clones here
|
|
1451
|
+
def createMasterDB(lst, outputName=None, progress_callback=None, database_name=None, force_track_cells_db_update=False, db_name='pyTA.db'):
|
|
1452
|
+
"""
|
|
1453
|
+
Creates a master database by combining data from multiple database files.
|
|
1454
|
+
|
|
1455
|
+
Args:
|
|
1456
|
+
lst (list): List of input filenames or database file paths.
|
|
1457
|
+
outputName (str, optional): Name of the output database file. If provided, the existing file will be removed
|
|
1458
|
+
before creating a new one. Defaults to None.
|
|
1459
|
+
progress_callback (function, optional): Callback function to track the progress of the database creation.
|
|
1460
|
+
Defaults to None.
|
|
1461
|
+
database_name (str, optional): Name of a specific database to include. Only the tables from this database will
|
|
1462
|
+
be added to the master database. Defaults to None.
|
|
1463
|
+
force_track_cells_db_update (bool, optional): If True, forces an update to the 'cell_tracks' table in the
|
|
1464
|
+
database. Defaults to False.
|
|
1465
|
+
db_name (str, optional): Name of the database. Defaults to 'pyTA.db'.
|
|
1466
|
+
|
|
1467
|
+
Returns:
|
|
1468
|
+
TAsql: The master database object.
|
|
1469
|
+
|
|
1470
|
+
Examples:
|
|
1471
|
+
lst = ['db1.db', 'db2.db', 'db3.db']
|
|
1472
|
+
createMasterDB(lst, outputName='master.db')
|
|
1473
|
+
|
|
1474
|
+
This example will create a master database named 'master.db' by combining the tables from 'db1.db', 'db2.db',
|
|
1475
|
+
and 'db3.db'. The existing 'master.db' file, if any, will be removed before creating the new database.
|
|
1476
|
+
"""
|
|
1477
|
+
masterDB = None
|
|
1478
|
+
try:
|
|
1479
|
+
frame_nb = "frame_nb"
|
|
1480
|
+
fileName = "filename"
|
|
1481
|
+
|
|
1482
|
+
# Create or remove the output file if specified
|
|
1483
|
+
if outputName is not None:
|
|
1484
|
+
try:
|
|
1485
|
+
os.remove(outputName)
|
|
1486
|
+
except OSError:
|
|
1487
|
+
pass
|
|
1488
|
+
|
|
1489
|
+
# Generate a list of database filenames
|
|
1490
|
+
database_list = smart_TA_list(lst, db_name)
|
|
1491
|
+
|
|
1492
|
+
# Create the masterDB object
|
|
1493
|
+
masterDB = TAsql(filename_or_connection=outputName, add_useful_missing_SQL_commands=False)
|
|
1494
|
+
|
|
1495
|
+
# Iterate over each database file
|
|
1496
|
+
if database_list is not None:
|
|
1497
|
+
for l, db_l in enumerate(database_list):
|
|
1498
|
+
try:
|
|
1499
|
+
# Check if the process should be stopped
|
|
1500
|
+
if early_stop.stop:
|
|
1501
|
+
return
|
|
1502
|
+
|
|
1503
|
+
# Update progress if a callback function is provided
|
|
1504
|
+
if progress_callback is not None:
|
|
1505
|
+
progress_callback.emit(int((l / len(database_list)) * 100))
|
|
1506
|
+
else:
|
|
1507
|
+
logger.info(str((l / len(database_list)) * 100) + '%')
|
|
1508
|
+
except:
|
|
1509
|
+
pass
|
|
1510
|
+
|
|
1511
|
+
# Open the database file
|
|
1512
|
+
dbHandler = None
|
|
1513
|
+
try:
|
|
1514
|
+
dbHandler = TAsql(filename_or_connection=db_l)
|
|
1515
|
+
|
|
1516
|
+
# Get all tables in the database
|
|
1517
|
+
tables = dbHandler.get_tables()
|
|
1518
|
+
|
|
1519
|
+
if force_track_cells_db_update:
|
|
1520
|
+
force_update = False
|
|
1521
|
+
|
|
1522
|
+
# Check if 'cell_tracks' table is missing or empty
|
|
1523
|
+
if 'cell_tracks' not in tables or dbHandler.isTableEmpty('cell_tracks'):
|
|
1524
|
+
force_update = True
|
|
1525
|
+
|
|
1526
|
+
if not force_update:
|
|
1527
|
+
try:
|
|
1528
|
+
# Check if 'track_id' column is empty
|
|
1529
|
+
data = dbHandler.run_SQL_command_and_get_results('SELECT track_id FROM cell_tracks LIMIT 1')
|
|
1530
|
+
if data == None or data[0][0] == None:
|
|
1531
|
+
force_update = True
|
|
1532
|
+
except:
|
|
1533
|
+
force_update = True
|
|
1534
|
+
|
|
1535
|
+
if force_update:
|
|
1536
|
+
# Drop 'cell_tracks' table and create a new one
|
|
1537
|
+
dbHandler.drop_table('cell_tracks')
|
|
1538
|
+
dbHandler.execute_command('CREATE TABLE cell_tracks AS SELECT local_id as local_id, CAST(NULL AS INTEGER) AS track_id FROM cells_2D')
|
|
1539
|
+
|
|
1540
|
+
masterDBTables = masterDB.get_tables()
|
|
1541
|
+
|
|
1542
|
+
# Iterate over each table in the database
|
|
1543
|
+
for string in tables:
|
|
1544
|
+
if database_name is not None:
|
|
1545
|
+
if database_name.lower() != string.lower():
|
|
1546
|
+
continue
|
|
1547
|
+
if string not in masterDBTables: # (!masterDBTables.contains(string)) {
|
|
1548
|
+
tableHeaderAndType = dbHandler.get_table_column_names_and_types(string)
|
|
1549
|
+
extra = {}
|
|
1550
|
+
extra[frame_nb] = 'INTEGER'
|
|
1551
|
+
extra[fileName] = 'TEXT'
|
|
1552
|
+
|
|
1553
|
+
for string1 in tableHeaderAndType.keys():
|
|
1554
|
+
extra[string1] = tableHeaderAndType[string1]
|
|
1555
|
+
masterDB.create_table(string, list(extra.keys()), list(extra.values()))
|
|
1556
|
+
|
|
1557
|
+
masterDB.attach_table(db_l, 'tmp') # --> so it is needed then # do I use that ????
|
|
1558
|
+
dbHandler.close()
|
|
1559
|
+
# in case there is a mismatch in the columns then do stuff to fix it
|
|
1560
|
+
|
|
1561
|
+
for string in tables:
|
|
1562
|
+
if database_name is not None:
|
|
1563
|
+
if database_name.lower() != string.lower():
|
|
1564
|
+
continue
|
|
1565
|
+
DB_to_read = 'tmp.' + str(string)
|
|
1566
|
+
cols = masterDB.get_table_column_names_and_types(DB_to_read)
|
|
1567
|
+
cols_master = masterDB.get_table_column_names_and_types(string)
|
|
1568
|
+
|
|
1569
|
+
fixed_cols = set(_to_lower(cols.keys()))
|
|
1570
|
+
fixed_cols.add(frame_nb)
|
|
1571
|
+
fixed_cols.add(fileName)
|
|
1572
|
+
|
|
1573
|
+
fixed_master = set(_to_lower(cols_master.keys()))
|
|
1574
|
+
|
|
1575
|
+
# make it case insensitive
|
|
1576
|
+
cols_master = {k.lower(): v for k, v in
|
|
1577
|
+
cols_master.items()} # TODO maybe replace by a case insensitive dict
|
|
1578
|
+
cols = {k.lower(): v for k, v in cols.items()}
|
|
1579
|
+
|
|
1580
|
+
if not fixed_cols == fixed_master:
|
|
1581
|
+
present_in_master_but_missing_in_cur = fixed_master - fixed_cols
|
|
1582
|
+
present_in_cur_but_missing_in_master = fixed_cols - fixed_master
|
|
1583
|
+
|
|
1584
|
+
if present_in_master_but_missing_in_cur:
|
|
1585
|
+
# Create a temporary table for adding missing columns
|
|
1586
|
+
masterDB.drop_table('pytaTMP')
|
|
1587
|
+
masterDB.execute_command('CREATE TABLE pytaTMP AS SELECT * from ' + str(DB_to_read))
|
|
1588
|
+
|
|
1589
|
+
for col in present_in_master_but_missing_in_cur:
|
|
1590
|
+
# Add missing columns to the temporary table
|
|
1591
|
+
masterDB.add_column('pytaTMP', col, col_type=cols_master[col])
|
|
1592
|
+
|
|
1593
|
+
DB_to_read = 'pytaTMP'
|
|
1594
|
+
|
|
1595
|
+
for col in present_in_cur_but_missing_in_master:
|
|
1596
|
+
# Add missing columns to the masterDB table
|
|
1597
|
+
masterDB.add_column(string, col, col_type=cols[col])
|
|
1598
|
+
|
|
1599
|
+
name = smart_name_parser(lst[l], ordered_output=['short'])[0]
|
|
1600
|
+
|
|
1601
|
+
# Insert data from the database table into the masterDB table
|
|
1602
|
+
masterDB.execute_command(
|
|
1603
|
+
"INSERT INTO '" + str(string) + "' SELECT " + str(l) + " AS '" + str(
|
|
1604
|
+
frame_nb) + "', '" + str(
|
|
1605
|
+
name) + "' AS '" + str(fileName) + "', * FROM " + str(DB_to_read))
|
|
1606
|
+
|
|
1607
|
+
# Drop the temporary table
|
|
1608
|
+
masterDB.drop_table('pytaTMP')
|
|
1609
|
+
|
|
1610
|
+
except:
|
|
1611
|
+
traceback.print_exc()
|
|
1612
|
+
finally:
|
|
1613
|
+
if dbHandler is not None:
|
|
1614
|
+
# Detach the database table and close the connection
|
|
1615
|
+
masterDB.detach_table('tmp')
|
|
1616
|
+
dbHandler.close()
|
|
1617
|
+
|
|
1618
|
+
except:
|
|
1619
|
+
traceback.print_exc()
|
|
1620
|
+
|
|
1621
|
+
finally:
|
|
1622
|
+
# print(outputName)
|
|
1623
|
+
|
|
1624
|
+
# Clean up and close the masterDB if an outputName is provided
|
|
1625
|
+
if masterDB is not None and outputName is not None:
|
|
1626
|
+
masterDB.clean()
|
|
1627
|
+
masterDB.close()
|
|
1628
|
+
else:
|
|
1629
|
+
return masterDB
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
def set_property(db_file, neo_data):
|
|
1633
|
+
"""
|
|
1634
|
+
Sets properties in a database table using data from Neo.
|
|
1635
|
+
|
|
1636
|
+
Args:
|
|
1637
|
+
db_file (str): The path to the database file.
|
|
1638
|
+
neo_data (dict): The dictionary containing the Neo data.
|
|
1639
|
+
|
|
1640
|
+
Returns:
|
|
1641
|
+
None
|
|
1642
|
+
|
|
1643
|
+
Raises:
|
|
1644
|
+
Exception: If an error occurs while inserting new properties.
|
|
1645
|
+
|
|
1646
|
+
# Examples:
|
|
1647
|
+
# >>> db_file = 'data.db'
|
|
1648
|
+
# >>> neo_data = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
|
|
1649
|
+
# >>> set_property(db_file, neo_data)
|
|
1650
|
+
"""
|
|
1651
|
+
if not neo_data:
|
|
1652
|
+
return
|
|
1653
|
+
|
|
1654
|
+
try:
|
|
1655
|
+
db = TAsql(db_file)
|
|
1656
|
+
|
|
1657
|
+
try:
|
|
1658
|
+
if db.exists('properties'):
|
|
1659
|
+
header, cols = db.run_SQL_command_and_get_results('SELECT * FROM properties', return_header=True)
|
|
1660
|
+
cols = cols[0]
|
|
1661
|
+
data = _to_dict(header, cols)
|
|
1662
|
+
data.update(neo_data)
|
|
1663
|
+
else:
|
|
1664
|
+
data = neo_data
|
|
1665
|
+
|
|
1666
|
+
data = {k: [v] for k, v in data.items()}
|
|
1667
|
+
db.create_and_append_table('properties', data)
|
|
1668
|
+
except:
|
|
1669
|
+
traceback.print_exc()
|
|
1670
|
+
print('An error occurred while reading properties from the image or writing them to the database')
|
|
1671
|
+
finally:
|
|
1672
|
+
try:
|
|
1673
|
+
db.close()
|
|
1674
|
+
except:
|
|
1675
|
+
pass
|
|
1676
|
+
except:
|
|
1677
|
+
traceback.print_exc()
|
|
1678
|
+
print('Error could not insert new properties')
|
|
1679
|
+
|
|
1680
|
+
|
|
1681
|
+
# returns the desired property from a TA db for the current file or None if not found
|
|
1682
|
+
def get_property(db_file, property_name):
|
|
1683
|
+
"""
|
|
1684
|
+
Retrieves a property value from a database table.
|
|
1685
|
+
|
|
1686
|
+
Args:
|
|
1687
|
+
db_file (str): The path to the database file.
|
|
1688
|
+
property_name (str): The name of the property to retrieve.
|
|
1689
|
+
|
|
1690
|
+
Returns:
|
|
1691
|
+
str or None: The value of the property, or None if it does not exist or an error occurs.
|
|
1692
|
+
|
|
1693
|
+
# Examples:
|
|
1694
|
+
# >>> db_file = 'data.db'
|
|
1695
|
+
# >>> property_name = 'key1'
|
|
1696
|
+
# >>> result = get_property(db_file, property_name)
|
|
1697
|
+
# >>> print(result)
|
|
1698
|
+
# 'value1'
|
|
1699
|
+
"""
|
|
1700
|
+
val = None
|
|
1701
|
+
db = TAsql(db_file)
|
|
1702
|
+
|
|
1703
|
+
try:
|
|
1704
|
+
val = db.run_SQL_command_and_get_results('SELECT ' + property_name + ' from properties')[0][0]
|
|
1705
|
+
|
|
1706
|
+
if isinstance(val, str) and (val.lower() == 'none' or val.lower() == 'null' or val.strip() == ''):
|
|
1707
|
+
val = None
|
|
1708
|
+
except:
|
|
1709
|
+
# If anything goes wrong or the table or column does not exist, return None
|
|
1710
|
+
pass
|
|
1711
|
+
|
|
1712
|
+
db.close()
|
|
1713
|
+
return val
|
|
1714
|
+
|
|
1715
|
+
|
|
1716
|
+
def get_properties_master_db(lst):
|
|
1717
|
+
"""
|
|
1718
|
+
Retrieves properties from multiple databases and creates a master database.
|
|
1719
|
+
|
|
1720
|
+
Args:
|
|
1721
|
+
lst (list): The list of database names.
|
|
1722
|
+
|
|
1723
|
+
Returns:
|
|
1724
|
+
TAsql: The master database containing the properties.
|
|
1725
|
+
|
|
1726
|
+
# Examples:
|
|
1727
|
+
# >>> lst = ['db1', 'db2', 'db3']
|
|
1728
|
+
# >>> result = get_properties_master_db(lst)
|
|
1729
|
+
# >>> print(result)
|
|
1730
|
+
# <TAsql object at 0x...>
|
|
1731
|
+
"""
|
|
1732
|
+
database_list = smart_TA_list(lst, 'pyTA.db')
|
|
1733
|
+
|
|
1734
|
+
if database_list is not None:
|
|
1735
|
+
for db_file in database_list:
|
|
1736
|
+
db = TAsql(db_file)
|
|
1737
|
+
|
|
1738
|
+
if not 'properties' in db.get_tables():
|
|
1739
|
+
db.create_table('properties', ['voxel_z_over_x_ratio', 'time'], ['float', 'float'])
|
|
1740
|
+
db.execute_command("INSERT INTO properties ('voxel_z_over_x_ratio', 'time') "
|
|
1741
|
+
"SELECT NULL, NULL "
|
|
1742
|
+
"WHERE NOT EXISTS (SELECT * FROM properties)"
|
|
1743
|
+
)
|
|
1744
|
+
else:
|
|
1745
|
+
if db.isTableEmpty('properties'):
|
|
1746
|
+
db.execute_command("INSERT INTO properties ('voxel_z_over_x_ratio', 'time') "
|
|
1747
|
+
"SELECT NULL, NULL "
|
|
1748
|
+
"WHERE NOT EXISTS (SELECT * FROM properties)"
|
|
1749
|
+
)
|
|
1750
|
+
|
|
1751
|
+
db.close()
|
|
1752
|
+
|
|
1753
|
+
database = createMasterDB(lst, database_name='properties')
|
|
1754
|
+
|
|
1755
|
+
return database
|
|
1756
|
+
|
|
1757
|
+
def reinject_properties_to_TA_files(lst, master_db, indices_to_update=None):
|
|
1758
|
+
"""
|
|
1759
|
+
Reinjects properties from the master database to individual TA files.
|
|
1760
|
+
|
|
1761
|
+
Args:
|
|
1762
|
+
lst (list): The list of TA file names.
|
|
1763
|
+
master_db (TAsql): The master database containing the properties.
|
|
1764
|
+
indices_to_update (list or None): The indices of the TA files to update. If None, update all files.
|
|
1765
|
+
|
|
1766
|
+
Returns:
|
|
1767
|
+
None
|
|
1768
|
+
|
|
1769
|
+
# Examples:
|
|
1770
|
+
# >>> lst = ['db1', 'db2', 'db3']
|
|
1771
|
+
# >>> master_db = TAsql('master.db')
|
|
1772
|
+
# >>> indices_to_update = [0, 2]
|
|
1773
|
+
# >>> reinject_properties_to_TA_files(lst, master_db, indices_to_update)
|
|
1774
|
+
"""
|
|
1775
|
+
database_list = smart_TA_list(lst, 'pyTA.db')
|
|
1776
|
+
|
|
1777
|
+
for iii, db_file in enumerate(database_list):
|
|
1778
|
+
if indices_to_update is not None:
|
|
1779
|
+
if iii not in indices_to_update:
|
|
1780
|
+
continue
|
|
1781
|
+
|
|
1782
|
+
master_db.attach_table(db_file, 'tmp')
|
|
1783
|
+
|
|
1784
|
+
master_db.execute_command('DROP TABLE IF EXISTS tmp.properties')
|
|
1785
|
+
|
|
1786
|
+
master_db.execute_command('CREATE TABLE tmp.properties AS SELECT ' +
|
|
1787
|
+
master_db.EXCEPT('properties', *['frame_nb', 'filename']) +
|
|
1788
|
+
' FROM properties WHERE frame_nb =' + str(iii))
|
|
1789
|
+
|
|
1790
|
+
master_db.execute_command('DETACH DATABASE tmp')
|
|
1791
|
+
|
|
1792
|
+
# TODO also I would need handle polarity smarty but keep it channels --> TODO --> treat all of them as a single piece of data
|
|
1793
|
+
|
|
1794
|
+
def populate_table_content(db_name, prepend='#', filtered_out_columns=['x', 'y', 'first_pixel', 'local_ID', 'pixel_within_cell', 'centroid', 'vx', 'cell_id', 'pixel_within_', 'perimeter_pixel_count', 'bond_cut_off', 'vertices', 'bonds', 'pixel_count']):
|
|
1795
|
+
"""
|
|
1796
|
+
Populates the content of a table in a database.
|
|
1797
|
+
|
|
1798
|
+
Args:
|
|
1799
|
+
db_name (str): The name of the database file.
|
|
1800
|
+
prepend (str): The string to prepend to each table and column name. Default is '#'.
|
|
1801
|
+
filtered_out_columns (list): The list of column names to filter out. Default is a list of column names.
|
|
1802
|
+
|
|
1803
|
+
Returns:
|
|
1804
|
+
list: The list of table and column names.
|
|
1805
|
+
|
|
1806
|
+
# Examples:
|
|
1807
|
+
# >>> db_name = 'data.db'
|
|
1808
|
+
# >>> prepend = '#'
|
|
1809
|
+
# >>> filtered_out_columns = ['x', 'y', 'first_pixel', 'local_ID']
|
|
1810
|
+
# >>> result = populate_table_content(db_name, prepend, filtered_out_columns)
|
|
1811
|
+
# >>> print(result)
|
|
1812
|
+
# ['#table1.column1', '#table1.column2', '#table2.column1', '#table2.column2']
|
|
1813
|
+
"""
|
|
1814
|
+
if not os.path.isfile(db_name):
|
|
1815
|
+
return None
|
|
1816
|
+
|
|
1817
|
+
table_content = []
|
|
1818
|
+
db = None
|
|
1819
|
+
|
|
1820
|
+
try:
|
|
1821
|
+
db = TAsql(db_name)
|
|
1822
|
+
tables = db.get_tables()
|
|
1823
|
+
|
|
1824
|
+
for table in tables:
|
|
1825
|
+
columns = db.get_table_column_names_and_types(table, return_colnames_only=True)
|
|
1826
|
+
cols_to_remove = []
|
|
1827
|
+
|
|
1828
|
+
if filtered_out_columns is not None:
|
|
1829
|
+
for filter in filtered_out_columns:
|
|
1830
|
+
for col in columns:
|
|
1831
|
+
if col.startswith(filter) or col == filter:
|
|
1832
|
+
cols_to_remove.append(col)
|
|
1833
|
+
|
|
1834
|
+
columns = [col for col in columns if col not in cols_to_remove]
|
|
1835
|
+
|
|
1836
|
+
for col in columns:
|
|
1837
|
+
table_content.append(('' if prepend is None else str(prepend)) + str(table) + '.' + str(col))
|
|
1838
|
+
except:
|
|
1839
|
+
traceback.print_exc()
|
|
1840
|
+
logger.error('Error populating the database')
|
|
1841
|
+
finally:
|
|
1842
|
+
if db is not None:
|
|
1843
|
+
db.close()
|
|
1844
|
+
|
|
1845
|
+
return table_content
|
|
1846
|
+
|
|
1847
|
+
|
|
1848
|
+
# do a sql class to store all TA data --> see what is the best way to add all
|
|
1849
|
+
# maybe do different classes
|
|
1850
|
+
# maybe a map with each column title and a corresponding array of values would be a good idea in fact to get started
|
|
1851
|
+
# or a row per row stuff and a header is also interesting --> really give it a try
|
|
1852
|
+
# maybe also a typing of the table is a good idea
|
|
1853
|
+
if __name__ == '__main__':
|
|
1854
|
+
import sys
|
|
1855
|
+
|
|
1856
|
+
if True:
|
|
1857
|
+
columns = ['id', 'fruit']
|
|
1858
|
+
data = np.array( [['1', 'apple'], ['2', 'banana'], ['3', 'cherry']])
|
|
1859
|
+
column_types = ['INTEGER', 'TEXT']
|
|
1860
|
+
create_table_and_append_data("mydb.db", "fruits", columns, data, column_types=column_types, temporary=False)
|
|
1861
|
+
sys.exit(0)
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
if True:
|
|
1865
|
+
table_to_xlsx_with_sheets('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/Sarah/kniR-405_knrlR-488_kniD-565_kniD-633/Image 23_Stitch/FISH.db', '/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/Sarah/kniR-405_knrlR-488_kniD-565_kniD-633/Image 23_Stitch/FISH.xlsx')
|
|
1866
|
+
|
|
1867
|
+
|
|
1868
|
+
sys.exit(0)
|
|
1869
|
+
|
|
1870
|
+
if True:
|
|
1871
|
+
|
|
1872
|
+
# print(os.path.exists('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/Benoit R1 R6/R1 565 R6 633 f W5 NS3/FISHcopie.db'))
|
|
1873
|
+
remove_dupes_from_table_and_overwrite_table('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/Benoit R1 R6/R1 565 R6 633 f W5 NS3/FISH (copie).db', 'human_curated_distances_3D')
|
|
1874
|
+
remove_dupes_from_table_and_overwrite_table('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/Benoit R1 R6/R1 565 R6 633 f W5 NS3/FISH (copie).db', 'human_curated_distances_3D_chromatic_aberrations_corrected')
|
|
1875
|
+
|
|
1876
|
+
|
|
1877
|
+
sys.exit(0)
|
|
1878
|
+
|
|
1879
|
+
if False:
|
|
1880
|
+
print(table_exists_in_db('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/tmp/R1 565 R6 633 f S1/FISH.db', 'human_curated_distances_3D'))
|
|
1881
|
+
|
|
1882
|
+
print(table_exists_in_db('/E/Sample_images/FISH/Sample_transcription_dot_detection/manue_manually_segmented_images/training_set/tests/full_real_analysis_tst1/tmp/R1 565 R6 633 f S1/FISH.db', 'breaking_bad'))
|
|
1883
|
+
|
|
1884
|
+
sys.exit(0)
|
|
1885
|
+
|
|
1886
|
+
# --> could create the master db and then even further filter it
|
|
1887
|
+
# can I also do ins by sets --> think about it
|
|
1888
|
+
|
|
1889
|
+
# NB in is same as
|
|
1890
|
+
#SELECT * FROM employees WHERE employee_id = 1 OR employee_id = 2 OR employee_id = 3 OR employee_id = 4;
|
|
1891
|
+
# SELECT * FROM employees WHERE first_name NOT IN ('Sarah', 'Jessica'); # maybe useful too
|
|
1892
|
+
|
|
1893
|
+
if False:
|
|
1894
|
+
db = TAsql('/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012/pyTA.db')
|
|
1895
|
+
# print(db.create_filtered_query('SELECT * from cells_2D', filtering_elements=[1,2,120],filter_name='local_ID'))
|
|
1896
|
+
print(db.create_filtered_query('SELECT * from cells_2D', filtering_elements_dict={'local_ID':[1,2,120]}))
|
|
1897
|
+
print(db.create_filtered_query('SELECT * from cells_2D', filtering_elements_dict={'local_ID':[1,2,120], 'cytoplasmic_area':[802]}))
|
|
1898
|
+
db.close()
|
|
1899
|
+
|
|
1900
|
+
sys.exit(0)
|
|
1901
|
+
|
|
1902
|
+
if False:
|
|
1903
|
+
new_properties = {'reg_x':16, 'reg_y':-32}
|
|
1904
|
+
set_property(
|
|
1905
|
+
'/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012/pyTA.db',
|
|
1906
|
+
new_properties) # ça marche!!!
|
|
1907
|
+
print(get_property(
|
|
1908
|
+
'/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012/pyTA.db',
|
|
1909
|
+
'reg_x') + 1) # ça marche!!!
|
|
1910
|
+
sys.exit(0)
|
|
1911
|
+
|
|
1912
|
+
if False:
|
|
1913
|
+
lst = {'toto':'tutu', 'tata':None}
|
|
1914
|
+
print(', '.join((str(x) + ' ' + str(y)) if y is not None else str(x) + ' ' + 'Null' for x, y in lst.items()))
|
|
1915
|
+
import sys
|
|
1916
|
+
sys.exit(0)
|
|
1917
|
+
|
|
1918
|
+
lst = [0, None, 123]
|
|
1919
|
+
print(_list_to_string(lst))
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
print(', '.join(map(str, lst)))
|
|
1923
|
+
print(', '.join(map(lambda x: str(x) if x is not None else 'Null', lst)))
|
|
1924
|
+
print("'" + '\', \''.join(map(lambda x: str(x) if x is not None else 'Null', lst)) + "'")
|
|
1925
|
+
add_quotes = True
|
|
1926
|
+
print(', '.join(map(lambda x: "'"+str(x)+"'" if x is not None and add_quotes else str(x) if x is not None and not add_quotes else 'Null', lst)))
|
|
1927
|
+
|
|
1928
|
+
|
|
1929
|
+
if False:
|
|
1930
|
+
# use these properties to compute the real 3D area values --> maybe also store pc data in this
|
|
1931
|
+
|
|
1932
|
+
print(get_property('/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012/pyTA.db', 'time')+1) # ça marche!!!
|
|
1933
|
+
print(get_property('/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012/pyTA.db', 'voxel_z_over_x_ratio'))
|
|
1934
|
+
import sys
|
|
1935
|
+
sys.exit(0)
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
if True:
|
|
1939
|
+
# do a real mount maybe ???
|
|
1940
|
+
|
|
1941
|
+
# all seems ok and in many aspects simpler than the java equivalent...
|
|
1942
|
+
# sql_file = '/E/Sample_images/sample_images_PA/mini/test.db'
|
|
1943
|
+
# sql_file = '/run/user/1000/gvfs/smb-share:server=teamdfs2,share=teamprudhomme/EqpPrudHomme2/To be analyzed/221123 R1y R6y/R1 565 R6 633 f S3/test.db' # test for sqlite on samba --> there is a bug on a samba drive --> how can I fix that
|
|
1944
|
+
sql_file = '/media/eqpPrudhomme/EqpPrudHomme2/To be analyzed/test.db' # FINALLY GOT IT TO WORK --> use the nobrl option in mount CIFS # test for sqlite on samba --> there is a bug on a samba drive --> how can I fix that # TO GET IT TO WORK I NEED THE 'NOBRL' command !!!
|
|
1945
|
+
table_name = 'test'
|
|
1946
|
+
table_columns = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
|
|
1947
|
+
table_cols_and_type = {'zzz': None, 'aaa': 'DOUBLE', 'bbb': 'INT',
|
|
1948
|
+
'ccc': 'TEXT'} # see all the types that exist and stick to that!!!
|
|
1949
|
+
|
|
1950
|
+
one_row = [None, 10, 123., True, 'toto']
|
|
1951
|
+
|
|
1952
|
+
print(_list_to_string(['10', '10.0', '20', 'TEXT1']))
|
|
1953
|
+
|
|
1954
|
+
# magic table
|
|
1955
|
+
table_magic = {'zzz': [10, 20], 'aaa': [10., 22.], 'bbb': [20, 30],
|
|
1956
|
+
'ccc': ['TEXT1', 'TEXT2']}
|
|
1957
|
+
|
|
1958
|
+
print(_list_to_string(table_columns), table_columns)
|
|
1959
|
+
print(_list_to_string(table_cols_and_type), table_cols_and_type)
|
|
1960
|
+
|
|
1961
|
+
print('line below will generate an error!')
|
|
1962
|
+
print(get_types_from_data(one_row))
|
|
1963
|
+
|
|
1964
|
+
# now really create a table and then fill it --> TODO
|
|
1965
|
+
|
|
1966
|
+
# test of all
|
|
1967
|
+
db = TAsql(sql_file) # maybe if file is none --> store in mem ???
|
|
1968
|
+
db.create_and_append_table(table_name=table_name, datas=table_magic)
|
|
1969
|
+
# create and append table
|
|
1970
|
+
|
|
1971
|
+
data = db.run_SQL_command_and_get_results('SELECT * FROM test WHERE ccc == "TEXT2"')
|
|
1972
|
+
print(data) # --> just gives me the data row by row --> probably pandas offers me more flexibility!!!
|
|
1973
|
+
|
|
1974
|
+
headers = db.get_table_header('test2')
|
|
1975
|
+
print('headers', headers)
|
|
1976
|
+
|
|
1977
|
+
headers = db.get_table_header('test')
|
|
1978
|
+
print('headers', headers)
|
|
1979
|
+
|
|
1980
|
+
print(db.exists('test 2312'))
|
|
1981
|
+
print(db.exists('test'))
|
|
1982
|
+
|
|
1983
|
+
print('list of tables', db.get_tables())
|
|
1984
|
+
# [('table1',), ('table2',), ('table3',)]
|
|
1985
|
+
|
|
1986
|
+
# sqlite3.Warning: You can only execute one statement at a time. -> see how I can concatenate the stuff
|
|
1987
|
+
# maybe just return the last data --> does make sense in fact
|
|
1988
|
+
# --> split the command and execute it
|
|
1989
|
+
print('test multi', db.run_SQL_command_and_get_results('SELECT * FROM test;'
|
|
1990
|
+
'SELECT "aaa" FROM test;')) # bug
|
|
1991
|
+
|
|
1992
|
+
print('get col', db.get_column('test', 'aaa'), type(db.get_column('test', 'aaa')[
|
|
1993
|
+
0])) # --> the type is correct --> so in theory no need for casting but maybe if wrong type then it is needed
|
|
1994
|
+
|
|
1995
|
+
# can use the stuff
|
|
1996
|
+
# can use the table headers TODO plots --> in fact that would be easy TODO I think
|
|
1997
|
+
# --> can easily add anything as a graph if I need it
|
|
1998
|
+
# add an edit for the images at the very end in the preview --> see how I can do that --> would be good if cell divisions and death could be edited there --> but need keep the cell relationship --> in fact easy just delete the table
|
|
1999
|
+
|
|
2000
|
+
# print type
|
|
2001
|
+
|
|
2002
|
+
# can do automatic plots for all
|
|
2003
|
+
# and maybe also offer color coding for all # --> either local or global --> in fact all is easy todo with my tool now!!!
|
|
2004
|
+
|
|
2005
|
+
# for some columns the plots should be specific for others
|
|
2006
|
+
|
|
2007
|
+
db.add_column('test', 'uuu', default_value=None)
|
|
2008
|
+
print(db.get_table_column_names_and_types('test'))
|
|
2009
|
+
|
|
2010
|
+
db.get_min_max('test', 'aaa')
|
|
2011
|
+
|
|
2012
|
+
db.clean()
|
|
2013
|
+
|
|
2014
|
+
db.close()
|
|
2015
|
+
|
|
2016
|
+
# try create a table
|
|
2017
|
+
# maybe also need typing of the data --> try
|
|
2018
|
+
|
|
2019
|
+
import sys
|
|
2020
|
+
sys.exit(0)
|
|
2021
|
+
|
|
2022
|
+
if False:
|
|
2023
|
+
values = ['10', 20, 30, None, '10.2', '0.1', '0', 5]
|
|
2024
|
+
convert_digit_to_number = True
|
|
2025
|
+
if convert_digit_to_number:
|
|
2026
|
+
non_str = [val for val in values if not isinstance(val, str)]
|
|
2027
|
+
st = [val for val in values if isinstance(val, str)]
|
|
2028
|
+
st = [float(val) for val in st if val.isdigit()] # ok but it does change the order --> should I care
|
|
2029
|
+
|
|
2030
|
+
# could convert to None all the data that cannot be processed!!!
|
|
2031
|
+
|
|
2032
|
+
print(values)
|
|
2033
|
+
print(non_str)
|
|
2034
|
+
print(st)
|
|
2035
|
+
|
|
2036
|
+
# non_str2 = [val if not isinstance(val, str) else float(val) if val.isdigit() for val in values]
|
|
2037
|
+
# do it all manually cause too complex with list comprehension
|
|
2038
|
+
# a way of converting str to int
|
|
2039
|
+
for iii, v in enumerate(values):
|
|
2040
|
+
if isinstance(v, str):
|
|
2041
|
+
# if v.isdigit():
|
|
2042
|
+
# values[iii]=float(v)
|
|
2043
|
+
# else:
|
|
2044
|
+
# values[iii] = None
|
|
2045
|
+
# this one is better as it should keep the type
|
|
2046
|
+
if '.' in v:
|
|
2047
|
+
try:
|
|
2048
|
+
values[iii] = float(v)
|
|
2049
|
+
except ValueError:
|
|
2050
|
+
values[iii] = None
|
|
2051
|
+
else:
|
|
2052
|
+
try:
|
|
2053
|
+
values[iii] = int(v)
|
|
2054
|
+
except ValueError:
|
|
2055
|
+
values[iii] = None
|
|
2056
|
+
|
|
2057
|
+
print(values)
|
|
2058
|
+
|
|
2059
|
+
# pbs will occur when files are deleted in preview --> prevent delete button from working
|
|
2060
|
+
# TODO populate the other based on that
|
|
2061
|
+
# concat table name to the column name
|
|
2062
|
+
# assume everything can be plotted directly except a few columns that I would name and plot differently --> TODO
|
|
2063
|
+
# add correspondance local cell id and track ID to the database
|
|
2064
|
+
if True:
|
|
2065
|
+
# nb some images have different nb of cols --> does not fit...
|
|
2066
|
+
# masterDB = createMasterDB(['/E/Sample_images/sample_images_PA/mini/focused_Series012.png','/E/Sample_images/sample_images_PA/mini/focused_Series014.png','/E/Sample_images/sample_images_PA/mini/focused_Series015.png','/E/Sample_images/sample_images_PA/mini/focused_Series016.png'])
|
|
2067
|
+
# masterDB = createMasterDB(
|
|
2068
|
+
# ['/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series012.png',
|
|
2069
|
+
# '/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series014.png',
|
|
2070
|
+
# '/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series015.png',
|
|
2071
|
+
# '/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series016.png',
|
|
2072
|
+
# '/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/focused_Series019.tif'])
|
|
2073
|
+
|
|
2074
|
+
# masterDB = createMasterDB(loadlist('/E/Sample_images/sample_images_PA/mini_different_nb_of_channels/list.lst'))
|
|
2075
|
+
|
|
2076
|
+
|
|
2077
|
+
outputName = None
|
|
2078
|
+
# outputName = '/E/Sample_images/sample_images_pyta/surface_projection/masterDB.db'
|
|
2079
|
+
masterDB = createMasterDB(loadlist('/E/Sample_images/sample_images_pyta/surface_projection/list.lst'), outputName=outputName, force_track_cells_db_update=True)
|
|
2080
|
+
|
|
2081
|
+
|
|
2082
|
+
if masterDB is not None:
|
|
2083
|
+
# masterDB = createMasterDB(['/E/Sample_images/sample_images_PA/mini/focused_Series012.png','/E/Sample_images/sample_images_PA/mini/focused_Series012.png','/E/Sample_images/sample_images_PA/mini/focused_Series012.png','/E/Sample_images/sample_images_PA/mini/focused_Series012.png'])
|
|
2084
|
+
print(masterDB.get_tables()) # why None
|
|
2085
|
+
# print(masterDB.run_SQL_command_and_get_results('SELECT * FROM cells_2D;')) # that seems to work
|
|
2086
|
+
# print(masterDB.get_min_max(table_name, 'area'))
|
|
2087
|
+
# print(masterDB.get_min_max(table_name, 'area', freq=[0.15,0.05])) # 0.1 --> est ce que ce sont des pourcentages ??? --> dois-je changer ma conversion --> sinon 10% --> may be a lot in fact --> ij fact ok
|
|
2088
|
+
# print(masterDB.get_table_column_names_and_types('bonds_2D'))
|
|
2089
|
+
|
|
2090
|
+
# print(masterDB.print_query('SELECT * FROM vertices_2D;'))
|
|
2091
|
+
# print(masterDB.print_query('SELECT * FROM cells_2D;'))
|
|
2092
|
+
# print(masterDB.print_query('SELECT frame_nb, filename, local_ID, cytoplasmic_area, area FROM cells_2D;'))
|
|
2093
|
+
# print(masterDB.print_query('SELECT frame_nb, filename, local_ID, sum_px_int_vertices_included_ch1, sum_px_int_vertices_included_ch3 FROM bonds_2D;')) # amyebe there is a bug in the db --> really need fix it, it should contain None and it does not --> I am doing a mistake --> fix it!!!
|
|
2094
|
+
# print(masterDB.get_table_column_names_and_types('cells_2D'))
|
|
2095
|
+
# print(masterDB.get_table_column_names_and_types('bonds_2D')) #
|
|
2096
|
+
|
|
2097
|
+
# print(masterDB.print_query('SELECT * FROM cells_2D NATURAL JOIN properties LIMIT 1'))
|
|
2098
|
+
|
|
2099
|
+
|
|
2100
|
+
# masterDB.drop_table('properties')
|
|
2101
|
+
# surprising that seems to work --> what if 0 DB inside
|
|
2102
|
+
# print(masterDB.print_query('SELECT * FROM cells_2D NATURAL JOIN properties')) # ça a l'air de marcher mais va t'il y avoir des bugs ??? sinon facile à gerer je pense
|
|
2103
|
+
|
|
2104
|
+
# how to make it work if the table does not exist at all --> did I take this into account --> # can I null it everywhere ????
|
|
2105
|
+
|
|
2106
|
+
# print(masterDB.print_query('SELECT * FROM cell_tracks NATURAL JOIN properties'))
|
|
2107
|
+
# print(masterDB.print_query('SELECT * FROM cell_tracks'))
|
|
2108
|
+
print(masterDB.print_query('SELECT * FROM cells_2D NATURAL JOIN cells_3D NATURAL JOIN cell_tracks NATURAL JOIN properties')) # ça a l'air de marcher mais va t'il y avoir des bugs ??? sinon facile à gerer je pense
|
|
2109
|
+
|
|
2110
|
+
|
|
2111
|
+
# print(masterDB.print_query('SELECT * FROM cells_2D NATURAL JOIN properties')) # ça a l'air de marcher mais va t'il y avoir des bugs ??? sinon facile à gerer je pense
|
|
2112
|
+
|
|
2113
|
+
print(masterDB.get_min_max('bonds_2D', 'sum_px_int_vertices_included_ch1')) # see how to exclude None values
|
|
2114
|
+
print(masterDB.get_min_max('bonds_2D', 'sum_px_int_vertices_included_ch1',
|
|
2115
|
+
ignore_None_and_string=False)) # see how to exclude None values
|
|
2116
|
+
print(masterDB.get_min_max('bonds_2D', 'sum_px_int_vertices_included_ch1', freq=[0.01, 0.01],
|
|
2117
|
+
ignore_None_and_string=False)) # see how to exclude None values
|
|
2118
|
+
print(masterDB.get_min_max('bonds_2D', 'sum_px_int_vertices_included_ch1', freq=[0.3, 0.01],
|
|
2119
|
+
ignore_None_and_string=False)) # see how to exclude None values
|
|
2120
|
+
print(masterDB.get_min_max('bonds_2D', 'sum_px_int_vertices_included_ch1', freq=[0.01, 0.01],
|
|
2121
|
+
ignore_None_and_string=True)) # see how to exclude None values
|
|
2122
|
+
# any way to print this ???
|
|
2123
|
+
|
|
2124
|
+
|
|
2125
|
+
# can I save an in mem table --> No
|
|
2126
|
+
|
|
2127
|
+
masterDB.close()
|
|
2128
|
+
|
|
2129
|
+
if True:
|
|
2130
|
+
sql_file = '/E/Sample_images/sample_images_PA/mini/focused_Series012/pyTA.db'
|
|
2131
|
+
|
|
2132
|
+
print(populate_table_content(sql_file))
|
|
2133
|
+
|
|
2134
|
+
table_name = 'cells_2D'
|
|
2135
|
+
|
|
2136
|
+
db = TAsql(sql_file) # maybe if file is none --> store in mem ???
|
|
2137
|
+
db.get_min_max(table_name, 'area')
|
|
2138
|
+
db.get_min_max(table_name, 'area', freq=[0.1, 0.1])
|
|
2139
|
+
print(db.get_table_column_names_and_types(table_name)) # what if two cols have same name ???
|
|
2140
|
+
db.clean()
|
|
2141
|
+
db.close()
|