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

Adding custom user profile fields #87

Closed
ryasmi opened this issue Nov 1, 2016 · 17 comments
Closed

Adding custom user profile fields #87

ryasmi opened this issue Nov 1, 2016 · 17 comments
Assignees
Labels

Comments

@ryasmi
Copy link
Member

ryasmi commented Nov 1, 2016

Question from Dillon Esterhuizen in our Moodle comments. I've moved this here so that it's easier to manage and thread together the conversation.

Hi there!

I have a similar question to Lee Kirkland who posted above. I'm trying to add custom user profile fields to the statements so that they show up in the LRS as well but I'm not sure how to add it in your code. I'm using the plugin version 1.0.0 and Learning Locker as the LRS.

I understand that there are 3 libraries I need to add it to, i.e. the expander, the translator and the emitter, but I can't figure out where inside of them to add it. I know that the standard log only records the user's id, which means that there must be some query somewhere that then gets all that user's information from the user table as well, but I can't seem to find it to add to that query.

Do I add it to the controller or the repo of each library, or both the controller and the repo (I would imagine that it shouldn't need to be added to any events as it is information required for every event)? And are there existing functions that I add it to or do I need to create a function specifically to query this data? Also, I would imagine I'd need to add it to the statements as well? I hope I'm asking the right questions.

Your help would be greatly appreciated!

@ryasmi ryasmi added this to the v1.1.1 milestone Nov 1, 2016
@ryasmi ryasmi self-assigned this Nov 1, 2016
@ryasmi
Copy link
Member Author

ryasmi commented Nov 1, 2016

Thank you for the question Dillon. You're certainly asking the right questions and I hope this response helps.

Changes to the Expander

If there are queries you need to make to the database, you will probably want to add a new method to the repository in the Expander. You will also need to change the base Event class so that it passes the additional data through to the Translator for all events.

Changes to the Translator

You will also need to change the base Event class in the translator so that it passes the additional data through the Emitter.

Changes to the Emitter

Again, you will need to change the base Event class here so that the additional data is added to all statements and therefore sent to the LRS.

That's a lot!

I know that's a lot of stuff to do and it's a pain to make the changes to three different repositories. Apologies for that, once I get more time on this project, I will hopefully be able to reduce the work required for changes of this nature.

@dillonhubble
Copy link

Thanks @ryansmith94 !

So far everything seems to be going okay. I'm doing the test with one user custom profile field just to make sure I can get the passing of the data through. I have reached a bit of a dead end though.

I put some log code in xapi-recipe-emitter/src/Events/Event.php in the "read" function so that I could see what $opts has inside of it. I added the custom data information to the readUser function in the same class. When I look at the log file, it shows that the custom user field is in the $opts array, however, this data is still not being emitted. Is there any other place in this script where I need to add the additional data or is it still a bit vague as to where the issue may be?

@ryasmi
Copy link
Member Author

ryasmi commented Nov 2, 2016

You're welcome @dillonhubble 👍

Good to hear. Yeah that's a good idea. Ok.

So I'm not sure the readUser method will be the best place to add the code since an xAPI actor is restricted to some fields by the xAPI specification. Can you show me your readUser method in that class?

@dillonhubble
Copy link

dillonhubble commented Nov 2, 2016

This is the code I have in that script:

    protected function readUser(array $opts, $key) {
        return [
            'name' => $opts[$key.'_name'],
            'data' => $opts[$key.'_data'],
            'account' => [
                'homePage' => $opts[$key.'_url'],
                'name' => $opts[$key.'_id'],
            ],
        ];
    }

where "data" is the parameter. So for example, if the custom field is "gender", in the $opts array, it will come through as "[user_data] => Male"

@ryasmi
Copy link
Member Author

ryasmi commented Nov 2, 2016

Right so this is where it won't be working. Unfortunately the xAPI restricts the fields that can be used inside the actor object (which is where the return value of readUser is used). You could instead put the additional data inside the context extensions. @garemoko may have some better suggestions around where the additional data could be placed.

@garemoko
Copy link
Contributor

garemoko commented Nov 2, 2016

I saw the bat signal.

Really xAPI statements aren't designed to store metadata about people, but if you're going to do that, a context extension is the best place. A better approach would be to transmit the people data using a completing different mechanism, the exact mechanism depending on how you're planning to use the data.

This Watershed LRS specific plugin uses Moodle profile fields to create and assign people to Watershed Groups. These groups can then be used for filtering, aggregating and controlling permissions for data. e.g. You could filter just people with a male gender. Or you could compare the average score of different departments. Or you could restrict data about a department only to the manager of that department (and above). This uses Watershed's people and groups API which is not a standard API and would not work with other Learning Record Stores.

Another approach would be to store the metadata using the Agent Profile Resource but again, there's no standard data structure defined and I'm not aware of any Learning Record Consumers that make use of agent metadata stored in that resource. (an exception to this is a small amount of user preference metadata defined by cmi5)

@dillonhubble how are you planning to use that data?

@garemoko
Copy link
Contributor

garemoko commented Nov 2, 2016

Note, this is the relevant file in the Watershed plugin: https://github.com/WatershedLRS/moodle-report_watershed/blob/master/classes/task/update_groups.php

@dillonhubble
Copy link

@garemoko thanks for the response.

I pretty much want to use the data like you mentioned with the Watershed plugin. So let's say a group of learners on Moodle are part of a particular department created using a custom profile field. I want the LRS to receive this data so that on the LRS, the user can then view by each department. If you think about it - although the custom profile fields are in separate tables, essentially they are still "part" of the user, and so should be accessible. However I do understand that there is a restriction on what xAPI can send through.

It seems as though the main issue here is just transmitting the data. I could add those fields into the areas you recommended, but then I guess it wouldn't show up as part of the actor; they would show up as separate fields or "contexts" in the statement. My guess is that this then needs a lot more functionality because the LRS will then need to be able to interpret the context differently because it now also contains some "actor" data as well.

So from what I understand, including custom profile fields is a lot more complicated than just pulling the data from the store and transmitting that extra data through to the LRS?

@garemoko
Copy link
Contributor

garemoko commented Nov 2, 2016

Well, including and using user profile fields needs careful thought, yes. Unless you're using Watershed, in which case you can just install that plugin :-)

@dillonhubble
Copy link

@ryansmith94 @garemoko thanks a lot for your feedback. From this thread I've come to the conclusion that I'd need to add the data in a different "object" to pass to the LRS and then also custom dev on the LRS to use that data as well. So this basically just requires a lot of custom dev and unfortunately I don't think that is possible from my side at the moment (but hopefully soon). In case this would help you in the future (if it doesn't that's also fine lol), here is what I did to get the custom profile fields into the $opts array.

In xapi/vendor/learninglocker/moodle-log-expander/src/Repository.php:

/**
     * Reads a user from the store with the given id.
     * @param String $id
     * @return PhpObj
     */
    public function readUser($id) {
        $type = 'user_info_data';
        $model = $this->readObject($id, 'user');
        $model->url = $this->cfg->wwwroot;
        **$model->data = $this->readObject($id, $type)->data;**
        return $model;
    }

In xapi/vendor/learninglocker/moodle-xapi-translator/src/Events/Event.php:

/**
     * Reads data for an event.
     * @param [String => Mixed] $opts
     * @return [String => Mixed]
     */
    public function read(array $opts) {
        $version = trim(file_get_contents(__DIR__.'/../../VERSION'));
        $opts['info']->{'https://github.com/LearningLocker/Moodle-xAPI-Translator'} = $version;
        $app_name = $opts['app']->fullname ?: 'A Moodle site';
        return [[
            'user_id' => $opts['user']->id,
            'user_url' => $opts['user']->url,
            'user_name' => $opts['user']->username,
            **'user_data' => $opts['user']->data,**
            'context_lang' => $opts['course']->lang,
            'context_platform' => 'Moodle',
          ....

In xapi/vendor/learninglocker/xapi-recipe-emitter/src/Events/Event.php:

protected function readUser(array $opts, $key) {
        return [
            'name' => $opts[$key.'_name'],
            **'data' => $opts[$key.'_data'],**
            'account' => [
                'homePage' => $opts[$key.'_url'],
                'name' => $opts[$key.'_id'],
            ],
        ];
    }

Thanks again for your time!

@ryasmi
Copy link
Member Author

ryasmi commented Nov 2, 2016

You're very welcome @dillonhubble, thanks for your questions and posting your snippets.

@caperneoignis
Copy link
Contributor

caperneoignis commented Nov 14, 2016

Yes, I had the same issue when I originally tried to add user's department or other items from the opts array. Sorry @dillonhubble I did not know you posted here, other wise I would have responded on here and also your emails.

The way I got around this is making a third party webpage that pulled from the same MongoDB table. I then used either: another database with the user information already in it to sort the users by organisation; or I sorted by quizzes since Orgs do different quizzes depending on what they are working on. The reason I resisted adding something to the Logstore plugin that included the org info, is because not all the org info is always added the same way. So I couldn't be sure if the information would be inputted correctly all the time. Especially when admin information is also included, when they do quiz test and course run throughs and the such. The other reason, is because we use Totara, I know its sacrilegious putting that on here, which stores the org information differently from Moodle, in that it has tables dedicated to position info and org hierarchy. Although that last part can be worked around by finding out what the plugin is running on, it presents a potential problem area.

TL;DR; You can potentially make a small site that does the report display and sort for you if all you need is reports.

@dwainbeck
Copy link

Hi guys I am having a similar issue but I am using the Tin Can Launch Link Plugin for sending my statements to the LRS and I need to add a new filed to the statements. Can I post that question here or under the Tin Can Launch Link repo?

@ryasmi
Copy link
Member Author

ryasmi commented Dec 6, 2016

Hey @dwainbeck, I'd probably post that in the TCLL repo, but thanks for thinking of us 😄

@carlosjgonza
Copy link

Hi people,

I have an issue, I need store custom data for an actor (Currently I'm using records of type agent profile), but in addition I want to use the Learning Locker Interface in the browser, feature Visualice (Data > Visualice) to generate a report filtered by that data stored in agent profile (Additional data of agents).

Is it possible?, What you recommend me to resolve this?

Thanks.

@ryasmi
Copy link
Member Author

ryasmi commented Apr 23, 2018

Hi @carlosjgonza, this plugin doesn't really handle that. You probably want to post this question in the Learning Locker Gitter chat room

@ryasmi
Copy link
Member Author

ryasmi commented Jul 6, 2018

The main use case of this is actually querying statements with those user attributes and because xAPI spec defines quite a restrictive querying API every LRS ends up implementing their own which handles people and attributes slightly differently.

Therefore I'm going to close this issue because I think each LRS needs a separate plugin/tool for these attributes. To my knowledge, Watershed have their plugin (as Andrew mentioned above) and Learning Locker is able to process a CSV of the user table.

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

No branches or pull requests

7 participants