File indexing completed on 2024-12-15 04:56:11

0001 /*
0002  * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) version 3, or any
0008  * later version accepted by the membership of KDE e.V. (or its
0009  * successor approved by the membership of KDE e.V.), which shall
0010  * act as a proxy defined in Section 6 of version 3 of the license.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019  */
0020 
0021 #include "dataset.h"
0022 
0023 #include <QDateTime>
0024 #include <QDebug>
0025 #include <QDataStream>
0026 
0027 #include <iostream>
0028 
0029 namespace HAWD
0030 {
0031 
0032 static const QString s_annotationKey("__annotation__");
0033 static const QString s_hashKey("__commithash__");
0034 static const QString s_timestampKey("__timestamp");
0035 static const int s_fieldWidth(20);
0036 
0037 Dataset::Row::Row(const Row &other)
0038     : m_key(other.m_key),
0039       m_columns(other.m_columns),
0040       m_data(other.m_data),
0041       m_annotation(other.m_annotation),
0042       m_commitHash(other.m_commitHash),
0043       m_dataset(other.m_dataset)
0044 {
0045 }
0046 
0047 Dataset::Row::Row(const Dataset &dataset, qint64 key)
0048     : m_key(key),
0049       m_columns(dataset.definition().columns()),
0050       m_dataset(&dataset)
0051 {
0052     // TODO: pre-populate m_data, or do that on buffer creation?
0053     for (const auto &colum : dataset.definition().columns()) {
0054         m_data.insert(colum.first, QVariant());
0055     }
0056 }
0057 
0058 Dataset::Row &Dataset::Row::operator=(const Row &rhs)
0059 {
0060     m_key = rhs.m_key;
0061     m_columns = rhs.m_columns;
0062     m_data = rhs.m_data;
0063     m_dataset = rhs.m_dataset;
0064     m_annotation = rhs.m_annotation;
0065     m_commitHash = rhs.m_commitHash;
0066     return *this;
0067 }
0068 
0069 void Dataset::Row::setValue(const QString &col, const QVariant &value)
0070 {
0071     for (const auto &column : m_columns) {
0072         if (column.first == col) {
0073             if (value.canConvert(column.second.type())) {
0074                 m_data[col] = value;
0075             }
0076             return;
0077         }
0078     }
0079 }
0080 
0081 QVariant Dataset::Row::value(const QString &col) const
0082 {
0083     return m_data.value(col);
0084 }
0085 
0086 void Dataset::Row::annotate(const QString &note)
0087 {
0088     m_annotation = note;
0089 }
0090 
0091 void Dataset::Row::setCommitHash(const QString &hash)
0092 {
0093     m_commitHash = hash;
0094 }
0095 
0096 qint64 Dataset::Row::key() const
0097 {
0098     if (m_key < 1) {
0099         const_cast<Dataset::Row *>(this)->m_key = QDateTime::currentMSecsSinceEpoch();
0100     }
0101 
0102     return m_key;
0103 }
0104 
0105 void Dataset::Row::fromBinary(QByteArray data)
0106 {
0107     QVariant value;
0108     QString key;
0109     QDataStream stream(&data, QIODevice::ReadOnly);
0110 
0111     while (!stream.atEnd()) {
0112         stream >> key;
0113         if (stream.atEnd()) {
0114             break;
0115         }
0116 
0117         stream >> value;
0118         if (key == s_annotationKey) {
0119             m_annotation = value.toString();
0120         } else if (key == s_hashKey) {
0121             m_commitHash = value.toString();
0122         } else if (key == s_timestampKey) {
0123             m_timeStamp = value.toDateTime();
0124         } else {
0125             setValue(key, value);
0126         }
0127     }
0128 }
0129 
0130 QByteArray Dataset::Row::toBinary() const
0131 {
0132     QByteArray data;
0133     QDataStream stream(&data, QIODevice::WriteOnly);
0134 
0135     QHashIterator<QString, QVariant> it(m_data);
0136     while (it.hasNext()) {
0137         it.next();
0138         if (it.value().isValid()) {
0139             stream << it.key() << it.value();
0140         }
0141     }
0142 
0143     if (!m_commitHash.isEmpty()) {
0144         stream << s_hashKey << QVariant(m_commitHash);
0145     }
0146 
0147     if (!m_timeStamp.isValid()) {
0148         stream << s_timestampKey << QVariant(m_timeStamp);
0149     }
0150 
0151     if (!m_annotation.isEmpty()) {
0152         stream << s_annotationKey << QVariant(m_annotation);
0153     }
0154 
0155     return data;
0156 }
0157 
0158 QString Dataset::tableHeaders(const QStringList &cols, int standardCols, const QString &seperator) const
0159 {
0160     if (!isValid()) {
0161         return QString();
0162     }
0163 
0164     QStringList strings;
0165 
0166     if (standardCols & Row::Timestamp) {
0167         strings << QObject::tr("Timestamp").leftJustified(s_fieldWidth, ' ');
0168     }
0169 
0170     if (standardCols & Row::CommitHash) {
0171         strings << QObject::tr("Commit").leftJustified(s_fieldWidth, ' ');
0172     }
0173 
0174     for (const auto &column : m_definition.columns()) {
0175         QString header = column.first;
0176         if (cols.isEmpty() || cols.contains(header)) {
0177             if (!column.second.unit().isEmpty()) {
0178                 header.append(" (").append(column.second.unit()).append(")");
0179             }
0180             strings << header.leftJustified(s_fieldWidth, ' ');
0181         }
0182     }
0183 
0184     if (standardCols & Row::Annotation) {
0185         strings << QObject::tr("Annotation").leftJustified(s_fieldWidth, ' ');
0186     }
0187 
0188     return strings.join(seperator);
0189 }
0190 
0191 
0192 QString Dataset::Row::commitHash() const
0193 {
0194     return m_commitHash;
0195 }
0196 
0197 QDateTime Dataset::Row::timestamp() const
0198 {
0199     if (m_timeStamp.isValid()) {
0200         return m_timeStamp;
0201     }
0202     QDateTime dt;
0203     dt.setMSecsSinceEpoch(m_key);
0204     return dt;
0205 }
0206 
0207 void Dataset::Row::setTimestamp(const QDateTime &dt)
0208 {
0209     m_timeStamp = dt;
0210 }
0211 
0212 QString Dataset::Row::toString(const QStringList &cols, int standardCols, const QString &seperator) const
0213 {
0214     if (m_data.isEmpty()) {
0215         return QString();
0216     }
0217 
0218     QStringList strings;
0219 
0220     if (standardCols & Timestamp) {
0221         strings << timestamp().toString("yyMMdd:hhmmss").leftJustified(s_fieldWidth, ' ');
0222     }
0223 
0224     if (standardCols & CommitHash) {
0225         strings << m_commitHash.leftJustified(s_fieldWidth, ' ');
0226     }
0227 
0228     for (const auto &column : m_columns) {
0229         const auto key = column.first;
0230         if (cols.isEmpty() || cols.contains(key)) {
0231             const auto value = m_data.value(key);
0232             if (value.canConvert<double>()) {
0233                 strings << QString("%1").arg(value.toDouble(), s_fieldWidth, 'f', 3);
0234             } else {
0235                 strings << value.toString().leftJustified(s_fieldWidth, ' ');
0236             }
0237         }
0238     }
0239 
0240     if (standardCols & Annotation) {
0241         strings << m_annotation.leftJustified(s_fieldWidth, ' ');
0242     }
0243 
0244     return strings.join(seperator);
0245 }
0246 
0247 Dataset::Dataset(const QString &name, const State &state)
0248     : m_definition(state.datasetDefinition(name)),
0249       m_storage(state.resultsPath(), name, Sink::Storage::DataStore::ReadWrite),
0250       m_transaction(m_storage.createTransaction()),
0251       m_commitHash(state.commitHash())
0252 {
0253 }
0254 
0255 Dataset::~Dataset()
0256 {
0257     m_transaction.commit();
0258 }
0259 
0260 bool Dataset::isValid() const
0261 {
0262     return m_definition.isValid();
0263 }
0264 
0265 const DatasetDefinition &Dataset::definition() const
0266 {
0267     return m_definition;
0268 }
0269 
0270 qint64 Dataset::insertRow(const Row &row)
0271 {
0272     if (row.m_dataset != this) {
0273         return 0;
0274     }
0275 
0276     qint64 key = row.key();
0277     m_transaction.openDatabase().write(QByteArray::fromRawData((const char *)&key, sizeof(qint64)), row.toBinary());
0278     return key;
0279 }
0280 
0281 void Dataset::removeRow(const Row &row)
0282 {
0283     //TODO
0284 }
0285 
0286 void Dataset::eachRow(const std::function<void(const Row &row)> &resultHandler)
0287 {
0288     if (!isValid()) {
0289         return;
0290     }
0291 
0292     Row row(*this);
0293     m_transaction.openDatabase().scan("",
0294                    [&](const QByteArray &key, const QByteArray &value) -> bool {
0295                        if (key.size() != sizeof(qint64)) {
0296                            return true;
0297                        }
0298 
0299                        row.fromBinary(value);
0300                        row.m_key = *(const qint64 *)key.data();
0301                        resultHandler(row);
0302                        return true;
0303                    },
0304                    Sink::Storage::DataStore::basicErrorHandler());
0305 }
0306 
0307 Dataset::Row Dataset::row(qint64 key)
0308 {
0309     if (key < 1) {
0310         Row row(*this, Sink::Storage::DataStore::maxRevision(m_transaction));
0311         row.setCommitHash(m_commitHash);
0312         return row;
0313     }
0314 
0315     Row row(*this, key);
0316     m_transaction.openDatabase().scan(QByteArray::fromRawData((const char *)&key, sizeof(qint64)),
0317             [&row](const QByteArray &key, const QByteArray &value) -> bool {
0318                 row.fromBinary(value);
0319                 return true;
0320             },
0321             Sink::Storage::DataStore::basicErrorHandler()
0322             );
0323     return row;
0324 }
0325 
0326 Dataset::Row Dataset::lastRow()
0327 {
0328     //TODO
0329     return Row(*this);
0330 }
0331 
0332 } // namespace HAWD
0333