~ubuntu-branches/ubuntu/precise/knemo/precise

« back to all changes in this revision

Viewing changes to src/knemod/interfacestatistics.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Mark Purcell
  • Date: 2010-01-06 17:16:51 UTC
  • mfrom: (1.1.9 upstream) (2.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100106171651-ff58ryfnav4l1zbm
Tags: 0.6.0-1
* New upstream release 
  - Fixes "FTBFS: sysbackend.cpp:362: error: 'KILO' was not declared in
  this scope" (Closes: #560496)
  - Fixes "context menu does not appear" (Closes: #504791)
* Add Build-Depends: libnl-dev - Linux netlink sockets library

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
   Boston, MA 02110-1301, USA.
19
19
*/
20
20
 
21
 
#include <QDate>
22
21
#include <QDomNode>
23
22
#include <QFile>
24
23
#include <QTextStream>
25
24
#include <QTimer>
26
25
 
 
26
#include <KCalendarSystem>
 
27
#include <KConfigGroup>
 
28
#include <KGlobal>
 
29
 
27
30
#include "interface.h"
28
31
#include "interfacestatistics.h"
29
32
 
 
33
static const char statistics_prefix[] = "/statistics_";
 
34
 
 
35
static const char doc_name[]     = "statistics";
 
36
static const char group_days[]   = "days";
 
37
static const char group_weeks[]  = "weeks";
 
38
static const char group_months[] = "months";
 
39
static const char group_years[]  = "years";
 
40
 
 
41
static const char elem_day[]   = "day";
 
42
static const char elem_week[]  = "week";
 
43
static const char elem_month[] = "month";
 
44
static const char elem_year[]  = "year";
 
45
 
 
46
static const char attrib_calendar[] = "calendar";
 
47
static const char attrib_version[]  = "version";
 
48
static const char attrib_span[]     = "span";
 
49
static const char attrib_rx[]       = "rxBytes";
 
50
static const char attrib_tx[]       = "txBytes";
 
51
 
 
52
// Needed to upgrade from earlier versions
 
53
static const char attrib_day[]   = "day";
 
54
static const char attrib_month[] = "month";
 
55
static const char attrib_year[]  = "year";
30
56
 
31
57
static bool statisticsLessThan( const StatisticEntry *s1, const StatisticEntry *s2 )
32
58
{
33
 
    if ( s1->year < s2->year )
34
 
        return true;
35
 
    else if ( s1->year > s2->year )
36
 
        return false;
37
 
 
38
 
    if ( s1->month < s2->month )
39
 
        return true;
40
 
    else if ( s1->month > s2->month )
41
 
        return false;
42
 
 
43
 
    return ( s1->day < s2->day );
 
59
    if ( s1->date < s2->date )
 
60
        return true;
 
61
    else
 
62
        return false;
44
63
}
45
64
 
46
65
 
47
66
InterfaceStatistics::InterfaceStatistics( Interface* interface )
48
67
    : QObject(),
49
 
      mInterface( interface )
 
68
      mInterface( interface ),
 
69
      mWarningDone( false ),
 
70
      mAllMonths( true ),
 
71
      mBillingStart( mInterface->getSettings().billingStart ),
 
72
      mCalendar( KCalendarSystem::create( mInterface->getSettings().calendar ) )
50
73
{
 
74
    loadStatistics();
 
75
    if ( mMonthStatistics.count() )
 
76
    {
 
77
        QDate nextStart = getNextMonthStart( mBillingStart );
 
78
        if ( mBillingStart.daysTo( nextStart ) != mMonthStatistics.at( mMonthStatistics.count() -1 )->span
 
79
             || mBillingStart != mMonthStatistics.at( mMonthStatistics.count() - 1 )->date )
 
80
            rebuildStats( mBillingStart, Month );
 
81
    }
51
82
    initStatistics();
52
 
 
53
83
    mSaveTimer = new QTimer();
54
84
    connect( mSaveTimer, SIGNAL( timeout() ), this, SLOT( saveStatistics() ) );
55
 
    mSaveTimer->start( mInterface->getGeneralData().saveInterval * 1000 );
56
85
}
57
86
 
58
87
InterfaceStatistics::~InterfaceStatistics()
61
90
    delete mSaveTimer;
62
91
 
63
92
    mDayStatistics.clear();
 
93
    mWeekStatistics.clear();
64
94
    mMonthStatistics.clear();
65
95
    mYearStatistics.clear();
 
96
 
 
97
    if ( mInterface->getSettings().customBilling )
 
98
        saveBillingStart();
 
99
}
 
100
 
 
101
void InterfaceStatistics::loadStatsGroup( const KCalendarSystem * cal, const QDomElement& parentItem,
 
102
                                          enum GroupType group, QList<StatisticEntry *>& statistics )
 
103
{
 
104
    QString groupName;
 
105
 
 
106
    switch ( group )
 
107
    {
 
108
        case Day:
 
109
            groupName = group_days;
 
110
            break;
 
111
        case Week:
 
112
            groupName = group_weeks;
 
113
            break;
 
114
        case Month:
 
115
            groupName = group_months;
 
116
            mAllMonths = true;
 
117
            break;
 
118
        case Year:
 
119
            groupName = group_years;
 
120
            break;
 
121
    }
 
122
 
 
123
    QDomNode n = parentItem.namedItem( groupName );
 
124
    if ( !n.isNull() )
 
125
    {
 
126
        QDomNode node = n.firstChild();
 
127
        while ( !node.isNull() )
 
128
        {
 
129
            QDomElement element = node.toElement();
 
130
            if ( !element.isNull() )
 
131
            {
 
132
                StatisticEntry* entry = new StatisticEntry();
 
133
                entry->rxBytes = element.attribute( attrib_rx ).toULongLong();
 
134
                entry->txBytes = element.attribute( attrib_tx ).toULongLong();
 
135
 
 
136
                // The following attributes are particular to each statistic category
 
137
                int day = 1;
 
138
                switch ( group )
 
139
                {
 
140
                    case Day:
 
141
                        cal->setDate( entry->date,
 
142
                                      element.attribute( attrib_year ).toInt(),
 
143
                                      element.attribute( attrib_month ).toInt(),
 
144
                                      element.attribute( attrib_day ).toInt() );
 
145
 
 
146
                        entry->span = 1;
 
147
                        break;
 
148
                    case Week:
 
149
                        cal->setDate( entry->date,
 
150
                                      element.attribute( attrib_year ).toInt(),
 
151
                                      element.attribute( attrib_month ).toInt(),
 
152
                                      element.attribute( attrib_day ).toInt() );
 
153
 
 
154
                        // If calendar has changed then this will be recalculated elsewhere
 
155
                        entry->span = element.attribute( attrib_span ).toInt();
 
156
                        break;
 
157
                    case Month:
 
158
                        if ( element.hasAttribute( attrib_day ) )
 
159
                            day = element.attribute( attrib_day ).toInt();
 
160
 
 
161
                        cal->setDate( entry->date,
 
162
                                      element.attribute( attrib_year ).toInt(),
 
163
                                      element.attribute( attrib_month ).toInt(),
 
164
                                      day );
 
165
 
 
166
                        entry->span = element.attribute( attrib_span ).toInt();
 
167
                        // Old format had no span, so daysInMonth using gregorian
 
168
                        if ( entry->span == 0 )
 
169
                            entry->span = entry->date.daysInMonth();
 
170
                        if ( cal->day( entry->date ) != 1 ||
 
171
                             entry->span != cal->daysInMonth( entry->date ) )
 
172
                            mAllMonths = false;
 
173
                        break;
 
174
                    case Year:
 
175
                        cal->setDate( entry->date,
 
176
                                      element.attribute( attrib_year ).toInt(),
 
177
                                      1,
 
178
                                      1 );
 
179
 
 
180
                        entry->span = element.attribute( attrib_span ).toInt();
 
181
                        // Old format had no span, so daysInYear using gregorian
 
182
                        if ( entry->span == 0 )
 
183
                            entry->span = entry->date.daysInYear();
 
184
                        break;
 
185
                }
 
186
                if ( entry->date.isValid() )
 
187
                    statistics.append( entry );
 
188
                else
 
189
                    delete entry;
 
190
            }
 
191
            node = node.nextSibling();
 
192
        }
 
193
        qSort( statistics.begin(), statistics.end(), statisticsLessThan );
 
194
    }
66
195
}
67
196
 
68
197
void InterfaceStatistics::loadStatistics()
69
198
{
70
 
    QDomDocument doc( "statistics" );
 
199
    QDomDocument doc( doc_name );
71
200
    KUrl dir( mInterface->getGeneralData().statisticsDir );
72
201
    if ( !dir.isLocalFile() )
73
202
        return;
74
 
    QFile file( dir.path() + "/statistics_" + mInterface->getName() );
 
203
    QFile file( dir.path() + statistics_prefix + mInterface->getName() );
75
204
 
76
205
    if ( !file.open( QIODevice::ReadOnly ) )
77
206
        return;
83
212
    file.close();
84
213
 
85
214
    mDayStatistics.clear();
 
215
    mWeekStatistics.clear();
86
216
    mMonthStatistics.clear();
87
217
    mYearStatistics.clear();
88
218
 
89
219
    QDomElement root = doc.documentElement();
90
 
    QDomNode n = root.namedItem( "days" );
91
 
    if ( !n.isNull() )
92
 
    {
93
 
        QDomNode dayNode = n.firstChild();
94
 
        while ( !dayNode.isNull() )
95
 
        {
96
 
            QDomElement day = dayNode.toElement();
97
 
            if ( !day.isNull() )
98
 
            {
99
 
                StatisticEntry* entry = new StatisticEntry();
100
 
                entry->day = day.attribute( "day" ).toInt();
101
 
                entry->month = day.attribute( "month" ).toInt();
102
 
                entry->year = day.attribute( "year" ).toInt();
103
 
                entry->rxBytes = (quint64) day.attribute( "rxBytes" ).toDouble();
104
 
                entry->txBytes = (quint64) day.attribute( "txBytes" ).toDouble();
105
 
                mDayStatistics.append( entry );
106
 
            }
107
 
            dayNode = dayNode.nextSibling();
108
 
        }
109
 
        qSort( mDayStatistics.begin(), mDayStatistics.end(), statisticsLessThan );
110
 
    }
111
 
 
112
 
    n = root.namedItem( "months" );
113
 
    if ( !n.isNull() )
114
 
    {
115
 
        QDomNode monthNode = n.firstChild();
116
 
        while ( !monthNode.isNull() )
117
 
        {
118
 
            QDomElement month = monthNode.toElement();
119
 
            if ( !month.isNull() )
120
 
            {
121
 
                StatisticEntry* entry = new StatisticEntry();
122
 
                entry->day = 0;
123
 
                entry->month = month.attribute( "month" ).toInt();
124
 
                entry->year = month.attribute( "year" ).toInt();
125
 
                entry->rxBytes = (quint64) month.attribute( "rxBytes" ).toDouble();
126
 
                entry->txBytes = (quint64) month.attribute( "txBytes" ).toDouble();
127
 
                mMonthStatistics.append( entry );
128
 
            }
129
 
            monthNode = monthNode.nextSibling();
130
 
        }
131
 
        qSort( mMonthStatistics.begin(), mMonthStatistics.end(), statisticsLessThan );
132
 
    }
133
 
 
134
 
    n = root.namedItem( "years" );
135
 
    if ( !n.isNull() )
136
 
    {
137
 
        QDomNode yearNode = n.firstChild();
138
 
        while ( !yearNode.isNull() )
139
 
        {
140
 
            QDomElement year = yearNode.toElement();
141
 
            if ( !year.isNull() )
142
 
            {
143
 
                StatisticEntry* entry = new StatisticEntry();
144
 
                entry->day = 0;
145
 
                entry->month = 0;
146
 
                entry->year = year.attribute( "year" ).toInt();
147
 
                entry->rxBytes = (quint64) year.attribute( "rxBytes" ).toDouble();
148
 
                entry->txBytes = (quint64) year.attribute( "txBytes" ).toDouble();
149
 
                mYearStatistics.append( entry );
150
 
            }
151
 
            yearNode = yearNode.nextSibling();
152
 
        }
153
 
        qSort( mYearStatistics.begin(), mYearStatistics.end(), statisticsLessThan );
154
 
    }
155
 
    initStatistics();
 
220
 
 
221
    // If unknown or empty calendar it will default to gregorian
 
222
    KCalendarSystem *inCal = KCalendarSystem::create( root.attribute( attrib_calendar ) );
 
223
    loadStatsGroup( inCal, root, Day, mDayStatistics );
 
224
    loadStatsGroup( inCal, root, Week, mWeekStatistics );
 
225
    loadStatsGroup( inCal, root, Month, mMonthStatistics );
 
226
    loadStatsGroup( inCal, root, Year, mYearStatistics );
 
227
 
 
228
    // Discrepency: rebuild week and year based on calendar type in settings
 
229
    if ( root.attribute( attrib_calendar ).isEmpty() ||
 
230
         inCal->calendarType() != mCalendar->calendarType() )
 
231
    {
 
232
        // Let's do a backup before a significant rebuild
 
233
        file.copy( dir.path() + statistics_prefix + mInterface->getName() +
 
234
                   QString( "_%1.bak" ).arg( QDateTime::currentDateTime().toString( "yyyy-MM-dd-hhmmss" ) ) );
 
235
        rebuildStats( mDayStatistics.first()->date, Week );
 
236
    }
 
237
    if ( inCal->calendarType() != mCalendar->calendarType() )
 
238
    {
 
239
        rebuildStats( mDayStatistics.first()->date, Year );
 
240
    }
 
241
    if ( mAllMonths == false && mInterface->getSettings().customBilling == false )
 
242
    {
 
243
        rebuildStats( mMonthStatistics.first()->date, Month );
 
244
    }
 
245
}
 
246
 
 
247
void InterfaceStatistics::buildStatsGroup( QDomDocument& doc, enum GroupType group,
 
248
                                           const QList<StatisticEntry *>& statistics )
 
249
{
 
250
    QString groupName;
 
251
    QString elementName;
 
252
    switch ( group )
 
253
    {
 
254
        case Day:
 
255
            groupName = group_days;
 
256
            elementName = elem_day;
 
257
            break;
 
258
        case Week:
 
259
            groupName = group_weeks;
 
260
            elementName = elem_week;
 
261
            break;
 
262
        case Month:
 
263
            groupName = group_months;
 
264
            elementName = elem_month;
 
265
            break;
 
266
        case Year:
 
267
            groupName = group_years;
 
268
            elementName = elem_year;
 
269
    }
 
270
    QDomElement elements = doc.createElement( groupName );
 
271
    foreach( StatisticEntry *entry, statistics )
 
272
    {
 
273
        QDomElement element = doc.createElement( elementName );
 
274
        element.setAttribute( attrib_day, mCalendar->day( entry->date ) );
 
275
        element.setAttribute( attrib_month, mCalendar->month( entry->date ) );
 
276
        element.setAttribute( attrib_year, mCalendar->year( entry->date ) );
 
277
        if ( group > Day )
 
278
            element.setAttribute( attrib_span, entry->span );
 
279
        element.setAttribute( attrib_rx, entry->rxBytes );
 
280
        element.setAttribute( attrib_tx, entry->txBytes );
 
281
        elements.appendChild( element );
 
282
    }
 
283
    QDomElement statElement = doc.elementsByTagName( doc_name ).at( 0 ).toElement();
 
284
    statElement.appendChild( elements );
156
285
}
157
286
 
158
287
void InterfaceStatistics::saveStatistics()
159
288
{
160
 
    QDomDocument doc( "statistics" );
161
 
    QDomElement root = doc.createElement( "statistics" );
162
 
    doc.appendChild( root );
163
 
 
164
 
    QDomElement days = doc.createElement( "days" );
165
 
    foreach( StatisticEntry *entry, mDayStatistics )
166
 
    {
167
 
        QDomElement day = doc.createElement( "day" );
168
 
        day.setAttribute( "day", entry->day );
169
 
        day.setAttribute( "month", entry->month );
170
 
        day.setAttribute( "year", entry->year );
171
 
        day.setAttribute( "rxBytes", (double) entry->rxBytes );
172
 
        day.setAttribute( "txBytes", (double) entry->txBytes );
173
 
        days.appendChild( day );
174
 
    }
175
 
    root.appendChild( days );
176
 
 
177
 
    QDomElement months = doc.createElement( "months" );
178
 
    foreach ( StatisticEntry *entry, mMonthStatistics )
179
 
    {
180
 
        QDomElement month = doc.createElement( "month" );
181
 
        month.setAttribute( "month", entry->month );
182
 
        month.setAttribute( "year", entry->year );
183
 
        month.setAttribute( "rxBytes", (double) entry->rxBytes );
184
 
        month.setAttribute( "txBytes", (double) entry->txBytes );
185
 
        months.appendChild( month );
186
 
    }
187
 
    root.appendChild( months );
188
 
 
189
 
    QDomElement years = doc.createElement( "years" );
190
 
    foreach ( StatisticEntry *entry, mYearStatistics )
191
 
    {
192
 
        QDomElement year = doc.createElement( "year" );
193
 
        year.setAttribute( "year", entry->year );
194
 
        year.setAttribute( "rxBytes", (double) entry->rxBytes );
195
 
        year.setAttribute( "txBytes", (double) entry->txBytes );
196
 
        years.appendChild( year );
197
 
    }
198
 
    root.appendChild( years );
 
289
    QDomDocument doc( doc_name );
 
290
    QDomElement docElement = doc.createElement( doc_name );
 
291
    docElement.setAttribute( attrib_calendar, mCalendar->calendarType() );
 
292
    docElement.setAttribute( attrib_version, "1.1" );
 
293
    doc.appendChild( docElement );
 
294
 
 
295
    buildStatsGroup( doc, Day, mDayStatistics );
 
296
    buildStatsGroup( doc, Week, mWeekStatistics );
 
297
    buildStatsGroup( doc, Month, mMonthStatistics );
 
298
    buildStatsGroup( doc, Year, mYearStatistics );
199
299
 
200
300
    KUrl dir( mInterface->getGeneralData().statisticsDir );
201
301
    if ( !dir.isLocalFile() )
202
302
        return;
203
 
    QFile file( dir.path() + "/statistics_" + mInterface->getName() );
 
303
    QFile file( dir.path() + statistics_prefix + mInterface->getName() );
204
304
    if ( !file.open( QIODevice::WriteOnly ) )
205
305
        return;
206
306
 
212
312
void InterfaceStatistics::configChanged()
213
313
{
214
314
    // restart the timer with the new value
215
 
    mSaveTimer->setInterval( mInterface->getGeneralData().saveInterval * 1000 );
216
 
}
217
 
 
218
 
const StatisticEntry* InterfaceStatistics::getCurrentDay() const
219
 
{
220
 
    return mCurrentDay;
221
 
}
222
 
 
223
 
const StatisticEntry* InterfaceStatistics::getCurrentMonth() const
224
 
{
225
 
    return mCurrentMonth;
226
 
}
227
 
 
228
 
const StatisticEntry* InterfaceStatistics::getCurrentYear() const
229
 
{
230
 
    return mCurrentYear;
231
 
}
232
 
 
233
 
const QList<StatisticEntry *>& InterfaceStatistics::getDayStatistics() const
234
 
{
235
 
   return mDayStatistics;
236
 
}
237
 
 
238
 
const QList<StatisticEntry *>& InterfaceStatistics::getMonthStatistics() const
239
 
{
240
 
    return mMonthStatistics;
241
 
}
242
 
 
243
 
const QList<StatisticEntry *>& InterfaceStatistics::getYearStatistics() const
244
 
{
245
 
    return mYearStatistics;
 
315
    mSaveTimer->stop();
 
316
    if ( mInterface->getGeneralData().saveInterval > 0 )
 
317
        mSaveTimer->setInterval( mInterface->getGeneralData().saveInterval * 1000 );
 
318
 
 
319
    mWarningDone = false;
 
320
    // force a new ref day for billing periods
 
321
    mBillingStart = mInterface->getSettings().billingStart;
 
322
    if ( mAllMonths == false && mInterface->getSettings().customBilling == false )
 
323
        mBillingStart = mMonthStatistics.first()->date.addDays( 1 - mCalendar->day( mMonthStatistics.first()->date ) );
 
324
    rebuildStats( mBillingStart, Month );
 
325
 
 
326
    mAllMonths = true;
 
327
    foreach ( StatisticEntry *entry, mMonthStatistics )
 
328
    {
 
329
        if ( mCalendar->day( entry->date ) != 1 ||
 
330
             entry->span != mCalendar->daysInMonth( entry->date ) )
 
331
            mAllMonths = false;
 
332
    }
 
333
    emit monthStatisticsChanged( true );
246
334
}
247
335
 
248
336
void InterfaceStatistics::addIncomingData( unsigned long data )
249
337
{
 
338
    if ( data == 0 )
 
339
        return;
 
340
 
250
341
    checkCurrentEntry();
251
342
 
252
343
    mCurrentDay->rxBytes += data;
 
344
    mCurrentWeek->rxBytes += data;
253
345
    mCurrentMonth->rxBytes += data;
254
346
    mCurrentYear->rxBytes += data;
255
347
 
 
348
    // Only warn once per interface per session
 
349
    if ( !mWarningDone && mInterface->getSettings().warnThreshold > 0.0 )
 
350
    {
 
351
        quint64 thresholdBytes = mInterface->getSettings().warnThreshold * 1073741824;
 
352
        if ( mInterface->getSettings().warnTotalTraffic )
 
353
        {
 
354
            if ( mCurrentMonth->rxBytes + mCurrentMonth->txBytes >= thresholdBytes )
 
355
            {
 
356
                mWarningDone = true;
 
357
                emit warnMonthlyTraffic( mCurrentMonth->rxBytes + mCurrentMonth->txBytes );
 
358
            }
 
359
        }
 
360
        else if ( mCurrentMonth->rxBytes >= thresholdBytes )
 
361
        {
 
362
            mWarningDone = true;
 
363
            emit warnMonthlyTraffic( mCurrentMonth->rxBytes );
 
364
        }
 
365
    }
256
366
    emit currentEntryChanged();
257
367
}
258
368
 
259
369
void InterfaceStatistics::addOutgoingData( unsigned long data )
260
370
{
 
371
    if ( data == 0 )
 
372
        return;
 
373
 
261
374
    checkCurrentEntry();
262
375
 
263
376
    mCurrentDay->txBytes += data;
 
377
    mCurrentWeek->txBytes += data;
264
378
    mCurrentMonth->txBytes += data;
265
379
    mCurrentYear->txBytes += data;
266
380
 
267
381
    emit currentEntryChanged();
268
382
}
269
383
 
270
 
void InterfaceStatistics::clearDayStatistics()
 
384
void InterfaceStatistics::clearStatistics()
271
385
{
 
386
    QDate currentDate = QDate::currentDate();
 
387
 
272
388
    mDayStatistics.clear();
273
 
    updateCurrentDay();
274
 
}
275
 
 
276
 
void InterfaceStatistics::clearMonthStatistics()
277
 
{
 
389
    mWeekStatistics.clear();
278
390
    mMonthStatistics.clear();
279
 
    updateCurrentMonth();
280
 
}
281
 
 
282
 
void InterfaceStatistics::clearYearStatistics()
283
 
{
284
391
    mYearStatistics.clear();
285
 
    updateCurrentYear();
 
392
    updateCurrentDay( currentDate );
 
393
    updateCurrentWeek( currentDate );
 
394
    updateCurrentMonth( currentDate );
 
395
    updateCurrentYear( currentDate );
286
396
}
287
397
 
288
398
void InterfaceStatistics::checkCurrentEntry()
289
399
{
290
 
    if ( mCurrentDay->day != QDate::currentDate().day() ||
291
 
         mCurrentDay->month != QDate::currentDate().month() ||
292
 
         mCurrentDay->year != QDate::currentDate().year() )
 
400
    QDate currentDate = QDate::currentDate();
 
401
 
 
402
    if ( mCurrentDay->date != currentDate )
293
403
    {
294
 
        // current day has changed
295
 
        updateCurrentDay();
296
 
 
297
 
        if ( mCurrentMonth->month != QDate::currentDate().month() ||
298
 
             mCurrentMonth->year != QDate::currentDate().year() )
299
 
        {
300
 
            // current month has also changed
301
 
            updateCurrentMonth();
302
 
        }
303
 
 
304
 
        if ( mCurrentYear->year != QDate::currentDate().year() )
305
 
        {
306
 
            // current year has also changed
307
 
            updateCurrentYear();
308
 
        }
 
404
        updateCurrentDay( currentDate );
 
405
 
 
406
        if ( mCalendar->weekNumber( mCurrentWeek->date ) != mCalendar->weekNumber( currentDate ) ||
 
407
             mCurrentWeek->date.addDays( mCalendar->daysInWeek( mCurrentWeek->date ) ) <= currentDate )
 
408
            updateCurrentWeek( currentDate );
 
409
 
 
410
        if ( mCurrentMonth->date.addDays( mCurrentMonth->span ) <= currentDate )
 
411
            updateCurrentMonth( currentDate );
 
412
 
 
413
        if ( mCalendar->year( mCurrentYear->date ) != mCalendar->year( currentDate ) )
 
414
            updateCurrentYear( currentDate );
309
415
    }
310
416
}
311
417
 
312
418
void InterfaceStatistics::initStatistics()
313
419
{
314
 
    updateCurrentDay();
315
 
    updateCurrentMonth();
316
 
    updateCurrentYear();
 
420
    QDate currentDate = QDate::currentDate();
 
421
 
 
422
    updateCurrentDay( currentDate );
 
423
    updateCurrentWeek( currentDate );
 
424
    updateCurrentMonth( currentDate );
 
425
    updateCurrentYear( currentDate );
317
426
 
318
427
    emit currentEntryChanged();
319
428
}
320
429
 
321
 
void InterfaceStatistics::updateCurrentDay()
 
430
void InterfaceStatistics::updateCurrentDay( const QDate &currentDate )
322
431
{
323
432
    foreach ( mCurrentDay, mDayStatistics )
324
433
    {
325
 
        if ( mCurrentDay->day == QDate::currentDate().day() &&
326
 
             mCurrentDay->month == QDate::currentDate().month() &&
327
 
             mCurrentDay->year == QDate::currentDate().year() )
328
 
        {
329
 
            // found current day in list
 
434
        if ( mCurrentDay->date == currentDate )
330
435
            return;
331
 
        }
332
436
    }
333
437
 
334
 
    // the current day is not in the list
335
438
    mCurrentDay = new StatisticEntry();
336
 
    mCurrentDay->day = QDate::currentDate().day();
337
 
    mCurrentDay->month = QDate::currentDate().month();
338
 
    mCurrentDay->year = QDate::currentDate().year();
339
 
    mCurrentDay->rxBytes = 0;
340
 
    mCurrentDay->txBytes = 0;
341
 
    mDayStatistics.append( mCurrentDay ); // TODO: insert at correct position
 
439
    mCurrentDay->date = currentDate;
 
440
    mDayStatistics.append( mCurrentDay );
342
441
    emit dayStatisticsChanged();
343
442
}
344
443
 
345
 
void InterfaceStatistics::updateCurrentMonth()
346
 
{
347
 
    foreach ( mCurrentMonth, mMonthStatistics )
348
 
    {
349
 
        if ( mCurrentMonth->month == QDate::currentDate().month() &&
350
 
             mCurrentMonth->year == QDate::currentDate().year() )
351
 
        {
352
 
            // found current month in list
353
 
            return;
354
 
        }
355
 
    }
356
 
 
357
 
    // the current month is not in the list
358
 
    mCurrentMonth = new StatisticEntry();
359
 
    mCurrentMonth->day = 0;
360
 
    mCurrentMonth->month = QDate::currentDate().month();
361
 
    mCurrentMonth->year = QDate::currentDate().year();
362
 
    mCurrentMonth->rxBytes = 0;
363
 
    mCurrentMonth->txBytes = 0;
364
 
    mMonthStatistics.append( mCurrentMonth ); // TODO: insert at correct position
365
 
    emit monthStatisticsChanged();
366
 
}
367
 
 
368
 
void InterfaceStatistics::updateCurrentYear()
369
 
{
370
 
    foreach ( mCurrentYear, mYearStatistics )
371
 
    {
372
 
        if ( mCurrentYear->year == QDate::currentDate().year() )
373
 
        {
374
 
            // found current year in list
375
 
            return;
376
 
        }
377
 
    }
378
 
 
379
 
    // the current year is not in the list
380
 
    mCurrentYear = new StatisticEntry();
381
 
    mCurrentYear->day = 0;
382
 
    mCurrentYear->month = 0;
383
 
    mCurrentYear->year = QDate::currentDate().year();
384
 
    mCurrentYear->rxBytes = 0;
385
 
    mCurrentYear->txBytes = 0;
386
 
    mYearStatistics.append( mCurrentYear ); // TODO: insert at correct position
 
444
StatisticEntry * InterfaceStatistics::genNewWeek( const QDate &date )
 
445
{
 
446
    StatisticEntry *week = new StatisticEntry();
 
447
    int dow = mCalendar->dayOfWeek( date );
 
448
    // ISO8601: week always starts on a Monday
 
449
    week->date = date.addDays( 1 - dow );
 
450
    week->span = mCalendar->daysInWeek( week->date );
 
451
    return week;
 
452
}
 
453
 
 
454
QDate InterfaceStatistics::getNextMonthStart( const QDate &startDate )
 
455
{
 
456
    QDate nextMonthStart( startDate );
 
457
    int length = mInterface->getSettings().billingMonths;
 
458
    for ( int i = 0; i < length; i++ )
 
459
    {
 
460
        QDate refDay;
 
461
        mCalendar->setDate( refDay, mCalendar->year( nextMonthStart ), mCalendar->month( nextMonthStart ), 1 );
 
462
        refDay = refDay.addDays( mCalendar->daysInMonth( refDay ) );
 
463
 
 
464
        nextMonthStart = nextMonthStart.addDays( mCalendar->daysInMonth( nextMonthStart ) );
 
465
 
 
466
        // Ensure we don't get weird spans like jan31->mar2
 
467
        // Instead, days will drift to a value that all months can handle.
 
468
        // Test for problematic dates in config module!
 
469
        if ( mCalendar->day( mBillingStart ) > 1 )
 
470
        {
 
471
            while ( mCalendar->month( nextMonthStart ) != mCalendar->month( refDay ) )
 
472
                nextMonthStart = nextMonthStart.addDays( -1 );
 
473
        }
 
474
    }
 
475
    return nextMonthStart;
 
476
}
 
477
 
 
478
bool InterfaceStatistics::checkValidSpan( const StatisticEntry& entry )
 
479
{
 
480
    if ( !mDayStatistics.count() )
 
481
        return false;
 
482
 
 
483
    QDate endDate = entry.date.addDays( entry.span );
 
484
    for ( int i = mDayStatistics.size() - 1; i >= 0; --i )
 
485
    {
 
486
        // No others will be valid after this; stop early
 
487
        if ( mDayStatistics.at( i )->date < entry.date )
 
488
            return false;
 
489
        if ( mDayStatistics.at( i )->date < endDate &&
 
490
             mDayStatistics.at( i )->date >= entry.date )
 
491
            return true;
 
492
    }
 
493
    return false;
 
494
}
 
495
 
 
496
StatisticEntry * InterfaceStatistics::genNewMonth( const QDate &date, QDate endDate )
 
497
{
 
498
    StatisticEntry *month = new StatisticEntry();
 
499
 
 
500
    // Partial month.  Very little to do.
 
501
    if ( endDate.isValid() )
 
502
    {
 
503
        month->date = date;
 
504
        month->span = date.daysTo( endDate );
 
505
        if ( checkValidSpan( *month ) )
 
506
        {
 
507
            return month;
 
508
        }
 
509
        else
 
510
        {
 
511
            // partial month contains no daily stats, so advance start date
 
512
            // and get a new period below
 
513
            mBillingStart = date.addDays( month->span );
 
514
        }
 
515
    }
 
516
 
 
517
    // Given a calendar day and a billing period start date, find a
 
518
    // billing period that the day belongs in.
 
519
    QDate nextMonthStart = mBillingStart;
 
520
    do
 
521
    {
 
522
        month->date = nextMonthStart;
 
523
        nextMonthStart = getNextMonthStart( month->date );
 
524
        month->span = month->date.daysTo( nextMonthStart );
 
525
    } while ( nextMonthStart <= date || !checkValidSpan( *month ) );
 
526
 
 
527
    mBillingStart = month->date;
 
528
 
 
529
    return month;
 
530
}
 
531
 
 
532
StatisticEntry * InterfaceStatistics::genNewYear( const QDate &date )
 
533
{
 
534
    StatisticEntry *year = new StatisticEntry();
 
535
    int doy = mCalendar->dayOfYear( date );
 
536
    year->date = date.addDays( 1 - doy );
 
537
    year->span = mCalendar->daysInYear( year->date );
 
538
    return year;
 
539
}
 
540
 
 
541
void InterfaceStatistics::updateCurrentWeek( const QDate &currentDate )
 
542
{
 
543
    if ( mWeekStatistics.count() )
 
544
    {
 
545
        mCurrentWeek = mWeekStatistics.last();
 
546
        if ( mCurrentWeek->date.addDays( mCurrentWeek->span ) > currentDate )
 
547
            return;
 
548
    }
 
549
 
 
550
    mCurrentWeek = genNewWeek( currentDate );
 
551
 
 
552
    mWeekStatistics.append( mCurrentWeek );
 
553
    emit weekStatisticsChanged();
 
554
}
 
555
 
 
556
void InterfaceStatistics::updateCurrentMonth( const QDate &currentDate )
 
557
{
 
558
    if ( mMonthStatistics.count() )
 
559
    {
 
560
        mCurrentMonth = mMonthStatistics.last();
 
561
        if ( mCurrentMonth->date.addDays( mCurrentMonth->span ) > currentDate )
 
562
            return;
 
563
    }
 
564
 
 
565
    mCurrentMonth = genNewMonth( currentDate );
 
566
    mMonthStatistics.append( mCurrentMonth );
 
567
 
 
568
    emit monthStatisticsChanged( false );
 
569
}
 
570
 
 
571
void InterfaceStatistics::saveBillingStart()
 
572
{
 
573
    mInterface->getSettings().billingStart = mBillingStart;
 
574
    KConfig *config = KGlobal::config().data();
 
575
    KConfigGroup interfaceGroup( config, confg_interface + mInterface->getName() );
 
576
    interfaceGroup.writeEntry( conf_billingStart, mBillingStart );
 
577
    config->sync();
 
578
}
 
579
 
 
580
void InterfaceStatistics::updateCurrentYear( const QDate &currentDate )
 
581
{
 
582
    if ( mYearStatistics.count() )
 
583
    {
 
584
        mCurrentYear = mYearStatistics.last();
 
585
        if ( mCurrentYear->date.addDays( mCurrentYear->span ) > currentDate )
 
586
            return;
 
587
    }
 
588
 
 
589
    mCurrentYear = genNewYear( currentDate );
 
590
    mYearStatistics.append( mCurrentYear );
387
591
    emit yearStatisticsChanged();
388
592
}
389
593
 
 
594
QDate InterfaceStatistics::setRebuildDate( QList<StatisticEntry *>& statistics,
 
595
                                           const QDate &recalcDate,
 
596
                                           enum GroupType group )
 
597
{
 
598
    QDate returnDate = recalcDate;
 
599
 
 
600
    // Keep removing entries and rolling back returnDate while
 
601
    // entry's start date + span > returnDate
 
602
    for ( int i = statistics.size() - 1; i >= 0; --i )
 
603
    {
 
604
        if ( statistics.at( i )->date.addDays( statistics.at( i )->span ) > mDayStatistics.first()->date &&
 
605
             ( statistics.at( i )->date.addDays( statistics.at( i )->span ) > returnDate ||
 
606
               statistics.at( i )->span < 1 )
 
607
           )
 
608
        {
 
609
            if ( returnDate > statistics.at( i )->date )
 
610
                returnDate = statistics.at( i )->date;
 
611
            statistics.removeAt( i );
 
612
        }
 
613
        else
 
614
            break;
 
615
    }
 
616
 
 
617
    // now take care of instances when we're going earlier than the first recorded stats.
 
618
 
 
619
    if ( group == Week )
 
620
    {
 
621
        returnDate = returnDate.addDays( 1 - mCalendar->dayOfWeek( returnDate ) );
 
622
        while ( returnDate > recalcDate )
 
623
            returnDate = returnDate.addDays( -mCalendar->daysInWeek( returnDate ) );
 
624
    }
 
625
    else if ( group == Year )
 
626
    {
 
627
        returnDate = returnDate.addDays( 1 - mCalendar->dayOfYear( returnDate ) );
 
628
        while ( returnDate > recalcDate )
 
629
            returnDate = returnDate.addDays( mCalendar->daysInYear( returnDate ) );
 
630
    }
 
631
 
 
632
    return returnDate;
 
633
}
 
634
 
 
635
void InterfaceStatistics::rebuildStats( const QDate &date, int groups )
 
636
{
 
637
    QDate recalcDate( date );
 
638
    bool partial = false;
 
639
    StatisticEntry* weekEntry = 0;
 
640
    StatisticEntry* monthEntry = 0;
 
641
    StatisticEntry* yearEntry = 0;
 
642
    QDate weekStart;
 
643
    QDate monthStart;
 
644
    QDate yearStart;
 
645
    QDate walkbackDate;
 
646
 
 
647
    QList<QDate> s;
 
648
    s.append( recalcDate );
 
649
 
 
650
    if ( groups & Week )
 
651
    {
 
652
        weekStart = setRebuildDate( mWeekStatistics, recalcDate, Week );
 
653
        s.append( weekStart );
 
654
    }
 
655
    if ( groups & Month )
 
656
    {
 
657
        monthStart = setRebuildDate( mMonthStatistics, recalcDate, Month );
 
658
        // force an old date
 
659
        mBillingStart = monthStart;
 
660
        if ( recalcDate > monthStart )
 
661
            partial = true;
 
662
        s.append( monthStart );
 
663
    }
 
664
    if ( groups & Year )
 
665
    {
 
666
        yearStart = setRebuildDate( mYearStatistics, recalcDate, Year );
 
667
        s.append( yearStart );
 
668
    }
 
669
 
 
670
    // Now find how far back we'll need to go
 
671
    qSort( s );
 
672
    walkbackDate = s.first();
 
673
 
 
674
    // Big loop, but this way we iterate through mDayStatistics once
 
675
    // no matter how many categories we're rebuilding
 
676
    foreach( StatisticEntry* day, mDayStatistics )
 
677
    {
 
678
        if ( day->date < walkbackDate )
 
679
            continue;
 
680
 
 
681
        if ( groups & Week && day->date >= weekStart )
 
682
        {
 
683
            if ( !weekEntry || mCalendar->weekNumber( weekEntry->date ) != mCalendar->weekNumber( day->date ) ||
 
684
                 weekEntry->date.addDays( mCalendar->daysInWeek( weekEntry->date ) ) <= day->date )
 
685
            {
 
686
                if ( weekEntry )
 
687
                    mWeekStatistics.append( weekEntry );
 
688
                weekEntry = genNewWeek( day->date );
 
689
            }
 
690
            weekEntry->rxBytes += day->rxBytes;
 
691
            weekEntry->txBytes += day->txBytes;
 
692
        }
 
693
 
 
694
        if ( groups & Month && day->date >= monthStart )
 
695
        {
 
696
            if ( !monthEntry || day->date >= monthEntry->date.addDays( monthEntry->span ) )
 
697
            {
 
698
                if ( monthEntry )
 
699
                    mMonthStatistics.append( monthEntry );
 
700
                if ( partial )
 
701
                {
 
702
                    monthEntry = genNewMonth( monthStart, recalcDate );
 
703
                    // Partial month created; next period will begin on recalcDate
 
704
                    mBillingStart = recalcDate;
 
705
                    partial = false;
 
706
                }
 
707
                else
 
708
                    monthEntry = genNewMonth( day->date );
 
709
            }
 
710
            monthEntry->rxBytes += day->rxBytes;
 
711
            monthEntry->txBytes += day->txBytes;
 
712
        }
 
713
        if ( groups & Year && day->date >= yearStart )
 
714
        {
 
715
            if ( !yearEntry || mCalendar->year( yearEntry->date ) != mCalendar->year( day->date ) )
 
716
            {
 
717
                if ( yearEntry )
 
718
                    mYearStatistics.append( yearEntry );
 
719
                yearEntry = genNewYear( day->date );
 
720
            }
 
721
            yearEntry->rxBytes += day->rxBytes;
 
722
            yearEntry->txBytes += day->txBytes;
 
723
        }
 
724
    }
 
725
 
 
726
    mCurrentWeek = appendStats( mWeekStatistics, weekEntry );
 
727
    mCurrentMonth = appendStats( mMonthStatistics, monthEntry );
 
728
    mCurrentYear = appendStats( mYearStatistics, yearEntry );
 
729
}
 
730
 
 
731
StatisticEntry * InterfaceStatistics::appendStats( QList<StatisticEntry *>& statistics, StatisticEntry *entry )
 
732
{
 
733
    if ( entry )
 
734
        statistics.append( entry );
 
735
    return statistics.last();
 
736
}