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 objectives.schema import ObjectivesQuery
|
||||||
from portfolio.mutations import PortfolioMutations
|
from portfolio.mutations import PortfolioMutations
|
||||||
from portfolio.schema import PortfolioQuery
|
from portfolio.schema import PortfolioQuery
|
||||||
|
from surveys.schema import SurveysQuery
|
||||||
|
from surveys.mutations import SurveysMutations
|
||||||
from rooms.mutations import RoomMutations
|
from rooms.mutations import RoomMutations
|
||||||
from rooms.schema import RoomsQuery
|
from rooms.schema import RoomsQuery
|
||||||
from users.schema import UsersQuery
|
from users.schema import UsersQuery
|
||||||
|
|
@ -22,7 +24,7 @@ from users.mutations import ProfileMutations
|
||||||
|
|
||||||
|
|
||||||
class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery,
|
class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery,
|
||||||
BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, graphene.ObjectType):
|
BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, SurveysQuery, graphene.ObjectType):
|
||||||
node = relay.Node.Field()
|
node = relay.Node.Field()
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
@ -30,7 +32,7 @@ class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery
|
||||||
|
|
||||||
|
|
||||||
class Mutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, CoreMutations, PortfolioMutations,
|
class Mutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, CoreMutations, PortfolioMutations,
|
||||||
ProfileMutations, graphene.ObjectType):
|
ProfileMutations, SurveysMutations, graphene.ObjectType):
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
debug = graphene.Field(DjangoDebug, name='__debug')
|
debug = graphene.Field(DjangoDebug, name='__debug')
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@ def get_graphql_mutation(filename):
|
||||||
return mutation
|
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):
|
def get_by_id_or_slug(model, **kwargs):
|
||||||
slug = kwargs.get('slug')
|
slug = kwargs.get('slug')
|
||||||
id = kwargs.get('id')
|
id = kwargs.get('id')
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,40 @@
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.contrib import admin
|
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.
|
# 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.db import models
|
||||||
from django.contrib.postgres.fields import JSONField
|
from django.contrib.postgres.fields import JSONField
|
||||||
|
|
||||||
|
|
@ -8,3 +9,11 @@ class Survey(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
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