Added parsing of CSV-formatted Symbian event logs
[qwerkisync] / DBBackends / RtcomEventLogger.cpp
index 2e1ee83..7957baf 100644 (file)
 #include "RtcomEventLogger.h"
 
 #include "EventProcessors/iEventProcessor.h"
+#include "EventTypes/eEventTypes.h"
 #include "EventTypes/iEvent.h"
+#include "EventTypes/PhoneCall.h"
 #include "EventTypes/SMS.h"
 #include "Settings.h"
 
 #include <QDebug>
+#include <QMutex>
+#include <QWaitCondition>
+
+// For reindexing
+#include <QDir>
+#include <QPair>
+#include <QStringList>
+#include <QtSql/QSqlDatabase>
+#include <QtSql/QSqlQuery>
+#include <QVariant>
 
 #include <uuid/uuid.h>
 
 #include <rtcom-eventlogger/event.h>
 #include <rtcom-eventlogger/eventlogger.h>
 
+#include <stdexcept>
+
 using namespace DBBackends;
+using namespace EventTypes;
 
 QDebug operator<<(QDebug, RTComElEvent &);
 QDebug operator<<(QDebug, RTComElAttachment &);
@@ -38,21 +53,21 @@ QDebug operator<<(QDebug, GList &);
 QDebug operator<<(QDebug, QList<RTComElAttachment*> &);
 
 RtcomEventLogger::RtcomEventLogger(const Settings &settings) :
-       m_Settings(settings)
+       m_Settings(settings), mk_DBPath("/.rtcom-eventlogger/el-v1.db")
 {
        RTComEl *el(rtcom_el_new());
        if(NULL != el)
        {
                // Grab the service IDs we want to work with
-               m_ServiceIDs.insert(SERVICE_ID_CALL, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_CALL"));
-               m_ServiceIDs.insert(SERVICE_ID_CHAT, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_CHAT"));
-               m_ServiceIDs.insert(SERVICE_ID_SMS, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_SMS"));
-               m_ServiceIDs.insert(SERVICE_ID_MMS, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_MMS"));
+               m_ServiceIDs.insert(EVENT_TYPE_CALL, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_CALL"));
+               //m_ServiceIDs.insert(EVENT_TYPE_CHAT, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_CHAT"));
+               m_ServiceIDs.insert(EVENT_TYPE_SMS, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_SMS"));
+               //m_ServiceIDs.insert(EVENT_TYPE_MMS, rtcom_el_get_service_id(el, "RTCOM_EL_SERVICE_MMS"));
 
                // Remove any service IDs that weren't found
-               foreach(ServiceID serviceID, m_ServiceIDs.keys())
-                       if(m_ServiceIDs.value(serviceID) == -1)
-                               m_ServiceIDs.remove(serviceID);
+               foreach(EventTypes::eEventTypes service, m_ServiceIDs.keys())
+                       if(m_ServiceIDs.value(service) == -1)
+                               m_ServiceIDs.remove(service);
 
                g_object_unref(el);
        }
@@ -61,7 +76,7 @@ RtcomEventLogger::RtcomEventLogger(const Settings &settings) :
 }
 
 RtcomEventLogger::RtcomEventLogger(const Settings &settings, const EventTypes::RtcomEvent &event) :
-       m_Settings(settings)
+       m_Settings(settings), mk_DBPath("/.rtcom-eventlogger/el-v1.db")
 {
 }
 
@@ -71,20 +86,35 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
        RTComEl *el = rtcom_el_new();
        if(NULL != el)
        {
+               foreach(eEventTypes service, m_ServiceIDs.keys())
+                       ProcessService(processor, service, *el);
+
+               g_object_unref(el);
+       }
+       else
+               qDebug() << "Failed to create event logger.";
+}
+
+void RtcomEventLogger::ProcessService(EventProcessors::iEventProcessor &processor, const EventTypes::eEventTypes service, const RTComEl &el)
+{
+       RTComEl *el_nonconst(const_cast<RTComEl *>(&el));
+
+       bool incoming = CurrentSettings().ShouldProcess( Settings::TYPE_RECIEVED, service);
+       bool outgoing = CurrentSettings().ShouldProcess( Settings::TYPE_SENT, service);
+
+       if(incoming || outgoing)
+       {
                // Initialise a query
-               RTComElQuery *query = rtcom_el_query_new(el);
+               RTComElQuery *query = rtcom_el_query_new(el_nonconst);
                if(query != NULL)
                {
-                       bool incoming = CurrentSettings().ShouldProcess( Settings::TYPE_RECIEVED, Settings::EVENTTYPE_SMS);
-                       bool outgoing = CurrentSettings().ShouldProcess( Settings::TYPE_SENT, Settings::EVENTTYPE_SMS);
-
                        // Prepare it...
                        bool prepared = false;
                        if(incoming && outgoing)
                        {
                                prepared = rtcom_el_query_prepare(query,
                                        "service-id",
-                                       m_ServiceIDs.value(SERVICE_ID_SMS),
+                                       m_ServiceIDs.value(service),
                                        RTCOM_EL_OP_EQUAL,
 
                                        NULL);
@@ -93,7 +123,7 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
                        {
                                prepared = rtcom_el_query_prepare(query,
                                        "service-id",
-                                       m_ServiceIDs.value(SERVICE_ID_SMS),
+                                       m_ServiceIDs.value(service),
                                        RTCOM_EL_OP_EQUAL,
 
                                        "outgoing",
@@ -107,7 +137,7 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
 
                        if(prepared)
                        {
-                               RTComElIter *it = rtcom_el_get_events(el, query);
+                               RTComElIter *it = rtcom_el_get_events(el_nonconst, query);
                                if(it != NULL)
                                {
                                        if(rtcom_el_iter_first(it))
@@ -120,7 +150,7 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
                                                // Reset the iterator and grab the actual values
                                                qDebug() << "Resetting iterator...";
                                                g_object_unref(it);
-                                               it = rtcom_el_get_events(el, query);
+                                               it = rtcom_el_get_events(el_nonconst, query);
                                                if(it != NULL)
                                                {
                                                        if(rtcom_el_iter_first(it))
@@ -135,20 +165,7 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
                                                                        RTComElEvent revent;
                                                                        memset(&revent, 0, sizeof(revent));
 
-                                                                       if(rtcom_el_iter_get_values (
-                                                                               it,
-                                                                               "id", &revent.fld_id,
-                                                                               "service-id", &revent.fld_service_id,
-                                                                               "start-time", &revent.fld_start_time,
-                                                                               "end-time", &revent.fld_end_time,
-                                                                               "local-uid", &revent.fld_local_uid,
-                                                                               "local-name", &revent.fld_local_name,
-                                                                               "remote-uid", &revent.fld_remote_uid,
-                                                                               "remote-name", &revent.fld_remote_name,
-                                                                               "is-read", &revent.fld_is_read,
-                                                                               "outgoing", &revent.fld_outgoing,
-                                                                               "free-text", &revent.fld_free_text,
-                                                                               NULL))
+                                                                       if(rtcom_el_iter_get_full(it, &revent))
                                                                        {
                                                                                qDebug() << revent;
 
@@ -200,25 +217,23 @@ void RtcomEventLogger::Process(EventProcessors::iEventProcessor &processor)
                }
                else
                        qDebug() << "Failed to create query.";
-
-               g_object_unref(el);
        }
        else
-               qDebug() << "Failed to create event logger.";
+               qDebug() << "Nothing to do for " << m_ServiceIDs.value(service);
 }
 
 EventTypes::iEvent *const RtcomEventLogger::CreateEvent(RTComElEvent &revent, QList<RTComElAttachment*> &rattachments)
 {
-       //if(m_ServiceIDs.contains(SERVICE_ID_CALL) && revent.fld_service_id == m_ServiceIDs.value(SERVICE_ID_CALL))
-       //      return new EventTypes::Call(CurrentSettings(), revent, rattachments);
+       if(m_ServiceIDs.contains(EVENT_TYPE_CALL) && revent.fld_service_id == m_ServiceIDs.value(EVENT_TYPE_CALL))
+               return new EventTypes::PhoneCall(CurrentSettings(), revent, rattachments);
 
-       //if(m_ServiceIDs.contains(SERVICE_ID_CHAT) && revent.fld_service_id == m_ServiceIDs.value(SERVICE_ID_CHAT))
+       //if(m_ServiceIDs.contains(EVENT_TYPE_CHAT) && revent.fld_service_id == m_ServiceIDs.value(EVENT_TYPE_CHAT))
        //      return new EventTypes::Chat(CurrentSettings(), revent, rattachments);
 
-       if(m_ServiceIDs.contains(SERVICE_ID_SMS) && revent.fld_service_id == m_ServiceIDs.value(SERVICE_ID_SMS))
+       if(m_ServiceIDs.contains(EVENT_TYPE_SMS) && revent.fld_service_id == m_ServiceIDs.value(EVENT_TYPE_SMS))
                return new EventTypes::SMS(CurrentSettings(), revent, rattachments);
 
-       //if(m_ServiceIDs.contains(SERVICE_ID_MMS) && revent.fld_service_id == m_ServiceIDs.value(SERVICE_ID_MMS))
+       //if(m_ServiceIDs.contains(EVENT_TYPE_MMS) && revent.fld_service_id == m_ServiceIDs.value(EVENT_TYPE_MMS))
        //      return new EventTypes::MMS(CurrentSettings(), revent, rattachments);
 
        return NULL;
@@ -230,7 +245,6 @@ void RtcomEventLogger::Insert(EventTypes::iEvent &event, const NumberToNameLooku
        {
                const uint UUID_STR_LEN(36);
 
-               int newEventID(0);
                _RTComEl *el(rtcom_el_new());
                if(NULL != el)
                {
@@ -254,13 +268,36 @@ void RtcomEventLogger::Insert(EventTypes::iEvent &event, const NumberToNameLooku
                        qDebug() << *rattachments;
 
                        // Add the event
-                       newEventID = rtcom_el_add_event_full(el, revent, rheaders, rattachments, &error);
-                       qDebug() << "new id: " << newEventID;
-                       if(error != NULL)
+                       QDateTime startTime(QDateTime::currentDateTimeUtc());
+                       int newEventID(-1);
+                       while(((newEventID = rtcom_el_add_event_full(el, revent, rheaders, rattachments, &error)) == -1)
+                                 && startTime.msecsTo(QDateTime::currentDateTimeUtc()) < 1000)
+                       {
+                               if(error != NULL)
+                               {
+                                       qDebug() << "err: " << error->message;
+                                       qDebug() << *revent << "\n";
+                                       g_error_free(error);
+                                       error = NULL;
+                               }
+
+                               // Don't hammer the DB when there's an error. Give it literally just a moment before retrying.
+                               QMutex mutex;
+                               mutex.lock();
+
+                               QWaitCondition waitCondition;
+                               waitCondition.wait(&mutex, 5);
+
+                               mutex.unlock();
+                       }
+
+                       if(-1 == newEventID)
                        {
-                               qDebug() << "err: " << error->message;
-                               g_error_free(error);
+                               qDebug() << "new id: " << newEventID;
+                               InsertedIDs().append(newEventID);
                        }
+                       else
+                               qDebug() << "Unable to insert event due to error.";
 
                        // Release the attachments
                        g_list_foreach (rattachments, (GFunc) rtcom_el_free_attachment, NULL);
@@ -278,17 +315,322 @@ void RtcomEventLogger::Insert(EventTypes::iEvent &event, const NumberToNameLooku
        return;
 }
 
+void RtcomEventLogger::PostInsert()
+{
+       // Reorder the DB IDs as Nokia are guilty of both premature
+       // optimisation as well as closed source UIs...
+       Reindex();
+}
+
+void RtcomEventLogger::ClearInsertedIDs()
+{
+       InsertedIDs().clear();
+}
+
+// Reorder the DB IDs as Nokia are guilty of both premature
+// optimisation as well as closed source UIs...
+void RtcomEventLogger::Reindex()
+{
+       // Set up the database connection...
+       QSqlDatabase db(QSqlDatabase::addDatabase( "QSQLITE" ));
+
+       db.setDatabaseName( QDir::homePath() + mk_DBPath );
+       if ( ! db.open() )
+       {
+               throw std::runtime_error("Cannot open database: Unable to establish database connection");
+       }
+       else
+       {
+               // Reorder the evnts by their start time
+               uint changesRequired(0);
+               do
+               {
+                       // Note the smallest event ID found, so we have a place to start.
+                       int min(0);
+
+                       // The required ID changes ( current, correct );
+                       QHash<int, int> mapping;
+
+                       // Grab the current records, and determine what changes need to
+                       // happen to get to the sorted results
+                       {
+                               qDebug() << "DB Opened";
+
+                               QSqlQuery * dbq1(new QSqlQuery( db )), * dbq2(new QSqlQuery( db ));
+
+                               dbq1->setForwardOnly( true );
+                               dbq2->setForwardOnly( true );
+
+                               QString s1("SELECT id, event_type_id, start_time, end_time "
+                                                  " FROM Events");
+                               QString s2("SELECT id, event_type_id, start_time, end_time "
+                                                  " FROM Events ORDER BY start_time ASC");
+
+                               if ( dbq1->exec( s1 ) && dbq2->exec( s2 ))
+                               {
+                                       qDebug() << "Query OK, " << dbq1->numRowsAffected() << " & " << dbq2->numRowsAffected() << " rows affected.";
+
+                                       while( dbq1->next() && dbq2->next())
+                                       {
+                                               int one (dbq1->value( 0 ).value< int >());
+                                               int two (dbq2->value( 0 ).value< int >());
+                                               //uint startTime( m_dbq->value( 1 ).value< uint >() );
+                                               //uint endTime( m_dbq->value( 2 ).value< uint >() );
+
+                                               //qDebug() << "Event: " << type << ", " << startTime << ", " << endTime << "";
+                                               //qDebug() << "( " << one << ", " << two << " )";
+
+                                               if(two != one)
+                                               {
+                                                       if(min == 0)
+                                                               min = one;
+
+                                                       qDebug() << "( " << one << ", " << two << " )";
+                                                       mapping.insert(one, two);
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       qDebug() << "SQL EXEC Error: "<< "EXEC query failed";
+                                       qDebug() << "Query1: " << s1;
+                                       qDebug() << "Query2: " << s1;
+                               }
+
+                               // Clear up database connections
+                               if ( dbq1 != NULL )
+                               {
+                                       qDebug() << "Cleaning up connection 1";
+
+                                       dbq1->finish();
+
+                                       delete dbq1;
+                                       dbq1 = NULL;
+                               }
+
+                               if ( dbq2 != NULL )
+                               {
+                                       qDebug() << "Cleaning up connection 2";
+
+                                       dbq2->finish();
+
+                                       delete dbq2;
+                                       dbq2 = NULL;
+                               }
+                       }
+
+                       QList<int> sequence;
+                       int val(min);
+                       sequence.append(0);
+                       sequence.append(val);
+                       qDebug().nospace() << "val1: " << val << ", ";
+
+                       while((val = mapping[val]) && val != min)
+                       {
+                               sequence.append(val);
+                               qDebug().nospace() << val << ", ";
+                       }
+                       sequence.append(0);
+
+                       qDebug().nospace() << "seq: ";
+                       QList<QPair<int,int> > updates;
+                       int last(sequence.first());
+                       foreach(int seq, sequence)
+                       {
+                               if(seq != last)
+                               {
+                                       qDebug().nospace() << seq << ", " << last << ", ";
+                                       updates.append(QPair<int,int>(seq, last));
+                               }
+
+                               last = seq;
+                       }
+
+                       // Used to keep iterating until no changes are required.
+                       // TODO: Shouldn't be required, but is. One to revisit later.
+                       changesRequired = updates.count();
+
+                       for( QList<QPair<int,int> >::const_iterator it(updates.constBegin()); it != updates.constEnd(); ++it)
+                       {
+                               //qDebug().nospace() << (*it).first << ", " << (*it).second;
+                       }
+
+                       QList<QString> tables = QList<QString>() << "Events" << "Attachments" << "Headers" << "GroupCache";
+                       QString query;
+                       for( QList<QString>::const_iterator currentTable(tables.constBegin()); currentTable != tables.constEnd(); ++currentTable)
+                       {
+                               QString curquery = "UPDATE %3 set %4 = %1 WHERE %4 = %2;";
+                               for( QList<QPair<int,int> >::const_iterator currentUpdate(updates.constBegin()); currentUpdate != updates.constEnd(); ++currentUpdate)
+                               {
+                                       query.append(
+                                               curquery
+                                                       .arg((*currentUpdate).second)
+                                                       .arg((*currentUpdate).first)
+                                                       .arg((*currentTable))
+                                                       .arg((*currentTable) == "Events" ? "id" : "event_id")
+                                               ).append("\n");
+
+                                       //qDebug().nospace() << (*it).first << ", " << (*it).second;
+                               }
+                       }
+
+                       qDebug() << query;
+
+                       QSqlQuery * UpdateQuery(new QSqlQuery( db ));
+                       if(UpdateQuery != NULL)
+                       {
+                               UpdateQuery->setForwardOnly( true );
+
+                               if(db.transaction())
+                               {
+                                       QStringList statements = query.trimmed().split(";", QString::SkipEmptyParts);
+                                       try
+                                       {
+                                               for( QStringList::const_iterator currentStatement(statements.constBegin()); currentStatement != statements.constEnd(); ++currentStatement)
+                                               {
+                                                       if ( UpdateQuery->exec( *currentStatement ))
+                                                               qDebug() << "Query OK, " << UpdateQuery->numRowsAffected() << " rows affected.";
+                                                       else
+                                                       {
+                                                               qDebug() << "Query Failed: " << *currentStatement;
+                                                               throw std::exception();
+                                                       }
+                                               }
+
+                                               qDebug() << "Committing.";
+                                               db.commit();
+                                       }
+                                       catch(...)
+                                       {
+                                               qDebug() << "Rolling back.";
+                                               db.rollback();
+                                       }
+                               }
+                               else
+                                       qDebug() << "Unable to start transaction.";
+                       }
+               }while(changesRequired > 0);
+
+               // Update the group cache so the last events are correct
+               {
+                       qDebug() << "Updating most recent events.";
+
+                       // Grab group UIDs from group cache
+                       QSqlQuery * dbq(new QSqlQuery( db ));
+                       dbq->setForwardOnly( true );
+
+                       const char * groupUIDListSQL("SELECT group_uid FROM GroupCache");
+                       if (dbq->exec(groupUIDListSQL))
+                       {
+                               qDebug() << "Query OK, " << dbq->numRowsAffected() << " rows affected.";
+                               qDebug() << "GroupUIDs:";
+
+                               QSet<QString> groupUIDs;
+                               while( dbq->next() )
+                               {
+                                       QString groupUID(dbq->value(0).value<QString>());
+
+                                       qDebug() << groupUID;
+                                       groupUIDs.insert(groupUID);
+                               }
+
+                               // Iterate over group UIDS
+                               if(groupUIDs.count() > 0)
+                               {
+                                       // Build a batch statement to update every group with
+                                       // the most recent event
+
+                                       // Ignore 'data' failures (i.e. no events but present in the
+                                       // cache)- something else's been monkeying with the DB, and
+                                       // we can't account for everything.
+                                       const QString updateGroupCacheWithLatestEventsSQL(
+                                               "UPDATE OR IGNORE GroupCache SET event_id = "
+                                                       "(SELECT id FROM events WHERE group_uid = \"%1\" "
+                                                       " ORDER BY id DESC LIMIT 1)"
+                                               " WHERE group_uid = \"%1\";");
+                                       QString updateGroupCacheWithLatestEventsBatchSQL;
+                                       foreach(QString groupUID, groupUIDs)
+                                       {
+                                               updateGroupCacheWithLatestEventsBatchSQL.append(
+                                                       updateGroupCacheWithLatestEventsSQL
+                                                       .arg(groupUID)
+                                                       ).append("\n");
+                                       }
+
+                                       // Execute the statement in single-statement chunks thanks
+                                       // to QT's inability to call the SQLite function supporting
+                                       // multiple statements
+
+                                       QSqlQuery * setLatestEventInGroupCacheSQL(new QSqlQuery( db ));
+                                       if(NULL != setLatestEventInGroupCacheSQL)
+                                       {
+                                               setLatestEventInGroupCacheSQL->setForwardOnly( true );
+
+                                               if(db.transaction())
+                                               {
+                                                       QStringList statements = updateGroupCacheWithLatestEventsBatchSQL.trimmed().split(";", QString::SkipEmptyParts);
+                                                       try
+                                                       {
+                                                               for( QStringList::const_iterator currentStatement(statements.constBegin()); currentStatement != statements.constEnd(); ++currentStatement)
+                                                               {
+                                                                       if ( setLatestEventInGroupCacheSQL->exec( *currentStatement ))
+                                                                               qDebug() << "Query OK, " << setLatestEventInGroupCacheSQL->numRowsAffected() << " rows affected.";
+                                                                       else
+                                                                       {
+                                                                               qDebug() << "Query Failed: " << *currentStatement;
+                                                                               throw std::exception();
+                                                                       }
+                                                               }
+
+                                                               qDebug() << "Committing.";
+                                                               db.commit();
+                                                       }
+                                                       catch(...)
+                                                       {
+                                                               qDebug() << "Rolling back.";
+                                                               db.rollback();
+                                                       }
+                                               }
+                                               else
+                                                       qDebug() << "Unable to start transaction.";
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               qDebug() << "SQL EXEC Error: "<< "EXEC query failed";
+                               qDebug() << "Query: " << groupUIDListSQL;
+                       }
+               }
+
+               qDebug() << "Closing.";
+               db.close();
+               QSqlDatabase::removeDatabase( "QSQLITE" );
+       }
+
+       return;
+}
+
 QDebug operator<<(QDebug dbg, RTComElEvent &event)
 {
        dbg.nospace() << "\tid:\t\t" << event.fld_id << "\n";
-       dbg.nospace() << "\tFolder:\t\t" << (event.fld_outgoing ? "Sent" : "Inbox") << "\n";
+       dbg.nospace() << "\tservice_id:\t" << event.fld_service_id << "\n";
+       dbg.nospace() << "\tservice:\t" << event.fld_service << "\n";
+       dbg.nospace() << "\tevt_typ_id:\t" << event.fld_event_type_id << "\n";
+       dbg.nospace() << "\tevt_typ:\t" << event.fld_event_type << "\n";
+       dbg.nospace() << "\tstore-time:\t" << QDateTime::fromTime_t(event.fld_storage_time) << "\n";
        dbg.nospace() << "\tstart-time:\t" << QDateTime::fromTime_t(event.fld_start_time) << "\n";
        dbg.nospace() << "\tend-time:\t\t" << QDateTime::fromTime_t(event.fld_end_time) << "\n";
-       //dbg.nospace() << "\tlocal-uid:\t" << event.fld_local_uid << "\n";
-       //dbg.nospace() << "\tlocal-name:\t" << event.fld_local_name << "\n";
+       dbg.nospace() << "\tis-read:\t\t" << (event.fld_is_read ? "true" : "false") << "\n";
+       dbg.nospace() << "\tdirection:\t" << (event.fld_outgoing ? "Outgoing" : "Incoming") << "\n";
+       dbg.nospace() << "\tflags:\t\t" << "0x" << QString::number(event.fld_flags, 16) << "\n";
+       dbg.nospace() << "\tbytes sent:\t" << event.fld_bytes_sent << "\n";
+       dbg.nospace() << "\tbytes recv:\t" << event.fld_bytes_received << "\n";
+       dbg.nospace() << "\tlocal-uid:\t" << event.fld_local_uid << "\n";
+       dbg.nospace() << "\tlocal-name:\t" << event.fld_local_name << "\n";
        dbg.nospace() << "\tremote-uid:\t" << event.fld_remote_uid << "\n";
        dbg.nospace() << "\tremote-name:\t" << event.fld_remote_name << "\n";
-       dbg.nospace() << "\tis-read:\t\t" << (event.fld_is_read ? "true" : "false") << "\n";
+       dbg.nospace() << "\tchannel:\t\t" << event.fld_channel << "\n";
        dbg.nospace() << "\tfree-text:\t" << event.fld_free_text << "\n";
        dbg.nospace() << "\tgroup-uid:\t" << event.fld_group_uid << "\n";