summaryrefslogtreecommitdiffstats
path: root/main/utils.py
blob: 2dc066dae00d85b94e10871faf49b839e0739e9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
try:
    import pickle as pickle
except ImportError:
    import pickle

import hashlib
import markdown
from markdown.extensions import Extension
from pgpdump.packet import SignaturePacket

from django.core.cache import cache
from django.db import connections, router
from django.http import HttpResponse
from django.utils.timezone import now
from django.template.defaultfilters import slugify


def cache_function_key(func, args, kwargs):
    raw = [func.__name__, func.__module__, args, kwargs]
    pickled = pickle.dumps(raw, protocol=pickle.HIGHEST_PROTOCOL)
    key = hashlib.md5(pickled).hexdigest()
    return 'cache_function.' + func.__name__ + '.' + key


def cache_function(length):
    """
    A variant of the snippet posted by Jeff Wheeler at
    http://www.djangosnippets.org/snippets/109/

    Caches a function, using the function and its arguments as the key, and the
    return value as the value saved. It passes all arguments on to the
    function, as it should.

    The decorator itself takes a length argument, which is the number of
    seconds the cache will keep the result around.
    """
    def decorator(func):
        def inner_func(*args, **kwargs):
            key = cache_function_key(func, args, kwargs)
            value = cache.get(key)
            if value is not None:
                return value
            else:
                result = func(*args, **kwargs)
                cache.set(key, result, length)
                return result
        return inner_func
    return decorator


def clear_cache_function(func, args, kwargs):
    key = cache_function_key(func, args, kwargs)
    cache.delete(key)


def empty_response():
    empty = HttpResponse('')
    # designating response as 'streaming' forces ConditionalGetMiddleware to
    # not add a 'Content-Length: 0' header
    empty.streaming = True
    return empty


# utility to make a pair of django choices
make_choice = lambda l: [(str(m), str(m)) for m in l]


def set_created_field(sender, **kwargs):
    '''This will set the 'created' field on any object to the current UTC time
    if it is unset.
    Additionally, this will set the 'last_modified' field on any object to the
    current UTC time on any save of the object.
    For use as a pre_save signal handler.'''
    obj = kwargs['instance']
    time = now()
    if hasattr(obj, 'created') and not obj.created:
        obj.created = time
    if hasattr(obj, 'last_modified'):
        obj.last_modified = time


def find_unique_slug(model, title):
    '''Attempt to find a unique slug for this model with given title.'''
    existing = set(model.objects.values_list(
        'slug', flat=True).order_by().distinct())

    suffixed = slug = slugify(title)
    suffix = 0
    while suffixed in existing:
        suffix += 1
        suffixed = "%s-%d" % (slug, suffix)

    return suffixed


def database_vendor(model, mode='read'):
    if mode == 'read':
        database = router.db_for_read(model)
    elif mode == 'write':
        database = router.db_for_write(model)
    else:
        raise Exception('Invalid database mode specified')
    return connections[database].vendor


class EscapeHtml(Extension):
    def extendMarkdown(self, md, md_globals):
        del md.preprocessors['html_block']
        del md.inlinePatterns['html']


def parse_markdown(text, allow_html=False):
    if allow_html:
        return markdown.markdown(text, enable_attributes=False)
    ext = [EscapeHtml()]
    return markdown.markdown(text, extensions=ext, enable_attributes=False)


def groupby_preserve_order(iterable, keyfunc):
    '''Take an iterable and regroup using keyfunc to determine whether items
    belong to the same group. The order of the iterable is preserved and
    similar keys do not have to be consecutive. This means the earliest
    occurrence of a given key will determine the order of the lists in the
    returned list.'''
    seen_keys = {}
    result = []
    for item in iterable:
        key = keyfunc(item)

        group = seen_keys.get(key, None)
        if group is None:
            group = []
            seen_keys[key] = group
            result.append(group)

        group.append(item)

    return result


class PackageStandin(object):
    '''Resembles a Package object, and has a few of the same fields, but is
    really a link to a pkgbase that has no package with matching pkgname.'''
    def __init__(self, package):
        self.package = package
        self.pkgname = package.pkgbase

    def __getattr__(self, name):
        return getattr(self.package, name)

    def get_absolute_url(self):
        return '/packages/%s/%s/%s/' % (
                self.repo.name.lower(), self.arch.name, self.pkgbase)


class DependStandin(object):
    '''Resembles a Depend object, and has a few of the same fields, but is
    really a link to a base package rather than a single package.'''
    def __init__(self, depends):
        self._depends = depends
        first = depends[0]
        self.name = first.name
        self.version = first.version
        self.comparison = first.comparison
        self.description = first.description
        self.deptype = first.deptype
        self.pkg = first.pkg.base_package() or PackageStandin(first.pkg)


class SignatureWrapper(SignaturePacket):
    'Decode key_id from raw SignaturePacket'
    def __init__(self, packet):
        for field in ("sig_version", "creation_time", "expiration_time"):
            setattr(self, field, getattr(packet, field))
        self.key_id = packet.key_id.decode() if packet.key_id else None

# vim: set ts=4 sw=4 et: