get_placeholders()   F
last analyzed

Complexity

Conditions 10

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
dl 0
loc 35
rs 3.1304
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A keep() 0 2 1

How to fix   Complexity   

Complexity

Complex classes like get_placeholders() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
"""A collection of functions for Page CMS"""
3
4
import re
5
import unicodedata
6
7
from django.conf import settings as django_settings
8
from django.utils import timezone
9
from django.template import Context
10
from django import template
11
from django.utils.encoding import force_text
12
from django.utils.safestring import SafeText, mark_safe
13
from django.utils.functional import allow_lazy
14
from django.utils import six
15
16
from datetime import datetime
17
18
dummy_context = Context()
19
20
21
def get_now():
22
    if django_settings.USE_TZ:
23
        return datetime.utcnow().replace(tzinfo=timezone.utc)
24
    else:
25
        return datetime.now()
26
27
28
def get_placeholders(template_name):
29
    """Return a list of PlaceholderNode found in the given template.
30
31
    :param template_name: the name of the template file
32
    """
33
    dummy_context.template = template.Template("")
34
    try:
35
        temp_wrapper = template.loader.get_template(template_name)
36
    except template.TemplateDoesNotExist:
37
        return []
38
39
    plist, blist = [], []
40
    temp = temp_wrapper.template
41
    _placeholders_recursif(temp.nodelist, plist, blist)
42
43
    previous = {}
44
    block_to_remove = []
45
    for block in blist:
46
        if block.name in previous:
47
            if not hasattr(block, 'has_super_var'):
48
                block_to_remove.append(previous[block.name])
49
        previous[block.name] = block
50
51
    def keep(p):
52
        return p.found_in_block not in block_to_remove
53
54
    placeholders = [p for p in plist if keep(p)]
55
    names = []
56
    pfiltered = []
57
    for p in placeholders:
58
        if p.ctype not in names:
59
            pfiltered.append(p)
60
            names.append(p.ctype)
61
62
    return pfiltered
63
64
65
def _placeholders_recursif(nodelist, plist, blist):
66
    """Recursively search into a template node list for PlaceholderNode
67
    node."""
68
    # I needed to do this lazy import to compile the documentation
69
    from django.template.loader_tags import BlockNode
70
71
    if len(blist):
72
        block = blist[-1]
73
    else:
74
        block = None
75
76
    for node in nodelist:
77
78
        if isinstance(node, BlockNode):
79
            if node not in blist:
80
                blist.append(node)
81
            if not block:
82
                block = node
83
84
        if block:
85
            if isinstance(node, template.base.VariableNode):
86
                if(node.filter_expression.var.var == u'block.super'):
87
                    block.has_super_var = True
88
89
        # extends node?
90
        if hasattr(node, 'parent_name'):
91
            # I do not know why I did this... but the tests are guarding it
92
            dummy_context2 = Context()
93
            dummy_context2.template = template.Template("")
94
            _placeholders_recursif(node.get_parent(dummy_context2).nodelist,
95
                                   plist, blist)
96
        # include node?
97
        elif hasattr(node, 'template') and hasattr(node.template, 'nodelist'):
98
            _placeholders_recursif(node.template.nodelist, plist, blist)
99
100
        # Is it a placeholder?
101
        if hasattr(node, 'page') and hasattr(node, 'parsed') and \
102
                hasattr(node, 'as_varname') and hasattr(node, 'name') \
103
                and hasattr(node, 'section'):
104
            if block:
105
                node.found_in_block = block
106
            plist.append(node)
107
            node.render(dummy_context)
108
109
        for key in ('nodelist', 'nodelist_true', 'nodelist_false'):
110
111
            if hasattr(node, key):
112
                try:
113
                    _placeholders_recursif(getattr(node, key), plist, blist)
114
                except:
115
                    pass
116
117
118
def normalize_url(url):
119
    """Return a normalized url with trailing and without leading slash.
120
121
     >>> normalize_url(None)
122
     '/'
123
     >>> normalize_url('/')
124
     '/'
125
     >>> normalize_url('/foo/bar')
126
     '/foo/bar'
127
     >>> normalize_url('foo/bar')
128
     '/foo/bar'
129
     >>> normalize_url('/foo/bar/')
130
     '/foo/bar'
131
    """
132
    if not url or len(url) == 0:
133
        return '/'
134
    if not url.startswith('/'):
135
        url = '/' + url
136
    if len(url) > 1 and url.endswith('/'):
137
        url = url[0:len(url) - 1]
138
    return url
139
140
141
def slugify(value, allow_unicode=False):
142
    """
143
    Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.
144
    Remove characters that aren't alphanumerics, underscores, or hyphens.
145
    Convert to lowercase. Also strip leading and trailing whitespace.
146
    Copyright: https://docs.djangoproject.com/en/1.9/_modules/django/utils/text/#slugify
147
    TODO: replace after stopping support for Django 1.8
148
    """
149
    value = force_text(value)
150
    if allow_unicode:
151
        value = unicodedata.normalize('NFKC', value)
152
        value = re.sub('[^\w\s-]', '', value, flags=re.U).strip().lower()
153
        return mark_safe(re.sub('[-\s]+', '-', value, flags=re.U))
154
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
155
    value = re.sub('[^\w\s-]', '', value).strip().lower()
156
    return mark_safe(re.sub('[-\s]+', '-', value))
157
158
slugify = allow_lazy(slugify, six.text_type, SafeText)
159