<commit_before>import fs from 'fs'
import parse from 'csv-parse'
import * as calculate from '../utils/calculate'
import * as getFromCSV from './scriptUtils/getFromCSV'
import R from 'ramda'

const readCSV = (filename, callback) => {
    const parser = parse({delimiter: ',', columns: true, relax: true, auto_parse: true}, (
        err, data) => {
            if (err) throw err
            callback(data)
    })
    fs.createReadStream(__dirname + '/source/' + filename).pipe(parser)
}

const createCourseObj = (csv) => {
    return csv.reduce((acc, ev) => {
        const year = getFromCSV.getYear(ev)
        const term = getFromCSV.getTerm(ev)
        const course = getFromCSV.getCourse(ev)
        const section = getFromCSV.getSection(ev)
        const courseName = getFromCSV.getCourseName(ev)
        const coureseLevel = getFromCSV.getCourseLevel(ev)
        const dept = getFromCSV.getDept(ev)
        const instructorName = getFromCSV.getInstructorName(ev)
        const PUID = getFromCSV.getPUID(ev)

        const uniqSectionInTerm = (x) => (x.year === year
            && x.course === course
            && x.term === term
            && x.section === section
            && x.instructorName === instructorName)

        if (acc.some(x => uniqSectionInTerm(x))) {
            const index = acc.findIndex(x => uniqSectionInTerm(x))
            // umi count
            if (typeof (acc[index].UMI1.count[getFromCSV.getUMI1(ev)]) == 'undefined') {
                acc[index].UMI1.count = { ...acc[index].UMI1.count, [getFromCSV.getUMI1(ev)]: 1 }
            } else acc[index].UMI1.count[getFromCSV.getUMI1(ev)] = acc[index].UMI1.count[getFromCSV.getUMI1(ev)] + 1

            if (typeof (acc[index].UMI2.count[getFromCSV.getUMI2(ev)]) == 'undefined') {
                acc[index].UMI2.count = { ...acc[index].UMI2.count, [getFromCSV.getUMI2(ev)]: 1 }
            } else acc[index].UMI2.count[getFromCSV.getUMI2(ev)] = acc[index].UMI2.count[getFromCSV.getUMI2(ev)] + 1

            if (typeof (acc[index].UMI3.count[getFromCSV.getUMI3(ev)]) == 'undefined') {
                acc[index].UMI3.count = { ...acc[index].UMI3.count, [getFromCSV.getUMI3(ev)]: 1 }
            } else acc[index].UMI3.count[getFromCSV.getUMI3(ev)] = acc[index].UMI3.count[getFromCSV.getUMI3(ev)] + 1

            if (typeof (acc[index].UMI4.count[getFromCSV.getUMI4(ev)]) == 'undefined') {
                acc[index].UMI4.count = { ...acc[index].UMI4.count, [getFromCSV.getUMI4(ev)]: 1 }
            } else acc[index].UMI4.count[getFromCSV.getUMI4(ev)] = acc[index].UMI4.count[getFromCSV.getUMI4(ev)] + 1

            if (typeof (acc[index].UMI5.count[getFromCSV.getUMI5(ev)]) == 'undefined') {
                acc[index].UMI5.count = { ...acc[index].UMI5.count, [getFromCSV.getUMI5(ev)]: 1 }
            } else acc[index].UMI5.count[getFromCSV.getUMI5(ev)] = acc[index].UMI5.count[getFromCSV.getUMI5(ev)] + 1

            if (typeof (acc[index].UMI6.count[getFromCSV.getUMI6(ev)]) == 'undefined') {
                acc[index].UMI6.count = { ...acc[index].UMI6.count, [getFromCSV.getUMI6(ev)]: 1}
            } else acc[index].UMI6.count[getFromCSV.getUMI6(ev)] = acc[index].UMI6.count[getFromCSV.getUMI6(ev)] + 1

            acc[index].gender[getFromCSV.getGender(ev)] = acc[index].gender[getFromCSV.getGender(ev)] + 1

            return acc

        } else {
            acc.push({
                year,
                term,
                course,
                section,
                courseName,
                coureseLevel,
                dept,
                instructorName,
                PUID,
                gender: {
                    Female: 0,
                    Male: 0
                },
                UMI1: {
                    count: {
                        [String(getFromCSV.getUMI1(ev))]: 1
                    }
                },
                UMI2: {
                    count: {
                        [String(getFromCSV.getUMI2(ev))]: 1
                    }
                },
                UMI3: {
                    count: {
                        [String(getFromCSV.getUMI3(ev))]: 1
                    }
                },
                UMI4: {
                    count: {
                        [String(getFromCSV.getUMI4(ev))]: 1
                    }
                },
                UMI5: {
                    count: {
                        [String(getFromCSV.getUMI5(ev))]: 1
                    }
                },
                UMI6: {
                    count: {
                        [String(getFromCSV.getUMI6(ev))]: 1
                    }
                }
            })
            return acc
        }
    }, [])
}

const insertDispersionIndex = (courseObj) => {
    courseObj.UMI1.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI1.count)
    courseObj.UMI2.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI2.count)
    courseObj.UMI3.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI3.count)
    courseObj.UMI4.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI4.count)
    courseObj.UMI5.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI5.count)
    courseObj.UMI6.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI6.count)
    return courseObj
}

const insertAvg = (courseObj) => {
    courseObj.UMI1.average = calculate.umiAvgV2(courseObj.UMI1.count)
    courseObj.UMI2.average = calculate.umiAvgV2(courseObj.UMI2.count)
    courseObj.UMI3.average = calculate.umiAvgV2(courseObj.UMI3.count)
    courseObj.UMI4.average = calculate.umiAvgV2(courseObj.UMI4.count)
    courseObj.UMI5.average = calculate.umiAvgV2(courseObj.UMI5.count)
    courseObj.UMI6.average = calculate.umiAvgV2(courseObj.UMI6.count)
    return courseObj
}

const insertPercentFav = (courseObj) => {
    courseObj.UMI1.percentFavourable = calculate.percentFavourableV2(courseObj.UMI1.count)
    courseObj.UMI2.percentFavourable = calculate.percentFavourableV2(courseObj.UMI2.count)
    courseObj.UMI3.percentFavourable = calculate.percentFavourableV2(courseObj.UMI3.count)
    courseObj.UMI4.percentFavourable = calculate.percentFavourableV2(courseObj.UMI4.count)
    courseObj.UMI5.percentFavourable = calculate.percentFavourableV2(courseObj.UMI5.count)
    courseObj.UMI6.percentFavourable = calculate.percentFavourableV2(courseObj.UMI6.count)
    return courseObj
}

// sorts all, doesn't sort just by term
const insertPercentileRanking = (courseObj) => {
    const sortedByUMI1Avg = R.compose(
        R.sort((a, b) => a.UMI1.average - b.UMI1.average)
    )(courseObj)
    sortedByUMI1Avg.map((course) => {
        course.UMI1.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI1', sortedByUMI1Avg)
    })

    const sortedByUMI2Avg = R.compose(
        R.sort((a, b) => a.UMI2.average - b.UMI2.average)
    )(sortedByUMI1Avg)
    sortedByUMI2Avg.map((course) => {
        course.UMI2.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI2', sortedByUMI2Avg)
    })

    const sortedByUMI3Avg = R.compose(
        R.sort((a, b) => a.UMI3.average - b.UMI3.average)
    )(sortedByUMI2Avg)
    sortedByUMI3Avg.map((course) => {
        course.UMI3.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI3', sortedByUMI3Avg)
    })

    const sortedByUMI4Avg = R.compose(
        R.sort((a, b) => a.UMI4.average - b.UMI4.average)
    )(sortedByUMI3Avg)
    sortedByUMI4Avg.map((course) => {
        course.UMI4.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI4', sortedByUMI4Avg)
    })

    const sortedByUMI5Avg = R.compose(
        R.sort((a, b) => a.UMI5.average - b.UMI5.average)
    )(sortedByUMI4Avg)
    sortedByUMI5Avg.map((course) => {
        course.UMI5.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI5', sortedByUMI5Avg)
    })

    const sortedByUMI6Avg = R.compose(
        R.sort((a, b) => a.UMI6.average - b.UMI6.average)
    )(sortedByUMI5Avg)
    sortedByUMI6Avg.map((course) => {
        course.UMI6.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI6', sortedByUMI6Avg)
    })

    return sortedByUMI6Avg
}
// crsnum is the unique identifier for a given year.
readCSV('EXP_0D0O3T6C.csv', (csv) => {
    const courseObjs = createCourseObj(csv)

    courseObjs.map(courseObj => {
        return R.pipe(
            x => insertDispersionIndex(x),
            x => insertAvg(x),
            x => insertPercentFav(x)
        )(courseObj)
    })

    const courseObjWithPercentileRanking = insertPercentileRanking(courseObjs)

    console.log(JSON.stringify(courseObjWithPercentileRanking, null, 2))

    //console.log(calculate.percentileRankingOfCourseV2(sortedByUMI1Avg[sortedByUMI1Avg.length-1], 'UMI1', sortedByUMI1Avg))

})

<commit_msg>Added filtering for term and year in insertPercentileRanking<commit_after>import fs from 'fs'
import parse from 'csv-parse'
import * as calculate from '../utils/calculate'
import * as getFromCSV from './scriptUtils/getFromCSV'
import R from 'ramda'

const readCSV = (filename, callback) => {
    const parser = parse({delimiter: ',', columns: true, relax: true, auto_parse: true}, (
        err, data) => {
            if (err) throw err
            callback(data)
    })
    fs.createReadStream(__dirname + '/source/' + filename).pipe(parser)
}

const createCourseObj = (csv) => {
    return csv.reduce((acc, ev) => {
        const year = getFromCSV.getYear(ev)
        const term = getFromCSV.getTerm(ev)
        const course = getFromCSV.getCourse(ev)
        const section = getFromCSV.getSection(ev)
        const courseName = getFromCSV.getCourseName(ev)
        const coureseLevel = getFromCSV.getCourseLevel(ev)
        const dept = getFromCSV.getDept(ev)
        const instructorName = getFromCSV.getInstructorName(ev)
        const PUID = getFromCSV.getPUID(ev)

        const uniqSectionInTerm = (x) => (x.year === year
            && x.course === course
            && x.term === term
            && x.section === section
            && x.instructorName === instructorName)

        if (acc.some(x => uniqSectionInTerm(x))) {
            const index = acc.findIndex(x => uniqSectionInTerm(x))
            // umi count
            if (typeof (acc[index].UMI1.count[getFromCSV.getUMI1(ev)]) == 'undefined') {
                acc[index].UMI1.count = { ...acc[index].UMI1.count, [getFromCSV.getUMI1(ev)]: 1 }
            } else acc[index].UMI1.count[getFromCSV.getUMI1(ev)] = acc[index].UMI1.count[getFromCSV.getUMI1(ev)] + 1

            if (typeof (acc[index].UMI2.count[getFromCSV.getUMI2(ev)]) == 'undefined') {
                acc[index].UMI2.count = { ...acc[index].UMI2.count, [getFromCSV.getUMI2(ev)]: 1 }
            } else acc[index].UMI2.count[getFromCSV.getUMI2(ev)] = acc[index].UMI2.count[getFromCSV.getUMI2(ev)] + 1

            if (typeof (acc[index].UMI3.count[getFromCSV.getUMI3(ev)]) == 'undefined') {
                acc[index].UMI3.count = { ...acc[index].UMI3.count, [getFromCSV.getUMI3(ev)]: 1 }
            } else acc[index].UMI3.count[getFromCSV.getUMI3(ev)] = acc[index].UMI3.count[getFromCSV.getUMI3(ev)] + 1

            if (typeof (acc[index].UMI4.count[getFromCSV.getUMI4(ev)]) == 'undefined') {
                acc[index].UMI4.count = { ...acc[index].UMI4.count, [getFromCSV.getUMI4(ev)]: 1 }
            } else acc[index].UMI4.count[getFromCSV.getUMI4(ev)] = acc[index].UMI4.count[getFromCSV.getUMI4(ev)] + 1

            if (typeof (acc[index].UMI5.count[getFromCSV.getUMI5(ev)]) == 'undefined') {
                acc[index].UMI5.count = { ...acc[index].UMI5.count, [getFromCSV.getUMI5(ev)]: 1 }
            } else acc[index].UMI5.count[getFromCSV.getUMI5(ev)] = acc[index].UMI5.count[getFromCSV.getUMI5(ev)] + 1

            if (typeof (acc[index].UMI6.count[getFromCSV.getUMI6(ev)]) == 'undefined') {
                acc[index].UMI6.count = { ...acc[index].UMI6.count, [getFromCSV.getUMI6(ev)]: 1}
            } else acc[index].UMI6.count[getFromCSV.getUMI6(ev)] = acc[index].UMI6.count[getFromCSV.getUMI6(ev)] + 1

            acc[index].gender[getFromCSV.getGender(ev)] = acc[index].gender[getFromCSV.getGender(ev)] + 1

            return acc

        } else {
            acc.push({
                year,
                term,
                course,
                section,
                courseName,
                coureseLevel,
                dept,
                instructorName,
                PUID,
                gender: {
                    Female: 0,
                    Male: 0
                },
                UMI1: {
                    count: {
                        [String(getFromCSV.getUMI1(ev))]: 1
                    }
                },
                UMI2: {
                    count: {
                        [String(getFromCSV.getUMI2(ev))]: 1
                    }
                },
                UMI3: {
                    count: {
                        [String(getFromCSV.getUMI3(ev))]: 1
                    }
                },
                UMI4: {
                    count: {
                        [String(getFromCSV.getUMI4(ev))]: 1
                    }
                },
                UMI5: {
                    count: {
                        [String(getFromCSV.getUMI5(ev))]: 1
                    }
                },
                UMI6: {
                    count: {
                        [String(getFromCSV.getUMI6(ev))]: 1
                    }
                }
            })
            return acc
        }
    }, [])
}

const insertDispersionIndex = (courseObj) => {
    courseObj.UMI1.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI1.count)
    courseObj.UMI2.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI2.count)
    courseObj.UMI3.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI3.count)
    courseObj.UMI4.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI4.count)
    courseObj.UMI5.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI5.count)
    courseObj.UMI6.dispersionIndex = calculate.dispersionIndexV2(courseObj.UMI6.count)
    return courseObj
}

const insertAvg = (courseObj) => {
    courseObj.UMI1.average = calculate.umiAvgV2(courseObj.UMI1.count)
    courseObj.UMI2.average = calculate.umiAvgV2(courseObj.UMI2.count)
    courseObj.UMI3.average = calculate.umiAvgV2(courseObj.UMI3.count)
    courseObj.UMI4.average = calculate.umiAvgV2(courseObj.UMI4.count)
    courseObj.UMI5.average = calculate.umiAvgV2(courseObj.UMI5.count)
    courseObj.UMI6.average = calculate.umiAvgV2(courseObj.UMI6.count)
    return courseObj
}

const insertPercentFav = (courseObj) => {
    courseObj.UMI1.percentFavourable = calculate.percentFavourableV2(courseObj.UMI1.count)
    courseObj.UMI2.percentFavourable = calculate.percentFavourableV2(courseObj.UMI2.count)
    courseObj.UMI3.percentFavourable = calculate.percentFavourableV2(courseObj.UMI3.count)
    courseObj.UMI4.percentFavourable = calculate.percentFavourableV2(courseObj.UMI4.count)
    courseObj.UMI5.percentFavourable = calculate.percentFavourableV2(courseObj.UMI5.count)
    courseObj.UMI6.percentFavourable = calculate.percentFavourableV2(courseObj.UMI6.count)
    return courseObj
}

const insertPercentileRanking = (courseObj) => {
    const sortedByUMI1Avg = R.compose(
        R.sort((a, b) => a.UMI1.average - b.UMI1.average)
    )(courseObj)
    sortedByUMI1Avg.map((course) => {
        const filteredByTerm = sortedByUMI1Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI1.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI1', filteredByTerm)
    })

    const sortedByUMI2Avg = R.compose(
        R.sort((a, b) => a.UMI2.average - b.UMI2.average)
    )(sortedByUMI1Avg)
    sortedByUMI2Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI2.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI2', filteredByTerm)
    })

    const sortedByUMI3Avg = R.compose(
        R.sort((a, b) => a.UMI3.average - b.UMI3.average)
    )(sortedByUMI2Avg)
    sortedByUMI3Avg.map((course) => {
        const filteredByTerm = sortedByUMI2Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI3.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI3', filteredByTerm)
    })

    const sortedByUMI4Avg = R.compose(
        R.sort((a, b) => a.UMI4.average - b.UMI4.average)
    )(sortedByUMI3Avg)
    sortedByUMI4Avg.map((course) => {
        const filteredByTerm = sortedByUMI4Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI4.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI4', filteredByTerm)
    })

    const sortedByUMI5Avg = R.compose(
        R.sort((a, b) => a.UMI5.average - b.UMI5.average)
    )(sortedByUMI4Avg)
    sortedByUMI5Avg.map((course) => {
        const filteredByTerm = sortedByUMI5Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI5.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI5', filteredByTerm)
    })

    const sortedByUMI6Avg = R.compose(
        R.sort((a, b) => a.UMI6.average - b.UMI6.average)
    )(sortedByUMI5Avg)
    sortedByUMI6Avg.map((course) => {
        const filteredByTerm = sortedByUMI6Avg.filter(x => x.year === course.year && x.term === course.term)
        course.UMI6.percentileRankingByFaculty = calculate.percentileRankingOfCourseV2(course, 'UMI6', filteredByTerm)
    })

    return sortedByUMI6Avg
}
// crsnum is the unique identifier for a given year.
readCSV('EXP_0D0O3T6C.csv', (csv) => {
    const courseObjs = createCourseObj(csv)

    courseObjs.map(courseObj => {
        return R.pipe(
            x => insertDispersionIndex(x),
            x => insertAvg(x),
            x => insertPercentFav(x)
        )(courseObj)
    })

    const courseObjWithPercentileRanking = insertPercentileRanking(courseObjs)

    console.log(JSON.stringify(courseObjWithPercentileRanking, null, 2))

    //console.log(calculate.percentileRankingOfCourseV2(sortedByUMI1Avg[sortedByUMI1Avg.length-1], 'UMI1', sortedByUMI1Avg))

})

<|endoftext|>


<commit_before>var querystring = require('querystring'),
    http = require('http');
var crypto = require('crypto');

var googleAnalytics = {
    tid: 'UA-967247-4',

    an: 'Hebcal',

    extend: function(destination, source) {
        if (typeof source === 'object') {
            for (var attr in source) {
                if (source.hasOwnProperty(attr)) {
                    destination[attr] = source[attr];
                }
            }
        }
        return destination;
    },

    hashUuid4: function(str) {
        var hash = crypto.createHash('md5'),
            digest;
        hash.update(str);
        digest = hash.digest('hex');
        return digest.substr(0, 8) +
            '-' + digest.substr(8, 4) +
            '-' + digest.substr(12, 4) +
            '-' + digest.substr(16, 4) +
            '-' + digest.substr(20, 12);
    },

    send: function(hitType, userId, params) {
        var postParams = {
            v: '1',
            tid: this.tid,
            t: hitType,
            cid: this.hashUuid4(userId),
            an: this.an
        };

        postParams = this.extend(postParams, params);

        var postData = querystring.stringify(postParams);
        console.log("TRACKING: " + postData);

        var options = {
            hostname: 'www.google-analytics.com',
            port: 80,
            path: '/collect',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': postData.length
            }
        };

        var req = http.request(options);

        req.on('error', function(e) {
            console.log('problem with request: ' + e.message);
        });

        // write data to request body
        req.write(postData);
        req.end();
    },

    screenview: function(userId, screenName, options) {
        var params = {
            cd: screenName,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('screenview', userId, params);
    },

    event: function(userId, category, action, label, options) {
        var params = {
            ec: category,
            ea: action,
            el: label,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('event', userId, params);
    },

    exception: function(userId, description, options) {
        var params = {
            exd: description,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('exception', userId, params);
    }
};

/*
var x = sendGoogleTracking(
    'amzn1.echo-sdk-account.AHRVRFOJLP4R4KXNPWESNCVBSWW5KMACYFEUBEZ4GGUBKS3DTSG7Q',
    'AwesomeIntent', {
        ec: 'video',
        ea: 'play',
        el: 'holiday',
        ev: 300
    });
console.log(x);
*/

module.exports = googleAnalytics;
<commit_msg>strict uuid v4 compliance<commit_after>var querystring = require('querystring'),
    http = require('http');
var crypto = require('crypto');

var googleAnalytics = {
    tid: 'UA-967247-4',

    an: 'Hebcal',

    extend: function(destination, source) {
        if (typeof source === 'object') {
            for (var attr in source) {
                if (source.hasOwnProperty(attr)) {
                    destination[attr] = source[attr];
                }
            }
        }
        return destination;
    },

    hashUuid4: function(str) {
        var hash = crypto.createHash('md5'),
            digest;
        hash.update(str);
        digest = hash.digest('hex');
        return digest.substr(0, 8) +
            '-' + digest.substr(8, 4) +
            '-4' + digest.substr(13, 3) +
            '-8' + digest.substr(17, 3) +
            '-' + digest.substr(20, 12);
    },

    send: function(hitType, userId, params) {
        var postParams = {
            v: '1',
            tid: this.tid,
            t: hitType,
            cid: this.hashUuid4(userId),
            an: this.an
        };

        postParams = this.extend(postParams, params);

        var postData = querystring.stringify(postParams);
        console.log("TRACKING: " + postData);

        var options = {
            hostname: 'www.google-analytics.com',
            port: 80,
            path: '/collect',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': postData.length
            }
        };

        var req = http.request(options);

        req.on('error', function(e) {
            console.log('problem with request: ' + e.message);
        });

        // write data to request body
        req.write(postData);
        req.end();
    },

    screenview: function(userId, screenName, options) {
        var params = {
            cd: screenName,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('screenview', userId, params);
    },

    event: function(userId, category, action, label, options) {
        var params = {
            ec: category,
            ea: action,
            el: label,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('event', userId, params);
    },

    exception: function(userId, description, options) {
        var params = {
            exd: description,
            geoid: 'US'
        };
        params = this.extend(params, options);
        this.send('exception', userId, params);
    }
};

/*
var x = sendGoogleTracking(
    'amzn1.echo-sdk-account.AHRVRFOJLP4R4KXNPWESNCVBSWW5KMACYFEUBEZ4GGUBKS3DTSG7Q',
    'AwesomeIntent', {
        ec: 'video',
        ea: 'play',
        el: 'holiday',
        ev: 300
    });
console.log(x);
*/

module.exports = googleAnalytics;
<|endoftext|>


<commit_before>/**************************************************************************
*          (C) Vrije Universiteit, Amsterdam (the Netherlands)            *
*                                                                         *
* This file is part of AmCAT - The Amsterdam Content Analysis Toolkit     *
*                                                                         *
* AmCAT is free software: you can redistribute it and/or modify it under  *
* the terms of the GNU Affero General Public License as published by the  *
* Free Software Foundation, either version 3 of the License, or (at your  *
* option) any later version.                                              *
*                                                                         *
* AmCAT is distributed in the hope that it will be useful, but WITHOUT    *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public     *
* License for more details.                                               *
*                                                                         *
* You should have received a copy of the GNU Affero General Public        *
* License along with AmCAT.  If not, see <http://www.gnu.org/licenses/>.  *
***************************************************************************/

(function(){

annotator.articletable = {};
annotator.articletable.MAX_STR_COLUMN_LENGTH = 35; // length of string when limiting length (in article table for headline/comment)
annotator.articletable.articleTable = null;
annotator.articletable.articleEditForm = null;

/************* Article table *************/

annotator.articletable.selectRow = function(row){
    // select a row in the article table. used by goToNextRow and goToPreviousRow functions below
    row.addClass('row_selected');
    annotator.articletable.showArticleFromRow(row);
    var scroller = $('#article-table-container table').parent();
    $(scroller).scrollTo(row, {offset:-50});
}

annotator.articletable.goToNextRow = function(){
    // used for articles table, by continue buttons and 'next'
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').each(function(){
        var row = $(this);
        if(row.next().length == 0){
            annotator.showMessage('last article reached!');
        } else {
            row.removeClass('row_selected');
            annotator.articletable.selectRow(row.next());
        }
    });

    if($('#article-table-container table .row_selected').length == 0) {
        // no selected row exists, pick first row
        var firstTr = $('#article-table-container table tbody tr:first')
        firstTr.addClass('row_selected');
        annotator.articletable.showArticleFromRow(firstTr);
    }
}

annotator.articletable.goToPreviousRow = function(){
    // used for articles table, by 'previous' button
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').each(function(){
        var row = $(this);
        if(row.prev().length == 0){
            annotator.showMessage('first article reached!');
        } else {
            row.removeClass('row_selected');
            annotator.articletable.selectRow(row.prev());
        }
    });
}

annotator.articletable.highlight = function(){
    console.log("Highlighting all labels in article..");
    $.each(annotator.fields.ontologies, function(ont_id, ont){
        // For each ontology
        $.each(ont, function(code_id, code){
            $("div.sentences").easymark("highlight", code.label);
        });
    });
};

// Function to force redraw (recalculation) of element
var redraw = function(){
    var ut = $("#unitcoding-table");
    var _prev = ut.css("display");
    ut.css("display", "none");
    ut.css("display", _prev);
}

/*
 * Helper function of fit_table(), which decreases the headers.
 *
 * @return: true if table resized, false if not
 */
annotator.articletable.decrease_headers = function(min_size){
    var ut = $("#unitcoding-table");
    var headers = $("th", ut);
    var old_table_size = ut.outerWidth();

    // Determine largest header
    var max_found = 0;
    $.each(headers, function(i, header){
        max_found = Math.max(max_found, $(header).text().length);
    });

    if (max_found <= min_size || min_size <= _DECREASE_BY){
        // All headers are already resized to their minimum
        return false;
    }

    var aim = max_found - _DECREASE_BY;
    var decreased_headers = [];
    $.each(headers, function(i, header){
        header = $(header);

        var cur_size = header.text().length;
        var new_size = cur_size - _DECREASE_BY;

        if (cur_size <= min_size || cur_size <= aim){
            // Smaller than we can accept.
            return;
        }

        // We would decrease the current header too much if
        // we blindly accept the new_size in case this headers
        // length is less than we aim for.
        new_size = (new_size < aim) ? aim : new_size;

        // Save original header for users hovering
        if (header.attr("title") === undefined){
            header.attr("title", header.text());
        }

        // Save current state in case resizing fails
        header.attr("__prev", header.text());
        header.text(header.text().substr(0, aim));

        //console.log(headers.toArray().indexOf(header.get(0)));
        decreased_headers.push(header);
    });

    // Force redraw
    redraw();

    if (old_table_size <= ut.outerWidth()){
        // Resizing failed :-(
        $.each(decreased_headers, function(i, header){
            header.text(header.attr("__prev"));
            header.attr("__prev", undefined);
        });

        return false;
    }

    return true;
}

/*
 * Some codingschemas have a lot of fields, causing the table
 * to grow larger than it actually can.
 *
 * @return: resizing succesfull
 */
annotator.articletable.fit_table = function(min_size_input, min_size_header){
    // Default min_size_input / min_size_header to 50px / 8px
    min_size_input = (min_size_input === undefined) ? 50 : min_size_input;
    min_size_header = (min_size_header === undefined) ? 8 : min_size_header;

    var ut = $("#unitcoding-table");
    var headers = $("th", ut);
    var inputs = $("input[type=text]", ut);
    var aimed_size = ut.parent().outerWidth();

    // Check if table initialised yet
    if(ut.children().length < 2){
        window.setTimeout(annotator.articletable.fit_table, 200);
        return;
    }

    // Check if previous resize matched our goals
    if (ut.outerWidth() <= aimed_size){
        return true;
    }

    // Try to resize headers by deleting characters
    var decr_h = annotator.articletable.decrease_headers;
    while(ut.outerWidth() >= aimed_size && decr_h(min_size_header)){ }

    if (ut.outerWidth() > aimed_size){
        console.log("Could not resize table to width of screen..");
    } else {
        $.each(inputs, function(i, input){
            redraw();
            //$(input).width("100%");
        });
    }

}

annotator.articletable.showArticleFromRow = function(row){
    var aid = row.children('td:first').text(); // get article id from first column of current row
    annotator.articleid = aid;

    $('.sentences').html('Loading..');
    $.ajax({
        "url": "../article/" + annotator.articleid + "/sentences",
        "success": function(json) {
            annotator.articleSentences = json.sentences;
            annotator.writeArticleSentences(json.sentences);

            if(json.video){
                console.debug('video html found');
                json.video = json.video.replace(/width="\d+"/g, 'width="250"').replace(/height="\d+"/g, 'height="140"')
                $('.video-container').html(json.video);
                //$('.sentences').css('height', '80%');
            } else {
               // $('.sentences').css('height', '100%');
                $('.video-container').html('').hide();
            }

            /*** set autocomplete for 'unit' column ***/
            annotator.fields.fields['unit'].items = [];
            $.each(json.sentences, function(i, sentenceObj){
                annotator.fields.fields['unit'].items.push({'label':sentenceObj.unit, 'value':sentenceObj.id})
            });
            console.debug('set fields for unit');

            annotator.articletable.highlight();
            annotator.articletable.fit_table();
        },
        "error": function(jqXHR, textStatus, errorThrown){
            console.debug('error loading article');
            $('.article-part').html('
Error loading article data from ' + this.url + '
');
        },
        "dataType": "json"
    });

    $('.coding-part').show();
    //$('#irrelevant-button').focus();
    //annotator.codingTypeSet();
    annotator.articlecodings.showArticleCodings();
    annotator.unitcodings.showSentenceCodings();

    annotator.codingsAreModified(false);
    annotator.resetArticleState();

    var comment = row.find('td:nth-child(8) div div').attr('title'); // very long comments are stored in title
    if(!comment) comment = row.children('td:nth-child(8)').text();
    console.debug('comment', comment);
    $('#article-comment').val(comment);

    console.debug('status', row.children('td:nth-child(7)').text());
    $('#article-status option:contains("' + row.children('td:nth-child(7)').text() + '")').attr('selected', 'selected');

    $('#edit-article-button').button( "option", "disabled", false); // when article is selected, it is possible to edit

    if(row.next().length == 0){ // no next article available
        $('#next-article-button').button( "option", "disabled", true);
    } else {
        $('#next-article-button').button( "option", "disabled", false);
    }
    if(row.prev().length == 0){ // no previous article available
        $('#previous-article-button').button( "option", "disabled", true);
    } else {
        $('#previous-article-button').button( "option", "disabled", false);
    }

    annotator.articletable.highlight();
}

annotator.articletable.clickArticleRow = function(row) { // called if user clicks on article table row
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').removeClass('row_selected');
    row.addClass('row_selected');
    annotator.articletable.showArticleFromRow(row);
}

annotator.articletable.onCreateArticleTable = function(){
    /*** set items of articles table ***/
    //$("#article-table-container table tbody").click(annotator.articletable.clickArticleRow);
    //$("#article-table-container table td").css('cursor', 'pointer');
    $('#next-article-button').button("option", "disabled", false);
}

annotator.articletable.showEditArticleDialog = function(articleid){
    if(!annotator.confirmArticleChange()) return;
    $('#article-edit-form').html('Loading...');
    $("#article-dialog-form").dialog("open");
    annotator.articletable.articleEditForm = new Form("annotator/articleFormJSON/" + articleid, {'sortFields':true});
    annotator.articletable.articleEditForm.ready = function(){
        $('#article-edit-form').html(annotator.articletable.articleEditForm.render.table);
    }
    annotator.articletable.articleEditForm.handle_loaderror = function(jqXHR, textStatus, errorThrown){
        console.debug('Error loading form: ' + textStatus);
        $('#article-edit-form').html('
Failed to load form: ' + textStatus + '
');
    }

    // ugly hack... should be removed when moving to django..
    annotator.articletable.articleEditForm.initialize_form2 = annotator.articletable.articleEditForm.initialize_form;
    annotator.articletable.articleEditForm.initialize_form = function(json){
        fields.autocompletes['source-6'] = {'showAll':true, 'isOntology':false, 'autoOpen':true, 'id':'source-6',
            items:json.mediumlist
        }
        annotator.articletable.articleEditForm.initialize_form2(json.form);
        annotator.fields.autocompletes.addAutocompletes($('#article-edit-form'));

        if(articleid == 0){ // new article form
            annotator.articletable.articleEditForm.fields['setnr'].initial = annotator.setnr;
            annotator.articletable.articleEditForm.fields['jobid'].initial = annotator.codingjobid;
            console.log('done setting setnr and jobid');
        }
    }

    console.debug('loading article edit form');
    annotator.articletable.articleEditForm.load();
}

annotator.articletable.saveEditArticleDetails = function(){
    annotator.articletable.articleEditForm.clean();
    if(annotator.articletable.articleEditForm.is_valid()){
        $('#article-edit-form').hide();
        $('#article-edit-status').html('Saving...');
        annotator.articletable.articleEditForm.send('annotator/storeEditArticle', function(data, textStatus, jqXHR){
            console.debug('article details', data);
            if(data.response == 'ok'){
                console.debug('saved edit article!' + textStatus);
                $("#article-dialog-form").dialog("close");
                location.reload();
            } else {
                 $('#article-edit-form').show();
                $('#article-edit-status').html('
Failed to save. Error message: ' + data.errormsg + '
');
            }
        }, function(){
             $('#article-edit-form').show();
            $('#article-edit-status').html('
Failed to save. Server error
');
        });
    } else {
        console.debug('invalid form');
    }
}

annotator.articletable.trimTableColumnString = function(obj) { // make sure that column text is not excessively long
    var sReturn = obj.aData[obj.iDataColumn];
    if (sReturn && sReturn.length > annotator.articletable.MAX_STR_COLUMN_LENGTH) {
        sReturn = '
' + sReturn.substring(0,annotator.articletable.MAX_STR_COLUMN_LENGTH) + '...
';
    }
    return '
' + sReturn + '
';
};

})();
<commit_msg>Made highlighting much faster<commit_after>/**************************************************************************
*          (C) Vrije Universiteit, Amsterdam (the Netherlands)            *
*                                                                         *
* This file is part of AmCAT - The Amsterdam Content Analysis Toolkit     *
*                                                                         *
* AmCAT is free software: you can redistribute it and/or modify it under  *
* the terms of the GNU Affero General Public License as published by the  *
* Free Software Foundation, either version 3 of the License, or (at your  *
* option) any later version.                                              *
*                                                                         *
* AmCAT is distributed in the hope that it will be useful, but WITHOUT    *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public     *
* License for more details.                                               *
*                                                                         *
* You should have received a copy of the GNU Affero General Public        *
* License along with AmCAT.  If not, see <http://www.gnu.org/licenses/>.  *
***************************************************************************/

(function(){

annotator.articletable = {};
annotator.articletable.MAX_STR_COLUMN_LENGTH = 35; // length of string when limiting length (in article table for headline/comment)
annotator.articletable.articleTable = null;
annotator.articletable.articleEditForm = null;

/************* Article table *************/

annotator.articletable.selectRow = function(row){
    // select a row in the article table. used by goToNextRow and goToPreviousRow functions below
    row.addClass('row_selected');
    annotator.articletable.showArticleFromRow(row);
    var scroller = $('#article-table-container table').parent();
    $(scroller).scrollTo(row, {offset:-50});
}

annotator.articletable.goToNextRow = function(){
    // used for articles table, by continue buttons and 'next'
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').each(function(){
        var row = $(this);
        if(row.next().length == 0){
            annotator.showMessage('last article reached!');
        } else {
            row.removeClass('row_selected');
            annotator.articletable.selectRow(row.next());
        }
    });

    if($('#article-table-container table .row_selected').length == 0) {
        // no selected row exists, pick first row
        var firstTr = $('#article-table-container table tbody tr:first')
        firstTr.addClass('row_selected');
        annotator.articletable.showArticleFromRow(firstTr);
    }
}

annotator.articletable.goToPreviousRow = function(){
    // used for articles table, by 'previous' button
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').each(function(){
        var row = $(this);
        if(row.prev().length == 0){
            annotator.showMessage('first article reached!');
        } else {
            row.removeClass('row_selected');
            annotator.articletable.selectRow(row.prev());
        }
    });
}

annotator.articletable.highlight = function(){
    console.log("Highlighting all labels in article..");
    $.each(annotator.fields.ontologies, function(ont_id, ont){
        // For each ontology
        var labels = [];
        $.each(ont, function(code_id, code){
            labels.push(code.label);
        });

        $("div.sentences").easymark("highlight", labels.join(" "));
    });
};

// Function to force redraw (recalculation) of element
var redraw = function(){
    var ut = $("#unitcoding-table");
    var _prev = ut.css("display");
    ut.css("display", "none");
    ut.css("display", _prev);
}

/*
 * Helper function of fit_table(), which decreases the headers.
 *
 * @return: true if table resized, false if not
 */
annotator.articletable.decrease_headers = function(min_size){
    var ut = $("#unitcoding-table");
    var headers = $("th", ut);
    var old_table_size = ut.outerWidth();

    // Determine largest header
    var max_found = 0;
    $.each(headers, function(i, header){
        max_found = Math.max(max_found, $(header).text().length);
    });

    if (max_found <= min_size || min_size <= _DECREASE_BY){
        // All headers are already resized to their minimum
        return false;
    }

    var aim = max_found - _DECREASE_BY;
    var decreased_headers = [];
    $.each(headers, function(i, header){
        header = $(header);

        var cur_size = header.text().length;
        var new_size = cur_size - _DECREASE_BY;

        if (cur_size <= min_size || cur_size <= aim){
            // Smaller than we can accept.
            return;
        }

        // We would decrease the current header too much if
        // we blindly accept the new_size in case this headers
        // length is less than we aim for.
        new_size = (new_size < aim) ? aim : new_size;

        // Save original header for users hovering
        if (header.attr("title") === undefined){
            header.attr("title", header.text());
        }

        // Save current state in case resizing fails
        header.attr("__prev", header.text());
        header.text(header.text().substr(0, aim));

        //console.log(headers.toArray().indexOf(header.get(0)));
        decreased_headers.push(header);
    });

    // Force redraw
    redraw();

    if (old_table_size <= ut.outerWidth()){
        // Resizing failed :-(
        $.each(decreased_headers, function(i, header){
            header.text(header.attr("__prev"));
            header.attr("__prev", undefined);
        });

        return false;
    }

    return true;
}

/*
 * Some codingschemas have a lot of fields, causing the table
 * to grow larger than it actually can.
 *
 * @return: resizing succesfull
 */
annotator.articletable.fit_table = function(min_size_input, min_size_header){
    // Default min_size_input / min_size_header to 50px / 8px
    min_size_input = (min_size_input === undefined) ? 50 : min_size_input;
    min_size_header = (min_size_header === undefined) ? 8 : min_size_header;

    var ut = $("#unitcoding-table");
    var headers = $("th", ut);
    var inputs = $("input[type=text]", ut);
    var aimed_size = ut.parent().outerWidth();

    // Check if table initialised yet
    if(ut.children().length < 2){
        window.setTimeout(annotator.articletable.fit_table, 200);
        return;
    }

    // Check if previous resize matched our goals
    if (ut.outerWidth() <= aimed_size){
        return true;
    }

    // Try to resize headers by deleting characters
    var decr_h = annotator.articletable.decrease_headers;
    while(ut.outerWidth() >= aimed_size && decr_h(min_size_header)){ }

    if (ut.outerWidth() > aimed_size){
        console.log("Could not resize table to width of screen..");
    } else {
        $.each(inputs, function(i, input){
            redraw();
            //$(input).width("100%");
        });
    }

}

annotator.articletable.showArticleFromRow = function(row){
    var aid = row.children('td:first').text(); // get article id from first column of current row
    annotator.articleid = aid;

    $('.sentences').html('Loading..');
    $.ajax({
        "url": "../article/" + annotator.articleid + "/sentences",
        "success": function(json) {
            annotator.articleSentences = json.sentences;
            annotator.writeArticleSentences(json.sentences);

            if(json.video){
                console.debug('video html found');
                json.video = json.video.replace(/width="\d+"/g, 'width="250"').replace(/height="\d+"/g, 'height="140"')
                $('.video-container').html(json.video);
                //$('.sentences').css('height', '80%');
            } else {
               // $('.sentences').css('height', '100%');
                $('.video-container').html('').hide();
            }

            /*** set autocomplete for 'unit' column ***/
            annotator.fields.fields['unit'].items = [];
            $.each(json.sentences, function(i, sentenceObj){
                annotator.fields.fields['unit'].items.push({'label':sentenceObj.unit, 'value':sentenceObj.id})
            });
            console.debug('set fields for unit');

            annotator.articletable.highlight();
            annotator.articletable.fit_table();
        },
        "error": function(jqXHR, textStatus, errorThrown){
            console.debug('error loading article');
            $('.article-part').html('
Error loading article data from ' + this.url + '
');
        },
        "dataType": "json"
    });

    $('.coding-part').show();
    //$('#irrelevant-button').focus();
    //annotator.codingTypeSet();
    annotator.articlecodings.showArticleCodings();
    annotator.unitcodings.showSentenceCodings();

    annotator.codingsAreModified(false);
    annotator.resetArticleState();

    var comment = row.find('td:nth-child(8) div div').attr('title'); // very long comments are stored in title
    if(!comment) comment = row.children('td:nth-child(8)').text();
    console.debug('comment', comment);
    $('#article-comment').val(comment);

    console.debug('status', row.children('td:nth-child(7)').text());
    $('#article-status option:contains("' + row.children('td:nth-child(7)').text() + '")').attr('selected', 'selected');

    $('#edit-article-button').button( "option", "disabled", false); // when article is selected, it is possible to edit

    if(row.next().length == 0){ // no next article available
        $('#next-article-button').button( "option", "disabled", true);
    } else {
        $('#next-article-button').button( "option", "disabled", false);
    }
    if(row.prev().length == 0){ // no previous article available
        $('#previous-article-button').button( "option", "disabled", true);
    } else {
        $('#previous-article-button').button( "option", "disabled", false);
    }

    annotator.articletable.highlight();
}

annotator.articletable.clickArticleRow = function(row) { // called if user clicks on article table row
    if(!annotator.confirmArticleChange()) return;
    $('#article-table-container table .row_selected').removeClass('row_selected');
    row.addClass('row_selected');
    annotator.articletable.showArticleFromRow(row);
}

annotator.articletable.onCreateArticleTable = function(){
    /*** set items of articles table ***/
    //$("#article-table-container table tbody").click(annotator.articletable.clickArticleRow);
    //$("#article-table-container table td").css('cursor', 'pointer');
    $('#next-article-button').button("option", "disabled", false);
}

annotator.articletable.showEditArticleDialog = function(articleid){
    if(!annotator.confirmArticleChange()) return;
    $('#article-edit-form').html('Loading...');
    $("#article-dialog-form").dialog("open");
    annotator.articletable.articleEditForm = new Form("annotator/articleFormJSON/" + articleid, {'sortFields':true});
    annotator.articletable.articleEditForm.ready = function(){
        $('#article-edit-form').html(annotator.articletable.articleEditForm.render.table);
    }
    annotator.articletable.articleEditForm.handle_loaderror = function(jqXHR, textStatus, errorThrown){
        console.debug('Error loading form: ' + textStatus);
        $('#article-edit-form').html('
Failed to load form: ' + textStatus + '
');
    }

    // ugly hack... should be removed when moving to django..
    annotator.articletable.articleEditForm.initialize_form2 = annotator.articletable.articleEditForm.initialize_form;
    annotator.articletable.articleEditForm.initialize_form = function(json){
        fields.autocompletes['source-6'] = {'showAll':true, 'isOntology':false, 'autoOpen':true, 'id':'source-6',
            items:json.mediumlist
        }
        annotator.articletable.articleEditForm.initialize_form2(json.form);
        annotator.fields.autocompletes.addAutocompletes($('#article-edit-form'));

        if(articleid == 0){ // new article form
            annotator.articletable.articleEditForm.fields['setnr'].initial = annotator.setnr;
            annotator.articletable.articleEditForm.fields['jobid'].initial = annotator.codingjobid;
            console.log('done setting setnr and jobid');
        }
    }

    console.debug('loading article edit form');
    annotator.articletable.articleEditForm.load();
}

annotator.articletable.saveEditArticleDetails = function(){
    annotator.articletable.articleEditForm.clean();
    if(annotator.articletable.articleEditForm.is_valid()){
        $('#article-edit-form').hide();
        $('#article-edit-status').html('Saving...');
        annotator.articletable.articleEditForm.send('annotator/storeEditArticle', function(data, textStatus, jqXHR){
            console.debug('article details', data);
            if(data.response == 'ok'){
                console.debug('saved edit article!' + textStatus);
                $("#article-dialog-form").dialog("close");
                location.reload();
            } else {
                 $('#article-edit-form').show();
                $('#article-edit-status').html('
Failed to save. Error message: ' + data.errormsg + '
');
            }
        }, function(){
             $('#article-edit-form').show();
            $('#article-edit-status').html('
Failed to save. Server error
');
        });
    } else {
        console.debug('invalid form');
    }
}

annotator.articletable.trimTableColumnString = function(obj) { // make sure that column text is not excessively long
    var sReturn = obj.aData[obj.iDataColumn];
    if (sReturn && sReturn.length > annotator.articletable.MAX_STR_COLUMN_LENGTH) {
        sReturn = '
' + sReturn.substring(0,annotator.articletable.MAX_STR_COLUMN_LENGTH) + '...
';
    }
    return '
' + sReturn + '
';
};

})();
<|endoftext|>


<commit_before>// Uncomment to debug browserify + shim
// process.env.BROWSERIFYSHIM_DIAGNOSTICS = 1;

var gulp = require('gulp'),
    plumber = require('gulp-plumber'),
    gutil = require('gulp-util'),
    notify = require("gulp-notify"),
    argv = require('yargs').argv,
    browserify = require('browserify'),
    watchify = require('watchify'),
    remapify = require('remapify'),
    gStreamify = require('gulp-streamify'),
    uglify = require('gulp-uglify'),
    bundleLogger = require('../utils/bundleLogger'),
    handleErrors = require('../utils/handleErrors'),
    source = require('vinyl-source-stream');

var env = argv.env != "production";

gulp.task('browserify', function()
{
    var b = browserify("./src/index.js",
    {
        cache: {},
        packageCache: {},
        debug: env
    }),
    file = 'build.js',
    folder = './build/';

    var bundler = global.isWatching ? watchify(b) : b;

    bundler.plugin(remapify, [{
        src: './**/*.js',
        expose: 'common',
        cwd: __dirname + '/../../src/common/'
    },
    {
        src: './**/*.js',
        expose: 'base',
        cwd: __dirname + '/../../src/base/'
    }]);

    var bundle = function() {
        bundleLogger.start();

        return bundler.bundle()
        .on('error', handleErrors)
        .pipe(source(file))
        .pipe(argv.env != "production" ? gutil.noop() : gStreamify(uglify()))
        .pipe(gulp.dest(folder))
        .on('end', bundleLogger.end);
    };

    if(global.isWatching) bundler.on('update', bundle);

    return bundle();
});<commit_msg>Fix the watchify build speed using "fullPaths"<commit_after>// Uncomment to debug browserify + shim
// process.env.BROWSERIFYSHIM_DIAGNOSTICS = 1;

var gulp = require('gulp'),
    plumber = require('gulp-plumber'),
    gutil = require('gulp-util'),
    notify = require("gulp-notify"),
    argv = require('yargs').argv,
    browserify = require('browserify'),
    watchify = require('watchify'),
    remapify = require('remapify'),
    gStreamify = require('gulp-streamify'),
    uglify = require('gulp-uglify'),
    bundleLogger = require('../utils/bundleLogger'),
    handleErrors = require('../utils/handleErrors'),
    source = require('vinyl-source-stream');

var env = argv.env != "production";

gulp.task('browserify', function()
{
    var b = browserify("./src/index.js",
    {
        cache: {},
        packageCache: {},
        debug: env,
        fullPaths: true
    }),
    file = 'build.js',
    folder = './build/';

    var bundler = global.isWatching ? watchify(b) : b;

    bundler.plugin(remapify, [{
        src: './**/*.js',
        expose: 'common',
        cwd: __dirname + '/../../src/common/'
    },
    {
        src: './**/*.js',
        expose: 'base',
        cwd: __dirname + '/../../src/base/'
    }]);

    var bundle = function() {
        bundleLogger.start();

        return bundler.bundle()
        .on('error', handleErrors)
        .pipe(source(file))
        .pipe(argv.env != "production" ? gutil.noop() : gStreamify(uglify()))
        .pipe(gulp.dest(folder))
        .on('end', bundleLogger.end);
    };

    if(global.isWatching) bundler.on('update', bundle);

    return bundle();
});<|endoftext|>


<commit_before>import { stateToUrl, urlToState } from './history'

describe('stateToUrl', () => {
  it('encodes state to url', () => {
    expect(stateToUrl({ fStr: '3.14', f64: true })).toBe('3.14')
    expect(stateToUrl({ fStr: '3.14', f64: false })).toBe('~3.14')
    expect(stateToUrl({ fStr: '~3.14', f64: true })).toBe('~~3.14')
    expect(stateToUrl({ fStr: '~3.14', f64: false })).toBe('~~~3.14')
    expect(stateToUrl({ fStr: '~~~3.14', f64: true })).toBe('~~~~~~3.14')
    expect(stateToUrl({ fStr: '~~~3.14', f64: false })).toBe('~~~~~~~3.14')
  })
})

describe('urlToState', () => {
  it('decodes state from url', () => {
    expect(urlToState('3.14')).toEqual({ fStr: '3.14', f64: true })
    expect(urlToState('~3.14')).toEqual({ fStr: '3.14', f64: false })
    expect(urlToState('~~3.14')).toEqual({ fStr: '~3.14', f64: true })
    expect(urlToState('~~~3.14')).toEqual({ fStr: '~3.14', f64: false })
    expect(urlToState('~~~~3.14')).toEqual({ fStr: '~~3.14', f64: true })
    expect(urlToState('~~~~~3.14')).toEqual({ fStr: '~~3.14', f64: false })
    expect(urlToState('~~~~~~3.14')).toEqual({ fStr: '~~~3.14', f64: true })
  })
})
<commit_msg>Add test<commit_after>import { stateToUrl, urlToState } from './history'

describe('stateToUrl', () => {
  it('encodes state to url', () => {
    expect(stateToUrl({ fStr: '3.14', f64: true })).toBe('3.14')
    expect(stateToUrl({ fStr: '3.14', f64: false })).toBe('~3.14')
    expect(stateToUrl({ fStr: '~3.14', f64: true })).toBe('~~3.14')
    expect(stateToUrl({ fStr: '~3.14', f64: false })).toBe('~~~3.14')
    expect(stateToUrl({ fStr: '~~~3.14', f64: true })).toBe('~~~~~~3.14')
    expect(stateToUrl({ fStr: '~~~3.14', f64: false })).toBe('~~~~~~~3.14')
  })
})

describe('urlToState', () => {
  it('decodes state from url', () => {
    expect(urlToState('3.14')).toEqual({ fStr: '3.14', f64: true })
    expect(urlToState('~3.14')).toEqual({ fStr: '3.14', f64: false })
    expect(urlToState('~~3.14')).toEqual({ fStr: '~3.14', f64: true })
    expect(urlToState('~~~3.14')).toEqual({ fStr: '~3.14', f64: false })
    expect(urlToState('~~~~3.14')).toEqual({ fStr: '~~3.14', f64: true })
    expect(urlToState('~~~~~3.14')).toEqual({ fStr: '~~3.14', f64: false })
    expect(urlToState('~~~~~~3.14')).toEqual({ fStr: '~~~3.14', f64: true })
  })
})

describe('urlToState o stateToUrl', () => {
  it('is identity', () => {
    const testIdentity = fStr => {
      expect(urlToState(stateToUrl({ fStr, f64: true }))).toEqual({ fStr, f64: true })
      expect(urlToState(stateToUrl({ fStr, f64: false }))).toEqual({ fStr, f64: false })
    }
    testIdentity('')
    testIdentity('3.14')
    testIdentity('1')
    testIdentity('-1')
    testIdentity('PI * 13.37')
    testIdentity('~')
    testIdentity('~~')
    testIdentity('~~~')
    testIdentity('~3.14')
    testIdentity('~~3.14')
    testIdentity('~~~3.14')
  })
})
<|endoftext|>


<commit_before>import {
  initAppointmentListMock,
  createPastVAAppointments,
  mockFeatureToggles,
} from './vaos-cypress-helpers';

describe('VAOS appointment list', () => {
  beforeEach(() => {
    initAppointmentListMock();
    cy.visit('health-care/schedule-view-va-appointments/appointments/');
    cy.get('.va-modal-body button').click();
    cy.injectAxe();
  });

  it('should render appointments list', () => {
    cy.get('#appointments-list').should('exist');
    cy.axeCheckBestPractice();
  });

  it('should allow for cancelling of appointments', () => {
    cy.get('li[data-is-cancelable="true"] button.vaos-appts__cancel-btn')
      .first()
      .click();
    cy.get('#cancelAppt .usa-button').click();
    cy.get('.usa-alert-success').should('exist');
    cy.get('#cancelAppt button:not([disabled])')
      .first()
      .click();
    cy.get('#cancelAppt').should('not.exist');
    cy.axeCheckBestPractice();
  });

  it('should show more info for appointments', () => {
    cy.get(
      'li[data-request-id="8a48912a6cab0202016cb4fcaa8b0038"] .additional-info-button.va-button-link',
    ).click();
    cy.get('[id="8a48912a6cab0202016cb4fcaa8b0038-vaos-info-content"]');
    cy.contains('Request 2 Message 1 Text');
    cy.axeCheckBestPractice();
  });

  it('should render past appointments', () => {
    cy.route({
      method: 'GET',
      url: /.*\/v0\/appointments.*type=va$/,
      response: createPastVAAppointments(),
    });
    cy.get('#tabpast').click();
    cy.findByText(/three day clinic name/i).should('exist');
    cy.findByLabelText(/select a date range/i).select('1');
    cy.findByText('Update').click();
    cy.findByText(/four month clinic name/i).should('exist');
    cy.get('#queryResultLabel').should('have.focus');
    cy.axeCheckBestPractice();
  });
});

describe('VAOS appointment list refresh', () => {
  beforeEach(() => {
    initAppointmentListMock();
    mockFeatureToggles({ homepageRefresh: true });
    cy.visit('health-care/schedule-view-va-appointments/appointments/');
    cy.injectAxe();
    cy.get('.va-modal-body button').click();
  });

  describe('appointments details', () => {
    beforeEach(() => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('#type-dropdown').should('exist');
    });

    it('community care appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Community Care/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/cc');
      cy.get('[data-cy=community-care-appointment-details-header]')
        .should('exist')
        .contains('Community care');
      cy.axeCheckBestPractice();
    });

    it('va appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA CLinic/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-appointment-details-header]')
        .should('exist')
        .contains('VA Appointment');
      cy.axeCheckBestPractice();
    });

    it('va phone appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Phone call/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-appointment-details-header]')
        .should('exist')
        .contains('VA Appointment over the phone');
      cy.axeCheckBestPractice();
    });

    it('express care appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Express Care request/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/express-care');
      cy.get('[data-cy=express-care-appointment-details-header]')
        .should('exist')
        .contains('Express Care request');
      cy.axeCheckBestPractice();
    });

    it('va video appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at a VA Location/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at VA location');
      cy.axeCheckBestPractice();
    });

    it('va video appointment at an ATLAS location', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at an ATLAS Location/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at an ATLAS location');
      cy.axeCheckBestPractice();
    });

    it('va video appointment at home', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at home/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at home');
      cy.axeCheckBestPractice();
    });

    it('should allow for canceling of appointments', () => {
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA CLinic/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
      cy.findByText(/Cancel appointment/i).click();
      cy.findByText(/Yes, cancel this appointment/i).click();
      cy.get('.usa-alert-success').should('exist');
      cy.findByText(/Continue/i).click();
      cy.get('#cancelAppt').should('not.exist');
      cy.get('.usa-alert-success').should('not.exist');
      cy.get('.usa-alert-error').should('exist');
    });
  });

  describe('upcoming appointments', () => {
    it('should render upcoming appointments list', () => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=upcoming-appointment-list]').should('exist');
      cy.get('#type-dropdown').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it('should navigate to upcoming appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
    });
  });

  describe('requested appointments', () => {
    beforeEach(() => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=upcoming-appointment-list]').should('exist');
      cy.get('#type-dropdown')
        .select('requested')
        .should('have.value', 'requested');
    });

    it('should render requested appointments list', () => {
      cy.get('[data-cy=requested-appointment-list]').should('exist');
      cy.get('[data-cy=requested-appointment-list-item]')
        .first()
        .should('exist');
    });

    it('should navigate to requested appointment details', () => {
      cy.get('[data-cy=requested-appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Request detail/i).should('exist');
    });
  });

  describe('past appointments', () => {
    beforeEach(() => {
      cy.route({
        method: 'GET',
        url: /.*\/v0\/appointments.*type=va$/,
        response: createPastVAAppointments(),
      });
      cy.get('#type-dropdown')
        .select('past')
        .should('have.value', 'past');
    });

    it('should render past appointments list', () => {
      cy.findByLabelText(/Select a date range/i).should('exist');
      cy.get('[data-cy=past-appointment-list]').should('exist');
      cy.get('[data-cy=past-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it('should navigate to requested appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .focus()
        .click();
      cy.findByText(/Request detail/i).should('exist');
    });

    it('should select an updated date range', () => {
      cy.get('select')
        .eq(1)
        .select('1')
        .should('have.value', '1');
      cy.get('button')
        .contains(/Update/i)
        .click();
      cy.get('h3').should('exist');
      cy.axeCheckBestPractice();
    });
  });

  describe('canceled appointments', () => {
    beforeEach(() => {
      cy.get('#type-dropdown')
        .select('canceled')
        .should('have.value', 'canceled');
    });
    it('should render canceled appointments list', () => {
      cy.get('[data-cy=canceled-appointment-list-header]').should('exist');
      cy.get('[data-cy=canceled-appointment-list]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it('should navigate to canceled appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
    });
  });
});
<commit_msg>Disable failing VAOS Cypress test (#17139)<commit_after>import {
  initAppointmentListMock,
  createPastVAAppointments,
  mockFeatureToggles,
} from './vaos-cypress-helpers';

describe('VAOS appointment list', () => {
  beforeEach(() => {
    initAppointmentListMock();
    cy.visit('health-care/schedule-view-va-appointments/appointments/');
    cy.get('.va-modal-body button').click();
    cy.injectAxe();
  });

  it('should render appointments list', () => {
    cy.get('#appointments-list').should('exist');
    cy.axeCheckBestPractice();
  });

  it('should allow for cancelling of appointments', () => {
    cy.get('li[data-is-cancelable="true"] button.vaos-appts__cancel-btn')
      .first()
      .click();
    cy.get('#cancelAppt .usa-button').click();
    cy.get('.usa-alert-success').should('exist');
    cy.get('#cancelAppt button:not([disabled])')
      .first()
      .click();
    cy.get('#cancelAppt').should('not.exist');
    cy.axeCheckBestPractice();
  });

  it('should show more info for appointments', () => {
    cy.get(
      'li[data-request-id="8a48912a6cab0202016cb4fcaa8b0038"] .additional-info-button.va-button-link',
    ).click();
    cy.get('[id="8a48912a6cab0202016cb4fcaa8b0038-vaos-info-content"]');
    cy.contains('Request 2 Message 1 Text');
    cy.axeCheckBestPractice();
  });

  it('should render past appointments', () => {
    cy.route({
      method: 'GET',
      url: /.*\/v0\/appointments.*type=va$/,
      response: createPastVAAppointments(),
    });
    cy.get('#tabpast').click();
    cy.findByText(/three day clinic name/i).should('exist');
    cy.findByLabelText(/select a date range/i).select('1');
    cy.findByText('Update').click();
    cy.findByText(/four month clinic name/i).should('exist');
    cy.get('#queryResultLabel').should('have.focus');
    cy.axeCheckBestPractice();
  });
});

describe('VAOS appointment list refresh', () => {
  beforeEach(() => {
    initAppointmentListMock();
    mockFeatureToggles({ homepageRefresh: true });
    cy.visit('health-care/schedule-view-va-appointments/appointments/');
    cy.injectAxe();
    cy.get('.va-modal-body button').click();
  });

  describe('appointments details', () => {
    beforeEach(() => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('#type-dropdown').should('exist');
    });

    it('community care appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Community Care/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/cc');
      cy.get('[data-cy=community-care-appointment-details-header]')
        .should('exist')
        .contains('Community care');
      cy.axeCheckBestPractice();
    });

    it('va appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA CLinic/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-appointment-details-header]')
        .should('exist')
        .contains('VA Appointment');
      cy.axeCheckBestPractice();
    });

    it('va phone appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Phone call/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-appointment-details-header]')
        .should('exist')
        .contains('VA Appointment over the phone');
      cy.axeCheckBestPractice();
    });

    it('express care appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/Express Care request/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/express-care');
      cy.get('[data-cy=express-care-appointment-details-header]')
        .should('exist')
        .contains('Express Care request');
      cy.axeCheckBestPractice();
    });

    it('va video appointment', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at a VA Location/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at VA location');
      cy.axeCheckBestPractice();
    });

    it('va video appointment at an ATLAS location', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at an ATLAS Location/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at an ATLAS location');
      cy.axeCheckBestPractice();
    });

    it('va video appointment at home', () => {
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA Video Connect at home/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.url().should('include', '/appointments/va');
      cy.get('[data-cy=va-video-appointment-details-header]')
        .should('exist')
        .contains('VA Video Connect at home');
      cy.axeCheckBestPractice();
    });

    it('should allow for canceling of appointments', () => {
      cy.get('[data-cy=appointment-list-item]')
        .contains(/VA CLinic/i)
        .parent()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
      cy.findByText(/Cancel appointment/i).click();
      cy.findByText(/Yes, cancel this appointment/i).click();
      cy.get('.usa-alert-success').should('exist');
      cy.findByText(/Continue/i).click();
      cy.get('#cancelAppt').should('not.exist');
      cy.get('.usa-alert-success').should('not.exist');
      cy.get('.usa-alert-error').should('exist');
    });
  });

  describe('upcoming appointments', () => {
    it('should render upcoming appointments list', () => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=upcoming-appointment-list]').should('exist');
      cy.get('#type-dropdown').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it('should navigate to upcoming appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
    });
  });

  describe('requested appointments', () => {
    beforeEach(() => {
      cy.findByText(/Your appointments/i).should('exist');
      cy.get('[data-cy=upcoming-appointment-list-header]').should('exist');
      cy.get('[data-cy=upcoming-appointment-list]').should('exist');
      cy.get('#type-dropdown')
        .select('requested')
        .should('have.value', 'requested');
    });

    it('should render requested appointments list', () => {
      cy.get('[data-cy=requested-appointment-list]').should('exist');
      cy.get('[data-cy=requested-appointment-list-item]')
        .first()
        .should('exist');
    });

    it('should navigate to requested appointment details', () => {
      cy.get('[data-cy=requested-appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Request detail/i).should('exist');
    });
  });

  describe('past appointments', () => {
    beforeEach(() => {
      cy.route({
        method: 'GET',
        url: /.*\/v0\/appointments.*type=va$/,
        response: createPastVAAppointments(),
      });
      cy.get('#type-dropdown')
        .select('past')
        .should('have.value', 'past');
    });

    it('should render past appointments list', () => {
      cy.findByLabelText(/Select a date range/i).should('exist');
      cy.get('[data-cy=past-appointment-list]').should('exist');
      cy.get('[data-cy=past-appointment-list-header]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it.skip('should navigate to requested appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .focus()
        .click();
      cy.findByText(/Request detail/i).should('exist');
    });

    it('should select an updated date range', () => {
      cy.get('select')
        .eq(1)
        .select('1')
        .should('have.value', '1');
      cy.get('button')
        .contains(/Update/i)
        .click();
      cy.get('h3').should('exist');
      cy.axeCheckBestPractice();
    });
  });

  describe('canceled appointments', () => {
    beforeEach(() => {
      cy.get('#type-dropdown')
        .select('canceled')
        .should('have.value', 'canceled');
    });
    it('should render canceled appointments list', () => {
      cy.get('[data-cy=canceled-appointment-list-header]').should('exist');
      cy.get('[data-cy=canceled-appointment-list]').should('exist');
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .should('exist');
      cy.axeCheckBestPractice();
    });

    it('should navigate to canceled appointment details', () => {
      cy.get('[data-cy=appointment-list-item]')
        .first()
        .findByText(/Details/i)
        .click();
      cy.findByText(/Appointment detail/i).should('exist');
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var log4js = require('log4js');
var _ = require('underscore');
var moment = require('moment');

/**
 * Map of log4js appender category names (also as specified in logger conf configurations) to log4js logger objects
 */
var log4js_loggers = {};

/**
 * Converts level string from logger config into numerical value. Valid strings (case insensitive) are: all. trace,
 * debug, info, warn, error, fatal
 */
var parse_level = function(level_string) {
  var levels = {
    all:   0,
    trace: 100,
    debug: 200,
    info:  300,
    warn:  400,
    error: 500,
    fatal: 600,
    off:   9999
  };
  if(levels[level_string.toLowerCase()] !== undefined) { return levels[level_string.toLowerCase()]; }
  else { throw new Error('Unknown log level string: ' + level_string); }
};

/**
 * A logger object, associated with a group and with some set of appenders attached to it
 * @param {Object} options Required and possible properties are String:level Array[String]:appenders, String:group
 */
var Logger = function(options) {
  // PRIVATE FUNCTIONS
  var that = this;

  var assemble_log_entry = function(message, level_string) {
    var timestamp = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
    return timestamp + ' ' + level_string.toUpperCase() + ' [' + that.group + '] ' + message;
  };
  // END PRIVATE FUNCTIONS

  // PUBLIC PRIVILEGED FUNCTIONS
  // TODO: Refactor so that these methos are on the prototype, not the instance
  this.is_trace_enabled = function() {
    return this.level <= parse_level('trace');
  };

  this.trace = function(message) {
    if(this.is_trace_enabled()) {
      var log_entry = assemble_log_entry(message, 'trace');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].trace(log_entry);
        }
      });
    }
  };

  this.is_debug_enabled = function() {
    return this.level <= parse_level('debug');
  };

  this.debug = function(message) {
    if(this.is_debug_enabled()) {
      var log_entry = assemble_log_entry(message, 'debug');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].debug(log_entry);
        }
      });
    }
  };

  this.is_info_enabled = function() {
    return this.level <= parse_level('info');
  };

  this.info = function(message) {
    if(this.is_info_enabled()) {
      var log_entry = assemble_log_entry(message, 'info');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].info(log_entry);
        }
      });
    }
  };

  this.is_warn_enabled = function() {
    return this.level <= parse_level('warn');
  };

  this.warn = function(message) {
    if(this.is_warn_enabled()) {
      var log_entry = assemble_log_entry(message, 'warn');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].warn(log_entry);
        }
      });
    }
  };

  this.is_error_enabled = function() {
    return this.level <= parse_level('error');
  };

  this.error = function(message) {
    if(this.is_error_enabled()) {
      var log_entry = assemble_log_entry(message, 'error');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].error(log_entry);
        }
      });
    }
  };

  this.is_fatal_enabled = function() {
    return this.level <= parse_level('fatal');
  };

  this.fatal = function(message) {
    if(this.is_fatal_enabled()) {
      var log_entry = assemble_log_entry(message, 'fatal');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].fatal(log_entry);
        }
      });
    }
  };
  // END PUBLIC FUNCTIONS

  // CONFIGURE OBJECT
  this.group = options.group;
  this.level = options.level; // parsed to number in get_group_config_clone and before validation
  this.destgroups = options.destgroups;
  // END CONFIGURE OBJECT
};

/**
 * Checks if a logger configuration is valid or not
 * @param  {[type]} config_to_validate The configuration to validate
 * @return {boolean}                   True if the configuration is valid, false otherwise
 */
var validate_config = function(config_to_validate) {
  if(!config_to_validate.group) {
    console.log('Invalid configuration: no valid group in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(typeof(config_to_validate.level) !== 'number') {
    console.log('Invalid configuration: level is not parsed to a number in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(config_to_validate.level < 0 || config_to_validate.level > parse_level('off')) {
    console.log('Invalid configuration: level is too big or too small in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(!config_to_validate.destgroups.length) {
    console.log('Invalid configuration: destgroups is not a non-empty array in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else {
    return true;
  }
};

/**
 * Returns the most specific conf in log_config for a given group. Throws an error if no valid configuration is found.
 *
 * E.g. if a logger config defines group configurations for js, js/foo and js/foo/bar/baz then the logger for
 * js/foo/bar/baz will use the config for js/foo/bar/baz, the logger for js/foo/bar will use the config for js/foo
 * and attempting to get the group configuration for baz will throw an error.
 *
 * @param  {String} group_string The group string to specify, /-delimited and assumed to begin with js
 * @param  {Object} log_config   The logger configuration
 * @return {Object}              A conf object with fields group, level and destgroups
 */
var get_group_config_clone = function(group_string, log_config) {
  var group_elements = group_string.split('/');
  var current_level = log_config;
  for(var i = 0; i < group_elements.length; ++i) {
    var current_group = group_elements[i];
    if(current_level[current_group]) {
      current_level = current_level[current_group];
    }
    else {
      break;
    }
  }
  if(!current_level.conf) {
    throw new Error('Group config for "' + group_string + '" is ' + current_level.conf);
  }
  else {
    var result = _.extend({ group: group_string }, current_level.conf); // TODO confirm deepy copy not needed
    result.level = parse_level(result.level);
    return result;
  }
};

/**
 * Configures log4js according to its specific config, then creates and caches a log4js logger to log4js_loggers as a
 * potential destination for each destgroup
 * @param  {string} log4js_config Path to the logger config JSON file
 */
var configure_log4js = function(log4js_config) {
  _.each(log4js_config.appenders, function(appender_obj) { // ensure log4js does not modify log messages
    appender_obj.layout = {};
    appender_obj.layout.type = 'messagePassThrough';
  });
  log4js.configure(log4js_config); // configure log4js appenders
  _.each(log4js_config.appenders, function(appender_obj) { // create log4js loggers - one for each appender
    if(!log4js_loggers[appender_obj.category]) {
      log4js_loggers[appender_obj.category] = log4js.getLogger(appender_obj.category);
      log4js_loggers[appender_obj.category].setLevel('TRACE'); // filter logging in this module, not in log4js
    }
  });
};

var raw_logger_config;
var cached_loggers = {};

module.exports = {
  /**
   * Returns a logger object. Also loads logger and caches log4js loggers (one or more per destgroup category) as nec.
   * @param  {string} group Get the logger for this group
   */
  get_logger: function(group) {
    if(!raw_logger_config) {
      raw_logger_config = require('app/util/logger/logger_config.json');
      configure_log4j(raw_logger_config.log4js); // configure log4js, create appenders in cache for each appender
    }
    if(cached_loggers[group]) {
      return cached_loggers[group];
    }
    else {
      var logger_specific_config = get_group_config_clone(group, raw_logger_config.loggers);
      if(!validate_config(logger_specific_config)) {
        throw new Error('Invalid configuration for logger: ' + JSON.stringify(logger_specific_config));
      }
      else {
        cached_loggers[group] = new Logger(logger_specific_config);
        return cached_loggers[group];
      }
    }
  },

  /**
   * Get a plain log4js logger from the cache, possibly associated with one or more log4js appenders
   * @param  {[type]} logger_name The logger name to get
   * @return {[type]}             The log4js logger of this name
   */
  get_log4js_logger: function(logger_name) {
    if(!log4js_loggers[logger_name]) {
      throw new Error('Unknown log4js logger_name - ensure appender with this category is in logger_config.json');
    }
    else {
      return log4js_loggers[logger_name];
    }
  }
};
<commit_msg>server/app/util/logger/index - typo bug fix<commit_after>'use strict';

var log4js = require('log4js');
var _ = require('underscore');
var moment = require('moment');

/**
 * Map of log4js appender category names (also as specified in logger conf configurations) to log4js logger objects
 */
var log4js_loggers = {};

/**
 * Converts level string from logger config into numerical value. Valid strings (case insensitive) are: all. trace,
 * debug, info, warn, error, fatal
 */
var parse_level = function(level_string) {
  var levels = {
    all:   0,
    trace: 100,
    debug: 200,
    info:  300,
    warn:  400,
    error: 500,
    fatal: 600,
    off:   9999
  };
  if(levels[level_string.toLowerCase()] !== undefined) { return levels[level_string.toLowerCase()]; }
  else { throw new Error('Unknown log level string: ' + level_string); }
};

/**
 * A logger object, associated with a group and with some set of appenders attached to it
 * @param {Object} options Required and possible properties are String:level Array[String]:appenders, String:group
 */
var Logger = function(options) {
  // PRIVATE FUNCTIONS
  var that = this;

  var assemble_log_entry = function(message, level_string) {
    var timestamp = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
    return timestamp + ' ' + level_string.toUpperCase() + ' [' + that.group + '] ' + message;
  };
  // END PRIVATE FUNCTIONS

  // PUBLIC PRIVILEGED FUNCTIONS
  // TODO: Refactor so that these methos are on the prototype, not the instance
  this.is_trace_enabled = function() {
    return this.level <= parse_level('trace');
  };

  this.trace = function(message) {
    if(this.is_trace_enabled()) {
      var log_entry = assemble_log_entry(message, 'trace');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].trace(log_entry);
        }
      });
    }
  };

  this.is_debug_enabled = function() {
    return this.level <= parse_level('debug');
  };

  this.debug = function(message) {
    if(this.is_debug_enabled()) {
      var log_entry = assemble_log_entry(message, 'debug');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].debug(log_entry);
        }
      });
    }
  };

  this.is_info_enabled = function() {
    return this.level <= parse_level('info');
  };

  this.info = function(message) {
    if(this.is_info_enabled()) {
      var log_entry = assemble_log_entry(message, 'info');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].info(log_entry);
        }
      });
    }
  };

  this.is_warn_enabled = function() {
    return this.level <= parse_level('warn');
  };

  this.warn = function(message) {
    if(this.is_warn_enabled()) {
      var log_entry = assemble_log_entry(message, 'warn');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].warn(log_entry);
        }
      });
    }
  };

  this.is_error_enabled = function() {
    return this.level <= parse_level('error');
  };

  this.error = function(message) {
    if(this.is_error_enabled()) {
      var log_entry = assemble_log_entry(message, 'error');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].error(log_entry);
        }
      });
    }
  };

  this.is_fatal_enabled = function() {
    return this.level <= parse_level('fatal');
  };

  this.fatal = function(message) {
    if(this.is_fatal_enabled()) {
      var log_entry = assemble_log_entry(message, 'fatal');
      _.each(this.destgroups, function(destgroup_name) {
        if(!log4js_loggers[destgroup_name]) {
          throw new Error('Unknown appender "' + destgroup_name + '" requested by ' + this.group);
        }
        else {
          log4js_loggers[destgroup_name].fatal(log_entry);
        }
      });
    }
  };
  // END PUBLIC FUNCTIONS

  // CONFIGURE OBJECT
  this.group = options.group;
  this.level = options.level; // parsed to number in get_group_config_clone and before validation
  this.destgroups = options.destgroups;
  // END CONFIGURE OBJECT
};

/**
 * Checks if a logger configuration is valid or not
 * @param  {[type]} config_to_validate The configuration to validate
 * @return {boolean}                   True if the configuration is valid, false otherwise
 */
var validate_config = function(config_to_validate) {
  if(!config_to_validate.group) {
    console.log('Invalid configuration: no valid group in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(typeof(config_to_validate.level) !== 'number') {
    console.log('Invalid configuration: level is not parsed to a number in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(config_to_validate.level < 0 || config_to_validate.level > parse_level('off')) {
    console.log('Invalid configuration: level is too big or too small in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else if(!config_to_validate.destgroups.length) {
    console.log('Invalid configuration: destgroups is not a non-empty array in ' + JSON.stringify(config_to_validate));
    return false;
  }
  else {
    return true;
  }
};

/**
 * Returns the most specific conf in log_config for a given group. Throws an error if no valid configuration is found.
 *
 * E.g. if a logger config defines group configurations for js, js/foo and js/foo/bar/baz then the logger for
 * js/foo/bar/baz will use the config for js/foo/bar/baz, the logger for js/foo/bar will use the config for js/foo
 * and attempting to get the group configuration for baz will throw an error.
 *
 * @param  {String} group_string The group string to specify, /-delimited and assumed to begin with js
 * @param  {Object} log_config   The logger configuration
 * @return {Object}              A conf object with fields group, level and destgroups
 */
var get_group_config_clone = function(group_string, log_config) {
  var group_elements = group_string.split('/');
  var current_level = log_config;
  for(var i = 0; i < group_elements.length; ++i) {
    var current_group = group_elements[i];
    if(current_level[current_group]) {
      current_level = current_level[current_group];
    }
    else {
      break;
    }
  }
  if(!current_level.conf) {
    throw new Error('Group config for "' + group_string + '" is ' + current_level.conf);
  }
  else {
    var result = _.extend({ group: group_string }, current_level.conf); // TODO confirm deepy copy not needed
    result.level = parse_level(result.level);
    return result;
  }
};

/**
 * Configures log4js according to its specific config, then creates and caches a log4js logger to log4js_loggers as a
 * potential destination for each destgroup
 * @param  {string} log4js_config Path to the logger config JSON file
 */
var configure_log4js = function(log4js_config) {
  _.each(log4js_config.appenders, function(appender_obj) { // ensure log4js does not modify log messages
    appender_obj.layout = {};
    appender_obj.layout.type = 'messagePassThrough';
  });
  log4js.configure(log4js_config); // configure log4js appenders
  _.each(log4js_config.appenders, function(appender_obj) { // create log4js loggers - one for each appender
    if(!log4js_loggers[appender_obj.category]) {
      log4js_loggers[appender_obj.category] = log4js.getLogger(appender_obj.category);
      log4js_loggers[appender_obj.category].setLevel('TRACE'); // filter logging in this module, not in log4js
    }
  });
};

var raw_logger_config;
var cached_loggers = {};

module.exports = {
  /**
   * Returns a logger object. Also loads logger and caches log4js loggers (one or more per destgroup category) as nec.
   * @param  {string} group Get the logger for this group
   */
  get_logger: function(group) {
    if(!raw_logger_config) {
      raw_logger_config = require('app/util/logger/logger_config.json');
      configure_log4js(raw_logger_config.log4js); // configure log4js, create appenders in cache for each appender
    }
    if(cached_loggers[group]) {
      return cached_loggers[group];
    }
    else {
      var logger_specific_config = get_group_config_clone(group, raw_logger_config.loggers);
      if(!validate_config(logger_specific_config)) {
        throw new Error('Invalid configuration for logger: ' + JSON.stringify(logger_specific_config));
      }
      else {
        cached_loggers[group] = new Logger(logger_specific_config);
        return cached_loggers[group];
      }
    }
  },

  /**
   * Get a plain log4js logger from the cache, possibly associated with one or more log4js appenders
   * @param  {[type]} logger_name The logger name to get
   * @return {[type]}             The log4js logger of this name
   */
  get_log4js_logger: function(logger_name) {
    if(!log4js_loggers[logger_name]) {
      throw new Error('Unknown log4js logger_name - ensure appender with this category is in logger_config.json');
    }
    else {
      return log4js_loggers[logger_name];
    }
  }
};
<|endoftext|>