diff --git a/src/main.cpp b/src/main.cpp index a9b892f..d7916dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,4 @@ +#include "nlohmann/json_fwd.hpp" #include "skwyward-api-utils.hpp" #include "spdlog/spdlog.h" @@ -13,5 +14,8 @@ int main (int argc, char *argv[]) { api_utils::ErrorResponse test_login = api_methods::get_auth_status(base_uri, test_username, test_password); spdlog::info("Auth attempt response : {}", test_login.success); + + api_utils::GradesResponse test_grades = api_methods::get_grades(base_uri, test_username, test_password); + spdlog::info("Grades: {}", nlohmann::json {test_grades}.dump()); return 0; } diff --git a/src/skwyward-api-utils.cpp b/src/skwyward-api-utils.cpp index b90deb3..63d7568 100644 --- a/src/skwyward-api-utils.cpp +++ b/src/skwyward-api-utils.cpp @@ -1,4 +1,5 @@ #include "skwyward-api-utils.hpp" +#include "nlohmann/json_fwd.hpp" #include #include @@ -9,20 +10,50 @@ using nlohmann::json; // ---------- Helpers ---------- -static double parse_percent_score(const json& j) { +static double parse_percent_score(const nlohmann::json& j) { + // Case 1: real number already if (j.is_number()) { return j.get(); } + // Case 2: string input if (j.is_string()) { std::string s = j.get(); - s.erase(std::remove(s.begin(), s.end(), '%'), s.end()); - return std::stod(s); + + // Trim whitespace + s.erase(0, s.find_first_not_of(" \t\n\r")); + s.erase(s.find_last_not_of(" \t\n\r") + 1); + + // Empty or known non-values + if (s.empty() || s == "N/A" || s == "--") { + return std::numeric_limits::quiet_NaN(); + } + + // Remove trailing % + if (!s.empty() && s.back() == '%') { + s.pop_back(); + } + + // Fast numeric parse (no exceptions) + double value{}; + auto [ptr, ec] = std::from_chars( + s.data(), + s.data() + s.size(), + value + ); + + if (ec == std::errc()) { + return value; + } + + return std::numeric_limits::quiet_NaN(); } - return 0.0; + // Everything else (null, object, etc.) + return std::numeric_limits::quiet_NaN(); } + // ---------- Login ---------- void to_json(json& j, const Login& r) { @@ -149,7 +180,7 @@ api_utils::ErrorResponse get_auth_status( std::string username, std::string password ) { - api_utils::Login login{username, password}; + api_utils::Login login = api_utils::Login{username, password}; cpr::Response r = cpr::Post( cpr::Url{url + "/check-auth"}, @@ -160,6 +191,15 @@ api_utils::ErrorResponse get_auth_status( return nlohmann::json::parse(r.text) .get(); } - + api_utils::GradesResponse get_grades(std::string url, std::string username, std::string password) { + api_utils::Login login = api_utils::Login{username, password}; + cpr::Response r = cpr::Post( + cpr::Url{url + "/fetch-grades"}, + cpr::Body{nlohmann::json(login).dump()}, + cpr::Header{{"Content-Type", "application/json"}} + ); + + return nlohmann::json::parse(r.text).get(); + } } // namespace api_methods diff --git a/src/skwyward-api-utils.hpp b/src/skwyward-api-utils.hpp index 6d0ea5a..f3d5f49 100644 --- a/src/skwyward-api-utils.hpp +++ b/src/skwyward-api-utils.hpp @@ -31,7 +31,7 @@ struct StatusResponse { struct AssignmentGrade { std::string name; std::string dueDate; - double score; // <-- parsed from "72.00%" + double score; std::string attempts; bool isMajorGrade; }; @@ -80,5 +80,6 @@ namespace api_methods { std::string username, std::string password ); + api_utils::GradesResponse get_grades( std::string url, std::string username, std::string password); }