Public Types | Public Slots | Signals | Public Member Functions

TagContainer Class Reference

TagContainer class holds the options of ImportFileProcessorModel and ImportFileHeaderSection. and acts as a mediator in between. More...

#include <tagcontainer.h>

Inheritance diagram for TagContainer:
Inheritance graph
[legend]
Collaboration diagram for TagContainer:
Collaboration graph
[legend]

List of all members.

Public Types

enum  GeneralTag { GeneralTagColumnDelimiter = Qt::UserRole + 200, GeneralTagSkipLines, GeneralTagMergeOnSplits, GeneralTagRowFilter }
enum  ColumnTag {
  ColumnTagType = Qt::UserRole + 101, ColumnTagTimestampFormat, ColumnTagName, ColumnTagDataUnitId,
  ColumnTagTreeId, ColumnTagToolTip
}
enum  ColumnType { ColumnTypeNone = 0, ColumnTypeData = Qt::UserRole + 300, ColumnTypeTimestamp }
typedef QMap< ColumnTag, QMap
< int, QVariant > > 
ColumnTagCache
typedef QHash< GeneralTag,
QString
GeneralTagCache

Public Slots

void setColumnTagValue (const int column, const TagContainer::ColumnTag &tag, const QVariant &data)
void setGeneralTagValue (const TagContainer::GeneralTag &tag, const QString &data)

Signals

void signal_columnTagCache_Changed (int column, TagContainer::ColumnTag, QVariant data)
void signal_generalTagCache_Changed (TagContainer::GeneralTag, QString data)

Public Member Functions

 TagContainer (const QString &SPCode=QString())
 ~TagContainer ()
void initialize (const QString &spCode)
 Sets up the tagcontainer with specific preferences.
QString writeTo () const
QVariant getColumnTagValue (const int column, const ColumnTag &tag) const
QString getGeneralTagValue (const GeneralTag &tag) const
bool isColumnAssignedAColumnTag (const int column, const ColumnTag &tag) const
void deleteColumnTagValue (const int &column, const ColumnTag &tag)
void deleteGeneralTagValue (const GeneralTag &tag)
bool isCacheValid () const
QString storedFunctionDefinition (const QString &spName)
void reset ()
void resetColumn (int column)
QVector< int > getArgToColumnMap (const QString &spArgNames) const
int maxColumnAssigned () const
int maxColumnAssigned (const QString &spArgNames) const

Detailed Description

TagContainer class holds the options of ImportFileProcessorModel and ImportFileHeaderSection. and acts as a mediator in between.

Best way this class should have been implemented is in the model itself, but the first implementation was problematic. So, the reason it is separate has historical roots.

Todo:
Implement a step-by-step reading process.
See also:
ImportFileProcessorModel, ImportFileHeaderSection

Member Typedef Documentation


Member Enumeration Documentation

Vraag hier is wat opslaan: gegevens die bepaalde zaken dat je nodig hebt volledig bepalen, zoals unit_conv_id, of zaken die het deels bepalen, maar in combinatie met andere het wel volledig doen zoals bv unit_id_from waarbij unit_conv_id dan gehaald kan worden via tree_id. Probleem met overdeterminatie is dat er inconsistentie zou kunnen optreden: bv unit_conv_id dat een andere unit_to impliceert dan tree_id. Probleem met tweede is dat bij het syncen problemen kunnen optreden als bijvoorbezeld tree_id niet voorlopig gesycnd is maar unit_id_from wel, kan unit_conv_id niet gekend worden. Oplossing voor dat tweede is asynchroon data lezen en synchroon syncen?

Note:
Make sure the tags retain their number, otherwise old tags used in templates will be invalidated, or associated with wrong tags, and vice versa.
Enumerator:
ColumnTagType 
ColumnTagTimestampFormat 
ColumnTagName 
ColumnTagDataUnitId 
ColumnTagTreeId 
ColumnTagToolTip 

    {
        //ColumnTagTreeItemInternalPointer = Qt::UserRole + 100,
        ColumnTagType = Qt::UserRole + 101, // = 133
        ColumnTagTimestampFormat, // = 134
        ColumnTagName, // = 135
        ColumnTagDataUnitId, // = 136
        ColumnTagTreeId, // = 137
        ColumnTagToolTip // = 138
    };

Used in conjunction with TagContainer::ColumnTagType

Enumerator:
ColumnTypeNone 
ColumnTypeData 
ColumnTypeTimestamp 

    {
        ColumnTypeNone = 0,
        ColumnTypeData = Qt::UserRole + 300, // = 332
        ColumnTypeTimestamp // = 333
    };

Note:
Make sure you also add an translation for the enum to the translator.
Enumerator:
GeneralTagColumnDelimiter 
GeneralTagSkipLines 
GeneralTagMergeOnSplits 
GeneralTagRowFilter 

    {
        GeneralTagColumnDelimiter = Qt::UserRole + 200,
        GeneralTagSkipLines,
        GeneralTagMergeOnSplits,
        GeneralTagRowFilter
    };


Constructor & Destructor Documentation

TagContainer::TagContainer ( const QString SPCode = QString()  ) 
Note:
The translator must be updated when one implements new tags

{
    PIMPL_INITIALIZE(TagContainer)
    typedef  TagContainer Tag;
    /*Translator moet geupdate worden bij elke extra tag dat gebruikt wordt*/
    TagContainer::translator.insert(Tag::GeneralTagColumnDelimiter, "column_delimiter");
    TagContainer::translator.insert(Tag::GeneralTagSkipLines, "skip_lines");
    TagContainer::translator.insert(Tag::GeneralTagMergeOnSplits, "merge_on_splits");
    TagContainer::translator.insert(Tag::GeneralTagRowFilter, "row_filter");
    if (!SPCode.isEmpty()) initialize(SPCode);
}

TagContainer::~TagContainer (  ) 

Destructs TagContainer and it's private implementation.


Member Function Documentation

void TagContainer::deleteColumnTagValue ( const int &  column,
const ColumnTag tag 
)

Deletes a column tag.

Referenced by resetColumn(), and setColumnTagValue().

{
    if (columnTagCache.contains(tag)){

        // Delete dependencies first
        if (tag == TagContainer::ColumnTagTreeId){
            deleteColumnTagValue(column, TagContainer::ColumnTagDataUnitId);
            deleteColumnTagValue(column, TagContainer::ColumnTagName);
            deleteColumnTagValue(column, TagContainer::ColumnTagToolTip);
        }

        // Delete value
        columnTagCache[tag].remove(column);
        qDebug() << "DELETED ColumnTag from TagContainer: " << column << tag;
        emit signal_columnTagCache_Changed(column, tag, QVariant());
    }
}

void TagContainer::deleteGeneralTagValue ( const GeneralTag tag  ) 

Deletes a general tag.

Referenced by setGeneralTagValue().

{
    if (generalTagCache.contains(tag)){
        // Delete dependencies first
        // ...

        // Delete value
        generalTagCache.remove(tag);
        emit signal_generalTagCache_Changed(tag, QString());
    }
}

QVector< int > TagContainer::getArgToColumnMap ( const QString spArgNames  )  const

This is essentialy a parser that takes the arguments of a Stored Function and returns an vector where the index is the argument placeholder, and it's value the number of the corresponding column.

Referenced by maxColumnAssigned().

{
    QVector<int> vector;
    QRegExp rx("(?:timestamp_columnNr:|data_columnNr:)(\\d+)");
    int pos = 0;
    while ((pos = rx.indexIn(spArgNames, pos)) != -1) {
        //qDebug() << "RX_CAP: " << rx.cap(1);
        vector << rx.cap(1).toInt();
        pos += rx.matchedLength();
    }
    qDebug() << "tagContainer args-to-columns map (" << vector << ") requested for args: " << spArgNames;
    return vector;
}

QVariant TagContainer::getColumnTagValue ( const int  column,
const ColumnTag tag 
) const [inline]

Get a column tag.

{
    QVariant value = columnTagCache.value(tag).value(column);
    qDebug() << "READ ColumnTag from TagContainer: " << value << " with specs: column: " << column << " tag: " << tag;
    return value;
}

QString TagContainer::getGeneralTagValue ( const GeneralTag tag  )  const [inline]

Get a general tag.

{
    return generalTagCache.value(tag);
}

void TagContainer::initialize ( const QString spCode  ) 

Sets up the tagcontainer with specific preferences.

Currently, a QString is used with specific formating which is stored in the Stored Function. This can easily be extended to support QRecord. Reason why a QString is used is because Postgres doesn't support triggers on system tables, and so one can not guarantee consistency between functions and it's preferences.

Referenced by ImportFileView::setupAsPreviewer(), and TagContainer().

{
        PIMPL_PRIVATE(TagContainer);
        // reset??
        priv->read(spCode);
}

bool TagContainer::isCacheValid (  )  const

Checks whether the current tags are valid. For example, whether there is only and exactly one column.

Referenced by storedFunctionDefinition().

                                      {
    //assertion there is only ONE timestamp column
    if (columnTagCache.value(TagContainer::ColumnTagType).keys(QVariant::fromValue((int) TagContainer::ColumnTypeTimestamp)).count() != 1) {
        qWarning() << "Only one timestamp column allowed.";
        return false;
    }
    //assertion there is at least one Data Column
    if (columnTagCache.value(TagContainer::ColumnTagType).keys(QVariant::fromValue((int) TagContainer::ColumnTypeData)).count() < 1) {
        qWarning() << "You need to assign at least one column to appropriate sensor channel.";
        return false;
    }
    //assertion there must be a unit assigned to a column if there is a tree_id assigned to it
    if (columnTagCache.value(TagContainer::ColumnTagType).keys((int) TagContainer::ColumnTypeData).toSet() != columnTagCache.value(TagContainer::ColumnTagDataUnitId).keys().toSet()) {
        qWarning() << "A unit must be assigned to each data column!";
        return false;
    }
    //assertion one column can not be assigned both data and timestamp
    // = automatisch
    return true;
}

bool TagContainer::isColumnAssignedAColumnTag ( const int  column,
const ColumnTag tag 
) const

Checks whether a column is assigned a tag.

{
        return columnTagCache.value(tag).contains(column);
}

int TagContainer::maxColumnAssigned (  )  const

Resets the specified column.

{
    QList<int> columnList;
    ColumnTagCache::const_iterator i = columnTagCache.constBegin();
    while(i != columnTagCache.end()){
        columnList.append(i.value().keys());
        i++;
    }
    qSort(columnList.begin(), columnList.end());
    return columnList.last();
}

int TagContainer::maxColumnAssigned ( const QString spArgNames  )  const

Resets the specified column.

{
    QList<int> columnList(getArgToColumnMap(spArgNames).toList());
    qSort(columnList.begin(), columnList.end());
    return columnList.last();
}

void TagContainer::reset (  ) 

Resets the TagContainer.

{
    qDebug() << "Begin reset TagContainer";

    // clear generalTagCache
    if (!generalTagCache.isEmpty()) {
        GeneralTagCache::iterator i = generalTagCache.begin();
        while(i != generalTagCache.end()) {
            emit signal_generalTagCache_Changed(i.key(), QString());
            i = generalTagCache.erase(i);
        }
    }

    // clear columnTagCache
    if (!columnTagCache.isEmpty()) {
        ColumnTagCache::iterator i = columnTagCache.begin();
        while(i != columnTagCache.end()) {
            QMap < int, QVariant >::iterator j = i.value().begin();
            while(j != i.value().end()){
                emit signal_columnTagCache_Changed(j.key(), i.key(), QVariant());
                j = i.value().erase(j);
            }
            // When the signal is emited, and connection is Qt::DirectConnection,
            // then there is a high probablility a signal will be sent back from the receiver,
            // to add a "null-value" or similar. If that happens, all such values will be deleted
            // by the following line. If the receiver sends a value under different i.key (i.e. TagContaienr:ColumnTag)
            // then this value might never get ereased, and the code might stop on the bottom Q_ASSERT(empty)
            i = columnTagCache.erase(i);
        }
    }

    // These two lines make sure the caches are clear, because it is not guaranteed
    // that they will be clearedfrom the above code because it sends signals,
    // and the receivers might (and usualy do) send data back.

    qDebug() << "ColumnTagCache should be empty: " << columnTagCache;
    qDebug() << "GeneralTagCache should be empty: " << generalTagCache;
    generalTagCache.clear();
    columnTagCache.clear();

    qDebug() << "End reset TagContainer";
}

void TagContainer::resetColumn ( int  column  ) 

Resets the specified column.

Referenced by setColumnTagValue().

{
    qDebug() << "Resetting column " << column << " in TagContainer";
    ColumnTagCache::iterator i = columnTagCache.begin();
    while(i != columnTagCache.end()){
        if (i.value().contains(column)) deleteColumnTagValue(column, i.key());
        i++;
    }
}

void TagContainer::setColumnTagValue ( const int  column,
const TagContainer::ColumnTag tag,
const QVariant data 
) [slot]

Sets a ColumnTag and emits signal_columnTagCache_Changed(int column, TagContainer::ColumnTag, QVariant data)

{
    Q_ASSERT(column >= 0);
    if (!data.isValid()){
        deleteColumnTagValue(column, tag);
        return;
    }

    // Check validity
    // One tree_id can only be assigned to one column
    if (tag == TagContainer::ColumnTagTreeId) {
        while (columnTagCache[tag].values().contains(data)){
            // Eigenlijk zouden we hier moeten checken op basis van sens_id & sens_chan_nr en niet zozeer op tree_id.
            // tree_id is niet zo strikt, maar goed... we gaan hier uit van onwetendheid. Indien er een inconsistentie
            // is zal de database toch klagen.
            qWarning() << "You allready have the same sensor channel assigned to column" << columnTagCache[tag].key(data) << "."
                       << "The old assignement is removed.";
            deleteColumnTagValue(columnTagCache[tag].key(data), tag);
        }
    }

    columnTagCache[tag][column] = data;
    qDebug() << "SAVED ColumnTag in TagContainer: " << columnTagCache[tag][column] << " with specs: " << column << tag;
    emit signal_columnTagCache_Changed(column, tag, data);

    // Add dependencies
    if (tag == TagContainer::ColumnTagTreeId){
        bool ok;
        TreeItem *item = treeModel.treeItemPointer(data.toInt(&ok));
        Q_ASSERT(ok);

        // This will fail here if someone loads a template that has tree_ids that do not exist in the database or the session_user cannot see them.
        if (!item){
            qWarning() << QString("The project-specific sensorchannel (%1) you are trying to associate with column %2 does not exist in your Project tree. "
                                  "You first need the read privilege on the project this column is meant for, and you also might need the privilege to upload "
                                  "in this project if you wish to upload data.").arg(data.toString(), QString::number(column));
            resetColumn(column);
            return;
        }

        setColumnTagValue(column, TagContainer::ColumnTagType, QVariant::fromValue((int) TagContainer::ColumnTypeData));
        setColumnTagValue(column, TagContainer::ColumnTagName, item->data(TreeItem::TreeItemName));
        setColumnTagValue(column, TagContainer::ColumnTagToolTip, item->data(TreeItem::TreeItemFullName));
    }
}

void TagContainer::setGeneralTagValue ( const TagContainer::GeneralTag tag,
const QString data 
) [slot]

Sets a ColumnTag and emits signal_generalTagCache_Changed(TagContainer::GeneralTag, QString data)

{
    if (data.isNull()){
        deleteGeneralTagValue(tag);
        return;
    }

    // Check validity
    // ...

    generalTagCache.insert(tag, data);
    emit signal_generalTagCache_Changed(tag, data);

    // Add dependencies
    // ...
}

void TagContainer::signal_columnTagCache_Changed ( int  column,
TagContainer::ColumnTag  ,
QVariant  data 
) [signal]
void TagContainer::signal_generalTagCache_Changed ( TagContainer::GeneralTag  ,
QString  data 
) [signal]
QString TagContainer::storedFunctionDefinition ( const QString spName  ) 

Returns the definition of the equivalent Stored Function.

{
    // check validity first
    if (!isCacheValid()) return QString();

    // define some constants to be used
    static const QString DATA_IDENTIFIER_NAME = QString("\"data_columnNr:%1\"");
    static const QString TIMESTAMP_IDENTIFIER_NAME = QString("\"timestamp_columnNr:%1\"");
    static const QString STORED_FUNCTION_PGSQL // %1 = procedure_naam, %2 = procedure_code, %3 = procedure_args, %4 = procedure_comments/tags
            = QString("CREATE FUNCTION \"" + GlobalConst::TEMPLATES_SCHEMA + "\".%1(%3) RETURNS void AS $$ \n"
                      "%4\nBEGIN \n%2\nEND; \n"
                      "$$ LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT");
    static const QString STORED_FUNCTION_INSERT_STATEMENTS
            = QString("INSERT INTO tbl_sensor_channel_data "
                        "(sens_chan_data_timestamp, sens_chan_data_data, tree_id) "
                      "VALUES (%1, %2, %3);\n");

    QString spTagsEnc = writeTo();

    const int tsColumnNumber = columnTagCache.value(TagContainer::ColumnTagType).key(QVariant::fromValue((int) ColumnTypeTimestamp));
    const QList<int> dataColumnNumbers = columnTagCache.value(ColumnTagType).keys(QVariant::fromValue((int) ColumnTypeData));
    Q_ASSERT(tsColumnNumber >= 0);
    Q_ASSERT(!dataColumnNumbers.isEmpty());

    QMap<int /*dataColumnNumber*/, int /*tree_id*/> dataColumnToConstraintMap; // constraint as value is here the tree_id
    foreach (int i, dataColumnNumbers){
        Q_ASSERT(i != -1); // is a valid column
        Q_ASSERT(columnTagCache.value(TagContainer::ColumnTagTreeId).contains(i)); // column actualy has a tree_id assigned
        bool ok;
        int tree_id = columnTagCache.value(TagContainer::ColumnTagTreeId).value(i).toInt(&ok);
        Q_ASSERT_X(ok, "TagContainer::storedFunctionDefinition()", columnTagCache.value(TagContainer::ColumnTagTreeId).value(i).toByteArray().data());
        Q_ASSERT_X(tree_id > 0, "TagContainer::storedFunctionDefinition()", QString::number(tree_id).toAscii().data()); // check whether tree_id is valid
        dataColumnToConstraintMap[i] = tree_id;
    }

    Q_ASSERT(dataColumnNumbers == dataColumnToConstraintMap.keys());

    // here starts the making of the definition of the stored function
    QString spCode;
    QString spArgs;

    const QString columnTimestampName = TIMESTAMP_IDENTIFIER_NAME.arg(QString::number(tsColumnNumber));

    // for each dataColumnNumber, make an argument and one insert statement
    for (QMap<int, int>::const_iterator i = dataColumnToConstraintMap.constBegin(); i != dataColumnToConstraintMap.constEnd(); i++){
        const QString columnDataName = DATA_IDENTIFIER_NAME.arg(QString::number(i.key()));
        spArgs.append(columnDataName)
              .append(" REAL, ");
        spCode.append(STORED_FUNCTION_INSERT_STATEMENTS.arg(columnTimestampName, columnDataName, QString::number(i.value())));
    }

    // now append also the timestamp argument
    spArgs.append(columnTimestampName)
          .append(" TIMESTAMP WITHOUT TIME ZONE ");

    // put all data in definition template and return
    return STORED_FUNCTION_PGSQL.arg(spName, spCode, spArgs, spTagsEnc);
}

QString TagContainer::writeTo (  )  const

Writes Tags to a specified return type.

Referenced by storedFunctionDefinition().

{
    PIMPL_PRIVATE(const TagContainer);
    return priv->getTagsEncoded();
}


The documentation for this class was generated from the following files: