<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

 /* eslint no-console:0 */

import React from 'react';
// because the component is wrapped, can't use shallow
import { mount, shallow } from 'enzyme';

import CollapsibleContainer from '../../../src/components/settings/common/CollapsibleContainer';
import NonTandem from '../../../src/components/settings/NonTandem';
import { MGDL_UNITS } from '../../../src/utils/constants';

const animasFlatRateData = require('../../../data/pumpSettings/animas/flatrate.json');
const animasMultiRateData = require('../../../data/pumpSettings/animas/multirate.json');
const omnipodFlatRateData = require('../../../data/pumpSettings/omnipod/flatrate.json');
const omnipodMultiRateData = require('../../../data/pumpSettings/omnipod/multirate.json');
const medtronicMultiRateData = require('../../../data/pumpSettings/medtronic/multirate.json');

const timePrefs = { timezoneAware: false, timezoneName: 'Europe/London' };

describe('NonTandem', () => {
  const activeAtUploadText = 'Active at upload';

  describe('Animas', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have Animas as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('Animas');
    });

    // these tables are the bolus settings + basal schedules
    it('should have six Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(6);
    });

    // these containers are the basal schedules
    it('should have three CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(3);
    });

    it('should preserve user capitalization of schedule name', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasFlatRateData.activeSchedule]: true }}
          pumpSettings={animasFlatRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('normal') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Weekday') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });
  });

  describe('Insulet', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have OmniPod as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('OmniPod');
    });

    // these tables are the bolus settings + basal schedules
    it('should have five Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(5);
    });

    // these containers are the basal schedules
    it('should have two CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(2);
    });

    it('should preserve user capitalization of schedule name', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodFlatRateData.activeSchedule]: true }}
          pumpSettings={omnipodFlatRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('normal') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });
  });

  describe('CareLink/Medtronic', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have Medtronic as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('Medtronic');
    });

    // these tables are the bolus settings + basal schedules
    it('should have six Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(6);
    });

    // these containers are the basal schedules
    it('should have three CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(3);
    });

    it('should capitalize all basal schedule names', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Standard') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Pattern A') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Pattern B') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });

    it('should also render w/o error with `medtronic` as the deviceKey', () => {
      console.error = sinon.spy();
      expect(console.error.callCount).to.equal(0);
      shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'medtronic'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(console.error.callCount).to.equal(0);
    });
  });
});
<commit_msg>add regression tests for values appearing in non-Tandem bolus settings<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2016, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

 /* eslint no-console:0 */

import React from 'react';
// because the component is wrapped, can't use shallow
import { mount, shallow } from 'enzyme';

import CollapsibleContainer from '../../../src/components/settings/common/CollapsibleContainer';
import NonTandem from '../../../src/components/settings/NonTandem';
import { MGDL_UNITS, MMOLL_UNITS } from '../../../src/utils/constants';
import { displayDecimal } from '../../../src/utils/format';

const animasFlatRateData = require('../../../data/pumpSettings/animas/flatrate.json');
const animasMultiRateData = require('../../../data/pumpSettings/animas/multirate.json');
const omnipodFlatRateData = require('../../../data/pumpSettings/omnipod/flatrate.json');
const omnipodMultiRateData = require('../../../data/pumpSettings/omnipod/multirate.json');
const medtronicMultiRateData = require('../../../data/pumpSettings/medtronic/multirate.json');

const timePrefs = { timezoneAware: false, timezoneName: 'Europe/London' };

describe('NonTandem', () => {
  const activeAtUploadText = 'Active at upload';

  describe('Animas', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have Animas as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('Animas');
    });

    // these tables are the bolus settings + basal schedules
    it('should have six Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(6);
    });

    // these containers are the basal schedules
    it('should have three CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(3);
    });

    it('should preserve user capitalization of schedule name', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasFlatRateData.activeSchedule]: true }}
          pumpSettings={animasFlatRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('normal') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Weekday') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'animas'}
          openedSections={{ [animasMultiRateData.activeSchedule]: true }}
          pumpSettings={animasMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });

    describe('bolus settings', () => {
      it('should surface the expected value for ISF', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'animas'}
            openedSections={{ [animasMultiRateData.activeSchedule]: true }}
            pumpSettings={animasMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const isfTable = wrapper.find('table').filterWhere(n => (n.text().search('ISF') !== -1));
        expect(isfTable.someWhere(
          n => (n.text().search(
            displayDecimal(animasMultiRateData.insulinSensitivity[0].amount, 1)
          ) !== -1)
        )).to.be.true;
      });

      it('should surface the expected target & range values for BG Target', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'animas'}
            openedSections={{ [animasMultiRateData.activeSchedule]: true }}
            pumpSettings={animasMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const bgTargetTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('BG Target') !== -1));
        expect(bgTargetTable.someWhere(
          n => (n.text().search(displayDecimal(animasMultiRateData.bgTarget[0].target, 1)) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(displayDecimal(animasMultiRateData.bgTarget[0].range, 1)) !== -1)
        )).to.be.true;
      });

      it('should surface the expected value for I:C Ratio', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'animas'}
            openedSections={{ [animasMultiRateData.activeSchedule]: true }}
            pumpSettings={animasMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const carbRatioTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('I:C Ratio') !== -1));
        expect(carbRatioTable.someWhere(
          n => (n.text().search(animasMultiRateData.carbRatio[0].amount) !== -1)
        )).to.be.true;
      });
    });
  });

  describe('Insulet', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have OmniPod as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('OmniPod');
    });

    // these tables are the bolus settings + basal schedules
    it('should have five Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(5);
    });

    // these containers are the basal schedules
    it('should have two CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(2);
    });

    it('should preserve user capitalization of schedule name', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodFlatRateData.activeSchedule]: true }}
          pumpSettings={omnipodFlatRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('normal') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'insulet'}
          openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
          pumpSettings={omnipodMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });

    describe('bolus settings', () => {
      it('should surface the expected value for Correction factor', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'insulet'}
            openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
            pumpSettings={omnipodMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const isfTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('Correction factor') !== -1));
        expect(isfTable.someWhere(
          n => (n.text().search(
            displayDecimal(omnipodMultiRateData.insulinSensitivity[0].amount, 1)
          ) !== -1)
        )).to.be.true;
      });

      it('should surface the expected target & correct above values for Target BG', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'insulet'}
            openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
            pumpSettings={omnipodMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const bgTargetTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('Target BG') !== -1));
        expect(bgTargetTable.someWhere(
          n => (n.text().search(displayDecimal(omnipodMultiRateData.bgTarget[0].target), 1) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(displayDecimal(omnipodMultiRateData.bgTarget[0].high, 1)) !== -1)
        )).to.be.true;
      });

      it('should surface the expected value for IC ratio', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'insulet'}
            openedSections={{ [omnipodMultiRateData.activeSchedule]: true }}
            pumpSettings={omnipodMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const carbRatioTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('IC ratio') !== -1));
        expect(carbRatioTable.someWhere(
          n => (n.text().search(omnipodMultiRateData.carbRatio[0].amount) !== -1)
        )).to.be.true;
      });
    });
  });

  describe('CareLink/Medtronic', () => {
    it('should have a header', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header')).to.have.length(1);
    });

    it('should have Medtronic as the Header deviceDisplayName', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Header').props().deviceDisplayName).to.equal('Medtronic');
    });

    // these tables are the bolus settings + basal schedules
    it('should have six Tables', () => {
      const wrapper = shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('Table')).to.have.length(6);
    });

    // these containers are the basal schedules
    it('should have three CollapsibleContainers', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find(CollapsibleContainer)).to.have.length(3);
    });

    it('should capitalize all basal schedule names', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Standard') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Pattern A') !== -1)))
        .to.be.true;
      expect(wrapper.find('.label').someWhere(n => (n.text().search('Pattern B') !== -1)))
        .to.be.true;
    });

    it('should have `Active at Upload` text somewhere', () => {
      const wrapper = mount(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'carelink'}
          openedSections={{}}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(wrapper.find('.label').someWhere(n => (n.text().search(activeAtUploadText) !== -1)))
        .to.be.true;
    });

    it('should also render w/o error with `medtronic` as the deviceKey', () => {
      console.error = sinon.spy();
      expect(console.error.callCount).to.equal(0);
      shallow(
        <NonTandem
          bgUnits={MGDL_UNITS}
          deviceKey={'medtronic'}
          openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
          pumpSettings={medtronicMultiRateData}
          timePrefs={timePrefs}
          toggleBasalScheduleExpansion={() => {}}
        />
      );
      expect(console.error.callCount).to.equal(0);
    });

    describe('bolus settings', () => {
      it('should surface the expected value for Sensitivity', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'medtronic'}
            openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
            pumpSettings={medtronicMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const isfTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('Sensitivity') !== -1));
        expect(isfTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.insulinSensitivity[0].amount, 1)
          ) !== -1)
        )).to.be.true;
      });

      it('should surface the expected low & high values for BG Target', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'medtronic'}
            openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
            pumpSettings={medtronicMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const bgTargetTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('BG Target') !== -1));
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[0].low, 1)
          ) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[0].high, 1)
          ) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[1].low, 1)
          ) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[1].high, 1)
          ) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[2].low, 1)
          ) !== -1)
        )).to.be.true;
        expect(bgTargetTable.someWhere(
          n => (n.text().search(
            displayDecimal(medtronicMultiRateData.bgTarget[2].high, 1)
          ) !== -1)
        )).to.be.true;
      });

      it('should surface the expected values for Carb Ratios', () => {
        const wrapper = mount(
          <NonTandem
            bgUnits={MMOLL_UNITS}
            deviceKey={'medtronic'}
            openedSections={{ [medtronicMultiRateData.activeSchedule]: true }}
            pumpSettings={medtronicMultiRateData}
            timePrefs={timePrefs}
            toggleBasalScheduleExpansion={() => {}}
          />
        );
        const carbRatioTable = wrapper.find('table')
          .filterWhere(n => (n.text().search('Carb Ratios') !== -1));
        expect(carbRatioTable.someWhere(
          n => (n.text().search(medtronicMultiRateData.carbRatio[0].amount) !== -1)
        )).to.be.true;
        expect(carbRatioTable.someWhere(
          n => (n.text().search(medtronicMultiRateData.carbRatio[1].amount) !== -1)
        )).to.be.true;
        expect(carbRatioTable.someWhere(
          n => (n.text().search(medtronicMultiRateData.carbRatio[2].amount) !== -1)
        )).to.be.true;
        expect(carbRatioTable.someWhere(
          n => (n.text().search(medtronicMultiRateData.carbRatio[3].amount) !== -1)
        )).to.be.true;
      });
    });
  });
});
<|endoftext|>


<commit_before>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    sha256 = require("sha256"),
    fs = require("fs"),
    knox = require("knox"),
    crypto = require('crypto'),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    querystring = require('querystring'),
    mkdirp = require('mkdirp'),
    _ = require('lodash');

var HTTP_OK = 200,
    HTTP_NOT_MODIFIED = 304;

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this;
    var gruntFinishTask = this.async();

    function createPath(path, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      mkdirp(path, options, function (error, created) {
        if (error) {
          grunt.log.error("Couldn't create %j: %s", path, error);
        }
        callback(error, created);
      });
    }

    function castToArray(thing) {
      if (!thing) {
        return [ ];
      }
      if (Array.isArray(thing)) {
        return thing;
      }
      return [ thing ];
    }

    function formatArgsPrefix(prefix) {
      return prefix ?
        [ "%j: ", prefix ] :
        [ "" ];
    }

    function responseOk(requestName, error, response, allowExtraStatusCodes) {
      var allowStatusCodes = [ HTTP_OK ].concat(castToArray(allowExtraStatusCodes)),
        formatArgs = formatArgsPrefix(requestName),
        ok = true;

      if (error) {
        formatArgs[0] += "Request error: %s";
        formatArgs.push(error);
        ok = false;
      } else if (!response) {
        formatArgs[0] += "No response";
        ok = false;
      } else if (allowStatusCodes.indexOf(response.statusCode) < 0) {
        formatArgs[0] += "Unexpected status code: %s";
        formatArgs.push(response.statusCode);
        ok = false;
      }

      if (!ok) {
        grunt.log.error.apply(grunt.log, formatArgs);
      }
      return ok;
    }

    function tryParseJson(requestName, text) {
      try {
        return JSON.parse(text);
      } catch (error) {
        var formatArgs = formatArgsPrefix(requestName);
        formatArgs[0] += "Error parsing JSON: %s";
        formatArgs.push(error);
        grunt.log.error.apply(grunt.log, formatArgs);
        return null;
      }
    }

    function makeClient( options ) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket', 'secure', 'headers', 'style'
      ]));
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {

      var objQueue = [ obj ],
        prefixQueue = [ "[Root]" ],
        remotePaths = { };

      while (objQueue.length > 0) {
        var thing = objQueue.shift(),
          prefix = prefixQueue.shift();

        // don't need to type check here, only objects and arrays will iterate,
        // which is what we want
        _.forOwn(thing, function (value, key) {
          var keyPath = prefix + "." + key;
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              // rewrite the property in the JSON with the local path
              thing[key] = path.join(mediaCwd, assetPath);

              grunt.verbose.writeln(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath, value, thing[key]
              );

              // record the remote path relative to its root downloading
              // setting it as an object property means dupes are naturally
              // eliminated
              remotePaths[assetPath] = true;
            }
          } else {
            objQueue.push(value);
            prefixQueue.push(keyPath);
          }
        });
      }

      return Object.keys(remotePaths);
    }

    function requestJson(url, options) {
      grunt.verbose.writeln("Downloading JSON");

      request(url, function( error, response, body ) {
        var jsonContent;
        if (responseOk(url, error, response)) {
          jsonContent = tryParseJson(body);
        }

        if (jsonContent && options.out) {
          if( options.mediaOut != null ) {

            saveMedia(options, jsonContent);

          } else {

            writeJsonFile( options.out, jsonContent );
            gruntFinishTask();
            return;

          }
        }

        // we reach here if request failed, json parse failed, or if options.out
        // is not set
        gruntFinishTask();
      });

    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(url, options) {

      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      createPath(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          gruntFinishTask(false);
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        request(url, function(error, response, body) {

          if (responseOk(url, error, response)) {
            var unzipper = new DecompressZip(zipPath);

            unzipper.on("extract", function (log) {

              // TODO: this should be debug, but take a pass over the whole
              // code and decide what should be debug
              grunt.verbose.writeln("%s extract log:\n%j", zipPath, log);

              //on extraction of the zip, check if mediaOut is set, if so, loop through all the unzipped files, and grab down the neccesary media.
              if(options.mediaOut == null) {
                // if not, we're done
                gruntFinishTask();
                return;
              }

              for(var key in log) {
                var unzippedFile = path.join(options.zipOut, log[key].deflated);

                fs.readFile( unzippedFile, function ( error, text ) {
                  if (error) {
                    grunt.log.error("Error reading %j: %s", unzippedFile, error);
                  } else {
                    var body = tryParseJson(text);
                    if (body) {
                      // make a new copy of options, with out set to match
                      // zipOut, as json files get written to options.out
                      var newOptions = _.merge({}, options, { out: unzippedFile });

                      saveMedia(newOptions, body);
                      return;
                    }
                  }
                });

              }

            });

            unzipper.on("error", function(error) {
              grunt.log.error("Error unzipping file %j: %s", zipPath, error);
              gruntFinishTask(false);
            });

            unzipper.extract({
              path: options.zipOut
            });
          } else {
            gruntFinishTask(false);
          }

        }).pipe(fs.createWriteStream(zipPath));

      });

    }

    function saveMedia(options, body) {

      var mediaAssets = getMediaAssets( body, options.mediaCwd );
      var client = makeClient( options.aws );

      grunt.verbose.writeln("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.verbose.writeln("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          writeJsonFile( options.out, body );
          gruntFinishTask();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( client, mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( client, remotePath, mediaOut, callback ) {

      var localPath = path.join(mediaOut, remotePath);
      var localHash = null;

      grunt.verbose.writeln("Verifying %j -> %j", remotePath, localPath);
      fs.readFile( localPath, function ( error, text ) {
        if (error) {
          grunt.verbose.error("Etag calculation of local file failed: %s", error);
        } else {
          localHash = crypto.createHash('md5').update(text).digest('hex');
        }

        createPath(path.dirname(localPath), function (error) {
          if (!error) {
            downloadFile(client, remotePath, localPath, localHash, callback);
          }
        });
      });

    }

    function writeJsonFile(out, body) {
      grunt.log.subhead( "Retrieving content..." );
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(body, null, '\t'));

    }

    function downloadFile(client, remotePath, localPath, etag, callback) {
      var requestHeaders = { };

      grunt.verbose.writeln("S3 GET %j", remotePath);

      if (etag) {
        grunt.verbose.writeln("If-None-Match: %s", etag);
        requestHeaders["If-None-Match"] = etag;
      }

      client.getFile(remotePath, requestHeaders, function (error, response) {
        if (responseOk(remotePath, error, response, HTTP_NOT_MODIFIED)) {
          var file = fs.createWriteStream(localPath);
          file.on("error", function (error) {
            logError("Error writing: %s", error);
            callback();
          });

          response
            .on('error', function (error) {
              logError("Error reading %j: %s", remotePath, error);
              callback();
            })
            .on('end', function () {
              logOk( "downloaded" );
              callback();
            });

          response.pipe(file);
        } else {
          callback();
        }
      });

    }

    function requestFiles(options) {
      grunt.log.subhead( "Retrieving files..." );
      var url = createApiUrl(options, createRequestId());
      grunt.verbose.writeln("Url is %j", url);

      if (isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(url, options);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(url, options);
      }
    }

    function isZipQuery(query) {
      return query.indexOf("dump/zip/") === 0;
    }

    function normalizeDir(path) {
      // TODO: change path string operations to use path lib, and make this
      // function unnecessary
      if (path == null) {
        return null;
      }
      path = String(path);
      if (path === "") {
        return "./";
      }
      if (path.slice(-1) !== "/") {
        return path + "/";
      }
      return path;
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];

      if (isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      options.mediaOut = normalizeDir(options.mediaOut);
      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      } else {
        options.mediaCwd = normalizeDir(options.mediaCwd);
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: messages.length === 0
      };
    }

    function createRequestId() {
      return Date.now().toString(36) +
        (Math.random() * 9007199254740992).toString(36);
    }

    function createToken(secret, key, requestId) {
      return sha256("" + secret + key + requestId);
    }

    function createApiUrl(options, requestId) {
      var url = [
          options.https ? "https" : "http",
          "://",
          options.domain,
          "/api/v",
          options.api,
          "/",
          options.query
        ].join("");

      var queryParams = _.merge({
          requestId: requestId,
          token: createToken(options.secret, options.key, requestId)
        }, options.params || {});

      return url + "?" + querystring.stringify(queryParams);
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");

      if (optionResult.ok) {
        grunt.log.writeln(messagesString);
        requestFiles(optionResult.options);
      } else {
        grunt.log.error(messagesString);
        gruntFinishTask(false);
      }

    }

    return main();
  });
};
<commit_msg>remove unused parameter<commit_after>/*
 * grunt-shampoo
 * https://github.com/soapcreative/grunt-shampoo
 *
 * Copyright (c) 2014 Soap Creative
 * Licensed under the MIT license.
 */

'use strict';

var request = require("request"),
    sha256 = require("sha256"),
    fs = require("fs"),
    knox = require("knox"),
    crypto = require('crypto'),
    rc = require('rc'),
    DecompressZip = require('decompress-zip'),
    fs = require('fs'),
    path = require('path'),
    util = require('util'),
    url = require('url'),
    querystring = require('querystring'),
    mkdirp = require('mkdirp'),
    _ = require('lodash');

var HTTP_OK = 200,
    HTTP_NOT_MODIFIED = 304;

var ZIP_FOLDER_NAME = "content-backups",
    ZIP_FILE_NAME_PREFIX = "content-dump-";

var DEFAULT_MAX_CONNECTIONS = 8;

module.exports = function( grunt ) {

  grunt.registerMultiTask( "shampoo", "Retrieve content from the Shampoo CMS API on shampoo.io.", function() {

    var thisTask = this;
    var gruntFinishTask = this.async();

    function createPath(path, options, callback) {
      if (typeof options === 'function') {
        callback = options;
        options = null;
      }
      mkdirp(path, options, function (error, created) {
        if (error) {
          grunt.log.error("Couldn't create %j: %s", path, error);
        }
        callback(error, created);
      });
    }

    function castToArray(thing) {
      if (!thing) {
        return [ ];
      }
      if (Array.isArray(thing)) {
        return thing;
      }
      return [ thing ];
    }

    function formatArgsPrefix(prefix) {
      return prefix ?
        [ "%j: ", prefix ] :
        [ "" ];
    }

    function responseOk(requestName, error, response, allowExtraStatusCodes) {
      var allowStatusCodes = [ HTTP_OK ].concat(castToArray(allowExtraStatusCodes)),
        formatArgs = formatArgsPrefix(requestName),
        ok = true;

      if (error) {
        formatArgs[0] += "Request error: %s";
        formatArgs.push(error);
        ok = false;
      } else if (!response) {
        formatArgs[0] += "No response";
        ok = false;
      } else if (allowStatusCodes.indexOf(response.statusCode) < 0) {
        formatArgs[0] += "Unexpected status code: %s";
        formatArgs.push(response.statusCode);
        ok = false;
      }

      if (!ok) {
        grunt.log.error.apply(grunt.log, formatArgs);
      }
      return ok;
    }

    function tryParseJson(requestName, text) {
      try {
        return JSON.parse(text);
      } catch (error) {
        var formatArgs = formatArgsPrefix(requestName);
        formatArgs[0] += "Error parsing JSON: %s";
        formatArgs.push(error);
        grunt.log.error.apply(grunt.log, formatArgs);
        return null;
      }
    }

    function makeClient( options ) {
      return knox.createClient( _.pick(options, [
        'region', 'endpoint', 'port', 'key', 'secret', 'access', 'bucket', 'secure', 'headers', 'style'
      ]));
    }

    function isMediaAssetUrl(assetUrl) {
      // a URL is a media asset URL if its scheme/protocol is http or https,
      // and its hostname is a subdomain of amazonaws.com.
      return Boolean(
        assetUrl &&
        /^https?:$/i .test(assetUrl.protocol) &&
        /\.amazonaws\.com$/i .test(assetUrl.hostname)
      );
    }

    function getMediaAssetPath(assetUrlString) {
      var assetUrl;
      try {
        assetUrl = url.parse(assetUrlString);
        if (isMediaAssetUrl(assetUrl)) {
          var pathname = assetUrl.pathname;
          if (pathname.charAt(0) === "/") {
            pathname = pathname.slice(1);
          }
          // url.parse automatically url-escapes characters, but we need to
          // reverse that for passing to the knox client, as well as for saving
          // locally
          return decodeURI(pathname);
        }
      } catch (error) { } // url.parse failed, so just fall through to return null
      return null;
    }

    function getMediaAssets( obj, mediaCwd ) {

      var objQueue = [ obj ],
        prefixQueue = [ "[Root]" ],
        remotePaths = { };

      while (objQueue.length > 0) {
        var thing = objQueue.shift(),
          prefix = prefixQueue.shift();

        // don't need to type check here, only objects and arrays will iterate,
        // which is what we want
        _.forOwn(thing, function (value, key) {
          var keyPath = prefix + "." + key;
          if (typeof value === "string") {
            var assetPath = getMediaAssetPath(value);

            if (assetPath != null) {
              // rewrite the property in the JSON with the local path
              thing[key] = path.join(mediaCwd, assetPath);

              grunt.verbose.writeln(
                "Rewriting %s\n" +
                "  old: %j\n" +
                "  new: %j\n",
                keyPath, value, thing[key]
              );

              // record the remote path relative to its root downloading
              // setting it as an object property means dupes are naturally
              // eliminated
              remotePaths[assetPath] = true;
            }
          } else {
            objQueue.push(value);
            prefixQueue.push(keyPath);
          }
        });
      }

      return Object.keys(remotePaths);
    }

    function requestJson(url, options) {
      grunt.verbose.writeln("Downloading JSON");

      request(url, function( error, response, body ) {
        var jsonContent;
        if (responseOk(url, error, response)) {
          jsonContent = tryParseJson(body);
        }

        if (jsonContent && options.out) {
          if( options.mediaOut != null ) {

            saveMedia(options, jsonContent);

          } else {

            writeJsonFile( options.out, jsonContent );
            gruntFinishTask();
            return;

          }
        }

        // we reach here if request failed, json parse failed, or if options.out
        // is not set
        gruntFinishTask();
      });

    }

    function generateZipFileName() {
      return ZIP_FILE_NAME_PREFIX + Date.now() + ".zip";
    }

    function requestZip(url, options) {

      var zipPath = path.join(
        options.zipOut, ZIP_FOLDER_NAME, generateZipFileName());

      createPath(path.dirname(zipPath), null, function (mkdirError) {
        if (mkdirError) {
          gruntFinishTask(false);
          return;
        }

        grunt.verbose.writeln("Downloading zip");
        request(url, function(error, response) {

          if (responseOk(url, error, response)) {
            var unzipper = new DecompressZip(zipPath);

            unzipper.on("extract", function (log) {

              // TODO: this should be debug, but take a pass over the whole
              // code and decide what should be debug
              grunt.verbose.writeln("%s extract log:\n%j", zipPath, log);

              //on extraction of the zip, check if mediaOut is set, if so, loop through all the unzipped files, and grab down the neccesary media.
              if(options.mediaOut == null) {
                // if not, we're done
                gruntFinishTask();
                return;
              }

              for(var key in log) {
                var unzippedFile = path.join(options.zipOut, log[key].deflated);

                fs.readFile( unzippedFile, function ( error, text ) {
                  if (error) {
                    grunt.log.error("Error reading %j: %s", unzippedFile, error);
                  } else {
                    var body = tryParseJson(text);
                    if (body) {
                      // make a new copy of options, with out set to match
                      // zipOut, as json files get written to options.out
                      var newOptions = _.merge({}, options, { out: unzippedFile });

                      saveMedia(newOptions, body);
                      return;
                    }
                  }
                });

              }

            });

            unzipper.on("error", function(error) {
              grunt.log.error("Error unzipping file %j: %s", zipPath, error);
              gruntFinishTask(false);
            });

            unzipper.extract({
              path: options.zipOut
            });
          } else {
            gruntFinishTask(false);
          }

        }).pipe(fs.createWriteStream(zipPath));

      });

    }

    function saveMedia(options, body) {

      var mediaAssets = getMediaAssets( body, options.mediaCwd );
      var client = makeClient( options.aws );

      grunt.verbose.writeln("Media queue is:");
      mediaAssets.forEach(function (p) {
        grunt.verbose.writeln("  %j", p);
      });

      var loadCounter = 0;
      var next = function() {
        loadCounter--;
        fillQueue();
      };

      var fillQueue = function() {
        if (mediaAssets.length === 0 && loadCounter === 0) {
          writeJsonFile( options.out, body );
          gruntFinishTask();
        } else {
          while (mediaAssets.length > 0 && loadCounter < options.maxConnections) {
            loadCounter++;
            verifyDownload( client, mediaAssets.shift(), options.mediaOut, next );
          }
        }
      };

      fillQueue();
    }

    function verifyDownload( client, remotePath, mediaOut, callback ) {

      var localPath = path.join(mediaOut, remotePath);
      var localHash = null;

      grunt.verbose.writeln("Verifying %j -> %j", remotePath, localPath);
      fs.readFile( localPath, function ( error, text ) {
        if (error) {
          grunt.verbose.error("Etag calculation of local file failed: %s", error);
        } else {
          localHash = crypto.createHash('md5').update(text).digest('hex');
        }

        createPath(path.dirname(localPath), function (error) {
          if (!error) {
            downloadFile(client, remotePath, localPath, localHash, callback);
          }
        });
      });

    }

    function writeJsonFile(out, body) {
      grunt.log.subhead( "Retrieving content..." );
      grunt.log.write( out + " ");
      grunt.log.ok( "saved" );
      grunt.file.write(out, JSON.stringify(body, null, '\t'));

    }

    function downloadFile(client, remotePath, localPath, etag, callback) {
      var requestHeaders = { };

      grunt.verbose.writeln("S3 GET %j", remotePath);

      if (etag) {
        grunt.verbose.writeln("If-None-Match: %s", etag);
        requestHeaders["If-None-Match"] = etag;
      }

      client.getFile(remotePath, requestHeaders, function (error, response) {
        if (responseOk(remotePath, error, response, HTTP_NOT_MODIFIED)) {
          var file = fs.createWriteStream(localPath);
          file.on("error", function (error) {
            logError("Error writing: %s", error);
            callback();
          });

          response
            .on('error', function (error) {
              logError("Error reading %j: %s", remotePath, error);
              callback();
            })
            .on('end', function () {
              logOk( "downloaded" );
              callback();
            });

          response.pipe(file);
        } else {
          callback();
        }
      });

    }

    function requestFiles(options) {
      grunt.log.subhead( "Retrieving files..." );
      var url = createApiUrl(options, createRequestId());
      grunt.verbose.writeln("Url is %j", url);

      if (isZipQuery(options.query)) {
        grunt.verbose.writeln("Zip job");
        requestZip(url, options);
      } else {
        grunt.verbose.writeln("JSON job");
        requestJson(url, options);
      }
    }

    function isZipQuery(query) {
      return query.indexOf("dump/zip/") === 0;
    }

    function normalizeDir(path) {
      // TODO: change path string operations to use path lib, and make this
      // function unnecessary
      if (path == null) {
        return null;
      }
      path = String(path);
      if (path === "") {
        return "./";
      }
      if (path.slice(-1) !== "/") {
        return path + "/";
      }
      return path;
    }

    function getOptions() {
      // Mix in default options, .shampoorc file
      var options = rc("shampoo", thisTask.options({
        api: 1,
        query: "dump/json/single-file",
        out: "data/content.json",
        mediaOut: null,
        mediaCwd: null,
        maxConnections: DEFAULT_MAX_CONNECTIONS
      }));

      var messages = [ ];
      var missing = { };
      var required = [ "key", "secret", "domain", "query", "out" ];

      if (isZipQuery(options.query)) {
        required.push("zipOut");
      }

      required.forEach(function (optionName) {
        if (!options[optionName]) {
          missing[optionName] = true;
        }
      });

      var missingArray = Object.keys(missing);
      if (missingArray.length > 0) {
        messages.push("The following required options are not set: " +
          missingArray.join(", "));
      }

      if (missing.key || missing.secret) {
        messages.push("Values for 'key' and 'secret' are found in your Shampoo account under Settings.");
      }

      if (missing.zipOut) {
        messages.push(util.format(
          "The query %j returns a zip file. This requires the 'zipOut' option to be set.",
          options.query
        ));
      }

      options.mediaOut = normalizeDir(options.mediaOut);
      if (options.mediaCwd == null) {
        options.mediaCwd = options.mediaOut;
      } else {
        options.mediaCwd = normalizeDir(options.mediaCwd);
      }

      var parsedMaxConnections = parseInt(options.maxConnections, 10);
      if (isNaN(options.maxConnections)) {
        messages.push(util.format(
          "Invalid value for maxConnections: %j. Assuming default of %d.",
          options.maxConnections, DEFAULT_MAX_CONNECTIONS
        ));
        parsedMaxConnections = DEFAULT_MAX_CONNECTIONS;
      } else if (parsedMaxConnections < 1) {
        messages.push(
          "Value for maxConnections must be 1 or greater. Assuming 1.");
        parsedMaxConnections = 1;
      }

      options.maxConnections = parsedMaxConnections;

      return {
        options: options,
        messages: messages,
        ok: messages.length === 0
      };
    }

    function createRequestId() {
      return Date.now().toString(36) +
        (Math.random() * 9007199254740992).toString(36);
    }

    function createToken(secret, key, requestId) {
      return sha256("" + secret + key + requestId);
    }

    function createApiUrl(options, requestId) {
      var url = [
          options.https ? "https" : "http",
          "://",
          options.domain,
          "/api/v",
          options.api,
          "/",
          options.query
        ].join("");

      var queryParams = _.merge({
          requestId: requestId,
          token: createToken(options.secret, options.key, requestId)
        }, options.params || {});

      return url + "?" + querystring.stringify(queryParams);
    }

    function main() {
      var optionResult = getOptions();
      var messagesString = optionResult.messages.join("\n");

      if (optionResult.ok) {
        grunt.log.writeln(messagesString);
        requestFiles(optionResult.options);
      } else {
        grunt.log.error(messagesString);
        gruntFinishTask(false);
      }

    }

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


<commit_before>app.controller('fichasCtrl', function($scope, $rootScope, $route, $location, $timeout, $document, SweetAlert, $mdDialog, Fichas){
	// Inicializar variables utilizadas en todo el codigo y que provienen de la sesion del usuario
	$rootScope.currentRoute='Tus Fichas';
	$scope.fichas = [];
	Fichas.all($rootScope.user.id).then(function(fichas) {
		$scope.fichas = fichas;
		console.log(fichas)
	});

	$scope.buscar = function(ev) {
		Fichas.search($scope.busqueda.ficha)
		.then(function(fichas) {
			$scope.fichas = fichas;
		})
		.catch(function (error) {
			SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
		});
	};

	$scope.borrar = function(ficha) {
		var ficha = ficha;
		SweetAlert.swal({
			title: "Estas seguro de querer eliminar a "+ficha.nombre+"?",
			text: "No vas a poder recuperarlo",
			type: "warning",
			showCancelButton: true,
			confirmButtonColor: "#692B8D",
			confirmButtonText: "Si, eliminalo!",
			closeOnConfirm: false
		},function(isConfirmed) {
			if(isConfirmed){
				Fichas.delete(ficha.id).then(function (s) {
					SweetAlert.swal({
						title: "Eliminado!",
						text: ''+ficha.nombre+' ha sido eliminado correctamente',
						type: "success",
						showCancelButton: false,
						confirmButtonColor: "#692B8D",
						confirmButtonText: "Ok!",
						closeOnConfirm: true
					},
					function(){
						console.log("EXITO");
						$route.reload();
					});
		        })
		        .catch(function (error) {
		        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
		        });
			}
		});
	}

	//Modal
	$scope.showAgregarFicha = function(ev) {
		$mdDialog.show({
			controller: function($scope){
				$scope.ficha = {};
				$scope.ficha.materiales = [];
				$scope.ficha.foto = 'img/fpo_avatar_multi.png';
				// Modal Actions
				$scope.cancel = function(){
					$mdDialog.cancel();
				};

				$scope.save = function(){
					console.log($scope.ficha);
					$mdDialog.hide($scope.ficha);
				};

				$scope.add = function($event){
					$event.preventDefault();
				   	$event.stopPropagation();
					$scope.ficha.materiales.push({material: ''});
					$timeout(function() {
						var objDiv = angular.element($document[0].querySelector("#dialog"));
						objDiv[0].scrollTop = objDiv[0].scrollHeight;
						console.log(objDiv)
						console.log(objDiv[0].scrollHeight)
						console.log(objDiv[0].scrollTop)
					});
				}

				$scope.del = function(i, $event){
					$event.preventDefault();
				   	$event.stopPropagation();
					$scope.ficha.materiales.splice(i,1);
				}
			},
			templateUrl: '/dialogs/agregarFichaReme.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
			fullscreen: 'false' // Only for -xs, -sm breakpoints.
		})
		.then(function(ficha) {
			if (ficha.myImage == 'img/fpo_avatar_multi.png') {
				ficha.foto = 'img/fpo_avatar_multi.png';
			}

			ficha.user_id = $rootScope.user.id;
			console.log(ficha);
			Fichas.new(ficha).then(function (ficha) {
				SweetAlert.swal({
					title: "Exito!",
					text: ''+ficha.nombre+' Fue Agregado Correctamente',
					type: "success",
					showCancelButton: false,
					confirmButtonColor: "#692B8D",
					confirmButtonText: "Ok!",
					closeOnConfirm: true
				},
				function(){
					console.log("EXITO");
					$route.reload();
				});
	        })
	        .catch(function (error) {
	        	console.log(error);
	        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
	        });
		}, function() {

		});

	}//end modal
});//end fichasCtrl

app.controller('fichaCtrl',function($scope,$rootScope, $route, $location, SweetAlert, $routeParams, Fichas){

	Fichas.get($routeParams.id).then(function(ficha) {
		// body...
		$scope.ficha = ficha[0];
		console.log($scope.ficha);
		$rootScope.currentRoute=$scope.ficha.nombreactividad;
	});
	// console.log($scope.ficha);

	$scope.save = function(){
		if ($scope.ficha.myImage == 'img/fpo_avatar_multi.png') {
			$scope.ficha.foto = 'img/fpo_avatar_multi.png';
		}
		// 'Exito', ''+ficha.nombre+' Fue Agregad'+s+' Correctamente'
		$scope.ficha.user_id = $rootScope.user.id
		Fichas.update($scope.ficha).then(function (ficha) {
			SweetAlert.swal({
			   title: "Exito!",
			   text: ''+$scope.ficha.nombreactividad+' Fue Editado Correctamente',
			   type: "success",
			   showCancelButton: false,
			   confirmButtonColor: "#692B8D",
			   confirmButtonText: "Ok!",
			   closeOnConfirm: true},
			function(){
				$location.path('/ficha/'+$scope.ficha.id);
			});
        })
        .catch(function (error) {
        	console.log(error)
        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
        });
	};
}); // end fichaCtrl

<commit_msg>Update fichasRemeCtrls.js<commit_after>app.controller('fichasCtrl', function($scope, $rootScope, $route, $location, $timeout, $document, SweetAlert, $mdDialog, Fichas){
	// Inicializar variables utilizadas en todo el codigo y que provienen de la sesion del usuario
	$rootScope.currentRoute='Tus Fichas';
	$scope.fichas = [];
	Fichas.all($rootScope.user.id).then(function(fichas) {
		$scope.fichas = fichas;
		console.log(fichas)
	});

	$scope.buscar = function(ev) {
		Fichas.search($scope.busqueda.ficha)
		.then(function(fichas) {
			$scope.fichas = fichas;
		})
		.catch(function (error) {
			SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
		});
	};

	$scope.borrar = function(ficha) {
		var ficha = ficha;
		SweetAlert.swal({
			title: "Estas seguro de querer eliminar a "+ficha.nombre+"?",
			text: "No vas a poder recuperarlo",
			type: "warning",
			showCancelButton: true,
			confirmButtonColor: "#692B8D",
			confirmButtonText: "Si, eliminalo!",
			closeOnConfirm: false
		},function(isConfirmed) {
			if(isConfirmed){
				Fichas.delete(ficha.id).then(function (s) {
					SweetAlert.swal({
						title: "Eliminado!",
						text: ''+ficha.nombre+' ha sido eliminado correctamente',
						type: "success",
						showCancelButton: false,
						confirmButtonColor: "#692B8D",
						confirmButtonText: "Ok!",
						closeOnConfirm: true
					},
					function(){
						console.log("EXITO");
						$route.reload();
					});
		        })
		        .catch(function (error) {
		        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
		        });
			}
		});
	}

	//Modal
	$scope.showAgregarFicha = function(ev) {
		$mdDialog.show({
			controller: function($scope){
				$scope.ficha = {};
				$scope.ficha.materiales = [];
				$scope.ficha.foto = 'img/fpo_avatar_multi.png';
				// Modal Actions
				$scope.cancel = function(){
					$mdDialog.cancel();
				};

				$scope.save = function(){
					console.log($scope.ficha);
					$mdDialog.hide($scope.ficha);
				};

				$scope.add = function($event){
					$event.preventDefault();
				   	$event.stopPropagation();
					$scope.ficha.materiales.push({material: ''});
				}

				$scope.del = function(i, $event){
					$event.preventDefault();
				   	$event.stopPropagation();
					$scope.ficha.materiales.splice(i,1);
				}
			},
			templateUrl: '/dialogs/agregarFichaReme.html',
			parent: angular.element(document.body),
			targetEvent: ev,
			clickOutsideToClose:true,
			fullscreen: 'false' // Only for -xs, -sm breakpoints.
		})
		.then(function(ficha) {
			if (ficha.myImage == 'img/fpo_avatar_multi.png') {
				ficha.foto = 'img/fpo_avatar_multi.png';
			}

			ficha.user_id = $rootScope.user.id;
			console.log(ficha);
			Fichas.new(ficha).then(function (ficha) {
				SweetAlert.swal({
					title: "Exito!",
					text: ''+ficha.nombre+' Fue Agregado Correctamente',
					type: "success",
					showCancelButton: false,
					confirmButtonColor: "#692B8D",
					confirmButtonText: "Ok!",
					closeOnConfirm: true
				},
				function(){
					console.log("EXITO");
					$route.reload();
				});
	        })
	        .catch(function (error) {
	        	console.log(error);
	        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
	        });
		}, function() {

		});

	}//end modal
});//end fichasCtrl

app.controller('fichaCtrl',function($scope,$rootScope, $route, $location, SweetAlert, $routeParams, Fichas){

	Fichas.get($routeParams.id).then(function(ficha) {
		// body...
		$scope.ficha = ficha[0];
		console.log($scope.ficha);
		$rootScope.currentRoute=$scope.ficha.nombreactividad;
	});
	// console.log($scope.ficha);

	$scope.save = function(){
		if ($scope.ficha.myImage == 'img/fpo_avatar_multi.png') {
			$scope.ficha.foto = 'img/fpo_avatar_multi.png';
		}
		// 'Exito', ''+ficha.nombre+' Fue Agregad'+s+' Correctamente'
		$scope.ficha.user_id = $rootScope.user.id
		Fichas.update($scope.ficha).then(function (ficha) {
			SweetAlert.swal({
			   title: "Exito!",
			   text: ''+$scope.ficha.nombreactividad+' Fue Editado Correctamente',
			   type: "success",
			   showCancelButton: false,
			   confirmButtonColor: "#692B8D",
			   confirmButtonText: "Ok!",
			   closeOnConfirm: true},
			function(){
				$location.path('/ficha/'+$scope.ficha.id);
			});
        })
        .catch(function (error) {
        	console.log(error)
        	SweetAlert.swal("Ooops..", "Ocurrio un error: "+error.data, "error");
        });
	};
}); // end fichaCtrl

<|endoftext|>


<commit_before>/**
_enyo.Object_ implements the Enyo framework's property publishing system, as
well as providing several utility functions for its subkinds.

Published properties are declared in a hash called _published_ within a call
to _enyo.kind_. Getter and setter methods are automatically generated for
properties declared in this manner. Also, by convention, the setter for a
published property will trigger an optional _<propertyName>Changed_ method
when called.

For more information, see the [documentation on Published
Properties](https://github.com/enyojs/enyo/wiki/Published-Properties) in the
Enyo Developer Guide.
*/
enyo.kind({
	name: "enyo.Object",
	//* @protected
	// has no base kind
	kind: null,
	//*@public
	// concatenated properties (default)
	concat: enyo.concat,
	//*@public
	/**
		An array of strings that represent a mixin to be applied
		to this class at the end of the constructor routine.
	*/
	mixins: [
		"enyo.MixinSupport",
		"enyo.ObserverSupport",
		"enyo.ComputedSupport",
		"enyo.BindingSupport"
	],

	constructor: function(props) {
		enyo._objectCount++;
		this.importProps(props);
	},

	importProps: function (props) {
		if (props) {
			for (var key in props) {
				if (!props.hasOwnProperty(key)) continue;
				this[key] = props[key];
			}
		}
	},

	//* @public
	//* Destroys object with passed-in name.
	destroyObject: function(inName) {
		if (this[inName] && this[inName].destroy) {
			this[inName].destroy();
		}
		this[inName] = null;
	},
	/**
		Sends a log message to the console, prepended with the name of the kind
		and method from which _log_ was invoked.  Multiple arguments are coerced
		to String and joined with spaces.

			enyo.kind({
				name: "MyObject",
				kind: enyo.Object,
				hello: function() {
					this.log("says", "hi");
					// shows in the console: MyObject.hello: says hi
				}
			});
	*/
	log: function() {
		var acc = arguments.callee.caller;
		var nom = ((acc ? acc.nom : "") || "(instance method)") + ":";
		enyo.logging.log("log", [nom].concat(enyo.cloneArray(arguments)));
	},
	//* Same as _log_, except uses the console's warn method (if it exists).
	warn: function() {
		this._log("warn", arguments);
	},
	//* Same as _log_, except uses the console's error method (if it exists).
	error: function() {
		this._log("error", arguments);
	},
	//* @protected
	_log: function(inMethod, inArgs) {
		if (enyo.logging.shouldLog(inMethod)) {
			try {
				throw new Error();
			} catch(x) {
				enyo.logging._log(inMethod, [inArgs.callee.caller.nom + ": "].concat(enyo.cloneArray(inArgs)));
				enyo.log(x.stack);
			}
		}
	},
	//*@protected
	/**
		This method accepts a string property as its only parameter.
		The value of this property will be evaluated and if it is itself
		a string the object will attempt to be resolved. The goal is
		to determine of the the property is a constructor, an instance or
		nothing. See _lang.js#enyo.findAndInstance_ for more information.

		If a method exists of the form `{property}FindAndInstance` it will
		be used as the callback accepting two parameters, the constructor
		if it was found and the instance if it was found or created,
		respectively. This allows for those methods to be overloaded by
		subkinds.
	*/
	findAndInstance: function (property) {
		// if there isn't a property do nothing
		if (!enyo.exists(property)) return;
		var fn = this[property + "FindAndInstance"];
		// go ahead and call the enyo scoped version of this method
		return enyo.findAndInstance.call(this, property, fn, this);
	},

	//*@public
	/**
		Call this method with the name (or path) to the desired property or
		computed property. If it encounters a computed property it will return
		the value of that property and not the function. If it cannot find
		or resolve the requested path relative to the object it will return
		undefined.

		This method is backwards compatible and will automatically call any
		existing _getter_ method that uses the getProperty convention although
		this convention ought to be replaced using a computed property moving
		forward.
	*/
	get: function (path) {
		return enyo.getPath.apply(this, arguments);
	},
	//*@public
	/**
		Call this method with a property (or path) and a value to be set. This
		will automatically notify any listeners/observers that the property has
		been changed if the values are not the same. If the property it finds
		is a computed property it will pass the intended value to the computed
		property (but will not return the value).

		This method is backwards compatible and will call any setter of the
		setProperty convention although these methods should be replaced with
		computed properties or observers where necessary.
	*/
	set: function (path, value) {
		return enyo.setPath.apply(this, arguments);
	},

	//*@protected
	destroy: function () {
		// JS objects are never truly destroyed (GC'd) until all references are gone,
		// we might have some delayed action on this object that needs to have access
		// to this flag.
		this.destroyed = true;
	},

	_is_object: true
});

//* @protected

enyo._objectCount = 0;

enyo.Object.subclass = function(ctor, props) {
	this.publish(ctor, props);
};

enyo.Object.publish = function(ctor, props) {
	var pp = props.published;
	if (pp) {
		var cp = ctor.prototype;
		for (var n in pp) {
			// need to make sure that even though a property is "published"
			// it does not overwrite any computed properties
			if (props[n] && enyo.isFunction(props[n]) && props[n].isProperty) continue;
			enyo.Object.addGetterSetter(n, pp[n], cp);
		}
	}
};

//*@protected
/**
	This method creates a getter/setter for a published property of
	an _enyo.Object_ but is deprecated. It is maintained for backwards
	compatability purposes. The prefered method is to mark public and
	protected (private) methods and properties using documentation or
	other means and rely on the _get_ and _set_ methods of _enyo.Object_
	instances.
*/
enyo.Object.addGetterSetter = function (property, value, proto) {
	var getter = "get" + enyo.cap(property);
	var setter = "set" + enyo.cap(property);
	var fn;
	// set the initial value for the prototype
	proto[property] = value;
	fn = proto[getter];
	// if there isn't already a getter provided create one
	if ("function" !== typeof fn) {
		fn = proto[getter] = function () {return this.get(property)};
		fn.overloaded = false;
	} else if (false !== fn.overloaded) {
		// otherwise we need to mark it as having been overloaded
		// so the global getter knows not to ignore it
		fn.overloaded = true;
	}
	// if there isn't already a setter provided create one
	fn = proto[setter];
	if ("function" !== typeof fn) {
		fn = proto[setter] = function () {return this.set(property, arguments[0])};
		fn.overloaded = false;
	} else if (false !== fn.overloaded) {
		// otherwise we need to mark it as having been overloaded
		// so the global setter knows not to ignore it
		fn.overloaded = true;
	}
};
<commit_msg>critical fix for flagging overloaded getter methods originally based on a published property of a parent kind<commit_after>/**
_enyo.Object_ implements the Enyo framework's property publishing system, as
well as providing several utility functions for its subkinds.

Published properties are declared in a hash called _published_ within a call
to _enyo.kind_. Getter and setter methods are automatically generated for
properties declared in this manner. Also, by convention, the setter for a
published property will trigger an optional _<propertyName>Changed_ method
when called.

For more information, see the [documentation on Published
Properties](https://github.com/enyojs/enyo/wiki/Published-Properties) in the
Enyo Developer Guide.
*/
enyo.kind({
	name: "enyo.Object",
	//* @protected
	// has no base kind
	kind: null,
	//*@public
	// concatenated properties (default)
	concat: enyo.concat,
	//*@public
	/**
		An array of strings that represent a mixin to be applied
		to this class at the end of the constructor routine.
	*/
	mixins: [
		"enyo.MixinSupport",
		"enyo.ObserverSupport",
		"enyo.ComputedSupport",
		"enyo.BindingSupport"
	],

	constructor: function(props) {
		enyo._objectCount++;
		this.importProps(props);
	},

	importProps: function (props) {
		if (props) {
			for (var key in props) {
				if (!props.hasOwnProperty(key)) continue;
				this[key] = props[key];
			}
		}
	},

	//* @public
	//* Destroys object with passed-in name.
	destroyObject: function(inName) {
		if (this[inName] && this[inName].destroy) {
			this[inName].destroy();
		}
		this[inName] = null;
	},
	/**
		Sends a log message to the console, prepended with the name of the kind
		and method from which _log_ was invoked.  Multiple arguments are coerced
		to String and joined with spaces.

			enyo.kind({
				name: "MyObject",
				kind: enyo.Object,
				hello: function() {
					this.log("says", "hi");
					// shows in the console: MyObject.hello: says hi
				}
			});
	*/
	log: function() {
		var acc = arguments.callee.caller;
		var nom = ((acc ? acc.nom : "") || "(instance method)") + ":";
		enyo.logging.log("log", [nom].concat(enyo.cloneArray(arguments)));
	},
	//* Same as _log_, except uses the console's warn method (if it exists).
	warn: function() {
		this._log("warn", arguments);
	},
	//* Same as _log_, except uses the console's error method (if it exists).
	error: function() {
		this._log("error", arguments);
	},
	//* @protected
	_log: function(inMethod, inArgs) {
		if (enyo.logging.shouldLog(inMethod)) {
			try {
				throw new Error();
			} catch(x) {
				enyo.logging._log(inMethod, [inArgs.callee.caller.nom + ": "].concat(enyo.cloneArray(inArgs)));
				enyo.log(x.stack);
			}
		}
	},
	//*@protected
	/**
		This method accepts a string property as its only parameter.
		The value of this property will be evaluated and if it is itself
		a string the object will attempt to be resolved. The goal is
		to determine of the the property is a constructor, an instance or
		nothing. See _lang.js#enyo.findAndInstance_ for more information.

		If a method exists of the form `{property}FindAndInstance` it will
		be used as the callback accepting two parameters, the constructor
		if it was found and the instance if it was found or created,
		respectively. This allows for those methods to be overloaded by
		subkinds.
	*/
	findAndInstance: function (property) {
		// if there isn't a property do nothing
		if (!enyo.exists(property)) return;
		var fn = this[property + "FindAndInstance"];
		// go ahead and call the enyo scoped version of this method
		return enyo.findAndInstance.call(this, property, fn, this);
	},

	//*@public
	/**
		Call this method with the name (or path) to the desired property or
		computed property. If it encounters a computed property it will return
		the value of that property and not the function. If it cannot find
		or resolve the requested path relative to the object it will return
		undefined.

		This method is backwards compatible and will automatically call any
		existing _getter_ method that uses the getProperty convention although
		this convention ought to be replaced using a computed property moving
		forward.
	*/
	get: function (path) {
		return enyo.getPath.apply(this, arguments);
	},
	//*@public
	/**
		Call this method with a property (or path) and a value to be set. This
		will automatically notify any listeners/observers that the property has
		been changed if the values are not the same. If the property it finds
		is a computed property it will pass the intended value to the computed
		property (but will not return the value).

		This method is backwards compatible and will call any setter of the
		setProperty convention although these methods should be replaced with
		computed properties or observers where necessary.
	*/
	set: function (path, value) {
		return enyo.setPath.apply(this, arguments);
	},

	//*@protected
	destroy: function () {
		// JS objects are never truly destroyed (GC'd) until all references are gone,
		// we might have some delayed action on this object that needs to have access
		// to this flag.
		this.destroyed = true;
	},

	_is_object: true
});

//* @protected

enyo._objectCount = 0;

enyo.Object.subclass = function(ctor, props) {
	this.publish(ctor, props);
	this.overload(ctor, props);
};

enyo.Object.publish = function(ctor, props) {
	var pp = props.published;
	if (pp) {
		var cp = ctor.prototype;
		for (var n in pp) {
			// need to make sure that even though a property is "published"
			// it does not overwrite any computed properties
			if (props[n] && enyo.isFunction(props[n]) && props[n].isProperty) continue;
			enyo.Object.addGetterSetter(n, pp[n], cp);
		}
	}
};

//*@protected
/**
	We need to find special cases and ensure that the overloaded
	getter of a published property of a parent kind is flagged for
	the global getter.
*/
enyo.Object.overload = function (ctor, props) {
	var proto = ctor.prototype.base? ctor.prototype.base.prototype: {};
	var regex = /(get|set).*/;
	var name;
	var prop;
	var base;
	for (name in props) {
		if (!regex.test(name)) continue;
		prop = props[name];
		if ("function" === typeof prop) {
			if ((base = proto[name])) {
				prop.overloaded = true;
			}
		}
	}
};

//*@protected
/**
	This method creates a getter/setter for a published property of
	an _enyo.Object_ but is deprecated. It is maintained for backwards
	compatability purposes. The prefered method is to mark public and
	protected (private) methods and properties using documentation or
	other means and rely on the _get_ and _set_ methods of _enyo.Object_
	instances.
*/
enyo.Object.addGetterSetter = function (property, value, proto) {
	var getter = "get" + enyo.cap(property);
	var setter = "set" + enyo.cap(property);
	var fn;
	// set the initial value for the prototype
	proto[property] = value;
	fn = proto[getter];
	// if there isn't already a getter provided create one
	if ("function" !== typeof fn) {
		fn = proto[getter] = function () {return this.get(property)};
		fn.overloaded = false;
	} else if (false !== fn.overloaded) {
		// otherwise we need to mark it as having been overloaded
		// so the global getter knows not to ignore it
		fn.overloaded = true;
	}
	// if there isn't already a setter provided create one
	fn = proto[setter];
	if ("function" !== typeof fn) {
		fn = proto[setter] = function () {return this.set(property, arguments[0])};
		fn.overloaded = false;
	} else if (false !== fn.overloaded) {
		// otherwise we need to mark it as having been overloaded
		// so the global setter knows not to ignore it
		fn.overloaded = true;
	}
};
<|endoftext|>


<commit_before>import React from 'react';
import Coroutine from '../../../modules/Coroutine';

export default Coroutine.create(SearchForm);

async function* SearchForm({ query }) {
  yield
Searching "{query}"
;
}
<commit_msg>search-form: implement primitive UI<commit_after>import React from 'react';
import Coroutine from '../../../modules/Coroutine';
import SearchAPI from './SearchAPI';

export default Coroutine.create(SearchForm);

async function* SearchForm({ query }) {
  yield
Searching "{query}"
;
  try {
    const results = await SearchAPI.retrieve(query);
    return <SearchResults results={results} />;
  } catch (error) {
    return
Oops...
;
  }
}

function SearchResults({ results }) {
  if (results.length === 0) {
    return
No results
;
  }

  return (

      {results.map((result) => (

{result.package.name} ({result.score.final})

      ))}

  );
}
<|endoftext|>


<commit_before>var request = require('request')
	, fs = require('fs-extra')
	, turf = require('turf')
	, xyzTileWorker = require('./xyzTileWorker')
	, geojsonvt = require('geojson-vt')
	, path = require('path')
	, CacheModel = require('../models/cache')
	, Readable = require('stream').Readable
 	, config = require('../config.json');

Math.radians = function(degrees) {
  return degrees * Math.PI / 180;
};

// Converts from radians to degrees.
Math.degrees = function(radians) {
  return radians * 180 / Math.PI;
};

function tile2lon(x,z) {
  return (x/Math.pow(2,z)*360-180);
}

function tile2lat(y,z) {
  var n=Math.PI-2*Math.PI*y/Math.pow(2,z);
  return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
}

exports.tileBboxCalculator = function(x, y, z) {
  console.log('tile box calculator for ' + x + ' ' + y + ' ' + z);
  x = Number(x);
  y = Number(y);
  var tileBounds = {
    north: tile2lat(y, z),
    east: tile2lon(x+1, z),
    south: tile2lat(y+1, z),
    west: tile2lon(x, z)
  };

  return tileBounds;
}

exports.xCalculator = function(bbox,z) {
	var x = [];
	var x1 = exports.getX(Number(bbox[0]), z);
	var x2 = exports.getX(Number(bbox[2]), z);
	x.max = Math.max(x1, x2);
	x.min = Math.min(x1, x2);
	if (z == 0){
		x.current = Math.min(x1, x2);
	}
	return x;
}

exports.yCalculator = function(bbox,z) {
	var y = [];
	var y1 = exports.getY(Number(bbox[1]), z);
	var y2 = exports.getY(Number(bbox[3]), z);
	y.max = Math.max(y1, y2);
	y.min = Math.min(y1, y2);
	y.current = Math.min(y1, y2);
	return y;
}

exports.getX = function(lon, zoom) {
	var xtile = Math.floor((lon + 180) / 360 * (1 << zoom));
	return xtile;
}

exports.getY = function(lat, zoom) {
	var ytile = Math.floor((1 - Math.log(Math.tan(Math.radians(parseFloat(lat))) + 1 / Math.cos(Math.radians(parseFloat(lat)))) / Math.PI) /2 * (1 << zoom));
	return ytile;
}

exports.generateMetadataTiles = function(source, file, callback) {
	source.status.message = "Generating metadata tiles";
	console.log('wrote the file');
	fs.readFile(file, function(err, fileData) {
	var gjData = JSON.parse(fileData);
		var geometry = turf.envelope(gjData);
		source.geometry = geometry;
		source.properties = [];
		var allProperties = {};
		for (var i = 0; i < gjData.features.length; i++) {
			var feature = gjData.features[i];
			for (var property in feature.properties) {
				allProperties[property] = allProperties[property] || {key: property, values:[]};
				if (allProperties[property].values.indexOf(feature.properties[property]) == -1) {
					allProperties[property].values.push(feature.properties[property]);
				}
			}
		}
		for (var property in allProperties) {
			source.properties.push(allProperties[property]);
		}

		source.style = {
			defaultStyle: {
				style: {
					'fill': "#000000",
					'fill-opacity': 0.5,
					'stroke': "#0000FF",
					'stroke-opacity': 1.0,
					'stroke-width': 1
				}
			},
			styles: []
		};
		source.save(function(err) {

			var tileIndex = geojsonvt(gjData, {
				indexMaxZoom: 3,
				maxZoom: 18
			});

			xyzTileWorker.createXYZTiles(source, 0, 5, function(tileInfo, tileDone) {
				console.log('get the shapefile tile %d, %d, %d', tileInfo.z, tileInfo.x, tileInfo.y);
				var dir = path.join(config.server.sourceDirectory.path, source.id.toString(), 'tiles', tileInfo.z.toString(), tileInfo.x.toString());
			  var file = path.join(dir, tileInfo.y.toString()+'.json');

			  if (!fs.existsSync(file)) {
					var tile = tileIndex.getTile(Number(tileInfo.z), Number(tileInfo.x), Number(tileInfo.y));
					if (tile) {
						exports.writeVectorTile(tile, source, tileInfo.z, tileInfo.x, tileInfo.y, function() {
							return tileDone();
						});
					} else {
						return tileDone();
					}
				} else {
					console.log('tile exists');
					return tileDone();
				}
			}, function(source, continueCallback) {
				continueCallback(null, true);
			}, function(source, zoom, zoomDoneCallback) {
				source.status.message="Processing " + ((zoom/6)*100) + "% complete";
				source.save(function() {
					zoomDoneCallback();
				});
			}, function(err, cache) {
				source.status.complete = true;
				source.status.message = "Complete";
				source.save(function() {
					callback(null, source);
				});
			});
		});
	});
}

exports.writeVectorTile = function(tile, source, z, x, y, callback) {
  var dir = path.join(config.server.sourceDirectory.path, source.id.toString(), 'tiles', z.toString(), x.toString());
  var file = path.join(dir, y.toString()+'.json');

  if (!fs.existsSync(file)) {
    fs.mkdirsSync(dir, function(err){
       if (err) console.log(err);
     });
    var outStream = fs.createWriteStream(file);
    outStream.on('finish',function(status){
      console.log('wrote the file');
      callback(null);
    });
    var s = new Readable();
    s.push(JSON.stringify(tile));
    s.push(null);
    s.pipe(outStream);
  } else {
    callback(null);
  }
}
<commit_msg>tweaking<commit_after>var request = require('request')
	, fs = require('fs-extra')
	, turf = require('turf')
	, xyzTileWorker = require('./xyzTileWorker')
	, geojsonvt = require('geojson-vt')
	, path = require('path')
	, CacheModel = require('../models/cache')
	, Readable = require('stream').Readable
 	, config = require('../config.json');

Math.radians = function(degrees) {
  return degrees * Math.PI / 180;
};

// Converts from radians to degrees.
Math.degrees = function(radians) {
  return radians * 180 / Math.PI;
};

function tile2lon(x,z) {
  return (x/Math.pow(2,z)*360-180);
}

function tile2lat(y,z) {
  var n=Math.PI-2*Math.PI*y/Math.pow(2,z);
  return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
}

exports.tileBboxCalculator = function(x, y, z) {
  console.log('tile box calculator for ' + x + ' ' + y + ' ' + z);
  x = Number(x);
  y = Number(y);
  var tileBounds = {
    north: tile2lat(y, z),
    east: tile2lon(x+1, z),
    south: tile2lat(y+1, z),
    west: tile2lon(x, z)
  };

  return tileBounds;
}

exports.xCalculator = function(bbox,z) {
	var x = [];
	var x1 = exports.getX(Number(bbox[0]), z);
	var x2 = exports.getX(Number(bbox[2]), z);
	x.max = Math.max(x1, x2);
	x.min = Math.min(x1, x2);
	if (z == 0){
		x.current = Math.min(x1, x2);
	}
	return x;
}

exports.yCalculator = function(bbox,z) {
	var y = [];
	var y1 = exports.getY(Number(bbox[1]), z);
	var y2 = exports.getY(Number(bbox[3]), z);
	y.max = Math.max(y1, y2);
	y.min = Math.min(y1, y2);
	y.current = Math.min(y1, y2);
	return y;
}

exports.getX = function(lon, zoom) {
	var xtile = Math.floor((lon + 180) / 360 * (1 << zoom));
	return xtile;
}

exports.getY = function(lat, zoom) {
	var ytile = Math.floor((1 - Math.log(Math.tan(Math.radians(parseFloat(lat))) + 1 / Math.cos(Math.radians(parseFloat(lat)))) / Math.PI) /2 * (1 << zoom));
	return ytile;
}

exports.generateMetadataTiles = function(source, file, callback) {
	source.status.message = "Generating metadata tiles";
	console.log('wrote the file');
	fs.readFile(file, function(err, fileData) {
	var gjData = JSON.parse(fileData);
		var geometry = turf.envelope(gjData);
		source.geometry = geometry;
		source.properties = [];
		var allProperties = {};
		for (var i = 0; i < gjData.features.length; i++) {
			var feature = gjData.features[i];
			for (var property in feature.properties) {
				allProperties[property] = allProperties[property] || {key: property, values:[]};
				if (allProperties[property].values.indexOf(feature.properties[property]) == -1) {
					allProperties[property].values.push(feature.properties[property]);
				}
			}
		}
		for (var property in allProperties) {
			source.properties.push(allProperties[property]);
		}

		source.style = {
			defaultStyle: {
				style: {
					'fill': "#000000",
					'fill-opacity': 0.5,
					'stroke': "#0000FF",
					'stroke-opacity': 1.0,
					'stroke-width': 1
				}
			},
			styles: []
		};
		source.save(function(err) {

			var tileIndex = geojsonvt(gjData, {
				indexMaxZoom: 6,
				maxZoom: 18
			});

			xyzTileWorker.createXYZTiles(source, 0, 5, function(tileInfo, tileDone) {
				console.log('get the shapefile tile %d, %d, %d', tileInfo.z, tileInfo.x, tileInfo.y);
				var dir = path.join(config.server.sourceDirectory.path, source.id.toString(), 'tiles', tileInfo.z.toString(), tileInfo.x.toString());
			  var file = path.join(dir, tileInfo.y.toString()+'.json');

			  if (!fs.existsSync(file)) {
					var tile = tileIndex.getTile(Number(tileInfo.z), Number(tileInfo.x), Number(tileInfo.y));
					if (tile) {
						exports.writeVectorTile(tile, source, tileInfo.z, tileInfo.x, tileInfo.y, function() {
							return tileDone();
						});
					} else {
						return tileDone();
					}
				} else {
					console.log('tile exists');
					return tileDone();
				}
			}, function(source, continueCallback) {
				continueCallback(null, true);
			}, function(source, zoom, zoomDoneCallback) {
				source.status.message="Processing " + ((zoom/6)*100) + "% complete";
				source.save(function() {
					zoomDoneCallback();
				});
			}, function(err, cache) {
				source.status.complete = true;
				source.status.message = "Complete";
				source.save(function() {
					callback(null, source);
				});
			});
		});
	});
}

exports.writeVectorTile = function(tile, source, z, x, y, callback) {
  var dir = path.join(config.server.sourceDirectory.path, source.id.toString(), 'tiles', z.toString(), x.toString());
  var file = path.join(dir, y.toString()+'.json');

  if (!fs.existsSync(file)) {
    fs.mkdirsSync(dir, function(err){
       if (err) console.log(err);
     });
    var outStream = fs.createWriteStream(file);
    outStream.on('finish',function(status){
      console.log('wrote the file');
      callback(null);
    });
    var s = new Readable();
    s.push(JSON.stringify(tile));
    s.push(null);
    s.pipe(outStream);
  } else {
    callback(null);
  }
}
<|endoftext|>