QuLab 2.10.10__cp313-cp313-macosx_10_13_universal2.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.
- qulab/__init__.py +33 -0
- qulab/__main__.py +4 -0
- qulab/cli/__init__.py +0 -0
- qulab/cli/commands.py +30 -0
- qulab/cli/config.py +170 -0
- qulab/cli/decorators.py +28 -0
- qulab/dicttree.py +523 -0
- qulab/executor/__init__.py +5 -0
- qulab/executor/analyze.py +188 -0
- qulab/executor/cli.py +434 -0
- qulab/executor/load.py +563 -0
- qulab/executor/registry.py +185 -0
- qulab/executor/schedule.py +543 -0
- qulab/executor/storage.py +615 -0
- qulab/executor/template.py +259 -0
- qulab/executor/utils.py +194 -0
- qulab/expression.py +827 -0
- qulab/fun.cpython-313-darwin.so +0 -0
- qulab/monitor/__init__.py +1 -0
- qulab/monitor/__main__.py +8 -0
- qulab/monitor/config.py +41 -0
- qulab/monitor/dataset.py +77 -0
- qulab/monitor/event_queue.py +54 -0
- qulab/monitor/mainwindow.py +234 -0
- qulab/monitor/monitor.py +115 -0
- qulab/monitor/ploter.py +123 -0
- qulab/monitor/qt_compat.py +16 -0
- qulab/monitor/toolbar.py +265 -0
- qulab/scan/__init__.py +2 -0
- qulab/scan/curd.py +221 -0
- qulab/scan/models.py +554 -0
- qulab/scan/optimize.py +76 -0
- qulab/scan/query.py +387 -0
- qulab/scan/record.py +603 -0
- qulab/scan/scan.py +1166 -0
- qulab/scan/server.py +450 -0
- qulab/scan/space.py +213 -0
- qulab/scan/utils.py +234 -0
- qulab/storage/__init__.py +0 -0
- qulab/storage/__main__.py +51 -0
- qulab/storage/backend/__init__.py +0 -0
- qulab/storage/backend/redis.py +204 -0
- qulab/storage/base_dataset.py +352 -0
- qulab/storage/chunk.py +60 -0
- qulab/storage/dataset.py +127 -0
- qulab/storage/file.py +273 -0
- qulab/storage/models/__init__.py +22 -0
- qulab/storage/models/base.py +4 -0
- qulab/storage/models/config.py +28 -0
- qulab/storage/models/file.py +89 -0
- qulab/storage/models/ipy.py +58 -0
- qulab/storage/models/models.py +88 -0
- qulab/storage/models/record.py +161 -0
- qulab/storage/models/report.py +22 -0
- qulab/storage/models/tag.py +93 -0
- qulab/storage/storage.py +95 -0
- qulab/sys/__init__.py +2 -0
- qulab/sys/chat.py +688 -0
- qulab/sys/device/__init__.py +3 -0
- qulab/sys/device/basedevice.py +255 -0
- qulab/sys/device/loader.py +86 -0
- qulab/sys/device/utils.py +79 -0
- qulab/sys/drivers/FakeInstrument.py +68 -0
- qulab/sys/drivers/__init__.py +0 -0
- qulab/sys/ipy_events.py +125 -0
- qulab/sys/net/__init__.py +0 -0
- qulab/sys/net/bencoder.py +205 -0
- qulab/sys/net/cli.py +169 -0
- qulab/sys/net/dhcp.py +543 -0
- qulab/sys/net/dhcpd.py +176 -0
- qulab/sys/net/kad.py +1142 -0
- qulab/sys/net/kcp.py +192 -0
- qulab/sys/net/nginx.py +194 -0
- qulab/sys/progress.py +190 -0
- qulab/sys/rpc/__init__.py +0 -0
- qulab/sys/rpc/client.py +0 -0
- qulab/sys/rpc/exceptions.py +96 -0
- qulab/sys/rpc/msgpack.py +1052 -0
- qulab/sys/rpc/msgpack.pyi +41 -0
- qulab/sys/rpc/router.py +35 -0
- qulab/sys/rpc/rpc.py +412 -0
- qulab/sys/rpc/serialize.py +139 -0
- qulab/sys/rpc/server.py +29 -0
- qulab/sys/rpc/socket.py +29 -0
- qulab/sys/rpc/utils.py +25 -0
- qulab/sys/rpc/worker.py +0 -0
- qulab/sys/rpc/zmq_socket.py +227 -0
- qulab/tools/__init__.py +0 -0
- qulab/tools/connection_helper.py +39 -0
- qulab/typing.py +2 -0
- qulab/utils.py +95 -0
- qulab/version.py +1 -0
- qulab/visualization/__init__.py +188 -0
- qulab/visualization/__main__.py +71 -0
- qulab/visualization/_autoplot.py +464 -0
- qulab/visualization/plot_circ.py +319 -0
- qulab/visualization/plot_layout.py +408 -0
- qulab/visualization/plot_seq.py +242 -0
- qulab/visualization/qdat.py +152 -0
- qulab/visualization/rot3d.py +23 -0
- qulab/visualization/widgets.py +86 -0
- qulab-2.10.10.dist-info/METADATA +110 -0
- qulab-2.10.10.dist-info/RECORD +107 -0
- qulab-2.10.10.dist-info/WHEEL +5 -0
- qulab-2.10.10.dist-info/entry_points.txt +2 -0
- qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
- qulab-2.10.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
from datetime import date, datetime
|
2
|
+
from typing import Sequence
|
3
|
+
|
4
|
+
import dill
|
5
|
+
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
|
6
|
+
from sqlalchemy.orm import Session, relationship
|
7
|
+
|
8
|
+
from .base import Base
|
9
|
+
from .config import Config
|
10
|
+
from .tag import get_object_with_tags, has_tags, tag
|
11
|
+
|
12
|
+
|
13
|
+
@has_tags
|
14
|
+
class Record(Base):
|
15
|
+
__tablename__ = 'records'
|
16
|
+
|
17
|
+
id = Column(Integer, primary_key=True)
|
18
|
+
ctime = Column(DateTime, default=datetime.utcnow)
|
19
|
+
mtime = Column(DateTime, default=datetime.utcnow)
|
20
|
+
atime = Column(DateTime, default=datetime.utcnow)
|
21
|
+
name = Column(String)
|
22
|
+
datafile_id = Column(Integer, ForeignKey('files.id'))
|
23
|
+
configfile_id = Column(Integer, ForeignKey('files.id'))
|
24
|
+
meta_id = Column(Integer, ForeignKey('files.id'))
|
25
|
+
|
26
|
+
datafile = relationship("File", foreign_keys=[datafile_id])
|
27
|
+
configfile = relationship("File", foreign_keys=[configfile_id])
|
28
|
+
metafile = relationship("File", foreign_keys=[meta_id])
|
29
|
+
|
30
|
+
@property
|
31
|
+
def data(self) -> dict:
|
32
|
+
result = dill.loads(self.datafile.read())
|
33
|
+
result['meta'] = self.meta
|
34
|
+
result['meta']['config'] = self.config
|
35
|
+
return result
|
36
|
+
|
37
|
+
@data.setter
|
38
|
+
def data(self, data: dict):
|
39
|
+
meta = data.pop('meta', {})
|
40
|
+
self.meta = meta
|
41
|
+
self.datafile.write(dill.dumps(data))
|
42
|
+
|
43
|
+
@property
|
44
|
+
def config(self) -> dict:
|
45
|
+
return dill.loads(self.configfile.read())
|
46
|
+
|
47
|
+
@config.setter
|
48
|
+
def config(self, data: dict):
|
49
|
+
self.configfile.write(dill.dumps(data))
|
50
|
+
|
51
|
+
@property
|
52
|
+
def meta(self) -> dict:
|
53
|
+
meta = dill.loads(self.metafile.read())
|
54
|
+
meta['id'] = self.id
|
55
|
+
meta['name'] = self.name
|
56
|
+
meta['ctime'] = self.ctime
|
57
|
+
meta['mtime'] = self.mtime
|
58
|
+
meta['atime'] = self.atime
|
59
|
+
return meta
|
60
|
+
|
61
|
+
@meta.setter
|
62
|
+
def meta(self, data: dict):
|
63
|
+
if 'config' in data:
|
64
|
+
config = data.pop('config', {})
|
65
|
+
self.config = config
|
66
|
+
self.metafile.write(dill.dumps(data))
|
67
|
+
|
68
|
+
def export(self, path):
|
69
|
+
with open(path, 'wb') as f:
|
70
|
+
f.write(self.data)
|
71
|
+
|
72
|
+
def __init__(self, name: str, data: bytes) -> None:
|
73
|
+
self.name = name
|
74
|
+
self.data = data
|
75
|
+
|
76
|
+
def __str__(self) -> str:
|
77
|
+
return self.name
|
78
|
+
|
79
|
+
def __repr__(self) -> str:
|
80
|
+
return f'Record({self.name!r})'
|
81
|
+
|
82
|
+
|
83
|
+
def query_record(
|
84
|
+
session: Session,
|
85
|
+
offset: int = 0,
|
86
|
+
limit: int = 10,
|
87
|
+
name: str | None = None,
|
88
|
+
tags: Sequence[str] = (),
|
89
|
+
before: datetime | date | None = None,
|
90
|
+
after: datetime | date | None = None) -> tuple[int, dict, dict]:
|
91
|
+
from waveforms.dicttree import foldDict
|
92
|
+
|
93
|
+
local_tm = datetime.fromtimestamp(0)
|
94
|
+
utc_tm = datetime.utcfromtimestamp(0)
|
95
|
+
tz_offset = local_tm - utc_tm
|
96
|
+
table = {'header': ['ID', 'Name', 'tags', 'created time'], 'body': []}
|
97
|
+
name_lst = sorted(
|
98
|
+
set([
|
99
|
+
n for n, *_ in get_object_with_tags(session.query(Record.name),
|
100
|
+
Record, *tags).all()
|
101
|
+
]))
|
102
|
+
name_tr = foldDict(dict([(name, None) for name in name_lst]))
|
103
|
+
|
104
|
+
query = get_object_with_tags(session, Record, *tags)
|
105
|
+
|
106
|
+
if name is not None:
|
107
|
+
if name.endswith('*'):
|
108
|
+
query = query.filter(Record.name.like(name[:-1] + '%'))
|
109
|
+
else:
|
110
|
+
query = query.filter(Record.name == name)
|
111
|
+
if before is not None:
|
112
|
+
if isinstance(before, date):
|
113
|
+
before = datetime(before.year, before.month, before.day)
|
114
|
+
query = query.filter(Record.ctime <= before - tz_offset)
|
115
|
+
if after is not None:
|
116
|
+
if isinstance(after, date):
|
117
|
+
after = datetime(after.year, after.month, after.day)
|
118
|
+
query = query.filter(Record.ctime >= after - tz_offset)
|
119
|
+
total = query.count()
|
120
|
+
for r in query.order_by(Record.ctime.desc()).limit(limit).offset(offset):
|
121
|
+
tags = sorted([t.text for t in r.tags])
|
122
|
+
ctime = r.ctime + tz_offset
|
123
|
+
row = [r.id, r.name, tags, ctime]
|
124
|
+
table['body'].append(row)
|
125
|
+
|
126
|
+
return total, name_tr, table
|
127
|
+
|
128
|
+
|
129
|
+
def update_tags(session: Session,
|
130
|
+
record_id: int,
|
131
|
+
tags: Sequence[str],
|
132
|
+
append: bool = False):
|
133
|
+
record = session.get(Record, record_id)
|
134
|
+
if record is None:
|
135
|
+
return False
|
136
|
+
if append:
|
137
|
+
old = [t.text for t in record.tags]
|
138
|
+
for t in old:
|
139
|
+
if t not in tags:
|
140
|
+
tags.append(t)
|
141
|
+
record.tags = [tag(session, t) for t in tags]
|
142
|
+
try:
|
143
|
+
session.commit()
|
144
|
+
except Exception:
|
145
|
+
session.rollback()
|
146
|
+
return False
|
147
|
+
return True
|
148
|
+
|
149
|
+
|
150
|
+
def remove_tags(session: Session, record_id: int, tags: Sequence[str]):
|
151
|
+
record = session.get(Record, record_id)
|
152
|
+
if record is None:
|
153
|
+
return False
|
154
|
+
old = [t.text for t in record.tags]
|
155
|
+
record.tags = [tag(session, t) for t in old if t not in tags]
|
156
|
+
try:
|
157
|
+
session.commit()
|
158
|
+
except Exception:
|
159
|
+
session.rollback()
|
160
|
+
return False
|
161
|
+
return True
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from .base import Base
|
2
|
+
from .tag import Tag, has_tags
|
3
|
+
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
|
4
|
+
from sqlalchemy.orm import relationship
|
5
|
+
from datetime import datetime
|
6
|
+
|
7
|
+
@has_tags
|
8
|
+
class Report(Base):
|
9
|
+
__tablename__ = 'reports'
|
10
|
+
|
11
|
+
id = Column(Integer, primary_key=True)
|
12
|
+
ctime = Column(DateTime, default=datetime.utcnow)
|
13
|
+
mtime = Column(DateTime, default=datetime.utcnow)
|
14
|
+
|
15
|
+
name = Column(String)
|
16
|
+
datafile_id = Column(Integer, ForeignKey('files.id'))
|
17
|
+
configfile_id = Column(Integer, ForeignKey('files.id'))
|
18
|
+
meta_id = Column(Integer, ForeignKey('files.id'))
|
19
|
+
|
20
|
+
datafile = relationship("File", foreign_keys=[datafile_id])
|
21
|
+
configfile = relationship("File", foreign_keys=[configfile_id])
|
22
|
+
metafile = relationship("File", foreign_keys=[meta_id])
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from typing import Type
|
2
|
+
|
3
|
+
from sqlalchemy import Column, ForeignKey, Integer, String, Table
|
4
|
+
from sqlalchemy.orm import Query, Session, aliased, relationship
|
5
|
+
from sqlalchemy.orm.exc import NoResultFound
|
6
|
+
|
7
|
+
from . import Base
|
8
|
+
|
9
|
+
|
10
|
+
class Tag(Base):
|
11
|
+
__tablename__ = 'tags'
|
12
|
+
|
13
|
+
id = Column(Integer, primary_key=True)
|
14
|
+
name = Column(String, unique=True)
|
15
|
+
|
16
|
+
def __init__(self, name: str) -> None:
|
17
|
+
self.name = name
|
18
|
+
|
19
|
+
def __str__(self) -> str:
|
20
|
+
return self.name
|
21
|
+
|
22
|
+
def __repr__(self) -> str:
|
23
|
+
return f'Tag({self.name!r})'
|
24
|
+
|
25
|
+
|
26
|
+
def has_tags(cls: Type[Base]) -> Type[Base]:
|
27
|
+
table = Table(
|
28
|
+
f'{cls.__tablename__}_tags', Base.metadata,
|
29
|
+
Column('item_id',
|
30
|
+
ForeignKey(f'{cls.__tablename__}.id'),
|
31
|
+
primary_key=True),
|
32
|
+
Column('tag_id', ForeignKey('tags.id'), primary_key=True))
|
33
|
+
|
34
|
+
cls.tags = relationship("Tag", secondary=table, backref=cls.__tablename__)
|
35
|
+
|
36
|
+
def add_tag(self, tag: Tag):
|
37
|
+
if tag not in self.tags:
|
38
|
+
self.tags.append(tag)
|
39
|
+
|
40
|
+
def remove_tag(self, tag: Tag):
|
41
|
+
if tag in self.tags:
|
42
|
+
self.tags.remove(tag)
|
43
|
+
|
44
|
+
cls.add_tag = add_tag
|
45
|
+
cls.remove_tag = remove_tag
|
46
|
+
|
47
|
+
return cls
|
48
|
+
|
49
|
+
|
50
|
+
def tag(session: Session, tag_text: str) -> Tag:
|
51
|
+
"""Get a tag from the database or create a new if not exists."""
|
52
|
+
try:
|
53
|
+
return session.query(Tag).filter(Tag.text == tag_text).one()
|
54
|
+
except NoResultFound:
|
55
|
+
tag = Tag(text=tag_text)
|
56
|
+
return tag
|
57
|
+
|
58
|
+
|
59
|
+
def get_object_with_tags(session: Session, cls: Type[Base],
|
60
|
+
*tags: str) -> Query:
|
61
|
+
"""
|
62
|
+
Query objects with the given tags.
|
63
|
+
|
64
|
+
Parameters
|
65
|
+
----------
|
66
|
+
session : :class:`sqlalchemy.orm.Session`
|
67
|
+
The database session.
|
68
|
+
cls : :class:`sqlalchemy.orm.Mapper`
|
69
|
+
The object class.
|
70
|
+
tags : str
|
71
|
+
The tags.
|
72
|
+
|
73
|
+
Returns
|
74
|
+
-------
|
75
|
+
:class:`sqlalchemy.orm.Query`
|
76
|
+
The query.
|
77
|
+
"""
|
78
|
+
if isinstance(session, Query):
|
79
|
+
q = session
|
80
|
+
else:
|
81
|
+
q = session.query(cls)
|
82
|
+
if not hasattr(cls, 'tags'):
|
83
|
+
return []
|
84
|
+
|
85
|
+
aliase = {tag: aliased(Tag) for tag in tags}
|
86
|
+
|
87
|
+
for tag, a in aliase.items():
|
88
|
+
q = q.join(a, cls.tags)
|
89
|
+
if '*' in tag:
|
90
|
+
q = q.filter(a.text.like(tag.replace('*', '%')))
|
91
|
+
else:
|
92
|
+
q = q.filter(a.text == tag)
|
93
|
+
return q
|
qulab/storage/storage.py
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
import shutil
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from datetime import date, datetime
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any
|
6
|
+
from uuid import UUID, uuid1, uuid5
|
7
|
+
|
8
|
+
from .file import load
|
9
|
+
|
10
|
+
|
11
|
+
class BaseStorage(ABC):
|
12
|
+
|
13
|
+
@abstractmethod
|
14
|
+
def _create_dataset(self) -> UUID:
|
15
|
+
"""Create a new dataset and return its id"""
|
16
|
+
pass
|
17
|
+
|
18
|
+
@abstractmethod
|
19
|
+
def _delet_dataset(self, id: UUID):
|
20
|
+
"""Delete a dataset"""
|
21
|
+
pass
|
22
|
+
|
23
|
+
@abstractmethod
|
24
|
+
def _dataset_getitem(self, id: UUID, key: str):
|
25
|
+
"""Get a dataset item"""
|
26
|
+
pass
|
27
|
+
|
28
|
+
@abstractmethod
|
29
|
+
def _dataset_setitem(self, id: UUID, key: str, value: Any):
|
30
|
+
"""Set a dataset item"""
|
31
|
+
pass
|
32
|
+
|
33
|
+
@abstractmethod
|
34
|
+
def _dataset_delitem(self, id: UUID, key: str):
|
35
|
+
"""Delete a dataset item"""
|
36
|
+
pass
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
def _dataset_keys(self, id: UUID):
|
40
|
+
"""Get dataset keys"""
|
41
|
+
pass
|
42
|
+
|
43
|
+
@abstractmethod
|
44
|
+
def _dataset_append(self, id: UUID, dataframe: dict[str, Any]):
|
45
|
+
"""Append a dataframe to a dataset"""
|
46
|
+
pass
|
47
|
+
|
48
|
+
@abstractmethod
|
49
|
+
def _dataset_item_append(self, id: UUID, key: str, value: Any):
|
50
|
+
"""Append a value to a dataset item"""
|
51
|
+
pass
|
52
|
+
|
53
|
+
|
54
|
+
class Storage(BaseStorage):
|
55
|
+
|
56
|
+
def __init__(self, base: Path | str):
|
57
|
+
if isinstance(base, str):
|
58
|
+
base = Path(base)
|
59
|
+
self.base = base
|
60
|
+
self.namespace = uuid5(UUID('f89f735a-791e-5a43-9ba6-f28d58601544'),
|
61
|
+
base.as_posix())
|
62
|
+
|
63
|
+
def clear(self):
|
64
|
+
shutil.rmtree(self.base)
|
65
|
+
self.base.mkdir(parents=True)
|
66
|
+
|
67
|
+
def uuid(self,
|
68
|
+
name: str | None = None,
|
69
|
+
namespace: UUID | None = None,
|
70
|
+
seq: int = 0) -> UUID:
|
71
|
+
if name is None:
|
72
|
+
name = str(uuid1())
|
73
|
+
if namespace is None:
|
74
|
+
return uuid5(self.namespace, f"{name}{seq}")
|
75
|
+
else:
|
76
|
+
return uuid5(namespace, f"{name}{seq}")
|
77
|
+
|
78
|
+
def uuid_to_path(self, uuid: UUID) -> Path:
|
79
|
+
return self.base / uuid.hex[:2] / uuid.hex[2:4] / uuid.hex[4:]
|
80
|
+
|
81
|
+
def create_dataset(self, title: str, tags: list[str] = []):
|
82
|
+
from .dataset import Dataset
|
83
|
+
id = self.uuid()
|
84
|
+
return Dataset(id, self)
|
85
|
+
|
86
|
+
def get_dataset(self, id: UUID):
|
87
|
+
from .dataset import Dataset
|
88
|
+
return Dataset(id, self)
|
89
|
+
|
90
|
+
def remove_dataset(self, id: UUID):
|
91
|
+
shutil.rmtree(self.uuid_to_path(id))
|
92
|
+
|
93
|
+
def query(self, title_pattern: str, tags: list[str],
|
94
|
+
before: datetime | date, after: datetime | date):
|
95
|
+
pass
|
qulab/sys/__init__.py
ADDED