setta 0.0.15.dev2__py3-none-any.whl → 0.0.17__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.
- setta/__init__.py +1 -1
- setta/code_gen/export_selected.py +8 -10
- setta/code_gen/python/generate_code.py +16 -9
- setta/lsp/reader_fns/signatureHelp.py +61 -20
- setta/routers/interactive.py +53 -5
- setta/routers/projects.py +42 -3
- setta/routers/settings.py +11 -1
- setta/static/constants/Settings.json +2 -1
- setta/static/constants/constants.json +8 -1
- setta/static/constants/db_init.sql +15 -0
- setta/static/constants/defaultValues.json +1 -1
- setta/static/frontend/assets/index-D-mLvWDA.css +32 -0
- setta/static/frontend/assets/{index-CFAyqpy6.js → index-D3_nMLig.js} +195 -195
- setta/static/frontend/index.html +2 -2
- setta/tasks/tasks.py +6 -2
- setta/utils/constants.py +9 -1
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/METADATA +25 -21
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/RECORD +22 -22
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/WHEEL +1 -1
- setta/static/frontend/assets/index-DG_u-B1j.css +0 -32
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/LICENSE +0 -0
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/entry_points.txt +0 -0
- {setta-0.0.15.dev2.dist-info → setta-0.0.17.dist-info}/top_level.txt +0 -0
setta/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.0.
|
1
|
+
__version__ = "0.0.17"
|
@@ -313,7 +313,7 @@ class Exporter:
|
|
313
313
|
(
|
314
314
|
used_params,
|
315
315
|
unused_params,
|
316
|
-
|
316
|
+
passingStyles,
|
317
317
|
) = self.export_section_params(id)
|
318
318
|
|
319
319
|
selected_item_ev_refs = get_selected_item_ev_refs(self.p, id)
|
@@ -334,7 +334,7 @@ class Exporter:
|
|
334
334
|
"callable": callable,
|
335
335
|
"usedParams": used_params,
|
336
336
|
"unusedParams": unused_params,
|
337
|
-
"
|
337
|
+
"passingStyles": passingStyles,
|
338
338
|
}
|
339
339
|
info["dependencies"] = [*used_params, *used_ref_var_names]
|
340
340
|
info["ref_var_name_positions"] = ref_var_name_positions
|
@@ -397,7 +397,7 @@ class Exporter:
|
|
397
397
|
(
|
398
398
|
used_params,
|
399
399
|
unused_params,
|
400
|
-
|
400
|
+
passingStyles,
|
401
401
|
) = self.export_section_params(id)
|
402
402
|
info = {
|
403
403
|
"sectionId": id,
|
@@ -407,7 +407,7 @@ class Exporter:
|
|
407
407
|
"callable": None,
|
408
408
|
"usedParams": used_params,
|
409
409
|
"unusedParams": unused_params,
|
410
|
-
"
|
410
|
+
"passingStyles": passingStyles,
|
411
411
|
},
|
412
412
|
"dependencies": used_params,
|
413
413
|
"ref_var_name_positions": [], # has to be populated when the code is generated
|
@@ -424,7 +424,7 @@ class Exporter:
|
|
424
424
|
var_names = [x for x in var_names if x]
|
425
425
|
used_params = []
|
426
426
|
unused_params = []
|
427
|
-
|
427
|
+
passingStyles = {}
|
428
428
|
for v in var_names:
|
429
429
|
if no_entered_value(self.output[v]["value"]):
|
430
430
|
unused_params.append(v)
|
@@ -432,12 +432,10 @@ class Exporter:
|
|
432
432
|
used_params.append(v)
|
433
433
|
|
434
434
|
curr_param_info_id = self.output[v]["paramInfoId"]
|
435
|
-
|
436
|
-
"
|
435
|
+
passingStyles[v] = self.p["codeInfo"][curr_param_info_id].get(
|
436
|
+
"passingStyle", None
|
437
437
|
)
|
438
|
-
|
439
|
-
positionalOnlyParams.add(v)
|
440
|
-
return used_params, unused_params, positionalOnlyParams
|
438
|
+
return used_params, unused_params, passingStyles
|
441
439
|
|
442
440
|
def export_param(self, section_id, id):
|
443
441
|
codeInfo = self.p["codeInfo"][id]
|
@@ -2,7 +2,7 @@ from setta.code_gen.export_selected import get_gen_code_template_var
|
|
2
2
|
from setta.code_gen.python.make_parseable import make_parseable
|
3
3
|
from setta.utils.utils import replace_at_positions
|
4
4
|
|
5
|
-
from ...utils.constants import C
|
5
|
+
from ...utils.constants import C, ParameterPassingStyle
|
6
6
|
from ..utils import push_var_deep
|
7
7
|
from .ast_utils import find_missing_imports, find_top_level_symbols
|
8
8
|
|
@@ -12,13 +12,22 @@ def get_import_text(code, chars_before_template_var):
|
|
12
12
|
return f"\n{chars_before_template_var}".join(imports)
|
13
13
|
|
14
14
|
|
15
|
-
def get_mappings(var_names, selected, is_dict,
|
15
|
+
def get_mappings(var_names, selected, is_dict=False, passingStyles=None):
|
16
|
+
if passingStyles is None:
|
17
|
+
passingStyles = {}
|
16
18
|
output = ""
|
17
19
|
relative_positions = []
|
18
20
|
curr_position = 0
|
19
21
|
for idx, v in enumerate(var_names):
|
20
22
|
prefix = "" if idx == 0 else ", "
|
21
|
-
if
|
23
|
+
if passingStyles.get(v) == ParameterPassingStyle.ARGS:
|
24
|
+
prefix = "*"
|
25
|
+
elif passingStyles.get(v) == ParameterPassingStyle.KWARGS:
|
26
|
+
prefix = "**"
|
27
|
+
elif is_dict or (
|
28
|
+
passingStyles.get(v) == ParameterPassingStyle.DEFAULT
|
29
|
+
or passingStyles.get(v) == ParameterPassingStyle.KEYWORD_ONLY
|
30
|
+
):
|
22
31
|
var_decl_prefix = var_declaration(
|
23
32
|
selected[v]["name"],
|
24
33
|
v,
|
@@ -40,14 +49,12 @@ def get_dict_contents(var_names, selected):
|
|
40
49
|
return get_mappings(var_names, selected, is_dict=True)
|
41
50
|
|
42
51
|
|
43
|
-
def get_callable_params(var_names,
|
44
|
-
return get_mappings(
|
45
|
-
var_names, selected, is_dict=False, positionalOnlyParams=positionalOnlyParams
|
46
|
-
)
|
52
|
+
def get_callable_params(var_names, passingStyles, selected):
|
53
|
+
return get_mappings(var_names, selected, passingStyles=passingStyles)
|
47
54
|
|
48
55
|
|
49
56
|
def get_list_of_vars(var_names):
|
50
|
-
return get_mappings(var_names, None
|
57
|
+
return get_mappings(var_names, None)
|
51
58
|
|
52
59
|
|
53
60
|
def get_boolean(value):
|
@@ -97,7 +104,7 @@ def get_value(x, selected):
|
|
97
104
|
prefix_len = len(prefix)
|
98
105
|
else:
|
99
106
|
output, relative_positions = get_callable_params(
|
100
|
-
x["value"]["usedParams"], x["value"]["
|
107
|
+
x["value"]["usedParams"], x["value"]["passingStyles"], selected
|
101
108
|
)
|
102
109
|
prefix = f"{x['value']['callable']}("
|
103
110
|
output = f"{prefix}{output})"
|
@@ -2,6 +2,8 @@ import re
|
|
2
2
|
|
3
3
|
import docstring_parser
|
4
4
|
|
5
|
+
from setta.utils.constants import ParameterPassingStyle
|
6
|
+
|
5
7
|
|
6
8
|
def process_signature_help_response(response):
|
7
9
|
content = None
|
@@ -40,13 +42,21 @@ def wrapper_parse_signature(signature, docstring, get_proposed_params):
|
|
40
42
|
params = get_proposed_params(signature, docstring)
|
41
43
|
param_info_list = []
|
42
44
|
positional_only_indicator_idx = -1
|
45
|
+
keyword_only_indicator_idx = -1
|
43
46
|
|
44
|
-
for idx, (name,
|
47
|
+
for idx, (name, _, _) in enumerate(params):
|
45
48
|
if name == "/":
|
46
49
|
positional_only_indicator_idx = idx
|
50
|
+
if name == "*":
|
51
|
+
keyword_only_indicator_idx = idx
|
52
|
+
|
53
|
+
for idx, (name, default, description) in enumerate(params):
|
54
|
+
if name in [None, "/", "*", "", "..."]:
|
47
55
|
continue
|
48
|
-
|
49
|
-
|
56
|
+
|
57
|
+
passingStyle = get_passing_style(
|
58
|
+
idx, positional_only_indicator_idx, keyword_only_indicator_idx, name
|
59
|
+
)
|
50
60
|
|
51
61
|
if not default:
|
52
62
|
default = ""
|
@@ -57,16 +67,13 @@ def wrapper_parse_signature(signature, docstring, get_proposed_params):
|
|
57
67
|
# Append parsed information to list
|
58
68
|
param_info_list.append(
|
59
69
|
{
|
60
|
-
"name": name,
|
70
|
+
"name": name.lstrip("*"),
|
61
71
|
"defaultVal": default,
|
62
72
|
"description": description,
|
63
|
-
"
|
73
|
+
"passingStyle": passingStyle,
|
64
74
|
}
|
65
75
|
)
|
66
76
|
|
67
|
-
for idx in range(0, positional_only_indicator_idx):
|
68
|
-
param_info_list[idx]["positionalOnly"] = True
|
69
|
-
|
70
77
|
return param_info_list
|
71
78
|
|
72
79
|
|
@@ -79,21 +86,55 @@ def get_pyright_param_proposals(signature, _):
|
|
79
86
|
params = [(p["label"], p["documentation"]["value"]) for p in parameters]
|
80
87
|
proposals = []
|
81
88
|
for param, description in params:
|
82
|
-
|
83
|
-
if param.startswith("/"):
|
84
|
-
proposals.append((param, None, None))
|
85
|
-
continue
|
86
|
-
parts = param.split("=")
|
87
|
-
name = extract_variable_name(parts[0].strip())
|
88
|
-
default = parts[1].strip() if len(parts) > 1 else None
|
89
|
+
name, _, default = parse_param_name(param)
|
89
90
|
proposals.append((name, default, description))
|
90
91
|
|
91
92
|
return proposals
|
92
93
|
|
93
94
|
|
94
|
-
def
|
95
|
-
#
|
96
|
-
|
95
|
+
def parse_param_name(param_string):
|
96
|
+
# Special cases for / and *
|
97
|
+
if param_string in ["/", "*"]:
|
98
|
+
return param_string, None, None
|
99
|
+
|
100
|
+
# Extract default value if present
|
101
|
+
default_value = None
|
102
|
+
if "=" in param_string:
|
103
|
+
param_string, default_part = param_string.split("=", 1)
|
104
|
+
default_value = default_part.strip()
|
105
|
+
|
106
|
+
# Extract type annotation if present
|
107
|
+
type_annotation = None
|
108
|
+
if ":" in param_string:
|
109
|
+
param_string, type_part = param_string.split(":", 1)
|
110
|
+
type_annotation = type_part.strip()
|
97
111
|
|
98
|
-
#
|
99
|
-
|
112
|
+
# Variable name is what remains, trimmed of whitespace
|
113
|
+
name = extract_variable_name(param_string)
|
114
|
+
|
115
|
+
return name, type_annotation, default_value
|
116
|
+
|
117
|
+
|
118
|
+
def extract_variable_name(code_string):
|
119
|
+
# Find potential variable names including * and ** prefixes
|
120
|
+
potential_names = re.findall(r"\*{1,2}?[a-zA-Z_]\w*|\b[a-zA-Z_]\w*\b", code_string)
|
121
|
+
|
122
|
+
# For names with * or **, we don't need to check isidentifier() since they're not standard identifiers
|
123
|
+
# Just return the first match
|
124
|
+
if potential_names:
|
125
|
+
return potential_names[0]
|
126
|
+
return None
|
127
|
+
|
128
|
+
|
129
|
+
def get_passing_style(
|
130
|
+
idx, positional_only_indicator_idx, keyword_only_indicator_idx, name
|
131
|
+
):
|
132
|
+
if name.startswith("**"): # Check for **kwargs first
|
133
|
+
return ParameterPassingStyle.KWARGS
|
134
|
+
if name.startswith("*"): # Check for *args second
|
135
|
+
return ParameterPassingStyle.ARGS
|
136
|
+
if 0 <= idx < positional_only_indicator_idx: # Then check position
|
137
|
+
return ParameterPassingStyle.POSITIONAL_ONLY
|
138
|
+
if keyword_only_indicator_idx > -1 and idx > keyword_only_indicator_idx:
|
139
|
+
return ParameterPassingStyle.KEYWORD_ONLY
|
140
|
+
return ParameterPassingStyle.DEFAULT
|
setta/routers/interactive.py
CHANGED
@@ -17,12 +17,17 @@ from setta.code_gen.export_selected import (
|
|
17
17
|
get_section_type,
|
18
18
|
)
|
19
19
|
from setta.code_gen.find_placeholders import parse_template_var
|
20
|
+
from setta.database.db.notifications.load import (
|
21
|
+
create_notification_dict,
|
22
|
+
load_notification,
|
23
|
+
)
|
24
|
+
from setta.database.db.notifications.save import save_notification
|
20
25
|
from setta.tasks.fns.utils import replace_template_vars_with_random_names
|
21
26
|
from setta.tasks.tasks import construct_subprocess_key
|
22
27
|
from setta.utils.constants import C
|
23
28
|
from setta.utils.utils import multireplace
|
24
29
|
|
25
|
-
from .dependencies import get_lsp_writers, get_tasks
|
30
|
+
from .dependencies import get_dbq, get_lsp_writers, get_tasks
|
26
31
|
|
27
32
|
router = APIRouter()
|
28
33
|
|
@@ -36,6 +41,10 @@ class FormatCodeRequest(BaseModel):
|
|
36
41
|
candidateTemplateVars: dict
|
37
42
|
|
38
43
|
|
44
|
+
class KillInMemorySubprocessesRequest(BaseModel):
|
45
|
+
projectConfigId: str
|
46
|
+
|
47
|
+
|
39
48
|
@router.post(C.ROUTE_SEND_PROJECT_TO_INTERACTIVE_CODE)
|
40
49
|
async def route_send_project_to_interactive_code(
|
41
50
|
x: UpdateInteractiveCodeRequest,
|
@@ -68,9 +77,20 @@ async def route_update_interactive_code(
|
|
68
77
|
|
69
78
|
async def process_returned_content_from_multiple_tasks(tasks, to_run):
|
70
79
|
all_content = await asyncio.gather(*to_run)
|
71
|
-
content = [
|
80
|
+
content = []
|
81
|
+
exception_occurred = False
|
82
|
+
for sublist in all_content:
|
83
|
+
for item in sublist:
|
84
|
+
if isinstance(item, Exception):
|
85
|
+
exception_occurred = True
|
86
|
+
else:
|
87
|
+
content.append(item)
|
72
88
|
inMemorySubprocessInfo = tasks.getInMemorySubprocessInfo()
|
73
|
-
return {
|
89
|
+
return {
|
90
|
+
"inMemorySubprocessInfo": inMemorySubprocessInfo,
|
91
|
+
"content": content,
|
92
|
+
"exceptionOccurred": exception_occurred,
|
93
|
+
}
|
74
94
|
|
75
95
|
|
76
96
|
async def update_interactive_code(p, tasks, lsp_writers, idx):
|
@@ -140,8 +160,36 @@ async def update_interactive_code(p, tasks, lsp_writers, idx):
|
|
140
160
|
|
141
161
|
|
142
162
|
@router.post(C.ROUTE_KILL_IN_MEMORY_SUBPROCESSES)
|
143
|
-
async def route_kill_in_memory_subprocesses(
|
144
|
-
tasks
|
163
|
+
async def route_kill_in_memory_subprocesses(
|
164
|
+
x: KillInMemorySubprocessesRequest, tasks=Depends(get_tasks), dbq=Depends(get_dbq)
|
165
|
+
):
|
166
|
+
notification = None
|
167
|
+
try:
|
168
|
+
num_killed = tasks.kill_in_memory_subprocesses()
|
169
|
+
if num_killed == 0:
|
170
|
+
notification = create_notification_dict(
|
171
|
+
message="No subprocesses to kill",
|
172
|
+
type=C.NOTIFICATION_TYPE_INFO,
|
173
|
+
temporary=True,
|
174
|
+
)
|
175
|
+
else:
|
176
|
+
with dbq as db:
|
177
|
+
notification_id = save_notification(
|
178
|
+
db,
|
179
|
+
x.projectConfigId,
|
180
|
+
C.NOTIFICATION_TYPE_INFO,
|
181
|
+
f"Killed {num_killed} subprocesses",
|
182
|
+
)
|
183
|
+
notification = load_notification(db, notification_id)
|
184
|
+
|
185
|
+
except Exception as e:
|
186
|
+
notification = create_notification_dict(
|
187
|
+
message="Failed to kill subprocesses",
|
188
|
+
type=C.NOTIFICATION_TYPE_ERROR,
|
189
|
+
temporary=True,
|
190
|
+
)
|
191
|
+
|
192
|
+
return {"notification": notification}
|
145
193
|
|
146
194
|
|
147
195
|
@router.post(C.ROUTE_FORMAT_CODE)
|
setta/routers/projects.py
CHANGED
@@ -5,6 +5,11 @@ from fastapi import APIRouter, Depends, HTTPException
|
|
5
5
|
from pydantic import BaseModel
|
6
6
|
|
7
7
|
from setta.database.backup import maybe_create_backup
|
8
|
+
from setta.database.db.notifications.delete import delete_notification
|
9
|
+
from setta.database.db.notifications.load import (
|
10
|
+
create_notification_dict,
|
11
|
+
load_project_notifications,
|
12
|
+
)
|
8
13
|
from setta.database.db.projects.delete import delete_project_configs
|
9
14
|
from setta.database.db.projects.load import (
|
10
15
|
ProjectNotFound,
|
@@ -85,6 +90,14 @@ class AddDefaultDataForJSONImportRequest(BaseModel):
|
|
85
90
|
project: dict
|
86
91
|
|
87
92
|
|
93
|
+
class GetNotificationsRequest(BaseModel):
|
94
|
+
projectConfigId: str
|
95
|
+
|
96
|
+
|
97
|
+
class DeleteNotificationRequest(BaseModel):
|
98
|
+
notificationId: str
|
99
|
+
|
100
|
+
|
88
101
|
@router.post(C.ROUTE_ALL_PROJECT_CONFIG_METADATA)
|
89
102
|
def route_all_project_config_metadata(dbq=Depends(get_dbq)):
|
90
103
|
with dbq as db:
|
@@ -120,9 +133,21 @@ def route_load_full_project(x: ProjectLoadFullRequest, dbq=Depends(get_dbq)):
|
|
120
133
|
@router.post(C.ROUTE_SAVE_PROJECT)
|
121
134
|
def route_save_project(x: ProjectSaveRequest, dbq=Depends(get_dbq)):
|
122
135
|
with dbq as db:
|
123
|
-
|
124
|
-
|
125
|
-
|
136
|
+
notification = None
|
137
|
+
try:
|
138
|
+
# Perform existing save operations
|
139
|
+
maybe_create_backup(db.path)
|
140
|
+
save_project_details(db, x.project)
|
141
|
+
maybe_export_database(db, db.path)
|
142
|
+
# Return success response with notification
|
143
|
+
notification = create_notification_dict(message="Saved!", temporary=True)
|
144
|
+
|
145
|
+
except Exception as e:
|
146
|
+
notification = create_notification_dict(
|
147
|
+
message="Failed to save", type=C.NOTIFICATION_TYPE_ERROR, temporary=True
|
148
|
+
)
|
149
|
+
|
150
|
+
return {"notification": notification}
|
126
151
|
|
127
152
|
|
128
153
|
@router.post(C.ROUTE_CREATE_PROJECT_CONFIG)
|
@@ -186,3 +211,17 @@ def router_filter_data_for_json_export(x: FilterDataForJSONExportRequest):
|
|
186
211
|
def router_add_default_data_for_json_import(x: FilterDataForJSONExportRequest):
|
187
212
|
add_defaults_to_project_and_load_json_sources(x.project)
|
188
213
|
return x.project
|
214
|
+
|
215
|
+
|
216
|
+
@router.post(C.ROUTE_GET_NOTIFICATIONS)
|
217
|
+
def route_get_notifications(x: GetNotificationsRequest, dbq=Depends(get_dbq)):
|
218
|
+
with dbq as db:
|
219
|
+
notifications = load_project_notifications(db, x.projectConfigId)
|
220
|
+
return notifications
|
221
|
+
|
222
|
+
|
223
|
+
@router.post(C.ROUTE_DELETE_NOTIFICATION)
|
224
|
+
def route_delete_notification(x: DeleteNotificationRequest, dbq=Depends(get_dbq)):
|
225
|
+
with dbq as db:
|
226
|
+
success = delete_notification(db, x.notificationId)
|
227
|
+
return {"success": success}
|
setta/routers/settings.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
from fastapi import APIRouter, Depends
|
2
2
|
from pydantic import BaseModel
|
3
3
|
|
4
|
+
from setta.database.db.notifications.load import create_notification_dict
|
4
5
|
from setta.utils.constants import C
|
5
6
|
|
6
7
|
from .dependencies import get_settings_file
|
@@ -32,7 +33,16 @@ def route_load_settings(settings_file=Depends(get_settings_file)):
|
|
32
33
|
def route_save_settings_project(
|
33
34
|
x: SaveSettingsProjectRequest, settings_file=Depends(get_settings_file)
|
34
35
|
):
|
35
|
-
|
36
|
+
notification = None
|
37
|
+
try:
|
38
|
+
settings_file.save_settings_project(x.settingsProject)
|
39
|
+
notification = create_notification_dict(message="Saved!", temporary=True)
|
40
|
+
except Exception as e:
|
41
|
+
notification = create_notification_dict(
|
42
|
+
message="Failed to save", type=C.NOTIFICATION_TYPE_ERROR, temporary=True
|
43
|
+
)
|
44
|
+
|
45
|
+
return {"notification": notification}
|
36
46
|
|
37
47
|
|
38
48
|
@router.post(C.ROUTE_LOAD_SETTINGS_PROJECT)
|
@@ -13,11 +13,12 @@
|
|
13
13
|
"loadShortcut": "Mod + L",
|
14
14
|
"searchShortcut": "Mod + K",
|
15
15
|
"advancedSearchShortcut": "Mod + P",
|
16
|
+
"commandPaletteShortcut": "Mod + Shift + P",
|
16
17
|
"resetZoomShortcut": "Mod + Comma",
|
17
18
|
"copyShortcut": "Mod + C",
|
18
19
|
"pasteShortcut": "Mod + V",
|
19
20
|
"pasteAsRefShortcut": "Mod + Shift + V",
|
20
|
-
"sortingShortcut": "
|
21
|
+
"sortingShortcut": "Mod + Alt",
|
21
22
|
"movingFreeShortcut": "Alt",
|
22
23
|
"showTooltipShortcut": "Q",
|
23
24
|
"tooltipToPlaintextShortcut": "Q + S",
|
@@ -97,6 +97,8 @@
|
|
97
97
|
"ROUTE_LOAD_ARTIFACT_FROM_DISK": "/loadArtifactFromDisk",
|
98
98
|
"ROUTE_RESTART_LANGUAGE_SERVER": "/restartLanguageServer",
|
99
99
|
"ROUTE_FILE_WATCH_LIST": "/fileWatchList",
|
100
|
+
"ROUTE_GET_NOTIFICATIONS": "/getNotifications",
|
101
|
+
"ROUTE_DELETE_NOTIFICATION": "/deleteNotification",
|
100
102
|
"NESTED_PARAM": "NESTED_PARAM",
|
101
103
|
"ARGS_PREFIX": "__",
|
102
104
|
"TEMPLATE_PREFIX": "$",
|
@@ -123,5 +125,10 @@
|
|
123
125
|
"IMAGE": ["img"],
|
124
126
|
"CHART": ["list"],
|
125
127
|
"CHAT": ["chatHistory"]
|
126
|
-
}
|
128
|
+
},
|
129
|
+
"NOTIFICATION_TYPE_SUCCESS": "success",
|
130
|
+
"NOTIFICATION_TYPE_ERROR": "error",
|
131
|
+
"NOTIFICATION_TYPE_WARNING": "warning",
|
132
|
+
"NOTIFICATION_TYPE_INFO": "info",
|
133
|
+
"NOTIFICATION_TYPE_SAVE": "save"
|
127
134
|
}
|
@@ -221,6 +221,21 @@ CREATE TABLE IF NOT EXISTS ArtifactGroup (
|
|
221
221
|
FOREIGN KEY (artifactId) REFERENCES Artifact(id) ON DELETE CASCADE
|
222
222
|
);
|
223
223
|
|
224
|
+
CREATE TABLE IF NOT EXISTS Notifications (
|
225
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
226
|
+
projectConfigId TEXT NOT NULL,
|
227
|
+
timestamp TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
228
|
+
type TEXT NOT NULL,
|
229
|
+
message TEXT NOT NULL,
|
230
|
+
metadata JSON,
|
231
|
+
read_status BOOLEAN NOT NULL DEFAULT 0,
|
232
|
+
FOREIGN KEY (projectConfigId) REFERENCES ProjectConfig(id)
|
233
|
+
);
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
|
238
|
+
|
224
239
|
-- INSERTS ---
|
225
240
|
INSERT OR IGNORE INTO Metadata (id, defaultProject) VALUES (1, NULL);
|
226
241
|
|