~ci-train-bot/history-service/history-service-ubuntu-zesty-2629

« back to all changes in this revision

Viewing changes to plugins/sqlite/sqlitehistoryplugin.cpp

  • Committer: Bileto Bot
  • Author(s): Gustavo Pichorim Boiko
  • Date: 2017-03-23 01:28:52 UTC
  • mfrom: (230.2.35 staging)
  • Revision ID: ci-train-bot@canonical.com-20170323012852-vzmjcare13zofbna
- Adapt to support VOIP accounts.
- Improve the notifications of participants changing
- Only start saving information events about contacts joining and leaving after the self contact is in the local list of participants.
- Improve Roles management performance by caching the retrieved data.
- Mark entire conversations as read.
- Allow pass multiple fields on sort clause.
- Reduce the dbus traffic when marking messages and threads as read.
- Use a QLockFile to ensure there will be only one instance of the daemon per user. As we now delay the registration on dbus, sometimes we ended up having two instances of the daeon running (because of dbus activation). This change makes sure that won't happen.
- Do not load the participants from threads automatically. If the client really needs it, it can use the newly added API to fetch the participants.
- Make it possible to debug sqlite commands.

Approved by: system-apps-ci-bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
306
306
    return new SQLiteHistoryEventView(this, type, sort, filter);
307
307
}
308
308
 
 
309
QVariantMap SQLiteHistoryPlugin::markThreadAsRead(const QVariantMap &thread)
 
310
{
 
311
    QSqlQuery query(SQLiteDatabase::instance()->database());
 
312
 
 
313
    if (thread[History::FieldAccountId].toString().isEmpty() ||
 
314
           thread[History::FieldThreadId].toString().isEmpty()) {
 
315
        return QVariantMap();
 
316
    }
 
317
 
 
318
    // first check if the thread actually has anything to change
 
319
    query.prepare("SELECT unreadCount from threads WHERE accountId=:accountId AND threadId=:threadId AND type=:type");
 
320
    query.bindValue(":accountId", thread[History::FieldAccountId].toString());
 
321
    query.bindValue(":threadId", thread[History::FieldThreadId].toString());
 
322
    query.bindValue(":type", (uint)History::EventTypeText);
 
323
    if (!query.exec() || !query.next()) {
 
324
        qCritical() << "Failed to verify the unread messages of the thread. Error:" << query.lastError();
 
325
        return QVariantMap();
 
326
    }
 
327
 
 
328
 
 
329
    int unreadCount = query.value(0).toUInt();
 
330
    if (unreadCount == 0) {
 
331
        // no messages to ack, so no need to update anything
 
332
        return QVariantMap();
 
333
    }
 
334
 
 
335
    query.prepare("UPDATE text_events SET newEvent=:newEvent WHERE accountId=:accountId AND threadId=:threadId AND newEvent=1");
 
336
    query.bindValue(":accountId", thread[History::FieldAccountId].toString());
 
337
    query.bindValue(":threadId", thread[History::FieldThreadId].toString());
 
338
    query.bindValue(":newEvent", false);
 
339
 
 
340
    if (!query.exec()) {
 
341
        qCritical() << "Failed to mark thread as read: Error:" << query.lastError();
 
342
        return QVariantMap();
 
343
    }
 
344
 
 
345
    QVariantMap existingThread = getSingleThread((History::EventType) thread[History::FieldType].toInt(),
 
346
                                                 thread[History::FieldAccountId].toString(),
 
347
                                                 thread[History::FieldThreadId].toString(),
 
348
                                                 QVariantMap());
 
349
    if (!existingThread.isEmpty()) {
 
350
        addThreadsToCache(QList<QVariantMap>() << existingThread);
 
351
        return existingThread;
 
352
    }
 
353
 
 
354
    return QVariantMap();
 
355
}
 
356
 
309
357
QVariantMap SQLiteHistoryPlugin::threadForProperties(const QString &accountId,
310
358
                                                       History::EventType type,
311
359
                                                       const QVariantMap &properties,
315
363
        return QVariantMap();
316
364
    }
317
365
 
318
 
    QSqlQuery query(SQLiteDatabase::instance()->database());
319
 
 
320
366
    History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt();
321
367
 
322
368
    if (chatType == History::ChatTypeRoom) {
332
378
    return threadForParticipants(accountId, type, participants.identifiers(), matchFlags);
333
379
}
334
380
 
 
381
QString SQLiteHistoryPlugin::threadIdForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties, History::MatchFlags matchFlags)
 
382
{
 
383
    if (properties.isEmpty()) {
 
384
        return QString::null;
 
385
    }
 
386
 
 
387
    // if chat type is room, just get the threadId directly
 
388
    History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt();
 
389
    if (chatType == History::ChatTypeRoom) {
 
390
          QString threadId = properties[History::FieldThreadId].toString();
 
391
          return threadId;
 
392
    }
 
393
 
 
394
    // if chat type is anything else, fallback to returning the threadId from the participants list
 
395
    History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]);
 
396
    return threadForParticipants(accountId, type, participants.identifiers(), matchFlags)[History::FieldThreadId].toString();
 
397
}
 
398
 
 
399
QList<QVariantMap> SQLiteHistoryPlugin::participantsForThreads(const QList<QVariantMap> &threadIds)
 
400
{
 
401
    QList<QVariantMap> results;
 
402
    Q_FOREACH(const QVariantMap &thread, threadIds) {
 
403
        QString accountId = thread[History::FieldAccountId].toString();
 
404
        QString threadId = thread[History::FieldThreadId].toString();
 
405
        History::EventType type = (History::EventType)thread[History::FieldType].toUInt();
 
406
        QVariantMap result = thread;
 
407
 
 
408
        QSqlQuery query;
 
409
        query.prepare("SELECT normalizedId, alias, state, roles FROM thread_participants "
 
410
                      "WHERE accountId=:accountId AND threadId=:threadId AND type=:type");
 
411
        query.bindValue(":accountId", accountId);
 
412
        query.bindValue(":threadId", threadId);
 
413
        query.bindValue(":type", type);
 
414
        QVariantList participants;
 
415
        if (!query.exec()) {
 
416
            qWarning() << "Failed to retrieve participants. Error:" << query.lastError().text() << query.lastQuery();
 
417
            results << result;
 
418
            continue;
 
419
        }
 
420
 
 
421
        while (query.next()) {
 
422
            QVariantMap participant;
 
423
            QString identifier = query.value(0).toString();
 
424
            participant[History::FieldIdentifier] = identifier;
 
425
            participant[History::FieldAlias] = query.value(1);
 
426
            participant[History::FieldParticipantState] = query.value(2);
 
427
            participant[History::FieldParticipantRoles] = query.value(3);
 
428
            participants << History::ContactMatcher::instance()->contactInfo(accountId, identifier, true, participant);
 
429
        }
 
430
 
 
431
        result[History::FieldParticipants] = participants;
 
432
        results << result;
 
433
    }
 
434
    return results;
 
435
}
 
436
 
335
437
QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId,
336
438
                                                       History::EventType type,
337
439
                                                       const QStringList &participants,
1093
1195
           << "threads.unreadCount"
1094
1196
           << "threads.lastEventTimestamp";
1095
1197
 
1096
 
    // get the participants in the query already
1097
 
    fields << "(SELECT group_concat(thread_participants.participantId,  \"|,|\") "
1098
 
              "FROM thread_participants WHERE thread_participants.accountId=threads.accountId "
1099
 
              "AND thread_participants.threadId=threads.threadId "
1100
 
              "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants";
1101
 
 
1102
 
    fields << "(SELECT group_concat(thread_participants.state,  \"|,|\") "
1103
 
              "FROM thread_participants WHERE thread_participants.accountId=threads.accountId "
1104
 
              "AND thread_participants.threadId=threads.threadId "
1105
 
              "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as state";
1106
 
 
1107
 
    fields << "(SELECT group_concat(thread_participants.roles,  \"|,|\") "
1108
 
              "FROM thread_participants WHERE thread_participants.accountId=threads.accountId "
1109
 
              "AND thread_participants.threadId=threads.threadId "
1110
 
              "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as roles";
1111
 
 
1112
1198
    QStringList extraFields;
1113
1199
    QString table;
1114
1200
 
1136
1222
QList<QVariantMap> SQLiteHistoryPlugin::parseThreadResults(History::EventType type, QSqlQuery &query, const QVariantMap &properties)
1137
1223
{
1138
1224
    QList<QVariantMap> threads;
 
1225
    QList<QVariantMap> threadsWithoutParticipants;
1139
1226
    QSqlQuery attachmentsQuery(SQLiteDatabase::instance()->database());
1140
1227
    QList<QVariantMap> attachments;
1141
1228
    bool grouped = false;
1170
1257
        thread[History::FieldEventId] = query.value(2);
1171
1258
        thread[History::FieldCount] = query.value(3);
1172
1259
        thread[History::FieldUnreadCount] = query.value(4);
1173
 
        QStringList participants = query.value(6).toString().split("|,|", QString::SkipEmptyParts);
1174
 
        QList<int> participantStatus;
1175
 
        QStringList participantStatusString = query.value(7).toString().split("|,|", QString::SkipEmptyParts);
1176
 
        Q_FOREACH(const QString &statusString, participantStatusString) {
1177
 
            participantStatus << statusString.toUInt();
1178
 
        }
1179
 
        QStringList participantRolesString = query.value(8).toString().split("|,|", QString::SkipEmptyParts);
1180
 
        QList<int> participantRoles;
1181
 
        Q_FOREACH(const QString &rolesString, participantRolesString) {
1182
 
            participantRoles << rolesString.toUInt();
1183
 
        }
1184
 
        QVariantList contactList;
1185
 
        QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
1186
 
        for (int i = 0; i < contactInfo.count(); ++i) {
1187
 
            QVariantMap map = contactInfo[i].toMap();
1188
 
            map["state"] = participantStatus[i];
1189
 
            map["roles"] = participantRoles[i];
1190
 
            contactList << map;
1191
 
        }
1192
 
        thread[History::FieldParticipants] = contactList;
1193
1260
 
1194
1261
        // the generic event fields
1195
 
        thread[History::FieldSenderId] = query.value(9);
 
1262
        thread[History::FieldSenderId] = query.value(6);
1196
1263
        thread[History::FieldTimestamp] = toLocalTimeString(query.value(5).toDateTime());
1197
 
        thread[History::FieldNewEvent] = query.value(10).toBool();
 
1264
        thread[History::FieldNewEvent] = query.value(7).toBool();
1198
1265
 
1199
1266
        // the next step is to get the last event
1200
1267
        switch (type) {
1225
1292
                thread[History::FieldAttachments] = QVariant::fromValue(attachments);
1226
1293
                attachments.clear();
1227
1294
            }
1228
 
            thread[History::FieldMessage] = query.value(11);
1229
 
            thread[History::FieldMessageType] = query.value(12);
1230
 
            thread[History::FieldMessageStatus] = query.value(13);
1231
 
            thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(14).toDateTime());
1232
 
            thread[History::FieldChatType] = query.value(15).toUInt();
 
1295
            thread[History::FieldMessage] = query.value(8);
 
1296
            thread[History::FieldMessageType] = query.value(9);
 
1297
            thread[History::FieldMessageStatus] = query.value(10);
 
1298
            thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(11).toDateTime());
 
1299
            thread[History::FieldChatType] = query.value(12).toUInt();
1233
1300
 
1234
 
            if (thread[History::FieldChatType].toInt() == 2) {
 
1301
            if (thread[History::FieldChatType].toInt() == History::ChatTypeRoom) {
1235
1302
                QVariantMap chatRoomInfo;
1236
1303
                QSqlQuery query1(SQLiteDatabase::instance()->database());
1237
1304
 
1291
1358
 
1292
1359
                thread[History::FieldChatRoomInfo] = chatRoomInfo;
1293
1360
            }
 
1361
            if (!History::Utils::shouldIncludeParticipants(History::Thread::fromProperties(thread))) {
 
1362
                thread.remove(History::FieldParticipants);
 
1363
                threadsWithoutParticipants << thread;
 
1364
            } else {
 
1365
                threads << thread;
 
1366
            }
1294
1367
            break;
1295
1368
        case History::EventTypeVoice:
1296
 
            thread[History::FieldMissed] = query.value(12);
1297
 
            thread[History::FieldDuration] = query.value(11);
1298
 
            thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(13).toString(), true);
 
1369
            thread[History::FieldMissed] = query.value(9);
 
1370
            thread[History::FieldDuration] = query.value(8);
 
1371
            thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(10).toString(), true);
 
1372
            threads << thread;
1299
1373
            break;
1300
1374
        }
1301
 
        threads << thread;
1302
1375
    }
 
1376
 
 
1377
    // get the participants
 
1378
    threads = participantsForThreads(threads);
 
1379
 
 
1380
    // and append the threads with no participants
 
1381
    threads << threadsWithoutParticipants;
 
1382
 
1303
1383
    return threads;
1304
1384
}
1305
1385
 
1317
1397
    QString queryText;
1318
1398
    switch (type) {
1319
1399
    case History::EventTypeText:
1320
 
        participantsField = participantsField.arg("text_events", QString::number(type));
 
1400
        // for text events we don't need the participants at all
 
1401
        participantsField = "\"\" as participants";
1321
1402
        queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, "
1322
1403
                            "message, messageType, messageStatus, readTimestamp, subject, informationType FROM text_events %2 %3").arg(participantsField, modifiedCondition, order);
1323
1404
        break;
1353
1434
        event[History::FieldSenderId] = query.value(3);
1354
1435
        event[History::FieldTimestamp] = toLocalTimeString(query.value(4).toDateTime());
1355
1436
        event[History::FieldNewEvent] = query.value(5).toBool();
1356
 
        QStringList participants = query.value(6).toString().split("|,|");
1357
 
        event[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
 
1437
        if (type != History::EventTypeText) {
 
1438
            QStringList participants = query.value(6).toString().split("|,|");
 
1439
            event[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
 
1440
        }
1358
1441
 
1359
1442
        switch (type) {
1360
1443
        case History::EventTypeText: