aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2019-02-14 00:00:35 -0500
committerDeterminant <ted.sybil@gmail.com>2019-02-14 00:00:35 -0500
commitb88894cd027d27b8fb7ab358128f52c6f24fbe0d (patch)
tree62c47a4685484d6165c039aba3631f1ecc12ec76
parent6ebb7f7ed0b603425c618bde9129ad09b1308c4e (diff)
...
-rw-r--r--package-lock.json9
-rw-r--r--package.json1
-rw-r--r--src/Analyze.tsx25
-rw-r--r--src/Chart.tsx73
-rw-r--r--src/background.ts38
-rw-r--r--src/duration.ts6
-rw-r--r--src/graph.ts85
7 files changed, 133 insertions, 104 deletions
diff --git a/package-lock.json b/package-lock.json
index 1b4fef3..07cd034 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1054,6 +1054,15 @@
"@types/react": "*"
}
},
+ "@types/react-dates": {
+ "version": "17.1.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dates/-/react-dates-17.1.5.tgz",
+ "integrity": "sha512-aLYYiAfZfsQR6IqBfegrQrTeNk0bLql5fwNjMwst9oRSeB7LIauf1QStfNHifa6fQA5fr4Ivr/YniGdedfaCxg==",
+ "requires": {
+ "@types/react": "*",
+ "moment": ">=2.17.1"
+ }
+ },
"@types/react-transition-group": {
"version": "2.0.15",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.0.15.tgz",
diff --git a/package.json b/package.json
index 2a7d1e3..48bbd7a 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
"@types/chrome": "0.0.79",
"@types/material-ui": "^0.21.6",
"@types/react": "^16.8.2",
+ "@types/react-dates": "^17.1.5",
"lru-cache": "^5.1.1",
"moment": "^2.24.0",
"react": "^16.8.1",
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} />
</div>
</FormGroup>
@@ -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) };
+ });
+}