/* statisticscontact.cpp Copyright (c) 2003-2004 by Marc Cramdal ************************************************************************* * * * 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. * * * ************************************************************************* */ #include #include #include #include #include #include "kopetemetacontact.h" #include "kopeteonlinestatus.h" #include "statisticscontact.h" #include "statisticsdb.h" StatisticsContact::StatisticsContact(Kopete::MetaContact *mc, StatisticsDB *db) : m_metaContact(mc),m_db(db), m_oldStatus(Kopete::OnlineStatus::Unknown) { m_isChatWindowOpen = false; m_oldStatusDateTime = QDateTime::currentDateTime(); // Last*Changed are always false at start m_timeBetweenTwoMessagesChanged = false; m_lastTalkChanged = false; m_lastPresentChanged = false; m_messageLengthChanged = false; } /** * \brief saves contact statistics */ StatisticsContact::~StatisticsContact() { if (m_statisticsContactId.isEmpty()) return; commonStatsSave("timebetweentwomessages",QString::number(m_timeBetweenTwoMessages), QString::number(m_timeBetweenTwoMessagesOn), m_timeBetweenTwoMessagesChanged); commonStatsSave("messagelength",QString::number(m_messageLength), QString::number(m_messageLengthOn), m_messageLengthChanged); commonStatsSave("lasttalk", m_lastTalk.toString(), "", m_lastTalkChanged); commonStatsSave("lastpresent", m_lastPresent.toString(), "", m_lastPresentChanged); } void StatisticsContact::initialize(Kopete::Contact *c) { // Generate statisticsContactId or get it from database QStringList buffer = m_db->query(QString("SELECT statisticid FROM contacts " "WHERE contactid LIKE '%1';" ).arg(c->contactId())); if (buffer.isEmpty()) { // Check if we don't have old data if ( !c->metaContact()->metaContactId().isEmpty() && !m_db->query(QString("SELECT metacontactid FROM commonstats " "WHERE metacontactid LIKE '%1';" ).arg(c->metaContact()->metaContactId())).isEmpty()) { // Use old style id m_statisticsContactId = c->metaContact()->metaContactId(); } else { // Create new id m_statisticsContactId = QUuid::createUuid().toString(); } // Assign contactId to m_statisticsContactId m_db->query(QString("INSERT INTO contacts (statisticid, contactid) VALUES('%1', '%2');" ).arg(m_statisticsContactId).arg(c->contactId())); } else { m_statisticsContactId = buffer[0]; } kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl; commonStatsCheck("timebetweentwomessages", m_timeBetweenTwoMessages, m_timeBetweenTwoMessagesOn, 0, -1); commonStatsCheck("messagelength", m_messageLength, m_messageLengthOn, 0, 0); // Check for last talk QString lastTalk; QString dummy = ""; commonStatsCheck("lasttalk", lastTalk, dummy); if (lastTalk.isEmpty()) { m_lastTalk.setTime_t(0); m_lastTalkChanged = true; } else m_lastTalk = QDateTime::fromString(lastTalk); // Get last time a message was received m_lastMessageReceived = QDateTime::currentDateTime(); // Check for lastPresent QString lastPresent = ""; commonStatsCheck("lastpresent", lastPresent, dummy); if (lastPresent.isEmpty()) { m_lastPresent.setTime_t(0); m_lastPresentChanged = true; } else m_lastPresent = QDateTime::fromString(lastPresent); } void StatisticsContact::contactAdded( Kopete::Contact *c ) { if ( !m_statisticsContactId.isEmpty() ) { // Check if contact is allready in database if not add it if (m_db->query(QString("SELECT id FROM contacts " "WHERE statisticid LIKE '%1' AND contactid LIKE '%2';" ).arg(m_statisticsContactId).arg(c->contactId())).isEmpty()) { // Assign contactId to m_statisticsContactId m_db->query(QString("INSERT INTO contacts (statisticid, contactid) VALUES('%1', '%2');" ).arg(m_statisticsContactId).arg(c->contactId())); } kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl; } else { // This is first contact, we need to initialize this object initialize(c); } } void StatisticsContact::contactRemoved( Kopete::Contact *c ) { if (m_statisticsContactId.isEmpty()) return; kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl; m_db->query(QString("DELETE FROM contacts WHERE statisticid LIKE '%1' AND contactid LIKE '%2';" ).arg(m_statisticsContactId).arg(c->contactId())); } void StatisticsContact::removeFromDB() { if (m_statisticsContactId.isEmpty()) return; kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl; m_db->query(QString("DELETE FROM contacts WHERE statisticid LIKE '%1';").arg(m_statisticsContactId)); m_db->query(QString("DELETE FROM contactstatus WHERE metacontactid LIKE '%1';").arg(m_statisticsContactId)); m_db->query(QString("DELETE FROM commonstats WHERE metacontactid LIKE '%1';").arg(m_statisticsContactId)); m_statisticsContactId = QString::null; } void StatisticsContact::commonStatsSave(const QString name, const QString statVar1, const QString statVar2, const bool statVarChanged) { // Only update the database if there was a change if (!statVarChanged) return; if (m_statisticsContactId.isEmpty()) return; m_db->query(QString("UPDATE commonstats SET statvalue1 = '%1', statvalue2='%2'" "WHERE statname LIKE '%3' AND metacontactid LIKE '%4';").arg(statVar1).arg(statVar2).arg(name).arg(m_statisticsContactId)); } void StatisticsContact::commonStatsCheck(const QString name, int& statVar1, int& statVar2, const int defaultValue1, const int defaultValue2) { QString a = QString::number(statVar1); QString b = QString::number(statVar2); commonStatsCheck(name, a, b, QString::number(defaultValue1), QString::number(defaultValue2)); statVar1 = a.toInt(); statVar2 = b.toInt(); } void StatisticsContact::commonStatsCheck(const QString name, QString& statVar1, QString& statVar2, const QString defaultValue1, const QString defaultValue2) { if (m_statisticsContactId.isEmpty()) return; QStringList buffer = m_db->query(QString("SELECT statvalue1,statvalue2 FROM commonstats WHERE statname LIKE '%1' AND metacontactid LIKE '%2';").arg(name, m_statisticsContactId)); if (!buffer.isEmpty()) { statVar1 = buffer[0]; statVar2 = buffer[1]; } else { m_db->query(QString("INSERT INTO commonstats (metacontactid, statname, statvalue1, statvalue2) VALUES('%1', '%2', 0, 0);").arg(m_statisticsContactId, name)); statVar1 = defaultValue1; statVar2 = defaultValue2; } } /** * \brief records informations from the new message * * Currently it does : *
    *
  • Recalculate the average time between two messages * It should only calculate this time if a chatwindow is open (sure, it isn't * perfect, because we could let a chatwindow open a whole day but that's at this * time the nicest way, maybe we could check the time between the two last messages * and if it is greater than, say, 10 min, do as there where no previous message) * So we do this when the chatwindow is open. We don't set m_isChatWindowOpen to true * when a new chatwindow is open, but when a new message arrives. However, we set it * to false when the chatwindow is closed (see StatisticsPlugin::slotViewClosed). * * Then it is only a question of some calculations. * *
  • Recalculate the average message lenght * *
  • Change last-talk datetime *
* */ void StatisticsContact::newMessageReceived(Kopete::Message& m) { kdDebug() << "statistics: new message received" << endl; QDateTime currentDateTime = QDateTime::currentDateTime(); if (m_timeBetweenTwoMessagesOn != -1 && m_isChatWindowOpen) { m_timeBetweenTwoMessages = (m_timeBetweenTwoMessages*m_timeBetweenTwoMessagesOn + m_lastMessageReceived.secsTo(currentDateTime))/(1 + m_timeBetweenTwoMessagesOn); } setIsChatWindowOpen(true); m_timeBetweenTwoMessagesOn += 1; m_lastMessageReceived = currentDateTime; // Message lenght m_messageLength= (m.plainBody().length() + m_messageLength * m_messageLengthOn)/(1 + m_messageLengthOn); m_messageLengthOn++; // Last talked /// @todo do this in message sent too. So we need setLastTalk() m_lastTalk = currentDateTime; m_messageLengthChanged = true; m_lastTalkChanged = true; m_timeBetweenTwoMessagesChanged = true; } /** * \brief Update the database for this contact when required. */ void StatisticsContact::onlineStatusChanged(Kopete::OnlineStatus::StatusType status) { if (m_statisticsContactId.isEmpty()) return; QDateTime currentDateTime = QDateTime::currentDateTime(); /// We don't want to log if oldStatus is unknown /// the change could not be a real one; see StatisticsPlugin::slotMySelfOnlineStatusChanged if (m_oldStatus != Kopete::OnlineStatus::Unknown) { kdDebug() << "statistics - status change for "<< metaContact()->metaContactId() << " : "<< QString::number(m_oldStatus) << endl; m_db->query(QString("INSERT INTO contactstatus " "(metacontactid, status, datetimebegin, datetimeend) " "VALUES('%1', '%2', '%3', '%4'" ");").arg(m_statisticsContactId).arg(Kopete::OnlineStatus::statusTypeToString(m_oldStatus)).arg(QString::number(m_oldStatusDateTime.toTime_t())).arg(QString::number(currentDateTime.toTime_t()))); } if (m_oldStatus == Kopete::OnlineStatus::Online || m_oldStatus == Kopete::OnlineStatus::Away) // If the last status was Online or Away, the last time contact was present is the time he goes offline { m_lastPresent = currentDateTime; m_lastPresentChanged = true; } m_oldStatus = status; m_oldStatusDateTime = currentDateTime; } bool StatisticsContact::wasStatus(QDateTime dt, Kopete::OnlineStatus::StatusType status) { if (m_statisticsContactId.isEmpty()) return false; QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend " "FROM contactstatus WHERE metacontactid LIKE '%1' AND datetimebegin <= %2 AND datetimeend >= %3 " "AND status LIKE '%4' " "ORDER BY datetimebegin;" ).arg(m_statisticsContactId).arg(dt.toTime_t()).arg(dt.toTime_t()).arg(Kopete::OnlineStatus::statusTypeToString(status))); if (!values.isEmpty()) return true; return false; } QString StatisticsContact::statusAt(QDateTime dt) { if (m_statisticsContactId.isEmpty()) return ""; QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend " "FROM contactstatus WHERE metacontactid LIKE '%1' AND datetimebegin <= %2 AND datetimeend >= %3 " "ORDER BY datetimebegin;" ).arg(m_statisticsContactId).arg(dt.toTime_t()).arg(dt.toTime_t())); if (!values.isEmpty()) return Kopete::OnlineStatus(Kopete::OnlineStatus::statusStringToType(values[0])).description(); else return ""; } QString StatisticsContact::mainStatusDate(const QDate& date) { if (m_statisticsContactId.isEmpty()) return ""; QDateTime dt1(date, QTime(0,0,0)); QDateTime dt2(date.addDays(1), QTime(0,0,0)); kdDebug() << "dt1:" << dt1.toString() << " dt2:" << dt2.toString() << endl; QString request = QString("SELECT status, datetimebegin, datetimeend, metacontactid " "FROM contactstatus WHERE metacontactid = '%1' AND " "(datetimebegin >= %2 AND datetimebegin <= %3 OR " "datetimeend >= %4 AND datetimeend <= %5) " "ORDER BY datetimebegin;" ).arg(m_statisticsContactId).arg(dt1.toTime_t()).arg(dt2.toTime_t()).arg(dt1.toTime_t()).arg(dt2.toTime_t()); kdDebug() << request << endl; QStringList values = m_db->query(request); unsigned int online = 0, offline = 0, away = 0; for(uint i=0; i= dt2.toTime_t()) datetimeend = dt2.toTime_t(); if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Online)) online += datetimeend - datetimebegin; else if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Away)) away += datetimeend - datetimebegin; else if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Offline)) offline += datetimeend - datetimebegin; } if (online > away && online > offline) return i18n("Online"); else if (away > online && away > offline) return i18n("Away"); else if (offline > online && offline > away) return i18n("Offline"); return ""; } // QDateTime StatisticsContact::nextOfflineEvent() // { // return nextEvent(Kopete::OnlineStatus::Offline); // } // // QDateTime StatisticsContact::nextOnlineEvent() // { // return nextEvent(Kopete::OnlineStatus::Online); // } // QDateTime StatisticsContact::nextEvent(const Kopete::OnlineStatus::StatusType& status) // { // // } QValueList StatisticsContact::mainEvents(const Kopete::OnlineStatus::StatusType& status) { QStringList buffer; QValueList mainEvents; if (m_statisticsContactId.isEmpty()) return mainEvents; QDateTime currentDateTime = QDateTime::currentDateTime(); buffer = m_db->query(QString("SELECT datetimebegin, datetimeend, status FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin").arg(m_statisticsContactId)); // Only select the events for which the previous is not Unknown AND the status is status. QStringList values; for (uint i=0; i 120) { values.push_back(buffer[i]); } } // No entries for this contact ... if (!values.count()) return mainEvents; // First we compute the average number of events/day : avEventsPerDay; int avEventsPerDay = 0; QDateTime dt1, dt2; dt1.setTime_t(values[0].toInt()); dt2.setTime_t(values[values.count()-1].toInt()); avEventsPerDay = qRound((double)values.count()/(double)dt1.daysTo(dt2)); kdDebug() << "statistics: average events per day : " < hoursValues; for (uint i=0; i centroids; int incr=qRound((double)hoursValues.count()/(double)avEventsPerDay); incr = incr ? incr : 1; for (uint i=0; i StatisticsContact::computeCentroids(const QValueList& centroids, const QValueList& values) { kdDebug() << "statistics: enter compute centroids"<< endl; QValueList whichCentroid; // whichCentroid[i] = j <=> values[i] has centroid j for closest one QValueList newCentroids; for (uint i=0; i 10) return computeCentroids(newCentroids, values); else { return newCentroids; } }