Skip to content

Commit 9723dbe

Browse files
author
Ronny Vedrilla
committed
v6.0.0
1 parent ecd0b20 commit 9723dbe

File tree

74 files changed

+562
-320
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+562
-320
lines changed

.gitlab-ci.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ variables:
1515
services:
1616
- docker:18.09-dind
1717

18-
lint:
18+
black and isort:
19+
image: python:3.9-slim
20+
stage: lint
21+
tags:
22+
- normal-load
23+
script:
24+
- pip install black isort typing-extensions
25+
- black --check --diff --color . && isort . --profile black --check --diff
26+
27+
flake8:
1928
image: python:3.9-slim
2029
stage: lint
2130
tags:

.pre-commit-config.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# you find the full pre-commit-tools docu under:
2+
# https://pre-commit.com/
3+
4+
repos:
5+
- repo: https://github.com/ambv/black
6+
rev: 22.3.0
7+
hooks:
8+
- id: black
9+
args: [ --check, --diff, --config, ./pyproject.toml ]
10+
language_version: python3.10
11+
stages: [ push ]
12+
13+
- repo: https://github.com/pycqa/isort
14+
rev: 5.10.1
15+
hooks:
16+
- id: isort
17+
name: isort
18+
args: [ --profile, black, --check, --diff ]
19+
language_version: python3.10
20+
stages: [ push ]
21+
22+
- repo: https://gitlab.com/pycqa/flake8
23+
rev: 4.0.1
24+
hooks:
25+
- id: flake8
26+
additional_dependencies: [ pep8-naming ]
27+
args: [ --config=./setup.cfg, -v, --count ]
28+
language_version: python3.10
29+
stages: [ push ]

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,31 @@ This package contains various useful helper functions. You can read up on all th
4949
pytest
5050
````
5151
52+
## Git hooks (via pre-commit)
53+
54+
We use pre-push hooks to ensure that only linted code reaches our remote repository and pipelines aren't triggered in
55+
vain.
56+
57+
To enable the configured pre-push hooks, you need to [install](https://pre-commit.com/) pre-commit and run once:
58+
59+
pre-commit install -t pre-push -t pre-commit --install-hooks
60+
61+
This will permanently install the git hooks for both, frontend and backend, in your local
62+
[`.git/hooks`](./.git/hooks) folder.
63+
The hooks are configured in the [`.pre-commit-config.yaml`](.pre-commit-config.yaml).
64+
65+
You can check whether hooks work as intended using the [run](https://pre-commit.com/#pre-commit-run) command:
66+
67+
pre-commit run [hook-id] [options]
68+
69+
Example: run single hook
70+
71+
pre-commit run flake8 --all-files --hook-stage push
72+
73+
Example: run all hooks of pre-push stage
74+
75+
pre-commit run --all-files --hook-stage push
76+
5277
## Update documentation
5378
5479
- To generate new auto-docs for new modules run: `sphinx-apidoc -o ./docs/modules/ ./ai_django_core/` (in the current

ai_django_core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Ambient toolbox - Lots of helper functions and useful widgets"""
22

3-
__version__ = '5.14.1'
3+
__version__ = '6.0.0'

ai_django_core/admin/model_admins/classes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ class ReadOnlyAdmin(admin.ModelAdmin):
99

1010
def get_readonly_fields(self, request, obj=None):
1111
if obj:
12-
self.readonly_fields = [field.name for field in self.opts.local_fields] + \
13-
[field.name for field in self.opts.local_many_to_many]
12+
self.readonly_fields = [field.name for field in self.opts.local_fields] + [
13+
field.name for field in self.opts.local_many_to_many
14+
]
1415
return self.readonly_fields
1516

1617
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):

ai_django_core/admin/model_admins/inlines.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class ReadOnlyTabularInline(admin.TabularInline):
66
Class for being extended by TabularInline-classes.
77
Disables all create, delete or edit functionality in the tabular inline admin.
88
"""
9+
910
can_delete = False
1011

1112
def has_add_permission(self, *args, **kwargs):
@@ -18,9 +19,11 @@ def has_delete_permission(self, *args, **kwargs):
1819
return False
1920

2021
def get_readonly_fields(self, request, obj=None):
21-
result = list(set(
22-
[field.name for field in self.opts.local_fields] +
23-
[field.name for field in self.opts.local_many_to_many]
24-
))
22+
result = list(
23+
set(
24+
[field.name for field in self.opts.local_fields]
25+
+ [field.name for field in self.opts.local_many_to_many]
26+
)
27+
)
2528
result.remove('id')
2629
return result

ai_django_core/admin/model_admins/mixins.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class AdminCreateFormMixin:
77
Mixin to easily use a different form for the create case (in comparison to "edit") in the django admin
88
Logic copied from `django.contrib.auth.admin.UserAdmin`
99
"""
10+
1011
add_form = None
1112

1213
def get_form(self, request, obj=None, **kwargs):
@@ -44,6 +45,7 @@ class FetchParentObjectInlineMixin:
4445
Fetches the parent object via the URL resolver and makes it available throughout the entire class.
4546
Attention: Use only in inline admin classes.
4647
"""
48+
4749
parent_object = None
4850

4951
@staticmethod
@@ -81,8 +83,12 @@ class CommonInfoAdminMixin:
8183
"""
8284

8385
def get_readonly_fields(self, request, obj=None):
84-
return super().get_readonly_fields(request, obj) + ('created_by', 'lastmodified_by', 'created_at',
85-
'lastmodified_at')
86+
return super().get_readonly_fields(request, obj) + (
87+
'created_by',
88+
'lastmodified_by',
89+
'created_at',
90+
'lastmodified_at',
91+
)
8692

8793
def save_form(self, request, form, change):
8894
if form.instance and request.user:
@@ -97,6 +103,7 @@ class DeactivatableChangeViewAdminMixin:
97103
"""
98104
Mixin to be used in model admins to disable the detail page / change view.
99105
"""
106+
100107
enable_change_view = True
101108

102109
def can_see_change_view(self, request) -> bool:
@@ -124,8 +131,10 @@ def change_view(self, request, *args, **kwargs):
124131
return super().change_view(request, *args, **kwargs)
125132
else:
126133
opts = self.model._meta
127-
url = reverse('admin:{app}_{model}_changelist'.format(
128-
app=opts.app_label,
129-
model=opts.model_name,
130-
))
134+
url = reverse(
135+
'admin:{app}_{model}_changelist'.format(
136+
app=opts.app_label,
137+
model=opts.model_name,
138+
)
139+
)
131140
return HttpResponseRedirect(url)

ai_django_core/admin/views/forms.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from crispy_forms.helper import FormHelper
2-
from crispy_forms.layout import Submit, Layout, Div, Fieldset, HTML
2+
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
33
from django import forms
44
from django.utils.translation import gettext_lazy as _
55

@@ -8,6 +8,7 @@ class AdminCrispyForm(forms.Form):
88
"""
99
Base crispy form to be used in custom views within the django admin.
1010
"""
11+
1112
section_title = _('No title defined')
1213
button_label = _('Save')
1314

@@ -25,10 +26,7 @@ def __init__(self, *args, **kwargs):
2526
self.helper.add_input(Submit('submit', self.button_label, css_class="button btn-primary"))
2627
self.helper.layout = Layout(
2728
Div(
28-
Div(
29-
HTML(f'<h2>{self.section_title}</h2>'),
30-
Fieldset(*fieldset_list),
31-
css_class='module aligned'
32-
), css_class='custom-form'
29+
Div(HTML(f'<h2>{self.section_title}</h2>'), Fieldset(*fieldset_list), css_class='module aligned'),
30+
css_class='custom-form',
3331
),
3432
)

ai_django_core/admin/views/mixins.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class AdminViewMixin:
66
"""
77
Mixin to provide a custom view with all the attributes it needs to look like a regular django admin page.
88
"""
9+
910
model = None
1011
admin_page_title = ''
1112

@@ -36,23 +37,25 @@ def get_admin_site(self):
3637
def get_context_data(self, **kwargs):
3738
context = super().get_context_data(**kwargs)
3839
admin_site = self.get_admin_site()
39-
context.update({
40-
'site_header': admin_site.site_header,
41-
'site_title': admin_site.site_title,
42-
'title': self.admin_page_title,
43-
'name': self.admin_page_title,
44-
'original': self.admin_page_title,
45-
'is_nav_sidebar_enabled': True,
46-
'available_apps': admin.site.get_app_list(self.request),
47-
'opts': {
48-
'app_label': self.model._meta.app_label,
49-
'verbose_name': self.model._meta.verbose_name,
50-
'verbose_name_plural': self.model._meta.verbose_name_plural,
51-
'model_name': self.model._meta.model_name,
52-
'app_config': {
53-
'verbose_name': self.model._meta.app_config.verbose_name,
54-
}
55-
},
56-
'has_permission': admin_site.has_permission(request=self.request),
57-
})
40+
context.update(
41+
{
42+
'site_header': admin_site.site_header,
43+
'site_title': admin_site.site_title,
44+
'title': self.admin_page_title,
45+
'name': self.admin_page_title,
46+
'original': self.admin_page_title,
47+
'is_nav_sidebar_enabled': True,
48+
'available_apps': admin.site.get_app_list(self.request),
49+
'opts': {
50+
'app_label': self.model._meta.app_label,
51+
'verbose_name': self.model._meta.verbose_name,
52+
'verbose_name_plural': self.model._meta.verbose_name_plural,
53+
'model_name': self.model._meta.model_name,
54+
'app_config': {
55+
'verbose_name': self.model._meta.app_config.verbose_name,
56+
},
57+
},
58+
'has_permission': admin_site.has_permission(request=self.request),
59+
}
60+
)
5861
return context

ai_django_core/drf/serializers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33

44
class BaseModelSerializer(ModelSerializer):
5-
65
def validate(self, data):
76
"""
87
Call Model's clean() method to ensure model-level-validation

ai_django_core/drf/tests.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class BaseViewSetTestMixin:
99
"""
1010
Base test mixin to lower the pain of testing API calls. Focuses on ViewSets.
1111
"""
12+
1213
default_api_user = None
1314
view_class = None
1415

@@ -35,15 +36,22 @@ def validate_authentication_required(self, *, url: str, method: str, view: str):
3536
"""
3637
Helper method to quickly ensure that a given view needs authorisation.
3738
"""
38-
response = self.execute_request(
39-
method=method,
40-
url=url,
41-
viewset_kwargs={method: view})
39+
response = self.execute_request(method=method, url=url, viewset_kwargs={method: view})
4240

4341
self.assertIn(response.status_code, [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN])
4442

45-
def execute_request(self, *, url, view_kwargs=None, method='get', data=None, view_class=None, user=None,
46-
viewset_kwargs=None, data_format='json') -> Response:
43+
def execute_request(
44+
self,
45+
*,
46+
url,
47+
view_kwargs=None,
48+
method='get',
49+
data=None,
50+
view_class=None,
51+
user=None,
52+
viewset_kwargs=None,
53+
data_format='json',
54+
) -> Response:
4755
"""
4856
Helper method which wraps all relevant setup to execute a request to the backends api
4957

ai_django_core/graphql/forms/mutations.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from graphene_django.forms.mutation import DjangoModelFormMutation
33
from graphql import GraphQLError
44
from graphql_jwt.decorators import login_required
5-
from promise import is_thenable, Promise
5+
from promise import Promise, is_thenable
66

77

88
class DjangoValidatedModelFormMutation(DjangoModelFormMutation):
@@ -24,11 +24,7 @@ def on_resolve(payload):
2424
try:
2525
payload.client_mutation_id = input.get("client_mutation_id")
2626
except Exception:
27-
raise Exception(
28-
"Cannot set client_mutation_id in the payload object {}".format(
29-
repr(payload)
30-
)
31-
)
27+
raise Exception("Cannot set client_mutation_id in the payload object {}".format(repr(payload)))
3228
return payload
3329

3430
result = cls.mutate_and_get_payload(root, info, **input)
@@ -51,5 +47,6 @@ class LoginRequiredDjangoModelFormMutation(DjangoValidatedModelFormMutation):
5147
"""
5248
Ensures that you need to be logged in with GraphQL JWT (json web token) authentication
5349
"""
50+
5451
class Meta:
5552
abstract = True

ai_django_core/graphql/schemes/mutations.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class DeleteMutation(graphene.ClientIDMutation):
88
"""
99
Provides a mutation for handling common delete cases. Exposes methods for custom validation and queryset filtering.
1010
"""
11+
1112
success = graphene.Boolean()
1213
model = None
1314

@@ -63,5 +64,6 @@ class LoginRequiredDeleteMutation(DeleteMutation):
6364
Deletes an object from the database.
6465
Ensures user is authenticated with GraphQL-JWT
6566
"""
67+
6668
class Meta:
6769
abstract = True

ai_django_core/graphql/sentry/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class SentryGraphQLView(GraphQLView):
1515
* sentry_sdk >= 0.13.0
1616
* graphene_django >=2.9.1, <3.0
1717
"""
18+
1819
def execute_graphql_request(self, *args, **kwargs):
1920
"""Extract any exceptions and send them to Sentry"""
2021
result = super().execute_graphql_request(*args, **kwargs)

ai_django_core/graphql/tests/base_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import json
22

3-
from django.test import TestCase, Client
3+
from django.test import Client, TestCase
44

55

66
class GraphQLTestCase(TestCase):
77
"""
88
Provides a best-practice wrapper for easily testing GraphQL endpoints.
99
"""
10+
1011
# URL to graphql endpoint
1112
GRAPHQL_URL = '/graphql/'
1213
# Here you need to set your graphql schema for the tests
@@ -36,8 +37,7 @@ def query(self, query: str, op_name: str = None, input_data: dict = None):
3637
if input_data:
3738
body['variables'] = {'input': input_data}
3839

39-
resp = self._client.post(self.GRAPHQL_URL, json.dumps(body),
40-
content_type='application/json')
40+
resp = self._client.post(self.GRAPHQL_URL, json.dumps(body), content_type='application/json')
4141
return resp
4242

4343
def assertResponseNoErrors(self, resp):

ai_django_core/mail/backends/whitelist_smtp.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ def get_email_regex():
3131
Getter for configuration variable from the settings.
3232
Will return a RegEX to match email whitelisted domains.
3333
"""
34-
return r'^[\w\-\.]+@(%s)$' % '|'.join(x for x in
35-
WhitelistEmailBackend.get_domain_whitelist()).replace('.', r'\.')
34+
return r'^[\w\-\.]+@(%s)$' % '|'.join(x for x in WhitelistEmailBackend.get_domain_whitelist()).replace(
35+
'.', r'\.'
36+
)
3637

3738
@staticmethod
3839
def get_backend_redirect_address() -> str:

0 commit comments

Comments
 (0)