Skip to content

Commit

Permalink
feat: get current month games, errors handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AmanMenda committed May 6, 2024
1 parent dfbd7cd commit d4fb6ce
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 82 deletions.
14 changes: 12 additions & 2 deletions include/App/Logic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,21 @@ namespace Stuckfish

struct Response
{
int errorCode;
std::string errorMessage;
int statusCode;
std::string message;
std::vector<GamesData> gamesData;
};

enum ResponseCode
{
OK = 0,
NO_GAMES_FOUND,
DATA_EXTRACTION_ERROR,
MISSING_FIELD_ERROR,
PARSING_ERROR,
NETWORK_ERROR
};

class Logic
{
public:
Expand Down
2 changes: 2 additions & 0 deletions include/UI/GamesPlayedPage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include "Stuckfish.hpp"
#include <ctime>

namespace Stuckfish
{
Expand All @@ -27,6 +28,7 @@ namespace Stuckfish
Logic& _logic;
UserData& _userdata;
Response _res;
GamesData _selectedGameData;

bool _hasRetrievedGames = false;
bool _isFirstElement = true;
Expand Down
137 changes: 77 additions & 60 deletions src/App/Logic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,59 +53,76 @@ void
Logic::GetInfosFromListOfGamesPlayed(const std::string& username, const Document& doc)
{
// Check if parsing succeeded
if (!doc.HasParseError()) {
if (doc.HasMember("games") && doc["games"].IsArray()) {
const Value& gamesArray = doc["games"];

if (gamesArray.Empty()) {
// No popup, just display a a message on the screen
std::cerr << "No games found !\n";
return;
}
// Iterate over the array and do whatever you need with each game object
for (SizeType i = 0; i < gamesArray.Size(); ++i) {
const Value& game = gamesArray[i];
GamesData gameData;

// get white username and rating
if ((gameData.whiteUsername = extractString(game["white"], "username")) == "")
std::cerr << "Unable to get white username\n";
if ((gameData.whiteRating = extractInt(game["white"], "rating")) == "")
std::cerr << "Unable to get white rating\n";

// get black username and rating
if ((gameData.blackUsername = extractString(game["black"], "username")) == "")
std::cerr << "Unable to get black username\n";
if ((gameData.blackRating = extractInt(game["black"], "rating")) == "")
std::cerr << "Unable to get black rating\n";

// get time_class
if ((gameData.timeClass = extractString(game, "time_class")) == "")
std::cerr << "Unable to get time class\n";

//get pgn
if ((gameData.pgn = extractString(game, "pgn")) == "")
std::cerr << "Unable to get png\n";

// find if player has played as white or black and display if he won or lost the game.
if (gameData.whiteUsername == username)
gameData.result = game["white"]["result"] != "win" ? "Lost" : "Won";
else
gameData.result = game["black"]["result"] != "win" ? "Lost" : "Won";

// insert this game data in the vector
_gamesData.push_back(gameData);
}
}
else {
std::cerr << "JSON does not contain a valid 'games' array field." << std::endl;
return;
}
if (doc.HasParseError())
{
_res.statusCode = ResponseCode::PARSING_ERROR;
_res.message = "Parsing failed with error code " + std::string(GetParseError_En(doc.GetParseError()));
return;
}
else {
std::cerr << "Parsing failed with error code " << GetParseError_En(doc.GetParseError()) << std::endl;

if (!doc.HasMember("games") || !doc["games"].IsArray()) {
_res.statusCode = ResponseCode::MISSING_FIELD_ERROR;
_res.message = "JSON does not contain a valid 'games' array field.\n";
return;
}

const Value& gamesArray = doc["games"];

if (gamesArray.Empty()) {
// No popup, just display a a message on the screen
_res.statusCode = ResponseCode::NO_GAMES_FOUND;
_res.message = "No games played yet !";
return;
}
// Iterate over the array and do whatever you need with each game object
for (SizeType i = 0; i < gamesArray.Size(); ++i) {
const Value& game = gamesArray[i];
GamesData gameData;

// get white username and rating
if ((gameData.whiteUsername = extractString(game["white"], "username")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get white username\n";
}
if ((gameData.whiteRating = extractInt(game["white"], "rating")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get white rating\n";
}
// get black username and rating
if ((gameData.blackUsername = extractString(game["black"], "username")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get black username\n";
}
if ((gameData.blackRating = extractInt(game["black"], "rating")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get black rating\n";
}
// get time_class
if ((gameData.timeClass = extractString(game, "time_class")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get time class\n";
}
//get pgn
if ((gameData.pgn = extractString(game, "pgn")) == "")
{
_res.statusCode = ResponseCode::DATA_EXTRACTION_ERROR;
_res.message = "Unable to get pgn\n";
}
// find if player has played as white or black and display if he won or lost the game.
if (gameData.whiteUsername == username)
gameData.result = game["white"]["result"] != "win" ? "Lost" : "Won";
else
gameData.result = game["black"]["result"] != "win" ? "Lost" : "Won";

// insert this game data in the vector
_gamesData.push_back(gameData);
}
_res.statusCode = ResponseCode::OK;
}

//------------------------------------------------------------------------------
Expand All @@ -116,18 +133,18 @@ Logic::GamesPlayedWithinPeriod(const std::string& username, const std::string& y
{
std::string url = "https://api.chess.com/pub/player/" + to_lower(username) + "/games/" + year + '/' + month;
cpr::Response res = cpr::Get(cpr::Url{ url });
Document doc;

if (res.status_code == cpr::status::HTTP_OK) {
Document doc;

doc.Parse(res.text.c_str());
GetInfosFromListOfGamesPlayed(username, doc);
_res.errorCode = EXIT_SUCCESS;
_res.gamesData = _gamesData;
} else {
_res.errorCode = res.status_code;
_res.errorMessage = res.error.message;
if (res.status_code == cpr::status::HTTP_NOT_FOUND)
{
_res.statusCode = NO_GAMES_FOUND;
_res.message = "No games played yet !";
return _res;
}

doc.Parse(res.text.c_str());
GetInfosFromListOfGamesPlayed(username, doc);
_res.gamesData = _gamesData;
return _res;
}

Expand Down
106 changes: 86 additions & 20 deletions src/UI/GamesPlayedPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@

#include "../../include/UI/GamesPlayedPage.hpp"


static
std::string getCurrentMonth(void)
{
std::time_t currentTime;
std::tm currentTm;
std::time(&currentTime);

if (localtime_s(&currentTm, &currentTime) != 0) {
std::cerr << "Error: Unable to get the current time." << std::endl;
return "";
}

int currentMonth = currentTm.tm_mon + 1;

if (currentMonth >= 1 && currentMonth <= 9)
{
return "0" + std::to_string(currentMonth);
}
return std::to_string(currentMonth);
}

static
std::string getCurrentYear(void)
{
std::time_t currentTime;
std::tm currentTm;
std::time(&currentTime);

if (localtime_s(&currentTm, &currentTime) != 0) {
std::cerr << "Error: Unable to get the current time." << std::endl;
return "";
}
return std::to_string(currentTm.tm_year + 1900);
}

namespace Stuckfish
{
//------------------------------------------------------------------------------
Expand All @@ -14,6 +50,11 @@ void GamesPlayedPage::OnUIRender()
{
float windowPosX = 20;
float windowPosY = 20;
std::string month = getCurrentMonth();
std::string year = getCurrentYear();

// raise an error if year/month is empty.


ImVec2 windowPos(windowPosX, windowPosY);
ImGui::SetNextWindowPos(windowPos, ImGuiCond_Always);
Expand All @@ -26,38 +67,53 @@ void GamesPlayedPage::OnUIRender()
// page title
ImGui::PushFont(_app._robotoFontHeader);
ImGui::SetCursorPos(ImVec2(windowMiddlePos.x - 170, windowMiddlePos.y - 340));
ImGui::Text("Games played: April 2024"); // TODO: get the current month using a C++ library
std::string pageTitleLabel = "Games played: " + month + '/' + year;
ImGui::Text(pageTitleLabel.c_str()); // TODO: get the current year using a C++ library
ImGui::PopFont();

// list of all games played in the specified month from which we can extract:
// the player and his ELO and same for the opponent, the game status for the player (win/lose/draw)
// the game time control (rapid, blitz, bullet)
ImVec2 buttonSize(confirmButtonSizeX*5, confirmButtonSizeY);
ImVec2 buttonSize(confirmButtonSizeX * 5, confirmButtonSizeY);
ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
if (!_hasRetrievedGames)
{
_res = _logic.GamesPlayedWithinPeriod(_userdata.username, "2024", "05");

// handle every errors here
if (_res.errorCode == EXIT_FAILURE)
{
_errorOccured = true;
_errorMessage = _res.errorMessage;
}
// just use the current month automatically for now;
_res = _logic.GamesPlayedWithinPeriod(_userdata.username, year, month);
_hasRetrievedGames = true;
}

// Begin a scrollable region
ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);
for (auto& game : _res.gamesData) {
// only when switch between months will be supported.
if (_res.statusCode == ResponseCode::NO_GAMES_FOUND)
{
ImGui::PushFont(_app._robotoFontHeader);
ImGui::SetCursorPos(ImVec2(windowMiddlePos.x - 170, windowMiddlePos.y));
ImGui::Text("No game found for the specified period !");
ImGui::PopFont();
ImGui::EndChild();
ImGui::End();
return;
}

// every other errors.
if (_res.statusCode != ResponseCode::OK)
{
ImGui::PushFont(_app._robotoFontHeader);
ImGui::SetCursorPos(ImVec2(windowMiddlePos.x - 170, windowMiddlePos.y));
ImGui::Text("Unexpected error occured while fetching your games !");
ImGui::PopFont();
ImGui::EndChild();
ImGui::End();
return;
}

for (const auto& game : _res.gamesData) {
std::string buttonLabel = game.whiteUsername + "(" + game.whiteRating + ") vs " +
game.blackUsername + "(" + game.blackRating + ") - " +
game.result + " - " + game.timeClass;

ImVec2 buttonPos = ImVec2((ImGui::GetWindowSize().x - buttonSize.x) / 2.0f, ImGui::GetCursorPosY());
ImGui::SetCursorPos(buttonPos);
if (ImGui::Button(buttonLabel.c_str(), buttonSize)) {
// link to the next page maybe ?
std::cout << game.pgn;
_selectedGameData = game;
OnUpdate();
}
}
ImGui::EndChild();
Expand All @@ -68,7 +124,9 @@ void GamesPlayedPage::OnUIRender()
/**
*/
void GamesPlayedPage::OnUpdate()
{}
{
OnDetach();
}

//------------------------------------------------------------------------------
/**
Expand All @@ -78,8 +136,16 @@ void GamesPlayedPage::OnAttach()

//------------------------------------------------------------------------------
/**
* Consider writing OnDetach() directly in Page.hpp
*/
void GamesPlayedPage::OnDetach()
{}
{
std::vector<std::shared_ptr<Page>>& pageStack = _app.GetPageStack();

if (!pageStack.empty())
pageStack.erase(pageStack.begin());

return;
}

}

0 comments on commit d4fb6ce

Please sign in to comment.