<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def constructPrimaryIdentifier(self, data, ordered_identifier_candidates):
 """ Construct and return a primary identifier value from the data asserted by the IdP using the ordered list of candidates from the configuration. """

        logprefix = PrimaryIdentifier.logprefix
        context = self.context

        attributes = data.attributes
        satosa_logging(logger, logging.DEBUG, "{} Input attributes {}".format(logprefix, attributes), context.state)

        value = None

        for candidate in ordered_identifier_candidates:
            satosa_logging(logger, logging.DEBUG, "{} Considering candidate {}".format(logprefix, candidate), context.state)

            # Get the values asserted by the IdP for the configured list of attribute names for this candidate
            # and substitute None if the IdP did not assert any value for a configured attribute.
            values = [ attributes.get(attribute_name, [None])[0] for attribute_name in candidate['attribute_names'] ]
            satosa_logging(logger, logging.DEBUG, "{} Found candidate values {}".format(logprefix, values), context.state)

            # If one of the configured attribute names is name_id then if there is also a configured
            # name_id_format add the value for the NameID of that format if it was asserted by the IdP
            # or else add the value None.
            if 'name_id' in candidate['attribute_names']:
                candidate_nameid_value = None
                candidate_nameid_value = None
                candidate_name_id_format = candidate.get('name_id_format')
                name_id_value = data.subject_id
                name_id_format = data.subject_type
                if (
                    name_id_value
                    and candidate_name_id_format
                    and candidate_name_id_format == name_id_format
                ):
                    satosa_logging(logger, logging.DEBUG, "{} IdP asserted NameID {}".format(logprefix, name_id_value), context.state)
                    candidate_nameid_value = name_id_value

                # Only add the NameID value asserted by the IdP if it is not already
                # in the list of values. This is necessary because some non-compliant IdPs
                # have been known, for example, to assert the value of eduPersonPrincipalName
                # in the value for SAML2 persistent NameID as well as asserting
                # eduPersonPrincipalName.
                if candidate_nameid_value not in values:
                    satosa_logging(logger, logging.DEBUG, "{} Added NameID {} to candidate values".format(logprefix, candidate_nameid_value), context.state)
                    values.append(candidate_nameid_value)
                else:
                    satosa_logging(logger, logging.WARN, "{} NameID {} value also asserted as attribute value".format(logprefix, candidate_nameid_value), context.state)

            # If no value was asserted by the IdP for one of the configured list of attribute names
            # for this candidate then go onto the next candidate.
            if None in values:
                satosa_logging(logger, logging.DEBUG, "{} Candidate is missing value so skipping".format(logprefix), context.state)
                continue

            # All values for the configured list of attribute names are present
            # so we can create a primary identifer. Add a scope if configured
            # to do so.
            if 'add_scope' in candidate:
                if candidate['add_scope'] == 'issuer_entityid':
                    scope = data.auth_info.issuer
                else:
                    scope = candidate['add_scope']
                satosa_logging(logger, logging.DEBUG, "{} Added scope {} to values".format(logprefix, scope), context.state)
                values.append(scope)

            # Concatenate all values to create the primary identifier.
            value = ''.join(values)
            break

        return value