809 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			809 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
| /***************************************************************************
 | |
|  *   Copyright (C) 2003-2007 by Oliver Saal                                *
 | |
|  *   osaal@gmx.de                                                          *
 | |
|  *   http://www.oliver-saal.de/software/afutrainer/                        *
 | |
|  *                                                                         *
 | |
|  *   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) any later version.                                   *
 | |
|  *                                                                         *
 | |
|  *   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, write to the                         *
 | |
|  *   Free Software Foundation, Inc.,                                       *
 | |
|  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 | |
|  ***************************************************************************/
 | |
| 
 | |
| #include "catalog.h"
 | |
| #include <qcoreapplication.h>
 | |
| 
 | |
| 
 | |
| QString CChapter::tr (const char *sourceText, const char *comment)
 | |
| {
 | |
| 	return QCoreApplication::translate("CChapter", sourceText, comment);
 | |
| }
 | |
| 
 | |
| void CChapter::clear()
 | |
| {
 | |
| 	m_pParentChapter=0;
 | |
| 	m_strId.clear();
 | |
| 	m_strText.clear();
 | |
| 	m_strComment.clear();
 | |
| 	m_recom = RecommendationNone;
 | |
| 	m_recom2 = RecommendationNone;
 | |
| 	qDeleteAll(m_listChapter);
 | |
| 	qDeleteAll(m_listQuestion);
 | |
| 	m_bHasLearningNew = false;
 | |
| 	m_bHasKnownQuestions = false;
 | |
| 	m_bHasKnownQuestionsRepeatToday = false;
 | |
| 	m_uNeverAskedCount = 0;
 | |
| 	for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0;
 | |
| 	for (int i=0; i<=RecommendationMax; i++) m_uRecomCount[i] = 0;
 | |
| //	m_mapExam.clear();
 | |
| }
 | |
| 
 | |
| int CChapter::countSubQuestion() const
 | |
| {
 | |
| int i=0, iRet=0;
 | |
| 	for (i=0; i<m_listChapter.size(); i++)
 | |
| 		iRet += m_listChapter.at(i)->countSubQuestion();
 | |
| 	return (iRet + m_listQuestion.size());
 | |
| }
 | |
| 
 | |
| bool CChapter::load (QDomElement elem)
 | |
| {
 | |
| CChapter *pChapter=0;
 | |
| 
 | |
| 	if (elem.tagName () != QString ("chapter")) return false;
 | |
| 	if (!elem.hasAttribute ("name")) return false;
 | |
| //	if (pParent != NULL && !elem.hasAttribute ("id")) return false;
 | |
| 	m_strId = elem.attribute ("id");
 | |
| 	m_strText = elem.attribute ("name");
 | |
| 	QDomNode n = elem.firstChild();
 | |
| 	while (!n.isNull())
 | |
| 	{
 | |
| 		if (n.isElement ())
 | |
| 		{
 | |
| 			QDomElement e = n.toElement ();
 | |
| 			if (e.tagName() == "comment")
 | |
| 				m_strComment = e.text();
 | |
| 			else if (e.tagName() == QString ("chapter"))
 | |
| 			{
 | |
| 				pChapter = new CChapter();
 | |
| 				if (pChapter->load (e))
 | |
| 					appendChapter(pChapter);
 | |
| 				else
 | |
| 					delete pChapter;
 | |
| 			}
 | |
| 			else if (e.tagName() == QString ("question"))
 | |
| 			{
 | |
| 				CQuestion *pQuestion = new CQuestion();
 | |
| 				if (pQuestion->load (e))
 | |
| 					appendQuestion(pQuestion);
 | |
| 				else
 | |
| 					delete pQuestion;
 | |
| 			}
 | |
| 		}
 | |
| 		n = n.nextSibling();
 | |
| 	}
 | |
| 	updateStatistic();
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| void CChapter::save (QDomElement& parent, QDomDocument& doc)
 | |
| {
 | |
| QDomElement elem = doc.createElement("chapter");
 | |
| 
 | |
| 	elem.setAttribute("name", text());
 | |
| 	elem.setAttribute("id", id());
 | |
| 
 | |
| 	if (!m_strComment.isEmpty())
 | |
| 	{
 | |
| 		QDomElement elemComment = doc.createElement("comment");
 | |
| 		QDomText textComment = doc.createTextNode(text());
 | |
| 		elemComment.appendChild(textComment);
 | |
| 		elem.appendChild(elemComment);
 | |
| 	}
 | |
| 
 | |
| 	parent.appendChild(elem);
 | |
| 
 | |
| 	// save exams
 | |
| /*	QStringList strl = m_mapExam.keys();
 | |
| 	for (int i=0; i<strl.size(); i++)
 | |
| 	{
 | |
| 		QDomElement e = doc.createElement("exam");
 | |
| 		e.setAttribute("id", strl.at(i));
 | |
| 		e.setAttribute("questions", QString("%1").arg(m_mapExam[strl.at(i)]));
 | |
| 		elem.appendChild(e);
 | |
| 	}
 | |
| */
 | |
| 	// save chapters
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		m_listChapter[i]->save(elem, doc);
 | |
| 	}
 | |
| 
 | |
| 	// save questions
 | |
| 	for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		m_listQuestion[i]->save(elem, doc);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool CChapter::loadLearnStatistic (QDomElement elem)
 | |
| {
 | |
| QList<CQuestion*> listQuestionPool = questionPool();
 | |
| 
 | |
| 	if (elem.tagName () != QString ("learning")) return false;
 | |
| 
 | |
| 	QDomNode n = elem.firstChild();
 | |
| 	while (!n.isNull())
 | |
| 	{
 | |
| 		if (n.isElement ())
 | |
| 		{
 | |
| 			QDomElement e = n.toElement ();
 | |
| 			if (e.tagName() == QString ("question"))
 | |
| 			{
 | |
| 				QString strId = e.attribute("id");
 | |
| 				for (int i=0; i<listQuestionPool.size(); i++)
 | |
| 				{
 | |
| 					if (listQuestionPool.at(i)->id() == strId)
 | |
| 						listQuestionPool[i]->loadLearnStatistic(e);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		n = n.nextSibling();
 | |
| 	}
 | |
| 	updateRecommendation();
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool CChapter::saveLearnStatistic (QDomElement& parent, QDomDocument& doc)
 | |
| {
 | |
| 	// questions from sub-chapters
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		m_listChapter[i]->saveLearnStatistic(parent, doc);
 | |
| 	}
 | |
| 
 | |
| 	// save questions
 | |
| 	for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		m_listQuestion[i]->saveLearnStatistic(parent, doc);
 | |
| 	}
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| QString CChapter::checkForErrors() const
 | |
| {
 | |
| QString str;
 | |
| 
 | |
| 	// check chapters
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		str += m_listChapter[i]->checkForErrors();
 | |
| 	}
 | |
| 
 | |
| 	// check questions
 | |
| 	for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		str += m_listQuestion[i]->checkForErrors();
 | |
| 	}
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| QList<CChapter*> CChapter::subChapters() const
 | |
| {
 | |
| QList<CChapter*> list;
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		list << m_listChapter.at(i);
 | |
| 		list << m_listChapter[i]->subChapters();
 | |
| 	}
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| QList<CQuestion*> CChapter::questionPool() const
 | |
| {
 | |
| QList<CQuestion*> list;
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		list << m_listChapter[i]->questionPool();
 | |
| 	}
 | |
| 	list << m_listQuestion;
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| QList<CQuestion*> CChapter::questionPoolLevel(const unsigned uLevel) const
 | |
| {
 | |
| QList<CQuestion*> list;
 | |
| int i;
 | |
| 	for (i=0; i<m_listChapter.size(); i++)
 | |
| 		list << m_listChapter[i]->questionPoolLevel(uLevel);
 | |
| 	for (i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		if (m_listQuestion.at(i)->level() == uLevel)
 | |
| 			list.append (m_listQuestion.at(i));
 | |
| 	}
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| QList<CQuestion*> CChapter::questionPoolDeepen() const
 | |
| {
 | |
| QList<CQuestion*> list;
 | |
| int i;
 | |
| 	for (i=0; i<m_listChapter.size(); i++)
 | |
| 		list << m_listChapter[i]->questionPoolDeepen();
 | |
| 	for (i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		CQuestion *q = m_listQuestion.at(i);
 | |
| 		if (q->level() == LEVEL_VERYOFTEN && !q->isNeverAsked())
 | |
| 			list.append (q);
 | |
| 	}
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| QList<CQuestion*> CChapter::questionPoolRepeat(const QDate d) const
 | |
| {
 | |
| QList<CQuestion*> list;
 | |
| int i;
 | |
| 	for (i=0; i<m_listChapter.size(); i++)
 | |
| 		list << m_listChapter[i]->questionPoolRepeat(d);
 | |
| 	for (i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		CQuestion *q = m_listQuestion.at(i);
 | |
| 		if (q->repeatDate() <= d)
 | |
| 			list.append (q);
 | |
| 	}
 | |
| 	return list;
 | |
| }
 | |
| 
 | |
| 
 | |
| void CChapter::updateStatisticCount()
 | |
| {
 | |
| /*		for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 		if (m_listQuestion.at(i)->isLearningNew()) return true;
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 		if (m_listChapter.at(i)->isLearningNew()) return true;
 | |
| 
 | |
| 	return false;
 | |
| */
 | |
| 	// Alle Statistiken zurücksetzen
 | |
| 	m_uNeverAskedCount = 0;
 | |
| 	m_bHasLearningNew = false;
 | |
| 	m_bHasKnownQuestions = false;
 | |
| 	m_bHasKnownQuestionsRepeatToday = false;
 | |
| 	for (int i=0; i<=LEVEL_MAX; i++) m_uLevelCount[i] = 0;
 | |
| 
 | |
| 	// Zuerst Statistiken für Unterkapitel berechnen und integrieren
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		CChapter *p = m_listChapter.at(i);
 | |
| 		p->updateStatisticCount();
 | |
| 		for (int j=0; j<=LEVEL_MAX; j++)
 | |
| 			m_uLevelCount[j] += p->m_uLevelCount[j];
 | |
| 		m_uNeverAskedCount += p->m_uNeverAskedCount;
 | |
| 		if (p->hasLearningNewQuestions()) m_bHasLearningNew = true;
 | |
| 		if (p->hasKnownQuestions()) m_bHasKnownQuestions = true;
 | |
| 		if (p->hasKnownQuestionsRepeatToday()) m_bHasKnownQuestionsRepeatToday = true;
 | |
| 	}
 | |
| 
 | |
| 	// Anschließend Statistiken für eigene Fragen neu berechnen und hinzufügen
 | |
| 	for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 	{
 | |
| 		CQuestion *q = m_listQuestion.at(i);
 | |
| 		if (q->isNeverAsked()) m_uNeverAskedCount++;
 | |
| 		m_uLevelCount[q->level()]++;
 | |
| 		if (q->isLearningNew()) m_bHasLearningNew = true;
 | |
| 		if (q->isKnownQuestion()) m_bHasKnownQuestions = true;
 | |
| 		if (q->isKnownQuestion() && q->isRepeatToday()) m_bHasKnownQuestionsRepeatToday = true;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void CChapter::updateRecommendationStatistic()
 | |
| {
 | |
| 	for (int i=0; i<RecommendationMax; i++) m_uRecomCount[i] = 0;
 | |
| 	m_uRecomCount[recommendation()]=1;
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		CChapter *p = m_listChapter.at(i);
 | |
| 		p->updateRecommendationStatistic();
 | |
| 		for (int j=0; j<RecommendationMax; j++)
 | |
| 			m_uRecomCount[j] += p->m_uRecomCount[j];
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
| Berechnet die Statistik für das Kapitel inkl. aller Unterkapitel neu.
 | |
| */
 | |
| void CChapter::updateStatistic()
 | |
| {
 | |
| 	updateStatisticCount();
 | |
| 	updateRecommendation();
 | |
| 	updateRecommendationStatistic();
 | |
| }
 | |
| 
 | |
| /*
 | |
| CRecommendation CChapter::recommendation() const
 | |
| {
 | |
| CRecommendation r;
 | |
| 	r.create(this);
 | |
| 	return r;
 | |
| }
 | |
| */
 | |
| /*
 | |
| CRecommendationStatistic CChapter::recommendationStatistic() const
 | |
| {
 | |
| CRecommendationStatistic rs;
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		rs.append(m_listChapter.at(i)->recommendationStatistic());
 | |
| 	}
 | |
| 	//TODO
 | |
| //	rs.append(recommendation());
 | |
| 	return rs;
 | |
| }
 | |
| */
 | |
| 
 | |
| QString CChapter::idWithParents() const
 | |
| {
 | |
| CChapter *pParent=m_pParentChapter;
 | |
| QString str;
 | |
| 	str = id();
 | |
| 	while (pParent != 0)
 | |
| 	{
 | |
| 		str = pParent->id() + str;
 | |
| 		pParent = pParent->m_pParentChapter;
 | |
| 	}
 | |
| 
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| /*!
 | |
| Das empfohlene Wiederholdatum eines Kapitels das frühestete Datum, 
 | |
| an dem eine Frage des Kapitels oder Unterkapitels wiederholt werden sollte. 
 | |
| \return Datum, wann das Kapitel wiederholt werden soll. 
 | |
| Gibt es kein solches Datum, so wird ein ungültiges Datum (QDate::isValid()) zurückgegeben.
 | |
| Hinweis: Das Datum kann auch in der Vergangenheit liegen!
 | |
| */
 | |
| QDate CChapter::repeatDate() const
 | |
| {
 | |
| QDate d;
 | |
| QList<CQuestion*> list = questionPool();
 | |
| 
 | |
| 	for (int i=0; i<list.size(); i++)
 | |
| 	{
 | |
| 		QDate dq = list.at(i)->repeatDate();
 | |
| 		if (!dq.isValid()) continue;
 | |
| 		if (!d.isValid() || dq < d) d = dq;
 | |
| 	}
 | |
| 	return d;
 | |
| }
 | |
| 
 | |
| /*!	
 | |
| Die Variable m_recomRepeatDate muss up to date sein!
 | |
| 
 | |
| \return Lernempfehlung dieses Kapitels ohne Berücksichtigung evt. vorhandener ünter- oder übergeordneter Kapitel.
 | |
| 
 | |
| \sa m_recomRepeatDate	
 | |
| */
 | |
| CChapter::Recommendation CChapter::recommendationIndividual() const
 | |
| {
 | |
| 	if (!m_recomRepeatDate.isValid())
 | |
| 		return RecommendationLearnNew;
 | |
| 	else if (m_recomRepeatDate <= QDate::currentDate())
 | |
| 	{
 | |
| 		if (hasLearningNewQuestions() && !hasKnownQuestionsRepeatToday())
 | |
| 			return RecommendationLearnNew;
 | |
| 		else
 | |
| 			return RecommendationRepeatToday;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (countNeverAsked() > 0)
 | |
| 			return RecommendationLearnNew;
 | |
| 		else
 | |
| 			return RecommendationRepeatLater;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*!	
 | |
| Aktualisiert die Lernempfehlung für dieses Kapitel und alle Unterkapitel
 | |
| \sa m_recom	
 | |
| */
 | |
| void CChapter::updateRecommendation()
 | |
| {
 | |
| 	m_recom = RecommendationNone;
 | |
| 	m_recom2 = RecommendationNone;
 | |
| 
 | |
| 	m_recomRepeatDate = repeatDate(); /*QDate();*/
 | |
| 	if (m_listChapter.isEmpty() && m_listQuestion.isEmpty()) return;
 | |
| 
 | |
| 	if (m_pParentChapter && m_pParentChapter->recommendation() != RecommendationSubChapter)
 | |
| 		m_recom = RecommendationParentChapter;
 | |
| 	else
 | |
| 	{
 | |
| 		if (countSubQuestion() > 20)
 | |
| 		{ // Nur wenn in den Unterkapiteln zusammen mehr als x Fragen sind, zuerst die Unterkapitel einzeln lernen lassen
 | |
| 			for (int i=0; i<m_listChapter.size(); i++)
 | |
| 			{
 | |
| 				CChapter *p = m_listChapter.at(i);
 | |
| 				if (p->levelAvg() < LEVEL_NORMAL || p->countNeverAsked() > 0)
 | |
| 					m_recom = RecommendationSubChapter;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (m_recom == RecommendationNone)
 | |
| 		{
 | |
| 			//m_recomRepeatDate = repeatDate();
 | |
| 			m_recom = recommendationIndividual();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Alternative Empfehlung
 | |
| 	if (m_recom == RecommendationParentChapter || m_recom == RecommendationSubChapter)
 | |
| 	{
 | |
| 		m_recom2 = recommendationIndividual();
 | |
| 		if (m_recom2 != RecommendationLearnNew && m_recom2 != RecommendationRepeatToday)
 | |
| 			m_recom2 = RecommendationNone;
 | |
| 	}
 | |
| 
 | |
| 	// find recommended questions
 | |
| 	m_listQuestionRecommended.clear();
 | |
| 	QList<CQuestion*> list = questionPool();
 | |
| 	for (int i=0; i<list.size(); i++)
 | |
| 	{
 | |
| 		CQuestion *q = list.at(i);
 | |
| 		if (m_recom == RecommendationLearnNew || m_recom2 == RecommendationLearnNew)
 | |
| 		{
 | |
| 			if (q->isNeverAsked() || q->isLearningNew())
 | |
| 				m_listQuestionRecommended.append(q);
 | |
| 		}
 | |
| 		else if (m_recom == RecommendationRepeatToday || m_recom2 == RecommendationRepeatToday)
 | |
| 		{
 | |
| 			if (q->isRepeatToday())
 | |
| 				m_listQuestionRecommended.append(q);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// update child chapters
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		CChapter *p = m_listChapter.at(i);
 | |
| 		p->updateRecommendation();
 | |
| 		//m_listQuestionRecommended << p->m_listQuestionRecommended;
 | |
| 	}
 | |
| }
 | |
| /*
 | |
| bool CChapter::hasRecommendedQuestions() const
 | |
| {
 | |
| 	return (m_recom == RecommendationLearnNew || m_recom == RecommendationRepeatToday);
 | |
| }
 | |
| 
 | |
| QList<CQuestion*> CChapter::recommendedQuestions() const
 | |
| {
 | |
| QList<CQuestion*> listRet, list = questionPool();
 | |
| 
 | |
| 	if (m_recom != RecommendationLearnNew && m_recom != RecommendationRepeatToday) return listRet;
 | |
| 
 | |
| 	for (int i=0; i<list.size(); i++)
 | |
| 	{
 | |
| 		CQuestion *q = list.at(i);
 | |
| 		switch (m_recom)
 | |
| 		{
 | |
| 		case RecommendationLearnNew:
 | |
| 			if (q->isNeverAsked() || q->isLearningNew())
 | |
| 				listRet.append(q);
 | |
| 			break;
 | |
| 		case RecommendationRepeatToday:
 | |
| 			if (q->isRepeatToday())
 | |
| 				listRet.append(q);
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	return listRet;
 | |
| }
 | |
| */
 | |
| 
 | |
| QString CChapter::recommendationText(const Recommendation r, const QDate dRepeat)
 | |
| {
 | |
| unsigned uDays=0;
 | |
| 	switch (r)
 | |
| 	{
 | |
| 	default:
 | |
| 	case RecommendationNone:
 | |
| 		return tr("Keine Lernempfehlung");
 | |
| 	case RecommendationSubChapter:
 | |
| 		return tr("Zuerst Unterkapitel lernen");
 | |
| 	case RecommendationParentChapter:
 | |
| 		return tr("Übergeordnetes Kapitel lernen");
 | |
| 	case RecommendationLearnNew:
 | |
| 		return tr("Neue Fragen lernen");
 | |
| 	case RecommendationRepeatToday:
 | |
| 		return tr("Heute wiederholen");
 | |
| 	case RecommendationRepeatLater:
 | |
| 		uDays = QDate::currentDate().daysTo(dRepeat);
 | |
| 		if (uDays == 1)
 | |
| 			return tr("Morgen wiederholen");
 | |
| 		else
 | |
| 			return tr("In %1 Tagen wiederholen").arg(uDays);
 | |
| 	}
 | |
| 	return QString();
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationText() const
 | |
| {
 | |
| 	return recommendationText(m_recom, m_recomRepeatDate);
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationToolTip() const
 | |
| {
 | |
| QString str = recommendationText();
 | |
| unsigned uCount = recommendedQuestionCount();
 | |
| 	if (hasRecommendedQuestions() && m_recom2 == RecommendationNone)
 | |
| 	{
 | |
| 		str += " ";
 | |
| 		if (uCount == 1)
 | |
| 			str += tr("(1 Frage)");
 | |
| 		else
 | |
| 			str += tr("(%1 Fragen)").arg(uCount);
 | |
| 	}
 | |
| 	if (m_recom2 != RecommendationNone)
 | |
| 	{
 | |
| 		str += "\n" + tr("Alternativ: ");
 | |
| 		if (m_recom2 == RecommendationLearnNew)
 | |
| 		{
 | |
| 			if (uCount == 1)
 | |
| 				str += tr("1 neue Frage lernen");
 | |
| 			else
 | |
| 				str += tr("%1 neue Fragen lernen").arg(uCount);
 | |
| 		}
 | |
| 		else if (m_recom2 == RecommendationRepeatToday)
 | |
| 		{
 | |
| 			if (uCount == 1)
 | |
| 				str += tr("1 Frage wiederholen");
 | |
| 			else
 | |
| 				str += tr("%1 Fragen wiederholen").arg(uCount);
 | |
| 		}
 | |
| 		else
 | |
| 			str += recommendationText (m_recom2, m_recomRepeatDate);
 | |
| 	}
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationTextExtended(const CCatalog *pCatalog) const
 | |
| {
 | |
| unsigned uDays=0;
 | |
| QString str;
 | |
| 
 | |
| 	switch (m_recom)
 | |
| 	{
 | |
| 	default:
 | |
| 	case RecommendationNone:
 | |
| 		str = tr("Keine Lernempfehlung");
 | |
| 		break;
 | |
| 	case RecommendationSubChapter:
 | |
| 		str = tr("Dieses Kapitel enthält Unterkapitel, dessen Fragen Sie noch nicht ausreichend gelernt haben.\nEs wird empohlen in kleinen Etappen zu lernen und damit zuerst die Unterkapitel zu vertiefen.");
 | |
| 		break;
 | |
| 	case RecommendationParentChapter:
 | |
| 		if (levelAvgRounded() >= LEVEL_NORMAL)
 | |
| 			str = tr("Sie können die Fragen dieses Kapitels gut beantworten.\n");
 | |
| 		str += tr("Es wird empfohlen, alle Fragen des übergeordneten Kapitels gemischt zusammen zu lernen.");
 | |
| 		break;
 | |
| 	case RecommendationLearnNew:
 | |
| 		if (!isRecommendedNow(pCatalog))
 | |
| 			str = tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden sollten. Bitte lernen Sie diese Kapitel zuerst.");
 | |
| 		else
 | |
| 			str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig.");
 | |
| 		break;
 | |
| 	case RecommendationRepeatToday:
 | |
| 		str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind.");
 | |
| 		break;
 | |
| 	case RecommendationRepeatLater:
 | |
| 		uDays = QDate::currentDate().daysTo(m_recomRepeatDate);
 | |
| 		if (uDays > 1)
 | |
| 			str = tr("Die Wiederholung dieses Kapitels ist erst in %1 Tagen geplant.\n").arg(uDays);
 | |
| 		else
 | |
| 			str = tr("Die Wiederholung dieses Kapitels ist erst für morgen geplant.\n");
 | |
| 		if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0)
 | |
| 			str += tr("Es gibt andere Kapitel, deren Fragen heute wiederholt werden müssen. Bitte lernen Sie diese Kapitel zuerst.");
 | |
| 		else if (pCatalog->m_uRecomCount[RecommendationLearnNew] > 0)
 | |
| 			str += tr("Bitte lernen Sie zuerst Kapitel mit neuen Fragen.");
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (hasRecommendedQuestions() && isRecommendedNow(pCatalog))
 | |
| 		str += tr("<p>Dafür sind noch %1 Fragen zu lernen.").arg(recommendedQuestionCount());
 | |
| 
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationTextExtended2(const CCatalog *pCatalog) const
 | |
| {
 | |
| QString str;
 | |
| 
 | |
| 	if (m_recom2 == RecommendationLearnNew || (m_recom == RecommendationLearnNew && !isRecommendedNow(pCatalog)))
 | |
| 	{
 | |
| 		//str = tr("Bitte beantworten Sie alle neuen Fragen mindestens einmal richtig.");
 | |
| 		str = tr("Alternativ können Sie jetzt die neuen Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount());
 | |
| 	}
 | |
| 	else if (m_recom2 == RecommendationRepeatToday)
 | |
| 	{
 | |
| 		if (m_recom == RecommendationSubChapter)
 | |
| 			str = tr("Bitte lernen Sie alle heute zu wiederholenden Fragen, bis sie eine Lernfortschritts-Stufe höher eingestuft sind (%1 Fragen).").arg(recommendedQuestionCount());
 | |
| 		else
 | |
| 			str = tr("Alternativ können Sie jetzt die heute zu wiederholenden Fragen dieses Kapitels lernen (%1 Fragen).").arg(recommendedQuestionCount());
 | |
| 	}
 | |
| 
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationIconName(const Recommendation r, const CCatalog *pCatalog)
 | |
| {
 | |
| 	switch (r)
 | |
| 	{
 | |
| 	case RecommendationSubChapter:
 | |
| 		return QString(":/icons/16x16/button_cancel.png");
 | |
| 	case RecommendationParentChapter:
 | |
| 		return QString(":/icons/16x16/button_ok.png");
 | |
| 	case RecommendationLearnNew:
 | |
| 		if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0)
 | |
| 			return QString(":/icons/16x16/idea_gray.png");
 | |
| 		else
 | |
| 			return QString(":/icons/16x16/idea.png");
 | |
| 	case RecommendationRepeatToday:
 | |
| 		return QString(":/icons/16x16/idea.png");
 | |
| 	case RecommendationRepeatLater:
 | |
| 		return QString(":/icons/16x16/idea_gray.png");
 | |
| 	default:
 | |
| 		return QString();
 | |
| 	}
 | |
| }
 | |
| 
 | |
| QString CChapter::recommendationIconName(const CCatalog *pCatalog) const
 | |
| {
 | |
| 	return recommendationIconName(m_recom, pCatalog);
 | |
| }
 | |
| 
 | |
| /*!
 | |
| \return true: Kapitel kann jetzt gelernt werden, 
 | |
| false: Kapitel sollte überhaupt nicht oder erst später gelernt werden
 | |
| */
 | |
| 
 | |
| bool CChapter::isRecommendedNow(const CCatalog *pCatalog) const
 | |
| {
 | |
| 	switch (m_recom)
 | |
| 	{
 | |
| 	case RecommendationSubChapter:
 | |
| 		if (pCatalog->m_uRecomCount[RecommendationRepeatToday] > 0)
 | |
| 			return true;
 | |
| 		break;
 | |
| 	case RecommendationRepeatToday:
 | |
| 		return true;
 | |
| 	case RecommendationLearnNew:
 | |
| 		if (pCatalog->m_uRecomCount[RecommendationRepeatToday] == 0)
 | |
| 			return true;
 | |
| 		break;
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| /*
 | |
| \return true: Das Kapitel enthält noch neue Fragen, die gerade (="heute") gelernt werden
 | |
| bool CChapter::isLearningNew() const
 | |
| {
 | |
| 	for (int i=0; i<m_listQuestion.size(); i++)
 | |
| 		if (m_listQuestion.at(i)->isLearningNew()) return true;
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 		if (m_listChapter.at(i)->isLearningNew()) return true;
 | |
| 
 | |
| 	return false;
 | |
| }
 | |
| */
 | |
| 
 | |
| CDayStatistic CChapter::dayStatistic (const QDate& date) const
 | |
| {
 | |
| CDayStatistic dsRet, ds;
 | |
| QList<CQuestion*> listPool = questionPool();
 | |
| 
 | |
| 	for (int i=0; i<listPool.size(); i++)
 | |
| 	{
 | |
| 		ds = listPool.at(i)->dayStatistic(date);
 | |
| 		dsRet += ds;
 | |
| 	}
 | |
| 	if (listPool.size() != 0) dsRet.m_dLevel /= listPool.size();
 | |
| 	return dsRet;
 | |
| }
 | |
| 
 | |
| CDayStatistic CChapter::completeStatistic() const
 | |
| {
 | |
| 	return dayStatistic(QDate());
 | |
| }
 | |
| 
 | |
| QDateTime CChapter::firstAnswerClicked() const
 | |
| {
 | |
| QList<CQuestion*> listPool = questionPool();
 | |
| QDateTime dtRet, dt;
 | |
| 
 | |
| 	for (int i=0; i<listPool.size(); i++)
 | |
| 	{
 | |
| 		dt = listPool.at(i)->firstClicked();
 | |
| 		if (dt.isNull()) continue;
 | |
| 		if (dtRet.isNull() || dt < dtRet) dtRet = dt;
 | |
| 	}
 | |
| 	return dtRet;
 | |
| }
 | |
| 
 | |
| double CChapter::levelAvg() const
 | |
| {
 | |
| double d=0.0;
 | |
| double dCount=0.0;
 | |
| 
 | |
| 	for (int i=0; i<=LEVEL_MAX; i++)
 | |
| 	{
 | |
| 		d += m_uLevelCount[i] * i;
 | |
| 		dCount += m_uLevelCount[i];
 | |
| 	}
 | |
| 	if (dCount != 0.0) d /= dCount;
 | |
| 	return d;
 | |
| }
 | |
| 
 | |
| unsigned CChapter::levelAvgRounded() const
 | |
| {
 | |
| 	return ((unsigned) (levelAvg()+0.5));
 | |
| }
 | |
| 
 | |
| QString CChapter::levelAvgText() const
 | |
| {
 | |
| 	return QString("%1").arg(CQuestion::levelText(levelAvgRounded()));
 | |
| }
 | |
| 
 | |
| QIcon CChapter::levelAvgIcon() const
 | |
| {
 | |
| 	return QIcon(CQuestion::levelIconName(levelAvgRounded()));
 | |
| }
 | |
| 
 | |
| QPixmap CChapter::levelAvgPixmap() const
 | |
| {
 | |
| 	return QPixmap(CQuestion::levelIconName(levelAvgRounded()));
 | |
| }
 | |
| 
 | |
| static bool chapterLessThan(const CChapter *c1, const CChapter *c2)
 | |
| {
 | |
|      return c1->id() < c2->id();
 | |
| }
 | |
| 
 | |
| void CChapter::sortSubChapters(bool bSortQuestions)
 | |
| {
 | |
| 	if (bSortQuestions) sortQuestions();
 | |
| 	qSort (m_listChapter.begin(), m_listChapter.end(), chapterLessThan);
 | |
| 
 | |
| 	for (int i=0; i<m_listChapter.size(); i++)
 | |
| 	{
 | |
| 		m_listChapter.at(i)->sortSubChapters(bSortQuestions);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static bool questionLessThan(const CQuestion *q1, const CQuestion *q2)
 | |
| {
 | |
|      return q1->id() < q2->id();
 | |
| }
 | |
| 
 | |
| void CChapter::sortQuestions()
 | |
| {
 | |
| 	qSort (m_listQuestion.begin(), m_listQuestion.end(), questionLessThan);
 | |
| }
 | |
| 
 |