lamindb 1.11.3__py3-none-any.whl → 1.12.1__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 (54) hide show
  1. lamindb/__init__.py +8 -14
  2. lamindb/_tracked.py +2 -0
  3. lamindb/base/types.py +1 -3
  4. lamindb/core/_context.py +16 -31
  5. lamindb/core/_mapped_collection.py +2 -2
  6. lamindb/core/storage/paths.py +5 -3
  7. lamindb/curators/core.py +15 -4
  8. lamindb/examples/__init__.py +3 -1
  9. lamindb/examples/croissant/__init__.py +3 -1
  10. lamindb/examples/mlflow/__init__.py +38 -0
  11. lamindb/examples/wandb/__init__.py +40 -0
  12. lamindb/integrations/__init__.py +26 -0
  13. lamindb/integrations/_lightning.py +87 -0
  14. lamindb/migrations/0120_add_record_fk_constraint.py +1 -1
  15. lamindb/migrations/0122_remove_personproject_person_and_more.py +219 -0
  16. lamindb/migrations/0123_alter_artifact_description_alter_branch_description_and_more.py +82 -0
  17. lamindb/migrations/0124_page_artifact_page_collection_page_feature_page_and_more.py +15 -0
  18. lamindb/migrations/0125_artifact_is_locked_collection_is_locked_and_more.py +79 -0
  19. lamindb/migrations/0126_alter_artifact_is_locked_alter_collection_is_locked_and_more.py +105 -0
  20. lamindb/migrations/0127_alter_run_status_code_feature_dtype.py +31 -0
  21. lamindb/migrations/0128_artifact__real_key.py +21 -0
  22. lamindb/migrations/0129_remove_feature_page_remove_project_page_and_more.py +779 -0
  23. lamindb/migrations/0130_branch_space_alter_artifactblock_artifact_and_more.py +170 -0
  24. lamindb/migrations/0131_record_unique_name_type_space.py +18 -0
  25. lamindb/migrations/0132_record_parents_record_reference_and_more.py +61 -0
  26. lamindb/migrations/0133_artifactuser_artifact_users.py +108 -0
  27. lamindb/migrations/{0119_squashed.py → 0133_squashed.py} +1211 -322
  28. lamindb/models/__init__.py +14 -4
  29. lamindb/models/_django.py +1 -2
  30. lamindb/models/_feature_manager.py +1 -0
  31. lamindb/models/_is_versioned.py +14 -16
  32. lamindb/models/_relations.py +7 -0
  33. lamindb/models/artifact.py +99 -56
  34. lamindb/models/artifact_set.py +20 -3
  35. lamindb/models/block.py +174 -0
  36. lamindb/models/can_curate.py +7 -9
  37. lamindb/models/collection.py +9 -9
  38. lamindb/models/feature.py +38 -38
  39. lamindb/models/has_parents.py +15 -6
  40. lamindb/models/project.py +44 -99
  41. lamindb/models/query_manager.py +1 -1
  42. lamindb/models/query_set.py +36 -8
  43. lamindb/models/record.py +169 -46
  44. lamindb/models/run.py +44 -10
  45. lamindb/models/save.py +7 -7
  46. lamindb/models/schema.py +9 -2
  47. lamindb/models/sqlrecord.py +87 -35
  48. lamindb/models/storage.py +13 -3
  49. lamindb/models/transform.py +7 -2
  50. lamindb/models/ulabel.py +6 -23
  51. {lamindb-1.11.3.dist-info → lamindb-1.12.1.dist-info}/METADATA +18 -21
  52. {lamindb-1.11.3.dist-info → lamindb-1.12.1.dist-info}/RECORD +54 -38
  53. {lamindb-1.11.3.dist-info → lamindb-1.12.1.dist-info}/LICENSE +0 -0
  54. {lamindb-1.11.3.dist-info → lamindb-1.12.1.dist-info}/WHEEL +0 -0
@@ -66,7 +66,7 @@ def _inspect(
66
66
  values = _concat_lists(values)
67
67
 
68
68
  field_str = get_name_field(cls, field=field)
69
- queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.objects.all()
69
+ queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.filter().all()
70
70
  registry = queryset.model
71
71
  model_name = registry._meta.model.__name__
72
72
  if isinstance(source, SQLRecord):
@@ -170,7 +170,7 @@ def _validate(
170
170
 
171
171
  field_str = get_name_field(cls, field=field)
172
172
 
173
- queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.objects.all()
173
+ queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.filter().all()
174
174
  registry = queryset.model
175
175
  if isinstance(source, SQLRecord):
176
176
  _check_if_record_in_db(source, queryset.db)
@@ -191,10 +191,8 @@ def _validate(
191
191
  )
192
192
  if field_values.empty:
193
193
  if not mute:
194
- msg = (
195
- f"Your {cls.__name__} registry is empty, consider populating it first!"
196
- )
197
- if hasattr(cls, "source_id"):
194
+ msg = f"Your {queryset.model.__name__} registry is empty, consider populating it first!"
195
+ if hasattr(queryset.model, "source_id"):
198
196
  msg += "\n → use `.import_source()` to import records from a source, e.g. a public ontology"
199
197
  logger.warning(msg)
200
198
  return np.array([False] * len(values))
@@ -238,7 +236,7 @@ def _standardize(
238
236
  return_field_str = get_name_field(
239
237
  cls, field=field if return_field is None else return_field
240
238
  )
241
- queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.objects.all()
239
+ queryset = cls.all() if isinstance(cls, (QuerySet, Manager)) else cls.filter().all()
242
240
  registry = queryset.model
243
241
  if isinstance(source, SQLRecord):
244
242
  _check_if_record_in_db(source, queryset.db)
@@ -358,7 +356,7 @@ def _add_or_remove_synonyms(
358
356
  from IPython.display import display
359
357
 
360
358
  syns_all = (
361
- record.__class__.objects.exclude(synonyms="").exclude(synonyms=None).all() # type: ignore
359
+ record.__class__.filter().exclude(synonyms="").exclude(synonyms=None).all() # type: ignore
362
360
  )
363
361
  if len(syns_all) == 0:
364
362
  return
@@ -591,7 +589,7 @@ class CanCurate:
591
589
  A list of validated records. For bionty registries. Also returns knowledge-coupled records.
592
590
 
593
591
  Notes:
594
- For more info, see tutorial: :doc:`docs:bio-registries`.
592
+ For more info, see tutorial: :doc:`docs:manage-ontologies`.
595
593
 
596
594
  Example::
597
595
 
@@ -49,6 +49,7 @@ if TYPE_CHECKING:
49
49
  from pyarrow.dataset import Dataset as PyArrowDataset
50
50
 
51
51
  from ..core.storage import UPath
52
+ from .block import CollectionBlock
52
53
  from .project import Project, Reference
53
54
  from .query_set import QuerySet
54
55
  from .transform import Transform
@@ -174,7 +175,7 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
174
175
  # below is the only case in which we use a TextField
175
176
  # for description; we do so because users had descriptions exceeding 255 chars
176
177
  # in their instances
177
- description: str | None = TextField(null=True, db_index=True)
178
+ description: str | None = TextField(null=True)
178
179
  """A description or title."""
179
180
  hash: str | None = CharField(
180
181
  max_length=HASH_LENGTH, db_index=True, null=True, unique=True
@@ -224,6 +225,8 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
224
225
  """Linked projects."""
225
226
  references: Reference
226
227
  """Linked references."""
228
+ blocks: CollectionBlock
229
+ """Blocks that annotate this collection."""
227
230
 
228
231
  @overload
229
232
  def __init__(
@@ -437,7 +440,6 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
437
440
 
438
441
  dataframe = _open_dataframe(paths, engine=engine, **kwargs)
439
442
  # track only if successful
440
- # is it really needed if tracking is done in self.ordered_artifacts.all()? - Sergei
441
443
  _track_run_input(self, is_run_input)
442
444
  return dataframe
443
445
 
@@ -538,24 +540,23 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
538
540
  dtype,
539
541
  )
540
542
  # track only if successful
541
- # is it really needed if tracking is done in self.ordered_artifacts.all()? - Sergei
542
543
  _track_run_input(self, is_run_input)
543
544
  return ds
544
545
 
545
546
  def cache(self, is_run_input: bool | None = None) -> list[UPath]:
546
547
  """Download cloud artifacts in collection to local cache.
547
548
 
548
- Follows synching logic: only caches outdated artifacts.
549
+ Follows syncing logic: only downloads outdated artifacts.
549
550
 
550
- Returns paths to locally cached on-disk artifacts.
551
+ Returns ordered paths to locally cached on-disk artifacts via `.ordered_artifacts.all()`:
551
552
 
552
553
  Args:
553
554
  is_run_input: Whether to track this collection as run input.
554
555
  """
555
556
  path_list = []
556
557
  for artifact in self.ordered_artifacts.all():
557
- path_list.append(artifact.cache())
558
- # is it really needed if tracking is done in self.ordered_artifacts.all()? - Sergei
558
+ # do not want to track data lineage on the artifact level
559
+ path_list.append(artifact.cache(is_run_input=False))
559
560
  _track_run_input(self, is_run_input)
560
561
  return path_list
561
562
 
@@ -573,7 +574,6 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
573
574
  artifacts = self.ordered_artifacts.all()
574
575
  concat_object = _load_concat_artifacts(artifacts, join, **kwargs)
575
576
  # only call it here because there might be errors during load or concat
576
- # is it really needed if tracking is done in self.ordered_artifacts.all()? - Sergei
577
577
  _track_run_input(self, is_run_input)
578
578
  return concat_object
579
579
 
@@ -640,7 +640,7 @@ class Collection(SQLRecord, IsVersioned, TracksRun, TracksUpdates):
640
640
  you non-deterministic order.
641
641
 
642
642
  Using the property `.ordered_artifacts` allows to iterate through a set
643
- that's ordered in the order of creation.
643
+ that's ordered by the order of the list that created the collection.
644
644
  """
645
645
  return self.artifacts.order_by("links_collection__id")
646
646
 
lamindb/models/feature.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import warnings
4
5
  from typing import TYPE_CHECKING, Any, get_args, overload
5
6
 
6
7
  import numpy as np
@@ -43,6 +44,8 @@ from .sqlrecord import BaseSQLRecord, Registry, SQLRecord, _get_record_kwargs
43
44
  if TYPE_CHECKING:
44
45
  from collections.abc import Iterable
45
46
 
47
+ from .block import FeatureBlock
48
+ from .projects import Project
46
49
  from .schema import Schema
47
50
 
48
51
  FEATURE_DTYPES = set(get_args(Dtype))
@@ -538,7 +541,7 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
538
541
  custom registries to manage high-level derived features like gene sets.
539
542
 
540
543
  See Also:
541
- :meth:`~lamindb.Feature.from_df`
544
+ :meth:`~lamindb.Feature.from_dataframe`
542
545
  Create feature records from DataFrame.
543
546
  :attr:`~lamindb.Artifact.features`
544
547
  Feature manager of an artifact or collection.
@@ -600,6 +603,12 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
600
603
  class Meta(SQLRecord.Meta, TracksRun.Meta, TracksUpdates.Meta):
601
604
  abstract = False
602
605
  app_label = "lamindb"
606
+ constraints = [
607
+ models.CheckConstraint(
608
+ condition=models.Q(is_type=True) | models.Q(dtype__isnull=False),
609
+ name="dtype_not_null_when_is_type_false",
610
+ )
611
+ ]
603
612
 
604
613
  _name_field: str = "name"
605
614
  _aux_fields: dict[str, tuple[str, type]] = {
@@ -616,7 +625,8 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
616
625
  """Universal id, valid across DB instances."""
617
626
  name: str = CharField(max_length=150, db_index=True)
618
627
  """Name of feature."""
619
- dtype: Dtype | None = CharField(db_index=True, null=True)
628
+ # dtype can be null if is_type is True
629
+ dtype: Dtype = CharField(db_index=True, null=True)
620
630
  """Data type (:class:`~lamindb.base.types.Dtype`)."""
621
631
  type: Feature | None = ForeignKey(
622
632
  "self", PROTECT, null=True, related_name="features"
@@ -631,7 +641,7 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
631
641
  """Distinguish types from instances of the type."""
632
642
  unit: str | None = CharField(max_length=30, db_index=True, null=True)
633
643
  """Unit of measure, ideally SI (`m`, `s`, `kg`, etc.) or 'normalized' etc. (optional)."""
634
- description: str | None = CharField(db_index=True, null=True)
644
+ description: str | None = TextField(null=True)
635
645
  """A description."""
636
646
  array_rank: int = models.SmallIntegerField(default=0, db_index=True)
637
647
  """Rank of feature.
@@ -684,6 +694,10 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
684
694
  # backward fields
685
695
  values: FeatureValue
686
696
  """Values for this feature."""
697
+ projects: Project
698
+ """Annotating projects."""
699
+ blocks: FeatureBlock
700
+ """Blocks that annotate this feature."""
687
701
 
688
702
  @overload
689
703
  def __init__(
@@ -828,71 +842,57 @@ class Feature(SQLRecord, CanCurate, TracksRun, TracksUpdates):
828
842
  field: FieldAttr | None = None,
829
843
  *,
830
844
  str_as_cat: bool | None = None,
845
+ type: Feature | None = None,
831
846
  mute: bool = False,
832
847
  ) -> SQLRecordList:
833
848
  """Create Feature records for dictionary keys.
834
849
 
835
850
  Args:
836
851
  dictionary: Source dictionary to extract key information from
837
- field: FieldAttr for Feature model validation, defaults to Feature.name
838
- str_as_cat: Whether to interpret string values as categorical
852
+ field: FieldAttr for Feature model validation, defaults to `Feature.name`
853
+ str_as_cat: Deprecated. Will be removed in LaminDB 2.0.0.
854
+ Create features explicitly with dtype='cat' for categorical values.
855
+ type: Feature type of all created features
839
856
  mute: Whether to mute dtype inference and feature creation warnings
840
857
  """
841
858
  from lamindb.models._feature_manager import infer_feature_type_convert_json
842
859
 
860
+ if str_as_cat is not None:
861
+ warnings.warn(
862
+ "`str_as_cat` is deprecated and will be removed in LaminDB 2.0.0. "
863
+ "Create features explicitly with dtype='cat' for categorical values.",
864
+ DeprecationWarning,
865
+ stacklevel=2,
866
+ )
867
+
843
868
  field = Feature.name if field is None else field
844
869
  registry = field.field.model # type: ignore
845
870
  if registry != Feature:
846
871
  raise ValueError("field must be a Feature FieldAttr!")
847
872
 
848
873
  dtypes = {}
849
- ambiguous_keys = []
850
874
  for key, value in dictionary.items():
851
875
  dtype, _, message = infer_feature_type_convert_json(key, value, mute=mute)
852
-
853
876
  if dtype == "cat ? str":
854
877
  if str_as_cat is None:
855
- ambiguous_keys.append(
856
- (key, "str or cat", message.strip("# ") if message else "")
857
- )
858
- continue
859
- if str_as_cat:
860
- dtype = "cat"
861
- else:
862
878
  dtype = "str"
863
-
879
+ else:
880
+ dtype = "cat" if str_as_cat else "str"
864
881
  elif dtype == "list[cat ? str]":
865
882
  if str_as_cat is None:
866
- ambiguous_keys.append(
867
- (
868
- key,
869
- "list[str] or list[cat]",
870
- message.strip("# ") if message else "",
871
- )
872
- )
873
- continue
874
- if str_as_cat:
875
- dtype = "list[cat]"
876
- else:
877
883
  dtype = "list[str]"
878
-
884
+ else:
885
+ dtype = "list[cat]" if str_as_cat else "list[str]"
879
886
  dtypes[key] = dtype
880
887
 
881
- if ambiguous_keys:
882
- error_msg = "Ambiguous dtypes detected. Please pass `str_as_cat` parameter or create features explicitly:\n"
883
- for key, options, msg in ambiguous_keys:
884
- error_msg += f" '{key}': {options}"
885
- if msg:
886
- error_msg += f" ({msg})"
887
- error_msg += "\n"
888
- error_msg += "\nUse `str_as_cat=True` to treat strings as categorical, or `str_as_cat=False` for plain strings."
889
- raise ValueError(error_msg)
890
-
891
888
  if mute:
892
889
  original_verbosity = logger._verbosity
893
890
  logger.set_verbosity(0)
894
891
  try:
895
- features = [Feature(name=key, dtype=dtype) for key, dtype in dtypes.items()] # type: ignore
892
+ features = [
893
+ Feature(name=key, dtype=dtype, type=type)
894
+ for key, dtype in dtypes.items()
895
+ ] # type: ignore
896
896
  assert len(features) == len(dictionary) # noqa: S101
897
897
  return SQLRecordList(features)
898
898
  finally:
@@ -42,13 +42,22 @@ def _query_relatives(
42
42
  attr: str,
43
43
  cls: type[HasParents],
44
44
  ) -> QuerySet:
45
- relatives = cls.objects.none() # type: ignore
46
- if len(records) == 0:
45
+ from .query_set import get_default_branch_ids
46
+
47
+ branch_ids = get_default_branch_ids()
48
+
49
+ def query_relatives_on_branches(records, attr, cls) -> QuerySet:
50
+ relatives = cls.objects.none() # type: ignore
51
+ if len(records) == 0:
52
+ return relatives
53
+ for record in records:
54
+ relatives = relatives.union(
55
+ getattr(record, attr).filter(branch_id__in=branch_ids)
56
+ )
57
+ relatives = relatives.union(query_relatives_on_branches(relatives, attr, cls))
47
58
  return relatives
48
- for record in records:
49
- relatives = relatives.union(getattr(record, attr).all())
50
- relatives = relatives.union(_query_relatives(relatives, attr, cls))
51
- return relatives
59
+
60
+ return query_relatives_on_branches(records, attr, cls)
52
61
 
53
62
 
54
63
  class HasParents:
lamindb/models/project.py CHANGED
@@ -12,14 +12,13 @@ from lamindb.base.fields import (
12
12
  CharField,
13
13
  DateField,
14
14
  DateTimeField,
15
- EmailField,
16
15
  ForeignKey,
17
16
  TextField,
18
17
  URLField,
19
18
  )
20
19
  from lamindb.base.users import current_user_id
21
20
 
22
- from ..base.ids import base62_8, base62_12
21
+ from ..base.ids import base62_12
23
22
  from .artifact import Artifact
24
23
  from .can_curate import CanCurate
25
24
  from .collection import Collection
@@ -35,59 +34,7 @@ if TYPE_CHECKING:
35
34
  from datetime import date as DateType
36
35
  from datetime import datetime
37
36
 
38
-
39
- class Person(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
40
- """People such as authors of a study or collaborators in a project.
41
-
42
- This registry is distinct from `User` and exists for project management.
43
-
44
- You'll soon be able to conveniently create persons from users.
45
-
46
- Example:
47
- >>> person = Person(
48
- ... name="Jane Doe",
49
- ... email="jane.doe@example.com",
50
- ... internal=True,
51
- ... ).save()
52
- """
53
-
54
- class Meta(SQLRecord.Meta, TracksRun.Meta, TracksUpdates.Meta):
55
- abstract = False
56
- app_label = "lamindb"
57
-
58
- id: int = models.AutoField(primary_key=True)
59
- """Internal id, valid only in one DB instance."""
60
- uid: str = CharField(
61
- editable=False, unique=True, max_length=8, db_index=True, default=base62_8
62
- )
63
- """Universal id, valid across DB instances."""
64
- name: str = CharField(db_index=True)
65
- """Name of the person (forename(s) lastname)."""
66
- email: str | None = EmailField(null=True, default=None)
67
- """Email of the person."""
68
- external: bool = BooleanField(default=True, db_index=True)
69
- """Whether the person is external to the organization."""
70
- records: Record = models.ManyToManyField(
71
- Record, through="RecordPerson", related_name="linked_people"
72
- )
73
- """Linked records."""
74
-
75
- @overload
76
- def __init__(
77
- self,
78
- name: str,
79
- email: str | None = None,
80
- external: bool = True,
81
- ): ...
82
-
83
- @overload
84
- def __init__(
85
- self,
86
- *db_args,
87
- ): ...
88
-
89
- def __init__(self, *args, **kwargs):
90
- super().__init__(*args, **kwargs)
37
+ from .block import ProjectBlock
91
38
 
92
39
 
93
40
  class Reference(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
@@ -118,6 +65,8 @@ class Reference(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
118
65
  """Universal id, valid across DB instances."""
119
66
  name: str = CharField(db_index=True)
120
67
  """Title or name of the reference document."""
68
+ description: str | None = TextField(null=True)
69
+ """A description."""
121
70
  type: Reference | None = ForeignKey(
122
71
  "self", PROTECT, null=True, related_name="references"
123
72
  )
@@ -150,30 +99,32 @@ class Reference(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
150
99
  ],
151
100
  )
152
101
  """Digital Object Identifier (DOI) for the reference."""
153
- description: str | None = CharField(null=True, db_index=True)
154
- """Description of the reference."""
155
102
  text: str | None = TextField(null=True, db_index=True)
156
103
  """Abstract or full text of the reference to make it searchable."""
157
104
  date: DateType | None = DateField(null=True, default=None)
158
105
  """Date of creation or publication of the reference."""
159
- authors: Person = models.ManyToManyField(Person, related_name="references")
160
- """All people associated with this reference."""
161
106
  artifacts: Artifact = models.ManyToManyField(
162
107
  Artifact, through="ArtifactReference", related_name="references"
163
108
  )
164
- """Artifacts associated with this reference."""
109
+ """Annotated artifacts."""
165
110
  transforms: Artifact = models.ManyToManyField(
166
111
  Transform, through="TransformReference", related_name="references"
167
112
  )
168
- """Transforms associated with this reference."""
113
+ """Annotated transforms."""
169
114
  collections: Artifact = models.ManyToManyField(
170
115
  Collection, through="CollectionReference", related_name="references"
171
116
  )
172
- """Collections associated with this reference."""
173
- records: Record = models.ManyToManyField(
117
+ """Annotated collections."""
118
+ linked_in_records: Record = models.ManyToManyField(
174
119
  Record, through="RecordReference", related_name="linked_references"
175
120
  )
176
- """Linked records."""
121
+ """Linked in records."""
122
+ records: Record = models.ManyToManyField(
123
+ Record, through="ReferenceRecord", related_name="references"
124
+ )
125
+ """Annotated records."""
126
+ projects: Project
127
+ """Projects that annotate this reference."""
177
128
 
178
129
  @overload
179
130
  def __init__(
@@ -227,6 +178,8 @@ class Project(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
227
178
  """Universal id, valid across DB instances."""
228
179
  name: str = CharField(db_index=True)
229
180
  """Title or name of the Project."""
181
+ description: str | None = TextField(null=True)
182
+ """A description."""
230
183
  type: Project | None = ForeignKey(
231
184
  "self", PROTECT, null=True, related_name="projects"
232
185
  )
@@ -261,50 +214,48 @@ class Project(SQLRecord, CanCurate, TracksRun, TracksUpdates, ValidateFields):
261
214
 
262
215
  Reverse accessor for `.predecessors`.
263
216
  """
264
- people: Person = models.ManyToManyField(
265
- Person, through="PersonProject", related_name="projects"
266
- )
267
- """Linked people."""
268
217
  artifacts: Artifact = models.ManyToManyField(
269
218
  Artifact, through="ArtifactProject", related_name="projects"
270
219
  )
271
- """Linked artifacts."""
220
+ """Annotated artifacts."""
272
221
  transforms: Transform = models.ManyToManyField(
273
222
  Transform, through="TransformProject", related_name="projects"
274
223
  )
275
- """Linked transforms."""
224
+ """Annotated transforms."""
276
225
  runs: Run = models.ManyToManyField(
277
226
  Run, through="RunProject", related_name="projects"
278
227
  )
279
- """Linked transforms."""
228
+ """Annotated runs."""
280
229
  ulabels: ULabel = models.ManyToManyField(
281
230
  ULabel, through="ULabelProject", related_name="projects"
282
231
  )
283
- """Linked ulabels."""
232
+ """Annotated ulabels."""
284
233
  features: ULabel = models.ManyToManyField(
285
234
  Feature, through="FeatureProject", related_name="projects"
286
235
  )
287
- """Linked features."""
236
+ """Annotated features."""
288
237
  schemas: ULabel = models.ManyToManyField(
289
238
  Schema, through="SchemaProject", related_name="projects"
290
239
  )
291
- """Linked schemas."""
240
+ """Annotated schemas."""
292
241
  linked_in_records: Record = models.ManyToManyField(
293
242
  Record, through="RecordProject", related_name="linked_projects"
294
243
  )
295
- """Linked records."""
244
+ """Linked in records."""
296
245
  records: Record = models.ManyToManyField(
297
246
  Record, through="ProjectRecord", related_name="projects"
298
247
  )
299
- """Annotated record."""
248
+ """Annotated records."""
300
249
  collections: Collection = models.ManyToManyField(
301
250
  Collection, through="CollectionProject", related_name="projects"
302
251
  )
303
- """Linked collections."""
252
+ """Annotated collections."""
304
253
  references: Reference = models.ManyToManyField("Reference", related_name="projects")
305
- """Linked references."""
254
+ """Annotated references."""
306
255
  _status_code: int = models.SmallIntegerField(default=0, db_index=True)
307
256
  """Status code."""
257
+ blocks: ProjectBlock
258
+ """Blocks that annotate this project."""
308
259
 
309
260
  @overload
310
261
  def __init__(
@@ -402,17 +353,6 @@ class ULabelProject(BaseSQLRecord, IsLink, TracksRun):
402
353
  unique_together = ("ulabel", "project")
403
354
 
404
355
 
405
- class PersonProject(BaseSQLRecord, IsLink, TracksRun):
406
- id: int = models.BigAutoField(primary_key=True)
407
- person: Person = ForeignKey(Person, CASCADE, related_name="links_project")
408
- project: Project = ForeignKey(Project, PROTECT, related_name="links_person")
409
- role: str | None = CharField(null=True, default=None)
410
-
411
- class Meta:
412
- app_label = "lamindb"
413
- unique_together = ("person", "project")
414
-
415
-
416
356
  class FeatureProject(BaseSQLRecord, IsLink, TracksRun):
417
357
  id: int = models.BigAutoField(primary_key=True)
418
358
  feature: Feature = ForeignKey(Feature, CASCADE, related_name="links_project")
@@ -433,15 +373,22 @@ class SchemaProject(BaseSQLRecord, IsLink, TracksRun):
433
373
  unique_together = ("schema", "project")
434
374
 
435
375
 
436
- class RecordPerson(BaseSQLRecord, IsLink):
376
+ # for annotation of records with references, RecordReference is for storing reference values
377
+ class ReferenceRecord(BaseSQLRecord, IsLink, TracksRun):
437
378
  id: int = models.BigAutoField(primary_key=True)
438
- record: Record = ForeignKey(Record, CASCADE, related_name="values_person")
439
- feature: Feature = ForeignKey(Feature, PROTECT, related_name="links_recordperson")
440
- value: Person = ForeignKey(Person, PROTECT, related_name="links_record")
379
+ reference: Reference = ForeignKey(Reference, PROTECT, related_name="links_record")
380
+ feature: Feature | None = ForeignKey(
381
+ Feature,
382
+ PROTECT,
383
+ null=True,
384
+ default=None,
385
+ related_name="links_referencerecord",
386
+ )
387
+ record: Record = ForeignKey(Record, CASCADE, related_name="links_reference")
441
388
 
442
389
  class Meta:
443
390
  app_label = "lamindb"
444
- unique_together = ("record", "feature", "value")
391
+ unique_together = ("reference", "feature", "record")
445
392
 
446
393
 
447
394
  class RecordReference(BaseSQLRecord, IsLink):
@@ -450,7 +397,7 @@ class RecordReference(BaseSQLRecord, IsLink):
450
397
  feature: Feature = ForeignKey(
451
398
  Feature, PROTECT, related_name="links_recordreference"
452
399
  )
453
- value: Reference = ForeignKey(Reference, PROTECT, related_name="links_record")
400
+ value: Reference = ForeignKey(Reference, PROTECT, related_name="links_in_record")
454
401
 
455
402
  class Meta:
456
403
  app_label = "lamindb"
@@ -460,7 +407,6 @@ class RecordReference(BaseSQLRecord, IsLink):
460
407
  # for annotation of records with projects, RecordProject is for storing project values
461
408
  class ProjectRecord(BaseSQLRecord, IsLink, TracksRun):
462
409
  id: int = models.BigAutoField(primary_key=True)
463
- record: Record = ForeignKey(Record, CASCADE, related_name="links_project")
464
410
  project: Project = ForeignKey(Project, PROTECT, related_name="links_record")
465
411
  feature: Feature | None = ForeignKey(
466
412
  Feature,
@@ -469,11 +415,11 @@ class ProjectRecord(BaseSQLRecord, IsLink, TracksRun):
469
415
  default=None,
470
416
  related_name="links_projectrecord",
471
417
  )
418
+ record: Record = ForeignKey(Record, CASCADE, related_name="links_project")
472
419
 
473
420
  class Meta:
474
- # can have the same label linked to the same artifact if the feature is different
475
421
  app_label = "lamindb"
476
- unique_together = ("record", "project", "feature")
422
+ unique_together = ("project", "feature", "record")
477
423
 
478
424
 
479
425
  class RecordProject(BaseSQLRecord, IsLink):
@@ -503,7 +449,6 @@ class ArtifactReference(BaseSQLRecord, IsLink, TracksRun):
503
449
 
504
450
  class Meta:
505
451
  app_label = "lamindb"
506
- # can have the same label linked to the same artifact if the feature is different
507
452
  unique_together = ("artifact", "reference", "feature")
508
453
 
509
454
 
@@ -302,7 +302,7 @@ class QueryManager(Manager):
302
302
  def all(self):
303
303
  """Return QuerySet of all.
304
304
 
305
- For `**kwargs`, see :meth:`lamindb.models.QuerySet.df`.
305
+ For `**kwargs`, see :meth:`lamindb.models.QuerySet.to_dataframe`.
306
306
  """
307
307
  self._track_run_input_manager()
308
308
  return super().all()