onejgordon/flow-dashboard

View on GitHub
src/js/components/analysis/AnalysisHabits.js

Summary

Maintainability
B
4 hrs
Test Coverage
var React = require('react');
var AppConstants = require('constants/AppConstants');
import PropTypes from 'prop-types';
import {Bar, Line} from "react-chartjs-2";
import {Toggle} from 'material-ui'
import connectToStores from 'alt-utils/lib/connectToStores';
var util = require('utils/util');
var api = require('utils/api');
import {changeHandler} from 'utils/component-utils';

@connectToStores
@changeHandler
export default class AnalysisHabits extends React.Component {
    static propTypes = {
        end_date: PropTypes.object,
        loaded: PropTypes.bool,
        days: PropTypes.number
    }

    static defaultProps = {
        days: 21
    };
    constructor(props) {
        super(props);
        this.state = {
            habitdays: {},
            habits: [],
            form: {
                count_scaled: false
            }
        };
        this.ROLLING_WINDOW = 7;
    }

    static getStores() {
        return [];
    }

    static getPropsFromStores() {
        return {};
    }

    have_data() {
        return this.state.habits.length > 0;
    }

    componentDidMount() {
        if (!this.have_data()) this.fetch_data();
    }

    fetch_data() {
        let {end_date} = this.props;
        let start = new Date();
        start.setDate(start.getDate() - this.props.days);
        let params = {
            date_start: util.printDateObj(start, 'UTC'),
            date_end: util.printDateObj(end_date, 'UTC'),
            with_habits: 1
        }
        api.get("/api/analysis", params, (res) => {
            console.log(res)
            this.setState({
                iso_dates: res.dates,
                habits: res.habits,
                habitdays: res.habitdays,
                loaded: true
            });
        });
    }

    habit_day_count_value(iso_date, habit) {
        let {habitdays, form} = this.state;
        let id = `habit:${habit.id}_day:${iso_date}`;
        if (habitdays[id]) return form.count_scaled ? habitdays[id].count : (habitdays[id].done ? 1 : 0);
        return 0;
    }

    habit_data() {
        let {iso_dates, habits} = this.state;
        let datasets = [];
        let day_counts = {}; // iso_date -> # of habits done
        habits.forEach((h) => {
            let data = iso_dates.map((iso_date) => {
                let count_value = this.habit_day_count_value(iso_date, h)
                if (count_value) {
                    if (!day_counts[iso_date]) day_counts[iso_date] = 0;
                    day_counts[iso_date] += 1;
                }
                return count_value;
            });
            let dataset = {
                label: h.name,
                data: data,
                backgroundColor: h.color || "#FFFFFF"
            };
            datasets.push(dataset);
        })
        let habit_data = {
            labels: iso_dates,
            datasets: datasets
        };
        return {habit_data, day_counts};
    }

    trend_data(day_counts) {
        let {iso_dates, habits} = this.state;
        let rwindow = [];
        let series_data = [];
        let labels = [];
        iso_dates.forEach((iso_date) => {
            let n_done = day_counts[iso_date] || 0;
            rwindow.push(n_done);
            if (rwindow.length > this.ROLLING_WINDOW) {
                rwindow.splice(0, 1); // Remove first element
            }
            let full_window = rwindow.length == this.ROLLING_WINDOW;
            if (full_window) {
                let {sum} = util.sum(rwindow);
                series_data.push(sum);
                labels.push(iso_date);
            }
        });
        let dataset = {
            label: "Rolling Average",
            data: series_data,
            pointBorderColor: '#444444',
            pointBackgroundColor: `rgba(255, 255, 255, 0.8)`,
            backgroundColor: `rgba(255, 255, 255, 0.6)`
        };
        let total_weekly_target = 0;
        habits.forEach((h) => {
            if (h.tgt_weekly) total_weekly_target += h.tgt_weekly;
        });
        let target_dataset = {
            label: `Total Weekly Target (${total_weekly_target})`,
            data: new Array(series_data.length).fill(total_weekly_target),
            borderColor: `rgba(244, 223, 66, 0.8)`,
            backgroundColor: `rgba(244, 223, 66, 0.1)`
        };
        let trend_data = {
            labels: labels,
            datasets: [dataset, target_dataset]
        };
        return {trend_data, total_weekly_target};
    }

    render() {
        let {loaded} = this.props;
        let {form} = this.state
        if (!this.have_data()) return <div className="empty">Loading</div>
        let {habit_data, day_counts} = this.habit_data();
        let {trend_data, total_weekly_target} = this.trend_data(day_counts);
        let habitOptions = {
            scales: {
                yAxes: [{
                    stacked: true
                }],
                xAxes: [{
                    type: 'time',
                    time: {
                        unit: 'day'
                    },
                    stacked: true
                }]
            }
        }
        let trendOpts = {
            scales: {
                xAxes: [{
                    type: 'time',
                    time: {
                        unit: 'day'
                    }
                }],
                yAxes: [{
                    ticks: {
                        suggestedMax: total_weekly_target + 3,
                        min: 0
                    }
                }],
            }
        }
        if (!loaded) return null;
        return (
            <div>

                <h4>Habits</h4>

                <div className="row">
                    <div className="col-sm-4 col-sm-offset-8">
                        <Toggle label="Scale by habit count" labelPosition="right" toggled={form.count_scaled} onToggle={this.changeHandlerToggle.bind(this, 'form', 'count_scaled')} />
                    </div>
                </div>

                <Bar data={habit_data} options={habitOptions} width={1000} height={450}/>

                <h5>Overall Trend</h5>
                <p className="lead">Completions per week (rolling 7 day average)</p>

                <Line data={trend_data} options={trendOpts} width={1000} height={450}/>

            </div>
        );
    }
}

module.exports = AnalysisHabits;