Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: enhance sponsorship uc documentation #1928

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ num2words==0.5.10
django-polymorphic==3.0.0
sorl-thumbnail==12.7.0
docxtpl==0.12.0
django-extensions==3.1.2
6 changes: 5 additions & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ factory-boy==3.1.0
Faker==0.8.1
tblib==1.7.0
responses==0.13.3
model-bakery==1.3.2

# Extra stuff required for local dev

django-debug-toolbar==3.2.1
coverage
ddt
model-bakery==1.3.2

# Extra stuff required by django-extensions Graph models command line
pyparsing==3.0.6
pydot==1.4.2
Binary file added docs/source/_images/sponsors-db.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions docs/source/administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ they represent:
:contract.py: The `Contract` model which is used to generate the final contract document and other
support models;

.. image:: _images/sponsors-db.png
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the image (and the dependency that generates it) is necessary here as it is quite complex and I feel more lost with it 😓

:alt: Sponsors app's database schema

The sponsors app is mostly an administrative one. Its only part that regular uses can interact with is
the sponsorship application form, available at ``/sponsors/application/new/``. Despite that, every
administrative operation should be done via admin actions buttons in both ``Sponsorship`` and ``Contract``
models.

Events
------
Expand Down
1 change: 1 addition & 0 deletions pydotorg/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
'django_countries',
'easy_pdf',
'sorl.thumbnail',
'django_extensions',

'banners',
'blogs',
Expand Down
5 changes: 5 additions & 0 deletions pydotorg/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,8 @@
REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] += (
'rest_framework.renderers.BrowsableAPIRenderer',
)

# detailed info https://django-extensions.readthedocs.io/en/latest/graph_models.html
GRAPH_MODELS = {
'app_labels': ["sponsors"],
}
65 changes: 60 additions & 5 deletions sponsors/use_cases.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
"""
This module holds Use Cases (UCs) implementations. These are indirections to trigger business rules
and avoid the sponsors core logic and state management to be spread across views codes.
"""
from abc import ABC, abstractmethod

from sponsors import notifications
from sponsors.models import Sponsorship, Contract, SponsorContact, SponsorEmailNotificationTemplate
from sponsors.pdf import render_contract_to_pdf_file, render_contract_to_docx_file


class BaseUseCaseWithNotifications:
class BaseUseCaseWithNotifications(ABC):
"""
Abstract base class to be used to implement use cases.
It holds a list of notifications to be dispatched by the UC if needed
"""
notifications = []

@classmethod
def build(cls):
"""
Factory method to explicity handle complex logic and/or dependency injection
"""
return cls(cls.notifications)

@abstractmethod
def execute(self, *args, **kwargs):
"""
Abstract method to implement specific UC business rules
"""
pass

def __init__(self, notifications):
self.notifications = notifications

def notify(self, **kwargs):
for notification in self.notifications:
notification.notify(**kwargs)

@classmethod
def build(cls):
return cls(cls.notifications)


class CreateSponsorshipApplicationUseCase(BaseUseCaseWithNotifications):
"""
Use case called to create a new sponsorships application for submitted by a user
"""

notifications = [
notifications.AppliedSponsorshipNotificationToPSF(),
notifications.AppliedSponsorshipNotificationToSponsors(),
Expand All @@ -31,6 +55,9 @@ def execute(self, user, sponsor, benefits, package=None, request=None):


class RejectSponsorshipApplicationUseCase(BaseUseCaseWithNotifications):
"""
Use case to enable PSF staff to reject an application
"""
notifications = [
notifications.RejectedSponsorshipNotificationToPSF(),
notifications.RejectedSponsorshipNotificationToSponsors(),
Expand All @@ -44,6 +71,9 @@ def execute(self, sponsorship, request=None):


class ApproveSponsorshipApplicationUseCase(BaseUseCaseWithNotifications):
"""
Use case to enable PSF staff to approve an application
"""
notifications = [
notifications.SponsorshipApprovalLogger(),
]
Expand Down Expand Up @@ -71,6 +101,10 @@ def execute(self, sponsorship, start_date, end_date, **kwargs):


class SendContractUseCase(BaseUseCaseWithNotifications):
"""
Use case to enable PSF staff to generate the contract .docx
file and sent it over email
"""
notifications = [
notifications.ContractNotificationToPSF(),
# TODO: sponsor's notification will be enabled again once
Expand All @@ -92,6 +126,14 @@ def execute(self, contract, **kwargs):


class ExecuteExistingContractUseCase(BaseUseCaseWithNotifications):
"""
Use case to PSF Staff to finalize a sponsorship by "executing" a contract.
This UC was created to enable to enable to upload existing contracts documents
that weren't generated by the sponsors app.

It's probable that this UC will become a legacy one once all the new
contracts and sponsorships were created via the Django application
"""
notifications = [
notifications.ExecutedExistingContractLogger(),
]
Expand All @@ -107,13 +149,22 @@ def execute(self, contract, contract_file, **kwargs):


class ExecuteContractUseCase(ExecuteExistingContractUseCase):
"""
Use case to PSF Staff to execute a contract created by the sponsors app.
Execute a contract requires the admin user to upload the signed contract
and this will flag the contract as Executed and the corresponding Sponsorship
as Finalized.
"""
notifications = [
notifications.ExecutedContractLogger(),
]
force_execute = False


class NullifyContractUseCase(BaseUseCaseWithNotifications):
"""
Use case to enable PSF staff to nullify non-executed contracts
"""
notifications = [
notifications.NullifiedContractLogger(),
]
Expand All @@ -127,6 +178,10 @@ def execute(self, contract, **kwargs):


class SendSponsorshipNotificationUseCase(BaseUseCaseWithNotifications):
"""
Use case to enable PSF staff to send DB stored email notifications
to a list of selected sponsorships.
"""
notifications = [
notifications.SendSponsorNotificationLogger(),
]
Expand Down