Skip to content

Commit 249e32f

Browse files
committed
add manually experiment record page, improve UI, many bugs fixed
1 parent 863c026 commit 249e32f

File tree

15 files changed

+847
-141
lines changed

15 files changed

+847
-141
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ pyerm.egg-info
44
__pycache__
55
*.DS_Store
66
.vscode
7-
*.baiduyun.*
7+
*.baiduyun.*
8+
examples/experiments.db

examples/example.ipynb

Lines changed: 36 additions & 42 deletions
Large diffs are not rendered by default.

pyerm/database/dbbase.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23-
# Version: 0.3.3
23+
# Version: 0.3.5
2424

2525
import sqlite3
2626
import re
2727

2828
class Database:
29-
def __init__(self, db_path:str, output_info=True) -> None:
29+
def __init__(self, db_path:str, output_info=False) -> None:
3030
self.db_path = db_path
3131
self.info = output_info
3232
self.conn = sqlite3.connect(db_path)
@@ -56,7 +56,10 @@ def __len__(self):
5656
return len(self.table_names)
5757

5858
def __del__(self):
59-
self.cursor.close()
59+
try:
60+
self.cursor.close()
61+
except:
62+
pass
6063
self.conn.close()
6164

6265
def get_table(self, table_name:str):

pyerm/database/experiment.py

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23-
# Version: 0.3.2
23+
# Version: 0.3.5
2424

2525
import os
2626
import typing
@@ -92,7 +92,7 @@ def __init__(self, db_path:str=None):
9292
self._method_id = None
9393
self._task = None
9494

95-
def experiment_start(self, description:str=None, start_time:float=None, tags:typing.Union[typing.List[str], str]=None, experimenters:typing.Union[typing.List[str], str]=None) -> int:
95+
def experiment_start(self, description:str=None, start_time:float=None, tags:typing.Union[typing.List[str], str]=None, experimenters:typing.Union[typing.List[str], str]=None, remark:str=None) -> int:
9696
"""
9797
Start an experiment, and record the experiment information in the database
9898
@@ -105,11 +105,13 @@ def experiment_start(self, description:str=None, start_time:float=None, tags:typ
105105
description : str, optional
106106
The description of the experiment, by default None, which means the description is empty
107107
start_time : float, optional
108-
The start time of the experiment, by default None, which means the current time
108+
The start timestamp of the experiment, by default None, which means the current time, "" means the start time is empty
109109
tags : str, optional
110110
The tags of the experiment, by default None
111111
experimenters : str, optional
112112
The experimenters of the experiment, by default None
113+
remark : str, optional
114+
The remark of the experiment, by default None, can't be positive int number
113115
114116
Returns
115117
-------
@@ -124,11 +126,12 @@ def handle_exception(exc_type, exc_value, exc_traceback):
124126
assert self._data is not None, 'Data not initialized, run data_init() first'
125127
assert self._method is not None, 'Method not initialized, run method_init() first'
126128
assert self._task is not None, 'Task not initialized, run task_init() first'
129+
assert remark is None or not remark.isdigit(), 'Remark cannot be positive int number'
127130
if tags is not None and isinstance(tags, typing.List):
128131
tags = ','.join(tags)
129132
if experimenters is not None and isinstance(experimenters, typing.List):
130133
experimenters = ','.join(experimenters)
131-
self._id = self.experiment_table.experiment_start(description, self._method, self._method_id, self._data, self._data_id, self._task, start_time, tags, experimenters)
134+
self._id = self.experiment_table.experiment_start(description, self._method, self._method_id, self._data, self._data_id, self._task, start_time, tags, experimenters, remark)
132135
self.run_times += 1
133136
sys.excepthook = handle_exception
134137
return self._id
@@ -149,7 +152,7 @@ def experiment_over(self, rst_dict:typing.Dict[str, typing.Any], image_dict:typi
149152
image_dict : typing.Dict[str, typing.Union[Image.Image, str, bytearray, bytes]], optional
150153
The image dictionary, contains the image data of the experiment, such as {'image1': Image.open('1.png'), 'image2': '2.png'}, by default no image
151154
end_time : float, optional
152-
The end time of the experiment, by default None, which means the current time
155+
The end timestamp of the experiment, by default None, which means the current time, "" means the end time is empty
153156
useful_time_cost : float, optional
154157
The useful time cost of the experiment, by default None, used to record the user-defined time cost
155158
@@ -181,7 +184,7 @@ def experiment_failed(self, error_info:str, end_time:float=None) -> None:
181184
The error information of the experiment
182185
183186
end_time : float, optional
184-
The end time of the experiment, by default None, which means the current time
187+
The end time of the experiment, by default None, which means the current time, "" means the end time is empty
185188
186189
"""
187190
assert self._id is not None, 'Experiment not started, run experiment_start() first'
@@ -214,7 +217,7 @@ def detail_update(self, detail_dict:typing.Dict[str, typing.Any]):
214217
self.detail_table = DetailTable(self._db, self._id, detail_def_dict)
215218
self.detail_table.insert(experiment_id=self._id, **detail_dict)
216219

217-
def data_init(self, data_name:str, param_dict:typing.Dict[str, typing.Any]=None, param_def_dict:typing.Dict[str, str]=None):
220+
def data_init(self, data_name:str, param_dict:typing.Dict[str, typing.Any]={}, param_def_dict:typing.Dict[str, str]=None, remark:str=None):
218221
"""
219222
Initialize the data table, and insert the data information into the database, such as the dataset preproessing parameters, etc.
220223
@@ -225,31 +228,36 @@ def data_init(self, data_name:str, param_dict:typing.Dict[str, typing.Any]=None,
225228
data_name : str
226229
The name of the data table, such as 'data'
227230
param_dict : typing.Dict[str, typing.Any], optional
228-
The parameter dictionary, contains the data information, by default None
231+
The parameter dictionary, contains the data information, by default empty dict
229232
param_def_dict : typing.Dict[str, str], optional
230233
The parameter definition dictionary, contains the data parameter definition, by default None, which means the parameter definition will be automatically detected
231-
234+
remark : str, optional
235+
The remark of the data setting, by default None, can't be positive int number
236+
232237
Returns
233238
-------
234239
int
235240
The data ID
236241
237242
"""
238-
assert " " not in data_name, 'Data name cannot contain space'
243+
# assert " " not in data_name, 'Data name cannot contain space'
239244
assert param_def_dict is None or len(param_def_dict) == len(param_dict), 'Parameter definition and parameter dict length mismatch'
245+
assert remark is None or not remark.isdigit(), 'Remark cannot be positive int number'
246+
data_name = data_name.replace(' ', '_')
240247
self._data = data_name
241248
param_dict = deepcopy(param_dict)
242249
if len(param_dict) == 0:
243250
self._data_id = -1
244251
print(f"No parameter for table data_{data_name}, table creating canceled")
245252
return
253+
param_dict['remark'] = remark
246254
if param_def_dict is None:
247255
param_def_dict = auto_detect_def(param_dict)
248256
self.data_table = DataTable(self._db, data_name, param_def_dict)
249257
self._data_id = self.data_table.insert(**param_dict)
250258
return self._data_id
251259

252-
def method_init(self, method_name:str, param_dict:typing.Dict[str, typing.Any]=None, param_def_dict:typing.Dict[str, str]=None, detail_def_dict:typing.Dict[str, str]=None):
260+
def method_init(self, method_name:str, param_dict:typing.Dict[str, typing.Any]={}, param_def_dict:typing.Dict[str, str]=None, detail_def_dict:typing.Dict[str, str]=None, remark:str=None) -> int:
253261
"""
254262
Initialize the method table, and insert the method information into the database, such as the method parameters, etc.
255263
@@ -260,18 +268,24 @@ def method_init(self, method_name:str, param_dict:typing.Dict[str, typing.Any]=N
260268
method_name : str
261269
The name of the method table, such as 'method'
262270
param_dict : typing.Dict[str, typing.Any], optional
263-
The parameter dictionary, contains the method information, by default None
271+
The parameter dictionary, contains the method information, by default empty dict
264272
param_def_dict : typing.Dict[str, str], optional
265273
The parameter definition dictionary, contains the method parameter definition, by default None, which means the parameter definition will be automatically detected
266-
274+
detail_def_dict : typing.Dict[str, str], optional
275+
The detail definition dictionary, contains the detail parameter definition, by default None
276+
remark : str, optional
277+
The remark of the method setting, by default None, can't be positive int number
278+
267279
Returns
268280
-------
269281
int
270282
The method ID
271283
272284
"""
273-
assert " " not in method_name, 'Method name cannot contain space'
285+
# assert " " not in method_name, 'Method name cannot contain space'
274286
assert param_def_dict is None or len(param_def_dict) == len(param_dict), 'Parameter definition and parameter dict length mismatch'
287+
assert remark is None or not remark.isdigit(), 'Remark cannot be positive int number'
288+
method_name = method_name.replace(' ', '_')
275289
self._method = method_name
276290
param_dict = deepcopy(param_dict)
277291
if detail_def_dict is not None:
@@ -280,6 +294,7 @@ def method_init(self, method_name:str, param_dict:typing.Dict[str, typing.Any]=N
280294
self._method_id = -1
281295
print(f"No parameter for table method_{method_name}, table creating canceled")
282296
return
297+
param_dict['remark'] = remark
283298
if param_def_dict is None:
284299
param_def_dict = auto_detect_def(param_dict)
285300
self.method_table = MethodTable(self._db, method_name, param_def_dict)
@@ -301,7 +316,8 @@ def task_init(self, task_name:str, rst_def_dict:typing.Dict[str, str]=None):
301316
The result definition dictionary, contains the result parameter definition, by default None, which means the parameter definition will be automatically detected when the result is recorded
302317
303318
"""
304-
assert " " not in task_name, 'Task name cannot contain space'
319+
# assert " " not in task_name, 'Task name cannot contain space'
320+
task_name = task_name.replace(' ', '_')
305321
self._task = task_name
306322
if rst_def_dict is not None:
307323
self.rst_table = ResultTable(self._db, task_name, rst_def_dict)

pyerm/database/tables.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23-
# Version: 0.3.2
23+
# Version: 0.3.5
2424

2525
from PIL import Image
2626
from io import BytesIO
@@ -55,30 +55,43 @@ def __init__(self, db: Database) -> None:
5555
}
5656
super().__init__(db, "experiment_list", columns)
5757

58-
def experiment_start(self, description:str, method:str, method_id:int, data:str, data_id, task:str, start_time:float=None, tags:str=None, experimenters:str=None) -> int:
58+
def experiment_start(self, description:str, method:str, method_id:int, data:str, data_id, task:str, start_time:float=None, tags:str=None, experimenters:str=None, remark:str=None) -> int:
5959
if start_time is None:
6060
start_time = time()
61-
start_time = localtime(start_time)
62-
start_time = strftime("%Y-%m-%d %H:%M:%S", start_time)
63-
return super().insert(description=description, method=method, method_id=method_id, data=data, data_id=data_id, task=task, tags=tags, experimenters=experimenters, start_time=strftime(start_time), status='running')
61+
elif start_time == "":
62+
start_time = None
63+
else:
64+
start_time = localtime(start_time)
65+
start_time = strftime("%Y-%m-%d %H:%M:%S", start_time)
66+
return super().insert(description=description, method=method, method_id=method_id,
67+
data=data, data_id=data_id, task=task, tags=tags, experimenters=experimenters,
68+
start_time=strftime(start_time) if start_time is not None else None, status='running', remark=remark)
6469

6570
def experiment_over(self, experiment_id:int, end_time:float=None, useful_time_cost:float=None) -> None:
6671
if end_time is None:
6772
end_time = time()
68-
end_time = localtime(end_time)
69-
end_time = strftime("%Y-%m-%d %H:%M:%S", end_time)
70-
super().update(f"id={experiment_id}", end_time=strftime(end_time), useful_time_cost=useful_time_cost, status='finished')
73+
elif end_time == "":
74+
end_time = None
75+
else:
76+
end_time = localtime(end_time)
77+
end_time = strftime("%Y-%m-%d %H:%M:%S", end_time)
78+
super().update(f"id={experiment_id}", end_time=strftime(end_time) if end_time is not None else None,
79+
useful_time_cost=useful_time_cost, status='finished')
7180

7281
def experiment_failed(self, experiment_id:int, error_info:str=None, end_time:float=None) -> None:
7382
if end_time is None:
7483
end_time = time()
75-
end_time = localtime(end_time)
76-
end_time = strftime("%Y-%m-%d %H:%M:%S", end_time)
84+
elif end_time == "":
85+
end_time = None
86+
else:
87+
end_time = localtime(end_time)
88+
end_time = strftime("%Y-%m-%d %H:%M:%S", end_time)
7789
# print(error_info)
7890
if error_info is None:
7991
error_info = traceback.format_exc()
8092
# print(error_info)
81-
super().update(f"id={experiment_id}", end_time=strftime(end_time), status='failed', failed_reason=error_info)
93+
super().update(f"id={experiment_id}", end_time=strftime(end_time) if end_time is not None else None,
94+
status='failed', failed_reason=error_info)
8295

8396
def get_experiment(self, experiment_id:int) -> dict:
8497
return super().select(where=f"id={experiment_id}")[0]
@@ -108,6 +121,7 @@ def insert(self, **kwargs):
108121
def_str = value2def(kwargs[key])
109122
self.add_column(key, def_str)
110123
self.update(**{key: 'NULL'})
124+
remark = kwargs.pop('remark', None)
111125
condition = ' AND '.join([f'{k.replace(" ", "_")}=?' for k in kwargs.keys()])
112126
values = list(kwargs.values())
113127

@@ -118,6 +132,8 @@ def insert(self, **kwargs):
118132
if id_list == []:
119133
return super().insert(**kwargs)
120134
else:
135+
if remark is not None:
136+
self.update(f"data_id={id_list[0][0]}", remark=remark)
121137
return id_list[0][0]
122138

123139

@@ -142,6 +158,7 @@ def insert(self, **kwargs):
142158
if key not in self.columns:
143159
self.add_column(key, value2def(kwargs[key]))
144160
self.update(**{key: 'NULL'})
161+
remark = kwargs.pop('remark', None)
145162
condition = ' AND '.join([f'{k.replace(" ", "_")}=?' for k in kwargs.keys()])
146163
values = list(kwargs.values())
147164

@@ -151,6 +168,8 @@ def insert(self, **kwargs):
151168
if id_list == []:
152169
return super().insert(**kwargs)
153170
else:
171+
if remark is not None:
172+
self.update(f"method_id={id_list[0][0]}", remark=remark)
154173
return id_list[0][0]
155174

156175
def image_def(i):

pyerm/database/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23-
# Version: 0.3.3
23+
# Version: 0.3.5
2424

2525
from time import time
2626
import pandas as pd
@@ -36,7 +36,7 @@ def auto_detect_def(param_dict:typing.Dict[str, typing.Any]) -> typing.Dict[str,
3636
param_def_dict = {}
3737
for k, v in param_dict.items():
3838
param_def_dict[k] = value2def(v)
39-
if param_def_dict[k] == 'TEXT':
39+
if param_def_dict[k] == 'TEXT' and v is not None:
4040
try:
4141
param_dict[k] = str(v)
4242
except:

0 commit comments

Comments
 (0)