diff --git a/avatar/api/__init__.py b/avatar/api/__init__.py index 96c4228..d087506 100644 --- a/avatar/api/__init__.py +++ b/avatar/api/__init__.py @@ -1 +1 @@ -import avatar.api.signals \ No newline at end of file +import avatar.api.signals diff --git a/avatar/api/apps.py b/avatar/api/apps.py index 88ee35e..f139ac4 100644 --- a/avatar/api/apps.py +++ b/avatar/api/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class ApiConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'avatar.api' + default_auto_field = "django.db.models.BigAutoField" + name = "avatar.api" diff --git a/avatar/api/docs/avatar.rst b/avatar/api/docs/avatar.rst index 6c49abc..f311ee9 100644 --- a/avatar/api/docs/avatar.rst +++ b/avatar/api/docs/avatar.rst @@ -189,7 +189,3 @@ API Setting .. py:data:: API_AVATAR_CHANGE_IMAGE It Allows the user to Change the avatar image in ``PUT`` method. Default is ``False``. - - - - diff --git a/avatar/api/docs/conf.py b/avatar/api/docs/conf.py index facfd19..743b82a 100644 --- a/avatar/api/docs/conf.py +++ b/avatar/api/docs/conf.py @@ -6,17 +6,17 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'django-avatar' +project = "django-avatar" copyright = "2013, django-avatar developers" -author = 'keyvan' +author = "keyvan" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [] -templates_path = ['_templates'] -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +templates_path = ["_templates"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] htmlhelp_basename = "django-avatar apidoc" @@ -28,6 +28,6 @@ pygments_style = "sphinx" # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'sphinx_rtd_theme' -html_static_path = ['_static'] -epub_show_urls = 'footnote' +html_theme = "sphinx_rtd_theme" +html_static_path = ["_static"] +epub_show_urls = "footnote" diff --git a/avatar/api/docs/index.rst b/avatar/api/docs/index.rst index 6e7b2e9..e28688c 100644 --- a/avatar/api/docs/index.rst +++ b/avatar/api/docs/index.rst @@ -308,5 +308,3 @@ after `Installation <#installation>`_ . :maxdepth: 1 avatar - - diff --git a/avatar/api/requirements.txt b/avatar/api/requirements.txt index 3117431..6f1c1f3 100644 --- a/avatar/api/requirements.txt +++ b/avatar/api/requirements.txt @@ -1 +1 @@ -djangorestframework \ No newline at end of file +djangorestframework diff --git a/avatar/api/serializers.py b/avatar/api/serializers.py index 5c4d219..6dc1b62 100644 --- a/avatar/api/serializers.py +++ b/avatar/api/serializers.py @@ -1,43 +1,51 @@ import os -from PIL import Image, ImageOps -from avatar.models import Avatar -from avatar.conf import settings -from rest_framework import serializers -from avatar.conf import settings as api_setting -from django.utils.translation import gettext_lazy as _ + from django.template.defaultfilters import filesizeformat +from django.utils.translation import gettext_lazy as _ +from PIL import Image, ImageOps +from rest_framework import serializers + +from avatar.conf import settings +from avatar.conf import settings as api_setting +from avatar.models import Avatar class AvatarSerializer(serializers.ModelSerializer): - avatar_url = serializers.HyperlinkedIdentityField(view_name='avatar-detail', ) + avatar_url = serializers.HyperlinkedIdentityField( + view_name="avatar-detail", + ) user = serializers.HiddenField(default=serializers.CurrentUserDefault()) class Meta: model = Avatar - fields = ['id', 'avatar_url', 'avatar', 'primary', 'user'] - extra_kwargs = {'avatar': {'required': True}} + fields = ["id", "avatar_url", "avatar", "primary", "user"] + extra_kwargs = {"avatar": {"required": True}} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - request = kwargs.get('context').get('request', None) + request = kwargs.get("context").get("request", None) self.user = request.user def get_fields(self, *args, **kwargs): fields = super(AvatarSerializer, self).get_fields(*args, **kwargs) - request = self.context.get('request', None) + request = self.context.get("request", None) # remove avatar url field in detail page - if bool(self.context.get('view').kwargs): - fields.pop('avatar_url') + if bool(self.context.get("view").kwargs): + fields.pop("avatar_url") # remove avatar field in put method - if request and getattr(request, 'method', None) == "PUT": + if request and getattr(request, "method", None) == "PUT": # avatar updates only when primary=true and API_AVATAR_CHANGE_IMAGE = True - if not api_setting.API_AVATAR_CHANGE_IMAGE or self.instance and not self.instance.primary: - fields.pop('avatar') + if ( + not api_setting.API_AVATAR_CHANGE_IMAGE + or self.instance + and not self.instance.primary + ): + fields.pop("avatar") else: - fields.get('avatar', None).required = False + fields.get("avatar", None).required = False return fields def validate_avatar(self, value): diff --git a/avatar/api/signals.py b/avatar/api/signals.py index 62afc3e..14c5dbf 100644 --- a/avatar/api/signals.py +++ b/avatar/api/signals.py @@ -1,8 +1,10 @@ import os -from avatar.conf import settings + from django.db.models import signals -from avatar.api.shortcut import get_object_or_none + from avatar.api.conf import settings as api_settings +from avatar.api.shortcut import get_object_or_none +from avatar.conf import settings from avatar.models import Avatar, invalidate_avatar_cache @@ -20,11 +22,12 @@ def create_default_thumbnails(sender, instance, created=False, **kwargs): instance.create_thumbnail(size[0], size[1]) -def remove_previous_avatar_images_when_update(sender, instance=None, created=False, update_main_avatar=True, **kwargs): +def remove_previous_avatar_images_when_update( + sender, instance=None, created=False, update_main_avatar=True, **kwargs +): if not created: old_instance = get_object_or_none(Avatar, pk=instance.pk) if old_instance and not old_instance.avatar == instance.avatar: - base_filepath = old_instance.avatar.name path, filename = os.path.split(base_filepath) # iterate through resized avatars directories and delete resized avatars @@ -33,10 +36,14 @@ def remove_previous_avatar_images_when_update(sender, instance=None, created=Fal resized_widths, _ = old_instance.avatar.storage.listdir(resized_path) for width in resized_widths: resized_width_path = os.path.join(resized_path, width) - resized_heights, _ = old_instance.avatar.storage.listdir(resized_width_path) + resized_heights, _ = old_instance.avatar.storage.listdir( + resized_width_path + ) for height in resized_heights: if old_instance.thumbnail_exists(width, height): - old_instance.avatar.storage.delete(old_instance.avatar_name(width, height)) + old_instance.avatar.storage.delete( + old_instance.avatar_name(width, height) + ) if update_main_avatar: if old_instance.avatar.storage.exists(old_instance.avatar.name): old_instance.avatar.storage.delete(old_instance.avatar.name) diff --git a/avatar/api/urls.py b/avatar/api/urls.py index a69381d..91dce0d 100644 --- a/avatar/api/urls.py +++ b/avatar/api/urls.py @@ -1,7 +1,8 @@ -from avatar.api.views import AvatarViewSets from rest_framework.routers import SimpleRouter +from avatar.api.views import AvatarViewSets + router = SimpleRouter() -router.register('avatar', AvatarViewSets) +router.register("avatar", AvatarViewSets) urlpatterns = router.urls diff --git a/avatar/api/utils.py b/avatar/api/utils.py index 62e978c..9784fc6 100644 --- a/avatar/api/utils.py +++ b/avatar/api/utils.py @@ -1,10 +1,11 @@ -from avatar.conf import settings from html.parser import HTMLParser +from avatar.conf import settings + class HTMLTagParser(HTMLParser): """ - URL parser for getting (url ,width ,height) from avatar templatetags + URL parser for getting (url ,width ,height) from avatar templatetags """ def __init__(self, output=None): @@ -20,30 +21,27 @@ class HTMLTagParser(HTMLParser): def assign_width_or_height(query_params): """ - Getting width and height in url parameters and specifying them + Getting width and height in url parameters and specifying them """ avatar_default_size = settings.AVATAR_DEFAULT_SIZE - width = query_params.get('width', avatar_default_size) - height = query_params.get('height', avatar_default_size) + width = query_params.get("width", avatar_default_size) + height = query_params.get("height", avatar_default_size) - if width == '': + if width == "": width = avatar_default_size - if height == '': + if height == "": height = avatar_default_size - if height == avatar_default_size and height != '': + if height == avatar_default_size and height != "": height = width - elif width == avatar_default_size and width != '': + elif width == avatar_default_size and width != "": width = height width = int(width) height = int(height) - context = { - 'width': width, - 'height': height - } + context = {"width": width, "height": height} return context diff --git a/avatar/api/views.py b/avatar/api/views.py index a757cd9..7b41a4f 100644 --- a/avatar/api/views.py +++ b/avatar/api/views.py @@ -1,21 +1,23 @@ -from avatar.models import Avatar from django.db.models import QuerySet -from avatar.api.utils import HTMLTagParser -from rest_framework.response import Response -from rest_framework.decorators import action -from avatar.templatetags.avatar_tags import avatar -from avatar.api.serializers import AvatarSerializer -from rest_framework.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ -from rest_framework import viewsets, status, permissions -from avatar.api.utils import set_new_primary, assign_width_or_height -from avatar.utils import invalidate_cache, get_default_avatar_url, get_primary_avatar +from rest_framework import permissions, status, viewsets +from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError +from rest_framework.response import Response + +from avatar.api.serializers import AvatarSerializer +from avatar.api.utils import HTMLTagParser, assign_width_or_height, set_new_primary +from avatar.models import Avatar +from avatar.templatetags.avatar_tags import avatar +from avatar.utils import get_default_avatar_url, get_primary_avatar, invalidate_cache class AvatarViewSets(viewsets.ModelViewSet): serializer_class = AvatarSerializer permission_classes = [permissions.IsAuthenticated] - queryset = Avatar.objects.select_related('user').order_by('-primary', '-date_uploaded') + queryset = Avatar.objects.select_related("user").order_by( + "-primary", "-date_uploaded" + ) @property def parse_html_to_json(self): @@ -26,9 +28,8 @@ class AvatarViewSets(viewsets.ModelViewSet): def get_queryset(self): assert self.queryset is not None, ( - "'%s' should either include a `queryset` attribute, " - "or override the `get_queryset()` method." - % self.__class__.__name__ + "'%s' should either include a `queryset` attribute, " + "or override the `get_queryset()` method." % self.__class__.__name__ ) queryset = self.queryset @@ -52,8 +53,9 @@ class AvatarViewSets(viewsets.ModelViewSet): return Response( { "message": "You haven't uploaded an avatar yet. Please upload one now.", - "default_avatar": self.parse_html_to_json - }) + "default_avatar": self.parse_html_to_json, + } + ) def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) @@ -62,10 +64,7 @@ class AvatarViewSets(viewsets.ModelViewSet): headers = self.get_success_headers(serializer.data) message = _("Successfully uploaded a new avatar.") - context_data = { - 'message': message, - 'data': serializer.data - } + context_data = {"message": message, "data": serializer.data} return Response(context_data, status=status.HTTP_201_CREATED, headers=headers) def destroy(self, request, *args, **kwargs): @@ -78,14 +77,14 @@ class AvatarViewSets(viewsets.ModelViewSet): return Response(message, status=status.HTTP_204_NO_CONTENT) def update(self, request, *args, **kwargs): - partial = kwargs.pop('partial', False) + partial = kwargs.pop("partial", False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) - avatar_image = serializer.validated_data.get('avatar') - primary_avatar = serializer.validated_data.get('primary') + avatar_image = serializer.validated_data.get("avatar") + primary_avatar = serializer.validated_data.get("primary") if not primary_avatar and avatar_image: - raise ValidationError('You cant update an avatar image that is not primary') + raise ValidationError("You cant update an avatar image that is not primary") if instance.primary is True: # Find the next avatar, and set it as the new primary @@ -94,17 +93,16 @@ class AvatarViewSets(viewsets.ModelViewSet): self.perform_update(serializer) invalidate_cache(request.user) message = _("Successfully updated your avatar.") - if getattr(instance, '_prefetched_objects_cache', None): + if getattr(instance, "_prefetched_objects_cache", None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} - context_data = { - 'message': message, - 'data': serializer.data - } + context_data = {"message": message, "data": serializer.data} return Response(context_data) - @action(['GET'], detail=False, url_path='render_primary', name='Render Primary Avatar') + @action( + ["GET"], detail=False, url_path="render_primary", name="Render Primary Avatar" + ) def render_primary(self, request, *args, **kwargs): """ @@ -117,19 +115,20 @@ class AvatarViewSets(viewsets.ModelViewSet): context_data = {} avatar_size = assign_width_or_height(request.query_params) - width = avatar_size.get('width') - height = avatar_size.get('height') + width = avatar_size.get("width") + height = avatar_size.get("height") primary_avatar = get_primary_avatar(request.user, width=width, height=height) if primary_avatar and primary_avatar.primary: - url = primary_avatar.avatar_url(width, height) else: url = get_default_avatar_url() if bool(request.query_params): - context_data.update({'message': 'Resize parameters not working for default avatar'}) + context_data.update( + {"message": "Resize parameters not working for default avatar"} + ) context_data.update({"image_url": request.build_absolute_uri(url)}) return Response(context_data) diff --git a/test_proj/test_proj/settings.py b/test_proj/test_proj/settings.py index 8fbad49..ea8507d 100644 --- a/test_proj/test_proj/settings.py +++ b/test_proj/test_proj/settings.py @@ -38,7 +38,7 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "avatar", - "rest_framework" + "rest_framework", ] MIDDLEWARE = [ diff --git a/test_proj/test_proj/urls.py b/test_proj/test_proj/urls.py index 2c7a6b5..93b0308 100644 --- a/test_proj/test_proj/urls.py +++ b/test_proj/test_proj/urls.py @@ -1,13 +1,13 @@ from django.conf import settings from django.conf.urls import include from django.contrib import admin -from django.views.static import serve from django.urls import re_path +from django.views.static import serve urlpatterns = [ re_path(r"^admin/", admin.site.urls), re_path(r"^avatar/", include("avatar.urls")), - re_path(r"^api/", include('avatar.api.urls')), + re_path(r"^api/", include("avatar.api.urls")), ]