QuLab 2.1.1__tar.gz → 2.1.3__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.1.1 → qulab-2.1.3}/PKG-INFO +1 -1
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/PKG-INFO +1 -1
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/SOURCES.txt +0 -1
- qulab-2.1.3/qulab/__init__.py +3 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/__main__.py +0 -2
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/__init__.py +1 -1
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/curd.py +78 -1
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/models.py +21 -6
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/query.py +8 -8
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/record.py +94 -3
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/scan.py +159 -23
- qulab-2.1.1/qulab/scan/recorder.py → qulab-2.1.3/qulab/scan/server.py +89 -13
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/zmq_socket.py +7 -1
- qulab-2.1.3/qulab/version.py +1 -0
- qulab-2.1.1/qulab/__init__.py +0 -1
- qulab-2.1.1/qulab/scan/server.py +0 -104
- qulab-2.1.1/qulab/version.py +0 -1
- {qulab-2.1.1 → qulab-2.1.3}/LICENSE +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/MANIFEST.in +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/dependency_links.txt +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/entry_points.txt +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/requires.txt +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/QuLab.egg-info/top_level.txt +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/README.md +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/pyproject.toml +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/__main__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/config.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/dataset.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/event_queue.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/mainwindow.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/monitor.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/ploter.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/qt_compat.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/monitor/toolbar.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/expression.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/optimize.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/space.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/scan/utils.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/__main__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/backend/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/backend/redis.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/base_dataset.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/chunk.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/dataset.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/file.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/base.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/config.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/file.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/ipy.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/models.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/record.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/report.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/models/tag.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/storage/storage.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/chat.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/device/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/device/basedevice.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/device/loader.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/device/utils.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/drivers/FakeInstrument.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/drivers/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/ipy_events.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/bencoder.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/cli.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/dhcp.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/dhcpd.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/kad.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/kcp.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/net/nginx.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/progress.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/client.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/exceptions.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/msgpack.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/msgpack.pyi +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/rpc.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/serialize.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/server.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/socket.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/utils.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/sys/rpc/worker.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/__init__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/__main__.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/_autoplot.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/plot_layout.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/plot_seq.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/qdat.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/qulab/visualization/widgets.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/setup.cfg +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/setup.py +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/src/qulab.h +0 -0
- {qulab-2.1.1 → qulab-2.1.3}/tests/test_scan.py +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
3
|
from .monitor.__main__ import main as monitor
|
|
4
|
-
from .scan.recorder import record
|
|
5
4
|
from .scan.server import server
|
|
6
5
|
from .sys.net.cli import dht
|
|
7
6
|
from .visualization.__main__ import plot
|
|
@@ -21,7 +20,6 @@ def hello():
|
|
|
21
20
|
main.add_command(monitor)
|
|
22
21
|
main.add_command(plot)
|
|
23
22
|
main.add_command(dht)
|
|
24
|
-
main.add_command(record)
|
|
25
23
|
main.add_command(server)
|
|
26
24
|
|
|
27
25
|
if __name__ == '__main__':
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import lzma
|
|
2
|
+
import pickle
|
|
1
3
|
from datetime import date, datetime, timezone
|
|
4
|
+
from pathlib import Path
|
|
2
5
|
from typing import Sequence, Type, Union
|
|
3
6
|
|
|
4
7
|
from sqlalchemy.orm import Query, Session, aliased
|
|
@@ -6,7 +9,8 @@ from sqlalchemy.orm.exc import NoResultFound
|
|
|
6
9
|
from sqlalchemy.orm.session import Session
|
|
7
10
|
from waveforms.dicttree import foldDict
|
|
8
11
|
|
|
9
|
-
from .models import Comment,
|
|
12
|
+
from .models import (Cell, Comment, Config, InputText, Notebook, Record,
|
|
13
|
+
Report, Sample, Tag, utcnow)
|
|
10
14
|
|
|
11
15
|
|
|
12
16
|
def tag(session: Session, tag_text: str) -> Tag:
|
|
@@ -142,3 +146,76 @@ def remove_tags(session: Session, record_id: int, tags: Sequence[str]):
|
|
|
142
146
|
session.rollback()
|
|
143
147
|
return False
|
|
144
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
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import pickle
|
|
3
|
-
import time
|
|
4
3
|
from datetime import datetime, timezone
|
|
5
4
|
from functools import singledispatchmethod
|
|
6
|
-
from typing import Optional
|
|
7
5
|
|
|
8
|
-
from sqlalchemy import (
|
|
6
|
+
from sqlalchemy import (Column, DateTime, Float, ForeignKey, Integer,
|
|
9
7
|
LargeBinary, String, Table, Text, create_engine)
|
|
10
8
|
from sqlalchemy.orm import (backref, declarative_base, relationship,
|
|
11
9
|
sessionmaker)
|
|
@@ -325,7 +323,7 @@ class InputText(Base):
|
|
|
325
323
|
__tablename__ = 'inputs'
|
|
326
324
|
|
|
327
325
|
id = Column(Integer, primary_key=True)
|
|
328
|
-
hash = Column(LargeBinary(20))
|
|
326
|
+
hash = Column(LargeBinary(20), index=True)
|
|
329
327
|
text_field = Column(Text, unique=True)
|
|
330
328
|
|
|
331
329
|
@property
|
|
@@ -432,6 +430,22 @@ class SampleTransfer(Base):
|
|
|
432
430
|
comments = relationship("Comment", secondary=sample_transfer_comments)
|
|
433
431
|
|
|
434
432
|
|
|
433
|
+
class Config(Base):
|
|
434
|
+
__tablename__ = 'configs'
|
|
435
|
+
|
|
436
|
+
id = Column(Integer, primary_key=True)
|
|
437
|
+
hash = Column(LargeBinary(20), index=True)
|
|
438
|
+
file = Column(String)
|
|
439
|
+
content_type = Column(String, default='application/pickle')
|
|
440
|
+
ctime = Column(DateTime, default=utcnow)
|
|
441
|
+
atime = Column(DateTime, default=utcnow)
|
|
442
|
+
|
|
443
|
+
records = relationship("Record", back_populates="config")
|
|
444
|
+
|
|
445
|
+
def __init__(self, data: bytes) -> None:
|
|
446
|
+
self.hash = hashlib.sha1(data).digest()
|
|
447
|
+
|
|
448
|
+
|
|
435
449
|
class Record(Base):
|
|
436
450
|
__tablename__ = 'records'
|
|
437
451
|
|
|
@@ -440,14 +454,14 @@ class Record(Base):
|
|
|
440
454
|
mtime = Column(DateTime, default=utcnow)
|
|
441
455
|
atime = Column(DateTime, default=utcnow)
|
|
442
456
|
user_id = Column(Integer, ForeignKey('users.id'))
|
|
457
|
+
config_id = Column(Integer, ForeignKey('configs.id'))
|
|
443
458
|
parent_id = Column(Integer, ForeignKey('records.id'))
|
|
444
459
|
cell_id = Column(Integer, ForeignKey('cells.id'))
|
|
445
460
|
|
|
446
461
|
app = Column(String)
|
|
447
462
|
file = Column(String)
|
|
463
|
+
content_type = Column(String, default='application/pickle')
|
|
448
464
|
key = Column(String)
|
|
449
|
-
config = Column(JSON)
|
|
450
|
-
task_hash = Column(LargeBinary(32))
|
|
451
465
|
|
|
452
466
|
parent = relationship("Record",
|
|
453
467
|
remote_side=[id],
|
|
@@ -456,6 +470,7 @@ class Record(Base):
|
|
|
456
470
|
remote_side=[parent_id],
|
|
457
471
|
back_populates="parent")
|
|
458
472
|
|
|
473
|
+
config = relationship("Config", back_populates="records")
|
|
459
474
|
user = relationship("User")
|
|
460
475
|
samples = relationship("Sample",
|
|
461
476
|
secondary=sample_records,
|
|
@@ -6,10 +6,13 @@ import dill
|
|
|
6
6
|
import ipywidgets as widgets
|
|
7
7
|
import zmq
|
|
8
8
|
from IPython.display import display
|
|
9
|
+
from sqlalchemy import create_engine
|
|
10
|
+
from sqlalchemy.orm import sessionmaker
|
|
9
11
|
|
|
10
12
|
from qulab.sys.rpc.zmq_socket import ZMQContextManager
|
|
11
13
|
|
|
12
14
|
from .record import Record
|
|
15
|
+
from .server import get_local_record
|
|
13
16
|
from .scan import default_server
|
|
14
17
|
|
|
15
18
|
|
|
@@ -26,18 +29,15 @@ def get_record(id, database=default_server) -> Record:
|
|
|
26
29
|
d._file = None
|
|
27
30
|
return d
|
|
28
31
|
else:
|
|
29
|
-
from .models import Record as RecordInDB
|
|
30
|
-
from .models import create_engine, sessionmaker
|
|
31
|
-
|
|
32
32
|
db_file = Path(database) / 'data.db'
|
|
33
33
|
engine = create_engine(f'sqlite:///{db_file}')
|
|
34
34
|
Session = sessionmaker(bind=engine)
|
|
35
35
|
with Session() as session:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
return get_local_record(session, id, database)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_record(file):
|
|
40
|
+
return Record.load(file)
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def _format_tag(tag):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import sys
|
|
3
3
|
import uuid
|
|
4
|
+
import zipfile
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from threading import Lock
|
|
6
7
|
from types import EllipsisType
|
|
@@ -13,7 +14,7 @@ from qulab.sys.rpc.zmq_socket import ZMQContextManager
|
|
|
13
14
|
|
|
14
15
|
from .space import OptimizeSpace, Space
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
_not_given = object()
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def random_path(base):
|
|
@@ -94,6 +95,11 @@ class BufferList():
|
|
|
94
95
|
dill.dump(item, f)
|
|
95
96
|
self._list.clear()
|
|
96
97
|
|
|
98
|
+
def delete(self):
|
|
99
|
+
if isinstance(self.file, Path):
|
|
100
|
+
self.file.unlink()
|
|
101
|
+
self.file = None
|
|
102
|
+
|
|
97
103
|
def append(self, pos, value, dims=None):
|
|
98
104
|
if dims is not None:
|
|
99
105
|
if any([p != 0 for i, p in enumerate(pos) if i not in dims]):
|
|
@@ -120,6 +126,18 @@ class BufferList():
|
|
|
120
126
|
yield pos, value
|
|
121
127
|
except EOFError:
|
|
122
128
|
break
|
|
129
|
+
elif isinstance(
|
|
130
|
+
self.file, tuple) and len(self.file) == 2 and isinstance(
|
|
131
|
+
self.file[0], str) and self.file[0].endswith('.zip'):
|
|
132
|
+
f, name = self.file
|
|
133
|
+
with zipfile.ZipFile(f, 'r') as z:
|
|
134
|
+
with z.open(name, 'r') as f:
|
|
135
|
+
while True:
|
|
136
|
+
try:
|
|
137
|
+
pos, value = dill.load(f)
|
|
138
|
+
yield pos, value
|
|
139
|
+
except EOFError:
|
|
140
|
+
break
|
|
123
141
|
|
|
124
142
|
def iter(self):
|
|
125
143
|
if self._data_id is None:
|
|
@@ -327,7 +345,7 @@ class Record():
|
|
|
327
345
|
ret = ret.toarray()
|
|
328
346
|
return ret
|
|
329
347
|
|
|
330
|
-
def get(self, key, default=
|
|
348
|
+
def get(self, key, default=_not_given, buffer_to_array=False, slice=None):
|
|
331
349
|
if self.is_remote_record():
|
|
332
350
|
with ZMQContextManager(zmq.DEALER,
|
|
333
351
|
connect=self.database) as socket:
|
|
@@ -355,7 +373,7 @@ class Record():
|
|
|
355
373
|
else:
|
|
356
374
|
return ret
|
|
357
375
|
else:
|
|
358
|
-
if default is
|
|
376
|
+
if default is _not_given:
|
|
359
377
|
d = self._items.get(key)
|
|
360
378
|
else:
|
|
361
379
|
d = self._items.get(key, default)
|
|
@@ -437,6 +455,79 @@ class Record():
|
|
|
437
455
|
with open(self._file, 'wb') as f:
|
|
438
456
|
dill.dump(self, f)
|
|
439
457
|
|
|
458
|
+
def delete(self):
|
|
459
|
+
if self.is_remote_record():
|
|
460
|
+
with ZMQContextManager(zmq.DEALER,
|
|
461
|
+
connect=self.database) as socket:
|
|
462
|
+
socket.send_pyobj({
|
|
463
|
+
'method': 'record_delete',
|
|
464
|
+
'record_id': self.id
|
|
465
|
+
})
|
|
466
|
+
elif self.is_local_record():
|
|
467
|
+
for key, value in self._items.items():
|
|
468
|
+
if isinstance(value, BufferList):
|
|
469
|
+
value.delete()
|
|
470
|
+
self._file.unlink()
|
|
471
|
+
|
|
472
|
+
def export(self, file):
|
|
473
|
+
with zipfile.ZipFile(file,
|
|
474
|
+
'w',
|
|
475
|
+
compression=zipfile.ZIP_DEFLATED,
|
|
476
|
+
compresslevel=9) as z:
|
|
477
|
+
items = {}
|
|
478
|
+
for key in self.keys():
|
|
479
|
+
value = self.get(key)
|
|
480
|
+
if isinstance(value, BufferList):
|
|
481
|
+
v = BufferList()
|
|
482
|
+
v.lu = value.lu
|
|
483
|
+
v.rd = value.rd
|
|
484
|
+
v.inner_shape = value.inner_shape
|
|
485
|
+
items[key] = v
|
|
486
|
+
with z.open(f'{key}.buf', 'w') as f:
|
|
487
|
+
for pos, data in value.iter():
|
|
488
|
+
dill.dump((pos, data), f)
|
|
489
|
+
else:
|
|
490
|
+
items[key] = value
|
|
491
|
+
with z.open('record.pkl', 'w') as f:
|
|
492
|
+
self.description['entry']['scripts'] = self.scripts()
|
|
493
|
+
dill.dump((self.description, items), f)
|
|
494
|
+
|
|
495
|
+
def scripts(self, session=None):
|
|
496
|
+
scripts = self.description['entry']['scripts']
|
|
497
|
+
if isinstance(scripts, list):
|
|
498
|
+
return scripts
|
|
499
|
+
else:
|
|
500
|
+
cell_id = scripts
|
|
501
|
+
|
|
502
|
+
if self.is_remote_record():
|
|
503
|
+
with ZMQContextManager(zmq.DEALER,
|
|
504
|
+
connect=self.database) as socket:
|
|
505
|
+
socket.send_pyobj({
|
|
506
|
+
'method': 'notebook_history',
|
|
507
|
+
'cell_id': cell_id
|
|
508
|
+
})
|
|
509
|
+
return socket.recv_pyobj()
|
|
510
|
+
elif self.is_local_record():
|
|
511
|
+
from .models import Cell
|
|
512
|
+
assert session is not None, "session is required for local record"
|
|
513
|
+
cell = session.get(Cell, cell_id)
|
|
514
|
+
return [
|
|
515
|
+
cell.input.text
|
|
516
|
+
for cell in cell.notebook.cells[1:cell.index + 2]
|
|
517
|
+
]
|
|
518
|
+
|
|
519
|
+
@classmethod
|
|
520
|
+
def load(cls, file: str):
|
|
521
|
+
with zipfile.ZipFile(file, 'r') as z:
|
|
522
|
+
with z.open('record.pkl', 'r') as f:
|
|
523
|
+
description, items = dill.load(f)
|
|
524
|
+
record = cls(None, None, description)
|
|
525
|
+
for key, value in items.items():
|
|
526
|
+
if isinstance(value, BufferList):
|
|
527
|
+
value.file = file, f'{key}.buf'
|
|
528
|
+
record._items[key] = value
|
|
529
|
+
return record
|
|
530
|
+
|
|
440
531
|
def __repr__(self):
|
|
441
532
|
return f"<Record: id={self.id} app={self.description['app']}, keys={self.keys()}>"
|
|
442
533
|
|