File indexing completed on 2024-04-21 05:54:04
0001 /* 0002 SPDX-FileCopyrightText: 2006 Bram Schoenmakers <bramschoenmakers@kde.nl> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "rsistats.h" 0008 #include "rsistatitem.h" 0009 0010 #include <QDateTime> 0011 #include <QLocale> 0012 0013 #include <KLocalizedString> 0014 0015 RSIStats::RSIStats() 0016 : m_doUpdates(false) 0017 { 0018 m_statistics.insert(TOTAL_TIME, new RSIStatItem(i18n("Total recorded time"))); 0019 m_statistics[TOTAL_TIME]->addDerivedItem(ACTIVITY_PERC); 0020 0021 m_statistics.insert(ACTIVITY, new RSIStatItem(i18n("Total time of activity"))); 0022 m_statistics[ACTIVITY]->addDerivedItem(ACTIVITY_PERC); 0023 m_statistics[ACTIVITY]->addDerivedItem(ACTIVITY_PERC_MINUTE); 0024 m_statistics[ACTIVITY]->addDerivedItem(ACTIVITY_PERC_HOUR); 0025 m_statistics[ACTIVITY]->addDerivedItem(ACTIVITY_PERC_6HOUR); 0026 0027 m_statistics.insert(IDLENESS, new RSIStatItem(i18n("Total time being idle"))); 0028 m_statistics[IDLENESS]->addDerivedItem(ACTIVITY_PERC_MINUTE); 0029 m_statistics[IDLENESS]->addDerivedItem(ACTIVITY_PERC_HOUR); 0030 m_statistics[IDLENESS]->addDerivedItem(ACTIVITY_PERC_6HOUR); 0031 0032 m_statistics.insert(ACTIVITY_PERC, new RSIStatItem(i18n("Percentage of activity"), 0)); 0033 0034 m_statistics.insert(ACTIVITY_PERC_MINUTE, new RSIStatBitArrayItem(i18n("Percentage of activity last minute"), QVariant(0), 60)); 0035 m_statistics.insert(ACTIVITY_PERC_HOUR, new RSIStatBitArrayItem(i18n("Percentage of activity last hour"), QVariant(0), 3600)); 0036 m_statistics.insert(ACTIVITY_PERC_6HOUR, new RSIStatBitArrayItem(i18n("Percentage of activity last 6 hours"), QVariant(0), 6 * 3600)); 0037 0038 m_statistics.insert(MAX_IDLENESS, new RSIStatItem(i18n("Maximum idle period"))); 0039 m_statistics[MAX_IDLENESS]->addDerivedItem(IDLENESS); 0040 0041 m_statistics.insert(CURRENT_IDLE_TIME, new RSIStatItem(i18n("Current idle period"))); 0042 0043 m_statistics.insert(IDLENESS_CAUSED_SKIP_TINY, new RSIStatItem(i18n("Number of skipped short breaks (idle)"))); 0044 0045 m_statistics.insert(IDLENESS_CAUSED_SKIP_BIG, new RSIStatItem(i18n("Number of skipped long breaks (idle)"))); 0046 0047 m_statistics.insert(TINY_BREAKS, new RSIStatItem(i18n("Total number of short breaks"))); 0048 m_statistics[TINY_BREAKS]->addDerivedItem(PAUSE_SCORE); 0049 m_statistics[TINY_BREAKS]->addDerivedItem(LAST_TINY_BREAK); 0050 0051 m_statistics.insert(TINY_BREAKS_SKIPPED, new RSIStatItem(i18n("Number of skipped short breaks (user)"))); 0052 m_statistics[TINY_BREAKS_SKIPPED]->addDerivedItem(PAUSE_SCORE); 0053 0054 m_statistics.insert(TINY_BREAKS_POSTPONED, new RSIStatItem(i18n("Number of postponed short breaks (user)"))); 0055 0056 QDateTime dt(QDate(-1, -1, -1), QTime(-1, -1, -1)); 0057 m_statistics.insert(LAST_TINY_BREAK, new RSIStatItem(i18n("Last short break"), dt)); 0058 0059 m_statistics.insert(BIG_BREAKS, new RSIStatItem(i18n("Total number of long breaks"))); 0060 m_statistics[BIG_BREAKS]->addDerivedItem(PAUSE_SCORE); 0061 m_statistics[BIG_BREAKS]->addDerivedItem(LAST_BIG_BREAK); 0062 0063 m_statistics.insert(BIG_BREAKS_SKIPPED, new RSIStatItem(i18n("Number of skipped long breaks (user)"))); 0064 m_statistics[BIG_BREAKS_SKIPPED]->addDerivedItem(PAUSE_SCORE); 0065 0066 m_statistics.insert(BIG_BREAKS_POSTPONED, new RSIStatItem(i18n("Number of postponed long breaks (user)"))); 0067 0068 m_statistics.insert(LAST_BIG_BREAK, new RSIStatItem(i18n("Last long break"), dt)); 0069 0070 m_statistics.insert(PAUSE_SCORE, new RSIStatItem(i18n("Pause score"), 100)); 0071 0072 // initialise labels 0073 for (int i = 0; i < STAT_COUNT; ++i) { 0074 QLabel *l = new QLabel(nullptr); 0075 const QString whatsThis = getWhatsThisText(static_cast<RSIStat>(i)); 0076 l->setWhatsThis(whatsThis); 0077 m_statistics[i]->getDescription()->setWhatsThis(whatsThis); 0078 m_labels << l; 0079 } 0080 0081 // initialise statistics 0082 reset(); 0083 } 0084 0085 RSIStats::~RSIStats() 0086 { 0087 qDeleteAll(m_labels); 0088 0089 for (int i = 0; i < STAT_COUNT; ++i) { 0090 RSIStatItem *item = m_statistics[i]; 0091 QLabel *l = item->getDescription(); 0092 delete l; 0093 delete item; 0094 } 0095 } 0096 0097 void RSIStats::reset() 0098 { 0099 for (int i = 0; i < STAT_COUNT; ++i) { 0100 m_statistics[i]->reset(); 0101 updateStat(static_cast<RSIStat>(i), /* update derived stats */ false); 0102 } 0103 } 0104 0105 void RSIStats::increaseStat(RSIStat stat, int delta) 0106 { 0107 QVariant v = m_statistics[stat]->getValue(); 0108 0109 if (v.type() == QVariant::Int) 0110 m_statistics[stat]->setValue(v.toInt() + delta); 0111 else if (v.type() == QVariant::Double) 0112 m_statistics[stat]->setValue(v.toDouble() + (double)delta); 0113 else if (v.type() == QVariant::DateTime) 0114 m_statistics[stat]->setValue(QDateTime(v.toDateTime()).addSecs(delta)); 0115 0116 updateStat(stat); 0117 } 0118 0119 void RSIStats::setStat(RSIStat stat, const QVariant &val, bool ifmax) 0120 { 0121 QVariant v = m_statistics[stat]->getValue(); 0122 0123 if (!ifmax || (v.type() == QVariant::Int && val.toInt() > v.toInt()) || (v.type() == QVariant::Double && val.toDouble() > v.toDouble()) 0124 || (v.type() == QVariant::DateTime && val.toDateTime() > v.toDateTime())) 0125 m_statistics[stat]->setValue(val); 0126 0127 // WATCH OUT: IDLENESS is derived from MAX_IDLENESS and needs to be 0128 // updated regardless if a new value is set. 0129 updateStat(stat); 0130 } 0131 0132 void RSIStats::updateDependentStats(RSIStat stat) 0133 { 0134 const QList<RSIStat> &stats = m_statistics[stat]->getDerivedItems(); 0135 for (int i = 0; i < stats.count(); ++i) { 0136 RSIStat it = stats.at(i); 0137 switch ((it)) { 0138 case PAUSE_SCORE: { 0139 double a = m_statistics[TINY_BREAKS_SKIPPED]->getValue().toDouble(); 0140 double b = m_statistics[BIG_BREAKS_SKIPPED]->getValue().toDouble(); 0141 double c = m_statistics[IDLENESS_CAUSED_SKIP_TINY]->getValue().toDouble(); 0142 double d = m_statistics[IDLENESS_CAUSED_SKIP_BIG]->getValue().toDouble(); 0143 0144 RSIGlobals *glbl = RSIGlobals::instance(); 0145 double ratio = (double)(glbl->intervals()[BIG_BREAK_DURATION]) / (double)(glbl->intervals()[TINY_BREAK_DURATION]); 0146 0147 double skipped = a - c + ratio * (b - d); 0148 skipped = skipped < 0 ? 0 : skipped; 0149 0150 double total = m_statistics[TINY_BREAKS]->getValue().toDouble(); 0151 total += ratio * m_statistics[BIG_BREAKS]->getValue().toDouble(); 0152 0153 if (total > 0) 0154 m_statistics[it]->setValue(100 - ((skipped / total) * 100)); 0155 else 0156 m_statistics[it]->setValue(0); 0157 0158 updateStat(it); 0159 break; 0160 } 0161 0162 case IDLENESS: { 0163 increaseStat(IDLENESS); 0164 break; 0165 } 0166 0167 case ACTIVITY_PERC: { 0168 /* 0169 seconds of activity 0170 activity_percentage = 100 - ------------------- 0171 total seconds 0172 */ 0173 0174 double activity = m_statistics[ACTIVITY]->getValue().toDouble(); 0175 double total = m_statistics[TOTAL_TIME]->getValue().toDouble(); 0176 0177 if (total > 0) 0178 m_statistics[it]->setValue((activity / total) * 100); 0179 else 0180 m_statistics[it]->setValue(0); 0181 0182 updateStat(it); 0183 break; 0184 } 0185 0186 case ACTIVITY_PERC_MINUTE: 0187 case ACTIVITY_PERC_HOUR: 0188 case ACTIVITY_PERC_6HOUR: { 0189 if (stat == ACTIVITY) 0190 static_cast<RSIStatBitArrayItem *>(m_statistics[it])->setActivity(); 0191 else 0192 static_cast<RSIStatBitArrayItem *>(m_statistics[it])->setIdle(); 0193 0194 updateStat(it); 0195 break; 0196 } 0197 0198 case LAST_BIG_BREAK: { 0199 setStat(LAST_BIG_BREAK, QDateTime::currentDateTime()); 0200 break; 0201 } 0202 0203 case LAST_TINY_BREAK: { 0204 setStat(LAST_TINY_BREAK, QDateTime::currentDateTime()); 0205 break; 0206 } 0207 0208 default:; // nada 0209 } 0210 } 0211 } 0212 0213 void RSIStats::updateStat(RSIStat stat, bool updateDerived) 0214 { 0215 if (updateDerived) 0216 updateDependentStats(stat); 0217 0218 if (m_doUpdates) 0219 updateLabel(stat); 0220 } 0221 0222 void RSIStats::updateLabel(RSIStat stat) 0223 { 0224 QLabel *l = m_labels[stat]; 0225 double v; 0226 0227 switch (stat) { 0228 // integer values representing a time 0229 case TOTAL_TIME: 0230 case ACTIVITY: 0231 case IDLENESS: 0232 case MAX_IDLENESS: 0233 case CURRENT_IDLE_TIME: 0234 l->setText(RSIGlobals::instance()->formatSeconds(m_statistics[stat]->getValue().toInt())); 0235 break; 0236 0237 // plain integer values 0238 case TINY_BREAKS: 0239 case TINY_BREAKS_SKIPPED: 0240 case TINY_BREAKS_POSTPONED: 0241 case IDLENESS_CAUSED_SKIP_TINY: 0242 case BIG_BREAKS: 0243 case BIG_BREAKS_SKIPPED: 0244 case BIG_BREAKS_POSTPONED: 0245 case IDLENESS_CAUSED_SKIP_BIG: 0246 l->setText(QString::number(m_statistics[stat]->getValue().toInt())); 0247 break; 0248 0249 // doubles 0250 case PAUSE_SCORE: 0251 v = m_statistics[stat]->getValue().toDouble(); 0252 setColor(stat, QColor((int)(255 - 2.55 * v), (int)(1.60 * v), 0)); 0253 l->setText(QString::number(m_statistics[stat]->getValue().toDouble(), 'f', 1)); 0254 break; 0255 case ACTIVITY_PERC: 0256 case ACTIVITY_PERC_MINUTE: 0257 case ACTIVITY_PERC_HOUR: 0258 case ACTIVITY_PERC_6HOUR: 0259 v = m_statistics[stat]->getValue().toDouble(); 0260 setColor(stat, QColor((int)(2.55 * v), (int)(160 - 1.60 * v), 0)); 0261 l->setText(QString::number(m_statistics[stat]->getValue().toDouble(), 'f', 1)); 0262 break; 0263 0264 // datetimes 0265 case LAST_BIG_BREAK: 0266 case LAST_TINY_BREAK: { 0267 QTime when(m_statistics[stat]->getValue().toTime()); 0268 when.isValid() ? l->setText(when.toString()) : l->clear(); 0269 break; 0270 } 0271 0272 default:; // nada 0273 } 0274 0275 // some stats need a % 0276 if (stat == PAUSE_SCORE || stat == ACTIVITY_PERC || stat == ACTIVITY_PERC_MINUTE || stat == ACTIVITY_PERC_HOUR || stat == ACTIVITY_PERC_6HOUR) 0277 l->setText(l->text() + '%'); 0278 } 0279 0280 void RSIStats::updateLabels() 0281 { 0282 if (!m_doUpdates) 0283 return; 0284 0285 for (int i = 0; i < STAT_COUNT; ++i) { 0286 updateLabel(static_cast<RSIStat>(i)); 0287 } 0288 } 0289 0290 QVariant RSIStats::getStat(RSIStat stat) const 0291 { 0292 return m_statistics[stat]->getValue(); 0293 } 0294 0295 QLabel *RSIStats::getLabel(RSIStat stat) const 0296 { 0297 return m_labels[stat]; 0298 } 0299 0300 QLabel *RSIStats::getDescription(RSIStat stat) const 0301 { 0302 return m_statistics[stat]->getDescription(); 0303 } 0304 0305 QString RSIStats::getWhatsThisText(RSIStat stat) const 0306 { 0307 switch (stat) { 0308 case TOTAL_TIME: 0309 return i18n("This is the total time RSIBreak has been running."); 0310 case ACTIVITY: 0311 return i18n( 0312 "This is the total amount of time you used the " 0313 "keyboard or mouse."); 0314 case IDLENESS: 0315 return i18n( 0316 "This is the total amount of time you did not use " 0317 "the keyboard or mouse."); 0318 case ACTIVITY_PERC: 0319 return i18n( 0320 "This is a percentage of activity, based on the " 0321 "periods of activity vs. the total time RSIBreak has been running. " 0322 "The color indicates the level of your activity. When the color is " 0323 "close to full red it is recommended you lower your work pace."); 0324 case MAX_IDLENESS: 0325 return i18n( 0326 "This is the longest period of inactivity measured " 0327 "while RSIBreak has been running."); 0328 case TINY_BREAKS: 0329 return i18n("This is the total number of short breaks"); 0330 case LAST_TINY_BREAK: 0331 return i18n( 0332 "This is the time of the last finished short break. " 0333 "The color of this text gradually turns from green to red, " 0334 "indicating when you can expect the next tiny break."); 0335 case TINY_BREAKS_SKIPPED: 0336 return i18n( 0337 "This is the total number of short breaks " 0338 "which you skipped."); 0339 case TINY_BREAKS_POSTPONED: 0340 return i18n( 0341 "This is the total number of short breaks " 0342 "which you postponed."); 0343 case IDLENESS_CAUSED_SKIP_TINY: 0344 return i18n( 0345 "This is the total number of short breaks " 0346 "which were skipped because you were idle."); 0347 case BIG_BREAKS: 0348 return i18n("This is the total number of long breaks."); 0349 case LAST_BIG_BREAK: 0350 return i18n( 0351 "This is the time of the last finished long break." 0352 "The color of this text gradually turns from green to red," 0353 "indicating when you can expect the next big break."); 0354 case BIG_BREAKS_SKIPPED: 0355 return i18n( 0356 "This is the total number of long breaks " 0357 "which you skipped."); 0358 case BIG_BREAKS_POSTPONED: 0359 return i18n( 0360 "This is the total number of long breaks " 0361 "which you postponed."); 0362 case IDLENESS_CAUSED_SKIP_BIG: 0363 return i18n( 0364 "This is the total number of long breaks " 0365 "which were skipped because you were idle."); 0366 case PAUSE_SCORE: 0367 return i18n( 0368 "This is an indication of how well you behaved " 0369 "with the breaks. It decreases every time you skip a break."); 0370 case CURRENT_IDLE_TIME: 0371 return i18n("This is the current idle time."); 0372 case ACTIVITY_PERC_MINUTE: 0373 return i18n( 0374 "This is a percentage of activity during the last minute. " 0375 "The color indicates the level of your activity. When the color is " 0376 "close to full red it is recommended you lower your work pace."); 0377 case ACTIVITY_PERC_HOUR: 0378 return i18n( 0379 "This is a percentage of activity during the last hour. " 0380 "The color indicates the level of your activity. When the color is " 0381 "close to full red it is recommended you lower your work pace."); 0382 case ACTIVITY_PERC_6HOUR: 0383 return i18n( 0384 "This is a percentage of activity during the last 6 hours. " 0385 "The color indicates the level of your activity. When the color is " 0386 "close to full red it is recommended you lower your work pace."); 0387 default:; 0388 } 0389 0390 return QString(); 0391 } 0392 0393 void RSIStats::setColor(RSIStat stat, const QColor &color) 0394 { 0395 QPalette normal; 0396 normal.setColor(QPalette::Active, QPalette::WindowText, color); 0397 m_statistics[stat]->getDescription()->setPalette(normal); 0398 m_labels[stat]->setPalette(normal); 0399 } 0400 0401 void RSIStats::doUpdates(bool b) 0402 { 0403 m_doUpdates = b; 0404 if (m_doUpdates) 0405 updateLabels(); 0406 }