Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Commit

Permalink
feat: rewrite how test cases are stored
Browse files Browse the repository at this point in the history
Instead of creating separate models for each test case data
type, we will store them all in a string and let the data
decide how to work with it. However, this method will result
in us losing the ability to do strict output comparisons (we have
implemented only  string based output correctness checking). I
do not think I am willing to give up such a big feature for a little
ease of db schema. However I will keep this in commit history
so that I can reference it later down the line.
  • Loading branch information
IgnisDa committed Nov 25, 2022
1 parent fe8d792 commit c9e20c3
Show file tree
Hide file tree
Showing 25 changed files with 92 additions and 414 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use edgedb_derive::Queryable;
use utilities::graphql::ApiError;
use uuid::Uuid;

use crate::learning::dto::queries::test_case::TestCase;
use crate::learning::dto::queries::test_case::TestCaseInput;

/// The input object used to create a new question
#[derive(Debug, InputObject, Getters)]
Expand All @@ -19,7 +19,7 @@ pub struct CreateQuestionInput {
class_ids: Vec<Uuid>,

/// All the test cases that are related to this question
test_cases: Vec<TestCase>,
test_cases: Vec<TestCaseInput>,
}

/// The result type if the question was created successfully
Expand Down
29 changes: 9 additions & 20 deletions apps/orchestrator/src/learning/dto/queries/question_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,34 @@ use serde::Deserialize;
use utilities::graphql::ApiError;
use uuid::Uuid;

use super::test_case::TestCaseUnit;

#[derive(SimpleObject, Deserialize)]
#[derive(Debug, SimpleObject, Deserialize)]
pub struct AuthoredByProfile {
username: String,
}

#[derive(SimpleObject, Deserialize)]
#[derive(Debug, SimpleObject, Deserialize)]
pub struct AuthoredByInformation {
profile: AuthoredByProfile,
}

#[derive(Debug, SimpleObject, Deserialize)]
pub struct TestCaseData {
pub unit_type: TestCaseUnit,
pub string_value: Option<String>,
pub string_collection_value: Option<Vec<String>>,
pub number_value: Option<f64>,
pub number_collection_value: Option<Vec<f64>>,
}

#[derive(Debug, SimpleObject, Deserialize)]
pub struct QuestionData {
/// The data related to this input
pub data: TestCaseData,
/// The actual data associated with the input/output
pub data: String,
}

#[derive(Debug, SimpleObject, Deserialize)]
pub struct QuestionTestCase {
/// The unique ID for this test case
id: Uuid,
pub id: Uuid,
/// The ordered inputs for this test case
pub inputs: Vec<QuestionData>,
pub inputs: Vec<TestCaseData>,
/// The ordered outputs for this test case
pub outputs: Vec<QuestionData>,
pub outputs: Vec<TestCaseData>,
}

/// The input object used to get details about a question
#[derive(SimpleObject, Deserialize)]
#[derive(Debug, SimpleObject, Deserialize)]
pub struct QuestionDetailsOutput {
/// The name/title of the question
pub name: String,
Expand All @@ -63,7 +52,7 @@ pub struct QuestionDetailsOutput {
}

/// The output object when creating a new question
#[derive(Union)]
#[derive(Debug, Union)]
pub enum QuestionDetailsResultUnion {
/// The type returned when getting details about a question was successful
Result(QuestionDetailsOutput),
Expand Down
41 changes: 5 additions & 36 deletions apps/orchestrator/src/learning/dto/queries/test_case.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,15 @@
use async_graphql::{Enum, InputObject, SimpleObject};
use main_db::ToEdgeqlString;
use serde::{Deserialize, Serialize};
use strum::EnumIter;

#[derive(Enum, Copy, Clone, Eq, PartialEq, Debug, EnumIter, Deserialize, Serialize)]
pub enum TestCaseUnit {
#[serde(rename = "NumberUnit")]
Number,
#[serde(rename = "StringUnit")]
String,
#[serde(rename = "NumberCollectionUnit")]
NumberCollection,
#[serde(rename = "StringCollectionUnit")]
StringCollection,
}

impl ToEdgeqlString for TestCaseUnit {
fn get_module_name() -> String {
"learning".to_string()
}
}

#[derive(Debug, SimpleObject, InputObject)]
pub struct InputCaseUnit {
pub data_type: TestCaseUnit,
pub data: String,
/// The name of the variable to store it as
pub name: String,
}
use async_graphql::{InputObject, SimpleObject};

#[derive(Debug, SimpleObject, InputObject)]
pub struct OutputCaseUnit {
/// The type of data to store this line as
pub data_type: TestCaseUnit,
pub struct TestCaseDataInput {
/// The data to store
pub data: String,
}

#[derive(Debug, SimpleObject, InputObject)]
pub struct TestCase {
pub struct TestCaseInput {
/// The inputs related to this test case
pub inputs: Vec<InputCaseUnit>,
pub inputs: Vec<TestCaseDataInput>,
/// The outputs related to this test case
pub outputs: Vec<OutputCaseUnit>,
pub outputs: Vec<TestCaseDataInput>,
}
6 changes: 0 additions & 6 deletions apps/orchestrator/src/learning/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use super::{
},
queries::{
class_details::ClassDetailsResultUnion, question_details::QuestionDetailsResultUnion,
test_case::TestCaseUnit,
},
},
service::{LearningService, LearningServiceTrait},
Expand All @@ -30,11 +29,6 @@ pub struct LearningMutation {}

#[Object]
impl LearningQuery {
/// Get all the types of test case units possible
async fn test_case_units(&self, ctx: &Context<'_>) -> Vec<TestCaseUnit> {
ctx.data_unchecked::<LearningService>().test_case_units()
}

/// Get information about a class
async fn class_details(
&self,
Expand Down
82 changes: 21 additions & 61 deletions apps/orchestrator/src/learning/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ use super::dto::{
execute_code_for_question::{ExecuteCodeForQuestionOutput, TestCaseStatus},
},
queries::{
class_details::ClassDetailsOutput,
question_details::QuestionDetailsOutput,
test_case::{TestCase, TestCaseUnit},
class_details::ClassDetailsOutput, question_details::QuestionDetailsOutput,
test_case::TestCaseInput,
},
};
use crate::{
farem::{
dto::mutations::execute_code::{ExecuteCodeError, ExecuteCodeErrorStep},
service::{FaremService, SupportedLanguage},
},
utils::case_unit_to_argument,
use crate::farem::{
dto::mutations::execute_code::{ExecuteCodeError, ExecuteCodeErrorStep},
service::{FaremService, SupportedLanguage},
};
use async_trait::async_trait;
use auth::validate_user_role;
Expand All @@ -24,7 +20,6 @@ use edgedb_tokio::Client as DbClient;
use rand::{distributions::Alphanumeric, Rng};
use slug::slugify;
use std::sync::Arc;
use strum::IntoEnumIterator;
use utilities::{graphql::ApiError, models::IdObject, users::AccountType};
use uuid::Uuid;

Expand All @@ -38,29 +33,16 @@ const CREATE_CLASS: &str =
include_str!("../../../../libs/main-db/edgeql/learning/create-class.edgeql");
const CREATE_QUESTION: &str =
include_str!("../../../../libs/main-db/edgeql/learning/create-question.edgeql");
const INSERT_NUMBER_COLLECTION_UNIT: &str = include_str!(
"../../../../libs/main-db/edgeql/learning/test-cases/number-collection-unit.edgeql"
);
const INSERT_NUMBER_UNIT: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/number-unit.edgeql");
const INSERT_STRING_UNIT: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/string-unit.edgeql");
const INSERT_STRING_COLLECTION_UNIT: &str = include_str!(
"../../../../libs/main-db/edgeql/learning/test-cases/string-collection-unit.edgeql"
const INSERT_TEST_CASE_DATA: &str = include_str!(
"../../../../libs/main-db/edgeql/learning/test-cases/insert-test-case-data.edgeql"
);
const INSERT_INPUT_CASE_UNIT: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/input-case-unit.edgeql");
const INSERT_OUTPUT_CASE_UNIT: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/output-case-unit.edgeql");
const INSERT_TEST_CASE: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/test-case.edgeql");
const UPDATE_QUESTION: &str =
include_str!("../../../../libs/main-db/edgeql/learning/test-cases/update-question.edgeql");

#[async_trait]
pub trait LearningServiceTrait: Sync + Send {
fn test_case_units(&self) -> Vec<TestCaseUnit>;

async fn class_details<'a>(&self, class_id: Uuid) -> Result<ClassDetailsOutput, ApiError>;

async fn question_details<'a>(
Expand All @@ -82,7 +64,7 @@ pub trait LearningServiceTrait: Sync + Send {
account_type: &AccountType,
name: &'a str,
problem: &'a str,
test_cases: &[TestCase],
test_cases: &[TestCaseInput],
class_ids: &[Uuid],
) -> Result<CreateQuestionOutput, ApiError>;

Expand Down Expand Up @@ -112,10 +94,6 @@ impl LearningService {}

#[async_trait]
impl LearningServiceTrait for LearningService {
fn test_case_units(&self) -> Vec<TestCaseUnit> {
TestCaseUnit::iter().collect()
}

async fn class_details<'a>(&self, class_id: Uuid) -> Result<ClassDetailsOutput, ApiError> {
self.db_conn
.query_required_single::<ClassDetailsOutput, _>(CLASS_DETAILS, &(class_id,))
Expand Down Expand Up @@ -169,7 +147,7 @@ impl LearningServiceTrait for LearningService {
account_type: &AccountType,
name: &'a str,
problem: &'a str,
test_cases: &[TestCase],
test_cases: &[TestCaseInput],
class_ids: &[Uuid],
) -> Result<CreateQuestionOutput, ApiError> {
validate_user_role(&AccountType::Teacher, account_type)?;
Expand Down Expand Up @@ -216,47 +194,27 @@ impl LearningServiceTrait for LearningService {
.to_string(),
}
})?;
fn get_insert_ql(test_case: &TestCaseUnit) -> &'static str {
match test_case {
TestCaseUnit::Number => INSERT_NUMBER_UNIT,
TestCaseUnit::NumberCollection => INSERT_NUMBER_COLLECTION_UNIT,
TestCaseUnit::String => INSERT_STRING_UNIT,
TestCaseUnit::StringCollection => INSERT_STRING_COLLECTION_UNIT,
}
}
let mut test_cases_to_associate = vec![];
for test_case in test_cases.iter() {
let mut input_case_units = vec![];
let mut output_case_units = vec![];
for (idx, input) in test_case.inputs.iter().enumerate() {
let insert_ql = get_insert_ql(&input.data_type);
let case_unit = self
.db_conn
.query_required_single::<IdObject, _>(insert_ql, &(&input.data,))
.await
.unwrap();
let input_case_unit = self
.db_conn
.query_required_single::<IdObject, _>(
INSERT_INPUT_CASE_UNIT,
&(&input.name, idx as i32, case_unit.id),
INSERT_TEST_CASE_DATA,
&(idx as i32, input.data.clone()),
)
.await
.unwrap();
input_case_units.push(input_case_unit.id);
}
for (idx, output) in test_case.outputs.iter().enumerate() {
let insert_ql = get_insert_ql(&output.data_type);
let case_unit = self
.db_conn
.query_required_single::<IdObject, _>(insert_ql, &(&output.data,))
.await
.unwrap();
let output_case_unit = self
.db_conn
.query_required_single::<IdObject, _>(
INSERT_OUTPUT_CASE_UNIT,
&(idx as i32, case_unit.id),
INSERT_TEST_CASE_DATA,
&(idx as i32, output.data.clone()),
)
.await
.unwrap();
Expand Down Expand Up @@ -306,7 +264,7 @@ impl LearningServiceTrait for LearningService {
let arguments = test_case
.inputs
.iter()
.map(case_unit_to_argument)
.map(|f| f.data.clone())
.collect::<Vec<_>>();
let user_output = self
.farem_service
Expand All @@ -316,13 +274,15 @@ impl LearningServiceTrait for LearningService {
error: f,
step: ExecuteCodeErrorStep::WasmExecution,
})?;
let expected_output = test_case
let collected_expected_output = test_case
.outputs
.iter()
.map(case_unit_to_argument)
.collect::<Vec<_>>()
.join("\n")
+ "\n";
.map(|f| f.data.clone())
.collect::<Vec<_>>();
let mut expected_output = collected_expected_output.join("\n");
if !collected_expected_output.is_empty() {
expected_output += "\n";
}
outputs.push(TestCaseStatus {
passed: user_output == expected_output,
user_output,
Expand Down
1 change: 0 additions & 1 deletion apps/orchestrator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ pub mod farem;
pub mod graphql;
pub mod learning;
pub mod users;
pub mod utils;

use anyhow::Result;
use config::JwtConfig;
Expand Down
23 changes: 0 additions & 23 deletions apps/orchestrator/src/utils.rs

This file was deleted.

Loading

0 comments on commit c9e20c3

Please sign in to comment.