lamindb 0.74.0__py3-none-any.whl → 0.74.2__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.
- lamindb/__init__.py +9 -9
- lamindb/_artifact.py +36 -46
- lamindb/_can_validate.py +24 -22
- lamindb/_collection.py +5 -6
- lamindb/{_annotate.py → _curate.py} +62 -40
- lamindb/_feature.py +7 -9
- lamindb/_feature_set.py +17 -18
- lamindb/_filter.py +5 -5
- lamindb/_finish.py +19 -7
- lamindb/_from_values.py +15 -15
- lamindb/_is_versioned.py +2 -2
- lamindb/_parents.py +7 -7
- lamindb/_query_manager.py +8 -8
- lamindb/_query_set.py +32 -30
- lamindb/{_registry.py → _record.py} +91 -50
- lamindb/_save.py +6 -6
- lamindb/_storage.py +1 -1
- lamindb/_view.py +4 -4
- lamindb/core/__init__.py +19 -16
- lamindb/core/_data.py +11 -11
- lamindb/core/_feature_manager.py +49 -32
- lamindb/core/_label_manager.py +5 -5
- lamindb/core/_mapped_collection.py +4 -1
- lamindb/core/_run_context.py +6 -4
- lamindb/core/_settings.py +45 -50
- lamindb/core/_sync_git.py +22 -12
- lamindb/core/_track_environment.py +5 -1
- lamindb/core/datasets/_core.py +3 -3
- lamindb/core/fields.py +1 -1
- lamindb/core/schema.py +6 -6
- lamindb/core/storage/_backed_access.py +56 -12
- lamindb/core/storage/paths.py +4 -4
- lamindb/core/subsettings/__init__.py +12 -0
- lamindb/core/subsettings/_creation_settings.py +38 -0
- lamindb/core/subsettings/_transform_settings.py +21 -0
- lamindb/core/versioning.py +1 -1
- lamindb/integrations/_vitessce.py +4 -3
- {lamindb-0.74.0.dist-info → lamindb-0.74.2.dist-info}/METADATA +7 -9
- lamindb-0.74.2.dist-info/RECORD +57 -0
- lamindb/core/_transform_settings.py +0 -9
- lamindb-0.74.0.dist-info/RECORD +0 -55
- {lamindb-0.74.0.dist-info → lamindb-0.74.2.dist-info}/LICENSE +0 -0
- {lamindb-0.74.0.dist-info → lamindb-0.74.2.dist-info}/WHEEL +0 -0
lamindb/_parents.py
CHANGED
@@ -5,12 +5,12 @@ from typing import TYPE_CHECKING
|
|
5
5
|
|
6
6
|
import lamindb_setup as ln_setup
|
7
7
|
from lamin_utils import logger
|
8
|
-
from lnschema_core import Artifact, Collection,
|
8
|
+
from lnschema_core import Artifact, Collection, Record, Run, Transform
|
9
9
|
from lnschema_core.models import HasParents, format_field_value
|
10
10
|
|
11
11
|
from lamindb._utils import attach_func_to_class_method
|
12
12
|
|
13
|
-
from .
|
13
|
+
from ._record import get_default_str_field
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from lnschema_core.types import StrField
|
@@ -137,7 +137,7 @@ def view_lineage(data: Artifact | Collection, with_children: bool = True) -> Non
|
|
137
137
|
|
138
138
|
|
139
139
|
def _view_parents(
|
140
|
-
record:
|
140
|
+
record: Record, field: str, with_children: bool = False, distance: int = 100
|
141
141
|
):
|
142
142
|
"""Graph of parents."""
|
143
143
|
if not hasattr(record, "parents"):
|
@@ -197,7 +197,7 @@ def _view_parents(
|
|
197
197
|
_view(u)
|
198
198
|
|
199
199
|
|
200
|
-
def _get_parents(record:
|
200
|
+
def _get_parents(record: Record, field: str, distance: int, children: bool = False):
|
201
201
|
"""Recursively get parent records within a distance."""
|
202
202
|
if children:
|
203
203
|
key = "parents"
|
@@ -228,7 +228,7 @@ def _get_parents(record: Registry, field: str, distance: int, children: bool = F
|
|
228
228
|
|
229
229
|
|
230
230
|
def _df_edges_from_parents(
|
231
|
-
record:
|
231
|
+
record: Record, field: str, distance: int, children: bool = False
|
232
232
|
):
|
233
233
|
"""Construct a DataFrame of edges as the input of graphviz.Digraph."""
|
234
234
|
key = "children" if children else "parents"
|
@@ -267,7 +267,7 @@ def _df_edges_from_parents(
|
|
267
267
|
return df_edges
|
268
268
|
|
269
269
|
|
270
|
-
def _record_label(record:
|
270
|
+
def _record_label(record: Record, field: str | None = None):
|
271
271
|
if isinstance(record, Artifact):
|
272
272
|
if record.description is None:
|
273
273
|
name = record.key
|
@@ -311,7 +311,7 @@ def _record_label(record: Registry, field: str | None = None):
|
|
311
311
|
)
|
312
312
|
|
313
313
|
|
314
|
-
def _add_emoji(record:
|
314
|
+
def _add_emoji(record: Record, label: str):
|
315
315
|
if record.__class__.__name__ == "Transform":
|
316
316
|
emoji = TRANSFORM_EMOJIS.get(record.type, "💫")
|
317
317
|
elif record.__class__.__name__ == "Run":
|
lamindb/_query_manager.py
CHANGED
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, NamedTuple
|
|
5
5
|
from django.db import models
|
6
6
|
from lamin_utils import logger
|
7
7
|
from lamindb_setup.core._docs import doc_args
|
8
|
-
from lnschema_core.models import
|
8
|
+
from lnschema_core.models import Record
|
9
9
|
|
10
10
|
from lamindb.core._settings import settings
|
11
11
|
|
@@ -45,7 +45,7 @@ class QueryManager(models.Manager):
|
|
45
45
|
|
46
46
|
if (
|
47
47
|
run_context.run is None
|
48
|
-
and not settings.
|
48
|
+
and not settings.creation.artifact_silence_missing_run_warning
|
49
49
|
):
|
50
50
|
logger.warning(WARNING_RUN_TRANSFORM)
|
51
51
|
_track_run_input(self.instance)
|
@@ -84,17 +84,17 @@ class QueryManager(models.Manager):
|
|
84
84
|
self._track_run_input_manager()
|
85
85
|
return self._all_base_class()
|
86
86
|
|
87
|
-
@doc_args(
|
87
|
+
@doc_args(Record.search.__doc__)
|
88
88
|
def search(self, string: str, **kwargs):
|
89
|
-
"""{}
|
90
|
-
from .
|
89
|
+
"""{}""" # noqa: D415
|
90
|
+
from ._record import _search
|
91
91
|
|
92
92
|
return _search(cls=self.all(), string=string, **kwargs)
|
93
93
|
|
94
|
-
@doc_args(
|
94
|
+
@doc_args(Record.lookup.__doc__)
|
95
95
|
def lookup(self, field: StrField | None = None, **kwargs) -> NamedTuple:
|
96
|
-
"""{}
|
97
|
-
from .
|
96
|
+
"""{}""" # noqa: D415
|
97
|
+
from ._record import _lookup
|
98
98
|
|
99
99
|
return _lookup(cls=self.all(), field=field, **kwargs)
|
100
100
|
|
lamindb/_query_set.py
CHANGED
@@ -12,7 +12,7 @@ from lnschema_core.models import (
|
|
12
12
|
CanValidate,
|
13
13
|
Collection,
|
14
14
|
IsVersioned,
|
15
|
-
|
15
|
+
Record,
|
16
16
|
Run,
|
17
17
|
Transform,
|
18
18
|
)
|
@@ -35,7 +35,7 @@ class MultipleResultsFound(Exception):
|
|
35
35
|
# return (series + timedelta).dt.strftime("%Y-%m-%d %H:%M:%S %Z")
|
36
36
|
|
37
37
|
|
38
|
-
def get_keys_from_df(data: list, registry:
|
38
|
+
def get_keys_from_df(data: list, registry: Record) -> list[str]:
|
39
39
|
if len(data) > 0:
|
40
40
|
if isinstance(data[0], dict):
|
41
41
|
keys = list(data[0].keys())
|
@@ -69,7 +69,7 @@ def one_helper(self):
|
|
69
69
|
class RecordsList(UserList):
|
70
70
|
"""Is ordered, can't be queried, but has `.df()`."""
|
71
71
|
|
72
|
-
def __init__(self, records: Iterable[
|
72
|
+
def __init__(self, records: Iterable[Record]):
|
73
73
|
super().__init__(record for record in records)
|
74
74
|
|
75
75
|
def df(self) -> pd.DataFrame:
|
@@ -77,7 +77,7 @@ class RecordsList(UserList):
|
|
77
77
|
values = [record.__dict__ for record in self.data]
|
78
78
|
return pd.DataFrame(values, columns=keys)
|
79
79
|
|
80
|
-
def one(self) ->
|
80
|
+
def one(self) -> Record:
|
81
81
|
"""Exactly one result. Throws error if there are more or none."""
|
82
82
|
return one_helper(self)
|
83
83
|
|
@@ -96,9 +96,11 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
96
96
|
>>> queryset
|
97
97
|
"""
|
98
98
|
|
99
|
-
@doc_args(
|
100
|
-
def df(
|
101
|
-
""
|
99
|
+
@doc_args(Record.df.__doc__)
|
100
|
+
def df(
|
101
|
+
self, include: str | list[str] | None = None, join: str = "inner"
|
102
|
+
) -> pd.DataFrame:
|
103
|
+
"""{}""" # noqa: D415
|
102
104
|
# re-order the columns
|
103
105
|
exclude_field_names = ["created_at"]
|
104
106
|
field_names = [
|
@@ -145,21 +147,21 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
145
147
|
lookup_str = "__".join(split[1:])
|
146
148
|
else:
|
147
149
|
lookup_str = "id"
|
148
|
-
|
149
|
-
field = getattr(
|
150
|
+
Record = self.model
|
151
|
+
field = getattr(Record, field_name)
|
150
152
|
if isinstance(field.field, models.ManyToManyField):
|
151
153
|
related_ORM = (
|
152
154
|
field.field.model
|
153
|
-
if field.field.model !=
|
155
|
+
if field.field.model != Record
|
154
156
|
else field.field.related_model
|
155
157
|
)
|
156
|
-
if
|
157
|
-
left_side_link_model = f"from_{
|
158
|
+
if Record == related_ORM:
|
159
|
+
left_side_link_model = f"from_{Record.__name__.lower()}"
|
158
160
|
values_expression = (
|
159
|
-
f"to_{
|
161
|
+
f"to_{Record.__name__.lower()}__{lookup_str}"
|
160
162
|
)
|
161
163
|
else:
|
162
|
-
left_side_link_model = f"{
|
164
|
+
left_side_link_model = f"{Record.__name__.lower()}"
|
163
165
|
values_expression = (
|
164
166
|
f"{related_ORM.__name__.lower()}__{lookup_str}"
|
165
167
|
)
|
@@ -173,7 +175,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
173
175
|
link_groupby = link_df.groupby(left_side_link_model)[
|
174
176
|
values_expression
|
175
177
|
].apply(list)
|
176
|
-
df = pd.concat((link_groupby, df), axis=1, join=
|
178
|
+
df = pd.concat((link_groupby, df), axis=1, join=join)
|
177
179
|
df.rename(columns={values_expression: expression}, inplace=True)
|
178
180
|
else:
|
179
181
|
# the F() based implementation could also work for many-to-many,
|
@@ -185,7 +187,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
185
187
|
)
|
186
188
|
df_anno = df_anno.set_index(pk_column_name)
|
187
189
|
df_anno.rename(columns={"expression": expression}, inplace=True)
|
188
|
-
df = pd.concat((df_anno, df), axis=1, join=
|
190
|
+
df = pd.concat((df_anno, df), axis=1, join=join)
|
189
191
|
return df
|
190
192
|
|
191
193
|
def delete(self, *args, **kwargs):
|
@@ -197,7 +199,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
197
199
|
else:
|
198
200
|
self._delete_base_class(*args, **kwargs)
|
199
201
|
|
200
|
-
def list(self, field: str | None = None) -> list[
|
202
|
+
def list(self, field: str | None = None) -> list[Record]:
|
201
203
|
"""Populate a list with the results.
|
202
204
|
|
203
205
|
Examples:
|
@@ -209,7 +211,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
209
211
|
else:
|
210
212
|
return list(self.values_list(field, flat=True))
|
211
213
|
|
212
|
-
def first(self) ->
|
214
|
+
def first(self) -> Record | None:
|
213
215
|
"""If non-empty, the first result in the query set, otherwise ``None``.
|
214
216
|
|
215
217
|
Examples:
|
@@ -219,7 +221,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
219
221
|
return None
|
220
222
|
return self[0]
|
221
223
|
|
222
|
-
def one(self) ->
|
224
|
+
def one(self) -> Record:
|
223
225
|
"""Exactly one result. Raises error if there are more or none.
|
224
226
|
|
225
227
|
Examples:
|
@@ -227,7 +229,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
227
229
|
"""
|
228
230
|
return one_helper(self)
|
229
231
|
|
230
|
-
def one_or_none(self) ->
|
232
|
+
def one_or_none(self) -> Record | None:
|
231
233
|
"""At most one result. Returns it if there is one, otherwise returns ``None``.
|
232
234
|
|
233
235
|
Examples:
|
@@ -246,32 +248,32 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
246
248
|
if issubclass(self.model, IsVersioned):
|
247
249
|
return filter_query_set_by_latest_version(self)
|
248
250
|
else:
|
249
|
-
raise ValueError("
|
251
|
+
raise ValueError("Record isn't subclass of `lamindb.core.IsVersioned`")
|
250
252
|
|
251
|
-
@doc_args(
|
253
|
+
@doc_args(Record.search.__doc__)
|
252
254
|
def search(self, string: str, **kwargs):
|
253
|
-
"""{}
|
254
|
-
from .
|
255
|
+
"""{}""" # noqa: D415
|
256
|
+
from ._record import _search
|
255
257
|
|
256
258
|
return _search(cls=self, string=string, **kwargs)
|
257
259
|
|
258
|
-
@doc_args(
|
260
|
+
@doc_args(Record.lookup.__doc__)
|
259
261
|
def lookup(self, field: StrField | None = None, **kwargs) -> NamedTuple:
|
260
|
-
"""{}
|
261
|
-
from .
|
262
|
+
"""{}""" # noqa: D415
|
263
|
+
from ._record import _lookup
|
262
264
|
|
263
265
|
return _lookup(cls=self, field=field, **kwargs)
|
264
266
|
|
265
267
|
@doc_args(CanValidate.validate.__doc__)
|
266
268
|
def validate(self, values: ListLike, field: str | StrField | None = None, **kwargs):
|
267
|
-
"""{}
|
269
|
+
"""{}""" # noqa: D415
|
268
270
|
from ._can_validate import _validate
|
269
271
|
|
270
272
|
return _validate(cls=self, values=values, field=field, **kwargs)
|
271
273
|
|
272
274
|
@doc_args(CanValidate.inspect.__doc__)
|
273
275
|
def inspect(self, values: ListLike, field: str | StrField | None = None, **kwargs):
|
274
|
-
"""{}
|
276
|
+
"""{}""" # noqa: D415
|
275
277
|
from ._can_validate import _inspect
|
276
278
|
|
277
279
|
return _inspect(cls=self, values=values, field=field, **kwargs)
|
@@ -280,7 +282,7 @@ class QuerySet(models.QuerySet, CanValidate):
|
|
280
282
|
def standardize(
|
281
283
|
self, values: Iterable, field: str | StrField | None = None, **kwargs
|
282
284
|
):
|
283
|
-
"""{}
|
285
|
+
"""{}""" # noqa: D415
|
284
286
|
from ._can_validate import _standardize
|
285
287
|
|
286
288
|
return _standardize(cls=self, values=values, field=field, **kwargs)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import builtins
|
4
|
-
from typing import TYPE_CHECKING,
|
4
|
+
from typing import TYPE_CHECKING, List, NamedTuple
|
5
5
|
|
6
6
|
import dj_database_url
|
7
7
|
import lamindb_setup as ln_setup
|
@@ -12,7 +12,7 @@ from lamin_utils._lookup import Lookup
|
|
12
12
|
from lamindb_setup._connect_instance import get_owner_name_from_identifier
|
13
13
|
from lamindb_setup.core._docs import doc_args
|
14
14
|
from lamindb_setup.core._hub_core import connect_instance
|
15
|
-
from lnschema_core import
|
15
|
+
from lnschema_core.models import IsVersioned, Record
|
16
16
|
|
17
17
|
from lamindb._utils import attach_func_to_class_method
|
18
18
|
from lamindb.core._settings import settings
|
@@ -20,12 +20,14 @@ from lamindb.core._settings import settings
|
|
20
20
|
from ._from_values import get_or_create_records
|
21
21
|
|
22
22
|
if TYPE_CHECKING:
|
23
|
+
import pandas as pd
|
23
24
|
from lnschema_core.types import ListLike, StrField
|
24
25
|
|
26
|
+
|
25
27
|
IPYTHON = getattr(builtins, "__IPYTHON__", False)
|
26
28
|
|
27
29
|
|
28
|
-
def init_self_from_db(self:
|
30
|
+
def init_self_from_db(self: Record, existing_record: Record):
|
29
31
|
new_args = [
|
30
32
|
getattr(existing_record, field.attname) for field in self._meta.concrete_fields
|
31
33
|
]
|
@@ -34,7 +36,7 @@ def init_self_from_db(self: Registry, existing_record: Registry):
|
|
34
36
|
self._state.db = "default"
|
35
37
|
|
36
38
|
|
37
|
-
def validate_required_fields(orm:
|
39
|
+
def validate_required_fields(orm: Record, kwargs):
|
38
40
|
required_fields = {
|
39
41
|
k.name for k in orm._meta.fields if not k.null and k.default is None
|
40
42
|
}
|
@@ -47,14 +49,16 @@ def validate_required_fields(orm: Registry, kwargs):
|
|
47
49
|
raise TypeError(f"{missing_fields} are required.")
|
48
50
|
|
49
51
|
|
50
|
-
def suggest_records_with_similar_names(record:
|
52
|
+
def suggest_records_with_similar_names(record: Record, kwargs) -> bool:
|
51
53
|
"""Returns True if found exact match, otherwise False.
|
52
54
|
|
53
55
|
Logs similar matches if found.
|
54
56
|
"""
|
55
57
|
if kwargs.get("name") is None:
|
56
58
|
return False
|
57
|
-
queryset = _search(
|
59
|
+
queryset = _search(
|
60
|
+
record.__class__, kwargs["name"], field="name", truncate_words=True, limit=20
|
61
|
+
)
|
58
62
|
if not queryset.exists(): # empty queryset
|
59
63
|
return False
|
60
64
|
for alternative_record in queryset:
|
@@ -73,7 +77,7 @@ def suggest_records_with_similar_names(record: Registry, kwargs) -> bool:
|
|
73
77
|
return False
|
74
78
|
|
75
79
|
|
76
|
-
def __init__(orm:
|
80
|
+
def __init__(orm: Record, *args, **kwargs):
|
77
81
|
if not args:
|
78
82
|
validate_required_fields(orm, kwargs)
|
79
83
|
|
@@ -82,17 +86,17 @@ def __init__(orm: Registry, *args, **kwargs):
|
|
82
86
|
has_consciously_provided_uid = False
|
83
87
|
if "_has_consciously_provided_uid" in kwargs:
|
84
88
|
has_consciously_provided_uid = kwargs.pop("_has_consciously_provided_uid")
|
85
|
-
if settings.
|
89
|
+
if settings.creation.search_names and not has_consciously_provided_uid:
|
86
90
|
match = suggest_records_with_similar_names(orm, kwargs)
|
87
91
|
if match:
|
88
92
|
if "version" in kwargs:
|
89
93
|
version_comment = " and version"
|
90
|
-
existing_record = orm.filter(
|
94
|
+
existing_record = orm.__class__.filter(
|
91
95
|
name=kwargs["name"], version=kwargs["version"]
|
92
96
|
).one_or_none()
|
93
97
|
else:
|
94
98
|
version_comment = ""
|
95
|
-
existing_record = orm.filter(name=kwargs["name"]).one()
|
99
|
+
existing_record = orm.__class__.filter(name=kwargs["name"]).one()
|
96
100
|
if existing_record is not None:
|
97
101
|
logger.important(
|
98
102
|
f"returning existing {orm.__class__.__name__} record with same"
|
@@ -100,27 +104,66 @@ def __init__(orm: Registry, *args, **kwargs):
|
|
100
104
|
)
|
101
105
|
init_self_from_db(orm, existing_record)
|
102
106
|
return None
|
103
|
-
super(
|
107
|
+
super(Record, orm).__init__(**kwargs)
|
104
108
|
elif len(args) != len(orm._meta.concrete_fields):
|
105
109
|
raise ValueError("please provide keyword arguments, not plain arguments")
|
106
110
|
else:
|
107
111
|
# object is loaded from DB (**kwargs could be omitted below, I believe)
|
108
|
-
super(
|
112
|
+
super(Record, orm).__init__(*args, **kwargs)
|
113
|
+
|
114
|
+
|
115
|
+
@classmethod # type:ignore
|
116
|
+
@doc_args(Record.filter.__doc__)
|
117
|
+
def filter(cls, **expressions) -> QuerySet:
|
118
|
+
"""{}""" # noqa: D415
|
119
|
+
from lamindb._filter import filter
|
120
|
+
|
121
|
+
return filter(cls, **expressions)
|
122
|
+
|
123
|
+
|
124
|
+
@classmethod # type:ignore
|
125
|
+
@doc_args(Record.get.__doc__)
|
126
|
+
def get(cls, idlike: int | str) -> Record:
|
127
|
+
"""{}""" # noqa: D415
|
128
|
+
from lamindb._filter import filter
|
129
|
+
|
130
|
+
if isinstance(idlike, int):
|
131
|
+
return filter(cls, id=idlike).one()
|
132
|
+
else:
|
133
|
+
qs = filter(cls, uid__startswith=idlike)
|
134
|
+
if issubclass(cls, IsVersioned):
|
135
|
+
return qs.latest_version().one()
|
136
|
+
else:
|
137
|
+
return qs.one()
|
138
|
+
|
139
|
+
|
140
|
+
@classmethod # type:ignore
|
141
|
+
@doc_args(Record.df.__doc__)
|
142
|
+
def df(
|
143
|
+
cls, include: str | list[str] | None = None, join: str = "inner"
|
144
|
+
) -> pd.DataFrame:
|
145
|
+
"""{}""" # noqa: D415
|
146
|
+
from lamindb._filter import filter
|
147
|
+
|
148
|
+
query_set = filter(cls)
|
149
|
+
if hasattr(cls, "updated_at"):
|
150
|
+
query_set = query_set.order_by("-updated_at")
|
151
|
+
return query_set.df(include=include, join=join)
|
109
152
|
|
110
153
|
|
111
154
|
# from_values doesn't apply for QuerySet or Manager
|
112
155
|
@classmethod # type:ignore
|
113
|
-
@doc_args(
|
156
|
+
@doc_args(Record.from_values.__doc__)
|
114
157
|
def from_values(
|
115
158
|
cls,
|
116
159
|
values: ListLike,
|
117
160
|
field: StrField | None = None,
|
118
161
|
create: bool = False,
|
119
|
-
organism:
|
120
|
-
public_source:
|
162
|
+
organism: Record | str | None = None,
|
163
|
+
public_source: Record | None = None,
|
121
164
|
mute: bool = False,
|
122
|
-
) -> list[
|
123
|
-
"""{}
|
165
|
+
) -> list[Record]:
|
166
|
+
"""{}""" # noqa: D415
|
124
167
|
from_public = True if cls.__module__.startswith("lnschema_bionty.") else False
|
125
168
|
field_str = get_default_str_field(cls, field=field)
|
126
169
|
return get_or_create_records(
|
@@ -134,14 +177,6 @@ def from_values(
|
|
134
177
|
)
|
135
178
|
|
136
179
|
|
137
|
-
# From: https://stackoverflow.com/a/37648265
|
138
|
-
def _order_queryset_by_ids(queryset: QuerySet, ids: Iterable):
|
139
|
-
from django.db.models import Case, When
|
140
|
-
|
141
|
-
preserved = Case(*[When(uid=pk, then=pos) for pos, pk in enumerate(ids)])
|
142
|
-
return queryset.filter(uid__in=ids).order_by(preserved)
|
143
|
-
|
144
|
-
|
145
180
|
def _search(
|
146
181
|
cls,
|
147
182
|
string: str,
|
@@ -172,7 +207,7 @@ def _search(
|
|
172
207
|
fields.append(field.field.name)
|
173
208
|
except AttributeError as error:
|
174
209
|
raise TypeError(
|
175
|
-
"Please pass a
|
210
|
+
"Please pass a Record string field, e.g., `CellType.name`!"
|
176
211
|
) from error
|
177
212
|
else:
|
178
213
|
fields.append(field)
|
@@ -187,7 +222,7 @@ def _search(
|
|
187
222
|
else:
|
188
223
|
return word
|
189
224
|
|
190
|
-
decomposed_string = string.split()
|
225
|
+
decomposed_string = str(string).split()
|
191
226
|
# add the entire string back
|
192
227
|
decomposed_string += [string]
|
193
228
|
for word in decomposed_string:
|
@@ -222,7 +257,7 @@ def _search(
|
|
222
257
|
|
223
258
|
|
224
259
|
@classmethod # type: ignore
|
225
|
-
@doc_args(
|
260
|
+
@doc_args(Record.search.__doc__)
|
226
261
|
def search(
|
227
262
|
cls,
|
228
263
|
string: str,
|
@@ -231,7 +266,7 @@ def search(
|
|
231
266
|
limit: int | None = 20,
|
232
267
|
case_sensitive: bool = False,
|
233
268
|
) -> QuerySet:
|
234
|
-
"""{}
|
269
|
+
"""{}""" # noqa: D415
|
235
270
|
return _search(
|
236
271
|
cls=cls,
|
237
272
|
string=string,
|
@@ -247,7 +282,7 @@ def _lookup(
|
|
247
282
|
return_field: StrField | None = None,
|
248
283
|
using_key: str | None = None,
|
249
284
|
) -> NamedTuple:
|
250
|
-
"""{}
|
285
|
+
"""{}""" # noqa: D415
|
251
286
|
queryset = _queryset(cls, using_key=using_key)
|
252
287
|
field = get_default_str_field(orm=queryset.model, field=field)
|
253
288
|
|
@@ -266,18 +301,18 @@ def _lookup(
|
|
266
301
|
|
267
302
|
|
268
303
|
@classmethod # type: ignore
|
269
|
-
@doc_args(
|
304
|
+
@doc_args(Record.lookup.__doc__)
|
270
305
|
def lookup(
|
271
306
|
cls,
|
272
307
|
field: StrField | None = None,
|
273
308
|
return_field: StrField | None = None,
|
274
309
|
) -> NamedTuple:
|
275
|
-
"""{}
|
310
|
+
"""{}""" # noqa: D415
|
276
311
|
return _lookup(cls=cls, field=field, return_field=return_field)
|
277
312
|
|
278
313
|
|
279
314
|
def get_default_str_field(
|
280
|
-
orm:
|
315
|
+
orm: Record | QuerySet | Manager,
|
281
316
|
*,
|
282
317
|
field: str | StrField | None = None,
|
283
318
|
) -> str:
|
@@ -307,7 +342,7 @@ def get_default_str_field(
|
|
307
342
|
# no default field can be found
|
308
343
|
if field is None:
|
309
344
|
raise ValueError(
|
310
|
-
"please pass a
|
345
|
+
"please pass a Record string field, e.g., `CellType.name`!"
|
311
346
|
)
|
312
347
|
else:
|
313
348
|
field = field.name # type:ignore
|
@@ -316,17 +351,20 @@ def get_default_str_field(
|
|
316
351
|
field = field.field.name
|
317
352
|
except AttributeError:
|
318
353
|
raise TypeError(
|
319
|
-
"please pass a
|
354
|
+
"please pass a Record string field, e.g., `CellType.name`!"
|
320
355
|
) from None
|
321
356
|
|
322
357
|
return field
|
323
358
|
|
324
359
|
|
325
|
-
def _queryset(cls:
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
360
|
+
def _queryset(cls: Record | QuerySet | Manager, using_key: str) -> QuerySet:
|
361
|
+
if isinstance(cls, (QuerySet, Manager)):
|
362
|
+
return cls.all()
|
363
|
+
elif using_key is None:
|
364
|
+
return cls.objects.all()
|
365
|
+
else:
|
366
|
+
# using must be called on cls, otherwise the connection isn't found
|
367
|
+
return cls.using(using_key).all()
|
330
368
|
|
331
369
|
|
332
370
|
def add_db_connection(db: str, using: str):
|
@@ -340,12 +378,12 @@ def add_db_connection(db: str, using: str):
|
|
340
378
|
|
341
379
|
|
342
380
|
@classmethod # type: ignore
|
343
|
-
@doc_args(
|
381
|
+
@doc_args(Record.using.__doc__)
|
344
382
|
def using(
|
345
383
|
cls,
|
346
384
|
instance: str,
|
347
385
|
) -> QuerySet:
|
348
|
-
"""{}
|
386
|
+
"""{}""" # noqa: D415
|
349
387
|
from lamindb_setup._connect_instance import (
|
350
388
|
load_instance_settings,
|
351
389
|
update_db_using_local,
|
@@ -378,7 +416,7 @@ REGISTRY_UNIQUE_FIELD = {
|
|
378
416
|
|
379
417
|
|
380
418
|
def update_fk_to_default_db(
|
381
|
-
records:
|
419
|
+
records: Record | list[Record] | QuerySet,
|
382
420
|
fk: str,
|
383
421
|
using_key: str | None,
|
384
422
|
):
|
@@ -418,12 +456,12 @@ def transfer_fk_to_default_db_bulk(records: list | QuerySet, using_key: str | No
|
|
418
456
|
|
419
457
|
|
420
458
|
def transfer_to_default_db(
|
421
|
-
record:
|
459
|
+
record: Record,
|
422
460
|
using_key: str | None,
|
423
461
|
save: bool = False,
|
424
462
|
mute: bool = False,
|
425
463
|
transfer_fk: bool = True,
|
426
|
-
) ->
|
464
|
+
) -> Record | None:
|
427
465
|
db = record._state.db
|
428
466
|
if db is not None and db != "default" and using_key is None:
|
429
467
|
registry = record.__class__
|
@@ -447,7 +485,7 @@ def transfer_to_default_db(
|
|
447
485
|
if run_context.run is not None:
|
448
486
|
record.run_id = run_context.run.id
|
449
487
|
else:
|
450
|
-
if not settings.
|
488
|
+
if not settings.creation.artifact_silence_missing_run_warning:
|
451
489
|
logger.warning(WARNING_RUN_TRANSFORM)
|
452
490
|
record.run_id = None
|
453
491
|
if hasattr(record, "transform_id") and record._meta.model_name != "run":
|
@@ -476,7 +514,7 @@ def transfer_to_default_db(
|
|
476
514
|
|
477
515
|
|
478
516
|
# docstring handled through attach_func_to_class_method
|
479
|
-
def save(self, *args, **kwargs) ->
|
517
|
+
def save(self, *args, **kwargs) -> Record:
|
480
518
|
using_key = None
|
481
519
|
if "using" in kwargs:
|
482
520
|
using_key = kwargs["using"]
|
@@ -496,7 +534,7 @@ def save(self, *args, **kwargs) -> Registry:
|
|
496
534
|
save_kwargs = kwargs.copy()
|
497
535
|
if "parents" in save_kwargs:
|
498
536
|
save_kwargs.pop("parents")
|
499
|
-
super(
|
537
|
+
super(Record, self).save(*args, **save_kwargs)
|
500
538
|
# perform transfer of many-to-many fields
|
501
539
|
# only supported for Artifact and Collection records
|
502
540
|
if db is not None and db != "default" and using_key is None:
|
@@ -531,6 +569,9 @@ def save(self, *args, **kwargs) -> Registry:
|
|
531
569
|
|
532
570
|
METHOD_NAMES = [
|
533
571
|
"__init__",
|
572
|
+
"filter",
|
573
|
+
"get",
|
574
|
+
"df",
|
534
575
|
"search",
|
535
576
|
"lookup",
|
536
577
|
"save",
|
@@ -542,10 +583,10 @@ if ln_setup._TESTING: # type: ignore
|
|
542
583
|
from inspect import signature
|
543
584
|
|
544
585
|
SIGS = {
|
545
|
-
name: signature(getattr(
|
586
|
+
name: signature(getattr(Record, name))
|
546
587
|
for name in METHOD_NAMES
|
547
588
|
if not name.startswith("__")
|
548
589
|
}
|
549
590
|
|
550
591
|
for name in METHOD_NAMES:
|
551
|
-
attach_func_to_class_method(name,
|
592
|
+
attach_func_to_class_method(name, Record, globals())
|