From 70a7d8a52b644598a6effa7479490166503e52e4 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 12:16:38 +0100 Subject: [PATCH 01/11] Add Person and Referene Signed-off-by: zethson --- .gitignore | 2 + ourprojects/__init__.py | 2 +- ...rson_project_persons_reference_and_more.py | 323 ++++++++++++++++++ ourprojects/models.py | 126 ++++++- 4 files changed, 450 insertions(+), 3 deletions(-) create mode 100644 ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py diff --git a/.gitignore b/.gitignore index 440bdf6..0cd8bef 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,5 @@ docs/conf.py _docs_tmp* docs/test-ourprojects/ test-ourprojects/ +test.ipynb +run-tests diff --git a/ourprojects/__init__.py b/ourprojects/__init__.py index 423fead..753b650 100644 --- a/ourprojects/__init__.py +++ b/ourprojects/__init__.py @@ -32,4 +32,4 @@ def __getattr__(name): import lamindb del __getattr__ # delete so that imports work out - from .models import Project + from .models import Project, Person, Reference diff --git a/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py b/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py new file mode 100644 index 0000000..70b252b --- /dev/null +++ b/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py @@ -0,0 +1,323 @@ +# Generated by Django 5.1.3 on 2024-11-20 12:42 + +import django.core.validators +import django.db.models.deletion +import lnschema_core.fields +import lnschema_core.ids +import lnschema_core.models +import lnschema_core.users +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("lnschema_core", "0069_squashed"), + ("ourprojects", "0002_alter_artifactproject_artifact_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="ArtifactReference", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ( + "label_ref_is_name", + lnschema_core.fields.BooleanField( + blank=True, default=None, null=True + ), + ), + ( + "feature_ref_is_name", + lnschema_core.fields.BooleanField( + blank=True, default=None, null=True + ), + ), + ( + "artifact", + lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="links_reference", + to="lnschema_core.artifact", + ), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "feature", + lnschema_core.fields.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="links_artifactreference", + to="lnschema_core.feature", + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + options={ + "abstract": False, + }, + bases=(lnschema_core.models.LinkORM, models.Model), + ), + migrations.CreateModel( + name="Person", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ( + "updated_at", + lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), + ), + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "uid", + lnschema_core.fields.CharField( + blank=True, + default=lnschema_core.ids.base62_12, + max_length=12, + unique=True, + ), + ), + ( + "name", + lnschema_core.fields.CharField( + blank=True, db_index=True, default=None, max_length=255 + ), + ), + ( + "email", + lnschema_core.fields.EmailField( + blank=True, default=None, max_length=254, null=True + ), + ), + ( + "internal", + lnschema_core.fields.BooleanField( + blank=True, default=False, null=True + ), + ), + ( + "_previous_runs", + models.ManyToManyField(related_name="+", to="lnschema_core.run"), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + options={ + "abstract": False, + }, + bases=( + lnschema_core.models.CanCurate, + models.Model, + lnschema_core.models.ValidateFields, + ), + ), + migrations.AddField( + model_name="project", + name="persons", + field=models.ManyToManyField( + related_name="project_persons", to="ourprojects.person" + ), + ), + migrations.CreateModel( + name="Reference", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ( + "updated_at", + lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), + ), + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "uid", + lnschema_core.fields.CharField( + blank=True, + default=lnschema_core.ids.base62_12, + max_length=12, + unique=True, + ), + ), + ( + "name", + lnschema_core.fields.CharField( + blank=True, db_index=True, default=None, max_length=255 + ), + ), + ( + "abbr", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=None, + max_length=32, + null=True, + unique=True, + ), + ), + ("url", lnschema_core.fields.URLField(blank=True, null=True)), + ( + "pubmed_id", + lnschema_core.fields.BigIntegerField(blank=True, null=True), + ), + ( + "doi", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=None, + max_length=255, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Must be a DOI (e.g., 10.1000/xyz123 or https://doi.org/10.1000/xyz123)", + regex="^(?:https?://(?:dx\\.)?doi\\.org/|doi:|DOI:)?10\\.\\d+/.*$", + ) + ], + ), + ), + ( + "preprint", + lnschema_core.fields.BooleanField( + blank=True, default=None, null=True + ), + ), + ( + "journal", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "description", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "text", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "published_at", + lnschema_core.fields.DateTimeField( + blank=True, default=None, null=True + ), + ), + ( + "_previous_runs", + models.ManyToManyField(related_name="+", to="lnschema_core.run"), + ), + ( + "artifacts", + models.ManyToManyField( + related_name="references", + through="ourprojects.ArtifactReference", + to="lnschema_core.artifact", + ), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "persons", + models.ManyToManyField( + related_name="reference_persons", to="ourprojects.person" + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + options={ + "abstract": False, + }, + bases=( + lnschema_core.models.CanCurate, + models.Model, + lnschema_core.models.ValidateFields, + ), + ), + migrations.AddField( + model_name="artifactreference", + name="reference", + field=lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="links_artifact", + to="ourprojects.reference", + ), + ), + migrations.AddField( + model_name="project", + name="references", + field=models.ManyToManyField( + related_name="project_references", to="ourprojects.reference" + ), + ), + ] diff --git a/ourprojects/models.py b/ourprojects/models.py index aa384d3..91fb186 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -1,9 +1,21 @@ from __future__ import annotations +from datetime import datetime # noqa + +from django.core.validators import RegexValidator from django.db import models from django.db.models import CASCADE, PROTECT from lnschema_core import ids -from lnschema_core.fields import BooleanField, CharField, ForeignKey +from lnschema_core.fields import ( + BigIntegerField, + BooleanField, + CharField, + DateTimeField, + ForeignKey, + TextField, + URLField, + EmailField +) from lnschema_core.models import ( Artifact, CanCurate, @@ -16,8 +28,33 @@ ) +class Person(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): + """Internal and external persons that can be a part of projects or references. + + Example: + >>> person = Person( + ... name="A paper title", + ... internal=False, + ... ).save() + """ + + class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): + abstract = False + + id: int = models.AutoField(primary_key=True) + """Internal id, valid only in one DB instance.""" + uid: str = CharField(max_length=12, unique=True, default=ids.base62_12) + """Universal id, valid across DB instances.""" + name: str = CharField(db_index=True) + """Name of the person (forename(s) lastname).""" + email: str = EmailField(null=True, default=None) + """Email of the person.""" + internal: bool = BooleanField(null=True, default=False) + """Whether the person is internal to the organization or not.""" + + class Project(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): - """Projects. + """Projects with associated persons and references. Example: >>> Project = Project( @@ -42,10 +79,95 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): max_length=255, null=True, default=None, blank=True ) """A URL to view.""" + persons: Person = models.ManyToManyField( + Person, + related_name="project_persons" + ) + references: Reference = models.ManyToManyField( + "Reference", + related_name="project_references" + ) + """References associated with this project.""" artifacts: Artifact = models.ManyToManyField( Artifact, through="ArtifactProject", related_name="Projects" ) """Artifacts labeled with this Project.""" + + +class Reference(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): + """References such as a publication or document, with unique identifiers and metadata. + + Example: + >>> reference = Reference( + ... name="A paper title", + ... doi="A doi", + ... ).save() + """ + + class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): + abstract = False + + id: int = models.AutoField(primary_key=True) + """Internal id, valid only in one DB instance.""" + uid: str = CharField(max_length=12, unique=True, default=ids.base62_12) + """Universal id, valid across DB instances.""" + name: str = CharField(db_index=True) + """Title or name of the reference document.""" + abbr: str | None = CharField( + max_length=32, + db_index=True, + unique=True, + null=True, + ) + """A unique abbreviation for the reference.""" + url: str | None = URLField(null=True, blank=True) + """URL linking to the reference.""" + pubmed_id: int | None = BigIntegerField(null=True) + """A PudMmed ID.""" + doi: int | None = CharField( + null=True, + db_index=True, + validators=[ + RegexValidator( + regex=r"^(?:https?://(?:dx\.)?doi\.org/|doi:|DOI:)?10\.\d+/.*$", + message="Must be a DOI (e.g., 10.1000/xyz123 or https://doi.org/10.1000/xyz123)", + ) + ], + ) + """Digital Object Identifier (DOI) for the reference.""" + preprint: bool = BooleanField(null=True, default=None) + """Whether the reference is from a preprint.""" + journal: str = TextField(null=True) + """Name of the journal.""" + description: str = TextField(null=True) + """Description of the reference.""" + text: str | None = TextField(null=True) + """Abstract or full text of the reference.""" + published_at: datetime = DateTimeField(null=True, default=None) + """Publication date.""" + persons: Person = models.ManyToManyField( + Person, + related_name="reference_persons" + ) + artifacts: Artifact = models.ManyToManyField( + Artifact, through="ArtifactReference", related_name="references" + ) + """Artifacts labeled with this reference.""" + + +class ArtifactReference(Record, LinkORM, TracksRun): + id: int = models.BigAutoField(primary_key=True) + artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_reference") + reference: Reference = ForeignKey(Reference, PROTECT, related_name="links_artifact") + feature: Feature = ForeignKey( + Feature, + PROTECT, + null=True, + default=None, + related_name="links_artifactreference", + ) + label_ref_is_name: bool | None = BooleanField(null=True, default=None) + feature_ref_is_name: bool | None = BooleanField(null=True, default=None) class ArtifactProject(Record, LinkORM, TracksRun): From 0bced45953f91dc84693aaece391bee0fc8a4722 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 12:40:52 +0100 Subject: [PATCH 02/11] :art: Pre-commit Signed-off-by: zethson --- ourprojects/__init__.py | 2 +- ...rson_project_persons_reference_and_more.py | 1 - ourprojects/models.py | 28 ++++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/ourprojects/__init__.py b/ourprojects/__init__.py index 753b650..b67b20b 100644 --- a/ourprojects/__init__.py +++ b/ourprojects/__init__.py @@ -32,4 +32,4 @@ def __getattr__(name): import lamindb del __getattr__ # delete so that imports work out - from .models import Project, Person, Reference + from .models import Person, Project, Reference diff --git a/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py b/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py index 70b252b..524e620 100644 --- a/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py +++ b/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py @@ -10,7 +10,6 @@ class Migration(migrations.Migration): - dependencies = [ ("lnschema_core", "0069_squashed"), ("ourprojects", "0002_alter_artifactproject_artifact_and_more"), diff --git a/ourprojects/models.py b/ourprojects/models.py index 981b9c6..05039a8 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -6,7 +6,16 @@ from django.db import models from django.db.models import CASCADE, PROTECT from lnschema_core import ids -from lnschema_core.fields import BooleanField, CharField, ForeignKey, URLField, EmailField, BigIntegerField, TextField, DateTimeField +from lnschema_core.fields import ( + BigIntegerField, + BooleanField, + CharField, + DateTimeField, + EmailField, + ForeignKey, + TextField, + URLField, +) from lnschema_core.models import ( Artifact, CanCurate, @@ -31,7 +40,7 @@ class Person(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): abstract = False - + id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" uid: str = CharField(max_length=12, unique=True, default=ids.base62_12) @@ -66,20 +75,16 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """A unique abbreviation.""" url: str | None = URLField(max_length=255, null=True, default=None) """A URL to view.""" - persons: Person = models.ManyToManyField( - Person, - related_name="project_persons" - ) + persons: Person = models.ManyToManyField(Person, related_name="project_persons") references: Reference = models.ManyToManyField( - "Reference", - related_name="project_references" + "Reference", related_name="project_references" ) """References associated with this project.""" artifacts: Artifact = models.ManyToManyField( Artifact, through="ArtifactProject", related_name="Projects" ) """Artifacts labeled with this Project.""" - + class Reference(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): """References such as a publication or document, with unique identifiers and metadata. @@ -132,10 +137,7 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Abstract or full text of the reference.""" published_at: datetime = DateTimeField(null=True, default=None) """Publication date.""" - persons: Person = models.ManyToManyField( - Person, - related_name="reference_persons" - ) + persons: Person = models.ManyToManyField(Person, related_name="reference_persons") artifacts: Artifact = models.ManyToManyField( Artifact, through="ArtifactReference", related_name="references" ) From d0df0a98e2030ac54f1b44b52f953cf22e27a251 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 15:06:13 +0100 Subject: [PATCH 03/11] :art: Iterate Signed-off-by: zethson --- ...lter_artifactproject_artifact_and_more.py} | 36 ++++++++++----- ourprojects/models.py | 45 ++++++++++++------- 2 files changed, 54 insertions(+), 27 deletions(-) rename ourprojects/migrations/{0003_artifactreference_person_project_persons_reference_and_more.py => 0002_alter_artifactproject_artifact_and_more.py} (91%) diff --git a/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py b/ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py similarity index 91% rename from ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py rename to ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py index 524e620..49385ba 100644 --- a/ourprojects/migrations/0003_artifactreference_person_project_persons_reference_and_more.py +++ b/ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-20 12:42 +# Generated by Django 5.1.3 on 2024-11-21 14:00 import django.core.validators import django.db.models.deletion @@ -12,10 +12,30 @@ class Migration(migrations.Migration): dependencies = [ ("lnschema_core", "0069_squashed"), - ("ourprojects", "0002_alter_artifactproject_artifact_and_more"), + ("ourprojects", "0001_initial"), ] operations = [ + migrations.AlterField( + model_name="artifactproject", + name="artifact", + field=lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="links_project", + to="lnschema_core.artifact", + ), + ), + migrations.AlterField( + model_name="artifactproject", + name="project", + field=lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="links_artifact", + to="ourprojects.project", + ), + ), migrations.CreateModel( name="ArtifactReference", fields=[ @@ -122,9 +142,7 @@ class Migration(migrations.Migration): ), ( "internal", - lnschema_core.fields.BooleanField( - blank=True, default=False, null=True - ), + lnschema_core.fields.BooleanField(blank=True, default=False), ), ( "_previous_runs", @@ -231,9 +249,7 @@ class Migration(migrations.Migration): ), ( "preprint", - lnschema_core.fields.BooleanField( - blank=True, default=None, null=True - ), + lnschema_core.fields.BooleanField(blank=True, default=False), ), ( "journal", @@ -249,9 +265,7 @@ class Migration(migrations.Migration): ), ( "published_at", - lnschema_core.fields.DateTimeField( - blank=True, default=None, null=True - ), + lnschema_core.fields.DateField(blank=True, default=None, null=True), ), ( "_previous_runs", diff --git a/ourprojects/models.py b/ourprojects/models.py index 05039a8..5dca632 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -1,6 +1,6 @@ from __future__ import annotations -from datetime import datetime # noqa +from datetime import date # noqa from django.core.validators import RegexValidator from django.db import models @@ -10,7 +10,7 @@ BigIntegerField, BooleanField, CharField, - DateTimeField, + DateField, EmailField, ForeignKey, TextField, @@ -33,8 +33,9 @@ class Person(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): Example: >>> person = Person( - ... name="A paper title", - ... internal=False, + ... name="Jane Doe", + ... email="jane.doe@example.com", + ... internal=True, ... ).save() """ @@ -43,13 +44,13 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField(max_length=12, unique=True, default=ids.base62_12) + uid: str = CharField(unique=True, max_length=12, default=ids.base62_12) """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Name of the person (forename(s) lastname).""" - email: str = EmailField(null=True, default=None) + email: str | None = EmailField(null=True, default=None) """Email of the person.""" - internal: bool = BooleanField(null=True, default=False) + internal: bool = BooleanField(default=False) """Whether the person is internal to the organization or not.""" @@ -57,8 +58,10 @@ class Project(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): """Projects with associated persons and references. Example: - >>> Project = Project( - ... name="My project name", + >>> project = Project( + ... name="My Project Name", + ... abbr="MPN", + ... url="https://example.com/my_project", ... ).save() """ @@ -76,6 +79,7 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): url: str | None = URLField(max_length=255, null=True, default=None) """A URL to view.""" persons: Person = models.ManyToManyField(Person, related_name="project_persons") + """Persons associated with this project.""" references: Reference = models.ManyToManyField( "Reference", related_name="project_references" ) @@ -91,8 +95,16 @@ class Reference(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): Example: >>> reference = Reference( - ... name="A paper title", - ... doi="A doi", + ... name="A Paper Title", + ... abbr="APT", + ... url="https://doi.org/10.1000/xyz123", + ... pubmed_id=12345678, + ... doi="10.1000/xyz123", + ... preprint=False, + ... journal="Nature Biotechnology", + ... description="A groundbreaking research paper.", + ... text="A really informative abstract.", + ... published_at=date(2023, 11, 21), ... ).save() """ @@ -101,7 +113,8 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField(max_length=12, unique=True, default=ids.base62_12) + uid: str = CharField(unique=True, max_length=12, default=ids.base62_12) + """Universal id, valid across DB instances.""" """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Title or name of the reference document.""" @@ -127,15 +140,15 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): ], ) """Digital Object Identifier (DOI) for the reference.""" - preprint: bool = BooleanField(null=True, default=None) + preprint: bool = BooleanField(default=False) """Whether the reference is from a preprint.""" - journal: str = TextField(null=True) + journal: str | None = TextField(null=True) """Name of the journal.""" - description: str = TextField(null=True) + description: str | None = TextField(null=True) """Description of the reference.""" text: str | None = TextField(null=True) """Abstract or full text of the reference.""" - published_at: datetime = DateTimeField(null=True, default=None) + published_at: date = DateField(null=True, default=None) """Publication date.""" persons: Person = models.ManyToManyField(Person, related_name="reference_persons") artifacts: Artifact = models.ManyToManyField( From eb5bd5abf58af8176bd9613d2006340ca7717662 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 15:08:54 +0100 Subject: [PATCH 04/11] :art: Iterate Signed-off-by: zethson --- ourprojects/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ourprojects/models.py b/ourprojects/models.py index 5dca632..ac17bf2 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -148,7 +148,7 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Description of the reference.""" text: str | None = TextField(null=True) """Abstract or full text of the reference.""" - published_at: date = DateField(null=True, default=None) + published_at: date | None = DateField(null=True, default=None) """Publication date.""" persons: Person = models.ManyToManyField(Person, related_name="reference_persons") artifacts: Artifact = models.ManyToManyField( From 86e476c96680af1b66916d99a487723c8028a724 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 15:56:53 +0100 Subject: [PATCH 05/11] :art: Review Signed-off-by: zethson --- ourprojects/migrations/0001_initial.py | 227 +++++++++++- ...alter_artifactproject_artifact_and_more.py | 336 ------------------ ourprojects/models.py | 51 ++- 3 files changed, 244 insertions(+), 370 deletions(-) delete mode 100644 ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 0963da7..60fa596 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,5 +1,6 @@ -# Generated by Django 5.2 on 2024-11-21 10:57 +# Generated by Django 5.1.3 on 2024-11-21 14:56 +import django.core.validators import django.db.models.deletion import lnschema_core.fields import lnschema_core.ids @@ -42,7 +43,6 @@ class Migration(migrations.Migration): "artifact", lnschema_core.fields.ForeignKey( blank=True, - default=None, on_delete=django.db.models.deletion.CASCADE, related_name="links_project", to="lnschema_core.artifact", @@ -83,6 +83,83 @@ class Migration(migrations.Migration): ], bases=(lnschema_core.models.LinkORM, models.Model), ), + migrations.CreateModel( + name="Person", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ( + "updated_at", + lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), + ), + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "uid", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=lnschema_core.ids.base62_8, + max_length=12, + unique=True, + ), + ), + ( + "name", + lnschema_core.fields.CharField( + blank=True, db_index=True, default=None, max_length=255 + ), + ), + ( + "email", + lnschema_core.fields.EmailField( + blank=True, default=None, max_length=254, null=True + ), + ), + ( + "internal", + lnschema_core.fields.BooleanField( + blank=True, db_index=True, default=False + ), + ), + ( + "_previous_runs", + models.ManyToManyField(related_name="+", to="lnschema_core.run"), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + options={ + "abstract": False, + }, + bases=( + lnschema_core.models.CanCurate, + models.Model, + lnschema_core.models.ValidateFields, + ), + ), migrations.CreateModel( name="Project", fields=[ @@ -101,6 +178,7 @@ class Migration(migrations.Migration): "uid", lnschema_core.fields.CharField( blank=True, + db_index=True, default=lnschema_core.ids.base62_12, max_length=12, unique=True, @@ -151,6 +229,12 @@ class Migration(migrations.Migration): to="lnschema_core.user", ), ), + ( + "people", + models.ManyToManyField( + related_name="project_people", to="ourprojects.person" + ), + ), ( "run", lnschema_core.fields.ForeignKey( @@ -177,12 +261,149 @@ class Migration(migrations.Migration): name="project", field=lnschema_core.fields.ForeignKey( blank=True, - default=None, on_delete=django.db.models.deletion.PROTECT, related_name="links_artifact", to="ourprojects.project", ), ), + migrations.CreateModel( + name="Reference", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ( + "updated_at", + lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), + ), + ("id", models.AutoField(primary_key=True, serialize=False)), + ( + "uid", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=lnschema_core.ids.base62_12, + max_length=12, + unique=True, + ), + ), + ( + "name", + lnschema_core.fields.CharField( + blank=True, db_index=True, default=None, max_length=255 + ), + ), + ( + "abbr", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=None, + max_length=32, + null=True, + unique=True, + ), + ), + ("url", lnschema_core.fields.URLField(blank=True, null=True)), + ( + "pubmed_id", + lnschema_core.fields.BigIntegerField( + blank=True, db_index=True, null=True + ), + ), + ( + "doi", + lnschema_core.fields.CharField( + blank=True, + db_index=True, + default=None, + max_length=255, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Must be a DOI (e.g., 10.1000/xyz123 or https://doi.org/10.1000/xyz123)", + regex="^(?:https?://(?:dx\\.)?doi\\.org/|doi:|DOI:)?10\\.\\d+/.*$", + ) + ], + ), + ), + ( + "preprint", + lnschema_core.fields.BooleanField(blank=True, default=False), + ), + ( + "journal", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "description", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "text", + lnschema_core.fields.TextField(blank=True, default=None, null=True), + ), + ( + "published_at", + lnschema_core.fields.DateField(blank=True, default=None, null=True), + ), + ( + "_previous_runs", + models.ManyToManyField(related_name="+", to="lnschema_core.run"), + ), + ( + "artifacts", + models.ManyToManyField( + related_name="references", to="lnschema_core.artifact" + ), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "people", + models.ManyToManyField( + related_name="references", to="ourprojects.person" + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + options={ + "abstract": False, + }, + bases=( + lnschema_core.models.CanCurate, + models.Model, + lnschema_core.models.ValidateFields, + ), + ), + migrations.AddField( + model_name="project", + name="references", + field=models.ManyToManyField( + related_name="project_references", to="ourprojects.reference" + ), + ), migrations.AlterUniqueTogether( name="artifactproject", unique_together={("artifact", "project", "feature")}, diff --git a/ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py b/ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py deleted file mode 100644 index 49385ba..0000000 --- a/ourprojects/migrations/0002_alter_artifactproject_artifact_and_more.py +++ /dev/null @@ -1,336 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-21 14:00 - -import django.core.validators -import django.db.models.deletion -import lnschema_core.fields -import lnschema_core.ids -import lnschema_core.models -import lnschema_core.users -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("lnschema_core", "0069_squashed"), - ("ourprojects", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="artifactproject", - name="artifact", - field=lnschema_core.fields.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="links_project", - to="lnschema_core.artifact", - ), - ), - migrations.AlterField( - model_name="artifactproject", - name="project", - field=lnschema_core.fields.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="links_artifact", - to="ourprojects.project", - ), - ), - migrations.CreateModel( - name="ArtifactReference", - fields=[ - ( - "created_at", - lnschema_core.fields.DateTimeField( - auto_now_add=True, db_index=True - ), - ), - ("id", models.BigAutoField(primary_key=True, serialize=False)), - ( - "label_ref_is_name", - lnschema_core.fields.BooleanField( - blank=True, default=None, null=True - ), - ), - ( - "feature_ref_is_name", - lnschema_core.fields.BooleanField( - blank=True, default=None, null=True - ), - ), - ( - "artifact", - lnschema_core.fields.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="links_reference", - to="lnschema_core.artifact", - ), - ), - ( - "created_by", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.users.current_user_id, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.user", - ), - ), - ( - "feature", - lnschema_core.fields.ForeignKey( - blank=True, - default=None, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="links_artifactreference", - to="lnschema_core.feature", - ), - ), - ( - "run", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.models.current_run, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.run", - ), - ), - ], - options={ - "abstract": False, - }, - bases=(lnschema_core.models.LinkORM, models.Model), - ), - migrations.CreateModel( - name="Person", - fields=[ - ( - "created_at", - lnschema_core.fields.DateTimeField( - auto_now_add=True, db_index=True - ), - ), - ( - "updated_at", - lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), - ), - ("id", models.AutoField(primary_key=True, serialize=False)), - ( - "uid", - lnschema_core.fields.CharField( - blank=True, - default=lnschema_core.ids.base62_12, - max_length=12, - unique=True, - ), - ), - ( - "name", - lnschema_core.fields.CharField( - blank=True, db_index=True, default=None, max_length=255 - ), - ), - ( - "email", - lnschema_core.fields.EmailField( - blank=True, default=None, max_length=254, null=True - ), - ), - ( - "internal", - lnschema_core.fields.BooleanField(blank=True, default=False), - ), - ( - "_previous_runs", - models.ManyToManyField(related_name="+", to="lnschema_core.run"), - ), - ( - "created_by", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.users.current_user_id, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.user", - ), - ), - ( - "run", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.models.current_run, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.run", - ), - ), - ], - options={ - "abstract": False, - }, - bases=( - lnschema_core.models.CanCurate, - models.Model, - lnschema_core.models.ValidateFields, - ), - ), - migrations.AddField( - model_name="project", - name="persons", - field=models.ManyToManyField( - related_name="project_persons", to="ourprojects.person" - ), - ), - migrations.CreateModel( - name="Reference", - fields=[ - ( - "created_at", - lnschema_core.fields.DateTimeField( - auto_now_add=True, db_index=True - ), - ), - ( - "updated_at", - lnschema_core.fields.DateTimeField(auto_now=True, db_index=True), - ), - ("id", models.AutoField(primary_key=True, serialize=False)), - ( - "uid", - lnschema_core.fields.CharField( - blank=True, - default=lnschema_core.ids.base62_12, - max_length=12, - unique=True, - ), - ), - ( - "name", - lnschema_core.fields.CharField( - blank=True, db_index=True, default=None, max_length=255 - ), - ), - ( - "abbr", - lnschema_core.fields.CharField( - blank=True, - db_index=True, - default=None, - max_length=32, - null=True, - unique=True, - ), - ), - ("url", lnschema_core.fields.URLField(blank=True, null=True)), - ( - "pubmed_id", - lnschema_core.fields.BigIntegerField(blank=True, null=True), - ), - ( - "doi", - lnschema_core.fields.CharField( - blank=True, - db_index=True, - default=None, - max_length=255, - null=True, - validators=[ - django.core.validators.RegexValidator( - message="Must be a DOI (e.g., 10.1000/xyz123 or https://doi.org/10.1000/xyz123)", - regex="^(?:https?://(?:dx\\.)?doi\\.org/|doi:|DOI:)?10\\.\\d+/.*$", - ) - ], - ), - ), - ( - "preprint", - lnschema_core.fields.BooleanField(blank=True, default=False), - ), - ( - "journal", - lnschema_core.fields.TextField(blank=True, default=None, null=True), - ), - ( - "description", - lnschema_core.fields.TextField(blank=True, default=None, null=True), - ), - ( - "text", - lnschema_core.fields.TextField(blank=True, default=None, null=True), - ), - ( - "published_at", - lnschema_core.fields.DateField(blank=True, default=None, null=True), - ), - ( - "_previous_runs", - models.ManyToManyField(related_name="+", to="lnschema_core.run"), - ), - ( - "artifacts", - models.ManyToManyField( - related_name="references", - through="ourprojects.ArtifactReference", - to="lnschema_core.artifact", - ), - ), - ( - "created_by", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.users.current_user_id, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.user", - ), - ), - ( - "persons", - models.ManyToManyField( - related_name="reference_persons", to="ourprojects.person" - ), - ), - ( - "run", - lnschema_core.fields.ForeignKey( - blank=True, - default=lnschema_core.models.current_run, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="lnschema_core.run", - ), - ), - ], - options={ - "abstract": False, - }, - bases=( - lnschema_core.models.CanCurate, - models.Model, - lnschema_core.models.ValidateFields, - ), - ), - migrations.AddField( - model_name="artifactreference", - name="reference", - field=lnschema_core.fields.ForeignKey( - blank=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="links_artifact", - to="ourprojects.reference", - ), - ), - migrations.AddField( - model_name="project", - name="references", - field=models.ManyToManyField( - related_name="project_references", to="ourprojects.reference" - ), - ), - ] diff --git a/ourprojects/models.py b/ourprojects/models.py index ac17bf2..73c514d 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -29,7 +29,7 @@ class Person(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): - """Internal and external persons that can be a part of projects or references. + """Internal and external people that can be a part of projects or references. Example: >>> person = Person( @@ -44,18 +44,20 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField(unique=True, max_length=12, default=ids.base62_12) + uid: str = CharField( + unique=True, max_length=12, db_index=True, default=ids.base62_8 + ) """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Name of the person (forename(s) lastname).""" email: str | None = EmailField(null=True, default=None) """Email of the person.""" - internal: bool = BooleanField(default=False) + internal: bool = BooleanField(default=False, db_index=True) """Whether the person is internal to the organization or not.""" class Project(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): - """Projects with associated persons and references. + """Projects with associated people and references. Example: >>> project = Project( @@ -70,7 +72,9 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField(unique=True, max_length=12, default=ids.base62_12) + uid: str = CharField( + unique=True, max_length=12, db_index=True, default=ids.base62_12 + ) """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Title or name of the Project.""" @@ -78,8 +82,8 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """A unique abbreviation.""" url: str | None = URLField(max_length=255, null=True, default=None) """A URL to view.""" - persons: Person = models.ManyToManyField(Person, related_name="project_persons") - """Persons associated with this project.""" + people: Person = models.ManyToManyField(Person, related_name="project_people") + """People associated with this project.""" references: Reference = models.ManyToManyField( "Reference", related_name="project_references" ) @@ -113,8 +117,9 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField(unique=True, max_length=12, default=ids.base62_12) - """Universal id, valid across DB instances.""" + uid: str = CharField( + unique=True, max_length=12, db_index=True, default=ids.base62_12 + ) """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Title or name of the reference document.""" @@ -125,11 +130,11 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): null=True, ) """A unique abbreviation for the reference.""" - url: str | None = URLField(null=True, blank=True) + url: str | None = URLField(null=True) """URL linking to the reference.""" - pubmed_id: int | None = BigIntegerField(null=True) + pubmed_id: int | None = BigIntegerField(null=True, db_index=True) """A PudMmed ID.""" - doi: int | None = CharField( + doi: str | None = CharField( null=True, db_index=True, validators=[ @@ -150,28 +155,12 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Abstract or full text of the reference.""" published_at: date | None = DateField(null=True, default=None) """Publication date.""" - persons: Person = models.ManyToManyField(Person, related_name="reference_persons") - artifacts: Artifact = models.ManyToManyField( - Artifact, through="ArtifactReference", related_name="references" - ) + people: Person = models.ManyToManyField(Person, related_name="references") + """All people associated with this reference.""" + artifacts: Artifact = models.ManyToManyField(Artifact, related_name="references") """Artifacts labeled with this reference.""" -class ArtifactReference(Record, LinkORM, TracksRun): - id: int = models.BigAutoField(primary_key=True) - artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_reference") - reference: Reference = ForeignKey(Reference, PROTECT, related_name="links_artifact") - feature: Feature = ForeignKey( - Feature, - PROTECT, - null=True, - default=None, - related_name="links_artifactreference", - ) - label_ref_is_name: bool | None = BooleanField(null=True, default=None) - feature_ref_is_name: bool | None = BooleanField(null=True, default=None) - - class ArtifactProject(Record, LinkORM, TracksRun): id: int = models.BigAutoField(primary_key=True) artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_project") From c3f59dc585844270c4a4fe7dd702b322ae6d5130 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 16:04:48 +0100 Subject: [PATCH 06/11] :art: Db_index for preprint Signed-off-by: zethson --- ourprojects/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ourprojects/models.py b/ourprojects/models.py index 73c514d..b75b796 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -145,7 +145,7 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): ], ) """Digital Object Identifier (DOI) for the reference.""" - preprint: bool = BooleanField(default=False) + preprint: bool = BooleanField(default=False, db_index=True) """Whether the reference is from a preprint.""" journal: str | None = TextField(null=True) """Name of the journal.""" From 2347fb5535bca3e8c7a2742802105eb57f89613a Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 16:06:34 +0100 Subject: [PATCH 07/11] :art: New migration script Signed-off-by: zethson --- ourprojects/migrations/0001_initial.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 60fa596..2a4ddae 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-21 14:56 +# Generated by Django 5.1.3 on 2024-11-21 15:06 import django.core.validators import django.db.models.deletion @@ -332,7 +332,9 @@ class Migration(migrations.Migration): ), ( "preprint", - lnschema_core.fields.BooleanField(blank=True, default=False), + lnschema_core.fields.BooleanField( + blank=True, db_index=True, default=False + ), ), ( "journal", From 9dd92cb140bd73bd0e3b9b3f5f20c114fe2ea944 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 16:57:17 +0100 Subject: [PATCH 08/11] :art: Add link model Signed-off-by: zethson --- ourprojects/__init__.py | 2 + ourprojects/migrations/0001_initial.py | 100 ++++++++++++++++++++++--- ourprojects/models.py | 32 ++++++-- 3 files changed, 118 insertions(+), 16 deletions(-) diff --git a/ourprojects/__init__.py b/ourprojects/__init__.py index b67b20b..c5099df 100644 --- a/ourprojects/__init__.py +++ b/ourprojects/__init__.py @@ -14,7 +14,9 @@ .. autosummary:: :toctree: . + Person Project + Reference """ __version__ = "0.0.1" # denote a pre-release for 0.1.0 with 0.1rc1 diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 2a4ddae..39eed75 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-21 15:06 +# Generated by Django 5.1.3 on 2024-11-21 15:40 import django.core.validators import django.db.models.deletion @@ -83,6 +83,72 @@ class Migration(migrations.Migration): ], bases=(lnschema_core.models.LinkORM, models.Model), ), + migrations.CreateModel( + name="ArtifactReference", + fields=[ + ( + "created_at", + lnschema_core.fields.DateTimeField( + auto_now_add=True, db_index=True + ), + ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ( + "label_ref_is_name", + lnschema_core.fields.BooleanField( + blank=True, default=None, null=True + ), + ), + ( + "feature_ref_is_name", + lnschema_core.fields.BooleanField( + blank=True, default=None, null=True + ), + ), + ( + "artifact", + lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="links_reference", + to="lnschema_core.artifact", + ), + ), + ( + "created_by", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.users.current_user_id, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.user", + ), + ), + ( + "feature", + lnschema_core.fields.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="links_artifactreference", + to="lnschema_core.feature", + ), + ), + ( + "run", + lnschema_core.fields.ForeignKey( + blank=True, + default=lnschema_core.models.current_run, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="lnschema_core.run", + ), + ), + ], + bases=(lnschema_core.models.LinkORM, models.Model), + ), migrations.CreateModel( name="Person", fields=[ @@ -103,7 +169,7 @@ class Migration(migrations.Migration): blank=True, db_index=True, default=lnschema_core.ids.base62_8, - max_length=12, + max_length=8, unique=True, ), ), @@ -359,7 +425,15 @@ class Migration(migrations.Migration): ( "artifacts", models.ManyToManyField( - related_name="references", to="lnschema_core.artifact" + related_name="references", + through="ourprojects.ArtifactReference", + to="lnschema_core.artifact", + ), + ), + ( + "authors", + models.ManyToManyField( + related_name="references", to="ourprojects.person" ), ), ( @@ -372,12 +446,6 @@ class Migration(migrations.Migration): to="lnschema_core.user", ), ), - ( - "people", - models.ManyToManyField( - related_name="references", to="ourprojects.person" - ), - ), ( "run", lnschema_core.fields.ForeignKey( @@ -406,8 +474,22 @@ class Migration(migrations.Migration): related_name="project_references", to="ourprojects.reference" ), ), + migrations.AddField( + model_name="artifactreference", + name="reference", + field=lnschema_core.fields.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="links_artifact", + to="ourprojects.reference", + ), + ), migrations.AlterUniqueTogether( name="artifactproject", unique_together={("artifact", "project", "feature")}, ), + migrations.AlterUniqueTogether( + name="artifactreference", + unique_together={("artifact", "reference", "feature")}, + ), ] diff --git a/ourprojects/models.py b/ourprojects/models.py index b75b796..007ffb3 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -44,9 +44,7 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): id: int = models.AutoField(primary_key=True) """Internal id, valid only in one DB instance.""" - uid: str = CharField( - unique=True, max_length=12, db_index=True, default=ids.base62_8 - ) + uid: str = CharField(unique=True, max_length=8, db_index=True, default=ids.base62_8) """Universal id, valid across DB instances.""" name: str = CharField(db_index=True) """Name of the person (forename(s) lastname).""" @@ -155,12 +153,33 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Abstract or full text of the reference.""" published_at: date | None = DateField(null=True, default=None) """Publication date.""" - people: Person = models.ManyToManyField(Person, related_name="references") + authors: Person = models.ManyToManyField(Person, related_name="references") """All people associated with this reference.""" - artifacts: Artifact = models.ManyToManyField(Artifact, related_name="references") + artifacts: Artifact = models.ManyToManyField( + Artifact, through="ArtifactReference", related_name="references" + ) """Artifacts labeled with this reference.""" +class ArtifactReference(Record, LinkORM, TracksRun): + id: int = models.BigAutoField(primary_key=True) + artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_reference") + reference: Reference = ForeignKey(Reference, PROTECT, related_name="links_artifact") + feature: Feature | None = ForeignKey( + Feature, + PROTECT, + null=True, + default=None, + related_name="links_artifactreference", + ) + label_ref_is_name: bool | None = BooleanField(null=True, default=None) + feature_ref_is_name: bool | None = BooleanField(null=True, default=None) + + class Meta: + # can have the same label linked to the same artifact if the feature is different + unique_together = ("artifact", "reference", "feature") + + class ArtifactProject(Record, LinkORM, TracksRun): id: int = models.BigAutoField(primary_key=True) artifact: Artifact = ForeignKey(Artifact, CASCADE, related_name="links_project") @@ -176,6 +195,5 @@ class ArtifactProject(Record, LinkORM, TracksRun): feature_ref_is_name: bool | None = BooleanField(null=True, default=None) class Meta: - # can have the same label linked to the same artifact if the feature is - # different + # can have the same label linked to the same artifact if the feature is different unique_together = ("artifact", "project", "feature") From 4f087b1fa695c984bc1b1c964a3a6ba1d9541259 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 17:03:54 +0100 Subject: [PATCH 09/11] :art: Polish Signed-off-by: zethson --- ourprojects/migrations/0001_initial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 39eed75..42ec2c7 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-21 15:40 +# Generated by Django 5.1.3 on 2024-11-21 16:00 import django.core.validators import django.db.models.deletion From f13b61c21d554737a178f65bb32c9f4ea9c3a2d1 Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 17:26:50 +0100 Subject: [PATCH 10/11] :art: Polish Signed-off-by: zethson --- ourprojects/migrations/0001_initial.py | 8 ++++---- ourprojects/models.py | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 42ec2c7..06b4eb5 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-21 16:00 +# Generated by Django 5.1.3 on 2024-11-21 16:26 import django.core.validators import django.db.models.deletion @@ -280,7 +280,7 @@ class Migration(migrations.Migration): ( "artifacts", models.ManyToManyField( - related_name="Projects", + related_name="projects", through="ourprojects.ArtifactProject", to="lnschema_core.artifact", ), @@ -298,7 +298,7 @@ class Migration(migrations.Migration): ( "people", models.ManyToManyField( - related_name="project_people", to="ourprojects.person" + related_name="projects", to="ourprojects.person" ), ), ( @@ -471,7 +471,7 @@ class Migration(migrations.Migration): model_name="project", name="references", field=models.ManyToManyField( - related_name="project_references", to="ourprojects.reference" + related_name="projects", to="ourprojects.reference" ), ), migrations.AddField( diff --git a/ourprojects/models.py b/ourprojects/models.py index 007ffb3..d6f7e15 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -80,14 +80,12 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """A unique abbreviation.""" url: str | None = URLField(max_length=255, null=True, default=None) """A URL to view.""" - people: Person = models.ManyToManyField(Person, related_name="project_people") + people: Person = models.ManyToManyField(Person, related_name="projects") """People associated with this project.""" - references: Reference = models.ManyToManyField( - "Reference", related_name="project_references" - ) + references: Reference = models.ManyToManyField("Reference", related_name="projects") """References associated with this project.""" artifacts: Artifact = models.ManyToManyField( - Artifact, through="ArtifactProject", related_name="Projects" + Artifact, through="ArtifactProject", related_name="projects" ) """Artifacts labeled with this Project.""" From 62ef3582c6de0bdbb3ab8fc3392c8dffec3d97eb Mon Sep 17 00:00:00 2001 From: zethson Date: Thu, 21 Nov 2024 17:47:43 +0100 Subject: [PATCH 11/11] :art: Alex PR feedback Signed-off-by: zethson --- ourprojects/migrations/0001_initial.py | 24 +++++++++++++++--------- ourprojects/models.py | 10 ++++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/ourprojects/migrations/0001_initial.py b/ourprojects/migrations/0001_initial.py index 06b4eb5..b9ce5ad 100644 --- a/ourprojects/migrations/0001_initial.py +++ b/ourprojects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-21 16:26 +# Generated by Django 5.1.3 on 2024-11-21 16:47 import django.core.validators import django.db.models.deletion @@ -186,9 +186,9 @@ class Migration(migrations.Migration): ), ), ( - "internal", + "external", lnschema_core.fields.BooleanField( - blank=True, db_index=True, default=False + blank=True, db_index=True, default=True ), ), ( @@ -285,6 +285,12 @@ class Migration(migrations.Migration): to="lnschema_core.artifact", ), ), + ( + "contributors", + models.ManyToManyField( + related_name="projects", to="ourprojects.person" + ), + ), ( "created_by", lnschema_core.fields.ForeignKey( @@ -295,12 +301,6 @@ class Migration(migrations.Migration): to="lnschema_core.user", ), ), - ( - "people", - models.ManyToManyField( - related_name="projects", to="ourprojects.person" - ), - ), ( "run", lnschema_core.fields.ForeignKey( @@ -402,6 +402,12 @@ class Migration(migrations.Migration): blank=True, db_index=True, default=False ), ), + ( + "public", + lnschema_core.fields.BooleanField( + blank=True, db_index=True, default=True + ), + ), ( "journal", lnschema_core.fields.TextField(blank=True, default=None, null=True), diff --git a/ourprojects/models.py b/ourprojects/models.py index d6f7e15..465a1c0 100644 --- a/ourprojects/models.py +++ b/ourprojects/models.py @@ -50,8 +50,8 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Name of the person (forename(s) lastname).""" email: str | None = EmailField(null=True, default=None) """Email of the person.""" - internal: bool = BooleanField(default=False, db_index=True) - """Whether the person is internal to the organization or not.""" + external: bool = BooleanField(default=True, db_index=True) + """Whether the person is external to the organization or not.""" class Project(Record, CanCurate, TracksRun, TracksUpdates, ValidateFields): @@ -80,8 +80,8 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """A unique abbreviation.""" url: str | None = URLField(max_length=255, null=True, default=None) """A URL to view.""" - people: Person = models.ManyToManyField(Person, related_name="projects") - """People associated with this project.""" + contributors: Person = models.ManyToManyField(Person, related_name="projects") + """Contributors associated with this project.""" references: Reference = models.ManyToManyField("Reference", related_name="projects") """References associated with this project.""" artifacts: Artifact = models.ManyToManyField( @@ -143,6 +143,8 @@ class Meta(Record.Meta, TracksRun.Meta, TracksUpdates.Meta): """Digital Object Identifier (DOI) for the reference.""" preprint: bool = BooleanField(default=False, db_index=True) """Whether the reference is from a preprint.""" + public: bool = BooleanField(default=True, db_index=True) + """Whether the reference is public or not.""" journal: str | None = TextField(null=True) """Name of the journal.""" description: str | None = TextField(null=True)