mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-05 02:30:44 +00:00
Add generic CSV reader class and switch ZEO loader to it.
No change in functionality for ZEO files. Resolves crashing (assertion failure) on non-ZEO CSV files.
This commit is contained in:
parent
8ef068af7e
commit
f33dd654f8
@ -18,6 +18,7 @@
|
||||
#include <QTextStream>
|
||||
#include "zeo_loader.h"
|
||||
#include "SleepLib/machine.h"
|
||||
#include "csv.h"
|
||||
|
||||
ZEOLoader::ZEOLoader()
|
||||
{
|
||||
@ -26,6 +27,7 @@ ZEOLoader::ZEOLoader()
|
||||
|
||||
ZEOLoader::~ZEOLoader()
|
||||
{
|
||||
closeCSV();
|
||||
}
|
||||
|
||||
int ZEOLoader::Open(const QString & dirpath)
|
||||
@ -93,6 +95,7 @@ int ZEOLoader::OpenFile(const QString & filename)
|
||||
count++;
|
||||
}
|
||||
mach->Save();
|
||||
closeCSV();
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -111,49 +114,46 @@ bool ZEOLoader::openCSV(const QString & filename)
|
||||
// not supported.
|
||||
}
|
||||
|
||||
text.setDevice(&file);
|
||||
QString headerdata = text.readLine();
|
||||
QStringList header = headerdata.split(",");
|
||||
QStringList header;
|
||||
csv = new CSVReader(file);
|
||||
bool ok = csv->readRow(header);
|
||||
if (!ok) {
|
||||
qWarning() << "no header row";
|
||||
return false;
|
||||
}
|
||||
csv->setFieldNames(header);
|
||||
|
||||
MachineInfo info = newInfo();
|
||||
mach = p_profile->CreateMachine(info);
|
||||
|
||||
idxZQ = header.indexOf("ZQ");
|
||||
//int idxTotalZ = header.indexOf("Total Z");
|
||||
idxAwakenings = header.indexOf("Awakenings");
|
||||
idxSG = header.indexOf("Sleep Graph");
|
||||
idxDSG = header.indexOf("Detailed Sleep Graph");
|
||||
idxTimeInWake = header.indexOf("Time in Wake");
|
||||
idxTimeToZ = header.indexOf("Time to Z");
|
||||
idxTimeInREM = header.indexOf("Time in REM");
|
||||
idxTimeInLight = header.indexOf("Time in Light");
|
||||
idxTimeInDeep = header.indexOf("Time in Deep");
|
||||
idxStartOfNight = header.indexOf("Start of Night");
|
||||
idxEndOfNight = header.indexOf("End of Night");
|
||||
idxRiseTime = header.indexOf("Rise Time");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZEOLoader::closeCSV()
|
||||
{
|
||||
if (csv != nullptr) {
|
||||
delete csv;
|
||||
csv = nullptr;
|
||||
}
|
||||
if (file.isOpen()) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
// int idxTotalZ = header.indexOf("Total Z");
|
||||
// int idxAlarmReason = header.indexOf("Alarm Reason");
|
||||
// int idxSnoozeTime = header.indexOf("Snooze Time");
|
||||
// int idxWakeTone = header.indexOf("Wake Tone");
|
||||
// int idxWakeWindow = header.indexOf("Wake Window");
|
||||
// int idxAlarmType = header.indexOf("Alarm Type");
|
||||
idxFirstAlaramRing = header.indexOf("First Alarm Ring");
|
||||
idxLastAlaramRing = header.indexOf("Last Alarm Ring");
|
||||
idxFirstSnoozeTime = header.indexOf("First Snooze Time");
|
||||
idxLastSnoozeTime = header.indexOf("Last Snooze Time");
|
||||
idxSetAlarmTime = header.indexOf("Set Alarm Time");
|
||||
idxMorningFeel = header.indexOf("Morning Feel");
|
||||
idxFirmwareVersion = header.indexOf("Firmware Version");
|
||||
idxMyZEOVersion = header.indexOf("My ZEO Version");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Session* ZEOLoader::readNextSession()
|
||||
{
|
||||
if (csv == nullptr) {
|
||||
qWarning() << "no CSV open!";
|
||||
return nullptr;
|
||||
}
|
||||
Session* sess = nullptr;
|
||||
QString line;
|
||||
QStringList linecomp;
|
||||
QDateTime start_of_night, end_of_night, rise_time;
|
||||
SessionID sid;
|
||||
|
||||
@ -174,121 +174,97 @@ Session* ZEOLoader::readNextSession()
|
||||
bool ok;
|
||||
bool dodgy;
|
||||
|
||||
do {
|
||||
line = text.readLine();
|
||||
QHash<QString,QString> row;
|
||||
while (csv->readRow(row)) {
|
||||
dodgy = false;
|
||||
|
||||
if (line.isEmpty()) { continue; }
|
||||
|
||||
linecomp = line.split(",");
|
||||
ZQ = linecomp[idxZQ].toInt(&ok);
|
||||
|
||||
ZQ = row["ZQ"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
// TotalZ = linecomp[idxTotalZ].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
TimeToZ = linecomp[idxTimeToZ].toInt(&ok);
|
||||
|
||||
TimeToZ = row["Time to Z"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
TimeInWake = linecomp[idxTimeInWake].toInt(&ok);
|
||||
|
||||
TimeInWake = row["Time in Wake"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
TimeInREM = linecomp[idxTimeInREM].toInt(&ok);
|
||||
|
||||
TimeInREM = row["Time in REM"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
TimeInLight = linecomp[idxTimeInLight].toInt(&ok);
|
||||
|
||||
TimeInLight = row["Time in Light"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
TimeInDeep = linecomp[idxTimeInDeep].toInt(&ok);
|
||||
|
||||
TimeInDeep = row["Time in Deep"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
Awakenings = linecomp[idxAwakenings].toInt(&ok);
|
||||
|
||||
Awakenings = row["Awakenings"].toInt(&ok);
|
||||
if (!ok) { dodgy = true; }
|
||||
|
||||
start_of_night = readDateTime(linecomp[idxStartOfNight]);
|
||||
|
||||
start_of_night = readDateTime(row["Start of Night"]);
|
||||
if (!start_of_night.isValid()) { dodgy = true; }
|
||||
|
||||
end_of_night = readDateTime(linecomp[idxEndOfNight]);
|
||||
|
||||
end_of_night = readDateTime(row["End of Night"]);
|
||||
if (!end_of_night.isValid()) { dodgy = true; }
|
||||
|
||||
rise_time = readDateTime(linecomp[idxRiseTime]);
|
||||
|
||||
rise_time = readDateTime(row["Rise Time"]);
|
||||
if (!rise_time.isValid()) { dodgy = true; }
|
||||
|
||||
// AlarmReason = linecomp[idxAlarmReason].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
// SnoozeTime = linecomp[idxSnoozeTime].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
// WakeTone = linecomp[idxWakeTone].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
// WakeWindow = linecomp[idxWakeWindow].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
// AlarmType = linecomp[idxAlarmType].toInt(&ok);
|
||||
|
||||
// if (!ok) { dodgy = true; }
|
||||
|
||||
if (!linecomp[idxFirstAlaramRing].isEmpty()) {
|
||||
FirstAlarmRing = readDateTime(linecomp[idxFirstAlaramRing]);
|
||||
|
||||
if (!row["First Alarm Ring"].isEmpty()) {
|
||||
FirstAlarmRing = readDateTime(row["First Alarm Ring"]);
|
||||
if (!FirstAlarmRing.isValid()) { dodgy = true; }
|
||||
}
|
||||
|
||||
if (!linecomp[idxLastAlaramRing].isEmpty()) {
|
||||
LastAlarmRing = readDateTime(linecomp[idxLastAlaramRing]);
|
||||
|
||||
if (!row["Last Alarm Ring"].isEmpty()) {
|
||||
LastAlarmRing = readDateTime(row["Last Alarm Ring"]);
|
||||
if (!LastAlarmRing.isValid()) { dodgy = true; }
|
||||
}
|
||||
|
||||
if (!linecomp[idxFirstSnoozeTime].isEmpty()) {
|
||||
FirstSnoozeTime = readDateTime(linecomp[idxFirstSnoozeTime]);
|
||||
if (!row["First Snooze Time"].isEmpty()) {
|
||||
FirstSnoozeTime = readDateTime(row["First Snooze Time"]);
|
||||
|
||||
if (!FirstSnoozeTime.isValid()) { dodgy = true; }
|
||||
}
|
||||
|
||||
if (!linecomp[idxLastSnoozeTime].isEmpty()) {
|
||||
LastSnoozeTime = readDateTime(linecomp[idxLastSnoozeTime]);
|
||||
|
||||
if (!row["Last Snooze Time"].isEmpty()) {
|
||||
LastSnoozeTime = readDateTime(row["Last Snooze Time"]);
|
||||
if (!LastSnoozeTime.isValid()) { dodgy = true; }
|
||||
}
|
||||
|
||||
if (!linecomp[idxSetAlarmTime].isEmpty()) {
|
||||
SetAlarmTime = readDateTime(linecomp[idxSetAlarmTime]);
|
||||
|
||||
if (!row["Set Alarm Time"].isEmpty()) {
|
||||
SetAlarmTime = readDateTime(row["Set Alarm Time"]);
|
||||
if (!SetAlarmTime.isValid()) { dodgy = true; }
|
||||
}
|
||||
|
||||
MorningFeel = linecomp[idxMorningFeel].toInt(&ok);
|
||||
|
||||
MorningFeel = row["Morning Feel"].toInt(&ok);
|
||||
if (!ok) { MorningFeel = 0; }
|
||||
|
||||
FirmwareVersion = linecomp[idxFirmwareVersion];
|
||||
FirmwareVersion = row["Firmware Version"];
|
||||
|
||||
if (idxMyZEOVersion >= 0) { MyZeoVersion = linecomp[idxMyZEOVersion]; }
|
||||
MyZeoVersion = row["My ZEO Version"];
|
||||
|
||||
if (dodgy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SG = linecomp[idxSG].split(" ");
|
||||
DSG = linecomp[idxDSG].split(" ");
|
||||
SG = row["Sleep Graph"].split(" ");
|
||||
DSG = row["Detailed Sleep Graph"].split(" ");
|
||||
|
||||
sid = start_of_night.toTime_t();
|
||||
|
||||
@ -302,11 +278,10 @@ Session* ZEOLoader::readNextSession()
|
||||
|
||||
sess = new Session(mach, sid);
|
||||
break;
|
||||
|
||||
} while (!line.isNull());
|
||||
};
|
||||
|
||||
if (sess) {
|
||||
const int WindowSize = 30000;
|
||||
const int WindowSize = 30 * 1000;
|
||||
|
||||
sess->settings[ZEO_Awakenings] = Awakenings;
|
||||
sess->settings[ZEO_MorningFeel] = MorningFeel;
|
||||
@ -324,11 +299,9 @@ Session* ZEOLoader::readNextSession()
|
||||
|
||||
for (int i = 0; i < DSG.size(); i++) {
|
||||
stage = DSG[i].toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
sleepstage->AddEvent(tt, stage);
|
||||
}
|
||||
|
||||
tt += WindowSize;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ class ZEOLoader : public MachineLoader
|
||||
}
|
||||
|
||||
bool openCSV(const QString & filename);
|
||||
void closeCSV();
|
||||
Session* readNextSession();
|
||||
|
||||
protected:
|
||||
@ -47,28 +48,8 @@ class ZEOLoader : public MachineLoader
|
||||
|
||||
private:
|
||||
QFile file;
|
||||
QTextStream text;
|
||||
class CSVReader* csv;
|
||||
Machine *mach;
|
||||
int idxZQ;
|
||||
int idxAwakenings;
|
||||
int idxSG;
|
||||
int idxDSG;
|
||||
int idxTimeInWake;
|
||||
int idxTimeToZ;
|
||||
int idxTimeInREM;
|
||||
int idxTimeInLight;
|
||||
int idxTimeInDeep;
|
||||
int idxStartOfNight;
|
||||
int idxEndOfNight;
|
||||
int idxRiseTime;
|
||||
int idxFirstAlaramRing;
|
||||
int idxLastAlaramRing;
|
||||
int idxFirstSnoozeTime;
|
||||
int idxLastSnoozeTime;
|
||||
int idxSetAlarmTime;
|
||||
int idxMorningFeel;
|
||||
int idxFirmwareVersion;
|
||||
int idxMyZEOVersion;
|
||||
};
|
||||
|
||||
#endif // ZEOLOADER_H
|
||||
|
65
oscar/csv.cpp
Normal file
65
oscar/csv.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* OSCAR CSV Reader Implementation
|
||||
*
|
||||
* Copyright (c) 2020 The OSCAR Team
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of the source code
|
||||
* for more details. */
|
||||
|
||||
#include "csv.h"
|
||||
#include <QDebug>
|
||||
|
||||
CSVReader::CSVReader(QIODevice & input, const QString & delim, const QString & comment)
|
||||
: m_stream(&input), m_delim(delim), m_comment(comment)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVReader::setFieldNames(QStringList & header)
|
||||
{
|
||||
m_field_names = header;
|
||||
}
|
||||
|
||||
// This is a very simplistic reader that splits lines on the delimiter and truncates
|
||||
// lines after the comment sequence (if specified). It doesn't do any quote handling.
|
||||
// If that's ultimately necessary, either rewrite it or subclass it.
|
||||
//
|
||||
// For a public domain version that handles RFC 4180, see:
|
||||
// https://stackoverflow.com/questions/27318631/parsing-through-a-csv-file-in-qt/40229435#40229435
|
||||
//
|
||||
bool CSVReader::readRow(QStringList & fields)
|
||||
{
|
||||
QString line;
|
||||
fields.clear();
|
||||
|
||||
// Read until the next non-empty/non-comment line.
|
||||
do {
|
||||
line = m_stream.readLine();
|
||||
if (line.isNull()) {
|
||||
return false;
|
||||
}
|
||||
if (m_comment.isNull() == false) {
|
||||
line = line.section(m_comment, 0, 0);
|
||||
}
|
||||
} while (line.isEmpty());
|
||||
|
||||
fields = line.split(m_delim);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSVReader::readRow(QHash<QString,QString> & row)
|
||||
{
|
||||
QStringList fields;
|
||||
row.clear();
|
||||
|
||||
if (!readRow(fields)) {
|
||||
return false;
|
||||
}
|
||||
if (fields.size() > m_field_names.size()) {
|
||||
qWarning() << "row has too many columns";
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
row[m_field_names.at(i)] = fields.at(i);
|
||||
}
|
||||
return true;
|
||||
}
|
28
oscar/csv.h
Normal file
28
oscar/csv.h
Normal file
@ -0,0 +1,28 @@
|
||||
/* OSCAR CSV Reader Implementation
|
||||
*
|
||||
* Copyright (c) 2020 The OSCAR Team
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of the source code
|
||||
* for more details. */
|
||||
|
||||
#include <QTextStream>
|
||||
|
||||
class CSVReader
|
||||
{
|
||||
public:
|
||||
CSVReader(QIODevice & stream, const QString & delim=",", const QString & comment=QString());
|
||||
virtual ~CSVReader() = default;
|
||||
|
||||
QStringList readRow();
|
||||
virtual bool readRow(QStringList & fields); // override this for more complicated processing
|
||||
void setFieldNames(QStringList & header);
|
||||
bool readRow(QHash<QString,QString> & row);
|
||||
|
||||
protected:
|
||||
QTextStream m_stream;
|
||||
QString m_delim;
|
||||
QString m_comment;
|
||||
QStringList m_field_names;
|
||||
};
|
||||
|
@ -296,6 +296,7 @@ SOURCES += \
|
||||
SleepLib/loader_plugins/zeo_loader.cpp \
|
||||
zip.cpp \
|
||||
miniz.c \
|
||||
csv.cpp \
|
||||
translation.cpp \
|
||||
statistics.cpp \
|
||||
oximeterimport.cpp \
|
||||
@ -371,6 +372,7 @@ HEADERS += \
|
||||
SleepLib/loader_plugins/zeo_loader.h \
|
||||
zip.h \
|
||||
miniz.h \
|
||||
csv.h \
|
||||
translation.h \
|
||||
statistics.h \
|
||||
oximeterimport.h \
|
||||
|
@ -48,6 +48,7 @@ static void parseAndEmitSessionYaml(const QString & path)
|
||||
if (count == 0) {
|
||||
qWarning() << "no sessions found";
|
||||
}
|
||||
s_loader->closeCSV();
|
||||
} else {
|
||||
qWarning() << "unable to open file";
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user