diff --git a/aleksis/core/checks.py b/aleksis/core/checks.py index ebd6f8844c0e234af8685d70592485e8bd95efa4..9518b5e26bfe2f8d2cc8eff7494eeb6f4495035d 100644 --- a/aleksis/core/checks.py +++ b/aleksis/core/checks.py @@ -11,7 +11,7 @@ from .util.apps import AppConfig def check_app_configs_base_class( app_configs: Optional[django.apps.registry.Apps] = None, **kwargs ) -> list: - """Checks whether all apps derive from AlekSIS's base app config""" + """Checks whether all apps derive from AlekSIS's base app config.""" results = [] if app_configs is None: @@ -39,7 +39,7 @@ def check_app_configs_base_class( def check_app_models_base_class( app_configs: Optional[django.apps.registry.Apps] = None, **kwargs ) -> list: - """Checks whether all app models derive from AlekSIS's base ExtensibleModel""" + """Checks whether all app models derive from AlekSIS's base ExtensibleModel.""" results = [] if app_configs is None: diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index a359415a8ee58350bca0537a101b31ad1050e0ed..2fcafc68ba35e6e9d650bc9d2d6e9b73e223fdb2 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -19,7 +19,7 @@ from .registries import ( class PersonAccountForm(forms.ModelForm): - """Form to assign user accounts to persons in the frontend :""" + """Form to assign user accounts to persons in the frontend.""" class Meta: model = Person @@ -67,7 +67,7 @@ PersonsAccountsFormSet = forms.modelformset_factory( class EditPersonForm(ExtensibleForm): - """Form to edit an existing person object in the frontend""" + """Form to edit an existing person object in the frontend.""" layout = Layout( Fieldset( @@ -122,7 +122,7 @@ class EditPersonForm(ExtensibleForm): class EditGroupForm(ExtensibleForm): - """Form to edit an existing group in the frontend""" + """Form to edit an existing group in the frontend.""" layout = Layout( Fieldset(_("Common data"), "name", "short_name"), @@ -154,7 +154,7 @@ class EditGroupForm(ExtensibleForm): class AnnouncementForm(ExtensibleForm): - """Form to create or edit an announcement in the frontend""" + """Form to create or edit an announcement in the frontend.""" valid_from = forms.DateTimeField(required=False) valid_until = forms.DateTimeField(required=False) @@ -259,24 +259,24 @@ class AnnouncementForm(ExtensibleForm): class ChildGroupsForm(forms.Form): - """Inline form for group editing to select child groups""" + """Inline form for group editing to select child groups.""" child_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all()) class SitePreferenceForm(PreferenceForm): - """Form to edit site preferences""" + """Form to edit site preferences.""" registry = site_preferences_registry class PersonPreferenceForm(PreferenceForm): - """Form to edit preferences valid for one person""" + """Form to edit preferences valid for one person.""" registry = person_preferences_registry class GroupPreferenceForm(PreferenceForm): - """Form to edit preferences valid for members of a group""" + """Form to edit preferences valid for members of a group.""" registry = group_preferences_registry diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py index c391b5d084e3dbd61d8fab278312690b20c300c7..6da2f056475c418019fd5b312fed9e37dda6d401 100644 --- a/aleksis/core/mixins.py +++ b/aleksis/core/mixins.py @@ -18,7 +18,7 @@ from rules.contrib.admin import ObjectPermissionsModelAdmin @reversion.register() class ExtensibleModel(): - """Base model for all objects in AlekSIS apps + """Base model for all objects in AlekSIS apps. This base model ensures all objects in AlekSIS apps fulfill the following properties: @@ -71,12 +71,12 @@ class ExtensibleModel(): objects_all_sites = models.Manager() def get_absolute_url(self) -> str: - """Get the URL o a view representing this model instance""" + """Get the URL o a view representing this model instance.""" pass @property def crud_events(self) -> QuerySet: - """Get all CRUD events connected to this object from easyaudit""" + """Get all CRUD events connected to this object from easyaudit.""" content_type = ContentType.objects.get_for_model(self) return CRUDEvent.objects.filter( @@ -85,23 +85,23 @@ class ExtensibleModel(): @property def crud_event_create(self) -> Optional[CRUDEvent]: - """Return create event of this object""" + """Return create event of this object.""" return self.crud_events.filter(event_type=CRUDEvent.CREATE).latest("datetime") @property def crud_event_update(self) -> Optional[CRUDEvent]: - """Return last event of this object""" + """Return last event of this object.""" return self.crud_events.latest("datetime") @property def created_at(self) -> Optional[datetime]: - """Determine creation timestamp from CRUD log""" + """Determine creation timestamp from CRUD log.""" if self.crud_event_create: return self.crud_event_create.datetime @property def updated_at(self) -> Optional[datetime]: - """Determine last timestamp from CRUD log""" + """Determine last timestamp from CRUD log.""" if self.crud_event_update: return self.crud_event_update.datetime @@ -109,13 +109,13 @@ class ExtensibleModel(): @property def created_by(self) -> Optional[models.Model]: - """Determine user who created this object from CRUD log""" + """Determine user who created this object from CRUD log.""" if self.crud_event_create: return self.crud_event_create.user @property def updated_by(self) -> Optional[models.Model]: - """Determine user who last updated this object from CRUD log""" + """Determine user who last updated this object from CRUD log.""" if self.crud_event_update: return self.crud_event_update.user @@ -156,8 +156,7 @@ class ExtensibleModel(): @classmethod def field(cls, **kwargs) -> None: - """Adds the passed jsonstore field. Must be one of the fields in - django-jsonstore. + """Adds the passed jsonstore field. Must be one of the fields in django-jsonstore. Accepts exactly one keyword argument, with the name being the desired model field name and the value the field instance. @@ -181,7 +180,7 @@ class ExtensibleModel(): class PureDjangoModel(object): - """No-op mixin to mark a model as deliberately not using ExtensibleModel""" + """No-op mixin to mark a model as deliberately not using ExtensibleModel.""" pass @@ -203,7 +202,7 @@ class _ExtensibleFormMetaclass(ModelFormMetaclass): class ExtensibleForm(ModelForm, metaclass=_ExtensibleFormMetaclass): - """Base model for extensible forms + """Base model for extensible forms. This mixin adds functionality which allows - apps to add layout nodes to the layout used by django-material @@ -224,8 +223,7 @@ class ExtensibleForm(ModelForm, metaclass=_ExtensibleFormMetaclass): @classmethod def add_node_to_layout(cls, node: Union[LayoutNode, str]): - """ - Add a node to `layout` attribute + """Add a node to `layout` attribute. :param node: django-material layout node (Fieldset, Row etc.) :type node: LayoutNode @@ -235,6 +233,6 @@ class ExtensibleForm(ModelForm, metaclass=_ExtensibleFormMetaclass): class BaseModelAdmin(GuardedModelAdmin, ObjectPermissionsModelAdmin): - """A base class for ModelAdmin combining django-guardian and rules""" + """A base class for ModelAdmin combining django-guardian and rules.""" pass diff --git a/aleksis/core/models.py b/aleksis/core/models.py index ea55dfe99dc4e504f8c1274f9155f32ad789e854..d924a7fdaabdcbb9e4c2aa728a390ce0e71cc54d 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -42,7 +42,8 @@ FIELD_CHOICES = ( class Person(ExtensibleModel): - """A model describing any person related to a school, including, but not + """ + A model describing any person related to a school, including, but not limited to, students, teachers and guardians (parents). """ @@ -117,16 +118,16 @@ class Person(ExtensibleModel): @property def primary_group_short_name(self) -> Optional[str]: - """Returns the short_name field of the primary - group related object. - """ + """Returns the short_name field of the primary group related object.""" if self.primary_group: return self.primary_group.short_name @primary_group_short_name.setter def primary_group_short_name(self, value: str) -> None: - """Sets the primary group related object by - a short name. It uses the first existing group + """ + Sets the primary group related object by a short name. + + It uses the first existing group with this short name it can find, creating one if it can't find one. """ @@ -135,12 +136,12 @@ class Person(ExtensibleModel): @property def full_name(self) -> str: - """Full name of person in last name, first name order""" + """Full name of person in last name, first name order.""" return f"{self.last_name}, {self.first_name}" @property def adressing_name(self) -> str: - """Full name of person in format configured for addressing""" + """Full name of person in format configured for addressing.""" if get_site_preferences()["notification__addressing_name_format"] == "last_first": return f"{self.last_name}, {self.first_name}" elif get_site_preferences()["notification__addressing_name_format"] == "first_last": @@ -148,11 +149,11 @@ class Person(ExtensibleModel): @property def age(self): - """Age of the person at current time""" + """Age of the person at current time.""" return self.age_at(timezone.datetime.now().date()) def age_at(self, today): - """Age of the person at a given date and time""" + """Age of the person at a given date and time.""" years = today.year - self.date_of_birth.year if self.date_of_birth.month > today.month or ( self.date_of_birth.month == today.month and self.date_of_birth.day > today.day @@ -191,7 +192,7 @@ class Person(ExtensibleModel): admin.save() def auto_select_primary_group(self, pattern: Optional[str] = None, force: bool = False) -> None: - """Auto-select the primary group among the groups the person is member of + """Auto-select the primary group among the groups the person is member of. Uses either the pattern passed as argument, or the pattern configured system-wide. @@ -223,7 +224,7 @@ class DummyPerson(Person): class AdditionalField(ExtensibleModel): - """An additional field that can be linked to a group""" + """An additional field that can be linked to a group.""" title = models.CharField(verbose_name=_("Title of field"), max_length=255) field_type = models.CharField( @@ -236,7 +237,8 @@ class AdditionalField(ExtensibleModel): class Group(ExtensibleModel): - """Any kind of group of persons in a school, including, but not limited + """ + Any kind of group of persons in a school, including, but not limited classes, clubs, and the like. """ @@ -287,7 +289,7 @@ class Group(ExtensibleModel): @property def announcement_recipients(self): - """Flat list of all members and owners to fulfill announcement API contract""" + """Flat list of all members and owners to fulfill announcement API contract.""" return list(self.members.all()) + list(self.owners.all()) def __str__(self) -> str: @@ -329,7 +331,7 @@ class PersonGroupThrough(ExtensibleModel): class Activity(ExtensibleModel): - """Activity of a user to trace some actions done in AlekSIS in displayable form""" + """Activity of a user to trace some actions done in AlekSIS in displayable form.""" user = models.ForeignKey( "Person", on_delete=models.CASCADE, related_name="activities", verbose_name=_("User") @@ -349,7 +351,7 @@ class Activity(ExtensibleModel): class Notification(ExtensibleModel): - """Notification to submit to a user""" + """Notification to submit to a user.""" sender = models.CharField(max_length=100, verbose_name=_("Sender")) recipient = models.ForeignKey( @@ -382,10 +384,11 @@ class Notification(ExtensibleModel): class AnnouncementQuerySet(models.QuerySet): - """Queryset for announcements providing time-based utility functions""" + """Queryset for announcements providing time-based utility functions.""" def relevant_for(self, obj: Union[models.Model, models.QuerySet]) -> models.QuerySet: - """Get a QuerySet with all announcements relevant for a certain Model (e.g. a Group) + """ + Get a QuerySet with all announcements relevant for a certain Model (e.g. a Group) or a set of models in a QuerySet. """ if isinstance(obj, models.QuerySet): @@ -398,7 +401,7 @@ class AnnouncementQuerySet(models.QuerySet): return self.filter(recipients__content_type=ct, recipients__recipient_id__in=pks) def at_time(self, when: Optional[datetime] = None) -> models.QuerySet: - """Get all announcements at a certain time""" + """Get all announcements at a certain time.""" when = when or timezone.datetime.now() # Get announcements by time @@ -407,7 +410,7 @@ class AnnouncementQuerySet(models.QuerySet): return announcements def on_date(self, when: Optional[date] = None) -> models.QuerySet: - """Get all announcements at a certain date""" + """Get all announcements at a certain date.""" when = when or timezone.datetime.now().date() # Get announcements by time @@ -416,14 +419,14 @@ class AnnouncementQuerySet(models.QuerySet): return announcements def within_days(self, start: date, stop: date) -> models.QuerySet: - """Get all announcements valid for a set of days""" + """Get all announcements valid for a set of days.""" # Get announcements announcements = self.filter(valid_from__date__lte=stop, valid_until__date__gte=start) return announcements def for_person(self, person: Person) -> List: - """Get all announcements for one person""" + """Get all announcements for one person.""" # Filter by person announcements_for_person = [] for announcement in self: @@ -434,7 +437,8 @@ class AnnouncementQuerySet(models.QuerySet): class Announcement(ExtensibleModel): - """Persistent announcement to display to groups or persons in various places during a + """ + Persistent announcement to display to groups or persons in various places during a specific time range. """ @@ -453,14 +457,15 @@ class Announcement(ExtensibleModel): @property def recipient_persons(self) -> Sequence[Person]: - """Return a list of Persons this announcement is relevant for""" + """Return a list of Persons this announcement is relevant for.""" persons = [] for recipient in self.recipients.all(): persons += recipient.persons return persons def get_recipients_for_model(self, obj: Union[models.Model]) -> Sequence[models.Model]: - """Get all recipients for this announcement + """ + Get all recipients for this announcement with a special content type (provided through model) """ ct = ContentType.objects.get_for_model(obj) @@ -475,7 +480,8 @@ class Announcement(ExtensibleModel): class AnnouncementRecipient(ExtensibleModel): - """Generalisation of a recipient for an announcement, used to wrap arbitrary + """ + Generalisation of a recipient for an announcement, used to wrap arbitrary objects that can receive announcements. Contract: Objects to serve as recipient have a property announcement_recipients @@ -492,7 +498,7 @@ class AnnouncementRecipient(ExtensibleModel): @property def persons(self) -> Sequence[Person]: - """Return a list of Persons selected by this recipient object + """Return a list of Persons selected by this recipient object. If the recipient is a Person, return that object. If not, it returns the list from the announcement_recipients field on the target model. @@ -511,7 +517,7 @@ class AnnouncementRecipient(ExtensibleModel): class DashboardWidget(PolymorphicModel, PureDjangoModel): - """Base class for dashboard widgets on the index page + """Base class for dashboard widgets on the index page. To implement a widget, add a model that subclasses DashboardWidget, sets the template and implements the get_context method to return a dictionary to be passed as context @@ -561,11 +567,12 @@ class DashboardWidget(PolymorphicModel, PureDjangoModel): active = models.BooleanField(blank=True, verbose_name=_("Activate Widget")) def get_context(self): - """Get the context dictionary to pass to the widget template""" + """Get the context dictionary to pass to the widget template.""" raise NotImplementedError("A widget subclass needs to implement the get_context method.") def get_template(self): - """Get the template to render the widget with. Defaults to the template attribute, + """ + Get the template to render the widget with. Defaults to the template attribute, but can be overridden to allow more complex template generation scenarios. """ return self.template @@ -579,7 +586,7 @@ class DashboardWidget(PolymorphicModel, PureDjangoModel): class CustomMenu(ExtensibleModel): - """A custom menu to display in the footer""" + """A custom menu to display in the footer.""" name = models.CharField(max_length=100, verbose_name=_("Menu ID"), unique=True) @@ -588,7 +595,7 @@ class CustomMenu(ExtensibleModel): @classmethod def get_default(cls, name): - """Get a menu by name or create if it does not exist""" + """Get a menu by name or create if it does not exist.""" menu, _ = cls.objects.get_or_create(name=name) return menu @@ -598,7 +605,7 @@ class CustomMenu(ExtensibleModel): class CustomMenuItem(ExtensibleModel): - """Single item in a custom menu""" + """Single item in a custom menu.""" menu = models.ForeignKey( CustomMenu, models.CASCADE, verbose_name=_("Menu"), related_name="items" @@ -618,7 +625,8 @@ class CustomMenuItem(ExtensibleModel): class GroupType(ExtensibleModel): - """Descriptive type of a group; used to tag groups and for apps to distinguish + """ + Descriptive type of a group; used to tag groups and for apps to distinguish how to display or handle a certain group. """ @@ -631,7 +639,7 @@ class GroupType(ExtensibleModel): class GlobalPermissions(ExtensibleModel): - """Container for global permissions""" + """Container for global permissions.""" class Meta: managed = False @@ -648,7 +656,7 @@ class GlobalPermissions(ExtensibleModel): class SitePreferenceModel(PerInstancePreferenceModel, PureDjangoModel): - """Preference model to hold pereferences valid for a site""" + """Preference model to hold pereferences valid for a site.""" instance = models.ForeignKey(Site, on_delete=models.CASCADE) @@ -657,7 +665,7 @@ class SitePreferenceModel(PerInstancePreferenceModel, PureDjangoModel): class PersonPreferenceModel(PerInstancePreferenceModel, PureDjangoModel): - """Preference model to hold pereferences valid for a person""" + """Preference model to hold pereferences valid for a person.""" instance = models.ForeignKey(Person, on_delete=models.CASCADE) @@ -666,7 +674,7 @@ class PersonPreferenceModel(PerInstancePreferenceModel, PureDjangoModel): class GroupPreferenceModel(PerInstancePreferenceModel, PureDjangoModel): - """Preference model to hold pereferences valid for members of a group""" + """Preference model to hold pereferences valid for members of a group.""" instance = models.ForeignKey(Group, on_delete=models.CASCADE) diff --git a/aleksis/core/registries.py b/aleksis/core/registries.py index 7a0221582b63aa0433b9c6a288ab3d6463fdb502..bccc9e57590d113641ac14534a1bfaabbcc5e1e0 100644 --- a/aleksis/core/registries.py +++ b/aleksis/core/registries.py @@ -1,22 +1,22 @@ -"""Custom registries for some preference containers""" +"""Custom registries for some preference containers.""" from dynamic_preferences.registries import PerInstancePreferenceRegistry class SitePreferenceRegistry(PerInstancePreferenceRegistry): - """Registry for preferences valid for a site""" + """Registry for preferences valid for a site.""" pass class PersonPreferenceRegistry(PerInstancePreferenceRegistry): - """Registry for preferences valid for a person""" + """Registry for preferences valid for a person.""" pass class GroupPreferenceRegistry(PerInstancePreferenceRegistry): - """Registry for preferences valid for members of a group""" + """Registry for preferences valid for members of a group.""" pass diff --git a/aleksis/core/search_indexes.py b/aleksis/core/search_indexes.py index 397bce3aab0de5ad3a3a20e68774e8eb74ed29c5..7583a774eaadebb1cbfdb35d08dc0ddcfb355eea 100644 --- a/aleksis/core/search_indexes.py +++ b/aleksis/core/search_indexes.py @@ -3,12 +3,12 @@ from .util.search import Indexable, SearchIndex class PersonIndex(SearchIndex, Indexable): - """Haystack index for searching persons""" + """Haystack index for searching persons.""" model = Person class GroupIndex(SearchIndex, Indexable): - """Haystack index for searching groups""" + """Haystack index for searching groups.""" model = Group diff --git a/aleksis/core/tasks.py b/aleksis/core/tasks.py index 09031dff5cee1e8417fcaf94b4d361af08b32ab5..f391fe05bd6dfd82eaf3e84ddb222bf322881b5f 100644 --- a/aleksis/core/tasks.py +++ b/aleksis/core/tasks.py @@ -17,7 +17,7 @@ def send_notification(notification: int, resend: bool = False) -> None: @celery_optional def backup_data() -> None: - """Backup database and media using django-dbbackup""" + """Backup database and media using django-dbbackup.""" # Assemble command-line options for dbbackup management command db_options = "-z " * settings.DBBACKUP_COMPRESS_DB + "-e" * settings.DBBACKUP_ENCRYPT_DB media_options = ( diff --git a/aleksis/core/templatetags/dashboard.py b/aleksis/core/templatetags/dashboard.py index 79cf15f23c851e13576546b8c87e6f0fd29562b2..b2c1541e8fedf08162d795f0224dce9bc7bfbbd6 100644 --- a/aleksis/core/templatetags/dashboard.py +++ b/aleksis/core/templatetags/dashboard.py @@ -5,7 +5,7 @@ register = Library() @register.simple_tag def include_widget(widget) -> dict: - """Render a template with context from a defined widget""" + """Render a template with context from a defined widget.""" template = loader.get_template(widget.get_template()) context = widget.get_context() diff --git a/aleksis/core/templatetags/data_helpers.py b/aleksis/core/templatetags/data_helpers.py index 5bed2efb95843fcca122d6c507ec67e5b55c848a..19f286434db34eb9e74b88950b8d87ee854959ef 100644 --- a/aleksis/core/templatetags/data_helpers.py +++ b/aleksis/core/templatetags/data_helpers.py @@ -7,7 +7,7 @@ register = template.Library() @register.filter def get_dict(value: Any, arg: Any) -> Any: - """Gets an attribute of an object dynamically from a string name""" + """Get an attribute of an object dynamically from a string name.""" if hasattr(value, str(arg)): return getattr(value, arg) elif hasattr(value, "keys") and arg in value.keys(): diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py index 33af811d655e7eb35399f12dfb6ce27efc2ac6d6..d7cbb67f104f11ed8c5179266528fe2d4345a403 100644 --- a/aleksis/core/util/apps.py +++ b/aleksis/core/util/apps.py @@ -50,13 +50,13 @@ class AppConfig(django.apps.AppConfig): @classmethod def get_name(cls): - """Get name of application package""" + """Get name of application package.""" return getattr(cls, "verbose_name", cls.name) # TODO Try getting from distribution if not set @classmethod def get_version(cls): - """Get version of application package""" + """Get version of application package.""" try: from .. import __version__ # noqa except ImportError: @@ -66,7 +66,7 @@ class AppConfig(django.apps.AppConfig): @classmethod def get_licence(cls) -> Tuple: - """Get tuple of licence information of application package""" + """Get tuple of licence information of application package.""" # Get string representation of licence in SPDX format licence = getattr(cls, "licence", None) @@ -118,13 +118,13 @@ class AppConfig(django.apps.AppConfig): @classmethod def get_urls(cls): - """Get list of URLs for this application package""" + """Get list of URLs for this application package.""" return getattr(cls, "urls", {}) # TODO Try getting from distribution if not set @classmethod def get_copyright(cls) -> Sequence[Tuple[str, str, str]]: - """Get copyright information tuples for application package""" + """Get copyright information tuples for application package.""" copyrights = getattr(cls, "copyright", tuple()) copyrights_processed = [] @@ -152,7 +152,7 @@ class AppConfig(django.apps.AppConfig): new_value: Optional[Any] = None, **kwargs, ) -> None: - """Called on every app instance if a dynamic preference changes, and once on startup + """Called on every app instance if a dynamic preference changes, and once on startup. By default, it does nothing. """ @@ -168,7 +168,7 @@ class AppConfig(django.apps.AppConfig): apps: django.apps.registry.Apps, **kwargs, ) -> None: - """Called on every app instance before its models are migrated + """Called on every app instance before its models are migrated. By default, it does nothing. """ @@ -184,7 +184,7 @@ class AppConfig(django.apps.AppConfig): apps: django.apps.registry.Apps, **kwargs, ) -> None: - """Called on every app instance after its models have been migrated + """Called on every app instance after its models have been migrated. By default, asks all models to do maintenance on their default data. """ @@ -193,7 +193,7 @@ class AppConfig(django.apps.AppConfig): def user_logged_in( self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs ) -> None: - """Called after a user logged in + """Called after a user logged in. By default, it does nothing. """ @@ -202,7 +202,7 @@ class AppConfig(django.apps.AppConfig): def user_logged_out( self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs ) -> None: - """Called after a user logged out + """Called after a user logged out. By default, it does nothing. """ diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index 0c44061371fcde86e584f612d9ca5c76b97bba67..b24f48c8cbf598ae52377348e93bcd882d3efd4a 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -10,14 +10,13 @@ from uuid import uuid4 from django.conf import settings from django.db.models import Model from django.http import HttpRequest -from django.shortcuts import get_object_or_404 from django.utils import timezone from django.utils.functional import lazy from django.shortcuts import get_object_or_404 def copyright_years(years: Sequence[int], seperator: str = ", ", joiner: str = "–") -> str: - """Takes a sequence of integegers and produces a string with ranges + """Takes a sequence of integegers and produces a string with ranges. >>> copyright_years([1999, 2000, 2001, 2005, 2007, 2008, 2009]) '1999–2001, 2005, 2007–2009' @@ -35,7 +34,7 @@ def copyright_years(years: Sequence[int], seperator: str = ", ", joiner: str = " def dt_show_toolbar(request: HttpRequest) -> bool: - """Helper to determin if Django debug toolbar should be displayed + """Helper to determin if Django debug toolbar should be displayed. Extends the default behaviour by enabling DJDT for superusers independent of source IP. @@ -67,7 +66,8 @@ def get_app_packages() -> Sequence[str]: def merge_app_settings( setting: str, original: Union[dict, list], deduplicate: bool = False ) -> Union[dict, list]: - """Get a named settings constant from all apps and merge it into the original. + """ + Get a named settings constant from all apps and merge it into the original. To use this, add a settings.py file to the app, in the same format as Django's main settings.py. @@ -100,14 +100,16 @@ def merge_app_settings( def get_site_preferences(): - """Get the preferences manager of the current site""" + """Get the preferences manager of the current site.""" from django.contrib.sites.models import Site # noqa return Site.objects.get_current().preferences def lazy_preference(section: str, name: str) -> Callable[[str, str], Any]: - """Lazily get a config value from dynamic preferences. Useful to bind preferences + """Lazily get a config value from dynamic preferences. + + Useful to bind preferences to other global settings to make them available to third-party apps that are not aware of dynamic preferences. """ @@ -122,7 +124,7 @@ def lazy_preference(section: str, name: str) -> Callable[[str, str], Any]: def lazy_get_favicon_url( title: str, size: int, rel: str, default: Optional[str] = None ) -> Callable[[str, str], Any]: - """Lazily get the URL to a favicon image""" + """Lazily get the URL to a favicon image.""" def _get_favicon_url(size: int, rel: str) -> Any: from favicon.models import Favicon # noqa @@ -137,7 +139,7 @@ def lazy_get_favicon_url( def is_impersonate(request: HttpRequest) -> bool: - """Check whether the user was impersonated by an admin""" + """Check whether the user was impersonated by an admin.""" if hasattr(request, "user"): return getattr(request.user, "is_impersonate", False) else: @@ -145,8 +147,9 @@ def is_impersonate(request: HttpRequest) -> bool: def has_person(obj: Union[HttpRequest, Model]) -> bool: - """Check wehether a model object has a person attribute linking it to a Person - object. The passed object can also be a HttpRequest object, in which case its + """Check wehether a model object has a person attribute linking it to a Person object. + + The passed object can also be a HttpRequest object, in which case its associated User object is unwrapped and tested. """ if isinstance(obj, HttpRequest): @@ -186,7 +189,7 @@ def celery_optional(orig: Callable) -> Callable: def path_and_rename(instance, filename: str, upload_to: str = "files") -> str: - """Updates path of an uploaded file and renames it to a random UUID in Django FileField""" + """Updates path of an uploaded file and renames it to a random UUID in Django FileField.""" _, ext = os.path.splitext(filename) # set filename as random string @@ -200,7 +203,7 @@ def path_and_rename(instance, filename: str, upload_to: str = "files") -> str: def custom_information_processor(request: HttpRequest) -> dict: - """Provides custom information in all templates""" + """Provides custom information in all templates.""" from ..models import CustomMenu return { @@ -209,14 +212,14 @@ def custom_information_processor(request: HttpRequest) -> dict: def now_tomorrow() -> datetime: - """Return current time tomorrow""" + """Return current time tomorrow.""" return timezone.now() + timedelta(days=1) def objectgetter_optional( model: Model, default: Optional[Any] = None, default_eval: bool = False ) -> Callable[[HttpRequest, Optional[int]], Model]: - """Get an object by pk, defaulting to None""" + """Get an object by pk, defaulting to None.""" def get_object(request: HttpRequest, id_: Optional[int] = None) -> Model: if id_ is not None: return get_object_or_404(model, pk=id_) diff --git a/aleksis/core/util/messages.py b/aleksis/core/util/messages.py index 57a137507b48820fafd4199eb3deb18d8c1861c6..bd125d1cb46b54d890dd80850627d06406f153f2 100644 --- a/aleksis/core/util/messages.py +++ b/aleksis/core/util/messages.py @@ -8,11 +8,11 @@ from django.http import HttpRequest def add_message( request: Optional[HttpRequest], level: int, message: str, **kwargs ) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to DEBUG level. - """ + """ if request: return messages.add_message(request, level, message, **kwargs) else: @@ -20,45 +20,45 @@ def add_message( def debug(request: Optional[HttpRequest], message: str, **kwargs) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to DEBUG level. - """ + """ return add_message(request, messages.DEBUG, message, **kwargs) def info(request: Optional[HttpRequest], message: str, **kwargs) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to INFO level. - """ + """ return add_message(request, messages.INFO, message, **kwargs) def success(request: Optional[HttpRequest], message: str, **kwargs) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to SUCCESS level. - """ + """ return add_message(request, messages.SUCCESS, message, **kwargs) def warning(request: Optional[HttpRequest], message: str, **kwargs) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to WARNING level. - """ + """ return add_message(request, messages.WARNING, message, **kwargs) def error(request: Optional[HttpRequest], message: str, **kwargs) -> Optional[Any]: - """ Add a message to either Django's message framework, if called from a web request, + """ Add a message to either Django's message framework, if called from a web request, or to the default logger. Default to ERROR level. - """ + """ return add_message(request, messages.ERROR, message, **kwargs) diff --git a/aleksis/core/util/notifications.py b/aleksis/core/util/notifications.py index 9698841d062e8428ba8c922681d665bae77abeb3..f2887dea1644c43eb029ff3f075aadb491fbb2d0 100644 --- a/aleksis/core/util/notifications.py +++ b/aleksis/core/util/notifications.py @@ -1,4 +1,4 @@ -"""Utility code for notification system""" +"""Utility code for notification system.""" from typing import Sequence, Union diff --git a/aleksis/core/util/predicates.py b/aleksis/core/util/predicates.py index ceb03c68c0619bef76aee1a9439a40bfb9078cc5..f57b55620e2f7d32136f7b96c1fc869e245ca757 100644 --- a/aleksis/core/util/predicates.py +++ b/aleksis/core/util/predicates.py @@ -12,24 +12,24 @@ from .core_helpers import has_person as has_person_helper def permission_validator(request: HttpRequest, perm: str) -> bool: - """Checks whether the request user has a permission""" + """Checks whether the request user has a permission.""" if request.user: return request.user.has_perm(perm) return False def check_global_permission(user: User, perm: str) -> bool: - """Checks whether a user has a global permission""" + """Checks whether a user has a global permission.""" return ModelBackend().has_perm(user, perm) def check_object_permission(user: User, perm: str, obj: Model) -> bool: - """Checks whether a user has a permission on a object""" + """Checks whether a user has a permission on a object.""" return ObjectPermissionBackend().has_perm(user, perm, obj) def has_global_perm(perm: str): - """Builds predicate which checks whether a user has a global permission""" + """Builds predicate which checks whether a user has a global permission.""" name = f"has_global_perm:{perm}" @predicate(name) @@ -40,7 +40,7 @@ def has_global_perm(perm: str): def has_object_perm(perm: str): - """Builds predicate which checks whether a user has a permission on a object""" + """Builds predicate which checks whether a user has a permission on a object.""" name = f"has_global_perm:{perm}" @predicate(name) @@ -53,8 +53,8 @@ def has_object_perm(perm: str): def has_any_object(perm: str, klass): - """Build predicate which checks whether a user has access - to objects with the provided permission + """ + Build predicate which checks whether a user has access to objects with the provided permission. """ name = f"has_any_object:{perm}" @@ -68,25 +68,26 @@ def has_any_object(perm: str, klass): @predicate def has_person(user: User) -> bool: - """Predicate which checks whether a user has a linked person""" + """Predicate which checks whether a user has a linked person.""" return has_person_helper(user) @predicate def is_current_person(user: User, obj: Model) -> bool: - """Predicate which checks if the provided object is the person linked to the user object""" + """Predicate which checks if the provided object is the person linked to the user object.""" return user.person == obj @predicate def is_group_owner(user: User, group: Group) -> bool: - """Predicate which checks if the user is a owner of the provided group""" + """Predicate which checks if the user is a owner of the provided group.""" return group.owners.filter(owners=user.person).exists() @predicate def is_notification_recipient(user: User, obj: Model) -> bool: - """Predicate which checks whether the recipient of the - notification a user wants to mark read is this user + """ + Predicate which checks whether the recipient of the + notification a user wants to mark read is this user. """ return user == obj.recipient.user diff --git a/aleksis/core/util/sass_helpers.py b/aleksis/core/util/sass_helpers.py index c28d037e17d0184506f7fb3fd21819fb7cb3aef6..cff75a936f81801eeeae1839bdc8d59eaef739cd 100644 --- a/aleksis/core/util/sass_helpers.py +++ b/aleksis/core/util/sass_helpers.py @@ -1,4 +1,4 @@ -"""Helpers for SASS/SCSS compilation""" +"""Helpers for SASS/SCSS compilation.""" import os from glob import glob @@ -12,7 +12,7 @@ from .core_helpers import get_site_preferences def get_colour(html_colour: str) -> SassColor: - """Get a SASS colour object from an HTML colour string""" + """Get a SASS colour object from an HTML colour string.""" rgb = web2hex(html_colour, force_long=True)[1:] r, g, b = int(rgb[0:2], 16), int(rgb[2:4], 16), int(rgb[4:6], 16) @@ -20,12 +20,12 @@ def get_colour(html_colour: str) -> SassColor: def get_preference(section: str, name: str) -> str: - """Get a preference from dynamic-preferences""" + """Get a preference from dynamic-preferences.""" return get_site_preferences()[f"{section}__{name}"] def clean_scss(*args, **kwargs) -> None: - """Unlink compiled CSS (i.e. cache invalidation)""" + """Unlink compiled CSS (i.e. cache invalidation).""" for source_map in glob(os.path.join(settings.STATIC_ROOT, "*.css.map")): try: os.unlink(source_map) diff --git a/aleksis/core/util/search.py b/aleksis/core/util/search.py index dd8fe793162c9fdde33cdb0803754e47e415dc35..437887ae6412c0e3f8b493908346be8315b69481 100644 --- a/aleksis/core/util/search.py +++ b/aleksis/core/util/search.py @@ -12,7 +12,7 @@ else: class SearchIndex(BaseSearchIndex): - """ Base class for search indexes on AlekSIS models + """ Base class for search indexes on AlekSIS models. It provides a default document field caleld text and exects the related model in the model attribute. diff --git a/aleksis/core/views.py b/aleksis/core/views.py index cfe22442f3109ba8280abc5360efb9001a931d68..d6387901a1a4e2561471a7d6f3b45cce7b923e6a 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -41,7 +41,7 @@ from .util.core_helpers import objectgetter_optional @permission_required("core.view_dashboard") def index(request: HttpRequest) -> HttpResponse: - """Dashboard""" + """View for dashboard.""" context = {} activities = request.user.person.activities.all()[:5] @@ -65,12 +65,12 @@ def index(request: HttpRequest) -> HttpResponse: def offline(request: HttpRequest) -> HttpResponse: - """Offline message for PWA""" + """Offline message for PWA.""" return render(request, "core/offline.html") def about(request: HttpRequest) -> HttpResponse: - """About page listing all apps""" + """About page listing all apps.""" context = {} context["app_configs"] = list( @@ -82,7 +82,7 @@ def about(request: HttpRequest) -> HttpResponse: @permission_required("core.view_persons") def persons(request: HttpRequest) -> HttpResponse: - """List view listing all persons""" + """List view listing all persons.""" context = {} # Get all persons @@ -102,7 +102,7 @@ def persons(request: HttpRequest) -> HttpResponse: "core.view_person", fn=objectgetter_optional(Person, "request.user.person", True) ) def person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: - """Detail view for one person; defaulting to logged-in person""" + """Detail view for one person; defaulting to logged-in person.""" context = {} person = objectgetter_optional(Person, "request.user.person", True)(request, id_) @@ -121,7 +121,7 @@ def person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: @permission_required("core.view_group", fn=objectgetter_optional(Group, None, False)) def group(request: HttpRequest, id_: int) -> HttpResponse: - """Detail view for one group""" + """Detail view for one group.""" context = {} group = objectgetter_optional(Group, None, False)(request, id_) @@ -151,7 +151,7 @@ def group(request: HttpRequest, id_: int) -> HttpResponse: @permission_required("core.view_groups") def groups(request: HttpRequest) -> HttpResponse: - """List view for listing all groups""" + """List view for listing all groups.""" context = {} # Get all groups @@ -167,7 +167,7 @@ def groups(request: HttpRequest) -> HttpResponse: @permission_required("core.link_persons_accounts") def persons_accounts(request: HttpRequest) -> HttpResponse: - """View allowing to batch-process linking of users to persons""" + """View allowing to batch-process linking of users to persons.""" context = {} # Get all persons @@ -187,7 +187,7 @@ def persons_accounts(request: HttpRequest) -> HttpResponse: @permission_required("core.assign_child_groups_to_groups") def groups_child_groups(request: HttpRequest) -> HttpResponse: - """View for batch-processing assignment from child groups to groups""" + """View for batch-processing assignment from child groups to groups.""" context = {} # Apply filter @@ -226,7 +226,7 @@ def groups_child_groups(request: HttpRequest) -> HttpResponse: "core.edit_person", fn=objectgetter_optional(Person, "request.user.person", True) ) def edit_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: - """Edit view for a single person, defaulting to logged-in person""" + """Edit view for a single person, defaulting to logged-in person.""" context = {} person = objectgetter_optional(Person, "request.user.person", True)(request, id_) @@ -256,7 +256,7 @@ def get_group_by_id(request: HttpRequest, id_: Optional[int] = None): @permission_required("core.edit_group", fn=objectgetter_optional(Group, None, False)) def edit_group(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: - """View to edit or create a group""" + """View to edit or create a group.""" context = {} group = objectgetter_optional(Group, None, False)(request, id_) @@ -284,14 +284,14 @@ def edit_group(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: @permission_required("core.manage_data") def data_management(request: HttpRequest) -> HttpResponse: - """View with special menu for data management""" + """View with special menu for data management.""" context = {} return render(request, "core/data_management.html", context) @permission_required("core.view_system_status") def system_status(request: HttpRequest) -> HttpResponse: - """View giving information about the system status""" + """View giving information about the system status.""" context = {} return render(request, "core/system_status.html", context) @@ -301,7 +301,7 @@ def system_status(request: HttpRequest) -> HttpResponse: "core.mark_notification_as_read", fn=objectgetter_optional(Notification, None, False) ) def notification_mark_read(request: HttpRequest, id_: int) -> HttpResponse: - """Mark a notification read""" + """Mark a notification read.""" notification = objectgetter_optional(Notification, None, False)(request, id_) notification.read = True @@ -313,7 +313,7 @@ def notification_mark_read(request: HttpRequest, id_: int) -> HttpResponse: @permission_required("core.view_announcements") def announcements(request: HttpRequest) -> HttpResponse: - """List view of announcements""" + """List view of announcements.""" context = {} # Get all announcements @@ -327,7 +327,7 @@ def announcements(request: HttpRequest) -> HttpResponse: "core.create_or_edit_announcement", fn=objectgetter_optional(Announcement, None, False) ) def announcement_form(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: - """View to create or edit an announcement""" + """View to create or edit an announcement.""" context = {} announcement = objectgetter_optional(Announcement, None, False)(request, id_) @@ -357,7 +357,7 @@ def announcement_form(request: HttpRequest, id_: Optional[int] = None) -> HttpRe "core.delete_announcement", fn=objectgetter_optional(Announcement, None, False) ) def delete_announcement(request: HttpRequest, id_: int) -> HttpResponse: - """View to delete an announcement""" + """View to delete an announcement.""" if request.method == "POST": announcement = objectgetter_optional(Announcement, None, False)(request, id_) announcement.delete() @@ -368,7 +368,7 @@ def delete_announcement(request: HttpRequest, id_: int) -> HttpResponse: @permission_required("core.search") def searchbar_snippets(request: HttpRequest) -> HttpResponse: - """View to return HTML snippet with searchbar autocompletion results""" + """View to return HTML snippet with searchbar autocompletion results.""" query = request.GET.get("q", "") limit = int(request.GET.get("limit", "5")) @@ -379,7 +379,7 @@ def searchbar_snippets(request: HttpRequest) -> HttpResponse: class PermissionSearchView(PermissionRequiredMixin, SearchView): - """Wrapper to apply permission to haystack's search view""" + """Wrapper to apply permission to haystack's search view.""" permission_required = "core.search" def create_response(self): @@ -395,7 +395,7 @@ def preferences( pk: Optional[int] = None, section: Optional[str] = None, ) -> HttpResponse: - """View for changing preferences""" + """View for changing preferences.""" context = {} # Decide which registry to use and check preferences