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.
- lamindb/__init__.py +30 -25
- lamindb/_tracked.py +1 -1
- lamindb/_view.py +2 -3
- lamindb/base/__init__.py +1 -1
- lamindb/base/ids.py +1 -10
- lamindb/core/__init__.py +7 -65
- lamindb/core/_compat.py +60 -0
- lamindb/core/_context.py +43 -20
- lamindb/core/_settings.py +6 -6
- lamindb/core/_sync_git.py +1 -1
- lamindb/core/loaders.py +30 -19
- lamindb/core/storage/_backed_access.py +4 -2
- lamindb/core/storage/_tiledbsoma.py +8 -6
- lamindb/core/storage/_zarr.py +104 -25
- lamindb/core/storage/objects.py +63 -28
- lamindb/core/storage/paths.py +4 -1
- lamindb/core/types.py +10 -0
- lamindb/curators/__init__.py +100 -85
- lamindb/errors.py +1 -1
- lamindb/integrations/_vitessce.py +4 -4
- lamindb/migrations/0089_subsequent_runs.py +159 -0
- lamindb/migrations/0090_runproject_project_runs.py +73 -0
- lamindb/migrations/{0088_squashed.py → 0090_squashed.py} +245 -177
- lamindb/models/__init__.py +79 -0
- lamindb/{core → models}/_describe.py +3 -3
- lamindb/{core → models}/_django.py +8 -5
- lamindb/{core → models}/_feature_manager.py +103 -87
- lamindb/{_from_values.py → models/_from_values.py} +5 -2
- lamindb/{core/versioning.py → models/_is_versioned.py} +94 -6
- lamindb/{core → models}/_label_manager.py +10 -17
- lamindb/{core/relations.py → models/_relations.py} +8 -1
- lamindb/models/artifact.py +2602 -0
- lamindb/{_can_curate.py → models/can_curate.py} +349 -180
- lamindb/models/collection.py +683 -0
- lamindb/models/core.py +135 -0
- lamindb/models/feature.py +643 -0
- lamindb/models/flextable.py +163 -0
- lamindb/{_parents.py → models/has_parents.py} +55 -49
- lamindb/models/project.py +384 -0
- lamindb/{_query_manager.py → models/query_manager.py} +10 -8
- lamindb/{_query_set.py → models/query_set.py} +40 -26
- lamindb/models/record.py +1762 -0
- lamindb/models/run.py +563 -0
- lamindb/{_save.py → models/save.py} +9 -7
- lamindb/models/schema.py +732 -0
- lamindb/models/transform.py +360 -0
- lamindb/models/ulabel.py +249 -0
- {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/METADATA +6 -6
- {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/RECORD +51 -51
- lamindb/_artifact.py +0 -1379
- lamindb/_collection.py +0 -440
- lamindb/_feature.py +0 -316
- lamindb/_is_versioned.py +0 -40
- lamindb/_record.py +0 -1064
- lamindb/_run.py +0 -60
- lamindb/_schema.py +0 -347
- lamindb/_storage.py +0 -15
- lamindb/_transform.py +0 -170
- lamindb/_ulabel.py +0 -56
- lamindb/_utils.py +0 -9
- lamindb/base/validation.py +0 -63
- lamindb/core/_data.py +0 -491
- lamindb/core/fields.py +0 -12
- lamindb/models.py +0 -4475
- {lamindb-1.1.1.dist-info → lamindb-1.2.0.dist-info}/LICENSE +0 -0
- {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
|
10
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
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")
|