<commit_before>const apps = [
  {
    title: 'Spaces by Wix',
    description:
      'Spaces by Wix, the place to connect with your favorite online store, business, website or community',
    image: require('./showcase/spaces.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.wix.android',
    appStore: 'https://apps.apple.com/us/app/wix/id1099748482',
  },
  {
    title: 'Wix Owner',
    description:
      'Use the Wix Owner app to build, customize and manage a website and mobile app experience for your business and community.',
    image: require('./showcase/owner.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.wix.admin',
    appStore: 'https://apps.apple.com/us/app/wix-owner-websites-apps/id1545924344',
  },
  {
    title: 'feastr',
    description: 'Weight loss meal plan.',
    image: require('./showcase/feastr.png'),
    playStore: 'https://play.google.com/store/apps/details?id=de.feastr.plan',
    appStore: 'https://apps.apple.com/us/app/feastr-weight-loss-meal-plan/id1200732621',
  },
  {
    title: 'Amber',
    description: 'Amber is the easiest and safest place to buy and sell bitcoin.',
    image: require('./showcase/amber.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.getamber.app&hl=en_US&gl=US',
    appStore: 'https://apps.apple.com/au/app/amber-bitcoin-made-easy/id1410639317',
  },
  {
    title: 'Klarna',
    description:
      'Try before you buy and make hassle-free returns, without taking money out of your pocket in the meantime.',
    image: require('./showcase/klarna.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.myklarnamobile',
    appStore: 'https://apps.apple.com/app/klarna-smoooth-shopping/id1115120118',
  },
  {
    title: 'Delta',
    description: 'Investment Portfolio Tracker',
    image: require('./showcase/delta.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.getdelta.android',
    appStore: 'https://apps.apple.com/us/app/apple-store/id1288676542',
  },
  {
    title: 'Squarespace Scheduling',
    description: `The Squarespace Scheduling app is your appointment-scheduling assistant that helps manage your calendar anytime, anywhere.`,
    image: require('./showcase/acuity.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.acuityscheduling.app.android',
    appStore: 'https://apps.apple.com/us/app/acuity-schedule-admin/id1179146771',
  },
  {
    title: 'Magic Eden',
    description: 'Magic Eden is the leading NFT Marketplace on Solana.',
    image: require('./showcase/magiceden.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.magiceden.android',
    appStore: 'https://apps.apple.com/us/app/magic-eden-nft-marketplace/id1602924580',
  },
  {
    title: 'Cookomix',
    description: `Cookomix est une application d'échange de recettes adaptées au Thermomix ®. Découvrez-y les meilleures recettes Thermomix ® écrites et commentées par la communauté et présentées de manière simple et lisible comme sur votre sur votre appareil !`,
    image: require('./showcase/cookomix.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.cookomix.mobile',
    appStore: 'https://itunes.apple.com/fr/app/cookomix/id1353826313',
  },
  {
    title: 'Taimi - LGBTQ+ Dating and Chat',
    description: 'World’s Largest LGBTQ+ Platform',
    image: require('./showcase/taimi.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.digitail.digitail&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/digitail-for-pets/id1473042508',
  },
  {
    title: 'getAbstract Book Summaries',
    description:
      'Get the key insights from 20,000+ of the best nonfiction books, articles and video talks summarized into 10-minute summaries.',
    image: require('./showcase/getAbstract.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.soxes.getAbstract&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/getabstract-book-summaries/id338552990',
  },
  {
    title: 'HoneyBook',
    description: `HoneyBook helps you get the client you really want. Reply to questions quickly, send professional invoices, contracts, quotes and brochures. Manage your clients and grow your business.`,
    image: require('./showcase/honeybook.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.honeybook.alfred',
    appStore: 'https://apps.apple.com/il/app/honeybook-small-business-crm/id1104772757',
  },
  {
    title: 'Digitail - Smarter Pet Care',
    description: 'Be the best pet parent you can be with Digitail! The app is a digital medical record that safe-guards all the info about your furry family member and allows you to share it with your vet.',
    image: require('./showcase/digitail.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.digitail.digitail&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/digitail-for-pets/id1473042508',
  },
  {
    title: 'BetMentor: Betting Tips',
    description: 'BetMentor Betting Tips is an app that offers several tools to help you to get football predictions increase your success rate at Social Betting Casino!',
    image: require('./showcase/bementor.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.betmentor',
    appStore: 'https://apps.apple.com/az/app/betmentor-football-predictions/id1542592232',
  },
  {
    title: 'Thera: Mood tracker & Journal',
    description: 'Thera – your personal mental health tracker and your diary with fingerprint lock.',
    image: require('./showcase/thera.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.sintylapse.therapydiaries',
    appStore: 'https://apps.apple.com/by/app/id1474161008',
  },
  {
    title: 'hello aurora: forecast app',
    description: 'Hello aurora is a mobile application that will act as your best friend when it comes to finding the Northern Lights.',
    image: require('./showcase/aurora.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.helloaurora',
    appStore: 'https://apps.apple.com/us/app/hello-aurora/id1457810302',
  },
  {
    title: 'Nifty',
    description: `Nifty helps you simplify your teams’ workflow by consolidating all phases of your project's lifecycle into one powerful and intuitive tool.`,
    image: require('./showcase/nifty.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.niftypm',
    appStore: 'https://apps.apple.com/us/app/nifty-manage-projects-tasks/id1366408429',
  },
  {
    title: 'Clubhouse',
    description: 'Clubhouse is collaborative project management without all the management.',
    image: require('./showcase/clubhouse.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.clubhouse.clubhouse',
    appStore: 'https://apps.apple.com/us/app/clubhouse/id1193784808',
  },
  {
    title: 'Bene',
    description: 'Wellness and sustainable awareness',
    image: require('./showcase/bene.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.jp.bene.app',
    appStore: 'https://apps.apple.com/jp/app/id1498032984',
  },
  {
    title: 'Nox',
    description: 'AI Powered Dream Journal',
    image: require('./showcase/Nox.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.aspect.nox',
    appStore: 'https://apps.apple.com/zw/app/nox-ai-powered-dream-journal/id1543257201',
  },
  {
    title: 'Obitrain',
    description:
      'Create your own exercises and training sessions, track your progress and share it with your community.',
    image: require('./showcase/obitrain.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.obitrain.obiapp',
    appStore: 'https://apps.apple.com/us/app/obitrain/id1462514186',
  },
  {
    title: 'Arkham Cards',
    description: 'Create and manage deck lists for the Arkham Horror LCG card game.',
    image: require('./showcase/arkhamCards.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.arkhamcards',
    appStore: 'https://apps.apple.com/us/app/arkham-cards/id1424000351',
  },
  {
    title: 'Galarm',
    description: 'Alarms and Reminders',
    image: require('./showcase/galarm.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.galarmapp&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/galarm-alarms-and-reminders/id1187849174',
  },
  {
    title: 'bob HR',
    description: "bob's mobile app provides a seamless HR experience on the go.",
    image: require('./showcase/hibob.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.hibob',
    appStore: 'https://apps.apple.com/us/app/bob-hr/id1297148884',
  },
  {
    title: 'intelinvest',
    image: require('./showcase/intelivest.png'),
    playStore: 'https://play.google.com/store/apps/details?id=ru.intelinvest.portfolio',
    appStore: 'https://apps.apple.com/ru/app/intelinvest-учет-инвестиций/id1422478197',
  },
  {
    title: 'ChurchTools',
    description: '',
    image: require('./showcase/churchtools.png'),
    playStore: 'https://play.google.com/store/apps/details?id=tools.church.app&hl=en&gl=US',
    appStore: 'https://apps.apple.com/de/app/churchtools/id1413263051#?platform=iphone',
  },
  {
    title: 'Deevent',
    description:
      'Highlight the best attractions, places, museums and monuments in the biggest cities!',
    image: require('./showcase/deevent.png'),
    playStore: 'https://play.google.com/store/apps/details?id=app.deevent',
    appStore: 'https://apps.apple.com/us/app/deevent/id1519499216',
  },
  {
    title: 'Cinepicks',
    description:
      'Browse a huge list of movies and see which ones are available on popular streaming services.',
    image: require('./showcase/cinepicks.png'),
    playStore:
      'https://play.google.com/store/apps/details?id=co.uk.jaygould.cinepicks&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1',
    appStore: 'https://apps.apple.com/gb/app/cinepicks-find-swipe-films/id1534621509',
  },
  {
    title: 'StretchMinder',
    description: 'StretchMinder helps to increase productivity while keeping your health in check.',
    image: require('./showcase/stretchminder.png'),
    appStore: 'https://apps.apple.com/us/app/stretchminder-stand-up-move/id1518522560',
  },
  {
    title: 'Подарки и Одежда на Новый Год',
    image: require('./showcase/christmas.png'),
    appStore: 'https://apps.apple.com/ru/app/id1446775875',
  }
];

apps.forEach((app) => {
  if (
    !app.image ||
    (app.image instanceof String && (app.image.startsWith('http') || app.image.startsWith('//')))
  ) {
    throw new Error(
      `Bad user site image preview = ${app.image}. The image should be hosted on Docusaurus site, and not use remote HTTP or HTTPS URLs`
    );
  }
});

export default apps;
<commit_msg>Fix wrong link in showcase section<commit_after>const apps = [
  {
    title: 'Spaces by Wix',
    description:
      'Spaces by Wix, the place to connect with your favorite online store, business, website or community',
    image: require('./showcase/spaces.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.wix.android',
    appStore: 'https://apps.apple.com/us/app/wix/id1099748482',
  },
  {
    title: 'Wix Owner',
    description:
      'Use the Wix Owner app to build, customize and manage a website and mobile app experience for your business and community.',
    image: require('./showcase/owner.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.wix.admin',
    appStore: 'https://apps.apple.com/us/app/wix-owner-websites-apps/id1545924344',
  },
  {
    title: 'feastr',
    description: 'Weight loss meal plan.',
    image: require('./showcase/feastr.png'),
    playStore: 'https://play.google.com/store/apps/details?id=de.feastr.plan',
    appStore: 'https://apps.apple.com/us/app/feastr-weight-loss-meal-plan/id1200732621',
  },
  {
    title: 'Amber',
    description: 'Amber is the easiest and safest place to buy and sell bitcoin.',
    image: require('./showcase/amber.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.getamber.app&hl=en_US&gl=US',
    appStore: 'https://apps.apple.com/au/app/amber-bitcoin-made-easy/id1410639317',
  },
  {
    title: 'Klarna',
    description:
      'Try before you buy and make hassle-free returns, without taking money out of your pocket in the meantime.',
    image: require('./showcase/klarna.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.myklarnamobile',
    appStore: 'https://apps.apple.com/app/klarna-smoooth-shopping/id1115120118',
  },
  {
    title: 'Delta',
    description: 'Investment Portfolio Tracker',
    image: require('./showcase/delta.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.getdelta.android',
    appStore: 'https://apps.apple.com/us/app/apple-store/id1288676542',
  },
  {
    title: 'Squarespace Scheduling',
    description: `The Squarespace Scheduling app is your appointment-scheduling assistant that helps manage your calendar anytime, anywhere.`,
    image: require('./showcase/acuity.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.acuityscheduling.app.android',
    appStore: 'https://apps.apple.com/us/app/acuity-schedule-admin/id1179146771',
  },
  {
    title: 'Magic Eden',
    description: 'Magic Eden is the leading NFT Marketplace on Solana.',
    image: require('./showcase/magiceden.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.magiceden.android',
    appStore: 'https://apps.apple.com/us/app/magic-eden-nft-marketplace/id1602924580',
  },
  {
    title: 'Cookomix',
    description: `Cookomix est une application d'échange de recettes adaptées au Thermomix ®. Découvrez-y les meilleures recettes Thermomix ® écrites et commentées par la communauté et présentées de manière simple et lisible comme sur votre sur votre appareil !`,
    image: require('./showcase/cookomix.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.cookomix.mobile',
    appStore: 'https://itunes.apple.com/fr/app/cookomix/id1353826313',
  },
  {
    title: 'Taimi - LGBTQ+ Dating and Chat',
    description: 'World’s Largest LGBTQ+ Platform',
    image: require('./showcase/taimi.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.takimi.android&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/taimi-lgbtq-dating-chat/id1282966364',
  },
  {
    title: 'getAbstract Book Summaries',
    description:
      'Get the key insights from 20,000+ of the best nonfiction books, articles and video talks summarized into 10-minute summaries.',
    image: require('./showcase/getAbstract.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.soxes.getAbstract&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/getabstract-book-summaries/id338552990',
  },
  {
    title: 'HoneyBook',
    description: `HoneyBook helps you get the client you really want. Reply to questions quickly, send professional invoices, contracts, quotes and brochures. Manage your clients and grow your business.`,
    image: require('./showcase/honeybook.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.honeybook.alfred',
    appStore: 'https://apps.apple.com/il/app/honeybook-small-business-crm/id1104772757',
  },
  {
    title: 'Digitail - Smarter Pet Care',
    description: 'Be the best pet parent you can be with Digitail! The app is a digital medical record that safe-guards all the info about your furry family member and allows you to share it with your vet.',
    image: require('./showcase/digitail.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.digitail.digitail&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/digitail-for-pets/id1473042508',
  },
  {
    title: 'BetMentor: Betting Tips',
    description: 'BetMentor Betting Tips is an app that offers several tools to help you to get football predictions increase your success rate at Social Betting Casino!',
    image: require('./showcase/bementor.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.betmentor',
    appStore: 'https://apps.apple.com/az/app/betmentor-football-predictions/id1542592232',
  },
  {
    title: 'Thera: Mood tracker & Journal',
    description: 'Thera – your personal mental health tracker and your diary with fingerprint lock.',
    image: require('./showcase/thera.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.sintylapse.therapydiaries',
    appStore: 'https://apps.apple.com/by/app/id1474161008',
  },
  {
    title: 'hello aurora: forecast app',
    description: 'Hello aurora is a mobile application that will act as your best friend when it comes to finding the Northern Lights.',
    image: require('./showcase/aurora.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.helloaurora',
    appStore: 'https://apps.apple.com/us/app/hello-aurora/id1457810302',
  },
  {
    title: 'Nifty',
    description: `Nifty helps you simplify your teams’ workflow by consolidating all phases of your project's lifecycle into one powerful and intuitive tool.`,
    image: require('./showcase/nifty.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.niftypm',
    appStore: 'https://apps.apple.com/us/app/nifty-manage-projects-tasks/id1366408429',
  },
  {
    title: 'Clubhouse',
    description: 'Clubhouse is collaborative project management without all the management.',
    image: require('./showcase/clubhouse.png'),
    playStore: 'https://play.google.com/store/apps/details?id=io.clubhouse.clubhouse',
    appStore: 'https://apps.apple.com/us/app/clubhouse/id1193784808',
  },
  {
    title: 'Bene',
    description: 'Wellness and sustainable awareness',
    image: require('./showcase/bene.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.jp.bene.app',
    appStore: 'https://apps.apple.com/jp/app/id1498032984',
  },
  {
    title: 'Nox',
    description: 'AI Powered Dream Journal',
    image: require('./showcase/Nox.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.aspect.nox',
    appStore: 'https://apps.apple.com/zw/app/nox-ai-powered-dream-journal/id1543257201',
  },
  {
    title: 'Obitrain',
    description:
      'Create your own exercises and training sessions, track your progress and share it with your community.',
    image: require('./showcase/obitrain.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.obitrain.obiapp',
    appStore: 'https://apps.apple.com/us/app/obitrain/id1462514186',
  },
  {
    title: 'Arkham Cards',
    description: 'Create and manage deck lists for the Arkham Horror LCG card game.',
    image: require('./showcase/arkhamCards.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.arkhamcards',
    appStore: 'https://apps.apple.com/us/app/arkham-cards/id1424000351',
  },
  {
    title: 'Galarm',
    description: 'Alarms and Reminders',
    image: require('./showcase/galarm.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.galarmapp&hl=en&gl=US',
    appStore: 'https://apps.apple.com/us/app/galarm-alarms-and-reminders/id1187849174',
  },
  {
    title: 'bob HR',
    description: "bob's mobile app provides a seamless HR experience on the go.",
    image: require('./showcase/hibob.png'),
    playStore: 'https://play.google.com/store/apps/details?id=com.hibob',
    appStore: 'https://apps.apple.com/us/app/bob-hr/id1297148884',
  },
  {
    title: 'intelinvest',
    image: require('./showcase/intelivest.png'),
    playStore: 'https://play.google.com/store/apps/details?id=ru.intelinvest.portfolio',
    appStore: 'https://apps.apple.com/ru/app/intelinvest-учет-инвестиций/id1422478197',
  },
  {
    title: 'ChurchTools',
    description: '',
    image: require('./showcase/churchtools.png'),
    playStore: 'https://play.google.com/store/apps/details?id=tools.church.app&hl=en&gl=US',
    appStore: 'https://apps.apple.com/de/app/churchtools/id1413263051#?platform=iphone',
  },
  {
    title: 'Deevent',
    description:
      'Highlight the best attractions, places, museums and monuments in the biggest cities!',
    image: require('./showcase/deevent.png'),
    playStore: 'https://play.google.com/store/apps/details?id=app.deevent',
    appStore: 'https://apps.apple.com/us/app/deevent/id1519499216',
  },
  {
    title: 'Cinepicks',
    description:
      'Browse a huge list of movies and see which ones are available on popular streaming services.',
    image: require('./showcase/cinepicks.png'),
    playStore:
      'https://play.google.com/store/apps/details?id=co.uk.jaygould.cinepicks&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1',
    appStore: 'https://apps.apple.com/gb/app/cinepicks-find-swipe-films/id1534621509',
  },
  {
    title: 'StretchMinder',
    description: 'StretchMinder helps to increase productivity while keeping your health in check.',
    image: require('./showcase/stretchminder.png'),
    appStore: 'https://apps.apple.com/us/app/stretchminder-stand-up-move/id1518522560',
  },
  {
    title: 'Подарки и Одежда на Новый Год',
    image: require('./showcase/christmas.png'),
    appStore: 'https://apps.apple.com/ru/app/id1446775875',
  }
];

apps.forEach((app) => {
  if (
    !app.image ||
    (app.image instanceof String && (app.image.startsWith('http') || app.image.startsWith('//')))
  ) {
    throw new Error(
      `Bad user site image preview = ${app.image}. The image should be hosted on Docusaurus site, and not use remote HTTP or HTTPS URLs`
    );
  }
});

export default apps;
<|endoftext|>


<commit_before>import EventEmitter from 'events'

import differenceBy from 'lodash.differenceby'
import eventToPromise from 'event-to-promise'
import filter from 'lodash.filter'
import includes from 'lodash.includes'
import intersection from 'lodash.intersection'
import uniq from 'lodash.uniq'

import { CronJob } from 'cron'
import { default as mapToArray } from 'lodash.map'

class Emitter extends EventEmitter {}

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

const noop = () => {}

const LOAD_BALANCER_DEBUG = 1
const debug = LOAD_BALANCER_DEBUG
  ? str => console.log(`[load-balancer]${str}`)
  : noop

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

const PERFORMANCE_MODE = 0
const DENSITY_MODE = 1

// Delay between each ressources evaluation in minutes.
// Must be less than MINUTES_OF_HISTORICAL_DATA.
const EXECUTION_DELAY = 1
const MINUTES_OF_HISTORICAL_DATA = 30

// CPU threshold in percent.
const DEFAULT_CRITICAL_THRESHOLD_CPU = 90.0

// Memory threshold in MB.
const DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE = 64.0

// Thresholds factors.
const HIGH_THRESHOLD_FACTOR = 0.85
const LOW_THRESHOLD_FACTOR = 0.25

const HIGH_THRESHOLD_MEMORY_FREE_FACTOR = 1.25
const LOW_THRESHOLD_MEMORY_FREE_FACTOR = 20.0

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

export const configurationSchema = {
  type: 'object',

  properties: {
    plans: {
      type: 'array',
      description: 'an array of plans',
      title: 'Plans',

      items: {
        type: 'object',
        title: 'Plan',

        properties: {
          name: {
            type: 'string',
            title: 'Name'
          },

          mode: {
            enum: [ 'Performance mode', 'Density mode' ],
            title: 'Mode'
          },

          pools: {
            type: 'array',
            $type: 'Pool',
            description: 'list of pools where to apply the policy'
          },

          thresholds: {
            type: 'object',
            title: 'Critical thresholds',

            properties: {
              cpu: {
                type: 'integer',
                title: 'CPU (%)',
                description: 'default: 90%'
              },
              memoryFree: {
                type: 'integer',
                title: 'RAM, Free memory (MB)',
                description: 'default: 64MB'
              }
            }
          },

          excludedHosts: {
            type: 'array',
            title: 'Excluded hosts',
            $type: 'Host',
            description: 'list of hosts that are not affected by the plan'
          }
        },

        required: [ 'name', 'mode', 'pools' ]
      },

      minItems: 1
    }
  },

  additionalProperties: false
}

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

// Create a job not enabled by default.
// A job is a cron task, a running and enabled state.
const makeJob = (cronPattern, fn) => {
  const job = {
    running: false,
    emitter: new Emitter()
  }

  job.cron = new CronJob(cronPattern, async () => {
    if (job.running) {
      return
    }

    job.running = true

    try {
      await fn()
    } catch (error) {
      console.error('[WARN] scheduled function:', error && error.stack || error)
    } finally {
      job.running = false
      job.emitter.emit('finish')
    }
  })

  job.isEnabled = () => job.cron.running

  return job
}

// Compare a list of objects and give the best.
function searchObject (objects, fun) {
  let object = objects[0]

  for (let i = 1; i < objects.length; i++) {
    if (fun(object, objects[i]) > 0) {
      object = objects[i]
    }
  }

  return object
}

function sortHostsByPool (hosts) {
  const struct = {}

  for (const host of hosts) {
    const poolId = host.$poolId
    let pool = struct[poolId]

    if (pool === undefined) {
      pool = struct[poolId] = []
    }

    pool.push(host)
  }

  return struct
}

// ===================================================================
// Averages.
// ===================================================================

function computeAverage (values, nPoints = values.length) {
  let sum = 0
  let tot = 0

  const { length } = values

  for (let i = length - nPoints; i < length; i++) {
    const value = values[i]

    sum += value || 0

    if (value) {
      tot += 1
    }
  }

  return sum / tot
}

function computeRessourcesAverage (objects, objectsStats, nPoints) {
  const averages = {}

  for (const object of objects) {
    const { id } = object
    const { stats } = objectsStats[id]
    const objectAverages = averages[id] = {}

    objectAverages.cpu = computeAverage(
      mapToArray(stats.cpus, cpu => computeAverage(cpu, nPoints))
    )

    objectAverages.memoryFree = computeAverage(stats.memoryFree, nPoints)
    objectAverages.memory = computeAverage(stats.memory, nPoints)
  }

  return averages
}

function computeRessourcesAverageWithWeight (averages1, averages2, ratio) {
  const averages = {}

  for (const id in averages1) {
    const objectAverages = averages[id] = {}

    for (const averageName in averages1[id]) {
      objectAverages[averageName] = averages1[id][averageName] * ratio + averages2[id][averageName] * (1 - ratio)
    }
  }

  return averages
}

function setRealCpuAverageOfVms (vms, vmsAverages) {
  for (const vm of vms) {
    vmsAverages[vm.id].cpu /= vm.CPUs.number
  }
}

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

const numberOrDefault = (value, def) => (value >= 0) ? value : def

class Plan {
  constructor (xo, name, poolIds, {
    excludedHosts,
    thresholds
  } = {}) {
    this.xo = xo
    this._name = name
    this._poolIds = poolIds
    this._excludedHosts = excludedHosts
    this._thresholds = {
      cpu: {
        critical: numberOrDefault(thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
      },
      memoryFree: {
        critical: numberOrDefault(thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
      }
    }

    for (const key in this._thresholds) {
      const attr = this._thresholds[key]
      const { critical } = attr

      if (key === 'memoryFree') {
        attr.high = critical * HIGH_THRESHOLD_MEMORY_FREE_FACTOR
        attr.low = critical * LOW_THRESHOLD_MEMORY_FREE_FACTOR
      } else {
        attr.high = critical * HIGH_THRESHOLD_FACTOR
        attr.low = critical * LOW_THRESHOLD_FACTOR
      }
    }
  }

  execute () {
    throw new Error('Not implemented')
  }

  // ===================================================================
  // Get hosts to optimize.
  // ===================================================================

  async _findHostsToOptimize () {
    const hosts = this._getHosts()
    const hostsStats = await this._getHostsStats(hosts, 'minutes')

    // Check if a ressource's utilization exceeds threshold.
    const avgNow = computeRessourcesAverage(hosts, hostsStats, EXECUTION_DELAY)
    let toOptimize = this._checkRessourcesThresholds(hosts, avgNow)

    // No ressource's utilization problem.
    if (toOptimize.length === 0) {
      debug('No hosts to optimize.')
      return
    }

    // Check in the last 30 min interval with ratio.
    const avgBefore = computeRessourcesAverage(hosts, hostsStats, MINUTES_OF_HISTORICAL_DATA)
    const avgWithRatio = computeRessourcesAverageWithWeight(avgNow, avgBefore, 0.75)

    toOptimize = this._checkRessourcesThresholds(toOptimize, avgWithRatio)

    // No ressource's utilization problem.
    if (toOptimize.length === 0) {
      debug('No hosts to optimize.')
      return
    }

    return {
      toOptimize,
      averages: avgWithRatio,
      hosts
    }
  }

  _checkRessourcesThresholds () {
    throw new Error('Not implemented')
  }

  // ===================================================================
  // Get objects.
  // ===================================================================

  _getPlanPools () {
    const pools = {}

    try {
      for (const poolId of this._poolIds) {
        pools[poolId] = this.xo.getObject(poolId)
      }
    } catch (_) {
      return {}
    }

    return pools
  }

  // Compute hosts for each pool. They can change over time.
  _getHosts () {
    return differenceBy(
      filter(this.xo.getObjects(), object =>
        object.type === 'host' && includes(this._poolIds, object.$poolId)
      ),
      this._excludedHosts,
      val => val.id || val
    )
  }

  async _getVms (hostId) {
    return filter(this.xo.getObjects(), object =>
      object.type === 'VM' &&
      object.power_state === 'Running' &&
      object.$container === hostId
    )
  }

  // ===================================================================
  // Get stats.
  // ===================================================================

  async _getHostsStats (hosts, granularity) {
    const hostsStats = {}

    await Promise.all(mapToArray(hosts, host =>
      this.xo.getXapiHostStats(host, granularity).then(hostStats => {
        hostsStats[host.id] = {
          nPoints: hostStats.stats.cpus[0].length,
          stats: hostStats.stats,
          averages: {}
        }
      })
    ))

    return hostsStats
  }

  async _getVmsStats (vms, granularity) {
    const vmsStats = {}

    await Promise.all(mapToArray(vms, vm =>
      this.xo.getXapiVmStats(vm, granularity).then(vmStats => {
        vmsStats[vm.id] = {
          nPoints: vmStats.stats.cpus[0].length,
          stats: vmStats.stats,
          averages: {}
        }
      })
    ))

    return vmsStats
  }

  async _getVmsAverages (vms) {
    const vmsStats = await this._getVmsStats(vms, 'minutes')
    const vmsAverages = computeRessourcesAverageWithWeight(
      computeRessourcesAverage(vms, vmsStats, EXECUTION_DELAY),
      computeRessourcesAverage(vms, vmsStats, MINUTES_OF_HISTORICAL_DATA),
      0.75
    )

    // Compute real CPU usage. Virtuals cpus to reals cpus.
    setRealCpuAverageOfVms(vms, vmsAverages)

    return vmsAverages
  }
}

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

class PerformancePlan extends Plan {
  constructor (xo, name, poolIds, options) {
    super(xo, name, poolIds, options)
  }

  _checkRessourcesThresholds (objects, averages) {
    return filter(objects, object => {
      const objectAverages = averages[object.id]

      return (
        objectAverages.cpu >= this._thresholds.cpu.high ||
        objectAverages.memoryFree <= this._thresholds.memoryFree.high
      )
    })
  }

  async execute () {
    const results = await this._findHostsToOptimize()

    if (!results) {
      return
    }

    const {
      averages,
      hosts,
      toOptimize
    } = results

    const exceededHost = searchObject(toOptimize, (a, b) => {
      a = averages[a.id]
      b = averages[b.id]

      return (b.cpu - a.cpu) || (a.memoryFree - b.memoryFree)
    })

    // Search bests combinations for the worst host.
    await this._optimize({
      exceededHost,
      hosts: filter(hosts, host => host.id !== exceededHost.id),
      hostsAverages: averages
    })
  }

  async _optimize ({ exceededHost, hosts, hostsAverages }) {
    const vms = await this._getVms(exceededHost.id)
    const vmsAverages = await this._getVmsAverages(vms)

    // Sort vms by cpu usage. (higher to lower)
    vms.sort((a, b) =>
      vmsAverages[b.id].cpu - vmsAverages[a.id].cpu
    )

    const exceededAverages = hostsAverages[exceededHost.id]
    const promises = []

    const xapiSrc = this.xo.getXapi(exceededHost)

    for (const vm of vms) {
      // Search host with lower cpu usage.
      const destination = searchObject(hosts, (a, b) =>
        hostsAverages[b.id].cpu - hostsAverages[a.id].cpu
      )
      const destinationAverages = hostsAverages[destination.id]
      const vmAverages = vmsAverages[vm.id]

      // Unable to move the vm.
      if (
        exceededAverages.cpu - vmAverages.cpu < destinationAverages.cpu + vmAverages.cpu ||
        destinationAverages.memoryFree > vmAverages.memory
      ) {
        continue
      }

      exceededAverages.cpu -= vmAverages.cpu
      destinationAverages.cpu += vmAverages.cpu

      exceededAverages.memoryFree += vmAverages.memory
      destinationAverages.memoryFree -= vmAverages.memory

      debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${exceededHost.id})`)

      // promises.push(
      //   xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
      // )
    }

    await Promise.all(promises)

    return
  }
}

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

class DensityPlan extends Plan {
  constructor (xo, name, poolIds, options) {
    super(xo, name, poolIds, options)
  }

  _checkRessourcesThresholds (objects, averages) {
    return filter(objects, object =>
      averages[object.id].cpu < this._thresholds.cpu.high
    )
  }

  async execute () {
    const results = await this._findHostsToOptimize()

    if (!results) {
      return
    }

    const {
      averages: hostsAverages,
      hosts
    } = results
    let { toOptimize } = results

    const pools = await this._getPlanPools()
    const hostsByPool = sortHostsByPool(hosts)

    // Remove masters from toOptimize and hosts.
    for (const poolId in hostsByPool) {
      const pool = hostsByPool[poolId]
      hostsByPool[poolId] = filter(pool, host => host.id !== pool.master)
    }
    toOptimize = differenceBy(toOptimize, pools, object => {
      object.type === 'host' ? object.id : object.master
    })

    // Optimize all masters.
    await Promise.all(
      mapToArray(hostsByPool, (hosts, poolId) => {
        this._optimizeMaster({ toOptimize, pool: pools[poolId], hosts, hostsAverages })
      })
    )

    // Optimize master.
    console.log(hosts)
  }

    async _optimizeMaster ({ toOptimize, pool, hosts, hostsAverages }) {
    // TODO
    throw new Error('Not yet implemented')
  }
}

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

class LoadBalancerPlugin {
  constructor (xo) {
    this.xo = xo
    this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, ::this._executePlans)
    this._emitter
  }

  async configure ({ plans }) {
    const job = this._job
    const enabled = job.isEnabled()

    if (enabled) {
      job.cron.stop()
    }

    // Wait until all old plans stopped running.
    if (job.running) {
      await eventToPromise(job.emitter, 'finish')
    }

    this._plans = []
    this._poolIds = [] // Used pools.

    if (plans) {
      for (const plan of plans) {
        this._addPlan(plan.mode === 'Performance mode' ? PERFORMANCE_MODE : DENSITY_MODE, plan)
      }
    }

    if (enabled) {
      job.cron.start()
    }
  }

  load () {
    this._job.cron.start()
  }

  unload () {
    this._job.cron.stop()
  }

  _addPlan (mode, { name, pools, ...options }) {
    pools = uniq(pools)

    // Check already used pools.
    if (intersection(pools, this._poolIds).length > 0) {
      throw new Error(`Pool(s) already included in an other plan: ${pools}`)
    }

    this._poolIds = this._poolIds.concat(pools)
    this._plans.push(mode === PERFORMANCE_MODE
      ? new PerformancePlan(this.xo, name, pools, options)
      : new DensityPlan(this.xo, name, pools, options)
    )
  }

  _executePlans () {
    debug('Execute plans !')

    return Promise.all(
      mapToArray(this._plans, plan => plan.execute())
    )
  }
}

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

export default ({ xo }) => new LoadBalancerPlugin(xo)
<commit_msg>Fix cpu average. Use the cpu number of host and vm.<commit_after>import EventEmitter from 'events'

import differenceBy from 'lodash.differenceby'
import eventToPromise from 'event-to-promise'
import filter from 'lodash.filter'
import includes from 'lodash.includes'
import intersection from 'lodash.intersection'
import uniq from 'lodash.uniq'

import { CronJob } from 'cron'
import { default as mapToArray } from 'lodash.map'

class Emitter extends EventEmitter {}

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

const noop = () => {}

const LOAD_BALANCER_DEBUG = 1
const debug = LOAD_BALANCER_DEBUG
  ? str => console.log(`[load-balancer]${str}`)
  : noop

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

const PERFORMANCE_MODE = 0
const DENSITY_MODE = 1

// Delay between each ressources evaluation in minutes.
// Must be less than MINUTES_OF_HISTORICAL_DATA.
const EXECUTION_DELAY = 1
const MINUTES_OF_HISTORICAL_DATA = 30

// CPU threshold in percent.
const DEFAULT_CRITICAL_THRESHOLD_CPU = 90.0

// Memory threshold in MB.
const DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE = 64.0

// Thresholds factors.
const HIGH_THRESHOLD_FACTOR = 0.85
const LOW_THRESHOLD_FACTOR = 0.25

const HIGH_THRESHOLD_MEMORY_FREE_FACTOR = 1.25
const LOW_THRESHOLD_MEMORY_FREE_FACTOR = 20.0

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

export const configurationSchema = {
  type: 'object',

  properties: {
    plans: {
      type: 'array',
      description: 'an array of plans',
      title: 'Plans',

      items: {
        type: 'object',
        title: 'Plan',

        properties: {
          name: {
            type: 'string',
            title: 'Name'
          },

          mode: {
            enum: [ 'Performance mode', 'Density mode' ],
            title: 'Mode'
          },

          pools: {
            type: 'array',
            $type: 'Pool',
            description: 'list of pools where to apply the policy'
          },

          thresholds: {
            type: 'object',
            title: 'Critical thresholds',

            properties: {
              cpu: {
                type: 'integer',
                title: 'CPU (%)',
                description: 'default: 90%'
              },
              memoryFree: {
                type: 'integer',
                title: 'RAM, Free memory (MB)',
                description: 'default: 64MB'
              }
            }
          },

          excludedHosts: {
            type: 'array',
            title: 'Excluded hosts',
            $type: 'Host',
            description: 'list of hosts that are not affected by the plan'
          }
        },

        required: [ 'name', 'mode', 'pools' ]
      },

      minItems: 1
    }
  },

  additionalProperties: false
}

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

// Create a job not enabled by default.
// A job is a cron task, a running and enabled state.
const makeJob = (cronPattern, fn) => {
  const job = {
    running: false,
    emitter: new Emitter()
  }

  job.cron = new CronJob(cronPattern, async () => {
    if (job.running) {
      return
    }

    job.running = true

    try {
      await fn()
    } catch (error) {
      console.error('[WARN] scheduled function:', error && error.stack || error)
    } finally {
      job.running = false
      job.emitter.emit('finish')
    }
  })

  job.isEnabled = () => job.cron.running

  return job
}

// Compare a list of objects and give the best.
function searchObject (objects, fun) {
  let object = objects[0]

  for (let i = 1; i < objects.length; i++) {
    if (fun(object, objects[i]) > 0) {
      object = objects[i]
    }
  }

  return object
}

function sortHostsByPool (hosts) {
  const struct = {}

  for (const host of hosts) {
    const poolId = host.$poolId
    let pool = struct[poolId]

    if (pool === undefined) {
      pool = struct[poolId] = []
    }

    pool.push(host)
  }

  return struct
}

// ===================================================================
// Averages.
// ===================================================================

function computeAverage (values, nPoints = values.length) {
  let sum = 0
  let tot = 0

  const { length } = values

  for (let i = length - nPoints; i < length; i++) {
    const value = values[i]

    sum += value || 0

    if (value) {
      tot += 1
    }
  }

  return sum / tot
}

function computeRessourcesAverage (objects, objectsStats, nPoints) {
  const averages = {}

  for (const object of objects) {
    const { id } = object
    const { stats } = objectsStats[id]
    const objectAverages = averages[id] = {}

    objectAverages.cpu = computeAverage(
      mapToArray(stats.cpus, cpu => computeAverage(cpu, nPoints))
    )
    objectAverages.nCpus = stats.cpus.length

    objectAverages.memoryFree = computeAverage(stats.memoryFree, nPoints)
    objectAverages.memory = computeAverage(stats.memory, nPoints)
  }

  return averages
}

function computeRessourcesAverageWithWeight (averages1, averages2, ratio) {
  const averages = {}

  for (const id in averages1) {
    const objectAverages = averages[id] = {}

    for (const averageName in averages1[id]) {
      objectAverages[averageName] = averages1[id][averageName] * ratio + averages2[id][averageName] * (1 - ratio)
    }
  }

  return averages
}

function setRealCpuAverageOfVms (vms, vmsAverages, nCpus) {
  for (const vm of vms) {
    const averages = vmsAverages[vm.id]
    averages.cpu *= averages.nCpus / nCpus
  }
}

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

const numberOrDefault = (value, def) => (value >= 0) ? value : def

class Plan {
  constructor (xo, name, poolIds, {
    excludedHosts,
    thresholds
  } = {}) {
    this.xo = xo
    this._name = name
    this._poolIds = poolIds
    this._excludedHosts = excludedHosts
    this._thresholds = {
      cpu: {
        critical: numberOrDefault(thresholds.cpu, DEFAULT_CRITICAL_THRESHOLD_CPU)
      },
      memoryFree: {
        critical: numberOrDefault(thresholds.memoryFree, DEFAULT_CRITICAL_THRESHOLD_MEMORY_FREE) * 1024
      }
    }

    for (const key in this._thresholds) {
      const attr = this._thresholds[key]
      const { critical } = attr

      if (key === 'memoryFree') {
        attr.high = critical * HIGH_THRESHOLD_MEMORY_FREE_FACTOR
        attr.low = critical * LOW_THRESHOLD_MEMORY_FREE_FACTOR
      } else {
        attr.high = critical * HIGH_THRESHOLD_FACTOR
        attr.low = critical * LOW_THRESHOLD_FACTOR
      }
    }
  }

  execute () {
    throw new Error('Not implemented')
  }

  // ===================================================================
  // Get hosts to optimize.
  // ===================================================================

  async _findHostsToOptimize () {
    const hosts = this._getHosts()
    const hostsStats = await this._getHostsStats(hosts, 'minutes')

    // Check if a ressource's utilization exceeds threshold.
    const avgNow = computeRessourcesAverage(hosts, hostsStats, EXECUTION_DELAY)
    let toOptimize = this._checkRessourcesThresholds(hosts, avgNow)

    // No ressource's utilization problem.
    if (toOptimize.length === 0) {
      debug('No hosts to optimize.')
      return
    }

    // Check in the last 30 min interval with ratio.
    const avgBefore = computeRessourcesAverage(hosts, hostsStats, MINUTES_OF_HISTORICAL_DATA)
    const avgWithRatio = computeRessourcesAverageWithWeight(avgNow, avgBefore, 0.75)

    toOptimize = this._checkRessourcesThresholds(toOptimize, avgWithRatio)

    // No ressource's utilization problem.
    if (toOptimize.length === 0) {
      debug('No hosts to optimize.')
      return
    }

    return {
      toOptimize,
      averages: avgWithRatio,
      hosts
    }
  }

  _checkRessourcesThresholds () {
    throw new Error('Not implemented')
  }

  // ===================================================================
  // Get objects.
  // ===================================================================

  _getPlanPools () {
    const pools = {}

    try {
      for (const poolId of this._poolIds) {
        pools[poolId] = this.xo.getObject(poolId)
      }
    } catch (_) {
      return {}
    }

    return pools
  }

  // Compute hosts for each pool. They can change over time.
  _getHosts () {
    return differenceBy(
      filter(this.xo.getObjects(), object =>
        object.type === 'host' && includes(this._poolIds, object.$poolId)
      ),
      this._excludedHosts,
      val => val.id || val
    )
  }

  async _getVms (hostId) {
    return filter(this.xo.getObjects(), object =>
      object.type === 'VM' &&
      object.power_state === 'Running' &&
      object.$container === hostId
    )
  }

  // ===================================================================
  // Get stats.
  // ===================================================================

  async _getHostsStats (hosts, granularity) {
    const hostsStats = {}

    await Promise.all(mapToArray(hosts, host =>
      this.xo.getXapiHostStats(host, granularity).then(hostStats => {
        hostsStats[host.id] = {
          nPoints: hostStats.stats.cpus[0].length,
          stats: hostStats.stats,
          averages: {}
        }
      })
    ))

    return hostsStats
  }

  async _getVmsStats (vms, granularity) {
    const vmsStats = {}

    await Promise.all(mapToArray(vms, vm =>
      this.xo.getXapiVmStats(vm, granularity).then(vmStats => {
        vmsStats[vm.id] = {
          nPoints: vmStats.stats.cpus[0].length,
          stats: vmStats.stats,
          averages: {}
        }
      })
    ))

    return vmsStats
  }

  async _getVmsAverages (vms, host) {
    const vmsStats = await this._getVmsStats(vms, 'minutes')
    const vmsAverages = computeRessourcesAverageWithWeight(
      computeRessourcesAverage(vms, vmsStats, EXECUTION_DELAY),
      computeRessourcesAverage(vms, vmsStats, MINUTES_OF_HISTORICAL_DATA),
      0.75
    )

    // Compute real CPU usage. Virtuals cpus to reals cpus.
    setRealCpuAverageOfVms(vms, vmsAverages, host.CPUs.cpu_count)

    return vmsAverages
  }
}

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

class PerformancePlan extends Plan {
  constructor (xo, name, poolIds, options) {
    super(xo, name, poolIds, options)
  }

  _checkRessourcesThresholds (objects, averages) {
    return filter(objects, object => {
      const objectAverages = averages[object.id]

      return (
        objectAverages.cpu >= this._thresholds.cpu.high ||
        objectAverages.memoryFree <= this._thresholds.memoryFree.high
      )
    })
  }

  async execute () {
    const results = await this._findHostsToOptimize()

    if (!results) {
      return
    }

    const {
      averages,
      hosts,
      toOptimize
    } = results

    const exceededHost = searchObject(toOptimize, (a, b) => {
      a = averages[a.id]
      b = averages[b.id]

      return (b.cpu - a.cpu) || (a.memoryFree - b.memoryFree)
    })

    // Search bests combinations for the worst host.
    await this._optimize({
      exceededHost,
      hosts: filter(hosts, host => host.id !== exceededHost.id),
      hostsAverages: averages
    })
  }

  async _optimize ({ exceededHost, hosts, hostsAverages }) {
    const vms = await this._getVms(exceededHost.id)
    const vmsAverages = await this._getVmsAverages(vms, exceededHost)

    // Sort vms by cpu usage. (higher to lower)
    vms.sort((a, b) =>
      vmsAverages[b.id].cpu - vmsAverages[a.id].cpu
    )

    const exceededAverages = hostsAverages[exceededHost.id]
    const promises = []

    const xapiSrc = this.xo.getXapi(exceededHost)

    for (const vm of vms) {
      // Search host with lower cpu usage.
      const destination = searchObject(hosts, (a, b) =>
        hostsAverages[b.id].cpu - hostsAverages[a.id].cpu
      )
      const destinationAverages = hostsAverages[destination.id]
      const vmAverages = vmsAverages[vm.id]

      // Unable to move the vm.
      if (
        exceededAverages.cpu - vmAverages.cpu < destinationAverages.cpu + vmAverages.cpu ||
        destinationAverages.memoryFree > vmAverages.memory
      ) {
        continue
      }

      exceededAverages.cpu -= vmAverages.cpu
      destinationAverages.cpu += vmAverages.cpu

      exceededAverages.memoryFree += vmAverages.memory
      destinationAverages.memoryFree -= vmAverages.memory

      debug(`Migrate VM (${vm.id}) to Host (${destination.id}) from Host (${exceededHost.id})`)

      // promises.push(
      //   xapiSrc.migrateVm(vm._xapiId, this.xo.getXapi(destination), destination._xapiId)
      // )
    }

    await Promise.all(promises)

    return
  }
}

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

class DensityPlan extends Plan {
  constructor (xo, name, poolIds, options) {
    super(xo, name, poolIds, options)
  }

  _checkRessourcesThresholds (objects, averages) {
    return filter(objects, object =>
      averages[object.id].cpu < this._thresholds.cpu.high
    )
  }

  async execute () {
    const results = await this._findHostsToOptimize()

    if (!results) {
      return
    }

    const {
      averages: hostsAverages,
      hosts
    } = results
    let { toOptimize } = results

    const pools = await this._getPlanPools()
    const hostsByPool = sortHostsByPool(hosts)

    // Remove masters from toOptimize and hosts.
    for (const poolId in hostsByPool) {
      const pool = hostsByPool[poolId]
      hostsByPool[poolId] = filter(pool, host => host.id !== pool.master)
    }
    toOptimize = differenceBy(toOptimize, pools, object => {
      object.type === 'host' ? object.id : object.master
    })

    // Optimize all masters.
    await Promise.all(
      mapToArray(hostsByPool, (hosts, poolId) => {
        this._optimizeMaster({ toOptimize, pool: pools[poolId], hosts, hostsAverages })
      })
    )

    // Optimize master.
    console.log(hosts)
  }

    async _optimizeMaster ({ toOptimize, pool, hosts, hostsAverages }) {
    // TODO
    throw new Error('Not yet implemented')
  }
}

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

class LoadBalancerPlugin {
  constructor (xo) {
    this.xo = xo
    this._job = makeJob(`*/${EXECUTION_DELAY} * * * *`, ::this._executePlans)
    this._emitter
  }

  async configure ({ plans }) {
    const job = this._job
    const enabled = job.isEnabled()

    if (enabled) {
      job.cron.stop()
    }

    // Wait until all old plans stopped running.
    if (job.running) {
      await eventToPromise(job.emitter, 'finish')
    }

    this._plans = []
    this._poolIds = [] // Used pools.

    if (plans) {
      for (const plan of plans) {
        this._addPlan(plan.mode === 'Performance mode' ? PERFORMANCE_MODE : DENSITY_MODE, plan)
      }
    }

    if (enabled) {
      job.cron.start()
    }
  }

  load () {
    this._job.cron.start()
  }

  unload () {
    this._job.cron.stop()
  }

  _addPlan (mode, { name, pools, ...options }) {
    pools = uniq(pools)

    // Check already used pools.
    if (intersection(pools, this._poolIds).length > 0) {
      throw new Error(`Pool(s) already included in an other plan: ${pools}`)
    }

    this._poolIds = this._poolIds.concat(pools)
    this._plans.push(mode === PERFORMANCE_MODE
      ? new PerformancePlan(this.xo, name, pools, options)
      : new DensityPlan(this.xo, name, pools, options)
    )
  }

  _executePlans () {
    debug('Execute plans !')

    return Promise.all(
      mapToArray(this._plans, plan => plan.execute())
    )
  }
}

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

export default ({ xo }) => new LoadBalancerPlugin(xo)
<|endoftext|>


<commit_before>'use strict';

const Command = require('../../Command.js');

/**
 * Send a bug report to owner
 */
class BugReport extends Command {
  /**
   * Constructs a callable command
   * @param {Bot} bot The bot object
   */
  constructor(bot) {
    super(bot, 'core.bug', 'bug', 'Send a bug report to the bot owner');
    this.regex = new RegExp(`^${this.call}\\s*(.*)?`, 'i');
    this.usages = [
      {
        description: 'Send a bug report to bot owner',
        parameters: ['bug report'],
      },
      {
        description: 'Send a bug report to bot owner with subject, message, and optional screenshot. The `|` are important.',
        parameters: ['subject', 'body', '(optional) screenshot'],
        separator: ' | ',
      },
      {
        description: 'Send a bug report to bot owner with subject, message, and optional screenshot. The `|` are important.',
        parameters: ['subject', 'body', 'attached image'],
        separator: ' | ',
      },
    ];
  }

  /**
   * Run the command
   * @param {Message} message Message with a command to handle, reply to,
   *                          or perform an action based on parameters.
   */
  run(message) {
    const bugReport = message.strippedContent.match(this.regex)[1];

    if (this.bot.owner) {
      if (bugReport) {
        const params = bugReport.split('|');
        const embed = {
          author: {
            icon_url: message.author.avatarURL,
            name: `${message.author.username}#${message.author.discriminator}`,
          },
          title: `Bug Report | ${message.author}`,
          fields: [],
        };

        if (params.length < 2) {
          embed.fields = [{ name: '_ _', value: bugReport }];
        } else {
          embed.fields[0] = {
            name: params[0].trim(),
            value: params[1].trim(),
          };
          if (params.length > 2) {
            embed.image = {
              url: params[2].trim(),
            };
          } else if (message.attachments.array().length > 0) {
            embed.image = {
              url: message.attachments.array()[0].url,
            };
          }
        }
        this.messageManager.sendDirectEmbedToOwner(embed);
        this.messageManager.reply(message, 'Bug report sent.', true, true);
      } else {
        const embed = {
          author: {
            icon_url: message.author.avatarURL,
            name: `${message.author.username}#${message.author.discriminator}`,
          },
          title: `Bug Report | ${message.author}`,
          fields: [{ name: '_ _', value: 'Need to provide a bug report, see `/help` for syntax.' }],
        };
        this.messageManager.embed(message, embed, true, false);
      }
    }
  }
}

module.exports = BugReport;
<commit_msg>add footer to add me as a friend so i can respond to  reports<commit_after>'use strict';

const Command = require('../../Command.js');

/**
 * Send a bug report to owner
 */
class BugReport extends Command {
  /**
   * Constructs a callable command
   * @param {Bot} bot The bot object
   */
  constructor(bot) {
    super(bot, 'core.bug', 'bug', 'Send a bug report to the bot owner');
    this.regex = new RegExp(`^${this.call}\\s*(.*)?`, 'i');
    this.usages = [
      {
        description: 'Send a bug report to bot owner',
        parameters: ['bug report'],
      },
      {
        description: 'Send a bug report to bot owner with subject, message, and optional screenshot. The `|` are important.',
        parameters: ['subject', 'body', '(optional) screenshot'],
        separator: ' | ',
      },
      {
        description: 'Send a bug report to bot owner with subject, message, and optional screenshot. The `|` are important.',
        parameters: ['subject', 'body', 'attached image'],
        separator: ' | ',
      },
    ];
  }

  /**
   * Run the command
   * @param {Message} message Message with a command to handle, reply to,
   *                          or perform an action based on parameters.
   */
  run(message) {
    const bugReport = message.strippedContent.match(this.regex)[1];

    if (this.bot.owner) {
      if (bugReport) {
        const params = bugReport.split('|');
        const embed = {
          author: {
            icon_url: message.author.avatarURL,
            name: `${message.author.username}#${message.author.discriminator}`,
          },
          title: `Bug Report | ${message.author}`,
          fields: [],
        };

        if (params.length < 2) {
          embed.fields = [{ name: '_ _', value: bugReport }];
        } else {
          embed.fields[0] = {
            name: params[0].trim(),
            value: params[1].trim(),
          };
          if (params.length > 2) {
            embed.image = {
              url: params[2].trim(),
            };
          } else if (message.attachments.array().length > 0) {
            embed.image = {
              url: message.attachments.array()[0].url,
            };
          }
        }
        this.messageManager.sendDirectEmbedToOwner(embed);
        this.messageManager.reply(message, 'Bug report sent.', true, true);
      } else {
        const embed = {
          author: {
            icon_url: message.author.avatarURL,
            name: `${message.author.username}#${message.author.discriminator}`,
          },
          title: `Bug Report | ${message.author}`,
          fields: [{ name: '_ _', value: 'Need to provide a bug report, see `/help` for syntax.' }],
          footer: { text: 'Add Tobiah#8452 as a friend so he can respond to your bug report' },
        };
        this.messageManager.embed(message, embed, true, false);
      }
    }
  }
}

module.exports = BugReport;
<|endoftext|>


<commit_before>/* exported domEventListener */
function domEventListener(settings, modules, parts) {
   'use strict';

   var actualSettings = settings.get(),
      rootNode = actualSettings.rootNode,
      attributeName = actualSettings.attribute,
      actualSelector = settings.getSelector(),
      registerStrategy = decideDomMutationStrategy();

   function decideDomMutationStrategy() {
      var strategy = tryToGetMutationObserverStrategy();
      // TODO remove the following line to enable/disable MutationObserver support
      //strategy = null;

      if(strategy === null) {
         return createLegacyDomMutationStrategy();
      }

      return strategy;
   }

   function onElementAdded(addedNode) {
      var addedModules = addedNode.querySelectorAll(actualSelector);

      if(addedNode.hasAttribute(attributeName)) {
         loadModule(addedNode);
      }

      each(addedModules, function(addedModule) {
         loadModule(addedModule);
      });

      modules.provisionFinished();
      parts.provisionFinished();
   }

   function onElementRemoved(removedElement) {
      var addedModuleElements = removedElement.querySelectorAll(actualSelector);

      each(addedModuleElements, function(moduleElement) {
         unloadModules(moduleElement);
      });

      if(removedElement.hasAttribute(attributeName)) {
         unloadModules(removedElement);
      }
   }

   function loadModule(moduleElement) {
      return modules.provisionModule(moduleElement);
   }

   function unloadModules(moduleElement) {
      modules.unloadModules(moduleElement);
   }

   return {
      registerToEvents : registerStrategy.register,
      unregisterToEvents : registerStrategy.unregister
   };

   function tryToGetMutationObserverStrategy() {

      var ObserverCreator = window.MutationObserver || window.WebKitMutationObserver || null,
         observerConfig = { attributes: true, childList: true, characterData: true, subtree: true },
         observer;

      if(ObserverCreator !== null) {
         observer = new ObserverCreator(onMutation);

         return {
            register : registerToEvents,
            unregister : unregisterToEvents
         };
      }
      else {
         return null;
      }

      function registerToEvents() {
         observer.observe(rootNode, observerConfig);
      }

      function unregisterToEvents() {
         //observer.disconnect();
      }

      function onMutation(mutations, observer) {

         each(mutations, function(mutationRecord) {

            each(mutationRecord.addedNodes, function(addedNode) {
               onElementAdded(addedNode);
            });

            each(mutationRecord.removedNodes, function(removedNode) {
               onElementRemoved(removedNode);
            });
         });

         observer.takeRecords();
      }
   }

   function createLegacyDomMutationStrategy() {

      var DOM_NODE_INSERTED_EVENT_NAME = 'DOMNodeInserted',
         DOM_NODE_REMOVED_EVENT_NAME = 'DOMNodeRemoved';

      function registerToEvents() {
         rootNode.addEventListener(DOM_NODE_INSERTED_EVENT_NAME, onDomNodeInserted, false);
         rootNode.addEventListener(DOM_NODE_REMOVED_EVENT_NAME, onDomNodeRemoved, false);
      }

      function onDomNodeInserted(event) {
         onElementAdded(event.target);
      }

      function unregisterToEvents() {
         rootNode.removeEventListener(DOM_NODE_INSERTED_EVENT_NAME, onDomNodeInserted);
         rootNode.removeEventListener(DOM_NODE_REMOVED_EVENT_NAME, onDomNodeRemoved, false);
      }

      function onDomNodeRemoved(event) {
         onElementRemoved(event.target);
      }

      return {
         register : registerToEvents,
         unregister : unregisterToEvents
      };
   }
}
<commit_msg>implemented disconnect and cleanup mutation observer<commit_after>/* exported domEventListener */
function domEventListener(settings, modules, parts) {
   'use strict';

   var actualSettings = settings.get(),
      rootNode = actualSettings.rootNode,
      attributeName = actualSettings.attribute,
      actualSelector = settings.getSelector(),
      registerStrategy = decideDomMutationStrategy();

   function decideDomMutationStrategy() {
      var strategy = tryToGetMutationObserverStrategy();
      // TODO remove the following line to enable/disable MutationObserver support
      //strategy = null;

      if(strategy === null) {
         return createLegacyDomMutationStrategy();
      }

      return strategy;
   }

   function onElementAdded(addedNode) {
      var addedModules = addedNode.querySelectorAll(actualSelector);

      if(addedNode.hasAttribute(attributeName)) {
         loadModule(addedNode);
      }

      each(addedModules, function(addedModule) {
         loadModule(addedModule);
      });

      modules.provisionFinished();
      parts.provisionFinished();
   }

   function onElementRemoved(removedElement) {
      var addedModuleElements = removedElement.querySelectorAll(actualSelector);

      each(addedModuleElements, function(moduleElement) {
         unloadModules(moduleElement);
      });

      if(removedElement.hasAttribute(attributeName)) {
         unloadModules(removedElement);
      }
   }

   function loadModule(moduleElement) {
      return modules.provisionModule(moduleElement);
   }

   function unloadModules(moduleElement) {
      modules.unloadModules(moduleElement);
   }

   return {
      registerToEvents : registerStrategy.register,
      unregisterToEvents : registerStrategy.unregister
   };

   function tryToGetMutationObserverStrategy() {

      var ObserverCreator = window.MutationObserver || window.WebKitMutationObserver || null,
         observerConfig = { attributes: true, childList: true, characterData: true, subtree: true },
         observer;

      if(ObserverCreator !== null) {
         observer = new ObserverCreator(onMutation);

         return {
            register : registerToEvents,
            unregister : unregisterToEvents
         };
      }
      else {
         return null;
      }

      function registerToEvents() {
         observer.observe(rootNode, observerConfig);
      }

      function unregisterToEvents() {
         observer.takeRecords();
         observer.disconnect();
      }

      function onMutation(mutations, observer) {

         each(mutations, function(mutationRecord) {

            each(mutationRecord.addedNodes, function(addedNode) {
               onElementAdded(addedNode);
            });

            each(mutationRecord.removedNodes, function(removedNode) {
               onElementRemoved(removedNode);
            });
         });

         observer.takeRecords();
      }
   }

   function createLegacyDomMutationStrategy() {

      var DOM_NODE_INSERTED_EVENT_NAME = 'DOMNodeInserted',
         DOM_NODE_REMOVED_EVENT_NAME = 'DOMNodeRemoved';

      function registerToEvents() {
         rootNode.addEventListener(DOM_NODE_INSERTED_EVENT_NAME, onDomNodeInserted, false);
         rootNode.addEventListener(DOM_NODE_REMOVED_EVENT_NAME, onDomNodeRemoved, false);
      }

      function onDomNodeInserted(event) {
         onElementAdded(event.target);
      }

      function unregisterToEvents() {
         rootNode.removeEventListener(DOM_NODE_INSERTED_EVENT_NAME, onDomNodeInserted);
         rootNode.removeEventListener(DOM_NODE_REMOVED_EVENT_NAME, onDomNodeRemoved, false);
      }

      function onDomNodeRemoved(event) {
         onElementRemoved(event.target);
      }

      return {
         register : registerToEvents,
         unregister : unregisterToEvents
      };
   }
}
<|endoftext|>


<commit_before>console.log('lord');
var express = require('express');

console.log('hey');
var app = express();

console.log('ok');

app.get('/', function(req, res){
	console.log('yay', res.render)
  res.send('Hello World');
});

app.listen(3000);
console.log('Listening on http://localhost:3000');<commit_msg>Test for Express no longer needs debug lines<commit_after>var express = require('express');

var app = express();

app.get('/', function(req, res){
  res.send('Hello World');
});

app.listen(3000);
console.log('Listening on http://localhost:3000');<|endoftext|>


<commit_before>Template.dotplot.onCreated(function(){
  var r = Template.instance().data['cityname'];
  this.my_city = new ReactiveVar(Cities.findOne({"city":r}));
});

Template.dotplot.helpers({
  city() {
    r = this.cityname;
    var city = [];
    if(r=="angeles"){ city = Cities.findOne({city:"angeles"}); city.cityname = "Los Angeles"; }
    if(r=="denver") { city = Cities.findOne({city:"denver" }); city.cityname = "Denver";  }
    if(r=="newyork"){ city = Cities.findOne({city:"newyork"}); city.cityname = "New York";}
    if(r=="houston"){ city = Cities.findOne({city:"houston"}); city.cityname = "Houston"; }
    if(r=="seattle"){ city = Cities.findOne({city:"seattle"}); city.cityname = "Seattle"; }
    if(r=="atlanta"){ city = Cities.findOne({city:"atlanta"}); city.cityname = "Atlanta"; }
    return city;
  },
  prediction() {
    var city = Template.instance().my_city.get();
    var sum_c = [];
    var sum_b = [];
    var sum_x = [];
    var sum_y = [];

    var sum_upr1 = [];
    var sum_upr2 = [];
    var sum_lwr1 = [];
    var sum_lwr2 = [];

    var r_w = Session.get("strength-h")/100;
    var r_t = Session.get("strength-t")/100;
    var r_s = Session.get("strength-s")/100;
    var r_p = Session.get("strength-p")/100;

    var weather   = ((city.w1 + city.w2)/2) * r_w;
    var traffic   = ((city.t1 + city.t2)/2) * r_t;
    var safety    = ((city.s1 + city.s2)/2) * r_s;
    var pollution = ((city.a1 + city.a2)/2) * r_p;

    if($('paper-checkbox[checked]').length == 1) {
      if($(".health").attr("checked")) {
        var mean = ((city.w1 + city.w2)/2);
        weather = city.w2 - ((city.w2-city.w1)*(r_w));
        if(mean > 50) weather  = city.w1 - ((city.w1-city.w2)*(r_w));
      }
      if($(".traffic").attr("checked")) {
        var mean = ((city.t1 + city.t2)/2);
        traffic = city.t2 - ((city.t2-city.t1)*(r_t));
        if(mean > 50) traffic = city.t1 - ((city.t1-city.t2)*(r_t));
      }
      if($(".safety").attr("checked")) {
        var mean = ((city.s1 + city.s2)/2);
        safety = city.s2 - ((city.s2-city.s1)*(r_s));
        if(mean > 50) safety = city.s1 - ((city.s1-city.s2)*(r_s));
      }
      if($(".polluted").attr("checked")) {
        var mean = ((city.a1 + city.a2)/2);
        pollution = city.a2 - ((city.a2-city.a1)*(r_p));
        if(mean > 50) pollution = city.a1 - ((city.a1-city.a2)*(r_p));
      }
    }

    if($(".health").attr("checked")) {
      sum_c.push(weather);
      sum_b.push(r_w);
      sum_x.push(city.w_m1);
      sum_y.push(city.w_m2);
      sum_upr1.push(city.w_upr_min);
      sum_upr2.push(city.w_upr_max);
      sum_lwr1.push(city.w_lwr_min);
      sum_lwr2.push(city.w_lwr_max);
    }
    if($(".traffic").attr("checked")) {
      sum_c.push(traffic);
      sum_b.push(r_t);
      sum_x.push(city.t_m1);
      sum_y.push(city.t_m2);
      sum_upr1.push(city.t_upr_min);
      sum_upr2.push(city.t_upr_max);
      sum_lwr1.push(city.t_lwr_min);
      sum_lwr2.push(city.t_lwr_max);
    }
    if($(".safety").attr("checked")) {
      sum_c.push(safety);
      sum_b.push(r_s);
      sum_x.push(city.s_m1);
      sum_y.push(city.s_m2);
      sum_upr1.push(city.s_upr_min);
      sum_upr2.push(city.s_upr_max);
      sum_lwr1.push(city.s_lwr_min);
      sum_lwr2.push(city.s_lwr_max);
    }
    if($(".polluted").attr("checked")) {
      sum_c.push(pollution);
      sum_b.push(r_p);
      sum_x.push(city.a_m1);
      sum_y.push(city.a_m2);
      sum_upr1.push(city.a_upr_min);
      sum_upr2.push(city.a_upr_max);
      sum_lwr1.push(city.a_lwr_min);
      sum_lwr2.push(city.a_lwr_max);
    }

    var c = (sum_c.reduce((a,b)=>a+b,0))/(sum_b.reduce((a,b)=>a+b,0));
    if($('paper-checkbox[checked]').length == 1) c = (sum_c.reduce((a,b)=>a+b,0));
    var x = sum_x.reduce((a,b)=>a+b,0)/sum_x.length;
    var y = sum_y.reduce((a,b)=>a+b,0)/sum_y.length;
    var upr1 = sum_upr1.reduce((a,b)=>a+b,0)/sum_upr1.length; //min
    var upr2 = sum_upr2.reduce((a,b)=>a+b,0)/sum_upr2.length; //max
    var lwr1 = sum_lwr1.reduce((a,b)=>a+b,0)/sum_lwr1.length; //min
    var lwr2 = sum_lwr2.reduce((a,b)=>a+b,0)/sum_lwr2.length; //max

    if(isNaN(c)) c = 0;
    if(isNaN(x)) x = 0;
    if(isNaN(y)) y = 0;
    if(isNaN(upr1)) upr1 = 0;
    if(isNaN(upr2)) upr2 = 0;
    if(isNaN(lwr1)) lwr1 = 0;
    if(isNaN(lwr2)) lwr2 = 0;

    var upr = (y*100)+x;
    var lwr = x;

    var r_upr2 = (((upr2 - upr )/5) * 20);
    var r_upr1 = (((upr1 - lwr )/5) * 20);
    var r_lwr2 = (((upr  - lwr2)/5) * 20);
    var r_lwr1 = (((lwr  - lwr1)/5) * 20);

    var p = (upr2-(((y * 100) + x))/5)*1.2;
    var px = 6; //dot width...
    var dots = (((y * c) + x)*20);
    var text = Math.round(((dots/20)*10));
    var qol  = Math.round(c);

    if(isNaN(text)) text = 0;
    if(isNaN(qol))   qol = 0;

    var upr = (y*100)+x;
    var lwr = x;

    var top = (((upr2 - upr1)/100) * (qol)) + upr1;
    var bot = (((lwr2 - lwr1)/100) * (qol)) + lwr1;
    var top_p = ((top-(dots/20))*20)/5;
    var bot_p = (((dots/20)-bot)*20)/5;
    //console.log("city: " + city.city + " t: " + top + " b: " + bot + " d:" + dots/20);
    return {
      a1: (dots-(bot_p*5))-px,
      a2: (dots-(bot_p*4))-px,
      a3: (dots-(bot_p*3))-px,
      a4: (dots-(bot_p*2))-px,
      a5: (dots-bot_p)-px,
      a6: (dots)-px,
      a7: (dots+top_p)-px,
      a8: (dots+(top_p*2))-px,
      a9: (dots+(top_p*3))-px,
      a10:(dots+(top_p*4))-px,
      a11:(dots+(top_p*5))-px,
      text: text,
      qol: qol,
      p: 0
    }
  }
});
<commit_msg>Fixed map faces<commit_after>Template.dotplot.onCreated(function(){
  var r = Template.instance().data['cityname'];
  this.my_city = new ReactiveVar(Cities.findOne({"city":r}));
});

Template.dotplot.helpers({
  city() {
    r = this.cityname;
    var city = [];
    if(r=="angeles"){ city = Cities.findOne({city:"angeles"}); city.cityname = "Los Angeles"; }
    if(r=="denver") { city = Cities.findOne({city:"denver" }); city.cityname = "Denver";  }
    if(r=="newyork"){ city = Cities.findOne({city:"newyork"}); city.cityname = "New York";}
    if(r=="houston"){ city = Cities.findOne({city:"houston"}); city.cityname = "Houston"; }
    if(r=="seattle"){ city = Cities.findOne({city:"seattle"}); city.cityname = "Seattle"; }
    if(r=="atlanta"){ city = Cities.findOne({city:"atlanta"}); city.cityname = "Atlanta"; }
    return city;
  },
  prediction() {
    var city = Template.instance().my_city.get();
    var sum_c = [];
    var sum_b = [];
    var sum_x = [];
    var sum_y = [];

    var sum_upr1 = [];
    var sum_upr2 = [];
    var sum_lwr1 = [];
    var sum_lwr2 = [];

    var r_w = Session.get("strength-h")/100;
    var r_t = Session.get("strength-t")/100;
    var r_s = Session.get("strength-s")/100;
    var r_p = Session.get("strength-p")/100;

    var weather   = ((city.w1 + city.w2)/2) * r_w;
    var traffic   = ((city.t1 + city.t2)/2) * r_t;
    var safety    = ((city.s1 + city.s2)/2) * r_s;
    var pollution = ((city.a1 + city.a2)/2) * r_p;

    if($('paper-checkbox[checked]').length == 1) {
      if($(".health").attr("checked")) {
        var mean = ((city.w1 + city.w2)/2);
        weather = city.w2 - ((city.w2-city.w1)*(r_w));
        if(mean > 50) weather  = city.w1 - ((city.w1-city.w2)*(r_w));
      }
      if($(".traffic").attr("checked")) {
        var mean = ((city.t1 + city.t2)/2);
        traffic = city.t2 - ((city.t2-city.t1)*(r_t));
        if(mean > 50) traffic = city.t1 - ((city.t1-city.t2)*(r_t));
      }
      if($(".safety").attr("checked")) {
        var mean = ((city.s1 + city.s2)/2);
        safety = city.s2 - ((city.s2-city.s1)*(r_s));
        if(mean > 50) safety = city.s1 - ((city.s1-city.s2)*(r_s));
      }
      if($(".polluted").attr("checked")) {
        var mean = ((city.a1 + city.a2)/2);
        pollution = city.a2 - ((city.a2-city.a1)*(r_p));
        if(mean > 50) pollution = city.a1 - ((city.a1-city.a2)*(r_p));
      }
    }

    if($(".health").attr("checked")) {
      sum_c.push(weather);
      sum_b.push(r_w);
      sum_x.push(city.w_m1);
      sum_y.push(city.w_m2);
      sum_upr1.push(city.w_upr_min);
      sum_upr2.push(city.w_upr_max);
      sum_lwr1.push(city.w_lwr_min);
      sum_lwr2.push(city.w_lwr_max);
    }
    if($(".traffic").attr("checked")) {
      sum_c.push(traffic);
      sum_b.push(r_t);
      sum_x.push(city.t_m1);
      sum_y.push(city.t_m2);
      sum_upr1.push(city.t_upr_min);
      sum_upr2.push(city.t_upr_max);
      sum_lwr1.push(city.t_lwr_min);
      sum_lwr2.push(city.t_lwr_max);
    }
    if($(".safety").attr("checked")) {
      sum_c.push(safety);
      sum_b.push(r_s);
      sum_x.push(city.s_m1);
      sum_y.push(city.s_m2);
      sum_upr1.push(city.s_upr_min);
      sum_upr2.push(city.s_upr_max);
      sum_lwr1.push(city.s_lwr_min);
      sum_lwr2.push(city.s_lwr_max);
    }
    if($(".polluted").attr("checked")) {
      sum_c.push(pollution);
      sum_b.push(r_p);
      sum_x.push(city.a_m1);
      sum_y.push(city.a_m2);
      sum_upr1.push(city.a_upr_min);
      sum_upr2.push(city.a_upr_max);
      sum_lwr1.push(city.a_lwr_min);
      sum_lwr2.push(city.a_lwr_max);
    }

    var c = (sum_c.reduce((a,b)=>a+b,0))/(sum_b.reduce((a,b)=>a+b,0));
    if($('paper-checkbox[checked]').length == 1) c = (sum_c.reduce((a,b)=>a+b,0));
    var x = sum_x.reduce((a,b)=>a+b,0)/sum_x.length;
    var y = sum_y.reduce((a,b)=>a+b,0)/sum_y.length;
    var upr1 = sum_upr1.reduce((a,b)=>a+b,0)/sum_upr1.length; //min
    var upr2 = sum_upr2.reduce((a,b)=>a+b,0)/sum_upr2.length; //max
    var lwr1 = sum_lwr1.reduce((a,b)=>a+b,0)/sum_lwr1.length; //min
    var lwr2 = sum_lwr2.reduce((a,b)=>a+b,0)/sum_lwr2.length; //max

    if(isNaN(c)) c = 0;
    if(isNaN(x)) x = 0;
    if(isNaN(y)) y = 0;
    if(isNaN(upr1)) upr1 = 0;
    if(isNaN(upr2)) upr2 = 0;
    if(isNaN(lwr1)) lwr1 = 0;
    if(isNaN(lwr2)) lwr2 = 0;

    var upr = (y*100)+x;
    var lwr = x;

    var r_upr2 = (((upr2 - upr )/5) * 20);
    var r_upr1 = (((upr1 - lwr )/5) * 20);
    var r_lwr2 = (((upr  - lwr2)/5) * 20);
    var r_lwr1 = (((lwr  - lwr1)/5) * 20);

    var p = (upr2-(((y * 100) + x))/5)*1.2;
    var px = 7; //dot width...
    var dots = (((y * c) + x)*20);
    var text = Math.round(((dots/20)*10));
    var qol  = Math.round(c);

    if(isNaN(text)) text = 0;
    if(isNaN(qol))   qol = 0;

    var upr = (y*100)+x;
    var lwr = x;

    var top = (((upr2 - upr1)/100) * (qol)) + upr1;
    var bot = (((lwr2 - lwr1)/100) * (qol)) + lwr1;
    var top_p = ((top-(dots/20))*20)/5;
    var bot_p = (((dots/20)-bot)*20)/5;
    //console.log("city: " + city.city + " t: " + top + " b: " + bot + " d:" + dots/20);
    return {
      a1: (dots-(bot_p*5))-px,
      a2: (dots-(bot_p*4))-px,
      a3: (dots-(bot_p*3))-px,
      a4: (dots-(bot_p*2))-px,
      a5: (dots-bot_p)-px,
      a6: (dots)-px,
      a7: (dots+top_p)-px,
      a8: (dots+(top_p*2))-px,
      a9: (dots+(top_p*3))-px,
      a10:(dots+(top_p*4))-px,
      a11:(dots+(top_p*5))-px,
      text: text,
      qol: qol,
      p: 0
    }
  }
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions/article';
import styled from 'styled-components';

const ArticleItem = styled.div`{
  border-radius: 6px;
  padding: 10px;
  &:hover {
    background: rgb(222,222,222);
    transition: background .2s ease;
    & > .imgContainer img {
      opacity: .7;
      transition: opacity .2s linear;
    }
  }
  & > .imgContainer img {
    width: 100%;
    border-radius: 6px;
    opacity: 1;
  }
  & > .articleInfo {
    display: flex;
    padding: 5px;
    justify-content: space-between;
    & > .caption {
      color: #444;
      font-weight: bold;
      padding-right: 10px
    }
    & > .pubDate {
      color: #888;
      font-size: 12px;
    }
  }
}
`;
class ArticleList extends Component {
  componentWillMount() {
    // Fetch user data prior to component mounting
     this.props.fetchArticles();
  }

  render() {
    const { articles } = this.props;
    const articleList = articles.map((article, index) => {
      return (

            <ArticleItem>

{article.title}

{article.pubdate}

            </ArticleItem>

      );
    });
    // var names = [JSON.stringify(articles[0]), 'Jon', 'Thruster'];
    //     var namesList = names.map(function(name){
    //                     return
{name}
;
    //                   })

    // const b;
    // for (let i=0; i<articles.length; i++) {
    //   b=JSON.stringify(articles[i])
    // }

    // var aNiceObject = JSON.parse(fixedJSON);
    return (

{ articleList }

    );
  }
}

function mapStateToProps(state) {
  return {
    articles: state.article.articles,
  };
}

export default connect(mapStateToProps, actions)(ArticleList);
<commit_msg>Article Comp Remove unnecessary<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions/article';
import styled from 'styled-components';

const ArticleItem = styled.div`{
  border-radius: 6px;
  padding: 10px;
  &:hover {
    background: rgb(222,222,222);
    transition: background .2s ease;
    & > .imgContainer img {
      opacity: .7;
      transition: opacity .2s linear;
    }
  }
  & > .imgContainer img {
    width: 100%;
    border-radius: 6px;
    opacity: 1;
  }
  & > .articleInfo {
    display: flex;
    padding: 5px;
    justify-content: space-between;
    & > .caption {
      color: #444;
      font-weight: bold;
      padding-right: 10px
    }
    & > .pubDate {
      color: #888;
      font-size: 12px;
    }
  }
}
`;
class ArticleList extends Component {
  componentWillMount() {
    // Fetch user data prior to component mounting
     this.props.fetchArticles();
  }

  render() {
    const { articles } = this.props;
    const articleList = articles.map((article, index) => {
      return (

            <ArticleItem>

{article.title}

{article.pubdate}

            </ArticleItem>

      );
    });
    return (

{ articleList }

    );
  }
}

function mapStateToProps(state) {
  return {
    articles: state.article.articles,
  };
}

export default connect(mapStateToProps, actions)(ArticleList);
<|endoftext|>