<commit_before>import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { push } from 'connected-react-router';
import { translate, Trans } from 'react-i18next';
import debounce from 'lodash/debounce';
import filter from 'lodash/filter';
import get from 'lodash/get'
import includes from 'lodash/includes';
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import values from 'lodash/values';
import sundial from 'sundial';
import { Box, Flex, Text } from 'rebass/styled-components';
import SearchIcon from '@material-ui/icons/Search';
import CloseRoundedIcon from '@material-ui/icons/CloseRounded';
import EditIcon from '@material-ui/icons/EditRounded';
import DeleteIcon from '@material-ui/icons/DeleteRounded';
import { components as vizComponents } from '@tidepool/viz';

import {
  MediumTitle,
  Body1,
} from '../../components/elements/FontStyles';

import Button from '../../components/elements/Button';
import Icon from '../../components/elements/Icon';
import Table from '../../components/elements/Table';
import Pagination from '../../components/elements/Pagination';
import TextInput from '../../components/elements/TextInput';

import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '../../components/elements/Dialog';

import { useToasts } from '../../providers/ToastProvider';
import * as actions from '../../redux/actions';
import { useIsFirstRender } from '../../core/hooks';

const { Loader } = vizComponents;

export const ClinicPatients = (props) => {
  const { t, api, trackMetric } = props;
  const isFirstRender = useIsFirstRender();
  const dispatch = useDispatch();
  const { set: setToast } = useToasts();
  const selectedClinicId = useSelector((state) => state.blip.selectedClinicId);
  const loggedInUserId = useSelector((state) => state.blip.loggedInUserId);
  const clinics = useSelector((state) => state.blip.clinics);
  const clinic = get(clinics, selectedClinicId);
  const isClinicAdmin = includes(get(clinic, ['clinicians', loggedInUserId, 'roles'], []), 'CLINIC_ADMIN');
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);
  const [showNames, setShowNames] = useState(false);
  const [search, setSearch] = useState('');
  const [selectedPatient, setSelectedPatient] = useState(false);
  const [loading, setLoading] = useState(false);
  const [patientFetchOptions, setPatientFetchOptions] = useState({ limit: 8, search: '', offset: 0 });

  const debounceSearch = useCallback(debounce(search => {
    setPatientFetchOptions({
      ...patientFetchOptions,
      search,
    });
  }, 1000), []);

  const {
    fetchingPatientsForClinic,
    deletingPatientFromClinic,
  } = useSelector((state) => state.blip.working);

  function handleAsyncResult(workingState, successMessage) {
    const { inProgress, completed, notification } = workingState;

    if (!isFirstRender && !inProgress) {
      if (completed) {
        setShowDeleteDialog(false);

        setToast({
          message: successMessage,
          variant: 'success',
        });

        setSelectedPatient(null);
      }

      if (completed === false) {
        setToast({
          message: get(notification, 'message'),
          variant: 'danger',
        });
      }

      setLoading(false);
    }
  }

  useEffect(() => {
    handleAsyncResult(deletingPatientFromClinic, t('{{name}} has been removed from the clinic.', {
      name: get(selectedPatient, 'fullName', t('This patient')),
    }));
  }, [deletingPatientFromClinic]);

  useEffect(() => {
    setLoading(fetchingPatientsForClinic.inProgress);
  }, [fetchingPatientsForClinic.inProgress]);

  // Fetchers
  useEffect(() => {
    if (
      loggedInUserId
      && clinic?.id
      && !fetchingPatientsForClinic.inProgress
      && !fetchingPatientsForClinic.notification
    ) {
      const fetchOptions = { ...patientFetchOptions };
      if (isEmpty(fetchOptions.search)) delete fetchOptions.search;
      dispatch(actions.async.fetchPatientsForClinic.bind(null, api, clinic.id, fetchOptions)());
    }
  }, [loggedInUserId, clinic?.id, patientFetchOptions]);

  function clinicPatients() {
    const filteredPatients = filter(values(clinic?.patients), patient => !isEmpty(patient.id));

    return map(filteredPatients, patient => {
      const birthday = (patient.birthDate)
        ? ` ${sundial.translateMask(patient.birthDate, 'YYYY-MM-DD', t('M/D/YYYY'))}`
        : undefined;

      return {
        fullName: patient.fullName,
        fullNameOrderable: (patient.fullName || '').toLowerCase(),
        link: `/patients/${patient.id}/data`,
        birthday,
        birthdayOrderable: new Date(birthday),
        id: patient.id,
        email: patient.email,
      };
    });
  }

  const renderHeader = () => {
    const toggleLabel = showNames ? t('Hide All') : t('Show All');

    return (
      <Flex mb={4} alignItems="center" justifyContent="space-between">
        <Flex
          alignItems="center"
          justifyContent="space-between"
          flexGrow={1}
          pt={0}
        >
          <TextInput
            themeProps={{
              width: 'auto',
              minWidth: '250px',
            }}
            id="patients-search"
            placeholder={t('Search by Name')}
            icon={!isEmpty(search) ? CloseRoundedIcon : SearchIcon}
            iconLabel={t('Search by Name')}
            onClickIcon={!isEmpty(search) ? handleClearSearch : null}
            name="search-prescriptions"
            onChange={handleSearchChange}
            value={search}
            variant="condensed"
          />

          <Button
            id="patients-view-toggle"
            variant="primary"
            disabled={!isEmpty(search)}
            onClick={handleToggleShowNames}
            mr={0}
          >
            {toggleLabel}
          </Button>
        </Flex>
      </Flex>
    );
  };

  function handleToggleShowNames() {
    let toggleLabel = 'Clicked Hide All';
    if ( !showNames ){
      toggleLabel = 'Clicked Show All';
    }

    trackMetric(toggleLabel);
    setShowNames(!showNames);
  }

  const renderPeopleInstructions = () => {
    return (
      <Trans className="peopletable-instructions" i18nKey="html.peopletable-instructions">
        Type a patient name in the search box or click Show All to display all patients.
      </Trans>
    );
  };

  const renderRemoveDialog = () => {
    const { t } = props;
    const fullName = selectedPatient?.fullName;

    return (
      <Dialog
        id="deleteUser"
        aria-labelledBy="dialog-title"
        open={showDeleteDialog}
        onClose={handleCloseOverlay}
      >
        <DialogTitle onClose={handleCloseOverlay}>
          <MediumTitle id="dialog-title">{t('Remove {{name}}', { name: fullName })}</MediumTitle>
        </DialogTitle>

        <DialogContent>
          <Trans className="ModalOverlay-content" i18nKey="html.peopletable-remove-patient-confirm">
            <Body1>
              Are you sure you want to remove patient: {{fullName}} from your list?
            </Body1>
            <Body1>
              You will no longer be able to see or comment on their data.
            </Body1>
          </Trans>
        </DialogContent>

        <DialogActions>
          <Button id="patientRemoveCancel" variant="secondary" onClick={handleCloseOverlay}>
            {t('Cancel')}
          </Button>
          <Button
            id="patientRemoveConfirm"
            variant="danger"
            onClick={handleRemovePatient}
          >
            {t('Remove')}
          </Button>
        </DialogActions>
      </Dialog>
    );
  };

  function handleRemove(patient) {
    return () => {
      if (props.selectedClinicId) {
        trackMetric('Clinic - Remove patient', { clinicId: props.selectedClinicId });
      }

      setSelectedPatient(patient);
      setShowDeleteDialog(true);
    };
  }

  function handleRemovePatient() {
    dispatch(actions.async.deletePatientFromClinic(api, selectedClinicId, selectedPatient?.id));
  }

  function handleCloseOverlay() {
    setShowDeleteDialog(false);
  }

  function handleClickPatient(link) {
    return () => {
      trackMetric('Selected PwD');

      dispatch(push(link));
    }
  }

  function handleClickEdit(patient) {
    return () => {
      const metric = selectedClinicId
        ? ['Clinic - Edit patient info', { clinicId: selectedClinicId }]
        : ['Clicked Edit PwD'];

      trackMetric(...metric);
      dispatch(push(`/patients/${patient.id}/profile#edit`));
    }
  }

  function handleSearchChange(event) {
    setSearch(event.target.value);
    setLoading(true);
    debounceSearch(event.target.value);
  }

  function handleClearSearch(event) {
    setSearch('');
    setLoading(true);
    debounceSearch('');
  }

  function handlePageChange(event, page) {
    setPatientFetchOptions({
      ...patientFetchOptions,
      offset: (page - 1) * patientFetchOptions.limit,
    });
  }

  const renderPatient = ({fullName, email, link}) => (
    <Box onClick={handleClickPatient(link)} sx={{ cursor: 'pointer' }}>
      <Text fontWeight="medium">{fullName}</Text>
      <Text>{email || '\u00A0'}</Text>
    </Box>
  );

  const renderBirthday = ({birthday, link}) => (
    <Box onClick={handleClickPatient(link)} sx={{ cursor: 'pointer' }}>
      <Text fontWeight="medium">{birthday}</Text>
    </Box>
  );

  const renderEdit = patient => (
    <Icon icon={EditIcon} label={'Edit'} variant={'button'} onClick={handleClickEdit(patient)} />
  );

  const renderRemove = patient => (
    <Icon icon={DeleteIcon} label={'Remove'} variant={'button'} onClick={handleRemove(patient)} />
  );

  const renderPeopleTable = () => {
    const { t } = props;
    const columns = [
      {
        title: t('Patient'),
        field: 'profile',
        align: 'left',
        sortable: true,
        sortBy: 'fullNameOrderable',
        render: renderPatient,
        searchable: true,
        searchBy: ['fullName', 'email'],
      },
      {
        title: t('Birthday'),
        field: 'birthday',
        align: 'left',
        sortable: true,
        sortBy: 'birthdayOrderable',
        render: renderBirthday,
      },
      {
        title: t('Edit'),
        field: 'edit',
        render: renderEdit,
        align: 'center',
        size: 'small',
        padding: 'checkbox',
      },
    ];

    if (isClinicAdmin) columns.push({
      title: t('Remove'),
      field: 'remove',
      render: renderRemove,
      align: 'center',
      size: 'small',
      padding: 'checkbox',
    });

    return (
      <Box sx={{ position: 'relative' }}>
        <Loader show={loading} overlay={true} />

        {clinic?.patientCount > patientFetchOptions.limit && (
          <Pagination
            mt={4}
            id="clinic-patients-pagination"
            count={Math.ceil(clinic.patientCount / patientFetchOptions.limit)}
            onChange={handlePageChange}
            showFirstButton={false}
            showLastButton={false}
          />
        )}
      </Box>
    );
  }

  const renderPeopleArea = () => {
    if (!showNames && !search) {
      return renderPeopleInstructions();
    } else {
      return renderPeopleTable();
    }
  }

  return (

      {renderHeader()}
      {renderPeopleArea()}
      {renderRemoveDialog()}

  );
};

ClinicPatients.propTypes = {
  api: PropTypes.object.isRequired,
  trackMetric: PropTypes.func.isRequired,
};

export default translate()(ClinicPatients);
<commit_msg>[WEB-1280] Temporarily disable clinic patient list sorting.<commit_after>import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { push } from 'connected-react-router';
import { translate, Trans } from 'react-i18next';
import debounce from 'lodash/debounce';
import filter from 'lodash/filter';
import get from 'lodash/get'
import includes from 'lodash/includes';
import isEmpty from 'lodash/isEmpty';
import map from 'lodash/map';
import values from 'lodash/values';
import sundial from 'sundial';
import { Box, Flex, Text } from 'rebass/styled-components';
import SearchIcon from '@material-ui/icons/Search';
import CloseRoundedIcon from '@material-ui/icons/CloseRounded';
import EditIcon from '@material-ui/icons/EditRounded';
import DeleteIcon from '@material-ui/icons/DeleteRounded';
import { components as vizComponents } from '@tidepool/viz';

import {
  MediumTitle,
  Body1,
} from '../../components/elements/FontStyles';

import Button from '../../components/elements/Button';
import Icon from '../../components/elements/Icon';
import Table from '../../components/elements/Table';
import Pagination from '../../components/elements/Pagination';
import TextInput from '../../components/elements/TextInput';

import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '../../components/elements/Dialog';

import { useToasts } from '../../providers/ToastProvider';
import * as actions from '../../redux/actions';
import { useIsFirstRender } from '../../core/hooks';

const { Loader } = vizComponents;

export const ClinicPatients = (props) => {
  const { t, api, trackMetric } = props;
  const isFirstRender = useIsFirstRender();
  const dispatch = useDispatch();
  const { set: setToast } = useToasts();
  const selectedClinicId = useSelector((state) => state.blip.selectedClinicId);
  const loggedInUserId = useSelector((state) => state.blip.loggedInUserId);
  const clinics = useSelector((state) => state.blip.clinics);
  const clinic = get(clinics, selectedClinicId);
  const isClinicAdmin = includes(get(clinic, ['clinicians', loggedInUserId, 'roles'], []), 'CLINIC_ADMIN');
  const [showDeleteDialog, setShowDeleteDialog] = useState(false);
  const [showNames, setShowNames] = useState(false);
  const [search, setSearch] = useState('');
  const [selectedPatient, setSelectedPatient] = useState(false);
  const [loading, setLoading] = useState(false);
  const [patientFetchOptions, setPatientFetchOptions] = useState({ limit: 8, search: '', offset: 0 });

  const debounceSearch = useCallback(debounce(search => {
    setPatientFetchOptions({
      ...patientFetchOptions,
      search,
    });
  }, 1000), []);

  const {
    fetchingPatientsForClinic,
    deletingPatientFromClinic,
  } = useSelector((state) => state.blip.working);

  function handleAsyncResult(workingState, successMessage) {
    const { inProgress, completed, notification } = workingState;

    if (!isFirstRender && !inProgress) {
      if (completed) {
        setShowDeleteDialog(false);

        setToast({
          message: successMessage,
          variant: 'success',
        });

        setSelectedPatient(null);
      }

      if (completed === false) {
        setToast({
          message: get(notification, 'message'),
          variant: 'danger',
        });
      }

      setLoading(false);
    }
  }

  useEffect(() => {
    handleAsyncResult(deletingPatientFromClinic, t('{{name}} has been removed from the clinic.', {
      name: get(selectedPatient, 'fullName', t('This patient')),
    }));
  }, [deletingPatientFromClinic]);

  useEffect(() => {
    setLoading(fetchingPatientsForClinic.inProgress);
  }, [fetchingPatientsForClinic.inProgress]);

  // Fetchers
  useEffect(() => {
    if (
      loggedInUserId
      && clinic?.id
      && !fetchingPatientsForClinic.inProgress
      && !fetchingPatientsForClinic.notification
    ) {
      const fetchOptions = { ...patientFetchOptions };
      if (isEmpty(fetchOptions.search)) delete fetchOptions.search;
      dispatch(actions.async.fetchPatientsForClinic.bind(null, api, clinic.id, fetchOptions)());
    }
  }, [loggedInUserId, clinic?.id, patientFetchOptions]);

  function clinicPatients() {
    const filteredPatients = filter(values(clinic?.patients), patient => !isEmpty(patient.id));

    return map(filteredPatients, patient => {
      const birthday = (patient.birthDate)
        ? ` ${sundial.translateMask(patient.birthDate, 'YYYY-MM-DD', t('M/D/YYYY'))}`
        : undefined;

      return {
        fullName: patient.fullName,
        fullNameOrderable: (patient.fullName || '').toLowerCase(),
        link: `/patients/${patient.id}/data`,
        birthday,
        birthdayOrderable: new Date(birthday),
        id: patient.id,
        email: patient.email,
      };
    });
  }

  const renderHeader = () => {
    const toggleLabel = showNames ? t('Hide All') : t('Show All');

    return (
      <Flex mb={4} alignItems="center" justifyContent="space-between">
        <Flex
          alignItems="center"
          justifyContent="space-between"
          flexGrow={1}
          pt={0}
        >
          <TextInput
            themeProps={{
              width: 'auto',
              minWidth: '250px',
            }}
            id="patients-search"
            placeholder={t('Search by Name')}
            icon={!isEmpty(search) ? CloseRoundedIcon : SearchIcon}
            iconLabel={t('Search by Name')}
            onClickIcon={!isEmpty(search) ? handleClearSearch : null}
            name="search-prescriptions"
            onChange={handleSearchChange}
            value={search}
            variant="condensed"
          />

          <Button
            id="patients-view-toggle"
            variant="primary"
            disabled={!isEmpty(search)}
            onClick={handleToggleShowNames}
            mr={0}
          >
            {toggleLabel}
          </Button>
        </Flex>
      </Flex>
    );
  };

  function handleToggleShowNames() {
    let toggleLabel = 'Clicked Hide All';
    if ( !showNames ){
      toggleLabel = 'Clicked Show All';
    }

    trackMetric(toggleLabel);
    setShowNames(!showNames);
  }

  const renderPeopleInstructions = () => {
    return (
      <Trans className="peopletable-instructions" i18nKey="html.peopletable-instructions">
        Type a patient name in the search box or click Show All to display all patients.
      </Trans>
    );
  };

  const renderRemoveDialog = () => {
    const { t } = props;
    const fullName = selectedPatient?.fullName;

    return (
      <Dialog
        id="deleteUser"
        aria-labelledBy="dialog-title"
        open={showDeleteDialog}
        onClose={handleCloseOverlay}
      >
        <DialogTitle onClose={handleCloseOverlay}>
          <MediumTitle id="dialog-title">{t('Remove {{name}}', { name: fullName })}</MediumTitle>
        </DialogTitle>

        <DialogContent>
          <Trans className="ModalOverlay-content" i18nKey="html.peopletable-remove-patient-confirm">
            <Body1>
              Are you sure you want to remove patient: {{fullName}} from your list?
            </Body1>
            <Body1>
              You will no longer be able to see or comment on their data.
            </Body1>
          </Trans>
        </DialogContent>

        <DialogActions>
          <Button id="patientRemoveCancel" variant="secondary" onClick={handleCloseOverlay}>
            {t('Cancel')}
          </Button>
          <Button
            id="patientRemoveConfirm"
            variant="danger"
            onClick={handleRemovePatient}
          >
            {t('Remove')}
          </Button>
        </DialogActions>
      </Dialog>
    );
  };

  function handleRemove(patient) {
    return () => {
      if (props.selectedClinicId) {
        trackMetric('Clinic - Remove patient', { clinicId: props.selectedClinicId });
      }

      setSelectedPatient(patient);
      setShowDeleteDialog(true);
    };
  }

  function handleRemovePatient() {
    dispatch(actions.async.deletePatientFromClinic(api, selectedClinicId, selectedPatient?.id));
  }

  function handleCloseOverlay() {
    setShowDeleteDialog(false);
  }

  function handleClickPatient(link) {
    return () => {
      trackMetric('Selected PwD');

      dispatch(push(link));
    }
  }

  function handleClickEdit(patient) {
    return () => {
      const metric = selectedClinicId
        ? ['Clinic - Edit patient info', { clinicId: selectedClinicId }]
        : ['Clicked Edit PwD'];

      trackMetric(...metric);
      dispatch(push(`/patients/${patient.id}/profile#edit`));
    }
  }

  function handleSearchChange(event) {
    setSearch(event.target.value);
    setLoading(true);
    debounceSearch(event.target.value);
  }

  function handleClearSearch(event) {
    setSearch('');
    setLoading(true);
    debounceSearch('');
  }

  function handlePageChange(event, page) {
    setPatientFetchOptions({
      ...patientFetchOptions,
      offset: (page - 1) * patientFetchOptions.limit,
    });
  }

  const renderPatient = ({fullName, email, link}) => (
    <Box onClick={handleClickPatient(link)} sx={{ cursor: 'pointer' }}>
      <Text fontWeight="medium">{fullName}</Text>
      <Text>{email || '\u00A0'}</Text>
    </Box>
  );

  const renderBirthday = ({birthday, link}) => (
    <Box onClick={handleClickPatient(link)} sx={{ cursor: 'pointer' }}>
      <Text fontWeight="medium">{birthday}</Text>
    </Box>
  );

  const renderEdit = patient => (
    <Icon icon={EditIcon} label={'Edit'} variant={'button'} onClick={handleClickEdit(patient)} />
  );

  const renderRemove = patient => (
    <Icon icon={DeleteIcon} label={'Remove'} variant={'button'} onClick={handleRemove(patient)} />
  );

  const renderPeopleTable = () => {
    const { t } = props;
    const columns = [
      {
        title: t('Patient'),
        field: 'profile',
        align: 'left',
        // sortable: true, // Sort disabled until backend functionality is ready
        // sortBy: 'fullNameOrderable', // Sort disabled until backend functionality is ready
        render: renderPatient,
        searchable: true,
        searchBy: ['fullName', 'email'],
      },
      {
        title: t('Birthday'),
        field: 'birthday',
        align: 'left',
        // sortable: true, // Sort disabled until backend functionality is ready
        // sortBy: 'birthdayOrderable',  // Sort disabled until backend functionality is ready
        render: renderBirthday,
      },
      {
        title: t('Edit'),
        field: 'edit',
        render: renderEdit,
        align: 'center',
        size: 'small',
        padding: 'checkbox',
      },
    ];

    if (isClinicAdmin) columns.push({
      title: t('Remove'),
      field: 'remove',
      render: renderRemove,
      align: 'center',
      size: 'small',
      padding: 'checkbox',
    });

    return (
      <Box sx={{ position: 'relative' }}>
        <Loader show={loading} overlay={true} />

        {clinic?.patientCount > patientFetchOptions.limit && (
          <Pagination
            mt={4}
            id="clinic-patients-pagination"
            count={Math.ceil(clinic.patientCount / patientFetchOptions.limit)}
            onChange={handlePageChange}
            showFirstButton={false}
            showLastButton={false}
          />
        )}
      </Box>
    );
  }

  const renderPeopleArea = () => {
    if (!showNames && !search) {
      return renderPeopleInstructions();
    } else {
      return renderPeopleTable();
    }
  }

  return (

      {renderHeader()}
      {renderPeopleArea()}
      {renderRemoveDialog()}

  );
};

ClinicPatients.propTypes = {
  api: PropTypes.object.isRequired,
  trackMetric: PropTypes.func.isRequired,
};

export default translate()(ClinicPatients);
<|endoftext|>


<commit_before>if (typeof(PhpDebugBar) == 'undefined') {
    // namespace
    var PhpDebugBar = {};
    PhpDebugBar.$ = jQuery;
}

(function($) {

    var csscls = function(cls) {
        return PhpDebugBar.utils.csscls(cls, 'phpdebugbar-openhandler-');
    };

    PhpDebugBar.OpenHandler = PhpDebugBar.Widget.extend({

        className: 'phpdebugbar-openhandler',

        defaults: {
            items_per_page: 20
        },

        render: function() {
            var self = this;

            this.$el.appendTo('body').hide();
            this.$closebtn = $('');
            this.$table = $('
');
            $('
PHP DebugBar | Open
').addClass(csscls('header')).append(this.$closebtn).appendTo(this.$el);
            $('

Date	Method	URL	IP	Filter data

').append(this.$table).appendTo(this.$el);
            this.$actions = $('
').addClass(csscls('actions')).appendTo(this.$el);

            this.$closebtn.on('click', function() {
                self.hide();
            });

            this.$loadmorebtn = $('Load more')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.find(self.last_find_request, self.last_find_request.offset + self.get('items_per_page'), self.handleFind.bind(self));
                });

            this.$showonlycurrentbtn = $('Show only current URL')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.$table.empty();
                    self.find({uri: window.location.pathname}, 0, self.handleFind.bind(self));
                });

            this.$showallbtn = $('Show all')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.refresh();
                });

            this.$clearbtn = $('Delete all')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.clear(function() {
                        self.hide();
                    });
                });

            this.addSearch();

            this.$overlay = $('
').addClass(csscls('overlay')).hide().appendTo('body');
            this.$overlay.on('click', function() {
                self.hide();
            });
        },

        refresh: function() {
            this.$table.empty();
            this.$loadmorebtn.show();
            this.find({}, 0, this.handleFind.bind(this));
        },

        addSearch: function(){
            var self = this;
            var searchBtn = $('<button />')
                .text('Search')
                .on('click', function(e) {
                    self.$table.empty();
                    var search = {};
                    var a = $(this).parent().serializeArray();
                    $.each(a, function() {
                        if(this.value){
                            search[this.name] = this.value;
                        }
                    });

                    self.find(search, 0, self.handleFind.bind(self));
                    e.preventDefault();
                });

            $('<form />')
                .append('
Filter results
')
                .append('Method: <select name="method"><option></option><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select>
')
                .append('Uri: <input type="text" name="uri">
')
                .append('IP: <input type="text" name="ip">
')
                .append(searchBtn)
                .appendTo(this.$actions);
        },

        handleFind: function(data) {
            var self = this;
            $.each(data, function(i, meta) {
                var a = $('')
                    .text('Load dataset')
                    .on('click', function(e) {
                        self.hide();
                        self.load(meta['id'], function(data) {
                            self.callback(meta['id'], data);
                        });
                        e.preventDefault();
                    });

                var method = $('')
                    .text(meta['method'])
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({method: meta['method']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                var uri = $('')
                    .text(meta['uri'])
                    .on('click', function(e) {
                        self.hide();
                        self.load(meta['id'], function(data) {
                            self.callback(meta['id'], data);
                        });
                        e.preventDefault();
                    });

                var ip = $('')
                    .text(meta['ip'])
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({ip: meta['ip']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                var search = $('')
                    .text('Show URL')
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({uri: meta['uri']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                $('
')
                    .append('' + meta['datetime'] + '	')
                    .append('' + meta['method'] + '	')
                    .append($('').append(uri))
                    .append($('').append(ip))
                    .append($('').append(search))
                    .appendTo(self.$table);
            });
            if (data.length < this.get('items_per_page')) {
                this.$loadmorebtn.hide();
            }
        },

        show: function(callback) {
            this.callback = callback;
            this.$el.show();
            this.$overlay.show();
            this.refresh();
        },

        hide: function() {
            this.$el.hide();
            this.$overlay.hide();
        },

        find: function(filters, offset, callback) {
            var data = $.extend({}, filters, {max: this.get('items_per_page'), offset: offset || 0});
            this.last_find_request = data;
            this.ajax(data, callback);
        },

        load: function(id, callback) {
            this.ajax({op: "get", id: id}, callback);
        },

        clear: function(callback) {
            this.ajax({op: "clear"}, callback);
        },

        ajax: function(data, callback) {
            $.ajax({
                dataType: 'json',
                url: this.get('url'),
                data: data,
                success: callback,
                ignoreDebugBarAjaxHandler: true
            });
        }

    });

})(PhpDebugBar.$);
<commit_msg>Tweak IP width<commit_after>if (typeof(PhpDebugBar) == 'undefined') {
    // namespace
    var PhpDebugBar = {};
    PhpDebugBar.$ = jQuery;
}

(function($) {

    var csscls = function(cls) {
        return PhpDebugBar.utils.csscls(cls, 'phpdebugbar-openhandler-');
    };

    PhpDebugBar.OpenHandler = PhpDebugBar.Widget.extend({

        className: 'phpdebugbar-openhandler',

        defaults: {
            items_per_page: 20
        },

        render: function() {
            var self = this;

            this.$el.appendTo('body').hide();
            this.$closebtn = $('');
            this.$table = $('
');
            $('
PHP DebugBar | Open
').addClass(csscls('header')).append(this.$closebtn).appendTo(this.$el);
            $('

Date	Method	URL	IP	Filter data

').append(this.$table).appendTo(this.$el);
            this.$actions = $('
').addClass(csscls('actions')).appendTo(this.$el);

            this.$closebtn.on('click', function() {
                self.hide();
            });

            this.$loadmorebtn = $('Load more')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.find(self.last_find_request, self.last_find_request.offset + self.get('items_per_page'), self.handleFind.bind(self));
                });

            this.$showonlycurrentbtn = $('Show only current URL')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.$table.empty();
                    self.find({uri: window.location.pathname}, 0, self.handleFind.bind(self));
                });

            this.$showallbtn = $('Show all')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.refresh();
                });

            this.$clearbtn = $('Delete all')
                .appendTo(this.$actions)
                .on('click', function() {
                    self.clear(function() {
                        self.hide();
                    });
                });

            this.addSearch();

            this.$overlay = $('
').addClass(csscls('overlay')).hide().appendTo('body');
            this.$overlay.on('click', function() {
                self.hide();
            });
        },

        refresh: function() {
            this.$table.empty();
            this.$loadmorebtn.show();
            this.find({}, 0, this.handleFind.bind(this));
        },

        addSearch: function(){
            var self = this;
            var searchBtn = $('<button />')
                .text('Search')
                .on('click', function(e) {
                    self.$table.empty();
                    var search = {};
                    var a = $(this).parent().serializeArray();
                    $.each(a, function() {
                        if(this.value){
                            search[this.name] = this.value;
                        }
                    });

                    self.find(search, 0, self.handleFind.bind(self));
                    e.preventDefault();
                });

            $('<form />')
                .append('
Filter results
')
                .append('Method: <select name="method"><option></option><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select>
')
                .append('Uri: <input type="text" name="uri">
')
                .append('IP: <input type="text" name="ip">
')
                .append(searchBtn)
                .appendTo(this.$actions);
        },

        handleFind: function(data) {
            var self = this;
            $.each(data, function(i, meta) {
                var a = $('')
                    .text('Load dataset')
                    .on('click', function(e) {
                        self.hide();
                        self.load(meta['id'], function(data) {
                            self.callback(meta['id'], data);
                        });
                        e.preventDefault();
                    });

                var method = $('')
                    .text(meta['method'])
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({method: meta['method']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                var uri = $('')
                    .text(meta['uri'])
                    .on('click', function(e) {
                        self.hide();
                        self.load(meta['id'], function(data) {
                            self.callback(meta['id'], data);
                        });
                        e.preventDefault();
                    });

                var ip = $('')
                    .text(meta['ip'])
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({ip: meta['ip']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                var search = $('')
                    .text('Show URL')
                    .on('click', function(e) {
                        self.$table.empty();
                        self.find({uri: meta['uri']}, 0, self.handleFind.bind(self));
                        e.preventDefault();
                    });

                $('
')
                    .append('' + meta['datetime'] + '	')
                    .append('' + meta['method'] + '	')
                    .append($('').append(uri))
                    .append($('').append(ip))
                    .append($('').append(search))
                    .appendTo(self.$table);
            });
            if (data.length < this.get('items_per_page')) {
                this.$loadmorebtn.hide();
            }
        },

        show: function(callback) {
            this.callback = callback;
            this.$el.show();
            this.$overlay.show();
            this.refresh();
        },

        hide: function() {
            this.$el.hide();
            this.$overlay.hide();
        },

        find: function(filters, offset, callback) {
            var data = $.extend({}, filters, {max: this.get('items_per_page'), offset: offset || 0});
            this.last_find_request = data;
            this.ajax(data, callback);
        },

        load: function(id, callback) {
            this.ajax({op: "get", id: id}, callback);
        },

        clear: function(callback) {
            this.ajax({op: "clear"}, callback);
        },

        ajax: function(data, callback) {
            $.ajax({
                dataType: 'json',
                url: this.get('url'),
                data: data,
                success: callback,
                ignoreDebugBarAjaxHandler: true
            });
        }

    });

})(PhpDebugBar.$);
<|endoftext|>


<commit_before>import * as Immutable from 'immutable'

import uuid from 'helpers/uuid'
import randomChar from 'helpers/randomChar'
import {checkScheduleTimeConflicts} from 'helpers/time'
import {getCourses} from 'helpers/courses'

let ScheduleRecord = Immutable.Record({
	id: uuid(),
	active: false,
	year: 0,
	semester: 0,
	index: 1,
	title: 'Schedule ' + randomChar().toUpperCase(),
	clbids: Immutable.List(),
})

class Schedule extends ScheduleRecord {
	constructor(data) {
		super(data)
		this._coursesAreDirty = true
	}

	// Getters
	get courses() {
		if (this._coursesAreDirty) {
			this._courseData = getCourses(this.clbids)
			this._coursesAreDirty = false
		}
		return this._courseData
	}

	// Schedule Maintenance
	move(to={}) {
		// `to` is an object: {year, semester}
		return this.withMutations((sched) => {
			if (to.year)
				sched = sched.set('year', to.year)
			if (to.semester)
				sched = sched.set('semester', to.semester)
		})
	}

	reorder(newIndex) {
		return this.set('index', newIndex)
	}

	rename(newTitle) {
		return this.set('title', newTitle)
	}

	// Course Maintenance

	reorderCourse(clbid, newIndex) {
		let oldIndex = this.clbids.findIndex((id) => id === clbid)
		this._coursesAreDirty = true
		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.splice(oldIndex, 1))
			sched = sched.set('clbids', sched.clbids.splice(newIndex, 0, clbid))
		})
	}

	addCourse(clbid, index) {
		index = (index >= 0) ? index : this.clbids.size - 1;
		this._coursesAreDirty = true
		return this.set('clbids', this.clbids.splice(index, 0, clbid))
	}

	removeCourse(clbid) {
		console.log(`removing course with clbid: ${clbid}`)
		let index = this.clbids.findIndex((id) => id === clbid)
		this._coursesAreDirty = true
		return this.set('clbids', this.clbids.splice(index, 1))
	}

	// Schedule Validation

	validate() {
		// Checks to see if the schedule is valid
		return this.courses
			// Step one: do any times conflict?
			.then(checkScheduleTimeConflicts)
			.then((conflicts) => {
				var hasConflict = _(conflicts)
					.flatten()      // flatten the nested arrays
					.any()          // and see if any of the resulting values are true

				if (hasConflict.length) {
					console.log('schedule conflicts', conflicts, hasConflict)
				}

				return {
					hasConflict: hasConflict,
					conflicts: conflicts
				}
			})
	}
}

export default Schedule
<commit_msg>Use immutable in schedule.validate<commit_after>import * as Immutable from 'immutable'

import uuid from 'helpers/uuid'
import randomChar from 'helpers/randomChar'
import {checkScheduleTimeConflicts} from 'helpers/time'
import {getCourses} from 'helpers/courses'

let ScheduleRecord = Immutable.Record({
	id: uuid(),
	active: false,
	year: 0,
	semester: 0,
	index: 1,
	title: 'Schedule ' + randomChar().toUpperCase(),
	clbids: Immutable.List(),
})

class Schedule extends ScheduleRecord {
	constructor(data) {
		super(data)
		this._coursesAreDirty = true
	}

	// Getters
	get courses() {
		if (this._coursesAreDirty) {
			this._courseData = getCourses(this.clbids)
			this._coursesAreDirty = false
		}
		return this._courseData
	}

	// Schedule Maintenance
	move(to={}) {
		// `to` is an object: {year, semester}
		return this.withMutations((sched) => {
			if (to.year)
				sched = sched.set('year', to.year)
			if (to.semester)
				sched = sched.set('semester', to.semester)
		})
	}

	reorder(newIndex) {
		return this.set('index', newIndex)
	}

	rename(newTitle) {
		return this.set('title', newTitle)
	}

	// Course Maintenance

	reorderCourse(clbid, newIndex) {
		let oldIndex = this.clbids.findIndex((id) => id === clbid)
		this._coursesAreDirty = true
		return this.withMutations((sched) => {
			sched = sched.set('clbids', sched.clbids.splice(oldIndex, 1))
			sched = sched.set('clbids', sched.clbids.splice(newIndex, 0, clbid))
		})
	}

	addCourse(clbid, index) {
		index = (index >= 0) ? index : this.clbids.size - 1;
		this._coursesAreDirty = true
		return this.set('clbids', this.clbids.splice(index, 0, clbid))
	}

	removeCourse(clbid) {
		console.log(`removing course with clbid: ${clbid}`)
		let index = this.clbids.findIndex((id) => id === clbid)
		this._coursesAreDirty = true
		return this.set('clbids', this.clbids.splice(index, 1))
	}

	// Schedule Validation

	validate() {
		// Checks to see if the schedule is valid
		return this.courses
			// Step one: do any times conflict?
			.then(checkScheduleTimeConflicts)
			.then((conflicts) => {
				var hasConflict = Immutable.fromJS(conflicts)
					// flatten the nested arrays
					.flatten()
					// and see if any of the resulting values are true
					.some((value) => value === true)

				if (hasConflict) {
					console.log('schedule conflicts', conflicts, hasConflict)
				}

				return {
					hasConflict: hasConflict,
					conflicts: conflicts,
				}
			})
	}
}

export default Schedule
<|endoftext|>


<commit_before>import * as queryString from 'query-string';

class CollectionsService {
  /**
   * Retrieve all collections of the user
   * @param {*} token User's token
   */
  static getAllCollections(token) {
    const queryParams = queryString.stringify({
      application: process.env.APPLICATIONS
    });

    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection?${queryParams}`, {
        method: 'GET',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;
          if (status === 200) return response.json();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch(errors => reject(errors));
    });
  }

  /**
   * Retrieve data of a specific collection
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection we are asking for.
   */
  static getCollection(token, collectionId) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'GET',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;
          if (status === 200) return response.json();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch(errors => reject(errors));
    });
  }

  /**
   * Creates a new collection attached to the current user
   * @param {*} token User's token
   * @param {*} name Name of the new collection
   * @param {*} resources List of resources attached to the new collection (optional)
   */
  static createCollection(token, name, resources = []) {
    const queryParams = queryString.stringify({
      application: process.env.APPLICATIONS
    });

    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection?${queryParams}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: token
        },
        body: JSON.stringify({
          name,
          resources
        })
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   * Deletes an existing collection attached to the current user
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be removed
   */
  static deleteCollection(token, collectionId) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'DELETE',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   * Edits an existing collection attached to the current user
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} name New name of the existing collection
   */
  static editCollection(token, collectionId, name) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'PATCH',
        headers: {
          Authorization: token
        },
        body: {
          name
        }
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /* Resources management  */

  /**
   *
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} resource Resource to be addded to the collection
   */
  static addResourceToCollection(token, collectionId, resource = []) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}/resource`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: token
        },
        body: JSON.stringify({ ...resource })
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   *
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} resource Resource to be removed from the collection
   */
  static removeResourceFromCollection(token, collectionId, resource = []) {
    const { type, id } = resource;
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}/resource/${type}/${id}`, {
        method: 'DELETE',
        headers: {
          Authorization: token
        },
        body: {}
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }
}

export default CollectionsService;
<commit_msg>Sets application when creates a collection<commit_after>import * as queryString from 'query-string';

class CollectionsService {
  /**
   * Retrieve all collections of the user
   * @param {*} token User's token
   */
  static getAllCollections(token) {
    const queryParams = queryString.stringify({
      application: [process.env.APPLICATIONS]
    });

    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection?${queryParams}`, {
        method: 'GET',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;
          if (status === 200) return response.json();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch(errors => reject(errors));
    });
  }

  /**
   * Retrieve data of a specific collection
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection we are asking for.
   */
  static getCollection(token, collectionId) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'GET',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;
          if (status === 200) return response.json();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch(errors => reject(errors));
    });
  }

  /**
   * Creates a new collection attached to the current user
   * @param {*} token User's token
   * @param {*} name Name of the new collection
   * @param {*} resources List of resources attached to the new collection (optional)
   */
  static createCollection(token, name, resources = []) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: token
        },
        body: JSON.stringify({
          application: process.env.APPLICATIONS,
          name,
          resources
        })
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   * Deletes an existing collection attached to the current user
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be removed
   */
  static deleteCollection(token, collectionId) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'DELETE',
        headers: {
          Authorization: token
        }
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   * Edits an existing collection attached to the current user
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} name New name of the existing collection
   */
  static editCollection(token, collectionId, name) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}`, {
        method: 'PATCH',
        headers: {
          Authorization: token
        },
        body: {
          name
        }
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /* Resources management  */

  /**
   *
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} resource Resource to be addded to the collection
   */
  static addResourceToCollection(token, collectionId, resource = []) {
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}/resource`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: token
        },
        body: JSON.stringify({ ...resource })
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }

  /**
   *
   * @param {*} token User's token
   * @param {*} collectionId Id of the collection to be edited
   * @param {*} resource Resource to be removed from the collection
   */
  static removeResourceFromCollection(token, collectionId, resource = []) {
    const { type, id } = resource;
    return new Promise((resolve, reject) => {
      fetch(`${process.env.RW_API_URL}/collection/${collectionId}/resource/${type}/${id}`, {
        method: 'DELETE',
        headers: {
          Authorization: token
        },
        body: {}
      })
        .then((response) => {
          const { status, statusText } = response;

          if (status === 200) return resolve();

          const errorObject = {
            errors: {
              status,
              details: statusText
            }
          };
          throw errorObject;
        })
        .then(data => resolve(data))
        .catch((errors) => { reject(errors); });
    });
  }
}

export default CollectionsService;
<|endoftext|>


<commit_before>'use strict';

const Fs = require('fs');
const OS = require('os');
const Path = require('path');
const ChildProcess = require('child_process');

const re = {
    slash: /\\/g,
    split: /[\/\\]/g
};

const dateParts = [
    'birthtime',
    'atime',
    'mtime'
];

/**
 * This class wraps a path to a file or directory and provides methods to ease processing
 * and operating on that path.
 *
 * ## Naming Conventions
 *
 * Since it can be confusing when a string is returned or a `File` instance, a naming
 * convention is used throughout. Any method that ends with "Path" returns a string,
 * while all other methods return a `File`. Methods often come in pairs: one that returns
 * the string form and one that returns a `File`. In general, it is safest/best to stay
 * in the realm of `File` objects so their names are more concise.
 */
class File {
    static run (cmd, ...args) {
        return new Promise(resolve => {
            var lines = "";
            var process = ChildProcess.spawn(cmd, args, { encoding: 'utf8' });

            process.stdout.on('data', data => {
                lines += data.toString();
            });

            // process.on('error', function(err) {
            // });

            process.on('close', (code, signal) => {
                if (process.exitCode) {
                    resolve(process.exitCode);
                }
                else {
                    resolve(lines.trim().split('\r\n'));
                }
            });
        });
    }

    static winDir (path) {
        return File.winRun('dir', path).then(lines => {
            let content = lines.filter(line => !!line);
            return content.map(File.winParseStat);
        });
    }

    static winParseStat (text) {
        // attr/ctime/atime/mtime/size/name
        // [0]  [1]   [2]   [3]   [4]  [5]
        let parts = text.split('/');
        let stat = new Fs.Stats();

        for (let i = 0; i < dateParts.length; ++i) {
            let d = new Date();

            d.setTime(+parts[i+1] * 1000); // millisec

            stat[dateParts[i]] = d;
        }
    }

    static winRun (...args) {
        return File.run(File.winExe, ...args);
    }

    static from (path) {
        var file = path || null;

        if (file && !file.$isFile) {
            file = new File(path);
        }

        return file;
    }

    /**
     * This method is the same as `join()` in the `path` module except that the items
     * can be `File` instances or `String` and a `File` instance is returned.
     * @param {File/String...} parts Path pieces to join using `path.join()`.
     * @return {File}
     */
    static join (...parts) {
        var f = File.joinPath(...parts);
        return new File(f);
    }

    /**
     * This method is the same as `join()` in the `path` module except that the items
     * can be `File` instances or `String`.
     * @param {File/String...} parts Path pieces to join using `path.join()`.
     * @return {String}
     */
    static joinPath (...parts) {
        let n = parts && parts.length || 0;

        for (let i = 0; i < n; ++i) {
            let p = parts[i];

            if (p.$isFile) {
                parts[i] = p.path;
            }
        }

        let ret = (n === 1) ? parts[0] : (n && Path.join(...parts));

        return ret || '';
    }

    static path (file) {
        return ((file && file.$isFile) ? file.path : file) || '';
    }

    /**
     * This method is the same as `resolve()` in the `path` module except that the items
     * can be `File` instances or `String` and a `File` instance is returned.
     * @param {File/String...} parts Path pieces to resolve using `path.resolve()`.
     * @return {File}
     */
    static resolve (...parts) {
        var f = File.resolvePath(...parts);
        return new File(f);
    }

    /**
     * This method is the same as `resolve()` in the `path` module except that the items
     * can be `File` instances or `String`.
     * @param {File/String...} parts Path pieces to resolve using `path.resolve()`.
     * @return {String}
     */
    static resolvePath (...parts) {
        for (let i = 0, n = parts.length; i < n; ++i) {
            let p = parts[i];

            if (p.$isFile) {
                parts[i] = p.path;
            }
        }

        return (parts && parts.length && Path.resolve(...parts)) || '';
    }

    static split (filePath) {
        let path = File.path(filePath);
        return path.split(re.split);
    }

    constructor (...parts) {
        this.path = File.joinPath(...parts);
    }

    //-----------------------------------------------------------------
    // Properties

    get name () {
        var name = this._name;

        if (name === undefined) {
            let index = this.lastSeparator();

            this._name = name = ((index > -1) && this.path.substr(index + 1)) || '';
        }

        return name;
    }

    get parent () {
        var parent = this._parent;

        if (parent === undefined) {
            let path = this.path;
            let ret = Path.resolve(path, '..');

            if (path === ret) {
                ret = null;
            }

            this._parent = parent = ret && new File(ret);
        }

        return parent;
    }

    get extent () {
        var ext = this._extent;

        if (ext === undefined) {
            let name = this.name;
            let index = name.lastIndexOf('.');

            this._extent = ext = ((index > -1) && name.substr(index + 1)) || '';
        }

        return ext;
    }

    //-----------------------------------------------------------------
    // Methods

    absolutePath () {
        return Path.resolve(this.path);
    }

    absolutify () {
        return File.from(this.absolutePath());
    }

    canonicalPath () {
        return Fs.realpathSync(Path.resolve(this.path));
    }

    canonicalize () {
        return File.from(this.canonicalPath());
    }

    contains (subPath) {
        subPath = File.from(subPath);

        if (subPath) {
            // Ensure we don't have trailing slashes ("/foo/bar/" => "/foo/bar")
            let a = this.slashify().unterminatedPath();
            let b = subPath.slashifiedPath();

            if (a.startsWith(b)) {
                // a = "/foo/bar"
                // b = "/foo/bar/zip" ==> true
                // b = "/foo/barf"    ==> false
                return b[a.length] === '/';
            }
        }

        return false;
    }

    equals (other) {
        other = File.from(other);

        // Treat "/foo/bar" and "/foo/bar/" as equal (by stripping trailing delimiters)
        let a = this.unterminatedPath();
        let b = other && other.unterminatedPath() || '';

        // If the platform has case-insensitive file names, ignore case...
        if (!File.CASE) {
            a = a.toLowerCase();
            b = b.toLowerCase();
        }

        return a === b;
    }

    isAbsolute () {
        var p = this.path;
        return p && Path.isAbsolute(p);
    }

    isRelative () {
        var p = this.path;
        return p && !Path.isAbsolute(p);
    }

    joinPath (...parts) {
        return File.joinPath(this, ...parts);
    }

    join (...parts) {
        return File.join(this, ...parts);
    }

    lastSeparator () {
        var path = this.path,
            i = path.lastIndexOf('/'),
            j = path.lastIndexOf('\\');

        return (i > j) ? i : j;
    }

    nativePath (separator) {
        var p = this.path;

        return p && p.replace(re.split, separator || File.separator);
    }

    nativize (separator) {
        return File.from(this.nativePath(separator));
    }

    normalize () {
        return File.from(this.normalizedPath());
    }

    normalizedPath () {
        var p = this.path;
        return p && Path.normalize(p);
    }

    relativePath (path) {
        if (path.$isFile) {
            path = path.getCanonicalPath();
        }

        let p = this.canonicalPath();

        return p && path && Path.relative(p, path);
    }

    relativize (path) {
        return File.from(this.relativePath(path));
    }

    resolvePath (...parts) {
        return File.resolvePath(this, ...parts);
    }

    resolve (...parts) {
        return File.resolve(this, ...parts);
    }

    slashifiedPath () {
        return this.path.replace(re.slash, '/');
    }

    /**
     * Replace forward/backward slashes with forward slashes.
     * @return {String}
     */
    slashify () {
        return File.from(this.slashifiedPath());
    }

    split () {
        return File.split(this);
    }

    stat () {
        if (File.winExe) {
            return File.winDir(this.path).then(lines => {
                //
            });
        }

        return new Promise((resolve, reject) => {
            Fs.stat(this.path, (err, stats) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(stats);
                }
            });
        });
    }

    toString () {
        return this.path;
    }

    terminatedPath (separator) {
        if (separator == null || separator === true) {
            separator = File.separator;
        }

        let p = this.path;

        if (p && p.length) {
            let n = p.length - 1;
            let c = p[n];

            if (separator) {
                if (c !== separator) {
                    p += separator;
                }
            }
            else {
                while (n >= 0 && (c === '/' || c === '\\')) {
                    p = p.substr(0, n--);
                    c = p[n];
                }
            }
        }

        return p || '';
    }

    terminate (separator) {
        return File.from(this.terminatedPath(separator));
    }

    unterminatedPath () {
        return this.terminatedPath(false);
    }

    unterminate () {
        return File.from(this.unterminatedPath());
    }
}

Object.assign(File.prototype, {
    $isFile: true,
    _re: re,

    _extent: undefined,
    _name: undefined,
    _parent: undefined
});

const platform = OS.platform();

File.WIN = /^win\d\d$/i.test(platform);
File.MAC = /^darwin$/i.test(platform);

File.CASE = !File.WIN && !File.MAC;

File.re = re;
File.separator = Path.sep;

File.winExe = File.WIN ? Path.resolve(__dirname, 'bin/phylo.exe') : null;

console.log(`File.winExe = ${File.winExe}`);

module.exports = File;

var f = File.from(process.cwd());
f = File.from('/foo/bar/baz');
//f = File.from('C:/foo//bar');
console.log(f.path);

let p = f.parent;
console.log(p.path);

p = p.parent;
console.log(p.path);

p = p.parent;
console.log(p);
console.log(p === null);

console.log(['a','','b'].filter(a => !!a).map(a => a.toUpperCase()));
<commit_msg>add isFile et al<commit_after>'use strict';

const Fs = require('fs');
const OS = require('os');
const Path = require('path');
const ChildProcess = require('child_process');

const re = {
    slash: /\\/g,
    split: /[\/\\]/g
};

/**
 * This class wraps a path to a file or directory and provides methods to ease processing
 * and operating on that path.
 *
 * ## Naming Conventions
 *
 * Since it can be confusing when a string is returned or a `File` instance, a naming
 * convention is used throughout. Any method that ends with "Path" returns a string,
 * while all other methods return a `File`. Methods often come in pairs: one that returns
 * the string form and one that returns a `File`. In general, it is safest/best to stay
 * in the realm of `File` objects so their names are more concise.
 */
class File {
    static from (path) {
        var file = path || null;

        if (file && !file.$isFile) {
            file = new File(path);
        }

        return file;
    }

    /**
     * This method is the same as `join()` in the `path` module except that the items
     * can be `File` instances or `String` and a `File` instance is returned.
     * @param {File/String...} parts Path pieces to join using `path.join()`.
     * @return {File}
     */
    static join (...parts) {
        var f = File.joinPath(...parts);
        return new File(f);
    }

    /**
     * This method is the same as `join()` in the `path` module except that the items
     * can be `File` instances or `String`.
     * @param {File/String...} parts Path pieces to join using `path.join()`.
     * @return {String}
     */
    static joinPath (...parts) {
        let n = parts && parts.length || 0;

        for (let i = 0; i < n; ++i) {
            let p = parts[i];

            if (p.$isFile) {
                parts[i] = p.path;
            }
        }

        let ret = (n === 1) ? parts[0] : (n && Path.join(...parts));

        return ret || '';
    }

    static path (file) {
        return ((file && file.$isFile) ? file.path : file) || '';
    }

    /**
     * This method is the same as `resolve()` in the `path` module except that the items
     * can be `File` instances or `String` and a `File` instance is returned.
     * @param {File/String...} parts Path pieces to resolve using `path.resolve()`.
     * @return {File}
     */
    static resolve (...parts) {
        var f = File.resolvePath(...parts);
        return new File(f);
    }

    /**
     * This method is the same as `resolve()` in the `path` module except that the items
     * can be `File` instances or `String`.
     * @param {File/String...} parts Path pieces to resolve using `path.resolve()`.
     * @return {String}
     */
    static resolvePath (...parts) {
        for (let i = 0, n = parts.length; i < n; ++i) {
            let p = parts[i];

            if (p.$isFile) {
                parts[i] = p.path;
            }
        }

        return (parts && parts.length && Path.resolve(...parts)) || '';
    }

    static split (filePath) {
        let path = File.path(filePath);
        return path.split(re.split);
    }

    constructor (...parts) {
        this.path = File.joinPath(...parts);
    }

    //-----------------------------------------------------------------
    // Properties

    get name () {
        var name = this._name;

        if (name === undefined) {
            let index = this.lastSeparator();

            this._name = name = ((index > -1) && this.path.substr(index + 1)) || '';
        }

        return name;
    }

    get parent () {
        var parent = this._parent;

        if (parent === undefined) {
            let path = this.path;
            let ret = Path.resolve(path, '..');

            if (path === ret) {
                ret = null;
            }

            this._parent = parent = ret && new File(ret);
        }

        return parent;
    }

    get extent () {
        var ext = this._extent;

        if (ext === undefined) {
            let name = this.name;
            let index = name.lastIndexOf('.');

            this._extent = ext = ((index > -1) && name.substr(index + 1)) || '';
        }

        return ext;
    }

    //-----------------------------------------------------------------
    // Methods

    absolutePath () {
        return Path.resolve(this.path);
    }

    absolutify () {
        return File.from(this.absolutePath());
    }

    canonicalPath () {
        return Fs.realpathSync(Path.resolve(this.path));
    }

    canonicalize () {
        return File.from(this.canonicalPath());
    }

    contains (subPath) {
        subPath = File.from(subPath);

        if (subPath) {
            // Ensure we don't have trailing slashes ("/foo/bar/" => "/foo/bar")
            let a = this.slashify().unterminatedPath();
            let b = subPath.slashifiedPath();

            if (a.startsWith(b)) {
                // a = "/foo/bar"
                // b = "/foo/bar/zip" ==> true
                // b = "/foo/barf"    ==> false
                return b[a.length] === '/';
            }
        }

        return false;
    }

    equals (other) {
        other = File.from(other);

        // Treat "/foo/bar" and "/foo/bar/" as equal (by stripping trailing delimiters)
        let a = this.unterminatedPath();
        let b = other && other.unterminatedPath() || '';

        // If the platform has case-insensitive file names, ignore case...
        if (!File.CASE) {
            a = a.toLowerCase();
            b = b.toLowerCase();
        }

        return a === b;
    }

    isAbsolute () {
        var p = this.path;
        return p && Path.isAbsolute(p);
    }

    isRelative () {
        var p = this.path;
        return p && !Path.isAbsolute(p);
    }

    joinPath (...parts) {
        return File.joinPath(this, ...parts);
    }

    join (...parts) {
        return File.join(this, ...parts);
    }

    lastSeparator () {
        var path = this.path,
            i = path.lastIndexOf('/'),
            j = path.lastIndexOf('\\');

        return (i > j) ? i : j;
    }

    nativePath (separator) {
        var p = this.path;

        return p && p.replace(re.split, separator || File.separator);
    }

    nativize (separator) {
        return File.from(this.nativePath(separator));
    }

    normalize () {
        return File.from(this.normalizedPath());
    }

    normalizedPath () {
        var p = this.path;
        return p && Path.normalize(p);
    }

    relativePath (path) {
        if (path.$isFile) {
            path = path.getCanonicalPath();
        }

        let p = this.canonicalPath();

        return p && path && Path.relative(p, path);
    }

    relativize (path) {
        return File.from(this.relativePath(path));
    }

    resolvePath (...parts) {
        return File.resolvePath(this, ...parts);
    }

    resolve (...parts) {
        return File.resolve(this, ...parts);
    }

    slashifiedPath () {
        return this.path.replace(re.slash, '/');
    }

    /**
     * Replace forward/backward slashes with forward slashes.
     * @return {String}
     */
    slashify () {
        return File.from(this.slashifiedPath());
    }

    split () {
        return File.split(this);
    }

    stat () {
        // if (File.WIN) {
        //     return Win.dir(this.path).then(stats => {
        //         console.log('stats:', stats);
        //         return stats[0];
        //     });
        // }

        return Fs.statSync(this.path);
    }

    statLink () {
        // if (File.WIN) {
        //     return Win.dir(this.path).then(stats => {
        //         console.log('stats:', stats);
        //         return stats[0];
        //     });
        // }

        return Fs.lstatSync(this.path);
    }

    toString () {
        return this.path;
    }

    terminatedPath (separator) {
        if (separator == null || separator === true) {
            separator = File.separator;
        }

        let p = this.path;

        if (p && p.length) {
            let n = p.length - 1;
            let c = p[n];

            if (separator) {
                if (c !== separator) {
                    p += separator;
                }
            }
            else {
                while (n >= 0 && (c === '/' || c === '\\')) {
                    p = p.substr(0, n--);
                    c = p[n];
                }
            }
        }

        return p || '';
    }

    terminate (separator) {
        return File.from(this.terminatedPath(separator));
    }

    unterminatedPath () {
        return this.terminatedPath(false);
    }

    unterminate () {
        return File.from(this.unterminatedPath());
    }

    //------------------------------------------------------------------

    getStat () {
        // if (File.WIN) {
        //     return Win.dir(this.path).then(stats => {
        //         console.log('stats:', stats);
        //         return stats[0];
        //     });
        // }

        return new Promise((resolve, reject) => {
            Fs.stat(this.path, (err, stats) => {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(stats);
                }
            });
        });
    }
}

const proto = File.prototype;

Object.assign(proto, {
    $isFile: true,
    _re: re,

    _extent: undefined,
    _name: undefined,
    _parent: undefined
});

const platform = OS.platform();

File.WIN = /^win\d\d$/i.test(platform);
File.MAC = /^darwin$/i.test(platform);

File.CASE = !File.WIN && !File.MAC;

File.re = re;
File.separator = Path.sep;

function addTypeTest (name, statMethod) {
    let prop = '_' + name;

    statMethod = statMethod || 'stat';
    proto[prop] = null;

    return proto[name] = function () {
        let value = this[prop];

        if (value === null) {
            let stat = this[statMethod]();

            this[prop] = value = stat[name]();
        }

        return value;
    };
}

proto.isLink = addTypeTest('isSymbolicLink', 'statLink');

[
    'isBlockDevice', 'isCharacterDevice', 'isDirectory', 'isFile', 'isFIFO',
    'isSocket'
].forEach(fn => addTypeTest(fn));

proto.isDir = proto.isDirectory;

//------------------------------------------------------------

const dateParts = [
    'birthtime',
    'atime',
    'mtime'
];

class Win {
    static dir (path) {
        return Win.run('dir', path).then(lines => {
            let content = lines.filter(line => !!line);
            return content.map(Win.parseStat);
        });
    }

    static parseStat (text) {
        // attr/ctime/atime/mtime/size/name
        // [0]  [1]   [2]   [3]   [4]  [5]
        let parts = text.split('/');
        let stat = new Fs.Stats();

        for (let i = 0; i < dateParts.length; ++i) {
            let d = new Date();

            d.setTime(+parts[i+1] * 1000); // millisec

            stat[dateParts[i]] = d;
        }

        stat.attribs = parts[0];
        stat.ctime = stat.mtime;  // no ctime on Windows
        stat.path = parts[5];
        stat.size = +parts[4];

        return stat;
    }

    static run (...args) {
        return Win.spawn(Win.exe, ...args);
    }

    static spawn (cmd, ...args) {
        return new Promise(resolve => {
            var lines = '';
            var process = ChildProcess.spawn(cmd, args, { encoding: 'utf8' });

            process.stdout.on('data', data => {
                lines += data.toString();
                console.log('data:', data);
            });

            process.on('error', function(err) {
                console.log('error', err);
            });
            process.on('exit', function(err) {
                console.log('exit', err);
            });

            process.on('close', (code, signal) => {
                console.log(`${cmd} ${args.join(" ")}:`, lines, ` (exit ${process.exitCode})`);
                if (process.exitCode) {
                    resolve(process.exitCode);
                }
                else {
                    resolve(lines.trim().split('\r\n'));
                }
            });
        });
    }
}

if (File.WIN) {
    Win.exe = Path.resolve(__dirname, 'bin/phylo.exe');

    File.Win = Win;

    console.log(`File.Win.exe = ${Win.exe}`);
}

//------------------------------------------------------------

module.exports = File;

//------------------------------------------------------------

var f = File.from(process.cwd());

console.log('The stat: ', f.stat());
console.log(`is: file=${f.isFile()} dir=${f.isDirectory()}`);
<|endoftext|>


<commit_before>
module.exports.calculate = function (workloadReports) {
  var capacities = []
  workloadReports.forEach(function (report) {
    var allPoints = report.total_points + report.sdr_points +
      report.sdr_conversion_points + report.paroms_points
    var capacityPercentage = (allPoints / report.available_points) * 100
    capacities.push({
      workload_report_date: report.effective_from,
      capacity_percentage: capacityPercentage,
      reductions: report.reduction_hours
    })
  })

  return capacities
}
<commit_msg>Replace total_points sum with total_points value in capacity-calculator<commit_after>
module.exports.calculate = function (workloadReports) {
  var capacities = []
  workloadReports.forEach(function (report) {
    var capacityPercentage = (report.total_points / report.available_points) * 100
    capacities.push({
      workload_report_date: report.effective_from,
      capacity_percentage: capacityPercentage,
      reductions: report.reduction_hours
    })
  })

  return capacities
}
<|endoftext|>


<commit_before>//create a Task object
module.exports = class Task {
    constructor() {
        //TODO change this out to use a data object instead of fields
        this.description = "";
        this.duration = 0;
        this.startDate = null;
        this.endDate = null;
        this.ID = 0;

        this.parentTask = null;
        this.childrenTasks = [];
    }

    static createFromData(data) {
        //this will create a task from a given JSON object
        var task = new Task();

        task.description = data.description;
        task.ID = data.ID;

        return task;
    }

    getObjectForSaving() {
        //this will be used up above, ideally they match
        return {
            "description": this.description,
            "ID": this.ID
        }
    }
}

<commit_msg>Remove some line breaks<commit_after>//create a Task object
module.exports = class Task {
    constructor() {
        //TODO change this out to use a data object instead of fields
        this.description = "";
        this.duration = 0;
        this.startDate = null;
        this.endDate = null;
        this.ID = 0;

        this.parentTask = null;
        this.childrenTasks = [];
    }

    static createFromData(data) {
        //this will create a task from a given JSON object
        var task = new Task();

        task.description = data.description;
        task.ID = data.ID;

        return task;
    }

    getObjectForSaving() {
        //this will be used up above, ideally they match
        return {
            "description": this.description,
            "ID": this.ID
        }
    }
}<|endoftext|>


<commit_before>const Scenario = require('../domain/scenario')

module.exports = function (results) {
  var scenarioMap = new Map()
  var scenarioArray = []
  results.forEach(function (result) {
    scenarioMap.set(result['workload_id'], [])
  })
  results.forEach(function (result) {
    var temporaryMappedItems = []
    temporaryMappedItems = scenarioMap.get(result['workload_id'])
    temporaryMappedItems.push(result)
    scenarioMap.set(result['workload_id'], temporaryMappedItems)
  })
  for (var value of scenarioMap.values()) {
    var scenarioObj = new Scenario(value)
    scenarioArray.push(scenarioObj)
  }
  scenarioArray.sort(sortScenarioArray)
  return scenarioArray
}

var sortScenarioArray = function (scenarioObj1, scenarioObj2) {
  if (scenarioObj1.name.toUpperCase() > scenarioObj2.name.toUpperCase()) {
    return 1
  }
  if (scenarioObj1.name.toUpperCase() < scenarioObj2.name.toUpperCase()) {
    return -1
  }
  return 0
}
<commit_msg>WMT0095: Adding a function to sort scenario data by ldu, team and offender manager<commit_after>const Scenario = require('../domain/scenario')

module.exports = function (results) {
  var scenarioMap = new Map()
  var scenarioArray = []
  results.forEach(function (result) {
    scenarioMap.set(result['workload_id'], [])
  })
  results.forEach(function (result) {
    var temporaryMappedItems = []
    temporaryMappedItems = scenarioMap.get(result['workload_id'])
    temporaryMappedItems.push(result)
    scenarioMap.set(result['workload_id'], temporaryMappedItems)
  })
  for (var value of scenarioMap.values()) {
    var scenarioObj = new Scenario(value)
    scenarioArray.push(scenarioObj)
  }
  scenarioArray.sort(fieldSorter(['ldu', 'team', 'name']))
  return scenarioArray
}

var sortScenarioArray = function (scenarioObj1, scenarioObj2) {
  if (scenarioObj1.name.toUpperCase() > scenarioObj2.name.toUpperCase()) {
    return 1
  }
  if (scenarioObj1.name.toUpperCase() < scenarioObj2.name.toUpperCase()) {
    return -1
  }
  return 0
}

function fieldSorter(fields) {
  return function (a, b) {
    return fields
      .map(function (o) {
        var dir = 1
        if (o[0] === '-') {
          dir = -1;
          o = o.substring(1)
        }
        if (a[o] > b[o]) return dir
        if (a[o] < b[o]) return -(dir)
        return 0
      })
      .reduce(function firstNonZeroValue (p, n) {
        return p ? p : n
      }, 0)
  }
}
<|endoftext|>