Coder Social home page Coder Social logo

Comments (10)

Pariecemckinney-apple avatar Pariecemckinney-apple commented on August 25, 2024

Hello @InfuriatingYetti,

I'd be happy to help. If I'm understanding correctly, it seems that you're attempting to:

  • present a task with one form within it
  • based on the result of that form direct the user to another form thats within a different task (Choice2QuestionStep)

If so, this is different than the expected flow for a navigable task. All navigation should be based on steps within the same ORKTask. So in your case the flow would look something like this I believe:

  1. create a task (with steps: [choices1, choices2, completionStep]) and present it
  2. user answers the item in choices1
  3. at this point the user will either be directed to the choices2 step or skip it based on the predicates you passed in. (* as opposed to an entirely new task being presented)

If you take a look the ORKCatalog app in the TaskListRow.swift file there is a good example of a predicate being implemented for the "private var eligibilityTask: ORKTask" variable. That should definitely help demystify things. If I misunderstood anything or if you simply have more questions please let me know. Hope this helps!

from carekit.

InfuriatingYetti avatar InfuriatingYetti commented on August 25, 2024

Hi @Pariecemckinney-apple!

Thank you so much for helping me out - it's a pleasure and thank you for the insight!! :) I checked out the TaskListRow.swift file in ResearchKit. From my understanding when using the ORKTask for approach (For example: TaskListRow.swift: steps += [questionStep3]), it will only show one question at a time, like in this example from the ResearchKit.org website (is that correct?):

Screenshot 2023-02-21 at 8 55 49 PM

Is it possible to navigate with this kind of set up below:

Screenshot 2023-02-21 at 8 41 35 PM

For example, if the user selects Severe or maybe something other than None it would either add additional questions to the two shown or open another set of questions like the two shown.

Thank you!

from carekit.

Pariecemckinney-apple avatar Pariecemckinney-apple commented on August 25, 2024

You're correct that you would have to show one page at a time for your use case. Conditional questions within one step isn't something ResearchKit supports. That does sound useful however.

from carekit.

pthealthtech avatar pthealthtech commented on August 25, 2024

This functionality would be very useful.

from carekit.

InfuriatingYetti avatar InfuriatingYetti commented on August 25, 2024

@pthealthtech @Pariecemckinney-apple

I was looking at the ORKOrderedTask+ORKPredefinedActiveTask.m inside ResearchKit and see that there is a way to prompt an ORKInstructionStep from an ORKTextChoice. I think this compliments what @erik-apple did in the WWDC 21 for the Range of Motion task using the options: [.excludeConclusion] to show a customized ORKInstructionStep:

From ORKOrderedTask+ORKPredefinedActiveTask.m:

     NSArray *textChoices = @[ [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE"],
                                  [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_2", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_2"],
                                  [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_3", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_3"],
                                  [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_4", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_4"],
                                  [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_5", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_5"],
                                  [ORKTextChoice choiceWithText:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_CHOICE_6", nil) value:@"TIMED_WALK_QUESTION_2_CHOICE_6"] ];
        ORKAnswerFormat *answerFormat2 = [ORKAnswerFormat valuePickerAnswerFormatWithTextChoices:textChoices];

        ORKFormItem *formItem2 = [[ORKFormItem alloc] initWithSectionTitle:ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TITLE", nil) detailText:nil learnMoreItem:nil showsProgress:YES];

        ORKFormItem *formItem3 = [[ORKFormItem alloc] initWithIdentifier:ORKTimedWalkFormAssistanceStepIdentifier
                                                                    text:nil
                                                            answerFormat:answerFormat2];
        formItem3.placeholder = ORKLocalizedString(@"TIMED_WALK_QUESTION_2_TEXT", nil);
        formItem3.optional = NO;

        step.formItems = @[formItem1, formItem2, formItem3];
        step.optional = NO;

        ORKStepArrayAddStep(steps, step);
    }

With the ORKInstructionStep :

`   if (!(options & ORKPredefinedTaskOptionExcludeInstructions)) {
        {
            ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier];
            step.title = ORKLocalizedString(@"TIMED_WALK_TITLE", nil);
            step.text = [NSString localizedStringWithFormat:ORKLocalizedString(@"TIMED_WALK_INTRO_2_TEXT_%@", nil), formattedLength];
            step.detailText = ORKLocalizedString(@"TIMED_WALK_INTRO_2_DETAIL", nil);
            step.image = [UIImage imageNamed:@"timer" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
            step.imageContentMode = UIViewContentModeCenter;
            step.shouldTintImages = YES;

            ORKStepArrayAddStep(steps, step);
        }
    }`

I was thinking, maybe you could change the ORKInstructionStep *step = [[ORKInstructionStep alloc] initWithIdentifier:ORKInstruction1StepIdentifier to relate to a specific ORKTextChoice answer response value(s)...from there would there be a way to modify the ORKInstructionStep to show a second ORKTextChoice form as opposed to just text??

Going back to what @erik-apple did in the WWDC 21, here is an example using ORKInstructionStep for the kneeModel (Range of Motion) in Survey.swift using the options: [.excludeConclusion]:, although, he just used text:

// MARK: Range of Motion.

static func rangeOfMotionCheck() -> ORKTask {
    
    let rangeOfMotionOrderedTask = ORKOrderedTask.kneeRangeOfMotionTask(
        withIdentifier: "rangeOfMotionTask",
        limbOption: .left,
        intendedUseDescription: nil,
        options: [.excludeConclusion]
    )
    
    let completionStep = ORKCompletionStep(identifier: "rom.completion")
    completionStep.title = "All done!"
    completionStep.detailText = "We know the road to recovery can be tough. Keep up the good work!"
    
    rangeOfMotionOrderedTask.appendSteps([completionStep])
    
    return rangeOfMotionOrderedTask
}

static func extractRangeOfMotionOutcome(
    _ result: ORKTaskResult) -> [OCKOutcomeValue]? {
        
        guard let motionResult = result.results?
            .compactMap({ $0 as? ORKStepResult })
            .compactMap({ $0.results })
            .flatMap({ $0 })
            .compactMap({ $0 as? ORKRangeOfMotionResult })
            .first else {
            
            assertionFailure("Failed to parse range of motion result")
            return nil
        }
        
        var range = OCKOutcomeValue(motionResult.range)
        range.kind = #keyPath(ORKRangeOfMotionResult.range)
        
        return [range]
    }

// MARK: 3D Knee Model

static func kneeModel() -> ORKTask {
    
    let instructionStep = ORKInstructionStep(
        identifier: "insights.instructionStep"
    )
    instructionStep.title = "Your Injury Visualized"
    instructionStep.detailText = "A 3D model will be presented to give you better insights on your specific injury."
    instructionStep.iconImage = UIImage(systemName: "bandage")
    
    let modelManager = ORKUSDZModelManager(usdzFileName: "toy_robot_vintage")
    
    let kneeModelStep = ORK3DModelStep(
        identifier: "insights.kneeModel",
        modelManager: modelManager
    )
    
    let kneeModelTask = ORKOrderedTask(
        identifier: "insights",
        steps: [instructionStep, kneeModelStep]
    )
    
    return kneeModelTask
    
}
}

I don't know if it would be possible to mod the instruction step like this to show an ORKTextChoice survey, what do you guys think? Could it be possible?!

Thank you!

from carekit.

InfuriatingYetti avatar InfuriatingYetti commented on August 25, 2024

I think I've got something going here!

Here is a form that is navigable to a second form - in the same window! After selecting the answer response in the first form and clicking Next, and after selecting the answer in the second form and clicking Next, you go to a completion step! ....also, all in the same window!

The problem I am having is while the code isn't giving me any errors, the code is not using logic to either skip the second question if I select None, where it should take me to the completionStep., or to the next form if I select Severe. I know it was said it couldn't be done, but I live outside the box and want to see how we can make this work :)

challenge-accepted-challenge

Under task.setNavigationRule(navigationRule1, forTriggerStepIdentifier: formStep1.identifier) if I change formStep1. identifier to completionStep.identifier it will cause me to skip the second form and go from the first form to the completionStep...what I am I missing here?!

anyone-answer

  import CareKitStore
  import ResearchKit
  
  struct ChoiceQuestions2 {
  
      static let choiceIdentifier = "choice"
      static let choiceFormIdentifier = "choice.form"
      static let choiceQuestion1Step = "checkin.form.choicequestion1"
      static let choiceStep = "checkin.form.Choice"
  
      static let choice2Identifier = "choice2"
      static let choice2FormIdentifier = "choice2.form"
      static let choice2Question1Step = "checkin.form.choice2question1"
      static let choice2Step = "checkin.form.Choice2"
      static let choice2CompletionStepIdentifier = "choice2CompletionStep"
  
      static func Choices2() -> ORKTask {
          let Choices = [
              ORKTextChoice(text: "None", value: 0 as NSNumber),
              ORKTextChoice(text: "Slight", value: 1 as NSNumber),
              ORKTextChoice(text: "Mild", value: 2 as NSNumber),
              ORKTextChoice(text: "Severe", value: 3 as NSNumber)
          ]
  
          let choiceAnswerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: Choices)
  
          let choiceStep = ORKFormItem(identifier: choiceQuestion1Step, text: "Choices 2 Text question goes here", answerFormat: choiceAnswerFormat)
          choiceStep.isOptional = false
  
          let formStep = ORKFormStep(identifier: choiceFormIdentifier, title: "Choice Question", text: "Instructions for questions")
          formStep.formItems = [choiceStep]
          formStep.isOptional = false
  
          let Choices2 = [
              ORKTextChoice(text: "None", value: 0 as NSNumber),
              ORKTextChoice(text: "Slight", value: 1 as NSNumber),
              ORKTextChoice(text: "Mild", value: 2 as NSNumber),
              ORKTextChoice(text: "Severe", value: 3 as NSNumber)
          ]
  
          let choiceAnswerFormat2 =  ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: Choices2)
  
          let choice2Step = ORKFormItem(identifier: choice2Question1Step, text: "Choices 1 Text question goes here", answerFormat: choiceAnswerFormat2)
          choice2Step.isOptional = false
  
          let form2Step = ORKFormStep(identifier: choice2FormIdentifier, title: "Choice Question", text: "Instructions for questions")
          form2Step.formItems = [choice2Step]
          form2Step.isOptional = false
  
          let completionStep = ORKCompletionStep(identifier: choice2CompletionStepIdentifier)
          completionStep.title = "Thank you!"
          completionStep.text = "Your responses have been recorded."
  
          let predicateFormat = ORKResultPredicate.predicateForChoiceQuestionResult(with: ORKResultSelector(resultIdentifier: choice2Question1Step), expectedAnswerValue: 3 as NSNumber)
          let navigationRule = ORKPredicateStepNavigationRule(resultPredicatesAndDestinationStepIdentifiers: [(predicateFormat, choiceStep.identifier)], defaultStepIdentifierOrNil: completionStep.identifier)
  
  
  
          let task = ORKNavigableOrderedTask(identifier: choice2Identifier, steps: [form2Step, formStep,  completionStep])
          task.setNavigationRule(navigationRule, forTriggerStepIdentifier: formStep.identifier)
  
          return task
      }
  }

Anyone have any ideas?
AchingExhaustedIcelandgull-size_restricted

lol you all are great - thank you for the help so far!!!

from carekit.

InfuriatingYetti avatar InfuriatingYetti commented on August 25, 2024

This is the best I could come up with using a step navigable form. I am having trouble though passing the Value to CareKit. I think it's almost there, but need a little guidance please! :)

When I answer Question 1 as "Severe", value: 3 I am successfully navigated to Question 2.
After selecting the an answer on Question 2 and press Next I am navigated to completeStep and once I click Done I get the error: Thread 1: Fatal error: Failed to extract answers from check in survey!; however, print("Result: \(result)") under static func extractAnswersFromChoicesi( is showing both answers from Question1 and Question 2 in the console. I selected answer Value: 3 and answer Value: 2 seen below:

Screenshot 2023-03-09 at 12 42 22 PM

If on Question 1 I select "None", "Slight", "Mild" and press Next I am navigated to completeStep and once I click Done, I again get the error: Thread 1: Fatal error: Failed to extract answers from check in survey!; however, print("Result: \(result)") under static func extractAnswersFromChoicesi( is the answer from Question1 in the console. I selected answer Value: 2 seen below:

Screenshot 2023-03-09 at 12 43 26 PM

This is a mod from what @gavirawson-apple helped me with in the other tread and I tried adapting it to the navigable form. It's interesting to me because it seems that it is able to extract the answers from the form initially as I can get it to print (seen above), but it isn't able to process the data in this area:

 guard
                let choiceResult = result.results?
                    .compactMap({ $0 as? ORKStepResult })
                    .first(where: { $0.identifier == choiceFormIdentifier }),
                
                    let scale2Results = choiceResult
                    .results?
                    .compactMap({ $0 as? ORKChoiceQuestionResult }),
//1
                    let selectedChoices = scale2Results
                    .first(where: { $0.identifier == ChoiceQuestion1Step })?
                    // This line was the key!
                    .choiceAnswers as? [NSNumber],
//2
                    let selectedChoices2 = scale2Results
                    .first(where: { $0.identifier == ChoiceQuestion2Step })?
                    // This line was the key!
                    .choiceAnswers as? [NSNumber]

Here's the full code:

import CareKitStore
import ResearchKit

struct ChoiceQuestions3 {

    static let choiceIdentifier = "choice"
    static let choiceFormIdentifier = "choice.form"
    static let ChoiceQuestion1Step = "checkin.form.choicequestion1"
    static let ChoiceQuestion2Step = "checkin.form.choicequestion2"
    static let choiceCompletionStep = "checkin.form.choiceCompletionStep"

    static func Choices2() -> ORKTask {
        
        var steps = [ORKStep]()
        
        // Question 1
        let question1 = ORKQuestionStep(identifier: ChoiceQuestion1Step)
        question1.title = "Choices 1 Text question goes here"
        let choices = [
            ORKTextChoice(text: "None", value: 0 as NSNumber),
            ORKTextChoice(text: "Slight", value: 1 as NSNumber),
            ORKTextChoice(text: "Mild", value: 2 as NSNumber),
            ORKTextChoice(text: "Severe", value: 3 as NSNumber)
        ]
        question1.answerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: choices)
        steps += [question1]
        
        // Question 2
        let question2 = ORKQuestionStep(identifier: ChoiceQuestion2Step)
        question2.title = "Choices 2 Text question goes here"
        question2.answerFormat = ORKAnswerFormat.choiceAnswerFormat(with: .singleChoice, textChoices: choices)
        steps += [question2]
        
        // Completion step
        let completionStep = ORKCompletionStep(identifier: choiceCompletionStep)
        completionStep.title = "Thank you!"
        completionStep.text = "Your responses have been recorded."
        steps += [completionStep]
        
        // Navigation
        let predicateFormat = ORKResultPredicate.predicateForChoiceQuestionResult(
            with: ORKResultSelector(resultIdentifier: ChoiceQuestion1Step),
            expectedAnswerValue: 3 as NSNumber
        )
        let navigationRule = ORKPredicateStepNavigationRule(
            resultPredicatesAndDestinationStepIdentifiers:
                [(predicateFormat, ChoiceQuestion2Step)],
            defaultStepIdentifierOrNil: choiceCompletionStep
        )
        
        let task = ORKNavigableOrderedTask(identifier: choiceFormIdentifier, steps: steps)
        task.setNavigationRule(navigationRule, forTriggerStepIdentifier: ChoiceQuestion1Step)
        
        return task
        
    }
        
    static func extractAnswersFromChoicesi(
        _ result: ORKTaskResult) -> [OCKOutcomeValue]? {
            
            print("Result: \(result)") // <--- Is printing the correct selection from the ORKTextChoice selection
            
            guard
                let choiceResult = result.results?
                    .compactMap({ $0 as? ORKStepResult })
                    .first(where: { $0.identifier == choiceFormIdentifier }),
                
                    let scale2Results = choiceResult
                    .results?
                    .compactMap({ $0 as? ORKChoiceQuestionResult }),
//1
                    let selectedChoices = scale2Results
                    .first(where: { $0.identifier == ChoiceQuestion1Step })?
                    // This line was the key!
                    .choiceAnswers as? [NSNumber],
//2
                    let selectedChoices2 = scale2Results
                    .first(where: { $0.identifier == ChoiceQuestion2Step })?
                    // This line was the key!
                    .choiceAnswers as? [NSNumber]

             
            else {
                assertionFailure("Failed to extract answers from check in survey!")
                return nil
            }
 //1
            // Convert the choice values to CareKit outcome values
            let outcomeValue = selectedChoices.map { selectedChoice -> OCKOutcomeValue in

                    var outcomeValue = OCKOutcomeValue(Double(truncating: selectedChoice))
            
            // Set the kind here!
            outcomeValue.kind = ChoiceQuestion1Step
            
            return outcomeValue
        }
 //2
            // Convert the choice values to CareKit outcome values
            let outcomeValue2 = selectedChoices2.map { selectedChoice -> OCKOutcomeValue in

                    var outcomeValue2 = OCKOutcomeValue(Double(truncating: selectedChoice))
            
            // Set the kind here!
            outcomeValue2.kind = ChoiceQuestion2Step
            
            return outcomeValue2
        }

            
                // makes an array style ie. [3.0, 2.0]
                let outcomeTotal = outcomeValue + outcomeValue2
 

            
                print()
                print(outcomeValue)
                print(outcomeValue2)
                return outcomeTotal
            }

Does anyone have any ideas??

Thank you!

from carekit.

InfuriatingYetti avatar InfuriatingYetti commented on August 25, 2024

An idea I had was maybe I need to somehow have each question step extract the data along the way as opposed to trying to extract it all at the end. The thought being, does each question step act as as it's own survey form (conceptually)...I messed around with it for a little bit last night trying to extract the data at each step, but wasn't able to get it work quite right still - had a bit of errors I'll have to figure out.

Theoretically, to get this to work would I need to extract each question's data along the way or is it possible to get it to extract all of the data at the end?

from carekit.

Pariecemckinney-apple avatar Pariecemckinney-apple commented on August 25, 2024

Hey @InfuriatingYetti! I wanted to circle back and see if you have reached a working solution yet. If not, I'm more than happy to keep working through this. Just let me know!

from carekit.

Pariecemckinney-apple avatar Pariecemckinney-apple commented on August 25, 2024

Hello @InfuriatingYetti, we still havenโ€™t heard anything from you. Please let us know if you are are still stuck, otherwise we will close this issue soon.

from carekit.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.