Add summary component
This commit is contained in:
parent
88848aa292
commit
44ed154814
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div class="px-6 py-5 bg-white mb-4">
|
||||
<h2 class="bg-feedback bg-no-repeat pl-[68px] heading-3 bg-60 leading-[60px] mb-4">{{ $t("general.feedback", 2) }}</h2>
|
||||
<ol>
|
||||
<ItRow
|
||||
v-for="feedbacks in feedbackSummary"
|
||||
:key="feedbacks.circle_id"
|
||||
>
|
||||
<template #firstRow><span class="text-bold">{{ $t("feedback.circleFeedback") }}</span></template>
|
||||
<template #center>
|
||||
<div class="flex justify-between w-full">
|
||||
<div>Circle: {{ feedbacks.circle.title }}</div>
|
||||
<div>{{ $t("feedback.sentByUsers", feedbacks.count) }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #link>
|
||||
<router-link
|
||||
:to="`${url}/cockpit/feedback/`"
|
||||
class="underline w-full text-right"
|
||||
>
|
||||
{{ $t("feedback.showDetails") }}
|
||||
</router-link>
|
||||
</template>
|
||||
</ItRow>
|
||||
</ol>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import ItRow from "@/components/ui/ItRow.vue";
|
||||
import { itGet } from "@/fetchHelpers";
|
||||
|
||||
import type { Circle } from "@/services/circle";
|
||||
|
||||
interface FeedbackSummary {
|
||||
circle_id: number;
|
||||
count: number;
|
||||
}
|
||||
|
||||
interface FeedbackDisplaySummary extends FeedbackSummary {
|
||||
circle: Circle;
|
||||
}
|
||||
|
||||
function makeSummary(feedbackData: FeedbackSummary[], circles: Circle[], selectedCircles: string[]) {
|
||||
const summary: FeedbackDisplaySummary[] = circles.filter(circle => selectedCircles.includes(circle.translation_key))
|
||||
.reduce((acc: FeedbackDisplaySummary[], circle) => {
|
||||
const circleFeedbacks = feedbackData
|
||||
.filter(data => data.circle_id === circle.id)
|
||||
.map(data => Object.assign({}, data, { circle }));
|
||||
|
||||
return acc.concat(circleFeedbacks);
|
||||
}, []);
|
||||
return summary;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
selctedCircles: string[];
|
||||
circles: Circle[];
|
||||
courseId: number;
|
||||
url: string;
|
||||
}>();
|
||||
|
||||
const feedbackSummary = ref<FeedbackDisplaySummary[]>([]);
|
||||
let feedbackData: FeedbackSummary[] = [];
|
||||
|
||||
onMounted(async () => {
|
||||
feedbackData = await itGet(`/api/core/feedback/${props.courseId}/summary`);
|
||||
feedbackSummary.value = makeSummary(feedbackData, props.circles, props.selctedCircles);
|
||||
})
|
||||
|
||||
watch(() => props, () => {
|
||||
if (feedbackData.length > 0 && props.circles.length > 0 && props.selctedCircles.length > 0) {
|
||||
feedbackSummary.value = makeSummary(feedbackData, props.circles, props.selctedCircles);
|
||||
}
|
||||
}, { deep: true });
|
||||
</script>
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { Dialog, DialogDescription, DialogPanel, DialogTitle } from "@headlessui/vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import ItRow from "@/components/ui/ItRow.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
avatarUrl: string;
|
||||
name: string;
|
||||
|
|
@ -6,20 +8,18 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<li
|
||||
class="flex flex-col justify-between border-t border-gray-500 py-4 leading-[45px] lg:flex-row"
|
||||
>
|
||||
<div class="flex flex-row items-center md:w-1/4">
|
||||
<img class="mr-2 h-[45px] rounded-full" :src="avatarUrl" />
|
||||
<ItRow>
|
||||
<template #firstRow>
|
||||
<img class="h-[45px] rounded-full mr-2" :src="avatarUrl" />
|
||||
<p class="text-bold lg:leading-[45px]">{{ name }}</p>
|
||||
</div>
|
||||
<div class="flex flex-1 items-center">
|
||||
</template>
|
||||
<template #center>
|
||||
<slot name="center"></slot>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
</template>
|
||||
<template #link>
|
||||
<slot name="link"></slot>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
</ItRow>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<li
|
||||
class="py-4 leading-[45px] border-t border-gray-500 flex flex-col lg:flex-row justify-between"
|
||||
>
|
||||
<div class="flex flex-row md:w-1/4 items-center">
|
||||
<slot name="firstRow"></slot>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center">
|
||||
<slot name="center"></slot>
|
||||
</div>
|
||||
<div class="flex items-center md:w-1/4">
|
||||
<slot name="link"></slot>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -131,7 +131,10 @@
|
|||
"completionTitle": "Schicke dein Feedback an {name}",
|
||||
"completionDescription": "Dein Feedback ist anonym. Dein Vor- und Nachname werden bei deiner Trainer/-in nicht angezeigt.",
|
||||
"sendFeedback": "Feedback abschicken",
|
||||
"feedbackSent": "Dein Feedback wurde abgeschickt"
|
||||
"feedbackSent": "Dein Feedback wurde abgeschickt",
|
||||
"circleFeedback": "Feedback zum Circle",
|
||||
"showDetails": "Details anzeigen",
|
||||
"sentByUsers": "Von {count} Teilnehmern ausgefüllt"
|
||||
},
|
||||
"constants": {
|
||||
"yes": "Ja",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||
import ItPersonRow from "@/components/ui/ItPersonRow.vue";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import FeedbackSummary from "@/components/feedback/feedbackSummary.vue";
|
||||
import type { LearningPath } from "@/services/learningPath";
|
||||
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
|
|
@ -10,6 +11,7 @@ import { useLearningPathStore } from "@/stores/learningPath";
|
|||
import { useUserStore } from "@/stores/user";
|
||||
import log from "loglevel";
|
||||
import { computed } from "vue";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -21,6 +23,7 @@ const userStore = useUserStore();
|
|||
const cockpitStore = useCockpitStore();
|
||||
const competenceStore = useCompetenceStore();
|
||||
const learningPathStore = useLearningPathStore();
|
||||
const courseSessionStore = useCourseSessionsStore();
|
||||
|
||||
function userCountStatus(userId: number) {
|
||||
return competenceStore.calcStatusCount(
|
||||
|
|
@ -92,8 +95,8 @@ function setActiveClasses(translationKey: string) {
|
|||
</ul>
|
||||
</div>
|
||||
<!-- Status -->
|
||||
<div class="mb-4 grid grid-rows-3 gap-4 lg:grid-cols-3 lg:grid-rows-none">
|
||||
<div class="bg-white px-6 py-5">
|
||||
<div class="grid gap-4 grid-rows-2 lg:grid-rows-none lg:grid-cols-2 mb-4">
|
||||
<div class="px-6 py-5 bg-white">
|
||||
<h1
|
||||
class="heading-3 mb-4 bg-assignment bg-60 bg-no-repeat pl-[68px] leading-[60px]"
|
||||
>
|
||||
|
|
@ -106,18 +109,7 @@ function setActiveClasses(translationKey: string) {
|
|||
</div>
|
||||
<div class="bg-white px-6 py-5">
|
||||
<h1
|
||||
class="heading-3 mb-4 bg-feedback bg-60 bg-no-repeat pl-[68px] leading-[60px]"
|
||||
>
|
||||
{{ $t("general.feedback", 2) }}
|
||||
</h1>
|
||||
<div class="mb-4">
|
||||
<ItProgress :status-count="data.transferProgress"></ItProgress>
|
||||
</div>
|
||||
<p>{{ $t("cockpit.feedbacksDone") }}</p>
|
||||
</div>
|
||||
<div class="bg-white px-6 py-5">
|
||||
<h1
|
||||
class="heading-3 mb-4 bg-test bg-60 bg-no-repeat pl-[68px] leading-[60px]"
|
||||
class="bg-test bg-no-repeat pl-[68px] heading-3 bg-60 leading-[60px] mb-4"
|
||||
>
|
||||
{{ $t("general.examResult", 2) }}
|
||||
</h1>
|
||||
|
|
@ -127,6 +119,13 @@ function setActiveClasses(translationKey: string) {
|
|||
<p>{{ $t("cockpit.examsDone") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Feedback -->
|
||||
<FeedbackSummary
|
||||
:selcted-circles="cockpitStore.selectedCircles || []"
|
||||
:circles="learningPathStore.learningPathForUser(props.courseSlug, userStore.id)?.circles || []"
|
||||
:course-id="courseSessionStore.courseSessionForRoute?.course.id"
|
||||
:url="courseSessionStore.courseSessionForRoute.course_url"
|
||||
></FeedbackSummary>
|
||||
<div>
|
||||
<!-- progress -->
|
||||
<div v-if="cockpitStore.courseSessionUsers?.length" class="bg-white p-6">
|
||||
|
|
@ -140,7 +139,7 @@ function setActiveClasses(translationKey: string) {
|
|||
>
|
||||
<template #center>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="flex flex-row items-center gap-4">
|
||||
<div class="flex flex-row items-center gap-4 mr-6">
|
||||
<div class="h-12">
|
||||
<LearningPathDiagram
|
||||
v-if="
|
||||
|
|
@ -198,6 +197,7 @@ function setActiveClasses(translationKey: string) {
|
|||
<template #link>
|
||||
<router-link
|
||||
:to="`/course/${props.courseSlug}/cockpit/profile/${csu.user_id}`"
|
||||
class="underline w-full text-right"
|
||||
>
|
||||
{{ $t("general.profileLink") }}
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ from django.urls import include, path, re_path
|
|||
from django.views import defaults as default_views
|
||||
from grapple import urls as grapple_urls
|
||||
from ratelimit.exceptions import Ratelimited
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
|
||||
from vbv_lernwelt.core.views import (
|
||||
|
|
@ -35,7 +32,13 @@ from vbv_lernwelt.course.views import (
|
|||
request_course_completion,
|
||||
request_course_completion_for_user,
|
||||
)
|
||||
from vbv_lernwelt.feedback.views import get_expert_feedbacks_for_course, get_feedback_for_circle
|
||||
from vbv_lernwelt.feedback.views import (
|
||||
get_expert_feedbacks_for_course,
|
||||
get_feedback_for_circle,
|
||||
)
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
|
||||
def raise_example_error(request):
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ from vbv_lernwelt.course.management.commands.create_uk_course import (
|
|||
)
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course.services import mark_course_completion
|
||||
from vbv_lernwelt.feedback.creators.create_demo_feedback import create_feedback
|
||||
from vbv_lernwelt.learnpath.create_vv_learning_path import create_vv_learning_path
|
||||
from vbv_lernwelt.learnpath.create_vv_new_learning_path import (
|
||||
create_vv_new_learning_path,
|
||||
|
|
@ -120,6 +121,7 @@ def command():
|
|||
for i, circle in enumerate(circles):
|
||||
expert = experts[i % len(experts)]
|
||||
expert.expert.add(circle)
|
||||
create_feedback(circle, cs, 10)
|
||||
|
||||
# course session Versicherungsvermittler/in (neu)
|
||||
# course session Versicherungsvermittler/in
|
||||
|
|
@ -192,6 +194,18 @@ def command():
|
|||
user=User.objects.get(username="evelyn.schmid@example.com"),
|
||||
)
|
||||
|
||||
create_feedback(
|
||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-kickoff"), cs, 10
|
||||
)
|
||||
create_feedback(
|
||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-haushalt-teil-2"),
|
||||
cs,
|
||||
14,
|
||||
)
|
||||
create_feedback(
|
||||
Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-basis"), cs, 12
|
||||
)
|
||||
|
||||
# course session Überbetriebliche Kurse Lehrjahr 1 - Region Zürich
|
||||
cs = CourseSession.objects.create(
|
||||
course_id=COURSE_UK1,
|
||||
|
|
|
|||
|
|
@ -191,6 +191,7 @@ class DocumentUploadApiTestCase(APITestCase):
|
|||
response = self.client.delete(f"/api/core/document/{document.id}/")
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
|
||||
# expert cannot upload in other course
|
||||
# expert cannot delete other upload
|
||||
# exper cannot change course
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.feedback.factories import FeedbackFactory
|
||||
from vbv_lernwelt.learnpath.models import Circle
|
||||
|
||||
|
||||
def create_feedback(circle: Circle, course_session: CourseSession, amount: int):
|
||||
for _i in range(amount):
|
||||
FeedbackFactory(circle=circle, course_session=course_session).save()
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from factory.django import DjangoModelFactory
|
||||
from factory.fuzzy import FuzzyChoice, FuzzyInteger
|
||||
|
||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||
|
||||
|
|
@ -6,3 +7,31 @@ from vbv_lernwelt.feedback.models import FeedbackResponse
|
|||
class FeedbackFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = FeedbackResponse
|
||||
|
||||
satisfaction = FuzzyInteger(1, 4)
|
||||
goal_attainment = FuzzyInteger(1, 4)
|
||||
proficiency = FuzzyChoice([20, 40, 60, 80])
|
||||
received_materials = FuzzyChoice([True, False])
|
||||
materials_rating = FuzzyInteger(1, 4)
|
||||
instructor_competence = FuzzyInteger(1, 4)
|
||||
instructor_respect = FuzzyInteger(1, 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 veranschaulicht!",
|
||||
]
|
||||
)
|
||||
course_negative_feedback = FuzzyChoice(
|
||||
[
|
||||
"Es wäre praktisch, Zugang zu einer FAQ zu haben.",
|
||||
"Es wäre schön, mehr Videos hinzuzufügen.",
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ class FeedbackApiBaseTestCase(APITestCase):
|
|||
|
||||
|
||||
class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
||||
|
||||
def test_can_get_feedback_summary_for_circles(self):
|
||||
number_basis_feedback = 5
|
||||
number_analyse_feedback = 10
|
||||
|
|
@ -65,18 +64,24 @@ class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
|||
csu.expert.add(basis_circle)
|
||||
|
||||
for i in range(number_basis_feedback):
|
||||
FeedbackFactory(circle=basis_circle, course_session=csu.course_session).save()
|
||||
FeedbackFactory(
|
||||
circle=basis_circle, course_session=csu.course_session
|
||||
).save()
|
||||
|
||||
for i in range(number_analyse_feedback):
|
||||
FeedbackFactory(circle=analyse_circle, course_session=csu.course_session).save()
|
||||
FeedbackFactory(
|
||||
circle=analyse_circle, course_session=csu.course_session
|
||||
).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/summary/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/summary/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected = {
|
||||
analyse_circle.id: {"circle_id": analyse_circle.id, "count": number_analyse_feedback},
|
||||
basis_circle.id: {"circle_id": basis_circle.id, "count": number_basis_feedback},
|
||||
}
|
||||
expected = [
|
||||
{"circle_id": basis_circle.id, "count": number_basis_feedback},
|
||||
{"circle_id": analyse_circle.id, "count": number_analyse_feedback},
|
||||
]
|
||||
self.assertEqual(response.data, expected)
|
||||
|
||||
def test_can_only_see_feedback_from_own_circle(self):
|
||||
|
|
@ -92,17 +97,23 @@ class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
|||
basis_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-basis")
|
||||
|
||||
for i in range(number_basis_feedback):
|
||||
FeedbackFactory(circle=basis_circle, course_session=csu.course_session).save()
|
||||
FeedbackFactory(
|
||||
circle=basis_circle, course_session=csu.course_session
|
||||
).save()
|
||||
|
||||
for i in range(number_analyse_feedback):
|
||||
FeedbackFactory(circle=analyse_circle, course_session=csu.course_session).save()
|
||||
FeedbackFactory(
|
||||
circle=analyse_circle, course_session=csu.course_session
|
||||
).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/summary/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/summary/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
expected = {
|
||||
analyse_circle.id: {"circle_id": analyse_circle.id, "count": number_analyse_feedback},
|
||||
}
|
||||
expected = [
|
||||
{"circle_id": analyse_circle.id, "count": number_analyse_feedback},
|
||||
]
|
||||
self.assertEqual(response.data, expected)
|
||||
|
||||
def test_student_does_not_see_feedback(self):
|
||||
|
|
@ -114,10 +125,12 @@ class FeedbackSummaryApiTestCase(FeedbackApiBaseTestCase):
|
|||
analyse_circle = Circle.objects.get(slug="test-lehrgang-lp-circle-analyse")
|
||||
FeedbackFactory(circle=analyse_circle, course_session=csu.course_session).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/summary/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/summary/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, {})
|
||||
self.assertEqual(response.data, [])
|
||||
|
||||
|
||||
class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
||||
|
|
@ -157,7 +170,9 @@ class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
|||
course_negative_feedback=expected["course_negative_feedback"][i],
|
||||
).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, expected)
|
||||
|
|
@ -172,7 +187,9 @@ class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
|||
circle = Circle.objects.get(slug="test-lehrgang-lp-circle-basis")
|
||||
FeedbackFactory(circle=circle, course_session=csu.course_session).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, {})
|
||||
|
|
@ -186,7 +203,9 @@ class FeedbackDetailApiTestCase(FeedbackApiBaseTestCase):
|
|||
circle = Circle.objects.get(slug="test-lehrgang-lp-circle-analyse")
|
||||
FeedbackFactory(circle=circle, course_session=csu.course_session).save()
|
||||
|
||||
response = self.client.get(f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/")
|
||||
response = self.client.get(
|
||||
f"/api/core/feedback/{csu.course_session.course.id}/{circle.id}/"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, {})
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import itertools
|
||||
|
||||
import structlog
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
|
|
@ -22,26 +24,31 @@ FEEDBACK_FIELDS = [
|
|||
|
||||
@api_view(["GET"])
|
||||
def get_expert_feedbacks_for_course(request, course_id):
|
||||
feedbacks = FeedbackResponse.objects.filter(course_session__course_id=course_id, circle__expert__user=request.user)
|
||||
circle_count = {}
|
||||
feedbacks = FeedbackResponse.objects.filter(
|
||||
course_session__course_id=course_id, circle__expert__user=request.user
|
||||
).order_by("circle_id")
|
||||
circle_count = []
|
||||
|
||||
for feedback in feedbacks:
|
||||
if feedback.circle_id not in circle_count:
|
||||
circle_count[feedback.circle_id] = {
|
||||
"circle_id": feedback.circle_id,
|
||||
"count": 0,
|
||||
grouped_feedbacks = itertools.groupby(feedbacks, lambda x: x.circle_id)
|
||||
|
||||
for key, feedbacks in grouped_feedbacks:
|
||||
circle_count.append(
|
||||
{
|
||||
"circle_id": key,
|
||||
"count": len(list(feedbacks)),
|
||||
}
|
||||
|
||||
circle_count[feedback.circle_id]["count"] += 1
|
||||
)
|
||||
|
||||
return Response(status=200, data=circle_count)
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
def get_feedback_for_circle(request, course_id, circle_id):
|
||||
feedbacks = FeedbackResponse.objects.filter(course_session__course_id=course_id,
|
||||
circle__expert__user=request.user,
|
||||
circle_id=circle_id)
|
||||
feedbacks = FeedbackResponse.objects.filter(
|
||||
course_session__course_id=course_id,
|
||||
circle__expert__user=request.user,
|
||||
circle_id=circle_id,
|
||||
)
|
||||
|
||||
# I guess this is ok for the üK case
|
||||
feedback_data = {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue