QuLab 2.0.0__tar.gz → 2.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {QuLab-2.0.0 → qulab-2.0.2}/PKG-INFO +4 -1
- {QuLab-2.0.0 → qulab-2.0.2}/QuLab.egg-info/PKG-INFO +4 -1
- {QuLab-2.0.0 → qulab-2.0.2}/QuLab.egg-info/SOURCES.txt +25 -13
- qulab-2.0.2/QuLab.egg-info/entry_points.txt +2 -0
- {QuLab-2.0.0 → qulab-2.0.2}/QuLab.egg-info/requires.txt +3 -0
- {QuLab-2.0.0 → qulab-2.0.2}/pyproject.toml +7 -1
- qulab-2.0.2/qulab/__main__.py +26 -0
- qulab-2.0.2/qulab/monitor/__init__.py +1 -0
- qulab-2.0.2/qulab/monitor/__main__.py +8 -0
- QuLab-2.0.0/qulab/monitor/__init__.py → qulab-2.0.2/qulab/monitor/monitor.py +2 -2
- qulab-2.0.2/qulab/scan/__init__.py +3 -0
- qulab-2.0.2/qulab/scan/curd.py +144 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/scan/expression.py +150 -5
- qulab-2.0.2/qulab/scan/models.py +540 -0
- qulab-2.0.2/qulab/scan/optimize.py +69 -0
- qulab-2.0.2/qulab/scan/query_record.py +361 -0
- qulab-2.0.2/qulab/scan/recorder.py +447 -0
- qulab-2.0.2/qulab/scan/scan.py +701 -0
- qulab-2.0.2/qulab/sys/rpc/zmq_socket.py +209 -0
- qulab-2.0.2/qulab/version.py +1 -0
- qulab-2.0.2/qulab/visualization/__init__.py +188 -0
- qulab-2.0.2/qulab/visualization/__main__.py +71 -0
- qulab-2.0.2/qulab/visualization/_autoplot.py +463 -0
- qulab-2.0.2/qulab/visualization/plot_layout.py +408 -0
- qulab-2.0.2/qulab/visualization/plot_seq.py +90 -0
- qulab-2.0.2/qulab/visualization/qdat.py +152 -0
- qulab-2.0.2/qulab/visualization/widgets.py +86 -0
- {QuLab-2.0.0 → qulab-2.0.2}/setup.py +1 -1
- qulab-2.0.2/tests/test_scan.py +2 -0
- QuLab-2.0.0/qulab/monitor/multiploter/__init__.py +0 -1
- QuLab-2.0.0/qulab/scan/__init__.py +0 -4
- QuLab-2.0.0/qulab/scan/base.py +0 -544
- QuLab-2.0.0/qulab/scan/dataset.py +0 -0
- QuLab-2.0.0/qulab/scan/optimize.py +0 -0
- QuLab-2.0.0/qulab/scan/scanner.py +0 -244
- QuLab-2.0.0/qulab/scan/transforms.py +0 -16
- QuLab-2.0.0/qulab/version.py +0 -1
- QuLab-2.0.0/tests/test_scan_iter.py +0 -375
- {QuLab-2.0.0 → qulab-2.0.2}/LICENSE +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/MANIFEST.in +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/QuLab.egg-info/dependency_links.txt +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/QuLab.egg-info/top_level.txt +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/README.md +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/__init__.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/config.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/dataset.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/event_queue.py +0 -0
- /QuLab-2.0.0/qulab/monitor/multiploter/main.py → /qulab-2.0.2/qulab/monitor/mainwindow.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/ploter.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/qt_compat.py +0 -0
- {QuLab-2.0.0/qulab/monitor/multiploter → qulab-2.0.2/qulab/monitor}/toolbar.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/scan/utils.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/__main__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/backend/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/backend/redis.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/base_dataset.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/chunk.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/dataset.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/file.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/base.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/config.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/file.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/ipy.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/models.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/record.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/report.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/models/tag.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/storage/storage.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/chat.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/device/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/device/basedevice.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/device/loader.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/device/utils.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/drivers/FakeInstrument.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/drivers/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/ipy_events.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/bencoder.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/cli.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/dhcp.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/dhcpd.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/kad.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/kcp.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/net/nginx.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/progress.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/__init__.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/client.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/exceptions.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/msgpack.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/msgpack.pyi +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/rpc.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/serialize.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/server.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/socket.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/utils.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/qulab/sys/rpc/worker.py +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/setup.cfg +0 -0
- {QuLab-2.0.0 → qulab-2.0.2}/src/qulab.h +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: QuLab
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: contral instruments and manage data
|
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
|
@@ -30,10 +30,13 @@ Requires-Dist: dill>=0.3.6
|
|
|
30
30
|
Requires-Dist: GitPython>=3.1.14
|
|
31
31
|
Requires-Dist: ipython>=7.4.0
|
|
32
32
|
Requires-Dist: ipywidgets>=7.4.2
|
|
33
|
+
Requires-Dist: loguru>=0.7.2
|
|
33
34
|
Requires-Dist: matplotlib>=3.7.2
|
|
34
35
|
Requires-Dist: numpy>=1.13.3
|
|
35
36
|
Requires-Dist: ply>=3.11
|
|
37
|
+
Requires-Dist: pyzmq>=25.1.0
|
|
36
38
|
Requires-Dist: scipy>=1.0.0
|
|
39
|
+
Requires-Dist: watchdog>=4.0.0
|
|
37
40
|
|
|
38
41
|
# QuLab
|
|
39
42
|
[](https://travis-ci.org/feihoo87/QuLab)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: QuLab
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: contral instruments and manage data
|
|
5
5
|
Author-email: feihoo87 <feihoo87@gmail.com>
|
|
6
6
|
Maintainer-email: feihoo87 <feihoo87@gmail.com>
|
|
@@ -30,10 +30,13 @@ Requires-Dist: dill>=0.3.6
|
|
|
30
30
|
Requires-Dist: GitPython>=3.1.14
|
|
31
31
|
Requires-Dist: ipython>=7.4.0
|
|
32
32
|
Requires-Dist: ipywidgets>=7.4.2
|
|
33
|
+
Requires-Dist: loguru>=0.7.2
|
|
33
34
|
Requires-Dist: matplotlib>=3.7.2
|
|
34
35
|
Requires-Dist: numpy>=1.13.3
|
|
35
36
|
Requires-Dist: ply>=3.11
|
|
37
|
+
Requires-Dist: pyzmq>=25.1.0
|
|
36
38
|
Requires-Dist: scipy>=1.0.0
|
|
39
|
+
Requires-Dist: watchdog>=4.0.0
|
|
37
40
|
|
|
38
41
|
# QuLab
|
|
39
42
|
[](https://travis-ci.org/feihoo87/QuLab)
|
|
@@ -6,26 +6,30 @@ setup.py
|
|
|
6
6
|
QuLab.egg-info/PKG-INFO
|
|
7
7
|
QuLab.egg-info/SOURCES.txt
|
|
8
8
|
QuLab.egg-info/dependency_links.txt
|
|
9
|
+
QuLab.egg-info/entry_points.txt
|
|
9
10
|
QuLab.egg-info/requires.txt
|
|
10
11
|
QuLab.egg-info/top_level.txt
|
|
11
12
|
qulab/__init__.py
|
|
13
|
+
qulab/__main__.py
|
|
12
14
|
qulab/version.py
|
|
13
15
|
qulab/monitor/__init__.py
|
|
14
|
-
qulab/monitor/
|
|
15
|
-
qulab/monitor/
|
|
16
|
-
qulab/monitor/
|
|
17
|
-
qulab/monitor/
|
|
18
|
-
qulab/monitor/
|
|
19
|
-
qulab/monitor/
|
|
20
|
-
qulab/monitor/
|
|
21
|
-
qulab/monitor/
|
|
16
|
+
qulab/monitor/__main__.py
|
|
17
|
+
qulab/monitor/config.py
|
|
18
|
+
qulab/monitor/dataset.py
|
|
19
|
+
qulab/monitor/event_queue.py
|
|
20
|
+
qulab/monitor/mainwindow.py
|
|
21
|
+
qulab/monitor/monitor.py
|
|
22
|
+
qulab/monitor/ploter.py
|
|
23
|
+
qulab/monitor/qt_compat.py
|
|
24
|
+
qulab/monitor/toolbar.py
|
|
22
25
|
qulab/scan/__init__.py
|
|
23
|
-
qulab/scan/
|
|
24
|
-
qulab/scan/dataset.py
|
|
26
|
+
qulab/scan/curd.py
|
|
25
27
|
qulab/scan/expression.py
|
|
28
|
+
qulab/scan/models.py
|
|
26
29
|
qulab/scan/optimize.py
|
|
27
|
-
qulab/scan/
|
|
28
|
-
qulab/scan/
|
|
30
|
+
qulab/scan/query_record.py
|
|
31
|
+
qulab/scan/recorder.py
|
|
32
|
+
qulab/scan/scan.py
|
|
29
33
|
qulab/scan/utils.py
|
|
30
34
|
qulab/storage/__init__.py
|
|
31
35
|
qulab/storage/__main__.py
|
|
@@ -74,5 +78,13 @@ qulab/sys/rpc/server.py
|
|
|
74
78
|
qulab/sys/rpc/socket.py
|
|
75
79
|
qulab/sys/rpc/utils.py
|
|
76
80
|
qulab/sys/rpc/worker.py
|
|
81
|
+
qulab/sys/rpc/zmq_socket.py
|
|
82
|
+
qulab/visualization/__init__.py
|
|
83
|
+
qulab/visualization/__main__.py
|
|
84
|
+
qulab/visualization/_autoplot.py
|
|
85
|
+
qulab/visualization/plot_layout.py
|
|
86
|
+
qulab/visualization/plot_seq.py
|
|
87
|
+
qulab/visualization/qdat.py
|
|
88
|
+
qulab/visualization/widgets.py
|
|
77
89
|
src/qulab.h
|
|
78
|
-
tests/
|
|
90
|
+
tests/test_scan.py
|
|
@@ -34,13 +34,19 @@ dependencies = [
|
|
|
34
34
|
"GitPython>=3.1.14",
|
|
35
35
|
"ipython>=7.4.0",
|
|
36
36
|
"ipywidgets>=7.4.2",
|
|
37
|
+
"loguru>=0.7.2",
|
|
37
38
|
"matplotlib>=3.7.2",
|
|
38
39
|
"numpy>=1.13.3",
|
|
39
40
|
"ply>=3.11",
|
|
40
|
-
"
|
|
41
|
+
"pyzmq>=25.1.0",
|
|
42
|
+
"scipy>=1.0.0",
|
|
43
|
+
"watchdog>=4.0.0"
|
|
41
44
|
]
|
|
42
45
|
dynamic = ["version"]
|
|
43
46
|
|
|
47
|
+
[project.scripts]
|
|
48
|
+
"qulab" = "qulab.__main__:main"
|
|
49
|
+
|
|
44
50
|
[project.urls]
|
|
45
51
|
Homepage = "https://github.com/feihoo87/QuLab"
|
|
46
52
|
"Bug Reports" = "https://github.com/feihoo87/QuLab/issues"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from .monitor.__main__ import main as monitor
|
|
4
|
+
from .scan.recorder import record
|
|
5
|
+
from .sys.net.cli import dht
|
|
6
|
+
from .visualization.__main__ import plot
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.group()
|
|
10
|
+
def main():
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@main.command()
|
|
15
|
+
def hello():
|
|
16
|
+
"""Print hello world."""
|
|
17
|
+
click.echo('hello, world')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
main.add_command(monitor)
|
|
21
|
+
main.add_command(plot)
|
|
22
|
+
main.add_command(dht)
|
|
23
|
+
main.add_command(record)
|
|
24
|
+
|
|
25
|
+
if __name__ == '__main__':
|
|
26
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .monitor import Monitor, get_monitor
|
|
@@ -11,8 +11,8 @@ def main(queue: mp.Queue,
|
|
|
11
11
|
ncols: int = 4,
|
|
12
12
|
minimum_height: int = 400,
|
|
13
13
|
colors: list[tuple[int, int, int]] = []):
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
14
|
+
from .mainwindow import MainWindow
|
|
15
|
+
from .qt_compat import QtWidgets
|
|
16
16
|
|
|
17
17
|
app = QtWidgets.QApplication(sys.argv)
|
|
18
18
|
main = MainWindow(queue, ncols, minimum_height, colors)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from datetime import date, datetime, timezone
|
|
2
|
+
from typing import Sequence, Type, Union
|
|
3
|
+
|
|
4
|
+
from sqlalchemy.orm import Query, Session, aliased
|
|
5
|
+
from sqlalchemy.orm.exc import NoResultFound
|
|
6
|
+
from sqlalchemy.orm.session import Session
|
|
7
|
+
from waveforms.dicttree import foldDict
|
|
8
|
+
|
|
9
|
+
from .models import Comment, Record, Report, Sample, Tag
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def tag(session: Session, tag_text: str) -> Tag:
|
|
13
|
+
"""Get a tag from the database or create a new if not exists."""
|
|
14
|
+
try:
|
|
15
|
+
return session.query(Tag).filter(Tag.text == tag_text).one()
|
|
16
|
+
except NoResultFound:
|
|
17
|
+
tag = Tag(text=tag_text)
|
|
18
|
+
return tag
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def tag_it(session: Session, tag_text: str, obj: Union[Sample, Record,
|
|
22
|
+
Report]) -> Tag:
|
|
23
|
+
"""Tag an object."""
|
|
24
|
+
if obj.id is None:
|
|
25
|
+
session.add(obj)
|
|
26
|
+
obj.tags.append(tag(session, tag_text))
|
|
27
|
+
else:
|
|
28
|
+
session.query(type(obj)).filter(
|
|
29
|
+
type(obj).id == obj.id).one().tags.append(tag(session, tag_text))
|
|
30
|
+
session.commit()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_object_with_tags(session: Session,
|
|
34
|
+
cls: Union[Type[Comment], Type[Sample], Type[Record],
|
|
35
|
+
Type[Report]], *tags: str) -> Query:
|
|
36
|
+
"""
|
|
37
|
+
Query objects with the given tags.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
session : :class:`sqlalchemy.orm.Session`
|
|
42
|
+
The database session.
|
|
43
|
+
cls : :class:`sqlalchemy.orm.Mapper`
|
|
44
|
+
The object class.
|
|
45
|
+
tags : str
|
|
46
|
+
The tags.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
:class:`sqlalchemy.orm.Query`
|
|
51
|
+
The query.
|
|
52
|
+
"""
|
|
53
|
+
if isinstance(session, Query):
|
|
54
|
+
q = session
|
|
55
|
+
else:
|
|
56
|
+
q = session.query(cls)
|
|
57
|
+
if not hasattr(cls, 'tags'):
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
aliase = {tag: aliased(Tag) for tag in tags}
|
|
61
|
+
|
|
62
|
+
for tag, a in aliase.items():
|
|
63
|
+
q = q.join(a, cls.tags)
|
|
64
|
+
if '*' in tag:
|
|
65
|
+
q = q.filter(a.text.like(tag.replace('*', '%')))
|
|
66
|
+
else:
|
|
67
|
+
q = q.filter(a.text == tag)
|
|
68
|
+
return q
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def query_record(session: Session,
|
|
72
|
+
offset: int = 0,
|
|
73
|
+
limit: int = 10,
|
|
74
|
+
app: str | None = None,
|
|
75
|
+
tags: Sequence[str] = (),
|
|
76
|
+
before: datetime | date | None = None,
|
|
77
|
+
after: datetime | date | None = None):
|
|
78
|
+
tz_offset = datetime.now(timezone.utc).astimezone().utcoffset()
|
|
79
|
+
table = {'header': ['ID', 'App', 'tags', 'created time'], 'body': []}
|
|
80
|
+
apps = sorted(
|
|
81
|
+
set([
|
|
82
|
+
n for n, *_ in get_object_with_tags(session.query(Record.app),
|
|
83
|
+
Record, *tags).all()
|
|
84
|
+
]))
|
|
85
|
+
apps = foldDict(dict([(app, None) for app in apps]))
|
|
86
|
+
|
|
87
|
+
query = get_object_with_tags(session, Record, *tags)
|
|
88
|
+
|
|
89
|
+
if app is not None:
|
|
90
|
+
if app.endswith('*'):
|
|
91
|
+
query = query.filter(Record.app.like(app[:-1] + '%'))
|
|
92
|
+
else:
|
|
93
|
+
query = query.filter(Record.app == app)
|
|
94
|
+
if before is not None:
|
|
95
|
+
if isinstance(before, date):
|
|
96
|
+
before = datetime(before.year, before.month, before.day)
|
|
97
|
+
query = query.filter(Record.ctime <= before - tz_offset)
|
|
98
|
+
if after is not None:
|
|
99
|
+
if isinstance(after, date):
|
|
100
|
+
after = datetime(after.year, after.month, after.day)
|
|
101
|
+
query = query.filter(Record.ctime >= after - tz_offset)
|
|
102
|
+
total = query.count()
|
|
103
|
+
for r in query.order_by(Record.ctime.desc()).limit(limit).offset(offset):
|
|
104
|
+
tags = sorted([t.text for t in r.tags])
|
|
105
|
+
ctime = r.ctime + tz_offset
|
|
106
|
+
row = [r.id, r.app, tags, ctime]
|
|
107
|
+
table['body'].append(row)
|
|
108
|
+
|
|
109
|
+
return total, apps, table
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def update_tags(session: Session,
|
|
113
|
+
record_id: int,
|
|
114
|
+
tags: Sequence[str],
|
|
115
|
+
append: bool = False):
|
|
116
|
+
record = session.get(Record, record_id)
|
|
117
|
+
if record is None:
|
|
118
|
+
return False
|
|
119
|
+
if append:
|
|
120
|
+
old = [t.text for t in record.tags]
|
|
121
|
+
for t in old:
|
|
122
|
+
if t not in tags:
|
|
123
|
+
tags.append(t)
|
|
124
|
+
record.tags = [tag(session, t) for t in tags]
|
|
125
|
+
try:
|
|
126
|
+
session.commit()
|
|
127
|
+
except Exception:
|
|
128
|
+
session.rollback()
|
|
129
|
+
return False
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def remove_tags(session: Session, record_id: int, tags: Sequence[str]):
|
|
134
|
+
record = session.get(Record, record_id)
|
|
135
|
+
if record is None:
|
|
136
|
+
return False
|
|
137
|
+
old = [t.text for t in record.tags]
|
|
138
|
+
record.tags = [tag(session, t) for t in old if t not in tags]
|
|
139
|
+
try:
|
|
140
|
+
session.commit()
|
|
141
|
+
except Exception:
|
|
142
|
+
session.rollback()
|
|
143
|
+
return False
|
|
144
|
+
return True
|
|
@@ -8,6 +8,7 @@ from pyparsing import (CaselessKeyword, Combine, Forward, Group, Keyword,
|
|
|
8
8
|
alphanums, alphas, delimitedList, nums, oneOf, opAssoc,
|
|
9
9
|
pyparsing_common, restOfLine, srange, stringEnd,
|
|
10
10
|
stringStart)
|
|
11
|
+
from scipy import special
|
|
11
12
|
|
|
12
13
|
LPAREN, RPAREN, LBRACK, RBRACK, LBRACE, RBRACE, DOT, TILDE, BANG, PLUS, MINUS = map(
|
|
13
14
|
Suppress, "()[]{}.~!+-")
|
|
@@ -60,15 +61,45 @@ class Env():
|
|
|
60
61
|
self.consts = {}
|
|
61
62
|
self.variables = {}
|
|
62
63
|
self.refs = {}
|
|
64
|
+
self.functions = {
|
|
65
|
+
'sin': np.sin,
|
|
66
|
+
'cos': np.cos,
|
|
67
|
+
'tan': np.tan,
|
|
68
|
+
'pi': np.pi,
|
|
69
|
+
'e': np.e,
|
|
70
|
+
'log': np.log,
|
|
71
|
+
'log2': np.log2,
|
|
72
|
+
'log10': np.log10,
|
|
73
|
+
'exp': np.exp,
|
|
74
|
+
'sqrt': np.sqrt,
|
|
75
|
+
'abs': np.abs,
|
|
76
|
+
'sinh': np.sinh,
|
|
77
|
+
'cosh': np.cosh,
|
|
78
|
+
'tanh': np.tanh,
|
|
79
|
+
'arcsin': np.arcsin,
|
|
80
|
+
'arccos': np.arccos,
|
|
81
|
+
'arctan': np.arctan,
|
|
82
|
+
'arctan2': np.arctan2,
|
|
83
|
+
'arcsinh': np.arcsinh,
|
|
84
|
+
'arccosh': np.arccosh,
|
|
85
|
+
'arctanh': np.arctanh,
|
|
86
|
+
'sinc': np.sinc,
|
|
87
|
+
'sign': np.sign,
|
|
88
|
+
'heaviside': np.heaviside,
|
|
89
|
+
'erf': special.erf,
|
|
90
|
+
'erfc': special.erfc,
|
|
91
|
+
}
|
|
63
92
|
|
|
64
93
|
def __contains__(self, key):
|
|
65
|
-
return key in self.consts or key in self.variables or key in self.refs
|
|
94
|
+
return key in self.consts or key in self.variables or key in self.functions or key in self.refs
|
|
66
95
|
|
|
67
96
|
def __getitem__(self, key):
|
|
68
97
|
if key in self.consts:
|
|
69
98
|
return self.consts[key]
|
|
70
99
|
if key in self.variables:
|
|
71
100
|
return self.variables[key]
|
|
101
|
+
if key in self.functions:
|
|
102
|
+
return self.functions[key]
|
|
72
103
|
if key in self.refs:
|
|
73
104
|
return self[self.refs[key]]
|
|
74
105
|
raise KeyError(f"Key {key} not found")
|
|
@@ -110,6 +141,9 @@ class Env():
|
|
|
110
141
|
return key in self.consts
|
|
111
142
|
|
|
112
143
|
|
|
144
|
+
_default_env = Env()
|
|
145
|
+
|
|
146
|
+
|
|
113
147
|
class Expression():
|
|
114
148
|
|
|
115
149
|
def __init__(self):
|
|
@@ -127,33 +161,85 @@ class Expression():
|
|
|
127
161
|
raise NotImplementedError
|
|
128
162
|
|
|
129
163
|
def __add__(self, other):
|
|
164
|
+
if isinstance(other, Expression):
|
|
165
|
+
other = other.eval(_default_env)
|
|
166
|
+
if isinstance(other, ConstType) and other == 0:
|
|
167
|
+
return self
|
|
130
168
|
return BinaryExpression(self, other, operator.add)
|
|
131
169
|
|
|
132
170
|
def __radd__(self, other):
|
|
171
|
+
if isinstance(other, Expression):
|
|
172
|
+
other = other.eval(_default_env)
|
|
173
|
+
if isinstance(other, ConstType) and other == 0:
|
|
174
|
+
return self
|
|
133
175
|
return BinaryExpression(other, self, operator.add)
|
|
134
176
|
|
|
135
177
|
def __sub__(self, other):
|
|
178
|
+
if isinstance(other, Expression):
|
|
179
|
+
other = other.eval(_default_env)
|
|
180
|
+
if isinstance(other, ConstType) and other == 0:
|
|
181
|
+
return self
|
|
136
182
|
return BinaryExpression(self, other, operator.sub)
|
|
137
183
|
|
|
138
184
|
def __rsub__(self, other):
|
|
185
|
+
if isinstance(other, Expression):
|
|
186
|
+
other = other.eval(_default_env)
|
|
187
|
+
if isinstance(other, ConstType) and other == 0:
|
|
188
|
+
return -self
|
|
139
189
|
return BinaryExpression(other, self, operator.sub)
|
|
140
190
|
|
|
141
191
|
def __mul__(self, other):
|
|
192
|
+
if isinstance(other, Expression):
|
|
193
|
+
other = other.eval(_default_env)
|
|
194
|
+
if isinstance(other, ConstType) and other == 0:
|
|
195
|
+
return 0
|
|
196
|
+
if isinstance(other, ConstType) and other == 1:
|
|
197
|
+
return self
|
|
198
|
+
if isinstance(other, ConstType) and other == -1:
|
|
199
|
+
return -self
|
|
142
200
|
return BinaryExpression(self, other, operator.mul)
|
|
143
201
|
|
|
144
202
|
def __rmul__(self, other):
|
|
203
|
+
if isinstance(other, Expression):
|
|
204
|
+
other = other.eval(_default_env)
|
|
205
|
+
if isinstance(other, ConstType) and other == 0:
|
|
206
|
+
return 0
|
|
207
|
+
if isinstance(other, ConstType) and other == 1:
|
|
208
|
+
return self
|
|
209
|
+
if isinstance(other, ConstType) and other == -1:
|
|
210
|
+
return -self
|
|
145
211
|
return BinaryExpression(other, self, operator.mul)
|
|
146
212
|
|
|
147
213
|
def __truediv__(self, other):
|
|
214
|
+
if isinstance(other, Expression):
|
|
215
|
+
other = other.eval(_default_env)
|
|
216
|
+
if isinstance(other, ConstType) and other == 1:
|
|
217
|
+
return self
|
|
218
|
+
if isinstance(other, ConstType) and other == -1:
|
|
219
|
+
return -self
|
|
148
220
|
return BinaryExpression(self, other, operator.truediv)
|
|
149
221
|
|
|
150
222
|
def __rtruediv__(self, other):
|
|
223
|
+
if isinstance(other, Expression):
|
|
224
|
+
other = other.eval(_default_env)
|
|
225
|
+
if isinstance(other, ConstType) and other == 0:
|
|
226
|
+
return 0
|
|
151
227
|
return BinaryExpression(other, self, operator.truediv)
|
|
152
228
|
|
|
153
229
|
def __pow__(self, other):
|
|
230
|
+
if isinstance(other, Expression):
|
|
231
|
+
other = other.eval(_default_env)
|
|
232
|
+
if isinstance(other, ConstType) and other == 0:
|
|
233
|
+
return 1
|
|
234
|
+
if isinstance(other, ConstType) and other == 1:
|
|
235
|
+
return self
|
|
154
236
|
return BinaryExpression(self, other, operator.pow)
|
|
155
237
|
|
|
156
238
|
def __rpow__(self, other):
|
|
239
|
+
if isinstance(other, Expression):
|
|
240
|
+
other = other.eval(_default_env)
|
|
241
|
+
if isinstance(other, ConstType) and other == 0:
|
|
242
|
+
return 0
|
|
157
243
|
return BinaryExpression(other, self, operator.pow)
|
|
158
244
|
|
|
159
245
|
def __neg__(self):
|
|
@@ -163,32 +249,52 @@ class Expression():
|
|
|
163
249
|
return UnaryExpression(self, operator.pos)
|
|
164
250
|
|
|
165
251
|
def __eq__(self, other):
|
|
252
|
+
if isinstance(other, Expression):
|
|
253
|
+
other = other.eval(_default_env)
|
|
166
254
|
return BinaryExpression(self, other, operator.eq)
|
|
167
255
|
|
|
168
256
|
def __ne__(self, other):
|
|
257
|
+
if isinstance(other, Expression):
|
|
258
|
+
other = other.eval(_default_env)
|
|
169
259
|
return BinaryExpression(self, other, operator.ne)
|
|
170
260
|
|
|
171
261
|
def __lt__(self, other):
|
|
262
|
+
if isinstance(other, Expression):
|
|
263
|
+
other = other.eval(_default_env)
|
|
172
264
|
return BinaryExpression(self, other, operator.lt)
|
|
173
265
|
|
|
174
266
|
def __le__(self, other):
|
|
267
|
+
if isinstance(other, Expression):
|
|
268
|
+
other = other.eval(_default_env)
|
|
175
269
|
return BinaryExpression(self, other, operator.le)
|
|
176
270
|
|
|
177
271
|
def __gt__(self, other):
|
|
272
|
+
if isinstance(other, Expression):
|
|
273
|
+
other = other.eval(_default_env)
|
|
178
274
|
return BinaryExpression(self, other, operator.gt)
|
|
179
275
|
|
|
180
276
|
def __ge__(self, other):
|
|
277
|
+
if isinstance(other, Expression):
|
|
278
|
+
other = other.eval(_default_env)
|
|
181
279
|
return BinaryExpression(self, other, operator.ge)
|
|
182
280
|
|
|
183
281
|
def __getitem__(self, other):
|
|
282
|
+
if isinstance(other, Expression):
|
|
283
|
+
other = other.eval(_default_env)
|
|
184
284
|
return ObjectMethod(self, '__getitem__', other)
|
|
185
285
|
|
|
186
286
|
def __getattr__(self, other):
|
|
287
|
+
if isinstance(other, Expression):
|
|
288
|
+
other = other.eval(_default_env)
|
|
187
289
|
return ObjectMethod(self, '__getattr__', other)
|
|
188
290
|
|
|
189
291
|
def __call__(self, *args):
|
|
292
|
+
args = [
|
|
293
|
+
o.eval(_default_env) if isinstance(o, Expression) else o
|
|
294
|
+
for o in args
|
|
295
|
+
]
|
|
190
296
|
return ObjectMethod(self, '__call__', *args)
|
|
191
|
-
|
|
297
|
+
|
|
192
298
|
def __round__(self, n=None):
|
|
193
299
|
return self
|
|
194
300
|
|
|
@@ -217,6 +323,14 @@ class UnaryExpression(Expression):
|
|
|
217
323
|
self.a = a
|
|
218
324
|
self.op = op
|
|
219
325
|
|
|
326
|
+
def __getstate__(self) -> dict:
|
|
327
|
+
return {'a': self.a, 'op': self.op}
|
|
328
|
+
|
|
329
|
+
def __setstate__(self, state: dict):
|
|
330
|
+
self.a = state['a']
|
|
331
|
+
self.op = state['op']
|
|
332
|
+
self.cache = _empty
|
|
333
|
+
|
|
220
334
|
def symbols(self) -> list[str]:
|
|
221
335
|
if isinstance(self.a, Expression):
|
|
222
336
|
return self.a.symbols()
|
|
@@ -243,7 +357,7 @@ class UnaryExpression(Expression):
|
|
|
243
357
|
return self.op(self.a.d(x))
|
|
244
358
|
else:
|
|
245
359
|
return 0
|
|
246
|
-
|
|
360
|
+
|
|
247
361
|
def __repr__(self) -> str:
|
|
248
362
|
return f"{self.op.__name__}({self.a!r})"
|
|
249
363
|
|
|
@@ -256,6 +370,15 @@ class BinaryExpression(Expression):
|
|
|
256
370
|
self.b = b
|
|
257
371
|
self.op = op
|
|
258
372
|
|
|
373
|
+
def __getstate__(self) -> dict:
|
|
374
|
+
return {'a': self.a, 'b': self.b, 'op': self.op}
|
|
375
|
+
|
|
376
|
+
def __setstate__(self, state: dict):
|
|
377
|
+
self.a = state['a']
|
|
378
|
+
self.b = state['b']
|
|
379
|
+
self.op = state['op']
|
|
380
|
+
self.cache = _empty
|
|
381
|
+
|
|
259
382
|
def symbols(self) -> list[str]:
|
|
260
383
|
symbs = set()
|
|
261
384
|
if isinstance(self.a, Expression):
|
|
@@ -300,7 +423,7 @@ class BinaryExpression(Expression):
|
|
|
300
423
|
return 0
|
|
301
424
|
else:
|
|
302
425
|
return 0
|
|
303
|
-
|
|
426
|
+
|
|
304
427
|
def __repr__(self) -> str:
|
|
305
428
|
return f"({self.a!r} {self.op.__name__} {self.b!r})"
|
|
306
429
|
|
|
@@ -313,6 +436,15 @@ class ObjectMethod(Expression):
|
|
|
313
436
|
self.method = method
|
|
314
437
|
self.args = args
|
|
315
438
|
|
|
439
|
+
def __getstate__(self) -> dict:
|
|
440
|
+
return {'obj': self.obj, 'method': self.method, 'args': self.args}
|
|
441
|
+
|
|
442
|
+
def __setstate__(self, state: dict):
|
|
443
|
+
self.obj = state['obj']
|
|
444
|
+
self.method = state['method']
|
|
445
|
+
self.args = state['args']
|
|
446
|
+
self.cache = _empty
|
|
447
|
+
|
|
316
448
|
def symbols(self) -> list[str]:
|
|
317
449
|
symbs = set()
|
|
318
450
|
if isinstance(self.obj, Expression):
|
|
@@ -334,6 +466,12 @@ class ObjectMethod(Expression):
|
|
|
334
466
|
else:
|
|
335
467
|
return getattr(obj, self.method)(*args)
|
|
336
468
|
|
|
469
|
+
def __repr__(self):
|
|
470
|
+
if self.method == '__call__':
|
|
471
|
+
return f"{self.obj!r}({', '.join(map(repr, self.args))})"
|
|
472
|
+
else:
|
|
473
|
+
return f"{self.obj!r}.{self.method}({', '.join(map(repr, self.args))})"
|
|
474
|
+
|
|
337
475
|
|
|
338
476
|
class Symbol(Expression):
|
|
339
477
|
|
|
@@ -341,6 +479,13 @@ class Symbol(Expression):
|
|
|
341
479
|
super().__init__()
|
|
342
480
|
self.name = name
|
|
343
481
|
|
|
482
|
+
def __getstate__(self) -> dict:
|
|
483
|
+
return {'name': self.name}
|
|
484
|
+
|
|
485
|
+
def __setstate__(self, state: dict):
|
|
486
|
+
self.name = state['name']
|
|
487
|
+
self.cache = _empty
|
|
488
|
+
|
|
344
489
|
def symbols(self) -> list[str]:
|
|
345
490
|
return [self.name]
|
|
346
491
|
|
|
@@ -355,6 +500,6 @@ class Symbol(Expression):
|
|
|
355
500
|
return 1
|
|
356
501
|
else:
|
|
357
502
|
return 0
|
|
358
|
-
|
|
503
|
+
|
|
359
504
|
def __repr__(self) -> str:
|
|
360
505
|
return self.name
|