Skip to content
Snippets Groups Projects
Commit 8024fe1d authored by Eric's avatar Eric
Browse files

Merge branch '159-create-a-dataset-programmatically' into 'master'

implements GRAPHQL Dataset and Datafile mutations

Closes #156 and #159

See merge request !85
parents 226e755d 1a0bb065
No related branches found
No related tags found
1 merge request!85implements GRAPHQL Dataset and Datafile mutations
......@@ -95,6 +95,10 @@ CRISPY_TEMPLATE_PACK = 'bootstrap4'
WSGI_APPLICATION = 'iric_data.wsgi.application'
# Graphene / GraphQL
GRAPHENE = {
"ATOMIC_MUTATIONS": True,
}
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
......
......@@ -94,7 +94,6 @@ class AlertForm(forms.ModelForm):
)
class UserForm(forms.ModelForm):
class Meta:
model = User
......
import os
import graphene
import hashlib
from django.contrib.auth import get_user_model
from django.db import connection
from django.utils import timezone
from graphql.error import GraphQLError
from graphene_django.types import DjangoObjectType
from graphene_django.views import GraphQLView
from graphene_django.forms.mutation import DjangoModelFormMutation
from portal.models import DataFile, DataSet, Profile
from portal.models import DataFile, DataSet, Lab, Profile
from portal.views import TokenLoginMixin
from portal.forms import DataSetForm
# Protect graphql API page
......@@ -25,7 +33,7 @@ class DataSetType(DjangoObjectType):
fields = "__all__"
class ProfileSetType(DjangoObjectType):
class ProfileType(DjangoObjectType):
class Meta:
model = Profile
......@@ -35,20 +43,27 @@ class UserType(DjangoObjectType):
model = get_user_model()
class LabType(DjangoObjectType):
class Meta:
model = Lab
# Create a Query type
class Query(graphene.ObjectType):
datafile = graphene.Field(DataFileType, id=graphene.String(), dbid=graphene.Int())
dataset = graphene.Field(DataSetType, id=graphene.String(), dbid=graphene.Int())
datafile = graphene.Field(DataFileType, id=graphene.String(), dbid=graphene.ID())
dataset = graphene.Field(DataSetType, id=graphene.String(), dbid=graphene.ID())
datafiles = graphene.List(
DataFileType,
key=graphene.String(),
value=graphene.String(),
key_values=graphene.JSONString(),
keys=graphene.JSONString(),
values=graphene.JSONString(),
dataset=graphene.String()
)
DataFileType,
key=graphene.String(),
value=graphene.String(),
key_values=graphene.JSONString(),
keys=graphene.JSONString(),
values=graphene.JSONString(),
dataset=graphene.String(),
file_hash=graphene.String()
)
datasets = graphene.List(DataSetType)
lab = graphene.Field(LabType, name=graphene.String(), dbid=graphene.ID())
def resolve_datafile(self, info, **kwargs):
id = kwargs.get('id')
......@@ -80,6 +95,7 @@ class Query(graphene.ObjectType):
keys = kwargs.get('keys', None)
values = kwargs.get('values', None)
dataset = kwargs.get('dataset', None)
file_hash = kwargs.get('file_hash', None)
if type(key_values) is not dict:
key_values = {}
......@@ -109,14 +125,92 @@ class Query(graphene.ObjectType):
if dataset:
qs = qs.filter(datasets__iric_data_id=dataset)
if file_hash:
iric_data_file_hash = os.path.join(file_hash[:3], file_hash)
qs = qs.filter(file=iric_data_file_hash)
return qs.distinct()
def resolve_datasets(self, info, **kwargs):
return DataSet.objects.all()
def resolve_lab(self, info, **kwargs):
name = kwargs.get('name')
dbid = kwargs.get('dbid')
if name is not None:
return Lab.objects.get(name=name)
elif dbid is not None:
return Lab.objects.get(pk=dbid)
class DataSetMutation(DjangoModelFormMutation):
"""Enable the creation or modification of a DataSet
Makes use of the DataSetForm, so all exposed fields from that form are available"""
class Meta:
form_class = DataSetForm
return_field_name = 'dataset'
@classmethod
def perform_mutate(cls, form, info):
object = form.save(commit=False)
if not object.pk:
object.read_only = True
object.created_by = info.context.user.profile
object.last_update_by = info.context.user.profile
object.save()
return super().perform_mutate(form, info)
class DataFileAttachMutation(graphene.Mutation):
attached_datafile = graphene.Field(DataFileType)
class Arguments:
# The input arguments for this mutation
file_hash = graphene.String(required=True)
filename = graphene.String(required=True)
target_user_email = graphene.String(required=True)
annotations = graphene.String()
@classmethod
def mutate(cls, root, info, file_hash, filename, target_user_email, annotations=None):
# check that calling user is staff:
if not info.context.user.is_staff:
raise GraphQLError("You are not allowed to use this API function")
# transform file_hash
iric_data_file_hash = os.path.join(file_hash[:3], file_hash)
target_profile = Profile.objects.get(user__email=target_user_email)
if not DataFile.objects.filter(file=iric_data_file_hash).exists():
raise GraphQLError("No datafile associated with the provided hash")
with connection.cursor() as cursor:
cursor.execute(
"INSERT INTO portal_datafile(file, filename, uploaded_by_id, upload_timestamp, annotations) VALUES (%s, %s, %s, %s, %s)",
[
iric_data_file_hash,
filename,
target_profile.id,
timezone.now(),
annotations
]
)
df = DataFile.objects.latest('id')
df.iric_data_id = "DF{}".format(hashlib.md5(str(df.id).encode()).hexdigest()[:8]).upper()
df.save()
return DataFileAttachMutation(attached_datafile=df)
class Mutation(graphene.ObjectType):
pass
dataset = DataSetMutation.Field()
attach_datafile_by_hash = DataFileAttachMutation.Field()
schema = graphene.Schema(query=Query, auto_camelcase=False)
schema = graphene.Schema(query=Query, mutation=Mutation, auto_camelcase=False)
# Generated by Django 2.2.17 on 2023-01-05 15:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('portal', '0028_profile_update_timestamp'),
]
operations = [
migrations.AlterField(
model_name='lab',
name='ldap',
field=models.CharField(blank=True, max_length=32, null=True, unique=True, verbose_name='LDAP'),
),
migrations.AlterField(
model_name='lab',
name='name',
field=models.CharField(max_length=64, unique=True, verbose_name='Name'),
),
]
......@@ -97,8 +97,8 @@ class Lab(models.Model):
class Meta:
ordering = ['name']
name = models.CharField(max_length=64, verbose_name=_('Name'))
ldap = models.CharField(max_length=32, null=True, blank=True, verbose_name='LDAP')
name = models.CharField(max_length=64, verbose_name=_('Name'), unique=True)
ldap = models.CharField(max_length=32, null=True, blank=True, verbose_name='LDAP', unique=True)
pi = models.OneToOneField(
Profile,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment