Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -47,6 +47,47 @@ source_asset_id,
|
||||
unit_number
|
||||
"#;
|
||||
|
||||
fn normalize_answer_keywords_value(value: serde_json::Value) -> serde_json::Value {
|
||||
match value {
|
||||
serde_json::Value::Array(items) => serde_json::Value::Array(
|
||||
items
|
||||
.into_iter()
|
||||
.map(normalize_answer_keywords_value)
|
||||
.collect(),
|
||||
),
|
||||
serde_json::Value::Object(map) => {
|
||||
let answer_text = map
|
||||
.get("answer")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
if map.contains_key("keywords") {
|
||||
if let Some(answer) = answer_text {
|
||||
return serde_json::Value::String(answer);
|
||||
}
|
||||
}
|
||||
|
||||
let normalized_map = map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, normalize_answer_keywords_value(v)))
|
||||
.collect();
|
||||
|
||||
serde_json::Value::Object(normalized_map)
|
||||
}
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_question_bank_payload_values(
|
||||
options: Option<serde_json::Value>,
|
||||
correct_answer: Option<serde_json::Value>,
|
||||
) -> (Option<serde_json::Value>, Option<serde_json::Value>) {
|
||||
(
|
||||
options.map(normalize_answer_keywords_value),
|
||||
correct_answer.map(normalize_answer_keywords_value),
|
||||
)
|
||||
}
|
||||
|
||||
async fn connect_mysql_pool(env_var: &str) -> Result<sqlx::MySqlPool, (StatusCode, String)> {
|
||||
use sqlx::mysql::MySqlPoolOptions;
|
||||
|
||||
@@ -288,6 +329,23 @@ pub async fn create_question(
|
||||
State(pool): State<PgPool>,
|
||||
Json(payload): Json<CreateQuestionBankPayload>,
|
||||
) -> Result<Json<QuestionBank>, (StatusCode, String)> {
|
||||
let CreateQuestionBankPayload {
|
||||
question_text,
|
||||
question_type,
|
||||
options,
|
||||
correct_answer,
|
||||
explanation,
|
||||
points,
|
||||
difficulty,
|
||||
tags,
|
||||
media_url,
|
||||
media_type,
|
||||
skill_assessed,
|
||||
} = payload;
|
||||
|
||||
let (normalized_options, normalized_correct_answer) =
|
||||
normalize_question_bank_payload_values(options, correct_answer);
|
||||
|
||||
let create_question_sql = format!(
|
||||
r#"
|
||||
INSERT INTO question_bank (
|
||||
@@ -306,17 +364,17 @@ pub async fn create_question(
|
||||
)
|
||||
.bind(org_ctx.id)
|
||||
.bind(claims.sub)
|
||||
.bind(&payload.question_text)
|
||||
.bind(&payload.question_type)
|
||||
.bind(&payload.options)
|
||||
.bind(&payload.correct_answer)
|
||||
.bind(&payload.explanation)
|
||||
.bind(payload.points.unwrap_or(1))
|
||||
.bind(payload.difficulty.as_deref().unwrap_or("medium"))
|
||||
.bind(payload.tags.as_deref())
|
||||
.bind(payload.skill_assessed.as_deref())
|
||||
.bind(payload.media_url.as_deref())
|
||||
.bind(payload.media_type.as_deref())
|
||||
.bind(&question_text)
|
||||
.bind(&question_type)
|
||||
.bind(&normalized_options)
|
||||
.bind(&normalized_correct_answer)
|
||||
.bind(&explanation)
|
||||
.bind(points.unwrap_or(1))
|
||||
.bind(difficulty.as_deref().unwrap_or("medium"))
|
||||
.bind(tags.as_deref())
|
||||
.bind(skill_assessed.as_deref())
|
||||
.bind(media_url.as_deref())
|
||||
.bind(media_type.as_deref())
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -486,6 +544,22 @@ pub async fn update_question(
|
||||
State(pool): State<PgPool>,
|
||||
Json(payload): Json<UpdateQuestionBankPayload>,
|
||||
) -> Result<Json<QuestionBank>, (StatusCode, String)> {
|
||||
let UpdateQuestionBankPayload {
|
||||
question_text,
|
||||
question_type,
|
||||
options,
|
||||
correct_answer,
|
||||
explanation,
|
||||
points,
|
||||
difficulty,
|
||||
tags,
|
||||
is_active,
|
||||
is_archived,
|
||||
} = payload;
|
||||
|
||||
let (normalized_options, normalized_correct_answer) =
|
||||
normalize_question_bank_payload_values(options, correct_answer);
|
||||
|
||||
let update_question_sql = format!(
|
||||
r#"
|
||||
UPDATE question_bank
|
||||
@@ -512,16 +586,16 @@ pub async fn update_question(
|
||||
)
|
||||
.bind(id)
|
||||
.bind(org_ctx.id)
|
||||
.bind(payload.question_text)
|
||||
.bind(payload.question_type.map(|t| t.to_string()))
|
||||
.bind(&payload.options)
|
||||
.bind(&payload.correct_answer)
|
||||
.bind(&payload.explanation)
|
||||
.bind(payload.points)
|
||||
.bind(payload.difficulty)
|
||||
.bind(payload.tags.as_deref())
|
||||
.bind(payload.is_active)
|
||||
.bind(payload.is_archived)
|
||||
.bind(question_text)
|
||||
.bind(question_type.map(|t| t.to_string()))
|
||||
.bind(&normalized_options)
|
||||
.bind(&normalized_correct_answer)
|
||||
.bind(&explanation)
|
||||
.bind(points)
|
||||
.bind(difficulty)
|
||||
.bind(tags.as_deref())
|
||||
.bind(is_active)
|
||||
.bind(is_archived)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
|
||||
@@ -13,6 +13,47 @@ use sqlx::PgPool;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
fn normalize_answer_keywords_value(value: serde_json::Value) -> serde_json::Value {
|
||||
match value {
|
||||
serde_json::Value::Array(items) => serde_json::Value::Array(
|
||||
items
|
||||
.into_iter()
|
||||
.map(normalize_answer_keywords_value)
|
||||
.collect(),
|
||||
),
|
||||
serde_json::Value::Object(map) => {
|
||||
let answer_text = map
|
||||
.get("answer")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
if map.contains_key("keywords") {
|
||||
if let Some(answer) = answer_text {
|
||||
return serde_json::Value::String(answer);
|
||||
}
|
||||
}
|
||||
|
||||
let normalized_map = map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, normalize_answer_keywords_value(v)))
|
||||
.collect();
|
||||
|
||||
serde_json::Value::Object(normalized_map)
|
||||
}
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_question_bank_payload_values(
|
||||
options: Option<serde_json::Value>,
|
||||
correct_answer: Option<serde_json::Value>,
|
||||
) -> (Option<serde_json::Value>, Option<serde_json::Value>) {
|
||||
(
|
||||
options.map(normalize_answer_keywords_value),
|
||||
correct_answer.map(normalize_answer_keywords_value),
|
||||
)
|
||||
}
|
||||
|
||||
// ==================== Query Parameters ====================
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -1736,6 +1777,12 @@ pub async fn generate_questions_with_rag(
|
||||
_ => common::models::QuestionBankType::MultipleChoice,
|
||||
};
|
||||
|
||||
let (normalized_options, normalized_correct_answer) =
|
||||
normalize_question_bank_payload_values(
|
||||
question.options.clone(),
|
||||
question.correct_answer.clone(),
|
||||
);
|
||||
|
||||
let result = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO question_bank (
|
||||
@@ -1750,8 +1797,8 @@ pub async fn generate_questions_with_rag(
|
||||
.bind(claims.sub)
|
||||
.bind(&question.question_text)
|
||||
.bind(&question_type)
|
||||
.bind(&question.options)
|
||||
.bind(&question.correct_answer)
|
||||
.bind(&normalized_options)
|
||||
.bind(&normalized_correct_answer)
|
||||
.bind(&question.explanation)
|
||||
.bind(question.points)
|
||||
.bind("medium")
|
||||
|
||||
@@ -27,7 +27,7 @@ use sqlx::postgres::PgPoolOptions;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::set_header::SetResponseHeaderLayer;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
@@ -114,8 +114,14 @@ async fn main() {
|
||||
"http://192.168.0.254:3000",
|
||||
"http://192.168.0.254:3003",
|
||||
"http://192.168.0.254",
|
||||
// Production - Norteamericano domains (HTTPS)
|
||||
// Production - Norteamericano domains (.cl and .com)
|
||||
"http://studio.norteamericano.com",
|
||||
"https://studio.norteamericano.com",
|
||||
"http://learning.norteamericano.com",
|
||||
"https://learning.norteamericano.com",
|
||||
"http://studio.norteamericano.cl",
|
||||
"https://studio.norteamericano.cl",
|
||||
"http://learning.norteamericano.cl",
|
||||
"https://learning.norteamericano.cl",
|
||||
];
|
||||
|
||||
@@ -124,17 +130,21 @@ async fn main() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check wildcard for subdomains: https://*.norteamericano.cl
|
||||
if origin_str.starts_with("https://") && origin_str.ends_with(".norteamericano.cl") {
|
||||
let subdomain = origin_str
|
||||
.strip_prefix("https://")
|
||||
.unwrap_or("")
|
||||
.strip_suffix(".norteamericano.cl")
|
||||
.unwrap_or("");
|
||||
|
||||
// Allow any subdomain (e.g., api., cdn., admin., etc.)
|
||||
if !subdomain.is_empty() && !subdomain.contains('/') {
|
||||
return true;
|
||||
// Check wildcard for subdomains in norteamericano.cl/.com over HTTP(S)
|
||||
for scheme in ["http://", "https://"] {
|
||||
for domain in [".norteamericano.cl", ".norteamericano.com"] {
|
||||
if origin_str.starts_with(scheme) && origin_str.ends_with(domain) {
|
||||
let subdomain = origin_str
|
||||
.strip_prefix(scheme)
|
||||
.unwrap_or("")
|
||||
.strip_suffix(domain)
|
||||
.unwrap_or("");
|
||||
|
||||
// Allow any subdomain (e.g., api., cdn., admin., etc.)
|
||||
if !subdomain.is_empty() && !subdomain.contains('/') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user