11 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | # Site settings
2 | title: "Learning Locker Documentation"
3 | email: "hello@learninglocker.net"
4 | description: "Documentation for Learning Locker, the open source learning record store."
5 | source: "./src"
6 | destination: "./out"
7 |
8 | # Build settings
9 | markdown: kramdown
10 | permalink: pretty
11 |
12 | # Plugins
13 | plugins:
14 | - jekyll-redirect-from
15 |
16 | # YAML Frontmatter variables
17 | defaults:
18 | -
19 | scope:
20 | path: "" # empty string for all files
21 | values:
22 | layout: "default"
23 |
24 |
--------------------------------------------------------------------------------
/src/guides-migrating.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Migrating from V1 Guide
5 | Before migrating from version 1, you will need to [install the current version](../guides-installing) with a blank database.
6 |
7 | Once you have the current version successfully installed and working, you may follow the instructions at [use the migration tool](https://github.com/LearningLocker/v1-to-v2-migrator) which will download your version 1 database, migrate your data to the current version, and upload the migrated data into your current version's database.
8 |
9 | Note that the migration tool will also copy your xAPI documents and attachments, but currently we only provide support for moving files from and to local storage and s3 storage.
10 |
11 |
--------------------------------------------------------------------------------
/src/http-personas.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Personas HTTP Interface
5 |
6 | A persona represents a person with many [identifiers](../http-persona-identifiers) and [attributes](../http-persona-attributes) across systems.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/persona.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/persona.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of the persona.
18 | organisation | The id of the [organisation](../http-organisations#schema) that this persona belongs to.
19 | name | The display name of this persona.
20 |
21 | ### Example
22 |
23 | ```json
24 | {
25 | "_id" : "59c1219936229d4ce9634601",
26 | "organisation" : "59c1219936229d4ce9634602",
27 | "name" : "Example Persona",
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/src/http-persona-attributes.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Persona Attributes HTTP Interface
5 |
6 | Represents an attribute of a [persona](../http-personas).
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/personaattribute.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/personaattribute.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this attribute.
18 | organisation | The id of the [organisation](../http-organisations) this attribute belongs to.
19 | personaId | The id of the [persona](../http-personas) this attribute belongs to.
20 | key | The name of the attribute.
21 | value | The value of the attribute.
22 |
23 | ### Example
24 |
25 | ```json
26 | {
27 | "_id" : "59c1219936229d4ce9634601",
28 | "organisation" : "59c1219936229d4ce9634602",
29 | "personaId": "59c1219936229d4ce9634603",
30 | "key": "hair-colour",
31 | "value": "brown"
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------
/src/http-stores.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Stores HTTP Interface
5 |
6 | A store holds collections of statements. (may be referred to as lrs).
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/lrs.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/lrs.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this store.
18 | Title | The name of this store.
19 | createdAt | When this store was created.
20 | updatedAt | When this was last updated.
21 | description | The description of this store.
22 | owner_id | The id of the [user](../http-users#schema) who created this store.
23 | organisation | The [organisation](../http-organisations#schema) this store belongs to.
24 | statementCount | Number of statements in this store.
25 |
26 | ### Example Model
27 |
28 | ```json
29 | {
30 | "_id" : "59c2371c16bc715f83c34508",
31 | "title" : "Example Store",
32 | "createdAt" : "2017-04-27T09:40:44.920Z",
33 | "updatedAt" : "2017-04-27T09:40:49.139Z",
34 | "organisation" : "59c2371c16bc715f83c34507",
35 | "statementCount" : 500
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/src/welcome.md:
--------------------------------------------------------------------------------
1 | ---
2 | redirect_from:
3 | - "/troubleshooting/"
4 | - "/contributing/"
5 | - "/contributors/"
6 | - "/administration/"
7 | ---
8 |
9 | # Welcome
10 |
11 | Learning Locker is a conformant open source Learning Record Store (LRS) started in 2013 by [HT2 Labs](https://www.ht2labs.com) (now [Learning Pool](https://learningpool.com)); a type of data repository designed to store learning activity statements generated by xAPI (Tin Can) compliant learning activities. This website provides technical documentation for Learning Locker, for anything else please see the [main website](https://learninglocker.net/).
12 |
13 | > Learning Locker only supports xAPI v1.0 and above. It is not suitable for platforms still using an earlier version of xAPI. At this time there are no plans to provide backwards compatibility for older versions of the spec.
14 |
15 | In addition to this website, there are several ways to find out more about Learning Locker such as:
16 |
17 | - [Main Website](http://learninglocker.net/)
18 | - [Github Repository](https://github.com/LearningLocker/learninglocker)
19 | - [Gitter Chat Room](https://gitter.im/LearningLocker/learninglocker)
20 | - [Twitter](https://twitter.com/learning_locker)
21 | - [Email via hello@learninglocker.net](mailto:hello@learninglocker.net)
22 |
--------------------------------------------------------------------------------
/src/http-queries.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Queries HTTP Interface
5 |
6 | This holds saved statement queries.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/query.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/query.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of the query.
18 | name | The name of this query.
19 | organisation | The id of the [organisation](../http-organisations#schema) that this persona belongs to.
20 | owner | The id of the [user](../http-users#schema) which created this query.
21 | conditions | A JSON encoded [mongo query](https://docs.mongodb.com/manual/tutorial/query-documents/).
22 | isPublic | If false then this visualisation is only available to the owner and users with [org/all/query/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission.
23 |
24 | ### Example Model
25 |
26 | ```json
27 | {
28 | "_id" : "59c2371c16bc715f83c34501",
29 | "name" : "All comments",
30 | "owner" : "59c2371c16bc715f83c34502",
31 | "organisation" : "59c2371c16bc715f83c34503",
32 | "isPublic" : false,
33 | "conditions" : "{\"statement.verb.id\":\"http://adlnet.gov/expapi/verbs/commented\"}"
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/src/http-activities.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Activities HTTP Interface
5 |
6 | It is accessible through the following HTTP interfaces:
7 |
8 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/activity.
9 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/activity.
10 |
11 | ### Schema
12 |
13 | Name | Description
14 | --- | ---
15 | _id | The id of the activity.
16 | organisation | The id of the [organisation](../http-organisations#schema) that this activity belongs to.
17 | name | The display name of this activity.
18 | definition | An object holding details of the activity. See [definition](#definition).
19 |
20 | ### Definition
21 |
22 | Name | Description
23 | --- | ---
24 | type | The type of the activity.
25 | interactionType | Possible values are: true-false, choice, fill-in, long-fill-in, matching, performance, sequencing, likert, numeric or other. See [Interaction Types](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#interaction-types) for full description.
26 |
27 | ### Example
28 |
29 | ```json
30 | {
31 | "_id": "59c1219936229d4ce9634601",
32 | "organisation": "59c1219936229d4ce9634602",
33 | "name": "Example Activity",
34 | "definition": {
35 | "type": "http://adlnet.gov/expapi/activities/question",
36 | "interactionType": "choice"
37 | },
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/src/_includes/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
14 | {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | The contribution guidelines can be found at http://docs.learninglocker.net/contribute.
2 |
3 | ### Issue Templates
4 | If you are reporting an issue, please click the links below and your issue will be prefilled with our template. You need to replace the parts of the template where you see `{{some text}}`.
5 |
6 | - [Mistake](../../issues/new?title={{Brief description of the mistake}}&body=**Expected text**%0A{{page}} should state {{expectedText}} because {{reason}}.%0A%0A**Actual text**%0A{{page}} states {{actualText}}.%0A%0A**Additional information**%0A{{additionalInfo}}): reports documentation that is incorrect.
7 | - [Bug](../../issues/new?title={{Brief description of your bug}}&body=**Version**%0A{{branch}} at {{commit}}%0A%0A**Steps to reproduce the bug**%0A{{steps}}%0A%0A**Expected behaviour**%0A{{feature}} should be {{expectedResult}} because {{reason}}.%0A%0A**Actual behaviour**%0A{{feature}} is {{actualResult}}.%0A%0A**Client information**%0AOS: {{operatingSystem}}%0ABrowser: {{browser}} version 1.0.1%0A%0A**Additional information**%0A{{additionalInfo}}): reports a feature that is not working as expected.
8 | - [Enhancement](../../issues/new?title={{Brief description of your enhancement}}&body=**Motive**%0A{{why the enhancement is needed}}%0A%0A**Result**%0A{{what the enhancement is}}%0A%0A**Additional information**%0A{{additionalInfo}}): requests a removal, addition, or change of a feature.
9 | - [Question](../../issues/new?title={{Brief description of your question}}&body={{question}}%3F): asks how a feature should be used or what the feature does.
10 |
--------------------------------------------------------------------------------
/src/http-exports.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Exports HTTP Interface
5 |
6 | This holds queries which are then used to export data form learning locker.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/export.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/export.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The unique id of document.
18 | organisation | The organisation id this export template belongs to.
19 | name | Title of this export template.
20 | owner | The id of the [user](../http-users#schema) who created this export template.
21 | projection | An array of stringified json mongo projection queries. [See mongo docs](https://docs.mongodb.com/manual/reference/operator/aggregation/project/)
22 | rawMode | If true, in Learning Locker UI, the projection will be displayed as JSON text, as opposed to field value inputs.
23 | downloads | A list ids of [downloads](../http-downloads#schema) which have used this export template.
24 | isPublic | If false then this dashboard is only available to the owner and users with [org/all/export/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission.
25 |
26 | ### Example Model
27 |
28 | ```json
29 | {
30 | "_id" : "59c2371c16bc715f83c34501",
31 | "name" : "Example Export",
32 | "organisation" : "59c2371c16bc715f83c34502",
33 | "downloads" : [ ],
34 | "rawMode" : false,
35 | "projections" : [
36 | "{\"_id\":1,\"version\":\"$statement.version\"}"
37 | ]
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/src/overview-xapi.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # xAPI Overview
5 | The Experience API (xAPI) is a specification that defines how to store and retrieve learning experiences across various sources for analysis. Recording learning experiences in this way improves the ability to comprehend and compare learning experiences and their outcomes.
6 |
7 | ## Data Models
8 | The xAPI provides four data models for recording elements of a learning experience, these are:
9 |
10 | - [**Statements**](../http-xapi-statements) which are an immutable record of an agent's interaction with an activity. A set of statements can be used to track a complete learning experience. [Voiding](https://vimeo.com/168961267) can be used to invalidate previous statements, henceforth removing them from normal retrieval methods.
11 | - [**Activity Profiles**](../http-xapi-activities) that record additional mutable information about an activity that you wouldn't want to record on every statement.
12 | - [**Agent Profiles**](../http-xapi-agents) that record additional mutable information about an agent that you wouldn't want to record on every statement.
13 | - [**State**](../http-xapi-states) which records mutable information about an agent in relation to an activity, such as how far they've progressed through a activity.
14 |
15 | ## Using the xAPI
16 | To begin using the xAPI in Learning Locker, you can checkout our guides for [integrating with Learning Locker](../guides-integrating), [inserting statements](../guides-inserting), and [retrieving statements](../guides-retrieving). For more detailed documentation of the xAPI conformant HTTP interfaces that Learning Locker provides, you can checkout the [xAPI HTTP interface documentation](../http-xapi).
17 |
--------------------------------------------------------------------------------
/src/css/tomorrow-night.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Night Theme */
2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
5 | .tomorrow-comment, pre .comment, pre .title {
6 | color: #969896;
7 | }
8 |
9 | .tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
10 | color: #cc6666;
11 | }
12 |
13 | .tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
14 | color: #de935f;
15 | }
16 |
17 | .tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
18 | color: #f0c674;
19 | }
20 |
21 | .tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
22 | color: #b5bd68;
23 | }
24 |
25 | .tomorrow-aqua, pre .css .hexcolor {
26 | color: #8abeb7;
27 | }
28 |
29 | .tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
30 | color: #81a2be;
31 | }
32 |
33 | .tomorrow-purple, pre .keyword, pre .javascript .function {
34 | color: #b294bb;
35 | }
36 |
37 | pre code {
38 | display: block;
39 | background: #1d1f21;
40 | color: #c5c8c6;
41 | padding: 0.5em;
42 | }
43 |
44 | pre .coffeescript .javascript,
45 | pre .javascript .xml,
46 | pre .tex .formula,
47 | pre .xml .javascript,
48 | pre .xml .vbscript,
49 | pre .xml .css,
50 | pre .xml .cdata {
51 | opacity: 0.5;
52 | }
53 |
--------------------------------------------------------------------------------
/src/faqs.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Frequently Asked Questions
5 |
6 | #### Why can't I use custom extensions in queries?
7 | When Learning Locker stores extensions, the dots in the keys are replaced with `&46;` because Mongo does not allow dots in keys. You can find more information in the [Connection HTTP Interface documentation](../http-connection#filtering-with-extension-keys) and the [Aggregation HTTP Interface documentation](../http-aggregation#projecting-with-extension-keys).
8 |
9 | #### Why are my queries running slowly?
10 | It's likely that you are querying without utilising [Mongo indexes](https://docs.mongodb.com/manual/indexes/). You can improve the performance of your queries by utilising Mongo indexes. More information is available in the [Connection HTTP Interface documentation](../http-connection#filtering-with-improved-performance) and the [Aggregation HTTP Interface documentation](../http-aggregation#matching-with-improved-performance).
11 |
12 | #### Why am I getting an unauthorised error using the xAPI?
13 | It's likely that you've either misconfigured your "Client" in Learning Locker or you're using an incorrect "Authorization" header. To correctly configure your Client and use the correct Authorization header, we recommend that you firstly follow [our documentation for creating a "Store" in Learning Locker](https://ht2ltd.zendesk.com/hc/en-us/articles/115000893009-Managing-your-Learning-Record-Stores#creating-a-new-store). Creating a Store will automatically create a new Client that is enabled with the correct scopes and associated to the Store you created. Therefore, once you've created a Store, go to `Settings > Clients` and find the new Client. The new Client will have a "Basic auth" token which you can use as the value for your "Authorization" header in any HTTP requests to the xAPI (be sure to prepend "Basic " to the start of the token).
14 |
--------------------------------------------------------------------------------
/src/guides-retrieving.md:
--------------------------------------------------------------------------------
1 | ---
2 | redirect_from:
3 | - "/reporting/"
4 | - "/exporting/"
5 | - "/report_api/"
6 | - "/exports_api/"
7 | ---
8 |
9 | # Retrieving Statements Guide
10 | Learning Locker provides many options for retrieving statements (listed below) to satisfy various needs, from just viewing a quick list of statements to direct database access for business intelligence (BI) tools.
11 |
12 | Option | Description
13 | --- | ---
14 | [Source page](https://ht2ltd.zendesk.com/hc/en-us/sections/115000232469-Filtering-Exploring-Statements) | The source page provides a quick way to list of statements that have been sent to an organisation in Learning Locker. It also allows you to filter this list using the Query Builder.
15 | [Visualisations](https://ht2ltd.zendesk.com/hc/en-us/sections/115000222689-Visualisations) | Visualisations provide a quick visual summary of statements via bar graphs, line graphs, scatter graphs, etc. These are great for quickly exploring potential correlations and trends.
16 | [Exports](https://ht2ltd.zendesk.com/hc/en-us/sections/115000232489-Exporting-Statements) | If you want data from your statements in a spreadsheet, you can download certain parts of your statements to a CSV file using the Export Panel.
17 | [xAPI](../http-xapi) | The xAPI HTTP interface provides an xAPI-conformant API for retrieving statements in your applications.
18 | [Aggregation API](../http-aggregation) | The Aggregation HTTP Interface is more advanced than the xAPI HTTP interface and allows you to access MongoDB's powerful Aggregation API for more custom filtration of statements.
19 | [Connection HTTP Interface](../http-connection) | The Connection HTTP Interface is a slightly more restricted version of the Aggregation API that utilises cursors to provide paginated statements for improved performance.
20 | Direct DB access | If you want to utilise BI Tools or improve performance further, you can simply access the MongoDB database which Learning Locker is using directly.
21 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | addressable (2.7.0)
5 | public_suffix (>= 2.0.2, < 5.0)
6 | colorator (1.1.0)
7 | concurrent-ruby (1.1.6)
8 | em-websocket (0.5.1)
9 | eventmachine (>= 0.12.9)
10 | http_parser.rb (~> 0.6.0)
11 | eventmachine (1.2.7)
12 | ffi (1.13.1)
13 | forwardable-extended (2.6.0)
14 | http_parser.rb (0.6.0)
15 | i18n (1.8.3)
16 | concurrent-ruby (~> 1.0)
17 | jekyll (4.1.1)
18 | addressable (~> 2.4)
19 | colorator (~> 1.0)
20 | em-websocket (~> 0.5)
21 | i18n (~> 1.0)
22 | jekyll-sass-converter (~> 2.0)
23 | jekyll-watch (~> 2.0)
24 | kramdown (~> 2.1)
25 | kramdown-parser-gfm (~> 1.0)
26 | liquid (~> 4.0)
27 | mercenary (~> 0.4.0)
28 | pathutil (~> 0.9)
29 | rouge (~> 3.0)
30 | safe_yaml (~> 1.0)
31 | terminal-table (~> 1.8)
32 | jekyll-redirect-from (0.16.0)
33 | jekyll (>= 3.3, < 5.0)
34 | jekyll-sass-converter (2.1.0)
35 | sassc (> 2.0.1, < 3.0)
36 | jekyll-watch (2.2.1)
37 | listen (~> 3.0)
38 | kramdown (2.3.0)
39 | rexml
40 | kramdown-parser-gfm (1.1.0)
41 | kramdown (~> 2.0)
42 | liquid (4.0.3)
43 | listen (3.2.1)
44 | rb-fsevent (~> 0.10, >= 0.10.3)
45 | rb-inotify (~> 0.9, >= 0.9.10)
46 | mercenary (0.4.0)
47 | pathutil (0.16.2)
48 | forwardable-extended (~> 2.6)
49 | public_suffix (4.0.5)
50 | rb-fsevent (0.10.4)
51 | rb-inotify (0.10.1)
52 | ffi (~> 1.0)
53 | rexml (3.2.4)
54 | rouge (3.20.0)
55 | safe_yaml (1.0.5)
56 | sassc (2.4.0)
57 | ffi (~> 1.9)
58 | terminal-table (1.8.0)
59 | unicode-display_width (~> 1.1, >= 1.1.1)
60 | unicode-display_width (1.7.0)
61 |
62 | PLATFORMS
63 | ruby
64 |
65 | DEPENDENCIES
66 | jekyll (~> 4.1)
67 | jekyll-redirect-from (~> 0.16.0)
68 | jekyll-watch (~> 2.2)
69 |
70 | BUNDLED WITH
71 | 2.1.4
72 |
--------------------------------------------------------------------------------
/src/http-downloads.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Downloads HTTP Interface
5 |
6 | Holds records of exports.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/download.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/download.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this download
18 | name | Name of the download.
19 | organisation | The id of the [organisation](../http-organisations#schema) this download happend from.
20 | owner | The id of the [user](../http-users#schema) who downloaded the export.
21 | isReady | Is the download ready.
22 | time | Date and time of the download.
23 | url | The url of this download.
24 | upload | An object holding details of the file to be downloaded. [See upload](#upload).
25 | isPublic | If false then this download is only available to the owner and users with [org/all/download/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission.
26 |
27 | ### Upload
28 |
29 | Holds details of the file to be uploaded.
30 |
31 | Name | Description
32 | --- | ---
33 | createdAt | When the content was created.
34 | updatedAt | When the content was last updated.
35 | key | The location of the content to be downloaded.
36 | repo | The repo to where the content is stored, ie local.
37 | mime | mime type.
38 |
39 | ### Example Model
40 |
41 | ```json
42 | {
43 | "_id" : "59c2371c16bc715f83c3450d",
44 | "url" : "/api/downloadexport/5915be242c3a240fb40801e4.csv",
45 | "time" : "2017-05-12T13:52:36.995Z",
46 | "organisation" : "59c2371c16bc715f83c3450e",
47 | "name" : "Example Download",
48 | "upload" : {
49 | "createdAt" : "2017-05-12T13:52:37.016Z",
50 | "updatedAt" : "2017-05-12T13:52:37.070Z",
51 | "mime" : "text/csv",
52 | "key" : "downloads/1494597156987.csv",
53 | "repo" : "local"
54 | },
55 | "isReady" : true
56 | }
57 | ```
58 |
--------------------------------------------------------------------------------
/src/guides-structuring.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Structuring your LRS Guide
5 | In Learning Locker you can structure your [organisations](https://learningpool.zendesk.com/hc/en-us/articles/115000904505-Managing-your-Organisations), [stores](https://learningpool.zendesk.com/hc/en-us/articles/115000893009-Managing-your-Learning-Record-Stores), [clients](https://learningpool.zendesk.com/hc/en-us/articles/115000951445-Managing-your-Clients), and [users](https://learningpool.zendesk.com/hc/en-us/articles/115000894529-Managing-your-Users) to manage the visibility of data in your Learning Locker instance. There are few key concepts that you should know before we get started.
6 |
7 | - Organisations contain stores and clients.
8 | - Stores contain xAPI statements and xAPI documents.
9 | - Clients can be used to access data within the organisation via HTTP interfaces.
10 | - Clients can be restricted to only access data within a single store in their organisation.
11 | - Users can be used to access the Browser Interface.
12 | - Users can be added to many organisations.
13 |
14 | Imagine you have a Learning Locker instance with one organisation (Organisation A) and inside that organisation you have one store (Store A) and one client (Client A) that only has access to Store A. Inside your Learning Locker instance you also have two users (User A and User B) and they've both been added to Organisation A.
15 |
16 | Now imagine that you want to store some data that User A is allowed to see, but User B shouldn't be allowed to see. In this scenario, you should create a new organisation (Organisation B) and add only User A to it, leaving User B out so they can't see the data in that organisation.
17 |
18 | Now imagine that you want to store some xAPI statements that User A and User B are both allowed to see, but you don't want to allow applications using Client A to access the xAPI statements. In this scenario, you should create a new store (Store B) and create a new client (Client B) that can access Store B. Note that Client B will be automatically created when you create Store B, as Learning Locker automatically creates a client when you create a store.
19 |
--------------------------------------------------------------------------------
/src/http-roles.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Roles HTTP Interface
5 |
6 | Roles contain a set of permissions, of which then users can be assigned.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/role.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/role.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of the role.
18 | createdAt | When this role was created.
19 | updatedAt | When this role was last updated.
20 | title | The name of this role.
21 | description | The description of this role.
22 | owner_id | The id of the [user](../http-users#schema) who created this role.
23 | organisation | The id of the [organisation](../http-organisations#schema) this role is in.
24 | scopes | A list of [organisation scopes](#organisation-scopes) of this role.
25 |
26 | ### Organisation Scopes
27 |
28 | An organisation scope is a scope that is applied to a user per organisation. Available scopes:
29 |
30 | Name |
31 | --- |
32 | all |
33 | org/public/dashboard/view |
34 | org/public/dashboard/edit |
35 | org/all/dashboard/view |
36 | org/all/dashboard/edit |
37 | org/public/visualisation/view |
38 | org/public/visualisation/edit |
39 | org/all/visualisation/view |
40 | org/all/visualisation/edit |
41 | org/public/journey/view |
42 | org/public/journey/edit |
43 | org/all/journey/view |
44 | org/all/journey/edit |
45 | org/public/statementForwarding/view |
46 | org/public/statementForwarding/edit |
47 | org/all/statementForwarding/view |
48 | org/all/statementForwarding/edit |
49 | org/all/query/view |
50 | org/all/query/edit |
51 | org/all/export/view |
52 | org/all/export/edit |
53 | org/all/download/view |
54 | org/all/download/edit |
55 | org/all/persona/manage |
56 | org/all/activity/manage |
57 | org/all/store/manage |
58 | org/all/user/manage |
59 | org/all/client/manage |
60 | org/all/role/manage |
61 | org/all/organisation/manage |
62 |
63 | ### Examples
64 |
65 | ```json
66 | {
67 | "_id" : "59c2371c16bc715f83c34501",
68 | "createdAt" : "2017-09-19T12:58:58.884Z",
69 | "updatedAt" : "2017-09-19T12:58:58.884Z",
70 | "title" : "Example Role",
71 | "owner_id" : "59c2371c16bc715f83c34502",
72 | "organisation" : "59c2371c16bc715f83c34503",
73 | "scopes" : [
74 | "all"
75 | ]
76 | }
77 | ```
78 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | This repository is now archived. It previously generated the documentation for https://docs.learninglocker.net which now redirects to https://learninglocker.atlassian.net/wiki/spaces/DOCS/overview hosted by Learning Pool's account on Confluence.
2 |
3 | [](https://learninglocker.net)
4 | > Documentation for Learning Locker.
5 |
6 | [](https://travis-ci.org/LearningLocker/docs)
7 | [](http://opensource.org/licenses/GPL-3.0)
8 | [](https://gitter.im/LearningLocker/learninglocker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9 |
10 | *Learning Locker is copyright [Learning Pool](http://learningpool.com)*
11 |
12 | ## Users
13 | Please view our [documentation](http://docs.learninglocker.net) site.
14 |
15 | ## Developers
16 | You may contribute to this project via [issues](/issues) and [pull request](/pulls), however, please see the [guidelines](/contributing.md) before doing so. More information about how you can get involved can be found on the [Learning Locker website](http://learninglocker.net/community/get-involved/).
17 |
18 | ### Getting Started
19 | 1. Open the file you wish to edit on Github.
20 | 2. Edit the file.
21 | 3. Enter a "commit message" (found below the edited file).
22 | 4. Click "Propose file changes" (found below the edited file).
23 |
24 | Alternatively:
25 |
26 | 1. [Fork](/fork) the repository.
27 | 2. Change the markdown documentation files.
28 | 3. Commit and push your changes to Github.
29 | 4. Create a [pull request](/pulls) on Github (ensuring that you follow the [guidelines](/contributing.md)).
30 |
31 | ### Development
32 | 1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/)
33 | 2. Install [Bundler](https://bundler.io/)
34 | 3. Run `bundler install`
35 | 4. Run `jekyll server --watch`
36 | 5. Go to `http://127.0.0.1:4000` in your browser
37 |
38 | For each change in the src files jekyll will automatically recompile the dist files.
39 |
40 | Note that the documentation site is generated using [Jekyll](http://jekyllrb.com/). To learn more about the repository structure, please view the [Jekyll documentation](http://jekyllrb.com/docs/home/).
41 |
--------------------------------------------------------------------------------
/src/http-dashboards.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Dashboards HTTP Interface
5 |
6 | Details of dashboards.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/dashboard.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/dashboard.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this dashboard.
18 | createdAt | When this dashboard was created.
19 | updatedAt | When this dashboard was last updated.
20 | title | String. Title of the dashboard.
21 | widgets | An array of the widgets on this dashboard. See [widgets](#widgets).
22 | organisation | Id of the [organisation](../http-organisations#schema) this dashboard belongs to.
23 | owner | Id of the [user](../http-users#schema) who created this dashboard
24 | visibility | NOWHERE, ANYWHERE, VALID_DOMAINS. The scope of where this dashboard is externally visible.
25 | validDomains | If visibility is VALID_DOMAINS, a string of domains which can view this dashboard.
26 | isPublic | If false then this dashboard is only available to the owner and users with [org/all/dashboard/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission. This is unrelated to visibility.
27 |
28 | ### Widgets
29 |
30 | An array of widgets. The properties for each widget are:
31 |
32 | Name | Description
33 | --- | ---
34 | title | The name of the widget.
35 | visualisation | The id of the [visualisation](../http-visualisations#schema) that this widget is displaying.
36 | x | The x position of this widget.
37 | y | The y position of this widget.
38 | h | The height of this widget.
39 | w | The width of this widget.
40 |
41 | ### Example Model
42 |
43 | ```json
44 | {
45 | "_id" : "59c2371c16bc715f83c3450c",
46 | "createdAt" : "2017-04-28T09:03:57.332Z",
47 | "updatedAt" : "2017-05-18T12:55:24.430Z",
48 | "owner" : "59c2371c16bc715f83c34509",
49 | "title" : "Example Dashboard",
50 | "organisation" : "59c2371c16bc715f83c34507",
51 | "visibility" : "NOWHERE",
52 | "widgets" : [
53 | {
54 | "title" : "Example Widget",
55 | "_id" : "59c2371c16bc715f83c3450a",
56 | "h" : 4,
57 | "w" : 4,
58 | "y" : 0,
59 | "x" : 0,
60 | "visualisation" : "59c2371c16bc715f83c3450b"
61 | }
62 | ],
63 | "filter" : "{}",
64 | "public" : false,
65 | "isPublic" : true
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/src/http-organisations.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Organisations HTTP Interface
5 |
6 | Organisations are a logical grouping of statements.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/organisation.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/organisation.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The unique id of document.
18 | createdAt | When this document was created.
19 | updatedAt | When the document was last updated.
20 | name | The name of the organisation.
21 | parent | The _id of the parent organisation.
22 | owner | The id of the [user](../http-users#schema) which created this organisation.
23 | settings | [JSON settings for this organisation](#settings).
24 |
25 | ### Settings
26 |
27 | Name | Description
28 | ---|---
29 | PASSWORD_CUSTOM_MESSAGE | The password message to display.
30 | PASSWORD_CUSTOM_REGEX | The regular expression which the password should match.
31 | PASSWORD_USE_CUSTOM_REGEX | Whether to use the custom regular expression.
32 | PASSWORD_REQUIRE_NUMBER | Whether the password requires numbers.
33 | PASSWORD_REQUIRE_ALPHA | Whether the password requires characters.
34 | PASSWORD_MIN_LENGTH | The minimum password length.
35 | PASSWORD_HISTORY_TOTAL | How many previous passwords the user is not allowed to use
36 | PASSWORD_HISTORY_CHECK | Whether to check password history
37 | LOCKOUT_SECONDS | How long to lock out a user after LOCK_ATTEMPTS failed login attempts.
38 | LOCKOUT_ATTEMPTS | How many login tries a user as allowed before triggering the LOCKOUT_SECONDS timeout.
39 | LOCKOUT_ENABLED | Whether the LOCKOUT functionality is enabled.
40 |
41 | ### Example Model
42 |
43 | ```json
44 | {
45 | "_id" : "59c2371c16bc715f83c34501",
46 | "createdAt" : "2017-04-27T15:45:34.298Z",
47 | "updatedAt" : "2017-04-27T15:45:38.138Z",
48 | "parent" : "59c2371c16bc715f83c34502",
49 | "owner" : "59c2371c16bc715f83c34503",
50 | "settings" : {
51 | "PASSWORD_CUSTOM_MESSAGE" : null,
52 | "PASSWORD_CUSTOM_REGEX" : null,
53 | "PASSWORD_USE_CUSTOM_REGEX" : false,
54 | "PASSWORD_REQUIRE_NUMBER" : false,
55 | "PASSWORD_REQUIRE_ALPHA" : true,
56 | "PASSWORD_MIN_LENGTH" : 8,
57 | "PASSWORD_HISTORY_TOTAL" : 3,
58 | "PASSWORD_HISTORY_CHECK" : true,
59 | "LOCKOUT_SECONDS" : 1800,
60 | "LOCKOUT_ATTEMPS" : 5,
61 | "LOCKOUT_ENABLED" : true
62 | },
63 | "name" : "Example Organisation"
64 | }
65 | ```
66 |
--------------------------------------------------------------------------------
/src/http-visualisations.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Visualisations HTTP Interface
5 |
6 | Visualisation configuration.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/visualisation.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/visualisation.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this visualisation.
18 | description | The name of this visualisation.
19 | createdAt | When this visualisation was created.
20 | updatedAt | When this visualisation was last updated.
21 | type | The type of this visualisation.
22 | axesgroup | What to group the visualisation data points as.
23 | axesxLabel | The x axes label.
24 | axesyLabel | The y axes label.
25 | axesxValue | A stringified json encode object of the x axes value options.
26 | axesyValue | A stringified json encode object of the y axes value options.
27 | axesvalue | A stringified json encode object of the value for this visualisation.
28 | axesyOperator | The y axes operator, eg uniqueCount.
29 | axesxOperator | The x axes operator, eg uniqueCount.
30 | axesOperator | The visualisation operator, eg uniqueCount.
31 | axesquery | Query for this visualisation.
32 | axesxQuery | Query for the x axes.
33 | axesyQuery | Query for the y axes.
34 | stacked | If this visualisation is stacked.
35 | chart | The type of visualisation.
36 | filter | An array of stringified [mongo filters](https://docs.mongodb.com/manual/reference/operator/aggregation/filter/).
37 | journey | The [journey's](../http-journeys#schema) id if this visualisation is a journey.
38 | organisation | The [organisation](../http-organisations#schema) this visualisation belongs to.
39 | owner | The id of the [user](../http-users#schema) who created this visualisation.
40 | previewPeriod | The time range which is shown in this visualisation.
41 | isPublic | If false then this visualisation is only available to the owner and users with [org/all/visualisation/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission.
42 |
43 | ### Example Model
44 |
45 | ```json
46 | {
47 | "_id" : "59c1087dfd869741959c5701",
48 | "createdAt" : "2017-09-19T13:13:09.312Z",
49 | "updatedAt" : "2017-09-19T13:22:00.404Z",
50 | "owner" : "59c1087dfd869741959c5702",
51 | "axes" : "{}",
52 | "organisation" : "59c1087dfd869741959c5703",
53 | "isPublic" : false,
54 | "previewPeriod" : "LAST_7_DAYS",
55 | "filters" : [
56 | "{\"color\":\"#1e8bc3\"}"
57 | ],
58 | "chart" : "LINE",
59 | "stacked" : true,
60 | "axesoperator" : "uniqueCount",
61 | "axesvalue" : "{\"optionKey\":\"statements\",\"searchString\":\"Statements\"}",
62 | "type" : "COUNTER",
63 | "description" : "Example Visualisation"
64 | }
65 | ```
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/guides-sales-statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Sales Statements
5 |
6 | - [User closed sale with Client](#user-closed-sale-with-client)
7 | - [User generated lead with Client](#user-generated-lead-with-client)
8 | - [User called Client](#user-called-client)
9 |
10 | The statements shown here are not currently guided by an existing Profile or Community of Practice. They have been created to define how we should create statements for SalesForce.
11 |
12 | ## User closed sale with Client
13 | A statement that is used to indicate that the User has closed a sale with a Client.
14 |
15 | ### Statement
16 |
17 | ```json
18 | {
19 | "actor": {
20 | "objectType": "Agent",
21 | "mbox": "mailto:user@example.org",
22 | "name": "Example User"
23 | },
24 | "verb": {
25 | "id": "http://id.tincanapi.com/verb/closed-sale",
26 | "display": {
27 | "en-US": "closed a sale with"
28 | }
29 | },
30 | "object": {
31 | "objectType": "Group",
32 | "name": "Example Client Name",
33 | "account": {
34 | "homePage": "https://learningpool.force.com/client",
35 | "name": "Example-Client-Account-ID"
36 | }
37 | },
38 | "context": {
39 | "platform": "SalesForce",
40 | "language": "en"
41 | }
42 | }
43 | ```
44 |
45 | ## User generated lead with Client
46 | A statement that is used to indicate that the User has genereated a lead with a Client.
47 |
48 | ### Statement
49 |
50 | ```json
51 | {
52 | "actor": {
53 | "objectType": "Agent",
54 | "mbox": "mailto:user@example.org",
55 | "name": "Example User"
56 | },
57 | "verb": {
58 | "id": "https://w3id.org/xapi/dod-isd/verbs/generated",
59 | "display": {
60 | "en-US": "generated a lead with"
61 | }
62 | },
63 | "object": {
64 | "objectType": "Group",
65 | "name": "Example Client Name",
66 | "account": {
67 | "homePage": "https://learningpool.force.com/client",
68 | "name": "Example-Client-Account-ID"
69 | }
70 | },
71 | "context": {
72 | "platform": "SalesForce",
73 | "language": "en"
74 | }
75 | }
76 | ```
77 |
78 |
79 | ## User called Client
80 | A statement that is used to indicate that the User has called a Client.
81 |
82 | ### Statement
83 |
84 | ```json
85 | {
86 | "actor": {
87 | "objectType": "Agent",
88 | "mbox": "mailto:user@example.org",
89 | "name": "Example User"
90 | },
91 | "verb": {
92 | "id": "http://id.tincanapi.com/verb/called",
93 | "display": {
94 | "en-US": "called"
95 | }
96 | },
97 | "object": {
98 | "objectType": "Group",
99 | "name": "Example Client Name",
100 | "account": {
101 | "homePage": "https://learningpool.force.com/client",
102 | "name": "Example-Client-Account-ID"
103 | }
104 | },
105 | "context": {
106 | "platform": "SalesForce",
107 | "language": "en"
108 | }
109 | }
110 | ```
111 |
--------------------------------------------------------------------------------
/src/guides-monitoring.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Monitoring your LRS
5 |
6 | ## PM2 / KeyMetrics
7 |
8 | If you have installed Learning Locker using the recommended build script, then the application will come pre-installed onto an instance of the excellent [`pm2`](http://pm2.keymetrics.io/) Node Process Management tool.
9 |
10 | Whilst this tool can be used to monitor the running processes from within each server running LL (refer to [the documentation](http://pm2.keymetrics.io/docs/usage/monitoring/)), it can also be setup to send performance metrics to the [Key Metrics](http://docs.keymetrics.io/) platform (costs may apply).
11 |
12 | 
13 |
14 |
15 | ## Logs
16 |
17 | ### Local
18 |
19 | By default logs are captured by pm2 and stored locally on each instance as defined in the pm2 process file ([pm2/all.json.dist](https://github.com/LearningLocker/learninglocker/blob/master/pm2/all.json.dist)). Errors and standard output are separated and can be viewed by running `pm2 logs` from within your instance. By default they are stored in `/var/log/learninglocker/` and are rotated using the [`pm2-logrotate`](https://github.com/pm2-hive/pm2-logrotate) module.
20 |
21 | 
22 |
23 | ### AWS Cloudwatch
24 | Learning Locker comes with the ability to push your logs to AWS Cloudwatch. To enable this, configure the relevant part of the `.env` files:
25 |
26 | ```
27 | #######################
28 | # AWS Cloudwatch logs #
29 | # AWS credentials must be configured for Cloudwatch access
30 | # Ref: http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html
31 | #######################
32 | # Enable cloudwatch logs (false*|true)
33 | WINSTON_CLOUDWATCH_ENABLED=
34 | WINSTON_CLOUDWATCH_LOG_GROUP_NAME=
35 | WINSTON_CLOUDWATCH_LOG_STREAM_NAME=
36 | WINSTON_CLOUDWATCH_ACCESS_KEY_ID=
37 | WINSTON_CLOUDWATCH_SECRET_ACCESS_KEY=
38 | WINSTON_CLOUDWATCH_REGION=
39 | ```
40 |
41 | Please note that the AWS credentials must have the required permissions to create and push to log streams/groups.
42 |
43 | 
44 |
45 | ### Other logging
46 |
47 | Learning Locker handles all logging via [Winston](https://www.npmjs.com/package/winston). New "transports" can be added and configured in both the Learning Locker application and xAPI services.
48 |
49 |
50 | ## New Relic
51 |
52 | The Learning Locker application is configured to send performance metrics to New Relic. Simply fill in the following in the `.env` of your application instance
53 |
54 | ```
55 | #############
56 | # New Relic #
57 | #############
58 |
59 | # New Relic License key
60 | NEW_RELIC_LICENSE_KEY=
61 | # APM name for API
62 | NEWRELIC_API_NAME=
63 | # APM name for UI
64 | NEWRELIC_UI_NAME=
65 | ```
66 |
67 | #### xAPI
68 | In the xAPI you can also add monitoring.
69 |
70 | ```
71 | # New Relic License key
72 | NEW_RELIC_LICENSE_KEY=
73 | # APM name
74 | NEW_RELIC_APP_NAME=
75 | ```
76 |
77 | 
78 |
79 | ## Others
80 |
81 | Other Node monitoring solutions are available but would need to be added manually to the script or web server.
82 |
--------------------------------------------------------------------------------
/src/http-clients.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Clients HTTP Interface
5 |
6 | Details of a clients which will be accessing Learning Locker. It contains details for permissions, authenticating and storing the xapi request.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/client.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/client.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The unique id of the document.
18 | createdAt | When this document was created.
19 | updatedAt | When this document was last updated.
20 | title | String. The title of the client.
21 | api | The client [basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication) details. [See api](#api).
22 | authority | A JSON encoded string of an agent object that will be set on any statements that the client pushes. [See authority](#authority).
23 | scopes | An array of strings. Permission [scopes](#client-scopes) that this client has permission for.
24 | isTrusted | Whether this client is enabled.
25 | organisation | The [organisation](../http-organisations#schema) this client is attached to.
26 | lrs_id | The id of the [store](../http-stores#schema) that the system will put received xapi statements.
27 |
28 | ### api
29 |
30 | Name | Description
31 | --- | ---
32 | basic_key | The basic auth key.
33 | basic_secret | The basic auth secret.
34 |
35 | ### authority
36 |
37 | This is a JSON encoded string, of the following properties:
38 |
39 | Name | Description
40 | --- | ---
41 | objectType | 'Agent'.
42 | name | Name of the agent.
43 | mbox | Optional, of its an mbox
44 | mbox_sha1sum | Optional, mbox sha1
45 | openid | Optional, the open id.
46 | account | Optional, A JSON object. [See Account](#account)
47 | homePage | Optional, the homePage.
48 |
49 | One and only one of mbox, mbox_sha1, openid, Account should be provided
50 |
51 | ### account
52 |
53 | Name | Description
54 | --- | ---
55 | name | The unique id or name used to log into the account.
56 | homePage | The url of the home page.
57 |
58 | ### Client Scopes
59 |
60 | A scope is a specific permission. Available scopes:
61 |
62 | Scope | Description
63 | --- | --- | ---
64 | all | Permission to read and write everything.
65 | all/read | Permission to read everything.
66 | xapi/all | Permission to read and write to the xAPI.
67 | xapi/read | Read all.
68 | statements/read | Read all statements.
69 | statements/write | Write statements (must be used with a read scope).
70 | statements/read/mine | Read my statements.
71 | state | Access state.
72 | profile | Access profiles.
73 |
74 | ### Example Model
75 |
76 | ```json
77 | {
78 | "_id" : "59c2371616bc715f83c34506",
79 | "createdAt" : "2017-09-20T09:40:44.962Z",
80 | "updatedAt" : "2017-09-20T09:40:58.376Z",
81 | "organisation" : "59c2371c16bc715f83c34507",
82 | "lrs_id" : "59c2371c16bc715f83c34508",
83 | "title" : "Example Client",
84 | "scopes" : [
85 | "xapi/all",
86 | "all"
87 | ],
88 | "isTrusted" : true,
89 | "authority" : "{\"objectType\":\"Agent\",\"name\":\"New Client\",\"mbox\":\"mailto:hello@learninglocker.net\"}",
90 | "api" : {
91 | "basic_secret" : "aaa",
92 | "basic_key" : "bbb"
93 | }
94 | }
95 | ```
96 |
--------------------------------------------------------------------------------
/src/guides-integrating.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Integrating your LRS Guide
5 | If you're planning to integrate a service with Learning Locker, you should firstly consider if you can use an existing integration between the service and the xAPI or Learning Locker. A [list of the existing integrations](#existing-integrations) has been provided below.
6 |
7 | If a tool does not already exist, then you should consider if creating the integration yourself is going to be too costly or challenging to implement. If this is the case, you can contact us via [hello@learninglocker.net](mailto:hello@learninglocker.net) to discuss your needs and gain advice using our experience.
8 |
9 | If you've decided you're going to implement the integration yourself, you're probably most interested in our guides for [inserting statements](../guides-inserting) and/or [retrieving statements](../guides-retrieving). You may also be interested in viewing the documentation for our HTTP interfaces especially our [xAPI HTTP interface](../http-xapi).
10 |
11 | ## Existing Integrations
12 | The below integrations can be implemented via our own internal tool. If you'd like to find out how to access these integrations, click on the links below or contact us at [hello@learninglocker.net](mailto:hello@learninglocker.net) for more information.
13 |
14 | - [Yammer](https://learningpool.com/solutions/learning-record-store-learning-locker/yammer/)
15 | - [Get Abstract](https://learningpool.com/solutions/learning-record-store-learning-locker/get-abstract/)
16 | - [Survey Monkey](https://learningpool.com/solutions/learning-record-store-learning-locker/survey-monkey/)
17 | - [Cornerstone on demand](https://learningpool.com/solutions/learning-record-store-learning-locker/cornerstone-on-demand/)
18 | - [Skillsoft](https://learningpool.com/solutions/learning-record-store-learning-locker/skillsoft/)
19 | - [Stream](https://learningpool.com/solutions/learning-experience-platform-stream/)
20 |
21 | ## Other integrations and Apps
22 |
23 | Below are a list of other integrations and some add-on Apps that might interest you. As mentioned above, click on the links or contact us at [hello@learninglocker.net](mailto:hello@learninglocker.net) for more information.
24 |
25 | - [Waves](https://learningpool.com/solutions/learning-experience-platform-stream/waves/) via our automation tool that uses statement forwards to trigger events such as emails, auto-enrolments and more.
26 | - [Semantic Analysis](https://learningpool.com/solutions/learning-record-store-learning-locker/semantic-analysis/) via our proprietary algorithm to drive insights into the quality of learners' submissions.
27 | - [Launchr](https://learningpool.com/solutions/learning-record-store-learning-locker/launchr/) via our proxy service that securely launch xAPI content packages.
28 | - [GDPR](https://learningpool.com/solutions/learning-record-store-learning-locker/gdpr/) via our standalone app that is a central administration tool for organizations seeking to comply with GDPR.
29 | - [CSV to xAPI](https://learningpool.com/solutions/learning-record-store-learning-locker/csv-to-xapi/) via our proprietary tool that converts CSV data into xAPI and inserts records into your LRS.
30 | - [Moodle](https://moodle.org/) via the [Logstore plugin](https://moodle.org/plugins/logstore_xapi) that we originally developed and released.
31 | - [Blackboard](http://www.blackboard.com/learning-management-system/blackboard-learn.aspx) via [JISC's Blackboard xAPI Plugin](https://github.com/jiscdev/blackboard-xapi-plugin/wiki).
32 |
--------------------------------------------------------------------------------
/src/http-xapi.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # xAPI HTTP Interface
5 | Learning Locker's HTTP interface conforms to the xAPI specification. The xAPI defines how to record learning experiences for analysis and serves as a guide to maximise interoperability between services that provide learning experiences.
6 |
7 | The table below describes the routes that the HTTP interface provides, all of the URLs are relative to http://www.example.org/data/xAPI where http://www.example.org is the URL of your Learning Locker instance. To access this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**.
8 |
9 | Route | Description
10 | --- | ---
11 | [GET /about](#get-about) | Retrieves information about the LRS.
12 | [PUT /statements](../http-xapi-statements#put-statements) | Stores a single statement.
13 | [POST /statements](../http-xapi-statements#post-statements) | Stores a single statement or multiple statements.
14 | [GET /statements](../http-xapi-statements#get-statements) | Retrieves statements.
15 | [PUT /activities/state](../http-xapi-states#put-activitiesstate) | Creates or overwrites a state document.
16 | [POST /activities/state](../http-xapi-states#post-activitiesstate) | Creates or merges a state document.
17 | [GET /activities/state](../http-xapi-states#get-activitiesstate) | Retrieves a single state document or multiple state identifiers.
18 | [DELETE /activities/state](../http-xapi-states#delete-activitiesstate) | Deletes a single state document or multiple state documents.
19 | [GET /activities](../http-xapi-activities#get-activities) | Retrieves a fully described activity.
20 | [PUT /activities/profile](../http-xapi-activities#put-activitiesprofile) | Creates or overwrites a profile document.
21 | [POST /activities/profile](../http-xapi-activities#post-activitiesprofile) | Creates or merges a profile document.
22 | [GET /activities/profile](../http-xapi-activities#get-activitiesprofile) | Retrieves a single profile document or multiple profile identifiers.
23 | [DELETE /activities/profile](../http-xapi-activities#delete-activitiesprofile) | Deletes a single profile document.
24 | [GET /agents](../http-xapi-agents#get-agents) | Retrieves all of the agents used by a person.
25 | [PUT /agents/profile](../http-xapi-agents#put-agentsprofile) | Creates or overwrites a profile document.
26 | [POST /agents/profile](../http-xapi-agents#post-agentsprofile) | Creates or merges a profile document.
27 | [GET /agents/profile](../http-xapi-agents#get-agentsprofile) | Retrieves a single profile document or multiple profile identifiers.
28 | [DELETE /agents/profile](../http-xapi-agents#delete-agentsprofile) | Deletes a single profile document.
29 |
30 | ## GET /about
31 | This route returns a JSON encoded object containing information about the LRS. A request to this route would look something like the request below.
32 |
33 | ```http
34 | GET http://www.example.org/data/xAPI/about
35 | Authorization: YOUR_BASIC_AUTH
36 | ```
37 |
38 | A request like the one above, will respond with a 200 response with a response body like the JSON below, which contains an array of the xAPI versions supported by your Learning Locker instance.
39 |
40 | ```http
41 | HTTP/1.1 200 OK
42 | Content-Type: application/json; charset=utf-8
43 | X-Experience-API-Version: 1.0.3
44 |
45 | { "version": ["1.0.1"] }
46 | ```
47 |
48 | For more information, view the [GET /about route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#28-about-resource).
49 |
--------------------------------------------------------------------------------
/src/http-persona-identifiers.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Persona Identifiers HTTP Interface
5 |
6 | Represents a unique identifier for a person. Statements that use these identifiers are linked to the [persona](../http-personas) that the identifier belongs to.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/personaidentifier.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/personaidentifier.
12 | - [Upsert HTTP Interface](#upsert-http-interface) via http://www.example.org/api/v2/personaidentifier/upsert.
13 |
14 | ### Schema
15 |
16 | Name | Description
17 | --- | ---
18 | _id | The id of this identifier.
19 | organisation | The id of the [organisation](../http-organisations) this identifier belongs to.
20 | persona | The id of the [persona](../http-personas) this identifier belongs to.
21 | ifi | A representation of the [inverse functional identifier](#inverse-functional-identifier).
22 |
23 | ### Example
24 |
25 | ```json
26 | {
27 | "_id" : "59c1219936229d4ce9634601",
28 | "organisation" : "59c1219936229d4ce9634602",
29 | "persona": "59c1219936229d4ce9634603",
30 | "ifi": {
31 | "key": "account",
32 | "value": {
33 | "homePage": "http://www.example.org",
34 | "name": "example-user"
35 | }
36 | }
37 | }
38 | ```
39 |
40 | # Inverse Functional Identifier
41 | According to the xAPI specification, an Inverse Functional Identifier (IFI) is "a value of an Agent or Identified Group that is guaranteed to only ever refer to that Agent or Identified Group".
42 |
43 | ### Schema
44 |
45 | Name | Description
46 | --- | ---
47 | key | The type of IFI ([account](#account-ifi), [mbox](#mbox-ifi), [mbox_sha1sum](#mbox-sha1sum-ifi), or [openid](#openid-ifi)).
48 | value | The value of the IFI.
49 |
50 | ### Account IFI
51 |
52 | ```json
53 | {
54 | "key": "account",
55 | "value": {
56 | "homePage": "http://www.example.org",
57 | "name": "example-user"
58 | }
59 | }
60 | ```
61 |
62 | ### Mbox IFI
63 |
64 | ```json
65 | {
66 | "key": "mbox",
67 | "value": "mailto:user@example.org"
68 | }
69 | ```
70 |
71 | ### Mbox Sha1Sum IFI
72 |
73 | ```json
74 | {
75 | "key": "mbox_sha1sum",
76 | "value": "cc1e39b02974c5d21e792d7febcaa6018bb6c574"
77 | }
78 | ```
79 |
80 | ### OpenID IFI
81 |
82 | ```json
83 | {
84 | "key": "openid",
85 | "value": "http://www.example.org/example-user"
86 | }
87 | ```
88 |
89 | # Upsert HTTP Interface
90 | This interface creates or updates a persona identifier depending on whether the IFI already exists.
91 |
92 | - If the `persona` property is not set, a persona will be created if the identifier's IFI doesn't exist.
93 | - If the `persona` property is set, the persona must already exist. Otherwise, a 404 response code will be returned.
94 |
95 | A request to the upsert route would look something like this:
96 |
97 | ```http
98 | POST http://www.example.org/api/v2/personaidentifier/upsert
99 | Authorization: YOUR_BASIC_AUTH
100 | Content-Type: application/json; charset=utf-8
101 |
102 | {
103 | "ifi": {
104 | "key": "account",
105 | "value": {
106 | "homePage": "http://www.example.org",
107 | "name": "example-user"
108 | }
109 | }
110 | }
111 | ```
112 |
113 | The interface will respond with a 200 response code, with detail the created/updated identifier.
114 |
115 | ```http
116 | HTTP/1.1 200 OK
117 | Content-Type: application/json; charset=utf-8
118 |
119 | {
120 | "_id" : "59c1219936229d4ce9634601",
121 | "organisation" : "59c1219936229d4ce9634602",
122 | "persona": "59c1219936229d4ce9634603",
123 | "ifi": {
124 | "key": "account",
125 | "value": {
126 | "homePage": "http://www.example.org",
127 | "name": "example-user"
128 | }
129 | }
130 | }
131 | ```
132 |
--------------------------------------------------------------------------------
/src/_layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include head.html %}
5 |
6 |
7 |
8 | {% include header.html %}
9 |
10 |
83 |
84 | {% include footer.html %}
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/http-users.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Users HTTP Interface
5 |
6 | A user of learning locker.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/user.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/user.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The id of this user.
18 | name | Name of the user.
19 | email | The email of the user.
20 | organisations | A list of [organisation](../http-organisations#schema) ids that this user belongs to.
21 | organisationSettings | A list of [organisation settings](#organisation-settings) for this user
22 | imageUrl | A url of an image for this user.
23 | googleId | The user's google id.
24 | ownerOrganisation | The id of the users owning [organisation](../http-organisations#schema)
25 | ownerOrganisationSettings | Duplication of [organisation settings](../http-organisations#schema).
26 | settings | An object of specific users settings. [see settings](#settings)
27 | scopes | Global [scopes](../http-roles#scopes) for this user.
28 | verified | Whether this user has been verified.
29 | resetTokens | A list of reset tokens. [See reset token](#reset-token).
30 | passwordHistory | A list of users previously used password hashes.
31 | authLastAttempt | When the user last attempted to authenticate.
32 | authFailedAttempts | Number of failed authentication attempts.
33 | authLockoutExpiry | When the user can attempt to authenticate again.
34 |
35 | ### Organisation Settings
36 |
37 | A list of organisation settings:
38 |
39 | Name | Description
40 | --- | ---
41 | organisation | The id of the [organisation](organisations#schema) of these settings.
42 | scopes | List of [scopes](../http-roles#scopes) that this user has permissions for in this [organisation](organisations#schema).
43 | roles | List of [role](../http-roles#schema) ids that this user has permissions for in the [organisation](organisations#schema).
44 | filter | A [statement](../http-statements#schema) filter which restricts which statements this user has access too.
45 |
46 | ### Settings
47 |
48 | Name | Description
49 | --- | ---
50 | CONFIRM_BEFORE_DELETE | If true, will prompt user before delete actions.
51 |
52 | ### Reset Token
53 |
54 | A token which is used to reset a user's password.
55 |
56 | Name | Description
57 | --- | ---
58 | token | The token.
59 | expires | When this token expires.
60 |
61 | ### Example Model
62 |
63 | ```json
64 | {
65 | "_id" : "59c2371c16bc715f83c34501",
66 | "createdAt" : "2017-09-19T12:07:25.536Z",
67 | "updatedAt" : "2017-09-19T15:06:54.027Z",
68 | "email" : "example@example.org",
69 | "password" : "aaa",
70 | "authLockoutExpiry" : null,
71 | "authFailedAttempts" : 0,
72 | "authLastAttempt" : "2017-09-19T15:06:54.020Z",
73 | "passwordHistory" : [
74 | {
75 | "date" : "2017-09-19T12:07:25.505Z",
76 | "hash" : "aaa",
77 | "_id" : "59c2371c16bc715f83c34502"
78 | },
79 | {
80 | "date" : "2017-09-19T12:47:34.093Z",
81 | "hash" : "bbb",
82 | "_id" : "59c2371c16bc715f83c34503"
83 | }
84 | ],
85 | "resetTokens" : [ ],
86 | "verified" : true,
87 | "scopes" : [
88 | "site_admin"
89 | ],
90 | "settings" : {
91 | "CONFIRM_BEFORE_DELETE" : true
92 | },
93 | "ownerOrganisationSettings" : {
94 | "PASSWORD_CUSTOM_MESSAGE" : null,
95 | "PASSWORD_CUSTOM_REGEX" : null,
96 | "PASSWORD_USE_CUSTOM_REGEX" : false,
97 | "PASSWORD_REQUIRE_NUMBER" : true,
98 | "PASSWORD_REQUIRE_ALPHA" : true,
99 | "PASSWORD_MIN_LENGTH" : 8,
100 | "PASSWORD_HISTORY_TOTAL" : 3,
101 | "PASSWORD_HISTORY_CHECK" : true,
102 | "LOCKOUT_SECONDS" : 1800,
103 | "LOCKOUT_ATTEMPS" : 5,
104 | "LOCKOUT_ENABLED" : true
105 | },
106 | "organisationSettings" : [
107 | {
108 | "organisation" : "59c2371c16bc715f83c34504",
109 | "_id" : "59c2371c16bc715f83c34505",
110 | "filter" : "{}",
111 | "roles" : [ ],
112 | "scopes" : [
113 | "all"
114 | ]
115 | }
116 | ],
117 | "organisations" : [
118 | "59c2371c16bc715f83c34504",
119 | "59c2371c16bc715f83c34506",
120 | "59c2371c16bc715f83c34507"
121 | ],
122 | "ownerOrganisation" : "59c2371c16bc715f83c34504"
123 | }
124 | ```
125 |
--------------------------------------------------------------------------------
/src/http-statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Statements HTTP Interface
5 |
6 | Statements hold all of the xAPI records that have been inserted into the database via the [xAPI Statements HTTP interface](../http-xapi-statements).
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/statement.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/statement.
12 |
13 | Whilst statements _can_ be inserted via the Models HTTP interface, this is not recommended as it bypasses all validation, reference and void checking, leading to invalid xAPI records.
14 |
15 | Statements can be deleted by the REST API. See [Statement Deletion API](../http-statement-deletion) for more information.
16 |
17 | Optional metadata can be added to a statement via the [Metadata API](../http-metadata).
18 | We automatically add metadata in cases such as for [question statements](../guides-assessment-statements) (typically coming from Adapt), which makes reporting on assessments much easier than previously possible.
19 |
20 | ### Schema
21 |
22 | Name | Type | Description
23 | --- | --- | ---
24 | _id | Mongo ID | The id of the role _(autogenerated)_
25 | stored | Mongo Date | When this statement was created _(autogenerated)_
26 | timestamp | Mongo Date | When this statement's activity occured _(autogenerated)_
27 | organisation | Mongo ID | The organisation this statement belongs in _(autogenerated)_
28 | lrs_id | Mongo ID | The store this statement belongs in _(autogenerated)_
29 | client | Mongo ID | The client that inserted this statement _(autogenerated)_
30 | person | [Person](#person) | Information about the person associated to the actor in this statement
31 | voided | Boolean | Has the statement been voided
32 | hash | String | A unique hash of the statement used for conflict checks on insert _(autogenerated)_
33 | statement | [Statement](#statement) | The statement object as constructed and validated by the xAPI service
34 | metadata | [Metadata](../http-metadata#schema) | The metadata related to the statement
35 | refs | Array [[Statement References](#statement-references)]
36 |
37 | ### Person
38 |
39 | Name | Type | Description
40 | --- | --- | ---
41 | _id | Mongo ID | The id of the person _(autogenerated)_
42 | name | String | The name of the person - this is auto populated for quick reference to avoid lookups _(autogenerated)_
43 |
44 | ### Statement
45 |
46 | Please refer to the [Statement Properties](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#24-statement-properties) in the xAPI specification for a full list of possible entries
47 |
48 | ### Statement References
49 |
50 | This contains an array of any statements that are referenced as part of this statement structure.
51 |
52 | ### Example GET data
53 | ```json
54 | {
55 | "_id" : "111aaa1111a111111aa11111",
56 | "hasGeneratedId" : false,
57 | "organisation" : "111aaa1111a111111aa11111",
58 | "lrs_id" : "111aaa1111a111111aa11111",
59 | "client" : "111aaa1111a111111aa11111",
60 | "person" : {
61 | _id: "111aaa1111a111111aa11111",
62 | name: "Joe Bloggs"
63 | },
64 | "voided" : false,
65 | "timestamp" : "2017-09-08T09:41:58.136Z",
66 | "stored" : "2017-09-08T09:41:58.136Z",
67 | "hash" : "05db245f890c4e5f9513658435ddd5e16c579df1",
68 | "refs" : [ ],
69 | "statement" : {
70 | "actor" : {
71 | "objectType" : "Agent",
72 | "name" : "xAPI mbox",
73 | "mbox" : "mailto:xapi@adlnet.gov"
74 | },
75 | "verb" : {
76 | "id" : "http://adlnet.gov/expapi/verbs/attended",
77 | "display" : {
78 | "en-GB" : "attended",
79 | "en-US" : "attended"
80 | }
81 | },
82 | "object" : {
83 | "objectType" : "Activity",
84 | "id" : "http://www.example.com/meetings/occurances/34534"
85 | },
86 | "id" : "3c5f9182-81ba-4e4c-9aa4-93a6b57a61ee",
87 | "timestamp" : "2017-09-08T09:41:58.136Z",
88 | "stored" : "2017-09-08T09:41:58.136Z",
89 | "authority" : {
90 | "mbox" : "mailto:hello@learninglocker.net",
91 | "name" : "New Client",
92 | "objectType" : "Agent"
93 | },
94 | "version" : "1.0.0"
95 | }
96 | }
97 | ```
98 |
--------------------------------------------------------------------------------
/src/http-statement-forwarding.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Statement Forwarding HTTP Interface
5 |
6 | Details of a statement forwarding configuration.
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/statementforwarding.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/statementforwarding.
12 |
13 | ### Schema
14 |
15 | Name | Description
16 | --- | ---
17 | _id | The unique id of the document.
18 | createdAt | When the document was created.
19 | updatedAt | When the document was updated.
20 | description | The name of this statement forwarder.
21 | lrs_id | The id of the [store](../http-stores#schema) that Learning Locker will forward statements for.
22 | active | If this statement forwarder is currently active.
23 | configuration | The configuration for this statement forwarder [See configuration](#configuration).
24 | owner | Id of the [user](../http-users#schema) who created this statement forwarder.
25 | query | A JSON Mongo query string - only statements which match this query will be forwarded.
e.g. `'{"statement.verb.id":"http://adlnet.gov/expapi/verbs/completed"}'`
26 | isPublic | If false then this statement forwarder is only available to the owner and users with [org/all/statementForwarding/view scope](../http-roles/#organisation-scopes), otherwise it's available to everyone in the organisation with permission.
27 |
28 | ### Configuration
29 |
30 | The configuration for the statement forwarding request.
31 |
32 | Name | Description
33 | --- | ---
34 | protocol | http, https. The protocol to forward statements to.
35 | url | The url to forward statement to.
36 | authType | no auth, token, basic auth. The auth method to use.
37 | secret | If authType is token, this is the token which will be sent with the request.
38 | basicUsername | If authType is basic auth, this is the basic auth username.
39 | basicPassword | If authType is basic auth, this is the basic auth password.
40 | maxRetries | The number of times the statement forwarder will retry before giving up.
41 | headers | A json array encoded as a string which contains additional headers to send with the request.
42 |
43 | ### Example Model
44 |
45 | ```json
46 | {
47 | "_id" : "59c8d14b0d82b3864a450604",
48 | "createdAt" : "2017-09-25T09:50:03.880Z",
49 | "updatedAt" : "2017-11-06T14:07:27.212Z",
50 | "owner" : "59198183d8ea540933227030",
51 | "query" : "{}",
52 | "organisation" : "59c209c4ad95fd50960c0362",
53 | "isPublic" : false,
54 | "configuration" : {
55 | "authType" : "no auth",
56 | "protocol" : "https",
57 | "url" : "example.org/endpoint",
58 | "maxRetries" : 10,
59 | "headers" : "{\"Test-Header-Key\":\"Test-Header-Value\"}",
60 | "secret" : "Dave"
61 | },
62 | "__v" : 0,
63 | "active" : true,
64 | "description" : "Statement forwarder"
65 | }
66 | ```
67 |
68 | ### *[Enterprise]*: Forwarding to AWS Kinesis
69 | _Note: Only available in Enterprise editions of Learning Locker_
70 |
71 | To create a statement forward configured for AWS Kinesis Firehose, configure the record with this modified data structure:
72 |
73 | ```js
74 | "configuration" : {
75 | "protocol" : "Kinesis",
76 | "authType" : "no auth"
77 | },
78 | "kinesisOptions" : {
79 | "streamName" : "KinesisFirehoseName", // The immutable name of the Kinesis Firehose configured in AWS
80 | "awsClientKey" : "xxxxxxxxxxxx", // AWS client access key with appropriate permission
81 | "awsClientSecret" : "xxxxxxxxxxxx", // AWS client secret key
82 | "awsRegion" : "us-east-1" // AWS Kinesis Firehose region
83 | },
84 | ```
85 |
86 | #### Permissions
87 | In order for Learning Locker to successfully write to the Kinesis Firehose, please ensure that the IAM user (attributed to the key/secret) has the minimum permissions in its policy (as shown below). When using the example below, please replace `region`, `account-id`, and `KinesisFirehoseName` with your details in the `Resource` array.
88 |
89 | ```json
90 | {
91 | "Version": "2012-10-17",
92 | "Statement": [
93 | {
94 | "Effect": "Allow",
95 | "Action": [
96 | "firehose:PutRecord",
97 | ],
98 | "Resource": [
99 | "arn:aws:firehose:region:account-id:deliverystream/KinesisFirehoseName"
100 | ]
101 | }
102 | ]
103 | }
104 | ```
105 |
--------------------------------------------------------------------------------
/src/css/docs.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Thanks to http://getbootstrap.com/examples/blog/ for initial inspiration.
3 | */
4 |
5 | /*
6 | * Globals
7 | */
8 |
9 | h1, .h1,
10 | h2, .h2,
11 | h3, .h3,
12 | h4, .h4,
13 | h5, .h5,
14 | h6, .h6 {
15 | margin-top: 0;
16 | font-family: 'Open Sans', Arial, sans-serif;
17 | font-weight: normal;
18 | text-transform: uppercase;
19 | text-decoration: none;
20 | color: #696969;
21 | line-height: 1.2em;
22 | }
23 |
24 | h1 .highlighter-rouge, .h1 .highlighter-rouge,
25 | h2 .highlighter-rouge, .h2 .highlighter-rouge,
26 | h3 .highlighter-rouge, .h3 .highlighter-rouge,
27 | h4 .highlighter-rouge, .h4 .highlighter-rouge,
28 | h5 .highlighter-rouge, .h5 .highlighter-rouge,
29 | h6 .highlighter-rouge, .h6 .highlighter-rouge {
30 | text-transform: none;
31 | }
32 |
33 | body {
34 | font-family: 'Open Sans', Arial, sans-serif;
35 | font-size: 14px;
36 | font-weight: 500;
37 | color: #666;
38 | line-height: 1.7em;
39 | -webkit-font-smoothing: antialiased;
40 | -moz-osx-font-smoothing: grayscale;
41 | }
42 |
43 | a { color: #ed7030; }
44 | a:focus, a:hover { color: #ed7030; }
45 |
46 | h1 {
47 | font-size: 30px;
48 | }
49 |
50 | h2 {
51 | font-size: 26px;
52 | margin-top: 30px;
53 | }
54 |
55 | h3 {
56 | font-size: 22px;
57 | margin-top: 30px;
58 | }
59 |
60 | h4 {
61 | font-size: 16px;
62 | margin-top: 20px;
63 | color: #95989a;
64 | }
65 |
66 | blockquote {
67 | background-color: #f1f1f1; /* #f4f8fa; */
68 | margin: 20px 0;
69 | padding: 20px;
70 | border-left: 3px solid #f2a942;
71 | }
72 | blockquote p {
73 | font-size:13px;
74 | }
75 | blockquote strong {
76 | color: #f2a942;
77 | font-weight:bold;
78 | font-size:14px;
79 | }
80 |
81 | .docs-sidebar .nav>li>a {
82 | padding: 2px 15px;
83 | }
84 |
85 | .banner{
86 | background:#e4e7ec; /* url(../img/banner.jpg) no-repeat; */
87 | margin-bottom:20px;
88 | }
89 |
90 | .logo {
91 | margin-top:4px;
92 | }
93 |
94 | .active {
95 | background:#e4e7ec;
96 | border-radius:6px;
97 | }
98 |
99 | /*
100 | * Masthead for nav
101 | */
102 |
103 | .docs-masthead {
104 | padding: 5px 0 5px 0;
105 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
106 | -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
107 | -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
108 | }
109 |
110 | .docs-masthead img {
111 | height: 48px;
112 | width: 200px;
113 | }
114 |
115 | /* Nav links */
116 | .docs-nav-item {
117 | position: relative;
118 | display: inline-block;
119 | padding: 18px;
120 | font-weight: 600;
121 | color: #696969;
122 | }
123 | .docs-nav-item:hover,
124 | .docs-nav-item:focus {
125 | opacity: 0.7;
126 | color: #696969;
127 | text-decoration: none;
128 | transition: all 0.4s ease-in-out;
129 | }
130 |
131 | /*
132 | * docs name and description
133 | */
134 |
135 | .docs-header {
136 | padding-top: 15px;
137 | padding-bottom: 15px;
138 | }
139 | .docs-title {
140 | margin-top: 30px;
141 | margin-bottom: 10px;
142 | font-size: 40px;
143 | font-weight: normal;
144 | /* color:#fff; */
145 | }
146 | .docs-description {
147 | font-size: 20px;
148 | color: #999;
149 | }
150 |
151 | .docs-main {
152 | margin-bottom:60px;
153 | margin-top: 20px;
154 | }
155 |
156 | .docs-main img {
157 | width: 100%;
158 | }
159 |
160 | .docs-sidebar {
161 | margin-top: 10px;
162 | margin-bottom:40px;
163 | }
164 |
165 | .docs-sidebar .sidebar-title {
166 | font-weight:bold;
167 | margin:10px 0 5px 0;
168 | }
169 |
170 | .docs-sidebar .sidebar-title .top {
171 | margin:0 0 10px 0;
172 | }
173 |
174 | .docs-sidebar .nav>li>a:hover,
175 | .docs-sidebar .nav>li>a :focus {
176 | background: none;
177 | text-decoration: underline;
178 | }
179 |
180 | /*
181 | * Footer
182 | */
183 |
184 | .docs-footer {
185 | padding: 40px 0;
186 | color: #999;
187 | text-align: center;
188 | background-color: #f8f9fa;
189 | }
190 |
191 | /* Laravel paste inspired code display
192 | *****************************************************************/
193 |
194 | pre{
195 | background:transparent;
196 | padding:0;
197 | font-size:12px;
198 | border:0;
199 | line-height:1.9em;
200 | border-radius:6px;
201 | }
202 |
203 | pre code {
204 | padding:20px;
205 | }
206 |
207 | code {
208 | white-space: pre-wrap !important;
209 | color: #cc7228;
210 | }
211 |
212 | @media (max-width: 770px) {
213 |
214 | .banner {
215 | background:#f1f1f1; /*transparent;*/
216 | }
217 |
218 | .docs-title {
219 | color:#222;
220 | font-size:30px;
221 | margin-bottom:20px;
222 | }
223 |
224 | }
225 |
--------------------------------------------------------------------------------
/src/http-aggregate.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Aggregate HTTP Interface
5 |
6 | The Learning Locker Aggregate HTTP interface utilises the [Mongo aggregation API](https://docs.mongodb.com/manual/aggregation/) and is only available for allowed collections.
7 | The Aggregate HTTP Interface is more advanced than the xAPI HTTP interface and allows you to access MongoDB's powerful Aggregation API for more custom filtration of documents.
8 |
9 | ## Allowed Collections
10 |
11 | This interface can be used to aggregate on multiple **allowed only** collections. To check whether a collection is allowed for aggregation or not, we use a regular expression. You can specify your own rule using `AGGREGATE_API_ALLOWED_COLLECTIONS` environment variable with `^rollup` default value that means that by default you can aggregate on collections that prefixed with "rollup" word.
12 |
13 | ## Request Setup
14 |
15 | When using this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header.
16 | Your Basic Auth details can be found under **Settings** > **Clients**. The interface can be passed options via a JSON object in the request body (described in the table below). The `collection` property is required. The `collection` is a string that determines the collection name used to execute the aggregation on. The other properties are optional.
17 |
18 | Name | Description | Default Value
19 | --- | --- | ---
20 | pipeline | Array containing a pipeline of stages for documents to pass through before being output from the interface. | `[]`
(in this case, you'll get records from the desired collection without any transformations)
21 | skip | Number that specifies a number of records that should be skipped from the beginning. This parameter will be transformed into `$skip` aggregation stage and executed at the very end. | `0`
22 | limit | Number that specifies a number of records that will be cut from the final results. This parameter will be transformed into `$limit` aggregation stage and executed at the very end. | `10000`
23 | maxTimeMS | Number that specifies the time limit for the query in Mongo. | `0`
(if nothing else was set to `MAX_TIME_MS` environment variable)
24 | batchSize | Specifies the number of documents to return in each batch of the response from the MongoDB instance. Defaults to `100` | `100`
25 |
26 | A simple request to this interface using all of the available parameters looks like the request below.
27 |
28 | ```http
29 | POST http://www.example.org/api/aggregate
30 | Authorization: Basic YOUR_BASIC_AUTH
31 | Content-Type: application/json; charset=utf-8
32 |
33 | {
34 | "collection": "rollupUniqueVerbsByPlatformDaily",
35 | "pipeline": [],
36 | "skip": 0,
37 | "limit": 1,
38 | "maxTimeMs": 100,
39 | "batchSize": 100
40 | }
41 |
42 | ```
43 | A response to this valid request will return a 200 response like the response below, where the JSON encoded body contains several keys.
44 |
45 | ```http
46 | {
47 | "result": [
48 | {
49 | "_id" : "111aaa1111a111111aa11111",
50 | "organisationId" : "111aaa1111a111111aa11111",
51 | "organisationName" : [
52 | "My amazing org"
53 | ],
54 | "lrsId" : "111aaa1111a111111aa11111",
55 | "lrsName" : [
56 | "My awesome store"
57 | ],
58 | "clientId" : "111aaa1111a111111aa11111",
59 | "clientName" : [
60 | "My incredible client"
61 | ],
62 | "platform" : "Some Cool Platform",
63 | "verb" : "http://adlnet.gov/expapi/verbs/scored",
64 | "count" : 200,
65 | "date" : "2017-08-01T00:00:00Z",
66 | "storedDate" : "2017-08-01T00:00:00Z"
67 | }
68 | ],
69 | "startedAt": "2020-04-03 14:17:30.924+03:00",
70 | "completedAt": "2020-04-03 14:17:30.943+03:00"
71 | }
72 | ```
73 |
74 | ## Pipeline Stages
75 | So far we've only seen the `limit` and `project` stages, however, there are many other [stages available in Mongo](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/). The common stages are listed in the table below.
76 |
77 | Name | Description
78 | --- | ---
79 | [project](#project-stage) | Reshapes the records from the previous stage of the pipeline for the next stage.
80 | [match](#match-stage) | Filters the records from the previous stage of the pipeline for the next stage.
81 | [limit](#limit-stage) | Limits the number of records from the previous stage of the pipeline for the next stage.
82 | [skip](#skip-stage) | Skips a number of records from the previous stage of the pipeline for the next stage.
83 | [group](#group-stage) | Groups records by a specified identifier from the previous stage of the pipeline for the next stage.
84 | [sort](#sort-stage) | Sorts the records from the previous stage of the pipeline for the next stage.
85 |
--------------------------------------------------------------------------------
/src/guides-indexing.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Indexing
5 | If some of your queries are taking a while to run, you can take advantage of database indexes to improve performance. You can create indexes in your database via [the Mongo shell](https://docs.mongodb.com/manual/mongo/). You can run the code below in your Mongo shell to create our recommended indexes. More information is available about [using indexes via Mongo's documentation](https://docs.mongodb.com/manual/indexes/). If utilising indexes doesn't have the required performance improvement, you can instead utilise [BI tools](../guides-retrieving).
6 |
7 | ```js
8 | // Statements
9 | db.statements.createIndex({ "statement.id": 1, lrs_id: 1 }, { background: true });
10 | db.statements.createIndex({ stored: -1 }, { background: true });
11 |
12 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "statement.object.id": 1, "statement.object.objectType": 1 }, { background: true });
13 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "statement.verb.id": 1, "statement.object.objectType": 1 }, { background: true });
14 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "statement.verb.id": 1, "statement.object.id": 1 }, { background: true });
15 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "statement.actor.mbox": 1 }, { background: true });
16 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "statement.actor.account.name": 1, "statement.actor.account.homePage": 1 }, { background: true });
17 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "timestamp": -1, _id: -1 }, { background: true });
18 | db.statements.createIndex({ organisation: 1, lrs_id: 1, "voided": 1, "stored": -1, _id: -1 }, { background: true });
19 |
20 | db.statements.createIndex({ organisation: 1, lrs_id: 1, hash: 1 }, {unique: true, background: true});
21 |
22 | db.statements.createIndex({ organisation: 1, lrs_id: 1, activities: 1, timestamp: -1}, {background: true});
23 | db.statements.createIndex({ organisation: 1, lrs_id: 1, agents: 1, timestamp: -1}, {background: true});
24 | db.statements.createIndex({ organisation: 1, lrs_id: 1, registrations: 1, timestamp: -1}, {background: true});
25 | db.statements.createIndex({ organisation: 1, lrs_id: 1, relatedActivities: 1, timestamp: -1}, {background: true});
26 | db.statements.createIndex({ organisation: 1, lrs_id: 1, relatedAgents: 1, timestamp: -1}, {background: true});
27 | db.statements.createIndex({ organisation: 1, lrs_id: 1, verbs: 1, timestamp: -1}, {background: true});
28 |
29 | db.statements.createIndex({ organisation: 1, lrs_id: 1, activities: 1, stored: -1}, {background: true});
30 | db.statements.createIndex({ organisation: 1, lrs_id: 1, agents: 1, stored: -1}, {background: true});
31 | db.statements.createIndex({ organisation: 1, lrs_id: 1, registrations: 1, stored: -1}, {background: true});
32 | db.statements.createIndex({ organisation: 1, lrs_id: 1, relatedActivities: 1, stored: -1}, {background: true});
33 | db.statements.createIndex({ organisation: 1, lrs_id: 1, relatedAgents: 1, stored: -1}, {background: true});
34 | db.statements.createIndex({ organisation: 1, lrs_id: 1, verbs: 1, stored: -1}, {background: true});
35 |
36 | db.statements.createIndex({ organisation: 1, "statement.object.id": 1 }, { background: true });
37 | db.statements.createIndex({ organisation: 1, "statement.verb.id": 1, "statement.object.id": 1 }, { background: true });
38 | db.statements.createIndex({ organisation: 1, "timestamp": -1, _id: 1 }, { background: true });
39 | db.statements.createIndex({ organisation: 1, "stored": -1, _id: 1 }, { background: true });
40 | db.statements.createIndex({ organisation: 1, "voided": 1 }, { background: true });
41 |
42 | db.statements.createIndex({ organisation: 1, personaIdentifier: 1 }, { background: true });
43 | db.statements.createIndex({ organisation: 1, "person._id": 1, timestamp: -1 }, { background: true });
44 | db.statements.createIndex({ "person._id": 1}, { background: true});
45 |
46 | // Personas
47 | db.personas.createIndex({organisation: 1}, {background: true});
48 | db.personaIdentifiers.createIndex({organisation: 1, persona: 1}, {background: true});
49 | db.personaIdentifiers.createIndex({ organisation: 1, "ifi.key": 1, "ifi.value.homePage": 1, "ifi.value.name": 1}, {background: true})
50 | db.personaIdentifiers.createIndex({ organisation: 1, "ifi.key": 1, "ifi.value": 1}, {background: true, unique: true});
51 | db.personaAttributes.createIndex({organisation: 1, personaId: 1, key: 1}, {background: true});
52 | db.personaAttributes.createIndex({personaId: 1, key: 1}, {background: true});
53 |
54 | // State API
55 | db.states.createIndex({ "organisation" : 1, "lrs" : 1, "activityId" : 1, "agent.account.homePage" : 1, "agent.account.name" : 1, "stateId" : 1, "registration" : 1}, {background: true});
56 | db.states.createIndex({ "organisation" : 1, "lrs" : 1, "activityId" : 1, "agent.mbox" : 1, "stateId" : 1}, {background: true});
57 | db.states.createIndex({ "organisation" : 1, "lrs" : 1, "activityId" : 1, "stateId" : 1}, {background: true});
58 |
59 | // Others
60 | db.client.createIndex({ "api.basic_key": 1, "api.basic_secret": 1}, {unique: true, background: true});
61 | db.fullActivities.createIndex({organisation:1, lrs_id: 1, activityId:1}, {unique: true, background:true});
62 |
63 | ```
64 |
--------------------------------------------------------------------------------
/src/guides-video-statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Video Statements
5 |
6 | - [User initialized video](#user-initialized-video)
7 | - [User played video](#user-played-video)
8 | - [User paused video](#user-paused-video)
9 | - [User terminated video](#user-paused-video)
10 |
11 | The statements shown here are guided by the [xAPI Video Profile](https://liveaspankaj.gitbooks.io/xapi-video-profile/content/statement_data_model.html) created by the [xAPI Video Community of Practice](https://liveaspankaj.gitbooks.io/xapi-video-profile/content/). All times and lengths represented as numbers below should use seconds as the unit.
12 |
13 | ## User initialized video
14 | An "Initialized" statement is used by the LRP to indicate that the video has been fully initialized or launched/accessed.
15 |
16 | ### Statement
17 | ```json
18 | {
19 | "actor": {
20 | "objectType": "Agent",
21 | "mbox": "mailto:user@example.org",
22 | "name": "Example User"
23 | },
24 | "verb": {
25 | "id": "http://adlnet.gov/expapi/verbs/initialized",
26 | "display": {
27 | "en-US": "initialized"
28 | }
29 | },
30 | "object": {
31 | "objectType": "Activity",
32 | "id": "http://www.example.org/video/1",
33 | "definition": {
34 | "type": "https://w3id.org/xapi/video/activity-type/video",
35 | "name": {
36 | "en-US": "Example Video"
37 | },
38 | "extensions": {
39 | "https://w3id.org/xapi/video/extensions/length": 124.3
40 | }
41 | }
42 | },
43 | "context": {
44 | "platform": "Brightcove",
45 | "language": "en",
46 | "extensions": {
47 | "https://w3id.org/xapi/video/extensions/session-id": "9a2ce995-c3f5-458a-9fd3-cab07ea005c0"
48 | }
49 | }
50 | }
51 | ```
52 |
53 | ## User played video
54 | Indicates that the actor started experiencing the recorded media object.
55 |
56 | ### Statement
57 | ```json
58 | {
59 | "actor": {
60 | "objectType": "Agent",
61 | "mbox": "mailto:user@example.org",
62 | "name": "Example User"
63 | },
64 | "verb": {
65 | "id": "https://w3id.org/xapi/video/verbs/played",
66 | "display": {
67 | "en-US": "played"
68 | }
69 | },
70 | "object": {
71 | "objectType": "Activity",
72 | "id": "http://www.example.org/video/1",
73 | "definition": {
74 | "type": "https://w3id.org/xapi/video/activity-type/video",
75 | "name": {
76 | "en-US": "Example Video"
77 | }
78 | }
79 | },
80 | "context": {
81 | "platform": "Brightcove",
82 | "language": "en",
83 | "extensions": {
84 | "https://w3id.org/xapi/video/extensions/session-id": "9a2ce995-c3f5-458a-9fd3-cab07ea005c0"
85 | }
86 | },
87 | "result": {
88 | "extensions": {
89 | "https://w3id.org/xapi/video/extensions/time": 0
90 | }
91 | }
92 | }
93 | ```
94 |
95 | ## User paused video
96 | Indicates that the actor temporary or permanently stopped experiencing the recorded media object.
97 |
98 | ### Statement
99 | ```json
100 | {
101 | "actor": {
102 | "objectType": "Agent",
103 | "mbox": "mailto:user@example.org",
104 | "name": "Example User"
105 | },
106 | "verb": {
107 | "id": "https://w3id.org/xapi/video/verbs/paused",
108 | "display": {
109 | "en-US": "paused"
110 | }
111 | },
112 | "object": {
113 | "objectType": "Activity",
114 | "id": "http://www.example.org/video/1",
115 | "definition": {
116 | "type": "https://w3id.org/xapi/video/activity-type/video",
117 | "name": {
118 | "en-US": "Example Video"
119 | }
120 | }
121 | },
122 | "context": {
123 | "platform": "Brightcove",
124 | "language": "en",
125 | "extensions": {
126 | "https://w3id.org/xapi/video/extensions/session-id": "9a2ce995-c3f5-458a-9fd3-cab07ea005c0"
127 | }
128 | },
129 | "result": {
130 | "extensions": {
131 | "https://w3id.org/xapi/video/extensions/time": 57.1
132 | }
133 | }
134 | }
135 | ```
136 |
137 | ## User terminated video
138 | Used to express that the actor ended a video.
139 |
140 | ### Metadata
141 | ```json
142 | {
143 | "https://learninglocker&46;net/duration": {
144 | "seconds": 124.3
145 | }
146 | }
147 | ```
148 |
149 | ### Statement
150 | ```json
151 | {
152 | "actor": {
153 | "objectType": "Agent",
154 | "mbox": "mailto:user@example.org",
155 | "name": "Example User"
156 | },
157 | "verb": {
158 | "id": "http://adlnet.gov/expapi/verbs/terminated",
159 | "display": {
160 | "en-US": "terminated"
161 | }
162 | },
163 | "object": {
164 | "objectType": "Activity",
165 | "id": "http://www.example.org/video/1",
166 | "definition": {
167 | "type": "https://w3id.org/xapi/video/activity-type/video",
168 | "name": {
169 | "en-US": "Example Video"
170 | },
171 | "extensions": {
172 | "https://w3id.org/xapi/video/extensions/length": 124.3
173 | }
174 | }
175 | },
176 | "context": {
177 | "platform": "Brightcove",
178 | "language": "en",
179 | "extensions": {
180 | "https://w3id.org/xapi/video/extensions/session-id": "9a2ce995-c3f5-458a-9fd3-cab07ea005c0"
181 | }
182 | },
183 | "result": {
184 | "completion": true,
185 | "duration": "PT124.3S",
186 | "extensions": {
187 | "https://w3id.org/xapi/video/extensions/time": 124.3,
188 | "https://w3id.org/xapi/video/extensions/progress": 1
189 | }
190 | }
191 | }
192 | ```
193 |
--------------------------------------------------------------------------------
/src/http-journey-progress.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Journey Progress HTTP Interface
5 |
6 | > `Please note:` Journeys are only available in our Enterprise hosted version of Learning Locker. For more information about the differences, please see [this page](https://learninglocker.net/faqs/open-source-vs-saas-lrs/).
7 |
8 | An actor/person's progress through a journey.
9 |
10 | It is accessible through the following HTTP interfaces:
11 |
12 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/journeyProgress.
13 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/journeyProgress.
14 |
15 | ### Schema
16 |
17 | Name | Type | Description
18 | --- | --- | ---
19 | _id | Mongo ID | The id of this journey progress _(autogenerated)_
20 | organisation| [Organisation](../http-organisations#schema) Mongo ID | The id of the organisation
21 | createdAt | Timestamp | When this journey progress was created _(autogenerated)_
22 | updatedAt | Timestamp | When this journey progress was last updated _(autogenerated)_
23 | owner | [owner](#owner) | The ident associated to this progress
24 | pendingProgress | [pendingProgress](#pending-progress) | An object of pendingProgress _(autogenerated)_
25 | completions | [completions](#completions) | An object of completed statements.
26 | startedAt | Timestamp | When the first attempt of a waypoint was registered
27 | isCompleted | Boolean | Has this owner completed this journey?
28 | completedAt | Timestamp | When was the journey first completed
29 | duration | microseconds | Time between `startedAt` and `completedAt` in [microseconds](https://en.wikipedia.org/wiki/Microsecond)
30 |
31 |
32 | ### Owner
33 |
34 | This is a reference to the xAPI actor or Learning Locker Persona associated to this progress.
35 |
36 | Name | Type | Description
37 | --- | --- | ---
38 | trackBy | string | Only `"actor"` currently supported, `"persona"` tracking coming soon
39 | ident | mixed | Either a full xAPI actor object or a [Persona Mongo ID](../http-personas)
40 |
41 | ### Pending Progress
42 |
43 | This is a reference to statements which need processing.
44 |
45 | It is an object whose key is the waypointID:
46 |
47 | Name | Type | Decsription
48 | --- | --- | ---
49 | statement | Array of [Journey Progress Statement](#journey-progress-statement) | The reference to the statement.
50 |
51 | ### Completions
52 |
53 | A journey can be completed multiple times if the journey is set to be repeatable. An array of completed journey attempts is stored in this value.
54 |
55 | Name | Type | Description
56 | --- | --- | ---
57 | waypoints | object | An object of [completed waypoints](#completed-waypoint) whose key are the waypoint ids.
58 | completedAt | Timestamp | When this journey attempt was completed.
59 | isCompleted | boolean | If this journey attempt has been completed.
60 |
61 |
62 | ### Completed Waypoint
63 |
64 | An array of completed waypoints for a journey attempt
65 |
66 | Name | Type | Description
67 | --- | --- | ---
68 | waypoint | Mongo ID | The id of the waypoint
69 | order | Integer | The order of the waypoint
70 | statements | Array of [journey progress statements](#journey-progress-statements) | A reference to the statements
71 |
72 | ### Journey Progress Statements
73 |
74 | Reference and timestamp of a statement.
75 |
76 | Name | Type | Description
77 | --- | --- | ---
78 | statement | Mongo ID | The id of a statement.
79 | timestamp | Timestamp | When this statement was added.
80 |
81 | ## Example GET data
82 |
83 | ```
84 | {
85 | "_id":"59c23b28f6463f3569446bdc",
86 | "journey":"59c23527bdf9ac67b2ab5ee6",
87 | "organisation":"59198183d8ea540933227033",
88 | "updatedAt":"2017-09-20T09:56:04.309Z",
89 | "createdAt":"2017-09-20T09:55:52.198Z",
90 | "pendingProgress":{
91 | "59c237bebdf9ac67b2ab5ee9":{
92 | "statements":[
93 | ]
94 | },
95 | "59c237bebdf9ac67b2ab5eea":{
96 | "statements":[
97 |
98 | ]
99 | }
100 | },
101 | "lockKey":"571bf02f-d41b-4b31-972d-f092845af816",
102 | "duration":12133,
103 | "startedAt":"2017-09-20T09:55:50.310Z",
104 | "completions":[
105 | {
106 | "waypoints":{
107 | "59c237bebdf9ac67b2ab5ee9":{
108 | "completedAt":"2017-09-20T09:56:02.443Z",
109 | "isCompleted":true,
110 | "statements":[
111 | {
112 | "timestamp":"2017-09-20T09:56:02.443Z",
113 | "statement":"59c23b32d138e44d720041a9"
114 | }
115 | ],
116 | "order":1,
117 | "waypoint":"59c237bebdf9ac67b2ab5ee9"
118 | },
119 | "59c237bebdf9ac67b2ab5eea":{
120 | "completedAt":"2017-09-20T09:55:53.799Z",
121 | "isCompleted":true,
122 | "statements":[
123 | {
124 | "statement":"59c23b26d138e44a720041ad",
125 | "timestamp":"2017-09-20T09:55:50.310Z"
126 | },
127 | {
128 | "timestamp":"2017-09-20T09:55:51.192Z",
129 | "statement":"59c23b27d138e44a720041ae"
130 | },
131 | {
132 | "statement":"59c23b28d138e44a720041af",
133 | "timestamp":"2017-09-20T09:55:52.313Z"
134 | },
135 | {
136 | "timestamp":"2017-09-20T09:55:52.880Z",
137 | "statement":"59c23b28d138e44a720041b0"
138 | },
139 | {
140 | "statement":"59c23b29d138e44a720041b1",
141 | "timestamp":"2017-09-20T09:55:53.799Z"
142 | }
143 | ],
144 | "order":0,
145 | "waypoint":"59c237bebdf9ac67b2ab5eea"
146 | }
147 | },
148 | "completedAt":"2017-09-20T09:56:02.443Z",
149 | "isCompleted":true
150 | }
151 | ]
152 | }
153 | ```
154 |
--------------------------------------------------------------------------------
/src/http-metadata.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Statement Metadata API HTTP Interface
5 | The [routes table](#routes) below describes the methods that the Statement Metadata HTTP interface provides. Both routes require that you specify an `_id` from the statement record, in order to determine to which statement the metadata should be added. This can be found be found by performing a [GET request on the statement model](#get-statement-_id).
6 |
7 | ```
8 | POST http://www.example.org/api/v2/statementmetadata/:id
9 | ```
10 |
11 | To access this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**.
12 |
13 | The JSON body of these methods is a key-value pair such as the following. Some examples of where we currently add metadata can be found on [question statements](../guides-assessment-statements) coming from Adapt.
14 | ```
15 | {
16 | "https://learninglocker.net/true-false-response": "Yes"
17 | }
18 | ```
19 |
20 | ## Schema
21 |
22 | Name | Description
23 | --- | --- | ---
24 | _id | The unique id of the [Statement](./http-statements#schema/)
25 | metadata | An object containing key-value pairs, where the key is an IRI representing the metadata field with requirements defined by the [xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#31-iri-requirements) and the value can be of any type.
26 |
27 | ## Routes
28 |
29 | Method | Description
30 | --- | ---
31 | [PATCH /:id](#patch-id) | Patches a model.
32 | [POST /:id](#post-id) | Creates or overwrites a model.
33 |
34 | ### PATCH /:id
35 | This route patches the metadata field on a statement that has the specified statement _id from the URL. A request to this route would look something like the request below.
36 |
37 | ```http
38 | PATCH http://www.example.org/api/v2/statementmetadata/111aaa1111a111111aa11112
39 | Authorization: Basic YOUR_BASIC_AUTH
40 | Content-Type: application/json; charset=utf-8
41 |
42 | {
43 | "example_key": "example_value"
44 | }
45 | ```
46 |
47 | A request like the one above, will respond with a 200 response like the one below containing the model as JSON in the body.
48 |
49 | ```http
50 | HTTP/1.1 200 OK
51 | Content-Type: application/json; charset=utf-8
52 |
53 | {
54 | "_id": "111aaa1111a111111aa11111",
55 | "metadata": {
56 | "example_key": "example_value"
57 | }
58 | }
59 | ```
60 |
61 | ### POST /:id
62 | This route creates or updates the metadata field on a statement that has the specified statement identifier from the URL.
63 | > `Please note:` It is preferable to use [PATCH](#patch-id) where possible to prevent overwriting metadata set by Learning Locker that is required for certain parts of the UI to work.
64 |
65 | A request to this route would look something like the request below.
66 |
67 | ```http
68 | POST http://www.example.org/api/v2/statementmetadata/111aaa1111a111111aa11112
69 | Authorization: Basic YOUR_BASIC_AUTH
70 | Content-Type: application/json; charset=utf-8
71 |
72 | {
73 | "example_key": "example_value"
74 | }
75 | ```
76 |
77 | A request like the one above, will respond with a 200 response like the one below containing the model as JSON in the body.
78 |
79 | ```http
80 | HTTP/1.1 200 OK
81 | Content-Type: application/json; charset=utf-8
82 |
83 | {
84 | "_id": "111aaa1111a111111aa11111",
85 | "metadata": {
86 | "example_key": "example_value"
87 | }
88 | }
89 | ```
90 |
91 | ## GET statement _id
92 |
93 | To get the statement _id for the route above, we can perform a GET request on statements via the [REST API](../http-rest/#get-).
94 |
95 | If you haven't already inserted your statements, they can be added via the [xAPI Statements HTTP Interface](../http-xapi-statements/), for example:
96 |
97 | ```http
98 | POST http://www.example.org/data/xAPI/statements
99 | Authorization: YOUR_BASIC_AUTH
100 | X-Experience-API-Version: 1.0.3
101 | Content-Type: application/json; charset=utf-8
102 |
103 | {
104 | "actor": { "mbox": "mailto:test1@example.org" },
105 | "verb": { "id": "http://www.example.org/verb" },
106 | "object": { "id": "http://www.example.org/activity" }
107 | }
108 | ```
109 |
110 | This route returns a 200 response with an array of statement identifiers when the statements are successfully created. A response from this route would look something like the response below.
111 |
112 | ```http
113 | HTTP/1.1 200 OK
114 | Content-Type: application/json; charset=utf-8
115 | X-Experience-API-Version: 1.0.3
116 | X-Experience-API-Consistent-Through: 2017-08-31T15:16:29.709Z
117 |
118 | ["dfb7218c-0fc9-4dfc-9524-d497097de027"]
119 | ```
120 |
121 | Then, we can use this statement identifier from the response to query the statement model ([detailed documentation can be found on the HTTP REST page](../http-rest/#get-)), in order to retrieve the `_id` which is used in the statement metadata routes. We can use [URL query parameters](https://florianholzapfel.github.io/express-restify-mongoose/#querying) to get the statement with the specified statement identifier, then return the statement identifier with the `_id`, which is returned by default.
122 |
123 | ```http
124 | GET http://www.example.org/api/v2/statement?query={"statement.id":"dfb7218c-0fc9-4dfc-9524-d497097de027"}&select={"statement.id":1}
125 | Authorization: Basic YOUR_BASIC_AUTH
126 | ```
127 |
128 | A request like the one above, will respond with a 200 response like the one below containing the statement model as JSON in the body with only the fields defined in the SELECT parameter.
129 |
130 | ```http
131 | HTTP/1.1 200 OK
132 | Content-Type: application/json; charset=utf-8
133 |
134 | [
135 | {
136 | "_id": "5f32b029f31b0ad6c241f7bb",
137 | "statement": {
138 | "id": "dfb7218c-0fc9-4dfc-9524-d497097de027",
139 | },
140 | }
141 | ]
142 | ```
143 |
144 | This `_id` is then used to add statement metadata via the [PATCH](#patch) and [POST](#post) routes.
--------------------------------------------------------------------------------
/src/guides-cli.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Command Line Interface
5 |
6 | Learning Locker comes with a range of CLI commands that can be used to execute common administrative tasks.
7 |
8 | > `Note:` In order to use the CLI commands, you must ensure that the CLI server is built. The default install script will have performed this action as part of the `yarn build-all` command, but it can manually be built using `yarn build-cli-server`
9 |
10 | > `Note:` These commands should be run from the Learning Locker working directory. By default, with the install script, this is `/usr/local/learninglocker/current/webapp`
11 |
12 | ## `createSiteAdmin`
13 |
14 | Create a site admin user
15 |
16 | ### Command:
17 | ```sh
18 | node cli/dist/server createSiteAdmin [email] [organisation] [password]
19 | ```
20 |
21 | Create a new user and organisation for the site. This user will have the Super User privilege (only assignable via this command) and will also automatically be made an admin of the new organisation.
22 |
23 | Additional organisation admins can be made within the platform but other super users need to be made via subsequent calls to this command.
24 |
25 |
26 | ### Arguments:
27 | #### `email`
28 | The email of the user - this is what they will use to login
29 |
30 | #### `organisation`
31 | The name of the new organisation. If the organisation already exists, the new user will be added to it.
32 |
33 | #### `password`
34 | The user's password
35 |
36 | ### Example
37 | ```
38 | node cli/dist/server createSiteAdmin "user@example.com" "Example" "password123"
39 | ```
40 | ___
41 |
42 | ## `migrateMongo`
43 |
44 | Runs any outstanding migrations on the Mongo database
45 |
46 | ### Command:
47 | ```sh
48 | node cli/dist/server migrateMongo
49 | ```
50 |
51 | Will check for any migrations that have not been run and apply them to the database. You can run migrations "up" or "down". The former will apply migrations, whilst the latter will roll back migrations.
52 |
53 | If you do not pass any arguments, defaults to `-u`; all pending migrations will be performed.
54 |
55 | ### Arguments:
56 | #### `-u, --up [target]`
57 | Runs all pending migrations.
58 |
59 | The optional [target] parameter can be passed to specify a certain migration to run. Pass `next` as [target] to run only the next pending migration, or the name of a certain pending migration to run only that one.
60 |
61 | #### `-d, --down [target]`
62 | Optional. Runs applied migrations down until the migration name specified as [target]. Also accepts `last` as [target] to only down the last applied migration.
63 |
64 | #### `-i, --info [level]`
65 | Display the state of migrations (which have been run, which haven't). Can optionally specify `v` or `verbose` as [level] to output more detailed information.
66 |
67 | ### Example
68 | Apply all outstanding migrations:
69 |
70 | ```
71 | node cli/dist/server migrateMongo --up
72 | ```
73 |
74 | Roll back the last applied migration:
75 |
76 | ```
77 | node cli/dist/server migrateMongo --down last
78 | ```
79 | ___
80 |
81 | ## `clearAggregationCache`
82 | Clear the cache of any aggregation data
83 |
84 | ### Command
85 | ```sh
86 | node cli/dist/server clearAggregationCache
87 | ```
88 |
89 | Will clear down any cached aggregation results. This can be useful if you require an up-to-date result for a particular visualisation or query.
90 |
91 | ### Arguments
92 |
93 | #### `-o --org ` (optional)
94 | An organisation's Mongo ObjectId.
95 |
96 | Filter to only clear down the cache for a particular organisation.
97 |
98 | ### Example
99 |
100 | Clear all caches:
101 | ```
102 | node cli/dist/server clearAggregationCache
103 | ```
104 |
105 | Clear a particular organisation's cache:
106 | ```
107 | node cli/dist/server clearAggregationCache 572cac001bb110583ed76177
108 | ```
109 |
110 | ___
111 |
112 | ## `batchJobs`
113 | Batch run the worker across existing statements
114 |
115 | ### Command
116 | ```sh
117 | node cli/dist/server batchJobs
118 | ```
119 |
120 | Will force statements back through the respective worker queue if they have not already been handled. This is useful if you have migrated statements into the LRS (e.g. if migrating from v1), or if your workers were not enabled at the time your statements were inserted into the LRS.
121 |
122 | Note this can be an intensive task and may be best done on a separate box.
123 |
124 | Currently you can batch process the Query Builder Cache generation (used to populate the items for the query builder) and also the persona generation.
125 |
126 | When a worker job is completed, the appropriate worker queue name will be populated into the `completedQueues` array on each statement document in the database. If you wish to reprocess a set of statements, then clearing the `completedQueues` will allow you to reprocess them.
127 |
128 |
129 | ### Arguments
130 |
131 | #### `-j --job [job]` (optional)
132 | Which worker job to run. `querybuildercache`* and `personas`.
133 |
134 | _* Default job_
135 |
136 | #### `-o --org [organisation_id]` (optional)
137 | An organisation's Mongo ObjectId.
138 |
139 | Filter to operate on only statements in this organisation.
140 |
141 | #### `-l, --lrs [lrs]` (optional)
142 | An LRS's (store) Mongo ObjectId.
143 |
144 | Filter to operate on only statements in this store.
145 |
146 | #### `-b, --batchSize [batchSize]` (optional)
147 | How many statements to include in each batch. For query builder cache generation, good performance is seen with 1000 per batch. For persona processing we've seen good results with this set to 100.
148 |
149 |
150 | #### `-f, --from [date]` (optional)
151 | ISO8601 formatted date to query the stored date from.
152 |
153 |
154 | #### `-t, --to [date]` (optional)
155 | ISO8601 formatted date to query the stored date to.
156 |
157 |
158 | ### Example
159 |
160 | Process persona data on all statements (100 per batch) personas in a particular store:
161 | ```
162 | node cli/dist/server batchJobs -j personas -b 100 -lrs 572cac001bb110583ed76177
163 | ```
164 |
165 | ## `updateStatementCount`
166 |
167 | Recalculates the statement counts on stores
168 |
169 | ### Command:
170 | ```sh
171 | node cli/dist/server updateStatementCount
172 | ```
173 |
174 | For each store, will count how many statements exist in the database and update the statementCount property on the store document. This may be required if statements have manually been deleted permanently from the database.
175 |
176 | _Note: as of v2.6.3 this process is done in synchronously per store to avoid large concurrency query issues on Learning Locker's with many stores._
177 |
--------------------------------------------------------------------------------
/src/overview-architecture.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Architecture Overview
5 | Learning Locker is divided into two Github repositories, one for [the Learning Locker application](#learning-locker-application) and one for [the xAPI service](#xapi-service).
6 |
7 | In Learning Locker the Browser Interface, HTTP Interface, and xAPI Service use their own HTTP ports, hence we'd recommend that you use a server such as [NGINX](https://www.nginx.com/) to route to the correct port upon receiving HTTP requests. If you utilise the [installation command provided in the installation documentation](../guides-installing), it will attempt to install and setup NGINX for you. The installation command will also use [PM2](https://github.com/Unitech/pm2) to manage your processes and restart them if they exit to ensure uptime.
8 |
9 | ## Learning Locker Application
10 | The Learning Locker application repository is made up of three parts (in the same Github repository), [the browser interface (UI)](#browser-interface-ui), [the HTTP interface (API)](#http-interface-api), and [the workers](#workers). The three parts are ran as their own process to share resources (since JavaScript is single-threaded) and ensure a degree of redundancy.
11 |
12 | ### Browser Interface (UI)
13 | The browser interface is written in JavaScript (ES6 using Webpack and Babel), utilising React to construct views and Redux to manage state. The browser interface utilises the HTTP interface to retrieve and change models in Learning Locker. Note that all models automatically save within 3 seconds after they're changed. For more information and help with the browser interface, you can go to the [Help Centre for Learning Locker](https://ht2ltd.zendesk.com/hc/en-us/categories/115000129989-Learning-Locker).
14 |
15 | ### HTTP Interface (API)
16 | The HTTP interface is also written in JavaScript (ES6 using Webpack and Babel) and uses Express, Restify, Mongo, and Mongoose. Express is used to provide HTTP routes and Restify is used on top of Express to provide [RESTful routes for each of the models in Learning Locker](../http-rest). Mongoose is used on top of Mongo to manage models in the Mongo database used by Learning Locker.
17 |
18 | ### Workers
19 | The workers are also written in JavaScript (ES6 using Webpack and Babel), they utilise Redis and optionally SQS via queue drivers. The workers make use of queues to process long running jobs. Multiple instances of the workers can be used in a cluster to process the queues in parallel across many machines and processors.
20 |
21 | Currently there are workers for managing the [query builder cache](https://ht2ltd.zendesk.com/hc/en-us/articles/115000925249-Query-Builder-Overview), [extracting personas from statements](../http-personas), [exporting statements via CSV](https://ht2ltd.zendesk.com/hc/en-us/articles/115000931369-Exporting-statements-to-CSV), [importing personas via CSV](https://ht2ltd.zendesk.com/hc/en-us/articles/115001223771-Adding-Additional-Data-to-People-via-CSV), and (in [Enterprise](https://www.ht2labs.com/learning-locker)) [recalculating journeys](https://ht2ltd.zendesk.com/hc/en-us/articles/115000857025-Journeys-Overview).
22 |
23 | ## xAPI Service
24 | The xAPI service is made up of four parts: [statements](#statements), [activity profiles](#activity-profiles), [agent profiles](#agent-profiles), and [state](#state). The xAPI service provides access to the [xAPI HTTP Interface](../xapi-http) for interacting with the xAPI as defined in the specification. The service is written in entirely TypeScript and primarily uses Express, Mongo, and Redis.
25 |
26 | If you require more information there are some useful links provided below.
27 | - [xAPI Specification of the HTTP Resources](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#20-resources).
28 | - [Documentation of the xAPI HTTP interface](../http-xapi).
29 | - [Github repository for the xAPI Service](http://github.com/LearningLocker/xapi-service).
30 |
31 | ### Statements
32 | This part provides a [xAPI compliant Statements HTTP interface](../http-xapi-statements) written in TypeScript to use Express, Mongo, and Redis. It also uses local file storage or AWS S3 storage to store attachments.
33 |
34 | The [Activity HTTP Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#25-activities-resource) is also provided as part of this because Learning Locker has to merge activity definitions from inserted statements to provide this resource. You can find out more about this resource in the [Activities HTTP interface documentation](../http-xapi-activities#get-activities).
35 |
36 | If you require more information there are some useful links provided below.
37 | - [xAPI Specification of the Statements Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#stmtres).
38 | - [Documentation of the xAPI Statements HTTP interface](../http-xapi-statements).
39 |
40 | ### Activity Profiles
41 | This part provides a [xAPI compliant Activity Profiles HTTP interface](../http-xapi-activities) written in TypeScript to use Express and Mongo. It also uses local file storage or AWS S3 storage to store documents.
42 |
43 | If you require more information there are some useful links provided below.
44 | - [xAPI Specification of the Activity Profiles Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#actprofres).
45 | - [Documentation of the xAPI Activity Profiles HTTP interface](../http-xapi-activities).
46 |
47 | ### Agent Profiles
48 | This part provides a [xAPI compliant Agent Profiles HTTP interface](../http-xapi-agents) written in TypeScript to use Express and Mongo. It also uses local file storage or AWS S3 storage to store documents.
49 |
50 | The [Agent HTTP Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#24-agents-resource) is also provided as part of this service to retrieve all of the agents that are used by a single person. A person can be created by inserting statements via the [xAPI Statements HTTP interface](../http-xapi-statements) or using the [Persona HTTP interface](../http-personas). Multiple agents can be associated with a person using the [Persona HTTP interface](../http-personas) too.
51 |
52 | If you require more information there are some useful links provided below.
53 | - [xAPI Specification of the Agent Profiles Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#26-agent-profile-resource).
54 | - [Documentation of the xAPI Agent Profiles HTTP interface](../http-xapi-agents).
55 |
56 | ### State
57 | This part provides a [xAPI compliant State HTTP interface](../http-xapi-states) written in TypeScript to use Express and Mongo. It also uses local file storage or AWS S3 storage to store documents.
58 |
59 | If you require more information there are some useful links provided below.
60 | - [xAPI Specification of the State Resource](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#23-state-resource).
61 | - [Documentation of the xAPI State HTTP interface](../http-xapi-states).
62 |
--------------------------------------------------------------------------------
/src/http-statement-deletion.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Statement Deletion HTTP Interfaces
5 |
6 | ### _Added in [v3.11.0](https://github.com/LearningLocker/learninglocker/releases)_
7 |
8 | By default, Learning Locker gives you the ability to delete statements via the API (this can be disabled via the `ENABLE_STATEMENT_DELETION` flag).
9 |
10 | Statements may be deleted individually, using the record's `_id`, or in bulk via a batch delete method.
11 |
12 | ## Authorization
13 |
14 | When using this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**.
15 |
16 | The deletion APIs require that the "Delete statements" (`'statements/delete'`) scope is enabled on the client. If a client also has a store (`lrs_id`) attached, this will be used to further filter down deletions only to this store.
17 |
18 |
19 | ## Single Statement Deletion
20 |
21 | This leverages the existing REST API for statements.
22 |
23 | ```
24 | DELETE http://www.example.org/api/v2/statement/111aaa1111a111111aa11112
25 | Authorization: Basic YOUR_BASIC_AUTH
26 | ```
27 |
28 | A request like the one above, will respond with a 204 response like the one below.
29 |
30 | ```
31 | HTTP/1.1 204 NO CONTENT
32 | ```
33 |
34 | The statement is deleted immediately and the 204 represents a receipt of the deletion event succeeding in the database.
35 |
36 | ## Batch Statement Deletion
37 |
38 | These endpoints allow you to send in deletion jobs to be processed, as well as stop a batch deletion job.
39 |
40 | As the filter passed in may apply to a large amount of data, the batch delete job is split out into batches, each deleting up to 1000 records at a time. Each successive batch will trigger another deletion job to the worker, until no more statements exist in the database matching that filter.
41 |
42 | ### Initialising a batch deletion
43 |
44 | Sending a POST with a JSON body holding the required deletion filter to the following endpoint will create a job to remove all data matching that filter from the respective organisation (or store) that the client is attached to.
45 |
46 | _e.g. Deletes all completions in the client's organisation or store_
47 |
48 | ```
49 | POST http://www.example.org/api/v2/batchdelete/initialise
50 | Authorization: Basic YOUR_BASIC_AUTH
51 | Content-Type: application/json; charset=utf-8
52 |
53 | {
54 | "filter": {
55 | "statement.verb.id": "http://adlnet.gov/expapi/verbs/completed"
56 | }
57 | }
58 | ```
59 |
60 | If the client used to make the request also has a store (`lrs_id`) attached, this will be used to further filter down deletions only to this store.
61 |
62 | An intialise request will return a 200 HTTP response with a JSON version of the batch deletion job (see [Schema](#schema))
63 |
64 | ### Terminating batch deletions
65 |
66 | You may choose to terminate one or all batch deletions for an organisation using the following commands.
67 |
68 | _Note that if a deletion is currently in progress and a worker job to delete a batch has been issued, up to 1000 records may be deleted before the termination command is respected._
69 |
70 | _This is due to how we batch the deletions into blocks of 1000. Once a batch has started, it cannot be stopped without manually stopping the Worker's Node process running the deletion job. However, no subsequent batches will be processed once that batch has finished._
71 |
72 | #### Terminating a single batch deletion
73 |
74 | Stop a specific batch deletion from executing any more batches.
75 |
76 | ```
77 | POST http://www.example.org/api/v2/batchdelete/terminate/111aaa1111a111111aa11112
78 | Authorization: Basic YOUR_BASIC_AUTH
79 | ```
80 |
81 | #### Terminating all batch deletions
82 |
83 | Stop all batch deletions from executing any more batches
84 |
85 | ```
86 | POST http://www.example.org/api/v2/batchdelete/terminate/all
87 | Authorization: Basic YOUR_BASIC_AUTH
88 | ```
89 |
90 | ### Viewing batch deletions
91 |
92 | #### Schema
93 |
94 | Name | Description
95 | --- | ---
96 | _id | The id of the batch delete job.
97 | organisation | The id of the [organisation](../http-organisations#schema) that this job belongs to.
98 | filter | A stringified JSON Mongo query - records matching this filter are deleted
99 | pageSize | Total records deleted per batch (defaults to 1000, no way to customise outside of code change and rebuild)
100 | deleteCount | How many records have been deleted so far
101 | total | Total number of statements found for deletion at initialise
102 | processing | Boolean value; is there a deletion batch currently being actioned
103 | done | Boolean value; has the job finished or been terminated
104 | createdAt | When this document was created
105 | updatedAt | When this document was last updated
106 |
107 | _The `filter` field is stored as text to account for `.` (dot) characters used in query keys - these are invalid in Mongo JSON structures.
108 |
109 |
110 | #### Example
111 |
112 | ```json
113 | {
114 | "_id": "111aaa1111a111111aa11111",
115 | "organisation": "111aaa1111a111111aa11111",
116 | "filter": "{\"statement.verb.id\": \"http://adlnet.gov/expapi/verbs/completed\"}",
117 | "pageSize": 1000,
118 | "deleteCount": 100000,
119 | "total": 100000,
120 | "processing": false,
121 | "done": true,
122 | "createdAt": "2019-01-01T00:00:00Z",
123 | "updatedAt": "2019-01-01T00:00:00Z"
124 | }
125 | ```
126 |
127 | The Batch Delete model may be retrieved using the GET [REST](../http-rest) or [Connection APIs](../http-connection) but other HTTP methods are disabled (PUT, PATCH, DELETE) and are instead replaced by the [initialise](#initialising-a-batch-deletion) and [terminate](#terminating-batch-deletions) routes specified above.
128 |
129 | #### Examples: (using the Connection API)
130 |
131 | _Note; query parameters should be URL encoded - these examples have not been, for readability_
132 |
133 | ##### Fetch a particular job by `_id`
134 |
135 | ```
136 | GET http://www.example.org/api/connection/batchdelete?filter={"_id":{"$oid":"111aaa1111a111111aa11112"}}
137 | Authorization: Basic YOUR_BASIC_AUTH
138 | ```
139 |
140 | ##### Fetch the 5 most recently completed/terminated jobs
141 | ```
142 | GET http://www.example.org/api/connection/batchdelete?filter={"done":true}&sort={"updatedAt":-1, "_id": 1}&first=5
143 | Authorization: Basic YOUR_BASIC_AUTH
144 | ```
145 |
146 | ##### Fetch the 5 most recently created and unfinished jobs
147 | ```
148 | GET http://www.example.org/api/connection/batchdelete?filter={"done":false}&sort={"createdAt":-1, "_id": 1}&first=5
149 | Authorization: Basic YOUR_BASIC_AUTH
150 | ```
151 |
152 |
153 |
154 | ### Deletion time window
155 | Because deletion can be an intensive job for the database, it is possible to configure Learning Locker to only trigger and process batch deletion during a specified window every day. This can be configured to enable deletions during periods of known low activity (e.g. night time).
156 |
157 | To configure this, update the document in the `siteSettings` collection with the required UTC hour, minute and duration:
158 |
159 | ```mongo
160 | db.siteSettings.updateOne(
161 | {
162 | "_id" : ObjectId("111111111111111111111111")
163 | },
164 | {
165 | $set: {
166 | batchDeleteWindowStartUTCHour: 00,
167 | batchDeleteWindowUTCMinutes: 00,
168 | batchDeleteWindowDurationSeconds: 18000
169 | }
170 | }
171 | )
172 | ```
173 |
174 | The configuration above, for example, would allow deletions to be triggered from midnight to 5am (UTC) everyday. Note that the Scheduler process is required to be run in order outstanding jobs at the start of the window everyday.
175 |
--------------------------------------------------------------------------------
/src/guides-custom-installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Custom installation
5 |
6 | ## Requirements
7 |
8 | In order to install Learning Locker you will require a machine that has these minimum requirements:
9 |
10 | * [Git](https://git-scm.com/)
11 | * [GCC toolchain](https://en.wikipedia.org/wiki/GNU_toolchain)
12 | * [Node](https://nodejs.org/en/) (v14)
13 | * [Yarn](https://yarnpkg.com/en/)
14 | * Connectivity to a [Mongo](https://www.mongodb.com/) instance (v4+ as of LL v7.0.0)
15 | * Connectivity to a [Redis](https://redis.io/) instance (v2.8.18+)
16 | * Web server _e.g. [Nginx](https://www.nginx.com/resources/wiki/) or [Apache](https://httpd.apache.org/)_ (optional)
17 | * Process management system _e.g. [PM2](http://pm2.keymetrics.io/docs/usage/quick-start/) or [Supervisor](http://supervisord.org/)_
18 |
19 | ### GCC and Git
20 |
21 | A good minimum set of requirements for Git and the GCC toolchain can be installed with the following commands:
22 |
23 | ### Fedora based systems
24 | ```
25 | yum update
26 | yum -y install curl git python make automake gcc gcc-c++ kernel-devel xorg-x11-server-Xvfb git-core
27 | ```
28 |
29 | ### Ubuntu/Debian based systems
30 | ```
31 | apt-get update && apt-get upgrade
32 | apt-get -y install curl git python build-essential xvfb apt-transport-https
33 | ```
34 |
35 | ### Node and Yarn installation
36 |
37 | There are multiple ways to install Node and Yarn (a package management system for Node). We would recommend the excellent NVM (Node Version Manager) which can be installed by following instructions here: [https://github.com/creationix/nvm](https://github.com/creationix/nvm)
38 |
39 | We are currently targeting builds on Node 14.*
40 |
41 | Instructions to install Yarn can be found here: [https://yarnpkg.com/en/docs/install](https://yarnpkg.com/en/docs/install) but if being used with `nvm` install via:
42 | ```
43 | npm install -g yarn
44 | ```
45 |
46 | ### Process Management
47 |
48 | [PM2](http://pm2.keymetrics.io/docs/usage/quick-start/) is an excellent tool that can be used to manage the Node processes. It also handles log management/rotation and can automatically restart failed services. Learning Locker comes pre-packaged with some [pm2 configuration scripts](https://github.com/LearningLocker/learninglocker/tree/master/pm2).
49 |
50 | To [install PM2](http://pm2.keymetrics.io/docs/usage/quick-start/#installation), run the following command:
51 |
52 | ```
53 | npm install -g pm2
54 | ```
55 |
56 | We also recommend installing the `pm2-logrotate` module, which can handle rotating and compressing your logs.
57 |
58 | ```
59 | pm2 install pm2-logrotate
60 | pm2 set pm2-logrotate:compress true
61 | ```
62 |
63 | ## User best practice
64 |
65 | It is always preferable that you do not run your Node processes as the `root` user. For this reason we would always suggest creating a new system user under which installation, builds and services can be run.
66 |
67 | ## Setup
68 |
69 | The Learning Locker application is divided into two logically separate codebases, each of which can be configured to talk to the same Mongo and Redis instances.
70 |
71 | Each codebase requires that it is downloaded, built and configured before it can be run. This guide will aim to guide you through manually installing and running a full Learning Locker stack.
72 |
73 | More distribution specific information can be found inside the [install script source code](https://github.com/LearningLocker/deploy/).
74 |
75 | ## Installing and Building the Learning Locker UI, API and Worker
76 |
77 | ### Clone and install
78 |
79 | Clone [the Learning Locker application](https://github.com/LearningLocker/learninglocker) into a new working directory on your server.
80 |
81 | ```
82 | git clone https://github.com/LearningLocker/learninglocker.git
83 | ```
84 |
85 | Enter the directory and install the requirements:
86 |
87 | ```
88 | npm_config_build_from_source=true yarn install --ignore-engines
89 | ```
90 |
91 | ### Build
92 |
93 | You are now ready to build the code. You have different option here depending on how you wish to deploy the services. The codebase has 5 distinct services that can be built:
94 |
95 | * UI Server - `yarn build-ui-server`
96 | * UI Client - `yarn build-ui-client`
97 | * API Server - `yarn build-api-server`
98 | * Worker - `yarn build-worker-server`
99 | * CLI - `yarn build-cli-server`
100 |
101 | If you wish to run the UI, API and Worker on the same machine and run CLI commands, you will probably want to build all the services in one simple command:
102 |
103 | ```
104 | yarn build-all
105 | ```
106 |
107 | ### Configuration
108 |
109 | Copy the `.env.example` into a new `.env` file and [edit as required](http://docs.learninglocker.net/guides-configuring/#learning-locker-application).
110 |
111 | ### Migrations
112 |
113 | The database requires some indexes adding and also when upgrading you will find migrations that take care of mutating your data where required.
114 |
115 | Once your instance is configured, run required migrations using the below command.
116 |
117 | ```
118 | yarn migrate
119 | ```
120 |
121 | ## Installing the xAPI service
122 |
123 | ### Clone and install
124 |
125 | Clone [the xAPI Service](https://github.com/LearningLocker/xapi-service) into a new working directory on your server.
126 |
127 | ```
128 | git clone https://github.com/LearningLocker/xapi-service.git
129 | ```
130 |
131 | Enter the directory and install the requirements:
132 |
133 | ```
134 | yarn install --ignore-engines
135 | ```
136 |
137 | ### Build
138 |
139 | ```
140 | yarn build
141 | ```
142 |
143 | ### Configuration
144 |
145 | Copy the `.env.example` into a new `.env` file and [edit as required](http://docs.learninglocker.net/guides-configuring/#xapi-service).
146 |
147 | ## Running the services via PM2
148 |
149 | If PM2 has been installed, you will be able to run the services using the preconfigured pm2 files in each codebase:
150 |
151 | ### Learning Locker UI, API, Worker
152 |
153 | To start all 3 services, navigate to the LL working directory and run:
154 | ```
155 | pm2 start pm2/all.json
156 | ```
157 |
158 | ### xAPI Service
159 |
160 | To start the xAPI service, navigate to the xAPI Service working directory and run:
161 | ```
162 | pm2 start pm2/xapi.json
163 | ```
164 |
165 | ### Config, status and logs
166 |
167 | Note you may wish to copy and modify these pm2 config files based on your setup. Documentation can be found [here](http://pm2.keymetrics.io/docs/usage/application-declaration/).
168 |
169 | To view the status of your services:
170 | ```
171 | pm2 status
172 | ```
173 |
174 | To view logs:
175 | ```
176 | pm2 logs
177 | ```
178 |
179 | To restart the services:
180 | ```
181 | pm2 restart all
182 | ```
183 |
184 |
185 | ## Running the services manually
186 |
187 | If you wish to use a different process management tool (e.g. Supervisor) or simply wish to run them manually for testing, you can start the services with these commands.
188 |
189 | #### Running the UI
190 |
191 | ```
192 | node ui/dist/server
193 | ```
194 |
195 | #### Running the API
196 |
197 | ```
198 | node api/dist/server
199 | ```
200 |
201 |
202 | #### Running the worker
203 |
204 | ```
205 | node worker/dist/server
206 | ```
207 |
208 |
209 | #### Running the xAPI service
210 |
211 | _In your xAPI service directory_
212 |
213 | ```
214 | node dist/server.js
215 | ```
216 |
217 | ## Server configuration
218 |
219 | The application is accessed through 3 web interfaces, the UI, API and xAPI. Each of these is configured to run on independent ports but it is recommended you setup a server to sit infront of all traffic and route accordingly. An example nginx config can be seen here: [nginx.conf.example](https://github.com/LearningLocker/learninglocker/blob/master/nginx.conf.example)
220 |
--------------------------------------------------------------------------------
/src/guides-installing.md:
--------------------------------------------------------------------------------
1 | ---
2 | redirect_from:
3 | - "/install/"
4 | - "/installing/"
5 | - "/installation/"
6 | - "/upgrading/"
7 | ---
8 |
9 | # Installing
10 |
11 | ## Via the install script
12 |
13 | To install Learning Locker using our deploy script, please follow the below instructions.
14 | For more information, you can view the [deployment repository's documentation](https://github.com/LearningLocker/deploy).
15 |
16 |
17 |
18 | **You must run this script as root user. Typically this can be done by running `sudo su -`**
19 |
20 | **Install with cURL**
21 | ```sh
22 | curl -o- -L https://raw.githubusercontent.com/LearningLocker/deploy/master/deployll.sh > deployll.sh && bash deployll.sh
23 | ```
24 | **Install with Wget**:
25 | ```sh
26 | wget -qO deployll.sh https://raw.githubusercontent.com/LearningLocker/deploy/master/deployll.sh && bash deployll.sh
27 | ```
28 |
29 | ### Upgrading
30 |
31 | You may choose to upgrade your Learning Locker to take advantage of new features and bug fixes. To make this process easier, it is strongly recommended that any Learning Locker running for production use has the database (Mongo) running on different servers to that of the application. This means you can seamlessly update your application without having to move your data.
32 |
33 | You can run the install script below on your existing EC2 server and that will grab and rebuild the code directly on the server. **Please note: this requires an EC2 instance with at least 2GB of RAM**. You will be asked if you wish to "upgrade" any existing instance, or perform a fresh install.
34 |
35 | If you plan on keeping the Mongo database on the same server as the application, you will need to perform a [backup and restoration](https://docs.mongodb.com/manual/tutorial/backup-and-restore-tools/) of your Mongo data between upgrades. For this reason we strongly recommend placing your database separate to your application.
36 |
37 | ### Logs
38 |
39 | By default, logs are written to `/var/log/learninglocker/`
40 |
41 | #### Install log
42 |
43 | If there is a problem installing the script, you can view the full install log output here:
44 | `/var/log/learninglocker/install.log`
45 |
46 | #### Service logs
47 |
48 | Individual logs for the different services outputs (stdout) and errors (stderr) are available in this directory under the following names:
49 |
50 | **stdout**
51 | * `xapi_stdout***.log`
52 | * `api_stdout***.log`
53 | * `ui_stdout***.log`
54 | * `worker_stdout***.log`
55 |
56 | **stderr**
57 | * `xapi_stderr***.log`
58 | * `api_stderr***.log`
59 | * `ui_stderr***.log`
60 | * `worker_stderr***.log`
61 |
62 | `***`: _Logs may have slightly different names due to rotation_
63 |
64 |
65 | ### Restarting the services
66 | To restart the services, simply run the following command:
67 | ```
68 | service pm2-learninglocker restart
69 | ```
70 | Where `learninglocker` is the system user you chose to install with in the script (defaults to `learninglocker`)
71 |
72 | ### Managing the services
73 |
74 | The PM2 service manages the 4 micro-services that Learning Locker requires. This is installed by default with the install script under the system user you chose.
75 |
76 | **In order to use the pm2 service, first ensure you are in as the correct system user:**
77 |
78 | (using the default `learninglocker` system user):
79 | ```
80 | sudo su learninglocker
81 | ```
82 |
83 | #### Service status
84 | To view the status of your processes (using the default `learninglocker` system user):
85 | ```
86 | pm2 status
87 | ```
88 |
89 | Output:
90 | ```
91 | ┌──────────┬─────────┬────────┬───┬──────┬────────────┐
92 | │ Name │ mode │ status │ ↺ │ cpu │ memory │
93 | ├──────────┼─────────┼────────┼───┼──────┼────────────┤
94 | │ API │ cluster │ online │ 0 │ 0% │ 144.0 MB │
95 | │ UIServer │ cluster │ online │ 0 │ 0% │ 105.8 MB │
96 | │ Worker │ cluster │ online │ 0 │ 0% │ 109.5 MB │
97 | │ xAPI │ cluster │ online │ 0 │ 0% │ 81.8 MB │
98 | └──────────┴─────────┴──-─────┴───┴──────┴────────────┘
99 | ```
100 |
101 | #### Service logs
102 |
103 | You can view a tail of the logs by running:
104 | ```
105 | pm2 logs
106 | ```
107 |
108 | Or view the logs for a particular service (by name or ID):
109 | ```
110 | pm2 logs xAPI
111 | ```
112 |
113 | To view more lines:
114 | ```
115 | pm2 logs --lines 1000
116 | ```
117 |
118 | ### Restarting the services manually
119 | You can restart all the services by running:
120 | ```
121 | pm2 restart all
122 | ```
123 |
124 | Or individual services by their name or ID:
125 | ```
126 | pm2 restart UIServer
127 | ```
128 |
129 |
130 | ### Application Structure
131 |
132 | There are two main repositories that are installed as part of a fresh Learning Locker installation, the [Learning Locker application](https://github.com/LearningLocker/learninglocker) and [xAPI service](https://github.com/LearningLocker/xapi-service). An in depth look at what both these packages do can be read in the [Architecture Overview](../overview-architecture).
133 |
134 | When installing your LL instance using the install script, these packages will be (by default) installed to `/usr/local/learninglocker/current` (as a symlink to a directory inside `/usr/local/learninglocker/release/...`).
135 |
136 | Inside `current/webapp/` lives the Learning Locker application which controls the User Interface, API and worker.
137 |
138 | We also install the xAPI Service here, inside the `current/xapi/` directory.
139 |
140 |
141 | ### Configuration & Environment Variables
142 |
143 | Each of these applications has their own `.env`. These hold all the configurations that the applications require in order to run, from database settings to logging configuration.
144 |
145 | By default the install script will copy the (.env.example) from [both](https://github.com/LearningLocker/learninglocker/blob/master/.env.example) repos [respectively](https://github.com/LearningLocker/xapi-service/blob/master/.env.example).
146 |
147 | It is likely you will wish to configure your application to connect to external databases, and whilst setup and configuration of these is beyond the scopes of this documentation, you will need to ensure that both `.env` files contain the same configuration values where appropriate.
148 |
149 | A full description of all configuration values in both repositories is available in the [Configuration Guide](../guides-configuring)
150 |
151 | ___
152 |
153 | ## Production Installations
154 | For production installations, we recommend the following setup**:**
155 |
156 | * 1 Load balancer (e.g. [AWS Elastic Load Balancing](https://aws.amazon.com/elasticloadbalancing/));
157 | * 2 Learning Locker servers (e.g. [AWS EC2](https://aws.amazon.com/ec2/));
158 | * 3 Mongo servers in a replica set (e.g. [Atlas](https://www.mongodb.com/cloud/atlas));
159 | * 1 Redis server (e.g. [AWS ElastiCache](https://aws.amazon.com/elasticache/)).
160 |
161 | This setup ensures good performance and a reasonable degree of redundancy in case of failures in some parts. We'd also recommend that you back up your Mongo database quite regularly depending on your own data requirements. If this sounds too costly or challenging, you may wish to consider using [our Software as a Service (SaaS) enterprise solution](https://learningpool.com/solutions/learning-record-store-learning-locker/). If you require more advice for your setup, please get in touch via [hello@learninglocker.net](mailto:hello@learninglocker.net).
162 |
163 | ___
164 |
165 | ## Custom installations
166 |
167 | Please follow instructions [here](../guides-custom-installation) if you wish to install Learning Locker manually.
168 |
--------------------------------------------------------------------------------
/src/http-persona-imports.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Persona Imports HTTP Interface
5 |
6 | Represents a CSV import (typically from a HR system) containing [persona](../http-personas) [identifiers](../http-persona-identifiers) and their [attributes](../http-persona-attributes).
7 |
8 | It is accessible through the following HTTP interfaces:
9 |
10 | - [Connection HTTP Interface](../http-connection) via http://www.example.org/api/connection/personasImport.
11 | - [REST HTTP Interface](../http-rest) via http://www.example.org/api/v2/personasImport.
12 |
13 | To import a CSV via the API, you need to make the three requests below in order.
14 |
15 | 1. Create the persona import model via the [REST HTTP Interface](../http-rest) (http://www.example.org/api/v2/personasImport).
16 | 2. Upload the CSV via the [Upload HTTP interface](#upload-http-interface).
17 | 3. Process the persona import model via the [Process HTTP interface](#process-http-interface).
18 |
19 | Alternatively, you can create/update personas via the [Persona Upsert HTTP Interface](#persona-upsert-http-interface).
20 |
21 | ### Schema
22 |
23 | Name | Description
24 | --- | ---
25 | _id | The id of this persona import model.
26 | organisation | The id of the [organisation](../http-organisations) this persona import belongs to.
27 | owner | The id of the [user](../http-users) that created the persona import.
28 | title | The title of this persona import.
29 | csvHeaders | An array of the header names from the CSV.
30 | structure | The [structure](#structure) of the CSV.
31 | importedAt | The date and time that the persona import was imported.
32 | csvHandle | The location of the CSV file in the storage provider.
33 | csvErrorHandle | The location of the CSV file with its errors in the storage provider.
34 | totalCount | Total number of data rows in the CSV.
35 | processedCount | Total number of processed data rows in the CSV.
36 | importErrors | An array of the errors in the CSV.
37 | result | The result of the import in terms of personas created and merged.
38 |
39 | ### Example
40 |
41 | ```json
42 | {
43 | "_id" : "59c1219936229d4ce9634601",
44 | "organisation" : "59c1219936229d4ce9634602",
45 | "owner": "59c1219936229d4ce9634603",
46 | "title": "Example Persona Import",
47 | "csvHeaders": ["Full Name", "Moodle User ID", "Moodle Home Page", "Age"],
48 | "structure": {
49 | "Full Name": {
50 | "columnName": "Full Name",
51 | "columnType": "COLUMN_NAME",
52 | "relatedColumn": "",
53 | "primary": null
54 | },
55 | "Moodle User ID": {
56 | "columnName": "Moodle User ID",
57 | "columnType": "COLUMN_ACCOUNT_VALUE",
58 | "relatedColumn": "Moodle Home Page",
59 | "primary": null
60 | },
61 | "Moodle Home Page": {
62 | "columnName": "Moodle Home Page",
63 | "columnType": "COLUMN_ACCOUNT_KEY",
64 | "relatedColumn": "Moodle User ID",
65 | "primary": 1
66 | },
67 | "Age": {
68 | "columnName": "Age",
69 | "columnType": "COLUMN_ATTRIBUTE_DATA",
70 | "relatedColumn": "",
71 | "primary": null
72 | }
73 | }
74 | }
75 | ```
76 |
77 | # Structure
78 |
79 | Defines the structure of the CSV and how it should be parsed. The structure property is stored as an object where the keys are the CSV column names and the values are objects that define the column structure (value object schema defined below).
80 |
81 | ### Schema
82 |
83 | Name | Description
84 | --- | ---
85 | columnName | The name of the column in the CSV (CSV header).
86 | [columnType](#column-types) | The type of the column or how it should be parsed.
87 | relatedColumn | For account names this is the account home page column, for account home pages this is the account name column.
88 | primary | Defines the order in which identifiers should be used.
89 |
90 | # Column Types
91 |
92 | Column Type | Description
93 | --- | ---
94 | COLUMN_NAME | Persona name.
95 | COLUMN_ACCOUNT_KEY | Persona identifier account home page.
96 | COLUMN_ACCOUNT_VALUE | Persona identifier account name.
97 | COLUMN_ATTRIBUTE_DATA | Persona attribute value.
98 | COLUMN_MBOX | Persona identifier mbox.
99 | COLUMN_MBOXSHA1SUM | Persona identifier mbox_sha1sum.
100 | COLUMN_OPENID | Persona identifier openid.
101 |
102 | # Upload HTTP Interface
103 | This interface uploads the CSV for the persona import model. Requests to this interface should look something like the request below.
104 |
105 | ```http
106 | POST http://www.example.org/api/uploadpersonas
107 | Authorization: YOUR_BASIC_AUTH
108 | Content-Type: multipart/form-data; boundary=YOUR_FORM_BOUNDARY
109 | Content-Length: YOUR_CONTENT_LENGTH
110 |
111 | --YOUR_FORM_BOUNDARY
112 | Content-Disposition form-data; name="id"
113 | Content-Length: YOUR_PERSONA_IMPORT_MODEL_ID_LENGTH
114 |
115 | YOUR_PERSONA_IMPORT_MODEL_ID
116 |
117 | --YOUR_FORM_BOUNDARY
118 | Content-Disposition form-data; name="file"; filename="import.csv"
119 | Content-Length: YOUR_CSV_CONTENT_LENGTH
120 | Content-Type: text/csv
121 |
122 | YOUR_CSV_CONTENT
123 |
124 | --YOUR_FORM_BOUNDARY--
125 | ```
126 |
127 | The interface will respond with a 200 response code when the CSV is successfully uploaded.
128 |
129 | # Process HTTP Interface
130 | This interface starts the processing of the persona import. Requests to this interface should look something like the request below.
131 |
132 | ```http
133 | POST http://www.example.org/api/importpersonas
134 | Authorization: YOUR_BASIC_AUTH
135 | Content-Type: application/json; charset=utf-8
136 |
137 | {
138 | "id": "YOUR_PERSONA_IMPORT_MODEL_ID"
139 | }
140 | ```
141 |
142 | The interface will respond with a 200 response code, processing of the persona import will start shortly after the response is received.
143 |
144 | # Persona Upsert HTTP Interface
145 | This interface provides API access to the function we use to process individual CSV rows in Persona Imports. Requests to this interface should look something like the request below.
146 |
147 | ```http
148 | POST http://www.example.org/api/uploadpersona
149 | Content-Type: application/json
150 | Authorization:
151 |
152 | {
153 | "personaName": "Sam Jackson",
154 | "ifis": [
155 | {
156 | "key": "account",
157 | "value": {
158 | "homePage": "https://sso.example.org",
159 | "name": "sam_jackson_sso_id"
160 | }
161 | },
162 | {
163 | "key": "mbox",
164 | "value": "mailto:sam.jackson@example.org"
165 | }
166 | ],
167 | "attributes": [
168 | {
169 | "key": "Team",
170 | "value": "Avengers"
171 | }
172 | ]
173 | }
174 | ```
175 |
176 | The interface will respond with a response similar to the one below after it has created/updated a matching persona.
177 |
178 | ```http
179 | HTTP/1.1 200 OK
180 | Content-Type: application/json; charset=utf-8
181 |
182 | {
183 | "merged": true
184 | }
185 | ```
186 |
187 | ## Retrieving Created/Updated Persona
188 | To retrieve the persona that was created/updated in the previous upsert request, you can make the following request to the [Connection HTTP Interface](/http-connection/) for [Persona Indentifiers](/http-persona-identifiers/).
189 |
190 | ```http
191 | GET http://www.example.org/api/connection/personaidentifier?filter={"ifi.key": "account", "ifi.value.homePage": "https://sso.example.org", "ifi.value.name": "sam_jackson_sso_id"}
192 | Authorization:
193 | ```
194 |
195 | This will provide a response similar to the one below.
196 |
197 | ```
198 | HTTP/1.1 200 OK
199 | Content-Type: application/json; charset=utf-8
200 |
201 | {
202 | "edges": [
203 | {
204 | "cursor": "",
205 | "node": {
206 | "_id": "",
207 | "ifi": {
208 | "key": "account",
209 | "value": {
210 | "homePage": "https://sso.example.org",
211 | "name": "sam_jackson_sso_id"
212 | }
213 | },
214 | "organisation": "",
215 | "locked": false,
216 | "persona": ""
217 | }
218 | }
219 | ],
220 | "pageInfo": {
221 | "endCursor": "",
222 | "hasNextPage": false,
223 | "hasPreviousPage": false,
224 | "startCursor": ""
225 | }
226 | }
227 | ```
228 |
--------------------------------------------------------------------------------
/src/guides-inserting.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Inserting Statements Guide
5 | To quickly try out inserting statements into your Learning Locker instance you can use Postman. Postman is a tool for creating and sending HTTP requests. You can [download and install Postman via their website](https://www.getpostman.com/). Once you've installed Postman, you can check out the [documentation for our xAPI HTTP Interface](../http-xapi-statements).
6 |
7 | Before you start designing and inserting your own statements in production, you should consider using [tools that already transmit xAPI statements](../guides-integrating/#existing-integrations). If you can't use any of these existing tools, there are a number of processes and best practices that you can follow to fall into the pit of success when designing and transmitting your own statements.
8 |
9 | - Design Statements
10 | 1. [List the experiences](#list-experiences) you wish to capture for analysis.
11 | 2. [Create a recipe](#create-recipes) (a statement template for an experience) for each experience from the previous step.
12 | 3. [Map the variables](#map-variables) in each recipe to variables in your application.
13 | - Transmit Statements
14 | 1. [Identify the sources](#identify-sources) that will construct and transmit statements.
15 | 2. [Implement the transmission](#implementing-transmission) from the sources to the LRS.
16 |
17 | ## List Experiences
18 | At this stage you should figure out which experiences are important to track (e.g. logging in, completing a quiz, watching a video, etc.) and what data you ideally need to capture for analysis (e.g. quiz score, video duration, etc.). When creating this list, you may wish to consider the data required to:
19 |
20 | - Answer your research questions.
21 | - Produce your stakeholder reports.
22 | - Adapt and improve the experiences of your users.
23 |
24 | ## Create Recipes
25 | At this stage you should create some example statements for each of the experiences you listed earlier ensuring that you include all of the data required for each experience inside the statement.
26 |
27 | Before you create a recipe, consider reusing one of the [recipes in the Tin Can Registry](https://registry.tincanapi.com/#home/profiles) as this will hopefully save you some time, improve your compatibility with existing systems, and ensure that you're using best practices. You may need to adapt these existing recipes to better meet your needs, as you may require additional data or not require some of the data they specify.
28 |
29 | If you can't find an existing recipe, don't worry, it's easy to create your own and we have some guidelines below.
30 |
31 | - Your actors should be identified by an [account](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#2424-account-object), where the `homePage` is an IRI for the application and the `name` is an identifier for the user. Other identifier types are available, but we recommend using an account.
32 | - Try to select an appropriate [verb from the Tin Can Registry](https://registry.tincanapi.com/#home/verbs) before creating your own.
33 | - Try to select an appropriate [activity type from the Tin Can Registry](https://registry.tincanapi.com/#home/activityTypes) before creating your own.
34 | - Only include one language in your languages maps. Preferably this should use the language of the user.
35 | - You should set a [timestamp](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#timestamp) with the correct time zone, otherwise the LRS will generate one that uses the time the statement was stored instead of the time an experience occurred, which is inaccurate for analysis.
36 | - Your verb and object identifiers should preferably resolve to a JSON representation of the verb or object.
37 | - The [statement context](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#context) should contain some minimal debugging information in the [extensions](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#miscext). For example, you might define the version of your statement transmission tool and platform.
38 |
39 | A statement for "viewing an application" using these guidelines might look a bit like the statement below.
40 |
41 | ```json
42 | {
43 | "actor": {
44 | "name": "Example User",
45 | "account": {
46 | "homePage": "http://www.example.org",
47 | "name": "example_user_id"
48 | }
49 | },
50 | "verb": {
51 | "id": "http://id.tincanapi.com/verb/viewed",
52 | "display": {
53 | "en": "viewed"
54 | }
55 | },
56 | "object": {
57 | "id": "http://www.example.org",
58 | "definition": {
59 | "type": "http://activitystrea.ms/schema/1.0/application",
60 | "name": {
61 | "en": "Example Application"
62 | }
63 | }
64 | },
65 | "context": {
66 | "platform": "Example Platform",
67 | "language": "en",
68 | "extensions": {
69 | "http://www.example.org/transition_tool_version": "1.0.0"
70 | },
71 | },
72 | "timestamp": "2015-01-01T01:00Z"
73 | }
74 | ```
75 |
76 | There are [more example statements provided in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#appendix-a-example-statements). For further support and consultation, you can email [hello@learninglocker.net](mailto:hello@learninglocker.net).
77 |
78 | ## Map Variables
79 | Using the recipes from the previous step, you should map the data required in each of the recipes to variables in your application. You might find it useful to use a table like the one below.
80 |
81 | Statement Property | Mapping
82 | --- | ---
83 | `actor.name` | User's full name from our user data model.
84 | `actor.account.homePage` | The URL of the application: "http://www.example.org"
85 | `actor.account.name` | User's identifier from our user data model.
86 | `object.id` | The URL of the application: "http://www.example.org"
87 | ... | ...
88 |
89 | ## Identify Sources
90 | At this stage, you should identify where the statement will be constructed and transmitted for each recipe that you identified earlier.
91 |
92 | You should consider whether you will be sending statements from the [client-side](https://en.wikipedia.org/wiki/Client-side) or the [server-side](https://en.wikipedia.org/wiki/Server-side) for each recipe as this will affect the transmission implementation. Alternatively, you may be creating statements using an external service, so at this stage you may also wish to consider whether you will need to [poll](https://en.wikipedia.org/wiki/Polling_(computer_science)) that service for events or whether you can use [webhooks](https://en.wikipedia.org/wiki/Webhook) or an [event listener](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) (with something like [Redis](https://redis.io/topics/pubsub)).
93 |
94 | ## Implementing Transmission
95 | At this stage, you can start planning and implementing the transmission of statements to your Learning Locker instance via the [xAPI HTTP Interface](../http-xapi-statements). However, there are a number of things listed below that you should consider before you begin.
96 |
97 | - [Batching Statements](#batching-statements)
98 | - [Handling Sending Failures](#handling-sending-failures)
99 | - [Sending Securely](#sending-securely)
100 |
101 | ### Batching Statements
102 | If you're potentially sending a significant number of statements in a short period of time, you should consider sending statements to the LRS in batches to improve response times, reduce HTTP requests, and reduce the elapsed time spent sending statements. For example, on the server-side this may be implemented with the use of a statement log and a cron job (the [Moodle Logstore plugin](https://github.com/xAPI-vle/moodle-logstore_xapi/pull/26) is an example of this).
103 |
104 | ### Handling Sending Failures
105 | If a statement fails to be sent you may want to consider implementing some retry strategies to resend statements that previously failed to be stored (normally because of downtime). We'd recommend that you send these failed statements in [batches](#batching-statements). If you're sending statements from the client-side, we'd recommend that you wait 5-60 seconds between retries and retry a maximum of 3-5 times. If you're sending statements server-side, you may want to consider storing failed statements somewhere and using a Cron job to send them.
106 |
107 | ### Sending Securely
108 | If you're sending statements from the server-side this shouldn't be an issue as the LRS credentials are not exposed. However, if you're sending statements from the client-side, you should consider finding a way to protect the LRS credentials so that they're not exposed to tech-savvy users, since a malicious user may try to read sensitive data from, or write unwanted data to, the LRS. We'd recommend using the [xAPI launch process](https://github.com/adlnet/xapi-launch) with Learning Locker's Launchr, you can email [hello@learninglocker.net](mailto:hello@learninglocker.net) for more information about this.
109 |
--------------------------------------------------------------------------------
/src/guides-configuring.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Configuring Learning Locker
5 |
6 | Both the UI/API/Worker and xAPI Service require a `.env` file which will contain all the configuration variables required the application to run.
7 |
8 | Where applicable, both of these files will need to have the same values (e.g. in order to connect to the same database).
9 |
10 | Both repositories contain .env.example templates which can be copied as new `.env` files. If the application has been installed using the install script then this step will be performed for you.
11 |
12 | ## Learning Locker Application
13 |
14 | **Variables in bold are required or strongly recommended**
15 |
16 | _Variables in italics are required for debugging or development only_
17 |
18 |
19 | Name | Description | Example | Default
20 | --- | --- | --- | ---
21 | **NODE_ENV** | Under what mode is Node running. This should be left as `production` in most circumstances | `production` | -
22 | **SITE_URL** | The host this application is running under, including protocol | `https://mylrs.com` | `127.0.0.1`
23 | **UI_PORT** | The port that the UI is attached to | `3000` | -
24 | **API_PORT** | The port that the API is attached to | `8080` | -
25 | _TEST_API_PORT_ | A port to expose the application on when running tests | `808080` | -
26 | **APP_SECRET** | Unique string used for hashing. Recommended length of 256 bits | `pleasechangetounique256bitstring` | -
27 | **MONGODB_PATH** | The [full Mongo connection string](https://docs.mongodb.com/manual/reference/connection-string/). This can include multiple hosts for replicas, and extra configuration values passed through query strings. | `mongodb://localhost:27017/learninglocker_v2`
28 | _MONGODB_TEST_PATH_ | A different Mongo URL to use when running tests | `mongodb://localhost:27017/llv2_tests` | -
29 | MONGO_SOCKET_TIMEOUT_MS | How long does the socket stay open when there is no activity | `300000` | `300000`
30 | MONGO_CONNECTION_POOLSIZE | https://blog.mlab.com/2013/11/deep-dive-into-connection-pooling/ | `20` | `20`
31 | **REDIS_HOST** | The host of the Redis instance | `127.0.0.1` | `127.0.0.1`
32 | **REDIS_PORT** | The port of the Redis instance | `example` | `6379`
33 | REDIS_DB | The database number of the Redis instance | `0` | -
34 | REDIS_PREFIX | A prefix to append to all keys within the Redis database | `learninglocker` | -
35 | ALLOW_AGGREGATION_DISK_USE | Can Mongo use its disks for aggregating | `true` | `true`
36 | AGGREGATE_API_ALLOWED_COLLECTIONS | Rule to check whether is collection allowed for Aggregate API or not | `^rollup` | `^rollup`
37 | AGGREGATION_CACHE_SECONDS | How many seconds are aggregation results cached | `300` | `300`
38 | AGGREGATION_REFRESH_AT_SECONDS | Refresh aggregations when this close to expiry | `120` | `120`
39 | MAX_TIME_MS | [Max time aggregations can run for in milliseconds](https://docs.mongodb.com/manual/reference/operator/meta/maxTimeMS/) | `300` | `0` (no limit)
40 | DISABLE_PERSONA_SCORING | Turn off fuzzy scoring on persona matching. This will make persona workers much faster at scale. | `false` | `false`
41 | **LOG_MIN_LEVEL** | Minimum logging level (error\|warning\|info\|debug\|silly) | `debug` | `info`
42 | LOG_DIR | Relative dir to store API access logs | `logs` | `logs`
43 | _TEST_LOG_MIN_LEVEL_ | Logging level for tests | `silly` | -
44 | COLOR_LOGS | Should logs be output using ANSI color | `true` | -
45 | **WINSTON_CLOUDWATCH_ENABLED** | Should logs be sent to AWS Cloudwatch?
[AWS credentials must be configured for Cloudwatch access](http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html) | `true` | `false`
46 | WINSTON_CLOUDWATCH_LOG_GROUP_NAME | The Cloudwatch Logs group name | `llv2` | `llv2`
47 | WINSTON_CLOUDWATCH_LOG_STREAM_NAME | The Cloudwatch Logs stream name | `application` | The server's hostname
48 | WINSTON_CLOUDWATCH_ACCESS_KEY_ID | AWS Access Key with suitable privileges | `12345678901` | -
49 | WINSTON_CLOUDWATCH_SECRET_ACCESS_KEY | AWS Secret Access Key | `12345678901` | -
50 | WINSTON_CLOUDWATCH_REGION | The region the logs will be sent to | `us-west-1` | -
51 | **SMTP_HOST** | The SMTP mailbox host | `smtp.mailserver.com` | -
52 | **SMTP_PORT** | The SMTP port | `1234` | -
53 | **SMTP_SECURED** | Use SSL for SMTP? | `true` | -
54 | **SMTP_USER** | The SMTP username | `username` | -
55 | **SMTP_PASS** | The SMTP password | `password` | -
56 | **QUEUE_PROVIDER** | Which queue provider should be used?
Options are `REDIS` or `SQS`
When using Redis, queues are held in the Redis database
When using SQS, queues are held and managed by the AWS Simple Queue Service | `SQS` | -
57 | QUEUE_NAMESPACE | A queue prefix for SQS | `example` | -
58 | AWS_SQS_ACCESS_KEY_ID | An AWS Access Key ID with privileges to read/write to SQS queue jobs | `12567890` | -
59 | AWS_SQS_SECRET_ACCESS_KEY | An AWS Secret Access Key for SQS | `example` | -
60 | AWS_SQS_DEFAULT_REGION | The AWS region for SQS | `us-west-1` | -
61 | GOOGLE_ENABLED | Enable OAuth via Google (Requires setup in the Google Developer Console) | `true` | `false`
62 | GOOGLE_CLIENT_ID | Google OAuth Client ID | `12456789` | -
63 | GOOGLE_CLIENT_SECRET | Google OAuth Client Secret | `12356789` | -
64 | **FS_REPO** | Define the storage method (`local` for local storage or `amazon` for AWS S3 storage) | `local` | -
65 | FS_SUBFOLDER | A subfolder for all uploads to live within | `storage` | `storage`
66 | FS_LOCAL_ENDPOINT | An absolute path to storage | `/custom/storage/dir` | Current working directory
67 | FS_AWS_S3_ACCESS_KEY_ID | If using the Amazon repo, an AWS Access Key with permissions to read and write to the specified S3 bucket | `12356789` | -
68 | FS_AWS_S3_SECRET_ACCESS_KEY | AWS Secret Access Key | `12356789` | -
69 | FS_AWS_S3_REGION | AWS Secret Access Key | `us-west-1` | -
70 | FS_AWS_S3_BUCKET | The S3 bucket name | `12356789` | -
71 | NEW_RELIC_LICENSE_KEY | A New Relic license key for monitoring the UI and API | `qwertyuiopsdfghjkl` | -
72 | NEWRELIC_API_NAME | Name for the API in New Relic | `12356789` | -
73 | NEWRELIC_UI_NAME | Name for the UI in New Relic | `12356789` | -
74 | CLAMSCAN_BINARY | Location of Clamscan binary if requiring anti-virus scans on uploaded files (e.g. images) | `/usr/bin/clamscan` | -
75 | **MAX_TRIP_COUNT** | Load control counter for Journey outcomes (Enterprise Only) | `15` | -
76 |
77 | ## xAPI Service
78 |
79 | Please note that some of these variables are slightly different to their Application equivalents. Future updates will bring these inline with each other, with the eventual goal of allowing for a single `.env` file.
80 |
81 | **Variables in bold are required or strongly recommended**
82 |
83 | _Variables in italics are required for debugging or development only_
84 |
85 |
86 | Name | Description | Example | Default
87 | --- | --- | --- | ---
88 | **EXPRESS_PORT** | The port that the UI is attached to | `8081` | `8081`
89 | _MODELS_REPO_ | Development setting to pick database type (`mongo` or `memory`). Memory only to be used for testing | `mongo` | `mongo`
90 | **MONGO_URL** | The [full Mongo connection string](https://docs.mongodb.com/manual/reference/connection-string/). This can include multiple hosts for replicas, and extra configuration values passed through query strings. | `mongodb://localhost:27017/learninglocker_v2`
91 | **REDIS_URL** | The full URL of the Redis instance including port, database number and authentication if required | `redis://127.0.0.1:6379/0` | `redis://127.0.0.1:6379/0`
92 | REDIS_PREFIX | A prefix to append to all keys within the Redis database | `learninglocker` | -
93 | REDIS_PREFIX | A prefix to append to all keys within the Redis database | `learninglocker` | 'LEARNINGLOCKER'
94 | **STORAGE_REPO** | Define the storage method (`local` for local storage or `s3` for AWS S3 storage) | `local` | -
95 | FS_LOCAL_STORAGE_DIR | An absolute path to storage | `/custom/storage/dir` | Current working directory
96 | FS_S3_ACCESS_KEY_ID | If using the Amazon repo, an AWS Access Key with permissions to read and write to the specified S3 bucket | `12356789` | -
97 | FS_S3_SECRET_ACCESS_KEY | AWS Secret Access Key | `12356789` | -
98 | FS_S3_REGION | AWS Secret Access Key | `us-west-1` | -
99 | WINSTON_CONSOLE_LEVEL | Minimum logging level (error\|warning\|info\|debug\|silly) | `info` | `info`
100 | **WINSTON_CLOUDWATCH_ENABLED** | Should logs be sent to AWS Cloudwatch?
[AWS credentials must be configured for Cloudwatch access](http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-identity-based-access-control-cwl.html) | `true` | `false`
101 | WINSTON_CLOUDWATCH_LOG_GROUP_NAME | The Cloudwatch Logs group name | `llv2` | `llv2`
102 | WINSTON_CLOUDWATCH_LOG_STREAM_NAME | The Cloudwatch Logs stream name | `application` | The server's hostname
103 | WINSTON_CLOUDWATCH_ACCESS_KEY_ID | AWS Access Key with suitable privileges | `12345678901` | -
104 | WINSTON_CLOUDWATCH_SECRET_ACCESS_KEY | AWS Secret Access Key | `12345678901` | -
105 | WINSTON_CLOUDWATCH_REGION | The region the logs will be sent to | `us-west-1` | -
106 |
--------------------------------------------------------------------------------
/src/guides-assessment-statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # Assessment Statements
5 |
6 | - [User completed Assessment](#user-completed-assessment)
7 | - [User answered True/False Question](#user-answered-true-false-question)
8 | - [User answered Numeric Question](#user-answered-numeric-question)
9 | - [User answered Fill-In Question](#user-answered-fill-in-question)
10 | - [User answered Choice Question](#user-answered-choice-question)
11 | - [User answered Sequencing Question](#user-answered-sequencing-question)
12 | - [User answered Likert Question](#user-answered-likert-question)
13 | - [User answered Matching Question](#user-answered-matching-question)
14 |
15 | ## User completed Assessment
16 |
17 | ### Statement
18 | ```json
19 | {
20 | "actor": {
21 | "objectType": "Agent",
22 | "mbox": "mailto:user@example.org",
23 | "name": "Example User"
24 | },
25 | "verb": {
26 | "id": "http://adlnet.gov/expapi/verbs/completed",
27 | "display": {
28 | "en-US": "completed"
29 | }
30 | },
31 | "object": {
32 | "objectType": "Activity",
33 | "id": "http://www.example.org/quiz/1",
34 | "definition": {
35 | "type": "http://adlnet.gov/expapi/activities/assessment",
36 | "name": {
37 | "en-US": "Example Assessment"
38 | }
39 | }
40 | },
41 | "result": {
42 | "completion": true,
43 | "success": true,
44 | "score": {
45 | "scaled": 1.0,
46 | "raw": 7,
47 | "min": 0,
48 | "max": 7
49 | },
50 | "duration": "PT0H5M2S"
51 | }
52 | }
53 | ```
54 |
55 | ## User answered True False Question
56 |
57 | ### Metadata
58 | ```json
59 | {
60 | "https://learninglocker&46;net/true-false-response": "Yes"
61 | }
62 | ```
63 |
64 | ### Statement
65 | ```json
66 | {
67 | "actor": {
68 | "objectType": "Agent",
69 | "name": "Example User",
70 | "mbox": "mailto:user@example.org"
71 | },
72 | "verb": {
73 | "id": "http://adlnet.gov/expapi/verbs/answered",
74 | "display": {
75 | "en": "answered"
76 | }
77 | },
78 | "object": {
79 | "objectType": "Activity",
80 | "id": "http://www.example.org/quiz/1/question/1",
81 | "definition": {
82 | "name": {"en":"Is 1 + 1 = 2?"},
83 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
84 | "interactionType": "true-false",
85 | "correctResponsesPattern": ["Yes"]
86 | }
87 | },
88 | "result": {
89 | "success": true,
90 | "response": "Yes"
91 | }
92 | }
93 | ```
94 |
95 | ## User answered Numeric Question
96 |
97 | ### Metadata
98 | ```json
99 | {
100 | "https://learninglocker&46;net/numeric-response": 2
101 | }
102 | ```
103 |
104 | ### Statement
105 | ```json
106 | {
107 | "actor": {
108 | "objectType": "Agent",
109 | "name": "Example User",
110 | "mbox": "mailto:user@example.org"
111 | },
112 | "verb": {
113 | "id": "http://adlnet.gov/expapi/verbs/answered",
114 | "display": {
115 | "en": "answered"
116 | }
117 | },
118 | "object": {
119 | "id": "http://www.example.org/quiz/1/question/2",
120 | "objectType": "Activity",
121 | "definition": {
122 | "name": {"en": "What is 1 + 1?"},
123 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
124 | "interactionType": "numeric",
125 | "correctResponsesPattern": ["2"]
126 | }
127 | },
128 | "result": {
129 | "success": true,
130 | "response": "2"
131 | }
132 | }
133 | ```
134 |
135 | ## User answered Fill-In Question
136 |
137 | ### Metadata
138 | No Metadata
139 |
140 | ### Statement
141 | ```json
142 | {
143 | "actor": {
144 | "objectType": "Agent",
145 | "name": "Example User",
146 | "mbox": "mailto:user@example.org"
147 | },
148 | "verb": {
149 | "id": "http://adlnet.gov/expapi/verbs/answered",
150 | "display": {
151 | "en": "answered"
152 | }
153 | },
154 | "object": {
155 | "id": "http://www.example.org/quiz/1/question/3",
156 | "objectType": "Activity",
157 | "definition": {
158 | "name": {"en": "What is Ben often heard saying?"},
159 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
160 | "interactionType": "fill-in",
161 | "correctResponsesPattern": ["Bob’s your uncle"]
162 | }
163 | },
164 | "result": {
165 | "success": true,
166 | "response": "Bob's your uncle"
167 | }
168 | }
169 | ```
170 |
171 | ## User answered Choice Question
172 |
173 | ### Metadata
174 | ```json
175 | {
176 | "https://learninglocker&46;net/choice-response": [
177 | "hedgehog",
178 | "shark"
179 | ]
180 | }
181 | ```
182 |
183 | ### Statement
184 | ```json
185 | {
186 | "actor": {
187 | "objectType": "Agent",
188 | "name": "Example User",
189 | "mbox": "mailto:user@example.org"
190 | },
191 | "verb": {
192 | "id": "http://adlnet.gov/expapi/verbs/answered",
193 | "display": {
194 | "en": "answered"
195 | }
196 | },
197 | "object": {
198 | "id": "http://www.example.org/quiz/1/question/4",
199 | "objectType": "Activity",
200 | "definition": {
201 | "name": {"en": "Which of these animals have a spine?"},
202 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
203 | "interactionType": "choice",
204 | "correctResponsesPattern": ["hedgehog[,]shark"],
205 | "choices": [
206 | {"id": "hedgehog", "description": {"en": "Hedgehog"}},
207 | {"id": "shark", "description": {"en": "Shark"}},
208 | {"id": "octopus", "description": {"en": "Octopus"}}
209 | ]
210 | }
211 | },
212 | "result": {
213 | "success": true,
214 | "response": "hedgehog[,]shark"
215 | }
216 | }
217 | ```
218 |
219 | ## User answered Sequencing Question
220 |
221 | ### Metadata
222 | ```json
223 | {
224 | "https://learninglocker&46;net/sequencing-response": [
225 | "12",
226 | "234",
227 | "4561"
228 | ]
229 | }
230 | ```
231 |
232 | ### Statement
233 | ```json
234 | {
235 | "actor": {
236 | "objectType": "Agent",
237 | "name": "Example User",
238 | "mbox": "mailto:user@example.org"
239 | },
240 | "verb": {
241 | "id": "http://adlnet.gov/expapi/verbs/answered",
242 | "display": {
243 | "en": "answered"
244 | }
245 | },
246 | "object": {
247 | "id": "http://www.example.org/quiz/1/question/5",
248 | "objectType": "Activity",
249 | "definition": {
250 | "name": {"en": "Order these numbers from lowest to highest?"},
251 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
252 | "interactionType": "sequencing",
253 | "correctResponsesPattern": ["12[,]234[,]4561"],
254 | "choices": [
255 | {"id": "234", "description": {"en": "Two hundred and thirty four"}},
256 | {"id": "12", "description": {"en": "Twelve"}},
257 | {"id": "4561", "description": {"en": "Four thousand, five hundred and sixty one"}}
258 | ]
259 | }
260 | },
261 | "result": {
262 | "success": true,
263 | "response": "12[,]234[,]4561"
264 | }
265 | }
266 | ```
267 |
268 | ## User answered Likert Question
269 |
270 | ### Metadata
271 | ```json
272 | {
273 | "https://learninglocker&46;net/likert-response": "likert_3"
274 | }
275 | ```
276 |
277 | ### Statement
278 | ```json
279 | {
280 | "actor": {
281 | "objectType": "Agent",
282 | "name": "Example User",
283 | "mbox": "mailto:user@example.org"
284 | },
285 | "verb": {
286 | "id": "http://adlnet.gov/expapi/verbs/answered",
287 | "display": {
288 | "en": "answered"
289 | }
290 | },
291 | "object": {
292 | "id": "http://www.example.org/quiz/1/question/6",
293 | "objectType": "Activity",
294 | "definition": {
295 | "name": {"en": "How awesome is xAPI?"},
296 | "type": "http://adlnet.gov/expapi/activities/cmi.interaction",
297 | "interactionType": "likert",
298 | "correctResponsesPattern": ["likert_3"],
299 | "scale": [
300 | {"id": "likert_0", "description": {"en": "It’s OK"}},
301 | {"id": "likert_1", "description": {"en": "It’s Pretty Cool"}},
302 | {"id": "likert_2", "description": {"en": "It’s Damn Cool"}},
303 | {"id": "likert_3", "description": {"en": "It’s Gonna Change the World"}}
304 | ]
305 | }
306 | },
307 | "result": {
308 | "success": true,
309 | "response": "likert_3"
310 | }
311 | }
312 | ```
313 |
314 | ## User answered Matching Question
315 |
316 | ### Metadata
317 | ```json
318 | {
319 | "https://learninglocker&46;net/matching-response": [
320 | ["apple", "fruit"],
321 | ["cheese", "dairy"],
322 | ["chicken", "meat"]
323 | ]
324 | }
325 | ```
326 |
327 | ### Statement
328 | ```json
329 | {
330 | "actor": {
331 | "objectType": "Agent",
332 | "name": "Example User",
333 | "mbox": "mailto:user@example.org"
334 | },
335 | "verb": {
336 | "id": "http://adlnet.gov/expapi/verbs/answered",
337 | "display": {
338 | "en": "answered"
339 | }
340 | },
341 | "object": {
342 | "id": "http://www.example.org/quiz/1/question/7",
343 | "objectType": "Activity",
344 | "definition":{
345 | "name":{"en":"Match these foods to their food group"},
346 | "type":"http://adlnet.gov/expapi/activities/cmi.interaction",
347 | "interactionType":"matching",
348 | "correctResponsesPattern":[
349 | "apple[.]fruit[,]cheese[.]dairy[,]chicken[.]meat"
350 | ],
351 | "source":[
352 | {
353 | "id":"apple",
354 | "description":{"en":"Apple"}
355 | },
356 | {
357 | "id":"cheese",
358 | "description":{"en":"Cheese"}
359 | },
360 | {
361 | "id":"chicken",
362 | "description":{"en":"Chicken"}
363 | }
364 | ],
365 | "target":[
366 | {
367 | "id":"fruit",
368 | "description":{"en":"Fruit"}
369 | },
370 | {
371 | "id":"dairy",
372 | "description":{"en":"Dairy"}
373 | },
374 | {
375 | "id":"meat",
376 | "description":{"en":"Meat"}
377 | }
378 | ]
379 | }
380 | },
381 | "result": {
382 | "success": true,
383 | "response": "apple[.]fruit[,]cheese[.]dairy[,]chicken[.]meat"
384 | }
385 | }
386 | ```
387 |
--------------------------------------------------------------------------------
/src/http-xapi-statements.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # xAPI Statements HTTP Interface
5 | The table below describes the routes that the HTTP interface provides, all of the URLs are relative to http://www.example.org/data/xAPI where http://www.example.org is the URL of your Learning Locker instance. To access this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**.
6 |
7 | If you receive an "unauthorised error" from this API, you can use the three checks below via the Client UI. To remove the need for these checks, Learning Locker will automatically create a new client when a new store is created, the new client is enabled by default, with the "All" scope, and the LRS set to the new store. To avoid unauthorised errors, try to use the new client and remember the checks below if you edit or create a client manually.
8 |
9 | 1. Check that the client is enabled.
10 | 2. Check that the "All" scope is selected under the "xAPI" heading.
11 | 3. Check that an LRS is selected.
12 |
13 | Go to the [xAPI HTTP interface documentation](../http-xapi) to see the rest of the xAPI routes.
14 |
15 | Route | Description
16 | --- | ---
17 | [PUT /statements](../http-xapi-statements#put-statements) | Stores a single statement.
18 | [POST /statements](../http-xapi-statements#post-statements) | Stores a single statement or multiple statements.
19 | [GET /statements](../http-xapi-statements#get-statements) | Retrieves statements.
20 |
21 | ## PUT /statements
22 | This route allows you to create a single statement with a statement identifier in the URL parameters and the statement itself in the JSON body. A request to this API would look something like the request below.
23 |
24 | ```http
25 | PUT http://www.example.org/data/xAPI/statements?statementId=dfb7218c-0fc9-4dfc-9524-d497097de027
26 | Authorization: YOUR_BASIC_AUTH
27 | X-Experience-API-Version: 1.0.3
28 | Content-Type: application/json
29 |
30 | {
31 | "id": "dfb7218c-0fc9-4dfc-9524-d497097de027",
32 | "actor": { "mbox": "mailto:test@example.org" },
33 | "verb": { "id": "http://www.example.org/verb" },
34 | "object": { "id": "http://www.example.org/activity" }
35 | }
36 | ```
37 |
38 | This route returns a 204 response with no content when the statement is successfully created, like the example response below.
39 |
40 | ```http
41 | HTTP/1.1 204 NO CONTENT
42 | X-Experience-API-Version: 1.0.3
43 | X-Experience-API-Consistent-Through: 2017-08-31T15:16:29.709Z
44 | ```
45 |
46 | For more information, view the [PUT /statements route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#211-put-statements).
47 |
48 | ## POST /statements
49 | This route allows you to create a multiple statements with or without statement identifiers. A request to this API would look something like the request below.
50 |
51 | ```http
52 | POST http://www.example.org/data/xAPI/statements
53 | Authorization: YOUR_BASIC_AUTH
54 | X-Experience-API-Version: 1.0.3
55 | Content-Type: application/json; charset=utf-8
56 |
57 | [{
58 | "id": "dfb7218c-0fc9-4dfc-9524-d497097de027",
59 | "actor": { "mbox": "mailto:test1@example.org" },
60 | "verb": { "id": "http://www.example.org/verb" },
61 | "object": { "id": "http://www.example.org/activity" }
62 | }, {
63 | "actor": { "mbox": "mailto:test2@example.org" },
64 | "verb": { "id": "http://www.example.org/verb" },
65 | "object": { "id": "http://www.example.org/activity" }
66 | }]
67 | ```
68 |
69 | This route returns a 200 response with an array of statement identifiers when the statements are successfully created. A response from this route would look something like the response below.
70 |
71 | ```http
72 | HTTP/1.1 200 OK
73 | Content-Type: application/json; charset=utf-8
74 | X-Experience-API-Version: 1.0.3
75 | X-Experience-API-Consistent-Through: 2017-08-31T15:16:29.709Z
76 |
77 | ["dfb7218c-0fc9-4dfc-9524-d497097de027", "dfb7218c-0fc9-4dfc-9524-d497097de028"]
78 | ```
79 |
80 | For more information, view the [POST /statements route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#212-post-statements).
81 |
82 | ## GET /statements
83 | This route allows you to retrieve a single statement or multiple statements. If the `statementId` or `voidedStatementId` URL parameters are set, it will [retrieve a single statement](#single-statement) with the given identifier, otherwise it will retrieve [many statements](#many-statements). For more information, view the [GET /statements route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#213-get-statements).
84 |
85 |
86 |
87 | ### Single Statement
88 | In addition to the `statementId` and `voidedStatementId` URL parameters (one of which must be set to retrieve a single statement), there are two additional optional parameters in the form of `format` and `attachments`. The `format` parameter defaults to "exact" if it's not set, the other options are "ids" (only includes minimal information) and "canonical" (only includes one language per language map). The `attachments` parameter is a boolean that determines if the statement's attachments are returned with the statement.
89 |
90 | ```http
91 | GET http://www.example.org/data/xAPI/statements?statementId=dfb7218c-0fc9-4dfc-9524-d497097de027&format=exact&attachments=false
92 | Authorization: YOUR_BASIC_AUTH
93 | X-Experience-API-Version: 1.0.3
94 | ```
95 |
96 | When getting a single statement successfully, this route will return a 200 response as shown below with the statement as the body of the request. If the single statement cannot be found, then this route will return a 404 response.
97 |
98 | ```http
99 | HTTP/1.1 200 OK
100 | Content-Type: application/json; charset=utf-8
101 | X-Experience-API-Version: 1.0.3
102 | X-Experience-API-Consistent-Through: 2017-08-31T15:16:29.709Z
103 |
104 | {
105 | "id": "dfb7218c-0fc9-4dfc-9524-d497097de027",
106 | "actor": { "objectType": "Agent", "mbox": "mailto:test@example.org" },
107 | "verb": { "id": "http://www.example.org/verb" },
108 | "object": { "objectType": "Activity", "id": "http://www.example.org/activity" },
109 | "version": "1.0.3",
110 | "authority": {
111 | "objectType": "Agent",
112 | "mbox": "mailto:authority@example.org"
113 | },
114 | "timestamp": "2017-09-05T12:45:31+00:00",
115 | "stored": "2017-09-05T12:45:31+00:00"
116 | }
117 | ```
118 |
119 | ### Many Statements
120 | When retrieving multiple statements there are a number of optional URL parameters listed below that can be used to filter statements. All of the URL parameters should be URL encoded (after JSON encoding if JSON encoding is required).
121 |
122 | Parameter | Description
123 | --- | ---
124 | agent | JSON encoded object containing an IFI to match an agent or group.
125 | verb | String matching the statement's verb identifier.
126 | activity | String matching the statement's object identifier.
127 | registration | String matching the statement's registration from the context.
128 | related_activities | Applies the activity filter to any activity in the statement when `true`. Defaults to `false`.
129 | related_agents | Applies the activity filter to any agent/group in the statement when `true`. Defaults to `false`.
130 | since | String that returns statements stored after the given timestamp (exclusive).
131 | until | String that returns statements stored before the given timestamp (inclusive).
132 | limit | Number of statements to return. Defaults to `0` which returns the maximum the server will allow.
133 | format | String ("exact"/"ids"/"canonical") determining how much of the statement is returned. Defaults to "exact" to return the full statement.
134 | attachments | Boolean determining if the statements' attachments should be returned. Defaults to `false`.
135 | ascending | Boolean determining if the statements should be returned in ascending stored order. Defaults to `false`.
136 |
137 | Below is an example of a request containing each of the URL parameters.
138 |
139 | ```http
140 | GET http://www.example.org/data/xAPI/statements?agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&verb=http%3A%2F%2Fwww.example.org%2Fverb&activity=http%3A%2F%2Fwww.example.org%2Factivity®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c&related_activities=false&related_agents=false&since=2017-09-04T12:45:31+00:00&until=2017-09-06T12:45:31+00:00&limit=1&format=exact&attachments=false&ascending=false
141 | Authorization: YOUR_BASIC_AUTH
142 | X-Experience-API-Version: 1.0.3
143 | ```
144 |
145 | This route will return a 200 response as shown below where the JSON body of the response contains a more link and an array of statements that match the URL parameters. The more link can be used to retrieve the next page of statements, if there aren't any more pages of statements the more link will be an empty string.
146 |
147 | ```http
148 | HTTP/1.1 200 OK
149 | Content-Type: application/json; charset=utf-8
150 | X-Experience-API-Version: 1.0.3
151 | X-Experience-API-Consistent-Through: 2017-08-31T15:16:29.709Z
152 |
153 | {
154 | "more": "/data/xAPI/statements?agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&verb=http%3A%2F%2Fwww.example.org%2Fverb&activity=http%3A%2F%2Fwww.example.org%2Factivity®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c&related_activities=false&related_agents=false&since=2017-09-04T12:45:31+00:00&until=2017-09-06T12:45:31+00:00&limit=1&format=exact&attachments=false&ascending=false&cursor=59a8289f399c5b1a19efa60e",
155 | "statements": [{
156 | "id": "dfb7218c-0fc9-4dfc-9524-d497097de027",
157 | "actor": { "objectType": "Agent", "mbox": "mailto:test@example.org" },
158 | "verb": { "id": "http://www.example.org/verb" },
159 | "object": { "objectType": "Activity", "id": "http://www.example.org/activity" },
160 | "context": { "registration": "361cd8ef-0f6a-40d2-81f2-b988865f640c" },
161 | "version": "1.0.3",
162 | "authority": {
163 | "objectType": "Agent",
164 | "mbox": "mailto:authority@example.org"
165 | },
166 | "timestamp": "2017-09-05T12:45:31+00:00",
167 | "stored": "2017-09-05T12:45:31+00:00"
168 | }]
169 | }
170 | ```
171 |
--------------------------------------------------------------------------------
/src/http-xapi-states.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 |
4 | # xAPI State HTTP Interface
5 | The table below describes the routes that the HTTP interface provides, all of the URLs are relative to http://www.example.org/data/xAPI where http://www.example.org is the URL of your Learning Locker instance. To access this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**. Go to the [xAPI HTTP interface documentation](../http-xapi) to see the rest of the xAPI routes.
6 |
7 | Route | Description
8 | --- | ---
9 | [PUT /activities/state](../http-xapi-states#put-activitiesstate) | Creates or overwrites a state document.
10 | [POST /activities/state](../http-xapi-states#post-activitiesstate) | Creates or merges a state document.
11 | [GET /activities/state](../http-xapi-states#get-activitiesstate) | Retrieves a single state document or multiple state identifiers.
12 | [DELETE /activities/state](../http-xapi-states#delete-activitiesstate) | Deletes a single state document or multiple state documents.
13 |
14 |
15 |
16 | ## PUT /activities/state
17 | This route allows you to create a single state document if it doesn't exist or overwrite an existing state document if it does exist. The route has 3 required URL parameters, an `activityId` (an IRI representing the activity), an `agent` (a JSON encoded object representing the agent the state belongs to), and a `stateId` (a string representing an identifier for the state). There is also an optional URL parameter for the `registration`. A request to this route would look something like the request below.
18 |
19 | ```http
20 | PUT http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
21 | Authorization: YOUR_BASIC_AUTH
22 | X-Experience-API-Version: 1.0.3
23 | Content-Type: application/json; charset=utf-8
24 |
25 | {
26 | "example_key": "example_value"
27 | }
28 | ```
29 |
30 | This route returns a 204 response with no content when the state document is successfully created/overwritten, like the example response below.
31 |
32 | ```http
33 | HTTP/1.1 204 NO CONTENT
34 | X-Experience-API-Version: 1.0.3
35 | ```
36 |
37 | For more information, view the [PUT /activities/state route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#single-document-put--post--get--delete).
38 |
39 | ## POST /activities/state
40 | This route allows you to create a single state document if it doesn't exist or merge an existing state document if it does exist. The route allows the same URL parameters as the [PUT /activities/state route](#put-activitiesstate). The state document is merged when the state document exists, the existing state document is a JSON encoded object, and the posted state document is a JSON encoded object. When the two JSON encoded documents are merged, only the top-level properties are merged. The example requests below demonstrate merging state documents. For more information, view the [POST /activities/state route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#single-document-put--post--get--delete).
41 |
42 | ### POST Initial State
43 | To POST an initial state, the request should be something like the request below.
44 |
45 | ```http
46 | POST http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
47 | Authorization: YOUR_BASIC_AUTH
48 | X-Experience-API-Version: 1.0.3
49 | Content-Type: application/json; charset=utf-8
50 |
51 | {
52 | "key_to_keep": "value_to_keep",
53 | "key_to_change": "value_before_change"
54 | }
55 | ```
56 |
57 | The response to the request above would be something similar to the response below.
58 |
59 | ```http
60 | HTTP/1.1 204 NO CONTENT
61 | X-Experience-API-Version: 1.0.3
62 | ```
63 |
64 | ### POST Merge State
65 | To POST a state for merging, the request should be something like the request below.
66 |
67 | ```http
68 | POST http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
69 | Authorization: YOUR_BASIC_AUTH
70 | X-Experience-API-Version: 1.0.3
71 | Content-Type: application/json; charset=utf-8
72 |
73 | {
74 | "key_to_change": "value_after_change",
75 | "key_to_add": "value_to_add"
76 | }
77 | ```
78 |
79 | The response to the request above would be something similar to the response below.
80 |
81 | ```http
82 | HTTP/1.1 204 NO CONTENT
83 | X-Experience-API-Version: 1.0.3
84 | ```
85 |
86 | ### GET Merged State
87 | To GET the merged state, the request should be something like the request below.
88 |
89 | ```http
90 | GET http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
91 | Authorization: YOUR_BASIC_AUTH
92 | X-Experience-API-Version: 1.0.3
93 | ```
94 |
95 | The response to the request above would be something similar to the response below.
96 |
97 | ```http
98 | HTTP/1.1 200 OK
99 | X-Experience-API-Version: 1.0.3
100 | Content-Type: application/json; charset=utf-8
101 |
102 | {
103 | "key_to_keep": "value_to_keep",
104 | "key_to_change": "value_after_change",
105 | "key_to_add": "value_to_add"
106 | }
107 | ```
108 |
109 | ## GET /activities/state
110 | This route allows you to retrieve a single state document or multiple state identifiers. If the `stateId` URL parameter is set, it will [retrieve a single state document](#retrieve-single-state-document) with the state identifier, otherwise it will [retrieve many state identifiers](#retrieve-many-state-identifiers). For more information, view the [GET /activities/state route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#single-document-put--post--get--delete).
111 |
112 | ### Retrieve Single State Document
113 | The route allows the same URL parameters as the [PUT /activities/state route](#put-activitiesstate). A request to this route should be similar to the request below.
114 |
115 | ```http
116 | GET http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
117 | Authorization: YOUR_BASIC_AUTH
118 | X-Experience-API-Version: 1.0.3
119 | ```
120 |
121 | The response to the request above would be something similar to the response below. If the state document cannot be found, a 404 response will be returned instead of a 200.
122 |
123 | ```http
124 | HTTP/1.1 200 OK
125 | X-Experience-API-Version: 1.0.3
126 | Content-Type: application/json; charset=utf-8
127 |
128 | {
129 | "example_key": "example_value"
130 | }
131 | ```
132 |
133 | ### Retrieve Many State Identifiers
134 | The route allows the same URL parameters as the [PUT /activities/state route](#put-activitiesstate) except for the `stateId` parameter and with the addition of the optional `since` parameter. The `since` URL parameter is an ISO timestamp that ensures only state identifiers stored since the timestamp (exclusive) are returned. A request to this route should be similar to the request below.
135 |
136 | ```http
137 | GET http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c&since=2017-09-04T12:45:31+00:00
138 | Authorization: YOUR_BASIC_AUTH
139 | X-Experience-API-Version: 1.0.3
140 | ```
141 |
142 | The response to the request above would be something similar to the response below, where the JSON encoded response body contains an array of state identifiers that match the URL parameters.
143 |
144 | ```http
145 | HTTP/1.1 200 OK
146 | X-Experience-API-Version: 1.0.3
147 | Content-Type: application/json; charset=utf-8
148 |
149 | ["example_state_id"]
150 | ```
151 |
152 | ## DELETE /activities/state
153 | This route allows you to delete a single state document or multiple state documents. If the `stateId` URL parameter is set, it will [delete a single state document](#delete-single-state-document) with the state identifier, otherwise it will [delete many state documents](#delete-many-state-documents). For more information, view the [DELETE /activities/state route in the xAPI specification](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#single-document-put--post--get--delete).
154 |
155 | ### Delete Single State Document
156 | The route allows the same URL parameters as the [PUT /activities/state route](#put-activitiesstate). A request to this route should be similar to the request below.
157 |
158 | ```http
159 | DELETE http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D&stateId=example_state_id®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
160 | Authorization: YOUR_BASIC_AUTH
161 | X-Experience-API-Version: 1.0.3
162 | ```
163 |
164 | The response to the request above would be something similar to the response below. If the state document cannot be found, a 404 response will be returned instead of a 204.
165 |
166 | ```http
167 | HTTP/1.1 204 NO CONTENT
168 | X-Experience-API-Version: 1.0.3
169 | ```
170 |
171 | ### Delete Many State Documents
172 | The route allows the same URL parameters as the [PUT /activities/state route](#put-activitiesstate) except for the `stateId` parameter. A request to this route should be similar to the request below.
173 |
174 | ```http
175 | DELETE http://www.example.org/data/xAPI/activities/state?activityId=http%3A%2F%2Fwww.example.org%2Factivity&agent=%7B%22mbox%22%3A%20%22mailto%3Atest%40example.org%22%7D®istration=361cd8ef-0f6a-40d2-81f2-b988865f640c
176 | Authorization: YOUR_BASIC_AUTH
177 | X-Experience-API-Version: 1.0.3
178 | ```
179 |
180 | The response to the request above would be something similar to the response below.
181 |
182 | ```http
183 | HTTP/1.1 204 NO CONTENT
184 | X-Experience-API-Version: 1.0.3
185 | ```
186 |
--------------------------------------------------------------------------------
/src/http-rest.md:
--------------------------------------------------------------------------------
1 | ---
2 | redirect_from:
3 | - "/http-models/"
4 | ---
5 |
6 | # REST API HTTP Interface
7 | The table below describes the routes that the HTTP interface provides. This HTTP interface is available for all models in Learning Locker.
8 |
9 | ```
10 | GET http://www.example.org/api/v2/MODEL_NAME
11 | ```
12 |
13 | For example, to get a count of stores via this API, you'd use the following route.
14 |
15 | ```
16 | GET http://www.example.org/api/v2/lrs
17 | ```
18 |
19 | To access this interface, you must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**.
20 |
21 | Method | Description
22 | --- | ---
23 | [GET /count](#get-count) | Gets a count of the models.
24 | [GET /](#get-) | Gets a subset of the models.
25 | [POST /](#post-) | Creates a model.
26 | [GET /:id](#get-id) | Gets a single model.
27 | [PUT /:id](#put-or-post-id) | Creates or overwrites a model.
28 | [POST /:id](#put-or-post-id) | Creates or overwrites a model.
29 | [PATCH /:id](#patch-id) | Patches a model.
30 | [DELETE /:id](#delete-id) | Deletes a model.
31 |
32 | ## Models
33 | The table below lists the models supported by this interface, you can view the model schemas by clicking the model names.
34 |
35 | Name | API Model Name | Description
36 | --- | --- | ---
37 | [Activity](../http-activities#schema) | `activity` | Activity with many identifiers.
38 | [Client](../http-clients#schema) | `client` | Credentials that access HTTP Interfaces.
39 | [Dashboard](../http-dashboards#schema) | `dashboard` | Customisable grid of visualisations.
40 | [Download](../http-downloads#schema) | `download` | Record of downloaded exports.
41 | [Export](../http-exports#schema) | `export` | Template for exporting statements.
42 | [Journey](../http-journeys#schema) | `journey` | Journeys visualisation.
43 | [Journey Progress](../http-journey-progress#schema) | `journeyprogress` | Journey progress.
44 | [Organisation](../http-organisations#schema) | `organisation` | Container of clients and stores that a subset of users can access.
45 | [Persona](../http-personas#schema) | `persona` | Person with many [identifiers](../http-persona-identifiers) and [attributes](../http-persona-attributes) across systems.
46 | [Persona Identifier](../http-persona-identifiers#schema) | `personaidentifier` | Unique xAPI identifier for a persona.
47 | [Persona Attribute](../http-persona-attributes#schema) | `personaattribute` | Attribute of a persona.
48 | [Query](../http-queries#schema) | `query` | Saved filter for statements.
49 | [Role](../http-roles#schema) | `role` | Group of permissions for accessing organisation data via users.
50 | [Store](../http-stores#schema) | `store` | Container for xAPI data (statements, documents, and attachments).
51 | [User](../http-users#schema) | `user` | Login details for accessing the UI.
52 | [Visualisation](../http-visualisations#schema) | `visualisation` | Graphical view of statements.
53 |
54 | ## Routes
55 | ### GET /count
56 | This route returns a count of the models. A request to this route would look something like the request below.
57 |
58 | ```http
59 | GET http://www.example.org/api/v2/lrs/count
60 | Authorization: Basic YOUR_BASIC_AUTH
61 | ```
62 |
63 | A request like the one above, will respond with a 200 response like the one below containing a count of the models in the body.
64 |
65 | ```http
66 | HTTP/1.1 200 OK
67 | Content-Type: application/json; charset=utf-8
68 |
69 | {
70 | "count": 3
71 | }
72 | ```
73 |
74 | For more information about the acceptable URL query parameters, view the [Restify documentation](https://florianholzapfel.github.io/express-restify-mongoose/#querying).
75 |
76 | **Important:** In case of **User** route, [query](https://florianholzapfel.github.io/express-restify-mongoose/#querying) parameter will be ignored and `search` parameter should be used instead (only available in Enterprise). `search` query parameter is a simple string and will be transformed into filter shown below.
77 |
78 | ```json
79 | {
80 | "$or": [
81 | { "name": { "$regex": "exampleSearchString", "$options": "i" } },
82 | { "email": { "$regex": "exampleSearchString", "$options": "i" } }
83 | ]
84 | }
85 | ```
86 |
87 | ### GET /
88 | This route returns an array of models. A request to this route would look something like the request below.
89 |
90 | ```http
91 | GET http://www.example.org/api/v2/lrs
92 | Authorization: Basic YOUR_BASIC_AUTH
93 | ```
94 |
95 | A request like the one above, will respond with a 200 response like the one below containing the models as JSON in the body. Different models will respond with a different schema, you can view the schemas by clicking the model names in the [model table above](#models).
96 |
97 | ```http
98 | HTTP/1.1 200 OK
99 | Content-Type: application/json; charset=utf-8
100 |
101 | [
102 | {
103 | "createdAt": "2017-08-08T14:35:18.400Z",
104 | "organisation": "111aaa1111a111111aa11111",
105 | "statementCount": 987,
106 | "title": "Example Store",
107 | "__v": 0,
108 | "updatedAt": "2017-08-08T14:35:33.721Z",
109 | "_id": "111aaa1111a111111aa11112"
110 | }
111 | ]
112 | ```
113 |
114 | For more information about the acceptable URL query parameters, view the [Restify documentation](https://florianholzapfel.github.io/express-restify-mongoose/#querying).
115 |
116 | **Important:** In case of **User** route, [query](https://florianholzapfel.github.io/express-restify-mongoose/#querying) parameter will be ignored and `search` parameter should be used instead. `search` query parameter is a simple string and will be transformed into filter shown below.
117 |
118 | ```json
119 | {
120 | "$or": [
121 | { "name": { "$regex": "exampleSearchString", "$options": "i" } },
122 | { "email": { "$regex": "exampleSearchString", "$options": "i" } }
123 | ]
124 | }
125 | ```
126 |
127 | ### POST /
128 | This route creates a model. A request to this route would look something like the request below. Different models will require and respond with a different schema, you can view the schemas by clicking the model names in the [model table above](#models).
129 |
130 | ```http
131 | POST http://www.example.org/api/v2/lrs
132 | Authorization: Basic YOUR_BASIC_AUTH
133 | Content-Type: application/json; charset=utf-8
134 |
135 | {
136 | "title": "Example Store"
137 | }
138 | ```
139 |
140 | A request like the one above, will respond with a 201 response like the one below containing the created model in the JSON body.
141 |
142 | ```http
143 | HTTP/1.1 201 CREATED
144 | Content-Type: application/json; charset=utf-8
145 |
146 | {
147 | "createdAt": "2017-08-08T14:35:18.400Z",
148 | "organisation": "111aaa1111a111111aa11111",
149 | "statementCount": 0,
150 | "title": "Example Store",
151 | "updatedAt": "2017-08-08T14:35:33.721Z",
152 | "_id": "111aaa1111a111111aa11112",
153 | "__v": 0
154 | }
155 | ```
156 |
157 | ### GET /:id
158 | This route returns a single model that has the specified identifier from the URL. A request to this route would look something like the request below.
159 |
160 | ```http
161 | GET http://www.example.org/api/v2/lrs/111aaa1111a111111aa11112
162 | Authorization: Basic YOUR_BASIC_AUTH
163 | ```
164 |
165 | A request like the one above, will respond with a 200 response like the one below containing the model as JSON in the body. Different models will respond with a different schema, you can view the schemas by clicking the model names in the [model table above](#models). Note that the request will return a 404 response if the model doesn't exist.
166 |
167 | ```http
168 | HTTP/1.1 200 OK
169 | Content-Type: application/json; charset=utf-8
170 |
171 | {
172 | "createdAt": "2017-08-08T14:35:18.400Z",
173 | "organisation": "111aaa1111a111111aa11111",
174 | "statementCount": 987,
175 | "title": "Example Store",
176 | "__v": 0,
177 | "updatedAt": "2017-08-08T14:35:33.721Z",
178 | "_id": "111aaa1111a111111aa11112"
179 | }
180 | ```
181 |
182 | For more information about the acceptable URL query parameters, view the [Restify documentation](https://florianholzapfel.github.io/express-restify-mongoose/#querying).
183 |
184 | ### PUT or POST /:id
185 | This route creates or updates a single model that has the specified identifier from the URL. A request to this route would look something like the request below. Different models will require and respond with a different schema, you can view the schemas by clicking the model names in the [model table above](#models).
186 |
187 | ```http
188 | PUT http://www.example.org/api/v2/lrs/111aaa1111a111111aa11112
189 | Authorization: Basic YOUR_BASIC_AUTH
190 | Content-Type: application/json; charset=utf-8
191 |
192 | {
193 | "createdAt": "2017-08-08T14:35:18.400Z",
194 | "organisation": "111aaa1111a111111aa11111",
195 | "statementCount": 987,
196 | "title": "Updated Title",
197 | "updatedAt": "2017-08-08T14:35:33.721Z",
198 | "_id": "111aaa1111a111111aa11112"
199 | }
200 | ```
201 |
202 | A request like the one above, will respond with a 200 response like the one below containing the model as JSON in the body.
203 |
204 | ```http
205 | HTTP/1.1 200 OK
206 | Content-Type: application/json; charset=utf-8
207 |
208 | {
209 | "createdAt": "2017-08-08T14:35:18.400Z",
210 | "organisation": "111aaa1111a111111aa11111",
211 | "statementCount": 987,
212 | "title": "Updated Title",
213 | "__v": 0,
214 | "updatedAt": "2017-08-08T14:35:33.721Z",
215 | "_id": "111aaa1111a111111aa11112"
216 | }
217 | ```
218 |
219 | ### PATCH /:id
220 | This route patches a single model that has the specified identifier from the URL. A request to this route would look something like the request below. Different models will require and respond with a different schema, you can view the schemas by clicking the model names in the [model table above](#models).
221 |
222 | ```http
223 | PATCH http://www.example.org/api/v2/lrs/111aaa1111a111111aa11112
224 | Authorization: Basic YOUR_BASIC_AUTH
225 | Content-Type: application/json; charset=utf-8
226 |
227 | {
228 | "title": "Patched Title"
229 | }
230 | ```
231 |
232 | A request like the one above, will respond with a 200 response like the one below containing the model as JSON in the body.
233 |
234 | ```http
235 | HTTP/1.1 200 OK
236 | Content-Type: application/json; charset=utf-8
237 |
238 | {
239 | "createdAt": "2017-08-08T14:35:18.400Z",
240 | "organisation": "111aaa1111a111111aa11111",
241 | "statementCount": 987,
242 | "title": "Patched Title",
243 | "__v": 0,
244 | "updatedAt": "2017-08-08T14:35:33.721Z",
245 | "_id": "111aaa1111a111111aa11112"
246 | }
247 | ```
248 |
249 | ### DELETE /:id
250 | This route deletes a single model that has the specified identifier from the URL. A request to this route would look something like the request below.
251 |
252 | ```http
253 | DELETE http://www.example.org/api/v2/lrs/111aaa1111a111111aa11112
254 | Authorization: Basic YOUR_BASIC_AUTH
255 | ```
256 |
257 | A request like the one above, will respond with a 204 response like the one below.
258 |
259 | ```http
260 | HTTP/1.1 204 NO CONTENT
261 | ```
262 |
--------------------------------------------------------------------------------
/src/http-connection.md:
--------------------------------------------------------------------------------
1 | ---
2 | redirect_from:
3 | - "/postman/"
4 | ---
5 |
6 | # Connection HTTP Interface
7 |
8 | The Learning Locker Connection API is a HTTP interface that [utilises cursors to provide paginated models](#pagination-example). The API is inspired by [GraphQL's connections](https://facebook.github.io/relay/graphql/connections.htm). The API is available for all models in Learning Locker, for example, to receive paginated statements via this API, you'd use the following URL.
9 |
10 | ```
11 | http://www.example.org/api/connection/statement
12 | ```
13 |
14 | You must additionally supply your Basic Auth details with each request in the `Authorization` header. Your Basic Auth details can be found under **Settings** > **Clients**. The API also accepts the following *optional* URL parameters for filtering the models returned.
15 |
16 | - [sort](#sort-parameter) (required - we recommend sorting by `_id` if nothing else)
17 | - [search](#search-parameter) (available only for user connection in Enterprise)
18 | - [filter](#filter-parameter) (not available for User connection)
19 | - [project](#project-parameter)
20 | - [hint](#hint-parameter)
21 | - [first](#first-parameter)
22 | - [after](#after-parameter)
23 |
24 | ## URL Parameters
25 | All of the URL parameters should be URL encoded (after JSON encoding if JSON encoding is required). For example, if you were using the [example sort parameter](#sort-parameter), your request would look something like the request below.
26 |
27 | ```http
28 | GET http://www.example.org/api/connection/statement?sort=%7b%22timestamp%22%3a-1%2c%22statement.id%22%3a1%7d
29 | Authorization: Basic YOUR_BASIC_AUTH
30 | ```
31 |
32 | ### Sort Parameter
33 | The sort parameter is a JSON encoded object. The keys of the object represent the names of the properties you wish to sort. The values of the object represent the order in which you want to sort the properties. To sort in ascending order, use the number 1; to sort in descending order, use the number -1.
34 |
35 | For example, to sort statements in descending order of their timestamp and ascending order of their Mongo ObjectId, you can use the following sort parameter.
36 |
37 | ```json
38 | {
39 | "timestamp": -1,
40 | "_id": 1
41 | }
42 | ```
43 |
44 | In the above example, we've included the `_id` because it should be unique and the sort parameter should always contain a unique property in order for pagination to work correctly with cursors. The order of the keys in the object determines which property is sorted first, so always include a unique property at the end such as the `_id` property.
45 |
46 | #### Sorting With Extension Keys
47 | Note that when using extension keys, you need to replace any dots with `&46;` because Mongo does not allow dots in keys. For example, when you have an extension key like `http://www.example.com/extension` you can sort by it using `http://www&46;example&46;com/extension` instead, so a sort parameter using this extension key might look something like the sort parameter below.
48 |
49 | ```json
50 | {
51 | "statement.context.extensions.http://www&46;example&46;com/extension": 1
52 | }
53 | ```
54 |
55 | #### Sorting With Improved Performance
56 | You may find that changing the sort parameter can vary the time it takes a query to run, especially when you have a large number of models. You can take advantage of database indexes to improve performance, more information is available about [using indexes via Mongo's documentation](https://docs.mongodb.com/manual/indexes/). If utilising indexes doesn't have the required performance improvement, you can instead utilise [BI tools](../guides-retrieving).
57 |
58 | ### Search Parameter
59 | The search parameter is a simple string and **available only for User connection**. This parameter is gonna search for the match over `name` and `email` fields of User schema. So if the value for this parameter is `exampleSearchString`, as shown in the request below, it will be transformed into filter as shown below.
60 |
61 | ```http
62 | GET http://www.example.org/api/connection/user?search=exampleSearchString
63 | Authorization: Basic YOUR_BASIC_AUTH
64 | ```
65 |
66 | ```json
67 | {
68 | "$or": [
69 | { "name": { "$regex": "exampleSearchString", "$options": "i" } },
70 | { "email": { "$regex": "exampleSearchString", "$options": "i" } }
71 | ]
72 | }
73 | ```
74 |
75 | ### Filter Parameter
76 | The filter parameter is a JSON encoded object. The keys of the object represent the names of the properties or operators. The values of the object represent the value you wish to filter by.
77 |
78 | For example, to filter statements by actor or verb, you can use the following filter parameter.
79 |
80 | ```json
81 | {
82 | "$or": [{
83 | "statement.actor.account.name": "123",
84 | "statement.actor.account.homePage": "http://www.example.org/user"
85 | }, {
86 | "statement.verb.id": "http://www.example.org/verb"
87 | }]
88 | }
89 | ```
90 |
91 | In the example above, [`$or`](https://docs.mongodb.com/manual/reference/operator/query/or/#op._S_or) is a operator, all operators start with a dollar (`$`). You can find a [list of the available operators in the Mongo documentation](https://docs.mongodb.com/manual/reference/operator/query/). The most common operators are the comparison operators ([`$eq`](https://docs.mongodb.com/manual/reference/operator/query/eq/#op._S_eq), [`$gt`](https://docs.mongodb.com/manual/reference/operator/query/gt/#op._S_gt), [`$gte`](https://docs.mongodb.com/manual/reference/operator/query/gte/#op._S_gte), [`$in`](https://docs.mongodb.com/manual/reference/operator/query/in/#op._S_in), [`$lt`](https://docs.mongodb.com/manual/reference/operator/query/lt/#op._S_lt), [`$lte`](https://docs.mongodb.com/manual/reference/operator/query/lte/#op._S_lte), [`$ne`](https://docs.mongodb.com/manual/reference/operator/query/ne/#op._S_ne), and [`$nin`](https://docs.mongodb.com/manual/reference/operator/query/nin/#op._S_nin)) and the logical operators ([`$and`](https://docs.mongodb.com/manual/reference/operator/query/and/#op._S_and), [`$not`](https://docs.mongodb.com/manual/reference/operator/query/not/#op._S_not), [`$nor`](https://docs.mongodb.com/manual/reference/operator/query/nor/#op._S_nor), and [`$or`](https://docs.mongodb.com/manual/reference/operator/query/or/#op._S_or)).
92 |
93 | #### Filtering With Extension Keys
94 | Note that when using extension keys, you need to replace any dots with `&46;` because Mongo does not allow dots in keys. For example, when you have an extension key like `http://www.example.com/extension` you can filter it using `http://www&46;example&46;com/extension` instead, so a filter parameter using this extension key might look something like the filter parameter below.
95 |
96 | ```json
97 | {
98 | "statement.context.extensions.http://www&46;example&46;com/extension": {
99 | "$ne": "example_value"
100 | }
101 | }
102 | ```
103 |
104 | #### Filtering With Improved Performance
105 | You may find that changing the filter parameter can vary the time it takes a query to run, especially when you have a large number of models. You can take advantage of database indexes to improve performance, more information is available about [using indexes via Mongo's documentation](https://docs.mongodb.com/manual/indexes/). If utilising indexes doesn't have the required performance improvement, you can instead utilise [BI tools](../guides-retrieving).
106 |
107 | ### Project Parameter
108 | The project parameter is a JSON encoded object. The keys of the object usually represent the names you want to give to the projected properties. The values of the object usually determine whether the property is included/excluded or the name of the property to project from the model.
109 |
110 | For example, to project the actor's account name as a user's identifier, the verb without a display, and the object's identifier you can use the following project parameter.
111 |
112 | ```json
113 | {
114 | "userId": "$statement.actor.account.name",
115 | "statement.verb": {
116 | "display": 0
117 | },
118 | "statement.object.id": 1
119 | }
120 | ```
121 |
122 | In the example above, the value `0` is used to exclude the verb's display property. Similarly, the value `1` is used to include the object's identifier. You can find out more about [projections via the Mongo documentation](https://docs.mongodb.com/manual/reference/operator/aggregation/project/).
123 |
124 | #### Projecting With Extension Keys
125 | Note that when using extension keys, you need to replace any dots with `&46;` because Mongo does not allow dots in keys. For example, when you have an extension key like `http://www.example.com/extension` you can project it using `http://www&46;example&46;com/extension` instead, so a project parameter using this extension key might look something like the project parameter below.
126 |
127 | ```json
128 | {
129 | "statement.context.extensions.http://www&46;example&46;com/extension": 1
130 | }
131 | ```
132 |
133 | ### Hint Parameter
134 | The hint parameter is a JSON encoded object that represents a Mongo index and is similar to the [sort parameter](#sort-parameter). A hint overrides Mongo's default index selection and query optimisation process.
135 |
136 | For example, to use an index you've created in Mongo for verb identifiers in ascending order, you can use the following hint parameter.
137 |
138 | ```json
139 | {
140 | "statement.verb.id": 1
141 | }
142 | ```
143 |
144 | For more information about hints, you can checkout [Mongo's hint documentation](https://docs.mongodb.com/manual/reference/method/cursor.hint/index.html).
145 |
146 | ### First Parameter
147 | The first parameter is a number that represents the number of models to be returned after the [after cursor parameter](#after-parameter) or from the very first model in Mongo.
148 |
149 | ### After Parameter
150 | The after parameter is a string that represents a cursor used for getting models after a specified point in the Mongo collection.
151 |
152 | ## Pagination Example
153 | To demonstrate pagination with this API, you can insert two statements using a request like the one below.
154 |
155 | ```http
156 | POST http://www.example.org/data/xAPI/statements
157 | Authorization: Basic YOUR_BASIC_AUTH
158 | X-Experience-API-Version: 1.0.3
159 | Content-Type: application/json
160 |
161 | [{
162 | "actor": { "mbox": "mailto:test1@example.org" },
163 | "verb": { "id": "http://www.example.org/verb" },
164 | "object": { "id": "http://www.example.org/activity" },
165 | }, {
166 | "actor": { "mbox": "mailto:test2@example.org" },
167 | "verb": { "id": "http://www.example.org/verb" },
168 | "object": { "id": "http://www.example.org/activity" },
169 | }]
170 | ```
171 |
172 | The request above should return you a statement identifier for each of the statements in an array.
173 |
174 | ### Retrieving Page One
175 | Once you've inserted two statements, you can make a request for the first statement, using a request like the one below.
176 |
177 | ```http
178 | GET http://www.example.org/api/connection/statement?first=1
179 | Authorization: Basic YOUR_BASIC_AUTH
180 | ```
181 |
182 | The request above should return you a connection, consisting of edges (which contain the models) and page info (which contains the cursors). For example, the above request would return something like the response below.
183 |
184 | ```json
185 | {
186 | "edges": {
187 | "cursor": "Zmlyc3RTdGF0ZW1lbnQ=",
188 | "node": {
189 | "_id": "59ad59a40334c1bd23322c3a",
190 | "statement": {
191 | "id": "0f748889-8d6c-4423-9919-189a14484d2f",
192 | "actor": { "objectType": "Agent", "mbox": "mailto:test1@example.org" },
193 | "verb": { "id": "http://www.example.org/verb" },
194 | "object": { "objectType": "Activity", "id": "http://www.example.org/activity" },
195 | "version": "1.0.3",
196 | "authority": {
197 | "objectType": "Agent",
198 | "mbox": "mailto:authority@example.org"
199 | }
200 | }
201 | }
202 | },
203 | "pageInfo": {
204 | "endCursor": "Zmlyc3RTdGF0ZW1lbnQ=",
205 | "hasNextPage": true,
206 | "hasPreviousPage": true,
207 | "startCursor": "Zmlyc3RTdGF0ZW1lbnQ="
208 | }
209 | }
210 | ```
211 |
212 | ### Retrieving Subsequent Pages
213 | Once you've received a page, you can use the cursors in the `pageInfo` from the previous response to retrieve the next page. For example, we can use a request like the one below to retrieve the second of the inserted statements from earlier in this example.
214 |
215 | ```http
216 | GET http://www.example.org/api/connection/statement?first=1&after=Zmlyc3RTdGF0ZW1lbnQ=
217 | Authorization: Basic YOUR_BASIC_AUTH
218 | ```
219 |
220 | The request above should return you another connection, again this will consist of edges (which contain the models) and page info (which contains the cursors). For example, the request for page two would return something like the response below.
221 |
222 | ```json
223 | {
224 | "edges": {
225 | "cursor": "c2Vjb25kU3RhdGVtZW50",
226 | "node": {
227 | "_id": "59ad5b9480fe0205abbd0aec",
228 | "statement": {
229 | "id": "3c44a187-ad17-41c1-bc73-fed40fdbb200",
230 | "actor": { "objectType": "Agent", "mbox": "mailto:test2@example.org" },
231 | "verb": { "id": "http://www.example.org/verb" },
232 | "object": { "objectType": "Activity", "id": "http://www.example.org/activity" },
233 | "version": "1.0.3",
234 | "authority": {
235 | "objectType": "Agent",
236 | "mbox": "mailto:authority@example.org"
237 | }
238 | }
239 | }
240 | },
241 | "pageInfo": {
242 | "endCursor": "c2Vjb25kU3RhdGVtZW50",
243 | "hasNextPage": true,
244 | "hasPreviousPage": true,
245 | "startCursor": "c2Vjb25kU3RhdGVtZW50"
246 | }
247 | }
248 | ```
249 |
--------------------------------------------------------------------------------