/*! qso number sent for last qso in log */ int Log::lastNr() const { QSqlQueryModel m; #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log where valid='true'", db); #else m.setQuery("SELECT * FROM log where valid=1", db); #endif while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount()) { QByteArray snt[MAX_EXCH_FIELDS]; snt[0] = m.record(m.rowCount() - 1).value(SQL_COL_SNT1).toByteArray(); snt[1] = m.record(m.rowCount() - 1).value(SQL_COL_SNT2).toByteArray(); snt[2] = m.record(m.rowCount() - 1).value(SQL_COL_SNT3).toByteArray(); snt[3] = m.record(m.rowCount() - 1).value(SQL_COL_SNT4).toByteArray(); bool ok = false; int nr = snt[nrField].toInt(&ok, 10); if (!ok) nr = 0; return(nr); } else { return(0); } }
void So2sdr::populateDupesheet() // populates dupe sheet. Needs to be called when switching bands // or first turning on the dupesheet { // if only one dupesheet is active, figure out which one it is bool oneactive=false; int nr=0; if (nDupesheet==1) { oneactive=true; for (int i=0;i<2;i++) { if (!dupesheet[i]) continue; if (dupesheet[i]->isVisible()) { nr=i; break; } } } for (int id=0;id<2;id++) { if (!dupesheet[id]) continue; int ib=id; if (oneactive) { if (nr!=id) continue; ib=activeRadio; } dupesheet[id]->Dupes0->clear(); dupesheet[id]->Dupes1->clear(); dupesheet[id]->Dupes2->clear(); dupesheet[id]->Dupes3->clear(); dupesheet[id]->Dupes4->clear(); dupesheet[id]->Dupes5->clear(); dupesheet[id]->Dupes6->clear(); dupesheet[id]->Dupes7->clear(); dupesheet[id]->Dupes8->clear(); dupesheet[id]->Dupes9->clear(); for (int i = 0; i < dsColumns; i++) { dupeCalls[id][i].clear(); dupeCallsKey[id][i].clear(); } QSqlQueryModel m; m.setQuery("SELECT * FROM log WHERE valid='true' and BAND=" + QString::number(band[ib]), mylog->db); while (m.canFetchMore()) { m.fetchMore(); } for (int i = 0; i < m.rowCount(); i++) { QByteArray tmp = m.record(i).value("call").toString().toAscii(); updateDupesheet(tmp,id); } dupesheet[id]->setWindowTitle("Dupesheet " + bandName[band[ib]]); } }
bool QueryHolder::runQuery(IDataSource::DatasourceMode mode) { m_mode = mode; QSqlDatabase db = QSqlDatabase::database(m_connectionName); if (!db.isValid()) { setLastError(QObject::tr("Invalid connection! %1").arg(m_connectionName)); return false; } if (!m_prepared){ extractParams(); if (!m_prepared) return false; } if (!m_query){ m_query = new QSqlQuery(db); m_query->prepare(m_preparedSQL); } fillParams(m_query); m_query->exec(); QSqlQueryModel *model = new QSqlQueryModel; model->setQuery(*m_query); while (model->canFetchMore()) model->fetchMore(); if (model->lastError().isValid()){ if (m_dataSource) m_dataSource.clear(); setLastError(model->lastError().text()); delete model; return false; } else setLastError(""); setDatasource(IDataSource::Ptr(new ModelToDataSource(model,true))); return true; }
/*! ADIF file export @todo look up correct ADIF BAND strings for VHF/UHF */ bool Log::exportADIF(QFile *adifFile) const { QSqlQueryModel m; #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log where valid='true'", db); #else m.setQuery("SELECT * FROM log where valid=1", db); #endif while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount() == 0) { adifFile->close(); return(false); // nothing to do } bool ok = true; for (int i = 0; i < m.rowCount(); i++) { // do not include invalid qsos if (!m.record(i).value(SQL_COL_VALID).toBool()) continue; QByteArray tmp; QByteArray tmp2 = m.record(i).value(SQL_COL_CALL).toByteArray(); // call tmp = "<CALL:" + QByteArray::number(tmp2.size()) + ">" + tmp2; // band switch (m.record(i).value(SQL_COL_BAND).toInt()) { case BAND160: tmp = tmp + "<BAND:4>160M"; break; case BAND80: tmp = tmp + "<BAND:3>80M"; break; case BAND60: tmp = tmp + "<BAND:3>60M"; break; case BAND40: tmp = tmp + "<BAND:3>40M"; break; case BAND30: tmp = tmp + "<BAND:3>30M"; break; case BAND20: tmp = tmp + "<BAND:3>20M"; break; case BAND17: tmp = tmp + "<BAND:3>17M"; break; case BAND15: tmp = tmp + "<BAND:3>15M"; break; case BAND12: tmp = tmp + "<BAND:3>12M"; break; case BAND10: tmp = tmp + "<BAND:3>10M"; break; case BAND6: tmp = tmp + "<BAND:2>6M"; break; case BAND2: tmp = tmp + "<BAND:3>2M"; break; } // frequency double f = m.record(i).value(SQL_COL_FREQ).toDouble() / 1000000.0; tmp2 = QString::number(f, 'f', 4).toLatin1(); tmp = tmp + "<FREQ:" + QString::number(tmp2.size()).toLatin1() + ">" + tmp2; // date // in SQL log, date is of format MMddyyyy; need yyyyMMdd for adif tmp2 = m.record(i).value(SQL_COL_DATE).toByteArray().right(4) + m.record(i).value(SQL_COL_DATE).toByteArray().left(2) + m.record(i).value(SQL_COL_DATE).toByteArray().mid(2, 2); tmp = tmp + "<QSO_DATE:8>" + tmp2; // time tmp = tmp + "<TIME_ON:4>" + m.record(i).value(SQL_COL_TIME).toByteArray(); int rsti = 0; switch (m.record(i).value(SQL_COL_MODE).toInt()) { case RIG_MODE_LSB: case RIG_MODE_USB: tmp = tmp + "<MODE:3>SSB"; rsti = 1; break; case RIG_MODE_CW: case RIG_MODE_CWR: tmp = tmp + "<MODE:2>CW"; break; case RIG_MODE_FM: tmp = tmp + "<MODE:2>FM"; rsti = 1; break; case RIG_MODE_AM: tmp = tmp + "<MODE:2>AM"; rsti = 1; break; case RIG_MODE_RTTY: case RIG_MODE_RTTYR: tmp = tmp + "<MODE:4>RTTY"; break; default: tmp = tmp + "<MODE:2>CW"; break; } // RS(T). If not in log, use 59/599 if (rsti == 0) { // RST for CW, RTTY if (rstField == -1) { tmp = tmp + "<RST_SENT:3>599<RST_RCVD:3>599"; } else { QByteArray tmp3; switch (rstField) { case 0: tmp2 = m.record(i).value(SQL_COL_SNT1).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV1).toByteArray(); break; case 1: tmp2 = m.record(i).value(SQL_COL_SNT2).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV2).toByteArray(); break; case 2: tmp2 = m.record(i).value(SQL_COL_SNT3).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV3).toByteArray(); break; case 3: tmp2 = m.record(i).value(SQL_COL_SNT4).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV4).toByteArray(); break; } tmp = tmp + "<RST_SENT:3>" + tmp2.left(3) + "<RST_RCVD:3>" + tmp3.left(3); } } else { // RS for voice modes if (rstField == -1) { tmp = tmp + "<RST_SENT:2>59<RST_RCVD:2>59"; } else { QByteArray tmp3; switch (rstField) { case 0: tmp2 = m.record(i).value(SQL_COL_SNT1).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV1).toByteArray(); break; case 1: tmp2 = m.record(i).value(SQL_COL_SNT2).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV2).toByteArray(); break; case 2: tmp2 = m.record(i).value(SQL_COL_SNT3).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV3).toByteArray(); break; case 3: tmp2 = m.record(i).value(SQL_COL_SNT4).toByteArray(); tmp3 = m.record(i).value(SQL_COL_RCV4).toByteArray(); break; } tmp = tmp + "<RST_SENT:2>" + tmp2.left(2) + "<RST_RCVD:2>" + tmp3.left(2); } } tmp = tmp + "<eor>\n"; if (adifFile->write(tmp) == -1) { ok = false; } } adifFile->close(); return(ok); }
/*! * \brief offTime Calculates the number of minutes of off-time taken since start. * \param minOffTime Minimum offtime length in minutes. * \param start Start of contest. QSO's must be after this time to be valid. * \param end End of contest. QSO's must be before this time to be valid. * \return string giving off time */ QString Log::offTime(int minOffTime,QDateTime start,QDateTime end) { QSqlQueryModel m; #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log where valid='true'", db); #else m.setQuery("SELECT * FROM log where valid=1", db); #endif while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount() == 0) { return "Off 00:00/00:00"; // nothing to do } // make sure times are rounded to minutes start=start.addSecs(-start.time().second()); end=end.addSecs(-end.time().second()+60); // need to add 1 minute since end is time of last possible qso int totOffTime=0; QDateTime lastQsoTime=start; for (int i = 0; i < m.rowCount(); i++) { if (!m.record(i).value(SQL_COL_VALID).toBool()) continue; int yr=m.record(i).value(SQL_COL_DATE).toByteArray().right(4).toInt(); int mon=m.record(i).value(SQL_COL_DATE).toByteArray().left(2).toInt(); int d=m.record(i).value(SQL_COL_DATE).toByteArray().mid(2,2).toInt(); int hr=m.record(i).value(SQL_COL_TIME).toByteArray().left(2).toInt(); int min=m.record(i).value(SQL_COL_TIME).toByteArray().right(2).toInt(); QDateTime qsoTime=QDateTime(QDate(yr,mon,d),QTime(hr,min),Qt::UTC); if (qsoTime<start || qsoTime>end) continue; // qso not during contest // calculate time difference from last qso int diff=lastQsoTime.secsTo(qsoTime); if (diff<0) continue; // probably means log is out of order, this will fail! diff/=60; diff--; if (diff>=minOffTime) { totOffTime+=diff; } lastQsoTime=qsoTime; } // add any additional off time taken up to current time int extra=0; if (lastQsoTime<end) { QDateTime current=QDateTime::currentDateTime(); if (end<current) { current=end; } // instead of current time, want only current minute current=current.addSecs(-current.time().second()); extra=lastQsoTime.secsTo(current); extra/=60; extra--; if (extra<0) extra=0; if (extra>=minOffTime) totOffTime+=extra; } if (totOffTime>=6039) { return "Off 00:00/99:99"; } else { int ehr=extra/60; int emin=extra-ehr*60; int hr=totOffTime/60; int min=totOffTime-hr*60; QString tmp="Off "+QString("%1").arg(QString::number(ehr), 2, QChar('0'))+ ":"+QString("%1").arg(QString::number(emin), 2, QChar('0')); tmp=tmp+"/"; tmp=tmp+QString("%1").arg(QString::number(hr), 2, QChar('0'))+ ":"+QString("%1").arg(QString::number(min), 2, QChar('0')); return tmp; } }
/*! Dupe checking subroutine. - qso->worked returns with bits set according to bands worked - DupeCheckingEveryBand controls whether calls can be worked on every band or only once. @todo Multiple-mode contests not implemented yet */ bool Log::isDupe(Qso *qso, bool DupeCheckingEveryBand, bool FillWorked) const { // if called with no call, abort if (qso->call.isEmpty()) return false; bool dupe = false; qso->worked = 0; qso->prefill.clear(); QSqlQueryModel m; // call can only be worked once on any band if (!DupeCheckingEveryBand) { #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log WHERE valid='true' and CALL='" + qso->call + "'", db); #else m.setQuery("SELECT * FROM log WHERE valid=1 and CALL='" + qso->call + "'", db); #endif while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount()) { dupe = true; if (FillWorked) { // mult not needed on any band qso->worked = 63; } } } else { // if mobile station, check for mobile dupe option. In this // case, count dupe only if exchange is identical #if QT_VERSION < 0x050000 QString query="SELECT * FROM log WHERE valid='true' and call='" + qso->call + "' AND band=" + QString::number(qso->band); #else QString query="SELECT * FROM log WHERE valid=1 and call='" + qso->call + "' AND band=" + QString::number(qso->band); #endif if (qso->isMobile && csettings.value(c_mobile_dupes,c_mobile_dupes_def).toBool()) { QString exch=qso->rcv_exch[csettings.value(c_mobile_dupes_col,c_mobile_dupes_col_def).toInt()-1]; // if exchange not entered, can't determine dupe status yet if (exch.isEmpty()) { return(false); } // if qso already has an assigned number in log (which is SQL primary key), only check // qso's BEFORE this one if (qso->nr) { query=query+" AND (nr < "+QString::number(qso->nr)+") "; } query=query+ " AND "; switch (csettings.value(c_mobile_dupes_col,c_mobile_dupes_col_def).toInt()) { case 1: query=query+"rcv1='"+exch+"'"; break; case 2: query=query+"rcv2='"+exch+"'"; break; case 3: query=query+"rcv3='"+exch+"'"; break; case 4: query=query+"rcv4='"+exch+"'"; break; default: return(false); } } m.setQuery(query, db); m.query().exec(); while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount()) { dupe=true; } if (FillWorked) { #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log WHERE valid='true' and CALL='" + qso->call + "'", db); #else m.setQuery("SELECT * FROM log WHERE valid=1 and CALL='" + qso->call + "'", db); #endif for (int i = 0; i < m.rowCount(); i++) { qso->worked += bits[m.record(i).value(SQL_COL_BAND).toInt()]; } } } // if a dupe, set zero pts if (dupe) qso->pts = 0; return(dupe); }
/*! Cabrillo export */ void Log::exportCabrillo(QFile *cbrFile,QString call,QString snt_exch1,QString snt_exch2,QString snt_exch3,QString snt_exch4) const { QSqlQueryModel m; #if QT_VERSION < 0x050000 m.setQuery("SELECT * FROM log where valid='true'", db); #else m.setQuery("SELECT * FROM log where valid=1", db); #endif while (m.canFetchMore()) { m.fetchMore(); } if (m.rowCount() == 0) return; // nothing to do // determine max field widths for sent/received data int sfw[MAX_EXCH_FIELDS], rfw[MAX_EXCH_FIELDS]; for (int i = 0; i < MAX_EXCH_FIELDS; i++) { sfw[i] = 0.; rfw[i] = 0; } QByteArray snt[MAX_EXCH_FIELDS]; for (int i = 0; i < m.rowCount(); i++) { // skip qsos marked as invalid if (!m.record(i).value(SQL_COL_VALID).toBool()) continue; // for sent exchange, if log field is empty use config file value snt[0]=m.record(i).value(SQL_COL_SNT1).toByteArray(); if (snt[0].isEmpty()) snt[0]=snt_exch1.toLatin1(); int j = snt[0].size(); if (j > sfw[0]) sfw[0] = j; snt[1]=m.record(i).value(SQL_COL_SNT2).toByteArray(); if (snt[1].isEmpty()) snt[1]=snt_exch2.toLatin1(); j = snt[1].size(); if (j > sfw[1]) sfw[1] = j; snt[2]=m.record(i).value(SQL_COL_SNT3).toByteArray(); if (snt[2].isEmpty()) snt[2]=snt_exch3.toLatin1(); j = snt[2].size(); if (j > sfw[2]) sfw[2] = j; snt[3]=m.record(i).value(SQL_COL_SNT4).toByteArray(); if (snt[3].isEmpty()) snt[3]=snt_exch4.toLatin1(); j = snt[3].size(); if (j > sfw[3]) sfw[3] = j; j = m.record(i).value(SQL_COL_RCV1).toByteArray().size(); if (j > rfw[0]) rfw[0] = j; j = m.record(i).value(SQL_COL_RCV2).toByteArray().size(); if (j > rfw[1]) rfw[1] = j; j = m.record(i).value(SQL_COL_RCV3).toByteArray().size(); if (j > rfw[2]) rfw[2] = j; j = m.record(i).value(SQL_COL_RCV4).toByteArray().size(); } for (int i = 0; i < m.rowCount(); i++) { // skip qsos marked as invalid if (!m.record(i).value(SQL_COL_VALID).toBool()) continue; // for sent exchange, if log field is empty use config file value snt[0]=m.record(i).value(SQL_COL_SNT1).toByteArray(); if (snt[0].isEmpty()) snt[0]=snt_exch1.toLatin1(); snt[1]=m.record(i).value(SQL_COL_SNT2).toByteArray(); if (snt[1].isEmpty()) snt[1]=snt_exch2.toLatin1(); snt[2]=m.record(i).value(SQL_COL_SNT3).toByteArray(); if (snt[2].isEmpty()) snt[2]=snt_exch3.toLatin1(); snt[3]=m.record(i).value(SQL_COL_SNT4).toByteArray(); if (snt[3].isEmpty()) snt[3]=snt_exch4.toLatin1(); QString tmp; tmp = "QSO: "; QString tmp2 = QString::number(qRound(m.record(i).value(SQL_COL_FREQ).toDouble() / 1000.0)); for (int j = 0; j < (5 - tmp2.size()); j++) { tmp = tmp + " "; } tmp = tmp + tmp2; switch (m.record(i).value(SQL_COL_MODE).toInt()) { case RIG_MODE_CW: case RIG_MODE_CWR: tmp = tmp + " CW "; break; case RIG_MODE_USB: case RIG_MODE_LSB: case RIG_MODE_AM: case RIG_MODE_FM: tmp = tmp + " PH "; break; case RIG_MODE_RTTY: case RIG_MODE_RTTYR: tmp = tmp + " RY "; break; default: tmp = tmp + " CW "; break; } // in SQL log, date is of format MMddyyyy tmp2 = m.record(i).value(SQL_COL_DATE).toByteArray(); tmp = tmp + tmp2.right(4) + "-" + tmp2.left(2) + "-" + tmp2.mid(2, 2); tmp = tmp + " " + m.record(i).value(SQL_COL_TIME).toByteArray(); tmp = tmp + " " + call; int n = 11 - call.size(); for (int j = 0; j < n; j++) tmp.append(" "); for (int j = 0; j < nExchange; j++) { QByteArray s; s.clear(); switch (j) { case 0: s=snt[0]; s = s.leftJustified(sfw[0], ' '); break; case 1: s=snt[1]; s = s.leftJustified(sfw[1], ' '); break; case 2: s=snt[2]; s = s.leftJustified(sfw[2], ' '); break; case 3: s=snt[3]; s = s.leftJustified(sfw[3], ' '); break; } tmp = tmp + s + " "; } tmp = tmp + m.record(i).value(SQL_COL_CALL).toByteArray(); n = 11 - m.record(i).value(SQL_COL_CALL).toByteArray().size(); for (int j = 0; j < n; j++) tmp.append(" "); for (int j = 0; j < nExchange; j++) { switch (j) { case 0: tmp = tmp + m.record(i).value(SQL_COL_RCV1).toByteArray().leftJustified(rfw[0], ' '); break; case 1: tmp = tmp + m.record(i).value(SQL_COL_RCV2).toByteArray().leftJustified(rfw[1], ' '); break; case 2: tmp = tmp + m.record(i).value(SQL_COL_RCV3).toByteArray().leftJustified(rfw[2], ' '); break; case 3: tmp = tmp + m.record(i).value(SQL_COL_RCV4).toByteArray().leftJustified(rfw[3], ' '); break; } tmp = tmp + " "; } tmp = tmp + "\n"; cbrFile->write(tmp.toLatin1()); } cbrFile->write("END-OF-LOG:\n"); cbrFile->close(); }