*/
#include "CSVSymbianEventLogParser.h"
+#include "EventTypes/PhoneCall.h"
+#include "Settings.h"
#include <QDebug>
#include <QString>
#include <QStringList>
+#include <stdexcept>
+
using namespace EventParsers;
+using EventTypes::PhoneCall;
class SortByValueDesc
{
public:
- inline bool operator()(const QPair<char, uint> &a, const QPair<char, uint> &b) const
+ inline bool operator()(const QPair<QChar, uint> &a, const QPair<QChar, uint> &b) const
{
return b.second < a.second;
}
};
-iEventParser *CSVSymbianEventLogParser::IsValid(QFile &eventFile)
+const QString ExtractString(const QString &originalString)
+{
+ QRegExp content("^[\"\']?(.*)?[\"\']?$");
+ content.indexIn(originalString.trimmed());
+ return content.cap(1);
+}
+
+iEventParser *CSVSymbianEventLogParser::IsValid(const Settings ¤tSettings, QFile &eventFile)
{
qDebug() << "Checking if a CSV call log file...";
- QByteArray firstLineContent(eventFile.readLine());
+ QTextStream stream(&eventFile);
+
+ QString firstLineContent(stream.readLine());
eventFile.seek(0);
if(firstLineContent.length() > 0)
{
// Count the non-alphanumeric characters used
- QHash<char, uint> counts;
- foreach(char c, firstLineContent)
+ QHash<QChar, uint> counts;
+ foreach(const QChar c, firstLineContent)
++counts[c];
- QList<QPair<char, uint> > orderedCounts;
+ QList<QPair<QChar, uint> > orderedCounts;
orderedCounts.reserve(counts.size());
- foreach(char c, counts.keys())
+ foreach(const QChar c, counts.keys())
if(!QChar(c).isLetterOrNumber())
- orderedCounts.append(QPair<char, uint>(c, counts.value(c)));
+ orderedCounts.append(QPair<QChar, uint>(c, counts.value(c)));
qSort(orderedCounts.begin(), orderedCounts.end(), SortByValueDesc());
// Work around Q_FOREACH macro limitation when dealing with
// multi-typed templates (comma issue)
- typedef QPair<char, uint> bodge;
+ typedef QPair<QChar, uint> bodge;
foreach(bodge count, orderedCounts)
qDebug() << count.first << " = " << count.second;
- char delim;
+ QChar delim;
// No-one would be mad enough to use quotation marks or apostrophes
// as their delimiter,but just in case, check the second most
// frequent character is present thr right number of times for
// Check we have the essential fields we need, and grab their
// column ordering
QStringList requiredHeadings;
- requiredHeadings << "etype" << "etime" << "remote"
- << "direction" << "duration" << "number";
+ requiredHeadings << "etype" << "etime" << "remote" << "direction"
+ << "duration" << "number" << "data";
EventParsers::CSVSymbianEventLogParser::ColumnIndicesHash headingPositions;
headingPositions.reserve(requiredHeadings.count());
- QStringList headings(QString(firstLineContent).split(delim));
+ QStringList headings(QString(firstLineContent).split(delim, QString::KeepEmptyParts, Qt::CaseSensitive));
+ int numColumnsPerRecord(headings.count());
for(QStringList::size_type i(0); i < headings.count(); ++i)
{
- QRegExp content("^[\"\']?(\\w*)?[\"\']?$");
- content.indexIn(headings.value(i).trimmed());
- QString heading(content.cap(1));
+ QString heading(ExtractString(headings.value(i)));
qDebug() << headings.value(i) << " : " << heading;
// Check over the required headings
// If we found all of the required headings, continue
if(requiredHeadings.count() == 0)
- {
- return new EventParsers::CSVSymbianEventLogParser(eventFile.fileName(), headingPositions);
- }
+ return new EventParsers::CSVSymbianEventLogParser(currentSettings, eventFile.fileName(), delim, numColumnsPerRecord, headingPositions);
}
return NULL;
}
-CSVSymbianEventLogParser::CSVSymbianEventLogParser(const QString &filename, const ColumnIndicesHash &columns)
+CSVSymbianEventLogParser::CSVSymbianEventLogParser(const Settings &settings, const QString &filename, const QChar delimiter, const int numColumnsPerRecord, const ColumnIndicesHash &headingIndices)
+ : m_Settings(settings), m_Delimiter(delimiter), m_NumColumnsPerRecord(numColumnsPerRecord), m_HeadingIndices(headingIndices)
{
}
EventTypes::EventFromFileList CSVSymbianEventLogParser::ParseFile(QFile &eventFile, const QList<uint> &recordsToReturn)
{
qDebug() << "CSV Parsing NYI!";
- return EventTypes::EventFromFileList();
+ EventTypes::EventFromFileList fileEvents;
+ //return fileEvents;
+
+ QSet<uint> recordsToReturnSet(QSet<uint>::fromList(recordsToReturn));
+
+ uint lineNumber(0);
+ uint recordNumber(0);
+ eventFile.seek(0);
+
+ // Read the first line
+ QTextStream stream(&eventFile);
+ QString firstLineContent(stream.readLine());
+ QStringList firstLineValues(QString(firstLineContent).split(m_Delimiter));
+ if(firstLineValues.count() != m_NumColumnsPerRecord)
+ throw new std::runtime_error(QString("Unexpected number of columns (%1, expected %2) on line %3 of %4")
+ .arg(firstLineValues.count())
+ .arg(m_NumColumnsPerRecord)
+ .arg(lineNumber)
+ .arg(eventFile.fileName()).toStdString());
+ ++lineNumber;
+
+ // Read the main body of the file
+ while(!stream.atEnd())
+ {
+ QStringList lineValues(QString(stream.readLine()).split(m_Delimiter));
+ ++lineNumber;
+ // Make sure we have enough columns (i.e. handle newlines in values)
+ while(lineValues.count() < m_NumColumnsPerRecord)
+ {
+ lineValues.append(QString(stream.readLine()).split(m_Delimiter));
+ ++lineNumber;
+ }
+
+ if(recordsToReturnSet.count() == 0 || recordsToReturnSet.contains(recordNumber))
+ {
+ bool bOK(false);
+ int eType(lineValues.at(m_HeadingIndices.value("etype")).toUInt(&bOK));
+ // We're only interested in phone calls
+ if(bOK && eType == 0)
+ {
+ qDebug() << "Parsing event from line #" << lineNumber << ". Values: " << lineValues;
+
+ QDateTime eTime(QDateTime::fromString(lineValues.at(m_HeadingIndices.value("etime")), "dd/MM/yyyy h:mm:ss ap"));
+ Settings::eDirection direction(lineValues.at(m_HeadingIndices.value("direction")) == "0"
+ ? Settings::INCOMING
+ : Settings::OUTGOING);
+
+ // We only care about the requested directions...
+ if(CurrentSettings().ShouldProcess(direction, EventTypes::EVENT_TYPE_CALL))
+ {
+ int duration(lineValues.at(m_HeadingIndices.value("duration")).toInt(&bOK));
+ if(!bOK)
+ {
+ qDebug() << QString("Unable to parse '%1' as a duration. Skipping record.")
+ .arg(lineValues.at(m_HeadingIndices.value("duration")));
+ continue;
+ }
+ QString number(ExtractString(lineValues.at(m_HeadingIndices.value("number"))));
+ // TODO: Not currently used...but almost certainly contains SIP call data
+ QString data(ExtractString(lineValues.at(m_HeadingIndices.value("data"))));
+
+ if(number.trimmed().length() == 0)
+ {
+ qDebug() << "Empty tel!";
+ }
+
+ QSharedPointer<EventTypes::iEvent> newEvent(new PhoneCall(
+ CurrentSettings(),
+ direction,
+ eTime,
+ number,
+ duration));
+ fileEvents.append(EventTypes::EventFromFile(newEvent, recordNumber));
+ }
+ }
+ }
+
+ ++recordNumber;
+ }
+
+ qDebug() << QString("File pos: %1, bAvail: %2, canReadLine: %3").arg(eventFile.pos()).arg(eventFile.bytesAvailable()).arg(eventFile.canReadLine());
+ qDebug() << fileEvents.count() << " events loaded from file";
+ return fileEvents;
}