1 /*
2 SPDX-FileCopyrightText: 2013-2014 Andreas Cord-Landwehr <cordlandwehr@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6
7 #include "profilemanager.h"
8 #include "learner.h"
9 #include "storage.h"
10
11 #include "liblearner_debug.h"
12 #include <KConfigCore/KConfig>
13 #include <KConfigCore/KConfigGroup>
14 #include <KLocalizedString>
15 #include <QFileDialog>
16 #include <QList>
17 #include <memory>
18
19 using namespace LearnerProfile;
20
21 /// BEGIN: ProfileManagerPrivate
22 namespace LearnerProfile
23 {
24 class ProfileManagerPrivate
25 {
26 public:
27 ProfileManagerPrivate();
~ProfileManagerPrivate()28 ~ProfileManagerPrivate()
29 {
30 }
31
32 void sync();
33
34 QList<Learner *> m_profiles;
35 Learner *m_activeProfile;
36 QList<LearningGoal *> m_goals;
37 std::unique_ptr<KConfig> m_config;
38 Storage m_storage;
39 };
40 }
41
ProfileManagerPrivate()42 LearnerProfile::ProfileManagerPrivate::ProfileManagerPrivate()
43 : m_profiles(QList<Learner *>())
44 , m_activeProfile(nullptr)
45 , m_config(new KConfig(QStringLiteral("learnerprofilerc")))
46 {
47 // load all profiles from storage
48 m_goals.append(m_storage.loadGoals());
49 m_profiles.append(m_storage.loadProfiles(m_goals));
50
51 // set last used profile
52 KConfigGroup activeProfileGroup(m_config.get(), "ActiveProfile");
53 int lastProfileId = activeProfileGroup.readEntry("profileId", "0").toInt();
54 QList<int> activeGoalsCategories = activeProfileGroup.readEntry("activeGoalsCategories", QList<int>());
55 QList<QString> activeGoalsIdentifiers = activeProfileGroup.readEntry("activeGoalsIdentifiers", QList<QString>());
56 foreach (Learner *learner, m_profiles) {
57 if (learner->identifier() == lastProfileId) {
58 m_activeProfile = learner;
59 // set active goals
60 if (activeGoalsCategories.count() == activeGoalsIdentifiers.count()) {
61 for (int i = 0; i < activeGoalsCategories.count(); ++i) {
62 m_activeProfile->setActiveGoal(static_cast<Learner::Category>(activeGoalsCategories.at(i)), activeGoalsIdentifiers.at(i));
63 }
64 } else {
65 qCCritical(LIBLEARNER_LOG()) << "Inconsistent goal category / identifier pairs found: aborting.";
66 }
67 break;
68 }
69 }
70 if (m_activeProfile == nullptr) {
71 qCDebug(LIBLEARNER_LOG()) << "No last active profile found, falling back to first found profile";
72 if (m_profiles.size() > 0) {
73 m_activeProfile = m_profiles.at(0);
74 }
75 }
76 }
77
sync()78 void ProfileManagerPrivate::sync()
79 {
80 // sync last used profile data
81 if (m_activeProfile) {
82 KConfigGroup activeProfileGroup(m_config.get(), "ActiveProfile");
83 activeProfileGroup.writeEntry("profileId", m_activeProfile->identifier());
84
85 // compute activer learning goals by category
86 QList<int> goalCatogries;
87 QList<QString> goalIdentifiers;
88 // compute used goals
89 foreach (LearningGoal *goal, m_activeProfile->goals()) {
90 if (!goalCatogries.contains(static_cast<int>(goal->category()))) {
91 goalCatogries.append(static_cast<int>(goal->category()));
92 }
93 }
94 // compute active goals
95 foreach (int category, goalCatogries) {
96 goalIdentifiers.append(m_activeProfile->activeGoal(static_cast<Learner::Category>(category))->identifier());
97 }
98 activeProfileGroup.writeEntry("activeGoalsCategories", goalCatogries);
99 activeProfileGroup.writeEntry("activeGoalsIdentifiers", goalIdentifiers);
100 } else {
101 qCCritical(LIBLEARNER_LOG()) << "No active profile selected, aborting sync.";
102 }
103 m_config->sync();
104
105 // TODO only sync changed learner
106 foreach (Learner *learner, m_profiles) {
107 m_storage.storeProfile(learner);
108 }
109 }
110 /// END: ProfileManagerPrivate
111
ProfileManager(QObject * parent)112 ProfileManager::ProfileManager(QObject *parent)
113 : QObject(parent)
114 , d(new ProfileManagerPrivate)
115 {
116 connect(this, &ProfileManager::profileAdded, this, &ProfileManager::profileCountChanged);
117 connect(this, &ProfileManager::profileRemoved, this, &ProfileManager::profileCountChanged);
118
119 foreach (Learner *learner, d->m_profiles) {
120 connect(learner, &Learner::goalRemoved, this, &ProfileManager::removeLearningGoal);
121 }
122 }
123
~ProfileManager()124 ProfileManager::~ProfileManager()
125 {
126 for (auto learner : d->m_profiles) {
127 learner->deleteLater();
128 }
129 for (auto goal : d->m_goals) {
130 goal->deleteLater();
131 }
132 }
133
profiles() const134 QList<Learner *> ProfileManager::profiles() const
135 {
136 return d->m_profiles;
137 }
138
profileCount() const139 int ProfileManager::profileCount() const
140 {
141 return profiles().length();
142 }
143
openImageFileDialog()144 void ProfileManager::openImageFileDialog()
145 {
146 const QString imagePath = QFileDialog::getOpenFileName(nullptr, i18n("Open Image"), QLatin1String(""), i18n("Image Files (*.png *.jpg *.bmp)"));
147 d->m_activeProfile->importImage(imagePath);
148 }
149
addProfile(const QString & name)150 Learner *ProfileManager::addProfile(const QString &name)
151 {
152 Learner *learner = new Learner(this);
153 learner->setName(name);
154
155 // set id
156 int maxUsedId = 0;
157 foreach (Learner *cpLearner, d->m_profiles) {
158 if (cpLearner->identifier() >= maxUsedId) {
159 maxUsedId = cpLearner->identifier();
160 }
161 }
162 learner->setIdentifier(maxUsedId + 1);
163
164 d->m_profiles.append(learner);
165 d->m_storage.storeProfile(learner);
166 emit profileAdded(learner, d->m_profiles.count() - 1);
167
168 if (activeProfile() == nullptr) {
169 setActiveProfile(learner);
170 }
171
172 connect(learner, &Learner::goalRemoved, this, &ProfileManager::removeLearningGoal);
173
174 return learner;
175 }
176
removeProfile(Learner * learner)177 void ProfileManager::removeProfile(Learner *learner)
178 {
179 int index = d->m_profiles.indexOf(learner);
180 if (index < 0) {
181 qCWarning(LIBLEARNER_LOG()) << "Profile was not found, aborting";
182 return;
183 }
184 emit profileAboutToBeRemoved(index);
185 d->m_profiles.removeAt(index);
186 d->m_storage.removeProfile(learner);
187
188 if (d->m_activeProfile == learner) {
189 if (d->m_profiles.isEmpty()) {
190 setActiveProfile(nullptr);
191 } else {
192 setActiveProfile(d->m_profiles.at(0));
193 }
194 }
195 emit profileRemoved();
196 }
197
removeLearningGoal(Learner * learner,LearningGoal * goal)198 void ProfileManager::removeLearningGoal(Learner *learner, LearningGoal *goal)
199 {
200 d->m_storage.removeRelation(learner, goal);
201 }
202
profile(int index)203 Learner *ProfileManager::profile(int index)
204 {
205 if (index < 0 || index >= profiles().count()) {
206 return nullptr;
207 }
208 return profiles().at(index);
209 }
210
goals() const211 QList<LearningGoal *> ProfileManager::goals() const
212 {
213 return d->m_goals;
214 }
215
registerGoal(LearningGoal::Category category,const QString & identifier,const QString & name)216 LearningGoal *ProfileManager::registerGoal(LearningGoal::Category category, const QString &identifier, const QString &name)
217 {
218 // test whether goal is already registered
219 foreach (LearningGoal *cmpGoal, d->m_goals) {
220 if (cmpGoal->category() == category && cmpGoal->identifier() == identifier) {
221 return cmpGoal;
222 }
223 }
224 LearningGoal *goal = new LearningGoal(category, identifier, this);
225 goal->setName(name);
226 d->m_goals.append(goal);
227 d->m_storage.storeGoal(goal);
228 return goal;
229 }
230
goal(LearningGoal::Category category,const QString & identifier) const231 LearnerProfile::LearningGoal *LearnerProfile::ProfileManager::goal(LearningGoal::Category category, const QString &identifier) const
232 {
233 foreach (LearningGoal *goal, d->m_goals) {
234 if (goal->category() == category && goal->identifier() == identifier) {
235 return goal;
236 }
237 }
238 return nullptr;
239 }
240
recordProgress(Learner * learner,LearningGoal * goal,const QString & container,const QString & item,int logPayload,int valuePayload)241 void ProfileManager::recordProgress(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int logPayload, int valuePayload)
242 {
243 if (!learner) {
244 qCDebug(LIBLEARNER_LOG()) << "No learner set, no data stored";
245 return;
246 }
247 d->m_storage.storeProgressLog(learner, goal, container, item, logPayload, QDateTime::currentDateTime());
248 d->m_storage.storeProgressValue(learner, goal, container, item, valuePayload);
249 }
250
progressValues(Learner * learner,LearningGoal * goal,const QString & container) const251 QHash<QString, int> ProfileManager::progressValues(Learner *learner, LearningGoal *goal, const QString &container) const
252 {
253 if (!learner || !goal) {
254 return QHash<QString, int>();
255 }
256 return d->m_storage.readProgressValues(learner, goal, container);
257 }
258
sync()259 void ProfileManager::sync()
260 {
261 d->sync();
262 }
263
sync(Learner * learner)264 void ProfileManager::sync(Learner *learner)
265 {
266 d->m_storage.storeProfile(learner);
267 }
268
activeProfile() const269 Learner *ProfileManager::activeProfile() const
270 {
271 return d->m_activeProfile;
272 }
273
setActiveProfile(Learner * learner)274 void ProfileManager::setActiveProfile(Learner *learner)
275 {
276 if (learner == d->m_activeProfile) {
277 return;
278 }
279 d->m_activeProfile = learner;
280 emit activeProfileChanged();
281 }
282