Crediting#
This page lists Crediting, the one place the contributors of a project, dataset, sample or measurement change, and the classes around it: the organization choice (AffiliationChoice), the two forms for people and organizations typed in by hand (NewPersonForm, NewOrganizationForm), the ORCID and ROR lookups (Orcid, Ror) and the pages of the Contributors tab. It does not cover the Contribution model, which is in Contributor models, or the levels a credit carries, which are in Contribution levels and record access. For the task, see How to credit a person on a record.
The Crediting class#
Crediting(record) is in fairdm.contrib.contributors.services.crediting. record is a project, dataset, sample or measurement of any registered type. A sample or measurement held as its base class is changed as its real type, which is where its contributors and their levels are read.
Every method that changes contributors runs in a transaction that first locks the record’s row with select_for_update, through the model’s all_objects manager where it has one so that a private record is found. Two changes to one record wait for each other, and each sees what the other left. A database that cannot lock rows, such as SQLite, runs the change in the transaction alone.
Each refusal is a ValidationError with a code, so a page can attach it to a field and a test can assert on it. When several refusals apply to one update, the error holds them all in its error_list and nothing is saved.
Method |
Returns |
What it does |
|---|---|---|
|
|
Lists the contributor last among its own kind. A person starts at the view level, an organization with no level. |
|
|
Replaces the roles with |
|
|
Deletes the credit, and the level goes with it |
|
|
Moves the credit one place earlier ( |
|
|
Lists the person who made the record at the manage level, with the named roles, or raises their existing entry to it. Does nothing and returns |
|
|
The concepts of the collection the record’s |
|
|
Organization id to the list of people credited from it on this record, in their order |
|
|
Lists an organization on the record, unless it already is |
|
|
Whether removing the credit ( |
|
|
The refusal with code |
|
Context manager |
Locks the record’s row for the length of a change. A method you add to |
UNCHANGED is a sentinel in the same module. Passed as organization to update, it leaves the organization as it is. An organization sets it and None sets it to none.
Refusal codes#
Method |
Raises when |
Code |
|---|---|---|
|
The contributor is already listed |
|
|
The contributor is a superuser |
|
|
The contributor is the placeholder account that stands for every visitor |
|
|
A role is not in the group the record’s type offers |
|
|
|
|
|
The change would leave the record with nobody who counts as able to manage it |
|
|
The credit is an organization that people on the record are credited from |
|
|
The credit is the only thing that makes the record manageable |
|
|
|
|
|
The credit is not listed on this record |
|
The credited_from refusal carries the people in params["people"], so a page can name them.
Order#
A record names its people in one order and its organizations in another. Crediting.move is the only code that changes either. It swaps order with the neighbour of the same kind on the same record. Credits that share an order value are told apart by their creation order, so a move gives the same result every time. add places a contributor last. update and remove leave the others’ order as it is.
Contribution.objects.for_entity(record).people() and .organizations() read the two lists, each ordered by order then pk. RecordOverviewPlugin.get_contributions() returns the two lists one after the other, people first. get_credits() follows that order and gives a person credited with no organization an affiliation of None.
The last manager#
A record that has someone who counts as able to manage it, as RecordAccess(record).managers() reckons, keeps one. update and remove refuse a change that would take the record from at least one such person to none, whoever asks. A record that has none already may lose or raise anyone. The person’s own level and the level they hold from a record above are both read, so lowering a manager who also manages through the dataset is allowed.
AffiliationChoice#
AffiliationChoice is a form in fairdm.contrib.contributors.plugins.shared. It is the choice of the organization a person is credited from on one record, and the form behind the c-contribution.affiliation component.
Argument |
Type |
Default |
What it does |
|---|---|---|---|
|
|
|
The person, when they are already in the portal. Their affiliations become the options, the current primary one first |
|
|
|
The person’s credit on the record, when it is being edited. Its organization is selected |
|
|
|
An organization name to offer, such as an employer from ORCID |
Field |
Accepts |
|---|---|
|
|
|
The organization’s name, up to the length of |
Member |
Returns |
What it does |
|---|---|---|
|
|
The organization chosen. For |
|
|
The options, which one is selected, the name typed for another organization, and every organization name for the suggestions. A refused form keeps what was entered |
|
Property, |
The first error message |
|
|
Static. One |
NewPersonForm and NewOrganizationForm#
Both are forms in fairdm.contrib.contributors.plugins.shared. They are behind the by-hand tab of the add pages. save() makes the contributor and does nothing else, so a page can make it, make the organization chosen and write the credit in one transaction.
NewPersonForm:
Field |
Type |
Accepts |
|---|---|---|
|
|
The given name, up to the length of |
|
|
The family name, up to the length of |
|
|
Whether the user has seen the profiles that share the name. Optional |
A posted email is ignored. save() returns an active, unclaimed Person with an unusable password and no account. same_name lists up to five people whose name matches, and the form is not valid while it is not empty unless confirmed is sent. The error has the code same_name.
NewOrganizationForm:
Field |
Type |
Accepts |
|---|---|---|
|
|
The name. Required |
|
|
A city. Optional |
|
|
A country name or a code from the country field’s list. Anything else is refused with the code |
|
|
A web address. A missing scheme is read as |
The form is never valid while same_name holds an organization with the same name (ignoring case). The error has the code same_name.
Orcid and Ror#
Orcid and Ror are in fairdm.contrib.contributors.services.registries. They are two plain classes with no shared base, and the add pages search them. Each has the same four methods.
Method |
Returns |
What it does |
|---|---|---|
|
|
Searches by name, or by identifier when |
|
record or |
Fetches one record. |
|
|
The contributor the portal already holds under the record’s identifier. A ROR identifier stored as an address is found too |
|
|
The contributor that |
A record is a plain dictionary:
Key |
|
|
|---|---|---|
|
The ORCID iD |
The ROR address, |
|
The ORCID iD |
The address without its scheme |
|
Given and family names |
ROR’s display name |
|
The employer and where it is, or a note that none is listed |
The kind of organization and where it is |
|
The two names and the first current employer (or |
Not present |
The module also holds:
Name |
Type |
What it is |
|---|---|---|
|
Exception |
Raised for a network error, a timeout, an answer that is not a 200 (a 404 on |
|
Function |
The one function that makes a request. |
|
|
The most matches a search returns |
|
|
The seconds a registry has to answer |
|
|
The seconds an answer is kept in the default cache, by address, parameters and active language. An answer of “no such record” is kept. A failure is not |
Search terms go in the request’s parameters and never into the address. No identifier reaches an address unless it matches ORCID_PATTERN or ROR_PATTERN, both in fairdm.contrib.contributors.models. Saving the identifier queues the sync that every ContributorIdentifier queues, so a worker fills in the rest of the profile later.
Level choices#
level_choices(record, floor=None) is in fairdm.contrib.contributors.plugins.shared. It returns the three levels as the edit page draws them, as a list of {"value", "label", "hint", "disabled"}. The hint names the record’s kind. A level below floor, the level the person holds from above, is disabled.
The Contributors tab#
ContributionList in fairdm.contrib.contributors.plugins.shared is the plugin behind the Contributors tab. It is registered on Project, Dataset, Sample and Measurement, with the label “Contributors”, the icon users and order 150. Registered sample and measurement types inherit it, and their roles, levels and addresses are read from the core model they extend.
The pages share the base class ContributionPage, which works out the record, its kind and whether the viewer may manage it. A page opens only when the record’s overview would. A page that changes anything answers 403 to a signed-in person who cannot manage the record and sends a visitor to sign in.
Class |
|
Method |
What it does |
|---|---|---|---|
|
|
GET |
Lists the people and the organizations, each in their own order |
|
|
GET, POST |
Adds a person from the portal, from ORCID or by hand |
|
|
GET, POST |
Adds an organization from the portal, from ROR or by hand |
|
|
GET, POST |
Sets a contributor’s roles and, for a person, the level and the organization |
|
|
GET, POST |
Confirms, then removes a contributor |
|
|
POST |
Moves a contributor one place. A |
ContributionAddPerson and ContributionAddOrganization share the base ContributionAdd:
Attribute |
Type |
Default |
What it does |
|---|---|---|---|
|
|
|
Whether the page adds a person |
|
|
|
The registry behind the second tab |
|
form |
|
The form behind the by-hand tab |
|
|
|
The most matches the portal search lists |
The page offers three tabs, portal, registry and new, and via in the address or the form names the one to open. The portal search reads q and the registry search reads rq. The context’s adding carries registry_results, registry_more and registry_unavailable. A registry that cannot be reached leaves the other two tabs working, and the page answers 200. Choosing a registry record only fetches it. Adding posts the identifier, and the page fetches the record again before it makes a profile.
A refused add or edit answers 422 and draws the page again with what was entered.
Addresses resolve with the plugin reverse:
from fairdm.contrib.plugins import reverse
reverse(dataset, "contribution-list")
reverse(dataset, "contribution-list-contribution-add-person")
reverse(dataset, "contribution-list-contribution-edit", pk=contribution.pk)
reverse(sample, "contribution-list-contribution-remove", pk=contribution.pk)
See Plugins for the plugin registration arguments.