Example #1
0
 def render(self, context):
     var = self.var.resolve(context)
     request = context["request"]
     if self.name in request.GET:
         var = deepmerge(var, json.loads(request.GET[self.name]))
     elif self.name in context:
         var = deepmerge(var, context[self.name])
     context[self.name] = var
     return ""
Example #2
0
    def test_key_order(self):
        # Delta key range does not span source key range. Source order wins.
        source = OrderedDict([
            ("one", "One"),
            ("two", "Two"),
            ("three", "Three"),
        ])
        delta = OrderedDict([("three", "Three X"), ("two", "Two X")])
        result = deepmerge(source, delta)
        self.assertEqual(["one", "two", "three"], list(result.keys()))

        # Delta key range spans source key range. Delta order wins.
        source = OrderedDict([
            ("one", "One"),
            ("two", "Two"),
            ("three", "Three"),
        ])
        delta = OrderedDict([("three", "Three X"), ("two", "Two X"),
                             ("one", "One X"), ("four", "Four X")])
        result = deepmerge(source, delta)
        self.assertEqual(["three", "two", "one", "four"], list(result.keys()))
Example #3
0
 def test_list_with_nones(self):
     """`None` values are discarded from lists.
     """
     source = {
         "three": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks"
                 }
             },
             {
                 "actor": {
                     "name": "Denzel",
                     "surname": "Washington"
                 }
             },
         ],
     }
     delta = {
         "three": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks"
                 }
             },
             None,
         ],
     }
     result = deepmerge(source, delta)
     self.assertEqual(
         result,
         {"three": [{
             "actor": {
                 "name": "Tom",
                 "surname": "Hanks"
             }
         }]})
Example #4
0
 def test_arbitrary_keys(self):
     """Introduce keys that are not in source.
     """
     source = {
         "three": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks"
                 }
             },
         ],
     }
     delta = {
         "three": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks",
                     "age": 50
                 }
             },
         ],
         "four": 1
     }
     result = deepmerge(source, delta)
     self.assertEqual(
         result, {
             "three": [{
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks",
                     "age": 50
                 }
             }],
             "four":
             1
         })
Example #5
0
    def render(self, context):
        # We must import late
        from mote.models import Project, Aspect, Pattern, Element, Variation

        element_or_identifier = self.element_or_identifier.resolve(context)

        # If element_or_identifier is a string convert it
        if isinstance(element_or_identifier, string_types):
            # The "self" project triggers a project lookup. It first checks for
            # a context variable (used internally by the Mote explorer) then
            # for a setting (used when calling render_element over the API).
            if element_or_identifier.startswith("self."):
                project_id = context.get("__mote_project_id__", None)
                if project_id is None:
                    try:
                        value = settings.MOTE["project"]
                    except (AttributeError, KeyError):
                        raise RuntimeError(
                            "Define MOTE[\"project\"] setting for project lookup"
                        )
                    if callable(value):
                        project_id = value(context["request"])
                    else:
                        project_id = value
                obj = get_object_by_dotted_name(
                    element_or_identifier.replace("self.", project_id + "."))
            else:
                obj = get_object_by_dotted_name(element_or_identifier)
            if not isinstance(obj, (Element, Variation)):
                raise template.TemplateSyntaxError("Invalid identifier %s" %
                                                   element_or_identifier)
        else:
            obj = element_or_identifier

        # Set the object in the context as "element"
        with context.push():
            context["element"] = obj

            # Resolve the kwargs
            resolved = {}
            for k, v in self.kwargs.items():
                try:
                    r = v.resolve(context)
                except VariableDoesNotExist:
                    continue
                if isinstance(r, Promise):
                    r = text_type(r)

                # Strings may be interpreted further
                if isinstance(r, string_types):

                    # Attempt to resolve any variables by rendering
                    t = template.Template(r)
                    raw_struct = t.render(context)

                    # Attempt to convert to JSON
                    try:
                        resolved[k] = json.loads(raw_struct)
                    except ValueError:
                        resolved[k] = r

                else:
                    resolved[k] = r

            # Find the correct view and construct view kwargs
            view_kwargs = dict(
                project=obj.project.id,
                aspect=obj.aspect.id,
                pattern=obj.pattern.id,
            )
            if isinstance(obj, Variation):
                view = VariationPartialView
                view_kwargs.update(
                    dict(element=obj.element.id, variation=obj.id))
            else:
                view = ElementPartialView
                view_kwargs.update(dict(element=obj.id))

# Compute a cache key before we pop from resolved
            li = [obj.modified, deephash(resolved)]
            li.extend(frozenset(sorted(view_kwargs.items())))
            hashed = md5(u":".join([text_type(l)
                                    for l in li]).encode("utf-8")).hexdigest()
            cache_key = "render-element-%s" % hashed
            cached = cache.get(cache_key, None)
            if cached is not None:
                return cached

        # Automatically perform masking with the default data
            request = context["request"]
            masked = obj.data
            # Omit top-level key
            if masked:
                masked = masked[list(masked.keys())[0]]
            if "data" in request.GET:
                masked = deepmerge(masked, json.loads(request.GET["data"]))
            elif "data" in resolved:
                masked = deepmerge(masked, resolved.pop("data"))
            context["data"] = masked

            # Construct a final kwargs that includes the context
            final_kwargs = context.flatten()
            del final_kwargs["request"]
            final_kwargs.update(resolved)
            final_kwargs.update(view_kwargs)

            # Call the view. Let any error propagate.
            result = view.as_view()(request, **final_kwargs)

            if isinstance(result, TemplateResponse):
                # The result of a generic view
                result.render()
                html = result.rendered_content
            elif isinstance(result, HttpResponse):
                # Old-school view
                html = result.content

            # Make output beautiful for Chris
            if not settings.DEBUG:
                beauty = BeautifulSoup(html, "html.parser")
                html = beauty.prettify()

            cache.set(cache_key, html, 300)
            return html
Example #6
0
    def render(self, context):
        # We must import late
        from mote.models import Project, Aspect, Pattern, Element, Variation

        # To keep templates as simple as possible we don't require quotes to
        # denote a string. That requires special handling.
        try:
            element_or_identifier = self.element_or_identifier.resolve(context)
        except template.VariableDoesNotExist:
            element_or_identifier = \
                context["element"].data[self.element_or_identifier.var]

        data = OrderedDict()
        if self.data:
            data = self.data.resolve(context)

        # Shortcut notation allows an element to be looked up from data
        if isinstance(element_or_identifier, dict):
            copied = deepcopy(element_or_identifier)
            element_or_identifier = copied.pop("id")
            data = copied

        # If element_or_identifier is a string convert it
        if isinstance(element_or_identifier, string_types):

            # The "self" project triggers a project lookup. It first checks for
            # a context variable (used internally by the Mote explorer) then
            # for a setting (used when calling render over the API).
            if element_or_identifier.startswith("self."):
                project_id = context.get("__mote_project_id__", None)
                if project_id is None:
                    try:
                        value = settings.MOTE["project"]
                    except (AttributeError, KeyError):
                        raise RuntimeError(
                            "Define MOTE[\"project\"] setting for project lookup"
                        )
                    if callable(value):
                        project_id = value(context["request"])
                    else:
                        project_id = value
                obj = get_object_by_dotted_name(
                    element_or_identifier.replace("self.", project_id + "."))

            # Non-self lookup
            else:
                obj = get_object_by_dotted_name(element_or_identifier)

            # Type check
            if not isinstance(obj, (Element, Variation)):
                raise template.TemplateSyntaxError("Invalid identifier %s" %
                                                   element_or_identifier)

        elif isinstance(element_or_identifier, (Element, Variation)):
            obj = element_or_identifier
            data = context.get("data")

        else:
            raise RuntimeError("Cannot identify %r" % element_or_identifier)

        with context.push():

            # We use a completely clean context to avoid leakage
            newcontext = {}

            # Set the object in the new context as "element"
            newcontext["element"] = obj

            # Convert self.data if possible
            if isinstance(data, Promise):
                data = text_type(data)

            # Strings may be interpreted further
            if isinstance(data, string_types):

                # Attempt to resolve any variables by rendering
                t = template.Template(data)
                raw_struct = t.render(context)

                # Attempt to convert to JSON
                try:
                    data = json.loads(raw_struct)
                except ValueError:
                    pass

            # Find the correct view and construct view kwargs
            view_kwargs = dict(
                project=obj.project.id,
                aspect=obj.aspect.id,
                pattern=obj.pattern.id,
            )
            if isinstance(obj, Variation):
                view = VariationPartialView
                view_kwargs.update(
                    dict(element=obj.element.id, variation=obj.id))
            else:
                view = ElementPartialView
                view_kwargs.update(dict(element=obj.id))

            # Compute a cache key
            li = [obj.checksum, deephash(data)]
            li.extend(frozenset(sorted(view_kwargs.items())))
            hashed = md5(":".join([text_type(l)
                                   for l in li]).encode("utf-8")).hexdigest()
            cache_key = "render-element-%s" % hashed
            cached = cache.get(cache_key, None)
            if cached is not None:
                return cached

        # Automatically perform masking with the default data
            request = context["request"]
            masked = obj.data
            if "data" in request.GET:
                masked = deepmerge(masked, json.loads(request.GET["data"]))
            elif data:
                masked = deepmerge(masked, data)

            # Set data on new context. Also set the keys in data directly on
            # new context to make for cleaner templates.
            newcontext["data"] = masked
            for k, v in masked.items():
                if k in ("data", "element", "original_element", "pretty_data"):
                    raise RuntimeError("%s is a reserved key" % k)
                newcontext[k] = v

            # Update new context with the view kwargs
            newcontext.update(view_kwargs)

            # Make data legible in debug mode
            if settings.DEBUG:
                newcontext["pretty_data"] = json.dumps(newcontext["data"],
                                                       indent=4)

            # Call the view. Let any error propagate.
            result = view.as_view()(request, **newcontext)

            if isinstance(result, TemplateResponse):
                # The result of a generic view
                result.render()
                html = result.rendered_content
            elif isinstance(result, HttpResponse):
                # Old-school view
                html = result.content

            # Make output beautiful for Chris. This is expensive but required
            # for production. Make it togglable for develop.
            if not settings.DEBUG or request.GET.get("beautify", False):
                beauty = BeautifulSoup(html, "html.parser")
                html = beauty.prettify()

            # Useful debug info
            if settings.DEBUG:
                html = "<!-- " + obj.dotted_name + " -->" + html

            cache.set(cache_key, html, 300)
            return html
Example #7
0
 def test_deepmerge(self):
     source = {
         "one": {
             "aaa": 1,
             "bbb": 2
         },
         "two": [1, 2, 3],
         "three": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks"
                 }
             },
             {
                 "actor": {
                     "name": "Denzel",
                     "surname": "Washington"
                 }
             },
         ],
         "four": [
             {
                 "actor": {
                     "name": "Alec",
                     "surname": "Baldwin"
                 }
             },
             {
                 "actor": {
                     "name": "Brad",
                     "surname": "Pitt"
                 }
             },
         ],
         "five": [{
             "movie": {
                 "title": "Good Will Hunting",
                 "actors": [{
                     "actor": {
                         "name": "Ben",
                         "surname": "Affleck"
                     }
                 }]
             }
         }],
         "six": [
             {
                 "actor": {
                     "name": "Tom",
                     "surname": "Hanks"
                 }
             },
             {
                 "actor": {
                     "name": "Denzel",
                     "surname": "Washington"
                 }
             },
         ],
     }
     delta = {
         "one": {
             "bbb": 777,
             "ccc": 888
         },
         "two": [3, 4, 5],
         "three": {
             "actor": {
                 "name": "Colin"
             }
         },
         "four": [{
             "actor": {
                 "name": "Stephen"
             }
         }, {
             "actor": {
                 "name": "Harrison",
                 "surname": "Ford"
             }
         }, {
             "actor": {
                 "name": "William"
             }
         }],
         "five": [{
             "movie": {
                 "title":
                 "Good Will Hunting",
                 "actors": [{
                     "actor": {
                         "name": "Matt",
                         "surname": "Damon"
                     }
                 }, {
                     "actor": {
                         "name": "Casey"
                     }
                 }]
             }
         }],
         "six": [{
             "archetype": False,
             "director": {
                 "name": "Guillermo"
             }
         }, {
             "score": {
                 "name": "Hans"
             }
         }],
         "infinity": {
             "a": 0
         }
     }
     result = deepmerge(source, delta)
     self.assertEqual(
         {
             "one": {
                 "aaa": 1,
                 "bbb": 777,
                 "ccc": 888
             },
             "two": [3, 4, 5],
             "three": [{
                 "actor": {
                     "name": "Colin",
                     "surname": "Hanks"
                 }
             }],
             "four": [{
                 "actor": {
                     "name": "Stephen",
                     "surname": "Baldwin"
                 }
             }, {
                 "actor": {
                     "name": "Harrison",
                     "surname": "Ford"
                 }
             }, {
                 "actor": {
                     "name": "William",
                     "surname": "Baldwin"
                 }
             }],
             "five": [{
                 "movie": {
                     "title":
                     "Good Will Hunting",
                     "actors": [{
                         "actor": {
                             "name": "Matt",
                             "surname": "Damon"
                         }
                     }, {
                         "actor": {
                             "name": "Casey",
                             "surname": "Affleck"
                         }
                     }]
                 }
             }],
             "six": [{
                 "archetype": False,
                 "director": {
                     "name": "Guillermo"
                 }
             }, {
                 "score": {
                     "name": "Hans"
                 }
             }],
             "infinity": {
                 "a": 0
             }
         }, result)
Example #8
0
    def test_deepmerge_nones(self):
        # If both source and delta are None the result must be None
        source = None
        delta = None
        result = deepmerge(source, delta)
        assert (result, None)

        # If delta is None, we want to retain source
        source = {
            "one": {
                "aaa": 1,
                "bbb": 2
            },
            "two": [1, 2, 3],
            "three": [
                {
                    "actor": {
                        "name": "Tom",
                        "surname": "Hanks"
                    }
                },
                {
                    "actor": {
                        "name": "Denzel",
                        "surname": "Washington"
                    }
                },
            ],
            "four": [
                {
                    "actor": {
                        "name": "Alec",
                        "surname": "Baldwin"
                    }
                },
                {
                    "actor": {
                        "name": "Brad",
                        "surname": "Pitt"
                    }
                },
            ],
            "five": [{
                "movie": {
                    "title": "Good Will Hunting",
                    "actors": [{
                        "actor": {
                            "name": "Ben",
                            "surname": "Affleck"
                        }
                    }]
                }
            }]
        }
        delta = None
        result = deepmerge(source, delta)
        self.assertEqual(result, source, "Result must be == source!")

        # If source is None we want to retain source
        source = None
        delta = {
            "one": {
                "aaa": 1,
                "bbb": 2
            },
            "two": [1, 2, 3],
            "three": [
                {
                    "actor": {
                        "name": "Tom",
                        "surname": "Hanks"
                    }
                },
                {
                    "actor": {
                        "name": "Denzel",
                        "surname": "Washington"
                    }
                },
            ],
            "four": [
                {
                    "actor": {
                        "name": "Alec",
                        "surname": "Baldwin"
                    }
                },
                {
                    "actor": {
                        "name": "Brad",
                        "surname": "Pitt"
                    }
                },
            ],
            "five": [{
                "movie": {
                    "title": "Good Will Hunting",
                    "actors": [{
                        "actor": {
                            "name": "Ben",
                            "surname": "Affleck"
                        }
                    }]
                }
            }]
        }
        result = deepmerge(source, delta)
        self.assertEqual(result, source, "Result must be == None!")