lamindb 1.12.1__py3-none-any.whl → 1.13.0__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 +2 -2
- lamindb/_finish.py +1 -1
- lamindb/_tracked.py +3 -15
- lamindb/core/_context.py +45 -19
- lamindb/curators/_legacy.py +1 -1
- lamindb/curators/core.py +51 -21
- lamindb/errors.py +6 -0
- lamindb/examples/datasets/_core.py +1 -1
- lamindb/integrations/__init__.py +0 -18
- lamindb/integrations/{_lightning.py → lightning.py} +13 -10
- lamindb/migrations/0134_run_params.py +17 -0
- lamindb/migrations/{0133_squashed.py → 0134_squashed.py} +93 -90
- lamindb/models/_feature_manager.py +30 -20
- lamindb/models/_label_manager.py +3 -5
- lamindb/models/artifact.py +250 -291
- lamindb/models/artifact_set.py +4 -4
- lamindb/models/block.py +11 -9
- lamindb/models/can_curate.py +1 -1
- lamindb/models/collection.py +16 -17
- lamindb/models/has_parents.py +1 -3
- lamindb/models/query_manager.py +7 -7
- lamindb/models/query_set.py +38 -12
- lamindb/models/run.py +53 -49
- lamindb/models/schema.py +79 -65
- lamindb/models/sqlrecord.py +32 -17
- lamindb/models/transform.py +6 -3
- {lamindb-1.12.1.dist-info → lamindb-1.13.0.dist-info}/METADATA +26 -22
- {lamindb-1.12.1.dist-info → lamindb-1.13.0.dist-info}/RECORD +30 -29
- {lamindb-1.12.1.dist-info → lamindb-1.13.0.dist-info}/LICENSE +0 -0
- {lamindb-1.12.1.dist-info → lamindb-1.13.0.dist-info}/WHEEL +0 -0
lamindb/models/schema.py
CHANGED
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Type, overload
|
|
4
4
|
|
5
5
|
import numpy as np
|
6
6
|
from django.db import models
|
7
|
-
from django.db.models import CASCADE, PROTECT, ManyToManyField
|
7
|
+
from django.db.models import CASCADE, PROTECT, ManyToManyField, Q
|
8
8
|
from lamin_utils import logger
|
9
9
|
from lamindb_setup.core import deprecated
|
10
10
|
from lamindb_setup.core.hashing import HASH_LENGTH, hash_string
|
@@ -248,10 +248,10 @@ class SchemaOptionals:
|
|
248
248
|
)
|
249
249
|
|
250
250
|
|
251
|
-
KNOWN_SCHEMAS = {
|
251
|
+
KNOWN_SCHEMAS = { # by hash
|
252
252
|
"kMi7B_N88uu-YnbTLDU-DA": "0000000000000000", # valid_features
|
253
253
|
"1gocc_TJ1RU2bMwDRK-WUA": "0000000000000001", # valid_ensembl_gene_ids
|
254
|
-
"
|
254
|
+
"UR_ozz2VI2sY8ckXop2RAg": "0000000000000002", # anndata_ensembl_gene_ids_and_valid_features_in_obs
|
255
255
|
}
|
256
256
|
|
257
257
|
|
@@ -536,7 +536,7 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
536
536
|
ordered_set: bool = kwargs.pop("ordered_set", False)
|
537
537
|
maximal_set: bool = kwargs.pop("maximal_set", False)
|
538
538
|
coerce_dtype: bool | None = kwargs.pop("coerce_dtype", False)
|
539
|
-
using:
|
539
|
+
using: str | None = kwargs.pop("using", None)
|
540
540
|
n_features: int | None = kwargs.pop("n", None)
|
541
541
|
kwargs.pop("branch", None)
|
542
542
|
kwargs.pop("branch_id", 1)
|
@@ -585,7 +585,10 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
585
585
|
if not is_type:
|
586
586
|
schema = (
|
587
587
|
Schema.objects.using(using)
|
588
|
-
.filter(
|
588
|
+
.filter(
|
589
|
+
~Q(branch_id=-1),
|
590
|
+
hash=validated_kwargs["hash"],
|
591
|
+
)
|
589
592
|
.one_or_none()
|
590
593
|
)
|
591
594
|
if schema is not None:
|
@@ -723,56 +726,56 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
723
726
|
|
724
727
|
if aux_dict:
|
725
728
|
validated_kwargs["_aux"] = aux_dict
|
729
|
+
HASH_CODE = {
|
730
|
+
"dtype": "a",
|
731
|
+
"itype": "b",
|
732
|
+
"minimal_set": "c",
|
733
|
+
"ordered_set": "d",
|
734
|
+
"maximal_set": "e",
|
735
|
+
"flexible": "f",
|
736
|
+
"coerce_dtype": "g",
|
737
|
+
"n": "h",
|
738
|
+
"optional": "i",
|
739
|
+
"features_hash": "j",
|
740
|
+
"index": "k",
|
741
|
+
"slots_hash": "l",
|
742
|
+
}
|
743
|
+
# we do not want pure informational annotations like otype, name, type, is_type, otype to be part of the hash
|
744
|
+
hash_args = ["dtype", "itype", "minimal_set", "ordered_set", "maximal_set"]
|
745
|
+
list_for_hashing = [
|
746
|
+
f"{HASH_CODE[arg]}={validated_kwargs[arg]}"
|
747
|
+
for arg in hash_args
|
748
|
+
if validated_kwargs[arg] is not None
|
749
|
+
]
|
750
|
+
# only include in hash if not default so that it's backward compatible with records for which flexible was never set
|
751
|
+
if flexible != flexible_default:
|
752
|
+
list_for_hashing.append(f"{HASH_CODE['flexible']}={flexible}")
|
753
|
+
if coerce_dtype != coerce_dtype_default:
|
754
|
+
list_for_hashing.append(f"{HASH_CODE['coerce_dtype']}={coerce_dtype}")
|
755
|
+
if n_features != n_features_default:
|
756
|
+
list_for_hashing.append(f"{HASH_CODE['n']}={n_features}")
|
757
|
+
if index is not None:
|
758
|
+
list_for_hashing.append(f"{HASH_CODE['index']}={index.uid}")
|
759
|
+
if features:
|
760
|
+
if optional_features:
|
761
|
+
feature_list_for_hashing = [
|
762
|
+
feature.uid
|
763
|
+
if feature not in set(optional_features)
|
764
|
+
else f"{feature.uid}({HASH_CODE['optional']})"
|
765
|
+
for feature in features
|
766
|
+
]
|
767
|
+
else:
|
768
|
+
feature_list_for_hashing = [feature.uid for feature in features]
|
769
|
+
if not ordered_set: # order matters if ordered_set is True, if not sort
|
770
|
+
feature_list_for_hashing = sorted(feature_list_for_hashing)
|
771
|
+
features_hash = hash_string(":".join(feature_list_for_hashing))
|
772
|
+
list_for_hashing.append(f"{HASH_CODE['features_hash']}={features_hash}")
|
726
773
|
if slots:
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
"minimal_set": "c",
|
733
|
-
"ordered_set": "d",
|
734
|
-
"maximal_set": "e",
|
735
|
-
"flexible": "f",
|
736
|
-
"coerce_dtype": "g",
|
737
|
-
"n": "h",
|
738
|
-
"optional": "i",
|
739
|
-
"features_hash": "j",
|
740
|
-
"index": "k",
|
741
|
-
}
|
742
|
-
# we do not want pure informational annotations like otype, name, type, is_type, otype to be part of the hash
|
743
|
-
hash_args = ["dtype", "itype", "minimal_set", "ordered_set", "maximal_set"]
|
744
|
-
list_for_hashing = [
|
745
|
-
f"{HASH_CODE[arg]}={validated_kwargs[arg]}"
|
746
|
-
for arg in hash_args
|
747
|
-
if validated_kwargs[arg] is not None
|
748
|
-
]
|
749
|
-
# only include in hash if not default so that it's backward compatible with records for which flexible was never set
|
750
|
-
if flexible != flexible_default:
|
751
|
-
list_for_hashing.append(f"{HASH_CODE['flexible']}={flexible}")
|
752
|
-
if coerce_dtype != coerce_dtype_default:
|
753
|
-
list_for_hashing.append(f"{HASH_CODE['coerce_dtype']}={coerce_dtype}")
|
754
|
-
if n_features != n_features_default:
|
755
|
-
list_for_hashing.append(f"{HASH_CODE['n']}={n_features}")
|
756
|
-
if index is not None:
|
757
|
-
list_for_hashing.append(f"{HASH_CODE['index']}={index.uid}")
|
758
|
-
if features:
|
759
|
-
if optional_features:
|
760
|
-
feature_list_for_hashing = [
|
761
|
-
feature.uid
|
762
|
-
if feature not in set(optional_features)
|
763
|
-
else f"{feature.uid}({HASH_CODE['optional']})"
|
764
|
-
for feature in features
|
765
|
-
]
|
766
|
-
else:
|
767
|
-
feature_list_for_hashing = [feature.uid for feature in features]
|
768
|
-
# order matters if ordered_set is True
|
769
|
-
if ordered_set:
|
770
|
-
features_hash = hash_string(":".join(feature_list_for_hashing))
|
771
|
-
else:
|
772
|
-
features_hash = hash_string(
|
773
|
-
":".join(sorted(feature_list_for_hashing))
|
774
|
-
)
|
775
|
-
list_for_hashing.append(f"{HASH_CODE['features_hash']}={features_hash}")
|
774
|
+
slots_list_for_hashing = sorted(
|
775
|
+
[f"{key}={component.hash}" for key, component in slots.items()]
|
776
|
+
)
|
777
|
+
slots_hash = hash_string(":".join(slots_list_for_hashing))
|
778
|
+
list_for_hashing.append(f"{HASH_CODE['slots_hash']}={slots_hash}")
|
776
779
|
|
777
780
|
if is_type:
|
778
781
|
validated_kwargs["hash"] = None
|
@@ -929,12 +932,18 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
929
932
|
"""Save schema."""
|
930
933
|
from .save import bulk_create
|
931
934
|
|
935
|
+
features_to_delete = []
|
936
|
+
|
932
937
|
if self.pk is not None:
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
+
existing_features = self.members.to_list() if self.members.exists() else []
|
939
|
+
if hasattr(self, "_features"):
|
940
|
+
features = self._features[1]
|
941
|
+
if features != existing_features:
|
942
|
+
features_to_delete = [
|
943
|
+
f for f in existing_features if f not in features
|
944
|
+
]
|
945
|
+
else:
|
946
|
+
features = existing_features
|
938
947
|
index_feature = self.index
|
939
948
|
_, validated_kwargs, _, _, _ = self._validate_kwargs_calculate_hash(
|
940
949
|
features=[f for f in features if f != index_feature], # type: ignore
|
@@ -958,10 +967,13 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
958
967
|
if validated_kwargs["hash"] != self.hash:
|
959
968
|
from .artifact import Artifact
|
960
969
|
|
961
|
-
datasets = Artifact.filter(schema=self)
|
970
|
+
datasets = Artifact.filter(schema=self)
|
962
971
|
if datasets.exists():
|
963
972
|
logger.warning(
|
964
|
-
f"you
|
973
|
+
f"you're removing these features: {features_to_delete}"
|
974
|
+
)
|
975
|
+
logger.warning(
|
976
|
+
f"you updated the schema hash and might invalidate datasets that were previously validated with this schema:\n{datasets.to_dataframe()}"
|
965
977
|
)
|
966
978
|
self.hash = validated_kwargs["hash"]
|
967
979
|
self.n = validated_kwargs["n"]
|
@@ -984,8 +996,9 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
984
996
|
using: bool | None = kwargs.pop("using", None)
|
985
997
|
related_name, records = self._features
|
986
998
|
|
987
|
-
# .set() does not preserve the order
|
988
|
-
#
|
999
|
+
# self.related_name.set(features) does **not** preserve the order
|
1000
|
+
# but orders by the feature primary key
|
1001
|
+
# hence we need the following more complicated logic
|
989
1002
|
through_model = getattr(self, related_name).through
|
990
1003
|
if self.itype == "Composite":
|
991
1004
|
related_model_split = ["Feature"]
|
@@ -1003,6 +1016,7 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
1003
1016
|
for record in records
|
1004
1017
|
]
|
1005
1018
|
through_model.objects.using(using).bulk_create(links, ignore_conflicts=True)
|
1019
|
+
getattr(self, related_name).remove(*features_to_delete)
|
1006
1020
|
delattr(self, "_features")
|
1007
1021
|
|
1008
1022
|
return self
|
@@ -1140,7 +1154,7 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
1140
1154
|
|
1141
1155
|
# define composite schema
|
1142
1156
|
anndata_schema = ln.Schema(
|
1143
|
-
name="
|
1157
|
+
name="mini_immuno_anndata_schema",
|
1144
1158
|
otype="AnnData",
|
1145
1159
|
slots={"obs": obs_schema, "var": var_schema},
|
1146
1160
|
).save()
|
@@ -1154,7 +1168,7 @@ class Schema(SQLRecord, CanCurate, TracksRun):
|
|
1154
1168
|
if self.itype == "Composite":
|
1155
1169
|
self._slots = {
|
1156
1170
|
link.slot: link.component
|
1157
|
-
for link in self.components.through.filter(composite_id=self.id)
|
1171
|
+
for link in self.components.through.filter(composite_id=self.id)
|
1158
1172
|
}
|
1159
1173
|
return self._slots
|
1160
1174
|
return {}
|
lamindb/models/sqlrecord.py
CHANGED
@@ -23,6 +23,7 @@ import lamindb_setup as ln_setup
|
|
23
23
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
24
24
|
from django.db import IntegrityError, ProgrammingError, connections, models, transaction
|
25
25
|
from django.db.models import CASCADE, PROTECT, Field, Manager, QuerySet
|
26
|
+
from django.db.models import ForeignKey as django_ForeignKey
|
26
27
|
from django.db.models.base import ModelBase
|
27
28
|
from django.db.models.fields.related import (
|
28
29
|
ManyToManyField,
|
@@ -305,11 +306,18 @@ def suggest_records_with_similar_names(
|
|
305
306
|
# but this isn't reliable: https://laminlabs.slack.com/archives/C04FPE8V01W/p1737812808563409
|
306
307
|
# the below needs to be .first() because there might be multiple records with the same
|
307
308
|
# name field in case the record is versioned (e.g. for Transform key)
|
308
|
-
|
309
|
+
if hasattr(record.__class__, "type"):
|
310
|
+
if kwargs.get("type", None) is None:
|
311
|
+
subset = record.__class__.filter(type__isnull=True)
|
312
|
+
else:
|
313
|
+
subset = record.__class__.filter(type=kwargs["type"])
|
314
|
+
else:
|
315
|
+
subset = record.__class__
|
316
|
+
exact_match = subset.filter(**{name_field: kwargs[name_field]}).first()
|
309
317
|
if exact_match is not None:
|
310
318
|
return exact_match
|
311
319
|
queryset = _search(
|
312
|
-
|
320
|
+
subset,
|
313
321
|
kwargs[name_field],
|
314
322
|
field=name_field,
|
315
323
|
truncate_string=True,
|
@@ -687,17 +695,17 @@ class Registry(ModelBase):
|
|
687
695
|
|
688
696
|
def __get_available_fields__(cls) -> set[str]:
|
689
697
|
if cls._available_fields is None:
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
}
|
698
|
+
available_fields = set()
|
699
|
+
for field in cls._meta.get_fields():
|
700
|
+
if not (field_name := field.name).startswith(("_", "links_")):
|
701
|
+
available_fields.add(field_name)
|
702
|
+
if isinstance(field, django_ForeignKey):
|
703
|
+
available_fields.add(field_name + "_id")
|
697
704
|
if cls.__name__ == "Artifact":
|
698
|
-
|
699
|
-
|
700
|
-
|
705
|
+
available_fields.add("visibility") # backward compat
|
706
|
+
available_fields.add("_branch_code") # backward compat
|
707
|
+
available_fields.add("transform")
|
708
|
+
cls._available_fields = available_fields
|
701
709
|
return cls._available_fields
|
702
710
|
|
703
711
|
|
@@ -957,13 +965,20 @@ class BaseSQLRecord(models.Model, metaclass=Registry):
|
|
957
965
|
init_self_from_db(self, pre_existing_record)
|
958
966
|
elif (
|
959
967
|
isinstance(e, ProgrammingError)
|
960
|
-
and hasattr(self, "space")
|
961
968
|
and "new row violates row-level security policy" in error_msg
|
969
|
+
and (
|
970
|
+
(is_locked := getattr(self, "is_locked", False))
|
971
|
+
or hasattr(self, "space")
|
972
|
+
)
|
962
973
|
):
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
974
|
+
if is_locked:
|
975
|
+
no_write_msg = "It is not allowed to modify or create locked ('is_locked=True') records."
|
976
|
+
else:
|
977
|
+
no_write_msg = (
|
978
|
+
f"You're not allowed to write to the space '{self.space.name}'.\n"
|
979
|
+
"Please contact administrators of the space if you need write access."
|
980
|
+
)
|
981
|
+
raise NoWriteAccess(no_write_msg) from None
|
967
982
|
else:
|
968
983
|
raise
|
969
984
|
# call the below in case a user makes more updates to the record
|
lamindb/models/transform.py
CHANGED
@@ -4,7 +4,7 @@ import warnings
|
|
4
4
|
from typing import TYPE_CHECKING, overload
|
5
5
|
|
6
6
|
from django.db import models
|
7
|
-
from django.db.models import PROTECT
|
7
|
+
from django.db.models import PROTECT, Q
|
8
8
|
from lamin_utils import logger
|
9
9
|
from lamindb_setup.core.hashing import HASH_LENGTH, hash_string
|
10
10
|
|
@@ -299,8 +299,11 @@ class Transform(SQLRecord, IsVersioned):
|
|
299
299
|
hash = None
|
300
300
|
if source_code is not None:
|
301
301
|
hash = hash_string(source_code)
|
302
|
-
|
303
|
-
|
302
|
+
|
303
|
+
transform_candidate = Transform.objects.filter(
|
304
|
+
~Q(branch_id=-1),
|
305
|
+
hash=hash,
|
306
|
+
is_latest=True,
|
304
307
|
).one_or_none()
|
305
308
|
if transform_candidate is not None:
|
306
309
|
init_self_from_db(self, transform_candidate)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: lamindb
|
3
|
-
Version: 1.
|
4
|
-
Summary: A data
|
3
|
+
Version: 1.13.0
|
4
|
+
Summary: A data framework for biology.
|
5
5
|
Author-email: Lamin Labs <open-source@lamin.ai>
|
6
6
|
Requires-Python: >=3.10,<3.14
|
7
7
|
Description-Content-Type: text/markdown
|
@@ -10,13 +10,14 @@ Classifier: Programming Language :: Python :: 3.11
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
12
12
|
Requires-Dist: lamin_utils==0.15.0
|
13
|
-
Requires-Dist: lamin_cli==1.8.
|
14
|
-
Requires-Dist: lamindb_setup[aws]==1.
|
13
|
+
Requires-Dist: lamin_cli==1.8.1
|
14
|
+
Requires-Dist: lamindb_setup[aws]==1.12.0
|
15
15
|
Requires-Dist: bionty==1.8.1
|
16
16
|
Requires-Dist: wetlab==1.6.1
|
17
17
|
Requires-Dist: nbproject==0.11.1
|
18
18
|
Requires-Dist: jupytext
|
19
19
|
Requires-Dist: nbconvert>=7.2.1
|
20
|
+
Requires-Dist: ipykernel<7.0.0
|
20
21
|
Requires-Dist: pyyaml
|
21
22
|
Requires-Dist: pyarrow
|
22
23
|
Requires-Dist: pandera>=0.24.0
|
@@ -56,38 +57,41 @@ Provides-Extra: zarr
|
|
56
57
|
[](https://pypi.org/project/lamindb)
|
57
58
|
[](https://pepy.tech/project/lamindb)
|
58
59
|
|
59
|
-
# LaminDB - A data
|
60
|
+
# LaminDB - A data framework for biology
|
60
61
|
|
61
|
-
|
62
|
+
Makes your data queryable, traceable, reproducible, and FAIR. One API: lakehouse, lineage, feature store, ontologies, LIMS, ELN.
|
62
63
|
|
63
64
|
<details>
|
64
65
|
<summary>Why?</summary>
|
65
66
|
|
66
67
|
Reproducing analytical results or understanding how a dataset or model was created can be a pain.
|
67
|
-
|
68
|
-
Even maintaining
|
68
|
+
Training models on historical data, LIMS & ELN systems, orthogonal assays, or datasets from other teams is even harder.
|
69
|
+
Even maintaining an overview of a project's datasets & analyses is more difficult than it should be.
|
69
70
|
|
70
|
-
Biological datasets are typically managed with versioned storage systems, GUI-focused
|
71
|
+
Biological datasets are typically managed with versioned storage systems, GUI-focused platforms, structureless data lakes, rigid data warehouses (SQL, monolithic arrays), or tabular lakehouses.
|
71
72
|
|
72
73
|
LaminDB extends the lakehouse architecture to biological registries & datasets beyond tables (`DataFrame`, `AnnData`, `.zarr`, `.tiledbsoma`, ...) with enough structure to enable queries and enough freedom to keep the pace of R&D high.
|
73
74
|
Moreover, it provides context through data lineage -- tracing data and code, scientists and models -- and abstractions for biological domain knowledge and experimental metadata.
|
74
75
|
|
75
76
|
</details>
|
76
77
|
|
77
|
-
|
78
|
+
<img width="800px" src="https://lamin-site-assets.s3.amazonaws.com/.lamindb/BunYmHkyFLITlM5M0005.png">
|
78
79
|
|
79
|
-
|
80
|
-
- **unified access:** storage locations (local, S3, GCP, ...), SQL databases (Postgres, SQLite) & ontologies
|
81
|
-
- **lakehouse**: manage, monitor & validate features, labels & dataset schemas; distributed queries and batch loading
|
82
|
-
- **biological formats:** validate & annotate `DataFrame`, `AnnData`, `SpatialData`, ... backed by `parquet`, `zarr`, HDF5, LanceDB, ...
|
83
|
-
- **biological entities**: manage experimental metadata & ontologies based on the Django ORM
|
84
|
-
- **reproducible & auditable:** auto-version & timestamp execution reports, source code & compute environments, attribute records to users
|
85
|
-
- **zero lock-in & scalable:** runs in your infrastructure; is _not_ a client for a rate-limited REST API
|
86
|
-
- **extendable:** create custom plug-ins for your own applications based on the Django ecosystem
|
87
|
-
- **integrations:** visualization tools like [vitessce](https://docs.lamin.ai/vitessce), workflow managers like [nextflow](https://docs.lamin.ai/nextflow) & [redun](https://docs.lamin.ai/redun), and [other tools](https://docs.lamin.ai/integrations)
|
88
|
-
- **production-ready:** used in BigPharma, BioTech, hospitals & top labs
|
80
|
+
Highlights:
|
89
81
|
|
90
|
-
|
82
|
+
- **lineage** → track inputs & outputs of notebooks, scripts, functions & pipelines with a single line of code
|
83
|
+
- **lakehouse** → manage, monitor & validate schemas; query across many datasets
|
84
|
+
- **feature store** → manage features & labels; leverage batch loading
|
85
|
+
- **FAIR datasets** → validate & annotate `DataFrame`, `AnnData`, `SpatialData`, `parquet`, `.h5ad`, `zarr`, ...
|
86
|
+
- **LIMS & ELN** → manage experimental metadata, ontologies & markdown notes
|
87
|
+
- **unified access** → storage locations (local, S3, GCP, ...), SQL databases (Postgres, SQLite) & ontologies
|
88
|
+
- **reproducible & auditable** → auto-version & timestamp execution reports, source code & environments; attribute records to users
|
89
|
+
- **integrations** → [vitessce](https://docs.lamin.ai/vitessce), [nextflow](https://docs.lamin.ai/nextflow), [redun](https://docs.lamin.ai/redun), and [more](https://docs.lamin.ai/integrations)
|
90
|
+
- **zero lock-in & scalable** → runs in your infrastructure; not a client for a rate-limited REST API
|
91
|
+
- **extendable** → create custom plug-ins based on the Django ORM
|
92
|
+
- **production-ready** → used in BigPharma, BioTech, hospitals & top labs
|
93
|
+
|
94
|
+
If you want a GUI, you can connect your LaminDB instance to LaminHub and close the drylab-wetlab feedback loop: [lamin.ai](https://lamin.ai).
|
91
95
|
|
92
96
|
## Docs
|
93
97
|
|
@@ -159,7 +163,7 @@ You can organize datasets with validation & annotation of any kind of metadata t
|
|
159
163
|
To annotate an artifact with a label, use:
|
160
164
|
|
161
165
|
```python
|
162
|
-
my_experiment = ln.Record(name="My experiment").save() # create a label
|
166
|
+
my_experiment = ln.Record(name="My experiment").save() # create a label record
|
163
167
|
artifact.records.add(my_experiment) # annotate the artifact with the label
|
164
168
|
```
|
165
169
|
|
@@ -1,8 +1,8 @@
|
|
1
|
-
lamindb/__init__.py,sha256=
|
2
|
-
lamindb/_finish.py,sha256=
|
3
|
-
lamindb/_tracked.py,sha256=
|
1
|
+
lamindb/__init__.py,sha256=f4CmvRtPbJVqFon19OSyUW6Z5ZkGirIx8q08q7lCoqM,3222
|
2
|
+
lamindb/_finish.py,sha256=YrdajfTIrz5dvJKsbUJ44SEgXULDgc6zvKQCbGnI2NQ,21351
|
3
|
+
lamindb/_tracked.py,sha256=MR6b283ErvwKcP9dOcju-swOmVjIrJuYCThuIeqvwbA,3972
|
4
4
|
lamindb/_view.py,sha256=GOKTfwnEaly9fdeWo9SlhYRc3UWEyLDmTlIUzjFXMYY,4960
|
5
|
-
lamindb/errors.py,sha256=
|
5
|
+
lamindb/errors.py,sha256=j-oUYLnXF8SPTeewWWSifN9ettZ2fdhGaZ9Zq1p5-po,2380
|
6
6
|
lamindb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
lamindb/base/__init__.py,sha256=u623ZDoNCwwyLLt_Jhtp3KDeum0LOg6cwAqn5TEgz_E,313
|
8
8
|
lamindb/base/dtypes.py,sha256=Bfrca8Slb3vIAIMLd-mjG4_9bTHz-LlThSIUHvfNmhA,3189
|
@@ -13,7 +13,7 @@ lamindb/base/uids.py,sha256=cLBi5mIlsf1ltkTb17r1FLzlOjlGmjvsCygoVJHQ-A8,2116
|
|
13
13
|
lamindb/base/users.py,sha256=8MSmAvCKoUF15YsDE6BGLBXsFWpfoEEg8iDTKZ7kD48,848
|
14
14
|
lamindb/core/__init__.py,sha256=I9F-GugBMZwFLpUPb1MXyLfccIVAj021Gb_00h_18MY,633
|
15
15
|
lamindb/core/_compat.py,sha256=NLnKk1qk4xdgMV-QwFDnBnbio02ujjlF86icvhpdv4c,2029
|
16
|
-
lamindb/core/_context.py,sha256=
|
16
|
+
lamindb/core/_context.py,sha256=HAsQXpeibHy8t1RTi_sTypSaHz5yrj3RkbUax7DYRKM,40950
|
17
17
|
lamindb/core/_mapped_collection.py,sha256=eOrBQX23N-3ifKVMzbhLip_cGHUaJe0T7mz_JtGAcWI,25750
|
18
18
|
lamindb/core/_settings.py,sha256=519bOSopRb7_nE874KDJB263v1xFlk4TOqhBNNeO4yw,10992
|
19
19
|
lamindb/core/_sync_git.py,sha256=Z7keuyS5X7CAj285sEbZIFExZF9mtjGH8DzKwz3xhHw,5881
|
@@ -36,8 +36,8 @@ lamindb/core/subsettings/__init__.py,sha256=f_vOqZOjVGez8pLmtrUuc_ayDGXl07t_ZY-P
|
|
36
36
|
lamindb/core/subsettings/_annotation_settings.py,sha256=o-yTYw-NmjFmtehbKU8qnf7tyaeDFkTRGan1pXAIVT0,370
|
37
37
|
lamindb/core/subsettings/_creation_settings.py,sha256=NGHWKqCFSzVNBxAr2VnmdYguiFdW29XUK7T9wRsVshg,906
|
38
38
|
lamindb/curators/__init__.py,sha256=WLnaVxrhQGZxGB3pjg-SM4oUu6DaKA78S_J3BfVKLEg,496
|
39
|
-
lamindb/curators/_legacy.py,sha256=
|
40
|
-
lamindb/curators/core.py,sha256=
|
39
|
+
lamindb/curators/_legacy.py,sha256=ZCHCcm9fd966IHTAJI6tZ6MSdbsmm1blxQCGe339F3A,55192
|
40
|
+
lamindb/curators/core.py,sha256=e7rwOs0hZ7eEpEDcgM2U8Mr0wEk0AhmW-wqRShUAiUY,80049
|
41
41
|
lamindb/examples/__init__.py,sha256=Ru_lqgw_uRsewYAB3NEkB5CQy7Y5KWxNf9vmL5Jxfk4,213
|
42
42
|
lamindb/examples/cellxgene/__init__.py,sha256=0itpr7sthjaZAbL5nGTVTisL5OeP-3WqKFr8zyDWxYM,247
|
43
43
|
lamindb/examples/cellxgene/_cellxgene.py,sha256=Vgot8L9ZmaX_PwOlsghmVKsnstxj979yRGjWNtDclUw,12885
|
@@ -45,7 +45,7 @@ lamindb/examples/cellxgene/cellxgene_schema_versions.csv,sha256=IbtgPbrMksqr3q9z
|
|
45
45
|
lamindb/examples/croissant/__init__.py,sha256=3YnURcsGWdkwyv-SdW1nfECE0CGMpJ38E2e-DJB1HQ4,2310
|
46
46
|
lamindb/examples/croissant/mini_immuno.anndata.zarr_metadata.json,sha256=XhY4wnFyMoK4Thkaolh2yJxtU6sX0bdFsJvRvt942k8,2921
|
47
47
|
lamindb/examples/datasets/__init__.py,sha256=SKeX5kgjfXtNkUek4GfLYsgn-bGO8UsuF4Qf3R_zN-4,1988
|
48
|
-
lamindb/examples/datasets/_core.py,sha256=
|
48
|
+
lamindb/examples/datasets/_core.py,sha256=u_wc2PJf7OVJBiDzWUlyRBuq17gKyW8EhT4xKhXlf9c,21557
|
49
49
|
lamindb/examples/datasets/_fake.py,sha256=BZF9R_1iF0HDnvtZNqL2FtsjSMuqDIfuFxnw_LJYIh4,953
|
50
50
|
lamindb/examples/datasets/_small.py,sha256=wHJb6eXzkQC_Ma8VqX7Orb3nGuAbyNdrr0jxJ93jjxc,4852
|
51
51
|
lamindb/examples/datasets/mini_immuno.py,sha256=ZEL9T4zhCKm8ggqU7VVhuihVKPR3MmlkJNOtdygH2v4,6107
|
@@ -56,10 +56,10 @@ lamindb/examples/schemas/__init__.py,sha256=NPDp7VjMOHStEIthx3xW9NSHtY7jnnMzrNPc
|
|
56
56
|
lamindb/examples/schemas/_anndata.py,sha256=TAQrnBLZhH4TgbznrJDdGK_Gze6cf1MvyXuCcKIvb1g,1210
|
57
57
|
lamindb/examples/schemas/_simple.py,sha256=Dspj5QRmv241IstBxuc1E1Q5YeEqTOnOvakg7ChPj1k,911
|
58
58
|
lamindb/examples/wandb/__init__.py,sha256=QOgHfXVlKae23MGyXaRaZoC9Byx_OkZOtda6E6hsgQk,1340
|
59
|
-
lamindb/integrations/__init__.py,sha256=
|
59
|
+
lamindb/integrations/__init__.py,sha256=r0aRmfRYDHy6a3N-AZ_miHbPBkpS_oV-NEZqNRICK1w,422
|
60
60
|
lamindb/integrations/_croissant.py,sha256=RNX6dDPPun1QG6t456GxK19t071_FJWzwmUXiVDkHFE,5200
|
61
|
-
lamindb/integrations/_lightning.py,sha256=RD_L3jf0IOBbD7GScJ6nwa1-R8SpNsg33vbyuC7KVaw,2730
|
62
61
|
lamindb/integrations/_vitessce.py,sha256=s2F8KPpYVG0zUOTaDJgH1XAJtQDg1zrD_SxC4ZHUkHk,4035
|
62
|
+
lamindb/integrations/lightning.py,sha256=lYIFpnrwUiNO36_Iqz_XERisRseCqaod-0laoaS8yzs,2817
|
63
63
|
lamindb/migrations/0069_squashed.py,sha256=7XdiRW0MBtr3Jck9dbIy_9qxmB_sjtLM1SH9x062d2k,62631
|
64
64
|
lamindb/migrations/0070_lamindbv1_migrate_data.py,sha256=tyq_xi6U8TXi9C2Raf6v_UTtfyfqQOUIFJzYj4oCgAE,2429
|
65
65
|
lamindb/migrations/0071_lamindbv1_migrate_schema.py,sha256=o47wYtMTuQ-LEVQSftiV0Wwvs3bcISZ_JtWsMxlLykk,25130
|
@@ -125,40 +125,41 @@ lamindb/migrations/0130_branch_space_alter_artifactblock_artifact_and_more.py,sh
|
|
125
125
|
lamindb/migrations/0131_record_unique_name_type_space.py,sha256=5xPS_BmMG0Swb9lOQm59yzFhzuCifxNvhUkgiD0Eciw,486
|
126
126
|
lamindb/migrations/0132_record_parents_record_reference_and_more.py,sha256=2n7pdin6n9BwGpX9g04X_zYmtg0lV9ioj1PlT3Oss5w,1896
|
127
127
|
lamindb/migrations/0133_artifactuser_artifact_users.py,sha256=GrZIvvIoPfbzBhLLfk7wFnaceBvdbzNSx0kDewRcQDA,3812
|
128
|
-
lamindb/migrations/
|
128
|
+
lamindb/migrations/0134_run_params.py,sha256=fTCpihgBBS1Cof5XU41wS84CBnfwvJO1VI9SE0xlbyw,383
|
129
|
+
lamindb/migrations/0134_squashed.py,sha256=zY2MWkp1LAmKe2KVBVRHpC-ghEs0nG2mihnybgz8mNI,199675
|
129
130
|
lamindb/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
130
131
|
lamindb/models/__init__.py,sha256=qxvjXDn23MtdSPVK0qHzdcLUDRZkIzmd1z3dDhAfXVA,2656
|
131
132
|
lamindb/models/_describe.py,sha256=Co6_whJG7Pm2Sl6YDmGoYL28XZyJ7VqICHTfgGedxvw,10919
|
132
133
|
lamindb/models/_django.py,sha256=L5Tg1NVG6e1zmvJrzk8xbDGnSWvc4l0kNMV1R1GH1zs,12289
|
133
|
-
lamindb/models/_feature_manager.py,sha256=
|
134
|
+
lamindb/models/_feature_manager.py,sha256=_1TPe0X9msYALVgtBwzWFKEAkTPVvwOfagVDKbFuhEo,57869
|
134
135
|
lamindb/models/_from_values.py,sha256=ymR8b0Qa3ZiTFTIuMsFYiBNH16ggDPlYeFjCaFgGETA,13372
|
135
136
|
lamindb/models/_is_versioned.py,sha256=ZlTuiAoQuXJqjPhemRU9U3YZGID6hiuclHT9KLzOg2w,7445
|
136
|
-
lamindb/models/_label_manager.py,sha256=
|
137
|
+
lamindb/models/_label_manager.py,sha256=zIfZuByPx6s8Q1tIwlMnacYKhjklW44IDZsCG1LvhtY,12054
|
137
138
|
lamindb/models/_relations.py,sha256=ShOIOWV20WQjQkeqrtAuOuud1rRHADv4d0COTJaYwiU,3937
|
138
|
-
lamindb/models/artifact.py,sha256
|
139
|
-
lamindb/models/artifact_set.py,sha256=
|
140
|
-
lamindb/models/block.py,sha256=
|
141
|
-
lamindb/models/can_curate.py,sha256=
|
142
|
-
lamindb/models/collection.py,sha256=
|
139
|
+
lamindb/models/artifact.py,sha256=BWldye5tdHHh8gBwoHvCRI33zxiQdvu1F1waTgjnV-s,125497
|
140
|
+
lamindb/models/artifact_set.py,sha256=GOWojhGtKYt0wWDZno-nqDkrO_ST-qrTvR2irmVwXY4,5933
|
141
|
+
lamindb/models/block.py,sha256=qlrw_agiBEi8oDu-OKYKm2bAVeE3xamYYIVpgsi5iWo,5455
|
142
|
+
lamindb/models/can_curate.py,sha256=KRartLa9V1JhlF7sx0XuHEpTX6EZSFjQzWlhYGr2cC0,29304
|
143
|
+
lamindb/models/collection.py,sha256=YCT3FwQG88FtcNh0QJF7yFcF0IfydEakGViGuwgRFwk,27196
|
143
144
|
lamindb/models/feature.py,sha256=nD2ilcVcXQngRpANzOjqqnpa1BvCVjg9pQS9Tc_xM6k,41781
|
144
|
-
lamindb/models/has_parents.py,sha256=
|
145
|
+
lamindb/models/has_parents.py,sha256=GJskgPVpWP5PNHCw10ZexaIEwBYhxl3ljc6Ov1VyFus,20620
|
145
146
|
lamindb/models/project.py,sha256=n4TpMj3gMYH0pQa7BC8PrZ-lKnwsPHPeM4Frd8c1qxE,16751
|
146
|
-
lamindb/models/query_manager.py,sha256=
|
147
|
-
lamindb/models/query_set.py,sha256=
|
147
|
+
lamindb/models/query_manager.py,sha256=op_bBEoY_qlulT7KpI7-yWdhZrXTM-5nK17DhW2pUJY,11351
|
148
|
+
lamindb/models/query_set.py,sha256=T43c7B3ce5qjNa3Bb6s7IrofmvKHifeHzD2RcBSqbP4,41712
|
148
149
|
lamindb/models/record.py,sha256=VDjhibmnNA7khBhyrRKflEVhAlT1M5SIQlsIEhTxrwg,18518
|
149
|
-
lamindb/models/run.py,sha256=
|
150
|
+
lamindb/models/run.py,sha256=ABFZAvLFHU72705nAoWsALen_ergTdva5h7PlDkNLII,15980
|
150
151
|
lamindb/models/save.py,sha256=1V3xufAPe03uMy2h10WK6wVhJbvjc0kVj_EF6ecj_Vk,17376
|
151
|
-
lamindb/models/schema.py,sha256=
|
152
|
-
lamindb/models/sqlrecord.py,sha256=
|
152
|
+
lamindb/models/schema.py,sha256=EA2nNz6yX5aOS5Ds-KGKv5APiyBWQ3rD1rH4kjJ99mY,51389
|
153
|
+
lamindb/models/sqlrecord.py,sha256=LHF9uut2AVNs93DRzfaHpsAd5bnA0YC_d0fksAzIHYY,76335
|
153
154
|
lamindb/models/storage.py,sha256=DyKC6hfCfmxZ9eZRTC6b_p8MqIlXCMHZNLN_nwDcOwc,15859
|
154
|
-
lamindb/models/transform.py,sha256=
|
155
|
+
lamindb/models/transform.py,sha256=0AOgHUGUIr1WQ8fkdo9FVH4HRPZzwi-PJZavmf6t23c,13153
|
155
156
|
lamindb/models/ulabel.py,sha256=bZvlyiZtm0Niwi60M052GsAGA19HmYwZICzSrRrIJWw,8646
|
156
157
|
lamindb/setup/__init__.py,sha256=QZ-JF8IzO_ckDOU223lsJrdO5ay7cDFgvCbkLeAuxYA,467
|
157
158
|
lamindb/setup/_switch.py,sha256=njZJN__JOhVrBFGClQG1wobdhJJp6l_XzPGKtKSCrfU,434
|
158
159
|
lamindb/setup/core/__init__.py,sha256=SevlVrc2AZWL3uALbE5sopxBnIZPWZ1IB0NBDudiAL8,167
|
159
160
|
lamindb/setup/errors/__init__.py,sha256=bAHTxOUJW1rm4zpF0Pvqkftn8W6iMGnQ-uyNBu13Nfg,171
|
160
161
|
lamindb/setup/types/__init__.py,sha256=ATaosOi6q-cDWB52T69_sRmLMqj8cHfc-vljzZsrJNw,169
|
161
|
-
lamindb-1.
|
162
|
-
lamindb-1.
|
163
|
-
lamindb-1.
|
164
|
-
lamindb-1.
|
162
|
+
lamindb-1.13.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
163
|
+
lamindb-1.13.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
164
|
+
lamindb-1.13.0.dist-info/METADATA,sha256=iEn3xVvHgygLuNlXK_TplFuunA-grJJiJroGcDC-ffI,8121
|
165
|
+
lamindb-1.13.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|