You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/plugins/statistics/statisticsdialog.cpp

544 lines
20 KiB

/*
statisticsdialog.cpp - Kopete History Dialog
Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
*************************************************************************
* *
* 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 <tqtabwidget.h>
#include <tqwidget.h>
#include <tqhbox.h>
#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqtextedit.h>
#include <tqcombobox.h>
#include <tqstring.h>
#include "kdialogbase.h"
#include "tdelocale.h"
#include "tdelistview.h"
#include "tdehtml_part.h"
#include "kstandarddirs.h"
#include "kdatepicker.h"
#include "ktimewidget.h"
#include "kopetemetacontact.h"
#include "kopeteonlinestatus.h"
#include "statisticsdialog.h"
#include "statisticscontact.h"
#include "statisticswidget.h"
#include "statisticsplugin.h"
#include "statisticsdb.h"
StatisticsDialog::StatisticsDialog(StatisticsContact *contact, StatisticsDB *db, TQWidget* parent,
const char* name) : KDialogBase(parent, name, false,
i18n("Statistics for %1").arg(contact->metaContact()->displayName()), Close, Close), m_db(db), m_contact(contact)
{
mainWidget = new StatisticsWidget(this);
setMainWidget(mainWidget);
setMinimumWidth(640);
setMinimumHeight(400);
adjustSize();
TQHBox *hbox = new TQHBox(this);
generalHTMLPart = new TDEHTMLPart(hbox);
generalHTMLPart->setOnlyLocalReferences(true);
connect ( generalHTMLPart->browserExtension(), TQT_SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
this, TQT_SLOT( slotOpenURLRequest( const KURL &, const KParts::URLArgs & ) ) );
mainWidget->tabWidget->insertTab(hbox, i18n("General"), 0);
mainWidget->tabWidget->setCurrentPage(0);
mainWidget->timePicker->setTime(TQTime::currentTime());
mainWidget->datePicker->setDate(TQDate::currentDate());
connect(mainWidget->askButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotAskButtonClicked()));
setFocus();
setEscapeButton(Close);
generatePageGeneral();
}
// We only generate pages when the user clicks on a link
void StatisticsDialog::slotOpenURLRequest(const KURL& url, const KParts::URLArgs&)
{
if (url.protocol() == "main")
{
generatePageGeneral();
}
else if (url.protocol() == "dayofweek")
{
generatePageForDay(url.path().toInt());
}
else if (url.protocol() == "monthofyear")
{
generatePageForMonth(url.path().toInt());
}
}
/*void StatisticsDialog::parseTemplate(TQString Template)
{
TQString fileString = ::locate("appdata", "kopete_statistics.template.html");
TQString templateString;
TQFile file(file);
if (file.open(IO_ReadOnly))
{
TQTextStream stream(&file);
templateString = stream.read();
file.close();
}
// The template is loaded in templateString now.
templateString.strReplace(
}*/
void StatisticsDialog::generatePageForMonth(const int monthOfYear)
{
TQStringList values = m_db->query(TQString("SELECT status, datetimebegin, datetimeend "
"FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;").arg(m_contact->statisticsContactId()));
TQStringList values2;
for (uint i=0; i<values.count(); i+=3)
{
TQDateTime dateTimeBegin;
dateTimeBegin.setTime_t(values[i+1].toInt());
/// @todo Same as for Day, check if second datetime is on the same month
if (dateTimeBegin.date().month() == monthOfYear)
{
values2.push_back(values[i]);
values2.push_back(values[i+1]);
values2.push_back(values[i+2]);
}
}
generatePageFromTQStringList(values2, TQDate::longMonthName(monthOfYear));
}
void StatisticsDialog::generatePageForDay(const int dayOfWeek)
{
TQStringList values = m_db->query(TQString("SELECT status, datetimebegin, datetimeend "
"FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;").arg(m_contact->statisticsContactId()));
TQStringList values2;
for (uint i=0; i<values.count(); i+=3)
{
TQDateTime dateTimeBegin;
dateTimeBegin.setTime_t(values[i+1].toInt());
TQDateTime dateTimeEnd;
dateTimeEnd.setTime_t(values[i+2].toInt());
if (dateTimeBegin.date().dayOfWeek() == dayOfWeek)
{
if (dateTimeEnd.date().dayOfWeek() != dayOfWeek)
// Day of week is not the same at beginning and at end of the event
{
values2.push_back(values[i]);
values2.push_back(values[i+1]);
// datetime from value[i+1]
dateTimeBegin = TQDateTime(dateTimeBegin.date(), TQTime(0, 0, 0));
dateTimeBegin.addSecs(dateTimeBegin.time().secsTo(TQTime(23, 59, 59)));
values2.push_back(TQString::number(dateTimeBegin.toTime_t()));
}
else
{
values2.push_back(values[i]);
values2.push_back(values[i+1]);
values2.push_back(values[i+2]);
}
}
}
generatePageFromTQStringList(values2, TQDate::longDayName(dayOfWeek));
}
/// @todo chart problem at midnight.
void StatisticsDialog::generatePageFromTQStringList(TQStringList values, const TQString & subTitle)
{
generalHTMLPart->begin();
generalHTMLPart->write(TQString("<html><head><style>.bar { margin:0px;} "
"body"
"{"
"font-size:11px"
"}"
".chart" // Style for the charts
"{ height:100px;"
"border-left:1px solid #999;"
"border-bottom:1px solid #999;"
"vertical-align:bottom;"
"}"
".statgroup" // Style for groups of similar statistics
"{ margin-bottom:10px;"
"background-color:white;"
"border-left: 5px solid #369;"
"border-top: 1px dashed #999;"
"border-bottom: 1px dashed #999;"
"margin-left: 10px;"
"margin-right: 5px;"
"padding:3px 3px 3px 10px;}"
"</style></head><body>" +
i18n("<h1>Statistics for %1</h1>").arg(m_contact->metaContact()->displayName()) +
"<h3>%1</h3><hr>").arg(subTitle));
generalHTMLPart->write(i18n("<div class=\"statgroup\"><b><a href=\"main:generalinfo\" title=\"General summary view\">General</a></b><br>"
"<span title=\"Select the a day or a month to view the stat for\"><b>Days: </b>"
"<a href=\"dayofweek:1\">Monday</a>&nbsp;"
"<a href=\"dayofweek:2\">Tuesday</a>&nbsp;"
"<a href=\"dayofweek:3\">Wednesday</a>&nbsp;"
"<a href=\"dayofweek:4\">Thursday</a>&nbsp;"
"<a href=\"dayofweek:5\">Friday</a>&nbsp;"
"<a href=\"dayofweek:6\">Saturday</a>&nbsp;"
"<a href=\"dayofweek:7\">Sunday</a><br>"
"<b>Months: </b>"
"<a href=\"monthofyear:1\">January</a>&nbsp;"
"<a href=\"monthofyear:2\">February</a>&nbsp;"
"<a href=\"monthofyear:3\">March</a>&nbsp;"
"<a href=\"monthofyear:4\">April</a>&nbsp;"
"<a href=\"monthofyear:5\">May</a>&nbsp;"
"<a href=\"monthofyear:6\">June</a>&nbsp;"
"<a href=\"monthofyear:7\">July</a>&nbsp;"
"<a href=\"monthofyear:8\">August</a>&nbsp;"
"<a href=\"monthofyear:9\">September</a>&nbsp;"
"<a href=\"monthofyear:10\">October</a>&nbsp;"
"<a href=\"monthofyear:11\">November</a>&nbsp;"
"<a href=\"monthofyear:12\">December</a>&nbsp;"
"</span></div><br>"));
// mainWidget->listView->addColumn(i18n("Status"));
// mainWidget->listView->addColumn(i18n("Start Date"));
// mainWidget->listView->addColumn(i18n("End Date"));
// mainWidget->listView->addColumn(i18n("Start Date"));
// mainWidget->listView->addColumn(i18n("End Date"));
TQString todayString;
todayString.append(i18n("<div class=\"statgroup\" title=\"Contact status history for today\"><h2>Today</h2><table width=\"100%\"><tr><td>Status</td><td>From</td><td>To</td></tr>"));
bool today;
int totalTime = 0; // this is in seconds
int totalAwayTime = 0; // this is in seconds
int totalOnlineTime = 0; // this is in seconds
int totalOfflineTime = 0; // idem
int hours[24]; // in seconds, too
int iMaxHours = 0;
int hoursOnline[24]; // this is in seconds
int iMaxHoursOnline = 0;
int hoursAway[24]; // this is in seconds
int iMaxHoursAway = 0;
int hoursOffline[24]; // this is in seconds. Hours where we are sure contact is offline
int iMaxHoursOffline = 0;
for (uint i=0; i<24; i++)
{
hours[i] = 0;
hoursOnline[i] = 0;
hoursAway[i] = 0;
hoursOffline[i] = 0;
}
for (uint i=0; i<values.count(); i+=3 /* because SELECT 3 columns */)
{
/* Here we try to interpret one database entry...
What's important here, is to not count two times the same hour for instance
This is why there are some if in all this stuff ;-)
*/
// it is the STARTDATE from the database
TQDateTime dateTime1;
dateTime1.setTime_t(values[i+1].toInt());
// it is the ENDDATE from the database
TQDateTime dateTime2;
dateTime2.setTime_t(values[i+2].toInt());
if (dateTime1.date() == TQDate::currentDate() || dateTime2.date() == TQDate::currentDate())
today = true;
else today = false;
totalTime += dateTime1.secsTo(dateTime2);
if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
totalOnlineTime += dateTime1.secsTo(dateTime2);
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
totalAwayTime += dateTime1.secsTo(dateTime2);
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
totalOfflineTime += dateTime1.secsTo(dateTime2);
/*
* To build the chart/hours
*/
// Number of hours between dateTime1 and dateTime2
int nbHours = (int)(dateTime1.secsTo(dateTime2)/3600.0);
uint tempHour =
dateTime1.time().hour() == dateTime2.time().hour()
? dateTime1.secsTo(dateTime2) // (*)
: 3600 - dateTime1.time().minute()*60 - dateTime1.time().second();
hours[dateTime1.time().hour()] += tempHour;
if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
hoursOnline[dateTime1.time().hour()] += tempHour;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
hoursAway[dateTime1.time().hour()] += tempHour;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
hoursOffline[dateTime1.time().hour()] += tempHour;
for (int j= dateTime1.time().hour()+1; j < dateTime1.time().hour() + nbHours - 1; j++)
{
hours[j%24] += 3600;
if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
hoursOnline[j%24] += 3600;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
hoursAway[j%24] += 3600;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
hoursOffline[j%24] += 3600;
}
if (dateTime1.time().hour() != dateTime2.time().hour())
// We dont want to count this if the hour from dateTime2 is the same than the one from dateTime1
// since it as already been taken in account in the (*) instruction
{
tempHour = dateTime2.time().minute()*60 +dateTime2.time().second();
hours[dateTime2.time().hour()] += tempHour;
if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
hoursOnline[dateTime2.time().hour()] += tempHour;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
hoursAway[dateTime2.time().hour()] += tempHour;
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
hoursOffline[dateTime2.time().hour()] += tempHour;
}
TQString color;
if (today)
{
TQString status;
if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
{
color="blue";
status = i18n("Online");
}
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
{
color="navy";
status = i18n("Away");
}
else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
{
color="gray";
status = i18n("Offline");
}
else color="white";
todayString.append(TQString("<tr style=\"color:%1\"><td>%2</td><td>%3</td><td>%4</td></tr>").arg(color, status, dateTime1.time().toString(), dateTime2.time().toString()));
}
// We add a listview item to the log list
// TQDateTime listViewDT1, listViewDT2;
// listViewDT1.setTime_t(values[i+1].toInt());
// listViewDT2.setTime_t(values[i+2].toInt());
// new TDEListViewItem(mainWidget->listView, values[i], values[i+1], values[i+2], listViewDT1.toString(), listViewDT2.toString());
}
todayString.append("</table></div>");
// Get the max from the hours*
for (uint i=1; i<24; i++)
{
if (hours[iMaxHours] < hours[i])
iMaxHours = i;
if (hoursOnline[iMaxHoursOnline] < hoursOnline[i])
iMaxHoursOnline = i;
if (hoursOffline[iMaxHoursOffline] < hoursOffline[i])
iMaxHoursOffline = i;
if (hoursAway[iMaxHoursAway] < hoursAway[i])
iMaxHoursAway = i;
}
//
/*
* Here we really generate the page
*/
// Some "total times"
generalHTMLPart->write(i18n("<div class=\"statgroup\">"));
generalHTMLPart->write(i18n("<b title=\"The total time I have been able to see %1 status\">"
"Total seen time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalTime)));
generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 online\">"
"Total online time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalOnlineTime)));
generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 away\">Total busy time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalAwayTime)));
generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 offline\">Total offline time :</b> %2 hour(s)").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalOfflineTime)));
generalHTMLPart->write(TQString("</div>"));
if (subTitle == i18n("General information"))
/*
* General stats that should not be shown on "day" or "month" pages
*/
{
generalHTMLPart->write(TQString("<div class=\"statgroup\">"));
generalHTMLPart->write(i18n("<b>Average message length :</b> %1 characters<br>").arg(m_contact->messageLength()));
generalHTMLPart->write(i18n("<b>Time between two messages : </b> %1 second(s)").arg(m_contact->timeBetweenTwoMessages()));
generalHTMLPart->write(TQString("</div>"));
generalHTMLPart->write(TQString("<div class=\"statgroup\">"));
generalHTMLPart->write(i18n("<b title=\"The last time you talked with %1\">Last talk :</b> %2<br>").arg(m_contact->metaContact()->displayName()).arg(TDEGlobal::locale()->formatDateTime(m_contact->lastTalk())));
generalHTMLPart->write(i18n("<b title=\"The last time I have seen %1 online or away\">Last time contact was present :</b> %2").arg(m_contact->metaContact()->displayName()).arg(TDEGlobal::locale()->formatDateTime(m_contact->lastPresent())));
generalHTMLPart->write(TQString("</div>"));
//generalHTMLPart->write(TQString("<div class=\"statgroup\">"));
//generalHTMLPart->write(i18n("<b title=\"%1 uses to set his status online at these hours (EXPERIMENTAL)\">Main online events :</b><br>").arg(m_contact->metaContact()->displayName()));
//TQValueList<TQTime> mainEvents = m_contact->mainEvents(Kopete::OnlineStatus::Online);
//for (uint i=0; i<mainEvents.count(); i++)
//generalHTMLPart->write(TQString("%1<br>").arg(mainEvents[i].toString()));
//generalHTMLPart->write(TQString("</div>"));
generalHTMLPart->write("<div title=\"" +i18n("Current status") + "\" class=\"statgroup\">");
generalHTMLPart->write(i18n("Is <b>%1</b> since <b>%2</b>").arg(
Kopete::OnlineStatus(m_contact->oldStatus()).description(),
TDEGlobal::locale()->formatDateTime(m_contact->oldStatusDateTime())));
generalHTMLPart->write(TQString("</div>"));
}
/*
* Chart which show the hours where plugin has seen this contact online
*/
generalHTMLPart->write(TQString("<div class=\"statgroup\">"));
generalHTMLPart->write(TQString("<table width=\"100%\"><tr><td colspan=\"3\">") + i18n("When have I seen this contact ?") + TQString("</td></tr>"));
generalHTMLPart->write(TQString("<tr><td height=\"200\" valign=\"bottom\" colspan=\"3\" class=\"chart\">"));
TQString chartString;
TQString colorPath = ::locate("appdata", "pics/statistics/black.png");
for (uint i=0; i<24; i++)
{
int hrWidth = tqRound((double)hours[i]/(double)hours[iMaxHours]*100.);
chartString += TQString("<img class=\"margin:0px;\" height=\"")
+(totalTime ? TQString::number(hrWidth) : TQString::number(0))
+TQString("\" src=\"file://")
+colorPath
+"\" width=\"4%\" title=\""
+i18n("Between %1:00 and %2:00, I was able to see %3 status %4% of the hour.").arg(i).arg((i+1)%24).arg(m_contact->metaContact()->displayName()).arg(hrWidth)
+TQString("\">");
}
generalHTMLPart->write(chartString);
generalHTMLPart->write(TQString("</td></tr>"));
generalHTMLPart->write(TQString( "<tr>"
"<td>")+i18n("Online time")+TQString("</td><td>")+i18n("Away time")+TQString("</td><td>")+i18n("Offline time")+TQString("</td>"
"</tr>"
"<td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
generalHTMLPart->write(generateHTMLChart(hoursOnline, hoursAway, hoursOffline, i18n("online"), "blue"));
generalHTMLPart->write(TQString("</td><td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
generalHTMLPart->write(generateHTMLChart(hoursAway, hoursOnline, hoursOffline, i18n("away"), "navy"));
generalHTMLPart->write(TQString("</td><td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
generalHTMLPart->write(generateHTMLChart(hoursOffline, hoursAway, hoursOnline, i18n("offline"), "gray"));
generalHTMLPart->write(TQString("</td></tr></table></div>"));
if (subTitle == i18n("General information"))
/* On main page, show the different status of the contact today
*/
{
generalHTMLPart->write(TQString(todayString));
}
generalHTMLPart->write(TQString("</body></html>"));
generalHTMLPart->end();
}
void StatisticsDialog::generatePageGeneral()
{
TQStringList values;
values = m_db->query(TQString("SELECT status, datetimebegin, datetimeend "
"FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;")
.arg(m_contact->statisticsContactId()));
generatePageFromTQStringList(values, i18n("General information"));
}
TQString StatisticsDialog::generateHTMLChart(const int *hours, const int *hours2, const int *hours3, const TQString & caption, const TQString & color)
{
TQString chartString;
TQString colorPath = ::locate("appdata", "pics/statistics/"+color+".png");
for (uint i=0; i<24; i++)
{
int totalTime = hours[i] + hours2[i] + hours3[i];
int hrWidth = tqRound((double)hours[i]/(double)totalTime*100.);
chartString += TQString("<img class=\"margin:0px;\" height=\"")
+(totalTime ? TQString::number(hrWidth) : TQString::number(0))
+TQString("\" src=\"file://")
+colorPath
+"\" width=\"4%\" title=\""+
i18n("Between %1:00 and %2:00, I have seen %3 %4% %5.").
arg(i).
arg((i+1) % 24).
arg(m_contact->metaContact()->displayName()).
arg(hrWidth).
arg(caption)
+".\">";
}
return chartString;
}
TQString StatisticsDialog::stringFromSeconds(const int seconds)
{
int h, m, s;
h = seconds/3600;
m = (seconds % 3600)/60;
s = (seconds % 3600) % 60;
return TQString::number(h)+":"+TQString::number(m)+":"+TQString::number(s);
}
void StatisticsDialog::slotAskButtonClicked()
{
if (mainWidget->questionComboBox->currentItem()==0)
{
TQString text = i18n("1 is date, 2 is contact name, 3 is online status", "%1, %2 was %3")
.arg(TDEGlobal::locale()->formatDateTime(TQDateTime(mainWidget->datePicker->date(), mainWidget->timePicker->time())))
.arg(m_contact->metaContact()->displayName())
.arg(m_contact->statusAt(TQDateTime(mainWidget->datePicker->date(), mainWidget->timePicker->time())));
mainWidget->answerEdit->setText(text);
}
else if (mainWidget->questionComboBox->currentItem()==1)
{
mainWidget->answerEdit->setText(m_contact->mainStatusDate(mainWidget->datePicker->date()));
}
else if (mainWidget->questionComboBox->currentItem()==2)
// Next online
{
}
}
#include "statisticsdialog.moc"