aboutsummaryrefslogtreecommitdiffstats
path: root/brevisurl/backends/local.py
blob: e09fb5318cba88533c3c1be87e91790409a8d473 (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
import math
import random
import logging

from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse

import brevisurl.settings
from brevisurl import Error
from brevisurl.backends.base import BaseBrevisUrlBackend
from brevisurl.models import ShortUrl


log = logging.getLogger(__name__)


class TokensExhaustedError(Error):
    """Exception is raised when tokens are exhausted."""


class BrevisUrlBackend(BaseBrevisUrlBackend):

    def shorten_url(self, original_url):
        """
        :raises: ImproperlyConfigured, django.core.exceptions.ValidationError
        :raises: brevisurl.backends.local.TokensExhaustedError
        """
        try:
            short_url = ShortUrl.objects.get(backend=self.class_path, original_url=original_url)
            log.info('Url "%s" already shortened to "%s"', original_url, short_url.shortened_url)
            return short_url
        except ShortUrl.DoesNotExist:
            pass

        try:
            short_url = ShortUrl()
            if self.kwargs.get('domain') is not None:
                # Domain is present in keyword arguments supplied by constructor.
                domain = self.kwargs.get('domain').rstrip('/')
            elif brevisurl.settings.LOCAL_BACKEND_DOMAIN is not None:
                # Domain is defined in settings.
                domain = brevisurl.settings.LOCAL_BACKEND_DOMAIN.rstrip('/')
            else:
                # Domain is taken from django site framework.
                domain = '{protocol:s}://{domain:s}'.format(protocol=brevisurl.settings.LOCAL_BACKEND_DOMAIN_PROTOCOL,
                                                            domain=Site.objects.get_current().domain)
            # Generating url path for shortened url.
            url_path = reverse('brevisurl_redirect', kwargs={'token': self.__generate_token()})
            if brevisurl.settings.LOCAL_BACKEND_STRIP_TOKEN_URL_SLASH:
                url_path = url_path.lstrip('/')
            # Saving newly generated shortened url.
            short_url.original_url = original_url
            short_url.shortened_url = '{domain:s}{url_path:s}'.format(domain=domain, url_path=url_path)
            short_url.backend = self.class_path
            short_url.save()
            log.info('Url "%s" shortened to "%s"', original_url, short_url.shortened_url)
            return short_url
        except Exception:
            if self.fail_silently:
                return None
            else:
                log.exception('Unknown exception raised while shortening url "%s"', original_url)
                raise

    def __generate_token(self):
        chars = brevisurl.settings.LOCAL_BACKEND_TOKEN_CHARS
        size = brevisurl.settings.LOCAL_BACKEND_TOKEN_LENGTH
        if ShortUrl.objects.count() >= math.pow(len(chars), size):
            raise TokensExhaustedError('Consider incrementing the token length or change the char list')
        random.shuffle(chars)
        while True:
            token = ''.join([random.choice(chars) for x in range(size)])
            if not ShortUrl.objects.filter(backend=self.class_path, shortened_url__endswith=token).count():
                break
        return token