Repository navigation
Conversation
HachemJ
added a commit
to HachemJ/Loris
that referenced
this pull request
Oct 13, 2026
This was referenced Oct 22, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brief summary of changes
This PR introduces the database structure for the candidate identifier rework discussed in #9036.
It adds three tables:
candidate_identifier_types— stores identifier type configuration.candidate_identifier_types_project_rel— associates identifier types with projects.candidate_identifiers— stores identifier values assigned to candidates.Design decisions and database schema
Candidate identifiers are split into two parts: the identifier types that define how an identifier behaves, and the identifier values assigned to candidates.
Identifier types
candidate_identifier_typesstores the configuration for each identifier type.CandidateIdentifierTypeIDNameFormatIsRequiredIsUniqueAutoGenerateAllowMultiplepermIDFor example, PSCID could be configured as required, unique, automatically generated, and limited to one value per candidate. Other identifier types could be optional, manually entered, or allow multiple values.
Projects and permissions
candidate_identifier_types_project_relassociates identifier types with the projects they apply to. An identifier type can be used by multiple projects.Visibility uses the existing LORIS permission system rather than introducing a separate
Visiblesetting.The optional permission is stored directly on the identifier type through
permID. An identifier type can have at most one permission, while the same permission can be used by multiple identifier types.The intended behavior is that users must have the associated permission to view an identifier type. If no permission is configured, anyone with access to the candidate should be able to view it. Permission enforcement will be implemented during application integration.
Candidate identifiers
candidate_identifiersstores the actual identifier values assigned to candidates.CandidateIdentifierIDCandidateIDCandidateIdentifierTypeIDValueCandidateIdentifierIDis a separate primary key because a candidate may have multiple values of the same type whenAllowMultipleis enabled.AllowMultipleandIsUniquewill be enforced by the application when identifier values are added or updated. Both depend on the configuration of the identifier type, so they cannot be represented by a single constraint that applies to every row.For example, making
(CandidateID, CandidateIdentifierTypeID)unique would enforceAllowMultiple = false, but would also prevent multiple values for types whereAllowMultipleis enabled.The current limitation is that the database does not enforce these rules on its own. Direct inserts into
candidate_identifierscould bypass them, so application code needs to check the identifier type configuration.Deletion behavior
Identifier values are deleted when their candidate is deleted since they belong to that candidate.
I decided not to cascade when an identifier type is deleted. Although its values would no longer make sense without the type, cascading here could make a configuration mistake destructive. Accidentally deleting the PSCID type, for example, could also delete every stored PSCID.
Instead, the foreign key prevents an identifier type from being deleted while identifier values still reference it. Removing a type therefore requires explicitly handling its existing values first.
Project relationships cascade when their related configuration is deleted since those rows only store associations. If a permission is deleted,
permIDis set toNULLrather than deleting the identifier type.It also includes a migration script that reads the existing PSCID and ExternalID configuration from
config.xmland migrates it intocandidate_identifier_types.Scope
This is the first PR in the candidate identifier rework, covering the database structure and configuration migration.
The application implementation, including identifier generation and validation, is handled in #11212, while the REST API changes are handled in #11253.
Existing identifier values and
config.xmlremain unchanged.Identifier format
Identifier formats are now stored as template strings in the
Formatcolumn, replacing the legacy configuration that definesstructureandgenerationseparately.Templates describe the identifier structure and, when
AutoGenerateis enabled, the generation method.The implementation that interprets these templates is handled in #11212.
Identifier template syntax
Templates combine literal text with placeholders enclosed in curly braces.
For example:
Supported placeholders include:
{SITE:ALIAS}{PROJECT:ALIAS}{SEQUENCE:N}N.{RANDOM:N}N.{NUMERIC:N}Ndigits.{ALPHA:N}Nletters.{ALPHANUMERIC:N}Nalphanumeric characters.SEQUENCEandRANDOMsupport the following options:FORMAT—numeric,alpha, oralphanumeric(defaults tonumeric).PADDING— character used for left-padding (defaults to0orA, depending on the alphabet).MIN— minimum generator value.MAX— maximum generator value.A generated identifier uses one
SEQUENCEorRANDOMplaceholder, optionally preceded by literal text, site aliases, or project aliases.When
AutoGenerateis disabled, identifiers are entered manually and validated against the template. TheNUMERIC,ALPHA, andALPHANUMERICplaceholders are validation-only.Testing instructions (if applicable)
Apply the SQL patch and verify that the three new tables are created.
Run the migration script:
Check that PSCID and ExternalID are added to
candidate_identifier_typeswith the correct formats and settings.Verify that
config.xmland the existing PSCID and ExternalID values in the candidate table remain unchanged.