<commit_before>/**
 * @file imprecisedate.js
 * @brief Display and manage an imprecise date format of type of descriptor
 * @author Frédéric SCHERMA (INRA UMR1095)
 * @date 2017-04-11
 * @copyright Copyright (c) 2017 INRA/CIRAD
 * @license MIT (see LICENSE file)
 * @details
 */

var DescriptorFormatType = require('./descriptorformattype');
var Marionette = require('backbone.marionette');

var ImpreciseDateType = function () {
    DescriptorFormatType.call(this);

    this.name = "imprecise_date";
    this.group = "single";
};

_.extend(ImpreciseDateType.prototype, DescriptorFormatType.prototype, {
    create: function (format, parent, readOnly) {
        readOnly || (readOnly = false);

        if (readOnly) {
            var input = this._createStdInput(parent, "glyphicon-calendar");

            this.parent = parent;
            this.readOnly = true;
            this.el = input;
        } else {
            var group = $('

');
            var input = $('<input class="form-control" width="100%">');
            var glyph = $('').css('cursor', 'pointer');

            group.append(input);
            group.append(glyph);

            parent.append(group);

            /// Partial datetime ///

            var accuracy = null;
            var current_date = null;

            var el = input.datetimepicker({
                locale: session.language,
                showClose: true,
                format: "L",
                showTodayButton: true,
                showClear: true,
                viewMode: 'years',
                minDate: '0001-01-01',
                maxDate: '9999-12-31',
                icons: {
                    close: 'OK'
                }
            });

            var ok_button = null;
            var widget_picker = null;

            el.on("dp.show", function (e) {
                el.data('DateTimePicker').format("L");
                el.data('DateTimePicker').viewMode("years");

                // fix position when parent has overflow-y defined
                // https://github.com/Eonasdan/bootstrap-datetimepicker/issues/790
                var dateTimePicker = $('body').find('.bootstrap-datetimepicker-widget:last'),
                    position = dateTimePicker.offset(),
                    parent = dateTimePicker.parent(),
                    parentPos = parent.offset(),
                    width = dateTimePicker.width(),
                    parentWid = parent.width();

                // move dateTimePicker to the exact same place it was but attached to body
                dateTimePicker.appendTo('body');
                dateTimePicker.css({
                    position: 'absolute',
                    top: position.top,
                    bottom: 'auto',
                    left: position.left,
                    right: 'auto',
                    'z-index': 10001
                });

                // if dateTimePicker is wider than the thing it is attached to then move it so the centers line up
                if (parentPos.left + parentWid < position.left + width) {
                    var newLeft = parentPos.left;
                    newLeft += parentWid / 2;
                    newLeft -= width / 2;
                    dateTimePicker.css({left: newLeft});
                }

                widget_picker = dateTimePicker;

                var button = $('.OK').html(gt.gettext("Undefined")).css('display', 'none');
                button.on('click', function (e) {
                    if (accuracy === "YYYY") {
                        el.data('DateTimePicker').format("YYYY");
                        el.data('DateTimePicker').date(current_date);
                    } else if (accuracy === "M") {
                        el.data('DateTimePicker').format("MM/YYYY");
                        el.data('DateTimePicker').date(current_date);
                    } else {
                        el.data('DateTimePicker').format("L");
                    }
                });

                ok_button = button;

                var picker_view = dateTimePicker.find('.picker-switch');
                picker_view.on('click', function () {
                    if (dateTimePicker.find('.datepicker-days').css('display') === 'block') {
                        ok_button.css('display', 'block');
                    } else {
                        ok_button.css('display', 'none');
                    }
                });
            });

            el.on("dp.update", function (e) {
                accuracy = e.change;
                current_date = e.viewDate;

                if (widget_picker.find('.datepicker-days').css('display') === 'block') {
                    ok_button.css('display', 'block');
                } else if (widget_picker.find('.datepicker-months').css('display') === 'block') {
                    ok_button.css('display', 'block');
                } else {
                    ok_button.css('display', 'none');
                }
            });

            this.parent = parent;
            this.el = input;
        }
    },

    destroy: function () {
        if (this.el && this.parent) {
            if (this.readOnly) {
                this.el.parent().remove();
            } else {
                this.el.data('DateTimePicker').destroy();
                this.el.parent().remove();
            }
        }
    },

    enable: function () {
        if (this.el) {
            this.el.data('DateTimePicker').enable();
        }
    },

    disable: function () {
        if (this.el) {
            this.el.data('DateTimePicker').disable();
        }
    },

    set: function (format, definesValues, defaultValues, descriptorTypeGroup, descriptorTypeId) {
        if (!this.el || !this.parent) {
            return;
        }

        definesValues = this.isValueDefined(definesValues, defaultValues);
        var date = null;

        if (this.readOnly) {
            if (definesValues) {
                // defaultValues
                date = moment();
                if (defaultValues[0] && !defaultValues[1] && !defaultValues[2]) {
                    // format: YYYY
                    date.year(defaultValues[0]);
                    this.el.val(date.format("YYYY"));
                } else if (defaultValues[0] && defaultValues[1] && !defaultValues[2]) {
                    // format: MM/YYYY
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1]-1);
                    this.el.val(date.format("MM/YYYY"));
                } else {
                    // format: L (ex: 20/05/1992)
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1]-1);
                    date.date(defaultValues[2]);
                    this.el.val(date.format("L"));
                }
            }
        } else {
            if (definesValues) {
                date = moment();
                if (defaultValues[0] && !defaultValues[1] && !defaultValues[2]) {
                    // format: YYYY
                    date.year(defaultValues[0]);
                    this.el.data('DateTimePicker').format("YYYY");
                } else if (defaultValues[0] && defaultValues[1] && !defaultValues[2]) {
                    // format: MM/YYYY
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1]-1);
                    this.el.data('DateTimePicker').format("MM/YYYY");
                } else {
                    // format: L (ex: 20/05/1992)
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1]-1);
                    date.date(defaultValues[2]);
                    this.el.data('DateTimePicker').format("L");
                }
                this.el.data('DateTimePicker').date(date);
            }
        }
    },

    values: function () {
        if (this.el && this.parent) {
            if (this.readOnly) {
                var value = this.el.val();
                return value !== "" ? value : null;
            } else {
                var date = this.el.data('DateTimePicker').date();
                var format = this.el.data('DateTimePicker').format();
                if (date != null) {
                    switch (format) {
                        case "YYYY":
                            console.log([parseInt(date.format("YYYY")), 0, 0]);
                            return [parseInt(date.format("YYYY")), 0, 0];

                        case "MM/YYYY":
                            console.log([parseInt(date.format("YYYY")), parseInt(date.format("MM")), 0]);
                            return [parseInt(date.format("YYYY")), parseInt(date.format("MM")), 0];

                        case "L":
                            return [parseInt(date.format("YYYY")), parseInt(date.format("MM")), parseInt(date.format("DD"))];
                    }
                } else {
                    return null;
                }
            }
        }

        return null;
    },

    checkCondition: function (condition, values) {
        switch (condition) {
            case 0:
                return this.values() == null;
            case 1:
                return this.values() != null;
            case 2:
                return this.values() === values;
            case 3:
                return this.values() !== values;
            default:
                return false;
        }
    },

    bindConditionListener: function (listeners, condition, values) {
        if (this.el && this.parent && !this.readOnly) {
            if (!this.bound) {
                this.el.parent().on('dp.change', $.proxy(this.onValueChanged, this));
                this.bound = true;
            }

            this.conditionType = condition;
            this.conditionValues = values;
            this.listeners = listeners || [];
        }
    },

    onValueChanged: function (e) {
        var display = this.checkCondition(this.conditionType, this.conditionValues);

        // show or hide the parent element
        if (display) {
            for (var i = 0; i < this.listeners.length; ++i) {
                this.listeners[i].parent.parent().show(true);
            }
        } else {
            for (var i = 0; i < this.listeners.length; ++i) {
                this.listeners[i].parent.parent().hide(true);
            }
        }
    }
});

ImpreciseDateType.DescriptorTypeDetailsView = Marionette.ItemView.extend({
    className: 'descriptor-type-details-format',
    template: "

",

    initialize: function () {
        this.listenTo(this.model, 'change', this.render, this);
    },

    getFormat: function () {
        return {}
    }
});

ImpreciseDateType.format = function (value) {
    if (value[0] !== 0 && value[1] !== 0 && value[2] !== 0) {
        return moment(value[0] + value[1] + value[2]).format("L");
    } else if (value[0] !== 0 && value[1] !== 0) {
        return moment(value[0] + value[1]).format("MM/YYYY");
    } else if (value[0] !== 0) {
        return moment(value[0] + "-01").format("YYYY");
    } else {
        return ""
    }
};

module.exports = ImpreciseDateType;

<commit_msg>Revert: Merge branch 'master' of /mnt/nas-crouel/sercom/Informatique/repositories/collgate<commit_after>/**
 * @file imprecisedate.js
 * @brief Display and manage an imprecise date format of type of descriptor
 * @author Frédéric SCHERMA (INRA UMR1095)
 * @date 2017-04-11
 * @copyright Copyright (c) 2017 INRA/CIRAD
 * @license MIT (see LICENSE file)
 * @details
 */

var DescriptorFormatType = require('./descriptorformattype');
var Marionette = require('backbone.marionette');

var ImpreciseDateType = function () {
    DescriptorFormatType.call(this);

    this.name = "imprecise_date";
    this.group = "single";
    var display_el = null;
};

_.extend(ImpreciseDateType.prototype, DescriptorFormatType.prototype, {
    create: function (format, parent, readOnly) {
        readOnly || (readOnly = false);

        if (readOnly) {
            var input = this._createStdInput(parent, "glyphicon-calendar");

            this.parent = parent;
            this.readOnly = true;
            this.el = input;
        } else {
            var group = $('

');
            var input = $('<input class="form-control" width="100%">').hide(); // element to hide
            var display_input = $('<input class="form-control" width="100%" pattern="[0-9\/\s]+">');
            var glyph = $('').css('cursor', 'pointer');

            group.append(display_input);
            group.append(input);
            group.append(glyph);

            parent.append(group);

            /// Partial datetime ///
            var accuracy = null;
            var current_date = null;

            var el = input.datetimepicker({
                locale: session.language,
                showClose: true,
                format: "L",
                showTodayButton: true,
                showClear: true,
                viewMode: 'years',
                minDate: '0001-01-01',
                maxDate: '9999-12-31',
                icons: {
                    close: 'OK'
                }
            });

            var lastFocusedElement = null;

            display_input.mousedown(function(e) {
                // To prevent case when element already had focus
                 if (lastFocusedElement === e.target) {
                    el.data('DateTimePicker').show();
                 }
            }).focus(function(e){
                el.data('DateTimePicker').show();
                lastFocusedElement = e.target;
            }).blur(function(e) {
                el.data('DateTimePicker').hide();
                lastFocusedElement = null;
            });

            display_input.on('keydown', function (e) {
                // Allow: backspace, delete, tab, escape, dash, enter, slash and .
                if ($.inArray(e.keyCode, [46, 8, 9, 27, 13, 109, 110, 111, 190]) !== -1 ||
                    // Allow: Ctrl+A, Command+A
                    (e.keyCode === 65 && (e.ctrlKey === true || e.metaKey === true)) ||
                    // Allow: home, end, left, right, down, up
                    (e.keyCode >= 35 && e.keyCode <= 40)) {
                    // let it happen, don't do anything
                    return;
                }
                // Ensure that it is a number and stop the keypress
                if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
                    e.preventDefault();
                }
            });

            display_input.on('change', function (e) {
                // define date accuracy
                var temp = (display_input.val().match(/[\/\-.]/g) || []).length;
                if (temp === 2) {
                    accuracy = null;
                    current_date = moment(display_input.val(), "L");
                    el.data('DateTimePicker').format("L");
                    el.data('DateTimePicker').date(current_date);
                    display_input.val(el.data('DateTimePicker').date().format("L"));
                }
                else if (temp === 1) {
                    accuracy = 'M';
                    current_date = moment(display_input.val(), "MM/YYYY");
                    el.data('DateTimePicker').format("MM/YYYY");
                    el.data('DateTimePicker').date(current_date);
                    display_input.val(el.data('DateTimePicker').date().format("MM/YYYY"));
                }
                else {
                    accuracy = 'YYYY';
                    current_date = moment(display_input.val(), "YYYY");
                    el.data('DateTimePicker').format("YYYY");
                    el.data('DateTimePicker').date(current_date);
                    display_input.val(el.data('DateTimePicker').date().format("YYYY"));
                }
                el.data('DateTimePicker').hide();
            });

            el.on('dp.hide', function(e) {
                display_input.trigger('change');
            });

            el.on("dp.change", function(e){
                if (el.data('DateTimePicker').format() === "YYYY") {
                    display_input.val(el.data('DateTimePicker').date().format("YYYY"));
                }
                else if (el.data('DateTimePicker').format() === "MM/YYYY") {
                    display_input.val(el.data('DateTimePicker').date().format("MM/YYYY"));
                }
                else {
                    display_input.val(el.data('DateTimePicker').date().format("L"));
                }
            });

            var ok_button = null;
            var widget_picker = null;

            el.on("dp.show", function (e) {
                el.data('DateTimePicker').format("L");
                el.data('DateTimePicker').viewMode("years");

                // fix position when parent has overflow-y defined
                // https://github.com/Eonasdan/bootstrap-datetimepicker/issues/790
                var dateTimePicker = $('body').find('.bootstrap-datetimepicker-widget:last'),
                    position = dateTimePicker.offset(),
                    parent = dateTimePicker.parent(),
                    parentPos = parent.offset(),
                    width = dateTimePicker.width(),
                    parentWid = parent.width();

                // move dateTimePicker to the exact same place it was but attached to body
                dateTimePicker.appendTo('body');
                dateTimePicker.css({
                    position: 'absolute',
                    top: position.top,
                    bottom: 'auto',
                    left: position.left,
                    right: 'auto',
                    'z-index': 10001
                });

                // if dateTimePicker is wider than the thing it is attached to then move it so the centers line up
                if (parentPos.left + parentWid < position.left + width) {
                    var newLeft = parentPos.left;
                    newLeft += parentWid / 2;
                    newLeft -= width / 2;
                    dateTimePicker.css({left: newLeft});
                }

                widget_picker = dateTimePicker;

                var button = $('.OK').html(gt.gettext("Undefined")).css('display', 'none');
                button.on('click', function (e) {
                    if (accuracy === "YYYY") {
                        el.data('DateTimePicker').format("YYYY");
                        el.data('DateTimePicker').date(current_date);
                        display_input.val(el.data('DateTimePicker').date().format("YYYY"));
                    } else if (accuracy === "M") {
                        el.data('DateTimePicker').format("MM/YYYY");
                        el.data('DateTimePicker').date(current_date);
                        display_input.val(el.data('DateTimePicker').date().format("MM/YYYY"));
                    }
                });

                ok_button = button;

                var picker_view = dateTimePicker.find('.picker-switch');
                picker_view.on('click', function () {
                    if (dateTimePicker.find('.datepicker-days').css('display') === 'block') {
                        ok_button.css('display', 'block');
                    } else {
                        ok_button.css('display', 'none');
                    }
                });
            });

            el.on("dp.update", function (e) {
                accuracy = e.change;
                current_date = e.viewDate;

                if (widget_picker.find('.datepicker-days').css('display') === 'block') {
                    ok_button.css('display', 'block');
                } else if (widget_picker.find('.datepicker-months').css('display') === 'block') {
                    ok_button.css('display', 'block');
                } else {
                    ok_button.css('display', 'none');
                }
            });

            this.parent = parent;
            this.el = input;
            this.display_el = display_input;
        }
    },

    destroy: function () {
        if (this.el && this.parent) {
            if (this.readOnly) {
                this.el.parent().remove();
            } else {
                this.el.data('DateTimePicker').destroy();
                this.el.parent().remove();
            }
        }
    },

    enable: function () {
        if (this.el) {
            this.el.data('DateTimePicker').enable();
        }
    },

    disable: function () {
        if (this.el) {
            this.el.data('DateTimePicker').disable();
        }
    },

    set: function (format, definesValues, defaultValues, descriptorTypeGroup, descriptorTypeId) {
        if (!this.el || !this.parent) {
            return;
        }

        definesValues = this.isValueDefined(definesValues, defaultValues);
        var date = null;

        if (this.readOnly) {
            if (definesValues) {
                // defaultValues
                date = moment();
                date.locale(session.language);
                if (defaultValues[0] && !defaultValues[1] && !defaultValues[2]) {
                    // format: YYYY
                    date.year(defaultValues[0]);
                    this.el.val(date.format("YYYY"));
                } else if (defaultValues[0] && defaultValues[1] && !defaultValues[2]) {
                    // format: MM/YYYY
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1] - 1);
                    this.el.val(date.format("MM/YYYY"));
                } else {
                    // format: L (ex: 20/05/1992)
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1] - 1);
                    date.date(defaultValues[2]);
                    this.el.val(date.format("L"));
                }
            }
        } else {
            if (definesValues) {
                date = moment();
                if (defaultValues[0] && !defaultValues[1] && !defaultValues[2]) {
                    // format: YYYY
                    date.year(defaultValues[0]);
                    this.el.data('DateTimePicker').format("YYYY");
                    this.display_el.val(date.format("YYYY"));
                } else if (defaultValues[0] && defaultValues[1] && !defaultValues[2]) {
                    // format: MM/YYYY
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1] - 1);
                    this.el.data('DateTimePicker').format("MM/YYYY");
                    this.display_el.val(date.format("MM/YYYY"));
                } else {
                    // format: L (ex: 20/05/1992)
                    date.year(defaultValues[0]);
                    date.month(defaultValues[1] - 1);
                    date.date(defaultValues[2]);
                    this.el.data('DateTimePicker').format("L");
                    this.display_el.val(date.format("L"));
                }
                this.el.data('DateTimePicker').date(date);
            }
        }
    },

    values: function () {
        if (this.el && this.parent) {
            if (this.readOnly) {
                var value = this.el.val();
                return value !== "" ? value : null;
            } else {
                var date = this.el.data('DateTimePicker').date();
                var format = this.el.data('DateTimePicker').format();
                if (date != null) {
                    switch (format) {
                        case "YYYY":
                            return [parseInt(date.format("YYYY")), 0, 0];

                        case "MM/YYYY":
                            return [parseInt(date.format("YYYY")), parseInt(date.format("MM")), 0];

                        case "L":
                            return [parseInt(date.format("YYYY")), parseInt(date.format("MM")), parseInt(date.format("DD"))];
                    }
                } else {
                    return null;
                }
            }
        }

        return null;
    },

    checkCondition: function (condition, values) {
        switch (condition) {
            case 0:
                return this.values() == null;
            case 1:
                return this.values() != null;
            case 2:
                return this.values() === values;
            case 3:
                return this.values() !== values;
            default:
                return false;
        }
    },

    bindConditionListener: function (listeners, condition, values) {
        if (this.el && this.parent && !this.readOnly) {
            if (!this.bound) {
                this.el.parent().on('dp.change', $.proxy(this.onValueChanged, this));
                this.bound = true;
            }

            this.conditionType = condition;
            this.conditionValues = values;
            this.listeners = listeners || [];
        }
    },

    onValueChanged: function (e) {
        var display = this.checkCondition(this.conditionType, this.conditionValues);

        // show or hide the parent element
        if (display) {
            for (var i = 0; i < this.listeners.length; ++i) {
                this.listeners[i].parent.parent().show(true);
            }
        } else {
            for (var i = 0; i < this.listeners.length; ++i) {
                this.listeners[i].parent.parent().hide(true);
            }
        }
    }
});

ImpreciseDateType.DescriptorTypeDetailsView = Marionette.ItemView.extend({
    className: 'descriptor-type-details-format',
    template: "

",

    initialize: function () {
        this.listenTo(this.model, 'change', this.render, this);
    },

    getFormat: function () {
        return {}
    }
});

ImpreciseDateType.format = function (value) {
    if (value[0] !== 0 && value[1] !== 0 && value[2] !== 0) {
        return moment().locale(session.language).year(value[0]).month(value[1]-1).date(value[2]).format("L");
    } else if (value[0] !== 0 && value[1] !== 0) {
        return moment().locale(session.language).year(value[0]).month(value[1]-1).format("MM/YYYY");
    } else if (value[0] !== 0) {
        return moment().locale(session.language).year(value[0]).format("YYYY");
    } else {
        return ""
    }
};

module.exports = ImpreciseDateType;

<|endoftext|>


<commit_before>function (user, context, callback) {
  const CHANGEAPI_TIMEOUT = 14000; // milliseconds
  const MIN_LOGINS_TO_ASSUME_EXISTENCE_IN_CIS = 6;
  const PERSONAPI_BEARER_TOKEN_REFRESH_AGE = 64800; // 18 hours
  const PERSONAPI_TIMEOUT = 5000;  // milliseconds
  const PUBLISHER_NAME = 'access_provider';
  const WHITELISTED_CONNECTIONS = ['email', 'firefoxaccounts', 'github', 'google-oauth2'];

  // if we don't have the configuration variables we need, bail
  // note that this requires the "PersonAPI - Auth0" application configured with the following scopes:
  // classification: public, display: none, display: public, write
  if (!configuration.changeapi_auth0_private_key ||
      !configuration.changeapi_null_profile ||
      !configuration.changeapi_url ||
      !configuration.personapi_client_id ||
      !configuration.personapi_client_secret ||
      !configuration.personapi_url) {
    console.log('Error: Unable to find PersonAPI and/or ChangeAPI configuration');
    return callback(null, user, context);
  }

  // the `personapi_url` and `personapi_audience` variables have historically been configured incorrectly,
  // so we don't continue onward unless they have been fixed, e.g.:
  // personapi_audience: api.dev.sso.allizom.org / api.sso.mozilla.com
  // personapi_url: https://person.api.dev.sso.allizom.org / https://person.api.sso.mozilla.com
  if (configuration.personapi_audience.includes('https') || configuration.personapi_url.includes('/v')) {
    console.log('Error: PersonAPI configured incorrectly');
    return callback(null, user, context);
  }

  // We can only provision users that have certain connection strategies
  if (!WHITELISTED_CONNECTIONS.includes(context.connectionStrategy)) {
    return callback(null, user, context);
  }

  // if you're explicitly flagged as existing in CIS, then we don't need to continue onward
  if (user.user_metadata && user.user_metadata.existsInCIS) {
    return callback(null, user, context);
  }

  // if you've logged in several times before, we're going to assume you exist, to speed
  // this function along. Once `existsInCIS` is reliable, we can probably remove this check
  if (context.stats && context.stats.loginsCount && context.stats.loginsCount >= MIN_LOGINS_TO_ASSUME_EXISTENCE_IN_CIS) {
    return callback(null, user, context);
  }

  // we'll need the node-fetch module, to add support for timeouts
  const fetch = require('node-fetch@2.6.0');

  // we also need to decode the private key from base64 into a PEM format that `jsonwebtoken` understands
  // generated with:
  // import base64
  // import boto3
  // base64.b64encode(boto3.client('ssm').get_parameter(Name='/iam/cis/development/keys/access_provider', WithDecryption=True)['Parameter']['Value'].encode('ascii')).decode('ascii')
  const privateKey = Buffer.from(configuration.changeapi_auth0_private_key, 'base64');

  const getBearerToken = async () => {
    // if we have the bearer token stored, we don't need to fetch it again
    if (global.personapi_bearer_token &&
        global.personapi_bearer_token_creation_time &&
        Date.now() - global.personapi_bearer_token_creation_time < PERSONAPI_BEARER_TOKEN_REFRESH_AGE) {
      return global.personapi_bearer_token;
    }

    console.log('Retrieving bearer token to create new user in CIS');

    const options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      timeout: PERSONAPI_TIMEOUT,
      body: JSON.stringify({
        audience: configuration.personapi_audience,
        client_id: configuration.personapi_client_id,
        client_secret: configuration.personapi_client_secret,
        grant_type: 'client_credentials',
      })
    };

    try {
      const response = await fetch(configuration.personapi_oauth_url, options);
      const data = await response.json();

      // store the bearer token in the global object, so it's not constantly retrieved
      global.personapi_bearer_token = data.access_token;
      global.personapi_bearer_token_creation_time = Date.now();

      console.log(`Successfully retrieved bearer token from Auth0`);
      return global.personapi_bearer_token;
    } catch (error) {
      throw Error(`Unable to retrieve bearer token from Auth0: ${error.message}`);
    }
  };

  const createPersonProfile = async () => {
    console.log(`Generating CIS profile for ${user.user_id}`);

    let now = new Date();
    now = now.toISOString();

    // load the user skeleton, as generated by:
    // base64.b64encode(json.dumps(requests.get('https://raw.githubusercontent.com/mozilla-iam/cis/master/python-modules/cis_profile/cis_profile/data/user_profile_null.json').json(), separators=(',', ':')).encode('ascii'))
    const profile = JSON.parse(Buffer.from(configuration.changeapi_null_profile, 'base64'));

    // update attributes in the skeleton
    // normally we shouldn't need to change anything but the values, but this is manually doing it because
    // I have no idea if the skeleton will ever change underneath me
    profile.active.metadata.last_modified = now;
    profile.active.signature.publisher.name = PUBLISHER_NAME;
    profile.active.value = true;

    profile.first_name.metadata.display = 'private';
    profile.first_name.metadata.last_modified = now;
    profile.first_name.signature.publisher.name = PUBLISHER_NAME;
    profile.first_name.value = user.given_name ? user.given_name : ' ';

    profile.last_name.metadata.display = 'private';
    profile.last_name.metadata.last_modified = now;
    profile.last_name.signature.publisher.name = PUBLISHER_NAME;
    profile.last_name.value = user.family_name ? user.family_name : ' ';

    profile.primary_email.metadata.last_modified = now;
    profile.primary_email.signature.publisher.name = PUBLISHER_NAME;
    profile.primary_email.value = user.email;

    profile.user_id.metadata.last_modified = now;
    profile.user_id.signature.publisher.name = PUBLISHER_NAME;
    profile.user_id.value = user.user_id;

    // now we need to go and update the identities values; this is based on the logic here:
    // https://github.com/mozilla-iam/cis/blob/master/python-modules/cis_publisher/cis_publisher/auth0.py
    // which may or may not be correct, I dunno
    for (let i = 0; i < user.identities.length; i++) {
      const identity = user.identities[i];
      // ignore a provider if it's not whitelisted
      if (!WHITELISTED_CONNECTIONS.includes(identity.connection)) {
        continue;
      }

      // store the login_method for the first identity
      console.log(`oh hey i is ${i}`);
      if (i === 0) {
        console.log(`setting login method for ${i}`);
        profile.login_method.metadata.last_modified = now;
        profile.login_method.signature.publisher.name = PUBLISHER_NAME;
        profile.login_method.value = identity.connection;
      }

      if (identity.provider === 'github') {
        profile.identities.github_id_v3.metadata.display = 'private';
        profile.identities.github_id_v3.metadata.last_modified = now;
        profile.identities.github_id_v3.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.github_id_v3.value = identity.user_id;

        if (user.nickname) {
          profile.usernames.metadata.display = 'private';
          profile.usernames.signature.publisher.name = PUBLISHER_NAME;
          profile.usernames.value = {"HACK#GITHUB": user.nickname};
        }

        if (identity.profileData) {
          // I could never seem to find a user that met this condition
          profile.identities.github_id_v4.metadata.display = 'private';
          profile.identities.github_id_v4.metadata.last_modified = now;
          profile.identities.github_id_v4.signature.publisher.name = PUBLISHER_NAME;
          profile.identities.github_id_v4.value = identity.profileData.node_id;

          profile.identities.github_primary_email.metadata.display = 'private';
          profile.identities.github_primary_email.metadata.last_modified = now;
          profile.identities.github_primary_email.metadata.verified = identity.profileData.email_verified === true ? true : false;
          profile.identities.github_primary_email.signature.publisher.name = PUBLISHER_NAME;
          profile.identities.github_primary_email.value = identity.profileData.email;
        }
      }

      else if (identity.provider === 'google-oauth2') {
        profile.identities.google_oauth2_id.metadata.display = 'private';
        profile.identities.google_oauth2_id.metadata.last_modified = now;
        profile.identities.google_oauth2_id.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.google_oauth2_id.value = identity.user_id;

        profile.identities.google_primary_email.metadata.display = 'private';
        profile.identities.google_primary_email.metadata.last_modified = now;
        profile.identities.google_primary_email.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.google_primary_email.value = user.email;
      }

      else if (identity.connection === 'firefoxaccounts' && identity.provider === 'oauth2') {
        profile.identities.firefox_accounts_id.metadata.display = 'private';
        profile.identities.firefox_accounts_id.metadata.last_modified = now;
        profile.identities.firefox_accounts_id.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.firefox_accounts_id.value = identity.user_id;

        profile.identities.firefox_accounts_primary_email.metadata.display = 'private';
        profile.identities.firefox_accounts_primary_email.metadata.last_modified = now;
        profile.identities.firefox_accounts_primary_email.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.firefox_accounts_primary_email.value = user.email;
      }
    }

    // now, we need to sign every field and subfield
    signAll(profile);

    // turn this on only for debugging
    // console.log(`Generated profile:\n${JSON.stringify(profile, null, 2)}`);
    return profile;
  };

  const getPersonProfile = async () => {
    const bearer = await getBearerToken();
    const options = {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${bearer}`,
      },
      timeout: PERSONAPI_TIMEOUT,
    };
    const url = `${configuration.personapi_url}/v2/user/user_id/${encodeURI(user.user_id)}?active=any`;

    console.log(`Fetching person profile of ${user.user_id}`);

    const response = await fetch(url, options);

    return response.json();
  };

  const postProfile = async profile => {
    console.log(`Posting profile for ${user.user_id} to ChangeAPI`);

    const bearer = await getBearerToken();
    const options = {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${bearer}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(profile),
      timeout: CHANGEAPI_TIMEOUT,
    };
    const url = `${configuration.changeapi_url}/v2/user?user_id=${encodeURI(user.user_id)}`;

    // POST the profile to the ChangeAPI
    const response = await fetch(url, options);

    return response.json();
  };

  const signAll = profile => {
    // now we need to sign every attribute in the profile, if we're allowed to
    // otherwise, we will descend one level deep for sub attributes
    // this is super hacky and ugly and I hate it
    Object.values(profile).forEach(attr => {  // works because profile is mutable
      if (attr.constructor === Object && attr.signature) {
        signAttribute(attr);
      } else if (attr.constructor === Object && !attr.signature) {
        signAll(attr);  // descend deeper
      }
    });
  };

  const signAttribute = attr => {
    const jwt = require('jsonwebtoken');

    // we can only sign attributes that access_provider (e.g. auth0) is allowed to sign
    // we also ignore things that don't have a pre-existing signature field
    // we also don't need to sign null attributes
    if (!attr.signature || attr.signature.publisher.name !== PUBLISHER_NAME || attr.value === null || attr.values === null) {
      return attr;
    }

    // we need to delete the existing signature and generate it anew
    delete(attr.signature);

    attr.signature = {
      additional: [{
        alg: 'RS256',
        name: null,
        typ: 'JWS',
        value: '',
      }],
      publisher: {
        alg: 'RS256',
        name: PUBLISHER_NAME,
        typ: 'JWS',
        value: jwt.sign(attr, privateKey, { algorithm: 'RS256', noTimestamp: true }),
      }
    };

    return attr;
  };

  // if we get this far, we need to 1) call the PersonAPI to check for existance, and 2) if the user
  // doesn't exist, call the ChangeAPI to create them
  getPersonProfile()
    .then(profile => {
      if (Object.keys(profile).length !== 0) {
        throw Error(`Profile for ${user.user_id} already exists in PersonAPI`);
      } else {
        return createPersonProfile();
      }
    })
    .then(profile => postProfile(profile))
    .then(response => {
      if (response.constructor === Object && response.status_code === 200) {
        console.log(`Successfully created profile for ${user.user_id} in ChangeAPI`);
        return callback(null, user, context);
      } else {
        throw Error(`Unable to create profile for ${user.user_id} in ChangeAPI`);
      }
    })
    .catch(error => {
      console.log(`Error: ${error.message}`);
      return callback(null, user, context);
    });
}
<commit_msg>Add code to set `existsInCIS` and remove min login check<commit_after>function (user, context, callback) {
  const CHANGEAPI_TIMEOUT = 14000; // milliseconds
  const PERSONAPI_BEARER_TOKEN_REFRESH_AGE = 64800; // 18 hours
  const PERSONAPI_TIMEOUT = 5000;  // milliseconds
  const PUBLISHER_NAME = 'access_provider';
  const WHITELISTED_CONNECTIONS = ['email', 'firefoxaccounts', 'github', 'google-oauth2'];

  // if we don't have the configuration variables we need, bail
  // note that this requires the "PersonAPI - Auth0" application configured with the following scopes:
  // classification: public, display: none, display: public, write
  if (!configuration.changeapi_auth0_private_key ||
      !configuration.changeapi_null_profile ||
      !configuration.changeapi_url ||
      !configuration.personapi_client_id ||
      !configuration.personapi_client_secret ||
      !configuration.personapi_url) {
    console.log('Error: Unable to find PersonAPI and/or ChangeAPI configuration');
    return callback(null, user, context);
  }

  // the `personapi_url` and `personapi_audience` variables have historically been configured incorrectly,
  // so we don't continue onward unless they have been fixed, e.g.:
  // personapi_audience: api.dev.sso.allizom.org / api.sso.mozilla.com
  // personapi_url: https://person.api.dev.sso.allizom.org / https://person.api.sso.mozilla.com
  if (configuration.personapi_audience.includes('https') || configuration.personapi_url.includes('/v')) {
    console.log('Error: PersonAPI configured incorrectly');
    return callback(null, user, context);
  }

  // We can only provision users that have certain connection strategies
  if (!WHITELISTED_CONNECTIONS.includes(context.connectionStrategy)) {
    return callback(null, user, context);
  }

  // if you're explicitly flagged as existing in CIS, then we don't need to continue onward
  if (user.user_metadata && user.user_metadata.existsInCIS) {
    return callback(null, user, context);
  }

  // we'll need the node-fetch module, to add support for timeouts
  const fetch = require('node-fetch@2.6.0');

  // we also need to decode the private key from base64 into a PEM format that `jsonwebtoken` understands
  // generated with:
  // import base64
  // import boto3
  // base64.b64encode(boto3.client('ssm').get_parameter(Name='/iam/cis/development/keys/access_provider', WithDecryption=True)['Parameter']['Value'].encode('ascii')).decode('ascii')
  const privateKey = Buffer.from(configuration.changeapi_auth0_private_key, 'base64');

  const getBearerToken = async () => {
    // if we have the bearer token stored, we don't need to fetch it again
    if (global.personapi_bearer_token &&
        global.personapi_bearer_token_creation_time &&
        Date.now() - global.personapi_bearer_token_creation_time < PERSONAPI_BEARER_TOKEN_REFRESH_AGE) {
      return global.personapi_bearer_token;
    }

    console.log('Retrieving bearer token to create new user in CIS');

    const options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      timeout: PERSONAPI_TIMEOUT,
      body: JSON.stringify({
        audience: configuration.personapi_audience,
        client_id: configuration.personapi_client_id,
        client_secret: configuration.personapi_client_secret,
        grant_type: 'client_credentials',
      })
    };

    try {
      const response = await fetch(configuration.personapi_oauth_url, options);
      const data = await response.json();

      // store the bearer token in the global object, so it's not constantly retrieved
      global.personapi_bearer_token = data.access_token;
      global.personapi_bearer_token_creation_time = Date.now();

      console.log(`Successfully retrieved bearer token from Auth0`);
      return global.personapi_bearer_token;
    } catch (error) {
      throw Error(`Unable to retrieve bearer token from Auth0: ${error.message}`);
    }
  };

  const createPersonProfile = async () => {
    console.log(`Generating CIS profile for ${user.user_id}`);

    let now = new Date();
    now = now.toISOString();

    // load the user skeleton, as generated by:
    // base64.b64encode(json.dumps(requests.get('https://raw.githubusercontent.com/mozilla-iam/cis/master/python-modules/cis_profile/cis_profile/data/user_profile_null.json').json(), separators=(',', ':')).encode('ascii'))
    const profile = JSON.parse(Buffer.from(configuration.changeapi_null_profile, 'base64'));

    // update attributes in the skeleton
    // normally we shouldn't need to change anything but the values, but this is manually doing it because
    // I have no idea if the skeleton will ever change underneath me
    profile.active.metadata.last_modified = now;
    profile.active.signature.publisher.name = PUBLISHER_NAME;
    profile.active.value = true;

    profile.first_name.metadata.display = 'private';
    profile.first_name.metadata.last_modified = now;
    profile.first_name.signature.publisher.name = PUBLISHER_NAME;
    profile.first_name.value = user.given_name ? user.given_name : ' ';

    profile.last_name.metadata.display = 'private';
    profile.last_name.metadata.last_modified = now;
    profile.last_name.signature.publisher.name = PUBLISHER_NAME;
    profile.last_name.value = user.family_name ? user.family_name : ' ';

    profile.primary_email.metadata.last_modified = now;
    profile.primary_email.signature.publisher.name = PUBLISHER_NAME;
    profile.primary_email.value = user.email;

    profile.user_id.metadata.last_modified = now;
    profile.user_id.signature.publisher.name = PUBLISHER_NAME;
    profile.user_id.value = user.user_id;

    // now we need to go and update the identities values; this is based on the logic here:
    // https://github.com/mozilla-iam/cis/blob/master/python-modules/cis_publisher/cis_publisher/auth0.py
    // which may or may not be correct, I dunno
    for (let i = 0; i < user.identities.length; i++) {
      const identity = user.identities[i];
      // ignore a provider if it's not whitelisted
      if (!WHITELISTED_CONNECTIONS.includes(identity.connection)) {
        continue;
      }

      // store the login_method for the first identity
      if (i === 0) {
        profile.login_method.metadata.last_modified = now;
        profile.login_method.signature.publisher.name = PUBLISHER_NAME;
        profile.login_method.value = identity.connection;
      }

      if (identity.provider === 'github') {
        profile.identities.github_id_v3.metadata.display = 'private';
        profile.identities.github_id_v3.metadata.last_modified = now;
        profile.identities.github_id_v3.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.github_id_v3.value = identity.user_id;

        if (user.nickname) {
          profile.usernames.metadata.display = 'private';
          profile.usernames.signature.publisher.name = PUBLISHER_NAME;
          profile.usernames.value = {"HACK#GITHUB": user.nickname};
        }

        if (identity.profileData) {
          // I could never seem to find a user that met this condition
          profile.identities.github_id_v4.metadata.display = 'private';
          profile.identities.github_id_v4.metadata.last_modified = now;
          profile.identities.github_id_v4.signature.publisher.name = PUBLISHER_NAME;
          profile.identities.github_id_v4.value = identity.profileData.node_id;

          profile.identities.github_primary_email.metadata.display = 'private';
          profile.identities.github_primary_email.metadata.last_modified = now;
          profile.identities.github_primary_email.metadata.verified = identity.profileData.email_verified === true ? true : false;
          profile.identities.github_primary_email.signature.publisher.name = PUBLISHER_NAME;
          profile.identities.github_primary_email.value = identity.profileData.email;
        }
      }

      else if (identity.provider === 'google-oauth2') {
        profile.identities.google_oauth2_id.metadata.display = 'private';
        profile.identities.google_oauth2_id.metadata.last_modified = now;
        profile.identities.google_oauth2_id.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.google_oauth2_id.value = identity.user_id;

        profile.identities.google_primary_email.metadata.display = 'private';
        profile.identities.google_primary_email.metadata.last_modified = now;
        profile.identities.google_primary_email.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.google_primary_email.value = user.email;
      }

      else if (identity.connection === 'firefoxaccounts' && identity.provider === 'oauth2') {
        profile.identities.firefox_accounts_id.metadata.display = 'private';
        profile.identities.firefox_accounts_id.metadata.last_modified = now;
        profile.identities.firefox_accounts_id.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.firefox_accounts_id.value = identity.user_id;

        profile.identities.firefox_accounts_primary_email.metadata.display = 'private';
        profile.identities.firefox_accounts_primary_email.metadata.last_modified = now;
        profile.identities.firefox_accounts_primary_email.signature.publisher.name = PUBLISHER_NAME;
        profile.identities.firefox_accounts_primary_email.value = user.email;
      }
    }

    // now, we need to sign every field and subfield
    signAll(profile);

    // turn this on only for debugging
    // console.log(`Generated profile:\n${JSON.stringify(profile, null, 2)}`);
    return profile;
  };

  const getPersonProfile = async () => {
    const bearer = await getBearerToken();
    const options = {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${bearer}`,
      },
      timeout: PERSONAPI_TIMEOUT,
    };
    const url = `${configuration.personapi_url}/v2/user/user_id/${encodeURI(user.user_id)}?active=any`;

    console.log(`Fetching person profile of ${user.user_id}`);

    const response = await fetch(url, options);

    return response.json();
  };

  const postProfile = async profile => {
    console.log(`Posting profile for ${user.user_id} to ChangeAPI`);

    const bearer = await getBearerToken();
    const options = {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${bearer}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(profile),
      timeout: CHANGEAPI_TIMEOUT,
    };
    const url = `${configuration.changeapi_url}/v2/user?user_id=${encodeURI(user.user_id)}`;

    // POST the profile to the ChangeAPI
    const response = await fetch(url, options);

    return response.json();
  };

  const signAll = profile => {
    // now we need to sign every attribute in the profile, if we're allowed to
    // otherwise, we will descend one level deep for sub attributes
    // this is super hacky and ugly and I hate it
    Object.values(profile).forEach(attr => {  // works because profile is mutable
      if (attr.constructor === Object && attr.signature) {
        signAttribute(attr);
      } else if (attr.constructor === Object && !attr.signature) {
        signAll(attr);  // descend deeper
      }
    });
  };

  const signAttribute = attr => {
    const jwt = require('jsonwebtoken');

    // we can only sign attributes that access_provider (e.g. auth0) is allowed to sign
    // we also ignore things that don't have a pre-existing signature field
    // we also don't need to sign null attributes
    if (!attr.signature || attr.signature.publisher.name !== PUBLISHER_NAME || attr.value === null || attr.values === null) {
      return attr;
    }

    // we need to delete the existing signature and generate it anew
    delete(attr.signature);

    attr.signature = {
      additional: [{
        alg: 'RS256',
        name: null,
        typ: 'JWS',
        value: '',
      }],
      publisher: {
        alg: 'RS256',
        name: PUBLISHER_NAME,
        typ: 'JWS',
        value: jwt.sign(attr, privateKey, { algorithm: 'RS256', noTimestamp: true }),
      }
    };

    return attr;
  };

  const setExistsInCIS = (exists = true) => {
    // update user metadata to store them existing
    user.user_metadata = user.user_metadata || {};
    user.user_metadata.existsInCIS = exists;

    auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
      .then(() => {
        console.log(`Updated user metadata on ${user.user_id} to set existsInCIS`);
        return exists;
      })
      .catch(() => {
        throw Error(`Unable to set existsInCIS on ${user.user_id}`);
      });
  };

  // if we get this far, we need to 1) call the PersonAPI to check for existance, and 2) if the user
  // doesn't exist, call the ChangeAPI to create them
  getPersonProfile()
    .then(profile => {
      if (Object.keys(profile).length !== 0) {
        setExistsInCIS();
        throw Error(`Profile for ${user.user_id} already exists in PersonAPI`);
      } else {
        return createPersonProfile();
      }
    })
    .then(profile => postProfile(profile))
    .then(response => {
      if (response.constructor === Object && response.status_code === 200) {
        console.log(`Successfully created profile for ${user.user_id} in ChangeAPI`);

        // set their profile as existing in CIS
        setExistsInCIS();

        return callback(null, user, context);
      } else {
        throw Error(`Unable to create profile for ${user.user_id} in ChangeAPI`);
      }
    })
    .catch(error => {
      console.log(`Error: ${error.message}`);
      return callback(null, user, context);
    });
}
<|endoftext|>


<commit_before>/*
 Copyright 2013 SimplifyOps Inc, <http://simplifyops.com>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

function Report(data) {
    var self = this;
    self.dateCompleted = ko.observable();
    self.dateStarted = ko.observable();
    self.execution = ko.observable();
    self.executionHref = ko.observable();
    self.jobId = ko.observable();
    self.executionId = ko.observable();
    self.reportId = ko.observable();
    self.title = ko.observable();
    self.jobAverageDuration = ko.observable(0);
    self.duration = ko.observable(0);

    self.durationSimple = ko.computed(function () {
        return MomentUtil.formatDurationSimple(self.duration());
    });
    self.durationHumanize = ko.computed(function () {
        return MomentUtil.formatDurationHumanize(self.duration());
    });
    self.startTimeFormat = function (format) {
        return MomentUtil.formatTime(self.dateStarted(), format);
    };
    self.endTimeSimple = ko.computed(function () {
        return MomentUtil.formatTimeSimple(self.dateCompleted());
    });

    self.jobPercentage = ko.computed(function () {
        if (self.jobAverageDuration() > 0) {
            return 100 * (self.duration() / self.jobAverageDuration());
        } else {
            return -1;
        }
    });
    self.jobPercentageFixed = ko.computed(function () {
        var pct = self.jobPercentage();
        if (pct >= 0) {
            return pct.toFixed(0)
        } else {
            return '0';
        }
    });
    self.jobOverrunDuration = ko.computed(function () {
        var jobAverageDuration = self.jobAverageDuration();
        var execDuration = self.duration();
        if (jobAverageDuration > 0 && execDuration > jobAverageDuration) {
            return MomentUtil.formatDurationSimple(execDuration - jobAverageDuration);
        } else {
            return '';
        }
    });
    self.endTimeFormat = function (format) {
        var value = self.dateCompleted();
        return MomentUtil.formatTime(value, format);
    };
    self.nodeFailCount = ko.computed(function () {
        var ncount = ko.utils.unwrapObservable(self.node);
        if(ncount){
            var ns = ncount.split('/');
            if (ns.length == 3) {
                return parseInt(ns[1]);
            }
        }
    });

    self.nodeSucceedCount = ko.computed(function () {
        var ncount = ko.utils.unwrapObservable(self.node);
        if(ncount){

        var ns = ncount.split('/');
        if (ns.length == 3) {
            return parseInt(ns[0]);
        }
        }
    });
    ko.mapping.fromJS(data, {}, self);
}
function History(ajaxHistoryLink) {
    var self = this;
    self.ajaxHistoryLink = ajaxHistoryLink;
    self.reports = ko.observableArray([]);
    self.nowrunning = ko.observableArray([]);
    self.showReports=ko.observable(false);
    self.nowRunningEnabled=ko.observable(true);
    self.href = ko.observable();
    self.selected = ko.observable(false);
    self.max = ko.observable(20);
    self.total = ko.observable(0);
    self.offset = ko.observable(0);
    self.params = ko.observable();
    self.reloadInterval=ko.observable(0);
    self.results=ko.computed(function(){
       if(self.showReports()){
           return self.reports()
       } else{
           return self.nowrunning()
       }
    });
    self.count = ko.computed(function () {
        return self.reports().length + self.offset() * self.max();
    });
    self.pageCount=ko.computed(function(){
        return totalPageCount(self.max(),self.total());
    });
    self.pages=ko.computed(function(){
        var total = self.total();
        var offset = self.offset();
        var max = self.max();
        var href = self.href();
        if (total < 1 || !href) {
            return '';
        }
        var pages = [];
        //remove offset/max params from href
        href=href.replace(/[&\?](offset|max)=\d+/ig,'');

        foreachPage(offset, max, total, {maxSteps:10}, function (pg) {
            var a;
            var url = _genUrl(href, {offset: pg.offset, max: pg.max});
            var label = pg.prevPage ? 'Previous' : pg.nextPage ? 'Next' : pg.skipped? '…' : (pg.page);
            pages.push(ko.utils.extend({url:url,label:label},pg));
        });

        return pages;
    });
    self.visitPage=function(page){
        loadHistoryLink(self,self.ajaxHistoryLink,page.url);
    };
}

var binding = {
    'reports': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function (options) {
            return new Report(options.data);
        }
    },
    'nowrunning': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function (options) {
            return new Report(jQuery.extend({execution: options.data},options.data));
        }
    }
};
function loadHistoryLink(history, ajaxBaseUrl, href,reload) {
    var params = href.substring(href.indexOf('?'));
    var url = ajaxBaseUrl + params;

    var handleResult;
    var load=function(){
        history.href(href);
        jQuery.getJSON(url, handleResult);
    }
    handleResult= function (data) {
        history.selected(true);
        ko.mapping.fromJS(Object.extend(data, { params: params }), binding, history);
        setTimeout(function(){
            if (reload && history.href() == href) {
                load();
            }
        }, reload * 1000);
    };
    load();
}

function setupActivityLinks(id, history, ajaxHistoryLink, ajaxNowrunningLink) {
    jQuery('#' + id + ' a.activity_link').click(function (e) {
        e.preventDefault();
        var me = jQuery(this)[0];
        jQuery('#' + id + ' .activity_links > li').removeClass('active');
        jQuery(me.parentNode).addClass('active');
        history.showReports(true);
        loadHistoryLink(history, ajaxHistoryLink, me.getAttribute('href'),jQuery(this).data('auto-refresh'));
    });
    jQuery('#' + id + ' a.running_link').click(function (e) {
        e.preventDefault();
        var me = jQuery(this)[0];
        if(history.nowRunningEnabled()){
            jQuery('#' + id + ' .activity_links > li').removeClass('active');
            jQuery(me.parentNode).addClass('active');
            history.showReports(false);

            loadHistoryLink(history, ajaxNowrunningLink, me.getAttribute('href'), jQuery(this).data('auto-refresh'));
        }
    });
}
<commit_msg>Activity tabs load properly with url parameters<commit_after>/*
 Copyright 2013 SimplifyOps Inc, <http://simplifyops.com>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

function Report(data) {
    var self = this;
    self.dateCompleted = ko.observable();
    self.dateStarted = ko.observable();
    self.execution = ko.observable();
    self.executionHref = ko.observable();
    self.jobId = ko.observable();
    self.executionId = ko.observable();
    self.reportId = ko.observable();
    self.title = ko.observable();
    self.jobAverageDuration = ko.observable(0);
    self.duration = ko.observable(0);

    self.durationSimple = ko.computed(function () {
        return MomentUtil.formatDurationSimple(self.duration());
    });
    self.durationHumanize = ko.computed(function () {
        return MomentUtil.formatDurationHumanize(self.duration());
    });
    self.startTimeFormat = function (format) {
        return MomentUtil.formatTime(self.dateStarted(), format);
    };
    self.endTimeSimple = ko.computed(function () {
        return MomentUtil.formatTimeSimple(self.dateCompleted());
    });

    self.jobPercentage = ko.computed(function () {
        if (self.jobAverageDuration() > 0) {
            return 100 * (self.duration() / self.jobAverageDuration());
        } else {
            return -1;
        }
    });
    self.jobPercentageFixed = ko.computed(function () {
        var pct = self.jobPercentage();
        if (pct >= 0) {
            return pct.toFixed(0)
        } else {
            return '0';
        }
    });
    self.jobOverrunDuration = ko.computed(function () {
        var jobAverageDuration = self.jobAverageDuration();
        var execDuration = self.duration();
        if (jobAverageDuration > 0 && execDuration > jobAverageDuration) {
            return MomentUtil.formatDurationSimple(execDuration - jobAverageDuration);
        } else {
            return '';
        }
    });
    self.endTimeFormat = function (format) {
        var value = self.dateCompleted();
        return MomentUtil.formatTime(value, format);
    };
    self.nodeFailCount = ko.computed(function () {
        var ncount = ko.utils.unwrapObservable(self.node);
        if(ncount){
            var ns = ncount.split('/');
            if (ns.length == 3) {
                return parseInt(ns[1]);
            }
        }
    });

    self.nodeSucceedCount = ko.computed(function () {
        var ncount = ko.utils.unwrapObservable(self.node);
        if(ncount){

        var ns = ncount.split('/');
        if (ns.length == 3) {
            return parseInt(ns[0]);
        }
        }
    });
    ko.mapping.fromJS(data, {}, self);
}
function History(ajaxHistoryLink) {
    var self = this;
    self.ajaxHistoryLink = ajaxHistoryLink;
    self.reports = ko.observableArray([]);
    self.nowrunning = ko.observableArray([]);
    self.showReports=ko.observable(false);
    self.nowRunningEnabled=ko.observable(true);
    self.href = ko.observable();
    self.selected = ko.observable(false);
    self.max = ko.observable(20);
    self.total = ko.observable(0);
    self.offset = ko.observable(0);
    self.params = ko.observable();
    self.reloadInterval=ko.observable(0);
    self.results=ko.computed(function(){
       if(self.showReports()){
           return self.reports()
       } else{
           return self.nowrunning()
       }
    });
    self.count = ko.computed(function () {
        return self.reports().length + self.offset() * self.max();
    });
    self.pageCount=ko.computed(function(){
        return totalPageCount(self.max(),self.total());
    });
    self.pages=ko.computed(function(){
        var total = self.total();
        var offset = self.offset();
        var max = self.max();
        var href = self.href();
        if (total < 1 || !href) {
            return '';
        }
        var pages = [];
        //remove offset/max params from href
        href=href.replace(/[&\?](offset|max)=\d+/ig,'');

        foreachPage(offset, max, total, {maxSteps:10}, function (pg) {
            var a;
            var url = _genUrl(href, {offset: pg.offset, max: pg.max});
            var label = pg.prevPage ? 'Previous' : pg.nextPage ? 'Next' : pg.skipped? '…' : (pg.page);
            pages.push(ko.utils.extend({url:url,label:label},pg));
        });

        return pages;
    });
    self.visitPage=function(page){
        loadHistoryLink(self,self.ajaxHistoryLink,page.url);
    };
}

var binding = {
    'reports': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function (options) {
            return new Report(options.data);
        }
    },
    'nowrunning': {
        key: function (data) {
            return ko.utils.unwrapObservable(data.id);
        },
        create: function (options) {
            return new Report(jQuery.extend({execution: options.data},options.data));
        }
    }
};
function loadHistoryLink(history, ajaxBaseUrl, href,reload) {
    var params = href.substring(href.indexOf('?')+1);
    var url = ajaxBaseUrl.indexOf("?")>0? ajaxBaseUrl+'&' + params : ajaxBaseUrl+'?' + params;

    var handleResult;
    var load=function(){
        history.href(href);
        jQuery.getJSON(url, handleResult);
    }
    handleResult= function (data) {
        history.selected(true);
        ko.mapping.fromJS(Object.extend(data, { params: params }), binding, history);
        setTimeout(function(){
            if (reload && history.href() == href) {
                load();
            }
        }, reload * 1000);
    };
    load();
}

function setupActivityLinks(id, history, ajaxHistoryLink, ajaxNowrunningLink) {
    jQuery('#' + id + ' a.activity_link').click(function (e) {
        e.preventDefault();
        var me = jQuery(this)[0];
        jQuery('#' + id + ' .activity_links > li').removeClass('active');
        jQuery(me.parentNode).addClass('active');
        history.showReports(true);
        loadHistoryLink(history, ajaxHistoryLink, me.getAttribute('href'),jQuery(this).data('auto-refresh'));
    });
    jQuery('#' + id + ' a.running_link').click(function (e) {
        e.preventDefault();
        var me = jQuery(this)[0];
        if(history.nowRunningEnabled()){
            jQuery('#' + id + ' .activity_links > li').removeClass('active');
            jQuery(me.parentNode).addClass('active');
            history.showReports(false);

            loadHistoryLink(history, ajaxNowrunningLink, me.getAttribute('href'), jQuery(this).data('auto-refresh'));
        }
    });
}
<|endoftext|>


<commit_before>export { BoundsArraySet };

import * as ConvBiasActivation from "../ConvBiasActivation.js";
import * as FloatValue from "../../Unpacker/FloatValue.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as Weights from "../../Unpacker/Weights.js";
import { ChannelPartInfo } from  "./Depthwise_ChannelPartInfo.js";

/**
 * The element value bounds set for depthwise convolution-bias-activation.
 *
 */
class BoundsArraySet extends ConvBiasActivation.BoundsArraySet {

  /**
   */
  constructor( inputChannelCount, outputChannelCount ) {
    super( inputChannelCount, outputChannelCount );
  }

  /**
   * Set this.bPassThrough[] according to inChannelPartInfoArray.
   *
   * @param {Depthwise.ChannelPartInfo[]} inChannelPartInfoArray
   *   The input channel range array which describe lower/higher half channels index range.
   */
  set_all_bPassThrough( inChannelPartInfoArray ) {
    for ( let inChannelPartIndex = 0; inChannelPartIndex < inChannelPartInfoArray.length; ++inChannelPartIndex ) {
      let inChannelPartInfo = inChannelPartInfoArray[ inChannelPartIndex ];
      let inChannelBegin = inChannelPartInfo.beginIndex;
      let inChannelEnd = inChannelPartInfo.endIndex;

      let outChannel = inChannelBegin * this.channelMultiplier;
      for ( let inChannel = inChannelBegin; inChannel < inChannelEnd; ++inChannel ) {
        for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub, ++outChannel ) {
          if ( inChannelPartInfo.bPassThrough ) { // For pass-through half channels.
            this.bPassThrough[ outChannel ] = true;

          } else { // Non pass-through half channels.
            this.bPassThrough[ outChannel ] = false;
          }
        }
      }
    }
  }

//!!! ...unfinished... (2022/01/07) What about ( bDepthwise == false )?

//!!! ...unfinished... (2022/01/09)
// Even if ( this.AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ),
// this function should work correctly and BoundsArraySet should result in pass-through input to output.

//!!! (2022/01/10 Remarked) Using two-passes in Depthwise.FiltersArray_BiasesArray.init().
//
//   /**
//    *
//    * @param {ConvBiasActivation.BoundsArraySet} previous_ConvBiasActivation_BoundsArraySet
//    *   The value bounds set of the previous convolution-bias-activation.
//    *
//    * @param {Depthwise.ChannelPartInfo[]} inChannelPartInfoArray
//    *   The input channel range array which describe lower/higher half channels index range.
//    *
//    * @param {number} channelMultiplier
//    *   The channel multiplier of this depthwise operation.
//    *
//    * @param {number} nActivationId
//    *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) of this depthwise convolution.
//    *
//    * @param {boolean} bFilter
//    *   Whether this depthwise convolution has bias operation. If ( bFilter == true ), this is a depthwise convolution.
//    * If ( bFilter == false ), this is an avg/max pooling (i.e. no filters weights).
//    *
//    * @param {boolean} bBias
//    *   Whether this depthwise convolution has bias operation.
//    */
//   set_all_by_inChannelPartInfoArray(
//     previous_ConvBiasActivation_BoundsArraySet, inChannelPartInfoArray,
//     channelMultiplier, nActivationId, bFilter, bBias
//   ) {
//
// //!!! ...unfinished... (2022/01/10)
// // It should be better to use real filter and bias to calculate per channel value bounds. This is especially important for ActivationEscaping.
// // Because activation function inputDomainLinear is not wide, using looser value bounds estimation has higher possibility to lost information.
// //
// // Perhaps, using two-passes in Depthwise.FiltersArray_BiasesArray.init():
// //
// //   - In the 1st pass, extracting real filter and bias value. At the same time, calculating .afterFilter and .afterBias
// //       by these extracted value combined with undoPreviousEscapingScale
// //       (i.e. previous_ConvBiasActivation_BoundsArraySet.activationEscaping_ScaleArraySet.undo.scales[ inChannel ]). Find out
// //       .activationEscaping_ScaleArraySet, .afterActivationEscaping, .afterActivation.
// //
// //   - In the 2nd pass, apply doEscapingScale (i.e. .activationEscaping_ScaleArraySet.do.scales[ outChannel ] )
// //       to filter and bias value (and also .afterFilter and .afterBias).
// //
//
//     // Because they are extracted from Weights which should have been regulated by Weights.Base.ValueBounds.Float32Array_RestrictedClone().
//     //
//     // Q: Why not use the real filter and bias value (i.e. not the supposed bounds range) directly.
//     // A: Their values are unknown when determine value bounds. Even if they are known, their values are pre-scaled (i.e. altered; not original) value.
//     const filtersValueBounds = Weights.Base.ValueBounds;
//     const biasesValueBounds = Weights.Base.ValueBounds;
//
// //!!! (2022/01/11 Remarked) Moved to parent class.
// //    let theActivationFunctionInfo = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );
//
//     // 1. Determine .input
//     //
//     // Note: Even if avg/max pooling, input value bounds is the same as the previous ooutput value bounds
//     this.input.set_all_byBoundsArray( previous_ConvBiasActivation_BoundsArraySet.output );
//
//     // ( inChannelPartIndex == 0 ), lower half channels. (or, all channels)
//     // ( inChannelPartIndex == 1 ), higher half channels.
//     for ( let inChannelPartIndex = 0; inChannelPartIndex < inChannelPartInfoArray.length; ++inChannelPartIndex ) {
//       let inChannelPartInfo = inChannelPartInfoArray[ inChannelPartIndex ];
//       let inChannelBegin = inChannelPartInfo.beginIndex;
//       let inChannelEnd = inChannelPartInfo.endIndex;
//
//       let outChannel = inChannelBegin * channelMultiplier;
//
//       for ( let inChannel = inChannelBegin; inChannel < inChannelEnd; ++inChannel ) {
//
//         let undoPreviousEscapingScale = previous_ConvBiasActivation_BoundsArraySet.activationEscaping_ScaleArraySet.undo.scales[ inChannel ];
//
//         // 2. Determine .afterUndoPreviousActivationEscaping
//         this.afterUndoPreviousActivationEscaping.set_one_byBoundsArray( inChannel, this.input, inChannel );
//         this.afterUndoPreviousActivationEscaping.multiply_one_byN( inChannel, undoPreviousEscapingScale );
//
//         for ( let outChannelSub = 0; outChannelSub < channelMultiplier; ++outChannelSub, ++outChannel ) {
//
//           if ( inChannelPartInfo.bPassThrough ) { // For pass-through half channels.
//             this.bPassThrough[ outChannel ] = true;
//
//             // Do nothing. The value bounds does not change at all because it is just be past through.
//
//           } else { // Non pass-through half channels.
//             this.bPassThrough[ outChannel ] = false;
//
//             // 3. Determine .afterFilter
//             this.afterFilter.set_one_byBoundsArray( outChannel, this.afterUndoPreviousActivationEscaping, inChannel );
//             if ( bFilter ) {
//               this.afterFilter
//                 .multiply_one_byBounds( outChannel, filtersValueBounds )
//                 .multiply_one_byN( outChannel, this.filterSize );
//
//             } else { // ( !bFilter ). No filters array to be extracted. (i.e. avg/max pooling)
//               // Do nothing. The value bounds does not change for avg/max pooling.
//             }
//
//             // 4. Determine .afterBias
//             this.afterBias.set_one_byBoundsArray( outChannel, this.afterFilter, outChannel );
//             if ( bBias ) {
//                 this.afterBias.add_one_byBounds( outChannel, biasesValueBounds ); // Shift the value bounds by the bias bounds.
//
//             } else { // ( !bBias ). No biases array to be extracted.
//               // Do nothing. The value bounds does not change since no bias.
//             }
//           }
//
//         } // outChannelSub, outChannel
//       } // inChannel
//     } // inChannelPartIndex
//
//     // 5. Determine .activationEscaping_ScaleArraySet, .afterActivationEscaping, .afterActivation
//     this.set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_nActivationId( nActivationId );
//   }

}

<commit_msg>Update Depthwise_BoundsArraySet.js<commit_after>export { BoundsArraySet };

import * as ConvBiasActivation from "../ConvBiasActivation.js";
import * as FloatValue from "../../Unpacker/FloatValue.js";
import * as ValueDesc from "../../Unpacker/ValueDesc.js";
import * as Weights from "../../Unpacker/Weights.js";
import { ChannelPartInfo } from  "./Depthwise_ChannelPartInfo.js";

/**
 * The element value bounds set for depthwise convolution-bias-activation.
 *
 */
class BoundsArraySet extends ConvBiasActivation.BoundsArraySet {

  /**
   */
  constructor( inputChannelCount, outputChannelCount ) {
    super( inputChannelCount, outputChannelCount );
  }

  /**
   * Set this.bPassThrough[] according to inChannelPartInfoArray.
   *
   * @param {Depthwise.ChannelPartInfo[]} inChannelPartInfoArray
   *   The input channel range array which describe lower/higher half channels index range.
   */
  set_bPassThrough_all_byChannelPartInfoArray( inChannelPartInfoArray ) {
    for ( let inChannelPartIndex = 0; inChannelPartIndex < inChannelPartInfoArray.length; ++inChannelPartIndex ) {
      let inChannelPartInfo = inChannelPartInfoArray[ inChannelPartIndex ];
      let inChannelBegin = inChannelPartInfo.beginIndex;
      let inChannelEnd = inChannelPartInfo.endIndex;

      let outChannel = inChannelBegin * this.channelMultiplier;
      for ( let inChannel = inChannelBegin; inChannel < inChannelEnd; ++inChannel ) {
        for ( let outChannelSub = 0; outChannelSub < this.channelMultiplier; ++outChannelSub, ++outChannel ) {
          if ( inChannelPartInfo.bPassThrough ) { // For pass-through half channels.
            this.bPassThrough[ outChannel ] = true;

          } else { // Non pass-through half channels.
            this.bPassThrough[ outChannel ] = false;
          }
        }
      }
    }
  }

//!!! ...unfinished... (2022/01/07) What about ( bDepthwise == false )?

//!!! ...unfinished... (2022/01/09)
// Even if ( this.AvgMax_Or_ChannelMultiplier == ValueDesc.AvgMax_Or_ChannelMultiplier.Singleton.Ids.NONE ),
// this function should work correctly and BoundsArraySet should result in pass-through input to output.

//!!! (2022/01/10 Remarked) Using two-passes in Depthwise.FiltersArray_BiasesArray.init().
//
//   /**
//    *
//    * @param {ConvBiasActivation.BoundsArraySet} previous_ConvBiasActivation_BoundsArraySet
//    *   The value bounds set of the previous convolution-bias-activation.
//    *
//    * @param {Depthwise.ChannelPartInfo[]} inChannelPartInfoArray
//    *   The input channel range array which describe lower/higher half channels index range.
//    *
//    * @param {number} channelMultiplier
//    *   The channel multiplier of this depthwise operation.
//    *
//    * @param {number} nActivationId
//    *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) of this depthwise convolution.
//    *
//    * @param {boolean} bFilter
//    *   Whether this depthwise convolution has bias operation. If ( bFilter == true ), this is a depthwise convolution.
//    * If ( bFilter == false ), this is an avg/max pooling (i.e. no filters weights).
//    *
//    * @param {boolean} bBias
//    *   Whether this depthwise convolution has bias operation.
//    */
//   set_all_by_inChannelPartInfoArray(
//     previous_ConvBiasActivation_BoundsArraySet, inChannelPartInfoArray,
//     channelMultiplier, nActivationId, bFilter, bBias
//   ) {
//
// //!!! ...unfinished... (2022/01/10)
// // It should be better to use real filter and bias to calculate per channel value bounds. This is especially important for ActivationEscaping.
// // Because activation function inputDomainLinear is not wide, using looser value bounds estimation has higher possibility to lost information.
// //
// // Perhaps, using two-passes in Depthwise.FiltersArray_BiasesArray.init():
// //
// //   - In the 1st pass, extracting real filter and bias value. At the same time, calculating .afterFilter and .afterBias
// //       by these extracted value combined with undoPreviousEscapingScale
// //       (i.e. previous_ConvBiasActivation_BoundsArraySet.activationEscaping_ScaleArraySet.undo.scales[ inChannel ]). Find out
// //       .activationEscaping_ScaleArraySet, .afterActivationEscaping, .afterActivation.
// //
// //   - In the 2nd pass, apply doEscapingScale (i.e. .activationEscaping_ScaleArraySet.do.scales[ outChannel ] )
// //       to filter and bias value (and also .afterFilter and .afterBias).
// //
//
//     // Because they are extracted from Weights which should have been regulated by Weights.Base.ValueBounds.Float32Array_RestrictedClone().
//     //
//     // Q: Why not use the real filter and bias value (i.e. not the supposed bounds range) directly.
//     // A: Their values are unknown when determine value bounds. Even if they are known, their values are pre-scaled (i.e. altered; not original) value.
//     const filtersValueBounds = Weights.Base.ValueBounds;
//     const biasesValueBounds = Weights.Base.ValueBounds;
//
// //!!! (2022/01/11 Remarked) Moved to parent class.
// //    let theActivationFunctionInfo = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );
//
//     // 1. Determine .input
//     //
//     // Note: Even if avg/max pooling, input value bounds is the same as the previous ooutput value bounds
//     this.input.set_all_byBoundsArray( previous_ConvBiasActivation_BoundsArraySet.output );
//
//     // ( inChannelPartIndex == 0 ), lower half channels. (or, all channels)
//     // ( inChannelPartIndex == 1 ), higher half channels.
//     for ( let inChannelPartIndex = 0; inChannelPartIndex < inChannelPartInfoArray.length; ++inChannelPartIndex ) {
//       let inChannelPartInfo = inChannelPartInfoArray[ inChannelPartIndex ];
//       let inChannelBegin = inChannelPartInfo.beginIndex;
//       let inChannelEnd = inChannelPartInfo.endIndex;
//
//       let outChannel = inChannelBegin * channelMultiplier;
//
//       for ( let inChannel = inChannelBegin; inChannel < inChannelEnd; ++inChannel ) {
//
//         let undoPreviousEscapingScale = previous_ConvBiasActivation_BoundsArraySet.activationEscaping_ScaleArraySet.undo.scales[ inChannel ];
//
//         // 2. Determine .afterUndoPreviousActivationEscaping
//         this.afterUndoPreviousActivationEscaping.set_one_byBoundsArray( inChannel, this.input, inChannel );
//         this.afterUndoPreviousActivationEscaping.multiply_one_byN( inChannel, undoPreviousEscapingScale );
//
//         for ( let outChannelSub = 0; outChannelSub < channelMultiplier; ++outChannelSub, ++outChannel ) {
//
//           if ( inChannelPartInfo.bPassThrough ) { // For pass-through half channels.
//             this.bPassThrough[ outChannel ] = true;
//
//             // Do nothing. The value bounds does not change at all because it is just be past through.
//
//           } else { // Non pass-through half channels.
//             this.bPassThrough[ outChannel ] = false;
//
//             // 3. Determine .afterFilter
//             this.afterFilter.set_one_byBoundsArray( outChannel, this.afterUndoPreviousActivationEscaping, inChannel );
//             if ( bFilter ) {
//               this.afterFilter
//                 .multiply_one_byBounds( outChannel, filtersValueBounds )
//                 .multiply_one_byN( outChannel, this.filterSize );
//
//             } else { // ( !bFilter ). No filters array to be extracted. (i.e. avg/max pooling)
//               // Do nothing. The value bounds does not change for avg/max pooling.
//             }
//
//             // 4. Determine .afterBias
//             this.afterBias.set_one_byBoundsArray( outChannel, this.afterFilter, outChannel );
//             if ( bBias ) {
//                 this.afterBias.add_one_byBounds( outChannel, biasesValueBounds ); // Shift the value bounds by the bias bounds.
//
//             } else { // ( !bBias ). No biases array to be extracted.
//               // Do nothing. The value bounds does not change since no bias.
//             }
//           }
//
//         } // outChannelSub, outChannel
//       } // inChannel
//     } // inChannelPartIndex
//
//     // 5. Determine .activationEscaping_ScaleArraySet, .afterActivationEscaping, .afterActivation
//     this.set_activationEscaping_afterActivationEscaping_afterActivation_by_afterBias_nActivationId( nActivationId );
//   }

}

<|endoftext|>


<commit_before>'use strict';
var fs = require('fs');
var _path = require('path');

var async = require('async');
var cpr = require('cpr');
var webpack = require('webpack');
var webpackConfig = require('../config/build');

var utils = require('./utils');

module.exports = function(config) {
  process.env.BUILD_DEV = 1;

  return new Promise(function(resolve, reject) {
    webpackConfig(config).then(function(c) {
      webpack(c, function(err) {
        if(err) {
          return reject(err);
        }

        var buildDir = _path.join(process.cwd(), './.antwar/build');
        var renderPage = require(_path.join(buildDir, 'bundleStaticPage.js'));

        async.parallel([
          fs.writeFile.bind(null,
            _path.join(buildDir, 'index.html'),
            renderPage('/antwar_devindex', null)
          ),
          cpr.bind(null,
            './assets',
            _path.join(buildDir, 'assets')),
          utils.copyExtraAssets.bind(null, buildDir, config.assets),
        ], function(err) {
          if(err) {
            return reject(err);
          }

          resolve();
        });
      });
    }).catch(function(err) {
      reject(err);
    });
  });
};
<commit_msg>Copy dev_index assets only if they exist<commit_after>'use strict';
var fs = require('fs');
var _path = require('path');

var async = require('async');
var cpr = require('cpr');
var webpack = require('webpack');
var webpackConfig = require('../config/build');

var utils = require('./utils');

module.exports = function(config) {
  process.env.BUILD_DEV = 1;

  return new Promise(function(resolve, reject) {
    webpackConfig(config).then(function(c) {
      webpack(c, function(err) {
        if(err) {
          return reject(err);
        }

        var buildDir = _path.join(process.cwd(), './.antwar/build');
        var renderPage = require(_path.join(buildDir, 'bundleStaticPage.js'));

        async.parallel([
          fs.writeFile.bind(null,
            _path.join(buildDir, 'index.html'),
            renderPage('/antwar_devindex', null)
          ),
          copyIfExists.bind(null, './assets', _path.join(buildDir, 'assets')),
          utils.copyExtraAssets.bind(null, buildDir, config.assets),
        ], function(err) {
          if(err) {
            return reject(err);
          }

          resolve();
        });
      });
    }).catch(function(err) {
      reject(err);
    });
  });
};

// TODO: push to utils
function copyIfExists(from, to, cb) {
  fs.exists(from, function(exists) {
    if(exists) {
      cpr(from, to, cb);
    }
    else {
      cb();
    }
  });
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import classnames from 'classnames';
import moment from 'moment';

class IconWithData extends Component {
  render() {
    const { props } = this;

    return (

{props.text}

    );
  }
}

export default class WeatherTile extends Component {

  constructor(props) {
    super(props);

    let ship = window.ship;
    let api = window.api;

    this.state = {
      latlng: '',
      manualEntry: false,
      error: false
    };
  }

  locationSubmit() {
    navigator.geolocation.getCurrentPosition((res) => {
      console.log(res);
      let latlng = `${res.coords.latitude},${res.coords.longitude}`;
      this.setState({
        latlng
      }, (err) => {
        console.log(err);
      }, { maximumAge: Infinity, timeout: 10000 });
      api.action('weather', 'json', latlng);
    });
  }

  manualLocationSubmit() {
    event.preventDefault();
    let gpsInput = document.getElementById('gps');
    let latlngNoSpace = gpsInput.value.replace(/\s+/g, '');
    let latlngParse = /-?[0-9]+(?:\.[0-9]*)?,-?[0-9]+(?:\.[0-9]*)?/g;
    if (latlngParse.test(latlngNoSpace)) {
      let latlng = latlngNoSpace;
      this.setState({latlng}, (err) => {console.log(err)}, {maximumAge: Infinity, timeout: 10000});
      api.action('weather', 'json', latlng);
      this.setState({manualEntry: !this.state.manualEntry});
    }
    else {
      this.setState({error: true});
      return false;
    }
  }

  keyPress(e) {
    if (e.keyCode === 13) {
      e.preventDefault();
      this.manualLocationSubmit(e.target.value);
    }
  }

  renderWrapper(child) {
    return (

        {child}

    );
  }

  renderManualEntry() {
    let secureCheck;
    let error;
    if (this.state.error === true) {
      error =

          Incorrect latitude/longitude formatting. Please try again.

          (eg. "29.558107, -95.089023")

    }
    if (location.protocol === "https:") {
      secureCheck =  this.locationSubmit()}>Detect location ->
    }
    return this.renderWrapper((

         this.setState({manualEntry: !this.state.manualEntry})}>
        <-

        {secureCheck}

        Please enter your latitude and longitude.

        {error}
        <form className="flex absolute" style={{"bottom": 0, "left": 8}}>
          <input id="gps"
            className="white pa1 bg-transparent outline-0 bn bb-ns b--white"
            style={{width: "86%"}}
            type="text"
            placeholder="29.558107, -95.089023"
            onKeyDown={this.keyPress.bind(this)}>
          </input>
          <input className="bg-transparent inter white w-20 outliner-0 bn pointer"
            type="submit"
            onClick={() => this.manualLocationSubmit()}
            value="->">
          </input>
        </form>

    ))
  }

  renderNoData() {
    return this.renderWrapper((

 this.setState({manualEntry: !this.state.manualEntry})}>

            Weather

-> Set location

    ));
  }

  renderWithData(data) {
    let c = data.currently;
    let d = data.daily.data[0];

    let da = moment.unix(d.sunsetTime).format('h:mm a') || '';

    return this.renderWrapper((

            Weather

           this.setState({manualEntry: !this.state.manualEntry})}>Update location ->

            {Math.round(c.temperature)}°

            <IconWithData
              icon='winddirection'
              text={c.windBearing + '°'} />
            <IconWithData
              icon='chancerain'
              text={c.precipProbability + '%'} />
            <IconWithData
              icon='windspeed'
              text={Math.round(c.windSpeed) + ' mph'} />

            <IconWithData
              icon='sunset'
              text={da} />
            <IconWithData
              icon='low'
              text={Math.round(d.temperatureLow) + '°'} />
            <IconWithData
              icon='high'
              text={Math.round(d.temperatureHigh) + '°'} />

    ));
  }

  render() {
    let data = !!this.props.data ? this.props.data : {};

    if (this.state.manualEntry === true) {
      return this.renderManualEntry();
    }

    if ('currently' in data && 'daily' in data) {
      return this.renderWithData(data);
    }

    return this.renderNoData();
  }

}

window.weatherTile = WeatherTile;
<commit_msg>weather: fix precipitation display<commit_after>import React, { Component } from 'react';
import classnames from 'classnames';
import moment from 'moment';

class IconWithData extends Component {
  render() {
    const { props } = this;

    return (

{props.text}

    );
  }
}

export default class WeatherTile extends Component {

  constructor(props) {
    super(props);

    let ship = window.ship;
    let api = window.api;

    this.state = {
      latlng: '',
      manualEntry: false,
      error: false
    };
  }

  locationSubmit() {
    navigator.geolocation.getCurrentPosition((res) => {
      console.log(res);
      let latlng = `${res.coords.latitude},${res.coords.longitude}`;
      this.setState({
        latlng
      }, (err) => {
        console.log(err);
      }, { maximumAge: Infinity, timeout: 10000 });
      api.action('weather', 'json', latlng);
    });
  }

  manualLocationSubmit() {
    event.preventDefault();
    let gpsInput = document.getElementById('gps');
    let latlngNoSpace = gpsInput.value.replace(/\s+/g, '');
    let latlngParse = /-?[0-9]+(?:\.[0-9]*)?,-?[0-9]+(?:\.[0-9]*)?/g;
    if (latlngParse.test(latlngNoSpace)) {
      let latlng = latlngNoSpace;
      this.setState({latlng}, (err) => {console.log(err)}, {maximumAge: Infinity, timeout: 10000});
      api.action('weather', 'json', latlng);
      this.setState({manualEntry: !this.state.manualEntry});
    }
    else {
      this.setState({error: true});
      return false;
    }
  }

  keyPress(e) {
    if (e.keyCode === 13) {
      e.preventDefault();
      this.manualLocationSubmit(e.target.value);
    }
  }

  renderWrapper(child) {
    return (

        {child}

    );
  }

  renderManualEntry() {
    let secureCheck;
    let error;
    if (this.state.error === true) {
      error =

          Incorrect latitude/longitude formatting. Please try again.

          (eg. "29.558107, -95.089023")

    }
    if (location.protocol === "https:") {
      secureCheck =  this.locationSubmit()}>Detect location ->
    }
    return this.renderWrapper((

         this.setState({manualEntry: !this.state.manualEntry})}>
        <-

        {secureCheck}

        Please enter your latitude and longitude.

        {error}
        <form className="flex absolute" style={{"bottom": 0, "left": 8}}>
          <input id="gps"
            className="white pa1 bg-transparent outline-0 bn bb-ns b--white"
            style={{width: "86%"}}
            type="text"
            placeholder="29.558107, -95.089023"
            onKeyDown={this.keyPress.bind(this)}>
          </input>
          <input className="bg-transparent inter white w-20 outliner-0 bn pointer"
            type="submit"
            onClick={() => this.manualLocationSubmit()}
            value="->">
          </input>
        </form>

    ))
  }

  renderNoData() {
    return this.renderWrapper((

 this.setState({manualEntry: !this.state.manualEntry})}>

            Weather

-> Set location

    ));
  }

  renderWithData(data) {
    let c = data.currently;
    let d = data.daily.data[0];

    let da = moment.unix(d.sunsetTime).format('h:mm a') || '';

    return this.renderWrapper((

            Weather

           this.setState({manualEntry: !this.state.manualEntry})}>Update location ->

            {Math.round(c.temperature)}°

            <IconWithData
              icon='winddirection'
              text={c.windBearing + '°'} />
            <IconWithData
              icon='chancerain'
              text={(c.precipProbability * 100) + '%'} />
            <IconWithData
              icon='windspeed'
              text={Math.round(c.windSpeed) + ' mph'} />

            <IconWithData
              icon='sunset'
              text={da} />
            <IconWithData
              icon='low'
              text={Math.round(d.temperatureLow) + '°'} />
            <IconWithData
              icon='high'
              text={Math.round(d.temperatureHigh) + '°'} />

    ));
  }

  render() {
    let data = !!this.props.data ? this.props.data : {};

    if (this.state.manualEntry === true) {
      return this.renderManualEntry();
    }

    if ('currently' in data && 'daily' in data) {
      return this.renderWithData(data);
    }

    return this.renderNoData();
  }

}

window.weatherTile = WeatherTile;
<|endoftext|>


<commit_before>const path = require('path')
const gulp = require('gulp')
const ts = require('gulp-typescript')
const rimraf = require('rimraf')
const run = require('gulp-run')
const sourcemaps = require('gulp-sourcemaps')

const buildJsonSchemas = require('./jsonschemas')
const tsProject = ts.createProject(path.resolve(__dirname, '../src/tsconfig.json'))

const wipe = () => {
  return gulp.src(['./node_modules', './out']).pipe(rimraf())
}

const clean = cb => {
  rimraf('./out', cb)
}

const buildTs = () => {
  return tsProject
    .src()
    .pipe(sourcemaps.init())
    .pipe(tsProject())
    .pipe(
      sourcemaps.write({
        sourceRoot: file => {
          const sourceFile = path.join(file.cwd, 'src/bp', file.sourceMap.file)
          return path.relative(path.dirname(sourceFile), file.cwd)
        }
      })
    )
    .pipe(gulp.dest('./out/bp'))
}

const watch = () => {
  return gulp.watch('./src/**/*.ts', buildTs)
}

const createDirectories = () => {
  return gulp
    .src('*.*', { read: false })
    .pipe(gulp.dest('./out/bp/data'))
    .pipe(gulp.dest('./out/bp/data/storage'))
}

const copyVanilla = () => {
  return gulp.src('./src/bp/vanilla/**/*').pipe(gulp.dest('./out/bp/data'))
}

const copyTempates = () => {
  return gulp.src('./src/templates/**/*').pipe(gulp.dest('./out/templates'))
}

const copyAdmin = () => {
  return gulp.src('./src/bp/ui-admin/build/**/*').pipe(gulp.dest('./out/bp/ui-admin/public'))
}

const copyStudio = () => {
  return gulp.src('./src/bp/ui-studio/static/**/*').pipe(gulp.dest('./out/bp/ui-studio/static'))
}

const buildSchemas = cb => {
  buildJsonSchemas()
  cb()
}

const runTests = () => {
  return gulp.src('.').pipe(run('./node_modules/.bin/jest --detectOpenHandles -c ./jest.config.js'))
}

module.exports = {
  clean,
  buildTs,
  buildSchemas,
  createDirectories,
  copyVanilla,
  copyAdmin,
  copyStudio,
  copyTempates,
  watch,
  wipe,
  runTests
}
<commit_msg>fix map path for vscode debugging<commit_after>const path = require('path')
const gulp = require('gulp')
const ts = require('gulp-typescript')
const rimraf = require('rimraf')
const run = require('gulp-run')
const sourcemaps = require('gulp-sourcemaps')

const buildJsonSchemas = require('./jsonschemas')
const tsProject = ts.createProject(path.resolve(__dirname, '../src/tsconfig.json'))

const wipe = () => {
  return gulp.src(['./node_modules', './out']).pipe(rimraf())
}

const clean = cb => {
  rimraf('./out', cb)
}

const buildTs = () => {
  return tsProject
    .src()
    .pipe(sourcemaps.init())
    .pipe(tsProject())
    .pipe(
      sourcemaps.write({
        sourceRoot: file => {
          const sourceFile = path.join(file.cwd, 'src', file.sourceMap.file)
          return path.relative(path.dirname(sourceFile), file.cwd)
        }
      })
    )
    .pipe(gulp.dest('./out/bp'))
}

const watch = () => {
  return gulp.watch('./src/**/*.ts', buildTs)
}

const createDirectories = () => {
  return gulp
    .src('*.*', { read: false })
    .pipe(gulp.dest('./out/bp/data'))
    .pipe(gulp.dest('./out/bp/data/storage'))
}

const copyVanilla = () => {
  return gulp.src('./src/bp/vanilla/**/*').pipe(gulp.dest('./out/bp/data'))
}

const copyTempates = () => {
  return gulp.src('./src/templates/**/*').pipe(gulp.dest('./out/templates'))
}

const copyAdmin = () => {
  return gulp.src('./src/bp/ui-admin/build/**/*').pipe(gulp.dest('./out/bp/ui-admin/public'))
}

const copyStudio = () => {
  return gulp.src('./src/bp/ui-studio/static/**/*').pipe(gulp.dest('./out/bp/ui-studio/static'))
}

const buildSchemas = cb => {
  buildJsonSchemas()
  cb()
}

const runTests = () => {
  return gulp.src('.').pipe(run('./node_modules/.bin/jest --detectOpenHandles -c ./jest.config.js'))
}

module.exports = {
  clean,
  buildTs,
  buildSchemas,
  createDirectories,
  copyVanilla,
  copyAdmin,
  copyStudio,
  copyTempates,
  watch,
  wipe,
  runTests
}
<|endoftext|>