.. _vc-credential-composition:

Credential Composition
======================

Each verifiable credential is assembled from an ``IssuanceLine`` record and
its related ``UserCredential``. The composition process maps Open edX data
into the structure required by the chosen :ref:`data model <vc-data-models>`.

Common fields
-------------

All data models share these root-level fields:

.. list-table::
   :header-rows: 1
   :widths: 25 75

   * - Field
     - Source
   * - ``id``
     - ``urn:uuid:{IssuanceLine.uuid}``
   * - ``issuer.id``
     - Active ``IssuanceConfiguration`` DID
   * - ``issuer.name``
     - Active ``IssuanceConfiguration`` name
   * - ``issuanceDate`` / ``issued`` / ``validFrom``
     - ``IssuanceLine.modified`` timestamp
   * - ``validUntil``
     - ``IssuanceLine.expiration_date`` (optional)
   * - ``credentialStatus``
     - Status List 2021 entry (see :ref:`vc-status-list-api`)
   * - ``credentialSubject.id``
     - Subject DID provided by the wallet during issuance

Subject DID
-----------

The ``credentialSubject.id`` (the learner's decentralized identifier) is not
generated by Open edX. It is provided by the wallet application during the
issuance request. When the wallet POSTs to the issuance endpoint, the
storage backend's request serializer extracts and validates the subject DID,
then stores it on the ``IssuanceLine``.

Achievement text generation
---------------------------

The ``IssuanceLine`` model builds human-readable text from the underlying
Open edX credential. Three text fields are generated: **name**,
**description**, and **narrative**. Each uses template variables resolved
from the database at issuance time.

Template variables
~~~~~~~~~~~~~~~~~~

.. list-table::
   :header-rows: 1
   :widths: 22 48 30

   * - Variable
     - Source
     - Fallback
   * - ``credential_type``
     - Mapped from credential class: ``"program certificate"`` or
       ``"course certificate"``
     - --
   * - ``program_title``
     - ``Program.title``
     - ``""``
   * - ``course_title``
     - ``Course.title`` (looked up via ``course_id``)
     - ``""``
   * - ``organizations``
     - ``Program.authoring_organizations`` names, comma-joined
     - ``""``
   * - ``organization``
     - ``course_key.org`` from the course credential
     - ``""``
   * - ``platform_name``
     - ``Site.siteconfiguration.platform_name``
     - ``""``
   * - ``recipient_name``
     - ``User.full_name`` (by ``UserCredential.username``)
     - ``"recipient"``
   * - ``course_count``
     - ``Program.course_runs.count()``
     - ``0``
   * - ``hours_of_effort``
     - ``Program.total_hours_of_effort``
     - omitted if not set

Course credential templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Name:** the course's configured title; if absent: ``"Course certificate"``.

**Description:**

.. code-block:: text

    {credential_type} is granted on course {course_title} completion
    offered by {organization}, in collaboration with {platform_name}

**Narrative** (placed into the achievement ``criteria`` field):

.. code-block:: text

    {recipient_name} successfully completed a course and received
    a passing grade for a Course Certificate in {course_title}
    a course offered by {organization}, in collaboration with
    {platform_name}.

Program credential templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Name:** the program's configured title; if absent, generated as:

.. code-block:: text

    Program certificate for passing a program {program_title}

**Description:**

.. code-block:: text

    {credential_type} is granted on program {program_title} completion
    offered by {organizations}, in collaboration with {platform_name}.
    The {program_title} program includes {course_count} course(s).

If ``total_hours_of_effort`` is set, the description appends:

.. code-block:: text

    , with total {hours_of_effort} Hours of effort required to complete it

**Narrative** (placed into the achievement ``criteria`` field):

.. code-block:: text

    {recipient_name} successfully completed all courses and received
    passing grades for a Professional Certificate in {program_title}
    a program offered by {organizations}, in collaboration with
    {platform_name}.

Data model differences
----------------------

.. list-table::
   :header-rows: 1
   :widths: 25 38 37

   * - Aspect
     - Open Badges v3.0
     - VC Data Model v1.1
   * - Extra context
     - ``purl.imsglobal.org/.../ob/v3p0``
     - ``schema.org``
   * - Credential type
     - ``OpenBadgeCredential``
     - ``EducationalOccupationalCredential``
   * - Subject type
     - ``AchievementSubject``
     - (schema.org structures)
   * - Achievement
     - Nested ``achievement`` object with ``criteria.narrative``
     - ``hasCredential`` relationship
   * - Top-level ``name``
     - Yes (credential name)
     - No

Open Badges v3.0.1 extends v3.0 with minor context additions but uses the
same structure.

Status index assignment
-----------------------

Each ``IssuanceLine`` receives a monotonically increasing ``status_index``
scoped to its issuer. The index starts at 0 and increments with each new
issuance. This index determines the credential's position in the issuer's
Status List bitstring. When a credential is revoked, the bit at that index
is flipped to 1 and the Status List is regenerated (gzip-compressed, then
base64url-encoded).
