Source: XnatDataMgmt/ImageAssessor.js

import queryString from 'query-string';
import debug from 'debug';
import { APP_NAME, CONTENT_TYPES, RESPONSE_FORMAT } from '../Common/Constant';
import Requestable from '../Common/Requestable';
import { IllegalArgumentsError } from '../Error';
import Resource from './Resource';

const log = debug(`${APP_NAME}:ImageAssessor`);

/**
 * Image Assessor related API
 */
export default class ImageAssessor extends Requestable {
  /**
   * Constructor
   * @param {JsXnat} jsXnat
   */
  constructor(jsXnat) {
    super(jsXnat);
  }

  /**
   * Get All Assessors Associated With An Image Session
   * @param {string} options.projectId Optional. project id
   * @param {string} options.subjectIdOrLabel Optional. subject id or label
   * @param {string} options.experimentId Optional. experiment id
   * @param {string} options.experimentLabel Optional. experiment label
   * @param {string | array} columns Optional. Column names can be either properties of ExperimentXmlPathShortcuts or XML Path Shortcuts: https://wiki.xnat.org/display/XAPI/Experiment+Data+REST+XML+Path+Shortcuts
   * @param {string | number | object} fromDate Optional. from-date condition. can be formatted as MM/DD/YYYY or be timestamp. Timestamp ignores hours, mins....
   * @param {string | number | object} toDate Optional. to-date condition. can be formatted as MM/DD/YYYY or be timestamp. Timestamp ignores hours, mins....
   * @param {object} conditions Optional. restrict the search by any string parameter listed in Experiment Data REST XML Path Shortcuts. e.g., {xsiType: 'xnat:mrSessionData', ID :'XNAT_E0001*'}
   * @param {string} format Optional. Set the format of the response. Format value can be json, html, xml, or csv. If not specified, default is JSON
   * @param {function} cb optional callback function
   */
  getAssessors(
    { projectId, subjectIdOrLabel, experimentId, experimentLabel },
    columns = [],
    fromDate,
    toDate,
    conditions,
    format = RESPONSE_FORMAT.json,
    cb = undefined
  ) {
    if (!RESPONSE_FORMAT[format]) {
      format = RESPONSE_FORMAT.json;
    }
    const date = this.__createDateRange(fromDate, toDate);
    const path = experimentId
      ? `/data/experiments/${experimentId}/assessors`
      : `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentLabel}/assessors`;
    return this._request(
      'GET',
      path,
      {
        columns,
        date,
        format,
        ...conditions,
      },
      cb
    );
  }

  /**
   * Get A Specific Assessor Associated With An Image Session
   * @param {string} options.projectId project id
   * @param {string} options.subjectIdOrLabel subject id or label
   * @param {string} options.experimentId experiment id
   * @param {string} options.experimentLabel experiment label
   * @param {string} options.assessorId assessor id
   * @param {string} options.asessorLabel assessor label
   * @param {string} format Optional. Set the format of the response. json by default
   * @param {function} cb optional callback function
   */
  getAssessor(
    {
      projectId,
      subjectIdOrLabel,
      experimentId,
      experimentLabel,
      assessorId,
      assessorLabel,
    },
    format = RESPONSE_FORMAT.json,
    cb = undefined
  ) {
    if (
      (!assessorId && !assessorLabel) ||
      (!experimentId && !experimentLabel) ||
      (!experimentId && (!projectId || !subjectIdOrLabel))
    ) {
      throw new IllegalArgumentsError(
        `the provided info is not enough to identify an assessor`
      );
    }
    if (!RESPONSE_FORMAT[format]) {
      format = RESPONSE_FORMAT.json;
    }
    let path = undefined;
    if (experimentId !== undefined) {
      path = `/data/experiments/${experimentId}/assessors/${
        assessorId ? assessorId : assessorLabel
      }`;
    } else {
      path = `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${
        experimentId ? experimentId : experimentLabel
      }/assessors/${assessorId ? assessorId : assessorLabel}`;
    }
    return this._request('GET', path, { format }, cb);
  }

  /**
   * Create A New Image Assessor
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentLabel experiment label
   * @param {object} xml assessor xml
   * @param {function} cb optional callback function
   * @returns {string} a simple string of the new assessor ID
   */
  createAssessor(
    projectId,
    subjectIdOrLabel,
    experimentLabel,
    xml,
    cb = undefined
  ) {
    if (!xml) {
      throw new IllegalArgumentsError(`assessor xml is required`);
    }
    return this._request(
      'POST',
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentLabel}/assessors?inbody=true`,
      xml,
      cb,
      CONTENT_TYPES.xml
    );
  }

  /**
   * Modify An Existing Image Assessor
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {object} xml assessor xml
   * @param {function} cb optional callback function
   * @returns {string} a simple string of the updated experiment's accession ID
   */
  updateAssessor(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    xml,
    cb = undefined
  ) {
    if (!xml) {
      throw new IllegalArgumentsError(`assessor xml is required`);
    }
    return this._request(
      'PUT',
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}?inbody=true`,
      xml,
      cb,
      CONTENT_TYPES.xml
    );
  }

  /**
   * Update an experiment represented by XML
   * @param {string} options.projectId project id
   * @param {string} options.subjectId subject id
   * @param {string} options.subjectLabel subject label
   * @param {string} xml project xml representation
   * @param {function} cb optional callback function
   * @returns {string} a simple string of the updated experiment's accession ID
   */
  updateExperimentWithRawXml(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    xml,
    cb = undefined
  ) {
    return this._request(
      'PUT',
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}`,
      xml,
      cb,
      CONTENT_TYPES.xml
    );
  }

  /**
   * Share An Image Assessor Into A New Project
   * @param {string} originalProjectId the ID of the project that owns a given experiment.
   * @param {string} sharedProjectId the ID of the project that you intend an experiment to be shared into
   * @param {string} subjectIdOrLabel subject id or label of the experiment to be shared
   * @param {string} experimentIdOrLabel experiment id or label to be shared
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} newLabel Optional. Specify a new label for this experiment that will be used in the shared project, if desired.
   * @param {string} primary Optional. If set to "true", you are changing the primary ownership of the subject from the original project to the new project.
   * @param {function} cb optional callback function
   * @returns {string} the experiment accession ID as a string
   */
  shareAssessor(
    originalProjectId,
    sharedProjectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    newLabel = undefined,
    primary = false,
    cb = undefined
  ) {
    const options = queryString.stringify({ label: newLabel, primary });
    return this._request(
      'PUT',
      `/data/projects/${originalProjectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}/projects/${sharedProjectId}?${options}`,
      undefined,
      cb
    );
  }

  /**
   * Get A List Of Shared Projects Associated With An Experiment
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label to be shared
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} format Optional. Set the format of the response. Format value can be json, html, xml, or csv. If not specified, default is JSON
   * @param {function} cb optional callback function
   * @returns a list of all projects associated with a subject. The root project ID in the URI can be either a project that owns the subject or a shared project.
   */
  getSharedProjects(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    format = RESPONSE_FORMAT.json,
    cb = undefined
  ) {
    if (!RESPONSE_FORMAT[format]) {
      format = RESPONSE_FORMAT.json;
    }
    return this._request(
      'GET',
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}/projects`,
      {
        format,
      },
      cb
    );
  }

  /**
   * Delete (Or Unshare) An Image Assessor
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label to be shared
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {function} cb optional callback function
   * @returns none
   */
  deleteAssessor(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    cb = undefined
  ) {
    return this._request(
      'DELETE',
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      undefined,
      cb
    );
  }

  /**
   * Delete (Or Unshare) An Image Assessor
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label to be shared
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {function} cb optional callback function
   * @returns none
   */
  unshareExperiment(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    cb = undefined
  ) {
    return this.deleteSubject(
      projectId,
      subjectIdOrLabel,
      experimentIdOrLabel,
      assessorIdOrLabel,
      cb
    );
  }

  /**
   * Get A Listing Of All Resource Collections Stored With An Image Assessor
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {array | string} sortBy Optional. Sort the returned results by one or more parameters in the Result array. Multiple parameters can be provided
   * @param {string} format Optional. Set the format of the response. Format value can be json, html, xml, or csv. If not specified, default is JSON
   * @param {function} cb optional callback function
   */
  getFolders(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    sortBy = [],
    format = RESPONSE_FORMAT.json,
    cb = undefined
  ) {
    return Resource.createResource(this).getFolders(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      sortBy,
      format,
      cb
    );
  }

  /**
   * Get A Listing Of Resource Files Stored With A Project
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {array | string} sortBy Optional. Sort the returned results by one or more parameters in the Result array. Multiple parameters can be provided
   * @param {string} format Optional. Set the format of the response. Format value can be json, html, xml, or csv. If not specified, default is JSON
   * @param {function} cb optional callback function
   */
  getFiles(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    sortBy = [],
    format = RESPONSE_FORMAT.json,
    cb = undefined
  ) {
    return Resource.createResource(this).getFiles(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      sortBy,
      format,
      cb
    );
  }

  /**
   * Get A Listing Of Resource Files Stored With A Project
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} resourceIdOrLabel resource id or label (folder name)
   * @param {string} filename filename
   * @param {function} cb optional callback function
   */
  getFile(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    resourceIdOrLabel,
    filename,
    cb = undefined
  ) {
    return Resource.createResource(this).getFile(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      resourceIdOrLabel,
      filename,
      cb
    );
  }

  /**
   * Create A New Project Resource Folder
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} resourceLabel resource label (folder name)
   * @param {string} format Optional. Specify a string format descriptor for this resource folder.
   * @param {string | array} tags Optional. Specify a comma-separated list of tags for this resource folder.
   * @param {string} contents Optional. Specify a string description of the resource folder's content.
   * @param {function} cb optional callback function
   * @returns none
   */
  createFolder(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    resourceLabel,
    format = undefined,
    tags = [],
    content = undefined,
    cb = undefined
  ) {
    return Resource.createResource(this).createFolder(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      resourceLabel,
      format,
      tags,
      content,
      cb
    );
  }

  /**
   * Upload A New Project Resource File
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} resourceIdOrLabel resource id or label (folder name)
   * @param {string} filename filename
   * @param {Buffer} file file buffer (need to check if this is compatilable from browser)
   * @param {boolean} overwrite Optional. overwrite the file if the file that has the same filename already exists in the same location
   * @param {function} cb optional callback function
   * @returns none
   */
  uploadFile(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    resourceIdOrLabel,
    filename,
    file,
    overwrite = false,
    cb = undefined
  ) {
    return Resource.createResource(this).uploadFile(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      resourceIdOrLabel,
      filename,
      file,
      overwrite,
      cb
    );
  }

  /**
   * Delete A Project Resource Folder
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} resourceIdOrLabel resource id or label (folder name)
   * @param {boolean} safe Optional. if throw errors if files exists in the resource folder
   * @param {function} cb optional callback function
   * @returns none
   */
  deleteFolder(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    resourceIdOrLabel,
    safe = false,
    cb = undefined
  ) {
    return Resource.createResource(this).deleteFolder(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      resourceIdOrLabel,
      safe,
      cb
    );
  }

  /**
   * Delete A Project Resource File
   * @param {string} projectId project id
   * @param {string} subjectIdOrLabel subject id or label
   * @param {string} experimentIdOrLabel experiment id or label
   * @param {string} assessorIdOrLabel assessor id or label
   * @param {string} resourceIdOrLabel resource id or label (folder name)
   * @param {string} filename filename
   * @param {function} cb optional callback function
   * @returns none
   */

  deleteFile(
    projectId,
    subjectIdOrLabel,
    experimentIdOrLabel,
    assessorIdOrLabel,
    resourceIdOrLabel,
    filename,
    cb = undefined
  ) {
    return Resource.createResource(this).deleteFile(
      `/data/projects/${projectId}/subjects/${subjectIdOrLabel}/experiments/${experimentIdOrLabel}/assessors/${assessorIdOrLabel}`,
      resourceIdOrLabel,
      filename,
      cb
    );
  }
  //chany

  /**
   * Get A Listing Of Pipelines In Your Project
   * @param {string} code
   * @param {obj} obj
   * @returns {boolean} returns true if the code exists in the property of the object.
   */
  __isOneOf(code, obj) {
    return Object.values(obj).find((c) => c == parseInt(code)) !== undefined;
  }

  __convertSubjectColumns(columns) {
    if (columns === undefined) {
      return columns;
    }
    if (!Array.isArray(columns)) {
      columns = [columns];
    }
    return columns
      .map((column) =>
        SubjectCommon.SubjectXmlPathShortcuts[column]
          ? SubjectCommon.SubjectXmlPathShortcuts[column]
          : column
      )
      .join(',');
  }

  __convertDate(date) {
    if (!date) {
      return undefined;
    }
    if (typeof date === 'number') {
      const dt = new Date(date);
      let year = dt.getFullYear();
      let month = (1 + dt.getMonth()).toString().padStart(2, '0');
      let day = dt.getDate().toString().padStart(2, '0');
      return month + '/' + day + '/' + year;
    } else if (typeof date === 'object') {
      let year = date.getFullYear();
      let month = (1 + date.getMonth()).toString().padStart(2, '0');
      let day = date.getDate().toString().padStart(2, '0');
      return month + '/' + day + '/' + year;
    } else {
      const dateRegex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/\d{4}$/;
      if (!dateRegex.test(date)) {
        throw new IllegalArgumentsError(`date is invalid: ${date}`);
      }
      return date;
    }
  }

  __createDateRange(fromDate, toDate) {
    const cleansedFromDate = this.__convertDate(fromDate);
    const cleansedToDate = this.__convertDate(toDate);

    if (fromDate && toDate) {
      return `${cleansedFromDate}-${cleansedToDate}`;
    } else if (fromDate) {
      return cleansedFromDate;
    } else if (toDate) {
      return cleansedToDate;
    } else {
      return undefined;
    }
  }
}