diff --git a/src/lib/components/Apps/components/Poll/index.svelte b/src/lib/components/Apps/components/Poll/index.svelte
index 853b0723..c9031615 100644
--- a/src/lib/components/Apps/components/Poll/index.svelte
+++ b/src/lib/components/Apps/components/Poll/index.svelte
@@ -155,7 +155,7 @@
-
+
{#if shouldCreatePoll}
{:else}
diff --git a/src/lib/components/Apps/index.svelte b/src/lib/components/Apps/index.svelte
index 3bf3b9b0..e3288b44 100644
--- a/src/lib/components/Apps/index.svelte
+++ b/src/lib/components/Apps/index.svelte
@@ -1,9 +1,7 @@
@@ -178,21 +98,32 @@
-
{#if !!$apps.selectedApp}
- {#if $apps.selectedApp === APPS_CONSTANTS.APPS.QANDA}
-
- {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.COMMENTS}
-
- {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.POLL}
-
- {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.NOTES}
-
- {/if}
+ {#key $apps.selectedApp}
+
+ {#if $apps.selectedApp === APPS_CONSTANTS.APPS.QANDA}
+
+ {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.COMMENTS}
+
+ {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.POLL}
+
+ {:else if $apps.selectedApp === APPS_CONSTANTS.APPS.NOTES}
+
+ {/if}
+
+ {/key}
{/if}
diff --git a/src/lib/components/Course/components/Lesson/Exercises/NewExerciseModal.svelte b/src/lib/components/Course/components/Lesson/Exercises/NewExerciseModal.svelte
index 9a921205..8df0b366 100644
--- a/src/lib/components/Course/components/Lesson/Exercises/NewExerciseModal.svelte
+++ b/src/lib/components/Course/components/Lesson/Exercises/NewExerciseModal.svelte
@@ -8,11 +8,19 @@
import { Tag } from 'carbon-components-svelte';
import { TEMPLATES, TAGS } from '$lib/mocks';
import type { ExerciseTemplate } from '$lib/utils/types';
+ import { lesson } from '../store/lessons';
+ import { useCompletion } from 'ai/svelte';
+ import Confetti from '$lib/components/Confetti/index.svelte';
+ import { toggleConfetti } from '$lib/components/Confetti/store';
+ import { VARIANTS } from '$lib/components/PrimaryButton/constants';
+ import { getTextFromHTML } from '$lib/utils/functions/course';
+ import { writable } from 'svelte/store';
+ import { Circle3 } from 'svelte-loading-spinners';
export let open = false;
export let handleAddExercise = () => {};
export let handleCancelAddExercise = () => {};
- export let handleTemplateCreate = (template: ExerciseTemplate) => Promise
;
+ export let handleTemplateCreate: (template: ExerciseTemplate) => Promise;
export let title = '';
enum Type {
@@ -23,6 +31,11 @@
let step = 0;
let type: Type = Type.SCRATCH;
+ let questionNumber = 5;
+ let optionNumber = 5;
+ let isLoading = writable(false);
+ let isAIStarted = false;
+ let note = '';
const options = [
{
@@ -41,7 +54,7 @@
title: 'Use AI',
subtitle: 'You can generate an exercise with AI from your notes',
type: Type.AI,
- isDisabled: true
+ isDisabled: false
}
];
@@ -50,9 +63,43 @@
let selectedTemplateId = '';
let isTemplateFinishedLoading = false;
+ const { input, handleSubmit, completion } = useCompletion({
+ api: '/api/completion/exerciseprompt',
+ onFinish: async (prompt: string, completion: string) => {
+ $isLoading = false;
+
+ if (!$lesson.id) return;
+
+ toggleConfetti();
+ const template: ExerciseTemplate = JSON.parse($completion);
+ await handleTemplateCreate(template);
+ toggleConfetti();
+ }
+ });
+
function handleNext() {
- step = 1;
+ step = step + 1;
}
+
+ function handleBack() {
+ step = step - 1;
+ }
+
+ function callAI() {
+ $input = JSON.stringify({
+ questionNumber,
+ optionNumber,
+ lessonNote: note
+ });
+
+ setTimeout(() => {
+ isAIStarted = true;
+ $isLoading = true;
+ handleSubmit({ preventDefault: () => {} });
+ }, 500);
+ }
+
+ $: note = getTextFromHTML($lesson?.materials?.note || '');
+ {#if !$isLoading && isAIStarted}
+
+ {/if}
{#if step === 0}
How do you want to create your exercise?
@@ -119,7 +169,13 @@
className="my-4"
/>
-
@@ -170,7 +226,13 @@
{/each}
-
+ {:else if type === Type.AI}
+
+
+
+ {#if note.length}
+
Create exercises from Notes with AI
+
+ Choose how many questions and options you want and AI will help you create an
+ exercise out of your note. Let's go.
+
+
+
+ {:else}
+
Please add a note to use this feature
+ {/if}
+
+
+
+ {#if $isLoading}
+
+ {:else if isAIStarted}
+
AI Generation Complete
+ {:else}
+
Click "Generate" for some magic
+ {/if}
+
+
+
+
{/if}
-
-
{/if}
diff --git a/src/lib/components/Course/components/Lesson/Exercises/index.svelte b/src/lib/components/Course/components/Lesson/Exercises/index.svelte
index d3146971..f734d5a7 100644
--- a/src/lib/components/Course/components/Lesson/Exercises/index.svelte
+++ b/src/lib/components/Course/components/Lesson/Exercises/index.svelte
@@ -30,21 +30,21 @@
description: ''
};
- async function handleTemplateCreate(template: ExerciseTemplate) {
+ async function handleTemplateCreate(template: ExerciseTemplate): Promise {
const newExercise = await createExerciseFromTemplate(lessonId, template);
console.log('newExercise', newExercise);
- if (!newExercise) return;
+ if (newExercise) {
+ lesson.update((_lesson) => ({
+ ..._lesson,
+ exercises: [..._lesson.exercises, newExercise]
+ }));
- lesson.update((_lesson) => ({
- ..._lesson,
- exercises: [..._lesson.exercises, newExercise]
- }));
-
- handleCancelAddExercise();
- goto(path + '/' + newExercise.id);
+ handleCancelAddExercise();
+ goto(path + '/' + newExercise.id);
+ }
}
+
async function handleAddExercise() {
- // createExerciseFromTemplate(lessonId);
const { data, error } = await createExercise({
title: newExercise.title,
lesson_id: lessonId
diff --git a/src/lib/components/Course/components/Lesson/Materials/index.svelte b/src/lib/components/Course/components/Lesson/Materials/index.svelte
index 90c12cc6..9c412e28 100644
--- a/src/lib/components/Course/components/Lesson/Materials/index.svelte
+++ b/src/lib/components/Course/components/Lesson/Materials/index.svelte
@@ -35,6 +35,7 @@
import ComponentVideo from './components/ComponentVideo.svelte';
import HtmlRender from '$lib/components/HTMLRender/HTMLRender.svelte';
import type { LessonPage } from '$lib/utils/types';
+ import { getTextFromHTML } from '$lib/utils/functions/course';
export let mode = MODES.view;
export let prevMode = '';
@@ -82,12 +83,7 @@
if (!document) return false;
- const dummyDiv = document.createElement('div');
- dummyDiv.innerHTML = note;
-
- const rawText = dummyDiv.textContent?.trim();
-
- return rawText === '';
+ return getTextFromHTML(note) === '';
}
function isMaterialsEmpty(materials: LessonPage['materials']) {
diff --git a/src/lib/utils/functions/course.ts b/src/lib/utils/functions/course.ts
index 1b3bbddd..fef3fdda 100644
--- a/src/lib/utils/functions/course.ts
+++ b/src/lib/utils/functions/course.ts
@@ -16,3 +16,10 @@ export const getStudentInviteLink = (_course: Course, orgSiteName: string) => {
return `https://${orgSiteName}.classroomio.com/invite/s/${hash}`;
};
+
+export const getTextFromHTML = (html: string): string => {
+ const dummyDiv = document.createElement('div');
+ dummyDiv.innerHTML = html;
+
+ return dummyDiv.textContent?.trim() || '';
+};
diff --git a/src/routes/api/completion/exerciseprompt/+server.js b/src/routes/api/completion/exerciseprompt/+server.js
new file mode 100644
index 00000000..7f09272c
--- /dev/null
+++ b/src/routes/api/completion/exerciseprompt/+server.js
@@ -0,0 +1,81 @@
+import { Configuration, OpenAIApi } from 'openai-edge';
+import { OpenAIStream, StreamingTextResponse } from 'ai';
+import { OPENAI_API_KEY } from '$env/static/private';
+
+// Create an OpenAI API client (that's edge friendly!)
+const openAIConfig = new Configuration({
+ apiKey: OPENAI_API_KEY
+});
+const openai = new OpenAIApi(openAIConfig);
+
+// Set the runtime to edge for best performance
+export const config = {
+ runtime: 'edge'
+};
+
+export async function POST({ request }) {
+ const { prompt } = await request.json();
+ const { questionNumber, optionNumber, lessonNote } = JSON.parse(prompt);
+
+ const refinedPrompt = `
+Please note this typescript interface:
+
+interface Question_type {
+ id: number;
+ label: any;
+}
+const QuestionTypes: Question_type[] = [
+ {
+ id: 1,
+ label: 'RADIO'
+ },
+ {
+ id: 2,
+ label: 'CHECKBOX'
+ },
+ {
+ id: 3,
+ label: 'TEXTAREA'
+ }
+];
+
+interface ExerciseTemplate {
+ title: string;
+ description: string;
+ questionnaire: {
+ questions: {
+ title: string;
+ name: string;
+ points: number; // a number out of 10 to score the student if student gets the correct answer
+ order: number; // a unique number from 0 to n+1, that orders the question in ascending order
+ question_type: Question_type;
+ options: {
+ label: string;
+ is_correct: boolean;
+ };
+ }[]
+ };
+}
+
+I am a teacher and I need your help evaluating my students after I have taught a lesson. Please generate a test with ${questionNumber} questions and ${optionNumber} options. The sourse of this test should come from my lesson note in thripple quotes: """${lessonNote}""". Format the quiz to match the ExerciseTemplate interface above and VERY IMPORTANTANTLY: ONLY RETURN JSON FORMAT AND DO NOT EXPLAIN ANTHING.
+NB: Ask a mixture of 10 DIFFERENT Question Types(RADIO, CHECKBOX AND TEXTAREA), mark the correct answer.`;
+
+ const response = await openai.createChatCompletion({
+ model: 'gpt-3.5-turbo',
+ messages: [
+ {
+ role: 'system',
+ content: 'You only spit out JSON and nothing else, dont give any explanation.'
+ },
+ {
+ role: 'user',
+ content: refinedPrompt
+ }
+ ],
+ stream: true
+ });
+ // Convert the response into a friendly text-stream
+ const stream = OpenAIStream(response);
+ // Respond with the stream
+ return new StreamingTextResponse(stream);
+}
diff --git a/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte b/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte
index 733d1f92..2c71ea56 100644
--- a/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte
+++ b/src/routes/courses/[id]/lessons/[...lessonParams]/+page.svelte
@@ -232,7 +232,7 @@
},
{
label: 'Exercises',
- badgeValue: $lesson.totalExercises,
+ badgeValue: $lesson.exercises.length,
isActive: !data.isMaterialsTabActive,
href: `${path}/exercises`
}