Addressing some compilation warnings:
[qwerkisync] / EventParsers / CSVSymbianEventLogParser.cpp
1 /*
2  * Copyright (C) 2011, Jamie Thompson
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License as published by the Free Software Foundation; either
7  * version 3 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public
15  * License along with this program; If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18
19 #include "CSV.h"
20 #include "CSVSymbianEventLogParser.h"
21 #include "EventTypes/PhoneCall.h"
22 #include "Settings.h"
23
24 #include <QDebug>
25
26 #include <QDir>
27 #include <QFile>
28 #include <QFileInfo>
29 #include <QString>
30 #include <QStringList>
31
32 #include <stdexcept>
33
34 using namespace EventParsers;
35 using EventTypes::PhoneCall;
36
37 class SortByValueDesc
38 {
39 public:
40         inline bool operator()(const QPair<QChar, uint> &a, const QPair<QChar, uint> &b) const
41         {
42                 return b.second < a.second;
43         }
44 };
45
46 const QString ExtractString(const QString &originalString)
47 {
48         QRegExp content("^[\"\']?(.*)?[\"\']?$");
49         content.indexIn(originalString.trimmed());
50         return content.cap(1);
51 }
52
53 iEventParser *CSVSymbianEventLogParser::IsValid(const Settings &currentSettings, QFile &eventFile)
54 {
55         qDebug() << "Checking if a CSV call log file...";
56
57         CSV eventsReader;
58         eventsReader.Open(eventFile);
59
60         // If we found all of the required headings, continue
61         if(eventsReader.HasRequiredHeadings(QStringList() << "EType" << "ETime"<< "Remote" << "Direction" << "Status" << "Duration" << "Number" << "Data").count() == 0)
62                 return new EventParsers::CSVSymbianEventLogParser(currentSettings, eventFile.fileName()/*, delim, numColumnsPerRecord, headingPositions*/);
63         else
64                 return NULL;
65 }
66
67 CSVSymbianEventLogParser::CSVSymbianEventLogParser(const Settings &settings, const QString &/*filename, const QChar delimiter, const int numColumnsPerRecord, const ColumnIndicesHash &headingIndices*/)
68         : m_Settings(settings)/*, m_Delimiter(delimiter), m_NumColumnsPerRecord(numColumnsPerRecord), m_HeadingIndices(headingIndices)*/
69 {
70 }
71
72 EventTypes::EventFromFileList CSVSymbianEventLogParser::ParseFile(QFile &eventFile, const QList<uint> &recordsToReturn)
73 {
74         EventTypes::EventFromFileList fileEvents;
75
76         // Parse secondary table for string values
77         {
78                 // Determine filename
79                 QFileInfo eventFileInfo(eventFile);
80                 QString filePath(eventFileInfo.absolutePath() + "/string.csv");
81
82                 QFile stringsFile(filePath);
83                 if(stringsFile.open(QFile::ReadOnly))
84                 {
85                         CSV stringsReader;
86                         stringsReader.Open(stringsFile);
87                         if(stringsReader.HasRequiredHeadings(QStringList() << "TEXT" << "Id").count() == 0)
88                         {
89                                 while(!stringsReader.AtEnd())
90                                 {
91                                         QHash<QString, QString> values(stringsReader.ReadRecord());
92                                         Strings().insert(values.value("text").toLower(), values.value("id").toUInt());
93                                 }
94                         }
95                 }
96                 else
97                         throw std::runtime_error(QString("Unable to open: '%1'. The error was: %2").arg(filePath).arg(stringsFile.error()).toStdString());
98         }
99
100         QSet<uint> recordsToReturnSet(QSet<uint>::fromList(recordsToReturn));
101
102         CSV eventsReader;
103         eventsReader.Open(eventFile);
104         if(eventsReader.HasRequiredHeadings(QStringList() << "EType" << "ETime"
105                                                                                 << "Remote" << "Direction" << "Status"
106                                                                                 << "Duration" << "Number" << "Data").count() == 0)
107         {
108                 while(!eventsReader.AtEnd())
109                 {
110                         QHash<QString, QString> recordValues(eventsReader.ReadRecord());
111
112                         if(recordsToReturnSet.count() == 0 || recordsToReturnSet.contains(eventsReader.CurrentRecordNumber()))
113                         {
114                                 bool bOK(false);
115                                 int eType(recordValues.value("etype").toUInt(&bOK));
116                                 // We're only interested in phone calls
117                                 if(bOK && eType == 0)
118                                 {
119                                         qDebug() << "Parsing event from record #" << eventsReader.CurrentRecordNumber() << ". Values: " << recordValues;
120
121                                         QDateTime eTime(QDateTime::fromString(recordValues.value("etime"), "dd/MM/yyyy h:mm:ss ap"));
122                                         Settings::eDirection direction(ReadDirection(recordValues.value("direction").toUInt()));
123
124                                         // We only care about the requested directions...
125                                         if(CurrentSettings().ShouldProcess(direction, EventTypes::EVENT_TYPE_CALL))
126                                         {
127                                                 int duration(recordValues.value("duration").toInt(&bOK));
128                                                 if(!bOK)
129                                                 {
130                                                         qDebug() << QString("Unable to parse '%1' as a duration. Skipping record.")
131                                                                                         .arg(recordValues.value("duration"));
132                                                         continue;
133                                                 }
134
135                                                 QString number(ExtractString(recordValues.value("number")));
136
137                                                 bool isMissedCall(ExtractString(recordValues.value("direction")).toUInt() == Strings().value("missed call"));
138
139                                                 uint test = ExtractString(recordValues.value("direction")).toUInt();
140                                                 if(test != Strings().value("incoming") && test != Strings().value("outgoing"))
141                                                         qDebug() << eventFile.fileName() << ", line #" << eventsReader.CurrentRecordNumber() << "Direction? : " << test;
142
143                                                 // TODO: Not currently used...but almost certainly contains SIP call data
144                                                 QString data(ExtractString(recordValues.value("data")));
145
146                                                 if(number.trimmed().length() == 0)
147                                                         qDebug() << "Empty tel!";
148
149                                                 QSharedPointer<EventTypes::iEvent> newEvent(new PhoneCall(
150                                                         CurrentSettings(),
151                                                         direction,
152                                                         eTime,
153                                                         number,
154                                                         duration,
155                                                         isMissedCall));
156                                                 fileEvents.append(EventTypes::EventFromFile(newEvent, eventsReader.CurrentRecordNumber()));
157                                         }
158                                 }
159                         }
160                 }
161         }
162
163         qDebug() << QString("File pos: %1, bAvail: %2, canReadLine: %3").arg(eventFile.pos()).arg(eventFile.bytesAvailable()).arg(eventFile.canReadLine());
164         qDebug() << fileEvents.count() << " events loaded from file";
165         return fileEvents;
166 }
167
168 Settings::eDirection CSVSymbianEventLogParser::ReadDirection(const uint value)
169 {
170         // Missed calls are
171         if(value == Strings().value("incoming") || value == Strings().value("missed call"))
172                 return Settings::INCOMING;
173         else if(value == Strings().value("outgoing"))
174                 return Settings::OUTGOING;
175         else
176         {
177                 // Determine which string this value corresponds to...
178                 QString unexpectedString("");
179                 foreach(QString string, Strings().keys())
180                         if(Strings().value(string) == value)
181                                 unexpectedString = string;
182
183                 throw std::runtime_error(
184                                 QString("Unexpected value found: \"%1\" (maps to \"%2\")")
185                                 .arg(value)
186                                         .arg(unexpectedString != "" ? unexpectedString : "<no known string>")
187                                 .toStdString());
188         }
189 }