diff --git a/backend/UniVerse.Application/Prompts/ReviewPromptTemplate.cs b/backend/UniVerse.Application/Prompts/ReviewPromptTemplate.cs index db01367..8c1d2ea 100644 --- a/backend/UniVerse.Application/Prompts/ReviewPromptTemplate.cs +++ b/backend/UniVerse.Application/Prompts/ReviewPromptTemplate.cs @@ -6,11 +6,37 @@ public static class ReviewPromptTemplate public const string ReviewTextPlaceholder = "{reviewText}"; public const string Default = """ - Проанализируй отзыв студента о лекции. Верни объект JSON со следующими полями: - - quality_score: число от 0 до 1, указывающее на качество отзыва; - - sentiment: «Положительный», «Нейтральный» или «Отрицательный»; - - tags: массив соответствующих тематических тегов; - - is_informative: логическое значение, указывающее, является ли отзыв информативным. + Проанализируй отзыв студента о лекции. Главная задача - определить, насколько отзыв информативен и полезен для аналитики качества лекции и обратной связи преподавателю. + + Верни только валидный JSON-объект без Markdown, пояснений и дополнительного текста: + { + "quality_score": 0.0, + "sentiment": "Нейтральный", + "tags": [], + "is_informative": false + } + + Правила оценки: + - quality_score: число от 0 до 1. Оценивай содержательность, конкретику, аргументацию, конструктивность и развернутость отзыва, а не оценку лекции как таковой. + - is_informative: true, если отзыв содержит конкретные наблюдения о лекции, преподавании, структуре, материалах, темпе, сложности, практике, организации или полезности. false для односложных, шаблонных, эмоциональных без конкретики или нерелевантных отзывов. + - sentiment: строго одно из значений "Положительный", "Нейтральный", "Отрицательный". + - tags: массив коротких тематических тегов на русском языке. Используй 1-5 тегов, если они подходят; для неинформативного отзыва можно вернуть пустой массив. + + Базовые теги: + - "структура лекции" + - "понятность объяснения" + - "темп" + - "сложность" + - "практические примеры" + - "материалы" + - "актуальность темы" + - "вовлеченность" + - "организация" + - "технические проблемы" + - "польза для обучения" + - "неинформативный отзыв" + + Можно добавлять новые теги, если они точнее отражают содержание отзыва. Не добавляй теги, которых нет в тексте отзыва или контексте лекции. Контекст лекции: {lectureContext} Текст отзыва: {reviewText} diff --git a/frontend/src/views/teacher/TeacherAnalyticsView.vue b/frontend/src/views/teacher/TeacherAnalyticsView.vue index bbf4798..ea8aba3 100644 --- a/frontend/src/views/teacher/TeacherAnalyticsView.vue +++ b/frontend/src/views/teacher/TeacherAnalyticsView.vue @@ -9,7 +9,6 @@ import { mapApiReview } from '@/api/mappers' import { useLecturesStore } from '@/stores/lectures' import { useAuthStore } from '@/stores/auth' -const ratingTrend = [4.2, 4.5, 4.6, 4.8, 4.7] const lecturesStore = useLecturesStore() const auth = useAuthStore() const reviews = ref([]) @@ -17,8 +16,9 @@ const reviews = ref([]) const positive = computed(() => reviews.value.filter((r) => r.sentiment === 'positive').length) const neutral = computed(() => reviews.value.filter((r) => r.sentiment === 'neutral').length) const negative = computed(() => reviews.value.filter((r) => r.sentiment === 'negative').length) -const total = computed(() => reviews.value.length || 1) -const pct = (value: number) => Math.round((value / total.value) * 100) +const total = computed(() => reviews.value.length) +const pct = (value: number) => (total.value ? Math.round((value / total.value) * 100) : 0) +const ratio = (value: number) => `${value}/${total.value}` async function fetchTeacherAnalytics() { if (!auth.user?.id) return @@ -39,37 +39,28 @@ watch(() => auth.user?.id, fetchTeacherAnalytics)

Аналитика преподавателя

- -
Динамика оценок
-
-
-
- Нед {{ i + 1 }} -
-
-
Средняя оценка: 4.6
-
-
Sentiment-анализ отзывов
-
Позитивные {{ pct(positive) }}%
- +
Позитивные {{ ratio(positive) }}
+
-
Нейтральные {{ pct(neutral) }}%
+
Нейтральные {{ ratio(neutral) }}
-
Негативные {{ pct(negative) }}%
+
Негативные {{ ratio(negative) }}
@@ -117,32 +108,6 @@ watch(() => auth.user?.id, fetchTeacherAnalytics) grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; } -.chart { - display: flex; - gap: 12px; - align-items: flex-end; - height: 160px; - padding: 10px 0; -} -.bar { - display: flex; - flex-direction: column; - align-items: center; - gap: 6px; -} -.bar-fill { - width: 26px; - border-radius: 6px 6px 0 0; - background: linear-gradient(180deg, #22c55e, #86efac); -} -.bar-label { - font-size: 11px; - color: var(--color-text-secondary); -} -.avg { - margin-top: 6px; - font-weight: 600; -} .sentiment { display: flex; flex-direction: column; diff --git a/frontend/src/views/teacher/TeacherDashboardView.vue b/frontend/src/views/teacher/TeacherDashboardView.vue index 778eec1..244c08b 100644 --- a/frontend/src/views/teacher/TeacherDashboardView.vue +++ b/frontend/src/views/teacher/TeacherDashboardView.vue @@ -20,9 +20,6 @@ const upcoming = computed(() => const enrolledTotal = computed(() => teacherLectures.value.reduce((sum, l) => sum + l.enrolledSeats, 0), ) -const visibility = computed(() => - teacherLectures.value.length ? Math.min(100, Math.round(enrolledTotal.value * 4)) : 0, -) function fetchTeacherLectures() { if (!auth.user?.id) return @@ -48,13 +45,7 @@ watch(() => auth.user?.id, fetchTeacherLectures)
- - +