lamindb 1.1.1__py3-none-any.whl → 1.2.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.
Files changed (66) hide show
  1. lamindb/__init__.py +30 -25
  2. lamindb/_tracked.py +1 -1
  3. lamindb/_view.py +2 -3
  4. lamindb/base/__init__.py +1 -1
  5. lamindb/base/ids.py +1 -10
  6. lamindb/core/__init__.py +7 -65
  7. lamindb/core/_compat.py +60 -0
  8. lamindb/core/_context.py +43 -20
  9. lamindb/core/_settings.py +6 -6
  10. lamindb/core/_sync_git.py +1 -1
  11. lamindb/core/loaders.py +30 -19
  12. lamindb/core/storage/_backed_access.py +4 -2
  13. lamindb/core/storage/_tiledbsoma.py +8 -6
  14. lamindb/core/storage/_zarr.py +104 -25
  15. lamindb/core/storage/objects.py +63 -28
  16. lamindb/core/storage/paths.py +4 -1
  17. lamindb/core/types.py +10 -0
  18. lamindb/curators/__init__.py +100 -85
  19. lamindb/errors.py +1 -1
  20. lamindb/integrations/_vitessce.py +4 -4
  21. lamindb/migrations/0089_subsequent_runs.py +159 -0
  22. lamindb/migrations/0090_runproject_project_runs.py +73 -0
  23. lamindb/migrations/{0088_squashed.py → 0090_squashed.py} +245 -177
  24. lamindb/models/__init__.py +79 -0
  25. lamindb/{core → models}/_describe.py +3 -3
  26. lamindb/{core → models}/_django.py +8 -5
  27. lamindb/{core → models}/_feature_manager.py +103 -87
  28. lamindb/{_from_values.py → models/_from_values.py} +5 -2
  29. lamindb/{core/versioning.py → models/_is_versioned.py} +94 -6
  30. lamindb/{core → models}/_label_manager.py +10 -17
  31. lamindb/{core/relations.py → models/_relations.py} +8 -1
  32. lamindb/models/artifact.py +2602 -0
  33. lamindb/{_can_curate.py → models/can_curate.py} +349 -180
  34. lamindb/models/collection.py +683 -0
  35. lamindb/models/core.py +135 -0
  36. lamindb/models/feature.py +643 -0
  37. lamindb/models/flextable.py +163 -0
  38. lamindb/{_parents.py → models/has_parents.py} +55 -49
  39. lamindb/models/project.py +384 -0
  40. lamindb/{_query_manager.py → models/query_manager.py} +10 -8
  41. lamindb/{_query_set.py → models/query_set.py} +40 -26
  42. lamindb/models/record.py +1762 -0
  43. lamindb/models/run.py +563 -0
  44. lamindb/{_save.py → models/save.py} +9 -7
  45. lamindb/models/schema.py +732 -0
  46. lamindb/models/transform.py +360 -0
  47. lamindb/models/ulabel.py +249 -0
  48. {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/METADATA +6 -6
  49. {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/RECORD +51 -51
  50. lamindb/_artifact.py +0 -1379
  51. lamindb/_collection.py +0 -440
  52. lamindb/_feature.py +0 -316
  53. lamindb/_is_versioned.py +0 -40
  54. lamindb/_record.py +0 -1064
  55. lamindb/_run.py +0 -60
  56. lamindb/_schema.py +0 -347
  57. lamindb/_storage.py +0 -15
  58. lamindb/_transform.py +0 -170
  59. lamindb/_ulabel.py +0 -56
  60. lamindb/_utils.py +0 -9
  61. lamindb/base/validation.py +0 -63
  62. lamindb/core/_data.py +0 -491
  63. lamindb/core/fields.py +0 -12
  64. lamindb/models.py +0 -4475
  65. {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/LICENSE +0 -0
  66. {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,163 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from django.db import models
6
+ from django.db.models import PROTECT, ManyToManyField
7
+
8
+ from lamindb.base.fields import (
9
+ BooleanField,
10
+ CharField,
11
+ ForeignKey,
12
+ IntegerField,
13
+ )
14
+
15
+ from .artifact import Artifact
16
+ from .feature import Feature
17
+ from .record import (
18
+ BasicRecord,
19
+ Record,
20
+ )
21
+ from .run import Param, TracksRun, TracksUpdates
22
+
23
+ if TYPE_CHECKING:
24
+ from .project import Project
25
+
26
+ from django.core.exceptions import ValidationError
27
+
28
+ from ..base.ids import base62_12
29
+ from .collection import Collection
30
+ from .project import Person, Project
31
+ from .record import Space
32
+ from .schema import Schema
33
+ from .ulabel import ULabel
34
+
35
+
36
+ class DataMixin(models.Model):
37
+ space: Space = ForeignKey(Space, PROTECT, default=1, db_default=1)
38
+ feature = ForeignKey(
39
+ Feature, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
40
+ )
41
+ param = ForeignKey(
42
+ Param, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
43
+ )
44
+ row = IntegerField(help_text="Use -1 for result data")
45
+
46
+ # Value fields
47
+ value_int = models.BigIntegerField(null=True, blank=True)
48
+ value_float = models.FloatField(null=True, blank=True)
49
+ value_str = models.TextField(null=True, blank=True)
50
+ value_datetime = models.DateTimeField(null=True, blank=True)
51
+ value_ulabel = models.ForeignKey(
52
+ ULabel, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
53
+ )
54
+ value_person = models.ForeignKey(
55
+ Person, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
56
+ )
57
+ value_artifact = models.ForeignKey(
58
+ Artifact, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
59
+ )
60
+ value_collection = models.ForeignKey(
61
+ Collection, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
62
+ )
63
+ value_project = models.ForeignKey(
64
+ Project, null=True, blank=True, on_delete=models.CASCADE, related_name="+"
65
+ )
66
+ value_json = models.JSONField(null=True, blank=True)
67
+
68
+ class Meta:
69
+ abstract = True
70
+
71
+ def clean(self):
72
+ # Validate feature/param mutual exclusivity
73
+ if (self.feature is not None) == (self.param is not None):
74
+ raise ValidationError("Exactly one of feature or param must be set")
75
+
76
+ # Validate value fields
77
+ values = [
78
+ self.value_int,
79
+ self.value_float,
80
+ self.value_str,
81
+ self.value_datetime,
82
+ self.value_ulabel,
83
+ self.value_artifact,
84
+ self.value_json,
85
+ ]
86
+ non_null_count = sum(1 for v in values if v is not None)
87
+
88
+ if non_null_count != 1:
89
+ raise ValidationError("Exactly one value field must be set")
90
+
91
+
92
+ class RunData(BasicRecord, DataMixin):
93
+ run = models.ForeignKey("Run", on_delete=models.CASCADE, related_name="_rundata")
94
+
95
+ class Meta:
96
+ constraints = [
97
+ models.CheckConstraint(
98
+ condition=(
99
+ models.Q(feature__isnull=False, param__isnull=True)
100
+ | models.Q(feature__isnull=True, param__isnull=False)
101
+ ),
102
+ name="run_data_feature_param_mutex",
103
+ ),
104
+ models.UniqueConstraint(
105
+ fields=["run", "row", "feature", "param"], name="run_data_unique"
106
+ ),
107
+ ]
108
+ indexes = [
109
+ models.Index(fields=["run", "row"]),
110
+ models.Index(fields=["feature"]),
111
+ models.Index(fields=["param"]),
112
+ ]
113
+
114
+
115
+ class FlexTable(Record, TracksRun, TracksUpdates):
116
+ uid: str = CharField(
117
+ editable=False, unique=True, max_length=12, db_index=True, default=base62_12
118
+ )
119
+ name = CharField()
120
+ schema: Schema | None = ForeignKey(
121
+ Schema, null=True, on_delete=models.SET_NULL, related_name="_tidytables"
122
+ )
123
+ type: FlexTable | None = ForeignKey(
124
+ "self", PROTECT, null=True, related_name="records"
125
+ )
126
+ """Type of tidy table, e.g., `Cell`, `SampleSheet`, etc."""
127
+ records: ULabel
128
+ """Records of this type."""
129
+ is_type: bool = BooleanField(default=False, db_index=True, null=True)
130
+ """Distinguish types from instances of the type."""
131
+ description: str = CharField(null=True, db_index=True)
132
+ """A description."""
133
+ projects: Project = ManyToManyField(Project, related_name="_tidytables")
134
+ ulabels: Project = ManyToManyField(ULabel, related_name="_tidytables")
135
+
136
+ class Meta:
137
+ indexes = [models.Index(fields=["uid"]), models.Index(fields=["name"])]
138
+
139
+
140
+ class FlexTableData(BasicRecord, DataMixin):
141
+ tidytable = models.ForeignKey(
142
+ FlexTable, on_delete=models.CASCADE, related_name="data"
143
+ )
144
+
145
+ class Meta:
146
+ constraints = [
147
+ models.CheckConstraint(
148
+ condition=(
149
+ models.Q(feature__isnull=False, param__isnull=True)
150
+ | models.Q(feature__isnull=True, param__isnull=False)
151
+ ),
152
+ name="tidy_table_data_feature_param_mutex",
153
+ ),
154
+ models.UniqueConstraint(
155
+ fields=["tidytable", "row", "feature", "param"],
156
+ name="tidy_table_data_unique",
157
+ ),
158
+ ]
159
+ indexes = [
160
+ models.Index(fields=["tidytable", "row"]),
161
+ models.Index(fields=["feature"]),
162
+ models.Index(fields=["param"]),
163
+ ]
@@ -1,27 +1,22 @@
1
+ # ruff: noqa: TC004
1
2
  from __future__ import annotations
2
3
 
3
4
  import builtins
4
5
  from typing import TYPE_CHECKING, Literal
5
6
 
6
- import lamindb_setup as ln_setup
7
7
  from lamin_utils import logger
8
8
 
9
- from lamindb.models import (
10
- Artifact,
11
- Collection,
12
- HasParents,
13
- Record,
14
- Run,
15
- Transform,
16
- format_field_value,
17
- )
18
-
19
- from ._record import get_name_field
20
- from ._utils import attach_func_to_class_method
9
+ from .record import format_field_value, get_name_field
10
+ from .run import Run
21
11
 
22
12
  if TYPE_CHECKING:
23
13
  from lamindb.base.types import StrField
24
- from lamindb.core import QuerySet
14
+
15
+ from .artifact import Artifact
16
+ from .collection import Collection
17
+ from .query_set import QuerySet
18
+ from .record import Record
19
+ from .transform import Transform
25
20
 
26
21
  LAMIN_GREEN_LIGHTER = "#10b981"
27
22
  LAMIN_GREEN_DARKER = "#065f46"
@@ -53,12 +48,50 @@ def _query_relatives(
53
48
  return relatives
54
49
 
55
50
 
56
- def query_parents(self) -> QuerySet:
57
- return _query_relatives([self], "parents", self.__class__)
51
+ class HasParents:
52
+ """Base class for hierarchical registries (ontologies)."""
53
+
54
+ def view_parents(
55
+ self,
56
+ field: StrField | None = None,
57
+ with_children: bool = False,
58
+ distance: int = 5,
59
+ ):
60
+ """View parents in an ontology.
61
+
62
+ Args:
63
+ field: Field to display on graph
64
+ with_children: Whether to also show children.
65
+ distance: Maximum distance still shown.
66
+
67
+ Ontological hierarchies: :class:`~lamindb.ULabel` (project & sub-project), :class:`~bionty.CellType` (cell type & subtype).
68
+
69
+ Examples:
70
+ >>> import bionty as bt
71
+ >>> bt.Tissue.from_source(name="subsegmental bronchus").save()
72
+ >>> record = bt.Tissue.get(name="respiratory tube")
73
+ >>> record.view_parents()
74
+ >>> tissue.view_parents(with_children=True)
75
+ """
76
+ if field is None:
77
+ field = get_name_field(self)
78
+ if not isinstance(field, str):
79
+ field = field.field.name
80
+
81
+ return _view_parents(
82
+ record=self, # type: ignore
83
+ field=field,
84
+ with_children=with_children,
85
+ distance=distance,
86
+ )
58
87
 
88
+ def query_parents(self) -> QuerySet:
89
+ """Query parents in an ontology."""
90
+ return _query_relatives([self], "parents", self.__class__) # type: ignore
59
91
 
60
- def query_children(self) -> QuerySet:
61
- return _query_relatives([self], "children", self.__class__)
92
+ def query_children(self) -> QuerySet:
93
+ """Query children in an ontology."""
94
+ return _query_relatives([self], "children", self.__class__) # type: ignore
62
95
 
63
96
 
64
97
  def _transform_emoji(transform: Transform):
@@ -93,22 +126,6 @@ def _view(u):
93
126
  )
94
127
 
95
128
 
96
- def view_parents(
97
- self,
98
- field: StrField | None = None,
99
- with_children: bool = False,
100
- distance: int = 5,
101
- ):
102
- if field is None:
103
- field = get_name_field(self)
104
- if not isinstance(field, str):
105
- field = field.field.name
106
-
107
- return _view_parents(
108
- record=self, field=field, with_children=with_children, distance=distance
109
- )
110
-
111
-
112
129
  def view_lineage(data: Artifact | Collection, with_children: bool = True) -> None:
113
130
  """Graph of data flow.
114
131
 
@@ -332,6 +349,10 @@ def _df_edges_from_parents(
332
349
 
333
350
 
334
351
  def _record_label(record: Record, field: str | None = None):
352
+ from .artifact import Artifact
353
+ from .collection import Collection
354
+ from .transform import Transform
355
+
335
356
  if isinstance(record, Artifact):
336
357
  if record.description is None:
337
358
  name = record.key
@@ -511,18 +532,3 @@ def _df_edges_from_runs(df_values: list):
511
532
  df["source_label"] = df["source_record"].apply(_record_label)
512
533
  df["target_label"] = df["target_record"].apply(_record_label)
513
534
  return df
514
-
515
-
516
- METHOD_NAMES = ["view_parents", "query_parents", "query_children"]
517
-
518
- if ln_setup._TESTING: # type: ignore
519
- from inspect import signature
520
-
521
- SIGS = {
522
- name: signature(getattr(HasParents, name))
523
- for name in METHOD_NAMES
524
- if not name.startswith("__")
525
- }
526
-
527
- for name in METHOD_NAMES:
528
- attach_func_to_class_method(name, HasParents, globals())
@@ -0,0 +1,384 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from django.core.validators import RegexValidator
6
+ from django.db import models
7
+ from django.db.models import CASCADE, PROTECT
8
+
9
+ from lamindb.base.fields import (
10
+ BigIntegerField,
11
+ BooleanField,
12
+ CharField,
13
+ DateField,
14
+ DateTimeField,
15
+ EmailField,
16
+ ForeignKey,
17
+ TextField,
18
+ URLField,
19
+ )
20
+ from lamindb.base.users import current_user_id
21
+
22
+ from ..base.ids import base62_8, base62_12
23
+ from .artifact import Artifact
24
+ from .can_curate import CanCurate
25
+ from .collection import Collection
26
+ from .feature import Feature
27
+ from .record import BasicRecord, LinkORM, Record, ValidateFields
28
+ from .run import Run, TracksRun, TracksUpdates, User
29
+ from .schema import Schema
30
+ from .transform import Transform
31
+ from .ulabel import ULabel
32
+
33
+ if TYPE_CHECKING:
34
+ from datetime import date as DateType
35
+ from datetime import datetime
36
+
37
+
38
+ class Person(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields):
39
+ """People.
40
+
41
+ This registry is distinct from `User` and exists for project management.
42
+
43
+ You'll soon be able to conveniently create persons from users.
44
+
45
+ Example:
46
+ >>> person = Person(
47
+ ... name="Jane Doe",
48
+ ... email="jane.doe@example.com",
49
+ ... internal=True,
50
+ ... ).save()
51
+ """
52
+
53
+ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta):
54
+ abstract = False
55
+
56
+ id: int = models.AutoField(primary_key=True)
57
+ """Internal id, valid only in one DB instance."""
58
+ uid: str = CharField(
59
+ editable=False, unique=True, max_length=8, db_index=True, default=base62_8
60
+ )
61
+ """Universal id, valid across DB instances."""
62
+ name: str = CharField(db_index=True)
63
+ """Name of the person (forename(s) lastname)."""
64
+ email: str | None = EmailField(null=True, default=None)
65
+ """Email of the person."""
66
+ external: bool = BooleanField(default=True, db_index=True)
67
+ """Whether the person is external to the organization."""
68
+
69
+
70
+ class Reference(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields):
71
+ """References such as internal studies, papers, documents, or URLs.
72
+
73
+ Example:
74
+ >>> reference = Reference(
75
+ ... name="A Paper Title",
76
+ ... abbr="APT",
77
+ ... url="https://doi.org/10.1000/xyz123",
78
+ ... pubmed_id=12345678,
79
+ ... doi="10.1000/xyz123",
80
+ ... description="Good paper.",
81
+ ... text="Some text I want to be searchable.",
82
+ ... date=date(2023, 11, 21),
83
+ ... ).save()
84
+ """
85
+
86
+ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta):
87
+ abstract = False
88
+
89
+ id: int = models.AutoField(primary_key=True)
90
+ """Internal id, valid only in one DB instance."""
91
+ uid: str = CharField(
92
+ editable=False, unique=True, max_length=12, db_index=True, default=base62_12
93
+ )
94
+ """Universal id, valid across DB instances."""
95
+ name: str = CharField(db_index=True)
96
+ """Title or name of the reference document."""
97
+ abbr: str | None = CharField(
98
+ max_length=32,
99
+ db_index=True,
100
+ null=True,
101
+ )
102
+ """An abbreviation for the reference."""
103
+ type: Reference | None = ForeignKey(
104
+ "self", PROTECT, null=True, related_name="records"
105
+ )
106
+ """Type of reference (e.g., 'Study', 'Paper', 'Preprint').
107
+
108
+ Allows to group reference by type, e.g., internal studies vs. all papers etc.
109
+ """
110
+ records: Reference
111
+ """Records of this type."""
112
+ is_type: bool = BooleanField(default=False, db_index=True, null=True)
113
+ """Distinguish types from instances of the type."""
114
+ url: str | None = URLField(null=True)
115
+ """URL linking to the reference."""
116
+ pubmed_id: int | None = BigIntegerField(null=True, db_index=True)
117
+ """A PudMmed ID."""
118
+ doi: str | None = CharField(
119
+ null=True,
120
+ db_index=True,
121
+ validators=[
122
+ RegexValidator(
123
+ regex=r"^(?:https?://(?:dx\.)?doi\.org/|doi:|DOI:)?10\.\d+/.*$",
124
+ message="Must be a DOI (e.g., 10.1000/xyz123 or https://doi.org/10.1000/xyz123)",
125
+ )
126
+ ],
127
+ )
128
+ """Digital Object Identifier (DOI) for the reference."""
129
+ description: str | None = CharField(null=True, db_index=True)
130
+ """Description of the reference."""
131
+ text: str | None = TextField(null=True)
132
+ """Abstract or full text of the reference to make it searchable."""
133
+ date: DateType | None = DateField(null=True, default=None)
134
+ """Date of creation or publication of the reference."""
135
+ authors: Person = models.ManyToManyField(Person, related_name="references")
136
+ """All people associated with this reference."""
137
+ artifacts: Artifact = models.ManyToManyField(
138
+ Artifact, through="ArtifactReference", related_name="references"
139
+ )
140
+ """Artifacts associated with this reference."""
141
+ transforms: Artifact = models.ManyToManyField(
142
+ Transform, through="TransformReference", related_name="references"
143
+ )
144
+ """Transforms associated with this reference."""
145
+ collections: Artifact = models.ManyToManyField(
146
+ Collection, through="CollectionReference", related_name="references"
147
+ )
148
+ """Collections associated with this reference."""
149
+
150
+
151
+ class Project(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields):
152
+ """Projects.
153
+
154
+ Example:
155
+ >>> project = Project(
156
+ ... name="My Project Name",
157
+ ... abbr="MPN",
158
+ ... url="https://example.com/my_project",
159
+ ... ).save()
160
+ """
161
+
162
+ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta):
163
+ abstract = False
164
+
165
+ id: int = models.AutoField(primary_key=True)
166
+ """Internal id, valid only in one DB instance."""
167
+ uid: str = CharField(
168
+ editable=False, unique=True, max_length=12, db_index=True, default=base62_12
169
+ )
170
+ """Universal id, valid across DB instances."""
171
+ name: str = CharField(db_index=True)
172
+ """Title or name of the Project."""
173
+ type: Project | None = ForeignKey(
174
+ "self", PROTECT, null=True, related_name="records"
175
+ )
176
+ """Type of project (e.g., 'Program', 'Project', 'GithubIssue', 'Task')."""
177
+ records: Project
178
+ """Records of this type."""
179
+ is_type: bool = BooleanField(default=False, db_index=True, null=True)
180
+ """Distinguish types from instances of the type."""
181
+ abbr: str | None = CharField(max_length=32, db_index=True, null=True)
182
+ """An abbreviation."""
183
+ url: str | None = URLField(max_length=255, null=True, default=None)
184
+ """A URL."""
185
+ start_date: DateType | None = DateField(null=True, default=None)
186
+ """Date of start of the project."""
187
+ end_date: DateType | None = DateField(null=True, default=None)
188
+ """Date of start of the project."""
189
+ parents: Project = models.ManyToManyField(
190
+ "self", symmetrical=False, related_name="children"
191
+ )
192
+ """Parent projects, the super-projects owning this project."""
193
+ children: Project
194
+ """Child projects, the sub-projects owned by this project.
195
+
196
+ Reverse accessor for `.parents`.
197
+ """
198
+ predecessors: Project = models.ManyToManyField(
199
+ "self", symmetrical=False, related_name="successors"
200
+ )
201
+ """The preceding projects required by this project."""
202
+ successors: Project
203
+ """The succeeding projects requiring this project.
204
+
205
+ Reverse accessor for `.predecessors`.
206
+ """
207
+ people: Person = models.ManyToManyField(
208
+ Person, through="PersonProject", related_name="projects"
209
+ )
210
+ """Linked people."""
211
+ artifacts: Artifact = models.ManyToManyField(
212
+ Artifact, through="ArtifactProject", related_name="projects"
213
+ )
214
+ """Linked artifacts."""
215
+ transforms: Transform = models.ManyToManyField(
216
+ Transform, through="TransformProject", related_name="projects"
217
+ )
218
+ """Linked transforms."""
219
+ runs: Run = models.ManyToManyField(
220
+ Run, through="RunProject", related_name="projects"
221
+ )
222
+ """Linked transforms."""
223
+ ulabels: ULabel = models.ManyToManyField(
224
+ ULabel, through="ULabelProject", related_name="projects"
225
+ )
226
+ """Linked ulabels."""
227
+ features: ULabel = models.ManyToManyField(
228
+ Feature, through="FeatureProject", related_name="projects"
229
+ )
230
+ """Linked features."""
231
+ schemas: ULabel = models.ManyToManyField(
232
+ Schema, through="SchemaProject", related_name="projects"
233
+ )
234
+ """Linked schemas."""
235
+ collections: Collection = models.ManyToManyField(
236
+ Collection, through="CollectionProject", related_name="projects"
237
+ )
238
+ """Linked collections."""
239
+ references: Reference = models.ManyToManyField("Reference", related_name="projects")
240
+ """Linked references."""
241
+ _status_code: int = models.SmallIntegerField(default=0, db_index=True)
242
+ """Status code."""
243
+
244
+
245
+ class ArtifactProject(BasicRecord, LinkORM, TracksRun):
246
+ id: int = models.BigAutoField(primary_key=True)
247
+ artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_project")
248
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_artifact")
249
+ feature: Feature | None = ForeignKey(
250
+ Feature,
251
+ PROTECT,
252
+ null=True,
253
+ default=None,
254
+ related_name="links_artifactproject",
255
+ )
256
+ label_ref_is_name: bool | None = BooleanField(null=True, default=None)
257
+ feature_ref_is_name: bool | None = BooleanField(null=True, default=None)
258
+
259
+ class Meta:
260
+ # can have the same label linked to the same artifact if the feature is different
261
+ unique_together = ("artifact", "project", "feature")
262
+
263
+
264
+ class RunProject(BasicRecord, LinkORM):
265
+ id: int = models.BigAutoField(primary_key=True)
266
+ run: Run = ForeignKey(Run, CASCADE, related_name="links_project")
267
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_run")
268
+ created_at: datetime = DateTimeField(
269
+ editable=False, db_default=models.functions.Now(), db_index=True
270
+ )
271
+ """Time of creation of record."""
272
+ created_by: User = ForeignKey(
273
+ "lamindb.User",
274
+ PROTECT,
275
+ editable=False,
276
+ default=current_user_id,
277
+ related_name="+",
278
+ )
279
+ """Creator of record."""
280
+
281
+ class Meta:
282
+ unique_together = ("run", "project")
283
+
284
+
285
+ class TransformProject(BasicRecord, LinkORM, TracksRun):
286
+ id: int = models.BigAutoField(primary_key=True)
287
+ transform: Transform = ForeignKey(Transform, CASCADE, related_name="links_project")
288
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_transform")
289
+
290
+ class Meta:
291
+ unique_together = ("transform", "project")
292
+
293
+
294
+ class CollectionProject(BasicRecord, LinkORM, TracksRun):
295
+ id: int = models.BigAutoField(primary_key=True)
296
+ collection: Collection = ForeignKey(
297
+ Collection, CASCADE, related_name="links_project"
298
+ )
299
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_collection")
300
+
301
+ class Meta:
302
+ unique_together = ("collection", "project")
303
+
304
+
305
+ class ULabelProject(BasicRecord, LinkORM, TracksRun):
306
+ id: int = models.BigAutoField(primary_key=True)
307
+ ulabel: Transform = ForeignKey(ULabel, CASCADE, related_name="links_project")
308
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_ulabel")
309
+
310
+ class Meta:
311
+ unique_together = ("ulabel", "project")
312
+
313
+
314
+ class PersonProject(BasicRecord, LinkORM, TracksRun):
315
+ id: int = models.BigAutoField(primary_key=True)
316
+ person: Transform = ForeignKey(Person, CASCADE, related_name="links_project")
317
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_person")
318
+ role: str | None = CharField(null=True, default=None)
319
+
320
+ class Meta:
321
+ unique_together = ("person", "project")
322
+
323
+
324
+ class FeatureProject(BasicRecord, LinkORM, TracksRun):
325
+ id: int = models.BigAutoField(primary_key=True)
326
+ feature: Feature = ForeignKey(Feature, CASCADE, related_name="links_project")
327
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_feature")
328
+
329
+ class Meta:
330
+ unique_together = ("feature", "project")
331
+
332
+
333
+ class SchemaProject(BasicRecord, LinkORM, TracksRun):
334
+ id: int = models.BigAutoField(primary_key=True)
335
+ schema: Schema = ForeignKey(Schema, CASCADE, related_name="links_project")
336
+ project: Project = ForeignKey(Project, PROTECT, related_name="links_schema")
337
+
338
+ class Meta:
339
+ unique_together = ("schema", "project")
340
+
341
+
342
+ class ArtifactReference(BasicRecord, LinkORM, TracksRun):
343
+ id: int = models.BigAutoField(primary_key=True)
344
+ artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_reference")
345
+ reference: Reference = ForeignKey(Reference, PROTECT, related_name="links_artifact")
346
+ feature: Feature | None = ForeignKey(
347
+ Feature,
348
+ PROTECT,
349
+ null=True,
350
+ default=None,
351
+ related_name="links_artifactreference",
352
+ )
353
+ label_ref_is_name: bool | None = BooleanField(null=True, default=None)
354
+ feature_ref_is_name: bool | None = BooleanField(null=True, default=None)
355
+
356
+ class Meta:
357
+ # can have the same label linked to the same artifact if the feature is different
358
+ unique_together = ("artifact", "reference", "feature")
359
+
360
+
361
+ class TransformReference(BasicRecord, LinkORM, TracksRun):
362
+ id: int = models.BigAutoField(primary_key=True)
363
+ transform: Transform = ForeignKey(
364
+ Transform, CASCADE, related_name="links_reference"
365
+ )
366
+ reference: Reference = ForeignKey(
367
+ Reference, PROTECT, related_name="links_transform"
368
+ )
369
+
370
+ class Meta:
371
+ unique_together = ("transform", "reference")
372
+
373
+
374
+ class CollectionReference(BasicRecord, LinkORM, TracksRun):
375
+ id: int = models.BigAutoField(primary_key=True)
376
+ collection: Collection = ForeignKey(
377
+ Collection, CASCADE, related_name="links_reference"
378
+ )
379
+ reference: Reference = ForeignKey(
380
+ Reference, PROTECT, related_name="links_collection"
381
+ )
382
+
383
+ class Meta:
384
+ unique_together = ("collection", "reference")