Skip to content

Commit

Permalink
prospector cleanup onadata/libs
Browse files Browse the repository at this point in the history
  • Loading branch information
ukanga committed Aug 23, 2022
1 parent 86a856a commit 7e0c7be
Show file tree
Hide file tree
Showing 14 changed files with 540 additions and 365 deletions.
1 change: 1 addition & 0 deletions .prospector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pylint:
options:
extension-pkg-allow-list:
- ujson
- lxml.etree

mccabe:
run: false
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-allow-list=ujson
extension-pkg-allow-list=ujson,lxml.etree

# Add files or directories to the blacklist. They should be base names, not
# paths.
Expand Down
6 changes: 4 additions & 2 deletions onadata/apps/api/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@
get_role_in_org,
is_organization,
)
from onadata.libs.serializers.project_serializer import ProjectSerializer
from onadata.libs.utils.api_export_tools import (
custom_response_handler, get_metadata_format
custom_response_handler,
get_metadata_format,
)
from onadata.libs.utils.cache_tools import (
PROJ_BASE_FORMS_CACHE,
Expand Down Expand Up @@ -491,7 +493,7 @@ def id_string_exists_in_account():
# Ensure the cached project is the updated version.
# Django lazy loads related objects as such we need to
# ensure the project retrieved is up to date.
reset_project_cache(xform.project, request)
reset_project_cache(xform.project, request, ProjectSerializer)
return xform


Expand Down
25 changes: 15 additions & 10 deletions onadata/libs/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from oauth2_provider.models import AccessToken
from oauth2_provider.oauth2_validators import OAuth2Validator
from oauth2_provider.settings import oauth2_settings
from oidc.utils import authenticate_sso
from rest_framework import exceptions
from rest_framework.authentication import (
BaseAuthentication,
Expand All @@ -29,16 +30,10 @@
)
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import AuthenticationFailed
from oidc.utils import authenticate_sso

from onadata.apps.api.models.temp_token import TempToken
from onadata.apps.api.tasks import send_account_lockout_email
from onadata.libs.utils.cache_tools import (
LOCKOUT_IP,
LOGIN_ATTEMPTS,
cache,
safe_key,
)
from onadata.libs.utils.cache_tools import LOCKOUT_IP, LOGIN_ATTEMPTS, cache, safe_key
from onadata.libs.utils.common_tags import API_TOKEN
from onadata.libs.utils.email import get_account_lockout_email_data

Expand Down Expand Up @@ -152,7 +147,7 @@ def authenticate(self, request):
raise exceptions.AuthenticationFailed(error_msg)
if len(auth) > 2:
error_msg = _(
"Invalid token header. " "Token string should not contain spaces."
"Invalid token header. Token string should not contain spaces."
)
raise exceptions.AuthenticationFailed(error_msg)

Expand Down Expand Up @@ -240,7 +235,7 @@ class SSOHeaderAuthentication(BaseAuthentication):
cookie or HTTP_SSO header.
"""

def authenticate(self, request): # pylint: disable=no-self-use
def authenticate(self, request):
return authenticate_sso(request)


Expand Down Expand Up @@ -367,9 +362,19 @@ class MasterReplicaOAuth2Validator(OAuth2Validator):
"""
Custom OAuth2Validator class that takes into account replication lag
between Master & Replica databases
https://github.com/jazzband/django-oauth-toolkit/blob/3bde632d5722f1f85ffcd8277504955321f00fff/oauth2_provider/oauth2_validators.py#L49
https://github.com/jazzband/django-oauth-toolkit/blob/
3bde632d5722f1f85ffcd8277504955321f00fff/oauth2_provider/oauth2_validators.py#L49
"""

def introspect_token(self, token, token_type_hint, request, *args, **kwargs):
pass

def validate_silent_authorization(self, request):
pass

def validate_silent_login(self, request):
pass

def validate_bearer_token(self, token, scopes, request):
if not token:
return False
Expand Down
12 changes: 10 additions & 2 deletions onadata/libs/baseviewset.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
class DefaultBaseViewset(object):
pass
# -*- coding: utf-8 -*-
"""
The DefaultBaseViewset class
"""


class DefaultBaseViewset:
"""
The DefaultBaseViewset class
"""
24 changes: 11 additions & 13 deletions onadata/libs/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,24 @@
"""
from uuid import UUID

import six

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import Http404
from django.shortcuts import get_object_or_404

import six
from django_filters import rest_framework as django_filter_filters
from rest_framework import filters
from rest_framework_guardian.filters import ObjectPermissionsFilter

from onadata.apps.api.models import OrganizationProfile, Team
from onadata.apps.logger.models import Instance, Project, XForm
from onadata.apps.viewer.models import Export
from onadata.libs.utils.numeric import int_or_parse_error
from onadata.libs.utils.common_tags import MEDIA_FILE_TYPES
from onadata.libs.permissions import exclude_items_from_queryset_using_xform_meta_perms

from onadata.libs.utils.common_tags import MEDIA_FILE_TYPES
from onadata.libs.utils.numeric import int_or_parse_error

# pylint: disable=invalid-name
User = get_user_model()
Expand Down Expand Up @@ -55,7 +54,7 @@ def filter_queryset(self, request, queryset, view):
if form_id:
if lookup_field == "pk":
int_or_parse_error(
form_id, "Invalid form ID. It must be a positive" " integer"
form_id, "Invalid form ID. It must be a positive integer"
)

try:
Expand Down Expand Up @@ -104,7 +103,6 @@ class XFormListObjectPermissionFilter(AnonDjangoObjectPermissionFilter):
class XFormListXFormPKFilter:
"""Filter forms via 'xform_pk' param."""

# pylint: disable=no-self-use
def filter_queryset(self, request, queryset, view):
"""Returns an XForm queryset filtered by the 1xform_pk' param."""
xform_pk = view.kwargs.get("xform_pk")
Expand Down Expand Up @@ -393,7 +391,7 @@ def _instance_filter(self, request, view, keyword):
for object_id in [instance_id, project_id]:
int_or_parse_error(
object_id,
"Invalid value for instanceid. It must be" " a positive integer.",
"Invalid value for instanceid. It must be a positive integer.",
)

instance = get_object_or_404(Instance, pk=instance_id)
Expand Down Expand Up @@ -506,7 +504,7 @@ def filter_queryset(self, request, queryset, view):
if instance_id:
int_or_parse_error(
instance_id,
"Invalid value for instance_id. It must be" " a positive integer.",
"Invalid value for instance_id. It must be a positive integer.",
)
instance = get_object_or_404(Instance, pk=instance_id)
queryset = queryset.filter(instance=instance)
Expand Down Expand Up @@ -615,8 +613,8 @@ class UserProfileFilter(filters.BaseFilterBackend):
"""Filter by the ``users`` query parameter."""

def filter_queryset(self, request, queryset, view):
"""Filter by the ``users`` query parameter - returns a queryset of only the users
in the users parameter when `view.action == "list"`"""
"""Filter by the ``users`` query parameter - returns a queryset of only the
users in the users parameter when `view.action == "list"`"""
if view.action == "list":
users = request.GET.get("users")
if users:
Expand All @@ -641,7 +639,7 @@ def filter_queryset(self, request, queryset, view):
if instance_id:
int_or_parse_error(
instance_id,
"Invalid value for instance_id. It must be" " a positive integer",
"Invalid value for instance_id. It must be a positive integer",
)

instance = get_object_or_404(Instance, pk=instance_id)
Expand Down Expand Up @@ -692,7 +690,7 @@ def filter_queryset(self, request, queryset, view):
class PublicDatasetsFilter:
"""Public data set filter where the share attribute is True"""

# pylint: disable=unused-argument,no-self-use
# pylint: disable=unused-argument
def filter_queryset(self, request, queryset, view):
"""Return a queryset of shared=True data if the user is anonymous."""
if request and request.user.is_anonymous:
Expand Down
93 changes: 65 additions & 28 deletions onadata/libs/pagination.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,57 @@
from django.core.paginator import Paginator
# -*- coding=utf-8 -*-
"""
Pagination classes.
"""
from django.conf import settings
from django.core.paginator import Paginator
from django.db.models import QuerySet

from rest_framework.pagination import (
PageNumberPagination, InvalidPage, NotFound, replace_query_param)
InvalidPage,
NotFound,
PageNumberPagination,
replace_query_param,
)
from rest_framework.request import Request


class StandardPageNumberPagination(PageNumberPagination):
"""The Standard PageNumberPagination class
Set's the default ``page_size`` to 1000 with a maximum page_size of 10,000 records
per page.
"""

page_size = 1000
page_size_query_param = 'page_size'
max_page_size = getattr(
settings, "STANDARD_PAGINATION_MAX_PAGE_SIZE", 10000)
page_size_query_param = "page_size"
max_page_size = getattr(settings, "STANDARD_PAGINATION_MAX_PAGE_SIZE", 10000)

def get_first_page_link(self):
"""Returns the URL to the first page."""
if self.page.number == 1:
return None

url = self.request.build_absolute_uri()

return replace_query_param(url, self.page_query_param, 1)

def get_last_page_link(self):
"""Returns the URL to the last page."""
if self.page.number == self.paginator.num_pages:
return None

url = self.request.build_absolute_uri()
return replace_query_param(
url, self.page_query_param, self.paginator.num_pages)

def generate_link_header(
self, request: Request, queryset: QuerySet
):
return replace_query_param(url, self.page_query_param, self.paginator.num_pages)

def generate_link_header(self, request: Request, queryset: QuerySet):
"""Generates pagination headers for a HTTP response object"""
links = []
page_size = self.get_page_size(request)
if not page_size:
return {}
page_number = request.query_params.get(self.page_query_param, 1)
# pylint: disable=attribute-defined-outside-init
self.paginator = self.django_paginator_class(queryset, page_size)
self.request = request

Expand All @@ -42,45 +61,62 @@ def generate_link_header(
return {}

for rel, link in (
('prev', self.get_previous_link()),
('next', self.get_next_link()),
('last', self.get_last_page_link()),
('first', self.get_first_page_link())):
("prev", self.get_previous_link()),
("next", self.get_next_link()),
("last", self.get_last_page_link()),
("first", self.get_first_page_link()),
):
if link:
links.append(f'<{link}>; rel="{rel}"')

return {'Link': ', '.join(links)}
return {"Link": ", ".join(links)}


class CountOverridablePaginator(Paginator):
"""Count override Paginator
Allows overriding the count especially in the event it may be expensive request.
"""

# pylint: disable=too-many-arguments
def __init__(
self, object_list, per_page,
orphans: int = 0, allow_empty_first_page: bool = True,
count_override: int = None) -> None:
self,
object_list,
per_page,
orphans: int = 0,
allow_empty_first_page: bool = True,
count_override: int = None,
) -> None:
self.count_override = count_override
super().__init__(
object_list, per_page,
orphans=orphans, allow_empty_first_page=allow_empty_first_page)
object_list,
per_page,
orphans=orphans,
allow_empty_first_page=allow_empty_first_page,
)

@property
def count(self):
if self.count_override:
return self.count_override
return super().count
return super().count()


class CountOverridablePageNumberPagination(StandardPageNumberPagination):
"""Count override PageNumberPagination
Allows overriding the count especially in the event it may be expensive request.
"""

django_paginator_class = CountOverridablePaginator

def paginate_queryset(self, queryset, request, view, count=None):
# pylint: disable=attribute-defined-outside-init
page_size = self.get_page_size(request)
if not page_size:
return None

paginator = self.django_paginator_class(
queryset,
page_size,
count_override=count
queryset, page_size, count_override=count
)
page_number = request.query_params.get(self.page_query_param, 1)
if page_number in self.last_page_strings:
Expand All @@ -89,9 +125,10 @@ def paginate_queryset(self, queryset, request, view, count=None):
try:
self.page = paginator.page(page_number)
except InvalidPage as exc:
msg = self.invalid_page_message.format(page_number=page_number,
message=str(exc))
raise NotFound(msg)
msg = self.invalid_page_message.format(
page_number=page_number, message=str(exc)
)
raise NotFound(msg) from exc

if paginator.num_pages > 1 and self.template is not None:
self.display_page_controls = True
Expand Down
Loading

0 comments on commit 7e0c7be

Please sign in to comment.