Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode rowValue of a componentList when it contains variables #5723

Merged
merged 2 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Modules/jaspTestModule
15 changes: 9 additions & 6 deletions QMLComponents/analysisbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,25 +149,28 @@ Json::Value& AnalysisBase::_getParentBoundValue(const QVector<JASPControl::Paren
{
Json::Value* parentBoundValues = &(*parentBoundValue)[parent.name];
if (parentBoundValues->isObject() && parentBoundValues->isMember("value") && parentBoundValues->isMember("types"))
parentBoundValues = &(*parentBoundValues)["value"];
parentBoundValues = &(*parentBoundValues)["value"]; // If the control contain variable names, the option values are inside the 'value' member

if (!parentBoundValues->isNull() && parentBoundValues->isArray())
{
for (Json::Value & boundValue : (*parentBoundValues))
{
if (boundValue.isMember(parent.key))
{
Json::Value &val = boundValue[parent.key];
Json::Value* keyValue = &(boundValue[parent.key]);
if (keyValue->isObject() && keyValue->isMember("value") && keyValue->isMember("types"))
keyValue = &((*keyValue)["value"]); // The key can be also a variable name

// The value can be a string or an array of strings (for interaction terms)
if (val.isString() && parent.value.size() == 1)
if (keyValue->isString() && parent.value.size() == 1)
{
if (val.asString() == parent.value[0]) found = true;
if (keyValue->asString() == parent.value[0]) found = true;
}
else if (val.isArray() && val.size() == parent.value.size())
else if (keyValue->isArray() && keyValue->size() == parent.value.size())
{
found = true;
size_t i = 0;
for (const Json::Value& compVal : val)
for (const Json::Value& compVal : *keyValue)
{
if (!compVal.isString() || compVal.asString() != parent.value[i]) found = false;
i++;
Expand Down
42 changes: 31 additions & 11 deletions QMLComponents/boundcontrols/boundcontrolbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,28 +150,48 @@ void BoundControlBase::_readTableValue(const Json::Value &value, const std::stri
}
}

Json::Value BoundControlBase::_getTableValueOption(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasMultipleTerms)
Json::Value BoundControlBase::_getTableValueOption(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasInteraction, bool keyHasVariables)
{
Json::Value result(Json::arrayValue);

for (const Term& term : terms)
{
QMap<QString, Json::Value> componentValues = componentValuesMap[term.asQString()];

Json::Value rowValues(Json::objectValue);
if (hasMultipleTerms)
Json::Value rowValues(Json::objectValue),
keyValue; // depending of keyHasVariables & hasInteraction, keyValue can be an object, an array or a string

if (keyHasVariables)
{
Json::Value keyValue(Json::arrayValue);
for (const std::string& comp : term.scomponents())
keyValue.append(comp);
rowValues[key] = keyValue;
// If the key of the row contains variables, set 'value' and 'types'
Json::Value jsonValue, jsonTypes;

if (hasInteraction)
{
for (const std::string& comp : term.scomponents())
jsonValue.append(comp);
for (columnType type : term.types())
jsonTypes.append(columnTypeToString(type));
}
else
{
jsonValue = term.asString();
jsonTypes = columnTypeToString(term.type());
}
keyValue["value"] = jsonValue;
keyValue["types"] = jsonTypes;
}
else
{
Json::Value keyValue(term.asString());
rowValues[key] = keyValue;
if (hasInteraction)
for (const std::string& comp : term.scomponents())
keyValue.append(comp);
else
keyValue = term.asString();
}

rowValues[key] = keyValue;

QMapIterator<QString, Json::Value> it2(componentValues);
while (it2.hasNext())
{
Expand All @@ -184,9 +204,9 @@ Json::Value BoundControlBase::_getTableValueOption(const Terms& terms, const Lis
return result;
}

void BoundControlBase::_setTableValue(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasMultipleTerms)
void BoundControlBase::_setTableValue(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasInteraction, bool keyHasVariables)
{
setBoundValue(_getTableValueOption(terms, componentValuesMap, key, hasMultipleTerms));
setBoundValue(_getTableValueOption(terms, componentValuesMap, key, hasInteraction, keyHasVariables));
}

bool BoundControlBase::_isValueWithTypes(const Json::Value &value) const
Expand Down
4 changes: 2 additions & 2 deletions QMLComponents/boundcontrols/boundcontrolbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class BoundControlBase : public BoundControl
std::string getName() const;
void handleComputedColumn(const Json::Value& value);

Json::Value _getTableValueOption(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasMultipleTerms);
void _setTableValue(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasMultipleTerms);
Json::Value _getTableValueOption(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasInteraction, bool keyHasVariables);
void _setTableValue(const Terms& terms, const ListModel::RowControlsValues& componentValuesMap, const std::string& key, bool hasInteraction, bool keyHasVariables = false);

void _readTableValue(const Json::Value& value, const std::string& key, bool hasMultipleTerms, Terms& terms, ListModel::RowControlsValues& allControlValues);
bool _isValueWithTypes(const Json::Value &value) const;
Expand Down
12 changes: 11 additions & 1 deletion QMLComponents/boundcontrols/boundcontrolterms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,17 @@ void BoundControlTerms::bindTo(const Json::Value &value)
for (Term& term : terms)
{
if (typesPart.size() > i) // If the type is given, use it
term.setType(columnTypeFromString(typesPart[i].asString(), columnType::unknown));
{
if (typesPart[i].isArray())
{
columnTypeVec types;
for (const Json::Value& jsonType : typesPart[i])
types.push_back(columnTypeFromString(jsonType.asString(), columnType::unknown));
term.setTypes(types);
}
else
term.setType(columnTypeFromString(typesPart[i].asString(), columnType::unknown));
}
else
{
if (term.type() == columnType::unknown)
Expand Down
4 changes: 2 additions & 2 deletions QMLComponents/controls/componentslistbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ void ComponentsListBase::termsChangedHandler()
{
JASPListControl::termsChangedHandler();

_setTableValue(_termsModel->terms(), _termsModel->getTermsWithComponentValues(), fq(_optionKey), containsInteractions());
_setTableValue(_termsModel->terms(), _termsModel->getTermsWithComponentValues(), fq(_optionKey), containsInteractions(), containsVariables());
bindOffsets();
emit controlNameXOffsetMapChanged();
}
Expand Down Expand Up @@ -308,7 +308,7 @@ Json::Value ComponentsListBase::getJsonFromComponentValues(const ListModel::RowC
for (const QString& term : termsWithComponentValues.keys())
terms.add(Term::readTerm(term));

return _getTableValueOption(terms, termsWithComponentValues, fq(_optionKey), containsInteractions());
return _getTableValueOption(terms, termsWithComponentValues, fq(_optionKey), containsInteractions(), containsVariables());
}

void ComponentsListBase::addItemHandler()
Expand Down
11 changes: 2 additions & 9 deletions QMLComponents/controls/jasplistcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,13 +299,6 @@ std::vector<std::string> JASPListControl::usedVariables() const
Json::Value JASPListControl::valueTypes() const
{
Json::Value types(Json::arrayValue);
strstrmap variableTypeMap;

// An interaction term has components that can be variables: if the model contains also such variables, the interaction term should get the same types.
// So first check which terms have only 1 component: these terms might be variable names, so keep in a map their types. Use then this map to set the type for interaction terms.
for (const Term& term : model()->terms())
if (term.components().size() == 1)
variableTypeMap[term.asString()] = columnTypeToString(term.type());

for (const Term& term : model()->terms())
{
Expand All @@ -314,8 +307,8 @@ Json::Value JASPListControl::valueTypes() const
else
{
Json::Value compTypes(Json::arrayValue);
for (const std::string& component : term.scomponents())
compTypes.append(variableTypeMap.count(component) > 0 ? variableTypeMap[component] : columnTypeToString(columnType::unknown));
for (columnType type : term.types())
compTypes.append(columnTypeToString(type));
types.append(compTypes);
}
}
Expand Down
42 changes: 29 additions & 13 deletions QMLComponents/models/listmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,9 @@ void ListModel::setVariableType(int ind, columnType type)
if (term.type() == type)
return;

term.setType(type);

emit dataChanged(index(ind, 0), index(ind, 0));
emit columnTypeChanged(term);
Term newTerm(term);
newTerm.setType(type);
sourceColumnTypeChanged(newTerm);
}

columnType ListModel::getVariableType(const QString& name) const
Expand Down Expand Up @@ -613,20 +612,37 @@ void ListModel::sourceNamesChanged(QMap<QString, QString> map)
emit namesChanged(changedNamesMap);
}

int ListModel::sourceColumnTypeChanged(Term sourceTerm)
bool ListModel::sourceColumnTypeChanged(Term sourceTerm)
{
int i = _terms.indexOf(sourceTerm);
if (i >= 0)
bool change = false;
for (int i = 0; i < _terms.size(); i++)
{
Term& term = _terms.at(i);
term.setType(sourceTerm.type());
QModelIndex ind = index(i, 0);
if (term.containsAll(sourceTerm))
{
// A term may have several components: if all sourceTerm's components are in the term's components,
// then for each component in term that is in sourceTerm, replace its type (in term) by the one in sourceTerm
columnTypeVec types = term.types(),
sourceTypes = sourceTerm.types();
int sourceInd = 0;

emit dataChanged(ind, ind, {ListModel::ColumnTypeRole, ListModel::ColumnTypeIconRole, ListModel::ColumnTypeDisabledIconRole});
emit columnTypeChanged(term);
}
for (const QString& sourceComponent : sourceTerm.components())
{
int ind = term.components().indexOf(sourceComponent);
if (ind >= 0 && types.size() > ind && sourceTypes.size() > sourceInd)
types[ind] = sourceTypes[sourceInd];
sourceInd++;
}
term.setTypes(types);
QModelIndex ind = index(i, 0);

emit dataChanged(ind, ind, {ListModel::ColumnTypeRole, ListModel::ColumnTypeIconRole, ListModel::ColumnTypeDisabledIconRole});
emit columnTypeChanged(term);

return i;
change = true;
}
}
return change;
}

bool ListModel::sourceLabelsChanged(QString columnName, QMap<QString, QString> changedLabels)
Expand Down
2 changes: 1 addition & 1 deletion QMLComponents/models/listmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ListModel : public QAbstractTableModel, public VariableInfoConsumer
public slots:
virtual void sourceTermsReset();
virtual void sourceNamesChanged(QMap<QString, QString> map);
virtual int sourceColumnTypeChanged(Term sourceTerm);
virtual bool sourceColumnTypeChanged(Term sourceTerm);
virtual bool sourceLabelsChanged(QString columnName, QMap<QString, QString> changedLabels = {});
virtual bool sourceLabelsReordered(QString columnName);
virtual void sourceColumnsChanged(QStringList columns);
Expand Down
8 changes: 4 additions & 4 deletions QMLComponents/models/listmodelavailableinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@ void ListModelAvailableInterface::sourceColumnsChanged(QStringList columns)
emit columnsChanged(changedColumns);
}

int ListModelAvailableInterface::sourceColumnTypeChanged(Term term)
bool ListModelAvailableInterface::sourceColumnTypeChanged(Term term)
{
int index = ListModelDraggable::sourceColumnTypeChanged(term);
bool change = ListModelDraggable::sourceColumnTypeChanged(term);

if (index == -1 && _allTerms.contains(term))
if (!change && _allTerms.contains(term))
emit columnTypeChanged(term);

return index;
return change;
}

bool ListModelAvailableInterface::sourceLabelsChanged(QString columnName, QMap<QString, QString> changedLabels)
Expand Down
2 changes: 1 addition & 1 deletion QMLComponents/models/listmodelavailableinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public slots:
void sourceTermsReset() override;
void sourceNamesChanged(QMap<QString, QString> map) override;
void sourceColumnsChanged(QStringList columns) override;
int sourceColumnTypeChanged(Term name) override;
bool sourceColumnTypeChanged(Term name) override;
bool sourceLabelsChanged(QString columnName, QMap<QString, QString> = {}) override;
bool sourceLabelsReordered(QString columnName) override;
void removeAssignedModel(ListModelAssignedInterface *assignedModel);
Expand Down
9 changes: 6 additions & 3 deletions QMLComponents/models/term.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ class Term
bool isDraggable() const { return _draggable; }
void setDraggable(bool draggable) { _draggable = draggable; }

columnType type() const { return _type; }
void setType(columnType type) { _type = type; }
// If a term has several components, its type self is unknown, but the components have maybe a type.
columnType type() const { return _types.size() == 1 ? _types[0] : columnType::unknown; }
void setType(columnType type) { _types = {type}; }
columnTypeVec types() const { return _types; }
void setTypes(columnTypeVec types) { _types = types; }

typedef QStringList::const_iterator const_iterator;
typedef QStringList::iterator iterator;
Expand Down Expand Up @@ -81,7 +84,7 @@ class Term
QStringList _components;
QString _asQString;
bool _draggable = true;
columnType _type = columnType::unknown;
columnTypeVec _types = {columnType::unknown};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_types should be settable in the constructor (and can then have `columnType::unknown' as the default argument.

};

#endif // TERM_H
38 changes: 23 additions & 15 deletions QMLComponents/models/terms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void Terms::add(const Term &term, bool isUnique)
else if (result == 0)
{
itr->setDraggable(term.isDraggable());
itr->setType(term.type());
itr->setTypes(term.types());
}
else if (result < 0)
_terms.push_back(term);
Expand All @@ -165,7 +165,7 @@ void Terms::add(const Term &term, bool isUnique)
else
{
_terms.at(i).setDraggable(term.isDraggable());
_terms.at(i).setType(term.type());
_terms.at(i).setTypes(term.types());
}
}
}
Expand Down Expand Up @@ -348,19 +348,21 @@ Terms Terms::crossCombinations() const
do {

vector<string> combination;
columnTypeVec types;

for (uint i = 0; i < _terms.size(); i++) {
if (!v[i])
{
vector<string> components = _terms.at(i).scomponents();
columnTypeVec termTypes = _terms.at(i).types();
combination.insert(combination.end(), components.begin(), components.end());
types.insert(types.end(), termTypes.begin(), termTypes.end());
}
}

if (combination.size() == 1)
t.add(at(indexOf(combination[0])));
else
t.add(Term(combination));
Term newTerm(combination);
newTerm.setTypes(types);
t.add(newTerm);

} while (std::next_permutation(v.begin(), v.end()));
}
Expand All @@ -380,19 +382,21 @@ Terms Terms::wayCombinations(int ways) const
do {

vector<string> combination;
columnTypeVec types;

for (uint i = 0; i < _terms.size(); ++i) {
if (!v[i])
{
vector<string> components = _terms.at(i).scomponents();
columnTypeVec termTypes = _terms.at(i).types();
combination.insert(combination.end(), components.begin(), components.end());
types.insert(types.end(), termTypes.begin(), termTypes.end());
}
}

if (combination.size() == 1)
t.add(at(indexOf(combination[0])));
else
t.add(Term(combination));
Term newTerm(combination);
newTerm.setTypes(types);
t.add(newTerm);

} while (std::next_permutation(v.begin(), v.end()));
}
Expand All @@ -415,11 +419,15 @@ Terms Terms::ffCombinations(const Terms &terms)
{
for (uint j = 0; j < combos.size(); j++)
{
QStringList term = _terms.at(i).components();
QStringList newTerm = combos.at(j).components();

term.append(newTerm);
newTerms.add(Term(term));
QStringList termComponents = _terms.at(i).components();
termComponents.append(combos.at(j).components());
columnTypeVec types = _terms.at(i).types();
columnTypeVec coTypes = combos.at(j).types();
types.insert(types.end(), coTypes.begin(), coTypes.end());

Term newTerm(termComponents);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not expand the constructor?

newTerm.setTypes(types);
newTerms.add(newTerm);
}
}

Expand Down
Loading