Student ID to Identification Code Translation

Overview

There are multiple student identifiers in the education data ecosystem. Several cases emerged in the Ed-Fi Community where student IDs in API transactions (i.e., the studentUniqueId field) are not known to the client application. As a result, a related transaction failed. This article describes features in the Ed-Fi ODS / API to address this issue. With this feature, API clients can interact with the Ed-Fi ODS / API using their chosen student identifier in the studentUniqueId property everywhere it appears in the requests and responses.

This article describes the current solution and provides an overview of changes from previous v2.x versions. 

Technical Approach

In previous versions of the Ed-Fi ODS / API, client systems identified students by supplying the StudentUniqueId used by the host in their API requests. Since the ODS uses an integer-based surrogate ID internally (StudentUSI), the API translates the StudentUniqueId to and from the corresponding internal-facing StudentUSI in the entity layer. This mapping is handled by the PersonUniqueIdToUsiCache class through the GetUniqueId and GetUsi methods.

The current version of the Admin database has a Student Identification System Descriptor that is used by the host API (and written by the SIS vendor in a Student's identification codes collection) that corresponds to the value used by each API client. This selection (if made) is communicated to the API server alongside the other claims information during the initial bearer token validation, and captured in the API key context.

The PersonUniqueIdToUsiCache has been expanded to support mappings between StudentUSI and the identification codes, using the API key context to determine which transformation is appropriate for the StudentUniqueId value.


Admin UI and Database Changes from Previous Versions

The Admin UI and Admin database have been augmented to allow an API administrator to specify which Student Identification System Descriptor value is used by each API client when identifying students. This setting is optional. When no value is specified, the API will simply use the StudentUniqueId stored in the ODS. The StudentIdentificationSystemDescriptor setting is captured and stored in the dbo.ApiClients table of the EdFi_Admin database:

The dbo.AccessTokenIsValid stored procedure has been updated to include the chosen identification system. This value flows to the API during token authorization and is stored in a new StudentIdentificationSystemDescriptor property on the ApiKeyContext class. The value is made accessible elsewhere through the IApiKeyContextProvider interface.

PersonUniqueIdToUsiCache Changes from Previous Versions

In previous versions of the ODS / API, the StudentUniqueId transformation to StudentUSI was performed using the PersonUniqueIdToUsiCache in the generated entities.

Outbound Request Changes

The following are the accessors for the StudentUniqueId property that map the persisted StudentUSI to StudentUniqueId for outbound (GET) requests: 

private int _studentUSI;
private string _studentUniqueId;

public virtual string StudentUniqueId
{
    get
    {
        if (_studentUniqueId == null)
            _studentUniqueId = PersonUniqueIdToUsiCache.GetCache().GetUniqueId("Student", _studentUSI);
            
        return _studentUniqueId;
    }
    set
    {
        _studentUniqueId = value;
    }
}

Inbound Request Changes

The following are the accessors for the StudentUSI property that map the incoming StudentUniqueId to StudentUSI for inbound (PUT and POST) requests:

private int _studentUSI;
private string _studentUniqueId;

public virtual int StudentUSI 
{
    get
    {
        if (_studentUSI == default(int))
            _studentUSI = PersonUniqueIdToUsiCache.GetCache().GetUsi("Student", _studentUniqueId);

        return _studentUSI;
    } 
    set
    {
        _studentUSI = value;
    }
}

API clients now use their chosen student identification system code values in place of the StudentUniqueIds references sent in requests to the API. The PersonUniqueIdToUsiCache was enhanced to support mappings between the persisted StudentUSI and the student identification codes. This functionality is similar to the previous implementation for mapping between StudentUniqueIds and StudentUSIs.

Inbound Request Change Details

For incoming PUT or POST requests from an API client:

  • The GetUsi method of the PersonUniqueIdToUsiCache is now used to try and find a distinct match using the key built based on the API client's Student Identification System Descriptor and the identification code value supplied in the StudentUniqueId:

    private struct StudentIdentificationCodeToUsiKey
    {
        public string StudentIdentificationSystemDescriptorUri;
        public string IdentificationCodeValue;
    }
  • The value is an array of StudentUSIs (int) because uniqueness is not enforced on Student Identification Codes and so there could be a single student identification code value associated with multiple students.

Lookup scenarios would be handled as follows:

  • If there is a match with a single StudentUSI, the resolved value is used as the StudentUSI for persistence.
  • If there is no match found in the cache, a query will be performed to try and load it. If still not found, a 0 will be returned. (This behavior mirrors the existing behavior of cache misses on StudentUniqueId.)
  • If there is a match with multiple StudentUSIs, the API client will receive an HTTP 400 response status with an error message of "Ambiguous student match on the supplied StudentUniqueId."

For outgoing GET requests for an API client: 

  • The GetUniqueId method is used to try and find a match, again using a key built based on the API client's Student Identification System Descriptor and the StudentUSI found in the persistent entity.

    private struct StudentUSIToIdentificationCodeKey
    {
        public int StudentUSI;
        public string StudentIdentificationSystemDescriptorUri;
    }
  •  Since StudentIdentificationCode table has the primary key composed by StudentIdentificationSystemDescriptor,  StudentUSI and AssigningOrganizationIdentificationCode, the value is an array of structs, as follows:

    private struct StudentUSIToIdentificationCodeValue
    {
       public string AssigningOrganizationIdentificationCode;
       public string IdentificationCode;
    }

The lookup scenarios are handled as follows:

  • If there is a unique match on the identification code value available for the StudentUSI, the value will be used as the StudentUniqueId in the response.

  • If there is no identification code, a database query will be performed to attempt to load it and augment the cache entry, if found. If there is still no match, API client will receive results where studentUniqueId is masked with "??????".

  • If there are multiple matching identification codes (e.g., from different assigning organizations), the API client will receive results where studentUniqueId is masked with "??????". This scenario is typically an edge case where SIS products are primarily responsible for managing IDs and are instructed by platform hosts to enforce uniqueness throughout the system context.

Student Entity Maintenance Note

Support for transparent translation of the StudentUniqueId values in references for affected API clients creates a hypothetical technical challenge for supporting clients with updates to the Student resource (i.e., where the raw StudentUniqueId value is stored). The challenge arises because POST or PUT requests cannot be used to create a new Student when the ODS / API has been configured with this translation feature. Existing Student records can be updated with PUT when not updating StudentUniqueId property itself. 

But, as a practical matter, the API clients who are given an "operational context" for matching students based on a different student identification codes would not also be the ones updating the Student resource (and specifically the StudentUniqueId property), so this is considered to be a rare or theoretical problem.