Looking up grades by IPTC URI¶
A third-party module that reads a C2PA manifest off an uploaded file ends up
with an IPTC digital source type URI, such as
http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia.
AiDisclosureGrade::getIptcDigitalSourceType() reads a URI FROM a grade, not
the other way around, so ai_disclosure.grade_lookup is the service for the
opposite direction: given a URI, which grades carry it.
A URI does not identify a single grade¶
Several grades can share the same URI, at different severities. This is the shipped mapping:
| IPTC digital source type URI | Grades that carry it |
|---|---|
.../trainedAlgorithmicMedia |
ai_generated_autonomous (severity 70), ai_deepfake (severity 80) |
.../compositeWithTrainedAlgorithmicMedia |
ai_summarized (severity 40), ai_partly_assisted (severity 50), ai_assisted_hitl (severity 60) |
.../algorithmicallyEnhanced |
ai_translated (severity 30) |
Every URI above is prefixed with http://cv.iptc.org/newscodes/digitalsourcetype/.
The remaining shipped grades, ai_metadata, ai_rated and human_only, carry
no IPTC URI at all.
Two formats, one method¶
use Drupal\ai_disclosure\GradeLookup;
use Drupal\ai_disclosure\GradeLookupFormat;
/** @var \Drupal\ai_disclosure\GradeLookup $lookup */
$lookup = \Drupal::service('ai_disclosure.grade_lookup');
// Entities, the default: the AiDisclosureGrade objects themselves.
$grades = $lookup->gradesForIptcUri('http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia');
foreach ($grades as $id => $grade) {
// $id is the grade's machine name; $grade is the AiDisclosureGrade entity.
}
// Data: flat associative arrays, safe to serialize.
$data = $lookup->gradesForIptcUri(
'http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia',
GradeLookupFormat::Data,
);
foreach ($data as $id => $grade) {
// $grade is ['id' => ..., 'severity' => ..., 'label' => ..., 'label_required' => ...].
}
gradesForIptcUri() takes the URI exactly as given: no trimming, no
case folding, no other normalization. The URI is an identifier, not free
text.
Every case, both formats¶
| Input | Entities |
Data |
|---|---|---|
URI on several grades, e.g. .../trainedAlgorithmicMedia |
2 entities, keyed by ID, ordered by severity | 2 arrays, same keys, same order |
URI on a single grade, e.g. .../algorithmicallyEnhanced |
1 entity, still inside an array | 1 array, still inside an array |
URI no shipped grade carries, e.g. .../digitalCapture |
Empty array | Empty array |
| Empty string | Empty array, nothing is loaded | Empty array, nothing is loaded |
| URI with different spacing or case | No match: the comparison is exact | No match: the comparison is exact |
A single match is still an array, not a shortcut value: a site can add a second grade on the same URI at any time.
The Data format's exact shape¶
Each value in a Data result is:
[
'id' => string, // the grade's machine name, redundant with the outer key
'severity' => int,
'label' => string,
'label_required' => bool,
]
Outer keys are grade machine names, a stable API in both formats. Order in both formats is by severity ascending, then by grade ID: stable across calls, not a ranking. Picking which matching grade applies to a given file is the caller's job, with context the service does not have.
An unknown URI, an empty string, or a malformed URI all return an empty array, with no log entry and no exception.
Entities do not survive json_encode() or a queue¶
AiDisclosureGrade has no JsonSerializable implementation and its
properties are protected. json_encode() on an Entities result gives
empty objects, not grade data. Observed on a two-grade match:
{"ai_generated_autonomous":{},"ai_deepfake":{}}
A queue has the same problem twice over: serialization mangles the
entity, and even a working entity captured on a queue item is stale by
the time the worker runs, since the grade list can change in between. Put
grade IDs, or the Data format, on the queue item, and have the worker
re-read from storage rather than trust an entity that traveled through
the queue.
Cacheability¶
$cache_tags = $lookup->getCacheTags();
getCacheTags() returns the grade list's own cache tags. The lookup itself
is read-only and keeps no state across requests, but its answer depends on
the grade configuration: a site that adds, edits or removes a grade changes
which grades a URI maps to, without a module release. Attach these tags to
whatever the caller builds from the lookup's result, so a cached page picks
up that change.