How to render default values from Django Serializer for charting purposes? - django-rest-framework

I'm using Django Rest to aggregate some data for my front end to render (through Chart.js)
Say I want to have a chart with the months (Jan-Dec) on the x-axis and total_cost values on the y-axis. How can I make sure to always spit out all 12 months and provide "0" for the y-axis if no value for that month is found?
E.g, this serialized data from Django rest is missing values for November and December, but I still need those labels for Chart JS to properly render
[
{
"month": "2020-01-01",
"total_cost": "199.00"
},
{
"month": "2020-02-01",
"total_cost": "222.00"
},
{
"month": "2020-03-01",
"total_cost": "399.00"
},
{
"month": "2020-04-01",
"total_cost": "414.00"
},
{
"month": "2020-05-01",
"total_cost": "555.00"
},
{
"month": "2020-06-01",
"total_cost": "615.00"
},
{
"month": "2020-07-01",
"total_cost": "700.00"
},
{
"month": "2020-08-01",
"total_cost": "913.00"
},
{
"month": "2020-09-01",
"total_cost": "552.00"
},
{
"month": "2020-10-01",
"total_cost": "1000.00"
}
]
Perhaps this is a matter for the frontend rather than the backend?
If it helps, my django query looks like this:
queryset = Expense.object.annotate(month=TruncMonth('expense_date'))
.values('month')
.annotate(total_cost=Sum('cost'))
.values('month', 'total_cost')
.order_by('month')

As you correctly guessed, this is best handled in the the frontend rather than in the backend.
You can define your x-axis as a time cartesian axis that accepts the data of your dataset as individual points through objects containing x and y properties each. Given the base data present in an array named baseData, such data can be created through Array.map() as follows:
baseData.map(o => ({ x: o.month, y: Number(o.total_cost) }))
To make sure, all 12 months are included in the chart, you'll further have to define ticks.min and ticks.max options on the x-axis.
ticks: {
min: '2020-01',
max: '2020-12'
}
Please take a look at the following runnable code and see how it works.
Note that Chart.js internally uses Moment.js for the functionality of the time axis. Therefore you should use the bundled version of Chart.js that includes Moment.js in a single file.
const baseData = [
{ "month": "2020-01-01", "total_cost": "199.00" },
{ "month": "2020-02-01", "total_cost": "222.00" },
{ "month": "2020-03-01", "total_cost": "399.00" },
{ "month": "2020-04-01", "total_cost": "414.00" },
{ "month": "2020-05-01", "total_cost": "555.00" },
{ "month": "2020-06-01", "total_cost": "615.00" },
{ "month": "2020-07-01", "total_cost": "700.00" },
{ "month": "2020-08-01", "total_cost": "913.00" },
{ "month": "2020-09-01", "total_cost": "552.00" },
{ "month": "2020-10-01", "total_cost": "1000.00" }
];
new Chart(document.getElementById('myChart'), {
type: 'line',
data: {
datasets: [{
label: 'My Dataset',
data: baseData.map(o => ({ x: o.month, y: Number(o.total_cost) })),
fill: false,
backgroundColor: 'green',
borderColor: 'green'
}]
},
options: {
responsive: true,
scales: {
xAxes: [{
type: 'time',
time: {
unit: 'month',
tooltipFormat: 'MMM YYYY'
},
ticks: {
min: '2020-01',
max: '2020-12'
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
stepSize: 200
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="myChart" height="90"></canvas>

Related

Datatables with laravel how to render hasMany relationship

i have the following response from my controller
[
{
"id": 1,
"place_id": 1,
"name": "Test",
"type": 5,
"created_at": "2018-06-04 15:29:02",
"updated_at": "2018-06-04 15:29:02",
"time": [
{
"id": 1,
"stadium_id": 1,
"day": "Saturday",
"from_hour": 7,
"to_hour": 12,
"created_at": "2018-06-04 15:29:42",
"updated_at": "2018-06-04 15:29:42"
},
{
"id": 2,
"stadium_id": 1,
"day": "Sunday",
"from_hour": 7,
"to_hour": 12,
"created_at": "2018-06-04 15:54:03",
"updated_at": "2018-06-04 15:54:03"
}
]
}
]
i want to access the day attribute in each time object
i tried the code below but of course it just loads the first result
is there a solution
columns: [
{ data: 'name' },
{ data: 'type' },
{ data: 'time.0.day' },
{ data: 'time.0.from_hour' },
{ data: 'time.0.to_hour' },
],
You can actually access time array by using the render property:
columns: [{
data: 'name'
},
{
data: 'type'
},
{
data: 'time',
render: function(data, type, row) {
var txt = '';
data.forEach(function(item) {
if (txt.length > 0) {
txt += '</br> '
}
txt += item.day;
});
return txt;
}
},
...
This would just add a line break for every objects in time. Here's a Fiddle for you to examine.

loading json data How to show clustered bar charts in dynamic graphs

Updated
Can you possible graphs value dynamic.
Below code pass a static value but i get dynamic code graphs.
Example: resultx get 3-4 subcategory i every time define graphs value.
var processedChartData = resultx.map(function(rawDataElement) {
var newDataElement = { "category": rawDataElement.category };
rawDataElement.data.forEach(function(nestedElement, index) {
newDataElement["value" + index] = nestedElement.value;
newDataElement["subcategory" + index] = nestedElement.subcategory
});
return newDataElement;
});
AmCharts.makeChart(id, {
"type": "serial",
"theme": "light",
"categoryField": "category",
"rotate": false,
"startDuration": 0,
"categoryAxis": {
"gridPosition": "start",
"position": "left"
},
"graphs": [{
"fillAlphas": 0.9,
"lineAlpha": 0.2,
"title": "2004",
"type": "column",
"balloonText": "[[subcategory0]]: [[value]]",
"valueField": "value0"
}, {
"fillAlphas": 0.9,
"lineAlpha": 0.2,
"title": "2005",
"type": "column",
"balloonText": "[[subcategory1]]: [[value]]",
"valueField": "value1"
},
{
"fillAlphas": 0.9,
"lineAlpha": 0.2,
"title": "2005",
"type": "column",
"balloonText": "[[subcategory2]]: [[value]]",
"valueField": "value2",
}],
"guides": [],
"allLabels": [],
"balloon": {},
"titles": [],
"dataProvider": processedChartData,
"export": {
"enabled":false
}
});
Original question:
Clustered bar charts Array inside key how to display multiple bar charts.
My json below:
[
{
"0":
{
"package_sold":"88",
"vSectorName":"Meat"
},
"country":"France"
},
{
"0":
{
"package_sold":"68",
"vSectorName":"Meat"
},
"1":
{
"package_sold":"151",
"vSectorName":"Poultry"
},
"country":"United Kingdom"
}
]
How to show in graph dataProvider
AmCharts doesn't support nested JSON. You'll need to flatten your JSON into a single object so that your valueFields are distinct in each element of your array.
For example, this:
{
"0":
{
"package_sold":"68",
"vSectorName":"Meat"
},
"1":
{
"package_sold":"151",
"vSectorName":"Poultry"
},
"country":"United Kingdom"
}
can be turned into this:
{
"Meat_package_sold": 68,
"Poultry_package_sold": 151,
"country": "United Kingdom"
}
From there you can set your graph valueField to "Meat_package_sold" and "Poultry_package_sold". I'm assuming your categoryField is "country".
You'll either need to change your backend or write some some JS to remap your data to a format that AmCharts can recognize.
Edit: Here's a basic example that remaps your JSON data using JS:
var rawData = [{
"0": {
"package_sold": "88",
"vSectorName": "Meat"
},
"country": "France"
},
{
"0": {
"package_sold": "68",
"vSectorName": "Meat"
},
"1": {
"package_sold": "151",
"vSectorName": "Poultry"
},
"country": "United Kingdom"
}
]
var newData = [];
rawData.forEach(function(dataItem) {
var newDataItem = {};
Object.keys(dataItem).forEach(function(key) {
if (typeof dataItem[key] === "object") {
newDataItem[dataItem[key]["vSectorName"] + "_package_sold"] = dataItem[key]["package_sold"];
} else {
newDataItem[key] = dataItem[key];
}
});
newData.push(newDataItem);
});
console.log(JSON.stringify(newData));
Demo of your chart using the correct JSON format:
var chart = AmCharts.makeChart("chartdiv", {
"type": "serial",
"theme": "light",
"categoryField": "country",
"graphs": [{
"fillAlphas": 0.8,
"lineAlpha": 0.2,
"type": "column",
"valueField": "Meat_package_sold"
},
{
"fillAlphas": 0.8,
"lineAlpha": 0.2,
"type": "column",
"valueField": "Poultry_package_sold"
}
],
"dataProvider": [{
"Meat_package_sold": 88,
"country": "France",
}, {
"Meat_package_sold": 68,
"Poultry_package_sold": 151,
"country": "United Kingdom"
}, {
"Meat_package_sold": 120,
"Poultry_package_sold": 110,
"country": "Germany"
}]
});
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
#chartdiv {
width: 100%;
height: 100%;
}
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<script src="//www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>
This is dynamic json data, but to show how to use and build I defined it so.
In actual, i use ajax to get json data and then building it dynamically.
Here I had Generate Graph,GLName against its opening for each month comparatively. hope it will helpful for all
var resp=[
{
"MONTH_": "01",
"MONTH_NAME": "JAN",
"YEAR_": "2018",
"GL_NAME": "CASH,FACTORY, SITE,OFFICE ",
"GL_ID": "79,81,522,89",
"OPENING": "606294,0,24851,170392",
"RECEIPT": "1641300,40000,210850,82300",
"PAYMENT": "2074921,103209,168893,40000",
"CLOSING": "172673,149483,66808,0"
},
{
"MONTH_": "02",
"MONTH_NAME": "FEB",
"YEAR_": "2018",
"GL_NAME": " SITE,CASH,OFFICE ,FACTORY",
"GL_ID": "81,79,522,89",
"OPENING": "66808,172673,0,149483",
"RECEIPT": "102650,40000,3479000,200000",
"PAYMENT": "239379,168339,40000,3388527",
"CLOSING": "-69921,0,181144,263146"
},
{
"MONTH_": "03",
"MONTH_NAME": "MAR",
"YEAR_": "2018",
"GL_NAME": "FACTORY,CASH,OFFICE , SITE",
"GL_ID": "89,81,79,522",
"OPENING": "181144,-69921,0,263146",
"RECEIPT": "30000,40000,1943500,200000",
"PAYMENT": "69242,1806551,18177",
"CLOSING": "141902,400095,40000,111902"
}
]
var newChartDataArr = [];
var colNameArr = [];
var GLID = [];
var amtArr = [];
var newBarGraph = [];
myJsonString1 = JSON.stringify(resp);
var obj = JSON.parse(myJsonString1);
var obj1 = resp[0];
//spliting of GLName
if (obj1.GL_NAME.toString().indexOf(',') != -1) {
colNameArr = obj1.GL_NAME.split(',');
GLID =obj1.GL_ID.split(',');
} else {
colNameArr.push(obj1.GL_NAME);
GLID =obj1.GL_ID.split(',');
}
//Getting Month and Opening of GL
$.each(resp, function (i, value) {
var newObj = {};
newObj['MONTH_NAME'] = value.MONTH_NAME+"-"+value.YEAR_;
$.each(value, function (k, v) {
if (k == 'OPENING') {
for (var i = 0; i < colNameArr.length; i++) {
if (v.toString().indexOf(',') != -1) {
newObj[colNameArr[i]] = parseFloat(v.split(',')[i]);
} else {
newObj[colNameArr[i]] = parseFloat(v);
}
}
}
});
newChartDataArr.push(newObj); //GL with Opening
});
for (var i = 0; i < colNameArr.length; i++) {
let graph = {};
graph["id"] = "v-"+GLID[i];
graph["balloonText"] = colNameArr[i] + " [[category]] Amount:[[value]]",
graph["title"] = colNameArr[i];
graph["valueField"] = colNameArr[i];
graph["fillAlphas"] = 0.8;
graph["lineAlpha"] = 0.2;
graph["type"] = "column";
newBarGraph.push(graph);
}
chart = AmCharts.makeChart("Monthdiv", {
"type": "serial",
"theme": "light",
"categoryField": "MONTH_NAME",
"startDuration": 1,
"trendLines": [],
"legend": {
"position": "bottom",
"maxColumns": 2,
"useGraphSettings": true
},
"depth3D": 10,
"angle": 60,
"graphs": newBarGraph,
"guides": [],
"valueAxes": [
{
"position": "left",
"title": "Opening"
}
],
"categoryAxis": {
"gridPosition": "start",
"labelRotation": 90,
"title": "Months"
},
"allLabels": [],
"balloon": {},
"titles": [{
"text":"Monthly Sale"
}],
"dataProvider": newChartDataArr,
"export": {
"enabled": true
},
"listeners": [{
"event": "clickGraphItem",
"method": function (event) {
var gl_ID=(event.item.graph.id).slice(2);
var month = (event.item.category).slice(0, 3);
var calender = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
var monthVal = calender.indexOf(month) + 1;
var year = (event.item.category).slice(4, 8);
$("#fromDate").val("01/" + monthVal + "/" + year);
$("#toDate").val("30/" + monthVal + "/" + year);
Daliy(gl_ID,event.item.category);
showSummary();
}
}]
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Resources -->
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<div class="col-sm-12" id="Monthdiv" style="height: 370px;">

amCharts zoomtodates not loading

First question ever on the stack. Trying to get zoomToDates to work in amCharts I'm developing. I've tried the other solutions I could find to others' questions and nothing worked. So without further ado ...
Resources:
//www.amcharts.com/lib/3/amcharts.js
//www.amcharts.com/lib/3/serial.js
//www.amcharts.com/lib/3/amstock.js
//www.amcharts.com/lib/3/themes/black.js
//www.amcharts.com/lib/3/plugins/export/export.min.js
//www.amcharts.com/lib/3/plugins/export/export.css
//www.amcharts.com/lib/3/plugins/dataloader/dataloader.min.js
HTML:
<div id="$CHART$" style="width: 100%; height: 450px;"></div>
JS:
AmCharts.makeChart( "$CHART$", {
"type": "stock",
"theme": "black",
"categoryAxesSettings.equalSpacing": true,
"dataDateFormat":"YYYY-MM-DD",
"valueAxes": [ {
"position": "left",
} ],
"dataSets": [ {
"title": "Bond",
"fieldMappings": [ {
"fromField": "open",
"toField": "open"
}, {
"fromField": "high",
"toField": "high"
}, {
"fromField": "low",
"toField": "low"
}, {
"fromField": "close",
"toField": "close"
} ],
"dataLoader": {
"url": "removed for stackoverflow post"
},
"categoryField": "date"
},
],
panels: [ {
title: "Security",
percentHeight: 100,
stockGraphs: [ {
"valueField": "security",
"type": "candlestick",
"showBalloon": true,
"proCandlesticks": true,
"balloonText": "Open:<b>[[open]]</b><br>Low:<b>[[low]]</b><br>High:<b>[[high]]</b><br>Close:<b>[[close]]</b><br>",
"closeField": "close",
"fillColors": "#7f8da9",
"highField": "high",
"lineColor": "#7f8da9",
"lineAlpha": 1,
"lowField": "low",
"fillAlphas": 0.9,
"negativeFillColors": "#db4c3c",
"negativeLineColor": "#db4c3c",
"openField": "open",
} ],
stockLegend: {
periodValueTextRegular: "[[security.close]]"
}
}
],
chartScrollbarSettings: {
graph: "g1"
},
chartCursorSettings: {
valueBalloonsEnabled: true
},
chart.addListener("rendered", zoomChart);
zoomChart();
function zoomChart() {
event.chart.zoomToDates(new Date(2017, 10, 22), new Date(2017, 10, 25));
}
});
Any help would be really appreciated. I've been working on this for too long to admit. Thanks.
It looks like you're using the AmCharts Wordpress plugin going by the $CHART idenfitier. The main issue is the syntax of your AmCharts.makeChart call - your addListener code is inside the config when it should be outside of the call entirely. You're also not referring to the actual chart instance, nor are you using the event argument correctly, for example:
var $CHART$ = AmCharts.makeChart("$CHART$", {
// ...
});
$CHART$.addListener("rendered", zoomChart);
function zoomChart(event) {
event.chart.zoom(new Date(2017, 10, 22), new Date(2017, 10, 25));
};
You also have to use zoom for stock charts as zoomToDates won't work.
Ideally, you should use the listeners array instead of addListener as you can sometimes run into timing issues where events like rendered can fire before you can call addListener:
AmCharts.makeChart("$CHART$", {
// all of your other config and data omitted
"listeners": [{
"event": "rendered",
"method": function(event) {
event.chart.zoom(new Date(2017, 10, 22), new Date(2017, 10, 25));
}
}]
});

Dynamic Data push for date based fields

I'm trying to push data dynamically to the dataProvider of XY charts in amcharts but I'm not able to achieve that.
My chart is not being drawn.
My x axis would be month and the y axis would be a numeric value.
I tried something like this
all the month and total arrays are declared . My obj looks something like this:
dataProviderObj{(date : 2015-Jan , y :80 , value :80 ),(date : 2015-Feb , y :70 , value :70)};
dataProviderObj={};
I'm trying to push like this
for(i=0;i<=month.length;i++){
dataProviderObj.push{(
"date" : month[i],
"y" : total[i],
"value" : total[i]
)}
}
dataprovider.push(dataProviderObj);
var chart = AmCharts.makeChart("chartdiv", {
"type": "xy",
"theme": "light",
"marginRight": 80,
"dataDateFormat": "YYYY-MMM",
"startDuration": 1.5,
"trendLines": [],
"balloon": {
"adjustBorderColor": false,
"shadowAlpha": 0,
"fixedPosition":true
},
"graphs": [{
"balloonText": "<div style='margin:5px;'><b>[[x]]</b><br>y:<b>[[y]]</b><br>value:<b>[[value]]</b></div>",
"bullet": "diamond",
"id": "AmGraph-1",
"lineAlpha": 0,
"lineColor": "#b0de09",
"fillAlphas": 0,
"valueField": "value",
"xField": "date",
"yField": "y"
}, {
"balloonText": "<div style='margin:5px;'><b>[[x]]</b><br>y:<b>[[y]]</b><br>value:<b>[[value]]</b></div>",
"bullet": "round",
"id": "AmGraph-2",
"lineAlpha": 0,
"lineColor": "#fcd202",
"fillAlphas": 0,
"valueField": "bValue",
"xField": "date",
"yField": "by"
}],
"valueAxes": [{
"id": "ValueAxis-1",
"axisAlpha": 0
}, {
"id": "ValueAxis-2",
"axisAlpha": 0,
"position": "bottom",
"type": "date",
"minimumDate": new Date(2015, 0, 01),
"maximumDate": new Date(2015, 12, 13)
}],
"allLabels": [],
"titles": [],
"dataProvider": dataprovider,
"export": {
"enabled": true
},
"chartScrollbar": {
"offset": 15,
"scrollbarHeight": 5
},
"chartCursor":{
"pan":true,
"cursorAlpha":0,
"valueLineAlpha":0
}
});
I want to get dynamic Date in x axis and dynamic numeric value in y axis with a value . Kindly help me draw such a xy Chart in amcharts
There are two issues -
1) Your logic for populating the dataprovider isn't right. You need to push directly to the dataprovider array in the loop. The logic should be:
var dataprovider = [];
for(i=0;i<=month.length;i++){
dataProvider.push({
"date" : month[i],
"y" : total[i],
"value" : total[i]
});
}
Note the placement of the parentheses and curly braces - you're calling the dataprovider array's push function with the parentheses and you're pushing an object ({ ... }) containing your data into the array.
2) "MMM" is not a supported in dataDateFormat. As you can see in AmCharts' formatting dates documentation, any format with an asterisk is not supported. Your data/dates should look like this in the resulting dataprovider array:
dataprovider = [{
"date": "2015-01",
"y": 19,
"value": 19
},
{
"date": "2015-02",
"y": 18,
"value": 18
},
// etc
]
Here's a demo with correctly formatted data
//dummy data:
var month = [ "2015-01", "2015-02", "2015-03", "2015-04", "2015-05", "2015-06", "2015-07", "2015-08", "2015-09", "2015-10", "2015-11", "2015-12"];
var total = [ 17, 16, 15, 16, 19, 20, 17, 20, 16, 19, 16, 18 ];
var dataprovider = [];
for(var i=0;i<=month.length;i++){
dataprovider.push({
"date" : month[i],
"y" : total[i],
"value" : total[i]
});
}
var chart = AmCharts.makeChart("chartdiv", {
"type": "xy",
"theme": "light",
"marginRight": 80,
"dataDateFormat": "YYYY-MM",
"startDuration": 1.5,
"trendLines": [],
"balloon": {
"adjustBorderColor": false,
"shadowAlpha": 0,
"fixedPosition": true
},
"graphs": [{
"balloonText": "<div style='margin:5px;'><b>[[x]]</b><br>y:<b>[[y]]</b><br>value:<b>[[value]]</b></div>",
"bullet": "diamond",
"id": "AmGraph-1",
"lineAlpha": 0,
"lineColor": "#b0de09",
"fillAlphas": 0,
"valueField": "value",
"xField": "date",
"yField": "y"
}, {
"balloonText": "<div style='margin:5px;'><b>[[x]]</b><br>y:<b>[[y]]</b><br>value:<b>[[value]]</b></div>",
"bullet": "round",
"id": "AmGraph-2",
"lineAlpha": 0,
"lineColor": "#fcd202",
"fillAlphas": 0,
"valueField": "bValue",
"xField": "date",
"yField": "by"
}],
"valueAxes": [{
"id": "ValueAxis-1",
"axisAlpha": 0
}, {
"id": "ValueAxis-2",
"axisAlpha": 0,
"position": "bottom",
"type": "date",
"minimumDate": new Date(2014, 11, 1),
"maximumDate": new Date(2016, 0, 1)
}],
"allLabels": [],
"titles": [],
"dataProvider": dataprovider,
"export": {
"enabled": true
},
"chartScrollbar": {
"offset": 15,
"scrollbarHeight": 5
},
"chartCursor": {
"pan": true,
"cursorAlpha": 0,
"valueLineAlpha": 0
}
});
<script type="text/javascript" src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script type="text/javascript" src="//www.amcharts.com/lib/3/xy.js"></script>
<div id="chartdiv" style="width 100%; height: 400px"></div>

Kendo UI Radar Chart

Hi I am trying to make a Kendo UI radar chart. I want to know the correct format in order to display the data.
{
new {year = year, thisyear = new {satisfaction = pq1, organisation=pq2, expecations=pq3, teaching=pq3, consistent=pq4} } //cpe
};
Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
Response.Cache.SetMaxAge(new TimeSpan(24 * 31, 0, 0));
return Json(radata, JsonRequestBehavior.AllowGet);ยจ
Is this correct?
Your question is a little short on details, but if I understand correctly you could organize your data like this:
var data = [
{
"Criteria": "satisfaction ",
"Y2015": 5,
"Y2014": 8
},
{
"Criteria": "organisation",
"Y2015": 8,
"Y2014": 7
},
{
"Criteria": "expecations",
"Y2015": 6,
"Y2014": 9
},
{
"Criteria": "teaching",
"Y2015": 7,
"Y2014": 7
},
{
"Criteria": "consistent",
"Y2015": 5,
"Y2014": 9
},
]
$("#chart").kendoChart({
title: {
text: "Survey"
},
dataSource: data,
seriesDefaults: {
type: "radarLine",
style: "smooth"
},
series: [{
name: "2015",
field: "Y2015"
}, {
name: "2014",
field: "Y2014"
}],
categoryAxis: {
field: "Criteria"
},
valueAxis: {
max: 10
},
theme: "Fiori"
});
DEMO

Resources