Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Migrate to ruff #119

Merged
merged 6 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@ repos:
- id: check-yaml
- id: check-toml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.10.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 7.0.0
hooks:
- id: flake8
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
- id: ruff
name: ruff lint
- id: ruff-format
name: ruff format
args: [ --check ]
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.13.0
hooks:
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,15 @@ The project is now running at [localhost:8000](http://localhost:8000)

## Code format

This project uses
[`black`](https://github.com/ambv/black),
[`flake8`](https://gitlab.com/pycqa/flake8) and
[`isort`](https://github.com/timothycrosley/isort)
for code formatting and quality checking. Project follows the basic
black config, without any modifications.

Basic `black` commands:
This project uses [Ruff](https://docs.astral.sh/ruff/) for code formatting and quality checking.

* To let `black` do its magic: `black .`
* To see which files `black` would change: `black --check .`
Basic `ruff` commands:

* lint: `ruff check`
* apply safe lint fixes: `ruff check --fix`
* check formatting: `ruff format --check`
* format: `ruff format`

[`pre-commit`](https://pre-commit.com/) can be used to install and
run all the formatting tools as git hooks automatically before a
Expand Down
8 changes: 5 additions & 3 deletions atv/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ def authenticate(self, request):
if not service_key.user:
raise exceptions.AuthenticationFailed("Permissions missing from API key.")

# Verified key is cached for 5 minutes. This improves subsequent requests' response times significantly.
# Default cache uses local memory caching. Keys aren't accessible from console or to other threads or pods
# TODO: if caching is refactored to use Redis or Memcached this needs to be reconsidered
# Verified key is cached for 5 minutes. This improves subsequent requests'
# response times significantly. Default cache uses local memory caching. Keys
# aren't accessible from console or to other threads or pods
# TODO: if caching is refactored to use Redis or Memcached this needs to be
# reconsidered
cache.set(key, service_key, timeout=60 * 5)

return service_key.user, service_key
5 changes: 3 additions & 2 deletions atv/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ def permission_checker(request):

def staff_required(required_permission=ServicePermissions.VIEW_DOCUMENTS):
"""
Returns a decorator that checks if the user has staff permission on resources for the service
specified in the request. Required permission can be defined as an argument, defaults to 'view'.
Returns a decorator that checks if the user has staff permission on resources for
the service specified in the request. Required permission can be defined as an
argument, defaults to 'view'.
"""

if not isinstance(required_permission, ServicePermissions):
Expand Down
6 changes: 4 additions & 2 deletions audit_log/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def send_audit_log_entries():

if not (settings.ELASTIC_HOST and settings.ELASTIC_AUDIT_LOG_INDEX):
logger.warning(
"Trying to send audit logs to Elasticsearch without proper configuration, process skipped"
"Trying to send audit logs to Elasticsearch without proper configuration,"
" process skipped"
)
return

Expand Down Expand Up @@ -63,5 +64,6 @@ def clear_audit_log_entries(days_to_keep=30):
if count := sent_entries.count():
sent_entries.delete()
logger.info(
f"Cleared {count} sent audit logs, which were older than {days_to_keep} days."
f"Cleared {count} sent audit logs, which were older than {days_to_keep}"
" days."
)
3 changes: 2 additions & 1 deletion audit_log/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ def _get_ip_address(self) -> str:
]
for ip in forwarded_for:
try:
# This regexp matches IPv4 addresses without including the port number
# This regexp matches IPv4 addresses without including the port
# number
regexp_for_ipv4 = re.match(
r"(^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4})", ip
)
Expand Down
515 changes: 323 additions & 192 deletions documents/api/docs.py

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions documents/api/filtersets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django_filters import BaseInFilter, CharFilter, Filter, rest_framework as filters
from django_filters import BaseInFilter, CharFilter, Filter
from django_filters import rest_framework as filters
from django_filters.constants import EMPTY_VALUES
from rest_framework.exceptions import ValidationError

Expand Down Expand Up @@ -47,8 +48,8 @@ def filter(self, qs, value):
raise ValidationError(
detail={
"Invalid Query": [
"Enter query in format 'key:value' without quotes."
" You can have multiple key and value pairs, separated by comma"
"Enter query in format 'key:value' without quotes. You can"
" have multiple key and value pairs, separated by comma"
]
}
)
Expand Down
3 changes: 2 additions & 1 deletion documents/api/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ def get_document_metadata_queryset(
user: User, api_key: ServiceAPIKey = None
) -> QuerySet:
"""
Superusers and staff(API key) are allowed to see document metadata of all services for all users.
Superusers and staff(API key) are allowed to see document metadata of all services
for all users.
Token users can only see their own documents metadata from across all services.
"""
queryset = (
Expand Down
6 changes: 4 additions & 2 deletions documents/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ def get_queryset(self):
service_api_key = get_service_api_key_from_request(self.request)
return get_document_metadata_queryset(user, service_api_key)

# Use retrieve to allow using user__uuid as a lookup_field to list documents of single user
# Use retrieve to allow using user__uuid as a lookup_field to list documents of
# single user
def retrieve(self, request, *args, **kwargs):
with self.record_action():
service_api_key = get_service_api_key_from_request(request)
Expand Down Expand Up @@ -502,7 +503,8 @@ def create(self, request, *args, **kwargs):

serializer = CreateAnonymousDocumentSerializer(data=data)

# If the data is not valid, it will raise a ValidationError and return Bad Request
# If the data is not valid, it will raise a ValidationError and return Bad
# Request
serializer.is_valid(raise_exception=True)

with self.record_action():
Expand Down
5 changes: 4 additions & 1 deletion documents/management/commands/delete_expired_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@


class Command(BaseCommand):
help = "Delete documents and related objects and files that have reached their deletion date"
help = (
"Delete documents and related objects and files that have reached their"
" deletion date"
)

def handle(self, *args, **kwargs):
delete_expired_documents()
3 changes: 2 additions & 1 deletion documents/management/commands/remove_outdated_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class Command(BaseCommand):
files_deleted = 0

def remove_document_outdated_files(self, document: Document, path, dry_run):
"""For a given document, remove the files that don't have an associated Attachment"""
"""For a given document, remove the files that don't have an associated
Attachment"""
document_path = get_document_attachment_directory_path(document)

for filename in os.listdir(path):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 3.2.3 on 2021-06-29 17:44

from django.db import migrations
import encrypted_fields.fields
from django.db import migrations


class Migration(migrations.Migration):
Expand Down
2 changes: 1 addition & 1 deletion documents/migrations/0005_add_statushistory_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 3.2.9 on 2022-03-04 15:20

from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
Expand Down
2 changes: 1 addition & 1 deletion documents/migrations/0006_document_status_timestamp.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 3.2.9 on 2022-04-13 14:00

from django.db import migrations, models
import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):
Expand Down
1 change: 1 addition & 0 deletions documents/migrations/0007_alter_document_content.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by Django 3.2.9 on 2022-05-03 08:33

from django.db import migrations

import documents.models


Expand Down
1 change: 1 addition & 0 deletions documents/migrations/0008_alter_attachment_file.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by Django 3.2.9 on 2022-06-08 14:15

from django.db import migrations

import documents.fields
import documents.utils

Expand Down
2 changes: 1 addition & 1 deletion documents/migrations/0011_add_activity_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Generated by Django 3.2.16 on 2023-05-15 12:02

from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Generated by Django 4.2.9 on 2024-01-23 14:16

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
Expand Down
19 changes: 12 additions & 7 deletions documents/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class Activity(models.Model):
default=dict,
blank=True,
help_text=_(
"Structure with links related to activity with display text in multiple languages"
"Structure with links related to activity with display text in multiple"
" languages"
),
)
show_to_user = models.BooleanField(
Expand Down Expand Up @@ -155,17 +156,19 @@ class Document(UUIDModel, TimestampedModel):
verbose_name=_("TOS function ID"),
help_text=_(
"UUID without dashes. Should correspond with a Function instance "
"(e.g. the id from https://api.hel.fi/helerm/v1/function/eb30af1d9d654ebc98287ca25f231bf6/) "
"which is applied to the stored document when considering storage time."
"(e.g. the id from"
" https://api.hel.fi/helerm/v1/function/eb30af1d9d654ebc98287ca25f231bf6/"
") which is applied to the stored document when considering storage time."
),
)
tos_record_id = models.CharField(
max_length=32,
verbose_name=_("TOS record ID"),
help_text=_(
"UUID without dashes. Should correspond to a record ID "
"(e.g. records[].id from https://api.hel.fi/helerm/v1/function/eb30af1d9d654ebc98287ca25f231bf6/) "
"within a Function instance which is applied to the stored document when "
"(e.g. records[].id from"
" https://api.hel.fi/helerm/v1/function/eb30af1d9d654ebc98287ca25f231bf6/"
") within a Function instance which is applied to the stored document when "
"considering storage time."
),
)
Expand Down Expand Up @@ -224,7 +227,8 @@ class Document(UUIDModel, TimestampedModel):
default=timezone.now,
verbose_name=_("status_timestamp"),
help_text=_(
"Date and time when document status was last changed. Field is automatically set."
"Date and time when document status was last changed. Field is"
" automatically set."
),
)
type = models.CharField(
Expand Down Expand Up @@ -264,7 +268,8 @@ class Document(UUIDModel, TimestampedModel):
delete_after = models.DateField(
null=True,
help_text=_(
"Date which after the document and related attachments are permanently deleted"
"Date which after the document and related attachments are permanently"
" deleted"
),
)

Expand Down
21 changes: 13 additions & 8 deletions documents/serializers/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class DocumentStatisticsSerializer(serializers.ModelSerializer):
source="service.name", required=False, read_only=True
)
attachments = AttachmentNameSerializer(many=True)
# Attachment count included here just for clarity. Field is added to response body in to_representation
# Attachment count included here just for clarity. Field is added to
# response body in to_representation
attachment_count = serializers.HiddenField(default=0)
user_id = serializers.UUIDField(source="user.uuid", read_only=True)

Expand All @@ -69,7 +70,8 @@ class Meta:

def to_representation(self, instance):
representation = super().to_representation(instance)
# Calculate attachment count here instead of aggregating it to queryset for performance reasons
# Calculate attachment count here instead of aggregating it to queryset
# for performance reasons
representation["attachment_count"] = len(representation["attachments"])
return representation

Expand Down Expand Up @@ -142,7 +144,7 @@ def to_representation(self, instance):


class DocumentSerializer(serializers.ModelSerializer):
"""Basic "read" serializer for the Document model"""
"""Basic "read" serializer for the Document model."""

user_id = serializers.UUIDField(
source="user.uuid", required=False, default=None, read_only=True
Expand Down Expand Up @@ -189,7 +191,8 @@ def update(self, document, validated_data):
# If the document has been locked, no further updates are allowed
if document.locked_after and document.locked_after <= now():
raise DocumentLockedException()
# Deletable field can be changed from True to False but not the other way
# Deletable field can be changed from True to False but not the other
# way
if validated_data.get("deletable") is True and not document.deletable:
raise PermissionDenied(
detail="Field 'deletable' can't be changed if set to False"
Expand Down Expand Up @@ -218,10 +221,11 @@ def to_representation(self, instance):


class CreateAnonymousDocumentSerializer(serializers.ModelSerializer):
"""Create a Document with Attachment for an anonymous user submitting the document
through a Service authorized with an API key.
"""Create a Document with Attachment for an anonymous user submitting the
document through a Service authorized with an API key.

Also handles the creation of the associated Attachments through `CreateAttachmentSerializer`.
Also handles the creation of the associated Attachments through
`CreateAttachmentSerializer`.
"""

user_id = serializers.UUIDField(source="user.uuid", required=False, default=None)
Expand Down Expand Up @@ -254,7 +258,8 @@ class Meta:
)

def validate(self, attrs):
# Validate that no additional fields are being passed (to sanitize the input)
# Validate that no additional fields are being passed (to sanitize the
# input)
if hasattr(self, "initial_data"):
invalid_keys = set(self.initial_data.keys()) - set(self.fields.keys())
if invalid_keys:
Expand Down
3 changes: 2 additions & 1 deletion documents/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def delete_expired_documents():
total, by_type_dict = documents_to_delete_qs.delete()
if total != 0:
logger.info(
f"Deleted {total} objects: {', '.join([f'{i[1]} {i[0]}' for i in by_type_dict.items()])}."
f"Deleted {total} objects:"
f" {', '.join([f'{i[1]} {i[0]}' for i in by_type_dict.items()])}."
)
else:
logger.info("Nothing to delete.")
2 changes: 1 addition & 1 deletion documents/tests/test_api_list_documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def test_document_batch_list_service_api_key(service_api_client, user):
data = {**VALID_DOCUMENT_DATA, "user_id": user.uuid}
other_service = ServiceFactory()
document_ids = []
for _i in range(0, 2):
for _i in range(2):
response = service_api_client.post(
reverse("documents-list"), data, format="multipart"
)
Expand Down
Loading
Loading