Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions codewit/api/src/controllers/attempt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import { AttemptWithEval } from '../typings/response.types';
import { EvaluationPayload, EvaluationResponse, executeCodeEvaluation } from '../utils/codeEvalService';
import { Language as LanguageEnum } from '@codewit/language';

function getEvaluationError(response: EvaluationResponse): string {
if (response.compilation_error) return response.compilation_error;
if (response.runtime_error) return response.runtime_error;
if (response.execution_time_exceeded) return 'Execution time exceeded';
if (response.memory_exceeded) return 'Memory limit exceeded';

const failureMessage = response.failure_details.find((detail) => detail.error_message)?.error_message;
if (failureMessage) return failureMessage;

return response.state === 'passed' ? '' : `Evaluation failed (${response.state})`;
}

async function createAttempt(
exerciseId: number,
Expand Down Expand Up @@ -75,12 +86,15 @@ async function createAttempt(
const response = await executeCodeEvaluation(evaluationPayload, cookies);
evalResponse = response;
console.log('Code evaluation response:', response);
const { tests_run, passed, error: eval_error } = response;
const { tests_run, passed } = response;
const evalError = getEvaluationError(response);

attempt.completionPercentage = 0;
attempt.error = evalError;

if (tests_run > 0) {
const completionPercentage = Math.round((passed / tests_run) * 100);
attempt.completionPercentage = completionPercentage;
attempt.error = eval_error;
console.log(`Completion Percentage: ${completionPercentage}%`);

// Update UserExerciseCompletion
Expand Down Expand Up @@ -166,10 +180,8 @@ async function createAttempt(
updatedModules.push({ moduleUid, completion: maxCompletion });
}
}

} else {
attempt.error = eval_error;
console.warn('Invalid response data for completion percentage calculation:', tests_run, passed, eval_error);
} else if (response.state === 'passed') {
console.warn('Code evaluation returned a passed state without runnable tests:', response);
}
} catch (err) {
console.error('Code evaluation failed:', err.message);
Expand Down
12 changes: 4 additions & 8 deletions codewit/api/src/utils/codeEvalService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createHttpClient, post } from '../utils/httpClient';
import type { FailureDetail, TestResult } from '@codewit/interfaces';

const codeEvalUrl = process.env.CODEEVAL_URL || 'http://codeeval:3002';
const httpClient = createHttpClient(codeEvalUrl);
Expand All @@ -10,17 +11,12 @@ export interface EvaluationPayload {
testCode: string;
}

export interface EvaluationResponse {
tests_run: number;
passed: number;
failed: number;
error: string;
state: string;
}
export type EvaluationFailureDetail = FailureDetail;
export type EvaluationResponse = TestResult;

export const executeCodeEvaluation = async (
payload: EvaluationPayload,
cookies?: string
): Promise<EvaluationResponse> => {
return await post<EvaluationResponse>(httpClient, '/execute', payload, cookies);
};
};
29 changes: 5 additions & 24 deletions codewit/client/src/components/codeblock/CodeSubmission.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,9 @@
import { BiSolidRightArrow, BiSolidLeftArrow } from 'react-icons/bi';
import { useState, useMemo } from 'react';

type FailureDetail = {
test_case: string;
expected?: string;
received?: string;
error_message: string;
rawout?: string;
};
import { useState } from 'react';
import type { EvaluationResponse } from '../../interfaces/evaluation';

type EvalProps = {
evaluation: {
state: string;
tests_run: number;
passed: number;
failed: number;
failure_details?: FailureDetail[];
compilation_error?: string;
runtime_error?: string;
execution_time_exceeded?: boolean;
memory_exceeded?: boolean;
rawout?: string;
error?: string;
} | null;
evaluation: EvaluationResponse | null;
};

const CodeSubmission = ({ evaluation }: EvalProps): JSX.Element => {
Expand All @@ -47,9 +28,9 @@ const CodeSubmission = ({ evaluation }: EvalProps): JSX.Element => {
runtime_error = '',
execution_time_exceeded = false,
memory_exceeded = false,
rawout = '',
error = '',
} = evaluation;
const error = 'error' in evaluation ? evaluation.error : '';
const rawout = failure_details[issueIdx]?.rawout || '';

let errorMessage = null;
if (compilation_error) errorMessage = compilation_error;
Expand Down
33 changes: 17 additions & 16 deletions codewit/client/src/interfaces/evaluation.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// Evaluation-related types for client-side consumption
import type { AttemptDTO, FailureDetail, TestResult } from '@codewit/interfaces';

export interface EvaluationTestCase {
message: string;
passed: boolean;
expected?: string;
actual?: string;
export interface EvaluationErrorResponse {
state: 'error';
tests_run: 0;
passed: 0;
failed: 0;
errors: 0;
no_tests_collected: false;
exit_code: null;
failure_details: FailureDetail[];
compilation_error: '';
runtime_error: '';
execution_time_exceeded: false;
memory_exceeded: false;
error: string;
}

export interface EvaluationResponse {
tests_run: number;
passed: number;
failed: number;
error?: string;
state: string;
test_cases?: EvaluationTestCase[];
rawout?: string;
}
export type EvaluationResponse = TestResult | EvaluationErrorResponse;

export interface AttemptWithEval {
attempt: any; // Can refine if Attempt type is imported
attempt: AttemptDTO | null;
evaluation: EvaluationResponse;
}
17 changes: 14 additions & 3 deletions codewit/client/src/pages/Read.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,21 @@ function RightPanel({info, course_id}: RightPanelProps) {
set_last_attempt({
attempt : null,
evaluation: {
state : 'error',
error : 'Unexpected error: ' + err.message,
state: 'error',
tests_run: 0,
passed: 0,
failed: 0,
errors: 0,
no_tests_collected: false,
exit_code: null,
failure_details: [],
compilation_error: '',
runtime_error: '',
execution_time_exceeded: false,
memory_exceeded: false,
error: 'Unexpected error: ' + err.message,
},
} as AttemptWithEval);
});
}
});

Expand Down
2 changes: 1 addition & 1 deletion codewit/client/src/pages/create/exercise/exercise_id.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ function ExerciseTest({
}

interface FailureDetailProps {
test_case: string,
test_case: string | number,
expected: string,
received: string,
error_message: string,
Expand Down
25 changes: 19 additions & 6 deletions codewit/lib/shared/interfaces/src/lib/output.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
type EvaluationState =
| 'passed'
| 'failed'
| 'compile_error'
| 'runtime_error'
| 'execution_error'
| 'execution_blocked';

interface FailureDetail {
test_case: string;
test_case: string | number;
expected: string;
received: string;
error_message: string;
rawout: string;
stderr?: string;
}

interface TestResult {
state: "passed" | "failed";
state: EvaluationState;
tests_run: number;
passed: number;
failed: number;
errors: number;
no_tests_collected: boolean;
exit_code: number | null;
failure_details: FailureDetail[];
stdout: string;
stderr: string;
compilation_error: string;
runtime_error: string;
execution_time_exceeded: boolean;
memory_exceeded: boolean;
}

export type {
TestResult
}
EvaluationState,
FailureDetail,
TestResult,
};
Loading