VRE Backend API and Scheduler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
6.4 KiB

from django.conf import settings
from django.db import models
from django.db.models import Count, Sum
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from lib.utils.general import get_random_int_value
from lib.models.base import MetaDataModel
from apps.researcher.models import Researcher
from apps.university.models import StudyField
import uuid
# Create your models here.
class StudyManager(models.Manager):
This is a custom study manager for getting extra information from a study model like:
- Total files / datadrops attached for a study.
- Total file size of all the data drops for a study.
- Total invitations that have been sent for a study.
def get_queryset(self):
Returns the queryset with extra fields
- '_total_files',
- '_total_file_size'
- '_total_invitations'.
This will overrule/alter the existing queryset.
return super().get_queryset().select_related('storagelocation').annotate(
_total_files=Count('files', distinct=True),
_total_file_size=Sum('files__filesize', distinct=True),
_total_invitations=Count('invitations', distinct=True),
class StudyRoleNames(models.TextChoices):
"""Research study role options
Valid status values:
- Administrator
- Researcher
- Member
ADMIN = ('ADMIN', _('Administrator'))
# OWNER = ('OWNER', _('Owner'))
RESEARCHER = ('RESEARCHER', _('Researcher'))
MEMBER = ('MEMBER', _('Member'))
class Study(MetaDataModel):
A model to represent a study of a researcher. This study will receive the dropoff files from various sources.
It will inherit the attributes :attr:`~lib.models.base.MetaDataModel.created_at` and :attr:`~lib.models.base.MetaDataModel.updated_at` from the Abstract model :class:`~lib.models.base.MetaDataModel`
researcher : Researcher
The Django Researcher model that is the owner of this study.
name : str
The name of the study. Can be entered freely by the researcher.
description : str
A small description what the study is about. This will be used on the upload page and invitation mail.
upload_code : str
A unique code that is used as a token for uploading. This is a general upload code. Every Invitation will get his own upload code as well. This will be auto generated.
upload_uuid : uuid4
A UUID v4 string that is used to create a unique upload url. This will be auto generated when created.
class Meta:
verbose_name = _('studie')
verbose_name_plural = _('studies')
ordering = ['name']
owner = models.ForeignKey(Researcher, verbose_name=Researcher._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The researcher that is the owner this study. By default the researcher that has created this study.'))
name = models.CharField(_('Name'), max_length=200, help_text=_('Name of the research study.'))
description = models.TextField(_('Description'), blank=True, null=True, help_text=_('Enter a short description for this study.'))
code = models.CharField(_('Code'), max_length=50, help_text=_('The research study code.'))
human_subject = models.BooleanField(_('Human subject'), help_text=_('Is this research study using real humans.'))
field = models.ForeignKey(StudyField, verbose_name=StudyField._meta.verbose_name, on_delete=models.CASCADE, help_text=_('The study field for this reaserch study.'))
contributors = models.ManyToManyField(Researcher, related_name='contributors', through='StudyRole', through_fields=('study', 'researcher'))
# These are used for accepting files (datadrops)
upload_code = models.CharField(_('Upload code'), max_length=20, default=get_random_int_value, editable=False, help_text=_('A unique upload code. Will be generated when a new study is saved.'))
upload_uuid = models.UUIDField(_('Upload url key'), unique=True, default=uuid.uuid4, editable=False, help_text=_('A unique upload url. Will be generated when a new study is saved.'))
# Here we load our custom StudyManager so we always have the amount of files, total file size and amount of invitations directly accessable
objects = StudyManager()
def has_storage(self):
"""boolean: Returns true when there is at least one storage location connected to this study"""
# print(dir(self.storagelocation))
return self.storagelocation is not None
def get_absolute_url(self):
"""str: Returns the full url to the study detail page."""
return 'aaa'
# return reverse('study:detail', kwargs={'study_id': self.pk})
def web_upload_url(self):
"""str: Returns the full url for the study upload page through web."""
return f'{settings.DROPOFF_BASE_URL.strip("/")}/{self.upload_uuid}'
def api_upload_url(self):
"""str: Returns the full url for the study upload API entrypoint."""
return f'{settings.DROPOFF_UPLOAD_HOST.strip("/")}/files/{self.upload_uuid}/'
def total_files(self):
"""int: Returns the total amount of uploaded files"""
return self._total_files
def total_file_size(self):
"""int: Returns the total upload amount of the uploaded files"""
return 0 if self._total_file_size is None else self._total_file_size
def total_invitations(self):
"""int: Returns the total amount of invitations"""
return self._total_invitations
def __str__(self):
"""str: Returns a readable string for the study."""
return f'{self.name}'
class StudyRole(MetaDataModel):
study = models.ForeignKey(Study, verbose_name=Study._meta.verbose_name, on_delete=models.CASCADE, help_text=_('Study'))
researcher = models.ForeignKey(Researcher, verbose_name=Researcher._meta.verbose_name, on_delete=models.CASCADE, help_text=_('Researcher'))
role = models.CharField(_('Role'), max_length=10, choices=StudyRoleNames.choices, default=StudyRoleNames.MEMBER, help_text=_('The role withing this research study.'))
active = models.BooleanField(_('Active'), default=True, help_text=_(''))
def __str__(self):
"""str: Returns a readable string for the study role."""
return f'{self.researcher} is {self.role} of {self.study.name}'