math/src/db/problems.cpp

194 lines
5.7 KiB
C++

#include "problems.h"
#include <print>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include <nlohmann/json.hpp>
#include <string>
using leveldb_ptr = leveldb::DB*;
auto problems_db = leveldb_ptr{nullptr};
auto max_problem_id = 0;
extern "C"
{
int open_problems_db()
{
auto opts = leveldb::Options{};
opts.create_if_missing = true;
auto status = leveldb::DB::Open(opts, "db/problems", &problems_db);
if (!status.ok()) {
std::println(stderr, "Failed to open problems database: {}", status.ToString());
return 0;
}
auto value = std::string{};
status = problems_db->Get(leveldb::ReadOptions{}, "@", &value);
if (status.ok()) {
auto json = nlohmann::json::parse(value);
json["max_problem_id"].get_to(max_problem_id);
} else if (status.IsNotFound()) {
auto json = nlohmann::json{
{"max_problem_id", 0}
};
status = problems_db->Put(leveldb::WriteOptions{}, "@", json.dump());
return 1;
} else {
std::println(stderr, "Failed to get max problem id: {}", status.ToString());
delete problems_db;
return 0;
}
return 1;
}
void close_problems_db()
{
delete problems_db;
}
int add_problem(const char* problem, const char* answer, const char* check_error, int* result)
{
auto batch = leveldb::WriteBatch{};
++max_problem_id;
auto json = nlohmann::json{
{"max_problem_id", max_problem_id}
};
batch.Put("@", json.dump());
json = nlohmann::json{
{"content", problem },
{"answer", answer },
{"error", check_error}
};
batch.Put(std::to_string(max_problem_id), json.dump());
auto status = problems_db->Write(leveldb::WriteOptions{}, &batch);
if (!status.ok()) {
std::println(stderr, "Failed to add a problem: {}", status.ToString());
return 0;
}
*result = max_problem_id;
return 1;
}
int delete_problems(int problems_id)
{
auto batch = leveldb::WriteBatch{};
auto status = problems_db->Delete(leveldb::WriteOptions{}, std::to_string(problems_id));
if (!status.ok()) {
std::println(stderr, "Failed to delete a problem: {}", status.ToString());
return 0;
}
return 1;
}
int get_problem(int id, char** result1, char** result2, char** result3)
{
auto value = std::string{};
auto status = problems_db->Get(leveldb::ReadOptions{}, std::to_string(id), &value);
if (!status.ok()) {
std::println(stderr, "Failed to get a problem: {}", status.ToString());
return 0;
}
auto json = nlohmann::json::parse(value);
if (result1) {
json["content"].get_to(value);
*result1 = strdup(value.data());
}
if (result2) {
json["answer"].get_to(value);
*result2 = strdup(value.data());
}
if (result3) {
json["error"].get_to(value);
*result3 = strdup(value.data());
}
return 1;
}
int modify_problem(int problems_id, const char* problem, const char* answer, const char* check_error)
{
auto json = nlohmann::json{
{"content", problem },
{"answer", answer },
{"error", check_error}
};
auto status = problems_db->Put(leveldb::WriteOptions{}, std::to_string(problems_id), json.dump());
if (!status.ok()) {
std::println(stderr, "Failed to modify a problem: {}", status.ToString());
return 0;
}
return 1;
}
int check_answer(int problems_id, const char* answer, int* result)
{
auto value = std::string{};
auto status = problems_db->Get(leveldb::ReadOptions{}, std::to_string(problems_id), &value);
if (!status.ok()) {
std::println(stderr, "Failed to check the answer: {}", status.ToString());
return 0;
}
auto value2 = std::string{};
auto json = nlohmann::json::parse(value);
json["answer"].get_to(value);
json["error"].get_to(value2);
if (value2.empty()) {
*result = value == answer;
} else {
auto error = std::stod(value2);
auto std_answer = std::stod(value);
auto user_answer = std::stod(answer);
*result = std::fabs(std_answer - user_answer) <= error;
}
return 1;
}
int has_problem(int problems_id, int* result)
{
auto value = std::string{};
auto status = problems_db->Get(leveldb::ReadOptions{}, std::to_string(problems_id), &value);
if (status.ok()) {
*result = 1;
} else if (status.IsNotFound()) {
*result = 0;
} else {
std::println(stderr, "Failed to check problem existence: {}", status.ToString());
return 0;
}
return 1;
}
int all_problems(char** result)
{
auto problems = std::vector<int>{};
leveldb::Iterator* it = problems_db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
if (!it->key().compare("@")) continue;
problems.emplace_back(std::stoi(it->key().ToString()));
}
if (!it->status().ok()) {
std::println(stderr, "Failed to get all problems: {}", it->status().ToString());
return 0;
}
auto json = nlohmann::json(problems);
*result = strdup(json.dump().c_str());
return 1;
}
}