Migrate form data to json field

This commit is contained in:
Christian Cueni 2023-02-06 15:16:08 +01:00
parent f5c7ab77e1
commit aa5b744285
12 changed files with 292 additions and 169 deletions

View File

@ -157,12 +157,9 @@ const MAX_STEPS = 12;
const sendFeedbackMutation = graphql(` const sendFeedbackMutation = graphql(`
mutation SendFeedbackMutation($input: SendFeedbackInput!) { mutation SendFeedbackMutation($input: SendFeedbackInput!) {
sendFeedback(input: $input) { sendFeedback(input: $input) {
id feedbackResponse {
satisfaction id
goalAttainment }
proficiency
receivedMaterials
materialsRating
errors { errors {
field field
messages messages
@ -205,17 +202,19 @@ const sendFeedback = () => {
return; return;
} }
const input: SendFeedbackInput = reactive({ const input: SendFeedbackInput = reactive({
materialsRating, data: {
courseNegativeFeedback, materials_rating: materialsRating,
coursePositiveFeedback, course_negative_feedback: courseNegativeFeedback,
goalAttainment, course_positive_feedback: coursePositiveFeedback,
instructorCompetence, goald_attainment: goalAttainment,
instructorRespect, instructor_competence: instructorCompetence,
instructorOpenFeedback, instructor_respect: instructorRespect,
satisfaction, instructor_open_feedback: instructorOpenFeedback,
proficiency, satisfaction,
receivedMaterials, proficiency,
wouldRecommend, received_materials: receivedMaterials,
would_recommend: wouldRecommend,
},
page: props.page.translation_key, page: props.page.translation_key,
courseSession: courseSession.id, courseSession: courseSession.id,
}); });

View File

@ -3,13 +3,13 @@ import type { TypedDocumentNode as DocumentNode } from "@graphql-typed-document-
import * as types from "./graphql"; import * as types from "./graphql";
const documents = { const documents = {
"\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n id\n satisfaction\n goalAttainment\n proficiency\n receivedMaterials\n materialsRating\n errors {\n field\n messages\n }\n }\n }\n": "\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n feedbackResponse {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n":
types.SendFeedbackMutationDocument, types.SendFeedbackMutationDocument,
}; };
export function graphql( export function graphql(
source: "\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n id\n satisfaction\n goalAttainment\n proficiency\n receivedMaterials\n materialsRating\n errors {\n field\n messages\n }\n }\n }\n" source: "\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n feedbackResponse {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n"
): typeof documents["\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n id\n satisfaction\n goalAttainment\n proficiency\n receivedMaterials\n materialsRating\n errors {\n field\n messages\n }\n }\n }\n"]; ): typeof documents["\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n sendFeedback(input: $input) {\n feedbackResponse {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n"];
export function graphql(source: string): unknown; export function graphql(source: string): unknown;
export function graphql(source: string) { export function graphql(source: string) {

View File

@ -17,6 +17,7 @@ export type Scalars = {
Int: number; Int: number;
Float: number; Float: number;
DateTime: any; DateTime: any;
GenericScalar: any;
JSONString: any; JSONString: any;
PositiveInt: any; PositiveInt: any;
UUID: any; UUID: any;
@ -152,6 +153,11 @@ export type CircleSiblingsArgs = {
searchQuery?: InputMaybe<Scalars["String"]>; searchQuery?: InputMaybe<Scalars["String"]>;
}; };
export type CircleDocument = {
__typename?: "CircleDocument";
id?: Maybe<Scalars["ID"]>;
};
export type CollectionObjectType = { export type CollectionObjectType = {
__typename?: "CollectionObjectType"; __typename?: "CollectionObjectType";
ancestors: Array<Maybe<CollectionObjectType>>; ancestors: Array<Maybe<CollectionObjectType>>;
@ -520,6 +526,14 @@ export type ErrorType = {
messages: Array<Scalars["String"]>; messages: Array<Scalars["String"]>;
}; };
export type FeedbackResponse = Node & {
__typename?: "FeedbackResponse";
circle: Circle;
courseSession: CourseSession;
data?: Maybe<Scalars["GenericScalar"]>;
id: Scalars["ID"];
};
export type FloatBlock = StreamFieldInterface & { export type FloatBlock = StreamFieldInterface & {
__typename?: "FloatBlock"; __typename?: "FloatBlock";
blockType: Scalars["String"]; blockType: Scalars["String"];
@ -1170,6 +1184,10 @@ export type MutationSendFeedbackArgs = {
input: SendFeedbackInput; input: SendFeedbackInput;
}; };
export type Node = {
id: Scalars["ID"];
};
export type Page = PageInterface & { export type Page = PageInterface & {
__typename?: "Page"; __typename?: "Page";
aliasOf?: Maybe<Page>; aliasOf?: Maybe<Page>;
@ -1581,6 +1599,7 @@ export type RichTextBlock = StreamFieldInterface & {
export type Search = export type Search =
| Circle | Circle
| CircleDocument
| CompetencePage | CompetencePage
| CompetenceProfilePage | CompetenceProfilePage
| Course | Course
@ -1609,37 +1628,16 @@ export type SecurityRequestResponseLog = {
export type SendFeedbackInput = { export type SendFeedbackInput = {
clientMutationId?: InputMaybe<Scalars["String"]>; clientMutationId?: InputMaybe<Scalars["String"]>;
courseNegativeFeedback?: InputMaybe<Scalars["String"]>; courseSession: Scalars["Int"];
coursePositiveFeedback?: InputMaybe<Scalars["String"]>; data?: InputMaybe<Scalars["GenericScalar"]>;
goalAttainment?: InputMaybe<Scalars["Int"]>;
id?: InputMaybe<Scalars["Int"]>;
instructorCompetence?: InputMaybe<Scalars["Int"]>;
instructorOpenFeedback?: InputMaybe<Scalars["String"]>;
instructorRespect?: InputMaybe<Scalars["Int"]>;
materialsRating?: InputMaybe<Scalars["Int"]>;
page: Scalars["String"]; page: Scalars["String"];
proficiency?: InputMaybe<Scalars["Int"]>;
receivedMaterials?: InputMaybe<Scalars["Boolean"]>;
satisfaction?: InputMaybe<Scalars["Int"]>;
wouldRecommend?: InputMaybe<Scalars["Boolean"]>;
}; };
export type SendFeedbackPayload = { export type SendFeedbackPayload = {
__typename?: "SendFeedbackPayload"; __typename?: "SendFeedbackPayload";
clientMutationId?: Maybe<Scalars["String"]>; clientMutationId?: Maybe<Scalars["String"]>;
courseNegativeFeedback?: Maybe<Scalars["String"]>;
coursePositiveFeedback?: Maybe<Scalars["String"]>;
errors?: Maybe<Array<Maybe<ErrorType>>>; errors?: Maybe<Array<Maybe<ErrorType>>>;
goalAttainment?: Maybe<Scalars["Int"]>; feedbackResponse?: Maybe<FeedbackResponse>;
id?: Maybe<Scalars["Int"]>;
instructorCompetence?: Maybe<Scalars["Int"]>;
instructorOpenFeedback?: Maybe<Scalars["String"]>;
instructorRespect?: Maybe<Scalars["Int"]>;
materialsRating?: Maybe<Scalars["Int"]>;
proficiency?: Maybe<Scalars["Int"]>;
receivedMaterials?: Maybe<Scalars["Boolean"]>;
satisfaction?: Maybe<Scalars["Int"]>;
wouldRecommend?: Maybe<Scalars["Boolean"]>;
}; };
export type SiteObjectType = { export type SiteObjectType = {
@ -1851,12 +1849,7 @@ export type SendFeedbackMutationMutation = {
__typename?: "Mutation"; __typename?: "Mutation";
sendFeedback?: { sendFeedback?: {
__typename?: "SendFeedbackPayload"; __typename?: "SendFeedbackPayload";
id?: number | null; feedbackResponse?: { __typename?: "FeedbackResponse"; id: string } | null;
satisfaction?: number | null;
goalAttainment?: number | null;
proficiency?: number | null;
receivedMaterials?: boolean | null;
materialsRating?: number | null;
errors?: Array<{ errors?: Array<{
__typename?: "ErrorType"; __typename?: "ErrorType";
field: string; field: string;
@ -1901,12 +1894,16 @@ export const SendFeedbackMutationDocument = {
selectionSet: { selectionSet: {
kind: "SelectionSet", kind: "SelectionSet",
selections: [ selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } }, {
{ kind: "Field", name: { kind: "Name", value: "satisfaction" } }, kind: "Field",
{ kind: "Field", name: { kind: "Name", value: "goalAttainment" } }, name: { kind: "Name", value: "feedbackResponse" },
{ kind: "Field", name: { kind: "Name", value: "proficiency" } }, selectionSet: {
{ kind: "Field", name: { kind: "Name", value: "receivedMaterials" } }, kind: "SelectionSet",
{ kind: "Field", name: { kind: "Name", value: "materialsRating" } }, selections: [
{ kind: "Field", name: { kind: "Name", value: "id" } },
],
},
},
{ {
kind: "Field", kind: "Field",
name: { kind: "Name", value: "errors" }, name: { kind: "Name", value: "errors" },

View File

@ -79,6 +79,10 @@ type Circle implements PageInterface {
ancestors(limit: PositiveInt, offset: PositiveInt, order: String, searchQuery: String, id: ID): [PageInterface!]! ancestors(limit: PositiveInt, offset: PositiveInt, order: String, searchQuery: String, id: ID): [PageInterface!]!
} }
type CircleDocument {
id: ID
}
type CollectionObjectType { type CollectionObjectType {
id: ID! id: ID!
path: String! path: String!
@ -281,6 +285,13 @@ type ErrorType {
messages: [String!]! messages: [String!]!
} }
type FeedbackResponse implements Node {
id: ID!
data: GenericScalar
circle: Circle!
courseSession: CourseSession!
}
type FloatBlock implements StreamFieldInterface { type FloatBlock implements StreamFieldInterface {
id: String id: String
blockType: String! blockType: String!
@ -289,6 +300,8 @@ type FloatBlock implements StreamFieldInterface {
value: Float! value: Float!
} }
scalar GenericScalar
type ImageChooserBlock implements StreamFieldInterface { type ImageChooserBlock implements StreamFieldInterface {
id: String id: String
blockType: String! blockType: String!
@ -608,6 +621,10 @@ type Mutation {
sendFeedback(input: SendFeedbackInput!): SendFeedbackPayload sendFeedback(input: SendFeedbackInput!): SendFeedbackPayload
} }
interface Node {
id: ID!
}
type Page implements PageInterface { type Page implements PageInterface {
id: ID id: ID
path: String! path: String!
@ -783,42 +800,21 @@ type RichTextBlock implements StreamFieldInterface {
value: String! value: String!
} }
union Search = CoursePage | LearningPath | Topic | Circle | LearningSequence | LearningUnit | LearningContent | CompetenceProfilePage | CompetencePage | PerformanceCriteria | MediaLibraryPage | MediaCategoryPage | Page | LibraryDocument | User | SecurityRequestResponseLog | Course | CourseCategory | CourseCompletion | CourseSession | CourseSessionUser union Search = CoursePage | LearningPath | Topic | Circle | LearningSequence | LearningUnit | LearningContent | CompetenceProfilePage | CompetencePage | PerformanceCriteria | MediaLibraryPage | MediaCategoryPage | Page | LibraryDocument | User | SecurityRequestResponseLog | Course | CourseCategory | CourseCompletion | CourseSession | CourseSessionUser | CircleDocument
type SecurityRequestResponseLog { type SecurityRequestResponseLog {
id: ID id: ID
} }
input SendFeedbackInput { input SendFeedbackInput {
id: Int
page: String! page: String!
satisfaction: Int courseSession: Int!
goalAttainment: Int data: GenericScalar
proficiency: Int
receivedMaterials: Boolean
materialsRating: Int
instructorCompetence: Int
instructorRespect: Int
instructorOpenFeedback: String
wouldRecommend: Boolean
coursePositiveFeedback: String
courseNegativeFeedback: String
clientMutationId: String clientMutationId: String
} }
type SendFeedbackPayload { type SendFeedbackPayload {
id: Int feedbackResponse: FeedbackResponse
satisfaction: Int
goalAttainment: Int
proficiency: Int
receivedMaterials: Boolean
materialsRating: Int
instructorCompetence: Int
instructorRespect: Int
instructorOpenFeedback: String
wouldRecommend: Boolean
coursePositiveFeedback: String
courseNegativeFeedback: String
errors: [ErrorType] errors: [ErrorType]
clientMutationId: String clientMutationId: String
} }

View File

@ -1,3 +1,4 @@
from factory import Dict
from factory.django import DjangoModelFactory from factory.django import DjangoModelFactory
from factory.fuzzy import FuzzyChoice, FuzzyInteger from factory.fuzzy import FuzzyChoice, FuzzyInteger
@ -5,33 +6,37 @@ from vbv_lernwelt.feedback.models import FeedbackResponse
class FeedbackFactory(DjangoModelFactory): class FeedbackFactory(DjangoModelFactory):
data = Dict(
{
"satisfaction": FuzzyInteger(2, 4),
"goal_attainment": FuzzyInteger(3, 4),
"proficiency": FuzzyChoice([20, 40, 60, 80]),
"received_materials": FuzzyChoice([True, False]),
"materials_rating": FuzzyInteger(2, 4),
"instructor_competence": FuzzyInteger(3, 4),
"instructor_respect": FuzzyInteger(3, 4),
"instructor_open_feedback": FuzzyChoice(
[
"Alles gut, manchmal etwas langfädig",
"Super, bin begeistert",
"Ok, enspricht den Erwartungen",
]
),
"would_recommend": FuzzyChoice([True, False]),
"course_positive_feedback": FuzzyChoice(
[
"Die Präsentation war super",
"Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
]
),
"course_negative_feedback": FuzzyChoice(
[
"Es wäre praktisch, Zugang zu einer FAQ zu haben.",
"Es wäre schön, mehr Videos hinzuzufügen.",
]
),
}
)
class Meta: class Meta:
model = FeedbackResponse model = FeedbackResponse
satisfaction = FuzzyInteger(2, 4)
goal_attainment = FuzzyInteger(3, 4)
proficiency = FuzzyChoice([20, 40, 60, 80])
received_materials = FuzzyChoice([True, False])
materials_rating = FuzzyInteger(2, 4)
instructor_competence = FuzzyInteger(3, 4)
instructor_respect = FuzzyInteger(3, 4)
instructor_open_feedback = FuzzyChoice(
[
"Alles gut, manchmal etwas langfädig",
"Super, bin begeistert",
"Ok, enspricht den Erwartungen",
]
)
would_recommend = FuzzyChoice([True, False])
course_positive_feedback = FuzzyChoice(
[
"Die Präsentation war super",
"Das Beispiel mit der Katze fand ich sehr gut veranschaulicht!",
]
)
course_negative_feedback = FuzzyChoice(
[
"Es wäre praktisch, Zugang zu einer FAQ zu haben.",
"Es wäre schön, mehr Videos hinzuzufügen.",
]
)

View File

@ -1,13 +1,56 @@
import graphene import structlog
from graphene_django.rest_framework.mutation import SerializerMutation from graphene import ClientIDMutation, Field, Int, List, String
from graphene.types.generic import GenericScalar
from graphene_django.types import ErrorType
from vbv_lernwelt.feedback.serializers import FeedbackResponseSerializer from vbv_lernwelt.course.models import CourseSession
from vbv_lernwelt.feedback.graphql.types import FeedbackResponse as FeedbackResponseType
from vbv_lernwelt.feedback.models import FeedbackResponse
from vbv_lernwelt.feedback.serializers import CourseFeedbackSerializer
from wagtail.models import Page
logger = structlog.get_logger(__name__)
class SendFeedback(SerializerMutation): # https://medium.com/open-graphql/jsonfield-models-in-graphene-django-308ae43d14ee
class Meta: class SendFeedback(ClientIDMutation):
serializer_class = FeedbackResponseSerializer feedback_response = Field(FeedbackResponseType)
model_operations = ["create"] errors = List(
ErrorType, description="May contain more than one error for same field."
)
class Input:
page = String(required=True)
course_session = Int(required=True)
data = GenericScalar()
@classmethod
def mutate_and_get_payload(cls, _, info, **input):
page_key = input["page"]
course_session_id = input["course_session"]
logger.info("creating feedback")
learning_content = Page.objects.get(
translation_key=page_key, locale__language_code="de-CH"
)
circle = learning_content.get_parent().specific
course_session = CourseSession.objects.get(id=course_session_id)
data = input.get("data", {})
serializer = CourseFeedbackSerializer(data=data)
if not serializer.is_valid():
logger.error(serializer.errors)
return SendFeedback(errors=serializer.errors)
feedback_response = FeedbackResponse.objects.create(
circle=circle,
course_session=course_session,
data=serializer.validated_data,
)
logger.info(feedback_response)
return SendFeedback(feedback_response=feedback_response)
class Mutation(object): class Mutation(object):

View File

@ -1,8 +1,13 @@
from graphene.relay import Node
from graphene.types.generic import GenericScalar
from graphene_django import DjangoObjectType from graphene_django import DjangoObjectType
from vbv_lernwelt.feedback.models import Feedback from vbv_lernwelt.feedback.models import FeedbackResponse as FeedbackResponseModel
# class FeedbackType(DjangoObjectType): class FeedbackResponse(DjangoObjectType):
# class Meta: data = GenericScalar()
# model = Feedback
class Meta:
model = FeedbackResponseModel
interfaces = (Node,)

View File

@ -0,0 +1,66 @@
# Generated by Django 3.2.13 on 2023-02-06 10:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("feedback", "0002_auto_20230111_1044"),
]
operations = [
migrations.RemoveField(
model_name="feedbackresponse",
name="course_negative_feedback",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="course_positive_feedback",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="goal_attainment",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="instructor_competence",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="instructor_open_feedback",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="instructor_respect",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="materials_rating",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="proficiency",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="received_materials",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="satisfaction",
),
migrations.RemoveField(
model_name="feedbackresponse",
name="would_recommend",
),
migrations.AddField(
model_name="feedbackresponse",
name="data",
field=models.JSONField(default=dict),
),
migrations.AddField(
model_name="feedbackresponse",
name="created_at",
field=models.DateTimeField(auto_now_add=True),
),
]

View File

@ -37,17 +37,20 @@ class FeedbackResponse(models.Model):
EIGHTY = 80, "80%" EIGHTY = 80, "80%"
HUNDRED = 100, "100%" HUNDRED = 100, "100%"
satisfaction = FeedbackIntegerField() # satisfaction = FeedbackIntegerField()
goal_attainment = FeedbackIntegerField() # goal_attainment = FeedbackIntegerField()
proficiency = models.IntegerField(null=True) # proficiency = models.IntegerField(null=True)
received_materials = models.BooleanField(null=True) # received_materials = models.BooleanField(null=True)
materials_rating = FeedbackIntegerField() # materials_rating = FeedbackIntegerField()
instructor_competence = FeedbackIntegerField() # instructor_competence = FeedbackIntegerField()
instructor_respect = FeedbackIntegerField() # instructor_respect = FeedbackIntegerField()
instructor_open_feedback = models.TextField(blank=True) # instructor_open_feedback = models.TextField(blank=True)
would_recommend = models.BooleanField(null=True) # would_recommend = models.BooleanField(null=True)
course_positive_feedback = models.TextField(blank=True) # course_positive_feedback = models.TextField(blank=True)
course_negative_feedback = models.TextField(blank=True) # course_negative_feedback = models.TextField(blank=True)
data = models.JSONField(default=dict)
created_at = models.DateTimeField(auto_now_add=True)
circle = models.ForeignKey("learnpath.Circle", models.PROTECT) circle = models.ForeignKey("learnpath.Circle", models.PROTECT)
course_session = models.ForeignKey("course.CourseSession", models.PROTECT) course_session = models.ForeignKey("course.CourseSession", models.PROTECT)

View File

@ -1,32 +1,31 @@
import structlog import structlog
from rest_framework import serializers from rest_framework import serializers
from wagtail.models import Page
from vbv_lernwelt.course.models import CourseSession
from vbv_lernwelt.feedback.models import FeedbackResponse
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
class FeedbackResponseSerializer(serializers.ModelSerializer): class FeedbackIntegerField(serializers.IntegerField):
page = serializers.CharField(write_only=True) def __init__(self, **kwargs):
course_session = serializers.CharField(write_only=True) super().__init__(
required=False, allow_null=True, min_value=1, max_value=5, **kwargs
class Meta:
model = FeedbackResponse
exclude = ["circle"]
# extra_kwargs = {"course", {"read_only": True}}
def create(self, validated_data):
logger.info("creating feedback")
page_key = validated_data.pop("page")
course_session_id = validated_data.pop("course_session")
learning_content = Page.objects.get(
translation_key=page_key, locale__language_code="de-CH"
)
circle = learning_content.get_parent().specific
course_session = CourseSession.objects.get(id=course_session_id)
return FeedbackResponse.objects.create(
**validated_data, circle=circle, course_session=course_session
) )
class CourseFeedbackSerializer(serializers.Serializer):
satisfaction = FeedbackIntegerField()
goal_attainment = FeedbackIntegerField()
proficiency = serializers.IntegerField(required=False, allow_null=True)
received_materials = serializers.BooleanField(required=False, allow_null=True)
materials_rating = FeedbackIntegerField()
instructor_competence = FeedbackIntegerField()
instructor_respect = FeedbackIntegerField()
instructor_open_feedback = serializers.CharField(
required=False, allow_null=True, allow_blank=True
)
would_recommend = serializers.BooleanField(required=False, allow_null=True)
course_positive_feedback = serializers.CharField(
required=False, allow_null=True, allow_blank=True
)
course_negative_feedback = serializers.CharField(
required=False, allow_null=True, allow_blank=True
)

View File

@ -159,17 +159,25 @@ class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
FeedbackFactory( FeedbackFactory(
circle=circle, circle=circle,
course_session=csu.course_session, course_session=csu.course_session,
satisfaction=feedback_data["satisfaction"][i], data={
goal_attainment=feedback_data["goal_attainment"][i], "satisfaction": feedback_data["satisfaction"][i],
proficiency=feedback_data["proficiency"][i], "goal_attainment": feedback_data["goal_attainment"][i],
received_materials=feedback_data["received_materials"][i], "proficiency": feedback_data["proficiency"][i],
materials_rating=feedback_data["materials_rating"][i], "received_materials": feedback_data["received_materials"][i],
instructor_competence=feedback_data["instructor_competence"][i], "materials_rating": feedback_data["materials_rating"][i],
instructor_open_feedback=feedback_data["instructor_open_feedback"][i], "instructor_competence": feedback_data["instructor_competence"][i],
instructor_respect=feedback_data["instructor_respect"][i], "instructor_open_feedback": feedback_data[
would_recommend=feedback_data["would_recommend"][i], "instructor_open_feedback"
course_positive_feedback=feedback_data["course_positive_feedback"][i], ][i],
course_negative_feedback=feedback_data["course_negative_feedback"][i], "instructor_respect": feedback_data["instructor_respect"][i],
"would_recommend": feedback_data["would_recommend"][i],
"course_positive_feedback": feedback_data[
"course_positive_feedback"
][i],
"course_negative_feedback": feedback_data[
"course_negative_feedback"
][i],
},
).save() ).save()
response = self.client.get( response = self.client.get(

View File

@ -49,7 +49,7 @@ def get_feedback_for_circle(request, course_id, circle_id):
course_session__course_id=course_id, course_session__course_id=course_id,
circle__expert__user=request.user, circle__expert__user=request.user,
circle_id=circle_id, circle_id=circle_id,
) ).order_by("created_at")
# I guess this is ok for the üK case # I guess this is ok for the üK case
feedback_data = {"amount": len(feedbacks), "questions": {}} feedback_data = {"amount": len(feedbacks), "questions": {}}
@ -62,6 +62,8 @@ def get_feedback_for_circle(request, course_id, circle_id):
for feedback in feedbacks: for feedback in feedbacks:
for field in FEEDBACK_FIELDS: for field in FEEDBACK_FIELDS:
feedback_data["questions"][field].append(getattr(feedback, field)) data = feedback.data.get(field, None)
if data is not None:
feedback_data["questions"][field].append(data)
return Response(status=200, data=feedback_data) return Response(status=200, data=feedback_data)