Profile claiming#
This page lists the functions, exceptions, adapters and models that take an unclaimed profile over for a person: the claiming services, the merge service, the duplicate finder, ClaimingError, ClaimMethod and ClaimingAuditLog. It does not explain why claiming works as it does, which is in How a profile is claimed, and it does not list settings, which are in Settings. To change the flow, see How to extend the profile-claiming flow.
Module |
Holds |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Claiming services#
Both services mark the person claimed and active, save only is_claimed and is_active, and write the outcome to the audit log, failures included.
Function |
Returns |
What it does |
|---|---|---|
|
|
Activates an unclaimed person through an ORCID sign-in and connects the social login to them. Raises |
|
|
Activates an unclaimed person after their email address is confirmed. Does nothing and returns |
The adapter and the signal handler call these. A portal that keeps the shipped adapter and handler does not call them itself.
Adapters and handlers#
SocialAccountAdapter is the value of SOCIALACCOUNT_ADAPTER, and AccountAdapter is the value of ACCOUNT_ADAPTER.
Member |
What it does |
|---|---|
|
Returns the contributor that holds the ORCID iD in a |
|
For an ORCID sign-in, finds the profile that holds the iD. When that profile is unclaimed, it calls |
|
For an ORCID sign-up, adopts the unclaimed profile that holds the iD. When no profile holds it, creates the user and attaches the iD |
|
Fills the user from the ORCID record with |
|
True when the |
|
False when the |
|
Connected to allauth’s |
Merge service#
merge_persons(person_keep, person_discard) merges person_discard into person_keep in one transaction and returns person_keep. Any error rolls the whole merge back. It raises ClaimingError when both arguments are the same person.
What moves from the discarded person to the kept person:
Data |
Rule |
|---|---|
Contributions |
A record both people are credited on keeps the kept person’s credit, with its organization as it is, at the higher of the two levels. The discarded person’s credit there is deleted. A credit only the discarded person held moves, with its level |
|
Moved unless the value exists elsewhere or the kept person already holds an identifier of that type. Those are deleted |
|
Moved unless the kept person already has an affiliation with that organization. Those are deleted |
allauth |
Moved unless the kept person already has the address (ignoring case). Those are deleted |
allauth |
All moved |
django-guardian |
Copied for objects that are not projects, datasets, samples or measurements. A stored row grants nothing on those four, so none is copied for them |
|
Copied when the kept person’s field is empty |
The kept person is marked claimed and active, the sessions of the discarded person are ended, an audit entry with the method admin_merge is written, and the discarded person is deleted.
Duplicate finder#
find_duplicate_candidates(person, threshold=0.85) returns a list of {"person": Person, "score": float}, highest score first. The score runs from 0 to 1 and is the rapidfuzz token_sort_ratio of the two names divided by 100, so reordered names such as “Smith, John” and “John Smith” score alike. Only candidates with a score of at least threshold are returned. The person itself is excluded, and a person with an empty name gives an empty list. Nothing is stored. The administration page of an unclaimed person calls it.
ClaimingError#
ClaimingError is an Exception. The claiming functions raise it for an expected, user-facing failure: a person who is already claimed, a person who is deactivated, or an attempt to merge a person with itself. Its message is safe to show to the user. Unexpected programmer errors are other exceptions.
ClaimMethod#
ClaimMethod is a TextChoices in fairdm.contrib.contributors.models.
Name |
Value |
Label |
Written by |
|---|---|---|---|
|
|
ORCID Social Login |
|
|
|
Email Verification |
|
|
|
Admin-Initiated Merge |
|
|
|
Admin Manual Activation |
Nothing in FairDM. Available to your own code |
ClaimingAuditLog#
ClaimingAuditLog is an audit trail of claiming events. save() on a row that already exists raises ValueError, so entries are created once. Rows are ordered by timestamp, newest first.
Field |
Type |
Default |
What it holds |
|---|---|---|---|
|
|
Set on creation |
When the event happened |
|
|
None, required |
A |
|
|
|
The unclaimed person being claimed. Set to |
|
|
|
The resulting claimed person. Reached as |
|
|
|
The user who started an admin-driven claim. Reached as |
|
|
|
The requester’s address |
|
|
None, required |
Whether the claim succeeded |
|
|
|
Why the claim failed |
|
|
|
Further structured details |
ClaimingAuditLog.objects is a ClaimingAuditLogManager:
Method |
Returns |
What it does |
|---|---|---|
|
Queryset |
Events where the person was the source or the target |
|
Queryset |
Events where |
|
Queryset |
Events with one |
|
Queryset |
Events within the last |
log_claiming_event#
log_claiming_event(method, source, target, initiated_by=None, ip_address=None, success=True, failure_reason="", details=None) creates a ClaimingAuditLog row and returns it. source and target are people or None. details is a JSON-serialisable dict.
from fairdm.contrib.contributors.models import ClaimMethod
from fairdm.contrib.contributors.utils.audit import log_claiming_event
log_claiming_event(
method=ClaimMethod.ADMIN_MANUAL,
source=unclaimed_person,
target=activated_person,
initiated_by=request.user,
ip_address=request.META.get("REMOTE_ADDR"),
success=True,
details={"note": "Activated by hand."},
)
The services do not record an ip_address.