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

navigateNextItem should wait for feedback before navigating. #7

Open
TomWoodward opened this issue May 17, 2024 · 13 comments
Open

navigateNextItem should wait for feedback before navigating. #7

TomWoodward opened this issue May 17, 2024 · 13 comments

Comments

@TomWoodward
Copy link

TomWoodward commented May 17, 2024

if an item generates feedback on completion, that feedback is immediately hidden by the navigation to the next item.

if i set a breakpoint inside navigateNextItem i can see feedback being shown but then it is immediately cleared when the navigation happens.

i'm using https://github.com/1EdTech/qti-examples/blob/master/qtiv3-examples/packaging/feedbackTest/id-fa3ee5852723/Example01-modalFeedback.xml for testing this. (submissionMode must be individual for it to try to do the feedback, which is not what this example's assessment.xml specifies, which is a whole different conversation)

@TomWoodward
Copy link
Author

this may be related to another issue where its not possible to trigger response processing without trying to go to a new question. in your monty hall example you add a "continue" button that triggers EndAttempt and handles this, but other examples like this one don't have that button and are impossible to progress in the player. this example can be tested in the player sandbox which has the "end attempt" button

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 18, 2024 via email

@TomWoodward
Copy link
Author

putting aside the simultaneous submission, i feel like what you're saying is that this project's main navigation, where all submission is done via navigation to another item, is just not compatible with questions that have completion feedback or are adaptive, unless those items have submission controls built into them.

is there a good way to detect if response processing has resulted in showing feedback? would the best way to handle feedback at the player level be to always have a stationary submit button and then show a "next" button when the item's state becomes completed? this would have the downside of requiring two clicks to progress a question even if it doesn't have feedback.

@svarokk
Copy link

svarokk commented May 20, 2024

Hey,

I've had this issue too, and there seems little ways to do that as the "modals" themselves do not store "hidden" attributes, instead they directly add and remove "hide" classes. Maybe I did overlook something, but I came up with a solution (which is hacky in my opinion)

First of i've used Vue's mixin so I don't have to overwrite the Qti3ItemPlayer source code:

Vue.mixin({
    mounted() {
        if ( this.$options.name == 'ModalDialog' ){
            this.isHide = function(){
                return !this.$refs.mymodal.classList.contains( 'show' );
            }

            this.isShow = function(){
                return this.$refs.mymodal.classList.contains( 'show' );
            }
        }

    },
    created() {
        if ( this.$options.name == 'ModalDialog' ){
            const originalHide = this.hide;
            const originalShow = this.show;

            this.isModalVisible = reactive( { value: false } );

            this.hide = function(){
                originalHide.apply( this, arguments );
                this.isModalVisible.value = false;
                this.$emit( 'hide' );
            },
        
            this.show = function(){
                originalShow.apply( this, arguments );
                this.isModalVisible.value = true;
                this.$emit( 'show' );
            }
        }
    }
});

Then in TestRunner.vue:

I've created 2 new data attributes:

feedbacks: [],
feedbackWatchers: [],

Created a method to list all modals:

getFeedbacks(){
    const assesmentItem = this.qti3Player.item;
    const store = assesmentItem.catalogFactory.store;

    return store?.getFeedbacks() ?? [];
}

Then edit the other navigation methods, i'll give an example with navigateNextItem method (had to get it from source code as my controller is veeeery different now):

navigateNextItem (state) {
    console.log('[NavigateNextItem]', state)

    const navigate = () => {
        this.currentItem += 1
        this.loadItemAtIndex(this.currentItem)
        this.updateButtonState()
    };

    this.handleNavigation( navigate );
}

the handleNavigation method is:

handleNavigation( navigateCallback ){
    let feedbackSpecificCallback = null;

    try{            
        const feedbacks = this.getFeedbacks();

        const feedbacksCollection = [];
        let shownFeedbacksLength = 0;

        if( feedbacks.length ){
            feedbacks.forEach(feedback => {
                const node = feedback?.node;
                const modal = node?.$refs?.modal;
                
                if( modal.isModalVisible.value == true ){
                    const watcher = this.watchFeedback( modal );
                    
                    feedbacksCollection.push( modal );
                    this.feedbackWatchers.push( watcher );
                    shownFeedbacksLength++;
                }
            });
        }

        if ( shownFeedbacksLength > 0 ) {
            this.feedbacks = feedbacksCollection;

            feedbackSpecificCallback = () => {
                navigateCallback();

                this.$off( 'navigateAfterFeedbacks', feedbackSpecificCallback );
            };

            this.$on( 'navigateAfterFeedbacks', feedbackSpecificCallback );
        }else{
            navigateCallback();
        }

    }catch( error ){
        console.error( '[Handle feedbacks] Error: ' + error + '. [Skipping validation, using navigateCallback method.]' );

        this.feedbackWatchers   = [];
        this.feedbacks          = [];

        if( feedbackSpecificCallback ){
            this.$off('navigateAfterFeedbacks', feedbackSpecificCallback);
        }

        navigateCallback();
    }
}

watchFeedback method:

watchFeedback( feedback ) {
    const checkAllFeedbacksGone = () => {
        const allRemoved = this.feedbacks.every( feedback => feedback.isModalVisible.value == false );

        if ( allRemoved ) {

            console.warn( '[Handle feedbacks] All feedbacks are removed.' );

            console.warn( '[Handle feedbacks] Destroying watchers...' );

            this.feedbackWatchers.forEach( ( unwatch, index ) => {
                console.warn( '[Handle feedbacks] Destroying watcher ' + ( index + 1 ) );
                unwatch();
            } );

            console.warn( '[Handle feedbacks] Emitting navigateAfterFeedbacks...' );

            this.$emit( 'navigateAfterFeedbacks' );

            console.warn( '[Handle feedbacks] Clearing feedback watchers...' );
            this.feedbackWatchers = [];
        }
    };

    return this.$watch(
        () => feedback.isModalVisible.value,
        ( newValue ) => {
            if ( newValue == false ) {
                console.warn( '[Handle feedbacks] Feedback removed.' );
                checkAllFeedbacksGone();
            }
        }, { immediate: true }
    );
}

And I think that's it, let me know if you need for me to explain any of this, I know it's not the best but it definitely works (if I didn't miss anything haha)

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 21, 2024 via email

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 21, 2024 via email

@TomWoodward
Copy link
Author

i'm considering the requirement of hiding answer information from the client, and if i were going to try to move response processing and item templating to the server, at that point it would probably be trivial to detect if feedback were being added and require pressing next again to continue to the next question.

i'm just trying to figure out how much i care because i'm not sure the player really supports async templating and the use case for feedback/adaptive in a summative assessment where you care about protecting answers may not be that big

@TomWoodward
Copy link
Author

@paulgrudnitski let me try to triage the various thoughts i've been raising in this issue. the core of it, i think, the part that could be considered a bug, is that it attempts to show feedback and it just doesn't work.

i think it might be appropriate to either...

if keeping the player navigation as-is, then non-adaptive feedback is unsupported, and could be stripped or suppressed from non-adaptive items

or...

it could be argued that the "next" button on individual submissions is a confusing ui, because students may sometimes be allowed to switch questions without submitting, and a next button might cause some students to submit without intending to. it might be more clear just to have a separate submit button in the ui, which solves several problems

the player also, relatedly, does not support adaptive questions that have no embedded qti-end-attempt-interaction, which are a real thing. having a separate submit button addresses this. you could even change the label of it to "continue" or something if the item is adaptive.

i'm not sure if this is really the best plan, but you could potentially accomplish this by injecting your endattempt-controller-bar element into the item if the submission mode is individual and it doesn't already contain a qti-end-attempt-interaction. this creates a consistent UI experience for submitting while respecting the item format if defined.

i understand that the player-controller isn't necessarily supposed to be a drop-in testing ui, more of an example implementation, but it seems valuable to try to get this right as part of the example.

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 23, 2024 via email

@TomWoodward
Copy link
Author

i don't explicitly disagree with anything you said, but in the player controller project i think you'll find that it does not support adaptive items that don't contain a built in qti-end-attempt-interaction

i ran into these in the qti-examples repo and i can track them down if you like, but i think it would be best if the player controller's example test flow handled these items, do you not agree?

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 25, 2024 via email

@TomWoodward
Copy link
Author

i think the sandbox is a great resource, but i still think that the usefulness of the test runner as an example of test ui is limited pretty severely if you make assumptions about the structure of the qti items.

i don't see completion feedback or minimally formatted adaptive items as particularly fringe use cases.

it seems like you don't really want to update it, which is fine, this is just my opinion, and maybe your new test runner project will address my concerns. feel free to close the issue if you don't intend to make any changes.

@paulgrudnitski
Copy link
Contributor

paulgrudnitski commented May 29, 2024 via email

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

No branches or pull requests

3 participants