TagContainer class holds the options of ImportFileProcessorModel and ImportFileHeaderSection. and acts as a mediator in between. More...
#include <tagcontainer.h>
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.
typedef QMap< ColumnTag, QMap < int, QVariant > > TagContainer::ColumnTagCache |
typedef QHash<GeneralTag, QString> TagContainer::GeneralTagCache |
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?
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
{ ColumnTypeNone = 0, ColumnTypeData = Qt::UserRole + 300, // = 332 ColumnTypeTimestamp // = 333 };
GeneralTagColumnDelimiter | |
GeneralTagSkipLines | |
GeneralTagMergeOnSplits | |
GeneralTagRowFilter |
{ GeneralTagColumnDelimiter = Qt::UserRole + 200, GeneralTagSkipLines, GeneralTagMergeOnSplits, GeneralTagRowFilter };
{ 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.
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()); } }
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; }
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] |
Referenced by deleteColumnTagValue(), reset(), and setColumnTagValue().
void TagContainer::signal_generalTagCache_Changed | ( | TagContainer::GeneralTag | , | |
QString | data | |||
) | [signal] |
Referenced by deleteGeneralTagValue(), reset(), and setGeneralTagValue().
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(); }