Source code for swh.web.add_forge_now.models

# Copyright (C) 2022  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

from __future__ import annotations

import enum
from typing import Dict, List
from urllib.parse import urlparse

from django.db import models

from ..config import get_config
from ..inbound_email.utils import get_address_for_pk
from .apps import APP_LABEL


[docs] class RequestStatus(enum.Enum): """Request statuses. Values are used in the ui. """ PENDING = "Pending" WAITING_FOR_FEEDBACK = "Waiting for feedback" FEEDBACK_TO_HANDLE = "Feedback to handle" ACCEPTED = "Accepted" SCHEDULED = "Scheduled" FIRST_LISTING_DONE = "First listing done" FIRST_ORIGIN_LOADED = "First origin loaded" REJECTED = "Rejected" SUSPENDED = "Suspended" UNSUCCESSFUL = "Unsuccessful"
[docs] @classmethod def choices(cls): return tuple((variant.name, variant.value) for variant in cls)
[docs] @classmethod def next_statuses(cls) -> Dict[RequestStatus, List[RequestStatus]]: return { cls.PENDING: [cls.WAITING_FOR_FEEDBACK, cls.REJECTED, cls.SUSPENDED], cls.WAITING_FOR_FEEDBACK: [ cls.FEEDBACK_TO_HANDLE, cls.ACCEPTED, cls.REJECTED, cls.SUSPENDED, cls.UNSUCCESSFUL, ], cls.FEEDBACK_TO_HANDLE: [ cls.WAITING_FOR_FEEDBACK, cls.ACCEPTED, cls.REJECTED, cls.SUSPENDED, cls.UNSUCCESSFUL, ], cls.ACCEPTED: [cls.SCHEDULED, cls.REJECTED, cls.UNSUCCESSFUL], cls.SCHEDULED: [ cls.FIRST_LISTING_DONE, # in case of race condition between lister and loader: cls.FIRST_ORIGIN_LOADED, ], cls.FIRST_LISTING_DONE: [cls.FIRST_ORIGIN_LOADED], cls.FIRST_ORIGIN_LOADED: [], cls.REJECTED: [], cls.SUSPENDED: [cls.PENDING], cls.UNSUCCESSFUL: [cls.ACCEPTED], }
[docs] @classmethod def next_statuses_str(cls) -> Dict[str, List[str]]: return { key.name: [value.name for value in values] for key, values in cls.next_statuses().items() }
[docs] def allowed_next_statuses(self) -> List[RequestStatus]: return self.next_statuses()[self]
[docs] class RequestActorRole(enum.Enum): MODERATOR = "moderator" SUBMITTER = "submitter" FORGE_ADMIN = "forge admin" EMAIL = "email"
[docs] @classmethod def choices(cls): return tuple((variant.name, variant.value) for variant in cls)
[docs] class RequestHistory(models.Model): """Comment or status change. This is commented or changed by either submitter or moderator. """ request = models.ForeignKey("Request", models.DO_NOTHING) text = models.TextField() actor = models.TextField() actor_role = models.TextField(choices=RequestActorRole.choices()) date = models.DateTimeField(auto_now_add=True) new_status = models.TextField(choices=RequestStatus.choices(), null=True) message_source = models.BinaryField(null=True) class Meta: app_label = APP_LABEL db_table = "add_forge_request_history"
[docs] class Request(models.Model): status = models.TextField( choices=RequestStatus.choices(), default=RequestStatus.PENDING.name, ) submission_date = models.DateTimeField(auto_now_add=True) submitter_name = models.TextField() submitter_email = models.TextField() submitter_forward_username = models.BooleanField(default=False) # FIXME: shall we do create a user model inside the webapp instead? forge_type = models.TextField() forge_url = models.URLField() forge_contact_email = models.EmailField() forge_contact_name = models.TextField() forge_contact_comment = models.TextField( null=True, help_text="Where did you find this contact information (url, ...)", ) last_moderator = models.TextField(default="None") last_modified_date = models.DateTimeField(null=True) class Meta: app_label = APP_LABEL db_table = "add_forge_request" @property def inbound_email_address(self) -> str: """Generate an email address for correspondence related to this request.""" base_address = get_config()["add_forge_now"]["email_address"] return get_address_for_pk(salt=APP_LABEL, base_address=base_address, pk=self.pk) @property def forge_domain(self) -> str: """Get the domain/netloc out of the forge_url. Fallback to using the first part of the url path, if the netloc can't be found (for instance, if the url scheme hasn't been set). """ parsed_url = urlparse(self.forge_url) domain = parsed_url.netloc if not domain: domain = parsed_url.path.split("/", 1)[0] return domain