diff --git a/liblearnerprofile/src/profilemanager.cpp b/liblearnerprofile/src/profilemanager.cpp index 81e8f60..b436531 100644 --- a/liblearnerprofile/src/profilemanager.cpp +++ b/liblearnerprofile/src/profilemanager.cpp @@ -1,308 +1,304 @@ /* * Copyright 2013-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "profilemanager.h" #include "storage.h" #include "learner.h" #include #include #include "liblearner_debug.h" #include #include #include #include using namespace LearnerProfile; ///BEGIN: ProfileManagerPrivate namespace LearnerProfile { class ProfileManagerPrivate { public: ProfileManagerPrivate(); ~ProfileManagerPrivate() {} void sync(); QList m_profiles; Learner *m_activeProfile; QList m_goals; KConfig *m_config; Storage m_storage; }; } LearnerProfile::ProfileManagerPrivate::ProfileManagerPrivate() : m_profiles(QList()) , m_activeProfile(nullptr) , m_config(nullptr) { // load all profiles from storage m_goals.append(m_storage.loadGoals()); m_profiles.append(m_storage.loadProfiles(m_goals)); // set last used profile m_config = new KConfig("learnerprofilerc"); KConfigGroup activeProfileGroup(m_config, "ActiveProfile"); int lastProfileId = activeProfileGroup.readEntry("profileId", "0").toInt(); QList activeGoalsCategories = activeProfileGroup.readEntry("activeGoalsCategories", QList()); QList activeGoalsIdentifiers = activeProfileGroup.readEntry("activeGoalsIdentifiers", QList()); foreach (Learner *learner, m_profiles) { if (learner->identifier() == lastProfileId) { m_activeProfile = learner; // set active goals if (activeGoalsCategories.count() == activeGoalsIdentifiers.count()) { for (int i = 0; i < activeGoalsCategories.count(); ++i) { m_activeProfile->setActiveGoal( (Learner::Category) activeGoalsCategories.at(i), activeGoalsIdentifiers.at(i)); } } else { qCritical() << "Inconsistent goal category / identifier pairs found: aborting."; } break; } } if (m_activeProfile == nullptr) { qCDebug(LIBLEARNER_LOG) << "No last active profile found, falling back to first found profile"; if (m_profiles.size() > 0) { m_activeProfile = m_profiles.at(0); } } } void ProfileManagerPrivate::sync() { // sync last used profile data if (m_activeProfile) { KConfigGroup activeProfileGroup(m_config, "ActiveProfile"); activeProfileGroup.writeEntry("profileId", m_activeProfile->identifier()); // compute activer learning goals by category QList goalCatogries; QList goalIdentifiers; // compute used goals foreach (LearningGoal *goal, m_activeProfile->goals()) { if (!goalCatogries.contains((int) goal->category())) { goalCatogries.append((int) goal->category()); } } // compute active goals foreach (int category, goalCatogries) { goalIdentifiers.append(m_activeProfile->activeGoal((Learner::Category) category)->identifier()); } activeProfileGroup.writeEntry("activeGoalsCategories", goalCatogries); activeProfileGroup.writeEntry("activeGoalsIdentifiers", goalIdentifiers); } else { qCritical() << "No active profile selected, aborting sync."; } m_config->sync(); //TODO only sync changed learner foreach (Learner *learner, m_profiles) { m_storage.storeProfile(learner); } } ///END: ProfileManagerPrivate ProfileManager::ProfileManager(QObject *parent) : QObject(parent) , d(new ProfileManagerPrivate) { connect (this, SIGNAL(profileAdded(Learner*,int)), this, SIGNAL(profileCountChanged())); connect (this, SIGNAL(profileRemoved()), this, SIGNAL(profileCountChanged())); foreach (Learner *learner, d->m_profiles) { connect (learner, SIGNAL(goalRemoved(Learner*,LearningGoal*)), this, SLOT(removeLearningGoal(Learner*,LearningGoal*))); } } ProfileManager::~ProfileManager() { foreach (Learner *learner, d->m_profiles) { learner->deleteLater(); } } QList< Learner* > ProfileManager::profiles() const { return d->m_profiles; } int ProfileManager::profileCount() const { return profiles().length(); } void ProfileManager::openImageFileDialog() { const QString imagePath = QFileDialog::getOpenFileName(0, i18n("Open Image"), "", i18n("Image Files (*.png *.jpg *.bmp)")); d->m_activeProfile->importImage(imagePath); } Learner * ProfileManager::addProfile(const QString &name) { Learner *learner = new Learner(this); learner->setName(name); // set id int maxUsedId = 0; foreach (Learner *cpLearner, d->m_profiles) { if (cpLearner->identifier() >= maxUsedId) { maxUsedId = cpLearner->identifier(); } } learner->setIdentifier(maxUsedId + 1); d->m_profiles.append(learner); d->m_storage.storeProfile(learner); emit profileAdded(learner, d->m_profiles.count() - 1); if (activeProfile() == 0) { setActiveProfile(learner); } connect (learner, SIGNAL(goalRemoved(Learner*,LearningGoal*)), this, SLOT(removeLearningGoal(Learner*,LearningGoal*))); return learner; } void ProfileManager::removeProfile(Learner *learner) { int index = d->m_profiles.indexOf(learner); if (index < 0) { qCWarning(LIBLEARNER_LOG) << "Profile was not found, aborting"; return; } emit profileAboutToBeRemoved(index); d->m_profiles.removeAt(index); d->m_storage.removeProfile(learner); if (d->m_activeProfile == learner) { if (d->m_profiles.isEmpty()) { setActiveProfile(nullptr); } else { setActiveProfile(d->m_profiles.at(0)); } } emit profileRemoved(); } void ProfileManager::removeLearningGoal(Learner* learner, LearningGoal* goal) { d->m_storage.removeRelation(learner, goal); } Learner * ProfileManager::profile(int index) { if (index < 0 || index >= profiles().count()) { return nullptr; } return profiles().at(index); } QList< LearningGoal* > ProfileManager::goals() const { return d->m_goals; } -void ProfileManager::registerGoal(LearningGoal::Category category, +LearningGoal * ProfileManager::registerGoal(LearningGoal::Category category, const QString &identifier, const QString &name) { // test whether goal is already registered foreach (LearningGoal *cmpGoal, d->m_goals) { if (cmpGoal->category() == category && cmpGoal->identifier() == identifier) { - return; + return cmpGoal; } } LearningGoal *goal = new LearningGoal(category, identifier, this); goal->setName(name); d->m_goals.append(goal); d->m_storage.storeGoal(goal); + return goal; } LearnerProfile::LearningGoal * LearnerProfile::ProfileManager::goal( LearningGoal::Category category, const QString& identifier) const { foreach (LearningGoal *goal, d->m_goals) { - if (goal->category() != category) { - continue; + if (goal->category() == category && goal->identifier() == identifier) { + return goal; } - if (goal->identifier() != identifier) { - continue; - } - return goal; } - qCWarning(LIBLEARNER_LOG) << "no goal found for:" << category << identifier; return nullptr; } void ProfileManager::recordProgress(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int logPayload, int valuePayload) { d->m_storage.storeProgressLog(learner, goal, container, item, logPayload, QDateTime::currentDateTime()); d->m_storage.storeProgressValue(learner, goal, container, item, valuePayload); } QHash ProfileManager::progressValues(Learner *learner, LearningGoal *goal, const QString &container) const { if (!learner || !goal) { return QHash(); } return d->m_storage.readProgressValues(learner, goal, container); } void ProfileManager::sync() { d->sync(); } void ProfileManager::sync(Learner *learner) { d->m_storage.storeProfile(learner); } Learner * ProfileManager::activeProfile() const { return d->m_activeProfile; } void ProfileManager::setActiveProfile(Learner* learner) { if (learner == d->m_activeProfile) { return; } d->m_activeProfile = learner; emit activeProfileChanged(); } diff --git a/liblearnerprofile/src/profilemanager.h b/liblearnerprofile/src/profilemanager.h index 6788788..a842c85 100644 --- a/liblearnerprofile/src/profilemanager.h +++ b/liblearnerprofile/src/profilemanager.h @@ -1,104 +1,104 @@ /* * Copyright 2013 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef PROFILEMANAGER_H #define PROFILEMANAGER_H #include "liblearnerprofile_export.h" #include "learninggoal.h" #include namespace LearnerProfile { class ProfileManagerPrivate; class Learner; class LearningGoal; /** * \class ProfileManager */ class LIBLEARNERPROFILE_EXPORT ProfileManager : public QObject { Q_OBJECT Q_PROPERTY (int profileCount READ profileCount NOTIFY profileCountChanged) Q_PROPERTY (LearnerProfile::Learner * activeProfile READ activeProfile WRITE setActiveProfile NOTIFY activeProfileChanged) public: enum Progress { Skip = 0, Next = 1 }; explicit ProfileManager(QObject *parent = nullptr); virtual ~ProfileManager(); QList< Learner* > profiles() const; int profileCount() const; Q_INVOKABLE LearnerProfile::Learner * addProfile(const QString &name); Q_INVOKABLE void removeProfile(LearnerProfile::Learner *learner); Q_INVOKABLE LearnerProfile::Learner * profile(int index); Q_INVOKABLE void openImageFileDialog(); QList< LearningGoal* > goals() const; /** * Register learning goal if not registered yet. The registered goals will be stored at the * internal database. */ - void registerGoal(LearningGoal::Category category, const QString &identifier, const QString &name); + LearningGoal * registerGoal(LearningGoal::Category category, const QString &identifier, const QString &name); LearningGoal * goal(LearningGoal::Category category, const QString &identifier) const; /** * updates current learning goal by activity, adds new learning goal if necessary, * stores log data for this activity */ void recordProgress(Learner *learner, LearningGoal *goal, const QString &container, const QString &item, int logPayload, int valuePayload); /** * \return progress value, or -1 if value is not available yet */ QHash progressValues(Learner *learner, LearningGoal *goal, const QString &container) const; /** * write all profiles to database */ Q_INVOKABLE void sync(); /** * write specified \p profile to database */ Q_INVOKABLE void sync(LearnerProfile::Learner *learner); void setActiveProfile(LearnerProfile::Learner *learner); LearnerProfile::Learner * activeProfile() const; Q_SIGNALS: void activeProfileChanged(); void profileAdded(Learner*,int); void profileAboutToBeRemoved(int); void profileRemoved(); void profileCountChanged(); private Q_SLOTS: void removeLearningGoal(Learner *learner, LearningGoal *goal); private: Q_DISABLE_COPY(ProfileManager) const QScopedPointer d; }; } #endif // PROFILEMANAGER_H diff --git a/src/core/trainingsession.cpp b/src/core/trainingsession.cpp index e876ce3..a7d1867 100644 --- a/src/core/trainingsession.cpp +++ b/src/core/trainingsession.cpp @@ -1,218 +1,218 @@ /* * Copyright 2013-2016 Andreas Cord-Landwehr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "trainingsession.h" #include "core/language.h" #include "core/course.h" #include "core/unit.h" #include "core/phrase.h" #include "core/phonemegroup.h" #include "profilemanager.h" #include "learner.h" #include "artikulate_debug.h" TrainingSession::TrainingSession(QObject *parent) : QObject(parent) , m_profileManager(nullptr) , m_language(nullptr) , m_course(nullptr) , m_unit(nullptr) , m_phrase(nullptr) { } void TrainingSession::setProfileManager(LearnerProfile::ProfileManager *manager) { if (m_profileManager == manager) { return; } m_profileManager = manager; } Language * TrainingSession::language() const { return m_language; } void TrainingSession::setLanguage(Language *language) { if (m_language == language) { return; } m_language = language; emit languageChanged(); } Course * TrainingSession::course() const { return m_course; } void TrainingSession::setCourse(Course *course) { if (!course) { return; } if (m_course == course) { return; } m_course = course; if (m_course && m_course->unitList().count() > 0) { setUnit(m_course->unitList().first()); } // lazy loading of training data LearnerProfile::LearningGoal * goal = m_profileManager->goal( LearnerProfile::LearningGoal::Language, m_course->id()); if (!goal) { - m_profileManager->registerGoal( + goal = m_profileManager->registerGoal( LearnerProfile::LearningGoal::Language, course->language()->id(), course->language()->i18nTitle() ); } auto data = m_profileManager->progressValues(m_profileManager->activeProfile(), goal, m_course->id() ); Q_FOREACH(Unit *unit, m_course->unitList()) { Q_FOREACH(Phrase *phrase, unit->phraseList()) { auto iter = data.find(phrase->id()); if (iter != data.end()) { phrase->setProgress(iter.value()); } } } emit courseChanged(); } Unit * TrainingSession::unit() const { return m_unit; } void TrainingSession::setUnit(Unit *unit) { if (m_unit == unit) { return; } m_unit = unit; if (m_unit && m_unit->phraseList().count() > 0) { setPhrase(m_unit->phraseList().first()); } return unitChanged(); } Phrase * TrainingSession::phrase() const { return m_phrase; } void TrainingSession::setPhrase(Phrase *phrase) { if (m_phrase == phrase) { return; } setUnit(phrase->unit()); m_phrase = phrase; return phraseChanged(); } Phrase * TrainingSession::nextPhrase() const { if (!m_phrase) { return nullptr; } const int index = m_phrase->unit()->phraseList().indexOf(m_phrase); if (index < m_phrase->unit()->phraseList().length() - 1) { return m_phrase->unit()->phraseList().at(index + 1); } else { Unit *unit = m_phrase->unit(); int uIndex = unit->course()->unitList().indexOf(unit); if (uIndex < unit->course()->unitList().length() - 1) { return unit->course()->unitList().at(uIndex + 1)->phraseList().first(); } } return nullptr; } void TrainingSession::showNextPhrase() { // possibly update goals of learner updateGoal(); m_phrase->updateProgress(Phrase::Progress::Done); // store training activity LearnerProfile::LearningGoal * goal = m_profileManager->goal( - LearnerProfile::LearningGoal::Language, m_course->id()); + LearnerProfile::LearningGoal::Language, m_course->language()->id()); m_profileManager->recordProgress(m_profileManager->activeProfile(), goal, m_course->id(), m_phrase->id(), static_cast(LearnerProfile::ProfileManager::Skip), m_phrase->progress() ); setPhrase(nextPhrase()); } void TrainingSession::skipPhrase() { // possibly update goals of learner updateGoal(); m_phrase->updateProgress(Phrase::Progress::Skip); // store training activity LearnerProfile::LearningGoal * goal = m_profileManager->goal( - LearnerProfile::LearningGoal::Language, m_course->id()); + LearnerProfile::LearningGoal::Language, m_course->language()->id()); m_profileManager->recordProgress(m_profileManager->activeProfile(), goal, m_course->id(), m_phrase->id(), static_cast(LearnerProfile::ProfileManager::Skip), m_phrase->progress() ); setPhrase(nextPhrase()); } bool TrainingSession::hasNextPhrase() const { return nextPhrase() != nullptr; } void TrainingSession::updateGoal() { if (!m_profileManager) { qCWarning(ARTIKULATE_LOG) << "No ProfileManager registered, aborting operation"; return; } LearnerProfile::Learner *learner = m_profileManager->activeProfile(); if (!learner) { qCWarning(ARTIKULATE_LOG) << "No active Learner registered, aborting operation"; return; } LearnerProfile::LearningGoal * goal = m_profileManager->goal( - LearnerProfile::LearningGoal::Language, m_course->id()); + LearnerProfile::LearningGoal::Language, m_course->language()->id()); learner->addGoal(goal); learner->setActiveGoal(goal); }