QuLab 2.0.0__py3-none-any.whl → 2.10.12__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.
Files changed (69) hide show
  1. qulab/__init__.py +33 -1
  2. qulab/__main__.py +4 -0
  3. qulab/cli/commands.py +31 -0
  4. qulab/cli/config.py +170 -0
  5. qulab/cli/decorators.py +28 -0
  6. qulab/executor/__init__.py +5 -0
  7. qulab/executor/analyze.py +188 -0
  8. qulab/executor/cli.py +447 -0
  9. qulab/executor/load.py +563 -0
  10. qulab/executor/registry.py +185 -0
  11. qulab/executor/schedule.py +543 -0
  12. qulab/executor/storage.py +615 -0
  13. qulab/executor/template.py +259 -0
  14. qulab/executor/utils.py +194 -0
  15. qulab/monitor/__init__.py +1 -93
  16. qulab/monitor/__main__.py +8 -0
  17. qulab/monitor/monitor.py +115 -0
  18. qulab/scan/__init__.py +2 -4
  19. qulab/scan/curd.py +221 -0
  20. qulab/scan/models.py +554 -0
  21. qulab/scan/optimize.py +76 -0
  22. qulab/scan/query.py +387 -0
  23. qulab/scan/record.py +603 -0
  24. qulab/scan/scan.py +1166 -0
  25. qulab/scan/server.py +450 -0
  26. qulab/scan/space.py +213 -0
  27. qulab/scan/utils.py +230 -34
  28. qulab/sys/__init__.py +2 -0
  29. qulab/sys/device/basedevice.py +50 -16
  30. qulab/sys/device/loader.py +1 -1
  31. qulab/sys/device/utils.py +46 -13
  32. qulab/sys/drivers/FakeInstrument.py +36 -20
  33. qulab/sys/net/nginx.py +16 -14
  34. qulab/sys/rpc/router.py +35 -0
  35. qulab/sys/rpc/zmq_socket.py +227 -0
  36. qulab/tools/__init__.py +0 -0
  37. qulab/tools/connection_helper.py +39 -0
  38. qulab/typing.py +2 -0
  39. qulab/utils.py +95 -0
  40. qulab/version.py +1 -1
  41. qulab/visualization/__init__.py +188 -0
  42. qulab/visualization/__main__.py +71 -0
  43. qulab/visualization/_autoplot.py +464 -0
  44. qulab/visualization/plot_circ.py +319 -0
  45. qulab/visualization/plot_layout.py +408 -0
  46. qulab/visualization/plot_seq.py +242 -0
  47. qulab/visualization/qdat.py +152 -0
  48. qulab/visualization/rot3d.py +23 -0
  49. qulab/visualization/widgets.py +86 -0
  50. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info}/METADATA +28 -12
  51. qulab-2.10.12.dist-info/RECORD +104 -0
  52. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info}/WHEEL +1 -1
  53. qulab-2.10.12.dist-info/entry_points.txt +2 -0
  54. QuLab-2.0.0.dist-info/RECORD +0 -71
  55. qulab/monitor/multiploter/__init__.py +0 -1
  56. qulab/scan/base.py +0 -544
  57. qulab/scan/expression.py +0 -360
  58. qulab/scan/scanner.py +0 -244
  59. qulab/scan/transforms.py +0 -16
  60. /qulab/{scan/dataset.py → cli/__init__.py} +0 -0
  61. /qulab/monitor/{multiploter/config.py → config.py} +0 -0
  62. /qulab/monitor/{multiploter/dataset.py → dataset.py} +0 -0
  63. /qulab/monitor/{multiploter/event_queue.py → event_queue.py} +0 -0
  64. /qulab/monitor/{multiploter/main.py → mainwindow.py} +0 -0
  65. /qulab/monitor/{multiploter/ploter.py → ploter.py} +0 -0
  66. /qulab/monitor/{multiploter/qt_compat.py → qt_compat.py} +0 -0
  67. /qulab/monitor/{multiploter/toolbar.py → toolbar.py} +0 -0
  68. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info/licenses}/LICENSE +0 -0
  69. {QuLab-2.0.0.dist-info → qulab-2.10.12.dist-info}/top_level.txt +0 -0
qulab/scan/curd.py ADDED
@@ -0,0 +1,221 @@
1
+ import lzma
2
+ import pickle
3
+ from datetime import date, datetime, timezone
4
+ from pathlib import Path
5
+ from typing import Sequence, Type, Union
6
+
7
+ from qlispreg.dicttree import foldDict
8
+ from sqlalchemy.orm import Query, Session, aliased
9
+ from sqlalchemy.orm.exc import NoResultFound
10
+ from sqlalchemy.orm.session import Session
11
+
12
+ from .models import (Cell, Comment, Config, InputText, Notebook, Record,
13
+ Report, Sample, Tag, utcnow)
14
+
15
+
16
+ def tag(session: Session, tag_text: str) -> Tag:
17
+ """Get a tag from the database or create a new if not exists."""
18
+ try:
19
+ return session.query(Tag).filter(Tag.text == tag_text).one()
20
+ except NoResultFound:
21
+ tag = Tag(text=tag_text)
22
+ return tag
23
+
24
+
25
+ def tag_it(session: Session, tag_text: str, obj: Union[Sample, Record,
26
+ Report]) -> Tag:
27
+ """Tag an object."""
28
+ if obj.id is None:
29
+ session.add(obj)
30
+ obj.tags.append(tag(session, tag_text))
31
+ else:
32
+ session.query(type(obj)).filter(
33
+ type(obj).id == obj.id).one().tags.append(tag(session, tag_text))
34
+ session.commit()
35
+
36
+
37
+ def get_object_with_tags(session: Session,
38
+ cls: Union[Type[Comment], Type[Sample], Type[Record],
39
+ Type[Report]], *tags: str) -> Query:
40
+ """
41
+ Query objects with the given tags.
42
+
43
+ Parameters
44
+ ----------
45
+ session : :class:`sqlalchemy.orm.Session`
46
+ The database session.
47
+ cls : :class:`sqlalchemy.orm.Mapper`
48
+ The object class.
49
+ tags : str
50
+ The tags.
51
+
52
+ Returns
53
+ -------
54
+ :class:`sqlalchemy.orm.Query`
55
+ The query.
56
+ """
57
+ if isinstance(session, Query):
58
+ q = session
59
+ else:
60
+ q = session.query(cls)
61
+ if not hasattr(cls, 'tags'):
62
+ return []
63
+
64
+ aliase = {tag: aliased(Tag) for tag in tags}
65
+
66
+ for tag, a in aliase.items():
67
+ q = q.join(a, cls.tags)
68
+ if '*' in tag:
69
+ q = q.filter(a.text.like(tag.replace('*', '%')))
70
+ else:
71
+ q = q.filter(a.text == tag)
72
+ return q
73
+
74
+
75
+ def query_record(session: Session,
76
+ offset: int = 0,
77
+ limit: int = 10,
78
+ app: str | None = None,
79
+ tags: Sequence[str] = (),
80
+ before: datetime | date | None = None,
81
+ after: datetime | date | None = None):
82
+ tz_offset = datetime.now(timezone.utc).astimezone().utcoffset()
83
+ table = {'header': ['ID', 'App', 'tags', 'created time'], 'body': []}
84
+ apps = sorted(
85
+ set([
86
+ n for n, *_ in get_object_with_tags(session.query(Record.app),
87
+ Record, *tags).all()
88
+ ]))
89
+ apps = foldDict(dict([(app, None) for app in apps]))
90
+
91
+ query = get_object_with_tags(session, Record, *tags)
92
+
93
+ if app is not None:
94
+ if app.endswith('*'):
95
+ query = query.filter(Record.app.like(app[:-1] + '%'))
96
+ else:
97
+ query = query.filter(Record.app == app)
98
+ if before is not None:
99
+ if isinstance(before, date):
100
+ before = datetime(before.year, before.month, before.day)
101
+ query = query.filter(Record.ctime <= before - tz_offset)
102
+ if after is not None:
103
+ if isinstance(after, date):
104
+ after = datetime(after.year, after.month, after.day)
105
+ query = query.filter(Record.ctime >= after - tz_offset)
106
+ total = query.count()
107
+ for r in query.order_by(Record.ctime.desc()).limit(limit).offset(offset):
108
+ tags = sorted([t.text for t in r.tags])
109
+ ctime = r.ctime + tz_offset
110
+ row = [r.id, r.app, tags, ctime]
111
+ table['body'].append(row)
112
+
113
+ return total, apps, table
114
+
115
+
116
+ def update_tags(session: Session,
117
+ record_id: int,
118
+ tags: Sequence[str],
119
+ append: bool = False):
120
+ record = session.get(Record, record_id)
121
+ if record is None:
122
+ return False
123
+ if append:
124
+ old = [t.text for t in record.tags]
125
+ for t in old:
126
+ if t not in tags:
127
+ tags.append(t)
128
+ record.tags = [tag(session, t) for t in tags]
129
+ try:
130
+ session.commit()
131
+ except Exception:
132
+ session.rollback()
133
+ return False
134
+ return True
135
+
136
+
137
+ def remove_tags(session: Session, record_id: int, tags: Sequence[str]):
138
+ record = session.get(Record, record_id)
139
+ if record is None:
140
+ return False
141
+ old = [t.text for t in record.tags]
142
+ record.tags = [tag(session, t) for t in old if t not in tags]
143
+ try:
144
+ session.commit()
145
+ except Exception:
146
+ session.rollback()
147
+ return False
148
+ return True
149
+
150
+
151
+ def create_notebook(session: Session, notebook_name: str) -> Notebook:
152
+ """Create a notebook in the database."""
153
+ notebook = Notebook(name=notebook_name)
154
+ session.add(notebook)
155
+ return notebook
156
+
157
+
158
+ def create_input_text(session: Session, input_text: str) -> InputText:
159
+ """Create an input text in the database."""
160
+ input = InputText()
161
+ input.text = input_text
162
+ try:
163
+ input = session.query(InputText).filter(
164
+ InputText.hash == input.hash,
165
+ InputText.text_field == input_text).one()
166
+ except NoResultFound:
167
+ session.add(input)
168
+ return input
169
+
170
+
171
+ def create_cell(session: Session, notebook: Notebook, input_text: str) -> Cell:
172
+ """Create a cell in the database."""
173
+ cell = Cell()
174
+ cell.notebook = notebook
175
+ cell.input = create_input_text(session, input_text)
176
+ cell.index = len(notebook.cells) - 1
177
+ session.add(cell)
178
+ notebook.atime = cell.ctime
179
+ return cell
180
+
181
+
182
+ def create_config(session: Session, config: dict | bytes, base: Path,
183
+ filename: str) -> Config:
184
+ """Create a config in the database."""
185
+
186
+ if not isinstance(config, bytes):
187
+ buf = pickle.dumps(config)
188
+ buf = lzma.compress(buf)
189
+ content_type = 'application/pickle+lzma'
190
+ else:
191
+ buf = config
192
+ content_type = 'application/octet-stream'
193
+ config = Config(buf)
194
+ config.content_type = content_type
195
+ for cfg in session.query(Config).filter(Config.hash == config.hash).all():
196
+ with open(base / cfg.file, 'rb') as f:
197
+ if f.read() == buf:
198
+ cfg.atime = utcnow()
199
+ return cfg
200
+ else:
201
+ path = base / filename
202
+ path.parent.mkdir(parents=True, exist_ok=True)
203
+ with open(path, 'wb') as f:
204
+ f.write(buf)
205
+ config.file = filename
206
+ session.add(config)
207
+ return config
208
+
209
+
210
+ def get_config(session: Session, config_id: int, base: Path):
211
+ config = session.get(Config, config_id)
212
+ if config is None:
213
+ return None
214
+ config.atime = utcnow()
215
+ path = base / config.file
216
+ with open(path, 'rb') as f:
217
+ buf = f.read()
218
+ if config.content_type == 'application/pickle+lzma':
219
+ buf = lzma.decompress(buf)
220
+ buf = pickle.loads(buf)
221
+ return buf