From b88894cd027d27b8fb7ab358128f52c6f24fbe0d Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 14 Feb 2019 00:00:35 -0500 Subject: ... --- src/Analyze.tsx | 25 ++++++++-------- src/Chart.tsx | 73 ----------------------------------------------- src/background.ts | 38 +++++++++++++------------ src/duration.ts | 6 ++++ src/graph.ts | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 104 deletions(-) create mode 100644 src/graph.ts (limited to 'src') diff --git a/src/Analyze.tsx b/src/Analyze.tsx index d9777a7..e0e852d 100644 --- a/src/Analyze.tsx +++ b/src/Analyze.tsx @@ -33,7 +33,7 @@ const styles = (theme: Theme) => ({ }, }); -class Analyze extends React.Component { +class Analyze extends React.Component<{classes: {buttonSpacer: string}}> { msgClient: MsgClient; dialogPromiseResolver: (r: boolean) => void; @@ -48,7 +48,8 @@ class Analyze extends React.Component { snackBarMsg: 'unknown', snackBarVariant: 'error', dialogOpen: false, - dialogMsg: {title: '', message: ''} + dialogMsg: {title: '', message: ''}, + focusedInput: null as any }; constructor(props: any) { @@ -135,7 +136,7 @@ class Analyze extends React.Component { } reset = () => { - this.handleDialogOpen("Reset", "Are you sure to reset the patterns?").then(ans => { + this.openDialog("Reset", "Are you sure to reset the patterns?").then(ans => { if (!ans) return; this.loadPatterns([]); this.setState({ startDate: null, endDate: null }); @@ -158,7 +159,7 @@ class Analyze extends React.Component { } default = () => { - this.handleDialogOpen("Load Default", "Load the calendars as patterns?").then(ans => { + this.openDialog("Load Default", "Load the calendars as patterns?").then(ans => { if (!ans) return; this.loadDefaultPatterns(); }); @@ -169,11 +170,11 @@ class Analyze extends React.Component { this.setState({ snackBarOpen: false }); } - handleSnackbarOpen = (msg: string, variant) => { + handleSnackbarOpen = (msg: string, variant: any) => { this.setState({ snackBarOpen: true, snackBarMsg: msg, snackBarVariant: variant }); } - handleDialogOpen = (title, message) => { + openDialog(title: string, message: string) { let pm = new Promise(resolver => { this.dialogPromiseResolver = resolver }); @@ -181,7 +182,7 @@ class Analyze extends React.Component { return pm; } - handleDialogClose = result => { + handleDialogClose = (result: boolean) => { this.dialogPromiseResolver(result); this.setState({ dialogOpen: false }); } @@ -226,11 +227,12 @@ class Analyze extends React.Component { startDateId="start_date_id" endDate={this.state.endDate} endDateId="end_date_id" - onDatesChange={({ startDate, endDate }) => { + onDatesChange={({ startDate, endDate }: + { startDate: moment.Moment, endDate: moment.Moment }) => { this.setState({ startDate, endDate }); - }} + }} focusedInput={this.state.focusedInput} - onFocusChange={focusedInput => this.setState({ focusedInput })} + onFocusChange={(focusedInput: any) => this.setState({ focusedInput })} isOutsideRange={() => false} /> @@ -267,8 +269,5 @@ class Analyze extends React.Component { } } -Analyze.propTypes = { - classes: PropTypes.object.isRequired, -}; export default withStyles(styles)(Analyze); diff --git a/src/Chart.tsx b/src/Chart.tsx index b1c36ed..3b541fa 100644 --- a/src/Chart.tsx +++ b/src/Chart.tsx @@ -6,79 +6,6 @@ import cyan from '@material-ui/core/colors/cyan'; import { PieChart, Pie, Cell, Tooltip } from 'recharts'; import { defaultChartColor } from './theme'; -export function getChartData(start, end, patterns, calendars, calEventsGetter) { - if (start >= end) return Promise.resolve({ patternGraphData: [], calendarGraphData: [] }); - let event_pms = []; - for (let id in calendars) - { - if (!calendars[id].enabled) continue; - let filtered = patterns.filter(p => p.cal.regex.test(calendars[id].name)); - if (filtered.length > 0) - event_pms.push(calEventsGetter(id, start, end) - .then(r => { return { id, events: r, filtered }; })); - } - return Promise.all(event_pms).then(all_events => { - let events = {}; - let patternsByCal = {}; - let results = {}; // pattern idx => time - let cal_results = {}; // cal id => time - all_events.forEach(e => { - events[e.id] = e.events; - patternsByCal[e.id] = e.filtered; - }); - for (let i = 0; i < patterns.length; i++) - results[i] = 0; - for (let id in calendars) { - if (!events[id]) continue; - events[id].forEach(event => { - patternsByCal[id].forEach(p => { - if (!p.event.regex.test(event.summary)) return; - if (!cal_results.hasOwnProperty(id)) { - cal_results[id] = 0; - } - let duration = (event.end - event.start) / 60000; - results[p.idx] += duration; - cal_results[id] += duration; - }); - }); - } - let patternGraphData = []; - let calendarGraphData = []; - const filterMarginal = data => { - let sum = 0; - let majorParts = []; - let minorSum = 0; - data.forEach(d => sum += d.value); - data.forEach(d => { - let ratio = d.value / sum; - if (ratio < 1e-2) minorSum += d.value; - else majorParts.push(d); - }); - majorParts.push({ - name: 'Other', - value: minorSum, - color: defaultChartColor, - }); - return majorParts; - }; - for (let i = 0; i < patterns.length; i++) { - patternGraphData.push({ - name: patterns[i].name, - value: results[i] / 60.0, - color: patterns[i].color.background}); - } - for (let id in cal_results) { - calendarGraphData.push({ - name: calendars[id].name, - value: (cal_results[id] / 60.0), - color: calendars[id].color.background}); - } - return {start, end, - patternGraphData: filterMarginal(patternGraphData), - calendarGraphData: filterMarginal(calendarGraphData) }; - }); -} - const styles = theme => ({ pieChart: { margin: '0 auto', diff --git a/src/background.ts b/src/background.ts index 2a23b57..fbae2b6 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,22 +1,22 @@ import * as gapi from './gapi'; -import { msgType, Msg } from './msg'; -import { Duration } from './duration'; +import { MsgType, Msg } from './msg'; +import { Duration, TrackPeriod } from './duration'; import moment from 'moment'; -import { getChartData } from './Chart'; +import { GraphData, getGraphData } from './graph'; import { PatternEntry } from './pattern'; -let mainPatterns: number[] = []; -let analyzePatterns = []; -let calendars = {}; -let calData = {}; -let config = { +let mainPatterns: PatternEntry[] = []; +let analyzePatterns: PatternEntry[] = []; +let calendars: {[id: string]: gapi.GCalendarMeta} = {}; +let calData: {[id: string]: gapi.GCalendar} = {}; +let config: TrackPeriod[] = { trackedPeriods: [ {name: 'Today', start: Duration.days(1), end: Duration.days(0)}, {name: 'Yesterday', start: Duration.days(2), end: Duration.days(1)}, {name: 'This Week', start: Duration.weeks(1), end: Duration.weeks(0)}, {name: 'This Month', start: Duration.months(1), end: Duration.months(0)}] }; -let mainGraphData = []; +let mainGraphData: GraphData[] = []; let dirtyMetadata = false; function loadMetadata() { @@ -107,7 +107,9 @@ function updateMainGraphData() { end: e.end.getTime() })))).then(results => { mainGraphData[i] = { - name: p.name, start, end, + name: p.name, + start: start.toDate(), + end: end.toDate(), data: results.patternGraphData }; })); @@ -133,7 +135,7 @@ chrome.runtime.onConnect.addListener(function(port) { let msg = Msg.inflate(_msg); console.log(msg); switch (msg.type) { - case msgType.updatePatterns: { + case MsgType.updatePatterns: { let patterns = msg.data.patterns.map(p => PatternEntry.inflate(p)); if (msg.data.id == 'analyze') analyzePatterns = patterns; @@ -143,7 +145,7 @@ chrome.runtime.onConnect.addListener(function(port) { port.postMessage(msg.genResp(null)); break; } - case msgType.getPatterns: { + case MsgType.getPatterns: { let patterns; if (msg.data.id == 'analyze') patterns = analyzePatterns; @@ -152,13 +154,13 @@ chrome.runtime.onConnect.addListener(function(port) { port.postMessage(msg.genResp(patterns.map(p => p.deflate()))); break; } - case msgType.updateCalendars: { + case MsgType.updateCalendars: { calendars = msg.data; dirtyMetadata = true; port.postMessage(msg.genResp(null)); break; } - case msgType.getCalendars: { + case MsgType.getCalendars: { let cals = calendars; if (msg.data.enabledOnly) { @@ -169,7 +171,7 @@ chrome.runtime.onConnect.addListener(function(port) { port.postMessage(msg.genResp(cals)); break; } - case msgType.getCalEvents: { + case MsgType.getCalEvents: { getCalEvents(msg.data.id, msg.data.start, msg.data.end).then(data => { console.log(data); let resp = msg.genResp(data.map(e => { @@ -184,7 +186,7 @@ chrome.runtime.onConnect.addListener(function(port) { }); break; } - case msgType.updateConfig: { + case MsgType.updateConfig: { config.trackedPeriods = msg.data.trackedPeriods.map(p => ({ name: p.name, start: Duration.inflate(p.start), @@ -194,13 +196,13 @@ chrome.runtime.onConnect.addListener(function(port) { port.postMessage(msg.genResp(null)); break; } - case msgType.getConfig: { + case MsgType.getConfig: { let res = {}; msg.data.forEach(prop => res[prop] = config[prop]); port.postMessage(msg.genResp(res)); break; } - case msgType.getGraphData: { + case MsgType.getGraphData: { (msg.data.sync ? updateMainGraphData() : Promise.resolve()).then(() => ( port.postMessage(msg.genResp(mainGraphData.map(d => ({ name: d.name, diff --git a/src/duration.ts b/src/duration.ts index 18849a0..b42b53b 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -24,3 +24,9 @@ export class Duration { deflate() { return { value: this.value, unit: this.unit }; } static inflate = (obj: { value: number, unit: TimeUnit }) => new Duration(obj.value, obj.unit); } + +export type TrackPeriod = { + name: string, + start: Duration, + end: Duration +}; diff --git a/src/graph.ts b/src/graph.ts new file mode 100644 index 0000000..507b671 --- /dev/null +++ b/src/graph.ts @@ -0,0 +1,85 @@ +export type PatternGraphData = { + name: string, + value: number, + color: string +}; + +export type GraphData = { + name: string, + start: Date, + end: Date, + data: PatternGraphData +}; + +export function getGraphData(start, end, patterns, calendars, calEventsGetter) { + if (start >= end) return Promise.resolve({ patternGraphData: [], calendarGraphData: [] }); + let event_pms = []; + for (let id in calendars) + { + if (!calendars[id].enabled) continue; + let filtered = patterns.filter(p => p.cal.regex.test(calendars[id].name)); + if (filtered.length > 0) + event_pms.push(calEventsGetter(id, start, end) + .then(r => { return { id, events: r, filtered }; })); + } + return Promise.all(event_pms).then(all_events => { + let events = {}; + let patternsByCal = {}; + let results = {}; // pattern idx => time + let cal_results = {}; // cal id => time + all_events.forEach(e => { + events[e.id] = e.events; + patternsByCal[e.id] = e.filtered; + }); + for (let i = 0; i < patterns.length; i++) + results[i] = 0; + for (let id in calendars) { + if (!events[id]) continue; + events[id].forEach(event => { + patternsByCal[id].forEach(p => { + if (!p.event.regex.test(event.summary)) return; + if (!cal_results.hasOwnProperty(id)) { + cal_results[id] = 0; + } + let duration = (event.end - event.start) / 60000; + results[p.idx] += duration; + cal_results[id] += duration; + }); + }); + } + let patternGraphData = []; + let calendarGraphData = []; + const filterMarginal = data => { + let sum = 0; + let majorParts = []; + let minorSum = 0; + data.forEach(d => sum += d.value); + data.forEach(d => { + let ratio = d.value / sum; + if (ratio < 1e-2) minorSum += d.value; + else majorParts.push(d); + }); + majorParts.push({ + name: 'Other', + value: minorSum, + color: defaultChartColor, + }); + return majorParts; + }; + for (let i = 0; i < patterns.length; i++) { + patternGraphData.push({ + name: patterns[i].name, + value: results[i] / 60.0, + color: patterns[i].color.background}); + } + for (let id in cal_results) { + calendarGraphData.push({ + name: calendars[id].name, + value: (cal_results[id] / 60.0), + color: calendars[id].color.background}); + } + return {start, end, + patternGraphData: filterMarginal(patternGraphData), + calendarGraphData: filterMarginal(calendarGraphData) }; + }); +} -- cgit v1.2.3-70-g09d2