Adding List-/StructBlocks programmatically - wagtail

i'm new to Wagtail and struggling to create content programmatically from a Django management command.
I have defined a StructBlock and Page with a StreamField like so:
class TaskBlock(blocks.StructBlock):
tasks = blocks.ListBlock(
blocks.StructBlock(
[
("title", blocks.CharBlock()),
("length", blocks.IntegerBlock())
]
)
)
class Meta:
template = "tasks/task_block.html"
icon = "edit"
label = "Task"
class TaskPage(Page):
tasks_first = StreamField([("tasks_first", TaskBlock())], null=True)
content_panels = Page.content_panels + [StreamFieldPanel("tasks_first")]
My command looks like:
class Command(BaseCommand):
def handle(self, *args, **options):
parent_page = Page.objects.get(title="Task Master").specific
task_page = TaskPage(
title="Task Page 5",
slug="task-page-5",
tasks_first=json.dumps(
[
{
"type": "tasks_first",
"value": [
{
"type": "tasks",
"value": [
{"type": "title", "value": "this is a task"},
{"type": "length", "value": 5}
],
}
],
}
]
),
)
parent_page.add_child(instance=task_page)
revision = task_page.save_revision()
revision.publish()
task_page.save()
The page is successfully created with a Task block, but the task is blank and does not contain the test data defined in the json.
I'm probably missing some basic knowledge on how to define the json for the TaskBlock() in the StreamField tasks_first.
Could you give me some guidance?

Probably clearer to start from the inside and work outwards here...
The JSON representation of a StructBlock value is a simple dict, where the keys are the sub-block names and the values are their values - you don't need "type" and "value" here. So, a value for the innermost StructBlock would look like:
{"title": "this is a task", "length": 5}
The value for ListBlock is a list of these values. If we only want one task in the list, this will be:
[
{"title": "this is a task", "length": 5}
]
TaskBlock is a StructBlock with a single sub-block named "tasks", so again this should be a simple dict:
{
"tasks": [
{"title": "this is a task", "length": 5}
]
}
And finally we place this in the stream - this is where the "type" and "value" come in.
[
{
"type": "tasks_first",
"value": {
"tasks": [
{"title": "this is a task", "length": 5}
]
}
}
]
This, then, is what you pass to json.dumps(...).
Finally, are you sure you need a StreamField at all here? If you just want a page with a list of tasks on, you could do this with an InlinePanel where each child object is one task.

Related

Encapsulate a JSON Array inside an object with JOLT?

I work on a project where the output of one of our APIs is a JSON array. I'd like to encapsulate this array inside an object.
I try to use a JOLT transformation (this is the first time I use this tool) to achieve this. I've already searched through a lot of example, but I still can't figure out what my JOLT specification has to be to perform the transformation. I can't find what I am looking for.
For example, if my input is like this:
[
{
"id": 1,
"name": "foo"
},
{
"id": 2,
"name": "bar"
}
]
I'd like the output to be:
{
"list":
[
{
"id": 1,
"name": "foo"
},
{
"id": 2,
"name": "bar"
}
]
}
In short, I just want to put my array inside a field of another object.
You can use a shift transformation spec such as
[
{
"operation": "shift",
"spec": {
"*": "list[]"
}
}
]
where "*" wildcard represents indices of the current wrapper array of objects
the demo on the site http://jolt-demo.appspot.com/ is

How to project a specific index inside a multilevel nested array in mongodb

I have a particular field in my document which has a multilevel nested array structure. The document looks like something this
{
"_id" : ObjectId("62171b4207476091a17f595f"),
"data" : [
{
"id" : "1",
"content" : [
{
"id" : "1.1",
"content" : []
},
{
"id" : "1.2",
"content" : [
{
"id" : "1.2.1",
"content" : [
{
"id" : "1.2.1.1",
"content" : []
}
]
},
{
"id" : "1.2.2",
"content" : []
}
]
}
]
}
]
}
(The ids in my actual data is a random string, I have added a more defined id here just for readability)
In my application code the nesting level is controlled so it won't go more than 5 levels deep.
I need to project a particular object inside one of the deeply nested arrays.
I have all the information needed to traverse the nested structure. For example if I need to fetch the object with id "1.2.2" my input will look something like this:
[{id: 1, index: 0}, {id: 1.2, index: 1}, {id: 1.2.2, index: 1}]
In the above array, each element represents one level of nesting. I have the Id and the index. So in the above example, I know I first need to travel to index 0 at the top level, then inside that to index 1 , then inside that to index 1 again to find my object.
Is there a way I can only get the inner object that I want directly using a query. Or will I need to get the whole "data" field and do the traversal in my application code. I have been unable to figure out any way to construct a query that would satisfy my need.
Query
if you know the path, you can do it using a series of nested
$getField
$arrayElemAt
you can do it in one stage with nested calls, or with many new fields like i did bellow, or with mongodb variables
*i am not sure what output you need, this goes inside to get the 2 using the indexes (if this is not what you need add if you can the expected output)
Test code here
Data
[
{
"_id": ObjectId( "62171b4207476091a17f595f"),
"data": [
{
"id": "1",
"content": [
{
"id": "1.1",
"content": []
},
{
"id": "1.2",
"content": [
{
"id": "1.2.1",
"content": [
{
"id": "1.2.1.1",
"content": []
}
]
},
{
"id": "1.2.2",
"content": [1,2]
}
]
}
]
}
]
}
]
Query
aggregate(
[{"$set":
{"c1":
{"$getField":
{"field":"content", "input":{"$arrayElemAt":["$data", 0]}}}}},
{"$set":
{"c2":
{"$getField":
{"field":"content", "input":{"$arrayElemAt":["$c1", 1]}}}}},
{"$set":
{"c3":
{"$getField":
{"field":"content", "input":{"$arrayElemAt":["$c2", 1]}}}}},
{"$project":{"_id":0, "c4":{"$arrayElemAt":["$c3", 1]}}}])
Results
[{
"c4": 2
}]

Update mongo array elements field from its other field

Let's assume following document
{
"companies": [
{"id": 1, "name": "Company 1", "new_name": "New company 1"},
{"id": 2, "name": "Company 2", "new_name": "New company 2"}
]
}
Is there a way I can reference element of array by itself to migrate to scheme
{
"companies": [
{"id": 1, "name": "New company 1", "new_name": "New company 1"},
{"id": 2, "name": "New company 2", "new_name": "New company 2"}
]
}
within a single call to update without scripting with forEach in mongo 4.2?
Something like
update(
...
{
"companies.$[].name": "$companies.<???>.new_name"
}
)
To access another field's value in a query you need to use aggregation, taking advantage of executing aggregation pipeline in .update()'s which got introduced in MongoDB version 4.2, try below query :
update(
...
[
{
$addFields: { // will replace existing field with new value
companies: {
$map: { // Iterates on an array & creates a new one
input: "$companies",
in: { $mergeObjects: [ "$$this", { name: "$$this.new_name" } ] } // `mergeObjects` will merge two objects `name` field will be replace in current object i.e; `$$this`
}
}
}
}
]
)
Test : You can test aggregation pipeline here : mongoplayground
Note : Seems to be using aggregation is only option to do this in one call, but downside could be only if your array is too huge, cause $map has to re-write entire array again.

Apache Nifi: Parse data with UpdateRecord Processor

I'm trying to parse some data in Nifi (1.7.1) using UpdateRecord Processor.
Original data are json files, that I would like to convert to Avro, based on a schema.
The Avro conversion is ok, but in that convertion I also need to parse one array element from the json data to a different structure in Avro.
This is a sample data of the input json:
{ "geometry" : {
"coordinates" : [ [ 4.963087975800593, 45.76365595859971 ], [ 4.962874487781098, 45.76320922779652 ], [ 4.962815443439148, 45.763116079159374 ], [ 4.962744732112515, 45.763010484202866 ], [ 4.962096825239138, 45.762112721939246 ] ]} ...}
Being its schema (specified in RecordReader):
{ "type": "record",
"name": "features",
"fields": [
{
"name": "geometry",
"type": {
"type": "record",
"name": "geometry",
"fields": [
{
"name": "coordinatesJson",
"type": {
"type": "array",
"items": {
"type": "array",
"items": "double"
}
}
},
]
}
},
....
]
}
As you can see, coordinates is an array of arrays.
And I need to parse those data to Avro, based on this schema (specified in RecordWriter):
{
"name": "outputdata",
"type": "record",
"fields": [
{"name": "coordinatesAvro",
"type": {
"type": "array",
"items" : {
"type" : "record",
"name" : "coordinatesAvro",
"fields" : [ {
"name" : "X",
"type" : "double"
}, {
"name" : "Y",
"type" : "double"
} ]
}
}
},
.....
]
}
The problem here is that I'm not being able to parse from coordinatesJson to coordinatesAvro, using RecordPath functions
I tried several mappings, like:
Property: Value:
/coordinatesJson[0..-1]/X /geometry/coordinatesAvro[*][0]
/coordinatesJson[0..-1]/Y /geometry/coordinatesAvro[*][1]
It should be a pretty straighforward parsing step, but as I said, I've been going in circles to achive this for a while.
Any help would be really appreciated.
When I collide with something like that I do next:
1) Transofrm Json into Json with strcuture that I need (for example in your case: coordinatesAvro) by ExecuteScript Processor. I have used ECMAScript cause you can simple parse JSON and work with objects (transform them).
2) ConvertJsonToAvro with one common schema (coordinatesAvro in your case) for Reader and Writer.
It works very good and I have used it on BigData cases. This is one of possible resolutions for your problem.

ExtJs Menu Binding from database

Please provide some sample code or idea about , How to bind menu dynamically from Json results
I get results from database as json ,so how to bind menu from json (Parent and childs)
Thanks in advance
Its pretty easy actually. When you return the data from the server all you need to do is include a metaData field in your JSON that defines the record structure.
See this documentation: http://dev.sencha.com/deploy/ext-3.3.1/docs/?class=Ext.data.JsonReader
The example from the docs is as follows:
{
metaData: {
"idProperty": "id",
"root": "rows",
"totalProperty": "results"
"successProperty": "success",
"fields": [
{"name": "name"},
{"name": "job", "mapping": "occupation"}
],
// used by store to set its sortInfo
"sortInfo":{
"field": "name",
"direction": "ASC"
},
// paging data (if applicable)
"start": 0,
"limit": 2,
// custom property
"foo": "bar"
},
// Reader's configured successProperty
"success": true,
// Reader's configured totalProperty
"results": 2000,
// Reader's configured root
// (this data simulates 2 results per page)
"rows": [ // *Note: this must be an Array
{ "id": 1, "name": "Bill", "occupation": "Gardener" },
{ "id": 2, "name": "Ben", "occupation": "Horticulturalist" }
]
}

Resources