fairdm.core.editing#

The pages that edit a record, written once and registered on every record type.

A project, a dataset, a sample and a measurement are edited through the same pages, each a plugin registered on all four models. A sample or measurement type a portal registers gets them with no further work. The pages are reached from the Manage menu, never from the tabs.

Module Contents#

Classes#

RecordEditingPage

What every editing page shares: who may open it and where it leads afterwards.

EditDetails

Edit the record’s own fields.

EditDescriptions

Edit the record’s descriptions, one area per type its vocabulary offers.

EditKeywords

Edit the record’s free keywords: words a person types, with no vocabulary behind them.

EditKeyDates

Edit the record’s dates, one row per date type its vocabulary offers.

EditIdentifiers

Edit the record’s identifiers, one row per identifier type its vocabulary offers.

DeleteRecord

Delete the record, confirmed by typing its name, or its portal ID when it has no name.

Functions#

manage_menu

List the editing pages the viewer may use on a record, in the order the menu shows them.

Data#

API#

class RecordEditingPage(**kwargs)#

Bases: fairdm.contrib.plugins.Plugin

What every editing page shares: who may open it and where it leads afterwards.

A page names its right in access. The rule that reads it lives here, because one page is registered on four record types and a plugin has one permission attribute, not four.

A request is answered in this order, on every request, so a save is refused as a view is:

  1. A viewer who may neither see the record nor holds the right on it gets a 404, as for a record that does not exist.

  2. A visitor who may see the record and may not use the page is sent to sign in.

  3. A signed-in person who may see the record and may not use the page gets a 403.

Attributes: access: "change" for the pages that edit the record, "delete" for the one that removes it. menu_label: The page’s entry in the Manage menu. menu_icon: The icon beside that entry.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

OPEN: ClassVar[str]#

‘open’

MISSING: ClassVar[str]#

‘missing’

REFUSED: ClassVar[str]#

‘refused’

PERMISSIONS: ClassVar[dict[str, dict[type, str]]]#

None

access: ClassVar[str]#

‘change’

menu_label: ClassVar[str | django.utils.functional.Promise] = <Multiline-String>#
menu_icon: ClassVar[str]#

‘edit’

classmethod permission_for(record)#

Name the permission that opens this page on a record.

Args: record: A project, dataset, sample or measurement, of any registered type.

Returns: The permission, with its app label.

static overview_for(record)#

Find the overview page of the record’s kind, whose check decides who may see it.

Resolved when asked, because the record modules import this module.

Args: record: A project, dataset, sample or measurement, of any registered type.

Returns: The overview plugin class.

classmethod verdict(request, record, may_see=None)#

Decide what the viewer is told when they ask for this page on a record.

Args: request: The current request. record: The record the page belongs to. may_see: Whether the viewer may open the record’s overview, when the caller already knows. Left out, it is checked here.

Returns: OPEN when the page may run, MISSING when the viewer is to be told the record does not exist, REFUSED when they may see the record and may not use the page.

classmethod may_be_used_by(request, record, may_see=None)#

Say whether the page would open for the viewer, for the Manage menu.

Args: request: The current request. record: The record the page belongs to. may_see: Whether the viewer may open the record’s overview, when the caller already knows.

Returns: True when the page would run.

dispatch(request, *args, **kwargs)#

Answer 404 for a record the viewer may not see, before any other refusal.

has_permission()#

Open the page for whoever verdict admits, so the save is checked as the view is.

get_object(queryset=None)#

Edit the record the address names, so no page depends on a model attribute.

static name_of(record)#

Name a record as a person does: a measurement by its name, else its portal ID.

Args: record: A project, dataset, sample or measurement, of any registered type.

Returns: The name to show for the record.

property record_name#

The page’s record, named by :meth:name_of.

static without_form_tag(form)#

Strip a form’s crispy helper of the tag and buttons, which the page draws itself.

A form element nested in another ends the outer one early, leaving the page’s Save buttons outside any form.

Args: form: A form that may carry a crispy helper.

Returns: The same form.

get_success_message(cleaned_data)#

Tell the person which record was saved.

get_success_url()#

Return to the record’s own page.

class EditDetails(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, fairdm.views.FairDMUpdateView

Edit the record’s own fields.

A sample or measurement is edited with the form its registered type creates records with, less the fields that would move it to another dataset or sample. Dates and identifiers have pages of their own.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘edit’

access#

‘change’

menu_label#

‘_(…)’

menu_icon#

‘edit’

page_title#

‘_(…)’

get_form_class()#

Choose the record’s form by its kind, and by its registered type for a sample or measurement.

get_form_kwargs()#

Pass the request to a form whose constructor asks for it.

get_form(form_class=None)#

Drop the fields that move a record and the form tag and buttons the page draws itself.

class EditDescriptions(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, meta.views.MetadataMixin, mvp.views.MVPFormView

Edit the record’s descriptions, one area per type its vocabulary offers.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘descriptions’

access#

‘change’

menu_label#

‘_(…)’

menu_icon#

‘description’

page_title#

‘_(…)’

form_class#

None

template_name#

‘form_view.html’

DESCRIPTIONS: ClassVar[dict[type, type]]#

None

get_form_kwargs()#

Pass the description model and the record to the form.

form_valid(form)#

Save the descriptions before redirecting.

class EditKeywords(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, fairdm.views.FairDMUpdateView

Edit the record’s free keywords: words a person types, with no vocabulary behind them.

Choosing keywords from a controlled vocabulary is a feature of its own (#298). Vocabulary keywords already on a record are left as they are.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘keywords’

access#

‘change’

menu_label#

‘_(…)’

menu_icon#

‘keywords’

page_title#

‘_(…)’

form_class#

None

get_form(form_class=None)#

Drop the form tag and buttons the page draws itself.

class EditKeyDates(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, fairdm.views.FairDMUpdateView

Edit the record’s dates, one row per date type its vocabulary offers.

A project and a dataset refuse an end that falls before the start.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘key-dates’

access#

‘change’

menu_label#

‘_(…)’

menu_icon#

‘date’

page_title#

‘_(…)’

fields#

()

template_name#

‘editing/rows.html’

INLINES: ClassVar[dict[type, list]]#

None

get_inlines()#

List the row set of this kind of record’s dates.

class EditIdentifiers(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, fairdm.views.FairDMUpdateView

Edit the record’s identifiers, one row per identifier type its vocabulary offers.

The identifier the portal gives the record is its uuid, which is not a row here.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘identifiers’

access#

‘change’

menu_label#

‘_(…)’

menu_icon#

‘identifier’

page_title#

‘_(…)’

fields#

()

template_name#

‘editing/rows.html’

INLINES: ClassVar[dict[type, list]]#

None

get_inlines()#

List the row set of this kind of record’s identifiers.

class DeleteRecord(**kwargs)#

Bases: fairdm.core.editing.RecordEditingPage, fairdm.contrib.plugins.mixins.RecordOwnPageBackFallbackMixin, fairdm.views.FairDMDeleteView

Delete the record, confirmed by typing its name, or its portal ID when it has no name.

A project and a dataset say what goes with them as counts by record type, because listing every row would run to thousands of lines. A sample and a measurement list the rows that go with them. A record that cannot be deleted says what stops it and offers no way to confirm. The measurements that stop a deletion and that the viewer may not see are counted in the protected_unlisted context entry and never named.

Initialization

Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.

name#

‘delete’

access#

‘delete’

menu_label#

‘_(…)’

menu_icon#

‘delete’

page_title#

‘_(…)’

template_name#

‘editing/delete_record.html’

require_confirmation#

True

True

get_confirmation_value()#

Ask the person to type the record’s name.

drop_unlisted(related)#

Remove from the walk what the page must not list, and count the rows that hide a sample.

The record’s own base row is dropped: a registered type reaches it through its core model, and it is not something that goes with the record. For a sample, a relationship row is dropped when either end is a sample the viewer may not see, because the row prints both names.

Args: related: What the collector found, by model. Changed in place.

Returns: How many relationship rows were dropped for naming a sample the viewer may not see.

get_success_message(cleaned_data)#

Tell the person which record was deleted.

get_success_url()#

Lead to the dataset a sample or measurement belonged to, else to a list page.

Read before the record goes. A person who may not open the dataset is led to the dataset list instead.

get_context_data(**kwargs)#

Add what stops the deletion, named only as far as the viewer may see, and the counts.

static protected_kind(protected)#

Say what kind of record stops the deletion, so the page can name it.

Args: protected: The objects that stop the deletion.

Returns: "measurements" or "datasets" when every object is one, else "records".

split_protected(protected)#

Separate what stops the deletion into what the viewer may see and a count of the rest.

A measurement can sit in a dataset the viewer holds no level on, so it is named only when the viewer may see it, and then by :meth:RecordEditingPage.name_of.

Args: protected: The objects that stop the deletion.

Returns: The objects to name, and how many measurements were left out.

related_objects_summary()#

Count what a project or dataset takes with it, by record type and concrete class.

Returns: A list of (label, lines, 0) groups, one each for datasets, samples and measurements that have instances.

form_valid(form)#

Draw the page again in its protected state when a public dataset blocks the deletion.

MENU_PAGES#

()

manage_menu(request, record)#

List the editing pages the viewer may use on a record, in the order the menu shows them.

Args: request: The current request. record: A project, dataset, sample or measurement, of any registered type.

Returns: One entry per page the viewer may open, each with a label, an icon, a url and destructive, which marks the entry that deletes the record.