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

Jt/visually segmented case search group #1389

Merged
merged 16 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
7 changes: 7 additions & 0 deletions src/cli/java/org/commcare/util/screen/QueryScreen.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.commcare.session.RemoteQuerySessionManager;
import org.commcare.suite.model.RemoteQueryDatum;
import org.commcare.suite.model.QueryPrompt;
import org.commcare.suite.model.QueryGroup;
import org.javarosa.core.model.instance.ExternalDataInstance;
import org.javarosa.core.model.instance.ExternalDataInstanceSource;
import org.javarosa.core.services.locale.Localization;
Expand All @@ -44,6 +45,7 @@ public class QueryScreen extends Screen {

private RemoteQuerySessionManager remoteQuerySessionManager;
protected OrderedHashtable<String, QueryPrompt> userInputDisplays;
protected OrderedHashtable<String, QueryGroup> groupHeaders;
private SessionWrapper sessionWrapper;
private String[] fields;
private String mTitle;
Expand Down Expand Up @@ -94,6 +96,7 @@ public void init(SessionWrapper sessionWrapper) throws CommCareSessionException
mTitle = getTitleLocaleString();
description = getDescriptionLocaleString();
dynamicSearch = getQueryDatum().getDynamicSearch();
groupHeaders = getQueryDatum().getUserQueryGroupHeaders();
}

private String getTitleLocaleString() {
Expand Down Expand Up @@ -262,6 +265,10 @@ public OrderedHashtable<String, QueryPrompt> getUserInputDisplays() {
return userInputDisplays;
}

public OrderedHashtable<String, QueryGroup> getGroupHeaders() {
return groupHeaders;
}

public String getCurrentMessage() {
return currentMessage;
}
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/org/commcare/suite/model/QueryGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.commcare.suite.model;

import org.javarosa.core.util.externalizable.DeserializationException;
import org.javarosa.core.util.externalizable.ExtUtil;
import org.javarosa.core.util.externalizable.Externalizable;
import org.javarosa.core.util.externalizable.PrototypeFactory;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

// Model for <group> node
public class QueryGroup implements Externalizable {

private String key;
private DisplayUnit display;

@SuppressWarnings("unused")
public QueryGroup() {
}

public QueryGroup(String key, DisplayUnit display) {
this.key = key;
this.display = display;
}

@Override
public void readExternal(DataInputStream in, PrototypeFactory pf)
throws IOException, DeserializationException {
key = (String)ExtUtil.read(in, String.class, pf);
display = (DisplayUnit)ExtUtil.read(in, DisplayUnit.class, pf);
}

@Override
public void writeExternal(DataOutputStream out) throws IOException {
ExtUtil.write(out, key);
ExtUtil.write(out, display);
}

public String getKey() {
return key;
}

public DisplayUnit getDisplay() {
return display;
}
}
13 changes: 12 additions & 1 deletion src/main/java/org/commcare/suite/model/QueryPrompt.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ public class QueryPrompt implements Externalizable {
@Nullable
private QueryPromptCondition validation;

@Nullable
private String groupKey;

@SuppressWarnings("unused")
public QueryPrompt() {
}

public QueryPrompt(String key, String appearance, String input, String receive,
String hidden, DisplayUnit display, ItemsetBinding itemsetBinding,
XPathExpression defaultValueExpr, boolean allowBlankValue, XPathExpression exclude,
QueryPromptCondition required, QueryPromptCondition validation) {
QueryPromptCondition required, QueryPromptCondition validation, String groupKey) {
this.key = key;
this.appearance = appearance;
this.input = input;
Expand All @@ -94,6 +97,7 @@ public QueryPrompt(String key, String appearance, String input, String receive,
this.exclude = exclude;
this.required = required;
this.validation = validation;
this.groupKey = groupKey;
}

@Override
Expand All @@ -111,6 +115,7 @@ public void readExternal(DataInputStream in, PrototypeFactory pf)
exclude = (XPathExpression)ExtUtil.read(in, new ExtWrapNullable(new ExtWrapTagged()), pf);
validation = (QueryPromptCondition)ExtUtil.read(in, new ExtWrapNullable(QueryPromptCondition.class), pf);
required = (QueryPromptCondition)ExtUtil.read(in, new ExtWrapNullable(QueryPromptCondition.class), pf);
groupKey = (String)ExtUtil.read(in, new ExtWrapNullable(String.class), pf);
}

@Override
Expand All @@ -128,6 +133,7 @@ public void writeExternal(DataOutputStream out) throws IOException {
ExtUtil.write(out, new ExtWrapNullable(exclude == null ? null : new ExtWrapTagged(exclude)));
ExtUtil.write(out, new ExtWrapNullable(validation));
ExtUtil.write(out, new ExtWrapNullable(required));
ExtUtil.write(out, new ExtWrapNullable(groupKey));
}

public String getKey() {
Expand Down Expand Up @@ -186,6 +192,11 @@ public QueryPromptCondition getValidation() {
return validation;
}

@Nullable
public String getGroupKey() {
return groupKey;
}

/**
* @return whether the prompt has associated choices to select from
*/
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/org/commcare/suite/model/RemoteQueryDatum.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
public class RemoteQueryDatum extends SessionDatum {
private List<QueryData> hiddenQueryValues;
private OrderedHashtable<String, QueryPrompt> userQueryPrompts;
private OrderedHashtable<String, QueryGroup> userQueryGroupHeaders;
private boolean useCaseTemplate;
private boolean defaultSearch;
private boolean dynamicSearch;
Expand All @@ -46,10 +47,12 @@ public RemoteQueryDatum() {
public RemoteQueryDatum(URL url, String storageInstance,
List<QueryData> hiddenQueryValues,
OrderedHashtable<String, QueryPrompt> userQueryPrompts,
boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description) {
boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description,
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
OrderedHashtable<String, QueryGroup> userQueryGroupHeaders) {
super(storageInstance, url.toString());
this.hiddenQueryValues = hiddenQueryValues;
this.userQueryPrompts = userQueryPrompts;
this.userQueryGroupHeaders = userQueryGroupHeaders;
this.useCaseTemplate = useCaseTemplate;
this.defaultSearch = defaultSearch;
this.dynamicSearch = dynamicSearch;
Expand All @@ -61,6 +64,10 @@ public OrderedHashtable<String, QueryPrompt> getUserQueryPrompts() {
return userQueryPrompts;
}

public OrderedHashtable<String, QueryGroup> getUserQueryGroupHeaders() {
return userQueryGroupHeaders;
}

public List<QueryData> getHiddenQueryValues() {
return hiddenQueryValues;
}
Expand Down Expand Up @@ -104,6 +111,9 @@ public void readExternal(DataInputStream in, PrototypeFactory pf)
userQueryPrompts =
(OrderedHashtable<String, QueryPrompt>)ExtUtil.read(in,
new ExtWrapMap(String.class, QueryPrompt.class, ExtWrapMap.TYPE_ORDERED), pf);
userQueryGroupHeaders =
(OrderedHashtable<String, QueryGroup>)ExtUtil.read(in,
new ExtWrapMap(String.class, QueryGroup.class, ExtWrapMap.TYPE_ORDERED), pf);
title = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf);
description = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf);
useCaseTemplate = ExtUtil.readBool(in);
Expand All @@ -116,6 +126,7 @@ public void writeExternal(DataOutputStream out) throws IOException {
super.writeExternal(out);
ExtUtil.write(out, new ExtWrapList(hiddenQueryValues, new ExtWrapTagged()));
ExtUtil.write(out, new ExtWrapMap(userQueryPrompts));
ExtUtil.write(out, new ExtWrapMap(userQueryGroupHeaders));
ExtUtil.write(out, new ExtWrapNullable(title));
ExtUtil.write(out, new ExtWrapNullable(description));
ExtUtil.writeBool(out, useCaseTemplate);
Expand Down
38 changes: 38 additions & 0 deletions src/main/java/org/commcare/xml/QueryGroupParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.commcare.xml;

import org.commcare.suite.model.DisplayUnit;
import org.commcare.suite.model.QueryGroup;
import org.javarosa.xml.util.InvalidStructureException;
import org.javarosa.xml.util.UnfullfilledRequirementsException;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

public class QueryGroupParser extends CommCareElementParser<QueryGroup> {

private static final String NAME_PROMPT = "group";
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
private static final String ATTR_KEY = "key";
private static final String NAME_DISPLAY = "display";

public QueryGroupParser(KXmlParser parser) {
super(parser);
}

@Override
public QueryGroup parse() throws InvalidStructureException, IOException, XmlPullParserException,
UnfullfilledRequirementsException {
checkNode("group");
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this use NAME_PROMPT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes 2b4ba65


String key = parser.getAttributeValue(null, ATTR_KEY);
DisplayUnit display = null;

while (nextTagInBlock(NAME_PROMPT)) {
if (NAME_DISPLAY.equalsIgnoreCase(parser.getName())) {
display = parseDisplayBlock();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain what this block is doing? It seems like display may get set multiple times here, not sure to what end.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The suite would look like this for a group block. nextTagInblock iterates through the elements within group (it automatically iterates to the next element within the block without needing to increment anything in the body of the while loop). The first would be display and that whole display block will be parsed via parseDisplayBlock. The parsed values will then be used to created an instance of QueryGroup so that instance can be used to pull values as needed.

<group key="group_header_0">
    <display>
      <text>
          <locale id="search_property.m0.group_header_0"/>
      </text>
    </display>
</group>

You're right that if there were multiple display blocks in the group element, only the last display block will be saved. But there should be only one


return new QueryGroup(key, display);
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
}
}
4 changes: 3 additions & 1 deletion src/main/java/org/commcare/xml/QueryPromptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class QueryPromptParser extends CommCareElementParser<QueryPrompt> {
private static final String ATTR_REQUIRED = "required";
private static final String ATTR_VALIDATION_TEST = "test";
private static final String NAME_TEXT = "text";
private static final String ATTR_GROUP_KEY = "group_key";

public QueryPromptParser(KXmlParser parser) {
super(parser);
Expand All @@ -60,6 +61,7 @@ public QueryPrompt parse() throws InvalidStructureException, IOException, XmlPul
XPathExpression defaultValue = xpathPropertyValue(defaultValueString);
String excludeValueString = parser.getAttributeValue(null, ATTR_EXCLUDE);
XPathExpression exclude = xpathPropertyValue(excludeValueString);
String groupKey = parser.getAttributeValue(null, ATTR_GROUP_KEY);
XPathExpression oldRequired = xpathPropertyValue(parser.getAttributeValue(null, ATTR_REQUIRED));

QueryPromptCondition validation = null;
Expand All @@ -86,7 +88,7 @@ public QueryPrompt parse() throws InvalidStructureException, IOException, XmlPul

return new QueryPrompt(key, appearance, input, receive, hidden, display,
itemsetBinding, defaultValue, allowBlankValue, exclude,
required, validation);
required, validation, groupKey);
}

private QueryPromptCondition parseRequiredBlock(String key)
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/commcare/xml/SessionDatumParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.commcare.suite.model.FormIdDatum;
import org.commcare.suite.model.MultiSelectEntityDatum;
import org.commcare.suite.model.QueryData;
import org.commcare.suite.model.QueryGroup;
import org.commcare.suite.model.QueryPrompt;
import org.commcare.suite.model.RemoteQueryDatum;
import org.commcare.suite.model.SessionDatum;
Expand Down Expand Up @@ -129,6 +130,7 @@ private RemoteQueryDatum parseRemoteQueryDatum()
boolean dynamicSearch = "true".equals(parser.getAttributeValue(null, "dynamic_search"));
Text title = null;
Text description = null;
OrderedHashtable<String, QueryGroup> groupPrompts = new OrderedHashtable<>();
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
ArrayList<QueryData> hiddenQueryValues = new ArrayList<QueryData>();
while (nextTagInBlock("query")) {
String tagName = parser.getName();
Expand All @@ -143,9 +145,12 @@ private RemoteQueryDatum parseRemoteQueryDatum()
} else if ("description".equals(tagName)) {
nextTagInBlock("description");
description = new TextParser(parser).parse();
} else if ("group".equals(tagName)){
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
String key = parser.getAttributeValue(null, "key");
shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
groupPrompts.put(key, new QueryGroupParser(parser).parse());
}
}
return new RemoteQueryDatum(queryUrl, queryResultStorageInstance, hiddenQueryValues,
userQueryPrompts, useCaseTemplate, defaultSearch, dynamicSearch, title, description);
userQueryPrompts, useCaseTemplate, defaultSearch, dynamicSearch, title, description, groupPrompts);
}
}
21 changes: 21 additions & 0 deletions src/test/java/org/commcare/xml/QueryDataParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import org.commcare.suite.model.QueryData;
import org.commcare.suite.model.QueryPrompt;
import org.commcare.suite.model.QueryGroup;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.DataInstance;
import org.javarosa.xml.util.InvalidStructureException;
Expand Down Expand Up @@ -143,4 +144,24 @@ public void testParseQueryData_badNesting() throws XmlPullParserException, IOExc
} catch (InvalidStructureException ignored) {
}
}

@Test
public void testParseValueData_withGroupKeyAttribute()
throws InvalidStructureException, XmlPullParserException,
IOException, UnfullfilledRequirementsException {
String query = "<prompt key=\"name\" group_key=\"group_header_1\"></prompt>";
QueryPromptParser parser = ParserTestUtils.buildParser(query, QueryPromptParser.class);
QueryPrompt queryData = parser.parse();
assertEquals("group_header_1", queryData.getGroupKey());
}

@Test
public void testParseValueData_withGroup()
throws InvalidStructureException, XmlPullParserException,
IOException, UnfullfilledRequirementsException {
String query = "<group key=\"group_header_0\"></group>";
QueryGroupParser parser = ParserTestUtils.buildParser(query, QueryGroupParser.class);
QueryGroup queryData = parser.parse();
assertEquals("group_header_0", queryData.getKey());
}
}