changeset 185:62e336597bb7

anime list: add support for different score formats
author Paper <mrpapersonic@gmail.com>
date Tue, 05 Dec 2023 13:45:23 -0500
parents 09492158bcc5
children 6ef31dbb90ca
files include/core/anime.h include/core/config.h include/gui/translate/anime.h src/core/anime.cc src/core/config.cc src/gui/pages/anime_list.cc src/gui/translate/anime.cc src/services/anilist.cc
diffstat 8 files changed, 93 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/include/core/anime.h	Mon Dec 04 13:44:42 2023 -0500
+++ b/include/core/anime.h	Tue Dec 05 13:45:23 2023 -0500
@@ -62,10 +62,18 @@
 	NB_SERVICES
 };
 
+enum class ScoreFormat {
+	POINT_100, // 0-100
+	POINT_10_DECIMAL, // 0.0-10.0
+	POINT_10, // 0-10
+	POINT_5, // 0-5, should be represented in stars
+	POINT_3 // 1-3, should be represented in smileys
+};
+
 struct ListInformation {
 		int id = 0;
 		int progress = 0;
-		int score = 0; // always in the AniList 100-based form internally
+		int score = 0; // note that this will ALWAYS be in POINT_100 format and must be converted
 		ListStatus status = ListStatus::NOT_IN_LIST;
 		Date started;
 		Date completed;
@@ -103,6 +111,7 @@
 		ListStatus GetUserStatus() const;
 		int GetUserProgress() const;
 		int GetUserScore() const;
+		std::string GetUserPresentableScore() const;
 		Date GetUserDateStarted() const;
 		Date GetUserDateCompleted() const;
 		bool GetUserIsPrivate() const;
--- a/include/core/config.h	Mon Dec 04 13:44:42 2023 -0500
+++ b/include/core/config.h	Tue Dec 05 13:45:23 2023 -0500
@@ -28,6 +28,7 @@
 		struct {
 			public:
 				Anime::TitleLanguage language;
+				Anime::ScoreFormat score_format;
 				bool display_aired_episodes;
 				bool display_available_episodes;
 				bool highlight_anime_if_available;
--- a/include/gui/translate/anime.h	Mon Dec 04 13:44:42 2023 -0500
+++ b/include/gui/translate/anime.h	Tue Dec 05 13:45:23 2023 -0500
@@ -1,3 +1,6 @@
+#ifndef __gui__translate__anime_h
+#define __gui__translate__anime_h
+
 #include "core/anime.h"
 
 namespace Translate {
@@ -8,6 +11,7 @@
 std::string ToString(const Anime::SeriesStatus status);
 std::string ToString(const Anime::Services service);
 std::string ToString(const Anime::TitleLanguage language);
+std::string ToString(const Anime::ScoreFormat format);
 
 std::string ToLocalString(const Anime::ListStatus status);
 std::string ToLocalString(const Anime::SeriesFormat format);
@@ -22,5 +26,8 @@
 Anime::SeriesStatus ToSeriesStatus(const std::string& str);
 Anime::Services ToService(const std::string& str);
 Anime::TitleLanguage ToLanguage(const std::string& str);
+Anime::ScoreFormat ToScoreFormat(const std::string& str);
 
 } // namespace Translate
+
+#endif // __gui__translate__anime_h
--- a/src/core/anime.cc	Mon Dec 04 13:44:42 2023 -0500
+++ b/src/core/anime.cc	Tue Dec 05 13:45:23 2023 -0500
@@ -42,6 +42,45 @@
 	return list_info_->score;
 }
 
+std::string Anime::GetUserPresentableScore() const {
+	assert(list_info_.get());
+	const int score = list_info_->score;
+	if (score == 0)
+		return "";
+
+	switch (session.config.anime_list.score_format) {
+		case ScoreFormat::POINT_10_DECIMAL: {
+			std::ostringstream out;
+			out.precision(1);
+			out << std::fixed << (static_cast<double>(score) / 10);
+			return std::move(out).str();
+		}
+		case ScoreFormat::POINT_10:
+			return std::to_string(score / 10);
+		case ScoreFormat::POINT_5: {
+			std::string stars = "";
+
+			for (int i = 0; i < 100; i += 20)
+				stars.append((i <= score) ? "★" : "☆");
+
+			return stars;
+		}
+		case ScoreFormat::POINT_3: {
+			if (score >= 100)
+				return ":)";
+			else if (score >= 66)
+				return ":|";
+			else if (score >= 33)
+				return ":(";
+			else
+				return "";
+		}
+		default:
+		case ScoreFormat::POINT_100:
+			return std::to_string(score);
+	}
+}
+
 Date Anime::GetUserDateStarted() const {
 	assert(list_info_.get());
 	return list_info_->started;
--- a/src/core/config.cc	Mon Dec 04 13:44:42 2023 -0500
+++ b/src/core/config.cc	Tue Dec 05 13:45:23 2023 -0500
@@ -35,6 +35,7 @@
 
 	service = Translate::ToService(INI::GetIniValue<std::string>(ini, "General", "Service", "None"));
 
+	anime_list.score_format = Translate::ToScoreFormat(INI::GetIniValue<std::string>(ini, "Anime List", "Score format", "POINT_100"));
 	anime_list.language = Translate::ToLanguage(INI::GetIniValue<std::string>(ini, "Anime List", "Title language", "Romaji"));
 	anime_list.display_aired_episodes = INI::GetIniValue<bool>(ini, "Anime List", "Display only aired episodes", true);
 	anime_list.display_available_episodes = INI::GetIniValue<bool>(ini, "Anime List", "Display only available episodes in library", true);
@@ -68,9 +69,8 @@
 			return false;
 
 		recognition.players.reserve(players.size());
-		for (const auto& player : players) {
+		for (const auto& player : players)
 			recognition.players.push_back({true, player});
-		}
 	}
 
 	for (auto& [enabled, player] : recognition.players) {
@@ -103,6 +103,7 @@
 	INI::SetIniValue(ini, "General", "Service", service);
 	INI::SetIniValue(ini, "General", "Locale", locale.GetLocale().name());
 
+	INI::SetIniValue(ini, "Anime List", "Score format", Translate::ToString(anime_list.score_format));
 	INI::SetIniValue(ini, "Anime List", "Title language", anime_list.language);
 	INI::SetIniValue(ini, "Anime List", "Display only aired episodes", anime_list.display_aired_episodes);
 	INI::SetIniValue(ini, "Anime List", "Display only available episodes in library", anime_list.display_available_episodes);
--- a/src/gui/pages/anime_list.cc	Mon Dec 04 13:44:42 2023 -0500
+++ b/src/gui/pages/anime_list.cc	Tue Dec 05 13:45:23 2023 -0500
@@ -107,12 +107,12 @@
 	switch (role) {
 		case Qt::DisplayRole:
 			switch (index.column()) {
-				case AL_TITLE: return QString::fromUtf8(list[index.row()].GetUserPreferredTitle().c_str());
+				case AL_TITLE: return Strings::ToQString(list[index.row()].GetUserPreferredTitle());
 				case AL_PROGRESS:
 					return QString::number(list[index.row()].GetUserProgress()) + "/" +
 						   QString::number(list[index.row()].GetEpisodes());
 				case AL_EPISODES: return list[index.row()].GetEpisodes();
-				case AL_SCORE: return list[index.row()].GetUserScore();
+				case AL_SCORE: return Strings::ToQString(list[index.row()].GetUserPresentableScore());
 				case AL_TYPE: return Strings::ToQString(Translate::ToString(list[index.row()].GetFormat()));
 				case AL_SEASON:
 					return Strings::ToQString(Translate::ToString(list[index.row()].GetSeason())) + " " +
@@ -124,15 +124,16 @@
 					if (list[index.row()].GetUserTimeUpdated() == 0)
 						return QString("-");
 					Time::Duration duration(Time::GetSystemTime() - list[index.row()].GetUserTimeUpdated());
-					return QString::fromUtf8(duration.AsRelativeString().c_str());
+					return Strings::ToQString(duration.AsRelativeString());
 				}
-				case AL_NOTES: return QString::fromUtf8(list[index.row()].GetUserNotes().c_str());
+				case AL_NOTES: return Strings::ToQString(list[index.row()].GetUserNotes());
 				default: return "";
 			}
 			break;
 		case Qt::UserRole:
 			switch (index.column()) {
 				case AL_PROGRESS: return list[index.row()].GetUserProgress();
+				case AL_SCORE: return list[index.row()].GetUserScore();
 				case AL_TYPE: return static_cast<int>(list[index.row()].GetFormat());
 				case AL_SEASON: return list[index.row()].GetAirDate().GetAsQDate();
 				case AL_AVG_SCORE: return list[index.row()].GetAudienceScore();
--- a/src/gui/translate/anime.cc	Mon Dec 04 13:44:42 2023 -0500
+++ b/src/gui/translate/anime.cc	Tue Dec 05 13:45:23 2023 -0500
@@ -71,6 +71,17 @@
 	}
 }
 
+std::string ToString(const Anime::ScoreFormat format) {
+	switch (format) {
+		case Anime::ScoreFormat::POINT_3: return "POINT_3";
+		case Anime::ScoreFormat::POINT_5: return "POINT_5";
+		case Anime::ScoreFormat::POINT_10: return "POINT_10";
+		case Anime::ScoreFormat::POINT_10_DECIMAL: return "POINT_10_DECIMAL";
+		default:
+		case Anime::ScoreFormat::POINT_100: return "POINT_100";
+	}
+}
+
 Anime::ListStatus ToListStatus(const std::string& str) {
 	const std::unordered_map<std::string, Anime::ListStatus> map = {
 	    {"Currently watching", Anime::ListStatus::CURRENT},
@@ -150,6 +161,20 @@
 	return map.at(str);
 }
 
+Anime::ScoreFormat ToScoreFormat(const std::string& str) {
+	const std::unordered_map<std::string, Anime::ScoreFormat> map = {
+	    {"POINT_3", Anime::ScoreFormat::POINT_3},
+	    {"POINT_5", Anime::ScoreFormat::POINT_5},
+	    {"POINT_10", Anime::ScoreFormat::POINT_10},
+	    {"POINT_10_DECIMAL", Anime::ScoreFormat::POINT_10_DECIMAL},
+	    {"POINT_100", Anime::ScoreFormat::POINT_100}
+	};
+
+	if (map.find(str) == map.end())
+		return Anime::ScoreFormat::POINT_100;
+	return map.at(str);
+}
+
 /* Localized versions of ToString() functions */
 
 std::string ToLocalString(const Anime::ListStatus status) {
--- a/src/services/anilist.cc	Mon Dec 04 13:44:42 2023 -0500
+++ b/src/services/anilist.cc	Tue Dec 05 13:45:23 2023 -0500
@@ -18,13 +18,13 @@
 
 #include <iostream>
 
-#define CLIENT_ID "13706"
-
 using namespace nlohmann::literals::json_literals;
 
 namespace Services {
 namespace AniList {
 
+constexpr int CLIENT_ID = 13706;
+
 class Account {
 	public:
 		std::string Username() const { return session.config.auth.anilist.username; }
@@ -305,7 +305,7 @@
 bool AuthorizeUser() {
 	/* Prompt for PIN */
 	QDesktopServices::openUrl(
-		QUrl("https://anilist.co/api/v2/oauth/authorize?client_id=" CLIENT_ID "&response_type=token"));
+		QUrl(Strings::ToQString("https://anilist.co/api/v2/oauth/authorize?client_id=" + std::to_string(CLIENT_ID) + "&response_type=token")));
 
 	bool ok;
 	QString token = QInputDialog::getText(