From abd5c4b5479349ccd6e89a451da90ad3439b1065 Mon Sep 17 00:00:00 2001 From: Dominik George <dominik.george@teckids.org> Date: Fri, 13 Dec 2019 00:21:39 +0100 Subject: [PATCH] Fully auto-reformat using black This finalises the migration to the black code style by applying double-quotes. --- biscuit/core/__init__.py | 6 +- biscuit/core/anonymizers.py | 26 +- biscuit/core/apps.py | 6 +- biscuit/core/cronjobs.py | 6 +- biscuit/core/forms.py | 104 ++--- biscuit/core/menus.py | 150 ++++---- biscuit/core/mixins.py | 6 +- biscuit/core/models.py | 102 ++--- biscuit/core/settings.py | 358 +++++++++--------- biscuit/core/signals.py | 2 +- biscuit/core/tables.py | 12 +- biscuit/core/templatetags/data_helpers.py | 2 +- biscuit/core/tests/browser/test_selenium.py | 26 +- biscuit/core/tests/models/test_person.py | 4 +- .../tests/templatetags/test_data_helpers.py | 8 +- biscuit/core/tests/views/test_account.py | 30 +- biscuit/core/urls.py | 58 +-- biscuit/core/util/apps.py | 2 +- biscuit/core/util/core_helpers.py | 16 +- biscuit/core/util/sass_helpers.py | 2 +- biscuit/core/views.py | 86 ++--- biscuit/core/wsgi.py | 2 +- 22 files changed, 507 insertions(+), 507 deletions(-) diff --git a/biscuit/core/__init__.py b/biscuit/core/__init__.py index ef696833a..4d87b0413 100644 --- a/biscuit/core/__init__.py +++ b/biscuit/core/__init__.py @@ -1,8 +1,8 @@ import pkg_resources try: - __version__ = pkg_resources.get_distribution('BiscuIT-ng').version + __version__ = pkg_resources.get_distribution("BiscuIT-ng").version except Exception: - __version__ = 'unknown' + __version__ = "unknown" -default_app_config = 'biscuit.core.apps.CoreConfig' +default_app_config = "biscuit.core.apps.CoreConfig" diff --git a/biscuit/core/anonymizers.py b/biscuit/core/anonymizers.py index 9a063a819..3a9094c41 100644 --- a/biscuit/core/anonymizers.py +++ b/biscuit/core/anonymizers.py @@ -7,20 +7,20 @@ class PersonAnonymizer(BaseAnonymizer): model = Person attributes = [ - ('first_name', faker.first_name), - ('last_name', faker.last_name), - ('additional_name', ''), - ('short_name', lambda **kwargs: faker.pystr(min_chars=3, max_chars=5, **kwargs)), - ('street', faker.street_name), - ('housenumber', faker.building_number), - ('postal_code', faker.postcode), - ('place', faker.city), - ('phone_number', ''), - ('mobile_number', ''), - ('email', faker.email), + ("first_name", faker.first_name), + ("last_name", faker.last_name), + ("additional_name", ""), + ("short_name", lambda **kwargs: faker.pystr(min_chars=3, max_chars=5, **kwargs)), + ("street", faker.street_name), + ("housenumber", faker.building_number), + ("postal_code", faker.postcode), + ("place", faker.city), + ("phone_number", ""), + ("mobile_number", ""), + ("email", faker.email), ( - 'date_of_birth', + "date_of_birth", lambda **kwargs: faker.date_of_birth(minimum_age=8, maximum_age=66, **kwargs), ), - ('photo', ''), + ("photo", ""), ] diff --git a/biscuit/core/apps.py b/biscuit/core/apps.py index ef62cfb30..5c5b3093b 100644 --- a/biscuit/core/apps.py +++ b/biscuit/core/apps.py @@ -5,9 +5,9 @@ from .signals import clean_scss class CoreConfig(AppConfig): - name = 'biscuit.core' - verbose_name = 'BiscuIT - The Free School Information System' + name = "biscuit.core" + verbose_name = "BiscuIT - The Free School Information System" def ready(self) -> None: clean_scss() - post_save.connect(clean_scss, sender=apps.get_model('dbsettings', 'Setting')) + post_save.connect(clean_scss, sender=apps.get_model("dbsettings", "Setting")) diff --git a/biscuit/core/cronjobs.py b/biscuit/core/cronjobs.py index 4c9d81ebb..66e6c069c 100644 --- a/biscuit/core/cronjobs.py +++ b/biscuit/core/cronjobs.py @@ -10,8 +10,8 @@ class Backup(CronJobBase): schedule = Schedule( run_at_times=RUN_AT_TIMES, retry_after_failure_mins=RETRY_AFTER_FAILURE_MINS ) - code = 'biscuit.core.Backup' + code = "biscuit.core.Backup" def do(self): - management.call_command('dbbackup', '-z') - management.call_command('mediabackup', '-z') + management.call_command("dbbackup", "-z") + management.call_command("mediabackup", "-z") diff --git a/biscuit/core/forms.py b/biscuit/core/forms.py index 119730401..e032df5f7 100644 --- a/biscuit/core/forms.py +++ b/biscuit/core/forms.py @@ -9,36 +9,36 @@ from .models import Person, Group, School, SchoolTerm class PersonAccountForm(forms.ModelForm): class Meta: model = Person - fields = ['last_name', 'first_name', 'user'] - widgets = {'user': Select2Widget} + fields = ["last_name", "first_name", "user"] + widgets = {"user": Select2Widget} new_user = forms.CharField(required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['first_name'].disabled = True - self.fields['last_name'].disabled = True + self.fields["first_name"].disabled = True + self.fields["last_name"].disabled = True def clean(self) -> None: User = get_user_model() - if self.cleaned_data.get('new_user', None): - if self.cleaned_data.get('user', None): + if self.cleaned_data.get("new_user", None): + if self.cleaned_data.get("user", None): self.add_error( - 'new_user', - _('You cannot set a new username when also selecting an existing user.'), + "new_user", + _("You cannot set a new username when also selecting an existing user."), ) - elif User.objects.filter(username=self.cleaned_data['new_user']).exists(): - self.add_error('new_user', _('This username is already in use.')) + elif User.objects.filter(username=self.cleaned_data["new_user"]).exists(): + self.add_error("new_user", _("This username is already in use.")) else: new_user_obj = User.objects.create_user( - self.cleaned_data['new_user'], + self.cleaned_data["new_user"], self.instance.email, first_name=self.instance.first_name, last_name=self.instance.last_name, ) - self.cleaned_data['user'] = new_user_obj + self.cleaned_data["user"] = new_user_obj PersonsAccountsFormSet = forms.modelformset_factory( @@ -50,73 +50,73 @@ class EditPersonForm(forms.ModelForm): class Meta: model = Person fields = [ - 'user', - 'is_active', - 'first_name', - 'last_name', - 'additional_name', - 'short_name', - 'street', - 'housenumber', - 'postal_code', - 'place', - 'phone_number', - 'mobile_number', - 'email', - 'date_of_birth', - 'sex', - 'photo', - 'photo_cropping', + "user", + "is_active", + "first_name", + "last_name", + "additional_name", + "short_name", + "street", + "housenumber", + "postal_code", + "place", + "phone_number", + "mobile_number", + "email", + "date_of_birth", + "sex", + "photo", + "photo_cropping", ] - widgets = {'user': Select2Widget} + widgets = {"user": Select2Widget} new_user = forms.CharField( - required=False, label=_('New user'), help_text=_('Create a new account') + required=False, label=_("New user"), help_text=_("Create a new account") ) def clean(self) -> None: User = get_user_model() - if self.cleaned_data.get('new_user', None): - if self.cleaned_data.get('user', None): + if self.cleaned_data.get("new_user", None): + if self.cleaned_data.get("user", None): self.add_error( - 'new_user', - _('You cannot set a new username when also selecting an existing user.'), + "new_user", + _("You cannot set a new username when also selecting an existing user."), ) - elif User.objects.filter(username=self.cleaned_data['new_user']).exists(): - self.add_error('new_user', _('This username is already in use.')) + elif User.objects.filter(username=self.cleaned_data["new_user"]).exists(): + self.add_error("new_user", _("This username is already in use.")) else: new_user_obj = User.objects.create_user( - self.cleaned_data['new_user'], + self.cleaned_data["new_user"], self.instance.email, first_name=self.instance.first_name, last_name=self.instance.last_name, ) - self.cleaned_data['user'] = new_user_obj + self.cleaned_data["user"] = new_user_obj class EditGroupForm(forms.ModelForm): class Meta: model = Group - fields = ['name', 'short_name', 'members', 'owners', 'parent_groups'] + fields = ["name", "short_name", "members", "owners", "parent_groups"] widgets = { - 'members': ModelSelect2MultipleWidget( + "members": ModelSelect2MultipleWidget( search_fields=[ - 'first_name__icontains', - 'last_name__icontains', - 'short_name__icontains', + "first_name__icontains", + "last_name__icontains", + "short_name__icontains", ] ), - 'owners': ModelSelect2MultipleWidget( + "owners": ModelSelect2MultipleWidget( search_fields=[ - 'first_name__icontains', - 'last_name__icontains', - 'short_name__icontains', + "first_name__icontains", + "last_name__icontains", + "short_name__icontains", ] ), - 'parent_groups': ModelSelect2MultipleWidget( - search_fields=['name__icontains', 'short_name__icontains'] + "parent_groups": ModelSelect2MultipleWidget( + search_fields=["name__icontains", "short_name__icontains"] ), } @@ -124,10 +124,10 @@ class EditGroupForm(forms.ModelForm): class EditSchoolForm(forms.ModelForm): class Meta: model = School - fields = ['name', 'name_official', 'logo', 'logo_cropping'] + fields = ["name", "name_official", "logo", "logo_cropping"] class EditTermForm(forms.ModelForm): class Meta: model = SchoolTerm - fields = ['caption', 'date_start', 'date_end'] + fields = ["caption", "date_start", "date_end"] diff --git a/biscuit/core/menus.py b/biscuit/core/menus.py index f4afc5577..cbb745d05 100644 --- a/biscuit/core/menus.py +++ b/biscuit/core/menus.py @@ -2,125 +2,125 @@ from django.conf import settings from django.utils.translation import ugettext_lazy as _ MENUS = { - 'NAV_MENU_CORE': [ + "NAV_MENU_CORE": [ { - 'name': _('Account'), - 'url': '#', - 'root': True, - 'submenu': [ + "name": _("Account"), + "url": "#", + "root": True, + "submenu": [ { - 'name': _('Stop impersonation'), - 'url': 'impersonate-stop', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'biscuit.core.util.core_helpers.is_impersonate', + "name": _("Stop impersonation"), + "url": "impersonate-stop", + "validators": [ + "menu_generator.validators.is_authenticated", + "biscuit.core.util.core_helpers.is_impersonate", ], }, { - 'name': _('Login'), - 'url': settings.LOGIN_URL, - 'validators': ['menu_generator.validators.is_anonymous'], + "name": _("Login"), + "url": settings.LOGIN_URL, + "validators": ["menu_generator.validators.is_anonymous"], }, { - 'name': _('Logout'), - 'url': 'logout', - 'validators': ['menu_generator.validators.is_authenticated'], + "name": _("Logout"), + "url": "logout", + "validators": ["menu_generator.validators.is_authenticated"], }, { - 'name': _('Two factor auth'), - 'url': 'two_factor:profile', - 'validators': [ - 'menu_generator.validators.is_authenticated', - lambda request: 'two_factor' in settings.INSTALLED_APPS, + "name": _("Two factor auth"), + "url": "two_factor:profile", + "validators": [ + "menu_generator.validators.is_authenticated", + lambda request: "two_factor" in settings.INSTALLED_APPS, ], }, ], }, { - 'name': _('Admin'), - 'url': '#', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("Admin"), + "url": "#", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], - 'submenu': [ + "submenu": [ { - 'name': _('Data management'), - 'url': 'data_management', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("Data management"), + "url": "data_management", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], }, { - 'name': _('System status'), - 'url': 'system_status', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("System status"), + "url": "system_status", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], }, { - 'name': _('Impersonation'), - 'url': 'impersonate-list', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("Impersonation"), + "url": "impersonate-list", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], }, { - 'name': _('Manage school'), - 'url': 'school_management', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("Manage school"), + "url": "school_management", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], }, ], }, { - 'name': _('People'), - 'url': '#', - 'root': True, - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'biscuit.core.util.core_helpers.has_person', + "name": _("People"), + "url": "#", + "root": True, + "validators": [ + "menu_generator.validators.is_authenticated", + "biscuit.core.util.core_helpers.has_person", ], - 'submenu': [ + "submenu": [ { - 'name': _('Persons'), - 'url': 'persons', - 'validators': ['menu_generator.validators.is_authenticated'], + "name": _("Persons"), + "url": "persons", + "validators": ["menu_generator.validators.is_authenticated"], }, { - 'name': _('Groups'), - 'url': 'groups', - 'validators': ['menu_generator.validators.is_authenticated'], + "name": _("Groups"), + "url": "groups", + "validators": ["menu_generator.validators.is_authenticated"], }, { - 'name': _('Persons and accounts'), - 'url': 'persons_accounts', - 'validators': [ - 'menu_generator.validators.is_authenticated', - 'menu_generator.validators.is_superuser', + "name": _("Persons and accounts"), + "url": "persons_accounts", + "validators": [ + "menu_generator.validators.is_authenticated", + "menu_generator.validators.is_superuser", ], }, ], }, ], - 'FOOTER_MENU_CORE': [ + "FOOTER_MENU_CORE": [ { - 'name': _('BiscuIT Software'), - 'url': '#', - 'submenu': [ - {'name': _('Website'), 'url': 'https://biscuit.edugit.org/'}, - {'name': 'Teckids e.V.', 'url': 'https://www.teckids.org/'}, + "name": _("BiscuIT Software"), + "url": "#", + "submenu": [ + {"name": _("Website"), "url": "https://biscuit.edugit.org/"}, + {"name": "Teckids e.V.", "url": "https://www.teckids.org/"}, ], }, ], - 'DATA_MANAGEMENT_MENU': [], - 'SCHOOL_MANAGEMENT_MENU': [ - {'name': _('Edit school information'), 'url': 'edit_school_information',}, - {'name': _('Edit school term'), 'url': 'edit_school_term',}, + "DATA_MANAGEMENT_MENU": [], + "SCHOOL_MANAGEMENT_MENU": [ + {"name": _("Edit school information"), "url": "edit_school_information",}, + {"name": _("Edit school term"), "url": "edit_school_term",}, ], } diff --git a/biscuit/core/mixins.py b/biscuit/core/mixins.py index a5d535ac2..6db9ffe40 100644 --- a/biscuit/core/mixins.py +++ b/biscuit/core/mixins.py @@ -46,11 +46,11 @@ class ExtensibleModel(object): if name.isidentifier(): prop_name = name else: - raise ValueError('%s is not a valid name.' % name) + raise ValueError("%s is not a valid name." % name) # Verify that property name does not clash with other names in the class if hasattr(cls, prop_name): - raise ValueError('%s already used.' % prop_name) + raise ValueError("%s already used." % prop_name) # Add function wrapped in property decorator if we got here setattr(cls, prop_name, obj) @@ -80,4 +80,4 @@ class CRUDMixin(models.Model): return CRUDEvent.objects.filter( object_id=self.pk, content_type=content_type - ).select_related('user') + ).select_related("user") diff --git a/biscuit/core/models.py b/biscuit/core/models.py index a659136d9..eab46395f 100644 --- a/biscuit/core/models.py +++ b/biscuit/core/models.py @@ -12,17 +12,17 @@ from .mixins import ExtensibleModel class ThemeSettings(dbsettings.Group): - colour_primary = dbsettings.StringValue(default='#007bff') - colour_secondary = dbsettings.StringValue(default='#6c757d') - colour_success = dbsettings.StringValue(default='#28a745') - colour_info = dbsettings.StringValue(default='#17a2b8') - colour_warning = dbsettings.StringValue(default='#ffc107') - colour_danger = dbsettings.StringValue(default='#dc3545') - colour_light = dbsettings.StringValue(default='#f8f9fa') - colour_dark = dbsettings.StringValue(default='#343a40') + colour_primary = dbsettings.StringValue(default="#007bff") + colour_secondary = dbsettings.StringValue(default="#6c757d") + colour_success = dbsettings.StringValue(default="#28a745") + colour_info = dbsettings.StringValue(default="#17a2b8") + colour_warning = dbsettings.StringValue(default="#ffc107") + colour_danger = dbsettings.StringValue(default="#dc3545") + colour_light = dbsettings.StringValue(default="#f8f9fa") + colour_dark = dbsettings.StringValue(default="#343a40") -theme_settings = ThemeSettings('Global theme settings') +theme_settings = ThemeSettings("Global theme settings") class School(models.Model): @@ -32,22 +32,22 @@ class School(models.Model): currently logged-in user. """ - name = models.CharField(verbose_name=_('Name'), max_length=30) + name = models.CharField(verbose_name=_("Name"), max_length=30) name_official = models.CharField( - verbose_name=_('Official name'), + verbose_name=_("Official name"), max_length=200, - help_text=_('Official name of the school, e.g. as given by supervisory authority'), + help_text=_("Official name of the school, e.g. as given by supervisory authority"), ) - logo = ImageCropField(verbose_name=_('School logo'), blank=True, null=True) - logo_cropping = ImageRatioField('logo', '600x600', size_warning=True) + logo = ImageCropField(verbose_name=_("School logo"), blank=True, null=True) + logo_cropping = ImageRatioField("logo", "600x600", size_warning=True) @property def current_term(self): return SchoolTerm.objects.get(current=True) class Meta: - ordering = ['name', 'name_official'] + ordering = ["name", "name_official"] class SchoolTerm(models.Model): @@ -55,10 +55,10 @@ class SchoolTerm(models.Model): be linked to. """ - caption = models.CharField(verbose_name=_('Visible caption of the term'), max_length=30) + caption = models.CharField(verbose_name=_("Visible caption of the term"), max_length=30) - date_start = models.DateField(verbose_name=_('Effective start date of term'), null=True) - date_end = models.DateField(verbose_name=_('Effective end date of term'), null=True) + date_start = models.DateField(verbose_name=_("Effective start date of term"), null=True) + date_end = models.DateField(verbose_name=_("Effective end date of term"), null=True) current = models.NullBooleanField(default=None, unique=True) @@ -74,43 +74,43 @@ class Person(models.Model, ExtensibleModel): """ class Meta: - ordering = ['last_name', 'first_name'] + ordering = ["last_name", "first_name"] - SEX_CHOICES = [('f', _('female')), ('m', _('male'))] + SEX_CHOICES = [("f", _("female")), ("m", _("male"))] user = models.OneToOneField( - get_user_model(), on_delete=models.SET_NULL, blank=True, null=True, related_name='person' + get_user_model(), on_delete=models.SET_NULL, blank=True, null=True, related_name="person" ) - is_active = models.BooleanField(verbose_name=_('Is person active?'), default=True) + is_active = models.BooleanField(verbose_name=_("Is person active?"), default=True) - first_name = models.CharField(verbose_name=_('First name'), max_length=30) - last_name = models.CharField(verbose_name=_('Last name'), max_length=30) + first_name = models.CharField(verbose_name=_("First name"), max_length=30) + last_name = models.CharField(verbose_name=_("Last name"), max_length=30) additional_name = models.CharField( - verbose_name=_('Additional name(s)'), max_length=30, blank=True + verbose_name=_("Additional name(s)"), max_length=30, blank=True ) short_name = models.CharField( - verbose_name=_('Short name'), max_length=5, blank=True, null=True, unique=True + verbose_name=_("Short name"), max_length=5, blank=True, null=True, unique=True ) - street = models.CharField(verbose_name=_('Street'), max_length=30, blank=True) - housenumber = models.CharField(verbose_name=_('Street number'), max_length=10, blank=True) - postal_code = models.CharField(verbose_name=_('Postal code'), max_length=5, blank=True) - place = models.CharField(verbose_name=_('Place'), max_length=30, blank=True) + street = models.CharField(verbose_name=_("Street"), max_length=30, blank=True) + housenumber = models.CharField(verbose_name=_("Street number"), max_length=10, blank=True) + postal_code = models.CharField(verbose_name=_("Postal code"), max_length=5, blank=True) + place = models.CharField(verbose_name=_("Place"), max_length=30, blank=True) - phone_number = PhoneNumberField(verbose_name=_('Home phone'), blank=True) - mobile_number = PhoneNumberField(verbose_name=_('Mobile phone'), blank=True) + phone_number = PhoneNumberField(verbose_name=_("Home phone"), blank=True) + mobile_number = PhoneNumberField(verbose_name=_("Mobile phone"), blank=True) - email = models.EmailField(verbose_name=_('E-mail address'), blank=True) + email = models.EmailField(verbose_name=_("E-mail address"), blank=True) - date_of_birth = models.DateField(verbose_name=_('Date of birth'), blank=True, null=True) - sex = models.CharField(verbose_name=_('Sex'), max_length=1, choices=SEX_CHOICES, blank=True) + date_of_birth = models.DateField(verbose_name=_("Date of birth"), blank=True, null=True) + sex = models.CharField(verbose_name=_("Sex"), max_length=1, choices=SEX_CHOICES, blank=True) - photo = ImageCropField(verbose_name=_('Photo'), blank=True, null=True) - photo_cropping = ImageRatioField('photo', '600x800', size_warning=True) + photo = ImageCropField(verbose_name=_("Photo"), blank=True, null=True) + photo_cropping = ImageRatioField("photo", "600x800", size_warning=True) import_ref = models.CharField( - verbose_name=_('Reference ID of import source'), + verbose_name=_("Reference ID of import source"), max_length=64, blank=True, null=True, @@ -119,10 +119,10 @@ class Person(models.Model, ExtensibleModel): ) guardians = models.ManyToManyField( - 'self', verbose_name=_('Guardians / Parents'), symmetrical=False, related_name='children' + "self", verbose_name=_("Guardians / Parents"), symmetrical=False, related_name="children" ) - primary_group = models.ForeignKey('Group', models.SET_NULL, null=True) + primary_group = models.ForeignKey("Group", models.SET_NULL, null=True) @property def primary_group_short_name(self) -> Optional[str]: @@ -141,12 +141,12 @@ class Person(models.Model, ExtensibleModel): if it can't find one. """ - group, created = Group.objects.get_or_create(short_name=value, defaults={'name': value}) + group, created = Group.objects.get_or_create(short_name=value, defaults={"name": value}) self.primary_group = group @property def full_name(self) -> str: - return '%s, %s' % (self.last_name, self.first_name) + return "%s, %s" % (self.last_name, self.first_name) def __str__(self) -> str: return self.full_name @@ -158,21 +158,21 @@ class Group(models.Model, ExtensibleModel): """ class Meta: - ordering = ['short_name', 'name'] + ordering = ["short_name", "name"] - name = models.CharField(verbose_name=_('Long name of group'), max_length=60, unique=True) - short_name = models.CharField(verbose_name=_('Short name of group'), max_length=16, unique=True) + name = models.CharField(verbose_name=_("Long name of group"), max_length=60, unique=True) + short_name = models.CharField(verbose_name=_("Short name of group"), max_length=16, unique=True) - members = models.ManyToManyField('Person', related_name='member_of') - owners = models.ManyToManyField('Person', related_name='owner_of') + members = models.ManyToManyField("Person", related_name="member_of") + owners = models.ManyToManyField("Person", related_name="owner_of") parent_groups = models.ManyToManyField( - 'self', - related_name='child_groups', + "self", + related_name="child_groups", symmetrical=False, - verbose_name=_('Parent groups'), + verbose_name=_("Parent groups"), blank=True, ) def __str__(self) -> str: - return '%s (%s)' % (self.name, self.short_name) + return "%s (%s)" % (self.name, self.short_name) diff --git a/biscuit/core/settings.py b/biscuit/core/settings.py index 66441e714..b265e7de3 100644 --- a/biscuit/core/settings.py +++ b/biscuit/core/settings.py @@ -9,14 +9,14 @@ from easy_thumbnails.conf import Settings as thumbnail_settings from .util.core_helpers import get_app_packages -ENVVAR_PREFIX_FOR_DYNACONF = 'BISCUIT' -DIRS_FOR_DYNACONF = ['/etc/biscuit'] +ENVVAR_PREFIX_FOR_DYNACONF = "BISCUIT" +DIRS_FOR_DYNACONF = ["/etc/biscuit"] SETTINGS_FILE_FOR_DYNACONF = [] for directory in DIRS_FOR_DYNACONF: - SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, '*.ini')) - SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, '*.yaml')) - SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, '*.toml')) + SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.ini")) + SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.yaml")) + SETTINGS_FILE_FOR_DYNACONF += glob(os.path.join(directory, "*.toml")) _settings = LazySettings( ENVVAR_PREFIX_FOR_DYNACONF=ENVVAR_PREFIX_FOR_DYNACONF, @@ -27,136 +27,136 @@ _settings = LazySettings( BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = _settings.get('secret_key', 'DoNotUseInProduction') +SECRET_KEY = _settings.get("secret_key", "DoNotUseInProduction") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = _settings.get('maintenance.debug', False) -INTERNAL_IPS = _settings.get('maintenance.internal_ips', []) +DEBUG = _settings.get("maintenance.debug", False) +INTERNAL_IPS = _settings.get("maintenance.internal_ips", []) DEBUG_TOOLBAR_CONFIG = { - 'RENDER_PANELS': True, - 'SHOW_COLLAPSED': True, - 'JQUERY_URL': '', - 'SHOW_TOOLBAR_CALLBACK': 'biscuit.core.util.core_helpers.dt_show_toolbar', + "RENDER_PANELS": True, + "SHOW_COLLAPSED": True, + "JQUERY_URL": "", + "SHOW_TOOLBAR_CALLBACK": "biscuit.core.util.core_helpers.dt_show_toolbar", } -ALLOWED_HOSTS = _settings.get('http.allowed_hosts', []) +ALLOWED_HOSTS = _settings.get("http.allowed_hosts", []) # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django_global_request', - 'settings_context_processor', - 'sass_processor', - 'easyaudit', - 'dbbackup', - 'dbsettings', - 'django_cron', - 'bootstrap4', - 'fa', - 'django_any_js', - 'django_yarnpkg', - 'django_tables2', - 'easy_thumbnails', - 'image_cropping', - 'maintenance_mode', - 'menu_generator', - 'phonenumber_field', - 'debug_toolbar', - 'django_select2', - 'hattori', - 'django_otp.plugins.otp_totp', - 'django_otp.plugins.otp_static', - 'django_otp', - 'otp_yubikey', - 'biscuit.core', - 'impersonate', - 'two_factor', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_global_request", + "settings_context_processor", + "sass_processor", + "easyaudit", + "dbbackup", + "dbsettings", + "django_cron", + "bootstrap4", + "fa", + "django_any_js", + "django_yarnpkg", + "django_tables2", + "easy_thumbnails", + "image_cropping", + "maintenance_mode", + "menu_generator", + "phonenumber_field", + "debug_toolbar", + "django_select2", + "hattori", + "django_otp.plugins.otp_totp", + "django_otp.plugins.otp_static", + "django_otp", + "otp_yubikey", + "biscuit.core", + "impersonate", + "two_factor", ] INSTALLED_APPS += get_app_packages() STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'django_yarnpkg.finders.NodeModulesFinder', - 'sass_processor.finders.CssFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "django_yarnpkg.finders.NodeModulesFinder", + "sass_processor.finders.CssFinder", ] MIDDLEWARE = [ # 'django.middleware.cache.UpdateCacheMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django_global_request.middleware.GlobalRequestMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', - 'django_otp.middleware.OTPMiddleware', - 'impersonate.middleware.ImpersonateMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'easyaudit.middleware.easyaudit.EasyAuditMiddleware', - 'maintenance_mode.middleware.MaintenanceModeMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django_global_request.middleware.GlobalRequestMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", + "django_otp.middleware.OTPMiddleware", + "impersonate.middleware.ImpersonateMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "easyaudit.middleware.easyaudit.EasyAuditMiddleware", + "maintenance_mode.middleware.MaintenanceModeMiddleware", # 'django.middleware.cache.FetchFromCacheMiddleware' ] -ROOT_URLCONF = 'biscuit.core.urls' +ROOT_URLCONF = "biscuit.core.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'maintenance_mode.context_processors.maintenance_mode', - 'settings_context_processor.context_processors.settings', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "maintenance_mode.context_processors.maintenance_mode", + "settings_context_processor.context_processors.settings", ], }, }, ] THUMBNAIL_PROCESSORS = ( - 'image_cropping.thumbnail_processors.crop_corners', + "image_cropping.thumbnail_processors.crop_corners", ) + thumbnail_settings.THUMBNAIL_PROCESSORS # Already included by base template / Bootstrap IMAGE_CROPPING_JQUERY_URL = None -WSGI_APPLICATION = 'biscuit.core.wsgi.application' +WSGI_APPLICATION = "biscuit.core.wsgi.application" # Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': _settings.get('database.name', 'biscuit'), - 'USER': _settings.get('database.username', 'biscuit'), - 'PASSWORD': _settings.get('database.password', None), - 'HOST': _settings.get('database.host', '127.0.0.1'), - 'PORT': _settings.get('database.port', '5432'), - 'ATOMIC_REQUESTS': True, + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": _settings.get("database.name", "biscuit"), + "USER": _settings.get("database.username", "biscuit"), + "PASSWORD": _settings.get("database.password", None), + "HOST": _settings.get("database.host", "127.0.0.1"), + "PORT": _settings.get("database.port", "5432"), + "ATOMIC_REQUESTS": True, } } -if _settings.get('caching.memcached.enabled', True): +if _settings.get("caching.memcached.enabled", True): CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': _settings.get('caching.memcached.address', '127.0.0.1:11211'), + "default": { + "BACKEND": "django.core.cache.backends.memcached.MemcachedCache", + "LOCATION": _settings.get("caching.memcached.address", "127.0.0.1:11211"), } } @@ -164,57 +164,57 @@ if _settings.get('caching.memcached.enabled', True): # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',}, - {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',}, - {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',}, - {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',}, + {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",}, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",}, ] # Authentication backends are dynamically populated AUTHENTICATION_BACKENDS = [] -if _settings.get('ldap.uri', None): +if _settings.get("ldap.uri", None): # LDAP dependencies are not necessarily installed, so import them here import ldap # noqa from django_auth_ldap.config import LDAPSearch, GroupOfNamesType # noqa # Enable Django's integration to LDAP - AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend') + AUTHENTICATION_BACKENDS.append("django_auth_ldap.backend.LDAPBackend") - AUTH_LDAP_SERVER_URI = _settings.get('ldap.uri') + AUTH_LDAP_SERVER_URI = _settings.get("ldap.uri") # Optional: non-anonymous bind - if _settings.get('ldap.bind.dn', None): - AUTH_LDAP_BIND_DN = _settings.get('ldap.bind.dn') - AUTH_LDAP_BIND_PASSWORD = _settings.get('ldap.bind.password') + if _settings.get("ldap.bind.dn", None): + AUTH_LDAP_BIND_DN = _settings.get("ldap.bind.dn") + AUTH_LDAP_BIND_PASSWORD = _settings.get("ldap.bind.password") # Search attributes to find users by username AUTH_LDAP_USER_SEARCH = LDAPSearch( - _settings.get('ldap.users.base'), + _settings.get("ldap.users.base"), ldap.SCOPE_SUBTREE, - _settings.get('ldap.users.filter', '(uid=%(user)s)'), + _settings.get("ldap.users.filter", "(uid=%(user)s)"), ) # Mapping of LDAP attributes to Django model fields AUTH_LDAP_USER_ATTR_MAP = { - 'first_name': _settings.get('ldap.map.first_name', 'givenName'), - 'last_name': _settings.get('ldap.map.first_name', 'sn'), - 'email': _settings.get('ldap.map.email', 'mail'), + "first_name": _settings.get("ldap.map.first_name", "givenName"), + "last_name": _settings.get("ldap.map.first_name", "sn"), + "email": _settings.get("ldap.map.email", "mail"), } # Add ModelBckend last so all other backends get a chance # to verify passwords first -AUTHENTICATION_BACKENDS.append('django.contrib.auth.backends.ModelBackend') +AUTHENTICATION_BACKENDS.append("django.contrib.auth.backends.ModelBackend") # Internationalization # https://docs.djangoproject.com/en/2.1/topics/i18n/ LANGUAGES = [ - ('de', _('German')), - ('en', _('English')), + ("de", _("German")), + ("en", _("English")), ] -LANGUAGE_CODE = _settings.get('l10n.lang', 'en') -TIME_ZONE = _settings.get('l10n.tz', 'UTC') +LANGUAGE_CODE = _settings.get("l10n.lang", "en") +TIME_ZONE = _settings.get("l10n.tz", "UTC") USE_I18N = True USE_L10N = True USE_TZ = True @@ -223,108 +223,108 @@ USE_TZ = True # https://docs.djangoproject.com/en/2.1/howto/static-files/ -STATIC_URL = _settings.get('static.url', '/static/') -MEDIA_URL = _settings.get('media.url', '/media/') +STATIC_URL = _settings.get("static.url", "/static/") +MEDIA_URL = _settings.get("media.url", "/media/") -LOGIN_REDIRECT_URL = 'index' -LOGOUT_REDIRECT_URL = 'index' +LOGIN_REDIRECT_URL = "index" +LOGOUT_REDIRECT_URL = "index" -STATIC_ROOT = _settings.get('static.root', os.path.join(BASE_DIR, 'static')) -MEDIA_ROOT = _settings.get('media.root', os.path.join(BASE_DIR, 'media')) -NODE_MODULES_ROOT = _settings.get('node_modules.root', os.path.join(BASE_DIR, 'node_modules')) +STATIC_ROOT = _settings.get("static.root", os.path.join(BASE_DIR, "static")) +MEDIA_ROOT = _settings.get("media.root", os.path.join(BASE_DIR, "media")) +NODE_MODULES_ROOT = _settings.get("node_modules.root", os.path.join(BASE_DIR, "node_modules")) -YARN_INSTALLED_APPS = ['bootstrap', 'font-awesome', 'jquery', 'popper.js', 'datatables', 'select2'] +YARN_INSTALLED_APPS = ["bootstrap", "font-awesome", "jquery", "popper.js", "datatables", "select2"] -JS_URL = _settings.get('js_assets.url', STATIC_URL) -JS_ROOT = _settings.get('js_assets.root', NODE_MODULES_ROOT + '/node_modules') +JS_URL = _settings.get("js_assets.url", STATIC_URL) +JS_ROOT = _settings.get("js_assets.root", NODE_MODULES_ROOT + "/node_modules") -FONT_AWESOME = {'url': JS_URL + '/font-awesome/css/font-awesome.min.css'} +FONT_AWESOME = {"url": JS_URL + "/font-awesome/css/font-awesome.min.css"} BOOTSTRAP4 = { - 'css_url': JS_URL + '/bootstrap/dist//css/bootstrap.min.css', - 'javascript_url': JS_URL + '/bootstrap/dist/js/bootstrap.min.js', - 'jquery_url': JS_URL + '/jquery/dist/jquery.min.js', - 'popper_url': JS_URL + '/popper.js/dist/umd/popper.min.js', - 'include_jquery': True, - 'include_popper': True, - 'javascript_in_head': True, + "css_url": JS_URL + "/bootstrap/dist//css/bootstrap.min.css", + "javascript_url": JS_URL + "/bootstrap/dist/js/bootstrap.min.js", + "jquery_url": JS_URL + "/jquery/dist/jquery.min.js", + "popper_url": JS_URL + "/popper.js/dist/umd/popper.min.js", + "include_jquery": True, + "include_popper": True, + "javascript_in_head": True, } -SELECT2_CSS = JS_URL + '/select2/dist/css/select2.min.css' -SELECT2_JS = JS_URL + '/select2/dist/js/select2.min.js' -SELECT2_I18N_PATH = JS_URL + '/select2/dist/js/i18n' +SELECT2_CSS = JS_URL + "/select2/dist/css/select2.min.css" +SELECT2_JS = JS_URL + "/select2/dist/js/select2.min.js" +SELECT2_I18N_PATH = JS_URL + "/select2/dist/js/i18n" ANY_JS = { - 'DataTables': {'js_url': JS_URL + '/datatables/media/js/jquery.dataTables.min.js'}, - 'DataTables-Bootstrap4': { - 'css_url': JS_URL + '/datatables/media/css/dataTables.bootstrap4.min.css', - 'js_url': JS_URL + '/datatables/media/js/dataTables.bootstrap4.min.js', + "DataTables": {"js_url": JS_URL + "/datatables/media/js/jquery.dataTables.min.js"}, + "DataTables-Bootstrap4": { + "css_url": JS_URL + "/datatables/media/css/dataTables.bootstrap4.min.css", + "js_url": JS_URL + "/datatables/media/js/dataTables.bootstrap4.min.js", }, } SASS_PROCESSOR_AUTO_INCLUDE = False SASS_PROCESSOR_CUSTOM_FUNCTIONS = { - 'get-colour': 'biscuit.core.util.sass_helpers.get_colour', - 'get-theme-setting': 'biscuit.core.util.sass_helpers.get_theme_setting', + "get-colour": "biscuit.core.util.sass_helpers.get_colour", + "get-theme-setting": "biscuit.core.util.sass_helpers.get_theme_setting", } -SASS_PROCESSOR_INCLUDE_DIRS = [_settings.get('bootstrap.sass_path', JS_ROOT + '/bootstrap/scss/')] - -ADMINS = _settings.get('contact.admins', []) -SERVER_EMAIL = _settings.get('contact.from', 'root@localhost') -DEFAULT_FROM_EMAIL = _settings.get('contact.from', 'root@localhost') -MANAGERS = _settings.get('contact.admins', []) - -if _settings.get('mail.server.host', None): - EMAIL_HOST = _settings.get('mail.server.host') - EMAIL_USE_TLS = _settings.get('mail.server.tls', False) - EMAIL_USE_SSL = _settings.get('mail.server.ssl', False) - if _settings.get('mail.server.port', None): - EMAIL_PORT = _settings.get('mail.server.port') - if _settings.get('mail.server.user', None): - EMAIL_HOST_USER = _settings.get('mail.server.user') - EMAIL_HOST_PASSWORD = _settings.get('mail.server.password') - -TEMPLATE_VISIBLE_SETTINGS = ['ADMINS', 'DEBUG'] - -MAINTENANCE_MODE = _settings.get('maintenance.enabled', None) +SASS_PROCESSOR_INCLUDE_DIRS = [_settings.get("bootstrap.sass_path", JS_ROOT + "/bootstrap/scss/")] + +ADMINS = _settings.get("contact.admins", []) +SERVER_EMAIL = _settings.get("contact.from", "root@localhost") +DEFAULT_FROM_EMAIL = _settings.get("contact.from", "root@localhost") +MANAGERS = _settings.get("contact.admins", []) + +if _settings.get("mail.server.host", None): + EMAIL_HOST = _settings.get("mail.server.host") + EMAIL_USE_TLS = _settings.get("mail.server.tls", False) + EMAIL_USE_SSL = _settings.get("mail.server.ssl", False) + if _settings.get("mail.server.port", None): + EMAIL_PORT = _settings.get("mail.server.port") + if _settings.get("mail.server.user", None): + EMAIL_HOST_USER = _settings.get("mail.server.user") + EMAIL_HOST_PASSWORD = _settings.get("mail.server.password") + +TEMPLATE_VISIBLE_SETTINGS = ["ADMINS", "DEBUG"] + +MAINTENANCE_MODE = _settings.get("maintenance.enabled", None) MAINTENANCE_MODE_IGNORE_IP_ADDRESSES = _settings.get( - 'maintenance.ignore_ips', _settings.get('maintenance.internal_ips', []) + "maintenance.ignore_ips", _settings.get("maintenance.internal_ips", []) ) -MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS = 'ipware.ip.get_ip' +MAINTENANCE_MODE_GET_CLIENT_IP_ADDRESS = "ipware.ip.get_ip" MAINTENANCE_MODE_IGNORE_SUPERUSER = True MAINTENANCE_MODE_STATE_FILE_PATH = _settings.get( - 'maintenance.statefile', 'maintenance_mode_state.txt' + "maintenance.statefile", "maintenance_mode_state.txt" ) -IMPERSONATE = {'USE_HTTP_REFERER': True, 'REQUIRE_SUPERUSER': True, 'ALLOW_SUPERUSER': True} +IMPERSONATE = {"USE_HTTP_REFERER": True, "REQUIRE_SUPERUSER": True, "ALLOW_SUPERUSER": True} DJANGO_TABLES2_TEMPLATE = "django_tables2/bootstrap4.html" -DBBACKUP_STORAGE = _settings.get('backup.storage', 'django.core.files.storage.FileSystemStorage') -DBBACKUP_STORAGE_OPTIONS = {'location': _settings.get('backup.location', '/var/backups/biscuit')} -DBBACKUP_CLEANUP_KEEP = _settings.get('backup.keep.database', 10) -DBBACKUP_CLEANUP_KEEP_MEDIA = _settings.get('backup.keep.media', 10) -DBBACKUP_CRON_TIMES = _settings.get('backup.times', None) or ['03:57'] +DBBACKUP_STORAGE = _settings.get("backup.storage", "django.core.files.storage.FileSystemStorage") +DBBACKUP_STORAGE_OPTIONS = {"location": _settings.get("backup.location", "/var/backups/biscuit")} +DBBACKUP_CLEANUP_KEEP = _settings.get("backup.keep.database", 10) +DBBACKUP_CLEANUP_KEEP_MEDIA = _settings.get("backup.keep.media", 10) +DBBACKUP_CRON_TIMES = _settings.get("backup.times", None) or ["03:57"] -CRON_CLASSES = ['biscuit.core.cronjobs.Backup'] +CRON_CLASSES = ["biscuit.core.cronjobs.Backup"] -ANONYMIZE_ENABLED = _settings.get('maintenance.anonymisable', True) +ANONYMIZE_ENABLED = _settings.get("maintenance.anonymisable", True) -LOGIN_URL = 'two_factor:login' +LOGIN_URL = "two_factor:login" -if _settings.get('2fa.call.enabled', False): - TWO_FACTOR_CALL_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio' +if _settings.get("2fa.call.enabled", False): + TWO_FACTOR_CALL_GATEWAY = "two_factor.gateways.twilio.gateway.Twilio" -if _settings.get('2fa.sms.enabled', False): - TWO_FACTOR_SMS_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio' +if _settings.get("2fa.sms.enabled", False): + TWO_FACTOR_SMS_GATEWAY = "two_factor.gateways.twilio.gateway.Twilio" -if _settings.get('2fa.twilio.sid', None): +if _settings.get("2fa.twilio.sid", None): MIDDLEWARE.insert( - MIDDLEWARE.index('django_otp.middleware.OTPMiddleware') + 1, - 'two_factor.middleware.threadlocals.ThreadLocals', + MIDDLEWARE.index("django_otp.middleware.OTPMiddleware") + 1, + "two_factor.middleware.threadlocals.ThreadLocals", ) - TWILIO_SID = _settings.get('2fa.twilio.sid') - TWILIO_TOKEN = _settings.get('2fa.twilio.token') - TWILIO_CALLER_ID = _settings.get('2fa.twilio.callerid') + TWILIO_SID = _settings.get("2fa.twilio.sid") + TWILIO_TOKEN = _settings.get("2fa.twilio.token") + TWILIO_CALLER_ID = _settings.get("2fa.twilio.callerid") _settings.populate_obj(sys.modules[__name__]) diff --git a/biscuit/core/signals.py b/biscuit/core/signals.py index e531c5b9e..2a2e34198 100644 --- a/biscuit/core/signals.py +++ b/biscuit/core/signals.py @@ -5,7 +5,7 @@ from django.conf import settings def clean_scss(*args, **kwargs) -> None: - for source_map in glob(os.path.join(settings.STATIC_ROOT, '*.css.map')): + for source_map in glob(os.path.join(settings.STATIC_ROOT, "*.css.map")): try: os.unlink(source_map) except OSError: diff --git a/biscuit/core/tables.py b/biscuit/core/tables.py index 39edd10b7..b81902729 100644 --- a/biscuit/core/tables.py +++ b/biscuit/core/tables.py @@ -4,15 +4,15 @@ from django_tables2.utils import A class PersonsTable(tables.Table): class Meta: - attrs = {'class': 'table table-striped table-bordered table-hover table-responsive-xl'} + attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} - first_name = tables.LinkColumn('person_by_id', args=[A('id')]) - last_name = tables.LinkColumn('person_by_id', args=[A('id')]) + first_name = tables.LinkColumn("person_by_id", args=[A("id")]) + last_name = tables.LinkColumn("person_by_id", args=[A("id")]) class GroupsTable(tables.Table): class Meta: - attrs = {'class': 'table table-striped table-bordered table-hover table-responsive-xl'} + attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} - name = tables.LinkColumn('group_by_id', args=[A('id')]) - short_name = tables.LinkColumn('group_by_id', args=[A('id')]) + name = tables.LinkColumn("group_by_id", args=[A("id")]) + short_name = tables.LinkColumn("group_by_id", args=[A("id")]) diff --git a/biscuit/core/templatetags/data_helpers.py b/biscuit/core/templatetags/data_helpers.py index 66b41efd3..0b195d136 100644 --- a/biscuit/core/templatetags/data_helpers.py +++ b/biscuit/core/templatetags/data_helpers.py @@ -11,7 +11,7 @@ def get_dict(value: Any, arg: Any) -> Any: if hasattr(value, str(arg)): return getattr(value, arg) - elif hasattr(value, 'keys') and arg in value.keys(): + elif hasattr(value, "keys") and arg in value.keys(): return value[arg] elif str(arg).isnumeric() and len(value) > int(arg): return value[int(arg)] diff --git a/biscuit/core/tests/browser/test_selenium.py b/biscuit/core/tests/browser/test_selenium.py index 21705ba50..795e23799 100644 --- a/biscuit/core/tests/browser/test_selenium.py +++ b/biscuit/core/tests/browser/test_selenium.py @@ -6,11 +6,11 @@ from django.conf import settings from django.test.selenium import SeleniumTestCase, SeleniumTestCaseBase from django.urls import reverse -SeleniumTestCaseBase.external_host = os.environ.get('TEST_HOST', '') or None +SeleniumTestCaseBase.external_host = os.environ.get("TEST_HOST", "") or None SeleniumTestCaseBase.browsers = list( - filter(bool, os.environ.get('TEST_SELENIUM_BROWSERS', '').split(',')) + filter(bool, os.environ.get("TEST_SELENIUM_BROWSERS", "").split(",")) ) -SeleniumTestCaseBase.selenium_hub = os.environ.get('TEST_SELENIUM_HUB', '') or None +SeleniumTestCaseBase.selenium_hub = os.environ.get("TEST_SELENIUM_HUB", "") or None class SeleniumTests(SeleniumTestCase): @@ -18,7 +18,7 @@ class SeleniumTests(SeleniumTestCase): @classmethod def _screenshot(cls, filename): - screenshot_path = os.environ.get('TEST_SCREENSHOT_PATH', None) + screenshot_path = os.environ.get("TEST_SCREENSHOT_PATH", None) if screenshot_path: os.makedirs(os.path.join(screenshot_path, cls.browser), exist_ok=True) return cls.selenium.save_screenshot( @@ -29,18 +29,18 @@ class SeleniumTests(SeleniumTestCase): @pytest.mark.django_db def test_index(self): - self.selenium.get(self.live_server_url + '/') - assert 'BiscuIT' in self.selenium.title - self._screenshot('index.png') + self.selenium.get(self.live_server_url + "/") + assert "BiscuIT" in self.selenium.title + self._screenshot("index.png") @pytest.mark.django_db def test_login_default_superuser(self): - username = 'admin' - password = 'admin' + username = "admin" + password = "admin" # Navigate to configured login page self.selenium.get(self.live_server_url + reverse(settings.LOGIN_URL)) - self._screenshot('login_default_superuser_blank.png') + self._screenshot("login_default_superuser_blank.png") # Find login form input fields and enter defined credentials self.selenium.find_element_by_xpath( @@ -49,12 +49,12 @@ class SeleniumTests(SeleniumTestCase): self.selenium.find_element_by_xpath( '//label[contains(text(), "Password")]/../input' ).send_keys(password) - self._screenshot('login_default_superuser_filled.png') + self._screenshot("login_default_superuser_filled.png") # Submit form by clicking django-two-factor-auth's Next button self.selenium.find_element_by_xpath('//button[contains(text(), "Next")]').click() - self._screenshot('login_default_superuser_submitted.png') + self._screenshot("login_default_superuser_submitted.png") # Should redirect away from login page and not put up an alert about wrong credentials - assert 'Please enter a correct username and password.' not in self.selenium.page_source + assert "Please enter a correct username and password." not in self.selenium.page_source assert reverse(settings.LOGIN_URL) not in self.selenium.current_url diff --git a/biscuit/core/tests/models/test_person.py b/biscuit/core/tests/models/test_person.py index 328f459b3..740031df0 100644 --- a/biscuit/core/tests/models/test_person.py +++ b/biscuit/core/tests/models/test_person.py @@ -5,6 +5,6 @@ from biscuit.core.models import Person @pytest.mark.django_db def test_full_name(): - _person = Person.objects.create(first_name='Jane', last_name='Doe') + _person = Person.objects.create(first_name="Jane", last_name="Doe") - assert _person.full_name == 'Doe, Jane' + assert _person.full_name == "Doe, Jane" diff --git a/biscuit/core/tests/templatetags/test_data_helpers.py b/biscuit/core/tests/templatetags/test_data_helpers.py index 22281a184..5f8e975ff 100644 --- a/biscuit/core/tests/templatetags/test_data_helpers.py +++ b/biscuit/core/tests/templatetags/test_data_helpers.py @@ -5,13 +5,13 @@ def test_get_dict_object(): class _Foo(object): bar = 12 - assert _Foo.bar == get_dict(_Foo, 'bar') + assert _Foo.bar == get_dict(_Foo, "bar") def test_get_dict_dict(): - _foo = {'bar': 12} + _foo = {"bar": 12} - assert _foo['bar'] == get_dict(_foo, 'bar') + assert _foo["bar"] == get_dict(_foo, "bar") def test_get_dict_list(): @@ -23,4 +23,4 @@ def test_get_dict_list(): def test_get_dict_invalid(): _foo = 12 - assert get_dict(_foo, 'bar') is None + assert get_dict(_foo, "bar") is None diff --git a/biscuit/core/tests/views/test_account.py b/biscuit/core/tests/views/test_account.py index 6a5971720..16016665f 100644 --- a/biscuit/core/tests/views/test_account.py +++ b/biscuit/core/tests/views/test_account.py @@ -6,52 +6,52 @@ from django.urls import reverse @pytest.mark.django_db def test_index_not_logged_in(client): - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert reverse(settings.LOGIN_URL) in response.content.decode('utf-8') + assert reverse(settings.LOGIN_URL) in response.content.decode("utf-8") @pytest.mark.django_db def test_login(client, django_user_model): - username = 'foo' - password = 'bar' + username = "foo" + password = "bar" django_user_model.objects.create_user(username=username, password=password) client.login(username=username, password=password) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert reverse(settings.LOGIN_URL) not in response.content.decode('utf-8') + assert reverse(settings.LOGIN_URL) not in response.content.decode("utf-8") @pytest.mark.django_db def test_index_not_linked_to_person(client, django_user_model): - username = 'foo' - password = 'bar' + username = "foo" + password = "bar" django_user_model.objects.create_user(username=username, password=password) client.login(username=username, password=password) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert 'You are not linked to a person' in response.content.decode('utf-8') + assert "You are not linked to a person" in response.content.decode("utf-8") @pytest.mark.django_db def test_logout(client, django_user_model): - username = 'foo' - password = 'bar' + username = "foo" + password = "bar" django_user_model.objects.create_user(username=username, password=password) client.login(username=username, password=password) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - response = client.get(reverse('logout'), follow=True) + response = client.get(reverse("logout"), follow=True) assert response.status_code == 200 - assert reverse(settings.LOGIN_URL) in response.content.decode('utf-8') + assert reverse(settings.LOGIN_URL) in response.content.decode("utf-8") diff --git a/biscuit/core/urls.py b/biscuit/core/urls.py index 3b172dc5e..470f5471d 100644 --- a/biscuit/core/urls.py +++ b/biscuit/core/urls.py @@ -11,30 +11,30 @@ from two_factor.urls import urlpatterns as tf_urls from . import views urlpatterns = [ - path('admin/', admin.site.urls), - path('data_management/', views.data_management, name='data_management'), - path('status/', views.system_status, name='system_status'), - path('school_management', views.school_management, name='school_management'), - path('school/information/edit', views.edit_school, name='edit_school_information'), - path('school/term/edit', views.edit_schoolterm, name='edit_school_term'), - path('', include(tf_urls)), - path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), - path('persons', views.persons, name='persons'), - path('persons/accounts', views.persons_accounts, name='persons_accounts'), - path('person', views.person, name='person'), - path('person/<int:id_>', views.person, {'template': 'full'}, name='person_by_id'), - path('person/<int:id_>/card', views.person, {'template': 'card'}, name='person_by_id_card'), - path('person/<int:id_>/edit', views.edit_person, name='edit_person_by_id'), - path('groups', views.groups, name='groups'), - path('group/create', views.edit_group, name='create_group'), - path('group/<int:id_>', views.group, {'template': 'full'}, name='group_by_id'), - path('group/<int:id_>/edit', views.edit_group, name='edit_group_by_id'), - path('', views.index, name='index'), - path('maintenance-mode/', include('maintenance_mode.urls')), - path('impersonate/', include('impersonate.urls')), - path('__i18n__/', include('django.conf.urls.i18n')), - path('select2/', include('django_select2.urls')), - path('settings/', include('dbsettings.urls')), + path("admin/", admin.site.urls), + path("data_management/", views.data_management, name="data_management"), + path("status/", views.system_status, name="system_status"), + path("school_management", views.school_management, name="school_management"), + path("school/information/edit", views.edit_school, name="edit_school_information"), + path("school/term/edit", views.edit_schoolterm, name="edit_school_term"), + path("", include(tf_urls)), + path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout"), + path("persons", views.persons, name="persons"), + path("persons/accounts", views.persons_accounts, name="persons_accounts"), + path("person", views.person, name="person"), + path("person/<int:id_>", views.person, {"template": "full"}, name="person_by_id"), + path("person/<int:id_>/card", views.person, {"template": "card"}, name="person_by_id_card"), + path("person/<int:id_>/edit", views.edit_person, name="edit_person_by_id"), + path("groups", views.groups, name="groups"), + path("group/create", views.edit_group, name="create_group"), + path("group/<int:id_>", views.group, {"template": "full"}, name="group_by_id"), + path("group/<int:id_>/edit", views.edit_group, name="edit_group_by_id"), + path("", views.index, name="index"), + path("maintenance-mode/", include("maintenance_mode.urls")), + path("impersonate/", include("impersonate.urls")), + path("__i18n__/", include("django.conf.urls.i18n")), + path("select2/", include("django_select2.urls")), + path("settings/", include("dbsettings.urls")), ] # Serve static files from STATIC_ROOT to make it work with runserver @@ -42,18 +42,18 @@ urlpatterns = [ urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) # Add URLs for optional features -if hasattr(settings, 'TWILIO_ACCOUNT_SID'): +if hasattr(settings, "TWILIO_ACCOUNT_SID"): from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls # noqa - urlpatterns += [path('', include(tf_twilio_urls))] + urlpatterns += [path("", include(tf_twilio_urls))] # Serve javascript-common if in development if settings.DEBUG: - urlpatterns.append(path('__debug__/', include(debug_toolbar.urls))) + urlpatterns.append(path("__debug__/", include(debug_toolbar.urls))) # Automatically mount URLs from all installed BiscuIT apps for app_config in apps.app_configs.values(): - if not app_config.name.startswith('biscuit.apps.'): + if not app_config.name.startswith("biscuit.apps."): continue - urlpatterns.append(path('app/%s/' % app_config.label, include('%s.urls' % app_config.name))) + urlpatterns.append(path("app/%s/" % app_config.label, include("%s.urls" % app_config.name))) diff --git a/biscuit/core/util/apps.py b/biscuit/core/util/apps.py index 9b732d651..f98649cdc 100644 --- a/biscuit/core/util/apps.py +++ b/biscuit/core/util/apps.py @@ -12,7 +12,7 @@ class AppConfig(django.apps.AppConfig): # Run model extension code try: import_module( - '.'.join(self.__class__.__module__.split('.')[:-1] + ['model_extensions']) + ".".join(self.__class__.__module__.split(".")[:-1] + ["model_extensions"]) ) except ImportError: # ImportErrors are non-fatal because model extensions are optional. diff --git a/biscuit/core/util/core_helpers.py b/biscuit/core/util/core_helpers.py index 4089026c8..d66eb3a53 100644 --- a/biscuit/core/util/core_helpers.py +++ b/biscuit/core/util/core_helpers.py @@ -14,7 +14,7 @@ def dt_show_toolbar(request: HttpRequest) -> bool: if show_toolbar(request): return True - elif hasattr(request, 'user') and request.user.is_superuser: + elif hasattr(request, "user") and request.user.is_superuser: return True return False @@ -31,28 +31,28 @@ def get_app_packages() -> Sequence[str]: pkgs = [] for pkg in pkgutil.iter_modules(biscuit.apps.__path__): - mod = import_module('biscuit.apps.%s' % pkg[1]) + mod = import_module("biscuit.apps.%s" % pkg[1]) # Add additional apps defined in module's INSTALLED_APPS constant - additional_apps = getattr(mod, 'INSTALLED_APPS', []) + additional_apps = getattr(mod, "INSTALLED_APPS", []) for app in additional_apps: if app not in pkgs: pkgs.append(app) - pkgs.append('biscuit.apps.%s' % pkg[1]) + pkgs.append("biscuit.apps.%s" % pkg[1]) return pkgs def is_impersonate(request: HttpRequest) -> bool: - if hasattr(request, 'user'): - return getattr(request.user, 'is_impersonate', False) + if hasattr(request, "user"): + return getattr(request.user, "is_impersonate", False) else: return False def has_person(request: HttpRequest) -> bool: - if hasattr(request, 'user'): - return getattr(request.user, 'person', None) is not None + if hasattr(request, "user"): + return getattr(request.user, "person", None) is not None else: return False diff --git a/biscuit/core/util/sass_helpers.py b/biscuit/core/util/sass_helpers.py index c609c0ed9..9dbb56bf2 100644 --- a/biscuit/core/util/sass_helpers.py +++ b/biscuit/core/util/sass_helpers.py @@ -12,4 +12,4 @@ def get_colour(html_colour: str) -> SassColor: def get_theme_setting(setting: str) -> str: - return getattr(theme_settings, setting, '') + return getattr(theme_settings, setting, "") diff --git a/biscuit/core/views.py b/biscuit/core/views.py index d3eb0e775..999f911d3 100644 --- a/biscuit/core/views.py +++ b/biscuit/core/views.py @@ -23,7 +23,7 @@ from .util import messages def index(request: HttpRequest) -> HttpResponse: context = {} - return render(request, 'core/index.html', context) + return render(request, "core/index.html", context) @login_required @@ -36,9 +36,9 @@ def persons(request: HttpRequest) -> HttpResponse: # Build table persons_table = PersonsTable(persons) RequestConfig(request).configure(persons_table) - context['persons_table'] = persons_table + context["persons_table"] = persons_table - return render(request, 'core/persons.html', context) + return render(request, "core/persons.html", context) @login_required @@ -52,7 +52,7 @@ def person(request: HttpRequest, id_: int, template: str) -> HttpResponse: # Turn not-found object into a 404 error raise Http404 from e - context['person'] = person + context["person"] = person # Get groups where person is member of groups = Group.objects.filter(members=id_) @@ -60,9 +60,9 @@ def person(request: HttpRequest, id_: int, template: str) -> HttpResponse: # Build table groups_table = GroupsTable(groups) RequestConfig(request).configure(groups_table) - context['groups_table'] = groups_table + context["groups_table"] = groups_table - return render(request, 'core/person_%s.html' % template, context) + return render(request, "core/person_%s.html" % template, context) @login_required @@ -76,7 +76,7 @@ def group(request: HttpRequest, id_: int, template: str) -> HttpResponse: # Turn not-found object into a 404 error raise Http404 from e - context['group'] = group + context["group"] = group # Get group group = Group.objects.get(pk=id_) @@ -87,7 +87,7 @@ def group(request: HttpRequest, id_: int, template: str) -> HttpResponse: # Build table members_table = PersonsTable(members) RequestConfig(request).configure(members_table) - context['members_table'] = members_table + context["members_table"] = members_table # Get owners owners = group.owners.filter(is_active=True) @@ -95,9 +95,9 @@ def group(request: HttpRequest, id_: int, template: str) -> HttpResponse: # Build table owners_table = PersonsTable(owners) RequestConfig(request).configure(owners_table) - context['owners_table'] = owners_table + context["owners_table"] = owners_table - return render(request, 'core/group_%s.html' % template, context) + return render(request, "core/group_%s.html" % template, context) @login_required @@ -110,9 +110,9 @@ def groups(request: HttpRequest) -> HttpResponse: # Build table groups_table = GroupsTable(groups) RequestConfig(request).configure(groups_table) - context['groups_table'] = groups_table + context["groups_table"] = groups_table - return render(request, 'core/groups.html', context) + return render(request, "core/groups.html", context) @admin_required @@ -122,13 +122,13 @@ def persons_accounts(request: HttpRequest) -> HttpResponse: persons_qs = Person.objects.all() persons_accounts_formset = PersonsAccountsFormSet(request.POST or None, queryset=persons_qs) - if request.method == 'POST': + if request.method == "POST": if persons_accounts_formset.is_valid(): persons_accounts_formset.save() - context['persons_accounts_formset'] = persons_accounts_formset + context["persons_accounts_formset"] = persons_accounts_formset - return render(request, 'core/persons_accounts.html', context) + return render(request, "core/persons_accounts.html", context) @admin_required @@ -139,18 +139,18 @@ def edit_person(request: HttpRequest, id_: int) -> HttpResponse: edit_person_form = EditPersonForm(request.POST or None, request.FILES or None, instance=person) - context['person'] = person + context["person"] = person - if request.method == 'POST': + if request.method == "POST": if edit_person_form.is_valid(): edit_person_form.save(commit=True) - messages.success(request, _('The person has been saved.')) - return redirect('edit_person_by_id', id_=person.id) + messages.success(request, _("The person has been saved.")) + return redirect("edit_person_by_id", id_=person.id) - context['edit_person_form'] = edit_person_form + context["edit_person_form"] = edit_person_form - return render(request, 'core/edit_person.html', context) + return render(request, "core/edit_person.html", context) @admin_required @@ -164,40 +164,40 @@ def edit_group(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: group = None edit_group_form = EditGroupForm(request.POST or None) - if request.method == 'POST': + if request.method == "POST": if edit_group_form.is_valid(): edit_group_form.save(commit=True) - messages.success(request, _('The group has been saved.')) - return redirect('groups') + messages.success(request, _("The group has been saved.")) + return redirect("groups") - context['group'] = group - context['edit_group_form'] = edit_group_form + context["group"] = group + context["edit_group_form"] = edit_group_form - return render(request, 'core/edit_group.html', context) + return render(request, "core/edit_group.html", context) @admin_required def data_management(request: HttpRequest) -> HttpResponse: context = {} - return render(request, 'core/data_management.html', context) + return render(request, "core/data_management.html", context) @admin_required def system_status(request: HttpRequest) -> HttpResponse: context = {} - context['backups'] = CronJobLog.objects.filter(code='biscuit.core.Backup').order_by( - '-end_time' + context["backups"] = CronJobLog.objects.filter(code="biscuit.core.Backup").order_by( + "-end_time" )[:10] - return render(request, 'core/system_status.html', context) + return render(request, "core/system_status.html", context) @admin_required def school_management(request: HttpRequest) -> HttpResponse: context = {} - return render(request, 'core/school_management.html', context) + return render(request, "core/school_management.html", context) @admin_required @@ -207,18 +207,18 @@ def edit_school(request: HttpRequest) -> HttpResponse: school = School.objects.first() edit_school_form = EditSchoolForm(request.POST or None, request.FILES or None, instance=school) - context['school'] = school + context["school"] = school - if request.method == 'POST': + if request.method == "POST": if edit_school_form.is_valid(): edit_school_form.save(commit=True) - messages.success(request, _('The school has been saved.')) - return redirect('index') + messages.success(request, _("The school has been saved.")) + return redirect("index") - context['edit_school_form'] = edit_school_form + context["edit_school_form"] = edit_school_form - return render(request, 'core/edit_school.html', context) + return render(request, "core/edit_school.html", context) @admin_required @@ -228,13 +228,13 @@ def edit_schoolterm(request: HttpRequest) -> HttpResponse: term = School.objects.first().current_term edit_term_form = EditTermForm(request.POST or None, instance=term) - if request.method == 'POST': + if request.method == "POST": if edit_term_form.is_valid(): edit_term_form.save(commit=True) - messages.success(request, _('The term has been saved.')) - return redirect('index') + messages.success(request, _("The term has been saved.")) + return redirect("index") - context['edit_term_form'] = edit_term_form + context["edit_term_form"] = edit_term_form - return render(request, 'core/edit_schoolterm.html', context) + return render(request, "core/edit_schoolterm.html", context) diff --git a/biscuit/core/wsgi.py b/biscuit/core/wsgi.py index afb5bc6b7..c364569d7 100644 --- a/biscuit/core/wsgi.py +++ b/biscuit/core/wsgi.py @@ -2,6 +2,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'biscuit.core.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "biscuit.core.settings") application = get_wsgi_application() -- GitLab