Skip to content

Commit

Permalink
Feature: Refresh schedule - code optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
ranbena committed Jan 3, 2019
1 parent 05c4267 commit da6a596
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 146 deletions.
252 changes: 106 additions & 146 deletions client/app/components/queries/ScheduleDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,80 +3,35 @@ import React from 'react';
import PropTypes from 'prop-types';
import Modal from 'antd/lib/modal';
import DatePicker from 'antd/lib/date-picker';
import { map, range, partial } from 'lodash';
import { range, padStart } from 'lodash';
import moment from 'moment';
import { secondsToInterval, IntervalEnum } from '@/filters';
import { secondsToInterval, intervalToSeconds, IntervalEnum } from '@/filters';

import './ScheduleDialog.css';

const WEEKDAYS_SHORT = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const WEEKDAYS_FULL = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
const INTERVAL_OPTIONS_MAP = {};
INTERVAL_OPTIONS_MAP[IntervalEnum.NEVER] = 1;
INTERVAL_OPTIONS_MAP[IntervalEnum.MINUTES] = 60;
INTERVAL_OPTIONS_MAP[IntervalEnum.HOURS] = 24;
INTERVAL_OPTIONS_MAP[IntervalEnum.DAYS] = 7;
INTERVAL_OPTIONS_MAP[IntervalEnum.WEEKS] = 5;

function padWithZeros(size, v) {
let str = String(v);
if (str.length < size) {
str = `0${str}`;
}
return str;
}

const hourOptions = map(range(0, 24), partial(padWithZeros, 2));
const minuteOptions = map(range(0, 60, 5), partial(padWithZeros, 2));

function scheduleInLocalTime(schedule) {
const parts = schedule.split(':');
const WEEKDAYS_SHORT = moment.weekdaysShort();
const WEEKDAYS_FULL = moment.weekdays();
const INTERVAL_OPTIONS_MAP = {
[IntervalEnum.NEVER]: 1,
[IntervalEnum.MINUTES]: 60,
[IntervalEnum.HOURS]: 24,
[IntervalEnum.DAYS]: 7,
[IntervalEnum.WEEKS]: 5,
};

const HOUR_OPTIONS = range(0, 24).map(x => padStart(x, 2, '0')); // [00, 01, 02, ..]
const MINUTE_OPTIONS = range(0, 60, 5).map(x => padStart(x, 2, '0')); // [00, 05, 10, ..]
const DATE_FORMAT = 'YYYY-MM-DD';
const HOUR_FORMAT = 'HH:mm';

function localizeTime(time) {
const [hrs, mins] = time.split(':');
return moment
.utc()
.hour(parts[0])
.minute(parts[1])
.hour(hrs)
.minute(mins)
.local()
.format('HH:mm');
}

function getAcceptableIntervals(refreshOptions) {
const acceptableIntervals = [
{
name: IntervalEnum.NEVER,
time: null,
},
];
refreshOptions.forEach((seconds) => {
const { count, interval } = secondsToInterval(seconds);
if (count === 1) {
acceptableIntervals.push({
name: interval,
time: seconds,
});
}
});
return acceptableIntervals;
}

function intervalToSeconds(count, interval) {
let intervalInSeconds = 0;
switch (interval) {
case IntervalEnum.MINUTES:
intervalInSeconds = 60;
break;
case IntervalEnum.HOURS:
intervalInSeconds = 3600;
break;
case IntervalEnum.DAYS:
intervalInSeconds = 86400;
break;
case IntervalEnum.WEEKS:
intervalInSeconds = 604800;
break;
default:
return null;
}
return intervalInSeconds * count;
.format(HOUR_FORMAT);
}

class ScheduleDialog extends React.Component {
Expand All @@ -92,55 +47,69 @@ class ScheduleDialog extends React.Component {
constructor(props) {
super(props);

let interval = {};
let parts = null;
const time = this.props.query.schedule.time;
if (time) {
parts = scheduleInLocalTime(this.props.query.schedule.time).split(':');
}
const secondsDelay = this.props.query.schedule.interval;
const dayOfWeek = this.props.query.schedule.day_of_week;
if (secondsDelay) {
interval = secondsToInterval(secondsDelay);
}
const { time, interval: secs, day_of_week: day } = props.query.schedule;
const interval = secs ? secondsToInterval(secs) : {};
const [hour, minute] = time ? localizeTime(time).split(':') : [null, null];

this.state = {
hour: parts ? parts[0] : null,
minute: parts ? parts[1] : null,
hour,
minute,
count: interval.count ? String(interval.count) : '1',
interval: interval.interval || IntervalEnum.NEVER,
dayOfWeek: dayOfWeek ? WEEKDAYS_SHORT[WEEKDAYS_FULL.indexOf(dayOfWeek)] : null,
dayOfWeek: day ? WEEKDAYS_SHORT[WEEKDAYS_FULL.indexOf(day)] : null,
};
}

getAcceptableCounts() {
get counts() {
return range(1, INTERVAL_OPTIONS_MAP[this.state.interval]);
}

setKeep = e => this.props.updateQuery({ schedule_resultset_size: parseInt(e.target.value, 10) });
get intervals() {
const ret = this.props.refreshOptions
.map((seconds) => {
const { count, interval } = secondsToInterval(seconds);
return count === 1 ? {
name: interval,
time: seconds,
} : null;
})
.filter(x => x !== null);
ret.unshift({ name: IntervalEnum.NEVER });

Object.defineProperty(this, 'intervals', { value: ret }); // memoize

return ret;
}

setTime = (h, m) => {
set newSchedule(newProps) {
this.props.updateQuery({
schedule: Object.assign({}, this.props.query.schedule, {
time:
h && m
? moment()
.hour(h)
.minute(m)
.utc()
.format('HH:mm')
: null,
}),
schedule: Object.assign({}, this.props.query.schedule, newProps),
});
}

setTime = (h, m) => {
this.newSchedule = {
time:
h && m
? moment()
.hour(h)
.minute(m)
.utc()
.format(HOUR_FORMAT)
: null,
};

this.setState({
hour: h,
minute: m,
});
};

setInterval = (e) => {
const newInterval = e.target.value;
const newSchedule = Object.assign({}, this.props.query.schedule);

// resets to defaults
if (newInterval === IntervalEnum.NEVER) {
newSchedule.until = null;
}
Expand All @@ -158,104 +127,96 @@ class ScheduleDialog extends React.Component {
.hour('00')
.minute('15')
.utc()
.format('HH:mm');
.format(HOUR_FORMAT);
}
if (newInterval === IntervalEnum.WEEKS && !this.state.dayOfWeek) {
newSchedule.day_of_week = WEEKDAYS_FULL[0];
}

const totalSeconds = newInterval ? intervalToSeconds(parseInt(this.state.count, 10), newInterval) : null;
const timeParts = newSchedule.time ? scheduleInLocalTime(newSchedule.time).split(':') : null;
newSchedule.interval = newInterval
? intervalToSeconds(Number(this.state.count), newInterval)
: null;

const [hour, minute] = newSchedule.time ?
localizeTime(newSchedule.time).split(':')
: [null, null];

this.setState({
interval: newInterval,
count: newInterval !== IntervalEnum.NEVER ? this.state.count : '1',
hour: timeParts ? timeParts[0] : null,
minute: timeParts ? timeParts[1] : null,
dayOfWeek: newSchedule.day_of_week ? WEEKDAYS_SHORT[WEEKDAYS_FULL.indexOf(newSchedule.day_of_week)] : null,
hour,
minute,
dayOfWeek: newSchedule.day_of_week
? WEEKDAYS_SHORT[WEEKDAYS_FULL.indexOf(newSchedule.day_of_week)]
: null,
});

this.props.updateQuery({
schedule: Object.assign(newSchedule, { interval: totalSeconds }),
});
this.newSchedule = newSchedule;
};

setCount = (e) => {
const newCount = e.target.value;
const totalSeconds = intervalToSeconds(parseInt(newCount, 10), this.state.interval);
this.setState({ count: newCount });

this.props.updateQuery({
schedule: Object.assign({}, this.props.query.schedule, { interval: totalSeconds }),
});
this.newSchedule = { interval: totalSeconds };
};

setScheduleUntil = (momentDate, date) => {
this.props.updateQuery({
schedule: Object.assign({}, this.props.query.schedule, { until: date }),
});
this.newSchedule = { until: date };
};

setWeekday = (e) => {
const dayOfWeek = e.target.value;
this.setState({ dayOfWeek });
this.props.updateQuery({
schedule: Object.assign({}, this.props.query.schedule, {
day_of_week: dayOfWeek ? WEEKDAYS_FULL[WEEKDAYS_SHORT.indexOf(dayOfWeek)] : null,
}),
});
this.newSchedule = {
day_of_week: dayOfWeek ? WEEKDAYS_FULL[WEEKDAYS_SHORT.indexOf(dayOfWeek)] : null,
};
};

render() {
const schedule = this.props.query.schedule;
const format = 'YYYY-MM-DD';
const additionalAttributes = {};
const {
interval, minute, hour, until, count,
} = this.state;

return (
<Modal
title="Refresh Schedule"
className="schedule"
visible={this.props.show}
onCancel={this.props.onClose}
footer={[null, null]}
footer={null}
>
<div className="schedule-component">
<div>Refresh every</div>
{schedule.interval ? (
<select value={this.state.count} onChange={this.setCount}>
{this.getAcceptableCounts().map(count => (
<option value={String(count)} key={count}>
{String(count)}
</option>
{interval !== IntervalEnum.NEVER ? (
<select value={count} onChange={this.setCount}>
{this.counts.map(cnt => (
<option value={String(cnt)} key={cnt}>{cnt}</option>
))}
</select>
) : null}
<select value={this.state.interval} onChange={this.setInterval}>
{getAcceptableIntervals(this.props.refreshOptions).map(iv => (
<option value={iv.name || ''} key={iv.name}>
{String(iv.name)}
</option>
<select value={interval} onChange={this.setInterval}>
{this.intervals.map(iv => (
<option value={iv.name} key={iv.name}>{iv.name}</option>
))}
</select>
</div>
{[IntervalEnum.DAYS, IntervalEnum.WEEKS].indexOf(this.state.interval) !== -1 ? (
{[IntervalEnum.DAYS, IntervalEnum.WEEKS].indexOf(interval) !== -1 ? (
<div className="schedule-component">
<div>At the following time</div>
<select value={this.state.hour} onChange={e => this.setTime(e.target.value, this.state.minute)}>
{hourOptions.map(h => (
<option key={h} value={h}>
{h}
</option>
<select value={hour} onChange={e => this.setTime(e.target.value, minute)}>
{HOUR_OPTIONS.map(h => (
<option key={h} value={h}>{h}</option>
))}
</select>
<select value={this.state.minute} onChange={e => this.setTime(this.state.hour, e.target.value)}>
{minuteOptions.map(m => (
<option key={m} value={m}>
{m}
</option>
<select value={minute} onChange={e => this.setTime(hour, e.target.value)}>
{MINUTE_OPTIONS.map(m => (
<option key={m} value={m}>{m}</option>
))}
</select>
</div>
) : null}
{IntervalEnum.WEEKS === this.state.interval ? (
{IntervalEnum.WEEKS === interval ? (
<div className="btn-toolbar schedule-component">
<div className="btn-group" data-toggle="buttons">
{WEEKDAYS_SHORT.map(day => (
Expand All @@ -271,13 +232,12 @@ class ScheduleDialog extends React.Component {
</div>
</div>
) : null}
{schedule.interval ? (
{interval ? (
<div className="schedule-component">
<div>Stop refresh on:</div>
<DatePicker
{...additionalAttributes}
format={format}
placeholder={schedule.until || 'Select Date'}
format={DATE_FORMAT}
placeholder={until || 'Select Date'}
onChange={this.setScheduleUntil}
/>
</div>
Expand Down
21 changes: 21 additions & 0 deletions client/app/filters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ export function secondsToInterval(seconds) {
return { count, interval };
}

export function intervalToSeconds(count, interval) {
let intervalInSeconds = 0;
switch (interval) {
case IntervalEnum.MINUTES:
intervalInSeconds = 60;
break;
case IntervalEnum.HOURS:
intervalInSeconds = 3600;
break;
case IntervalEnum.DAYS:
intervalInSeconds = 86400;
break;
case IntervalEnum.WEEKS:
intervalInSeconds = 604800;
break;
default:
return null;
}
return intervalInSeconds * count;
}

export function durationHumanize(duration) {
let humanized = '';

Expand Down

0 comments on commit da6a596

Please sign in to comment.