ivoryos 0.1.8__py3-none-any.whl → 0.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +118 -99
- ivoryos/config.py +47 -47
- ivoryos/routes/auth/auth.py +100 -65
- ivoryos/routes/auth/templates/auth/login.html +25 -25
- ivoryos/routes/auth/templates/auth/signup.html +32 -32
- ivoryos/routes/control/control.py +400 -272
- ivoryos/routes/control/templates/control/controllers.html +75 -75
- ivoryos/routes/control/templates/control/controllers_home.html +50 -50
- ivoryos/routes/control/templates/control/controllers_new.html +89 -89
- ivoryos/routes/database/database.py +188 -114
- ivoryos/routes/database/templates/database/experiment_database.html +72 -72
- ivoryos/routes/design/design.py +542 -406
- ivoryos/routes/design/templates/design/experiment_builder.html +415 -412
- ivoryos/routes/design/templates/design/experiment_run.html +325 -325
- ivoryos/routes/main/main.py +42 -25
- ivoryos/routes/main/templates/main/help.html +141 -141
- ivoryos/routes/main/templates/main/home.html +68 -68
- ivoryos/static/.DS_Store +0 -0
- ivoryos/static/js/overlay.js +12 -12
- ivoryos/static/js/socket_handler.js +34 -34
- ivoryos/static/js/sortable_card.js +24 -24
- ivoryos/static/js/sortable_design.js +36 -36
- ivoryos/static/style.css +201 -201
- ivoryos/templates/base.html +143 -143
- ivoryos/utils/db_models.py +518 -500
- ivoryos/utils/form.py +316 -316
- ivoryos/utils/global_config.py +67 -67
- ivoryos/utils/llm_agent.py +183 -183
- ivoryos/utils/script_runner.py +165 -164
- ivoryos/utils/utils.py +425 -422
- ivoryos/version.py +1 -0
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/LICENSE +21 -21
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/METADATA +170 -166
- ivoryos-0.1.10.dist-info/RECORD +47 -0
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/WHEEL +1 -1
- ivoryos-0.1.8.dist-info/RECORD +0 -45
- {ivoryos-0.1.8.dist-info → ivoryos-0.1.10.dist-info}/top_level.txt +0 -0
|
@@ -1,114 +1,188 @@
|
|
|
1
|
-
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app
|
|
2
|
-
from flask_login import login_required
|
|
3
|
-
|
|
4
|
-
from ivoryos.utils.db_models import Script, db
|
|
5
|
-
from ivoryos.utils.utils import get_script_file, post_script_file
|
|
6
|
-
|
|
7
|
-
database = Blueprint('database', __name__, template_folder='templates/database')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@database.route("/edit_workflow/<workflow_name>")
|
|
12
|
-
@login_required
|
|
13
|
-
def edit_workflow(workflow_name):
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
1
|
+
from flask import Blueprint, redirect, url_for, flash, request, render_template, session, current_app
|
|
2
|
+
from flask_login import login_required
|
|
3
|
+
|
|
4
|
+
from ivoryos.utils.db_models import Script, db
|
|
5
|
+
from ivoryos.utils.utils import get_script_file, post_script_file
|
|
6
|
+
|
|
7
|
+
database = Blueprint('database', __name__, template_folder='templates/database')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@database.route("/edit_workflow/<workflow_name>")
|
|
12
|
+
@login_required
|
|
13
|
+
def edit_workflow(workflow_name):
|
|
14
|
+
"""
|
|
15
|
+
.. :quickref: Database; load workflow to canvas
|
|
16
|
+
|
|
17
|
+
load the selected workflow to the design canvas
|
|
18
|
+
|
|
19
|
+
.. http:get:: /edit_workflow/<workflow_name>
|
|
20
|
+
|
|
21
|
+
:param workflow_name: workflow name
|
|
22
|
+
:type workflow_name: str
|
|
23
|
+
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
24
|
+
"""
|
|
25
|
+
row = Script.query.get(workflow_name)
|
|
26
|
+
script = Script(**row.as_dict())
|
|
27
|
+
post_script_file(script)
|
|
28
|
+
pseudo_name = session.get("pseudo_deck", "")
|
|
29
|
+
off_line = current_app.config["OFF_LINE"]
|
|
30
|
+
if off_line and pseudo_name and not script.deck == pseudo_name:
|
|
31
|
+
flash(f"Choose the deck with name {script.deck}")
|
|
32
|
+
return redirect(url_for('design.experiment_builder'))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@database.route("/delete_workflow/<workflow_name>")
|
|
36
|
+
@login_required
|
|
37
|
+
def delete_workflow(workflow_name: str):
|
|
38
|
+
"""
|
|
39
|
+
.. :quickref: Database; delete workflow
|
|
40
|
+
|
|
41
|
+
delete workflow from database
|
|
42
|
+
|
|
43
|
+
.. http:get:: /delete_workflow/<workflow_name>
|
|
44
|
+
|
|
45
|
+
:param workflow_name: workflow name
|
|
46
|
+
:type workflow_name: str
|
|
47
|
+
:status 302: redirect to :http:get:`/ivoryos/database/`
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
Script.query.filter(Script.name == workflow_name).delete()
|
|
51
|
+
db.session.commit()
|
|
52
|
+
return redirect(url_for('database.load_from_database'))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@database.route("/publish")
|
|
56
|
+
@login_required
|
|
57
|
+
def publish():
|
|
58
|
+
"""
|
|
59
|
+
.. :quickref: Database; save workflow to database
|
|
60
|
+
|
|
61
|
+
save workflow to database
|
|
62
|
+
|
|
63
|
+
.. http:get:: /publish
|
|
64
|
+
|
|
65
|
+
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
66
|
+
"""
|
|
67
|
+
script = get_script_file()
|
|
68
|
+
if not script.name or not script.deck:
|
|
69
|
+
flash("Deck cannot be empty, try to re-submit deck configuration on the left panel")
|
|
70
|
+
row = Script.query.get(script.name)
|
|
71
|
+
if row and row.status == "finalized":
|
|
72
|
+
flash("This is a protected script, use save as to rename.")
|
|
73
|
+
elif row and not session['user'] == row.author:
|
|
74
|
+
flash("You are not the author, use save as to rename.")
|
|
75
|
+
else:
|
|
76
|
+
db.session.merge(script)
|
|
77
|
+
db.session.commit()
|
|
78
|
+
flash("Saved!")
|
|
79
|
+
return redirect(url_for('design.experiment_builder'))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@database.route("/finalize")
|
|
83
|
+
@login_required
|
|
84
|
+
def finalize():
|
|
85
|
+
"""
|
|
86
|
+
.. :quickref: Database; finalize the workflow
|
|
87
|
+
|
|
88
|
+
[protected workflow] prevent saving edited workflow to the same workflow name
|
|
89
|
+
|
|
90
|
+
.. http:get:: /finalize
|
|
91
|
+
|
|
92
|
+
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
script = get_script_file()
|
|
96
|
+
script.finalize()
|
|
97
|
+
if script.name:
|
|
98
|
+
db.session.merge(script)
|
|
99
|
+
db.session.commit()
|
|
100
|
+
post_script_file(script)
|
|
101
|
+
return redirect(url_for('design.experiment_builder'))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@database.route("/database/")
|
|
105
|
+
@database.route("/database/<deck_name>")
|
|
106
|
+
@login_required
|
|
107
|
+
def load_from_database(deck_name=None):
|
|
108
|
+
"""
|
|
109
|
+
.. :quickref: Database; database page
|
|
110
|
+
|
|
111
|
+
backend control through http requests
|
|
112
|
+
|
|
113
|
+
.. http:get:: /database/<deck_name>
|
|
114
|
+
|
|
115
|
+
:param deck_name: filter for deck name
|
|
116
|
+
:type deck_name: str
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
session.pop('edit_action', None) # reset cache
|
|
120
|
+
query = Script.query
|
|
121
|
+
search_term = request.args.get("keyword", None)
|
|
122
|
+
if search_term:
|
|
123
|
+
query = query.filter(Script.name.like(f'%{search_term}%'))
|
|
124
|
+
if deck_name is None:
|
|
125
|
+
temp = Script.query.with_entities(Script.deck).distinct().all()
|
|
126
|
+
deck_list = [i[0] for i in temp]
|
|
127
|
+
else:
|
|
128
|
+
query = query.filter(Script.deck == deck_name)
|
|
129
|
+
deck_list = ["ALL"]
|
|
130
|
+
page = request.args.get('page', default=1, type=int)
|
|
131
|
+
per_page = 10
|
|
132
|
+
|
|
133
|
+
workflows = query.paginate(page=page, per_page=per_page, error_out=False)
|
|
134
|
+
return render_template("experiment_database.html", workflows=workflows, deck_list=deck_list, deck_name=deck_name)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@database.route("/edit_run_name", methods=['POST'])
|
|
138
|
+
@login_required
|
|
139
|
+
def edit_run_name():
|
|
140
|
+
"""
|
|
141
|
+
.. :quickref: Database; edit workflow name
|
|
142
|
+
|
|
143
|
+
edit the name of the current workflow, won't save to the database
|
|
144
|
+
|
|
145
|
+
.. http:post:: /edit_run_name
|
|
146
|
+
|
|
147
|
+
: form run_name: new workflow name
|
|
148
|
+
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
if request.method == "POST":
|
|
152
|
+
run_name = request.form.get("run_name")
|
|
153
|
+
exist_script = Script.query.get(run_name)
|
|
154
|
+
if not exist_script:
|
|
155
|
+
script = get_script_file()
|
|
156
|
+
script.save_as(run_name)
|
|
157
|
+
post_script_file(script)
|
|
158
|
+
else:
|
|
159
|
+
flash("Script name is already exist in database")
|
|
160
|
+
return redirect(url_for("design.experiment_builder"))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@database.route("/save_as", methods=['POST'])
|
|
164
|
+
@login_required
|
|
165
|
+
def save_as():
|
|
166
|
+
"""
|
|
167
|
+
.. :quickref: Database; save the run name as
|
|
168
|
+
|
|
169
|
+
save the workflow name as
|
|
170
|
+
|
|
171
|
+
.. http:post:: /save_as
|
|
172
|
+
|
|
173
|
+
: form run_name: new workflow name
|
|
174
|
+
:status 302: redirect to :http:get:`/ivoryos/experiment/build/`
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
if request.method == "POST":
|
|
178
|
+
run_name = request.form.get("run_name")
|
|
179
|
+
exist_script = Script.query.get(run_name)
|
|
180
|
+
if not exist_script:
|
|
181
|
+
script = get_script_file()
|
|
182
|
+
script.save_as(run_name)
|
|
183
|
+
script.author = session.get('user')
|
|
184
|
+
post_script_file(script)
|
|
185
|
+
publish()
|
|
186
|
+
else:
|
|
187
|
+
flash("Script name is already exist in database")
|
|
188
|
+
return redirect(url_for("design.experiment_builder"))
|
|
@@ -1,72 +1,72 @@
|
|
|
1
|
-
{% extends 'base.html' %}
|
|
2
|
-
|
|
3
|
-
{% block title %}IvoryOS | Design Database{% endblock %}
|
|
4
|
-
{% block body %}
|
|
5
|
-
<div class="database-filter">
|
|
6
|
-
{% for deck_name in deck_list %}
|
|
7
|
-
{% if deck_name == "ALL" %}<a class="btn btn-secondary" href="{{url_for('database.load_from_database')}}">Back</a>
|
|
8
|
-
{% else %}<a class="btn btn-secondary" href="{{url_for('database.load_from_database',deck_name=deck_name)}}">{{deck_name}}</a>
|
|
9
|
-
{% endif %}
|
|
10
|
-
{% endfor %}
|
|
11
|
-
|
|
12
|
-
<form id="search" style="display: inline-block;float: right;" action="{{url_for('database.load_from_database',deck_name=deck_name)}}" method="GET">
|
|
13
|
-
<div class="input-group">
|
|
14
|
-
<div class="form-outline">
|
|
15
|
-
<input type="search" name="keyword" id="keyword" class="form-control" placeholder="Search workflows...">
|
|
16
|
-
</div>
|
|
17
|
-
<button type="submit" class="btn btn-primary">
|
|
18
|
-
<i class="bi bi-search"></i>
|
|
19
|
-
</button>
|
|
20
|
-
</div>
|
|
21
|
-
</form>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<table class="table table-hover" id="workflowLibrary">
|
|
25
|
-
<thead>
|
|
26
|
-
<tr>
|
|
27
|
-
<th scope="col">Workflow name</th>
|
|
28
|
-
<th scope="col">Deck </th>
|
|
29
|
-
<th scope="col">Current status</th>
|
|
30
|
-
<th scope="col">Time created</th>
|
|
31
|
-
<th scope="col">Last modified</th>
|
|
32
|
-
<th scope="col">Author</th>
|
|
33
|
-
<th scope="col"></th>
|
|
34
|
-
</tr>
|
|
35
|
-
</thead>
|
|
36
|
-
<tbody>
|
|
37
|
-
{% for workflow in workflows %}
|
|
38
|
-
<tr>
|
|
39
|
-
<td><a href="{{ url_for('database.edit_workflow', workflow_name=workflow.name) }}">{{ workflow.name }}</a></td>
|
|
40
|
-
<td>{{ workflow.deck }}</td>
|
|
41
|
-
<td>{{ workflow.status }}</td>
|
|
42
|
-
<td>{{ workflow.time_created }}</td>
|
|
43
|
-
<td>{{ workflow.last_modified }}</td>
|
|
44
|
-
<td>{{ workflow.author }}</td>
|
|
45
|
-
<td>
|
|
46
|
-
{#not workflow.status == "finalized" or#}
|
|
47
|
-
{% if session['user'] == 'admin' or session['user'] == workflow.author %}
|
|
48
|
-
<a href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
49
|
-
{% else %}
|
|
50
|
-
<a class="disabled-link" href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
51
|
-
{% endif %}
|
|
52
|
-
<td>
|
|
53
|
-
</tr>
|
|
54
|
-
{% endfor %}
|
|
55
|
-
</tbody>
|
|
56
|
-
</table>
|
|
57
|
-
|
|
58
|
-
{# paging#}
|
|
59
|
-
<div class="pagination justify-content-center">
|
|
60
|
-
<div class="page-item {{ 'disabled' if not workflows.has_prev else '' }}">
|
|
61
|
-
<a class="page-link" href="{{ url_for('database.load_from_database', page=workflows.prev_num) }}">Previous</a>
|
|
62
|
-
</div>
|
|
63
|
-
{% for num in workflows.iter_pages() %}
|
|
64
|
-
<div class="page-item">
|
|
65
|
-
<a class="page-link {{ 'active' if num == workflows.page else '' }}" href="{{ url_for('database.load_from_database', page=num) }}">{{ num }}</a>
|
|
66
|
-
</div>
|
|
67
|
-
{% endfor %}
|
|
68
|
-
<div class="page-item {{ 'disabled' if not workflows.has_next else '' }}">
|
|
69
|
-
<a class="page-link" href="{{ url_for('database.load_from_database', page=workflows.next_num) }}">Next</a>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
{% endblock %}
|
|
1
|
+
{% extends 'base.html' %}
|
|
2
|
+
|
|
3
|
+
{% block title %}IvoryOS | Design Database{% endblock %}
|
|
4
|
+
{% block body %}
|
|
5
|
+
<div class="database-filter">
|
|
6
|
+
{% for deck_name in deck_list %}
|
|
7
|
+
{% if deck_name == "ALL" %}<a class="btn btn-secondary" href="{{url_for('database.load_from_database')}}">Back</a>
|
|
8
|
+
{% else %}<a class="btn btn-secondary" href="{{url_for('database.load_from_database',deck_name=deck_name)}}">{{deck_name}}</a>
|
|
9
|
+
{% endif %}
|
|
10
|
+
{% endfor %}
|
|
11
|
+
|
|
12
|
+
<form id="search" style="display: inline-block;float: right;" action="{{url_for('database.load_from_database',deck_name=deck_name)}}" method="GET">
|
|
13
|
+
<div class="input-group">
|
|
14
|
+
<div class="form-outline">
|
|
15
|
+
<input type="search" name="keyword" id="keyword" class="form-control" placeholder="Search workflows...">
|
|
16
|
+
</div>
|
|
17
|
+
<button type="submit" class="btn btn-primary">
|
|
18
|
+
<i class="bi bi-search"></i>
|
|
19
|
+
</button>
|
|
20
|
+
</div>
|
|
21
|
+
</form>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<table class="table table-hover" id="workflowLibrary">
|
|
25
|
+
<thead>
|
|
26
|
+
<tr>
|
|
27
|
+
<th scope="col">Workflow name</th>
|
|
28
|
+
<th scope="col">Deck </th>
|
|
29
|
+
<th scope="col">Current status</th>
|
|
30
|
+
<th scope="col">Time created</th>
|
|
31
|
+
<th scope="col">Last modified</th>
|
|
32
|
+
<th scope="col">Author</th>
|
|
33
|
+
<th scope="col"></th>
|
|
34
|
+
</tr>
|
|
35
|
+
</thead>
|
|
36
|
+
<tbody>
|
|
37
|
+
{% for workflow in workflows %}
|
|
38
|
+
<tr>
|
|
39
|
+
<td><a href="{{ url_for('database.edit_workflow', workflow_name=workflow.name) }}">{{ workflow.name }}</a></td>
|
|
40
|
+
<td>{{ workflow.deck }}</td>
|
|
41
|
+
<td>{{ workflow.status }}</td>
|
|
42
|
+
<td>{{ workflow.time_created }}</td>
|
|
43
|
+
<td>{{ workflow.last_modified }}</td>
|
|
44
|
+
<td>{{ workflow.author }}</td>
|
|
45
|
+
<td>
|
|
46
|
+
{#not workflow.status == "finalized" or#}
|
|
47
|
+
{% if session['user'] == 'admin' or session['user'] == workflow.author %}
|
|
48
|
+
<a href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
49
|
+
{% else %}
|
|
50
|
+
<a class="disabled-link" href="{{ url_for('database.delete_workflow', workflow_name=workflow.name) }}">delete</a>
|
|
51
|
+
{% endif %}
|
|
52
|
+
<td>
|
|
53
|
+
</tr>
|
|
54
|
+
{% endfor %}
|
|
55
|
+
</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
|
|
58
|
+
{# paging#}
|
|
59
|
+
<div class="pagination justify-content-center">
|
|
60
|
+
<div class="page-item {{ 'disabled' if not workflows.has_prev else '' }}">
|
|
61
|
+
<a class="page-link" href="{{ url_for('database.load_from_database', page=workflows.prev_num) }}">Previous</a>
|
|
62
|
+
</div>
|
|
63
|
+
{% for num in workflows.iter_pages() %}
|
|
64
|
+
<div class="page-item">
|
|
65
|
+
<a class="page-link {{ 'active' if num == workflows.page else '' }}" href="{{ url_for('database.load_from_database', page=num) }}">{{ num }}</a>
|
|
66
|
+
</div>
|
|
67
|
+
{% endfor %}
|
|
68
|
+
<div class="page-item {{ 'disabled' if not workflows.has_next else '' }}">
|
|
69
|
+
<a class="page-link" href="{{ url_for('database.load_from_database', page=workflows.next_num) }}">Next</a>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{% endblock %}
|