Add answers to surveys
This commit is contained in:
parent
f9642ff49e
commit
9b85560795
|
|
@ -0,0 +1,20 @@
|
|||
mutation UpdateAnswer($input:UpdateAnswerInput!) {
|
||||
updateAnswer(input:$input){
|
||||
answer {
|
||||
id
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# input
|
||||
|
||||
#{
|
||||
# "input": {
|
||||
# "answer": {
|
||||
# "surveyId": "U3VydmV5Tm9kZTox",
|
||||
# "data": "{\"some\": \"json\"}"
|
||||
# },
|
||||
# }
|
||||
#}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
export const css = {
|
||||
'root': 'survey',
|
||||
'header': '',
|
||||
'body': '',
|
||||
'bodyEmpty': '',
|
||||
'footer': '',
|
||||
'navigationButton': 'button button--primary',
|
||||
'completedPage': '',
|
||||
'navigation': {
|
||||
'complete': 'button button--primary',
|
||||
'prev': 'button button--primary',
|
||||
'next': 'button button--primary',
|
||||
'start': 'button button--primary'
|
||||
},
|
||||
'progress': 'progress center-block mx-auto mb-4',
|
||||
'progressBar': 'progress-bar',
|
||||
'page': {
|
||||
'root': '',
|
||||
'title': '',
|
||||
'survey__page-description': ''
|
||||
},
|
||||
'pageTitle': '',
|
||||
'pageDescription': 'small',
|
||||
'row': 'sv_row',
|
||||
'question': {
|
||||
'mainRoot': 'survey__question question',
|
||||
'flowRoot': 'sv_q_flow sv_qstn',
|
||||
'titleLeftRoot': 'sv_qstn_left',
|
||||
'title': 'survey__question-title',
|
||||
'number': 'sv_q_num',
|
||||
'survey__question-description': 'small',
|
||||
'comment': 'survey__question-input skillbox-input',
|
||||
'required': '',
|
||||
'titleRequired': '',
|
||||
'hasError': 'has-error',
|
||||
'indent': 20
|
||||
},
|
||||
'panel': {
|
||||
'title': 'survey__panel-title',
|
||||
'description': 'small survey__panel-description',
|
||||
'container': 'sv_p_container'
|
||||
},
|
||||
'error': {
|
||||
'root': 'alert alert-danger',
|
||||
'icon': 'glyphicon glyphicon-exclamation-sign',
|
||||
'item': '',
|
||||
'locationTop': 'sv_qstn_error_top',
|
||||
'locationBottom': 'sv_qstn_error_bottom'
|
||||
},
|
||||
'boolean': {
|
||||
'root': 'sv_qbln form-inline checkbox',
|
||||
'item': '',
|
||||
'label': '',
|
||||
'materialDecorator': 'checkbox-material'
|
||||
},
|
||||
'checkbox': {
|
||||
'root': 'sv_qcbc sv_qcbx form-inline',
|
||||
'item': 'checkbox',
|
||||
'itemControl': '',
|
||||
'controlLabel': '',
|
||||
'materialDecorator': 'checkbox-material',
|
||||
'other': 'sv_q_checkbox_other skillbox-input',
|
||||
'column': 'sv_q_select_column'
|
||||
},
|
||||
'comment': 'survey__input skillbox-input',
|
||||
'dropdown': {
|
||||
'root': '',
|
||||
'control': 'skillbox-input',
|
||||
'other': 'sv_q_dd_other skillbox-input'
|
||||
},
|
||||
'html': {
|
||||
'root': ''
|
||||
},
|
||||
'matrix': {
|
||||
'root': 'table table-striped',
|
||||
'label': 'sv_q_m_label',
|
||||
'cellText': 'sv_q_m_cell_text',
|
||||
'cellTextSelected': 'sv_q_m_cell_selected bg-primary',
|
||||
'cellLabel': 'sv_q_m_cell_label'
|
||||
},
|
||||
'matrixdropdown': {
|
||||
'root': 'table'
|
||||
},
|
||||
'matrixdynamic': {
|
||||
'root': 'table',
|
||||
'button': 'button',
|
||||
'buttonAdd': '',
|
||||
'buttonRemove': '',
|
||||
'iconAdd': '',
|
||||
'iconRemove': ''
|
||||
},
|
||||
'paneldynamic': {
|
||||
'root': '',
|
||||
'button': 'button',
|
||||
'buttonPrev': '',
|
||||
'buttonNext': '',
|
||||
'buttonAdd': '',
|
||||
'buttonRemove': ''
|
||||
},
|
||||
'multipletext': {
|
||||
'root': 'table',
|
||||
'itemTitle': '',
|
||||
'itemValue': 'sv_q_mt_item_value skillbox-input'
|
||||
},
|
||||
'radiogroup': {
|
||||
'root': 'sv_qcbc form-inline',
|
||||
'item': 'radio',
|
||||
'label': '',
|
||||
'itemControl': '',
|
||||
'controlLabel': '',
|
||||
'materialDecorator': 'circle',
|
||||
'other': 'sv_q_radiogroup_other skillbox-input',
|
||||
'clearButton': 'sv_q_radiogroup_clear button',
|
||||
'column': 'sv_q_select_column'
|
||||
},
|
||||
'imagepicker': {
|
||||
'root': 'sv_imgsel',
|
||||
'item': 'sv_q_imgsel',
|
||||
'label': 'sv_q_imgsel_label',
|
||||
'itemControl': 'sv_q_imgsel_control_item',
|
||||
'image': 'sv_q_imgsel_image',
|
||||
'itemText': 'sv_q_imgsel_text',
|
||||
'clearButton': 'sv_q_radiogroup_clear'
|
||||
},
|
||||
'rating': {
|
||||
'root': 'btn-group',
|
||||
'item': 'btn btn-default btn-secondary',
|
||||
'selected': 'active',
|
||||
'minText': 'sv_q_rating_min_text',
|
||||
'itemText': 'sv_q_rating_item_text',
|
||||
'maxText': 'sv_q_rating_max_text'
|
||||
},
|
||||
'text': 'survey__input skillbox-input',
|
||||
'expression': 'survey__input skillbox-input',
|
||||
'file': {
|
||||
'root': 'sv_q_file',
|
||||
'placeholderInput': 'sv_q_file_placeholder',
|
||||
'preview': 'sv_q_file_preview',
|
||||
'removeButton': 'sv_q_file_remove_button',
|
||||
'fileInput': 'sv_q_file_input',
|
||||
'removeFile': 'sv_q_file_remove'
|
||||
},
|
||||
'saveData': {
|
||||
'root': '',
|
||||
'saving': 'alert alert-info',
|
||||
'error': 'alert alert-danger',
|
||||
'success': 'alert alert-success',
|
||||
'saveAgainButton': ''
|
||||
},
|
||||
'window': {
|
||||
'root': 'modal-content',
|
||||
'body': 'modal-body',
|
||||
'header': {
|
||||
'root': 'modal-header panel-title',
|
||||
'title': 'pull-left',
|
||||
'button': 'glyphicon pull-right',
|
||||
'buttonExpanded': 'glyphicon pull-right glyphicon-chevron-up',
|
||||
'buttonCollapsed': 'glyphicon pull-right glyphicon-chevron-down'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -15,6 +15,8 @@ from objectives.mutations import ObjectiveMutations
|
|||
from objectives.schema import ObjectivesQuery
|
||||
from portfolio.mutations import PortfolioMutations
|
||||
from portfolio.schema import PortfolioQuery
|
||||
from surveys.schema import SurveysQuery
|
||||
from surveys.mutations import SurveysMutations
|
||||
from rooms.mutations import RoomMutations
|
||||
from rooms.schema import RoomsQuery
|
||||
from users.schema import UsersQuery
|
||||
|
|
@ -22,7 +24,7 @@ from users.mutations import ProfileMutations
|
|||
|
||||
|
||||
class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery,
|
||||
BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, graphene.ObjectType):
|
||||
BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, SurveysQuery, graphene.ObjectType):
|
||||
node = relay.Node.Field()
|
||||
|
||||
if settings.DEBUG:
|
||||
|
|
@ -30,7 +32,7 @@ class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery
|
|||
|
||||
|
||||
class Mutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, CoreMutations, PortfolioMutations,
|
||||
ProfileMutations, graphene.ObjectType):
|
||||
ProfileMutations, SurveysMutations, graphene.ObjectType):
|
||||
|
||||
if settings.DEBUG:
|
||||
debug = graphene.Field(DjangoDebug, name='__debug')
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ def get_graphql_mutation(filename):
|
|||
return mutation
|
||||
|
||||
|
||||
def get_by_id(model, **kwargs):
|
||||
id = kwargs.get('id')
|
||||
if id is not None:
|
||||
return get_object(model, id)
|
||||
return None
|
||||
|
||||
def get_by_id_or_slug(model, **kwargs):
|
||||
slug = kwargs.get('slug')
|
||||
id = kwargs.get('id')
|
||||
|
|
|
|||
|
|
@ -1,3 +1,40 @@
|
|||
import json
|
||||
import logging
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.forms import widgets
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Register your models here.
|
||||
from surveys.models import Survey, Answer
|
||||
|
||||
|
||||
class PrettyJSONWidget(widgets.Textarea):
|
||||
|
||||
def format_value(self, value):
|
||||
try:
|
||||
value = json.dumps(json.loads(value), indent=2, sort_keys=True)
|
||||
# these lines will try to adjust size of TextArea to fit to content
|
||||
row_lengths = [len(r) for r in value.split('\n')]
|
||||
self.attrs['rows'] = min(max(len(row_lengths) + 2, 10), 30)
|
||||
self.attrs['cols'] = min(max(max(row_lengths) + 2, 40), 120)
|
||||
return value
|
||||
except Exception as e:
|
||||
logger.warning("Error while formatting JSON: {}".format(e))
|
||||
return super(PrettyJSONWidget, self).format_value(value)
|
||||
|
||||
|
||||
class JSONAdmin(admin.ModelAdmin):
|
||||
formfield_overrides = {
|
||||
JSONField: {'widget': PrettyJSONWidget}
|
||||
}
|
||||
|
||||
@admin.register(Survey)
|
||||
class SurveyAdmin(JSONAdmin):
|
||||
pass
|
||||
|
||||
@admin.register(Answer)
|
||||
class AnswerAdmin(JSONAdmin):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import graphene
|
||||
from graphene import InputObjectType
|
||||
|
||||
class UpdateAnswerArgument(InputObjectType):
|
||||
survey_id = graphene.ID(required=True)
|
||||
data = graphene.String(required=True)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 2.0.6 on 2019-06-27 14:35
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('surveys', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Answer',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('data', django.contrib.postgres.fields.jsonb.JSONField()),
|
||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('survey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='surveys.Survey')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
|
||||
|
|
@ -8,3 +9,11 @@ class Survey(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class Answer(models.Model):
|
||||
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='answers')
|
||||
data = JSONField()
|
||||
survey = models.ForeignKey(Survey, on_delete=models.CASCADE, related_name='answers')
|
||||
|
||||
def __str__(self):
|
||||
return '{} - {}'.format(self.owner.username, self.survey.title)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import graphene
|
||||
import json
|
||||
from graphene import relay
|
||||
|
||||
from api.utils import get_object
|
||||
from surveys.inputs import UpdateAnswerArgument
|
||||
from surveys.models import Survey, Answer
|
||||
from surveys.schema import AnswerNode
|
||||
|
||||
|
||||
class UpdateAnswer(relay.ClientIDMutation):
|
||||
class Input:
|
||||
answer = graphene.Argument(UpdateAnswerArgument)
|
||||
|
||||
answer = graphene.Field(AnswerNode)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, root, info, **kwargs):
|
||||
user = info.context.user
|
||||
|
||||
answer = kwargs.get('answer')
|
||||
survey_id = answer.get('survey_id')
|
||||
data = json.loads(answer.get('data'))
|
||||
survey = get_object(Survey, survey_id)
|
||||
|
||||
try:
|
||||
answer = survey.answers.get(owner=user)
|
||||
answer.data = data
|
||||
answer.save()
|
||||
except Answer.DoesNotExist:
|
||||
answer = Answer.objects.create(owner=user, survey=survey, data=data)
|
||||
|
||||
return cls(answer=answer)
|
||||
|
||||
|
||||
class SurveysMutations:
|
||||
update_answer = UpdateAnswer.Field()
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import graphene
|
||||
from graphene import relay
|
||||
from graphene_django import DjangoObjectType
|
||||
from graphene_django.filter import DjangoFilterConnectionField
|
||||
|
||||
from api.utils import get_by_id
|
||||
from surveys.models import Answer
|
||||
from .models import Survey
|
||||
|
||||
|
||||
class AnswerNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
|
||||
class Meta:
|
||||
model = Answer
|
||||
interfaces = (relay.Node,)
|
||||
|
||||
def resolve_pk(self, *args, **kwargs):
|
||||
return self.id
|
||||
|
||||
|
||||
class SurveyNode(DjangoObjectType):
|
||||
pk = graphene.Int()
|
||||
answer = graphene.Field(AnswerNode)
|
||||
|
||||
class Meta:
|
||||
model = Survey
|
||||
filter_fields = []
|
||||
interfaces = (relay.Node,)
|
||||
|
||||
def resolve_pk(self, *args, **kwargs):
|
||||
return self.id
|
||||
|
||||
def resolve_answer(self, info, **kwargs):
|
||||
user = info.context.user
|
||||
try:
|
||||
return Answer.objects.get(owner=user, survey=self)
|
||||
except Answer.DoesNotExist:
|
||||
return None
|
||||
|
||||
class SurveysQuery(object):
|
||||
survey = graphene.Field(SurveyNode, id=graphene.ID())
|
||||
surveys = DjangoFilterConnectionField(SurveyNode)
|
||||
|
||||
def resolve_surveys(self, info, **kwargs):
|
||||
return Survey.objects.all()
|
||||
|
||||
def resolve_survey(self, info, **kwargs):
|
||||
return get_by_id(Survey, **kwargs)
|
||||
Loading…
Reference in New Issue