<commit_before>

 var duration_longue = true;
  var  duration_courte = false;
  var  enfant = false;
   var adult = true;
   var duree = "longue";

  $(document).ready(function(){
  //on initialise les variables avec les valeurs par défaut

    var pub = "adu";
    var nbDePersonnes = 16;
    var sce = 1;

  //  localStorage.duree = 'longue_adult';

  //on appuie les boutons en conséquence
    $("#dur_lon").addClass("active");
    $("#pub_adu").addClass("active");
    $("#sce_1").addClass("active");
    //$("#car").load('carousel_long.html');

    //On remplit le champ du bouton +/- avec la valeur par défaut
    $("#inputnb").val(nbDePersonnes);

   //localStorage.duree = 'longue_adult';
    //var carousel_c = $.get("carousel_court.html");
    //var carousel_l = $.get("carousel_long.html");
  //On change la valeur des variables en fonction des actions utilisateurs
  //Pour la durée :
    $("#dur_cou").click(function(e){

  //    localStorage.duree = 'courte';

      duree = "courte";
      duration_courte = true;
      duration_longue = false;
      $("#dur_lon").removeClass("active");
      $("#dur_cou").addClass("active");
      //var code = load("carousel_court.html");
      //$("#myCarousel").replaceWith("
 This text is strong
  This text is italic
");
      //alert(carousel_c);
      //$("#car").empty();
      $("#car").load('carousel_court.html');
      $("#p1").show();
//       alert("Chargement terminé \n Guide pour atelier court !");
      $("#rest").hide();
      $("#plusbtn, #minubtn").click(function(){$("#rest").show();});

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";

    });
    $("#dur_lon").click(function(e){

    //  localStorage.duree = 'longue';

      duree = "longue";
      duration_longue = true;
      duration_courte = false;
      $("#dur_cou").removeClass("active");
      $("#dur_lon").addClass("active");
      //$("#myCarousel").replaceWith(carousel_l);
      //$("#car").empty();
      $("#car").load('carousel_long.html');
      $("#p1").show();
//       alert("Chargement terminé \n Guide pour atelier long !");
      $("#rest").hide();
      $("#plusbtn, #minubtn").click(function(){
        localStorage.numGroupe = $("#inputnb").val();
       $("#rest").show();
      })

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
  //Pour le public :
    $("#pub_enf").click(function(e){

      localStorage.type = 'enfant';

      pub = "enf";
      adult = false;
      enfant = true;
      $("#pub_adu").removeClass("active");
      $("#pub_enf").addClass("active");

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
    $("#pub_adu").click(function(e){

      localStorage.type = 'adult';

      pub = "adu";
      adult = true;
      enfant = false;
      $("#pub_enf").removeClass("active");
      $("#pub_adu").addClass("active");

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
  //Pour le scénario :
    $("#sce_2").click(function(e){
      sce = 2;
     localStorage.dataset = "deux";
      $("#sce_1").removeClass("active");
      $("#sce_2").addClass("active");
    });
    $("#sce_1").click(function(e){
      sce = 1;
      localStorage.dataset = "un";
      $("#sce_2").removeClass("active");
      $("#sce_1").addClass("active");
    });

    $("#pointLien").click(function(e){
      if(pub == "enf"){
        $("#pointLien").attr('href','graphiques_enfants.html');
      }else{
        $("#pointLien").attr('href','graphiques_adultes.html');
      }
    });

   localStorage.numGroupe = $("#inputnb").val();

  });

function demoFromHTML() {

     var pdf = new jsPDF('p', 'pt', 'a4', false);

  //   alert(localStorage.duree);

    var logo = new Image();

    alert(localStorage.numGroupe);

          if(localStorage.duree == "longue_adult"){
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_adults/long_adults_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_adults/long_adults_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }
          }
          else if(localStorage.duree == "longue_enfant"){
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_enfants/long_enfants_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_enfants/long_enfants_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }

      }
      else if(localStorage.duree == "courte_enfant"){
            if(localStorage.dataset == "un"){
                logo.src = "https://vizkit.github.io/pdf_images/court/Court_enfants/court_enfants_dataset1_1.PNG";
                pdf.addImage(logo, 'PNG', 2, 2, 590,850);
                pdf.addPage();
            }else{
                logo.src = "https://vizkit.github.io/pdf_images/court/Court_enfants/court_enfants_dataset2_1.PNG";
                pdf.addImage(logo, 'PNG', 2, 2, 590,850);
                pdf.addPage();
            }

          }
          else {
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/court/Court_adults/court_adults_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/court/Court_adults/court_adults_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }
          // var uno= 1;
          }

       /*   var paso;
        for (paso = 4; paso < 6; paso++) {
            pdf.addPage();
            var logo = new Image();
            logo.src = paso+".png";
            pdf.addImage(logo, 'PNG', 2, 2, 590,850);
          // Se ejecuta 5 veces, con valores desde paso desde 0 hasta 4.

          };*/

        pdf.output("dataurlnewwindow");

}
<commit_msg>Update dynamic_carousel.js<commit_after>

 var duration_longue = true;
  var  duration_courte = false;
  var  enfant = false;
   var adult = true;
   var duree = "longue";

  $(document).ready(function(){
  //on initialise les variables avec les valeurs par défaut

    var pub = "adu";
    var nbDePersonnes = 16;
    var sce = 1;
    localStorage.numGroupe = $("#inputnb").val();

  //  localStorage.duree = 'longue_adult';

  //on appuie les boutons en conséquence
    $("#dur_lon").addClass("active");
    $("#pub_adu").addClass("active");
    $("#sce_1").addClass("active");
    //$("#car").load('carousel_long.html');

    //On remplit le champ du bouton +/- avec la valeur par défaut
    $("#inputnb").val(nbDePersonnes);

   //localStorage.duree = 'longue_adult';
    //var carousel_c = $.get("carousel_court.html");
    //var carousel_l = $.get("carousel_long.html");
  //On change la valeur des variables en fonction des actions utilisateurs
  //Pour la durée :
    $("#dur_cou").click(function(e){

  //    localStorage.duree = 'courte';

      duree = "courte";
      duration_courte = true;
      duration_longue = false;
      $("#dur_lon").removeClass("active");
      $("#dur_cou").addClass("active");
      //var code = load("carousel_court.html");
      //$("#myCarousel").replaceWith("
 This text is strong
  This text is italic
");
      //alert(carousel_c);
      //$("#car").empty();
      $("#car").load('carousel_court.html');
      $("#p1").show();
//       alert("Chargement terminé \n Guide pour atelier court !");
      $("#rest").hide();
      $("#plusbtn, #minubtn").click(function(){$("#rest").show();});

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";

    });
    $("#dur_lon").click(function(e){

    //  localStorage.duree = 'longue';

      duree = "longue";
      duration_longue = true;
      duration_courte = false;
      $("#dur_cou").removeClass("active");
      $("#dur_lon").addClass("active");
      //$("#myCarousel").replaceWith(carousel_l);
      //$("#car").empty();
      $("#car").load('carousel_long.html');
      $("#p1").show();
//       alert("Chargement terminé \n Guide pour atelier long !");
      $("#rest").hide();
      $("#plusbtn, #minubtn").click(function(){
        localStorage.numGroupe = $("#inputnb").val();
       $("#rest").show();
      })

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
  //Pour le public :
    $("#pub_enf").click(function(e){

      localStorage.type = 'enfant';

      pub = "enf";
      adult = false;
      enfant = true;
      $("#pub_adu").removeClass("active");
      $("#pub_enf").addClass("active");

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
    $("#pub_adu").click(function(e){

      localStorage.type = 'adult';

      pub = "adu";
      adult = true;
      enfant = false;
      $("#pub_enf").removeClass("active");
      $("#pub_adu").addClass("active");

      if (duree == "longue" && enfant) localStorage.duree = "longue_enfant";
      else if (duree == "courte" && adult) localStorage.duree = "courte_adult";
      else if (duree == "courte" && enfant) localStorage.duree = "courte_enfant";
      else  localStorage.duree = "longue_adult";
    });
  //Pour le scénario :
    $("#sce_2").click(function(e){
      sce = 2;
     localStorage.dataset = "deux";
      $("#sce_1").removeClass("active");
      $("#sce_2").addClass("active");
    });
    $("#sce_1").click(function(e){
      sce = 1;
      localStorage.dataset = "un";
      $("#sce_2").removeClass("active");
      $("#sce_1").addClass("active");
    });

    $("#pointLien").click(function(e){
      if(pub == "enf"){
        $("#pointLien").attr('href','graphiques_enfants.html');
      }else{
        $("#pointLien").attr('href','graphiques_adultes.html');
      }
    });

  });

function demoFromHTML() {

     var pdf = new jsPDF('p', 'pt', 'a4', false);

  //   alert(localStorage.duree);

    var logo = new Image();

    alert(localStorage.numGroupe);

          if(localStorage.duree == "longue_adult"){
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_adults/long_adults_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_adults/long_adults_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }
          }
          else if(localStorage.duree == "longue_enfant"){
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_enfants/long_enfants_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/long/Long_enfants/long_enfants_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }

      }
      else if(localStorage.duree == "courte_enfant"){
            if(localStorage.dataset == "un"){
                logo.src = "https://vizkit.github.io/pdf_images/court/Court_enfants/court_enfants_dataset1_1.PNG";
                pdf.addImage(logo, 'PNG', 2, 2, 590,850);
                pdf.addPage();
            }else{
                logo.src = "https://vizkit.github.io/pdf_images/court/Court_enfants/court_enfants_dataset2_1.PNG";
                pdf.addImage(logo, 'PNG', 2, 2, 590,850);
                pdf.addPage();
            }

          }
          else {
            if(localStorage.dataset == "un"){
              logo.src = "https://vizkit.github.io/pdf_images/court/Court_adults/court_adults_dataset1_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }else{
              logo.src = "https://vizkit.github.io/pdf_images/court/Court_adults/court_adults_dataset2_1.PNG";
              pdf.addImage(logo, 'PNG', 2, 2, 590,850);
              pdf.addPage();
            }
          // var uno= 1;
          }

       /*   var paso;
        for (paso = 4; paso < 6; paso++) {
            pdf.addPage();
            var logo = new Image();
            logo.src = paso+".png";
            pdf.addImage(logo, 'PNG', 2, 2, 590,850);
          // Se ejecuta 5 veces, con valores desde paso desde 0 hasta 4.

          };*/

        pdf.output("dataurlnewwindow");

}
<|endoftext|>


<commit_before>Gratipay.profile = {};

Gratipay.profile.init = function() {

    // Wire up textarea for statement.
    // ===============================

    $('textarea').focus();

    Gratipay.forms.jsEdit({
        confirmBeforeUnload: true,
        root: $('.statement.js-edit'),
    });

    $('.statement textarea').on('change keyup paste', function(){
        var changed = $(this).val() !== $(this).data('original');
        $('.statement button.save').prop('disabled', !changed);
    });

    $('.statement select.langs').on('change', function(e){
        var $form = $('.statement form');
        var $inputs = $form.find('button, input, select, textarea').filter(':not(:disabled)');
        var $textarea = $form.find('textarea');
        var $this = $(this);
        var lang = $this.val();
        if ($textarea.val() !== $textarea.data('original')) {
            if(!confirm($textarea.data('confirm-discard'))) {
                $this.val($this.data('value'));
                return;
            }
        }
        $inputs.prop('disabled', true);
        $textarea.val("Loading...");
        $.ajax({
            url: 'statement.json',
            data: {lang: lang},
            dataType: 'json',
            success: function (d) {
                $inputs.prop('disabled', false);
                $textarea.attr('lang', lang);
                $textarea.val(d.content || "");
                $textarea.data('original', $textarea.val());
                $form.find('button.save').prop('disabled', true);
                $this.data('value', lang);
            },
            error: [
                function () { $inputs.prop('disabled', false); },
                Gratipay.error,
            ]
        });
    });

    $('.statement button.edit').on('click', function(){
        $('.statement textarea').val('').data('original', '');
        $('.statement select.langs').change();
    });

    if ($('.statement div.view').html().trim() === '') {
        $('.statement button.edit').click();
    }

    // Wire up cryptocoin inputs.
    // ==========================

    $('tr.cryptocoin.js-edit').each(function() {
        var $root = $(this);
        Gratipay.forms.jsEdit({
            root: $root,
            success: function(){
                var addr = $root.find('[name="address"]').val();
                $root.find('.view').text(addr);
                $root.find('button.delete').data('address', addr);
                $root.addClass('not-empty');
            },
        });
        $root.find('button.delete').click(Gratipay.payments.deleteRoute);
    });

    // Wire up account deletion.
    // =========================

    $('.account-delete').on('click', function () {
        var $this = $(this);

        jQuery.ajax({
            url: "delete-elsewhere.json",
            type: "POST",
            dataType: "json",
            success: function ( ) {
                location.reload();
            },
            error: Gratipay.error,
            data: { platform: this.dataset.platform, user_id: this.dataset.user_id }
        });

        return false;
    });

    // Wire up user_name_prompt
    // ========================

    $('.user_name_prompt').on('click', function () {
        var user_name = prompt('Please enter the name of the GitHub account you would like to connect:');
        if(!user_name) return false;
        $(this).children('[name="user_name"]').val(user_name);
    });

};
<commit_msg>Fix bitcoin delete button<commit_after>Gratipay.profile = {};

Gratipay.profile.init = function() {

    // Wire up textarea for statement.
    // ===============================

    $('textarea').focus();

    Gratipay.forms.jsEdit({
        confirmBeforeUnload: true,
        root: $('.statement.js-edit'),
    });

    $('.statement textarea').on('change keyup paste', function(){
        var changed = $(this).val() !== $(this).data('original');
        $('.statement button.save').prop('disabled', !changed);
    });

    $('.statement select.langs').on('change', function(e){
        var $form = $('.statement form');
        var $inputs = $form.find('button, input, select, textarea').filter(':not(:disabled)');
        var $textarea = $form.find('textarea');
        var $this = $(this);
        var lang = $this.val();
        if ($textarea.val() !== $textarea.data('original')) {
            if(!confirm($textarea.data('confirm-discard'))) {
                $this.val($this.data('value'));
                return;
            }
        }
        $inputs.prop('disabled', true);
        $textarea.val("Loading...");
        $.ajax({
            url: 'statement.json',
            data: {lang: lang},
            dataType: 'json',
            success: function (d) {
                $inputs.prop('disabled', false);
                $textarea.attr('lang', lang);
                $textarea.val(d.content || "");
                $textarea.data('original', $textarea.val());
                $form.find('button.save').prop('disabled', true);
                $this.data('value', lang);
            },
            error: [
                function () { $inputs.prop('disabled', false); },
                Gratipay.error,
            ]
        });
    });

    $('.statement button.edit').on('click', function(){
        $('.statement textarea').val('').data('original', '');
        $('.statement select.langs').change();
    });

    if ($('.statement div.view').html().trim() === '') {
        $('.statement button.edit').click();
    }

    // Wire up cryptocoin inputs.
    // ==========================

    $('tr.cryptocoin.js-edit').each(function() {
        var $root = $(this);
        Gratipay.forms.jsEdit({
            root: $root,
            success: function(){
                var addr = $root.find('[name="address"]').val();
                $root.find('.view').text(addr);
                $root.find('button.delete').data('address', addr);
                $root.addClass('not-empty');
            },
        });
        $root.find('button.delete').click(Gratipay.routes.deleteRoute);
    });

    // Wire up account deletion.
    // =========================

    $('.account-delete').on('click', function () {
        var $this = $(this);

        jQuery.ajax({
            url: "delete-elsewhere.json",
            type: "POST",
            dataType: "json",
            success: function ( ) {
                location.reload();
            },
            error: Gratipay.error,
            data: { platform: this.dataset.platform, user_id: this.dataset.user_id }
        });

        return false;
    });

    // Wire up user_name_prompt
    // ========================

    $('.user_name_prompt').on('click', function () {
        var user_name = prompt('Please enter the name of the GitHub account you would like to connect:');
        if(!user_name) return false;
        $(this).children('[name="user_name"]').val(user_name);
    });

};
<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * IO type for a DOMEvent
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Chris Klusendorf (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  const scenery = require( 'SCENERY/scenery' );

  // ifphetio
  const assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  const phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );

  /**
   * IO type for phet/sun's DOMEvent class.
   * @param {DOMEvent} domEvent
   * @param {string} phetioID
   * @constructor
   */
  function DOMEventIO( domEvent, phetioID ) {
    assert && assertInstanceOf( domEvent, Event ); // Event is the browser DOM event type, not the scenery one.
    ObjectIO.call( this, domEvent, phetioID );
  }

  // we must use phetioInherit, even in es6
  phetioInherit( ObjectIO, 'DOMEventIO', DOMEventIO, {}, {
    documentation: 'A DOM Event',

    /**
     * Encodes a DOMEvent instance to a state.
     * @param {Event} domEvent
     * @returns {Object} - a state object
     */
    toStateObject( domEvent ) {
      return scenery.Input.serializeDomEvent( domEvent );
    }
  } );

  return scenery.register( 'DOMEventIO', DOMEventIO );
} );

<commit_msg>Overrides, see https://github.com/phetsims/phet-io/issues/1278<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * IO type for a DOMEvent
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Chris Klusendorf (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  const scenery = require( 'SCENERY/scenery' );

  // ifphetio
  const assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  const phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );

  /**
   * IO type for phet/sun's DOMEvent class.
   * @param {DOMEvent} domEvent
   * @param {string} phetioID
   * @constructor
   */
  function DOMEventIO( domEvent, phetioID ) {
    assert && assertInstanceOf( domEvent, Event ); // Event is the browser DOM event type, not the scenery one.
    ObjectIO.call( this, domEvent, phetioID );
  }

  // we must use phetioInherit, even in es6
  phetioInherit( ObjectIO, 'DOMEventIO', DOMEventIO, {}, {
    documentation: 'A DOM Event',

    /**
     * Encodes a DOMEvent instance to a state.
     * @param {Event} domEvent
     * @returns {Object} - a state object
     * @override
     */
    toStateObject( domEvent ) {
      return scenery.Input.serializeDomEvent( domEvent );
    }
  } );

  return scenery.register( 'DOMEventIO', DOMEventIO );
} );

<|endoftext|>


<commit_before>import { query, queryAACT } from '../../util/db';

import { Client } from '@googlemaps/google-maps-services-js';
import { bulkUpdate } from '../../search/elastic';
import logger from '../../util/logger';

const zg = require('zip2geo');

const util = require('util');

let IS_RUNNING = false;

export const geocodeStudies = async payload => {
    //added queries to updae geo2zip in postgres
    //convert this to just indexing, not geocoding, until improved algorithm for address lookup and cleanup implemented
    logger.info('indexing study locations');
    const idList = payload.studies;
    logger.info('Geocoding ' + idList.length + ' studies');
    let facilityMap = new Map();
    const paramList = ['United States']; //not using but leaving since don't know how to fix
    let params = idList.map((id, index) => {
        paramList.push(id);
        return '$' + (index + 2);
    });
    const facilityQuery =
        //query joins facility to get nct_id with facility_loctions to get lat/lon
        //don't limit by country because name, city, state, country all have to index regardless
        //order by so bulk processes nct_ids together ()
            ' select f.nct_id , f."name" , f.city , f.state , f.zip , f.country , fl.latitude , fl.longitude , fl.status ' +
			' from ctgov.facilities f ' +
			' left outer join public.facility_locations fl ' +
			' on f."name" = fl."name" and f.city = fl.city and f.state = fl.state and f.zip = fl.zip and f.country = fl.country ' +
			' where (f.country=$1 OR f.country !=$1) and f.nct_id in (' +
            params.join(',') +
            ') ' +
            ' ORDER BY f.nct_id';
            // 'select * from facilities where country=$1 and nct_id in (' +
            // params.join(',') +
            // ') ';
    const facilities = await queryAACT(facilityQuery, paramList);
    logger.info('Geocoding study location facilities ' + facilities.rowCount);
    for (let i = 0; i < facilities.rowCount; i++) {
        try {
            const facility = facilities.rows[i];

            console.log(util.inspect(facility, false, null, true));
            logger.info('jumping to format row to index', facility);
            //facility objec/row now has lat/lon
            addFacilityToStudyMap(facilityMap, {
                nct_id: facility.nct_id,
                name: facility.name,
                city: facility.city,
                state: facility.state,
                country: facility.country,
                //not sure lat/lon handle null, so keeping with prior code that added || ''
                latitude: facility.latitude || '',
                longitude: facility.longitude | '',
            });

        } catch (err) {
            console.log(err);
        }
    }

    console.log('MAP before spread', facilityMap);
    // Now send the map to Elasticsearch
    const listToUpdate = [...facilityMap.values()];
    console.log(listToUpdate);
    await bulkUpdate(listToUpdate);
    logger.info('Finished geocoding study locations');
};

const addFacilityToStudyMap = (map, facility) => {
    console.log('In Add facility Locations');
    console.log('Map' + util.inspect(map, false, null, true));
    console.log(
        'Facility in AFTSM' + util.inspect(facility, false, null, true)
    );
    let found = map.get(facility.nct_id);
    console.log('FOUND');
    console.log(found);
    if (!found) {
        console.log('IN NOT FOUND');
        found = {
            nct_id: facility.nct_id,
            facility_names: [],
            facility_states: [],
            facility_cities: [],
            facility_countries: [],
            locations: [],
        };
    }
    found.facility_names.push(facility.name);
    found.facility_states.push(facility.state);
    found.facility_cities.push(facility.city);
    found.facility_countries.push(facility.country);
    //Seems like part of the issue was that the object was mismatched to the expected type when facility.latitude and/or longitude was undefined
    // locations would send as [{}] was expecting something more like [{lat: undefined, lon: undefined}]
    facility.latitude &&
        facility.longitude &&
        found.locations.push({
            lat: facility.latitude,
            lon: facility.longitude,
        });

    // console.log("POST FOUND" + util.inspect(found, false, null, true));
    map.set(facility.nct_id, found);
    // console.log("Post MAP" + util.inspect(map, false, null, true));
};

<commit_msg>fixed error in longitude truncation<commit_after>import { query, queryAACT } from '../../util/db';

import { Client } from '@googlemaps/google-maps-services-js';
import { bulkUpdate } from '../../search/elastic';
import logger from '../../util/logger';

const zg = require('zip2geo');

const util = require('util');

let IS_RUNNING = false;

export const geocodeStudies = async payload => {
    //added queries to updae geo2zip in postgres
    //convert this to just indexing, not geocoding, until improved algorithm for address lookup and cleanup implemented
    logger.info('indexing study locations');
    const idList = payload.studies;
    logger.info('Geocoding ' + idList.length + ' studies');
    let facilityMap = new Map();
    const paramList = ['United States']; //not using but leaving since don't know how to fix
    let params = idList.map((id, index) => {
        paramList.push(id);
        return '$' + (index + 2);
    });
    const facilityQuery =
        //query joins facility to get nct_id with facility_loctions to get lat/lon
        //don't limit by country because name, city, state, country all have to index regardless
        //order by so bulk processes nct_ids together ()
            ' select f.nct_id , f."name" , f.city , f.state , f.zip , f.country , fl.latitude , fl.longitude , fl.status ' +
			' from ctgov.facilities f ' +
			' left outer join public.facility_locations fl ' +
			' on f."name" = fl."name" and f.city = fl.city and f.state = fl.state and f.zip = fl.zip and f.country = fl.country ' +
			' where (f.country=$1 OR f.country !=$1) and f.nct_id in (' +
            params.join(',') +
            ') ' +
            ' ORDER BY f.nct_id';
            // 'select * from facilities where country=$1 and nct_id in (' +
            // params.join(',') +
            // ') ';
    const facilities = await queryAACT(facilityQuery, paramList);
    logger.info('Geocoding study location facilities ' + facilities.rowCount);
    for (let i = 0; i < facilities.rowCount; i++) {
        try {
            const facility = facilities.rows[i];

            console.log(util.inspect(facility, false, null, true));
            logger.info('jumping to format row to index', facility);
            //facility objec/row now has lat/lon
            addFacilityToStudyMap(facilityMap, {
                nct_id: facility.nct_id,
                name: facility.name,
                city: facility.city,
                state: facility.state,
                country: facility.country,
                //not sure lat/lon handle null, so keeping with prior code that added || ''
                latitude: facility.latitude || '',
                longitude: facility.longitude || '',
            });

        } catch (err) {
            console.log(err);
        }
    }

    console.log('MAP before spread', facilityMap);
    // Now send the map to Elasticsearch
    const listToUpdate = [...facilityMap.values()];
    console.log(listToUpdate);
    await bulkUpdate(listToUpdate);
    logger.info('Finished geocoding study locations');
};

const addFacilityToStudyMap = (map, facility) => {
    console.log('In Add facility Locations');
    console.log('Map' + util.inspect(map, false, null, true));
    console.log(
        'Facility in AFTSM' + util.inspect(facility, false, null, true)
    );
    let found = map.get(facility.nct_id);
    console.log('FOUND');
    console.log(found);
    if (!found) {
        console.log('IN NOT FOUND');
        found = {
            nct_id: facility.nct_id,
            facility_names: [],
            facility_states: [],
            facility_cities: [],
            facility_countries: [],
            locations: [],
        };
    }
    found.facility_names.push(facility.name);
    found.facility_states.push(facility.state);
    found.facility_cities.push(facility.city);
    found.facility_countries.push(facility.country);
    //Seems like part of the issue was that the object was mismatched to the expected type when facility.latitude and/or longitude was undefined
    // locations would send as [{}] was expecting something more like [{lat: undefined, lon: undefined}]
    facility.latitude &&
        facility.longitude &&
        found.locations.push({
            lat: facility.latitude,
            lon: facility.longitude,
        });

    // console.log("POST FOUND" + util.inspect(found, false, null, true));
    map.set(facility.nct_id, found);
    // console.log("Post MAP" + util.inspect(map, false, null, true));
};

<|endoftext|>


<commit_before>    (function( $ ) {

    $.fn.circliful = function(options) {

        var settings = $.extend({
            // These are the defaults.
            foregroundColor: "#556b2f",
            backgroundColor: "#eee",
            fillColor: false,
            width: 15,
            dimension: 200,
            size: 15,
			percent: 50,
            animationStep: 1.0
        }, options );
         return this.each(function() {
                var dimension = '';
                var text = '';
				var info = '';
                var width = '';
                var size = 0;
				var percent = 0;
				var endPercent = 100;
				var fgcolor = '';
				var bgcolor = '';
				var icon = '';
                var animationstep = 0.0;

                $(this).addClass('circliful');

                if($(this).data('dimension') != undefined) {
                    dimension = $(this).data('dimension');
                } else {
                    dimension = settings.dimension;
                }

                if($(this).data('width') != undefined) {
                    width = $(this).data('width');
                } else {
                    width = settings.width;
                }

                if($(this).data('fontsize') != undefined) {
                    size = $(this).data('fontsize');
                } else {
                    size = settings.size;
                }

				if($(this).data('percent') != undefined) {
                    percent = $(this).data('percent') / 100;
					endPercent = $(this).data('percent');
                } else {
                    percent = settings.percent / 100;
                }

				if($(this).data('fgcolor') != undefined) {
                    fgcolor = $(this).data('fgcolor');
                } else {
                    fgcolor = settings.foregroundColor;
                }

				if($(this).data('bgcolor') != undefined) {
                    bgcolor = $(this).data('bgcolor');
                } else {
                    bgcolor = settings.backgroundColor;
                }

                if($(this).data('animation-step') != undefined) {
                    animationstep = parseFloat($(this).data('animation-step'));
                } else {
                    animationstep = settings.animationStep;
                }
                if($(this).data('text') != undefined) {
                    text = $(this).data('text');

					if($(this).data('icon') != undefined) {
						icon = '';
					}

					 if($(this).data('type') != undefined) {
						type = $(this).data('type');

						if(type == 'half') {
							$(this).append('' +  icon  + text + '');
							$(this).find('.circle-text-half').css({'line-height': (dimension / 1.45) + 'px', 'font-size' : size + 'px' });
						} else {
							$(this).append('' + icon + text + '');
							$(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' });
						}
					} else {
						$(this).append('' + icon + text + '');
						$(this).find('.circle-text').css({'line-height': dimension + 'px', 'font-size' : size + 'px' });
					}
                } else if($(this).data('icon') != undefined) {

				}

				if($(this).data('info') != undefined) {
                    info = $(this).data('info');

					if($(this).data('type') != undefined) {
						type = $(this).data('type');

						if(type == 'half') {
							$(this).append('' + info + '');
							$(this).find('.circle-info-half').css({'line-height': (dimension * 0.9) + 'px', });
						} else {
							$(this).append('' + info + '');
							$(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', });
						}
					} else {
						$(this).append('' + info + '');
						$(this).find('.circle-info').css({'line-height': (dimension * 1.25) + 'px', });
					}
                }

                $(this).width(dimension + 'px');

              var canvas = $('<canvas></canvas>').attr({ width: dimension, height: dimension }).appendTo($(this)).get(0);
              var context = canvas.getContext('2d');
              var x = canvas.width / 2;
              var y = canvas.height / 2;
			  var degrees = percent * 360.0;
			  var radians = degrees * (Math.PI / 180);
              var radius = canvas.width / 2.5;
              var startAngle = 2.3 * Math.PI;
              var endAngle = 0;
              var counterClockwise = false;
              var curPerc = animationstep === 0.0 ? endPercent : 0.0;
              var curStep = Math.max(animationstep, 0.0);
			  var circ = Math.PI * 2;
			  var quart = Math.PI / 2;
			  var type = '';
			  var fill = false;

			  if($(this).data('type') != undefined) {
                    type = $(this).data('type');

					if(type == 'half') {
						var startAngle = 2.0 * Math.PI;
						var endAngle = 3.13;
						var circ = Math.PI * 1.0;
						var quart = Math.PI / 0.996;
					}
                }

				if($(this).data('fill') != undefined) {
					fill = $(this).data('fill');
				} else {
					fill = settings.fillColor;
				}
			  //animate foreground circle
			  function animate(current) {
				context.clearRect(0, 0, canvas.width, canvas.height);

				context.beginPath();
				context.arc(x, y, radius, endAngle, startAngle, false);
				context.lineWidth = width - 1;

				// line color
				context.strokeStyle = bgcolor;
				context.stroke();

				if(fill) {
					context.fillStyle = fill;
					context.fill();
				}

				context.beginPath();
				context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
				context.lineWidth = width;
				// line color
				context.strokeStyle = fgcolor;
				context.stroke();

				if (curPerc < endPercent) {
  				     curPerc += curStep;
					 requestAnimationFrame(function () {
						 animate(Math.min(curPerc, endPercent) / 100);
					 });
				}

			 }

			 animate(curPerc / 100);

        });

    };

}( jQuery ));<commit_msg>refactored code and added data-part and data-total again #9<commit_after>(function ($) {

    $.fn.circliful = function (options) {

        var settings = $.extend({
            // These are the defaults.
            fgcolor: "#556b2f",
            bgcolor: "#eee",
            fill: false,
            width: 15,
            dimension: 200,
            fontsize: 15,
            percent: 50,
            animationstep: 1.0,
            iconsize: '20px',
            iconcolor: '#999'
        }, options);

        return this.each(function () {
            var customSettings = ["fgcolor", "bgcolor", "fill", "width", "dimension", "fontsize", "animationstep", "endPercent", "icon", "iconcolor", "iconsize"];
            var customSettingsObj = {};
            var icon = '';
            var endPercent = 0;
            var obj = $(this);
            var fill = false;
            var text, info;

            obj.addClass('circliful');

            checkDataAttributes(obj);

            if (obj.data('text') != undefined) {
                text = obj.data('text');

                if (obj.data('icon') != undefined) {
                    icon = $('')
                        .addClass('fa ' + $(this).data('icon'))
                        .css({
                            'color': customSettingsObj.iconcolor,
                            'font-size': customSettingsObj.iconsize
                        });
                }

                if (obj.data('type') != undefined) {
                    type = $(this).data('type');

                    if (type == 'half') {
                        addCircleText(obj, 'circle-text-half', (customSettingsObj.dimension / 1.45));
                    } else {
                        addCircleText(obj, 'circle-text', customSettingsObj.dimension);
                    }
                } else {
                    addCircleText(obj, 'circle-text', customSettingsObj.dimension);
                }
            }

            if ($(this).data("total") != undefined && $(this).data("part") != undefined) {
                var total = $(this).data("total") / 100;

                percent = (($(this).data("part") / total) / 100).toFixed(3);
                endPercent = ($(this).data("part") / total).toFixed(3)
            } else {
                if ($(this).data("percent") != undefined) {
                    percent = $(this).data("percent") / 100;
                    endPercent = $(this).data("percent")
                } else {
                    percent = settings.percent / 100
                }
            }

            if ($(this).data('info') != undefined) {
                info = $(this).data('info');

                if ($(this).data('type') != undefined) {
                    type = $(this).data('type');

                    if (type == 'half') {
                        addInfoText(obj, 0.9);
                    } else {
                        addInfoText(obj, 1.25);
                    }
                } else {
                    addInfoText(obj, 1.25);
                }
            }

            $(this).width(customSettingsObj.dimension + 'px');

            var canvas = $('<canvas></canvas>').attr({
                width: customSettingsObj.dimension,
                height: customSettingsObj.dimension
            }).appendTo($(this)).get(0);
            var context = canvas.getContext('2d');
            var x = canvas.width / 2;
            var y = canvas.height / 2;
            var degrees = customSettingsObj.percent * 360.0;
            var radians = degrees * (Math.PI / 180);
            var radius = canvas.width / 2.5;
            var startAngle = 2.3 * Math.PI;
            var endAngle = 0;
            var counterClockwise = false;
            var curPerc = customSettingsObj.animationstep === 0.0 ? endPercent : 0.0;
            var curStep = Math.max(customSettingsObj.animationstep, 0.0);
            var circ = Math.PI * 2;
            var quart = Math.PI / 2;
            var type = '';

            if ($(this).data('type') != undefined) {
                type = $(this).data('type');

                if (type == 'half') {
                    var startAngle = 2.0 * Math.PI;
                    var endAngle = 3.13;
                    var circ = Math.PI * 1.0;
                    var quart = Math.PI / 0.996;
                }
            }

            /**
             * adds text to circle
             *
             * @param obj
             * @param cssClass
             * @param lineHeight
             */
            function addCircleText(obj, cssClass, lineHeight) {
                $("")
                    .appendTo(obj)
                    .addClass(cssClass)
                    .text(text)
                    .prepend(icon)
                    .css({
                        'line-height': lineHeight + 'px',
                        'font-size': customSettingsObj.fontsize + 'px'
                    });
            }

            /**
             * adds info text to circle
             *
             * @param obj
             * @param factor
             */
            function addInfoText(obj, factor) {
                $('')
                    .appendTo(obj)
                    .addClass('circle-info-half')
                    .css(
                    'line-height', (customSettingsObj.dimension * factor) + 'px'
                );
            }

            /**
             * checks which data attributes are defined
             * @param obj
             */
            function checkDataAttributes(obj) {
                $.each(customSettings, function (index, attribute) {
                    if (obj.data(attribute) != undefined) {
                        customSettingsObj[attribute] = obj.data(attribute);
                    } else {
                        customSettingsObj[attribute] = $(settings).attr(attribute);
                    }

                    if (attribute == 'fill' && obj.data('fill') != undefined) {
                        fill = true;
                    }
                });
            }

            /**
             * animate foreground circle
             * @param current
             */
            function animate(current) {
                context.clearRect(0, 0, canvas.width, canvas.height);

                context.beginPath();
                context.arc(x, y, radius, endAngle, startAngle, false);
                context.lineWidth = customSettingsObj.width - 1;

                context.strokeStyle = customSettingsObj.bgcolor;
                context.stroke();

                if (fill) {
                    context.fillStyle = customSettingsObj.fill;
                    context.fill();
                }

                context.beginPath();
                context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
                context.lineWidth = customSettingsObj.width;

                context.strokeStyle = customSettingsObj.fgcolor;
                context.stroke();

                if (curPerc < endPercent) {
                    curPerc += curStep;
                    requestAnimationFrame(function () {
                        animate(Math.min(curPerc, endPercent) / 100);
                    }, obj);
                }
            }

            animate(curPerc / 100);

        });
    };
}(jQuery));<|endoftext|>


<commit_before>
openerp.web.formats = function(openerp) {
var _t = openerp.web._t;

/**
 * Intersperses ``separator`` in ``str`` at the positions indicated by
 * ``indices``.
 *
 * ``indices`` is an array of relative offsets (from the previous insertion
 * position, starting from the end of the string) at which to insert
 * ``separator``.
 *
 * There are two special values:
 *
 * ``-1``
 *   indicates the insertion should end now
 * ``0``
 *   indicates that the previous section pattern should be repeated (until all
 *   of ``str`` is consumed)
 *
 * @param {String} str
 * @param {Array<Number>} indices
 * @param {String} separator
 * @returns {String}
 */
openerp.web.intersperse = function (str, indices, separator) {
    separator = separator || '';
    var result = [], last = str.length;

    for(var i=0; i<indices.length; ++i) {
        var section = indices[i];
        if (section === -1 || last <= 0) {
            // Done with string, or -1 (stops formatting string)
            break;
        } else if(section === 0 && i === 0) {
            // repeats previous section, which there is none => stop
            break;
        } else if (section === 0) {
            // repeat previous section forever
            //noinspection AssignmentToForLoopParameterJS
            section = indices[--i];
        }
        result.push(str.substring(last-section, last));
        last -= section;
    }

    var s = str.substring(0, last);
    if (s) { result.push(s); }
    return result.reverse().join(separator);
};
/**
 * Insert "thousands" separators in the provided number (which is actually
 * a string)
 *
 * @param {String} num
 * @returns {String}
 */
openerp.web.insert_thousand_seps = function (num) {
    var negative = num[0] === '-';
    num = (negative ? num.slice(1) : num);
    return (negative ? '-' : '') + openerp.web.intersperse(
        num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
};

/**
 * removes literal (non-format) text from a date or time pattern, as datejs can
 * not deal with literal text in format strings (whatever the format), whereas
 * strftime allows for literal characters
 *
 * @param {String} value original format
 */
openerp.web.strip_raw_chars = function (value) {
    var isletter = /[a-zA-Z]/, output = [];
    for(var index=0; index < value.length; ++index) {
        var character = value[index];
        if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) {
            continue;
        }
        output.push(character);
    }
    return output.join('');
};
var normalize_format = function (format) {
    return Date.normalizeFormat(openerp.web.strip_raw_chars(format));
};
/**
 * Formats a single atomic value based on a field descriptor
 *
 * @param {Object} value read from OpenERP
 * @param {Object} descriptor union of orm field and view field
 * @param {Object} [descriptor.widget] widget to use to display the value
 * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
 * @param {Object} [descriptor.digits] used for the formatting of floats
 * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
 */
openerp.web.format_value = function (value, descriptor, value_if_empty) {
    // If NaN value, display as with a `false` (empty cell)
    if (typeof value === 'number' && isNaN(value)) {
        value = false;
    }
    //noinspection FallthroughInSwitchStatementJS
    switch (value) {
        case '':
            if (descriptor.type === 'char') {
                return '';
            }
            console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
        case false:
        case Infinity:
        case -Infinity:
            return value_if_empty === undefined ?  '' : value_if_empty;
    }
    var l10n = _t.database.parameters;
    switch (descriptor.widget || descriptor.type) {
        case 'id':
            return value.toString();
        case 'integer':
            return openerp.web.insert_thousand_seps(
                _.str.sprintf('%d', value));
        case 'float':
            var precision = descriptor.digits ? descriptor.digits[1] : 2;
            var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
            formatted[0] = openerp.web.insert_thousand_seps(formatted[0]);
            return formatted.join(l10n.decimal_point);
        case 'float_time':
            var pattern = '%02d:%02d';
            if (value < 0) {
                value = Math.abs(value);
                pattern = '-' + pattern;
            }
            return _.str.sprintf(pattern,
                    Math.floor(value),
                    Math.round((value % 1) * 60));
        case 'many2one':
            // name_get value format
            return value[1];
        case 'datetime':
            if (typeof(value) == "string")
                value = openerp.web.auto_str_to_date(value);

            return value.toString(normalize_format(l10n.date_format)
                        + ' ' + normalize_format(l10n.time_format));
        case 'date':
            if (typeof(value) == "string")
                value = openerp.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.date_format));
        case 'time':
            if (typeof(value) == "string")
                value = openerp.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.time_format));
        case 'selection': case 'statusbar':
            // Each choice is [value, label]
            if(_.isArray(value)) {
                 value = value[0]
            }
            var result = _(descriptor.selection).detect(function (choice) {
                return choice[0] === value;
            });
            if (result) { return result[1]; }
            return;
        default:
            return value;
    }
};

openerp.web.parse_value = function (value, descriptor, value_if_empty) {
    var date_pattern = normalize_format(_t.database.parameters.date_format),
        time_pattern = normalize_format(_t.database.parameters.time_format);
    switch (value) {
        case false:
        case "":
            return value_if_empty === undefined ?  false : value_if_empty;
    }
    switch (descriptor.widget || descriptor.type) {
        case 'integer':
            var tmp;
            do {
                tmp = value;
                value = value.replace(openerp.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== value);
            tmp = Number(value);
            if (isNaN(tmp))
                throw new Error(value + " is not a correct integer");
            return tmp;
        case 'float':
            var tmp = Number(value);
            if (!isNaN(tmp))
                return tmp;

            var tmp2 = value;
            do {
                tmp = tmp2;
                tmp2 = tmp.replace(openerp.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== tmp2);
            var reformatted_value = tmp.replace(openerp.web._t.database.parameters.decimal_point, ".");
            var parsed = Number(reformatted_value);
            if (isNaN(parsed))
                throw new Error(value + " is not a correct float");
            return parsed;
        case 'float_time':
            var factor = 1;
            if (value[0] === '-') {
                value = value.slice(1);
                factor = -1;
            }
            var float_time_pair = value.split(":");
            if (float_time_pair.length != 2)
                return factor * openerp.web.parse_value(value, {type: "float"});
            var hours = openerp.web.parse_value(float_time_pair[0], {type: "integer"});
            var minutes = openerp.web.parse_value(float_time_pair[1], {type: "integer"});
            return factor * (hours + (minutes / 60));
        case 'progressbar':
            return openerp.web.parse_value(value, {type: "float"});
        case 'datetime':
            var datetime = Date.parseExact(
                    value, (date_pattern + ' ' + time_pattern));
            if (datetime !== null)
                return openerp.web.datetime_to_str(datetime);
            datetime = Date.parse(value);
            if (datetime !== null)
                return openerp.web.datetime_to_str(datetime);
            throw new Error(value + " is not a valid datetime");
        case 'date':
            var date = Date.parseExact(value, date_pattern);
            if (date !== null)
                return openerp.web.date_to_str(date);
            date = Date.parse(value);
            if (date !== null)
                return openerp.web.date_to_str(date);
            throw new Error(value + " is not a valid date");
        case 'time':
            var time = Date.parseExact(value, time_pattern);
            if (time !== null)
                return openerp.web.time_to_str(time);
            time = Date.parse(value);
            if (time !== null)
                return openerp.web.time_to_str(time);
            throw new Error(value + " is not a valid time");
    }
    return value;
};

openerp.web.auto_str_to_date = function(value, type) {
    try {
        return openerp.web.str_to_datetime(value);
    } catch(e) {}
    try {
        return openerp.web.str_to_date(value);
    } catch(e) {}
    try {
        return openerp.web.str_to_time(value);
    } catch(e) {}
    throw new Error("'" + value + "' is not a valid date, datetime nor time");
};

openerp.web.auto_date_to_str = function(value, type) {
    switch(type) {
        case 'datetime':
            return openerp.web.datetime_to_str(value);
        case 'date':
            return openerp.web.date_to_str(value);
        case 'time':
            return openerp.web.time_to_str(value);
        default:
            throw new Error(type + " is not convertible to date, datetime nor time");
    }
};

/**
 * Formats a provided cell based on its field type. Most of the field types
 * return a correctly formatted value, but some tags and fields are
 * special-cased in their handling:
 *
 * * buttons will return an actual ``<button>`` tag with a bunch of error handling
 *
 * * boolean fields will return a checkbox input, potentially disabled
 *
 * * binary fields will return a link to download the binary data as a file
 *
 * @param {Object} row_data record whose values should be displayed in the cell
 * @param {Object} column column descriptor
 * @param {"button"|"field"} column.tag base control type
 * @param {String} column.type widget type for a field control
 * @param {String} [column.string] button label
 * @param {String} [column.icon] button icon
 * @param {Object} [options]
 * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
 * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
 * @param {String} [options.model] current record's model
 * @param {Number} [options.id] current record's id
 *
 */
openerp.web.format_cell = function (row_data, column, options) {
    options = options || {};
    var attrs = {};
    if (options.process_modifiers !== false) {
        attrs = column.modifiers_for(row_data);
    }
    if (attrs.invisible) { return ''; }

    if (column.tag === 'button') {
        return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
            '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
            '</button>', {
                title: column.string || '',
                additional_attributes: isNaN(row_data["id"].value) && openerp.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
                    'disabled="disabled" class="oe-listview-button-disabled"' : '',
                prefix: openerp.connection.prefix,
                icon: column.icon,
                alt: column.string || ''
            });
    }
    if (!row_data[column.id]) {
        return options.value_if_empty === undefined ? '' : options.value_if_empty;
    }

    switch (column.widget || column.type) {
    case "boolean":
        return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
                 row_data[column.id].value ? 'checked="checked"' : '');
    case "binary":
        var text = _t("Download"),
            download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', openerp.connection.session_id, options.model, column.id, options.id);
        if (column.filename) {
            download_url += '&filename_field=' + column.filename;
            if (row_data[column.filename]) {
                text = _.str.sprintf(_t("Download \"%s\""), openerp.web.format_value(
                        row_data[column.filename].value, {type: 'char'}));
            }
        }
        return _.template('<a href="<%-href%>"><%-text%> (%<-size%>)', {
            text: text,
            href: download_url,
            size: row_data[column.id].value
        });
    case 'progressbar':
        return _.template(
            '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
                value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
            });
    }

    return _.escape(openerp.web.format_value(
            row_data[column.id].value, column, options.value_if_empty));
}

};
<commit_msg>[imp] refactored formats.js<commit_after>
openerp.web.formats = function(instance) {
var _t = instance.web._t;

/**
 * Intersperses ``separator`` in ``str`` at the positions indicated by
 * ``indices``.
 *
 * ``indices`` is an array of relative offsets (from the previous insertion
 * position, starting from the end of the string) at which to insert
 * ``separator``.
 *
 * There are two special values:
 *
 * ``-1``
 *   indicates the insertion should end now
 * ``0``
 *   indicates that the previous section pattern should be repeated (until all
 *   of ``str`` is consumed)
 *
 * @param {String} str
 * @param {Array<Number>} indices
 * @param {String} separator
 * @returns {String}
 */
instance.web.intersperse = function (str, indices, separator) {
    separator = separator || '';
    var result = [], last = str.length;

    for(var i=0; i<indices.length; ++i) {
        var section = indices[i];
        if (section === -1 || last <= 0) {
            // Done with string, or -1 (stops formatting string)
            break;
        } else if(section === 0 && i === 0) {
            // repeats previous section, which there is none => stop
            break;
        } else if (section === 0) {
            // repeat previous section forever
            //noinspection AssignmentToForLoopParameterJS
            section = indices[--i];
        }
        result.push(str.substring(last-section, last));
        last -= section;
    }

    var s = str.substring(0, last);
    if (s) { result.push(s); }
    return result.reverse().join(separator);
};
/**
 * Insert "thousands" separators in the provided number (which is actually
 * a string)
 *
 * @param {String} num
 * @returns {String}
 */
instance.web.insert_thousand_seps = function (num) {
    var negative = num[0] === '-';
    num = (negative ? num.slice(1) : num);
    return (negative ? '-' : '') + instance.web.intersperse(
        num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep);
};

/**
 * removes literal (non-format) text from a date or time pattern, as datejs can
 * not deal with literal text in format strings (whatever the format), whereas
 * strftime allows for literal characters
 *
 * @param {String} value original format
 */
instance.web.strip_raw_chars = function (value) {
    var isletter = /[a-zA-Z]/, output = [];
    for(var index=0; index < value.length; ++index) {
        var character = value[index];
        if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) {
            continue;
        }
        output.push(character);
    }
    return output.join('');
};
var normalize_format = function (format) {
    return Date.normalizeFormat(instance.web.strip_raw_chars(format));
};
/**
 * Formats a single atomic value based on a field descriptor
 *
 * @param {Object} value read from OpenERP
 * @param {Object} descriptor union of orm field and view field
 * @param {Object} [descriptor.widget] widget to use to display the value
 * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown
 * @param {Object} [descriptor.digits] used for the formatting of floats
 * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty
 */
instance.web.format_value = function (value, descriptor, value_if_empty) {
    // If NaN value, display as with a `false` (empty cell)
    if (typeof value === 'number' && isNaN(value)) {
        value = false;
    }
    //noinspection FallthroughInSwitchStatementJS
    switch (value) {
        case '':
            if (descriptor.type === 'char') {
                return '';
            }
            console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
        case false:
        case Infinity:
        case -Infinity:
            return value_if_empty === undefined ?  '' : value_if_empty;
    }
    var l10n = _t.database.parameters;
    switch (descriptor.widget || descriptor.type) {
        case 'id':
            return value.toString();
        case 'integer':
            return instance.web.insert_thousand_seps(
                _.str.sprintf('%d', value));
        case 'float':
            var precision = descriptor.digits ? descriptor.digits[1] : 2;
            var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
            formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
            return formatted.join(l10n.decimal_point);
        case 'float_time':
            var pattern = '%02d:%02d';
            if (value < 0) {
                value = Math.abs(value);
                pattern = '-' + pattern;
            }
            return _.str.sprintf(pattern,
                    Math.floor(value),
                    Math.round((value % 1) * 60));
        case 'many2one':
            // name_get value format
            return value[1];
        case 'datetime':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);

            return value.toString(normalize_format(l10n.date_format)
                        + ' ' + normalize_format(l10n.time_format));
        case 'date':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.date_format));
        case 'time':
            if (typeof(value) == "string")
                value = instance.web.auto_str_to_date(value);
            return value.toString(normalize_format(l10n.time_format));
        case 'selection': case 'statusbar':
            // Each choice is [value, label]
            if(_.isArray(value)) {
                 value = value[0]
            }
            var result = _(descriptor.selection).detect(function (choice) {
                return choice[0] === value;
            });
            if (result) { return result[1]; }
            return;
        default:
            return value;
    }
};

instance.web.parse_value = function (value, descriptor, value_if_empty) {
    var date_pattern = normalize_format(_t.database.parameters.date_format),
        time_pattern = normalize_format(_t.database.parameters.time_format);
    switch (value) {
        case false:
        case "":
            return value_if_empty === undefined ?  false : value_if_empty;
    }
    switch (descriptor.widget || descriptor.type) {
        case 'integer':
            var tmp;
            do {
                tmp = value;
                value = value.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== value);
            tmp = Number(value);
            if (isNaN(tmp))
                throw new Error(value + " is not a correct integer");
            return tmp;
        case 'float':
            var tmp = Number(value);
            if (!isNaN(tmp))
                return tmp;

            var tmp2 = value;
            do {
                tmp = tmp2;
                tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, "");
            } while(tmp !== tmp2);
            var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, ".");
            var parsed = Number(reformatted_value);
            if (isNaN(parsed))
                throw new Error(value + " is not a correct float");
            return parsed;
        case 'float_time':
            var factor = 1;
            if (value[0] === '-') {
                value = value.slice(1);
                factor = -1;
            }
            var float_time_pair = value.split(":");
            if (float_time_pair.length != 2)
                return factor * instance.web.parse_value(value, {type: "float"});
            var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"});
            var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"});
            return factor * (hours + (minutes / 60));
        case 'progressbar':
            return instance.web.parse_value(value, {type: "float"});
        case 'datetime':
            var datetime = Date.parseExact(
                    value, (date_pattern + ' ' + time_pattern));
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            datetime = Date.parse(value);
            if (datetime !== null)
                return instance.web.datetime_to_str(datetime);
            throw new Error(value + " is not a valid datetime");
        case 'date':
            var date = Date.parseExact(value, date_pattern);
            if (date !== null)
                return instance.web.date_to_str(date);
            date = Date.parse(value);
            if (date !== null)
                return instance.web.date_to_str(date);
            throw new Error(value + " is not a valid date");
        case 'time':
            var time = Date.parseExact(value, time_pattern);
            if (time !== null)
                return instance.web.time_to_str(time);
            time = Date.parse(value);
            if (time !== null)
                return instance.web.time_to_str(time);
            throw new Error(value + " is not a valid time");
    }
    return value;
};

instance.web.auto_str_to_date = function(value, type) {
    try {
        return instance.web.str_to_datetime(value);
    } catch(e) {}
    try {
        return instance.web.str_to_date(value);
    } catch(e) {}
    try {
        return instance.web.str_to_time(value);
    } catch(e) {}
    throw new Error("'" + value + "' is not a valid date, datetime nor time");
};

instance.web.auto_date_to_str = function(value, type) {
    switch(type) {
        case 'datetime':
            return instance.web.datetime_to_str(value);
        case 'date':
            return instance.web.date_to_str(value);
        case 'time':
            return instance.web.time_to_str(value);
        default:
            throw new Error(type + " is not convertible to date, datetime nor time");
    }
};

/**
 * Formats a provided cell based on its field type. Most of the field types
 * return a correctly formatted value, but some tags and fields are
 * special-cased in their handling:
 *
 * * buttons will return an actual ``<button>`` tag with a bunch of error handling
 *
 * * boolean fields will return a checkbox input, potentially disabled
 *
 * * binary fields will return a link to download the binary data as a file
 *
 * @param {Object} row_data record whose values should be displayed in the cell
 * @param {Object} column column descriptor
 * @param {"button"|"field"} column.tag base control type
 * @param {String} column.type widget type for a field control
 * @param {String} [column.string] button label
 * @param {String} [column.icon] button icon
 * @param {Object} [options]
 * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
 * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
 * @param {String} [options.model] current record's model
 * @param {Number} [options.id] current record's id
 *
 */
instance.web.format_cell = function (row_data, column, options) {
    options = options || {};
    var attrs = {};
    if (options.process_modifiers !== false) {
        attrs = column.modifiers_for(row_data);
    }
    if (attrs.invisible) { return ''; }

    if (column.tag === 'button') {
        return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
            '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
            '</button>', {
                title: column.string || '',
                additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
                    'disabled="disabled" class="oe-listview-button-disabled"' : '',
                prefix: instance.connection.prefix,
                icon: column.icon,
                alt: column.string || ''
            });
    }
    if (!row_data[column.id]) {
        return options.value_if_empty === undefined ? '' : options.value_if_empty;
    }

    switch (column.widget || column.type) {
    case "boolean":
        return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
                 row_data[column.id].value ? 'checked="checked"' : '');
    case "binary":
        var text = _t("Download"),
            download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id);
        if (column.filename) {
            download_url += '&filename_field=' + column.filename;
            if (row_data[column.filename]) {
                text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
                        row_data[column.filename].value, {type: 'char'}));
            }
        }
        return _.template('<a href="<%-href%>"><%-text%> (%<-size%>)', {
            text: text,
            href: download_url,
            size: row_data[column.id].value
        });
    case 'progressbar':
        return _.template(
            '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
                value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
            });
    }

    return _.escape(instance.web.format_value(
            row_data[column.id].value, column, options.value_if_empty));
}

};
<|endoftext|>


<commit_before>'use strict'

module.exports = {
  extends: ['plugin:flowtype/recommended'],
  plugins: ['eslint-plugin-flowtype'],
  rules: {
    // no trailing comma
    'flowtype/delimiter-dangle': ['error', 'never'],
    // avoid sort by import-sort
    'flowtype/newline-after-flow-annotation': ['error', 'always'],
    // no duplicated key
    'flowtype/no-dupe-keys': 'error',
    // existential type is deprecated
    'flowtype/no-existential-type': 'error',
    // use boolean instead of Boolean
    'flowtype/no-primitive-constructor-types': 'error',
    'flowtype/no-unused-expressions': 'error',
    'flowtype/no-weak-types': [
      'error',
      {
        any: false,
        Object: false
      }
    ],
    // use `,` for object separator
    'flowtype/object-type-delimiter': 'error',
    // allow HOC passthrough and merging object type (exact type doesn't support)
    'flowtype/require-exact-type': 'off',
    'flowtype/require-valid-file-annotation': ['error', 'never', {annotationStyle: 'line'}],
    'flowtype/semi': ['error', 'never'],
    'flowtype/sort-keys': 'error'
  }
}
<commit_msg>fix: allow block flow annotation<commit_after>'use strict'

module.exports = {
  extends: ['plugin:flowtype/recommended'],
  plugins: ['eslint-plugin-flowtype'],
  rules: {
    // no trailing comma
    'flowtype/delimiter-dangle': ['error', 'never'],
    // avoid sort by import-sort
    'flowtype/newline-after-flow-annotation': ['error', 'always'],
    // no duplicated key
    'flowtype/no-dupe-keys': 'error',
    // existential type is deprecated
    'flowtype/no-existential-type': 'error',
    // use boolean instead of Boolean
    'flowtype/no-primitive-constructor-types': 'error',
    'flowtype/no-unused-expressions': 'error',
    'flowtype/no-weak-types': [
      'error',
      {
        any: false,
        Object: false
      }
    ],
    // use `,` for object separator
    'flowtype/object-type-delimiter': 'error',
    // allow HOC passthrough and merging object type (exact type doesn't support)
    'flowtype/require-exact-type': 'off',
    'flowtype/require-valid-file-annotation': [
      'error',
      'never',
      // allow /* flow */ because some packages may require to be the first annotation in the file
      // such as /* @jest-environment: node */
      {annotationStyle: 'none'}
    ],
    'flowtype/semi': ['error', 'never'],
    'flowtype/sort-keys': 'error'
  }
}
<|endoftext|>


<commit_before>import assign from 'lodash/assign'
import Bluebird from 'bluebird'
import every from 'lodash/every'
import filter from 'lodash/filter'
import isArray from 'lodash/isArray'
import isPlainObject from 'lodash/isPlainObject'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import size from 'lodash/size'
import some from 'lodash/some'
import { BaseError } from 'make-error'

import { crossProduct } from './math'
import {
  serializeError,
  thunkToArray
} from './utils'

export class JobExecutorError extends BaseError {}
export class UnsupportedJobType extends JobExecutorError {
  constructor (job) {
    super('Unknown job type: ' + job.type)
  }
}
export class UnsupportedVectorType extends JobExecutorError {
  constructor (vector) {
    super('Unknown vector type: ' + vector.type)
  }
}

// ===================================================================

const match = (pattern, value) => {
  if (isPlainObject(pattern)) {
    if (pattern.__or && size(pattern) === 1) {
      return some(pattern.__or, subpattern => match(subpattern, value))
    }

    return isPlainObject(value) && every(pattern, (subpattern, key) => (
      value[key] !== undefined && match(subpattern, value[key])
    ))
  }

  if (isArray(pattern)) {
    return isArray(value) && every(pattern, subpattern =>
      some(value, subvalue => match(subpattern, subvalue))
    )
  }

  return pattern === value
}

const paramsVectorActionsMap = {
  extractProperties ({ mapping, value }) {
    return mapValues(mapping, key => value[key])
  },
  crossProduct ({ items }) {
    return thunkToArray(crossProduct(
      map(items, value => resolveParamsVector.call(this, value))
    ))
  },
  fetchObjects ({ pattern }) {
    return filter(this.xo.getObjects(), object => match(pattern, object))
  },
  map ({ collection, iteratee, paramName = 'value' }) {
    return map(resolveParamsVector.call(this, collection), value => {
      return resolveParamsVector.call(this, {
        ...iteratee,
        [paramName]: value
      })
    })
  },
  set: ({ values }) => values
}

export function resolveParamsVector (paramsVector) {
  const visitor = paramsVectorActionsMap[paramsVector.type]
  if (!visitor) {
    throw new Error(`Unsupported function '${paramsVector.type}'.`)
  }

  return visitor.call(this, paramsVector)
}

// ===================================================================

export default class JobExecutor {
  constructor (xo) {
    this.xo = xo
    this._extractValueCb = {
      'set': items => items.values
    }

    // The logger is not available until Xo has started.
    xo.on('start', () => xo.getLogger('jobs').then(logger => {
      this._logger = logger
    }))
  }

  async exec (job) {
    const runJobId = this._logger.notice(`Starting execution of ${job.id}.`, {
      event: 'job.start',
      userId: job.userId,
      jobId: job.id,
      key: job.key
    })

    try {
      if (job.type === 'call') {
        const execStatus = await this._execCall(job, runJobId)

        this.xo.emit('job:terminated', execStatus)
      } else {
        throw new UnsupportedJobType(job)
      }

      this._logger.notice(`Execution terminated for ${job.id}.`, {
        event: 'job.end',
        runJobId
      })
    } catch (error) {
      this._logger.error(`The execution of ${job.id} has failed.`, {
        event: 'job.end',
        runJobId,
        error: serializeError(error)
      })

      throw error
    }
  }

  async _execCall (job, runJobId) {
    const { paramsVector } = job
    const paramsFlatVector = paramsVector
      ? resolveParamsVector.call(this, paramsVector)
      : [{}] // One call with no parameters

    const connection = this.xo.createUserConnection()

    connection.set('user_id', job.userId)

    const execStatus = {
      runJobId,
      start: Date.now(),
      calls: {}
    }

    await Bluebird.map(paramsFlatVector, params => {
      const runCallId = this._logger.notice(`Starting ${job.method} call. (${job.id})`, {
        event: 'jobCall.start',
        runJobId,
        method: job.method,
        params
      })

      const call = execStatus.calls[runCallId] = {
        method: job.method,
        params,
        start: Date.now()
      }

      return this.xo.callApiMethod(connection, job.method, assign({}, params)).then(
        value => {
          this._logger.notice(`Call ${job.method} (${runCallId}) is a success. (${job.id})`, {
            event: 'jobCall.end',
            runJobId,
            runCallId,
            returnedValue: value
          })

          call.returnedValue = value
          call.end = Date.now()
        },
        reason => {
          this._logger.notice(`Call ${job.method} (${runCallId}) has failed. (${job.id})`, {
            event: 'jobCall.end',
            runJobId,
            runCallId,
            error: serializeError(reason)
          })

          call.error = reason
          call.end = Date.now()
        }
      )
    }, {
      concurrency: 2
    })

    connection.close()
    execStatus.end = Date.now()

    return execStatus
  }
}
<commit_msg>feat(job-executor.match): __not pattern property (#459)<commit_after>import assign from 'lodash/assign'
import Bluebird from 'bluebird'
import every from 'lodash/every'
import filter from 'lodash/filter'
import isArray from 'lodash/isArray'
import isPlainObject from 'lodash/isPlainObject'
import map from 'lodash/map'
import mapValues from 'lodash/mapValues'
import size from 'lodash/size'
import some from 'lodash/some'
import { BaseError } from 'make-error'

import { crossProduct } from './math'
import {
  serializeError,
  thunkToArray
} from './utils'

export class JobExecutorError extends BaseError {}
export class UnsupportedJobType extends JobExecutorError {
  constructor (job) {
    super('Unknown job type: ' + job.type)
  }
}
export class UnsupportedVectorType extends JobExecutorError {
  constructor (vector) {
    super('Unknown vector type: ' + vector.type)
  }
}

// ===================================================================

const match = (pattern, value) => {
  if (isPlainObject(pattern)) {
    if (size(pattern) === 1) {
      if (pattern.__or) {
        return some(pattern.__or, subpattern => match(subpattern, value))
      }
      if (pattern.__not) {
        return !match(pattern.__not, value)
      }
    }

    return isPlainObject(value) && every(pattern, (subpattern, key) => (
      value[key] !== undefined && match(subpattern, value[key])
    ))
  }

  if (isArray(pattern)) {
    return isArray(value) && every(pattern, subpattern =>
      some(value, subvalue => match(subpattern, subvalue))
    )
  }

  return pattern === value
}

const paramsVectorActionsMap = {
  extractProperties ({ mapping, value }) {
    return mapValues(mapping, key => value[key])
  },
  crossProduct ({ items }) {
    return thunkToArray(crossProduct(
      map(items, value => resolveParamsVector.call(this, value))
    ))
  },
  fetchObjects ({ pattern }) {
    return filter(this.xo.getObjects(), object => match(pattern, object))
  },
  map ({ collection, iteratee, paramName = 'value' }) {
    return map(resolveParamsVector.call(this, collection), value => {
      return resolveParamsVector.call(this, {
        ...iteratee,
        [paramName]: value
      })
    })
  },
  set: ({ values }) => values
}

export function resolveParamsVector (paramsVector) {
  const visitor = paramsVectorActionsMap[paramsVector.type]
  if (!visitor) {
    throw new Error(`Unsupported function '${paramsVector.type}'.`)
  }

  return visitor.call(this, paramsVector)
}

// ===================================================================

export default class JobExecutor {
  constructor (xo) {
    this.xo = xo
    this._extractValueCb = {
      'set': items => items.values
    }

    // The logger is not available until Xo has started.
    xo.on('start', () => xo.getLogger('jobs').then(logger => {
      this._logger = logger
    }))
  }

  async exec (job) {
    const runJobId = this._logger.notice(`Starting execution of ${job.id}.`, {
      event: 'job.start',
      userId: job.userId,
      jobId: job.id,
      key: job.key
    })

    try {
      if (job.type === 'call') {
        const execStatus = await this._execCall(job, runJobId)

        this.xo.emit('job:terminated', execStatus)
      } else {
        throw new UnsupportedJobType(job)
      }

      this._logger.notice(`Execution terminated for ${job.id}.`, {
        event: 'job.end',
        runJobId
      })
    } catch (error) {
      this._logger.error(`The execution of ${job.id} has failed.`, {
        event: 'job.end',
        runJobId,
        error: serializeError(error)
      })

      throw error
    }
  }

  async _execCall (job, runJobId) {
    const { paramsVector } = job
    const paramsFlatVector = paramsVector
      ? resolveParamsVector.call(this, paramsVector)
      : [{}] // One call with no parameters

    const connection = this.xo.createUserConnection()

    connection.set('user_id', job.userId)

    const execStatus = {
      runJobId,
      start: Date.now(),
      calls: {}
    }

    await Bluebird.map(paramsFlatVector, params => {
      const runCallId = this._logger.notice(`Starting ${job.method} call. (${job.id})`, {
        event: 'jobCall.start',
        runJobId,
        method: job.method,
        params
      })

      const call = execStatus.calls[runCallId] = {
        method: job.method,
        params,
        start: Date.now()
      }

      return this.xo.callApiMethod(connection, job.method, assign({}, params)).then(
        value => {
          this._logger.notice(`Call ${job.method} (${runCallId}) is a success. (${job.id})`, {
            event: 'jobCall.end',
            runJobId,
            runCallId,
            returnedValue: value
          })

          call.returnedValue = value
          call.end = Date.now()
        },
        reason => {
          this._logger.notice(`Call ${job.method} (${runCallId}) has failed. (${job.id})`, {
            event: 'jobCall.end',
            runJobId,
            runCallId,
            error: serializeError(reason)
          })

          call.error = reason
          call.end = Date.now()
        }
      )
    }, {
      concurrency: 2
    })

    connection.close()
    execStatus.end = Date.now()

    return execStatus
  }
}
<|endoftext|>