comparison src/core/anime_db.cc @ 177:122fad646f81

anime/db: upgrade to c++17 style, make things easier to read
author Paper <mrpapersonic@gmail.com>
date Fri, 01 Dec 2023 13:32:29 -0500
parents 121c2d5b321f
children 9613d72b097e
comparison
equal deleted inserted replaced
176:121c2d5b321f 177:122fad646f81
12 #include <iostream> 12 #include <iostream>
13 #include <exception> 13 #include <exception>
14 14
15 namespace Anime { 15 namespace Anime {
16 16
17 int Database::GetTotalAnimeAmount() { 17 size_t Database::GetTotalAnimeAmount() {
18 int total = 0; 18 size_t total = 0;
19 for (const auto& a : items) { 19
20 if (a.second.IsInUserList()) 20 for (const auto& [id, anime] : items)
21 if (anime.IsInUserList())
21 total++; 22 total++;
22 } 23
23 return total; 24 return total;
24 } 25 }
25 26
26 int Database::GetListsAnimeAmount(ListStatus status) { 27 size_t Database::GetListsAnimeAmount(ListStatus status) {
27 if (status == ListStatus::NOT_IN_LIST) 28 if (status == ListStatus::NOT_IN_LIST)
28 return 0; 29 return 0;
29 int total = 0; 30
30 for (const auto& a : items) { 31 size_t total = 0;
31 if (a.second.IsInUserList() && a.second.GetUserStatus() == status) 32
33 for (const auto& [id, anime] : items)
34 if (anime.IsInUserList() && anime.GetUserStatus() == status)
32 total++; 35 total++;
33 } 36
34 return total; 37 return total;
35 } 38 }
36 39
37 int Database::GetTotalEpisodeAmount() { 40 size_t Database::GetTotalEpisodeAmount() {
38 int total = 0; 41 size_t total = 0;
39 for (const auto& a : items) { 42
40 if (a.second.IsInUserList()) { 43 for (const auto& [id, anime] : items)
41 total += a.second.GetUserRewatchedTimes() * a.second.GetEpisodes(); 44 if (anime.IsInUserList())
42 total += a.second.GetUserProgress(); 45 total += anime.GetUserRewatchedTimes() * anime.GetEpisodes() + anime.GetUserProgress();
43 } 46
44 }
45 return total; 47 return total;
46 } 48 }
47 49
48 /* Returns the total watched amount in minutes. */ 50 /* Returns the total watched amount in minutes. */
49 int Database::GetTotalWatchedAmount() { 51 size_t Database::GetTotalWatchedAmount() {
50 int total = 0; 52 size_t total = 0;
51 for (const auto& a : items) { 53
52 if (a.second.IsInUserList()) { 54 for (const auto& [id, anime] : items)
53 total += a.second.GetDuration() * a.second.GetUserProgress(); 55 if (anime.IsInUserList())
54 total += a.second.GetEpisodes() * a.second.GetDuration() * a.second.GetUserRewatchedTimes(); 56 total += anime.GetDuration() * anime.GetUserProgress()
55 } 57 + anime.GetEpisodes() * anime.GetDuration() * anime.GetUserRewatchedTimes();
56 } 58
57 return total; 59 return total;
58 } 60 }
59 61
60 /* Returns the total planned amount in minutes. 62 /* Returns the total planned amount in minutes.
61 Note that we should probably limit progress to the 63 Note that we should probably limit progress to the
62 amount of episodes, as AniList will let you 64 amount of episodes, as AniList will let you
63 set episode counts up to 32768. But that should 65 set episode counts up to 32768. But that should
64 rather be handled elsewhere. */ 66 rather be handled elsewhere. */
65 int Database::GetTotalPlannedAmount() { 67 size_t Database::GetTotalPlannedAmount() {
66 int total = 0; 68 size_t total = 0;
67 for (const auto& a : items) { 69
68 if (a.second.IsInUserList()) 70 for (const auto& [id, anime] : items)
69 total += a.second.GetDuration() * (a.second.GetEpisodes() - a.second.GetUserProgress()); 71 if (anime.IsInUserList())
70 } 72 total += anime.GetDuration() * (anime.GetEpisodes() - anime.GetUserProgress());
71 return total; 73
72 } 74 return total;
73 75 }
74 /* In Taiga this is called a mean, but "average" is 76
77 /* In Taiga this is called the mean, but "average" is
75 what's primarily used in conversation, at least 78 what's primarily used in conversation, at least
76 in the U.S. */ 79 in the U.S. */
77 double Database::GetAverageScore() { 80 double Database::GetAverageScore() {
78 double avg = 0; 81 double avg = 0;
79 int amt = 0; 82 size_t amt = 0;
80 for (const auto& a : items) { 83
81 if (a.second.IsInUserList() && a.second.GetUserScore()) { 84 for (const auto& [id, anime] : items) {
82 avg += a.second.GetUserScore(); 85 if (anime.IsInUserList() && anime.GetUserScore()) {
86 avg += anime.GetUserScore();
83 amt++; 87 amt++;
84 } 88 }
85 } 89 }
86 return avg / amt; 90 return avg / amt;
87 } 91 }
88 92
89 double Database::GetScoreDeviation() { 93 double Database::GetScoreDeviation() {
90 double squares_sum = 0, avg = GetAverageScore(); 94 double squares_sum = 0, avg = GetAverageScore();
91 int amt = 0; 95 size_t amt = 0;
92 for (const auto& a : items) { 96
93 if (a.second.IsInUserList() && a.second.GetUserScore()) { 97 for (const auto& [id, anime] : items) {
94 squares_sum += std::pow(static_cast<double>(a.second.GetUserScore()) - avg, 2); 98 if (anime.IsInUserList() && anime.GetUserScore()) {
99 squares_sum += std::pow(static_cast<double>(anime.GetUserScore()) - avg, 2);
95 amt++; 100 amt++;
96 } 101 }
97 } 102 }
103
98 return (amt > 0) ? std::sqrt(squares_sum / amt) : 0; 104 return (amt > 0) ? std::sqrt(squares_sum / amt) : 0;
99 } 105 }
100 106
101 template <typename T, typename U> 107 template<typename T, typename U>
102 static T get_lowest_in_map(const std::unordered_map<T, U>& map) { 108 static T get_lowest_in_map(const std::unordered_map<T, U>& map) {
103 if (map.size() <= 0) 109 if (map.size() <= 0)
104 return 0; 110 return 0;
105 111
106 T id = 0; 112 T id = 0;
159 165
160 static bool GetListDataAsJSON(const Anime& anime, nlohmann::json& json) { 166 static bool GetListDataAsJSON(const Anime& anime, nlohmann::json& json) {
161 if (!anime.IsInUserList()) 167 if (!anime.IsInUserList())
162 return false; 168 return false;
163 169
170 // clang-format off
164 json = { 171 json = {
165 {"status", Translate::ToString(anime.GetUserStatus())}, 172 {"status", Translate::ToString(anime.GetUserStatus())},
166 {"progress", anime.GetUserProgress()}, 173 {"progress", anime.GetUserProgress()},
167 {"score", anime.GetUserScore()}, 174 {"score", anime.GetUserScore()},
168 {"started", anime.GetUserDateStarted().GetAsAniListJson()}, 175 {"started", anime.GetUserDateStarted().GetAsAniListJson()},
171 {"rewatched_times", anime.GetUserRewatchedTimes()}, 178 {"rewatched_times", anime.GetUserRewatchedTimes()},
172 {"rewatching", anime.GetUserIsRewatching()}, 179 {"rewatching", anime.GetUserIsRewatching()},
173 {"updated", anime.GetUserTimeUpdated()}, 180 {"updated", anime.GetUserTimeUpdated()},
174 {"notes", anime.GetUserNotes()} 181 {"notes", anime.GetUserNotes()}
175 }; 182 };
183 // clang-format on
176 184
177 return true; 185 return true;
178 } 186 }
179 187
180 static bool GetAnimeAsJSON(const Anime& anime, nlohmann::json& json) { 188 static bool GetAnimeAsJSON(const Anime& anime, nlohmann::json& json) {
189 // clang-format off
181 json = { 190 json = {
182 {"id", anime.GetId()}, 191 {"id", anime.GetId()},
183 {"title", { 192 {"title", {
184 {"native", anime.GetNativeTitle()}, 193 {"native", anime.GetNativeTitle()},
185 {"romaji", anime.GetRomajiTitle()}, 194 {"romaji", anime.GetRomajiTitle()},
196 {"audience_score", anime.GetAudienceScore()}, 205 {"audience_score", anime.GetAudienceScore()},
197 {"synopsis", anime.GetSynopsis()}, 206 {"synopsis", anime.GetSynopsis()},
198 {"duration", anime.GetDuration()}, 207 {"duration", anime.GetDuration()},
199 {"poster_url", anime.GetPosterUrl()} 208 {"poster_url", anime.GetPosterUrl()}
200 }; 209 };
210 // clang-format on
201 211
202 nlohmann::json user; 212 nlohmann::json user;
203 if (GetListDataAsJSON(anime, user)) 213 if (GetListDataAsJSON(anime, user))
204 json.push_back({"list_data", user}); 214 json.push_back({"list_data", user});
205 215
248 anime.SetUserNotes(JSON::GetString<std::string>(json, "/notes"_json_pointer, "")); 258 anime.SetUserNotes(JSON::GetString<std::string>(json, "/notes"_json_pointer, ""));
249 259
250 return true; 260 return true;
251 } 261 }
252 262
253 bool Database::ParseAnimeInfoJSON(const nlohmann::json& json) { 263 static bool ParseAnimeInfoJSON(const nlohmann::json& json, Database& database) {
254 int id = JSON::GetNumber(json, "/id"_json_pointer, 0); 264 int id = JSON::GetNumber(json, "/id"_json_pointer, 0);
255 if (!id) 265 if (!id)
256 return false; 266 return false;
257 267
258 Anime& anime = items[id]; 268 Anime& anime = database.items[id];
259 269
260 anime.SetId(id); 270 anime.SetId(id);
261 anime.SetNativeTitle(JSON::GetString<std::string>(json, "/title/native"_json_pointer, "")); 271 anime.SetNativeTitle(JSON::GetString<std::string>(json, "/title/native"_json_pointer, ""));
262 anime.SetRomajiTitle(JSON::GetString<std::string>(json, "/title/romaji"_json_pointer, "")); 272 anime.SetRomajiTitle(JSON::GetString<std::string>(json, "/title/romaji"_json_pointer, ""));
263 anime.SetEnglishTitle(JSON::GetString<std::string>(json, "/title/english"_json_pointer, "")); 273 anime.SetEnglishTitle(JSON::GetString<std::string>(json, "/title/english"_json_pointer, ""));
280 return true; 290 return true;
281 } 291 }
282 292
283 bool Database::ParseDatabaseJSON(const nlohmann::json& json) { 293 bool Database::ParseDatabaseJSON(const nlohmann::json& json) {
284 for (const auto& anime_json : json) 294 for (const auto& anime_json : json)
285 ParseAnimeInfoJSON(anime_json); 295 ParseAnimeInfoJSON(anime_json, *this);
286 296
287 return true; 297 return true;
288 } 298 }
289 299
290 bool Database::LoadDatabaseFromFile() { 300 bool Database::LoadDatabaseFromDisk() {
291 std::filesystem::path db_path = Filesystem::GetAnimeDBPath(); 301 std::filesystem::path db_path = Filesystem::GetAnimeDBPath();
292 Filesystem::CreateDirectories(db_path); 302 Filesystem::CreateDirectories(db_path);
293 303
294 std::ifstream db_file(db_path); 304 std::ifstream db_file(db_path);
295 if (!db_file) 305 if (!db_file)