Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Initial DataReview GraphQL API #574

Merged
merged 5 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions creator/data_reviews/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = "creator.data_reviews.apps.DataReviewsConfig"
8 changes: 8 additions & 0 deletions creator/data_reviews/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig


class DataReviewsConfig(AppConfig):
name = "creator.data_reviews"

def ready(self):
import creator.data_reviews.signals
36 changes: 36 additions & 0 deletions creator/data_reviews/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytz
import factory
from django.contrib.auth import get_user_model
from creator.users.factories import UserFactory
from creator.studies.factories import StudyFactory
from creator.data_reviews.models import DataReview, State

User = get_user_model()


class DataReviewFactory(factory.DjangoModelFactory):
class Meta:
model = DataReview

created_at = factory.Faker(
"date_time_between", start_date="-2y", end_date="now", tzinfo=pytz.UTC
)
creator = factory.SubFactory(UserFactory)
name = factory.Faker("name")
description = factory.Faker("paragraph", nb_sentences=3)
state = factory.fuzzy.FuzzyChoice(
[
attr
for attr in dir(State)
if not callable(getattr(State, attr)) and not attr.startswith("__")
]
)
study = factory.SubFactory(StudyFactory)

@factory.post_generation
def files(self, create, extracted, **kwargs):
if not create:
return

if extracted:
self.files.set(extracted)
106 changes: 106 additions & 0 deletions creator/data_reviews/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Generated by Django 2.2.13 on 2021-02-23 19:30

import creator.data_reviews.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django_fsm
import uuid


class Migration(migrations.Migration):

initial = True

dependencies = [
("files", "0022_add_genomic_workflow_output_type"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="DataReview",
fields=[
(
"kf_id",
models.CharField(
default=creator.data_reviews.models.data_review_id,
help_text="Kids First ID assigned to the data review",
max_length=11,
primary_key=True,
serialize=False,
),
),
(
"uuid",
models.UUIDField(
default=uuid.uuid4, help_text="UUID used internally"
),
),
(
"created_at",
models.DateTimeField(
auto_now_add=True,
help_text="Time when the data_review was created",
null=True,
),
),
(
"name",
models.CharField(
help_text="Name of the data review", max_length=256
),
),
(
"description",
models.TextField(
blank=True,
help_text="Description of data review",
max_length=10000,
null=True,
),
),
(
"state",
django_fsm.FSMField(
default="not_started",
help_text="The current state of the data review",
max_length=50,
),
),
(
"creator",
models.ForeignKey(
blank=True,
help_text="The user who created the data review",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="data_reviews",
to=settings.AUTH_USER_MODEL,
),
),
(
"study",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="data_reviews",
to="studies.Study",
),
),
(
"versions",
models.ManyToManyField(
help_text="kf_ids of the file versions in this data review",
related_name="data_reviews",
to="files.Version",
),
),
],
options={
"permissions": [
("list_all_datareview", "Show all data_reviews")
],
},
),
]
Empty file.
153 changes: 153 additions & 0 deletions creator/data_reviews/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import uuid
from django.db import models
from django.contrib.auth import get_user_model
from django_fsm import FSMField, transition

from creator.fields import kf_id_generator
from creator.studies.models import Study
from creator.files.models import Version

User = get_user_model()


def data_review_id():
return kf_id_generator("DR")


class State(object):
znatty22 marked this conversation as resolved.
Show resolved Hide resolved
NOT_STARTED = "not_started"
IN_REVIEW = "in_review"
WAITING = "awaiting_updates"
COMPLETED = "completed"
CLOSED = "closed"


class DataReview(models.Model):
"""
The iterative process between a data contributor and the Data Resource
Center where study data is continually supplied, validated, and refined
to meet quality standards.
"""

class Meta:
permissions = [
("list_all_datareview", "Show all data_reviews"),
znatty22 marked this conversation as resolved.
Show resolved Hide resolved
]

kf_id = models.CharField(
znatty22 marked this conversation as resolved.
Show resolved Hide resolved
max_length=11,
primary_key=True,
default=data_review_id,
help_text="Kids First ID assigned to the data review",
)
uuid = models.UUIDField(
default=uuid.uuid4, help_text="UUID used internally"
)
created_at = models.DateTimeField(
auto_now_add=True,
null=True,
help_text="Time when the data_review was created",
)
creator = models.ForeignKey(
User,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="data_reviews",
help_text="The user who created the data review",
)
name = models.CharField(
max_length=256, help_text="Name of the data review"
)
description = models.TextField(
max_length=10000,
null=True,
blank=True,
help_text="Description of data review",
)
state = FSMField(
default=State.NOT_STARTED,
help_text="The current state of the data review",
)
study = models.ForeignKey(
Study,
on_delete=models.CASCADE,
null=True,
related_name="data_reviews",
)
versions = models.ManyToManyField(
Version,
related_name="data_reviews",
help_text="kf_ids of the file versions in this data review",
)

@transition(field=state, source=State.NOT_STARTED, target=State.IN_REVIEW)
def start(self):
""" Start the data review """

self._save_event(
"DR_STA",
f"{self.creator.username} started a data review",
)

@transition(field=state, source=State.IN_REVIEW, target=State.WAITING)
def wait_for_updates(self):
""" Feedback was sent to data contributors, now wait for updates """

self._save_event(
"DR_WAI",
"Data review is waiting for updates",
)

@transition(field=state, source=State.WAITING, target=State.IN_REVIEW)
def receive_updates(self):
""" Receive and process updates to data review """

self._save_event(
"DR_UPD",
"Data review received file updates",
)

@transition(field=state, source=State.IN_REVIEW, target=State.COMPLETED)
def approve(self):
""" Approve/complete the data review """

self._save_event(
"DR_APP",
f"{self.creator.username} completed the data review",
)

@transition(
field=state,
source=[State.NOT_STARTED, State.IN_REVIEW, State.WAITING],
target=State.CLOSED,
)
def close(self):
""" Close the data review before it is completed """

self._save_event(
"DR_CLO",
f"{self.creator.username} closed the data review",
)

@transition(field=state, source=State.CLOSED, target=State.IN_REVIEW)
def reopen(self):
""" Re-open a closed data review """

self._save_event(
"DR_REO",
f"{self.creator.username} re-opened the data review",
)

def _save_event(self, event_type, message):
""" Create and save an event for a data review state transition """

from creator.events.models import Event

return Event(
study=self.study,
user=self.creator,
data_review=self,
description=message,
event_type=event_type,
).save()
Loading