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

Add support for pulldata with local entities #6451

Merged
merged 10 commits into from
Oct 17, 2024

Conversation

seadowg
Copy link
Member

@seadowg seadowg commented Oct 10, 2024

Closes #6443
Blocked by getodk/javarosa#802

Why is this the best possible solution? Were any other approaches considered?

I'd initially wanted to implement this by just executing a dynamically built XPath expression, but this proved tricky as JavaRosa currently doesn't attach secondary instances that aren't referenced by an instance expression. This would mean that a form that contained only a pulldata referencing an entity list wouldn't work (as the external instance would never be parsed and attached to the main instance). We'll look to move to the originally intended implementation in the next release as part of #6454.

Instead, I've ended up using the LocalEntityInstanceAdapter in a similar way to LocalEntityFilterStrategy with modifications so that it now supports querying any child of an entity item (like label for example). This new support doesn't add anything significant in the way of optimisation however: queries for children like label work by just loading every entity in the list from the DB and then performing an in-mem filter. This limits the amount of work we do to just support pulldata.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

The big risks here will be to all pulldata forms (including ones that use with entities obviously) and to entity filters in forms in general.

One thing to note: I've not extended the instance "normalising" that pulldata does to entity lists. For normal secondary instances, pulldata would let you use the wrong case for the name or add .csv to the end. We'll discuss if we want to maintain this (currently undocumented) behaviour separately.

Before submitting this PR, please make sure you have:

  • added or modified tests for any new or changed behavior
  • run ./gradlew connectedAndroidTest (or ./gradlew testLab) and confirmed all checks still pass
  • added a comment above any new strings describing it for translators
  • added any new strings with date formatting to DateFormatsTest
  • verified that any code or assets from external sources are properly credited in comments and/or in the about file.
  • verified that any new UI elements use theme colors. UI Components Style guidelines

@seadowg seadowg force-pushed the entities-pulldata branch 2 times, most recently from 7eb396e to bed7c86 Compare October 14, 2024 09:10
@seadowg seadowg marked this pull request as ready for review October 14, 2024 13:39
@seadowg seadowg requested a review from grzesiek2010 October 14, 2024 13:39
val filterValue = XPathFuncExpr.toString(args[3])

return if (instanceAdapter.supportsInstance(instanceId)) {
instanceAdapter.queryEq(instanceId, filterChild, filterValue)!!.firstOrNull()
Copy link
Member

Choose a reason for hiding this comment

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

It looks like queryEq can't return null values so we could update that function by removing ? from the returned type and then get rid of those !!.

@@ -33,62 +33,82 @@ class LocalEntitiesInstanceAdapter(private val entitiesRepository: EntitiesRepos
}
} else {
entitiesRepository.getEntities(instanceId).map { entity ->
convertToElement(entity, false)
convertToElement(entity)
}
}
}

fun queryEq(instanceId: String, child: String, value: String): List<TreeElement>? {
return when {
Copy link
Member

Choose a reason for hiding this comment

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

You can now simplify this by adding child as a subject of when:

return when (child) {
            EntityItemElement.ID ->...


override fun eval(args: Array<Any>, ec: EvaluationContext): Any {
val instanceId = XPathFuncExpr.toString(args[0])
val child = XPathFuncExpr.toString(args[1])
Copy link
Member

Choose a reason for hiding this comment

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

I think you can move extracting child, filterChild and filterValue down and call only if instanceAdapter.supportsInstance(instanceId) returns true.

@@ -23,7 +23,7 @@ class LocalEntitiesInstanceAdapter(private val entitiesRepository: EntitiesRepos

0.until(count).map {
if (it == 0) {
convertToElement(first, true)
convertToElement(first)
} else {
TreeElement("item", it, true)
Copy link
Member

Choose a reason for hiding this comment

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

Now I wonder if we need that isPartial flag at all... such elements should have at least label and value right so if they don't have it we could treat them as partial and try to populate.

Copy link
Member Author

Choose a reason for hiding this comment

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

The partial concept is currently too general for something like that as any TreeElement can be "partial". It's not confined to secondary instances with item children.

@@ -23,7 +23,7 @@ class LocalEntitiesInstanceAdapter(private val entitiesRepository: EntitiesRepos

0.until(count).map {
if (it == 0) {
convertToElement(first, true)
Copy link
Member

Choose a reason for hiding this comment

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

Now I wonder if we need that isPartial flag at all... such elements should have at least label and value right so if they don't have it we could treat them as partial and try to populate.
It is set to true in only one place.

Copy link
Member Author

Choose a reason for hiding this comment

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


val instanceAdapter = LocalEntitiesInstanceAdapter(entitiesRepository)
val results = instanceAdapter.queryEq("things", EntityItemElement.LABEL, "Thing 2")
assertThat(results!!.size, equalTo(1))
Copy link
Member

Choose a reason for hiding this comment

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

!! is unnecessary here. The same in other tests.

Copy link
Member

@lognaturel lognaturel left a comment

Choose a reason for hiding this comment

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

I've taken a quick look through and discussed briefly with @grzesiek2010 who says his comments could be addressed in follow-up. I'm going to merge this now so it can be part of our next beta to maximize time to get user feedback.

@lognaturel lognaturel merged commit 4603e67 into getodk:master Oct 17, 2024
6 checks passed
@seadowg seadowg deleted the entities-pulldata branch October 18, 2024 08:11
@seadowg
Copy link
Member Author

seadowg commented Oct 18, 2024

@lognaturel @grzesiek2010 I'll add an issue to follow up.

@WKobus
Copy link

WKobus commented Oct 22, 2024

Tested with success

Verified on device with Android 15

Verified cases:

  • Entity update forms with pulldata
  • Entity select forms with pulldata
  • Pulldata in non-entity forms
  • Entiy forms with filters

@dbemke
Copy link

dbemke commented Oct 22, 2024

Tested with success

Verified on device with Android 10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

pulldata should support local entities
5 participants