Skip to content

Commit

Permalink
#3172: Add aggregation configs, date format
Browse files Browse the repository at this point in the history
  • Loading branch information
cemalettin-work committed Oct 23, 2020
1 parent 328b592 commit 66cc367
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 104 deletions.
34 changes: 15 additions & 19 deletions client/src/components/Chart.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
import useLayout from "layouts/useLayout"
import { LAYOUT_AGGREGATORS } from "layouts/utils"
import PropTypes from "prop-types"
import React from "react"

const Chart = ({ items, layoutType, element: Element, style, viewDate }) => {
const [ref, layout, uniqueKey] = useLayout(layoutType, viewDate)
const Chart = ({ items, layoutType, element: Element, style }) => {
const aggregator = LAYOUT_AGGREGATORS[layoutType]
const [aggregatedItems, aggregationKey] = aggregator(items)
const [ChartElement, layout, initViewState, ref] = useLayout(
layoutType,
aggregationKey
)

return (
<svg ref={ref} style={style}>
{items.map(item => {
const boundingRect = layout(item)
// if it wasn't in the layout ( Ex: different year, month)
if (!boundingRect) {
return null
}
return (
<g
transform={`translate(${boundingRect.x}, ${boundingRect.y})`}
key={item[uniqueKey]}
>
<Element item={item} dimensions={boundingRect} />
</g>
)
})}
<ChartElement
items={aggregatedItems}
layout={layout}
element={Element}
initViewState={initViewState}
/>
</svg>
)
}
Chart.propTypes = {
items: PropTypes.arrayOf(PropTypes.object),
layoutType: PropTypes.string,
element: PropTypes.func,
style: PropTypes.object,
viewDate: PropTypes.object
style: PropTypes.object
}
export default Chart
36 changes: 36 additions & 0 deletions client/src/components/DateChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import PropTypes from "prop-types"
import React from "react"

const DateChart = ({
items,
layout,
element: Element,
initViewState: viewDate
}) => {
return (
<>
{items.map(item => {
const boundingRect = layout(item, viewDate)
// if it isn't in the layout ( e.g different year, month)
if (!boundingRect) {
return null
}
return (
<g
transform={`translate(${boundingRect.x}, ${boundingRect.y})`}
key={item.aggregationKey}
>
<Element item={item} dimensions={boundingRect} />
</g>
)
})}
</>
)
}
DateChart.propTypes = {
items: PropTypes.arrayOf(PropTypes.object),
element: PropTypes.func,
layout: PropTypes.func,
initViewState: PropTypes.object
}
export default DateChart
10 changes: 10 additions & 0 deletions client/src/components/GeoChart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import PropTypes from "prop-types"
import React from "react"

const GeoChart = ({ items }) => {
return <>{items.title}</>
}
GeoChart.propTypes = {
items: PropTypes.arrayOf(PropTypes.object)
}
export default GeoChart
37 changes: 33 additions & 4 deletions client/src/components/HeatMap.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
import HeatWidget from "components/aggregations/HeatWidget"
import Chart from "components/Chart"
import { LAYOUT_TYPES } from "layouts"
import PropTypes from "prop-types"
import React from "react"
import React, { useState } from "react"

const HeatMap = ({ children }) => {
return <div>{children}</div>
const HeatMap = ({ items, style }) => {
const [layout, setLayout] = useState(LAYOUT_TYPES.YEAR)
return (
<div>
<div>
<label htmlFor="layouts">Layout:</label>
<select
value={layout}
name="layouts"
id="layouts"
onChange={e => setLayout(e.target.value)}
>
<option value={LAYOUT_TYPES.MONTH}>Month</option>
<option value={LAYOUT_TYPES.YEAR}>Year</option>
{/* FIXME: Add geo option when ready */}
{/* <option value={LAYOUT_TYPES.GEO}>Geo</option> */}
</select>
</div>
<Chart
items={items}
layoutType={layout}
element={HeatWidget}
style={style}
/>
</div>
)
}
HeatMap.propTypes = {
children: PropTypes.node
items: PropTypes.arrayOf(PropTypes.object),
style: PropTypes.object
}

export default HeatMap
16 changes: 12 additions & 4 deletions client/src/layouts.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
export yearLayout from "layouts/yearLayout"
export monthLayout from "layouts/monthLayout"
export geoLayout from "layouts/geoLayout"
import geoLayout from "layouts/geoLayout"
import monthLayout from "layouts/monthLayout"
import yearLayout from "layouts/yearLayout"

export const TYPES = {
export const LAYOUT_TYPES = {
YEAR: "year",
MONTH: "month",
GEO: "geo"
}

const LAYOUTS = {
[LAYOUT_TYPES.GEO]: geoLayout,
[LAYOUT_TYPES.MONTH]: monthLayout,
[LAYOUT_TYPES.YEAR]: yearLayout
}

export default LAYOUTS
36 changes: 13 additions & 23 deletions client/src/layouts/useLayout.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,24 @@
import * as layouts from "layouts"
import LAYOUTS from "layouts"
import { INIT_LAYOUT_STATES, LAYOUT_CHART_ELEMENTS } from "layouts/utils"
import { useMemo } from "react"
import useDimensions from "react-use-dimensions"

const useLayout = (layoutType, viewDate) => {
const useLayout = (layoutType, aggregationKey) => {
const [ref, dimensions] = useDimensions()
const layout = useMemo(() => {
let layoutTemp
switch (layoutType) {
case layouts.TYPES.YEAR:
layoutTemp = layouts.yearLayout
break
case layouts.TYPES.MONTH:
layoutTemp = layouts.monthLayout
break
case layouts.TYPES.GEO:
layoutTemp = layouts.geoLayout
break
default:
layoutTemp = layouts.yearLayout
break
}
return item => {
const vars = useMemo(() => {
const chartElement = LAYOUT_CHART_ELEMENTS[layoutType]
const specificLayout = LAYOUTS[layoutType]
const initViewState = INIT_LAYOUT_STATES[layoutType]
const layout = (item, viewArgs) => {
return !dimensions?.width || !dimensions?.height
? null
: layoutTemp(item, dimensions, viewDate)
: specificLayout(item, dimensions, aggregationKey, viewArgs)
}
}, [layoutType, dimensions, viewDate])

// TODO: return dynamic key instead of "id"
return [ref, layout, "id"]
return [chartElement, layout, initViewState]
}, [layoutType, dimensions, aggregationKey])

return [...vars, ref]
}

export default useLayout
41 changes: 30 additions & 11 deletions client/src/layouts/utils.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,48 @@
import * as layouts from "layouts"
import DateChart from "components/DateChart"
import GeoChart from "components/GeoChart"
import { LAYOUT_TYPES } from "layouts"
import _groupBy from "lodash/groupBy"
import moment from "moment"

export const AGGREGATOR_TYPES = {
[layouts.TYPES.GEO]: groupByLocation,
[layouts.TYPES.MONTH]: groupByDay,
[layouts.TYPES.YEAR]: groupByDay
export const DATE_LAYOUT_FORMAT = "DD-MM-YYYY"

export const LAYOUT_AGGREGATORS = {
[LAYOUT_TYPES.GEO]: groupByLocation,
[LAYOUT_TYPES.MONTH]: groupByDay,
[LAYOUT_TYPES.YEAR]: groupByDay
}

export const LAYOUT_CHART_ELEMENTS = {
[LAYOUT_TYPES.GEO]: GeoChart,
[LAYOUT_TYPES.MONTH]: DateChart,
[LAYOUT_TYPES.YEAR]: DateChart
}

export const INIT_LAYOUT_STATES = {
// FIXME: Add default location when ready
[LAYOUT_TYPES.GEO]: {},
[LAYOUT_TYPES.MONTH]: moment(),
[LAYOUT_TYPES.YEAR]: moment()
}

export function groupByDay(inItems, dateField) {
const uniqueKey = "date"
export function groupByDay(inItems) {
const aggregationKey = "date"
const outItems = []
// group items by same day in an object { day1: [item1, item2], day2: [item3, item4]}
const tempItemsObj = _groupBy(inItems, item =>
moment(item[dateField]).format("DD-MM-YYYY")
moment(item[aggregationKey]).format(DATE_LAYOUT_FORMAT)
)
// console.log("groupByDay intermediate object", tempItemsObj)

// convert that object to a list of objects
tempItemsObj.keys().forEach(dayStr => {
Object.keys(tempItemsObj).forEach(dayStr => {
outItems.push({
[uniqueKey]: dayStr,
[aggregationKey]: dayStr,
numOfEvents: tempItemsObj[dayStr].length
})
})
// console.log("out items", outItems)

return [outItems, uniqueKey]
return [outItems, aggregationKey]
}
export function groupByLocation() {}
14 changes: 10 additions & 4 deletions client/src/layouts/yearLayout.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DATE_LAYOUT_FORMAT } from "layouts/utils"
import moment from "moment"

const yearLayout = (item, dimensions, viewDate) => {
const yearLayout = (item, dimensions, dateField, viewDate) => {
// figure out which year input is
// figure out where the item located according to its day
// calculate the how much x-y translation
const momentDate = moment(item.date)
const momentDate = moment(item[dateField], DATE_LAYOUT_FORMAT)
if (!viewDate.isSame(momentDate, "year")) {
return null
}
Expand All @@ -21,7 +21,13 @@ const yearLayout = (item, dimensions, viewDate) => {
const weekDiff = Math.floor(dayDiff / 7)

const weekDayDiff = dayDiff % 7

// console.log("yearLayout item=", item, {
// momentDate,
// dayDiff,
// weekDiff,
// weekDayDiff,
// numOfWeeks
// })
return {
x: (dimensions.width * weekDiff) / numOfWeeks,
y: (dimensions.height * weekDayDiff) / 7,
Expand Down
Loading

0 comments on commit 66cc367

Please sign in to comment.