range_return()   B
last analyzed

Complexity

Conditions 5

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 5
c 3
b 0
f 0
dl 0
loc 31
rs 8.6693
1
# -*- coding: utf-8 -*-
2
"""
3
Utility functions to help with range handling.
4
5
.. versionadded:: 0.1.0
6
"""
7
8
import re
9
10
MAX_NUMBER_ITEMS = 5000
11
12
13
def parse_range_header(range):
14
    """
15
    Parse a range header as used by the dojo Json Rest store.
16
17
    :param str range: The content of the range header to be parsed. 
18
        eg. `items=0-9`
19
    :returns: A dict with keys start, finish and number or `False` if the
20
        range is invalid.
21
    """
22
    match = re.match('^items=([0-9]+)-([0-9]+)$', range)
23
24
    if match:
25
        start = int(match.group(1))
26
        finish = int(match.group(2))
27
28
        if finish < start:
29
            finish = start
30
        return {
31
            'start': start,
32
            'finish': finish,
33
            'count': finish - start + 1
34
        }
35
    else:
36
        return False
37
38
39
def range_return(request, items):
40
    """
41
    Determine what range of objects to return.
42
43
    Will check fot both `Range` and `X-Range` headers in the request and
44
    set both `Content-Range` and 'X-Content-Range' headers.
45
46
    :rtype: list
47
    """
48
    if ('Range' in request.headers):
49
        range = parse_range_header(request.headers['Range'])
50
    elif 'X-Range' in request.headers:
51
        range = parse_range_header(request.headers['X-Range'])
52
    else:
53
        range = {
54
            'start': 0,
55
            'finish': MAX_NUMBER_ITEMS - 1,
56
            'count': MAX_NUMBER_ITEMS
57
        }
58
    filtered = items[range['start']:range['finish'] + 1]
59
    if len(filtered) < range['count']:
60
        # Something was stripped, deal with it
61
        range['count'] = len(filtered)
62
        range['finish'] = range['start'] + range['count'] - 1
63
    if range['finish'] - range['start'] + 1 >= MAX_NUMBER_ITEMS:
64
        range['finish'] = range['start'] + MAX_NUMBER_ITEMS - 1
65
        filtered = items[range['start']:range['finish'] + 1]
66
67
    request.response.headers['Content-Range'] = 'items %d-%d/%d' % (range['start'], range['finish'], len(items))
68
    request.response.headers['X-Content-Range'] = request.response.headers['Content-Range']
69
    return filtered
70
71
72
def set_http_caching(request, gateway='crab', region='permanent'):
73
    """
74
    Set an HTTP Cache Control header on a request.
75
76
    :param pyramid.request.Request request: Request to set headers on.
77
    :param str gateway: What gateway are we caching for? Defaults to `crab`.
78
    :param str region: What caching region to use? Defaults to `permanent`.
79
    :rtype: pyramid.request.Request
80
    """
81
    crabpy_exp = request.registry.settings.get('crabpy.%s.cache_config.%s.expiration_time' % (gateway, region), None)
82
    if crabpy_exp is None:
83
        return request
84
    ctime = int(int(crabpy_exp) * 1.05)
85
    request.response.cache_expires(ctime, public=True)
86
    return request
87