ferme #443, ajout des filtres de regions pays disciplines thematiques
authorolivier larcheveque <olivier.larcheveque@u-ol.(none)>
Wed, 22 Sep 2010 15:46:13 +0000 (11:46 -0400)
committerolivier larcheveque <olivier.larcheveque@u-ol.(none)>
Wed, 22 Sep 2010 15:46:13 +0000 (11:46 -0400)
auf_savoirs_en_partage/savoirs/admin.py
auf_savoirs_en_partage/savoirs/templatetags/smart_if.py [new file with mode: 0644]
auf_savoirs_en_partage/templates/admin/filter.html [new file with mode: 0644]

index ea18bfa..c0a39e6 100644 (file)
@@ -90,7 +90,14 @@ class RecordAdmin(ReadOnlyAdminFields, admin.ModelAdmin):
     search_fields = []
     readonly_fields = []
 
-    list_filter = ('server', 'validated', 'pays', 'regions')
+    list_filter = (
+      'server',
+      'validated',
+      'pays',
+      'regions',
+      'disciplines',
+      'thematiques',
+      )
     list_display = (
       #OAI et extra AUF
       'title',
diff --git a/auf_savoirs_en_partage/savoirs/templatetags/smart_if.py b/auf_savoirs_en_partage/savoirs/templatetags/smart_if.py
new file mode 100644 (file)
index 0000000..a8fc194
--- /dev/null
@@ -0,0 +1,401 @@
+"""\r
+A smarter {% if %} tag for django templates.\r
+\r
+While retaining current Django functionality, it also handles equality,\r
+greater than and less than operators. Some common case examples::\r
+\r
+    {% if articles|length >= 5 %}...{% endif %}\r
+    {% if "ifnotequal tag" != "beautiful" %}...{% endif %}\r
+"""\r
+import unittest\r
+from django import template\r
+\r
+\r
+register = template.Library()\r
+\r
+\r
+#==============================================================================\r
+# Calculation objects\r
+#==============================================================================\r
+\r
+class BaseCalc(object):\r
+    def __init__(self, var1, var2=None, negate=False):\r
+        self.var1 = var1\r
+        self.var2 = var2\r
+        self.negate = negate\r
+\r
+    def resolve(self, context):\r
+        try:\r
+            var1, var2 = self.resolve_vars(context)\r
+            outcome = self.calculate(var1, var2)\r
+        except:\r
+            outcome = False\r
+        if self.negate:\r
+            return not outcome\r
+        return outcome\r
+\r
+    def resolve_vars(self, context):\r
+        var2 = self.var2 and self.var2.resolve(context)\r
+        return self.var1.resolve(context), var2\r
+\r
+    def calculate(self, var1, var2):\r
+        raise NotImplementedError()\r
+\r
+\r
+class Or(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 or var2\r
+\r
+\r
+class And(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 and var2\r
+\r
+\r
+class Equals(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 == var2\r
+\r
+\r
+class Greater(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 > var2\r
+\r
+\r
+class GreaterOrEqual(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 >= var2\r
+\r
+\r
+class In(BaseCalc):\r
+    def calculate(self, var1, var2):\r
+        return var1 in var2\r
+\r
+\r
+#==============================================================================\r
+# Tests\r
+#==============================================================================\r
+\r
+class TestVar(object):\r
+    """\r
+    A basic self-resolvable object similar to a Django template variable. Used\r
+    to assist with tests.\r
+    """\r
+    def __init__(self, value):\r
+        self.value = value\r
+\r
+    def resolve(self, context):\r
+        return self.value\r
+\r
+\r
+class SmartIfTests(unittest.TestCase):\r
+    def setUp(self):\r
+        self.true = TestVar(True)\r
+        self.false = TestVar(False)\r
+        self.high = TestVar(9000)\r
+        self.low = TestVar(1)\r
+\r
+    def assertCalc(self, calc, context=None):\r
+        """\r
+        Test a calculation is True, also checking the inverse "negate" case.\r
+        """\r
+        context = context or {}\r
+        self.assert_(calc.resolve(context))\r
+        calc.negate = not calc.negate\r
+        self.assertFalse(calc.resolve(context))\r
+\r
+    def assertCalcFalse(self, calc, context=None):\r
+        """\r
+        Test a calculation is False, also checking the inverse "negate" case.\r
+        """\r
+        context = context or {}\r
+        self.assertFalse(calc.resolve(context))\r
+        calc.negate = not calc.negate\r
+        self.assert_(calc.resolve(context))\r
+\r
+    def test_or(self):\r
+        self.assertCalc(Or(self.true))\r
+        self.assertCalcFalse(Or(self.false))\r
+        self.assertCalc(Or(self.true, self.true))\r
+        self.assertCalc(Or(self.true, self.false))\r
+        self.assertCalc(Or(self.false, self.true))\r
+        self.assertCalcFalse(Or(self.false, self.false))\r
+\r
+    def test_and(self):\r
+        self.assertCalc(And(self.true, self.true))\r
+        self.assertCalcFalse(And(self.true, self.false))\r
+        self.assertCalcFalse(And(self.false, self.true))\r
+        self.assertCalcFalse(And(self.false, self.false))\r
+\r
+    def test_equals(self):\r
+        self.assertCalc(Equals(self.low, self.low))\r
+        self.assertCalcFalse(Equals(self.low, self.high))\r
+\r
+    def test_greater(self):\r
+        self.assertCalc(Greater(self.high, self.low))\r
+        self.assertCalcFalse(Greater(self.low, self.low))\r
+        self.assertCalcFalse(Greater(self.low, self.high))\r
+\r
+    def test_greater_or_equal(self):\r
+        self.assertCalc(GreaterOrEqual(self.high, self.low))\r
+        self.assertCalc(GreaterOrEqual(self.low, self.low))\r
+        self.assertCalcFalse(GreaterOrEqual(self.low, self.high))\r
+\r
+    def test_in(self):\r
+        list_ = TestVar([1,2,3])\r
+        invalid_list = TestVar(None)\r
+        self.assertCalc(In(self.low, list_))\r
+        self.assertCalcFalse(In(self.low, invalid_list))\r
+\r
+    def test_parse_bits(self):\r
+        var = IfParser([True]).parse()\r
+        self.assert_(var.resolve({}))\r
+        var = IfParser([False]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+        var = IfParser([False, 'or', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([False, 'and', True]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+        var = IfParser(['not', False, 'and', 'not', False]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser(['not', 'not', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([1, '=', 1]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([1, 'not', '=', 1]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+        var = IfParser([1, 'not', 'not', '=', 1]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([1, '!=', 1]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+        var = IfParser([3, '>', 2]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([1, '<', 2]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([2, 'not', 'in', [2, 3]]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+        var = IfParser([1, 'or', 1, '=', 2]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+    def test_boolean(self):\r
+        var = IfParser([True, 'and', True, 'and', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+        var = IfParser([False, 'or', False, 'or', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+        var = IfParser([True, 'and', False, 'or', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+        var = IfParser([False, 'or', True, 'and', True]).parse()\r
+        self.assert_(var.resolve({}))\r
+\r
+        var = IfParser([True, 'and', True, 'and', False]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+        var = IfParser([False, 'or', False, 'or', False]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+        var = IfParser([False, 'or', True, 'and', False]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+        var = IfParser([False, 'and', True, 'or', False]).parse()\r
+        self.assertFalse(var.resolve({}))\r
+\r
+    def test_invalid(self):\r
+        self.assertRaises(ValueError, IfParser(['not']).parse)\r
+        self.assertRaises(ValueError, IfParser(['==']).parse)\r
+        self.assertRaises(ValueError, IfParser([1, 'in']).parse)\r
+        self.assertRaises(ValueError, IfParser([1, '>', 'in']).parse)\r
+        self.assertRaises(ValueError, IfParser([1, '==', 'not', 'not']).parse)\r
+        self.assertRaises(ValueError, IfParser([1, 2]).parse)\r
+\r
+\r
+OPERATORS = {\r
+    '=': (Equals, True),\r
+    '==': (Equals, True),\r
+    '!=': (Equals, False),\r
+    '>': (Greater, True),\r
+    '>=': (GreaterOrEqual, True),\r
+    '<=': (Greater, False),\r
+    '<': (GreaterOrEqual, False),\r
+    'or': (Or, True),\r
+    'and': (And, True),\r
+    'in': (In, True),\r
+}\r
+BOOL_OPERATORS = ('or', 'and')\r
+\r
+\r
+class IfParser(object):\r
+    error_class = ValueError\r
+\r
+    def __init__(self, tokens):\r
+        self.tokens = tokens\r
+\r
+    def _get_tokens(self):\r
+        return self._tokens\r
+\r
+    def _set_tokens(self, tokens):\r
+        self._tokens = tokens\r
+        self.len = len(tokens)\r
+        self.pos = 0\r
+\r
+    tokens = property(_get_tokens, _set_tokens)\r
+\r
+    def parse(self):\r
+        if self.at_end():\r
+            raise self.error_class('No variables provided.')\r
+        var1 = self.get_bool_var()\r
+        while not self.at_end():\r
+            op, negate = self.get_operator()\r
+            var2 = self.get_bool_var()\r
+            var1 = op(var1, var2, negate=negate)\r
+        return var1\r
+\r
+    def get_token(self, eof_message=None, lookahead=False):\r
+        negate = True\r
+        token = None\r
+        pos = self.pos\r
+        while token is None or token == 'not':\r
+            if pos >= self.len:\r
+                if eof_message is None:\r
+                    raise self.error_class()\r
+                raise self.error_class(eof_message)\r
+            token = self.tokens[pos]\r
+            negate = not negate\r
+            pos += 1\r
+        if not lookahead:\r
+            self.pos = pos\r
+        return token, negate\r
+\r
+    def at_end(self):\r
+        return self.pos >= self.len\r
+\r
+    def create_var(self, value):\r
+        return TestVar(value)\r
+\r
+    def get_bool_var(self):\r
+        """\r
+        Returns either a variable by itself or a non-boolean operation (such as\r
+        ``x == 0`` or ``x < 0``).\r
+\r
+        This is needed to keep correct precedence for boolean operations (i.e.\r
+        ``x or x == 0`` should be ``x or (x == 0)``, not ``(x or x) == 0``).\r
+        """\r
+        var = self.get_var()\r
+        if not self.at_end():\r
+            op_token = self.get_token(lookahead=True)[0]\r
+            if isinstance(op_token, basestring) and (op_token not in\r
+                                                     BOOL_OPERATORS):\r
+                op, negate = self.get_operator()\r
+                return op(var, self.get_var(), negate=negate)\r
+        return var\r
+\r
+    def get_var(self):\r
+        token, negate = self.get_token('Reached end of statement, still '\r
+                                       'expecting a variable.')\r
+        if isinstance(token, basestring) and token in OPERATORS:\r
+            raise self.error_class('Expected variable, got operator (%s).' %\r
+                                   token)\r
+        var = self.create_var(token)\r
+        if negate:\r
+            return Or(var, negate=True)\r
+        return var\r
+\r
+    def get_operator(self):\r
+        token, negate = self.get_token('Reached end of statement, still '\r
+                                       'expecting an operator.')\r
+        if not isinstance(token, basestring) or token not in OPERATORS:\r
+            raise self.error_class('%s is not a valid operator.' % token)\r
+        if self.at_end():\r
+            raise self.error_class('No variable provided after "%s".' % token)\r
+        op, true = OPERATORS[token]\r
+        if not true:\r
+            negate = not negate\r
+        return op, negate\r
+\r
+\r
+#==============================================================================\r
+# Actual templatetag code.\r
+#==============================================================================\r
+\r
+class TemplateIfParser(IfParser):\r
+    error_class = template.TemplateSyntaxError\r
+\r
+    def __init__(self, parser, *args, **kwargs):\r
+        self.template_parser = parser\r
+        return super(TemplateIfParser, self).__init__(*args, **kwargs)\r
+\r
+    def create_var(self, value):\r
+        return self.template_parser.compile_filter(value)\r
+\r
+\r
+class SmartIfNode(template.Node):\r
+    def __init__(self, var, nodelist_true, nodelist_false=None):\r
+        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false\r
+        self.var = var\r
+\r
+    def render(self, context):\r
+        if self.var.resolve(context):\r
+            return self.nodelist_true.render(context)\r
+        if self.nodelist_false:\r
+            return self.nodelist_false.render(context)\r
+        return ''\r
+\r
+    def __repr__(self):\r
+        return "<Smart If node>"\r
+\r
+    def __iter__(self):\r
+        for node in self.nodelist_true:\r
+            yield node\r
+        if self.nodelist_false:\r
+            for node in self.nodelist_false:\r
+                yield node\r
+\r
+    def get_nodes_by_type(self, nodetype):\r
+        nodes = []\r
+        if isinstance(self, nodetype):\r
+            nodes.append(self)\r
+        nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))\r
+        if self.nodelist_false:\r
+            nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))\r
+        return nodes\r
+\r
+\r
+@register.tag('if')\r
+def smart_if(parser, token):\r
+    """\r
+    A smarter {% if %} tag for django templates.\r
+\r
+    While retaining current Django functionality, it also handles equality,\r
+    greater than and less than operators. Some common case examples::\r
+\r
+        {% if articles|length >= 5 %}...{% endif %}\r
+        {% if "ifnotequal tag" != "beautiful" %}...{% endif %}\r
+\r
+    Arguments and operators _must_ have a space between them, so\r
+    ``{% if 1>2 %}`` is not a valid smart if tag.\r
+\r
+    All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``),\r
+    ``!=``, ``>``, ``>=``, ``<`` and ``<=``.\r
+    """\r
+    bits = token.split_contents()[1:]\r
+    var = TemplateIfParser(parser, bits).parse()\r
+    nodelist_true = parser.parse(('else', 'endif'))\r
+    token = parser.next_token()\r
+    if token.contents == 'else':\r
+        nodelist_false = parser.parse(('endif',))\r
+        parser.delete_first_token()\r
+    else:\r
+        nodelist_false = None\r
+    return SmartIfNode(var, nodelist_true, nodelist_false)\r
+\r
+\r
+if __name__ == '__main__':\r
+    unittest.main()\r
diff --git a/auf_savoirs_en_partage/templates/admin/filter.html b/auf_savoirs_en_partage/templates/admin/filter.html
new file mode 100644 (file)
index 0000000..388aafc
--- /dev/null
@@ -0,0 +1,23 @@
+{% load i18n %}
+{% load smart_if %}
+
+<h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</h3>
+
+{% if title == 'pays' or title == 'region' or title == 'discipline' or title == 'thematique' %}
+<select style="width: 160px;" onchange="window.location=window.location.pathname+this.options[this.selectedIndex].value">
+{% for choice in choices %}
+    <option{% if choice.selected %} selected="selected"{% endif %} style="width: 130px;"
+     value="{{ choice.query_string|iriencode }}">{{ choice.display }}</option>
+{% endfor %}
+</select>
+
+{% else %}
+<ul>
+{% for choice in choices %}
+    <li{% if choice.selected %} class="selected"{% endif %}>
+    <a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
+{% endfor %}
+</ul>
+{% endif %}
+
+