Source code for mep.common.utils
from collections import OrderedDict
from functools import wraps
import uuid
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
[docs]def absolutize_url(local_url, request=None):
'''Convert a local url to an absolute url, with scheme and server name,
based on the current configured :class:`~django.contrib.sites.models.Site`.
:param local_url: local url to be absolutized, e.g. something generated by
:meth:`~django.urls.reverse`
'''
if local_url.startswith('https'):
return local_url
# add scheme and server (i.e., the http://example.com) based
# on the django Sites infrastructure.
root = Site.objects.get_current().domain
# add http:// if necessary, since most sites docs
# suggest using just the domain name
if not root.startswith('http'):
# if in debug mode and request is passed in, use
# the current scheme (i.e. http for localhost/runserver)
if settings.DEBUG and request:
root = '%s://%s' % (request.scheme, root)
# assume https for production sites
else:
root = 'https://' + root
# make sure there is no double slash between site url and local url
if local_url.startswith('/'):
root = root.rstrip('/')
return root + local_url
[docs]def alpha_pagelabels(paginator, objects, attr_meth, max_chars=None):
"""Generate abbreviated, alphabetical page labels for pagination items.
Label format should be something like 'Ab - Ad', 'Ard - Art'.
:param paginator: a django paginator
:param objects: the complete list of objects paginated by the paginator
:param attr_meth: method or lambda to retrieve the attribute on the
object that should be used for page labels
:returns: :class:`~collections.OrderedDict` where keys are page
numbers and values are page labels
"""
# adapted from Emory findingaids
# https://github.com/emory-libraries/findingaids/blob/master/findingaids/fa/utils.py#L163
page_labels = OrderedDict()
labels = []
if paginator.count <= 1:
# if there is not enough content to paginate, bail out
return page_labels
# get all labels for first and last objects on each page
for i in range(paginator.num_pages):
page = paginator.page(i + 1) # paginator page is 1-based
# get objects at start & end of each page (paginator index also 1-based)
page_label = attr_meth(objects[page.start_index() - 1])
if max_chars:
page_label = page_label[:max_chars]
labels.append(page_label)
# don't go beyond the end of the actual number of objects
end_index = min(page.end_index() - 1, paginator.count)
# add end label only if not the same as first
# (e.g., page of a single item)
if page.start_index() - 1 != end_index:
end_label = attr_meth(objects[end_index])
if max_chars:
end_label = end_label[:max_chars]
labels.append(end_label)
# if maximum length not specified, use abbreviation logic
if not max_chars:
labels = abbreviate_labels(labels)
# iterate over the abbreviate labels in pairs and generate page labels
for i in range(0, len(labels), 2):
page_index = int((i + 2) / 2)
try:
page_labels[page_index] = '%s – %s' % \
(labels[i].strip(),
labels[i + 1].strip())
except IndexError:
# paginator was not created with orphan protection,
# it's possible we could get a single item at the end
page_labels[page_index] = labels[i].strip()
return page_labels
[docs]def abbreviate_labels(labels):
'''Abbreviate labels so they are as short as possible but distinct from
preceding and following labels. Optionally used with
:meth:`alpha_pagelabels`
'''
abbreviated_labels = []
# iterate over all labels and generate the shortest distinct version
for i, label in enumerate(labels):
next_label = labels[i + 1] if i + 1 < len(labels) else ''
prev_label = labels[i - 1] if i > 0 else ''
# In the rare case that two labels are *exactly* the same,
# just add the full label.
if label in (next_label, prev_label):
abbreviated_labels.append(label)
continue
# iterate through the label one letter at a time
# to determine the minimum length needed to differentiate
# start with one letter, go up to full length of the label if necessary
abbr = ''
for j, char in enumerate(label, 1):
abbr += char # or abbr = label[:j]
# if abbreviation is different from neighboring labels
# at the current length, then use it
# - compare case-insensitively
# NOTE: should possibly ignore trailing punctuation here...
# possible to get "Gray" / "Gray,"
if abbr.lower() not in (next_label[:j].lower(),
prev_label[:j].lower()):
break
# if we don't break before the loop finishes, use
# the whole label
abbreviated_labels.append(abbr)
return abbreviated_labels
[docs]def login_temporarily_required(func):
'''Test decorator for views that have LoginRequiredOr404
enabled. Creates a user with no permissions on first run for a given
class, and logs in as that user before running the decorated test
method. Intended for use on Django TestCase class methods.
'''
@wraps(func)
def wrapper(testclass, *args, **kwargs):
# if no login info is set, create the user
if not hasattr(testclass, 'loginrequired_login_info'):
# define login required user
testclass.loginrequired_login_info = {
'username': 'loginrequired',
'password': str(uuid.uuid4())
}
User.objects.create_user(**testclass.loginrequired_login_info)
# login with test client as the user
testclass.client.login(**testclass.loginrequired_login_info)
# then call the test method
func(testclass, *args, **kwargs)
return wrapper