Skip to content

Commit

Permalink
Encapsulate speedinfo settings in a single object.
Browse files Browse the repository at this point in the history
  • Loading branch information
catcombo committed Dec 9, 2018
1 parent cc1e44e commit f49d2ed
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 43 deletions.
15 changes: 8 additions & 7 deletions speedinfo/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, HttpResponse

from speedinfo import profiler, settings
from speedinfo import profiler
from speedinfo.models import ViewProfiler
from speedinfo.settings import speedinfo_settings

try:
from django.urls import reverse # Django >= 1.10
Expand Down Expand Up @@ -55,17 +56,17 @@ def __init__(self, *args, **kwargs):
super(ViewProfilerAdmin, self).__init__(*args, **kwargs)
self.list_display = []

for rc in settings.SPEEDINFO_REPORT_COLUMNS_FORMAT:
if rc.attr_name in settings.SPEEDINFO_REPORT_COLUMNS:
for rc in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS_FORMAT:
if rc.attr_name in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS:
method_name = '{}_wrapper'.format(rc.attr_name)
setattr(self, method_name, field_wrapper(rc))
self.list_display.append(method_name)

def get_queryset(self, request):
qs = super(ViewProfilerAdmin, self).get_queryset(request)

for rc in settings.SPEEDINFO_REPORT_COLUMNS_FORMAT:
if (rc.attr_name in settings.SPEEDINFO_REPORT_COLUMNS) and not isinstance(rc.order_field, str):
for rc in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS_FORMAT:
if (rc.attr_name in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS) and not isinstance(rc.order_field, str):
qs = qs.annotate(**{
rc.attr_name: rc.order_field
})
Expand Down Expand Up @@ -106,8 +107,8 @@ def export(self, request):
:rtype: :class:`django.http.HttpResponse`
"""
output = StringIO()
export_columns = list(filter(lambda col: col.attr_name in settings.SPEEDINFO_REPORT_COLUMNS,
settings.SPEEDINFO_REPORT_COLUMNS_FORMAT))
export_columns = list(filter(lambda col: col.attr_name in speedinfo_settings.SPEEDINFO_REPORT_COLUMNS,
speedinfo_settings.SPEEDINFO_REPORT_COLUMNS_FORMAT))

csv_writer = csv.writer(output)
csv_writer.writerow([col.name for col in export_columns])
Expand Down
4 changes: 2 additions & 2 deletions speedinfo/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.core.cache import InvalidCacheBackendError
from django.utils.module_loading import import_string

from speedinfo.settings import SPEEDINFO_CACHED_RESPONSE_ATTR_NAME
from speedinfo.settings import speedinfo_settings


def proxy_cache(location, params):
Expand All @@ -24,7 +24,7 @@ def get(self, key, default=None, version=None):

# Sets the flag for marking response from cache
if (response is not None) and key.startswith('views.decorators.cache.cache_page'):
setattr(response, SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, True)
setattr(response, speedinfo_settings.SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, True)

return response

Expand Down
7 changes: 4 additions & 3 deletions speedinfo/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from timeit import default_timer

from django.db import connection
from speedinfo import profiler, settings
from speedinfo import profiler
from speedinfo.settings import speedinfo_settings

try:
from django.urls import resolve, Resolver404 # Django >= 1.10
Expand All @@ -25,7 +26,7 @@ def __init__(self, get_response=None):
self.force_debug_cursor = False
self.start_time = 0
self.existing_sql_count = 0
self.exclude_urls_re = [re.compile(pattern) for pattern in settings.SPEEDINFO_EXCLUDE_URLS]
self.exclude_urls_re = [re.compile(pattern) for pattern in speedinfo_settings.SPEEDINFO_EXCLUDE_URLS]

def match_exclude_urls(self, path):
"""Looks for a match requested page url to the exclude urls list.
Expand Down Expand Up @@ -95,7 +96,7 @@ def process_response(self, request, response):
# Collects request and response params
view_name = self.get_view_name(request)
is_anon_call = request.user.is_anonymous() if callable(request.user.is_anonymous) else request.user.is_anonymous
is_cache_hit = getattr(response, settings.SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, False)
is_cache_hit = getattr(response, speedinfo_settings.SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, False)

# Saves profiler data
profiler.data.add(view_name, request.method, is_anon_call, is_cache_hit, sql_time, sql_count, view_execution_time)
Expand Down
60 changes: 38 additions & 22 deletions speedinfo/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,42 @@
from django.conf import settings
from django.db.models import ExpressionWrapper, F, FloatField, IntegerField

SPEEDINFO_CACHED_RESPONSE_ATTR_NAME = getattr(settings, 'SPEEDINFO_CACHED_RESPONSE_ATTR_NAME', 'is_cached')
SPEEDINFO_EXCLUDE_URLS = getattr(settings, 'SPEEDINFO_EXCLUDE_URLS', [])
SPEEDINFO_REPORT_COLUMNS = getattr(settings, 'SPEEDINFO_REPORT_COLUMNS', (
'view_name', 'method', 'anon_calls_ratio', 'cache_hits_ratio',
'sql_count_per_call', 'sql_time_ratio', 'total_calls', 'time_per_call', 'total_time'))

ReportColumnFormat = namedtuple('ReportColumnFormat', ['name', 'format', 'attr_name', 'order_field'])
SPEEDINFO_REPORT_COLUMNS_FORMAT = [
ReportColumnFormat('View name', '{}', 'view_name', 'view_name'),
ReportColumnFormat('HTTP method', '{}', 'method', 'method'),
ReportColumnFormat('Anonymous calls', '{:.1f}%', 'anon_calls_ratio',
ExpressionWrapper(100.0 * F('anon_calls') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('Cache hits', '{:.1f}%', 'cache_hits_ratio',
ExpressionWrapper(100.0 * F('cache_hits') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('SQL queries per call', '{}', 'sql_count_per_call',
ExpressionWrapper(F('sql_total_count') / F('total_calls'), output_field=IntegerField())),
ReportColumnFormat('SQL time', '{:.1f}%', 'sql_time_ratio',
ExpressionWrapper(100.0 * F('sql_total_time') / F('total_time'), output_field=FloatField())),
ReportColumnFormat('Total calls', '{}', 'total_calls', 'total_calls'),
ReportColumnFormat('Time per call', '{:.8f}', 'time_per_call',
ExpressionWrapper(F('total_time') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('Total time', '{:.4f}', 'total_time', 'total_time'),
]

DEFAULTS = {
'SPEEDINFO_CACHED_RESPONSE_ATTR_NAME': 'is_cached',
'SPEEDINFO_EXCLUDE_URLS': [],
'SPEEDINFO_REPORT_COLUMNS': (
'view_name', 'method', 'anon_calls_ratio', 'cache_hits_ratio',
'sql_count_per_call', 'sql_time_ratio', 'total_calls', 'time_per_call', 'total_time'),
'SPEEDINFO_REPORT_COLUMNS_FORMAT': [
ReportColumnFormat('View name', '{}', 'view_name', 'view_name'),
ReportColumnFormat('HTTP method', '{}', 'method', 'method'),
ReportColumnFormat('Anonymous calls', '{:.1f}%', 'anon_calls_ratio',
ExpressionWrapper(100.0 * F('anon_calls') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('Cache hits', '{:.1f}%', 'cache_hits_ratio',
ExpressionWrapper(100.0 * F('cache_hits') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('SQL queries per call', '{}', 'sql_count_per_call',
ExpressionWrapper(F('sql_total_count') / F('total_calls'), output_field=IntegerField())),
ReportColumnFormat('SQL time', '{:.1f}%', 'sql_time_ratio',
ExpressionWrapper(100.0 * F('sql_total_time') / F('total_time'), output_field=FloatField())),
ReportColumnFormat('Total calls', '{}', 'total_calls', 'total_calls'),
ReportColumnFormat('Time per call', '{:.8f}', 'time_per_call',
ExpressionWrapper(F('total_time') / F('total_calls'), output_field=FloatField())),
ReportColumnFormat('Total time', '{:.4f}', 'total_time', 'total_time'),
]
}


class SpeedinfoSettings:
def __init__(self, defaults=None):
self.defaults = defaults or DEFAULTS

def __getattr__(self, name):
if name not in self.defaults:
raise AttributeError('Invalid setting: "{}"'.format(name))

return getattr(settings, name, self.defaults.get(name))


speedinfo_settings = SpeedinfoSettings()
23 changes: 16 additions & 7 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.core.cache import cache
from django.test import TestCase, override_settings

from speedinfo import profiler, settings
from speedinfo import profiler
from speedinfo.models import ViewProfiler

try:
Expand All @@ -17,11 +17,11 @@
from django.core.urlresolvers import reverse


@override_settings(SPEEDINFO_EXCLUDE_URLS=[])
class ProfilerTest(TestCase):
def setUp(self):
cache.clear()
profiler.is_on = True
settings.SPEEDINFO_EXCLUDE_URLS = []

self.class_view_url = reverse('class-view')
self.cached_class_view_url = reverse('cached-class-view')
Expand Down Expand Up @@ -158,9 +158,18 @@ def test_reset(self):
profiler.data.reset()
self.assertFalse(ViewProfiler.objects.exists())

def test_exclude_urls(self):
settings.SPEEDINFO_EXCLUDE_URLS = [reverse('func-view')]

@override_settings(SPEEDINFO_EXCLUDE_URLS=[reverse('func-view')])
class ExcludeURLConditionTest(TestCase):
def setUp(self):
cache.clear()
profiler.is_on = True

self.class_view_url = reverse('class-view')
self.func_view_url = reverse('func-view')
self.cached_func_view_url = reverse('cached-func-view')

def test_exclude_urls(self):
self.client.get(self.func_view_url)
self.assertFalse(ViewProfiler.objects.exists())

Expand All @@ -171,10 +180,10 @@ def test_exclude_urls(self):
self.assertTrue(ViewProfiler.objects.exists())


@override_settings(SPEEDINFO_EXCLUDE_URLS=[reverse('admin:index')])
class ProfilerAdminTest(TestCase):
def setUp(self):
cache.clear()
settings.SPEEDINFO_EXCLUDE_URLS = [reverse('admin:index')]

User.objects.create_superuser(username='admin', email='', password='123456')
self.client.login(username='admin', password='123456')
Expand Down Expand Up @@ -207,9 +216,9 @@ def test_export(self):
self.assertEqual(output, 'View name,HTTP method,Anonymous calls,Cache hits,SQL queries per call,SQL time,Total calls,Time per call,Total time\r\n'
'app.view_name,GET,100.0%,50.0%,3,50.0%,2,1.00000000,2.0000\r\n')

@override_settings(SPEEDINFO_REPORT_COLUMNS=('view_name', 'method', 'total_calls', 'time_per_call', 'total_time'))
def test_custom_columns_export(self):
# Export with custom columns
settings.SPEEDINFO_REPORT_COLUMNS = ('view_name', 'method', 'total_calls', 'time_per_call', 'total_time')

response = self.client.get(reverse('admin:speedinfo-profiler-export'))
output = response.content.decode()

Expand Down
4 changes: 2 additions & 2 deletions tests/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.views.generic import View
from django.views.decorators.cache import cache_page

from speedinfo.settings import SPEEDINFO_CACHED_RESPONSE_ATTR_NAME
from speedinfo.settings import speedinfo_settings


class ClassBasedView(View):
Expand All @@ -19,7 +19,7 @@ def get(self, request, *args, **kwargs):
class CachedView(View):
def get(self, request, *args, **kwargs):
response = HttpResponse()
setattr(response, SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, True)
setattr(response, speedinfo_settings.SPEEDINFO_CACHED_RESPONSE_ATTR_NAME, True)
return response


Expand Down

0 comments on commit f49d2ed

Please sign in to comment.