mongaku/mongaku

View on GitHub
src/schemas/types/Dimension.js

Summary

Maintainability
F
6 days
Test Coverage
const pd = require("parse-dimensions");

const numRange = bucket =>
    bucket.to
        ? `${bucket.from || 0}-${bucket.to}${bucket.unit}`
        : `${bucket.from}${bucket.unit}+`;

const Dimension = function(options) {
    this.options = options;
    this.defaultUnit = options.defaultUnit || "mm";
    this.defaultSearchUnit = options.defaultSearchUnit || "cm";

    /*
    name
    type
    searchName
    defaultUnit
    defaultSearchUnit
    title(i18n)
    widthTitle(i18n)
    heightTitle(i18n)
    placeholder(i18n)
    */
};

Dimension.prototype = {
    searchName() {
        return this.options.searchName || this.options.name;
    },

    value(query) {
        const heightMin = query[`${this.searchName()}.heightMin`];
        const heightMax = query[`${this.searchName()}.heightMax`];
        const widthMin = query[`${this.searchName()}.widthMin`];
        const widthMax = query[`${this.searchName()}.widthMax`];
        const unit =
            query[`${this.searchName()}.unit`] ||
            this.defaultSearchUnit ||
            this.defaultUnit;

        if (heightMin || heightMax || widthMin || widthMax) {
            return {heightMin, heightMax, widthMin, widthMax, unit};
        }
    },

    searchTitle(value, i18n) {
        const expectedUnit = this.defaultSearchUnit || this.defaultUnit;
        const unit = value.unit || expectedUnit;
        const title = [];

        if (value.heightMin || value.heightMax) {
            const name = this.options.heightTitle(i18n);
            const range = numRange({
                from: pd.convertNumber(value.heightMin, unit, expectedUnit),
                to: pd.convertNumber(value.heightMax, unit, expectedUnit),
                unit,
            });
            title.push(`${name}: ${range}`);
        }

        if (value.widthMin || value.widthMax) {
            const name = this.options.widthTitle(i18n);
            const range = numRange({
                from: pd.convertNumber(value.widthMin, unit, expectedUnit),
                to: pd.convertNumber(value.widthMax, unit, expectedUnit),
                unit,
            });
            title.push(`${name}: ${range}`);
        }

        return title.join(", ");
    },

    fields(value) {
        const ret = {};
        const defaultUnit = this.defaultSearchUnit || this.defaultUnit;

        if (value.height) {
            ret[`${this.searchName()}.heightMin`] = value.height;
            ret[`${this.searchName()}.heightMax`] = value.height;
        }

        if (value.heightMin) {
            ret[`${this.searchName()}.heightMin`] = value.heightMin;
        }

        if (value.heightMax) {
            ret[`${this.searchName()}.heightMax`] = value.heightMax;
        }

        if (value.width) {
            ret[`${this.searchName()}.widthMin`] = value.width;
            ret[`${this.searchName()}.widthMax`] = value.width;
        }

        if (value.widthMin) {
            ret[`${this.searchName()}.widthMin`] = value.widthMin;
        }

        if (value.widthMax) {
            ret[`${this.searchName()}.widthMax`] = value.widthMax;
        }

        if (value.unit && value.unit !== defaultUnit) {
            ret[`${this.searchName()}.unit`] = value.unit;
        }

        return ret;
    },

    breadcrumbs(value, i18n) {
        const breadcrumbs = [];

        if (value.heightMin || value.heightMax) {
            const title = this.options.heightTitle(i18n);
            const range = numRange({
                from: value.heightMin,
                to: value.heightMax,
                unit: value.unit,
            });

            const newValue = Object.assign({}, value);
            delete newValue.heightMin;
            delete newValue.heightMax;

            breadcrumbs.push({
                name: `${title}: ${range}`,
                value: newValue,
            });
        }

        if (value.widthMin || value.widthMax) {
            const title = this.options.widthTitle(i18n);
            const range = numRange({
                from: value.widthMin,
                to: value.widthMax,
                unit: value.unit,
            });

            const newValue = Object.assign({}, value);
            delete newValue.widthMin;
            delete newValue.widthMax;

            breadcrumbs.push({
                name: `${title}: ${range}`,
                value: newValue,
            });
        }

        return breadcrumbs;
    },

    filter(value) {
        const filters = [];

        if (value.widthMin) {
            filters.push({
                range: {
                    [`${this.options.name}.width`]: {
                        gte: pd.convertNumber(
                            parseFloat(value.widthMin),
                            value.unit,
                            this.defaultUnit,
                        ),
                    },
                },
            });
        }

        if (value.widthMax) {
            filters.push({
                range: {
                    [`${this.options.name}.width`]: {
                        lte: pd.convertNumber(
                            parseFloat(value.widthMax),
                            value.unit,
                            this.defaultUnit,
                        ),
                    },
                },
            });
        }

        if (value.heightMin) {
            filters.push({
                range: {
                    [`${this.options.name}.height`]: {
                        gte: pd.convertNumber(
                            parseFloat(value.heightMin),
                            value.unit,
                            this.defaultUnit,
                        ),
                    },
                },
            });
        }

        if (value.heightMax) {
            filters.push({
                range: {
                    [`${this.options.name}.height`]: {
                        lte: pd.convertNumber(
                            parseFloat(value.heightMax),
                            value.unit,
                            this.defaultUnit,
                        ),
                    },
                },
            });
        }

        return filters;
    },

    facet() {
        const defaultUnit = this.defaultUnit;
        const unit = this.defaultSearchUnit || this.defaultUnit;

        const formatFacetBucket = bucket => {
            const text = numRange({
                from: pd.convertNumber(bucket.from, defaultUnit, unit),
                to: pd.convertNumber(bucket.to, defaultUnit, unit),
                unit,
            });

            return {
                text,
                count: bucket.doc_count,
                url: {
                    [this.options.name]: {
                        widthMin: bucket.from,
                        widthMax: bucket.to,
                        unit,
                    },
                },
            };
        };

        const ranges = [
            {to: 99},
            {from: 100, to: 199},
            {from: 200, to: 299},
            {from: 300, to: 399},
            {from: 400, to: 499},
            {from: 500, to: 599},
            {from: 600, to: 699},
            {from: 700, to: 799},
            {from: 800, to: 899},
            {from: 900, to: 999},
            {from: 1000, to: 1249},
            {from: 1250, to: 1599},
            {from: 1500, to: 1749},
            {from: 1750, to: 1999},
            {from: 2000},
        ];

        return {
            [`${this.options.name}.width`]: {
                title: i18n => this.options.widthTitle(i18n),

                facet: () => ({
                    range: {
                        field: `${this.options.name}.width`,
                        ranges,
                    },
                }),

                formatBuckets: buckets => buckets.map(formatFacetBucket),
            },

            [`${this.options.name}.height`]: {
                title: i18n => this.options.heightTitle(i18n),

                facet: () => ({
                    range: {
                        field: `${this.options.name}.height`,
                        ranges,
                    },
                }),

                formatBuckets: buckets => buckets.map(formatFacetBucket),
            },
        };
    },

    schema(Schema) {
        const DimensionSchema = new Schema({
            // An ID for the dimension, computed from the original +
            // width/height properties before validation.
            _id: String,

            // The source string from which the dimensions were generated
            original: String,

            // The width/height/depth of the object (stored in millimeters)
            width: {type: Number, es_indexed: true},
            height: {type: Number, es_indexed: true},
            depth: {type: Number, es_indexed: true},

            // A label for the dimensions (e.g. "with frame")
            label: String,

            // The unit for the dimensions (defaults to millimeters)
            unit: {type: String, es_indexed: true},
        });

        DimensionSchema.methods = {
            toJSON() {
                const obj = this.toObject();
                delete obj.original;
                return obj;
            },
        };

        // Dynamically generate the _id attribute
        DimensionSchema.pre("validate", function(next) {
            this._id =
                this.original || [this.width, this.height, this.unit].join(",");
            next();
        });

        return {
            type: [DimensionSchema],
            convert: obj =>
                typeof obj === "string"
                    ? pd.parseDimension(obj, true, this.defaultUnit)
                    : pd.convertDimension(obj, this.defaultUnit),
            validateArray: val => (val.width || val.height) && val.unit,
            validationMsg: i18n =>
                i18n.gettext(
                    "Dimensions must have a " +
                        "unit specified and at least a width or height.",
                ),
        };
    },
};

module.exports = Dimension;