Add support for dates and increase robustness

This commit is contained in:
Andrew Kingston 2020-11-04 12:43:56 +00:00
parent ac40abac56
commit e7eab46435
7 changed files with 132 additions and 68 deletions

View File

@ -452,6 +452,13 @@ export default {
dependsOn: "datasource",
control: MultiTableViewFieldSelect,
},
{
label: "Format",
key: "yAxisUnits",
control: OptionSelect,
options: ["Default", "Thousands", "Millions"],
defaultValue: "Default",
},
{
label: "Y Axis Label",
key: "yAxisLabel",
@ -486,12 +493,6 @@ export default {
valueKey: "checked",
defaultValue: false,
},
{
label: "Horizontal",
key: "horizontal",
control: Checkbox,
valueKey: "checked",
},
{
label: "Data Labels",
key: "dataLabels",

View File

@ -413,7 +413,6 @@
"default": "400"
},
"width": "number",
"horizontal": "bool",
"dataLabels": "bool",
"animate": {
"type": "bool",
@ -422,7 +421,14 @@
"xAxisLabel": "string",
"yAxisLabel": "string",
"legend": "bool",
"stacked": "bool"
"stacked": "bool",
"yAxisUnits": {
"type": "options",
"default": "Default",
"options": [
"Default", "Thousands", "Millions"
]
}
}
},
"line": {

View File

@ -4,7 +4,11 @@
export let options
</script>
<div use:chart={options} />
{#if options}
<div use:chart={options} />
{:else}
<div>Invalid chart options</div>
{/if}
<style>
div :global(.apexcharts-legend-series) {

View File

@ -1,3 +1,5 @@
import { labelColumn, valueColumns } from "./BarChart.svelte"
export class ApexOptionsBuilder {
formatters = {
["Default"]: val => Math.round(val * 100) / 100,
@ -136,4 +138,8 @@ export class ApexOptionsBuilder {
this.formatters[units || "Default"]
)
}
xType(type) {
return this.setOption(["xaxis", "type"], type)
}
}

View File

@ -1,7 +1,7 @@
<script>
import { onMount } from "svelte"
import fetchData from "../fetchData"
import { isEmpty, sortBy } from "lodash/fp"
import fetchData, { fetchSchema } from "../fetchData"
import { sortBy } from "lodash/fp"
import { ApexOptionsBuilder } from "./ApexOptionsBuilder"
import ApexChart from "./ApexChart.svelte"
@ -14,55 +14,80 @@
export let height
export let width
export let color
export let horizontal
export let dataLabels
export let animate
export let legend
export let stacked
export let yAxisUnits
let data
$: options = getChartOptions(data)
let options
// Fetch data on mount
onMount(async () => {
if (!isEmpty(datasource)) {
const result = (await fetchData(datasource)).slice(0, 20)
data = sortBy(row => row[labelColumn])(result)
if (!datasource || !labelColumn || !valueColumns || !valueColumns.length) {
return
}
const result = (await fetchData(datasource)).slice(0, 20)
const data = sortBy(row => row[labelColumn])(result)
const schema = await fetchSchema(datasource.tableId)
if (!schema || !data || !data.length) {
return
}
// Check columns are valid
if (datasource.type !== "view") {
if (schema[labelColumn] == null) {
return
}
for (let i = 0; i < valueColumns.length; i++) {
if (schema[valueColumns[i]] == null) {
return
}
}
}
})
function getChartOptions(rows = []) {
// Initialise default chart
let builder = new ApexOptionsBuilder()
.title(title)
.type("bar")
.title(title)
.width(width)
.height(height)
.xLabel(xAxisLabel)
.yLabel(yAxisLabel)
.horizontal(horizontal)
.dataLabels(dataLabels)
.animate(animate)
.legend(legend)
.stacked(stacked)
.yUnits(yAxisUnits)
// Add data if valid datasource
if (rows && rows.length) {
if (valueColumns && valueColumns.length) {
const series = valueColumns.map(column => ({
name: column,
data: rows.map(row => parseFloat(row[column])),
}))
builder = builder.series(series)
}
if (!isEmpty(rows[0][labelColumn])) {
builder = builder.categories(rows.map(row => row[labelColumn]))
}
// Add data
let useDates = false
if (datasource.type !== "view" && schema[labelColumn]) {
const labelFieldType = schema[labelColumn].type
builder = builder.xType(labelFieldType)
useDates = labelFieldType === "datetime"
}
const series = valueColumns.map(column => ({
name: column,
data: data.map(row => {
if (!useDates) {
return row[column]
} else {
return [row[labelColumn], row[column]]
}
}),
}))
builder = builder.series(series)
if (!useDates && data[0][labelColumn] != null) {
builder = builder.categories(data.map(row => row[labelColumn]))
}
// Build chart options
return builder.getOptions()
}
options = builder.getOptions()
})
$: console.log(options)
</script>
<ApexChart {options} />

View File

@ -1,7 +1,7 @@
<script>
import { onMount } from "svelte"
import fetchData from "../fetchData"
import { isEmpty, sortBy } from "lodash/fp"
import fetchData, { fetchSchema } from "../fetchData"
import { sortBy } from "lodash/fp"
import { ApexOptionsBuilder } from "./ApexOptionsBuilder"
import ApexChart from "./ApexChart.svelte"
@ -26,23 +26,37 @@
export let stacked
export let gradient
let data = []
$: options = getChartOptions(data)
let options
// Fetch data on mount
onMount(async () => {
if (!isEmpty(datasource)) {
const result = (await fetchData(datasource)).slice(0, 20)
data = sortBy(row => row[labelColumn])(result)
if (!datasource || !labelColumn || !valueColumns || !valueColumns.length) {
return
}
const result = (await fetchData(datasource)).slice(0, 100)
const data = sortBy(row => row[labelColumn])(result)
const schema = await fetchSchema(datasource.tableId)
if (!schema || !data || !data.length) {
return
}
// Check columns are valid
if (datasource.type !== "view") {
if (schema[labelColumn] == null) {
return
}
for (let i = 0; i < valueColumns.length; i++) {
if (schema[valueColumns[i]] == null) {
return
}
}
}
})
function getChartOptions(rows = []) {
// Initialise default chart
let builder = new ApexOptionsBuilder()
.title(title)
.type(area ? "area" : "line")
// .color(color)
.width(width)
.height(height)
.xLabel(xAxisLabel)
@ -55,23 +69,31 @@
.legend(legend)
.yUnits(yAxisUnits)
// Add data if valid datasource
if (rows && rows.length) {
if (valueColumns && valueColumns.length) {
const series = valueColumns.map(column => ({
name: column,
data: rows.map(row => parseFloat(row[column])),
}))
builder = builder.series(series)
}
if (!isEmpty(rows[0][labelColumn])) {
builder = builder.categories(rows.map(row => row[labelColumn]))
}
// Add data
let useDates = false
if (datasource.type !== "view" && schema[labelColumn]) {
const labelFieldType = schema[labelColumn].type
builder = builder.xType(labelFieldType)
useDates = labelFieldType === "datetime"
}
const series = valueColumns.map(column => ({
name: column,
data: data.map(row => {
if (!useDates) {
return row[column]
} else {
return [row[labelColumn], row[column]]
}
}),
}))
builder = builder.series(series)
if (!useDates && schema[labelColumn]) {
builder = builder.categories(data.map(row => row[labelColumn]))
}
// Build chart options
return builder.getOptions()
}
options = builder.getOptions()
})
</script>
<ApexChart {options} />

View File

@ -15,11 +15,11 @@ export default async function fetchData(datasource, store) {
// Fetch table schema so we can check for linked rows
if (rows && rows.length) {
const table = await fetchTable()
const keys = Object.keys(table.schema)
const schema = await fetchSchema(datasource.tableId)
const keys = Object.keys(schema)
rows.forEach(row => {
for (let key of keys) {
const type = table.schema[key].type
const type = schema[key].type
if (type === "link") {
row[`${key}_count`] = Array.isArray(row[key]) ? row[key].length : 0
} else if (type === "attachment") {
@ -38,12 +38,6 @@ export default async function fetchData(datasource, store) {
return []
}
async function fetchTable() {
const FETCH_TABLE_URL = `/api/tables/${datasource.tableId}`
const response = await api.get(FETCH_TABLE_URL)
return await response.json()
}
async function fetchTableData() {
if (!name.startsWith("all_")) {
throw new Error("Incorrect table convention - must begin with all_")
@ -85,3 +79,9 @@ export default async function fetchData(datasource, store) {
return row[datasource.fieldName]
}
}
export async function fetchSchema(id) {
const FETCH_TABLE_URL = `/api/tables/${id}`
const response = await api.get(FETCH_TABLE_URL)
return (await response.json()).schema
}