fix: test case with no input issue
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog v-model="show" :options="{ size: '2xl' }">
|
||||
<Dialog v-model="show" :options="{ size: '5xl' }">
|
||||
<template #body-title>
|
||||
<div class="text-xl font-semibold text-ink-gray-9">
|
||||
{{
|
||||
@@ -10,41 +10,45 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #body-content>
|
||||
<div class="space-y-4">
|
||||
<FormControl
|
||||
v-model="exercise.title"
|
||||
:label="__('Title')"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="exercise.language"
|
||||
:label="__('Language')"
|
||||
type="select"
|
||||
:options="languageOptions"
|
||||
:required="true"
|
||||
/>
|
||||
<div>
|
||||
<div class="text-xs text-ink-gray-5 mb-2">
|
||||
{{ __('Problem Statement') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<TextEditor
|
||||
:content="exercise.problem_statement"
|
||||
@change="(val: string) => (exercise.problem_statement = val)"
|
||||
<div class="grid grid-cols-2 gap-10">
|
||||
<div class="space-y-4">
|
||||
<FormControl
|
||||
v-model="exercise.title"
|
||||
:label="__('Title')"
|
||||
:required="true"
|
||||
/>
|
||||
<FormControl
|
||||
v-model="exercise.language"
|
||||
:label="__('Language')"
|
||||
type="select"
|
||||
:options="languageOptions"
|
||||
:required="true"
|
||||
/>
|
||||
<ChildTable
|
||||
v-model="exercise.test_cases"
|
||||
:columns="testCaseColumns"
|
||||
:required="true"
|
||||
:addable="true"
|
||||
:deletable="true"
|
||||
:editable="true"
|
||||
:fixedMenu="true"
|
||||
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[18rem] overflow-y-auto"
|
||||
:placeholder="__('Add Test Case')"
|
||||
/>
|
||||
</div>
|
||||
<ChildTable
|
||||
v-model="exercise.test_cases"
|
||||
:columns="testCaseColumns"
|
||||
:required="true"
|
||||
:addable="true"
|
||||
:deletable="true"
|
||||
:editable="true"
|
||||
:placeholder="__('Add Test Case')"
|
||||
/>
|
||||
<div>
|
||||
<div>
|
||||
<div class="text-xs text-ink-gray-5 mb-2">
|
||||
{{ __('Problem Statement') }}
|
||||
<span class="text-ink-red-3">*</span>
|
||||
</div>
|
||||
<TextEditor
|
||||
:content="exercise.problem_statement"
|
||||
@change="(val: string) => (exercise.problem_statement = val)"
|
||||
:editable="true"
|
||||
:fixedMenu="true"
|
||||
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[21rem] overflow-y-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ close }">
|
||||
@@ -65,7 +69,9 @@
|
||||
<router-link
|
||||
:to="{
|
||||
name: 'ProgrammingExerciseSubmissions',
|
||||
params: { exerciseID: props.exerciseID },
|
||||
query: {
|
||||
exercise: props.exerciseID,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<Button>
|
||||
@@ -105,12 +111,14 @@ const exercises = defineModel<{
|
||||
) => void
|
||||
}
|
||||
}>('exercises')
|
||||
|
||||
const exercise = ref<ProgrammingExercise>({
|
||||
title: '',
|
||||
language: 'Python',
|
||||
problem_statement: '',
|
||||
test_cases: [],
|
||||
})
|
||||
|
||||
const languageOptions = [
|
||||
{ label: 'Python', value: 'Python' },
|
||||
{ label: 'JavaScript', value: 'JavaScript' },
|
||||
@@ -159,6 +167,8 @@ const fetchTestCases = () => {
|
||||
testCases.update({
|
||||
filters: {
|
||||
parent: props.exerciseID,
|
||||
parenttype: 'LMS Programming Exercise',
|
||||
parentfield: 'test_cases',
|
||||
},
|
||||
})
|
||||
testCases.reload()
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
class="ProseMirror prose prose-table:table-fixed prose-td:p-2 prose-th:p-2 prose-td:border prose-th:border prose-td:border-outline-gray-2 prose-th:border-outline-gray-2 prose-td:relative prose-th:relative prose-th:bg-surface-gray-2 prose-sm max-w-none !whitespace-normal border-r px-5 py-2 h-full"
|
||||
></div>
|
||||
<div>
|
||||
<div class="flex items-center justify-between p-2 bg-surface-gray-1">
|
||||
<div class="flex items-center justify-between p-2 bg-surface-gray-2">
|
||||
<div class="font-semibold">
|
||||
{{ exercise.doc?.language }}
|
||||
</div>
|
||||
@@ -38,7 +38,7 @@
|
||||
maxHeight="1000px"
|
||||
/>
|
||||
<span v-if="error" class="text-xs text-ink-gray-5 px-2">
|
||||
{{ __('Compiler Error') }}:
|
||||
{{ __('Compiler Message') }}:
|
||||
</span>
|
||||
<textarea
|
||||
v-if="error"
|
||||
@@ -48,6 +48,7 @@
|
||||
/>
|
||||
<!-- <textarea v-else v-model="output" class="bg-surface-gray-1 border-none text-sm h-28 leading-6" readonly /> -->
|
||||
</div>
|
||||
|
||||
<div ref="testCaseSection" v-if="testCases.length" class="p-3">
|
||||
<span class="text-lg font-semibold text-ink-gray-9">
|
||||
{{ __('Test Cases') }}
|
||||
@@ -132,7 +133,7 @@ const testCases = ref<
|
||||
}>
|
||||
>([])
|
||||
const boilerplate = ref<string>(
|
||||
`with open("stdin", "r") as f:\n data = f.read()\n\ninputs = data.split()\n\n# inputs is a list of strings\n# write your code below\n\n`
|
||||
`with open("stdin", "r") as f:\n data = f.read()\n\ninputs = data.split() if len(data) else []\n\n# inputs is a list of strings\n# write your code below\n\n`
|
||||
)
|
||||
const { brand } = sessionStore()
|
||||
const router = useRouter()
|
||||
@@ -179,7 +180,9 @@ watch(
|
||||
(doc) => {
|
||||
if (doc) {
|
||||
code.value = `${boilerplate.value}${doc.code || ''}\n`
|
||||
testCases.value = doc.test_cases || []
|
||||
if (testCases.value.length === 0) {
|
||||
testCases.value = doc.test_cases || []
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
@@ -207,6 +210,7 @@ const runCode = async () => {
|
||||
if (testCaseSection.value) {
|
||||
testCaseSection.value.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
|
||||
for (const test_case of exercise.doc.test_cases) {
|
||||
let result = await execute(test_case.input)
|
||||
if (error.value) {
|
||||
@@ -217,7 +221,6 @@ const runCode = async () => {
|
||||
}
|
||||
let status =
|
||||
result.trim() === test_case.expected_output.trim() ? 'Passed' : 'Failed'
|
||||
|
||||
testCases.value.push({
|
||||
input: test_case.input,
|
||||
output: result,
|
||||
@@ -256,41 +259,57 @@ const createSubmission = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const execute = async (stdin = '') => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const execute = (stdin = ''): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let outputChunks: string[] = []
|
||||
let hasExited = false
|
||||
let hasError = false
|
||||
|
||||
const session = new LiveCodeSession({
|
||||
let session = new LiveCodeSession({
|
||||
base_url: 'https://falcon.frappe.io',
|
||||
runtime: 'python',
|
||||
code: code.value,
|
||||
files: [{ filename: 'stdin', contents: stdin }],
|
||||
onMessage: (msg: any) => {
|
||||
console.log('msg', msg)
|
||||
|
||||
if (msg.msgtype === 'write' && msg.file === 'stdout') {
|
||||
outputChunks.push(msg.data)
|
||||
}
|
||||
|
||||
if (msg.msgtype == 'exitstatus') {
|
||||
if (msg.exitstatus != 0) {
|
||||
if (msg.msgtype === 'write' && msg.file === 'stderr') {
|
||||
hasError = true
|
||||
errorMessage.value = msg.data
|
||||
}
|
||||
|
||||
if (msg.msgtype === 'exitstatus') {
|
||||
hasExited = true
|
||||
if (msg.exitstatus !== 0) {
|
||||
error.value = true
|
||||
} else {
|
||||
error.value = false
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.msgtype === 'exitstatus') {
|
||||
resolve(outputChunks.join(''))
|
||||
resolve(outputChunks.join('').trim())
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => reject('Execution timed out.'), 10000)
|
||||
setTimeout(() => {
|
||||
if (!hasExited) {
|
||||
error.value = true
|
||||
errorMessage.value = 'Execution timed out.'
|
||||
reject('Execution timed out.')
|
||||
}
|
||||
}, 20000)
|
||||
})
|
||||
}
|
||||
|
||||
const breadcrumbs = computed(() => {
|
||||
return [
|
||||
{ label: __('Programming Exercises') },
|
||||
{
|
||||
label: __('Programming Exercise Submissions'),
|
||||
route: { name: 'ProgrammingExerciseSubmissions' },
|
||||
},
|
||||
{ label: exercise.doc?.title },
|
||||
]
|
||||
})
|
||||
|
||||
@@ -129,18 +129,20 @@ import {
|
||||
ListRowItem,
|
||||
usePageMeta,
|
||||
} from 'frappe-ui'
|
||||
import { computed, inject, ref, watch } from 'vue'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import type {
|
||||
ProgrammingExerciseSubmission,
|
||||
Filters,
|
||||
} from '@/pages/ProgrammingExercises/types'
|
||||
import { computed, inject, onMounted, ref, watch } from 'vue'
|
||||
import { sessionStore } from '@/stores/session'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Link from '@/components/Controls/Link.vue'
|
||||
import EmptyState from '@/components/EmptyState.vue'
|
||||
|
||||
const { brand } = sessionStore()
|
||||
const dayjs = inject('$dayjs') as any
|
||||
const user = inject('$user') as any
|
||||
const filterFields = ['exercise', 'member', 'status']
|
||||
const filters = ref<Filters>({
|
||||
exercise: '',
|
||||
member: '',
|
||||
@@ -148,6 +150,19 @@ const filters = ref<Filters>({
|
||||
})
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
if (!user.data?.is_instructor && !user.data?.is_moderator) {
|
||||
router.push({ name: 'Courses' })
|
||||
}
|
||||
filterFields.forEach((field) => {
|
||||
if (router.currentRoute.value.query[field]) {
|
||||
filters.value[field as keyof Filters] = router.currentRoute.value.query[
|
||||
field
|
||||
] as string
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const submissions = createListResource({
|
||||
doctype: 'LMS Programming Exercise Submission',
|
||||
fields: [
|
||||
@@ -172,7 +187,6 @@ const submissions = createListResource({
|
||||
|
||||
watch(filters.value, () => {
|
||||
let filtersToApply: Record<string, any> = {}
|
||||
const filterFields = ['exercise', 'member', 'status']
|
||||
filterFields.forEach((field) => {
|
||||
if (filters.value[field as keyof Filters]) {
|
||||
filtersToApply[field] = filters.value[field as keyof Filters]
|
||||
@@ -211,7 +225,7 @@ const submissionColumns = computed(() => {
|
||||
{
|
||||
label: __('Exercise'),
|
||||
key: 'exercise_title',
|
||||
width: '40%',
|
||||
width: '30%',
|
||||
icon: 'code',
|
||||
},
|
||||
{
|
||||
@@ -225,6 +239,7 @@ const submissionColumns = computed(() => {
|
||||
key: 'modified',
|
||||
width: '20%',
|
||||
icon: 'clock',
|
||||
align: 'right',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
showForm = true
|
||||
}
|
||||
"
|
||||
class="flex flex-col border rounded-md p-3 h-full hover:border-outline-gray-3 space-y-2"
|
||||
class="flex flex-col border rounded-md p-3 h-full hover:border-outline-gray-3 space-y-2 cursor-pointer"
|
||||
>
|
||||
<div class="text-lg font-semibold text-ink-gray-9">
|
||||
{{ exercise.title }}
|
||||
|
||||
Reference in New Issue
Block a user