npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lti-1p3-ags

v2.0.1

Published

Node.js implementation for the LTI 1.3 Assignment Grading Services specification. Handles lineitem CRUD operations, and grade-passback.

Downloads

117

Readme

LTI 1.3 Assignment Grading Services (AGS)

A library implementing the 1EdTechLTI 1.3 Assignment Grading Services (AGS) Specification

MIT License

Table of Contents

Installation

pnpm install lti-1p3-ags

Supported LTI Version

LTI 1.3

General Overview

Assignment and Grade Services Overview Assignment and Grade Services Overview -- taken from the official 1EdTech AGS Specification

Supported Methods

// Core methods:
init();
  // - have the ability to pass a callback if need be.
generateLTIAdvantageServicesAccessToken();

// lineitem CRUD methods
postScore();
createLineitem();
fetchAllLineitems();
fetchLineitem();

Other CRUD operations currently in progress.

Usage

The LTI 1.3 Core Specification won't be covered here, however, it would be good to familiarize yourself with the specification before utilizing this package. It would give you a core understanding behind the design choices and implementation differences from LTI 1.1.

The same is true for the LTI Advantage Assignment Grading Services (AGS) specification. I highly advise and recommend becoming familiar with the new specificiation. An official migration guide is provided by 1EdTech, which could be found here. It briefly discusses the migration from the Basic Outcome Service to the new Assignment Grading Services 2.0 (which is a part of the core LTI Advantage Services).

An important thing to note -- this library doesn't help you manage anything in relation to creating your public/private RSA keys. You will have to handle that on your own. More information could be found in the official 1EdTech Security Framework.

/**
 * Testing for the AGS npm package module.
 */
import AGS from 'lti-1p3-ags';
import fs from 'fs';

try {

  /**
   * First and foremost, you have to create an AGS instance!
   * 
   * This library exposes two options for instantiating the AGS service:
   * 1. Statically
   * 2. Creating an AGS object instance.
   * 
   * In the end, both accomplish the same thing and this mainly comes down to how your application
   * is structured and how you want to use this library.
   */
  // Option 1:
  const ags = new AGS(
    issuer,
    clientId,
    deploymentId,
    oAuth2AccessEndpoint,
    keyId,
    fs.readFileSync('path/to/private-key.pem || string', 'utf-8'),
    debug,
  );
  
  // Option 2:
  const ags = AGS.new(
    issuer,
    clientId,
    deploymentId,
    oAuth2AccessEndpoint,
    keyId,
    fs.readFileSync('path/to/private-key.pem || string', 'utf-8'),
    debug,
  ).getAGSInstance();


  /**
   * After instantiating an AGS instance, you have 1 of 2 options to obtain an Access Token:
   * 1. invoke the `init()` method.
   * 2. explicitly invoke the `generateLTIAdvantageServicesAccessToken()` method.
   * 
   * Once again, determining which method to use mainly comes down to how your application
   * is structured and how you want to use this library but in the end, these two options accomplish
   * the same thing -- obtaining an Access Token.
   * 
   * This might be overkill, but one of my goals with this library was to implement it in such a way that
   * it could be expanded upon, potentially by other individuals, and leaving the choice up to the
   * individual to determine which method is best suited for the needs and application.
   */
  // Option 1:
  /**
   * You have the ability to pass an optional callback function, which will be invoked after
   * the Access Token has been fetched.
   */
  await ags.init();
  // OR
  await ags.init( (data) => {
    const {
      accessToken,
      createDate,
      tokenType,
    } = data;
    // Finished initializing!

    // Do something here if you need to!
  });

  // Option 2:
  const {
    accessToken,
    tokenType, // will always be `Bearer` in this instance.
    createdDate,
  } = await ags.generateLTIAdvantageServicesAccessToken();

  // From here, you can get or set the Access Token value and creation date if you would like:
  // Get:
  console.log(ags.accessToken);
  console.log(ags.accessTokenCreatedDate);
  // Set:
  ags.accessToken = 'some-access-token-you-already-generated';
  ags.accessTokenCreatedDate = 'access-tokens-creation-date';
  
  // Lastly, you can perform CRUD operations on lineitem(s):

  // Get all lineitems in context:
  const lineitems = await ags.fetchAllLineitems({
    lineitemsContainerUrl: 'lineitems-container-url-endpoint',
  });
  console.log(lineitems);
  /**
   * [
      {
        id: 'https://lorem ipsum',
        scoreMaximum: 2,
        label: 'user profile - enable captions',
        resourceLinkId: 'resource-link-id'
      },
      {
        id: 'https://lorem ipsum',
        scoreMaximum: 40,
        label: 'Taylor Swift - All Of The Girls You Loved Before (Audio)',
        resourceLinkId: 'resource-link-id'
      },
      {
        id: 'https://lorem ipsum',
        scoreMaximum: 40,
        label: 'Moving | Official Trailer | Hulu',
        resourceLinkId: 'resource-link-id'
      }
    ]
  */

  // If you pass `params`, it will fetch the lineitems based on off the params passed:
  // You can also use the `fetchLineitem()` method to return a specific lineitem.
  // @see: https://www.imsglobal.org/spec/lti-ags/v2p0#container-request-filters
   const params = {
    resource_link_id: 'resource-link-id',
  };
  const lineitem = await ags.fetchAllLineitems({
    lineitemsUrl: 'lineitem-url-endpoint',
    params,
  });

  // OR

  // You can invoke the `fetchLineitem()` method to fetch a specific line item,
  // IF you have the `resourceId` value.
  const lineitem = await ags.fetchLineitem({
    lineitemsUrl,
  });
  console.log(lineitem);
  /**
   *
    {
      id: 'url-id',
      startDate: '',
      endDateTime: '',
      scoreMaximum: 10,
      label: '10 Second Timer -- new worth 10 points.',
      tag: 'grade',
      resourceId: '<whatever-value-was-given>',
      resourceLinkId: 'resource-link-id-value'
    }
  */

 // Posting scores:
 // Compile this data your own way, but has to conform to the object structure below.
 // More info could be found in the `ScorePayload.d.ts` type.
 const scorePayload: ScorePayload = {
  timestamp: string, // MUST be formatted using ISO 8601 with a sub-second precision
  scoreGiven: number,
  scoreMaximum: number,
  comment?: string,
  activityProgress: 'Initialized' | 'Started' | 'InProgress' |  'Submitted' | 'Completed',
  gradingProgress: 'FullyGraded' | 'Pending' | 'PendingManual' | 'Failed' | 'NotReady',
  userId: string,
  scoringUserId?: string
};
const { status: gradePostResponseStatus }  = await ags.postScore({
  lineitemUrl,
  scorePayload,
});

if (
  gradePostResponseStatus === 200
  || gradePostResponseStatus === 201
) {
  console.log('Succeeded in posting back scores!');
  if (updatedScoresUrlEndpoint) {
    console.log('Here is the updated scores url endpoint: ', updatedScoresUrlEndpoint);
  }
}

} catch (error) {
  console.log(error);  
}

StudentAttempt.d.ts interface

License

Distributed under the MIT License. See LICENSE for more information.

Contact

Tyrus Malmstrom - @TirustheVirus - [email protected]