<commit_before>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt

// Module CRM

cur_frm.cscript.tname = "Sales Order Item";
cur_frm.cscript.fname = "sales_order_details";
cur_frm.cscript.other_fname = "other_charges";
cur_frm.cscript.sales_team_fname = "sales_team";

wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');

erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
	refresh: function(doc, dt, dn) {
		this._super();
		this.frm.dashboard.reset();

		if(doc.docstatus==1) {
			if(doc.status != 'Stopped') {

				cur_frm.dashboard.add_progress(cint(doc.per_delivered) + wn._("% Delivered"),
					doc.per_delivered);
				cur_frm.dashboard.add_progress(cint(doc.per_billed) + wn._("% Billed"),
					doc.per_billed);

				cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
				// delivery note
				if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales')
					cur_frm.add_custom_button('Make Delivery', this.make_delivery_note);

				// maintenance
				if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) {
					cur_frm.add_custom_button('Make Maint. Visit', this.make_maintenance_visit);
					cur_frm.add_custom_button('Make Maint. Schedule',
						this.make_maintenance_schedule);
				}

				// indent
				if(!doc.order_type || (doc.order_type == 'Sales'))
					cur_frm.add_custom_button('Make ' + wn._('Material Request'),
						this.make_material_request);

				// sales invoice
				if(flt(doc.per_billed, 2) < 100)
					cur_frm.add_custom_button('Make Invoice', this.make_sales_invoice);

				// stop
				if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
					cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']);
			} else {
				// un-stop
				cur_frm.dashboard.set_headline_alert("Stopped", "alert-danger", "icon-stop");
				cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']);
			}
		}

		if (this.frm.doc.docstatus===0) {
			cur_frm.add_custom_button(wn._('From Quotation'),
				function() {
					wn.model.map_current_doc({
						method: "selling.doctype.quotation.quotation.make_sales_order",
						source_doctype: "Quotation",
						get_query_filters: {
							docstatus: 1,
							status: ["!=", "Order Lost"],
							order_type: cur_frm.doc.order_type,
							customer: cur_frm.doc.customer || undefined,
							company: cur_frm.doc.company
						}
					})
				});
		}

		this.order_type(doc);
	},

	order_type: function() {
		this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
	},

	tc_name: function() {
		this.get_terms();
	},

	reserved_warehouse: function(doc, cdt, cdn) {
		var item = wn.model.get_doc(cdt, cdn);
		if(item.item_code && item.reserved_warehouse) {
			return this.frm.call({
				method: "selling.utils.get_available_qty",
				child: item,
				args: {
					item_code: item.item_code,
					warehouse: item.reserved_warehouse,
				},
			});
		}
	},

	make_material_request: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_material_request",
			source_name: cur_frm.doc.name
		})
	},

	make_delivery_note: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_delivery_note",
			source_name: cur_frm.doc.name
		})
	},

	make_sales_invoice: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_sales_invoice",
			source_name: cur_frm.doc.name
		})
	},

	make_maintenance_schedule: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_maintenance_schedule",
			source_name: cur_frm.doc.name
		})
	},

	make_maintenance_visit: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_maintenance_visit",
			source_name: cur_frm.doc.name
		})
	},
});

// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));

cur_frm.cscript.new_contact = function(){
	tn = wn.model.make_new_doc_and_get_name('Contact');
	locals['Contact'][tn].is_customer = 1;
	if(doc.customer) locals['Contact'][tn].customer = doc.customer;
	loaddoc('Contact', tn);
}

cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
	return {
		query: "controllers.queries.get_project_name",
		filters: {
			'customer': doc.customer
		}
	}
}

cur_frm.cscript['Stop Sales Order'] = function() {
	var doc = cur_frm.doc;

	var check = confirm("Are you sure you want to STOP " + doc.name);

	if (check) {
		return $c('runserverobj', {
			'method':'stop_sales_order',
			'docs': wn.model.compress(make_doclist(doc.doctype, doc.name))
			}, function(r,rt) {
			cur_frm.refresh();
		});
	}
}

cur_frm.cscript['Unstop Sales Order'] = function() {
	var doc = cur_frm.doc;

	var check = confirm("Are you sure you want to UNSTOP " + doc.name);

	if (check) {
		return $c('runserverobj', {
			'method':'unstop_sales_order',
			'docs': wn.model.compress(make_doclist(doc.doctype, doc.name))
		}, function(r,rt) {
			cur_frm.refresh();
		});
	}
}

cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
	if(cint(wn.boot.notification_settings.sales_order)) {
		cur_frm.email_doc(wn.boot.notification_settings.sales_order_message);
	}
};<commit_msg>Fix Translation in Sales Order.js<commit_after>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt

// Module CRM

cur_frm.cscript.tname = "Sales Order Item";
cur_frm.cscript.fname = "sales_order_details";
cur_frm.cscript.other_fname = "other_charges";
cur_frm.cscript.sales_team_fname = "sales_team";

wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');

erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
	refresh: function(doc, dt, dn) {
		this._super();
		this.frm.dashboard.reset();

		if(doc.docstatus==1) {
			if(doc.status != 'Stopped') {

				cur_frm.dashboard.add_progress(cint(doc.per_delivered) + wn._("% Delivered"),
					doc.per_delivered);
				cur_frm.dashboard.add_progress(cint(doc.per_billed) + wn._("% Billed"),
					doc.per_billed);

				cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms);
				// delivery note
				if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales')
					cur_frm.add_custom_button(wn._('Make Delivery'), this.make_delivery_note);

				// maintenance
				if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) {
					cur_frm.add_custom_button(wn._('Make Maint. Visit'), this.make_maintenance_visit);
					cur_frm.add_custom_button(wn._('Make Maint. Schedule'),
						this.make_maintenance_schedule);
				}

				// indent
				if(!doc.order_type || (doc.order_type == 'Sales'))
					cur_frm.add_custom_button(wn._('Make ') + wn._('Material Request'),
						this.make_material_request);

				// sales invoice
				if(flt(doc.per_billed, 2) < 100)
					cur_frm.add_custom_button(wn._('Make Invoice'), this.make_sales_invoice);

				// stop
				if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
					cur_frm.add_custom_button(wn._('Stop!'), cur_frm.cscript['Stop Sales Order']);
			} else {
				// un-stop
				cur_frm.dashboard.set_headline_alert(wn._("Stopped"), "alert-danger", "icon-stop");
				cur_frm.add_custom_button(wn._('Unstop'), cur_frm.cscript['Unstop Sales Order']);
			}
		}

		if (this.frm.doc.docstatus===0) {
			cur_frm.add_custom_button(wn._('From Quotation'),
				function() {
					wn.model.map_current_doc({
						method: "selling.doctype.quotation.quotation.make_sales_order",
						source_doctype: "Quotation",
						get_query_filters: {
							docstatus: 1,
							status: ["!=", "Order Lost"],
							order_type: cur_frm.doc.order_type,
							customer: cur_frm.doc.customer || undefined,
							company: cur_frm.doc.company
						}
					})
				});
		}

		this.order_type(doc);
	},

	order_type: function() {
		this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
	},

	tc_name: function() {
		this.get_terms();
	},

	reserved_warehouse: function(doc, cdt, cdn) {
		var item = wn.model.get_doc(cdt, cdn);
		if(item.item_code && item.reserved_warehouse) {
			return this.frm.call({
				method: "selling.utils.get_available_qty",
				child: item,
				args: {
					item_code: item.item_code,
					warehouse: item.reserved_warehouse,
				},
			});
		}
	},

	make_material_request: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_material_request",
			source_name: cur_frm.doc.name
		})
	},

	make_delivery_note: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_delivery_note",
			source_name: cur_frm.doc.name
		})
	},

	make_sales_invoice: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_sales_invoice",
			source_name: cur_frm.doc.name
		})
	},

	make_maintenance_schedule: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_maintenance_schedule",
			source_name: cur_frm.doc.name
		})
	},

	make_maintenance_visit: function() {
		wn.model.open_mapped_doc({
			method: "selling.doctype.sales_order.sales_order.make_maintenance_visit",
			source_name: cur_frm.doc.name
		})
	},
});

// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));

cur_frm.cscript.new_contact = function(){
	tn = wn.model.make_new_doc_and_get_name('Contact');
	locals['Contact'][tn].is_customer = 1;
	if(doc.customer) locals['Contact'][tn].customer = doc.customer;
	loaddoc('Contact', tn);
}

cur_frm.fields_dict['project_name'].get_query = function(doc, cdt, cdn) {
	return {
		query: "controllers.queries.get_project_name",
		filters: {
			'customer': doc.customer
		}
	}
}

cur_frm.cscript['Stop Sales Order'] = function() {
	var doc = cur_frm.doc;

	var check = confirm(wn._("Are you sure you want to STOP ") + doc.name);

	if (check) {
		return $c('runserverobj', {
			'method':'stop_sales_order',
			'docs': wn.model.compress(make_doclist(doc.doctype, doc.name))
			}, function(r,rt) {
			cur_frm.refresh();
		});
	}
}

cur_frm.cscript['Unstop Sales Order'] = function() {
	var doc = cur_frm.doc;

	var check = confirm(wn._("Are you sure you want to UNSTOP ") + doc.name);

	if (check) {
		return $c('runserverobj', {
			'method':'unstop_sales_order',
			'docs': wn.model.compress(make_doclist(doc.doctype, doc.name))
		}, function(r,rt) {
			cur_frm.refresh();
		});
	}
}

cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
	if(cint(wn.boot.notification_settings.sales_order)) {
		cur_frm.email_doc(wn.boot.notification_settings.sales_order_message);
	}
};
<|endoftext|>


<commit_before>/* eslint-disable max-len */
import { createSelector } from 'reselect';
import {
  getColorByIndex,
  createLegendBuckets,
  shouldShowPath
} from 'utils/map';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import getIPPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { COUNTRY_STYLES } from 'components/ndcs/shared/constants';

// TODO: Remove ndc_enhancement when new data on production
import {
  ENHANCEMENT_CATEGORIES,
  ENHANCEMENT_LABEL_SLUGS,
  ENHANCEMENT_LABEL_COLORS,
  INDICATOR_SLUGS
} from 'data/constants';

const ENHANCEMENT_SLUGS = {
  EMISSIONS: INDICATOR_SLUGS.emissions,
  MAP: INDICATOR_SLUGS.enhancements
};

const getSearch = state => state.search || null;
export const getCountries = state =>
  (state.countries && state.countries.data) || null;
const getCategories = state =>
  (state.ndcs && state.ndcs.data && state.ndcs.data.categories) || null;
const getIndicatorsData = state =>
  (state.ndcs && state.ndcs.data && state.ndcs.data.indicators) || null;
const getZoom = state => state.map.zoom || null;

const getPreviousComparisonIndicators = state =>
  state.ndcsPreviousComparison && state.ndcsPreviousComparison.data;
const getCountriesDocuments = state => state.countriesDocuments.data || null;

export const getIsEnhancedChecked = createSelector(
  getSearch,
  search => search.showEnhancedAmbition === 'true'
);

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getCategories, getIndicatorsData, getISOCountries],
  (categories, indicators, isos) => {
    if (!categories || !indicators || !indicators.length) return null;
    const categoryId = Object.keys(categories).find(id =>
      ENHANCEMENT_CATEGORIES.includes(categories[id].slug)
    );
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(parseInt(categoryId, 10)) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value === ENHANCEMENT_SLUGS.MAP
    );
    if (!mapIndicator) return null;

    const updatedMapIndicator = { ...mapIndicator };
    const noInfoId = Object.keys(updatedMapIndicator.legendBuckets).find(
      id =>
        updatedMapIndicator.legendBuckets[id].slug ===
        ENHANCEMENT_LABEL_SLUGS.NO_INFO
    );

    // Set all countries without values to "No Information" by default
    if (noInfoId) {
      isos.forEach(iso => {
        if (!updatedMapIndicator.locations[iso]) {
          updatedMapIndicator.locations[iso] = {
            value: updatedMapIndicator.legendBuckets[noInfoId].name,
            label_id: noInfoId,
            label_slug: updatedMapIndicator.legendBuckets[noInfoId].slug
          };
        }
      });
    }
    return updatedMapIndicator;
  }
);

export const getCompareLinks = createSelector(
  [getCountriesDocuments],
  countriesDocuments => {
    if (!countriesDocuments) return null;
    const links = {};
    const baseURL =
      '/custom-compare/overview?section=overall_comparison_with_previous_ndc&targets=';
    // eslint-disable-next-line consistent-return
    Object.keys(countriesDocuments).forEach(iso => {
      // Only documents with submission date have been submitted
      const ndcDocuments = countriesDocuments[iso].filter(
        d => d.is_ndc && !!d.submission_date
      );
      if (!ndcDocuments.length) return null;

      const orderedDocuments = sortBy(uniqBy(ndcDocuments, 'id'), 'ordering')
        .reverse()
        .slice(0, 3);

      const link = `${baseURL}${orderedDocuments
        .map(d => `${iso}-${d.slug}`)
        .join(',')}`;

      links[iso] = {
        link,
        documents: orderedDocuments.map(d => d.description)
      };
    });

    return links;
  }
);

export const filterEnhancedValueOnIndicator = createSelector(
  [getMapIndicator, getIsEnhancedChecked],
  (indicator, isEnhancedChecked) => {
    if (!indicator) return null;
    if (isEnhancedChecked) return indicator;
    const { legendBuckets, locations } = indicator;
    const enhancedLabelId = Object.keys(legendBuckets).find(
      key =>
        legendBuckets[key].slug === ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION
    );
    const submittedLabelId = Object.keys(legendBuckets).find(
      key => legendBuckets[key].slug === ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
    );

    const updatedLegendBuckets = { ...legendBuckets };
    delete updatedLegendBuckets[enhancedLabelId];

    const updatedLocations = { ...locations };
    Object.keys(updatedLocations).forEach(iso => {
      const countryData = updatedLocations[iso];
      if (countryData && countryData.label_id) {
        const shouldHideEnhancedLabel =
          !isEnhancedChecked &&
          String(countryData.label_id) === enhancedLabelId;
        const updatedLabelId = shouldHideEnhancedLabel
          ? submittedLabelId
          : countryData.label_id;
        updatedLocations[iso] = {
          ...updatedLocations[iso],
          label_id: +updatedLabelId
        };
      }
    });
    return {
      ...indicator,
      locations: updatedLocations,
      legendBuckets: updatedLegendBuckets
    };
  }
);

export const sortIndicatorLegend = createSelector(
  [filterEnhancedValueOnIndicator],
  indicator => {
    if (!indicator) return null;
    const updatedIndicator = { ...indicator };
    const slugsLegendOrder = [
      ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION,
      ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020,
      ENHANCEMENT_LABEL_SLUGS.INTENDS_TO_ENHANCE,
      null,
      ENHANCEMENT_LABEL_SLUGS.NO_INFO
    ]; // null it's for 'Not Applicable'
    Object.entries(updatedIndicator.legendBuckets).forEach(([key, value]) => {
      updatedIndicator.legendBuckets[key] = {
        ...updatedIndicator.legendBuckets[key],
        order: value.slug ? slugsLegendOrder.indexOf(value.slug) : 3 // Not Applicable should have order number 3
      };
    });
    return updatedIndicator;
  }
);

const MAP_LABEL_COLORS = [
  ...Object.values(ENHANCEMENT_LABEL_COLORS),
  'rgb(204, 204, 204)'
];
export const MAP_COLORS = [
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS]
];

export const getPathsWithStyles = createSelector(
  [filterEnhancedValueOnIndicator, getIPPaths, getZoom],
  (indicator, worldPaths, zoom) => {
    if (!indicator || !worldPaths) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (shouldShowPath(path)) {
        const { locations, legendBuckets } = indicator;
        if (!locations) {
          paths.push({
            ...path,
            COUNTRY_STYLES
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = COUNTRY_STYLES;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          const strokeWidth = zoom > 2 ? (1 / zoom) * 2 : 0.5;
          style = {
            ...COUNTRY_STYLES,
            default: {
              ...COUNTRY_STYLES.default,
              'stroke-width': strokeWidth,
              fill: color,
              fillOpacity: 1
            },
            hover: {
              ...COUNTRY_STYLES.hover,
              cursor: 'pointer',
              'stroke-width': strokeWidth,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector(
  [getSearch, getCategories],
  (search, categories) => {
    if (!categories) return null;
    const category = Object.values(categories).find(c =>
      ENHANCEMENT_CATEGORIES.includes(c.slug)
    );
    if (!category) return null;

    const section = 'ndc-content';
    return generateLinkToDataExplorer(
      {
        category: category.slug,
        ...search
      },
      section
    );
  }
);

export const getPreviousComparisonCountryValues = createSelector(
  [getPreviousComparisonIndicators, getISOCountries],
  (previousComparisonIndicators, isos) => {
    if (!previousComparisonIndicators) return null;
    const previousComparisonCountryValues = {};
    isos.forEach(iso => {
      previousComparisonCountryValues[
        iso
      ] = previousComparisonIndicators.map(indicator => [
        indicator.name,
        indicator.locations[iso].value
      ]);
    });
    return previousComparisonCountryValues;
  }
);

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    const summaryData = {};

    const locations = Object.keys(indicator.locations);
    const summaryLabels = [
      'enhance_2020',
      'enhanced_migitation',
      'submitted_2020'
    ];
    const labels = Object.keys(indicator.legendBuckets)
      .filter(id => summaryLabels.includes(indicator.legendBuckets[id].slug))
      .map(id => ({
        ...indicator.legendBuckets[id],
        id
      }));

    labels.forEach(label => {
      const color = getColorByIndex(
        indicator.legendBuckets,
        label.index,
        MAP_COLORS
      );
      summaryData[label.slug] = {
        includesEU: false,
        countries: {
          value: 0,
          max: locations.length,
          opts: { color }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: { color }
        }
      };
    });

    const emissionsIndicator = indicators.find(
      ind => ind.value === ENHANCEMENT_SLUGS.EMISSIONS
    );

    // Calculate countries number and emissions
    locations.forEach(l => {
      const location = indicator.locations[l];
      const type = location.label_slug;
      const emissionPercentages = emissionsIndicator.locations;
      if (type && summaryData[type]) {
        if (l === europeSlug) {
          const EUTotal = parseFloat(emissionPercentages[europeSlug].value);
          const europeanLocationIsos = locations.filter(iso =>
            europeanCountries.includes(iso)
          );
          const europeanLocationsValue = europeanLocationIsos.reduce(
            (acc, iso) => acc + parseFloat(emissionPercentages[iso].value),
            0
          );
          summaryData[type].emissions.value += EUTotal - europeanLocationsValue; // To avoid double counting
          summaryData[type].countries.value +=
            europeanCountries.length - europeanLocationIsos.length; // To avoid double counting

          summaryData[type].includesEU = true;
        } else {
          summaryData[type].countries.value += 1;

          // Enhanced mitigation should be counted as submitted 2020
          if (type === ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION) {
            summaryData[
              ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
            ].countries.value += 1;
          }

          if (emissionsIndicator.locations[l]) {
            summaryData[type].emissions.value += parseFloat(
              emissionsIndicator.locations[l].value
            );
          }
        }
      }
    });

    // Add emissions values
    Object.keys(summaryData).forEach(type => {
      // Enhanced mitigation should be counted as submitted 2020
      const emissionsParsedValue =
        type === ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
          ? parseFloat(summaryData.submitted_2020.emissions.value) +
            parseFloat(
              summaryData[ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION].emissions
                .value
            )
          : parseFloat(summaryData[type].emissions.value);

      summaryData[type].emissions.value = emissionsParsedValue.toFixed(1);
    });
    // Add text
    Object.keys(summaryData).forEach(type => {
      const emissionsString = `<span title="2018 emissions data">${summaryData[type].emissions.value}% of global emissions</span>`;
      summaryData[type].countries.opts.label = {
        [ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION]: `of the ${
          summaryData[ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020].countries.value
        } countries (${emissionsString}) have submitted a new or updated NDC with reduced total emissions compared to their previous NDC`,
        [ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020]: `<strong>countries</strong> (${emissionsString}) have submitted a <strong>new or updated NDC</strong>`
      }[type];
    });
    return summaryData;
  }
);
<commit_msg>Start show enhanced ambition checked<commit_after>/* eslint-disable max-len */
import { createSelector } from 'reselect';
import {
  getColorByIndex,
  createLegendBuckets,
  shouldShowPath
} from 'utils/map';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import getIPPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { COUNTRY_STYLES } from 'components/ndcs/shared/constants';

// TODO: Remove ndc_enhancement when new data on production
import {
  ENHANCEMENT_CATEGORIES,
  ENHANCEMENT_LABEL_SLUGS,
  ENHANCEMENT_LABEL_COLORS,
  INDICATOR_SLUGS
} from 'data/constants';

const ENHANCEMENT_SLUGS = {
  EMISSIONS: INDICATOR_SLUGS.emissions,
  MAP: INDICATOR_SLUGS.enhancements
};

const getSearch = state => state.search || null;
export const getCountries = state =>
  (state.countries && state.countries.data) || null;
const getCategories = state =>
  (state.ndcs && state.ndcs.data && state.ndcs.data.categories) || null;
const getIndicatorsData = state =>
  (state.ndcs && state.ndcs.data && state.ndcs.data.indicators) || null;
const getZoom = state => state.map.zoom || null;

const getPreviousComparisonIndicators = state =>
  state.ndcsPreviousComparison && state.ndcsPreviousComparison.data;
const getCountriesDocuments = state => state.countriesDocuments.data || null;

export const getIsEnhancedChecked = createSelector(
  getSearch,
  search => search.showEnhancedAmbition !== 'false'
);

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getCategories, getIndicatorsData, getISOCountries],
  (categories, indicators, isos) => {
    if (!categories || !indicators || !indicators.length) return null;
    const categoryId = Object.keys(categories).find(id =>
      ENHANCEMENT_CATEGORIES.includes(categories[id].slug)
    );
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(parseInt(categoryId, 10)) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value === ENHANCEMENT_SLUGS.MAP
    );
    if (!mapIndicator) return null;

    const updatedMapIndicator = { ...mapIndicator };
    const noInfoId = Object.keys(updatedMapIndicator.legendBuckets).find(
      id =>
        updatedMapIndicator.legendBuckets[id].slug ===
        ENHANCEMENT_LABEL_SLUGS.NO_INFO
    );

    // Set all countries without values to "No Information" by default
    if (noInfoId) {
      isos.forEach(iso => {
        if (!updatedMapIndicator.locations[iso]) {
          updatedMapIndicator.locations[iso] = {
            value: updatedMapIndicator.legendBuckets[noInfoId].name,
            label_id: noInfoId,
            label_slug: updatedMapIndicator.legendBuckets[noInfoId].slug
          };
        }
      });
    }
    return updatedMapIndicator;
  }
);

export const getCompareLinks = createSelector(
  [getCountriesDocuments],
  countriesDocuments => {
    if (!countriesDocuments) return null;
    const links = {};
    const baseURL =
      '/custom-compare/overview?section=overall_comparison_with_previous_ndc&targets=';
    // eslint-disable-next-line consistent-return
    Object.keys(countriesDocuments).forEach(iso => {
      // Only documents with submission date have been submitted
      const ndcDocuments = countriesDocuments[iso].filter(
        d => d.is_ndc && !!d.submission_date
      );
      if (!ndcDocuments.length) return null;

      const orderedDocuments = sortBy(uniqBy(ndcDocuments, 'id'), 'ordering')
        .reverse()
        .slice(0, 3);

      const link = `${baseURL}${orderedDocuments
        .map(d => `${iso}-${d.slug}`)
        .join(',')}`;

      links[iso] = {
        link,
        documents: orderedDocuments.map(d => d.description)
      };
    });

    return links;
  }
);

export const filterEnhancedValueOnIndicator = createSelector(
  [getMapIndicator, getIsEnhancedChecked],
  (indicator, isEnhancedChecked) => {
    if (!indicator) return null;
    if (isEnhancedChecked) return indicator;
    const { legendBuckets, locations } = indicator;
    const enhancedLabelId = Object.keys(legendBuckets).find(
      key =>
        legendBuckets[key].slug === ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION
    );
    const submittedLabelId = Object.keys(legendBuckets).find(
      key => legendBuckets[key].slug === ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
    );

    const updatedLegendBuckets = { ...legendBuckets };
    delete updatedLegendBuckets[enhancedLabelId];

    const updatedLocations = { ...locations };
    Object.keys(updatedLocations).forEach(iso => {
      const countryData = updatedLocations[iso];
      if (countryData && countryData.label_id) {
        const shouldHideEnhancedLabel =
          !isEnhancedChecked &&
          String(countryData.label_id) === enhancedLabelId;
        const updatedLabelId = shouldHideEnhancedLabel
          ? submittedLabelId
          : countryData.label_id;
        updatedLocations[iso] = {
          ...updatedLocations[iso],
          label_id: +updatedLabelId
        };
      }
    });
    return {
      ...indicator,
      locations: updatedLocations,
      legendBuckets: updatedLegendBuckets
    };
  }
);

export const sortIndicatorLegend = createSelector(
  [filterEnhancedValueOnIndicator],
  indicator => {
    if (!indicator) return null;
    const updatedIndicator = { ...indicator };
    const slugsLegendOrder = [
      ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION,
      ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020,
      ENHANCEMENT_LABEL_SLUGS.INTENDS_TO_ENHANCE,
      null,
      ENHANCEMENT_LABEL_SLUGS.NO_INFO
    ]; // null it's for 'Not Applicable'
    Object.entries(updatedIndicator.legendBuckets).forEach(([key, value]) => {
      updatedIndicator.legendBuckets[key] = {
        ...updatedIndicator.legendBuckets[key],
        order: value.slug ? slugsLegendOrder.indexOf(value.slug) : 3 // Not Applicable should have order number 3
      };
    });
    return updatedIndicator;
  }
);

const MAP_LABEL_COLORS = [
  ...Object.values(ENHANCEMENT_LABEL_COLORS),
  'rgb(204, 204, 204)'
];
export const MAP_COLORS = [
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS],
  [...MAP_LABEL_COLORS]
];

export const getPathsWithStyles = createSelector(
  [filterEnhancedValueOnIndicator, getIPPaths, getZoom],
  (indicator, worldPaths, zoom) => {
    if (!indicator || !worldPaths) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (shouldShowPath(path)) {
        const { locations, legendBuckets } = indicator;
        if (!locations) {
          paths.push({
            ...path,
            COUNTRY_STYLES
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = COUNTRY_STYLES;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          const strokeWidth = zoom > 2 ? (1 / zoom) * 2 : 0.5;
          style = {
            ...COUNTRY_STYLES,
            default: {
              ...COUNTRY_STYLES.default,
              'stroke-width': strokeWidth,
              fill: color,
              fillOpacity: 1
            },
            hover: {
              ...COUNTRY_STYLES.hover,
              cursor: 'pointer',
              'stroke-width': strokeWidth,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector(
  [getSearch, getCategories],
  (search, categories) => {
    if (!categories) return null;
    const category = Object.values(categories).find(c =>
      ENHANCEMENT_CATEGORIES.includes(c.slug)
    );
    if (!category) return null;

    const section = 'ndc-content';
    return generateLinkToDataExplorer(
      {
        category: category.slug,
        ...search
      },
      section
    );
  }
);

export const getPreviousComparisonCountryValues = createSelector(
  [getPreviousComparisonIndicators, getISOCountries],
  (previousComparisonIndicators, isos) => {
    if (!previousComparisonIndicators) return null;
    const previousComparisonCountryValues = {};
    isos.forEach(iso => {
      previousComparisonCountryValues[
        iso
      ] = previousComparisonIndicators.map(indicator => [
        indicator.name,
        indicator.locations[iso].value
      ]);
    });
    return previousComparisonCountryValues;
  }
);

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    const summaryData = {};

    const locations = Object.keys(indicator.locations);
    const summaryLabels = [
      'enhance_2020',
      'enhanced_migitation',
      'submitted_2020'
    ];
    const labels = Object.keys(indicator.legendBuckets)
      .filter(id => summaryLabels.includes(indicator.legendBuckets[id].slug))
      .map(id => ({
        ...indicator.legendBuckets[id],
        id
      }));

    labels.forEach(label => {
      const color = getColorByIndex(
        indicator.legendBuckets,
        label.index,
        MAP_COLORS
      );
      summaryData[label.slug] = {
        includesEU: false,
        countries: {
          value: 0,
          max: locations.length,
          opts: { color }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: { color }
        }
      };
    });

    const emissionsIndicator = indicators.find(
      ind => ind.value === ENHANCEMENT_SLUGS.EMISSIONS
    );

    // Calculate countries number and emissions
    locations.forEach(l => {
      const location = indicator.locations[l];
      const type = location.label_slug;
      const emissionPercentages = emissionsIndicator.locations;
      if (type && summaryData[type]) {
        if (l === europeSlug) {
          const EUTotal = parseFloat(emissionPercentages[europeSlug].value);
          const europeanLocationIsos = locations.filter(iso =>
            europeanCountries.includes(iso)
          );
          const europeanLocationsValue = europeanLocationIsos.reduce(
            (acc, iso) => acc + parseFloat(emissionPercentages[iso].value),
            0
          );
          summaryData[type].emissions.value += EUTotal - europeanLocationsValue; // To avoid double counting
          summaryData[type].countries.value +=
            europeanCountries.length - europeanLocationIsos.length; // To avoid double counting

          summaryData[type].includesEU = true;
        } else {
          summaryData[type].countries.value += 1;

          // Enhanced mitigation should be counted as submitted 2020
          if (type === ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION) {
            summaryData[
              ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
            ].countries.value += 1;
          }

          if (emissionsIndicator.locations[l]) {
            summaryData[type].emissions.value += parseFloat(
              emissionsIndicator.locations[l].value
            );
          }
        }
      }
    });

    // Add emissions values
    Object.keys(summaryData).forEach(type => {
      // Enhanced mitigation should be counted as submitted 2020
      const emissionsParsedValue =
        type === ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020
          ? parseFloat(summaryData.submitted_2020.emissions.value) +
            parseFloat(
              summaryData[ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION].emissions
                .value
            )
          : parseFloat(summaryData[type].emissions.value);

      summaryData[type].emissions.value = emissionsParsedValue.toFixed(1);
    });
    // Add text
    Object.keys(summaryData).forEach(type => {
      const emissionsString = `<span title="2018 emissions data">${summaryData[type].emissions.value}% of global emissions</span>`;
      summaryData[type].countries.opts.label = {
        [ENHANCEMENT_LABEL_SLUGS.ENHANCED_MITIGATION]: `of the ${
          summaryData[ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020].countries.value
        } countries (${emissionsString}) have submitted a new or updated NDC with reduced total emissions compared to their previous NDC`,
        [ENHANCEMENT_LABEL_SLUGS.SUBMITTED_2020]: `<strong>countries</strong> (${emissionsString}) have submitted a <strong>new or updated NDC</strong>`
      }[type];
    });
    return summaryData;
  }
);
<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import { getColorByIndex, createLegendBuckets } from 'utils/map';
import { deburrUpper } from 'app/utils';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import worldPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { PATH_LAYERS } from 'app/data/constants';
import isEmpty from 'lodash/isEmpty';

const getSearch = state => state.search || null;
const getCountries = state => state.countries || null;
const getIndicatorsData = state => state.indicators || null;
const getQuery = state => deburrUpper(state.query) || '';

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getIndicatorsData, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(11) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators = [], isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value == 'ndce_status_2020'
    );

    //Set all countries without values to "No Information" by default
    isos.forEach(iso => {
      if (!mapIndicator.locations[iso]) {
        mapIndicator.locations[iso] = {
          value: mapIndicator.legendBuckets[237].name,
          label_id: 237
        };
      }
    });
    return mapIndicator;
  }
);

const countryStyles = {
  default: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  hover: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  pressed: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  }
};

export const MAP_COLORS = [
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)'],
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)'],
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)']
];

export const getPathsWithStyles = createSelector(
  [getMapIndicator, getISOCountries],
  (indicator, isos) => {
    if (!indicator) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (path.properties.layer !== PATH_LAYERS.ISLANDS) {
        const { locations, legendBuckets } = indicator;

        if (!locations) {
          paths.push({
            ...path,
            countryStyles
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = countryStyles;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          style = {
            ...countryStyles,
            default: {
              ...countryStyles.default,
              fill: color,
              fillOpacity: 1
            },
            hover: {
              ...countryStyles.hover,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector([getSearch], search => {
  const section = 'ndc-enhancements';
  return generateLinkToDataExplorer(search, section);
});

//Chart data methods

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    let summaryData = {};
    //Retain functionality for showing submitted 2020 NDCs in case this becomes useful to display later
    //ONLY planned 2020 NDCs currently displayed in component
    ['planned', 'submitted'].forEach(type => {
      summaryData[type] = {
        countries: {
          value: 0,
          max: Object.keys(indicator.locations).length,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              type == 'submitted' ? '1' : '2',
              MAP_COLORS
            ),
            label: 'countries have stated their intention to submit a 2020 NDC'
          }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              type == 'submitted' ? '1' : '2',
              MAP_COLORS
            ),
            suffix: '%',
            label:
              'of global emissions (2015 emissions data) are represented by these countries'
          }
        }
      };
    });
    const emissionsIndicator = indicators.find(
      indicator => indicator.value == 'ndce_ghg'
    );
    for (let l in indicator.locations) {
      const location = indicator.locations[l];
      var type =
        location.label_id == 235
          ? 'submitted'
          : location.label_id == 236 ? 'planned' : null;
      if (type) {
        summaryData[type].countries.value++;
        if (emissionsIndicator.locations[l])
          summaryData[type].emissions.value += parseFloat(
            emissionsIndicator.locations[l].value
          );
      }
    }
    return summaryData;
  }
);

//Table data methods

export const tableGetSelectedData = createSelector(
  [getIndicatorsParsed, getMapIndicator, getCountries],
  (indicators, indicator, countries) => {
    if (!indicators || !indicators.length || !indicators[0].locations)
      return [];

    return Object.keys(indicator.locations).map(iso => {
      if (indicator.locations[iso].label_id !== 237) {
        const countryData =
          countries.find(country => country.iso_code3 === iso) || {};
        let row = {
          country: countryData.wri_standard_name || iso,
          iso
        };
        indicators.forEach(ind => {
          if (ind.locations[iso]) {
            row[ind.label] = ind.locations[iso].value;
          }
        });
        return row;
      }
      return false;
    });
  }
);

export const tableGetFilteredData = createSelector(
  [tableGetSelectedData, getQuery],
  (data, query) => {
    if (!data || isEmpty(data)) return null;
    return data.filter(d => {
      let match = false;
      for (let col in d) {
        if (deburrUpper(d[col]).indexOf(query) > -1) {
          match = true;
          break;
        }
      }
      return match;
    });
  }
);

export const tableRemoveIsoFromData = createSelector(
  [tableGetFilteredData],
  data => {
    if (!data || isEmpty(data)) return null;
    return data.map(d => {
      const iso = d.iso;
      delete d.iso;
      return {
        ...d,
        urlNotShow: `/ndcs/country/${iso}`
      };
    });
  }
);

export default {
  getMapIndicator,
  tableRemoveIsoFromData,
  summarizeIndicators,
  getPathsWithStyles
};
<commit_msg>Updating target of Data Explorer link<commit_after>import { createSelector } from 'reselect';
import { getColorByIndex, createLegendBuckets } from 'utils/map';
import { deburrUpper } from 'app/utils';
import uniqBy from 'lodash/uniqBy';
import sortBy from 'lodash/sortBy';
import { generateLinkToDataExplorer } from 'utils/data-explorer';
import worldPaths from 'app/data/world-50m-paths';
import { europeSlug, europeanCountries } from 'app/data/european-countries';
import { PATH_LAYERS } from 'app/data/constants';
import isEmpty from 'lodash/isEmpty';

const getSearch = state => state.search || null;
const getCountries = state => state.countries || null;
const getIndicatorsData = state => state.indicators || null;
const getQuery = state => deburrUpper(state.query) || '';

export const getISOCountries = createSelector([getCountries], countries =>
  countries.map(country => country.iso_code3)
);

export const getIndicatorsParsed = createSelector(
  [getIndicatorsData, getISOCountries],
  (indicators, isos) => {
    if (!indicators || !indicators.length) return null;
    return sortBy(
      uniqBy(
        indicators.map(i => {
          const legendBuckets = createLegendBuckets(
            i.locations,
            i.labels,
            isos
          );
          return {
            label: i.name,
            value: i.slug,
            categoryIds: i.category_ids,
            locations: i.locations,
            legendBuckets
          };
        }),
        'value'
      ),
      'label'
    ).filter(ind => ind.categoryIds.indexOf(11) > -1);
  }
);

export const getMapIndicator = createSelector(
  [getIndicatorsParsed, getISOCountries],
  (indicators = [], isos) => {
    if (!indicators || !indicators.length) return null;
    const mapIndicator = indicators.find(
      ind => ind.value == 'ndce_status_2020'
    );

    //Set all countries without values to "No Information" by default
    isos.forEach(iso => {
      if (!mapIndicator.locations[iso]) {
        mapIndicator.locations[iso] = {
          value: mapIndicator.legendBuckets[237].name,
          label_id: 237
        };
      }
    });
    return mapIndicator;
  }
);

const countryStyles = {
  default: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  hover: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  },
  pressed: {
    fill: '#e9e9e9',
    fillOpacity: 1,
    stroke: '#f5f6f7',
    strokeWidth: 1,
    outline: 'none'
  }
};

export const MAP_COLORS = [
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)'],
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)'],
  ['rgb(254, 224, 141)', 'rgb(80, 129, 166)', 'rgb(204, 204, 204)']
];

export const getPathsWithStyles = createSelector(
  [getMapIndicator, getISOCountries],
  (indicator, isos) => {
    if (!indicator) return [];
    const paths = [];
    worldPaths.forEach(path => {
      if (path.properties.layer !== PATH_LAYERS.ISLANDS) {
        const { locations, legendBuckets } = indicator;

        if (!locations) {
          paths.push({
            ...path,
            countryStyles
          });
          return null;
        }

        const iso = path.properties && path.properties.id;
        const isEuropeanCountry = europeanCountries.includes(iso);
        const countryData = isEuropeanCountry
          ? locations[europeSlug]
          : locations[iso];

        let style = countryStyles;
        if (countryData && countryData.label_id) {
          const legendIndex = legendBuckets[countryData.label_id].index;
          const color = getColorByIndex(legendBuckets, legendIndex, MAP_COLORS);
          style = {
            ...countryStyles,
            default: {
              ...countryStyles.default,
              fill: color,
              fillOpacity: 1
            },
            hover: {
              ...countryStyles.hover,
              fill: color,
              fillOpacity: 1
            }
          };
        }

        paths.push({
          ...path,
          style
        });
      }
      return null;
    });
    return paths;
  }
);

export const getLinkToDataExplorer = createSelector([getSearch], search => {
  const section = 'ndcs';
  return generateLinkToDataExplorer(search, section);
});

//Chart data methods

export const summarizeIndicators = createSelector(
  [getIndicatorsParsed, getMapIndicator],
  (indicators, indicator) => {
    if (!indicator || !indicators) return null;
    let summaryData = {};
    //Retain functionality for showing submitted 2020 NDCs in case this becomes useful to display later
    //ONLY planned 2020 NDCs currently displayed in component
    ['planned', 'submitted'].forEach(type => {
      summaryData[type] = {
        countries: {
          value: 0,
          max: Object.keys(indicator.locations).length,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              type == 'submitted' ? '1' : '2',
              MAP_COLORS
            ),
            label: 'countries have stated their intention to submit a 2020 NDC'
          }
        },
        emissions: {
          value: 0,
          max: 100,
          opts: {
            color: getColorByIndex(
              indicator.legendBuckets,
              type == 'submitted' ? '1' : '2',
              MAP_COLORS
            ),
            suffix: '%',
            label:
              'of global emissions (2015 emissions data) are represented by these countries'
          }
        }
      };
    });
    const emissionsIndicator = indicators.find(
      indicator => indicator.value == 'ndce_ghg'
    );
    for (let l in indicator.locations) {
      const location = indicator.locations[l];
      var type =
        location.label_id == 235
          ? 'submitted'
          : location.label_id == 236 ? 'planned' : null;
      if (type) {
        summaryData[type].countries.value++;
        if (emissionsIndicator.locations[l])
          summaryData[type].emissions.value += parseFloat(
            emissionsIndicator.locations[l].value
          );
      }
    }
    return summaryData;
  }
);

//Table data methods

export const tableGetSelectedData = createSelector(
  [getIndicatorsParsed, getMapIndicator, getCountries],
  (indicators, indicator, countries) => {
    if (!indicators || !indicators.length || !indicators[0].locations)
      return [];

    return Object.keys(indicator.locations).map(iso => {
      if (indicator.locations[iso].label_id !== 237) {
        const countryData =
          countries.find(country => country.iso_code3 === iso) || {};
        let row = {
          country: countryData.wri_standard_name || iso,
          iso
        };
        indicators.forEach(ind => {
          if (ind.locations[iso]) {
            row[ind.label] = ind.locations[iso].value;
          }
        });
        return row;
      }
      return false;
    });
  }
);

export const tableGetFilteredData = createSelector(
  [tableGetSelectedData, getQuery],
  (data, query) => {
    if (!data || isEmpty(data)) return null;
    return data.filter(d => {
      let match = false;
      for (let col in d) {
        if (deburrUpper(d[col]).indexOf(query) > -1) {
          match = true;
          break;
        }
      }
      return match;
    });
  }
);

export const tableRemoveIsoFromData = createSelector(
  [tableGetFilteredData],
  data => {
    if (!data || isEmpty(data)) return null;
    return data.map(d => {
      const iso = d.iso;
      delete d.iso;
      return {
        ...d,
        urlNotShow: `/ndcs/country/${iso}`
      };
    });
  }
);

export default {
  getMapIndicator,
  tableRemoveIsoFromData,
  summarizeIndicators,
  getPathsWithStyles
};
<|endoftext|>


<commit_before>/**
 * @flow
 */

import styled from 'styled-components'
import { colors, animations } from '../theme'

const Bars = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  background: ${colors.primary};
  animation: ${animations.load} 1s infinite ease-in-out;
  width: 1em;
  height: 4em;
  color: ${colors.primary};
  text-indent: -9999em;
  margin: 88px auto;
  position: relative;
  font-size: 11px;
  transform: translateZ(0);
  animation-delay: -0.16s;

  &:before,
  &:after {
    background: ${colors.primary};
    animation: ${animations.load} 1s infinite ease-in-out;
    width: 1em;
    height: 4em;
    position: absolute;
    top: 0;
    content: '';
  }

  &:before {
    left: -1.5em;
    animation-delay: -0.32s;
  }

  &:after {
    left: 1.5em;
  }
`

type Props = {
  error?: boolean,
  timedOut?: boolean,
  pastDelay?: number
}

function Loader({ error, timedOut, pastDelay }: Props) {
  if (error) {
    return
Error!

  } else if (timedOut) {
    return
Taking a long time...

  } else if (pastDelay) {
    return <Bars />
  } else {
    return null
  }
}

export default Loader
<commit_msg>import React in Loader<commit_after>/**
 * @flow
 */

import React from 'react'
import styled from 'styled-components'
import { colors, animations } from '../theme'

const Bars = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  background: ${colors.primary};
  animation: ${animations.load} 1s infinite ease-in-out;
  width: 1em;
  height: 4em;
  color: ${colors.primary};
  text-indent: -9999em;
  margin: 88px auto;
  position: relative;
  font-size: 11px;
  transform: translateZ(0);
  animation-delay: -0.16s;

  &:before,
  &:after {
    background: ${colors.primary};
    animation: ${animations.load} 1s infinite ease-in-out;
    width: 1em;
    height: 4em;
    position: absolute;
    top: 0;
    content: '';
  }

  &:before {
    left: -1.5em;
    animation-delay: -0.32s;
  }

  &:after {
    left: 1.5em;
  }
`

type Props = {
  error?: boolean,
  timedOut?: boolean,
  pastDelay?: number
}

function Loader({ error, timedOut, pastDelay }: Props) {
  if (error) {
    return
Error!

  } else if (timedOut) {
    return
Taking a long time...

  } else if (pastDelay) {
    return <Bars />
  } else {
    return null
  }
}

export default Loader
<|endoftext|>


<commit_before>import Email from '../models/email.model';
import mongoose from 'mongoose';
import User from '../models/user.model';
import striptags from 'striptags';
const ObjectId = mongoose.Types.ObjectId;

function getMonthlyPunchCardWithYear(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {year: {$year: "$date"}, month: {$month: "$date"}, day: {$dayOfMonth: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.year':1, '_id.month': 1, '_id.day': 1}},
    {$project: {_id: 0, year: '$_id.year', month: '$_id.month', day: '$_id.day', count: 1}}
  ]);
}

function getMonthlyPunchCard(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {month: {$month: "$date"}, day: {$dayOfMonth: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.month': 1, '_id.day': 1}},
    {$project: {_id: 0, month: '$_id.month', day: '$_id.day', count: 1}}
  ]);
}

function getDailyPunchCard(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {day: {$dayOfWeek: "$date"}, hour: {$hour: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.day': 1, '_id.hour': 1}},
    {$project: {_id: 0, day: '$_id.day', hour: '$_id.hour', count: 1}}
  ]);
}

function getTopSender(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$unwind: '$from'},
    {$group: {_id: {address: '$from.address', name: '$from.name'}, count: {$sum: 1}}},
    {$sort: {count: -1}},
    {$project: {_id: 0, address: '$_id.address', name: '$_id.name', count: 1}},
    {$limit : 15}
  ]);
}

function getTopReceiver(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$unwind: '$to'},
    {$group: {_id: {address: '$to.address', name: '$to.name'}, count: {$sum: 1}}},
    {$sort: {'count': -1}},
    {$project: {_id: 0, address: '$_id.address', name: '$_id.name', count: 1}},
    {$limit : 15}
  ]);
}

function getNetworkGraph(userId){

  class Edge{

    constructor(from, to){
      this.from = from.toLowerCase();
      this.to = to.toLowerCase();
      this.count = 1;
    }

    increaseCount(){
      this.count++;
    }

    key(){
      return this.from+'_'+this.to;
    }

    toJson(){
      return {from: this.from, to: this.to, count: this.count};
    }
  }

  class Node{

    constructor(email, name){
      this.email = email.toLowerCase();
      this.name = name;
    }

    key(){
      return this.email;
    }

    toJson(){
      return {email: this.email, name: this.name};
    }
  }

  /** Not implemented as set due the fact that the
   *  compare for objects is not implemented in es6 */
  const edges = new Map();
  const nodes = new Map();

  return new Promise((resolve, reject) => {
    Email.find({user: ObjectId(userId)}, {from: 1, to: 1}).lean()
      .then((emails)=> {
        emails.forEach((email)=> {
          email.from.forEach((from)=> {
            email.to.forEach((to)=> {
              let edge = new Edge(from.address, to.address);
              if (edges.has(edge.key())) {
                edge = edges.get(edge.key());
                edge.increaseCount();
              } else {
                edges.set(edge.key(), edge);
              }
              let node = new Node(from.address, from.name);
              if(!nodes.has(node.key()))
                nodes.set(node.key(), node);
              node = new Node(to.address, to.name);
              if(!nodes.has(node.key()))
                nodes.set(node.key(), node);
            });
          });
        });
        const g = {
          edges: [],
          nodes: []
        };
        edges.forEach((e)=> {
          g.edges.push(e.toJson());
        });
        nodes.forEach((n)=> {
          g.nodes.push(n.toJson());
        });
        resolve(g);
      }).catch((err)=>{
        reject(err);
      });
  });
}

function getActions(userId){

  return {
    all: undefined,
    needreply: undefined,
    noreply : undefined,
  }
}

function getLabels(userId){
  return Email.aggregate([
    {$match: {user: userId}},
    {$unwind: '$labels'},
    {$group: {_id: {label: '$labels'}, count: {$sum: 1}}},
    {$sort: {count: -1}},
    {$project: {_id: 0, label: '$_id.label', count: 1}}
  ]);
}

function getConversations(userId){
  return Email.aggregate([
    {$match: {user: userId}},
    {$group: {_id: {thrid: '$thrid'}, nrmsg: {$sum: 1}}},
    {$group: {_id: {nrmsg: '$nrmsg'}, nrthr: {$sum: 1}}},
    {$project: {_id: 0, nrmsg: '$_id.nrmsg', nrthr: 1}},
    {$sort: {nrmsg: -1}}
  ]);
}

//TODO remove
function testNer(){
  Email.find({}).then((emails)=>{
    emails.forEach((email)=>{
      if(email.text != undefined){
        email.plainText = email.text;
      }else if(email.html != undefined){
        email.plainText = striptags(email.html);
      }
      console.log(email.flags);
      console.log(email.labels);
      console.log('#############');

    });
    console.log(emails.length);
  });
}

function getTimeline(req, res){
  let userId = req.user._id;
  const s = {};
  console.log(userId);
  getMonthlyPunchCard(userId)
    .then((m) => {
      s.monthly = m;
      return getDailyPunchCard(userId);
    })
    .then((d) => {
      s.daily = d;
      res.status(200).send(s);
    });
}

function getNetwork(req, res){
  let userId = req.user._id;
  const s = {};
  getTopSender(userId)
    .then((ts) => {
      s.topsender = ts;
      return getTopReceiver(userId);
    })
    .then((tr) => {
      s.topreceiver = tr;
      return getNetworkGraph(userId);
    })
    .then((g) => {
      s.graph = g;
      res.status(200).send(s);
    });
}

function getStructure(req, res){
  let userId = req.user._id;
  const s = {};
  getConversations(userId)
    .then((c)=>{
      s.converstations = c;
      return getLabels(userId);
    })
    .then((l)=>{
      s.labels = l;
      res.status(200).send(s);
    });
}

function getSummary(req, res) {

  let userId = null;
  const s = {
    punchcard: {},
    network: {},
    structural: {}
  };

  //TODO differentiate between send, received and all mails
  //TODO use id of current user?? assuming that the dashboard is embedded in WebApp??
  User.findOne()
    .then((user) => {
      userId = user._id;
      return getMonthlyPunchCard(userId);
    })
    .then((monthlySummary) => {
      s.punchcard.monthly = monthlySummary;
      return getDailyPunchCard(userId);
    })
    .then((dailySummary) => {
      s.punchcard.daily = dailySummary;
      return getTopSender(userId)
    })
    .then((topSender) => {
      s.network.topsender = topSender;
      return getTopReceiver(userId)
    })
    .then((topReceiver) => {
      s.network.topreceiver = topReceiver;
      return getNetworkGraph(userId);
    })
    .then((graph) => {
      s.network.graph = graph;
      return getActions(userId);
    })
    .then((actions)=>{
      s.structural.actions = actions;
      return getConversations(userId)
    })
    .then((conversations)=>{
      s.structural.converstations = conversations;
      return getLabels(userId)
    })
    .then((labels)=>{
      s.structural.labels = labels;
      res.status(200).send(s);
    })
}

export default {
  getSummary,
  getTimeline,
  getNetwork,
  getStructure
};
<commit_msg>dashboard network fix for not existing addresses<commit_after>import Email from '../models/email.model';
import mongoose from 'mongoose';
import User from '../models/user.model';
import striptags from 'striptags';
const ObjectId = mongoose.Types.ObjectId;

function getMonthlyPunchCardWithYear(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {year: {$year: "$date"}, month: {$month: "$date"}, day: {$dayOfMonth: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.year':1, '_id.month': 1, '_id.day': 1}},
    {$project: {_id: 0, year: '$_id.year', month: '$_id.month', day: '$_id.day', count: 1}}
  ]);
}

function getMonthlyPunchCard(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {month: {$month: "$date"}, day: {$dayOfMonth: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.month': 1, '_id.day': 1}},
    {$project: {_id: 0, month: '$_id.month', day: '$_id.day', count: 1}}
  ]);
}

function getDailyPunchCard(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$group: {_id: {day: {$dayOfWeek: "$date"}, hour: {$hour: "$date"}}, count: {$sum: 1}}},
    {$sort: {'_id.day': 1, '_id.hour': 1}},
    {$project: {_id: 0, day: '$_id.day', hour: '$_id.hour', count: 1}}
  ]);
}

function getTopSender(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$unwind: '$from'},
    {$group: {_id: {address: '$from.address', name: '$from.name'}, count: {$sum: 1}}},
    {$sort: {count: -1}},
    {$project: {_id: 0, address: '$_id.address', name: '$_id.name', count: 1}},
    {$limit : 15}
  ]);
}

function getTopReceiver(userId) {
  return Email.aggregate([
    {$match: {user: ObjectId(userId)}},
    {$unwind: '$to'},
    {$group: {_id: {address: '$to.address', name: '$to.name'}, count: {$sum: 1}}},
    {$sort: {'count': -1}},
    {$project: {_id: 0, address: '$_id.address', name: '$_id.name', count: 1}},
    {$limit : 15}
  ]);
}

function getNetworkGraph(userId){

  class Edge{

    constructor(from, to){
      if(from)
        this.from = from.toLowerCase();
      if(to)
        this.to = to.toLowerCase();
      this.count = 1;
    }

    increaseCount(){
      this.count++;
    }

    key(){
      return this.from+'_'+this.to;
    }

    toJson(){
      return {from: this.from, to: this.to, count: this.count};
    }
  }

  class Node{

    constructor(email, name){
      if(email)
        this.email = email.toLowerCase();
      this.name = name;
    }

    key(){
      return this.email;
    }

    toJson(){
      return {email: this.email, name: this.name};
    }
  }

  /** Not implemented as set due the fact that the
   *  compare for objects is not implemented in es6 */
  const edges = new Map();
  const nodes = new Map();

  return new Promise((resolve, reject) => {
    Email.find({user: ObjectId(userId)}, {from: 1, to: 1}).lean()
      .then((emails)=> {
        emails.forEach((email)=> {
          if(email.from != null)
            email.from.forEach((from)=> {
              if(email.to != null)
                email.to.forEach((to)=> {
                  let edge = new Edge(from.address, to.address);
                  if (edges.has(edge.key())) {
                    edge = edges.get(edge.key());
                    edge.increaseCount();
                  } else {
                    edges.set(edge.key(), edge);
                  }
                  let node = new Node(from.address, from.name);
                  if(!nodes.has(node.key()))
                    nodes.set(node.key(), node);
                  node = new Node(to.address, to.name);
                  if(!nodes.has(node.key()))
                    nodes.set(node.key(), node);
                });
            });
        });
        const g = {
          edges: [],
          nodes: []
        };
        edges.forEach((e)=> {
          g.edges.push(e.toJson());
        });
        nodes.forEach((n)=> {
          g.nodes.push(n.toJson());
        });
        resolve(g);
      }).catch((err)=>{
        reject(err);
      });
  });
}

function getActions(userId){

  return {
    all: undefined,
    needreply: undefined,
    noreply : undefined,
  }
}

function getLabels(userId){
  return Email.aggregate([
    {$match: {user: userId}},
    {$unwind: '$labels'},
    {$group: {_id: {label: '$labels'}, count: {$sum: 1}}},
    {$sort: {count: -1}},
    {$project: {_id: 0, label: '$_id.label', count: 1}}
  ]);
}

function getConversations(userId){
  return Email.aggregate([
    {$match: {user: userId}},
    {$group: {_id: {thrid: '$thrid'}, nrmsg: {$sum: 1}}},
    {$group: {_id: {nrmsg: '$nrmsg'}, nrthr: {$sum: 1}}},
    {$project: {_id: 0, nrmsg: '$_id.nrmsg', nrthr: 1}},
    {$sort: {nrmsg: -1}}
  ]);
}

//TODO remove
function testNer(){
  Email.find({}).then((emails)=>{
    emails.forEach((email)=>{
      if(email.text != undefined){
        email.plainText = email.text;
      }else if(email.html != undefined){
        email.plainText = striptags(email.html);
      }
      console.log(email.flags);
      console.log(email.labels);
      console.log('#############');

    });
    console.log(emails.length);
  });
}

function getTimeline(req, res){
  let userId = req.user._id;
  const s = {};
  console.log(userId);
  getMonthlyPunchCard(userId)
    .then((m) => {
      s.monthly = m;
      return getDailyPunchCard(userId);
    })
    .then((d) => {
      s.daily = d;
      res.status(200).send(s);
    });
}

function getNetwork(req, res){
  let userId = req.user._id;
  const s = {};
  getTopSender(userId)
    .then((ts) => {
      s.topsender = ts;
      return getTopReceiver(userId);
    })
    .then((tr) => {
      s.topreceiver = tr;
      return getNetworkGraph(userId);
    })
    .then((g) => {
      s.graph = g;
      res.status(200).send(s);
    });
}

function getStructure(req, res){
  let userId = req.user._id;
  const s = {};
  getConversations(userId)
    .then((c)=>{
      s.converstations = c;
      return getLabels(userId);
    })
    .then((l)=>{
      s.labels = l;
      res.status(200).send(s);
    });
}

function getSummary(req, res) {

  let userId = null;
  const s = {
    punchcard: {},
    network: {},
    structural: {}
  };

  //TODO differentiate between send, received and all mails
  //TODO use id of current user?? assuming that the dashboard is embedded in WebApp??
  User.findOne()
    .then((user) => {
      userId = user._id;
      return getMonthlyPunchCard(userId);
    })
    .then((monthlySummary) => {
      s.punchcard.monthly = monthlySummary;
      return getDailyPunchCard(userId);
    })
    .then((dailySummary) => {
      s.punchcard.daily = dailySummary;
      return getTopSender(userId)
    })
    .then((topSender) => {
      s.network.topsender = topSender;
      return getTopReceiver(userId)
    })
    .then((topReceiver) => {
      s.network.topreceiver = topReceiver;
      return getNetworkGraph(userId);
    })
    .then((graph) => {
      s.network.graph = graph;
      return getActions(userId);
    })
    .then((actions)=>{
      s.structural.actions = actions;
      return getConversations(userId)
    })
    .then((conversations)=>{
      s.structural.converstations = conversations;
      return getLabels(userId)
    })
    .then((labels)=>{
      s.structural.labels = labels;
      res.status(200).send(s);
    })
}

export default {
  getSummary,
  getTimeline,
  getNetwork,
  getStructure
};
<|endoftext|>


<commit_before>import React from 'react';
import Head from '../components/head';

const NotFoundPage = () => (
  <>

NOT FOUND

You just hit a route that doesn't exist... the sadness.

  </>
);

export default NotFoundPage;
<commit_msg>Improve messaging on 404 page<commit_after>import React from 'react';
import Head from '../components/head';
import {Link} from 'gatsby';

const NotFoundPage = () => (
  <>

        404: Page not found

It looks like this page doesn't exist. Sorry about that!

Do you want to <Link to="/">go back home</Link>?

  </>
);

export default NotFoundPage;
<|endoftext|>


<commit_before>(function() {

  var onkey = function(event) {
    if (event.key === 'z' || event.keyCode == 102) {
      vrControls.zeroSensor();
    }
    if (event.key === 'f' || event.keyCode == 102) {
      return vrEffect.setFullScreen(true);
    }
  };

  window.addEventListener("keypress", onkey, true);

}).call(this);<commit_msg>Oops, fix chrome fullscreen hotkey<commit_after>(function() {

  var onkey = function(event) {
    if (event.key === 'z' || event.keyCode == 122) {
      vrControls.zeroSensor();
    }
    if (event.key === 'f' || event.keyCode == 102) {
      return vrEffect.setFullScreen(true);
    }
  };

  window.addEventListener("keypress", onkey, true);

}).call(this);<|endoftext|>


<commit_before>/*
 *  PlugfestTransport.js
 *
 *  David Janes
 *  IOTDB.org
 *  2016-01-23
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var iotdb = require('iotdb');
var iotdb_transport = require('iotdb-transport');
var iotdb_links = require('iotdb-links');
var _ = iotdb._;

var coap = require('coap');

var path = require('path');
var events = require('events');
var util = require('util');
var url = require('url');

var logger = iotdb.logger({
    name: 'iotdb-transport-plugfest',
    module: 'PlugfestTransport',
});

/* --- constructor --- */

/**
 *  Create a transport for Plugfest.
 */
var PlugfestTransport = function (initd) {
    var self = this;

    self.initd = _.defaults(
        initd, {
            channel: iotdb_transport.channel,
            unchannel: iotdb_transport.unchannel,
        },
        iotdb.keystore().get("/transports/PlugfestTransport/initd"), {
            prefix: "",
            server_host: null,
            server_port: 22001,
        }
    );

    self._emitter = new events.EventEmitter();
    self.native = null;
    self.server_url = null;

    self._emitter.on("server-ready", function() {
        self._setup_server();
    });

    _.net.external.ipv4(function (error, ipv4) {
        if (self.initd.server_host) {
            ipv4 = self.initd.server_host;
        } else if (error) {
            ipv4 = _.net.ipv4();
        }

        var server = coap.createServer();
        // server.listen(self.initd.server_port, "0.0.0.0", function (error) {
        server.listen(self.initd.server_port, "0.0.0.0", function (error) {
            if (error) {
                console.log("ERROR", error);
                return;
            }

            self.server_url = "coap://" + ipv4 + ":" + self.initd.server_port;

            console.log("===============================");
            console.log("=== Plugfest CoAP Server Up");
            console.log("=== ");
            console.log("=== Connect at:");
            console.log("=== " + self.server_url);
            console.log("===============================");

            self.native = server;
            self._emitter.emit("server-ready");
        });
    });
};

PlugfestTransport.prototype = new iotdb_transport.Transport();
PlugfestTransport.prototype._class = "PlugfestTransport";

/* --- CoAP server -- */
PlugfestTransport.prototype._setup_server = function () {
    var self = this;

    self.native.on('request', function (req, res) {
        try {
            logger.info({
                method: "_setup_server/on('request')",
                request_url: req.url,
                request_method: req.method,
            }, "CoAP request");

            var _done = function(error, content) {
                if (error) {
                    request.code = 500;
                }

                if (content) {
                    if (_.is.Dictionary(content)) {
                        content = JSON.stringify(content);
                    }

                    res.write(content);
                }

                res.end();
            }

            if (req.url === "/.well-known/core") {
                self._get_well_known(_done);
            } else if (req.url === "/bulletins") {
                self._get_bulletins(_done);
            } else {
                done(null, {});
            }

        } catch (x) {
            logger.error({
                method: "_setup_server/on('request')",
                exception: _.error.message(x),
                stack: x.stack,
            }, "unexpected exception");
        }
    });

};

PlugfestTransport.prototype._get_well_known = function (done) {
    var self = this;

    var resultd = {};
    resultd["/.well-known/core"] = {
        ct: 65202,
    };
    resultd["/bulletins"] = {};

    iotdb_links.produce(resultd, done);
};

PlugfestTransport.prototype._get_bulletins = function (done) {
    done(null, {});
};

/* --- methods --- */

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.list = function (paramd, callback) {
    var self = this;

    self._validate_list(paramd, callback);

    callback({
        end: true,
    });
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.added = function (paramd, callback) {
    var self = this;

    self._validate_added(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.get = function (paramd, callback) {
    var self = this;

    self._validate_get(paramd, callback);

    var gd = _.shallowCopy(paramd);
    gd.value = null;

    callback(new errors.NotImplemented(), gd);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.put = function (paramd, callback) {
    var self = this;

    self._validate_update(paramd, callback);

    var pd = _.shallowCopy(paramd);

    callback(new errors.NotImplemented(), pd);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.updated = function (paramd, callback) {
    var self = this;

    self._validate_updated(paramd, callback);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.remove = function (paramd, callback) {
    var self = this;

    self._validate_remove(paramd, callback);

    var channel = self.initd.channel(self.intid, paramd.id, paramd.band);
};

/* --- internals --- */

/**
 *  API
 */
exports.PlugfestTransport = PlugfestTransport;
<commit_msg>bands() modernized<commit_after>/*
 *  PlugfestTransport.js
 *
 *  David Janes
 *  IOTDB.org
 *  2016-01-23
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

var iotdb = require('iotdb');
var iotdb_transport = require('iotdb-transport');
var iotdb_links = require('iotdb-links');
var _ = iotdb._;

var coap = require('coap');

var path = require('path');
var events = require('events');
var util = require('util');
var url = require('url');

var logger = iotdb.logger({
    name: 'iotdb-transport-plugfest',
    module: 'PlugfestTransport',
});

/* --- constructor --- */

/**
 *  Create a transport for Plugfest.
 */
var PlugfestTransport = function (initd) {
    var self = this;

    self.initd = _.defaults(
        initd, {
            channel: iotdb_transport.channel,
            unchannel: iotdb_transport.unchannel,
        },
        iotdb.keystore().get("/transports/PlugfestTransport/initd"), {
            prefix: "",
            server_host: null,
            server_port: 22001,
        }
    );

    self._emitter = new events.EventEmitter();
    self.native = null;
    self.server_url = null;

    self._emitter.on("server-ready", function() {
        self._setup_server();
    });

    _.net.external.ipv4(function (error, ipv4) {
        if (self.initd.server_host) {
            ipv4 = self.initd.server_host;
        } else if (error) {
            ipv4 = _.net.ipv4();
        }

        var server = coap.createServer();
        // server.listen(self.initd.server_port, "0.0.0.0", function (error) {
        server.listen(self.initd.server_port, "0.0.0.0", function (error) {
            if (error) {
                console.log("ERROR", error);
                return;
            }

            self.server_url = "coap://" + ipv4 + ":" + self.initd.server_port;

            console.log("===============================");
            console.log("=== Plugfest CoAP Server Up");
            console.log("=== ");
            console.log("=== Connect at:");
            console.log("=== " + self.server_url);
            console.log("===============================");

            self.native = server;
            self._emitter.emit("server-ready");
        });
    });
};

PlugfestTransport.prototype = new iotdb_transport.Transport();
PlugfestTransport.prototype._class = "PlugfestTransport";

/* --- CoAP server -- */
PlugfestTransport.prototype._setup_server = function () {
    var self = this;

    self.native.on('request', function (req, res) {
        try {
            logger.info({
                method: "_setup_server/on('request')",
                request_url: req.url,
                request_method: req.method,
            }, "CoAP request");

            var _done = function(error, content) {
                if (error) {
                    request.code = 500;
                }

                if (content) {
                    if (_.is.Dictionary(content)) {
                        content = JSON.stringify(content);
                    }

                    res.write(content);
                }

                res.end();
            }

            if (req.url === "/.well-known/core") {
                self._get_well_known(_done);
            } else if (req.url === "/bulletins") {
                self._get_bulletins(_done);
            } else {
                done(null, {});
            }

        } catch (x) {
            logger.error({
                method: "_setup_server/on('request')",
                exception: _.error.message(x),
                stack: x.stack,
            }, "unexpected exception");
        }
    });

};

PlugfestTransport.prototype._get_well_known = function (done) {
    var self = this;

    var resultd = {};
    resultd["/.well-known/core"] = {
        ct: 65202,
    };
    resultd["/bulletins"] = {};

    iotdb_links.produce(resultd, done);
};

PlugfestTransport.prototype._get_bulletins = function (done) {
    done(null, {});
};

/* --- methods --- */

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.list = function (paramd, callback) {
    var self = this;

    self._validate_list(paramd, callback);

    callback({
        end: true,
    });
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.added = function (paramd, callback) {
    var self = this;

    self._validate_added(paramd, callback);

    var channel = self.initd.channel(self.initd, paramd.id);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.get = function (paramd, callback) {
    var self = this;

    self._validate_get(paramd, callback);

    var gd = _.shallowCopy(paramd);
    gd.value = null;

    callback(new errors.NotImplemented(), gd);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.put = function (paramd, callback) {
    var self = this;

    self._validate_update(paramd, callback);

    var pd = _.shallowCopy(paramd);

    callback(new errors.NotImplemented(), pd);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.updated = function (paramd, callback) {
    var self = this;

    self._validate_updated(paramd, callback);
};

/**
 *  See {iotdb_transport.Transport#bands} for documentation.
 */
PlugfestTransport.prototype.bands = function (paramd, callback) {
    var self = this;

    self._validate_bands(paramd, callback);

    var bd = _.shallowCopy(paramd);

    callback(new errors.NeverImplemented(), bd);
};

/**
 *  See {iotdb_transport.Transport#Transport} for documentation.
 */
PlugfestTransport.prototype.remove = function (paramd, callback) {
    var self = this;

    self._validate_remove(paramd, callback);

    var channel = self.initd.channel(self.intid, paramd.id, paramd.band);
};

/* --- internals --- */

/**
 *  API
 */
exports.PlugfestTransport = PlugfestTransport;
<|endoftext|>


<commit_before>// ==========================================================================
//                          DG.MapController
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph_map_common/data_display_controller');

/** @class

  DG.MapController provides controller functionality, for maps.

  @extends SC.DataDisplayController
*/
DG.MapController = DG.DataDisplayController.extend(
/** @scope DG.MapController.prototype */
  (function() {

/*
    function getCollectionClientFromDragData( iContext, iDragData) {
      var collectionID = iDragData.collection && iDragData.collection.get('id');
      return iContext && !SC.none( collectionID) && iContext.getCollectionByID( collectionID);
    }
*/

    return {
      mapModel: function() {
        return this.get('dataDisplayModel');
      }.property('dataDisplayModel'),
      mapView: null,

      createComponentStorage: function() {
        var storage = sc_super();

        storage.mapModelStorage = this.get('mapModel').createStorage();
        return storage;
      },

      restoreComponentStorage: function( iStorage, iDocumentID) {
        sc_super();

        this.get('dataDisplayModel').restoreStorage( iStorage);
      },

      viewDidChange: function() {
        var componentView = this.get('view'),
            mapView = componentView && componentView.get('contentView');
        if( mapView) {
          this.set('mapView', mapView);
          this.set('legendView', mapView.get('legendView'));
          mapView.set('controller', this);
        }
      }.observes('view'),

      /**
      Get the menu items from the graph and its components.
        @property { Array of menu items }
      */
      gearMenuItems: function() {
        var tMap = this.getPath('mapModel');
        return SC.none( tMap) ? [] : tMap.getGearMenuItems();
      }.property('mapModel')

    };

  }()) // function closure
);

<commit_msg>[Fixes #89947030] Write out hidden cases because superclass doesn't.<commit_after>// ==========================================================================
//                          DG.MapController
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('components/graph_map_common/data_display_controller');

/** @class

  DG.MapController provides controller functionality, for maps.

  @extends SC.DataDisplayController
*/
DG.MapController = DG.DataDisplayController.extend(
/** @scope DG.MapController.prototype */
  (function() {

/*
    function getCollectionClientFromDragData( iContext, iDragData) {
      var collectionID = iDragData.collection && iDragData.collection.get('id');
      return iContext && !SC.none( collectionID) && iContext.getCollectionByID( collectionID);
    }
*/

    return {
      mapModel: function() {
        return this.get('dataDisplayModel');
      }.property('dataDisplayModel'),
      mapView: null,

      createComponentStorage: function() {
        var storage = sc_super(),
            dataConfiguration = this.getPath('mapModel.dataConfiguration'),
            hiddenCases = dataConfiguration && dataConfiguration.get('hiddenCases' );

        if( hiddenCases) {
          storage.hiddenCases = hiddenCases.map(function (iCase) {
            return iCase.get('id');
          });
        }
        storage.mapModelStorage = this.get('mapModel').createStorage();
        return storage;
      },

      restoreComponentStorage: function( iStorage, iDocumentID) {
        sc_super();

        this.get('dataDisplayModel').restoreStorage( iStorage);
      },

      viewDidChange: function() {
        var componentView = this.get('view'),
            mapView = componentView && componentView.get('contentView');
        if( mapView) {
          this.set('mapView', mapView);
          this.set('legendView', mapView.get('legendView'));
          mapView.set('controller', this);
        }
      }.observes('view'),

      /**
      Get the menu items from the graph and its components.
        @property { Array of menu items }
      */
      gearMenuItems: function() {
        var tMap = this.getPath('mapModel');
        return SC.none( tMap) ? [] : tMap.getGearMenuItems();
      }.property('mapModel')

    };

  }()) // function closure
);

<|endoftext|>


<commit_before>var autocomplete = function (node, rooms, templateManager) {
	"use strict";

	var currentUsers = [];
	var current = 0;

	node.addEventListener("keydown", function (e) {
		var el;

		if (currentUsers.length === 0) {
			return;
		}

		if (e.which === 9 || e.which === 13) {
			e.preventDefault();
			e.stopImmediatePropagation();

			var text = node.value.lastIndexOf(" ");
			node.value = (text > -1 ? node.value.substr(0, text + 1) : "") + "@" + currentUsers[current].name + " ";
			Input.adjust();
			document.getElementById("autocomplete").innerHTML = "";
			currentUsers = [];
			current = 0;

			return false;
		}

		if (e.which === 38) {
			e.preventDefault();
			e.stopImmediatePropagation();

			current = --current < 0 ? currentUsers.length - 1 : current;
			el = document.querySelector("#autocomplete-current");

			if (el) {
				el.id = "";
			}

			document.querySelectorAll(".autocomplete-entry")[current].id = "autocomplete-current";

			return false;
		}

		if (e.which === 40) {
			e.preventDefault();
			e.stopImmediatePropagation();

			current = ++current % currentUsers.length;
			el = document.querySelector("#autocomplete-current");

			if (el) {
				el.id = "";
			}

			document.querySelectorAll(".autocomplete-entry")[current].id = "autocomplete-current";

			return false;
		}
	});

	node.addEventListener("input", function () {
		var pos = this.selectionStart;
		var word = this.value;
		var pre = this.value.substr(0, pos);

		if (pre.indexOf(" ") > -1) {
			var words = pre.split(" ");
			word = words[words.length - 1];
		}

		if (/^@[a-z][a-z-]*$/i.test(word)) {
			var name = word.substr(1);
			var room = rooms.getCurrent();
			currentUsers = [];

			room.getUsers().forEach(function (user) {
				if (user.name.toLowerCase().startsWith(name.toLowerCase())) {
					currentUsers.push(user);
				}
			});

			document.getElementById("autocomplete").innerHTML = templateManager.get("autocomplete")(currentUsers);

			if (currentUsers.length > 0) {
				document.querySelector(".autocomplete-entry").id = "autocomplete-current";
			}
		} else {
			document.getElementById("autocomplete").innerHTML = "";
			currentUsers = [];
		}
	});
};
<commit_msg>Allow more keys to autocomplete usernames<commit_after>var autocomplete = function (node, rooms, templateManager) {
	"use strict";

	var currentUsers = [];
	var current = 0;

	node.addEventListener("keydown", function (e) {
		var el;

		if (currentUsers.length === 0) {
			return;
		}

		// tab, enter, comma, dot, question mark, exclamation mark, colon
		var keys = [9, 13, 188, 190, 219, 49];

		if (keys.indexOf(e.which) > -1) {
			if (e.which === 9 || e.which === 13) {
				e.preventDefault();
				e.stopImmediatePropagation();
			}

			var text = node.value.lastIndexOf(" ");
			node.value = (text > -1 ? node.value.substr(0, text + 1) : "") + "@" + currentUsers[current].name + (e.which === 9 || e.which === 13 ? " " : "");
			Input.adjust();
			document.getElementById("autocomplete").innerHTML = "";
			currentUsers = [];
			current = 0;

			return false;
		}

		if (e.which === 38) {
			e.preventDefault();
			e.stopImmediatePropagation();

			current = --current < 0 ? currentUsers.length - 1 : current;
			el = document.querySelector("#autocomplete-current");

			if (el) {
				el.id = "";
			}

			document.querySelectorAll(".autocomplete-entry")[current].id = "autocomplete-current";

			return false;
		}

		if (e.which === 40) {
			e.preventDefault();
			e.stopImmediatePropagation();

			current = ++current % currentUsers.length;
			el = document.querySelector("#autocomplete-current");

			if (el) {
				el.id = "";
			}

			document.querySelectorAll(".autocomplete-entry")[current].id = "autocomplete-current";

			return false;
		}
	});

	node.addEventListener("input", function () {
		var pos = this.selectionStart;
		var word = this.value;
		var pre = this.value.substr(0, pos);

		if (pre.indexOf(" ") > -1) {
			var words = pre.split(" ");
			word = words[words.length - 1];
		}

		if (/^@[a-z][a-z-]*$/i.test(word)) {
			var name = word.substr(1);
			var room = rooms.getCurrent();
			currentUsers = [];

			room.getUsers().forEach(function (user) {
				if (user.name.toLowerCase().startsWith(name.toLowerCase())) {
					currentUsers.push(user);
				}
			});

			document.getElementById("autocomplete").innerHTML = templateManager.get("autocomplete")(currentUsers);

			if (currentUsers.length > 0) {
				document.querySelector(".autocomplete-entry").id = "autocomplete-current";
			}
		} else {
			document.getElementById("autocomplete").innerHTML = "";
			currentUsers = [];
		}
	});
};
<|endoftext|>