feat: enhance LMS retention data with completion rates, improve LTI key handling, and refine dev setup scripts

This commit is contained in:
2026-02-24 12:43:58 -03:00
parent 06c0290813
commit c76125c96a
13 changed files with 117 additions and 19 deletions
+2 -2
View File
@@ -1240,7 +1240,7 @@ pub async fn get_user_gamification(
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let badges = sqlx::query_as::<_, BadgeResponse>(
"SELECT b.id, b.name, b.description, b.icon_url, ub.earned_at
"SELECT b.id, b.name, b.description, b.icon_url, ub.awarded_at AS earned_at
FROM user_badges ub
JOIN badges b ON ub.badge_id = b.id
WHERE ub.user_id = $1 AND ub.organization_id = $2",
@@ -1550,7 +1550,7 @@ pub async fn get_advanced_analytics(
// 2. Retention Analysis using DB function
let retention_data = sqlx::query_as::<_, common::models::RetentionData>(
"SELECT lesson_id, lesson_title, student_count FROM fn_get_retention_data($1, $2)",
"SELECT lesson_id, lesson_title, student_count, completion_rate FROM fn_get_retention_data($1, $2)",
)
.bind(course_id)
.bind(org_ctx.id)
+17 -3
View File
@@ -4,11 +4,25 @@ use std::env;
pub fn get_lti_private_key() -> jsonwebtoken::EncodingKey {
let key_str = env::var("LTI_PRIVATE_KEY").unwrap_or_else(|_| {
// Fallback for development (DO NOT USE IN PRODUCTION)
include_str!("../dev_keys/lti_private.pem").to_string()
let dev_key_path = "services/lms-service/dev_keys/lti_private.pem";
std::fs::read_to_string(dev_key_path).unwrap_or_else(|_| {
// Return a dummy key or handle error gracefully in production
// For now, we'll return a string that will likely fail decoding if used,
// but allows the service to start if LTI is not used.
tracing::warn!("LTI private key not found at {} and LTI_PRIVATE_KEY is not set.", dev_key_path);
String::new()
})
});
jsonwebtoken::EncodingKey::from_rsa_pem(key_str.as_bytes()).expect("Invalid LTI private key")
if key_str.is_empty() {
// Handle the empty key case - maybe return a specialized error or a dummy key
// that fails later. jsonwebtoken::EncodingKey::from_rsa_pem usually expects valid PEM.
// We'll use a dummy valid-looking but useless PEM if it's empty to avoid panic on startup
// but it will fail on actual LTI usage.
return jsonwebtoken::EncodingKey::from_rsa_pem(b"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA7f...dummy...\n-----END RSA PRIVATE KEY-----").expect("Dummy key failed");
}
jsonwebtoken::EncodingKey::from_rsa_pem(key_str.as_bytes()).expect("Invalid LTI private key format")
}
pub fn get_lti_jwks() -> JwkSet {
+1 -1
View File
@@ -6,7 +6,7 @@ use axum::{
use chrono::Utc;
use common::auth::Claims;
use common::models::Meeting;
use sqlx::{PgPool, Row};
use sqlx::PgPool;
use uuid::Uuid;
use serde::Deserialize;
+1 -1
View File
@@ -8,7 +8,7 @@ use jsonwebtoken::{decode, decode_header, jwk::JwkSet, DecodingKey, Validation};
use serde::{Deserialize};
use sqlx::{PgPool};
use uuid::Uuid;
use common::models::{LtiLaunchClaims, LtiRegistration, LtiResourceLink, User};
use common::models::{LtiLaunchClaims, LtiRegistration, User};
use common::auth::Claims;
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
-1
View File
@@ -16,7 +16,6 @@ use axum::{
Router, middleware,
routing::{delete, get, post, put},
};
use axum::Json; // Added based on instruction
use dotenvy::dotenv;
use sqlx::postgres::PgPoolOptions;
use std::env;