signoffs.core.approvals#
An Approval manages logic for collecting and sequencing one or more Signoffs.
Approval Types are registered subclasses of AbstractApproval
they define the behaviour, sequencing logic, and state transition logic for an Approval instance.
Persistence layer for Approval state is provided by a Stamp model (think āStamp of Approvalā or TimeStamp)
one concrete
Stampmodel can back any number of Approval Typesan
Approvalprovides the business and presentation logic for aStampinstance.
Module Contents#
Classes#
Defines the semantics for Approving something using a sequence of one or more |
|
A base Approval Type to be used as base class or to register concrete Approval Types Concrete Types will require a concrete Stamp Model to back the approval. |
|
Defines the business logic for Approving and Revoking an Approval instance |
|
Public API: Alias for `DefaultApprovalBusinessLogic |
Functions#
Force revoke the given approval for user regardless of permissions or approval state! |
API#
- signoffs.core.approvals.revoke_approval(approval, user, reason='')[source]#
Force revoke the given approval for user regardless of permissions or approval state!
Default implementation revokes ALL related signets on behalf of the user
a user with permission to revoke an approval DOES NOT NEED permission to revoke all signoffs within!
- class signoffs.core.approvals.AbstractApproval(stamp=None, subject=None, **kwargs)[source]#
Defines the semantics for Approving something using a sequence of one or more
SignoffsAn Approval Type (subclass of
AbstractApproval) defines the business and presentation logic for a specific type of approval. The state of anApprovalinstance is persisted by anApprovalStampand its relatedSignets.Approval Types are pure-code objects, not stored in DB, as they define application logic, not application data. An Approval Type defines:
how the
Stampis labelled and rendered,what sequecne of signoffs is required to complete it;
what permission is required to revoke it,
define and register new Approval Types with factory method::
BaseApproval.register(...)
Default meta-data & services can be overridden by subclasses or passed to
.register()factory Approval Types are registered in thesignoffs.registry.approvalswhere they can be retrieved byidCaution
Stamprecords are stored in DB with a reference toApproval.idBe cautions not to change or delete idās that are in-use.Use a
SigningOrderto define the sequence of Signgoffs required.default signing order is sequential, ordered alphabetically, based on
SignoffFields/ attribute defined on theApproval
Tip
most Approvals will override
signing_orderuse ordering API on Approval instance rather than accessing
signing_orderdirectly!
Initialization
Construct an Approval instance backed by the given stamp or an instance of cls.stampModel(**kwargs) subject is optional: the object this approval is meant to approve - set by ApprovalField but otherwise unused.
- id: str#
āapproval.abstractā
unique identifier for type - used like FK, donāt mess with these!
- stampModel: signoffs.core.approvals.stamp_type#
None
- signing_order: signoffs.core.signing_order.SigningOrder#
None
- logic: signoffs.core.approvals.ApprovalLogic#
None
- status: signoffs.core.status.ApprovalStatus#
None
- render: signoffs.core.renderers.ApprovalRenderer#
None
- urls: signoffs.core.urls.ApprovalUrlsManager#
None
- classmethod register(id, **kwargs)[source]#
Create, register, and return a new subclass of cls with overrides for given kwargs attributes
Standard mechanism to define new Approval Types, typically in
my_app/models.pyormy_app/approvals.pyUsage:MyApproval = AbstractApproval.register('my_appproval_type', label='Approve it!', ...)
- classmethod validate()[source]#
Run any class validation that must pass before class can be registered. Invoked by registry.
- classmethod get_stampModel()[source]#
Always use this accessor as the stampModel attribute may be an āapp.Modelā label
- classmethod get_stamp_queryset(prefetch=('signatories',))[source]#
Return a base (unfiltered) queryset of ALL Stamps for this Approval Type
- property subject#
The object being approved, if provided. Sub-classes with stamp FK relations may want to override this to access the stamp related object. subject is set by model Fields for convenient access to owner obj, but value is not used by any core logic.
- property slug#
A slugified version of the signoff id, for places where a unique identifier slug is required
- property stamp_model#
return the signoff model for this type
- __contains__(item)[source]#
Return True iff this approvalās signatories contains the item: signoff id, type, or user
- property signoffs#
Return an ApprovalSignoffsManager for access to this approvalās signoff set Default implementation returns manager for signoffs backed by stampās signet_set manager. Concrete Approval Types may inject a custom signoffsManager for custom set of signoffs.
- has_signed(user)[source]#
Return True iff given user is a signatory on this approvalās set of signoffs
- has_signoff(signoff_id_or_type)[source]#
Return True iff this approval already has a signoff of the given signoff_type
- can_sign(user, signoff=None)[source]#
return True iff the given user can sign given signoff on this approval, or any of the next signoffs in its signing order
- ready_to_approve()[source]#
Return True iff this approvalās signing order is complete and ready to be approved
- approve_if_ready(commit=True)[source]#
Approve and save this approval is it meets all ready conditions
- approve(commit=True, **kwargs)[source]#
Approve this stamp. No permissions or signoff logic involved here - just force into approved state! Raises PermissionDenied if self.is_approved() ā canāt approval an approved approval :-P kwargs passed directly to save() - use commit=False to approve without saving
- is_revokable(by_user=None)[source]#
Return True iff this approval is in a state it could be revoked, optionally by given user
- classmethod is_permitted_revoker(user)[source]#
Return True iff user has permission to revoke approvals of this type
- revoke_if_permitted(user, **kwargs)[source]#
Revoke and save the approval is it meets all conditions for revocation
- revoke(user, **kwargs)[source]#
Revoke this approval regardless of permissions or approval state - careful!
Prefer to use
revoke_if_permittedto enforce business rules.
- can_revoke_signoff(signoff, user)[source]#
Return True iff the given signoff can be revoked from this approval by given user
- property signatories#
Return queryset of Signets representing the signatories on this approval
Default implementation simply returns Stampās āreverseā signet_set manager. Concrete Approval Types can override to provide any sensible qs of signets.
- property timestamp#
Return the timestamp approval was granted, None otherwise
- save(*args, **kwargs)[source]#
Attempt to save the Stamp of Approval, with the provided given associated data
- is_complete()[source]#
Is this approval process complete and ready to be approved? Default implementation returns False if no signing order, True if the signing order is complete. Concrete Approval Types can override this method to customize conditions under which this approval is complete.
- next_signoff_types(for_user=None)[source]#
Return list of next signoff type(s) (Signoff Type) required in this approval process.
Default impl returns next signoffs from the approvalās signing order or [] if no signing order is available. Concrete Approval Types can override this with custom business logic to provide signing order automation.
- next_signoffs(for_user=None)[source]#
Return list of next signoff instance(s) required in this approval process.
- Returns:
list[AbstractSignoff] where all(s.can_sign(for_user) for s in list)
If a user object is supplied, filter out instances not available to that user. Most applications will define custom business logic for ordering signoffs, restricting duplicate signs, etc. - ideally, use ApprovalLogic and SigningOrder to handle these, but this gives total control!
- class signoffs.core.approvals.BaseApproval(stamp=None, subject=None, **kwargs)[source]#
Bases:
signoffs.core.approvals.AbstractApprovalA base Approval Type to be used as base class or to register concrete Approval Types Concrete Types will require a concrete Stamp Model to back the approval.
Initialization
Construct an Approval instance backed by the given stamp or an instance of cls.stampModel(**kwargs) subject is optional: the object this approval is meant to approve - set by ApprovalField but otherwise unused.
- id#
āsignoffs.base-approvalā
- stampModel#
None
- class signoffs.core.approvals.DefaultApprovalBusinessLogic(revoke_perm=None, revoke_method=None)[source]#
Defines the business logic for Approving and Revoking an Approval instance
Initialization
Override default actions, or leave parameter None to use class default
- revoke_perm: signoffs.core.approvals.opt_str = <Multiline-String>#
- is_signable(approval, by_user=None)[source]#
Return True iff this approval is in a state it could be signed by the given user
Important
this is approval-level logic only ā keep signoff-level rules in signoff.can_sign
does not determine if there is a signoff available to be signed, only about the state of this approval!
use can_sign to determine if there are any actual signoffs available to the user to be signed.
- can_sign(approval, user, signoff=None)[source]#
Return True iff the given user can sign given signoff on this approval, or any of the next signoffs in its signing order
If a
Signoffinstance is provided, check that the user can sign this specific signoff.
- ready_to_approve(approval)[source]#
Return True iff the approvalās signing order is complete and ready to be approved
- approve_if_ready(approval, commit=True)[source]#
Approve and save the approval is it meets all ready conditions; return True iff this was done.
- approve(approval, commit=True, **kwargs)[source]#
Approve the approval and save itās Stamp.
No permissions or signoff completion logic involved here - just force into approved state!
Prefer to use
approve_if_readyto enforce business rules.kwargspassed directly to save() - use commit=False to approve without saving
- is_revokable(approval, by_user=None)[source]#
Return True iff this approval is in a state it could be revoked by the given user
Important
this is approval-level logic only ā keep signoff-level rules in signoff.can_revoke
does not determine if there is a signoff available to be revoked, only about the state of this approval!
use can_revoke to determine if the approval is actually available to the user to be revoked.
- is_permitted_revoker(approval_type, user)[source]#
return True iff user has permission to revoke approvals of given Type
- revoke_if_permitted(approval, user, reason='')[source]#
Revoke and save the approval if it meets all conditions for revocation.
- Raises:
PermissionDenied ā if not
- revoke(approval, user, reason='')[source]#
Revoke the approval and save its Stamp.
No permissions or completion logic involved here - just force into revoked state! Prefer to use
revoke_if_permittedto enforce business rules.
- can_revoke_signoff(approval, signoff, user)[source]#
Return True iff the given signoff can be revoked from this approval by given user
Note
Default logic restricts revoke to the last signoff collected on unapproved approvals. Think carefully before overriding this restriction - users sign in-order and that often has meaning, even in cases where signoffs are collected purely āin-parallelā.
- class signoffs.core.approvals.ApprovalLogic(revoke_perm=None, revoke_method=None)[source]#
Bases:
signoffs.core.approvals.DefaultApprovalBusinessLogicPublic API: Alias for `DefaultApprovalBusinessLogic
Initialization
Override default actions, or leave parameter None to use class default
signoffs.contrib.approvals.approvals#
Some basic Approval Types backed by the Stamp model defined in this package.
Module Contents#
Classes#
An abstract, base Signoff Type backed by a ApprovalSignet - a Signet with a FK relation to an ApprovalStamp |
|
A base Approval Type that can be used out-of-the-box for simple use-cases where any user can sign off Backed by signoffs.contrib.approvals.models.Stamp model. |
|
A SimpleApproval that can never be revoked |
Functions#
Avoid circular imports that might arise from importing form before models are finished loading |
API#
- signoffs.contrib.approvals.approvals.approval_signoff_form()[source]#
Avoid circular imports that might arise from importing form before models are finished loading
- class signoffs.contrib.approvals.approvals.ApprovalSignoff(signet=None, subject=None, **kwargs)[source]#
Bases:
signoffs.core.signoffs.BaseSignoffAn abstract, base Signoff Type backed by a ApprovalSignet - a Signet with a FK relation to an ApprovalStamp
Initialization
Construct a Signoff instance backed by the given signet or an instance of
cls.signetModel(**kwargs)subjectis optional: the object this signoff is signing off on - set bySignoffFieldbut otherwise unused.- signetModel#
None
- forms#
None
- property subject#
Subject is the approval being signed off on.
- property approval#
friendly name for subject
- class signoffs.contrib.approvals.approvals.SimpleApproval(stamp=None, subject=None, **kwargs)#
Bases:
signoffs.core.approvals.BaseApprovalA base Approval Type that can be used out-of-the-box for simple use-cases where any user can sign off Backed by signoffs.contrib.approvals.models.Stamp model.
Initialization
Construct an Approval instance backed by the given stamp or an instance of cls.stampModel(**kwargs) subject is optional: the object this approval is meant to approve - set by ApprovalField but otherwise unused.
- stampModel#
None
- label#
āApproveā
- class signoffs.contrib.approvals.approvals.IrrevokableApproval(stamp=None, subject=None, **kwargs)#
Bases:
signoffs.contrib.approvals.approvals.SimpleApprovalA SimpleApproval that can never be revoked
Initialization
Construct an Approval instance backed by the given stamp or an instance of cls.stampModel(**kwargs) subject is optional: the object this approval is meant to approve - set by ApprovalField but otherwise unused.
- logic#
None