feat(database): Missing queries, rename lecture -> course

This commit is contained in:
2025-09-25 11:51:46 +02:00
parent 1cbb4d69b1
commit 9c6b194b3a
10 changed files with 73 additions and 35 deletions

View File

@@ -37,6 +37,7 @@ private:
bool loaded_ { false };
std::vector<std::string> default_config_paths_ = {
"config.toml",
"./src/config.toml",
"../config.toml",
"../../config.toml",
"../../../config.toml",

View File

@@ -75,7 +75,7 @@ std::vector<StudentCourse> DatabaseManager::queue_pending_combinations() {
for (const auto& row : result) {
StudentCourse sc;
sc.student_name = row[0].as<std::string>();
sc.lecture_name = row[1].as<std::string>();
sc.course_name = row[1].as<std::string>();
combinations.push_back(sc);
}
@@ -96,7 +96,7 @@ bool DatabaseManager::enroll_student_into_course(const StudentCourse& sc) {
try {
pqxx::work txn(*conn_);
txn.exec_params(SQL_INSERT_STUDENT_ENROLLMENT, sc.student_name, sc.lecture_name);
txn.exec_params(SQL_INSERT_STUDENT_ENROLLMENT, sc.student_name, sc.course_name);
txn.commit();
return true;
@@ -110,7 +110,7 @@ bool DatabaseManager::enroll_student_into_course(const StudentCourse& sc) {
}
}
bool DatabaseManager::store_exam_result(const std::string& student_name, const std::string& lecture_name, int grade) {
bool DatabaseManager::store_exam_result(const std::string& student_name, const std::string& course_name, int grade) {
if (!is_connected()) return false;
try {
@@ -118,7 +118,7 @@ bool DatabaseManager::store_exam_result(const std::string& student_name, const s
txn.exec_params(
SQL_INSERT_EXAM_RESULT,
student_name, lecture_name, grade
student_name, course_name, grade
);
txn.commit();
@@ -133,6 +133,33 @@ bool DatabaseManager::store_exam_result(const std::string& student_name, const s
}
}
bool DatabaseManager::store_final_course_result(
const StudentCourse& sc,
int exam_count,
int final_grade
) {
if (!is_connected()) return false;
try {
pqxx::work txn(*conn_);
txn.exec_params(
SQL_INSERT_FINAL_COURSE_RESULT,
sc.student_name, sc.course_name, exam_count, final_grade
);
txn.commit();
return true;
} catch (const pqxx::sql_error &e) {
RCLCPP_ERROR(logger_, "[DBS] store final course result failed: %s", e.what());
return false;
} catch (const std::exception &e) {
RCLCPP_ERROR(logger_, "[DBS] database error: %s", e.what());
return false;
}
}
void DatabaseManager::create_tables() {
if (!conn_ || !conn_->is_open()) return;
@@ -141,7 +168,7 @@ void DatabaseManager::create_tables() {
txn.exec(SQL_CREATE_ENROLLMENTS_TABLE);
txn.exec(SQL_CREATE_EXAM_RESULTS);
txn.exec(SQL_CREATE_LECTURE_RESULTS);
txn.exec(SQL_CREATE_COURSE_RESULTS);
txn.commit();

View File

@@ -33,8 +33,9 @@ public:
// Data operations
std::vector<StudentCourse> queue_pending_combinations();
bool store_exam_result(const std::string& student_name, const std::string& lecture_name, int grade);
bool store_exam_result(const std::string& student_name, const std::string& course_name, int grade);
bool enroll_student_into_course(const StudentCourse& sc);
bool store_final_course_result(const StudentCourse& sc, int exam_count, int final_grade);
private:
rclcpp::Logger logger_;

View File

@@ -11,53 +11,55 @@ static const std::string SQL_CREATE_ENROLLMENTS_TABLE = R"(
CREATE TABLE IF NOT EXISTS student_enrollments (
id SERIAL PRIMARY KEY,
student_name VARCHAR(100) NOT NULL,
lecture_name VARCHAR(100) NOT NULL,
enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(student_name, lecture_name)
course_name VARCHAR(100) NOT NULL,
is_retake BOOLEAN DEFAULT FALSE,
enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
)";
// UNIQUE(student_name, course_name)
static const std::string SQL_CREATE_EXAM_RESULTS = R"(
CREATE TABLE IF NOT EXISTS exam_results (
id SERIAL PRIMARY KEY,
student_name VARCHAR(100) NOT NULL,
lecture_name VARCHAR(100) NOT NULL,
exam_grade FLOAT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(student_name, lecture_name)
course_name VARCHAR(100) NOT NULL,
exam_grade INTEGER NOT NULL,
is_retake BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
)";
// UNIQUE(student_name, course_name)
static const std::string SQL_CREATE_LECTURE_RESULTS = R"(
static const std::string SQL_CREATE_COURSE_RESULTS = R"(
CREATE TABLE IF NOT EXISTS final_course_results (
id SERIAL PRIMARY KEY,
student_name VARCHAR(100) NOT NULL,
lecture_name VARCHAR(100) NOT NULL,
final_grade FLOAT NOT NULL,
course_name VARCHAR(100) NOT NULL,
exam_count INTEGER NOT NULL,
final_grade INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(student_name, lecture_name)
UNIQUE(student_name, course_name)
);
)";
static const std::string SQL_SELECT_MISSING_RESULTS = R"(
SELECT se.student_name, se.lecture_name
SELECT se.student_name, se.course_name
FROM student_enrollments se
LEFT JOIN final_course_results fcr
ON se.student_name = fcr.student_name
AND se.lecture_name = fcr.lecture_name
AND se.course_name = fcr.course_name
WHERE fcr.id IS NULL
)";
static const std::string SQL_INSERT_EXAM_RESULT = R"(
INSERT INTO exam_results (student_name, lecture_name, exam_grade)
INSERT INTO exam_results (student_name, course_name, exam_grade)
VALUES ($1, $2, $3)
)";
// ON CONFLICT (student_name, lecture_name)
// ON CONFLICT (student_name, course_name)
// DO UPDATE SET exam_grade = EXCLUDED.exam_grade, created_at = CURRENT_TIMESTAMP
static const std::string SQL_INSERT_STUDENT_ENROLLMENT = R"(
INSERT INTO student_enrollments (student_name, lecture_name)
INSERT INTO student_enrollments (student_name, course_name)
VALUES ($1, $2)
ON CONFLICT DO NOTHING
)";
@@ -65,3 +67,10 @@ static const std::string SQL_INSERT_STUDENT_ENROLLMENT = R"(
static const std::string SQL_SELECT_STUDENT_LIST = R"(
SELECT COUNT(*) FROM student_enrollments
)";
static const std::string SQL_INSERT_FINAL_COURSE_RESULT = R"(
INSERT INTO final_course_results (student_name, course_name, exam_count, final_grade)
VALUES ($1, $2, $3, $4)
)";
// ON CONFLICT (student_name, course_name)
// DO UPDATE SET exam_count = EXCLUDED.exam_count, final_grade = EXCLUDED.final_grade, created_at = CURRENT_TIMESTAMP

View File

@@ -13,10 +13,10 @@ namespace assignments::one::exam_result_generator {
struct StudentCourse {
std::string student_name;
std::string lecture_name;
std::string course_name;
bool operator==(const StudentCourse& other) const {
return student_name == other.student_name && lecture_name == other.lecture_name;
return student_name == other.student_name && course_name == other.course_name;
}
};

View File

@@ -61,37 +61,37 @@ void ExamResultGenerator::generate_random_result() {
int grade = grade_dist_(gen_);
db_manager_->store_exam_result(selected.student_name, selected.lecture_name, grade);
db_manager_->store_exam_result(selected.student_name, selected.course_name, grade);
// Publish exam result
auto exam_msg = g2_2025_interfaces::msg::Exam();
exam_msg.lecture_name = selected.lecture_name;
exam_msg.course_name = selected.course_name;
exam_msg.result = grade;
exam_publisher_->publish(exam_msg);
RCLCPP_INFO(this->get_logger(),
"generated grade: (%d) %s in %s",
grade, selected.student_name.c_str(), selected.lecture_name.c_str()
grade, selected.student_name.c_str(), selected.course_name.c_str()
);
}
void ExamResultGenerator::student_management_callback(const g2_2025_interfaces::msg::Student::SharedPtr msg) {
StudentCourse sc;
sc.student_name = msg->student_name;
sc.lecture_name = msg->lecture_name;
sc.course_name = msg->course_name;
auto it = std::find(operations_queue_.begin(), operations_queue_.end(), sc);
if (it != operations_queue_.end()) {
operations_queue_.erase(it);
RCLCPP_INFO(this->get_logger(), "removed from queue: %s - %s",
sc.student_name.c_str(), sc.lecture_name.c_str()
sc.student_name.c_str(), sc.course_name.c_str()
);
} else {
add_student_course_combination(sc);
RCLCPP_INFO(this->get_logger(), "added to queue: %s - %s",
sc.student_name.c_str(), sc.lecture_name.c_str()
sc.student_name.c_str(), sc.course_name.c_str()
);
}
}

View File

@@ -1,5 +1,5 @@
string student_name
string lecture_name
string course_name
---
---
float32 result

View File

@@ -1,3 +1,3 @@
string lecture_name
string course_name
int32 result
builtin_interfaces/Time timestamp

View File

@@ -1,3 +1,3 @@
string student_name
string lecture_name
string course_name
builtin_interfaces/Time timestamp

View File

@@ -1,6 +1,6 @@
# Request
string student_name
string lecture_name
string course_name
int32[] exam_grades
---
# Response