mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-05 02:30:44 +00:00
Oximetry summary work, Prescription changes best and worst highlighting, Summary percentile calculations and load all nastiness test
This commit is contained in:
parent
e4132512f5
commit
fe73e0ae5f
@ -53,11 +53,13 @@ void gLineOverlayBar::paint(gGraph & w, int left, int topp, int width, int heigh
|
||||
QHash<ChannelID,QVector<EventList *> >::iterator cei;
|
||||
|
||||
m_count=0;
|
||||
m_sum=0;
|
||||
m_flag_color=schema::channel[m_code].defaultColor();
|
||||
|
||||
if (m_flt==FT_Span) {
|
||||
m_flag_color.setAlpha(128);
|
||||
}
|
||||
EventStoreType raw;
|
||||
for (QVector<Session *>::iterator s=m_day->begin();s!=m_day->end(); s++) {
|
||||
cei=(*s)->eventlist.find(m_code);
|
||||
if (cei==(*s)->eventlist.end()) continue;
|
||||
@ -67,8 +69,9 @@ void gLineOverlayBar::paint(gGraph & w, int left, int topp, int width, int heigh
|
||||
|
||||
for (quint32 i=0;i<el.count();i++) {
|
||||
X=el.time(i);
|
||||
raw=el.data(i);
|
||||
if (m_flt==FT_Span) {
|
||||
Y=X-(qint64(el.raw(i))*1000.0L); // duration
|
||||
Y=X-(qint64(raw)*1000.0L); // duration
|
||||
|
||||
if (X < w.min_x) continue;
|
||||
if (Y > w.max_x) break;
|
||||
@ -80,6 +83,7 @@ void gLineOverlayBar::paint(gGraph & w, int left, int topp, int width, int heigh
|
||||
//x1=w.x2p(X);
|
||||
x1=double(width)/double(xx)*double(X-w.min_x)+left;
|
||||
m_count++;
|
||||
m_sum+=raw;
|
||||
if (m_flt==FT_Span) {
|
||||
//x2=w.x2p(Y);
|
||||
x2=double(width)/double(xx)*double(Y-w.min_x)+left;
|
||||
@ -149,8 +153,12 @@ void gLineOverlaySummary::paint(gGraph & w,int left, int top, int width, int hei
|
||||
Q_UNUSED(width);
|
||||
Q_UNUSED(height);
|
||||
float cnt=0;
|
||||
double sum=0;
|
||||
bool isSpan=false;
|
||||
for (int i=0;i<m_overlays.size();i++) {
|
||||
cnt+=m_overlays[i]->count();
|
||||
sum+=m_overlays[i]->sum();
|
||||
if (m_overlays[i]->flagtype()==FT_Span) isSpan=true;
|
||||
}
|
||||
|
||||
double val,first,last;
|
||||
@ -187,5 +195,14 @@ void gLineOverlaySummary::paint(gGraph & w,int left, int top, int width, int hei
|
||||
|
||||
|
||||
QString a="Event Count="+QString::number(cnt)+" Selection Time="+QString().sprintf("%02i:%02i:%02i",h,m,s)+" "+m_text+"="+QString::number(val,'f',2);
|
||||
|
||||
if (isSpan) {
|
||||
float sph;
|
||||
if (!time) sph=0; else {
|
||||
sph=(100.0/float(time))*(sum/3600.0);
|
||||
if (sph>100) sph=100;
|
||||
}
|
||||
a+=QString(" (\%%1 in events)").arg(sph,0,'f',2);
|
||||
}
|
||||
w.renderText(a,left+m_x,top+m_y);
|
||||
}
|
||||
|
@ -31,11 +31,14 @@ class gLineOverlayBar:public Layer
|
||||
|
||||
//! \brief returns a count of all flags drawn during render. (for gLineOverlaySummary)
|
||||
int count() { return m_count; }
|
||||
double sum() { return m_sum; }
|
||||
FlagType flagtype() { return m_flt; }
|
||||
protected:
|
||||
QColor m_flag_color;
|
||||
QString m_label;
|
||||
FlagType m_flt;
|
||||
int m_count;
|
||||
double m_sum;
|
||||
|
||||
GLShortBuffer *points,*quads, *lines;
|
||||
};
|
||||
|
@ -596,20 +596,28 @@ int calcSPO2Drop(Session *session)
|
||||
int cnt=0;
|
||||
tmp=0;
|
||||
|
||||
// Calculate baseline from first 15 minutes of data, hopefully meaning they are still awake..
|
||||
qint64 start=0;
|
||||
|
||||
// Calculate median baseline
|
||||
QList<EventDataType> med;
|
||||
for (int e=0;e<it.value().size();e++) {
|
||||
EventList & el=*(it.value()[e]);
|
||||
for (unsigned i=0;i<el.count();i++) {
|
||||
val=el.data(i);
|
||||
time=el.time(i);
|
||||
if (val>0) med.push_back(val);
|
||||
if (!start) start=time;
|
||||
if (time > start+900000) break;// 15 minutes
|
||||
if (time > start+3600000) break; // just look at the first hour
|
||||
tmp+=val;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
EventDataType baseline=round(tmp/EventDataType(cnt));
|
||||
qSort(med);
|
||||
int midx=float(med.size())*0.90;
|
||||
if (midx>med.size()-1) midx=med.size()-1;
|
||||
EventDataType baseline=med[midx];
|
||||
session->settings[OXI_SPO2Drop]=baseline;
|
||||
//EventDataType baseline=round(tmp/EventDataType(cnt));
|
||||
EventDataType current;
|
||||
qDebug() << "Calculated baseline" << baseline;
|
||||
|
||||
|
@ -489,6 +489,18 @@ bool Day::settingExists(ChannelID id)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Day::eventsLoaded()
|
||||
{
|
||||
bool r=false;
|
||||
for (int i=0;i<sessions.size();i++) {
|
||||
if (sessions[i]->eventsLoaded()) {
|
||||
r=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
bool Day::channelExists(ChannelID id)
|
||||
{
|
||||
bool r=false;
|
||||
@ -522,5 +534,12 @@ void Day::OpenEvents()
|
||||
for (s=sessions.begin();s!=sessions.end();s++) {
|
||||
(*s)->OpenEvents();
|
||||
}
|
||||
|
||||
}
|
||||
void Day::CloseEvents()
|
||||
{
|
||||
QVector<Session *>::iterator s;
|
||||
|
||||
for (s=sessions.begin();s!=sessions.end();s++) {
|
||||
(*s)->TrashEvents();
|
||||
}
|
||||
}
|
||||
|
@ -136,12 +136,18 @@ public:
|
||||
//! \brief Loads all Events files for this Days Sessions
|
||||
void OpenEvents();
|
||||
|
||||
//! \brief Closes all Events files for this Days Sessions
|
||||
void CloseEvents();
|
||||
|
||||
//! \brief Returns this days sessions list
|
||||
QVector<Session *> & getSessions() { return sessions; }
|
||||
|
||||
//! \brief Returns true if this Day contains loaded Event Data for this channel.
|
||||
bool channelExists(ChannelID id);
|
||||
|
||||
//! \brief Returns true if session events are loaded
|
||||
bool eventsLoaded();
|
||||
|
||||
//! \brief Returns true if this Day contains loaded Event Data or a cached count for this channel
|
||||
bool channelHasData(ChannelID id);
|
||||
|
||||
|
@ -263,6 +263,7 @@ bool Machine::Load()
|
||||
|
||||
if (sess->LoadSummary(s.value()[0])) {
|
||||
sess->SetEventFile(s.value()[1]);
|
||||
sess->OpenEvents();
|
||||
AddSession(sess,profile);
|
||||
} else {
|
||||
qWarning() << "Error unpacking summary data";
|
||||
|
@ -380,7 +380,8 @@ QList<Machine *> Profile::GetMachines(MachineType t)
|
||||
qWarning() << "Profile::GetMachines() i->second == NULL";
|
||||
continue;
|
||||
}
|
||||
if (i.value()->GetType()==t) {
|
||||
MachineType mt=i.value()->GetType();
|
||||
if ((t==MT_UNKNOWN) || (mt==t)) {
|
||||
vec.push_back(i.value());
|
||||
}
|
||||
}
|
||||
@ -472,7 +473,9 @@ Profile *Create(QString name)
|
||||
|
||||
Machine *m=new Machine(prof,0);
|
||||
m->SetClass("Journal");
|
||||
m->properties[STR_PROP_Brand]="Virtual";
|
||||
m->properties[STR_PROP_Brand]="Journal";
|
||||
m->properties[STR_PROP_Model]="Journal Data Machine Object";
|
||||
m->properties[STR_PROP_Serial]=m->hexid();
|
||||
m->properties[STR_PROP_Path]="{DataFolder}/"+m->GetClass()+"_"+m->hexid();
|
||||
m->SetType(MT_JOURNAL);
|
||||
prof->AddMachine(m);
|
||||
@ -616,6 +619,23 @@ const char * US_STR_RebuildCache="RebuildCache";
|
||||
const char * US_STR_ShowDebug="ShowDebug";
|
||||
const char * US_STR_LinkGroups="LinkGroups";
|
||||
|
||||
int Profile::countDays(MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
if (!end.isValid()) end=LastDay(mt);
|
||||
QDate date=start;
|
||||
int days=0;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
if ((mt==MT_UNKNOWN) || (day->machine->GetType()==mt)) days++;
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
return days;
|
||||
|
||||
}
|
||||
|
||||
EventDataType Profile::calcCount(ChannelID code, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
@ -706,6 +726,78 @@ EventDataType Profile::calcWavg(ChannelID code, MachineType mt, QDate start, QDa
|
||||
val=val/hours;
|
||||
return val;
|
||||
}
|
||||
EventDataType Profile::calcMin(ChannelID code, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
if (!end.isValid()) end=LastDay(mt);
|
||||
QDate date=start;
|
||||
|
||||
double min=99999999,tmp;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
tmp=day->Min(code);
|
||||
if (min>tmp) min=tmp;
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
if (min>=99999999) min=0;
|
||||
return min;
|
||||
}
|
||||
EventDataType Profile::calcMax(ChannelID code, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
if (!end.isValid()) end=LastDay(mt);
|
||||
QDate date=start;
|
||||
|
||||
double max=-99999999,tmp;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
tmp=day->Max(code);
|
||||
if (max<tmp) max=tmp;
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
if (max<=-99999999) max=0;
|
||||
return max;
|
||||
}
|
||||
EventDataType Profile::calcSettingsMin(ChannelID code, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
if (!end.isValid()) end=LastDay(mt);
|
||||
QDate date=start;
|
||||
|
||||
double min=99999999,tmp;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
tmp=day->settings_min(code);
|
||||
if (min>tmp) min=tmp;
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
if (min>=99999999) min=0;
|
||||
return min;
|
||||
}
|
||||
EventDataType Profile::calcSettingsMax(ChannelID code, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
if (!end.isValid()) end=LastDay(mt);
|
||||
QDate date=start;
|
||||
|
||||
double max=-99999999,tmp;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
tmp=day->settings_max(code);
|
||||
if (max<tmp) max=tmp;
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
if (max<=-99999999) max=0;
|
||||
return max;
|
||||
}
|
||||
EventDataType Profile::calcPercentile(ChannelID code, EventDataType percent, MachineType mt, QDate start, QDate end)
|
||||
{
|
||||
if (!start.isValid()) start=LastDay(mt);
|
||||
@ -713,11 +805,38 @@ EventDataType Profile::calcPercentile(ChannelID code, EventDataType percent, Mac
|
||||
|
||||
QDate date=start;
|
||||
|
||||
// This is one messy function.. It requires all data to be loaded.. :(
|
||||
|
||||
QVector<EventDataType> array;
|
||||
//double val=0,tmp,hours=0;
|
||||
do {
|
||||
Day * day=GetDay(date,mt);
|
||||
if (day) {
|
||||
bool open=day->eventsLoaded();
|
||||
if (!open)
|
||||
day->OpenEvents();
|
||||
for (int i=0;i<day->size();i++) {
|
||||
for (QVector<Session *>::iterator s=day->begin();s!=day->end();s++) {
|
||||
QHash<ChannelID,QVector<EventList *> >::iterator el=(*s)->eventlist.find(code);
|
||||
if (el==(*s)->eventlist.end()) continue;
|
||||
for (int j=0;j<el.value().size();j++) {
|
||||
EventList & e=*el.value()[j];
|
||||
for (unsigned k=0;k<e.count();k++) {
|
||||
array.push_back(e.data(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if (!open)
|
||||
//day->CloseEvents();
|
||||
}
|
||||
date=date.addDays(1);
|
||||
} while (date<end);
|
||||
return 0;
|
||||
qSort(array);
|
||||
int idx=array.size()*percent;
|
||||
if (idx>array.size()-1) idx=array.size()-1;
|
||||
return array[idx];
|
||||
}
|
||||
|
||||
QDate Profile::FirstDay(MachineType mt)
|
||||
|
@ -85,7 +85,7 @@ public:
|
||||
Day * GetDay(QDate date,MachineType type=MT_UNKNOWN);
|
||||
|
||||
//! \brief Returns a list of all machines of type t
|
||||
QList<Machine *> GetMachines(MachineType t);
|
||||
QList<Machine *> GetMachines(MachineType t=MT_UNKNOWN);
|
||||
|
||||
//! \brief Returns the machine of type t used on date, NULL if none..
|
||||
Machine * GetMachine(MachineType t,QDate date);
|
||||
@ -96,13 +96,19 @@ public:
|
||||
//! \brief Returns true if this profile stores this variable identified by key
|
||||
bool contains(QString key) { return p_preferences.contains(key); }
|
||||
|
||||
int countDays(MachineType mt=MT_UNKNOWN, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcCount(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
double calcSum(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcHours(MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcAvg(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcWavg(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcMin(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcMax(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcPercentile(ChannelID code, EventDataType percent, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
|
||||
EventDataType calcSettingsMin(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
EventDataType calcSettingsMax(ChannelID code, MachineType mt=MT_CPAP, QDate start=QDate(), QDate end=QDate());
|
||||
|
||||
virtual void ExtraLoad(QDomElement & root);
|
||||
virtual QDomElement ExtraSave(QDomDocument & doc);
|
||||
|
||||
|
@ -204,6 +204,8 @@ public:
|
||||
bool IsLoneSession() { return s_lonesession; }
|
||||
void SetLoneSession(bool b) { s_lonesession=b; }
|
||||
|
||||
bool eventsLoaded() { return s_events_loaded; }
|
||||
|
||||
//! \brief Sets the event file linked to the summary (during load, for ondemand loading)
|
||||
void SetEventFile(QString & filename) { s_eventfile=filename; }
|
||||
|
||||
|
70
daily.cpp
70
daily.cpp
@ -237,8 +237,12 @@ Daily::Daily(QWidget *parent,gGraphView * shared)
|
||||
//INTPULSE->AddLayer(AddCPAP(new gLineChart(OXI_Pulse,Qt::red,square)));
|
||||
//INTSPO2->AddLayer(AddCPAP(new gLineChart(OXI_SPO2,Qt::blue,square)));
|
||||
|
||||
PULSE->AddLayer(AddOXI(new gLineOverlayBar(OXI_PulseChange,QColor("light gray"),tr("PD"),FT_Span)));
|
||||
SPO2->AddLayer(AddOXI(new gLineOverlayBar(OXI_SPO2Drop,QColor("light blue"),tr("O2"),FT_Span)));
|
||||
gLineOverlaySummary *los1=new gLineOverlaySummary(tr("Events/hour"),5,-4);
|
||||
gLineOverlaySummary *los2=new gLineOverlaySummary(tr("Events/hour"),5,-4);
|
||||
PULSE->AddLayer(AddOXI(los1->add(new gLineOverlayBar(OXI_PulseChange,QColor("light gray"),tr("PD"),FT_Span))));
|
||||
PULSE->AddLayer(AddOXI(los1));
|
||||
SPO2->AddLayer(AddOXI(los2->add(new gLineOverlayBar(OXI_SPO2Drop,QColor("light blue"),tr("O2"),FT_Span))));
|
||||
SPO2->AddLayer(AddOXI(los2));
|
||||
|
||||
PULSE->AddLayer(AddOXI(new gLineChart(OXI_Pulse,Qt::red,square)));
|
||||
SPO2->AddLayer(AddOXI(new gLineChart(OXI_SPO2,Qt::blue,true)));
|
||||
@ -656,10 +660,15 @@ void Daily::Load(QDate date)
|
||||
CPAPMode mode=MODE_UNKNOWN;
|
||||
QString a;
|
||||
bool isBrick=false;
|
||||
if (graphsAvailable==0) {
|
||||
|
||||
if (graphsAvailable>0) {
|
||||
GraphView->setCubeImage(images["sheep"]);
|
||||
GraphView->setEmptyText(tr("Graphs Switched Off"));
|
||||
} else {
|
||||
GraphView->setCubeImage(images["nodata"]);
|
||||
GraphView->setEmptyText(tr("No Data"));
|
||||
}
|
||||
GraphView->setCubeImage(images["nodata"]);
|
||||
|
||||
if (cpap) {
|
||||
if (GraphView->isEmpty()) {
|
||||
GraphView->setCubeImage(images["brick"]);
|
||||
@ -667,9 +676,7 @@ void Daily::Load(QDate date)
|
||||
|
||||
isBrick=true;
|
||||
} else {
|
||||
if (graphsAvailable>0) {
|
||||
GraphView->setEmptyText(tr("Graphs Switched Off"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mode=(CPAPMode)(int)cpap->settings_max(CPAP_Mode);
|
||||
@ -783,7 +790,26 @@ void Daily::Load(QDate date)
|
||||
}
|
||||
}
|
||||
|
||||
html+="</table>";
|
||||
|
||||
} else { // machine is a brick
|
||||
html+="<tr><td colspan='5' align='center'><b><h2>"+tr("BRICK :(")+"</h2></b></td></tr>";
|
||||
html+="<tr><td colspan='5' align='center'><i>"+tr("Sorry, your machine does not record data.")+"</i></td></tr>\n";
|
||||
html+="<tr><td colspan='5' align='center'><i>"+tr("Complain to your Equipment Provider!")+"</i></td></tr>\n";
|
||||
html+="<tr><td colspan='5'> </td></tr>\n";
|
||||
}
|
||||
html+="</table>";
|
||||
|
||||
} // if (!CPAP)
|
||||
if (!cpap && oxi) {
|
||||
html+="<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
|
||||
html+="<tr><td colspan=4 align=center><b>"+oxi->machine->properties[STR_PROP_Brand]+"</b> <br>"+oxi->machine->properties[STR_PROP_Model]+"</td></tr>\n";
|
||||
html+="<tr><td colspan=4 align=center> </td></tr>";
|
||||
html+=QString("<tr><td colspan=4 align=center>SpO2 Baseline Used: %1\%</td></tr>").arg(oxi->settings_wavg(OXI_SPO2Drop));
|
||||
html+=QString("<tr><td colspan=4 align=center>SpO2 Desaturations: %1 (%2)\%</td></tr>").arg(oxi->count(OXI_SPO2Drop)).arg((100.0/oxi->hours()) * (oxi->sum(OXI_SPO2Drop)/3600.0));
|
||||
html+=QString("<tr><td colspan=4 align=center>Pulse Change events: %1 (%2)\%</td></tr>").arg(oxi->count(OXI_PulseChange)).arg((100.0/oxi->hours()) * (oxi->sum(OXI_PulseChange)/3600.0));
|
||||
html+="</table>";
|
||||
}
|
||||
if ((cpap && !isBrick) || oxi) {
|
||||
html+="<table cellspacing=0 cellpadding=0 border=0 width='100%'>\n";
|
||||
if (cpap || oxi) {
|
||||
html+="<tr height='2'><td colspan=5 height='2'><hr></td></tr>\n";
|
||||
@ -824,12 +850,7 @@ void Daily::Load(QDate date)
|
||||
html+="</td><tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
html+="<tr><td colspan='5' align='center'><b><h2>"+tr("BRICK :(")+"</h2></b></td></tr>";
|
||||
html+="<tr><td colspan='5' align='center'><i>"+tr("Sorry, your machine does not record data.")+"</i></td></tr>\n";
|
||||
html+="<tr><td colspan='5' align='center'><i>"+tr("Complain to your Equipment Provider!")+"</i></td></tr>\n";
|
||||
html+="<tr><td colspan='5'> </td></tr>\n";
|
||||
|
||||
}
|
||||
} else {
|
||||
html+="<tr><td colspan=5 align=center><i>"+tr("No data available")+"</i></td></tr>";
|
||||
@ -892,15 +913,22 @@ void Daily::Load(QDate date)
|
||||
|
||||
}
|
||||
html+="</table><hr height=2>";
|
||||
}
|
||||
|
||||
{
|
||||
//}
|
||||
html+="<table cellpadding=0 cellspacing=0 border=0 width=100%>";
|
||||
QDateTime fd,ld;
|
||||
bool corrupted_waveform=false;
|
||||
QString tooltip;
|
||||
if (cpap || oxi)
|
||||
html+=QString("<tr><td align=left><b>%1</b></td><td align=center><b>%2</b></td><td align=center><b>%3</b></td><td align=center><b>%4</b></td></tr>")
|
||||
.arg(tr("SessionID"))
|
||||
.arg(tr("Date"))
|
||||
.arg(tr("Start"))
|
||||
.arg(tr("End"));
|
||||
if (cpap) {
|
||||
html+="<tr><td align=left><b>"+tr("SessionID")+"</b></td><td align=center><b>"+tr("Date")+"</b></td><td align=center><b>"+tr("Start")+"</b></td><td align=center><b>"+tr("End")+"</b></td></tr>";
|
||||
html+="<tr><td align=left colspan=4><i>"+tr("CPAP Sessions")+"</i></td></tr>";
|
||||
html+=QString("<tr><td align=left colspan=4><i>%1</i></td></tr>").arg(tr("CPAP Sessions"));
|
||||
for (QVector<Session *>::iterator s=cpap->begin();s!=cpap->end();s++) {
|
||||
fd=QDateTime::fromTime_t((*s)->first()/1000L);
|
||||
ld=QDateTime::fromTime_t((*s)->last()/1000L);
|
||||
@ -919,7 +947,7 @@ void Daily::Load(QDate date)
|
||||
//if (oxi) html+="<tr><td colspan=4><hr></td></tr>";
|
||||
}
|
||||
if (oxi) {
|
||||
html+="<tr><td align=left colspan=4><i>"+tr("Oximetry Sessions")+"</i></td></tr>";
|
||||
html+=QString("<tr><td align=left colspan=4><i>%1</i></td></tr>").arg(tr("Oximetry Sessions"));
|
||||
for (QVector<Session *>::iterator s=oxi->begin();s!=oxi->end();s++) {
|
||||
fd=QDateTime::fromTime_t((*s)->first()/1000L);
|
||||
ld=QDateTime::fromTime_t((*s)->last()/1000L);
|
||||
@ -1039,12 +1067,10 @@ void Daily::Load(QDate date)
|
||||
ui->bookmarkTable->setItem(i,1,tw);
|
||||
tw->setData(Qt::UserRole,st);
|
||||
tw->setData(Qt::UserRole+1,et);
|
||||
}
|
||||
} // for (int i
|
||||
ui->bookmarkTable->blockSignals(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // if (journal->settings.contains(Bookmark_Start))
|
||||
} // if (journal)
|
||||
}
|
||||
|
||||
void Daily::UnitsChanged()
|
||||
|
534
mainwindow.cpp
534
mainwindow.cpp
@ -381,7 +381,7 @@ EventDataType calcAHI(QDate start, QDate end)
|
||||
|
||||
struct RXChange
|
||||
{
|
||||
RXChange() {}
|
||||
RXChange() { highlight=0; }
|
||||
RXChange(const RXChange & copy) {
|
||||
first=copy.first;
|
||||
last=copy.last;
|
||||
@ -389,8 +389,10 @@ struct RXChange
|
||||
ahi=copy.ahi;
|
||||
mode=copy.mode;
|
||||
min=copy.min;
|
||||
max=copy.min;
|
||||
max=copy.max;
|
||||
p90=copy.p90;
|
||||
highlight=copy.highlight;
|
||||
weighted=copy.weighted;
|
||||
}
|
||||
QDate first;
|
||||
QDate last;
|
||||
@ -400,11 +402,74 @@ struct RXChange
|
||||
EventDataType min;
|
||||
EventDataType max;
|
||||
EventDataType p90;
|
||||
EventDataType weighted;
|
||||
short highlight;
|
||||
};
|
||||
|
||||
enum RXSortMode { RX_first, RX_last, RX_days, RX_ahi, RX_mode, RX_min, RX_max, RX_p90, RX_weighted };
|
||||
RXSortMode RXsort=RX_first;
|
||||
bool RXorder=false;
|
||||
|
||||
bool operator<(const RXChange & comp1, const RXChange & comp2) {
|
||||
return comp1.ahi < comp2.ahi;
|
||||
if (RXorder) {
|
||||
switch (RXsort) {
|
||||
case RX_ahi: return comp1.ahi < comp2.ahi;
|
||||
case RX_days: return comp1.days < comp2.days;
|
||||
case RX_first: return comp1.first < comp2.first;
|
||||
case RX_last: return comp1.last < comp2.last;
|
||||
case RX_mode: return comp1.mode < comp2.mode;
|
||||
case RX_min: return comp1.min < comp2.min;
|
||||
case RX_max: return comp1.max < comp2.max;
|
||||
case RX_p90: return comp1.p90 < comp2.p90;
|
||||
case RX_weighted: return comp1.weighted < comp2.weighted;
|
||||
};
|
||||
} else {
|
||||
switch (RXsort) {
|
||||
case RX_ahi: return comp1.ahi > comp2.ahi;
|
||||
case RX_days: return comp1.days > comp2.days;
|
||||
case RX_first: return comp1.first > comp2.first;
|
||||
case RX_last: return comp1.last > comp2.last;
|
||||
case RX_mode: return comp1.mode > comp2.mode;
|
||||
case RX_min: return comp1.min > comp2.min;
|
||||
case RX_max: return comp1.max > comp2.max;
|
||||
case RX_p90: return comp1.p90 > comp2.p90;
|
||||
case RX_weighted: return comp1.weighted > comp2.weighted;
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool RXSort(const RXChange * comp1, const RXChange * comp2) {
|
||||
if (RXorder) {
|
||||
switch (RXsort) {
|
||||
case RX_ahi: return comp1->ahi < comp2->ahi;
|
||||
case RX_days: return comp1->days < comp2->days;
|
||||
case RX_first: return comp1->first < comp2->first;
|
||||
case RX_last: return comp1->last < comp2->last;
|
||||
case RX_mode: return comp1->mode < comp2->mode;
|
||||
case RX_min: return comp1->min < comp2->min;
|
||||
case RX_max: return comp1->max < comp2->max;
|
||||
case RX_p90: return comp1->p90 < comp2->p90;
|
||||
case RX_weighted: return comp1->weighted < comp2->weighted;
|
||||
};
|
||||
} else {
|
||||
switch (RXsort) {
|
||||
case RX_ahi: return comp1->ahi > comp2->ahi;
|
||||
case RX_days: return comp1->days > comp2->days;
|
||||
case RX_first: return comp1->first > comp2->first;
|
||||
case RX_last: return comp1->last > comp2->last;
|
||||
case RX_mode: return comp1->mode > comp2->mode;
|
||||
case RX_min: return comp1->min > comp2->min;
|
||||
case RX_max: return comp1->max > comp2->max;
|
||||
case RX_p90: return comp1->p90 > comp2->p90;
|
||||
case RX_weighted: return comp1->weighted > comp2->weighted;
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//bool operator<(const RXChange * & comp1, const RXChange * & comp2) {
|
||||
|
||||
//}
|
||||
|
||||
void MainWindow::on_homeButton_clicked()
|
||||
{
|
||||
@ -425,60 +490,159 @@ void MainWindow::on_homeButton_clicked()
|
||||
int cpapmonthdays=cpapmonth.daysTo(lastcpap);
|
||||
int cpapyeardays=cpapyear.daysTo(lastcpap);
|
||||
int cpap6monthdays=cpap6month.daysTo(lastcpap);
|
||||
QList<Machine *> mach=PROFILE.GetMachines(MT_CPAP);
|
||||
QList<Machine *> cpap_machines=PROFILE.GetMachines(MT_CPAP);
|
||||
QList<Machine *> oximeters=PROFILE.GetMachines(MT_OXIMETER);
|
||||
QList<Machine *> mach;
|
||||
mach.append(cpap_machines);
|
||||
mach.append(oximeters);
|
||||
|
||||
int days=firstcpap.daysTo(lastcpap);
|
||||
if (days==0) {
|
||||
int cpapdays=PROFILE.countDays(MT_CPAP,firstcpap,lastcpap);
|
||||
if (mach.size()==0) {
|
||||
html+="<p>No Machine Data Imported</p>";
|
||||
} else {
|
||||
html+=QString("<p>%1 day%2 of CPAP Data</p>").arg(days).arg(days>1 ? "s" : "");
|
||||
|
||||
html+=QString("<b>Summary Information as of %1</b>").arg(lastcpap.toString(Qt::SystemLocaleLongDate));
|
||||
html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=100%>"
|
||||
"<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
|
||||
.arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("AHI"))
|
||||
.arg(calcAHI(lastcpap,lastcpap),0,'f',2)
|
||||
.arg(calcAHI(cpapweek,lastcpap),0,'f',2)
|
||||
.arg(calcAHI(cpapmonth,lastcpap),0,'f',2)
|
||||
.arg(calcAHI(cpap6month,lastcpap),0,'f',2)
|
||||
.arg(calcAHI(cpapyear,lastcpap),0,'f',2);
|
||||
html+="<tr><td colspan=6>Note, these are different to overview calcs.. Overview shows a simple average AHI, this shows combined counts divide by combined hours</td></tr>";
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Usage (Average)"))
|
||||
.arg(p_profile->calcHours(MT_CPAP),0,'f',2)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapweek,lastcpap)/float(cpapweekdays),0,'f',2)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapmonth,lastcpap)/float(cpapmonthdays),0,'f',2)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpap6month,lastcpap)/float(cpap6monthdays),0,'f',2)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapyear,lastcpap)/float(cpapyeardays),0,'f',2);
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average Pressure"))
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP))
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',3);
|
||||
html+="<tr><td colspan=6>TODO: 90% pressure.. Any point showing if this is all CPAP?</td></tr>";
|
||||
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average Leaks"))
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP))
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapweek,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpap6month,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapyear,lastcpap),0,'f',3);
|
||||
html+="<tr><td colspan=6>What about median leak values? 90% Leaks?</td></tr>";
|
||||
html+=QString("<p><b>Summary Information as of %1</b></p>").arg(lastcpap.toString(Qt::SystemLocaleLongDate));
|
||||
html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=100%>");
|
||||
|
||||
if (cpap_machines.size()>0) {
|
||||
html+=QString("<tr><td colspan=6><b>%1</b></td></tr>").arg(tr("CPAP Summary"));
|
||||
|
||||
if (!cpapdays) {
|
||||
html+=QString("<tr><td colspan=6><b>%1</b></td></tr>").arg(tr("No CPAP data available."));
|
||||
} else if (cpapdays==1) {
|
||||
html+=QString("<tr><td colspan=6>%1</td></tr>").arg(QString(tr("%1 day of CPAP Data, on %2.")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)));
|
||||
} else {
|
||||
html+=QString("<tr><td colspan=6>%1</td></tr>").arg(QString(tr("%1 days of CPAP Data, between %2 and %3")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)).arg(lastcpap.toString(Qt::SystemLocaleShortDate)));
|
||||
}
|
||||
|
||||
html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
|
||||
.arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("AHI"))
|
||||
.arg(calcAHI(lastcpap,lastcpap),0,'f',3)
|
||||
.arg(calcAHI(cpapweek,lastcpap),0,'f',3)
|
||||
.arg(calcAHI(cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(calcAHI(cpap6month,lastcpap),0,'f',3)
|
||||
.arg(calcAHI(cpapyear,lastcpap),0,'f',3);
|
||||
html+="<tr><td colspan=6>Note, these are different to overview calcs.. Overview shows a simple average AHI, this shows combined counts divide by combined hours</td></tr>";
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Usage (Average)"))
|
||||
.arg(p_profile->calcHours(MT_CPAP),0,'f',3)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapweek,lastcpap)/float(cpapweekdays),0,'f',3)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapmonth,lastcpap)/float(cpapmonthdays),0,'f',3)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpap6month,lastcpap)/float(cpap6monthdays),0,'f',3)
|
||||
.arg(p_profile->calcHours(MT_CPAP,cpapyear,lastcpap)/float(cpapyeardays),0,'f',3);
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average Pressure"))
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',3);
|
||||
|
||||
if (p_profile->calcSettingsMax(CPAP_Mode,MT_CPAP,firstcpap,lastcpap)>MODE_CPAP) {
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("90% Pressure"))
|
||||
.arg(p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP),0,'f',3)
|
||||
.arg(p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,cpapweek,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,cpap6month,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,cpapyear,lastcpap),0,'f',3);
|
||||
}
|
||||
//html+="<tr><td colspan=6>TODO: 90% pressure.. Any point showing if this is all CPAP?</td></tr>";
|
||||
|
||||
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average Leaks"))
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapweek,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapmonth,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpap6month,lastcpap),0,'f',3)
|
||||
.arg(p_profile->calcWavg(CPAP_Leak,MT_CPAP,cpapyear,lastcpap),0,'f',3);
|
||||
html+="<tr><td colspan=6>What about median leak values? 90% Leaks?</td></tr>";
|
||||
}
|
||||
if (oximeters.size()>0) {
|
||||
QDate lastoxi=p_profile->LastDay(MT_OXIMETER);
|
||||
QDate firstoxi=p_profile->FirstDay(MT_OXIMETER);
|
||||
html+=QString("<tr><td colspan=6><b>%1</b></td></tr>").arg(tr("Oximetry Summary"));
|
||||
int days=PROFILE.countDays(MT_OXIMETER,firstcpap,lastcpap);
|
||||
if (!days) {
|
||||
html+=QString("<tr><td colspan=6><b>%1</b></td></tr>").arg(tr("No Oximetry data available."));
|
||||
} else if (days==1) {
|
||||
html+=QString("<tr><td colspan=6>%1</td></tr>").arg(QString(tr("%1 day of Oximetry Data, on %2.")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)));
|
||||
} else {
|
||||
html+=QString("<tr><td colspan=6>%1</td></tr>").arg(QString(tr("%1 days of Oximetry Data, between %2 and %3")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)).arg(lastoxi.toString(Qt::SystemLocaleShortDate)));
|
||||
}
|
||||
|
||||
html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
|
||||
.arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
|
||||
QDate oxiweek=lastcpap.addDays(-7);
|
||||
QDate oximonth=lastcpap.addDays(-30);
|
||||
QDate oxi6month=lastcpap.addMonths(-6);
|
||||
QDate oxiyear=lastcpap.addYears(-12);
|
||||
if (oxiweek<firstoxi) oxiweek=firstoxi;
|
||||
if (oximonth<firstoxi) oximonth=firstoxi;
|
||||
if (oxi6month<firstoxi) oxi6month=firstoxi;
|
||||
if (oxiyear<firstoxi) oxiyear=firstoxi;
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average SpO2"))
|
||||
.arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Minimum SpO2"))
|
||||
.arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("SpO2 Events / Hour"))
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("SpO2 Events / Hour"))
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Average Pulse Rate"))
|
||||
.arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Minimum Pulse Rate"))
|
||||
.arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
|
||||
.arg(tr("Maximum Pulse Rate"))
|
||||
.arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER),0,'f',3)
|
||||
.arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',3)
|
||||
.arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',3);
|
||||
}
|
||||
|
||||
html+="</table>";
|
||||
html+=QString("<br/><b>Changes to Prescription Settings</b>");
|
||||
html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=100%>");
|
||||
html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</b></td><td><b>%7</b></td><td><b>%8</b></td></tr>")
|
||||
if (cpap_machines.size()>0) {
|
||||
html+=QString("<br/><b>Changes to Prescription Settings</b>");
|
||||
html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=100%>");
|
||||
html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</b></td><td><b>%7</b></td><td><b>%8</b></td></tr>")
|
||||
.arg(tr("First"))
|
||||
.arg(tr("Last"))
|
||||
.arg(tr("Days"))
|
||||
@ -488,78 +652,110 @@ void MainWindow::on_homeButton_clicked()
|
||||
.arg(tr("Max Pressure"))
|
||||
.arg(tr("90% Pressure"));
|
||||
|
||||
QDate first,last=lastcpap;
|
||||
CPAPMode mode,cmode=MODE_UNKNOWN;
|
||||
EventDataType cmin=0,cmax=0,min,max;
|
||||
QDate date=lastcpap;
|
||||
Day * day;
|
||||
bool lastchanged;
|
||||
int cnt=0;
|
||||
QList<RXChange> rxchange;
|
||||
QDate first,last=lastcpap;
|
||||
CPAPMode mode,cmode=MODE_UNKNOWN;
|
||||
EventDataType cmin=0,cmax=0,min,max;
|
||||
QDate date=lastcpap;
|
||||
Day * day;
|
||||
bool lastchanged;
|
||||
int cnt=0;
|
||||
QVector<RXChange> rxchange;
|
||||
|
||||
do {
|
||||
day=PROFILE.GetDay(date,MT_CPAP);
|
||||
lastchanged=false;
|
||||
if (day) {
|
||||
mode=(CPAPMode)round(day->settings_wavg(CPAP_Mode));
|
||||
min=day->settings_min(CPAP_PressureMin);
|
||||
if (mode==MODE_CPAP) {
|
||||
max=day->settings_max(CPAP_PressureMin);
|
||||
} else max=day->settings_max(CPAP_PressureMax);
|
||||
do {
|
||||
day=PROFILE.GetDay(date,MT_CPAP);
|
||||
lastchanged=false;
|
||||
if (day) {
|
||||
mode=(CPAPMode)round(day->settings_wavg(CPAP_Mode));
|
||||
min=day->settings_min(CPAP_PressureMin);
|
||||
if (mode==MODE_CPAP) {
|
||||
max=day->settings_max(CPAP_PressureMin);
|
||||
} else max=day->settings_max(CPAP_PressureMax);
|
||||
|
||||
if ((mode!=cmode) || (min!=cmin) || (max!=cmax)) {
|
||||
if (cmode!=MODE_UNKNOWN) {
|
||||
first=date.addDays(1);
|
||||
RXChange rx;
|
||||
rx.first=first;
|
||||
rx.last=last;
|
||||
rx.days=first.daysTo(last)+1;
|
||||
rx.ahi=calcAHI(first,last);
|
||||
rx.mode=cmode;
|
||||
rx.min=cmin;
|
||||
rx.max=cmax;
|
||||
rx.p90=0;
|
||||
rxchange.push_back(rx);
|
||||
if ((mode!=cmode) || (min!=cmin) || (max!=cmax)) {
|
||||
if (cmode!=MODE_UNKNOWN) {
|
||||
first=date.addDays(1);
|
||||
int days=PROFILE.countDays(MT_CPAP,first,last);
|
||||
RXChange rx;
|
||||
rx.first=first;
|
||||
rx.last=last;
|
||||
rx.days=days;
|
||||
rx.ahi=calcAHI(first,last);
|
||||
rx.mode=cmode;
|
||||
rx.min=cmin;
|
||||
rx.max=cmax;
|
||||
rx.p90=p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,first,last);
|
||||
rx.weighted=float(rx.days)/float(cpapdays)*rx.ahi;
|
||||
rxchange.push_back(rx);
|
||||
}
|
||||
cmode=mode;
|
||||
cmin=min;
|
||||
cmax=max;
|
||||
last=date;
|
||||
lastchanged=true;
|
||||
}
|
||||
cmode=mode;
|
||||
cmin=min;
|
||||
cmax=max;
|
||||
last=date;
|
||||
lastchanged=true;
|
||||
|
||||
}
|
||||
|
||||
date=date.addDays(-1);
|
||||
} while (date>=firstcpap);
|
||||
if (!lastchanged) {
|
||||
last=date.addDays(1);
|
||||
first=firstcpap;
|
||||
int days=PROFILE.countDays(MT_CPAP,first,last);
|
||||
RXChange rx;
|
||||
rx.first=first;
|
||||
rx.last=last;
|
||||
rx.days=days;
|
||||
rx.ahi=calcAHI(first,last);
|
||||
rx.mode=mode;
|
||||
rx.min=min;
|
||||
rx.max=max;
|
||||
rx.p90=p_profile->calcPercentile(CPAP_Pressure,0.9,MT_CPAP,first,last);
|
||||
rx.weighted=float(rx.days)/float(cpapdays);
|
||||
//rx.weighted=float(days)*rx.ahi;
|
||||
rxchange.push_back(rx);
|
||||
}
|
||||
date=date.addDays(-1);
|
||||
} while (date>=firstcpap);
|
||||
if (!lastchanged) {
|
||||
last=date.addDays(1);
|
||||
first=firstcpap;
|
||||
RXChange rx;
|
||||
rx.first=first;
|
||||
rx.last=last;
|
||||
rx.days=first.daysTo(last)+1;
|
||||
rx.ahi=calcAHI(first,last);
|
||||
rx.mode=mode;
|
||||
rx.min=min;
|
||||
rx.max=max;
|
||||
rx.p90=0;
|
||||
rxchange.push_back(rx);
|
||||
}
|
||||
qSort(rxchange);
|
||||
for (int i=0;i<rxchange.size();i++) {
|
||||
RXChange rx=rxchange.at(i);
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td><td>%7</td><td>%8</td></tr>")
|
||||
.arg(rx.first.toString(Qt::SystemLocaleShortDate))
|
||||
.arg(rx.last.toString(Qt::SystemLocaleShortDate))
|
||||
.arg(rx.days)
|
||||
.arg(rx.ahi,0,'f',2)
|
||||
.arg(schema::channel[CPAP_Mode].option(int(rx.mode)-1))
|
||||
.arg(rx.min)
|
||||
.arg(rx.max)
|
||||
.arg(rx.p90);
|
||||
}
|
||||
QVector<RXChange *> tmpRX;
|
||||
for (int i=0;i<rxchange.size();i++) {
|
||||
RXChange & rx=rxchange[i];
|
||||
if (rx.days>1)
|
||||
tmpRX.push_back(&rx);
|
||||
}
|
||||
RXsort=RX_ahi;
|
||||
qSort(tmpRX.begin(),tmpRX.end(),RXSort);
|
||||
tmpRX[0]->highlight=4; // worst
|
||||
tmpRX[tmpRX.size()-1]->highlight=1; //best
|
||||
|
||||
html+="</table>";
|
||||
if (tmpRX.size()>4) {
|
||||
tmpRX[1]->highlight=3; // worst
|
||||
tmpRX[tmpRX.size()-2]->highlight=2; //best
|
||||
}
|
||||
//RXsort=RX_first;
|
||||
//qSort(rxchange);
|
||||
|
||||
for (int i=0;i<rxchange.size();i++) {
|
||||
RXChange rx=rxchange.at(i);
|
||||
QString color;
|
||||
if (rx.highlight==1) {
|
||||
color=" bgcolor='#80ff80'";
|
||||
} else if (rx.highlight==2) {
|
||||
color=" bgcolor='#d0ffd0'";
|
||||
} else if (rx.highlight==3) {
|
||||
color=" bgcolor='#ffd0d0'";
|
||||
} else if (rx.highlight==4) {
|
||||
color=" bgcolor='#ff8080'";
|
||||
} else color="";
|
||||
html+=QString("<tr"+color+"><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td><td>%7</td><td>%8</td></tr>")
|
||||
.arg(rx.first.toString(Qt::SystemLocaleShortDate))
|
||||
.arg(rx.last.toString(Qt::SystemLocaleShortDate))
|
||||
.arg(rx.days)
|
||||
.arg(rx.ahi,0,'f',2)
|
||||
.arg(schema::channel[CPAP_Mode].option(int(rx.mode)-1))
|
||||
.arg(rx.min)
|
||||
.arg(rx.max)
|
||||
.arg(rx.p90);
|
||||
}
|
||||
html+="</table>";
|
||||
}
|
||||
|
||||
}
|
||||
if (mach.size()>0) {
|
||||
@ -575,6 +771,7 @@ void MainWindow::on_homeButton_clicked()
|
||||
Machine *m;
|
||||
for (int i=0;i<mach.size();i++) {
|
||||
m=mach.at(i);
|
||||
if (m->GetType()==MT_JOURNAL) continue;
|
||||
QString mn=m->properties[STR_PROP_ModelNumber];
|
||||
//if (mn.isEmpty())
|
||||
html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td></tr>")
|
||||
@ -842,33 +1039,6 @@ void MainWindow::on_action_Frequently_Asked_Questions_triggered()
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
}
|
||||
|
||||
EventList *packEventList(EventList *ev)
|
||||
{
|
||||
EventList *nev=new EventList(EVL_Event);
|
||||
|
||||
EventDataType val,lastval=0;
|
||||
qint64 time,lasttime=0;//,lasttime2=0;
|
||||
|
||||
lastval=ev->data(0);
|
||||
lasttime=ev->time(0);
|
||||
nev->AddEvent(lasttime,lastval);
|
||||
|
||||
for (unsigned i=1;i<ev->count();i++) {
|
||||
val=ev->data(i);
|
||||
time=ev->time(i);
|
||||
if (val!=lastval) {
|
||||
nev->AddEvent(time,val);
|
||||
//lasttime2=time;
|
||||
}
|
||||
lastval=val;
|
||||
lasttime=time;
|
||||
}
|
||||
if (val==lastval) {
|
||||
nev->AddEvent(lasttime,val);
|
||||
}
|
||||
return nev;
|
||||
}
|
||||
|
||||
void MainWindow::PrintReport(gGraphView *gv,QString name, QDate date)
|
||||
{
|
||||
if (!gv) return;
|
||||
@ -1118,7 +1288,7 @@ void MainWindow::PrintReport(gGraphView *gv,QString name, QDate date)
|
||||
gGraph *g=(*gv)[i];
|
||||
if (g->isEmpty()) continue;
|
||||
if (!g->visible()) continue;
|
||||
if (print_bookmarks && (g->title()==tr("Flow Rate"))) {
|
||||
if (print_bookmarks) {
|
||||
normal=false;
|
||||
start.push_back(st);
|
||||
end.push_back(et);
|
||||
@ -1130,10 +1300,12 @@ void MainWindow::PrintReport(gGraphView *gv,QString name, QDate date)
|
||||
QVariantList et1=journal->settings[Bookmark_End].toList();
|
||||
QStringList notes=journal->settings[Bookmark_Notes].toStringList();
|
||||
for (int i=0;i<notes.size();i++) {
|
||||
labels.push_back(notes.at(i));
|
||||
start.push_back(st1.at(i).toLongLong());
|
||||
end.push_back(et1.at(i).toLongLong());
|
||||
graphs.push_back(g);
|
||||
if ((g->title()==tr("Flow Rate")) || (g->title()==tr("SpO2")) || (g->title()==tr("Pulse"))) {
|
||||
labels.push_back(notes.at(i));
|
||||
start.push_back(st1.at(i).toLongLong());
|
||||
end.push_back(et1.at(i).toLongLong());
|
||||
graphs.push_back(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1223,6 +1395,59 @@ void MainWindow::PrintReport(gGraphView *gv,QString name, QDate date)
|
||||
Notify("SleepyHead has finished sending the job to the printer.");
|
||||
}
|
||||
|
||||
void packEventList(EventList *el, EventDataType minval=0)
|
||||
{
|
||||
if (el->count()<2) return;
|
||||
EventList nel(EVL_Waveform);
|
||||
EventDataType t,lastt=0; //el->data(0);
|
||||
qint64 ti;//=el->time(0);
|
||||
//nel.AddEvent(ti,lastt);
|
||||
bool f;
|
||||
qint64 lasttime=0;
|
||||
EventDataType min=999,max=0;
|
||||
for (quint32 i=0;i<el->count();i++) {
|
||||
t=el->data(i);
|
||||
ti=el->time(i);
|
||||
f=false;
|
||||
if (t>minval) {
|
||||
if (t!=lastt) {
|
||||
if (!lasttime) {
|
||||
nel.setFirst(ti);
|
||||
}
|
||||
nel.AddEvent(ti,t);
|
||||
if (t < min) min=t;
|
||||
if (t > max) max=t;
|
||||
lasttime=ti;
|
||||
f=true;
|
||||
}
|
||||
} else {
|
||||
if (lastt>minval) {
|
||||
nel.AddEvent(ti,lastt);
|
||||
lasttime=ti;
|
||||
f=true;
|
||||
}
|
||||
}
|
||||
lastt=t;
|
||||
}
|
||||
if (!f) {
|
||||
if (t>minval) {
|
||||
nel.AddEvent(ti,t);
|
||||
lasttime=ti;
|
||||
}
|
||||
}
|
||||
el->setFirst(nel.first());
|
||||
el->setLast(nel.last());
|
||||
el->setMin(min);
|
||||
el->setMax(max);
|
||||
|
||||
el->getData().clear();
|
||||
el->getTime().clear();
|
||||
el->setCount(nel.count());
|
||||
|
||||
el->getData()=nel.getData();
|
||||
el->getTime()=nel.getTime();
|
||||
}
|
||||
|
||||
void MainWindow::on_action_Rebuild_Oximetry_Index_triggered()
|
||||
{
|
||||
QVector<ChannelID> valid;
|
||||
@ -1236,6 +1461,8 @@ void MainWindow::on_action_Rebuild_Oximetry_Index_triggered()
|
||||
|
||||
QList<Machine *> machines=PROFILE.GetMachines(MT_OXIMETER);
|
||||
|
||||
qint64 f=0,l=0;
|
||||
|
||||
int discard_threshold=PROFILE.oxi->oxiDiscardThreshold();
|
||||
Machine *m;
|
||||
for (int z=0;z<machines.size();z++) {
|
||||
@ -1263,20 +1490,27 @@ void MainWindow::on_action_Rebuild_Oximetry_Index_triggered()
|
||||
}
|
||||
}
|
||||
for (int i=0;i<newlist.size();i++) {
|
||||
EventList *nev=packEventList(newlist[i]);
|
||||
packEventList(newlist[i],8);
|
||||
|
||||
/*EventList *nev=packEventList(newlist[i]);
|
||||
if (nev->count()!=e.value()[i]->count() ) {
|
||||
delete newlist[i];
|
||||
newlist[i]=nev;
|
||||
} else {
|
||||
delete nev;
|
||||
}
|
||||
}*/
|
||||
EventList * el=newlist[i];
|
||||
if (!f || f > el->first()) f=el->first();
|
||||
if (!l || l < el->last()) l=el->last();
|
||||
}
|
||||
e.value()=newlist;
|
||||
e.value()=newlist;
|
||||
}
|
||||
}
|
||||
for (int i=0;i<invalid.size();i++) {
|
||||
sess->eventlist.erase(sess->eventlist.find(invalid[i]));
|
||||
}
|
||||
if (f) sess->really_set_first(f);
|
||||
if (l) sess->really_set_last(l);
|
||||
sess->m_cnt.clear();
|
||||
sess->m_sum.clear();
|
||||
sess->m_min.clear();
|
||||
|
17
oximetry.cpp
17
oximetry.cpp
@ -600,6 +600,7 @@ void CMS50Serial::ReadyRead()
|
||||
}
|
||||
}
|
||||
lastbytesize=size;
|
||||
bool fixtime=false;
|
||||
|
||||
while (i<bytes.size()) {
|
||||
if (import_mode) {
|
||||
@ -620,16 +621,16 @@ void CMS50Serial::ReadyRead()
|
||||
// otherwise pick the first session of the last days data..
|
||||
Day *day=PROFILE.GetDay(PROFILE.LastDay(),MT_CPAP);
|
||||
QDateTime d;
|
||||
|
||||
fixtime=true;
|
||||
if (day) {
|
||||
int ti=day->first()/1000L;
|
||||
|
||||
d=QDateTime::fromTime_t(ti);
|
||||
qDebug() << "Guessing session starting from CPAP data" << d;
|
||||
} else {
|
||||
qDebug() << "Can't guess start time, defaulting to 6pm yesterday" << d;
|
||||
qDebug() << "Can't guess start time, defaulting to ending at 7:30am this morning" << d;
|
||||
d=QDateTime::currentDateTime();
|
||||
d.setTime(QTime(18,0,0));
|
||||
d.setTime(QTime(7,30,0));
|
||||
//d.addDays(-1);
|
||||
}
|
||||
f2time.push_back(d);
|
||||
@ -647,6 +648,16 @@ void CMS50Serial::ReadyRead()
|
||||
datasize=(((unsigned char)bytes.at(i) & 0x3f) << 14) | (((unsigned char)bytes.at(i+1)&0x7f) << 7) | ((unsigned char)bytes.at(i+2) & 0x7f);
|
||||
received_bytes=0;
|
||||
qDebug() << "Data Size=" << datasize << "??";
|
||||
if (fixtime) {
|
||||
QDateTime time;
|
||||
for (int i=0;i<f2time.size();i++) {
|
||||
time=f2time.at(i);
|
||||
time.addSecs(-(datasize/3));
|
||||
break;
|
||||
}
|
||||
f2time.clear();
|
||||
f2time.push_back(time);
|
||||
}
|
||||
done_import=false;
|
||||
|
||||
i+=3;
|
||||
|
@ -88,7 +88,7 @@ public:
|
||||
void compactToWaveform(EventList *el);
|
||||
|
||||
//! \brief Packs EventList to time delta format, also pruning zeros.
|
||||
void compactToEvent(EventList *el);
|
||||
static void compactToEvent(EventList *el);
|
||||
|
||||
//! \brief Packs SPO2 & Pulse to Events, and Plethy to Waveform EventList types.
|
||||
void compactAll();
|
||||
|
Loading…
Reference in New Issue
Block a user