I am following the tutorial on https://www.amcharts.com/docs/v5/tutorials/creating-multi-content-pdf-export/ in order to make a PDF using Amcharts5.
This is the layout of my files:
src/input/chart.html
src/input/javascript.js
src/input/style.css
src/output/.gitkeep
src/make_pdf_from_input.py
I want to use chart.html, javascript.js and style.css to make a PDF when I run the script make_pdf_from_input.py.
src/input/chart.html
<html lang="en-US">
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="https://cdn.amcharts.com/lib/5/index.js"></script>
<script src="https://cdn.amcharts.com/lib/5/xy.js"></script>
<script src="https://cdn.amcharts.com/lib/5/percent.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Animated.js"></script>
<script src="https://cdn.amcharts.com/lib/5/themes/Dataviz.js"></script>
<div class="main">
<h1>In accumsan velit in orci tempor</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sem quam, sodales ac volutpat sed, vestibulum id quam. Sed quis arcu non elit fringilla mattis. Sed auctor mi sed efficitur vehicula. Sed bibendum odio urna, quis lobortis dui luctus ac. Duis eu lacus sodales arcu tincidunt ultrices viverra a risus. Vivamus justo massa, malesuada quis pellentesque ut, placerat in massa. Nunc bibendum diam justo, in consequat ipsum fringilla ac. Praesent porta nibh ac arcu viverra, at scelerisque neque venenatis. Donec aliquam lorem non ultrices ultrices. Aliquam efficitur eros quis tortor condimentum, id pellentesque metus iaculis. Aenean at consequat neque, a posuere lectus. In eu libero magna. Pellentesque molestie tellus nec nisi molestie, eu dignissim lacus tristique. Sed tellus nulla, suscipit a velit non, mattis dictum metus. Curabitur mi mi, convallis nec libero quis, venenatis vestibulum ante.</p>
<h2>Aliquam lacinia justo</h2>
<div id="chartdiv" class="chart"></div>
<h2>Phasellus suscipit in diam a interdum</h2>
<table>
<tr>
<th>USA</th>
<th>Japan</th>
<th>France</th>
<th>Mexico</th>
</tr>
<tr>
<td>2500</td>
<td>1900</td>
<td>2200</td>
<td>1200</td>
</tr>
<tr>
<td>800</td>
<td>1200</td>
<td>990</td>
<td>708</td>
</tr>
<tr>
<td>2100</td>
<td>2150</td>
<td>900</td>
<td>1260</td>
</tr>
</table>
<h2>Duis sed efficitur mauris</h2>
<div>
<div class="col">
<div id="chartdiv2" class="chart"></div>
</div>
<div class="col">
<div id="chartdiv3" class="chart"></div>
</div>
</div>
<br>
<h2>Aliquam semper lacinia</h2>
<div id="chartdiv4" class="chart"></div>
<p>Maecenas congue leo vel tortor faucibus, non semper odio viverra. In ac libero rutrum libero elementum blandit vel in orci. Donec sit amet nisl ac eros mollis molestie. Curabitur ut urna vitae turpis bibendum malesuada sit amet imperdiet orci. Etiam pulvinar quam at lorem pellentesque congue. Integer sed odio enim. Maecenas eu nulla justo. Sed quis enim in est sodales facilisis non sed erat. Aenean vel ornare urna. Praesent viverra volutpat ex a aliquet.</p>
<p>Fusce sed quam pharetra, ornare ligula id, maximus risus. Integer dignissim risus in placerat mattis. Fusce malesuada dui ut lectus ultricies, et sollicitudin nisl placerat. In dignissim elit in pretium lobortis. Fusce ornare enim at metus laoreet, ut convallis elit lacinia. Maecenas pharetra aliquet mi. Nulla orci nunc, egestas id nisi ut, volutpat sollicitudin mi.</p>
</div>
</body>
</html>
src/input/javascript.js
/**
* ---------------------------------------
* This demo was created using amCharts 5.
*
* For more information visit:
* https://www.amcharts.com/
*
* Documentation is available at:
* https://www.amcharts.com/docs/v5/
* ---------------------------------------
*/
/**
* Chart 1
*/
// Create root element
var root = am5.Root.new("chartdiv");
// Set themes
root.setThemes([
am5themes_Animated.new(root),
am5themes_Dataviz.new(root)
]);
var data = [{
date: new Date(2018, 0, 1).getTime(),
value: 450,
value2: 362,
value3: 699
}, {
date: new Date(2018, 0, 2).getTime(),
value: 269,
value2: 450,
value3: 841
}, {
date: new Date(2018, 0, 3).getTime(),
value: 700,
value2: 358,
value3: 699
}, {
date: new Date(2018, 0, 4).getTime(),
value: 490,
value2: 367,
value3: 500
}, {
date: new Date(2018, 0, 5).getTime(),
value: 500,
value2: 485,
value3: 369
}, {
date: new Date(2018, 0, 6).getTime(),
value: 550,
value2: 354,
value3: 250
}, {
date: new Date(2018, 0, 7).getTime(),
value: 420,
value2: 350,
value3: 600
}];
// Create chart
var chart = root.container.children.push(
am5xy.XYChart.new(root, {
paddingBottom: 20
})
);
// Create axes
var xAxis = chart.xAxes.push(
am5xy.DateAxis.new(root, {
maxDeviation: 0.1,
groupData: false,
baseInterval: {
timeUnit: "day",
count: 1
},
renderer: am5xy.AxisRendererX.new(root, {
minGridDistance: 50
})
})
);
xAxis.get("renderer").labels.template.set("forceHidden", true);
var yAxis = chart.yAxes.push(
am5xy.ValueAxis.new(root, {
maxDeviation: 0.1,
renderer: am5xy.AxisRendererY.new(root, {})
})
);
yAxis.get("renderer").labels.template.set("forceHidden", true);
// Add series
function createSeries(field) {
var series = chart.series.push(
am5xy.LineSeries.new(root, {
minBulletDistance: 10,
xAxis: xAxis,
yAxis: yAxis,
valueYField: field,
valueXField: "date",
tooltip: am5.Tooltip.new(root, {
pointerOrientation: "horizontal",
labelText: "{valueY}"
})
})
);
series.strokes.template.setAll({
strokeWidth: 3
});
series.bullets.push(function () {
return am5.Bullet.new(root, {
sprite: am5.Circle.new(root, {
radius: 7,
fill: series.get("fill"),
stroke: root.interfaceColors.get("background"),
strokeWidth: 2
})
});
});
series.data.setAll(data);
}
createSeries("value");
createSeries("value2");
createSeries("value3");
/**
* Chart 2
*/
// Create root element
var root2 = am5.Root.new("chartdiv2");
// Set themes
root2.setThemes([
am5themes_Animated.new(root2),
am5themes_Dataviz.new(root2)
]);
var data2 = [{
country: "USA",
visits: 3025
}, {
country: "China",
visits: 1882
}, {
country: "Japan",
visits: 1809
}, {
country: "Germany",
visits: 1322
}, {
country: "UK",
visits: 1122
}, {
country: "France",
visits: 1114
}, {
country: "India",
visits: 984
}];
// Create chart
var chart2 = root2.container.children.push(
am5xy.XYChart.new(root2, {
paddingBottom: 20
})
);
// Create axes
var xAxis2 = chart2.xAxes.push(
am5xy.CategoryAxis.new(root2, {
categoryField: "country",
renderer: am5xy.AxisRendererX.new(root2, {
minGridDistance: 10
})
})
);
xAxis2.get("renderer").labels.template.set("forceHidden", true);
xAxis2.data.setAll(data2);
var yAxis2 = chart2.yAxes.push(
am5xy.ValueAxis.new(root2, {
maxDeviation: 0.1,
renderer: am5xy.AxisRendererY.new(root2, {})
})
);
yAxis2.get("renderer").labels.template.set("forceHidden", true);
// Add series
var series2 = chart2.series.push(
am5xy.ColumnSeries.new(root2, {
minBulletDistance: 10,
xAxis: xAxis2,
yAxis: yAxis2,
valueYField: "visits",
categoryXField: "country"
})
);
series2.columns.template.setAll({
strokeOpacity: 0,
cornerRadiusTL: 10,
cornerRadiusTR: 10,
width: am5.percent(80)
});
series2.columns.template.adapters.add("fill", function(fill, target) {
return chart2.get("colors").getIndex(data2.indexOf(target.dataItem.dataContext));
});
series2.data.setAll(data2);
/**
* Chart 3
*/
// Create root element
var root3 = am5.Root.new("chartdiv3");
// Set themes
root3.setThemes([
am5themes_Animated.new(root3),
am5themes_Dataviz.new(root3)
]);
var data3 = [{
country: "USA",
year2004: 3.5,
year2005: 4.2
}, {
country: "UK",
year2004: 1.7,
year2005: 3.1
}, {
country: "Canada",
year2004: 2.8,
year2005: 2.9
}, {
country: "Japan",
year2004: 2.6,
year2005: 2.3
}, {
country: "France",
year2004: 1.4,
year2005: 2.1
}, {
country: "Brazil",
year2004: 2.6,
year2005: 4.9
}];
// Create chart
var chart3 = root3.container.children.push(
am5xy.XYChart.new(root3, {
paddingBottom: 20
})
);
// Create axes
var xAxis3 = chart3.xAxes.push(
am5xy.CategoryAxis.new(root3, {
categoryField: "country",
renderer: am5xy.AxisRendererX.new(root3, {
minGridDistance: 10
})
})
);
xAxis3.get("renderer").labels.template.set("forceHidden", true);
xAxis3.data.setAll(data3);
var yAxis3 = chart3.yAxes.push(
am5xy.ValueAxis.new(root3, {
maxDeviation: 0.1,
renderer: am5xy.AxisRendererY.new(root3, {})
})
);
yAxis3.get("renderer").labels.template.set("forceHidden", true);
// Add series
var series31 = chart3.series.push(
am5xy.ColumnSeries.new(root3, {
minBulletDistance: 10,
xAxis: xAxis3,
yAxis: yAxis3,
clustered: false,
valueYField: "year2004",
categoryXField: "country"
})
);
series31.columns.template.setAll({
strokeOpacity: 0,
cornerRadiusTL: 10,
cornerRadiusTR: 10,
width: am5.percent(80)
});
series31.data.setAll(data3);
var series32 = chart3.series.push(
am5xy.ColumnSeries.new(root3, {
minBulletDistance: 10,
xAxis: xAxis3,
yAxis: yAxis3,
clustered: false,
valueYField: "year2005",
categoryXField: "country"
})
);
series32.columns.template.setAll({
strokeOpacity: 0,
cornerRadiusTL: 6,
cornerRadiusTR: 6,
width: am5.percent(50)
});
series32.data.setAll(data3);
/**
* Chart 4
*/
// Create root and chart
var root4 = am5.Root.new("chartdiv4");
root4.setThemes([
am5themes_Animated.new(root4),
am5themes_Dataviz.new(root4)
]);
var chart4 = root4.container.children.push(
am5percent.PieChart.new(root4, {
paddingTop: 0,
paddingRight: 0,
paddingBottom: 0,
paddingLeft: 0
})
);
// Define data
var data4 = [{
country: "Lithuania",
value: 260
}, {
country: "Czechia",
value: 230
}, {
country: "Ireland",
value: 200
}, {
country: "Germany",
value: 165
}, {
country: "Australia",
value: 139
}, {
country: "Austria",
value: 128
}];
// Create series
var series4 = chart4.series.push(
am5percent.PieSeries.new(root4, {
name: "Series",
valueField: "value",
categoryField: "country"
})
);
series4.labels.template.set("forceHidden", true);
series4.ticks.template.set("forceHidden", true);
series4.slices.template.set("cornerRadius", 6);
series4.slices.template.adapters.add("radius", function (radius, target) {
var dataItem = target.dataItem;
var high = series4.getPrivate("valueHigh");
if (dataItem) {
var value = target.dataItem.get("valueWorking", 0);
return radius * value / high
}
return radius;
});
series4.data.setAll(data4);
src/input/style.css
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
.chart {
width: 100%;
height: 250px;
border: 1px solid #eee;
}
#chartdiv4 {
float: left;
margin: 0 20px 20px 0;
width: 30%;
}
.main {
max-width: 600px;
margin: 0 auto;
}
h2 {
margin-top: 2em;
clear: both;
}
br {
clear: both;
}
table {
width: 100%;
border: 1px solid #eee;
border-collapse: collapse;
}
table td, table th {
border: 1px solid #eee;
padding: 5px;
}
.col {
width: 50%;
float: left;
}
src/make_pdf_from_input.py
def make_pdf_from_input():
if __name__ == '__main__':
make_pdf_from_input()
Here I am completely stuck and need some guidance to where I should continue in order to combine the files into a PDF using Python.
Related
I'm creating a shiny app with Python, but for some reason there's a lot of white space below the footer. The body uses 100% of the height, but the ui_fluid makes the content stick to the top.
Anyone knows how to fix it?
from shiny import App, render, ui
from shiny.types import ImgData
from model.panel import panel_ui
from model.file import css_file, img_file
app_ui = ui.page_fluid(
{"class": "p-4"},
ui.head_content(
ui.tags.meta(name="viewport",
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalabel=no"),
ui.tags.style(css_file("src/static/css/body.css")),
ui.tags.style(css_file("src/static/css/sidebar.css")),
ui.tags.style(css_file("src/static/css/footer.css")),
),
ui.layout_sidebar(
ui.panel_sidebar(
ui.navset_tab_card(
ui.nav(
"Quality assurance",
ui.input_file("file1", "Choose forward fastaq file",
accept=[".fastq"], multiple=False),
ui.input_file("file2", "Choose reverse fastaq file",
accept=[".fastq"], multiple=False),
ui.input_slider("q_threshold", "Qual threshold:",
min=0, max=40, value=20),
ui.input_slider("l_threshold", "Length threshold:",
min=0, max=40, value=20),
ui.input_select(
"quality_type",
"Choose quality type",
["illumina", "sanger", "solexa"]
),
ui.input_checkbox(
"five_prime", "Five prime trimming", True),
),
ui.nav(
"Alligment",
ui.input_file("file3", "Choose reference genome file",
accept=[".fa"], multiple=False),
),
ui.nav(
"Post-alligment",
ui.input_select(
"sequence_read",
"Choose sequence read type",
["short", "long"]
),
),
),
panel_ui(
{"class": "card my-3 mb-0"},
ui.p(
"Make sure all of the requirements are satisfied before running the app"),
ui.div(
{"class": "text-center"},
ui.input_action_button(
"run", "Run app", class_="btn-primary w-50"
)
)
)
),
# code over here in development ...
ui.panel_main(
),
),
ui.row(
ui.column(12,
ui.div({"class": "footer mt-3 p-3 w-100"},
ui.output_image("logo_1", inline=True),
ui.output_image("logo_2", inline=True),
ui.p("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eget imperdiet nisi, eget dapibus erat. Proin a orci id mi cursus cursus nec a purus. Ut lacus lectus, lobortis in suscipit quis, luctus pharetra ipsum. Etiam mollis vestibulum sem non bibendum.")
),
)
)
)
def server(input, output, session):
# output
# render.image
def logo_fcv():
return img_file("src/static/images/logo-1.png", "150px")
# output
# render.image
def logo_udes():
return img_file("src/static/images/logo-2.png", "200px")
app = App(app_ui, server)
body.css
body {
height: 100%;
width: 100%;
}
footer.css
.footer {
background-color: #dee2e6;
position: relative;
width: 100%;
text-align: center;
}
#media screen and (max-width: 725px) {
.footer {
display: grid;
}
.footer p {
padding-top: 1em;
}
}
sidebar.css (paddint: 0, because ui.panel_sidebar makes it look like there's a bar inside the other)
.well {
padding: 0;
}
I tried forcing x height and minimum-height in the css, using another layout and a sticky footer (that leaves even more space in the middle 😒)
Given this json response:
api_schema = schema({
"sts": "OK",
"values": [
{
"mark": And(str, lambda s: len(s) > 1),
"desc": And(str, lambda s: len(s) > 1),
"observer": Enum(["testObs", "test"])
"created": And(int, lambda s: len(str(s)) >= 5),
}
]
})
rsp = {
"sts":"OK",
"values":[
{
"mark":"test",
"created":123213213,
"desc":"Ok",
"observer":"testObs",
}
]
}
print(api_schema.validate(data=rsp))
Raises:
schema.SchemaError: Key 'values' error:
Or({'mark': And(<class 'str'>, <function <lambda> at 0x0000010A9B6A04A0>), 'desc': And(<class 'str'>, <function <lambda> at 0x0000010A9B858E00>), 'observer': Enum(['testObs', 'BY_CARRIER', 'BY_ALL_DEVICES', 'BY_ALL_USERS', 'BY_USER_ID', 'BY_DEVICE_ID']), 'created': And(<class 'int'>, <function <lambda> at 0x0000010A9B859C60>)}) did not validate {'mark': 'test', 'created': 123213213, 'desc': 'Ok', 'observer': 'testObs'}
Key 'observer' error:
Enum(['testObs', 'BY_CARRIER', 'BY_ALL_DEVICES', 'BY_ALL_USERS', 'BY_USER_ID', 'BY_DEVICE_ID']) did not validate 'testObs'
'testObs' should be instance of 'list'
But it dosent make sense "testObs" is indeed part of "observer": Enum(["testObs"...
Lorem ipsum so i can post:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. "
It seems as if the "Full example" in their Readme is outdated. This PR shows that instead of passing your Enum values as a list, you should pass them as arguments:
"observer": Enum("testObs", "test"),
Putting it back in your example it now works as expected:
from pytest_schema import schema, Enum, And, Regex, Optional, Or
api_schema = schema({
"sts": "OK",
"values": [
{
"mark": And(str, lambda s: len(s) > 1),
"desc": And(str, lambda s: len(s) > 1),
"observer": Enum("testObs", "test"),
"created": And(int, lambda s: len(str(s)) >= 5),
}
]
})
rsp = {
"sts":"OK",
"values":[
{
"mark":"test",
"created":123213213,
"desc":"Ok",
"observer":"testObs",
}
]
}
print(api_schema.validate(data=rsp))
=>
❯ python python_test.py
{'sts': 'OK', 'values': [{'mark': 'test', 'created': 123213213, 'desc': 'Ok', 'observer': 'testObs'}]}
I got two json objects that I need to combine together based on ID and do count and sort operations on it.
Here is the first object comments:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
"userId": 1,
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
},
{
"userId": 1,
"id": 4,
"title": "eum et est occaecati",
"body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
},
]
This is second json object:
[
{
"postId": 1,
"id": 1,
"name": "id labore ex et quam laborum",
"email": "Eliseo#gardner.biz",
"body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium"
},
{
"postId": 1,
"id": 2,
"name": "quo vero reiciendis velit similique earum",
"email": "Jayne_Kuhic#sydney.com",
"body": "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et"
},
{
"postId": 1,
"id": 3,
"name": "odio adipisci rerum aut animi",
"email": "Nikita#garfield.biz",
"body": "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione"
},
{
"postId": 1,
"id": 4,
"name": "alias odio sit",
"email": "Lew#alysha.tv",
"body": "non et atque\noccaecati deserunt quas accusantium unde odit nobis qui voluptatem\nquia voluptas consequuntur itaque dolor\net qui rerum deleniti ut occaecati"
},
{
"postId": 2,
"id": 5,
"name": "et fugit eligendi deleniti quidem qui sint nihil autem",
"email": "Presley.Mueller#myrl.com",
"body": "doloribus at sed quis culpa deserunt consectetur qui praesentium\naccusamus fugiat dicta\nvoluptatem rerum ut voluptate autem\nvoluptatem repellendus aspernatur dolorem in"
},
{
"postId": 2,
"id": 6,
"name": "repellat consequatur praesentium vel minus molestias voluptatum",
"email": "Dallas#ole.me",
"body": "maiores sed dolores similique labore et inventore et\nquasi temporibus esse sunt id et\neos voluptatem aliquam\naliquid ratione corporis molestiae mollitia quia et magnam dolor"
},
]
Object one is basically posts with poster details and object two is comments with commenter details.
So expected that object one has one to many relationships with second object. For example one post has many comments. This relationship is based on id in object one is postId in object two. The ultimate objective is to count and sort post by number of comments.
I attempt the problem with simple for loops and creating new json object, I managed to combine them together, but I dont know how to count and sort them properly.
in the views:
for i in posts:
if (id==postId):
newobj.append(objtwo[i])
count+=1
else:
newobj.append(count)
count=0
Normally I use django ORM to sort this but I dont have access to the database and model of the table. How to count and sort the new object so it can return list of posts with most comments counts and descend to lower comments counts?
Assuming your posts and comments data structures are lists, you can use python's defaultdict to count the comments. Then, use posts.sort(key=...) to sort your posts based on the collected counts using the key parameter. Altogether, it could like like this:
import json
from collections import defaultdict
posts = [ ... ]
comments = [ ... ]
# data structure to count the to comments
# automatically initializes to 0
comments_per_post = defaultdict(int)
# iterate through the comments to increase the count for the posts
for comment in comments:
comments_per_post[comment['postId']] += 1
# add comment count to post
for post in posts:
post['number_of_comments'] = comments_per_post[post['id']]
# sort the posts based on the counts collected
posts.sort(key=lambda post: post['number_of_comments'], reverse=True)
# print them to verify
# number of comments per Post will be in the `number_of_comments` key on the post dict.
print(json.dumps(posts, indent=2))
Note: this sorts the posts array in-place. If you don't want this, you can use sorted_posts = sorted(posts, key=... instead.
My answer is very similar to Byted's answer.
I would use Counter from the built-in collections to count the number of postIds in the second object.
Then sort the first object by using these counts from the previous step as a sorting key. Counter object returns 0 if a key is not present in it, so just use it as a lookup as a sorting key. The negative sign ensures a descending order (because sorted() sorts in ascending order by default).
import json
from collections import Counter
# count the comments
counts = Counter([d['postId'] for d in objtwo])
# add the counts to each post
for d in objone:
d["number of comments"] = counts[d['id']]
# sort posts by number of comments in descending order
objone.sort(key=lambda x: -x['number of comments'])
# convert to json
json.dumps(objone, indent=4)
Intermediate output for this input:
print(counts)
# Counter({1: 4, 2: 2})
I'm pretty sure there's a better way to do this:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('category', 'id', 'title', 'image', 'slug', 'author', 'excerpt', 'content', 'status', 'published')
class FrontendPostSerializer(serializers.ModelSerializer):
author = AuthorSerializer(many=False, read_only=True)
category = CategorySerializer(many=False, read_only=True)
class Meta:
model = Post
fields = ('category', 'id', 'title', 'image', 'slug', 'author', 'excerpt', 'content', 'status', 'published')
PostSerializer is gonna look like this
{
"category": 1,
"id": 45,
"title": "Lorem Ipsum - Lorem ipsum dolor sit amet consectetur",
"image": "http://localhost:8000/media/posts/car_SxXcUTV.jpg",
"slug": "lorem-ipsum-lorem-ipsum-dolor-sit-amet-consectetur",
"author": 4,
"excerpt": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"content": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"status": "published",
"published": "2021-10-01T14:46:34.872576Z"
}
FrontendPostSerializer is gonna look like this
{
"category": {
"name": "django"
},
"id": 45,
"title": "Lorem Ipsum - Lorem ipsum dolor sit amet consectetur",
"image": "http://localhost:8000/media/posts/car_SxXcUTV.jpg",
"slug": "lorem-ipsum-lorem-ipsum-dolor-sit-amet-consectetur",
"author": {
"username": "luigi.verdi"
},
"excerpt": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"content": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"status": "published",
"published": "2021-10-01T14:46:34.872576Z"
}
What I'm doing atm is using FrontendPostSerializer for showing data in the frontend, for example in a table with category name, author name and title. Instead, I'm using PostSerializer for the backend CRUD.
These are the viewsets I'm using in the views.py
class ManagePosts(viewsets.ModelViewSet):
serializer_class = PostSerializer
parser_classes = [MultiPartParser, FormParser]
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('pk')
return get_object_or_404(Post, slug=item)
# Define Custom Queryset
def get_queryset(self):
return Post.objects.all()
class FrontendPosts(viewsets.ModelViewSet):
serializer_class = FrontendPostSerializer
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('pk')
return get_object_or_404(Post, slug=item)
# Define Custom Queryset
def get_queryset(self):
return Post.objects.all()
I already tried to use only one serializer, I had this:
class PostSerializer(serializers.ModelSerializer):
author = AuthorSerializer(many=False, read_only=True)
category = CategorySerializer(many=False, read_only=True)
class Meta:
model = Post
fields = ('category', 'id', 'title', 'image', 'slug', 'author', 'excerpt', 'content', 'status', 'published')
but, for example, when I try to create a new post it doesn't work, because category and author are not numbers, but objects.
I'm also gonna put here create.js I have in my React frontend that handles the create submit.
const handleSubmit = (e) => {
e.preventDefault();
let formData = new FormData();
formData.append('category', 1);
formData.append('title', postData.title);
formData.append('slug', postData.slug);
formData.append('author', userInfo.id);
formData.append('excerpt', postData.excerpt);
formData.append('content', postData.content);
if(postImage.image !== null) {
formData.append('image', postImage.image);
}
axiosInstance.post('', formData);
history.push({
pathname: '/admin/',
});
window.location.reload();
};
Is there a better way? I'm sure I can use only one serializer, but I'm not sure how yet.
Thanks!
Actually yes. You can add specific fields you want by using the source attribute. Example:
class PostSerializer(serializers.ModelSerializer):
authorUserName = serializers.CharField(read_only=true, source="author.username")
categoryName = serializers.CharField(read_only=true, source="category.name"
class Meta:
model = Post
fields = (
'category', 'id', 'title',
'image', 'slug', 'author',
'excerpt', 'content', 'status',
'published', 'authorName', 'categoryName')
# Remember add the field that are created
And when you try to get you should get the result like this:
{
"categoryName": "django",
"category": 1,
"id": 45,
"title": "Lorem Ipsum - Lorem ipsum dolor sit amet consectetur",
"image": "http://localhost:8000/media/posts/car_SxXcUTV.jpg",
"slug": "lorem-ipsum-lorem-ipsum-dolor-sit-amet-consectetur",
"authorName": "luigi.verdi",
"author": 4,
"excerpt": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"content": "Officiis iure rerum voluptates a cumque velit \nquibusdam sed amet tempora. Sit laborum ab, eius fugit doloribus tenetur \nfugiat, temporibus enim commodi iusto libero magni deleniti quod quam \nconsequuntur! Commodi minima excepturi repudiandae velit hic maxime\ndoloremque.",
"status": "published",
"published": "2021-10-01T14:46:34.872576Z"
}
Trying to do a recursive filter query on RethinkDB with they Python wrapper. Having a lot of trouble getting it work.
Tried a lot of variations of the query, to no avail. Essentially, I'm trying to find the rows which do not have a document nested under label with a particular user_id.
In plain english: if the current user already labeled this example, don’t return it to them again.
My non-working query:
open_tasks = rdbt \
.order_by(index=r.desc('labels_completed')) \
.filter(r.row['locked'] == False) \
.filter(lambda task:
task['labels']['user_id'] != current_user.id) \
.limit(qty) \
.run(conn)
My dataset
[
{
"id": "e54893b4-b1d0-49c5-b6aa-9aa9e7d2b73b",
"image": "https://dl.dropboxusercontent.com/u/5822/crowdlabeler/ABLXOTODWJKTXECYZTST.jpg",
"labels": [
{
"account_qty_labeled": 54,
"account_signup_date": "Tue Aug 04 2015 10:12:25 GMT-04:00",
"compensation": 0.01,
"dataset_id": 144,
"label": {
"$$hashKey": "object:45",
"answer": "Yes",
"selected": true
},
"label_duration_sec": 3,
"labeled_at": "Wed Aug 05 2015 16:26:04 GMT-05:00",
"sess_duration_sec": 3,
"sess_qty_labeled": 0,
"user_id": 1
}
],
"labels_completed": 0,
"locked": false,
"text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate adipisci vero minus laudantium reprehenderit exercitationem eius, suscipit facilis laboriosam consequuntur, eligendi quis mollitia excepturi deserunt dicta, dolorem quaerat pariatur provident sint explicabo. Magnam possimus dolorum beatae quidem excepturi quibusdam dolore reprehenderit accusantium quae ad libero, voluptatum laborum, incidunt, voluptate reiciendis."
},
{
"id": "9f08869e-79fd-49c0-a184-c43d2a1c95cf",
"image": "https://dl.dropboxusercontent.com/u/5822/crowdlabeler/ACSGHDYECQWQXDHIOBYC.jpg",
"labels": [],
"labels_completed": 0,
"locked": false,
"text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate adipisci vero minus laudantium reprehenderit exercitationem eius, suscipit facilis laboriosam consequuntur, eligendi quis mollitia excepturi deserunt dicta, dolorem quaerat pariatur provident sint explicabo. Magnam possimus dolorum beatae quidem excepturi quibusdam dolore reprehenderit accusantium quae ad libero, voluptatum laborum, incidunt, voluptate reiciendis."
},
{
"id": "9fba0a39-4cfd-4a97-b48f-e8bf2b0d46c5",
"image": "https://dl.dropboxusercontent.com/u/5822/crowdlabeler/ADMNIUYKUHAIOHMAFXBK.jpg",
"labels": [],
"labels_completed": 0,
"locked": false,
"text": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cupiditate adipisci vero minus laudantium reprehenderit exercitationem eius, suscipit facilis laboriosam consequuntur, eligendi quis mollitia excepturi deserunt dicta, dolorem quaerat pariatur provident sint explicabo. Magnam possimus dolorum beatae quidem excepturi quibusdam dolore reprehenderit accusantium quae ad libero, voluptatum laborum, incidunt, voluptate reiciendis."
}
]
Thank you for your help!
This was a tough one but the solution ended up being two-fold.
A slight modification to the schema when saving, then using RethinkDB's contains method.
Here is the modified query that works well.
open_tasks = rdbt \
.order_by(index=r.desc('labels_completed')) \
.filter(r.row['locked'] == False) \
.filter(lambda task:
r.not_(task['labeler_ids'].contains(current_user.id))) \
.limit(qty) \
.run(conn)