10
|
1 #include "core/anime_db.h"
|
|
2 #include "core/anime.h"
|
64
|
3 #include "core/strings.h"
|
|
4 #include <QDebug>
|
10
|
5
|
|
6 namespace Anime {
|
|
7
|
|
8 int Database::GetTotalAnimeAmount() {
|
|
9 int total = 0;
|
11
|
10 for (const auto& a : items) {
|
|
11 if (a.second.IsInUserList())
|
10
|
12 total++;
|
|
13 }
|
|
14 return total;
|
|
15 }
|
|
16
|
|
17 int Database::GetListsAnimeAmount(ListStatus status) {
|
|
18 if (status == ListStatus::NOT_IN_LIST)
|
|
19 return 0;
|
|
20 int total = 0;
|
11
|
21 for (const auto& a : items) {
|
|
22 if (a.second.IsInUserList() && a.second.GetUserStatus() == status)
|
10
|
23 total++;
|
|
24 }
|
|
25 return total;
|
|
26 }
|
|
27
|
|
28 int Database::GetTotalEpisodeAmount() {
|
|
29 int total = 0;
|
11
|
30 for (const auto& a : items) {
|
|
31 if (a.second.IsInUserList()) {
|
|
32 total += a.second.GetUserRewatchedTimes() * a.second.GetEpisodes();
|
|
33 total += a.second.GetUserProgress();
|
10
|
34 }
|
|
35 }
|
|
36 return total;
|
|
37 }
|
|
38
|
|
39 /* Returns the total watched amount in minutes. */
|
|
40 int Database::GetTotalWatchedAmount() {
|
|
41 int total = 0;
|
11
|
42 for (const auto& a : items) {
|
|
43 if (a.second.IsInUserList()) {
|
|
44 total += a.second.GetDuration() * a.second.GetUserProgress();
|
|
45 total += a.second.GetEpisodes() * a.second.GetDuration() * a.second.GetUserRewatchedTimes();
|
10
|
46 }
|
|
47 }
|
|
48 return total;
|
|
49 }
|
|
50
|
|
51 /* Returns the total planned amount in minutes.
|
|
52 Note that we should probably limit progress to the
|
|
53 amount of episodes, as AniList will let you
|
|
54 set episode counts up to 32768. But that should
|
|
55 rather be handled elsewhere. */
|
|
56 int Database::GetTotalPlannedAmount() {
|
|
57 int total = 0;
|
11
|
58 for (const auto& a : items) {
|
|
59 if (a.second.IsInUserList())
|
|
60 total += a.second.GetDuration() * (a.second.GetEpisodes() - a.second.GetUserProgress());
|
10
|
61 }
|
|
62 return total;
|
|
63 }
|
|
64
|
83
|
65 /* In Taiga this is called a mean, but "average" is
|
|
66 what's primarily used in conversation, at least
|
|
67 in the U.S. */
|
10
|
68 double Database::GetAverageScore() {
|
|
69 double avg = 0;
|
|
70 int amt = 0;
|
11
|
71 for (const auto& a : items) {
|
|
72 if (a.second.IsInUserList() && a.second.GetUserScore()) {
|
|
73 avg += a.second.GetUserScore();
|
10
|
74 amt++;
|
|
75 }
|
|
76 }
|
|
77 return avg / amt;
|
|
78 }
|
|
79
|
|
80 double Database::GetScoreDeviation() {
|
|
81 double squares_sum = 0, avg = GetAverageScore();
|
|
82 int amt = 0;
|
11
|
83 for (const auto& a : items) {
|
|
84 if (a.second.IsInUserList() && a.second.GetUserScore()) {
|
64
|
85 squares_sum += std::pow(static_cast<double>(a.second.GetUserScore()) - avg, 2);
|
10
|
86 amt++;
|
|
87 }
|
|
88 }
|
|
89 return (amt > 0) ? std::sqrt(squares_sum / amt) : 0;
|
|
90 }
|
|
91
|
83
|
92 template <typename T, typename U>
|
|
93 static T get_lowest_in_map(const std::unordered_map<T, U>& map) {
|
|
94 if (map.size() <= 0)
|
|
95 return 0;
|
|
96 T id;
|
|
97 U ret = std::numeric_limits<U>::max();
|
|
98 for (const auto& t : map) {
|
|
99 if (t.second < ret) {
|
|
100 ret = t.second;
|
|
101 id = t.first;
|
|
102 }
|
|
103 }
|
|
104 return id;
|
|
105 }
|
|
106
|
|
107 /* This is really fugly but WHO CARES :P
|
|
108
|
|
109 This sort of ""advanced"" algorithm is only in effect because
|
|
110 there are some special cases, e.g. Another and Re:ZERO, where
|
|
111 we get the wrong match, so we have to create Advanced Techniques
|
|
112 to solve this
|
|
113
|
|
114 This algorithm:
|
|
115 1. searches each anime item for a match to the preferred title
|
|
116 AND all synonyms and marks those matches with
|
|
117 `synonym.length() - (synonym.find(needle) + needle.length());`
|
|
118 which, on a title that exactly matches, will be 0
|
|
119 2. returns the id of the match that is the lowest, which will most
|
|
120 definitely match anything that exactly matches the title of the
|
|
121 filename */
|
|
122 int Database::GetAnimeFromTitle(const std::string& title) {
|
64
|
123 if (title.empty())
|
|
124 return 0;
|
83
|
125 std::unordered_map<int, long long> map;
|
64
|
126 for (const auto& a : items) {
|
83
|
127 long long ret = a.second.GetUserPreferredTitle().find(title);
|
|
128 if (ret != static_cast<long long>(std::string::npos)) {
|
|
129 map[a.second.GetId()] = a.second.GetUserPreferredTitle().length() - (ret + title.length());
|
|
130 continue;
|
|
131 }
|
|
132 for (const auto& synonym : a.second.GetTitleSynonyms()) {
|
|
133 ret = synonym.find(title);
|
|
134 if (ret != static_cast<long long>(std::string::npos)) {
|
|
135 map[a.second.GetId()] = synonym.length() - (ret + title.length());
|
|
136 continue;
|
64
|
137 }
|
|
138 }
|
|
139 }
|
83
|
140 return get_lowest_in_map(map);
|
64
|
141 }
|
|
142
|
11
|
143 Database db;
|
|
144
|
9
|
145 } // namespace Anime |