Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / lib / api / utils.py @ b694875c

History | View | Annotate | Download (4.2 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import datetime
35
from dateutil.parser import parse as date_parse
36
from django.utils import simplejson as json
37

    
38
from django.conf import settings
39
from snf_django.lib.api import faults
40

    
41

    
42
class UTC(datetime.tzinfo):
43
    """
44
    Helper UTC time information object.
45
    """
46

    
47
    def utcoffset(self, dt):
48
        return datetime.timedelta(0)
49

    
50
    def tzname(self, dt):
51
        return 'UTC'
52

    
53
    def dst(self, dt):
54
        return datetime.timedelta(0)
55

    
56

    
57
def isoformat(d):
58
    """Return an ISO8601 date string that includes a timezone.
59

60
    >>> from datetime import datetime
61
    >>> d = datetime(2012, 8, 10, 00, 59, 59)
62
    >>> isoformat(d)
63
    '2012-08-10T00:59:59+00:00'
64
    """
65

    
66
    return d.replace(tzinfo=UTC()).isoformat()
67

    
68

    
69
def isoparse(s):
70
    """Parse an ISO8601 date string into a datetime object."""
71

    
72
    if not s:
73
        return None
74

    
75
    try:
76
        since = date_parse(s)
77
        utc_since = since.astimezone(UTC()).replace(tzinfo=None)
78
    except ValueError:
79
        raise faults.BadRequest('Invalid changes-since parameter.')
80

    
81
    now = datetime.datetime.now()
82
    if utc_since > now:
83
        raise faults.BadRequest('changes-since value set in the future.')
84

    
85
    if now - utc_since > datetime.timedelta(seconds=settings.POLL_LIMIT):
86
        raise faults.BadRequest('Too old changes-since value.')
87

    
88
    return utc_since
89

    
90

    
91
def get_request_dict(request):
92
    """Return data sent by the client as python dictionary.
93

94
    Only JSON format is supported
95

96
    """
97
    data = request.body
98
    content_type = request.META.get("CONTENT_TYPE")
99
    if content_type is None:
100
        raise faults.BadRequest("Missing Content-Type header field")
101
    if content_type.startswith("application/json"):
102
        try:
103
            return json.loads(data)
104
        except ValueError:
105
            raise faults.BadRequest("Invalid JSON data")
106
    else:
107
        raise faults.BadRequest("Unsupported Content-type: '%s'" %
108
                                content_type)
109

    
110

    
111
def prefix_pattern(prefix, append_slash=True):
112
    """Return a reliable urls.py pattern from a prefix"""
113
    prefix = prefix.strip('/')
114
    if prefix and append_slash:
115
        prefix += '/'
116
    pattern = '^' + prefix
117
    return pattern
118

    
119

    
120
def filter_modified_since(request, objects):
121
    """Filter DB objects based on 'changes-since' request parameter.
122

123
    Parse request for 'changes-since' parameter and get only the DB objects
124
    that have been updated after that time. Otherwise, return the non-deleted
125
    objects.
126

127
    """
128
    since = isoparse(request.GET.get("changes-since"))
129
    if since:
130
        modified_objs = objects.filter(updated__gte=since)
131
        if not modified_objs:
132
            raise faults.NotModified()
133
        return modified_objs
134
    else:
135
        return objects.filter(deleted=False)