implemented paging
This commit is contained in:
		@@ -12,6 +12,24 @@
 | 
			
		||||
        schema:
 | 
			
		||||
          type: string
 | 
			
		||||
        example: all
 | 
			
		||||
      - name: last-id
 | 
			
		||||
        description: last id of current page, if not given the results are displayed from start
 | 
			
		||||
        in: query
 | 
			
		||||
        schema:
 | 
			
		||||
          type: string
 | 
			
		||||
        example: 5ea0450ed851c30a90e70894
 | 
			
		||||
      - name: to-page
 | 
			
		||||
        description: relative change of pages, use negative values to get back, defaults to 0 (if last-id is given, the sample after is the first of the result, so the next page is selected automatically), works only together with page-size
 | 
			
		||||
        in: query
 | 
			
		||||
        schema:
 | 
			
		||||
          type: string
 | 
			
		||||
        example: 1
 | 
			
		||||
      - name: page-size
 | 
			
		||||
        description: number of items per page
 | 
			
		||||
        in: query
 | 
			
		||||
        schema:
 | 
			
		||||
          type: string
 | 
			
		||||
        example: 30
 | 
			
		||||
    responses:
 | 
			
		||||
      200:
 | 
			
		||||
        description: samples overview
 | 
			
		||||
@@ -25,6 +43,8 @@
 | 
			
		||||
        $ref: 'api.yaml#/components/responses/400'
 | 
			
		||||
      401:
 | 
			
		||||
        $ref: 'api.yaml#/components/responses/401'
 | 
			
		||||
      404:
 | 
			
		||||
        $ref: 'api.yaml#/components/responses/404'
 | 
			
		||||
      500:
 | 
			
		||||
        $ref: 'api.yaml#/components/responses/500'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,9 @@ SampleRefs:
 | 
			
		||||
      $ref: 'api.yaml#/components/schemas/Id'
 | 
			
		||||
    user_id:
 | 
			
		||||
      $ref: 'api.yaml#/components/schemas/Id'
 | 
			
		||||
    added:
 | 
			
		||||
      type: string
 | 
			
		||||
      example: 1970-01-01T00:00:00.000Z
 | 
			
		||||
Sample:
 | 
			
		||||
  allOf:
 | 
			
		||||
    - $ref: 'api.yaml#/components/schemas/_Id'
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,485 @@
 | 
			
		||||
const csv = require('csv-parser');
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const axios = require('axios');
 | 
			
		||||
const {Builder} = require('selenium-webdriver');
 | 
			
		||||
const chrome = require('selenium-webdriver/chrome');
 | 
			
		||||
const pdfReader = require('pdfreader');
 | 
			
		||||
const iconv = require('iconv-lite');
 | 
			
		||||
 | 
			
		||||
const metadata = 'C:\\Users\\vle2fe\\Documents\\Data\\Rng_200622\\VZ.csv';  // metadata file
 | 
			
		||||
const nmDocs = 'C:\\Users\\vle2fe\\Documents\\Data\\Rng_200622\\nmDocs';  // NormMaster Documents
 | 
			
		||||
const dptFiles = 'C:\\Users\\vle2fe\\Documents\\Data\\Rng_200622\\DPT';  // Spectrum files
 | 
			
		||||
let data = [];  // metadata contents
 | 
			
		||||
let materials = {};
 | 
			
		||||
let samples = [];
 | 
			
		||||
let normMaster = {};
 | 
			
		||||
 | 
			
		||||
// TODO: integrate measurement device information from DPT names using different users
 | 
			
		||||
// TODO: supplier: other for supplierless samples
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  if (0) {  // materials
 | 
			
		||||
    await getNormMaster();
 | 
			
		||||
    await importCsv();
 | 
			
		||||
    await allMaterials();
 | 
			
		||||
    fs.writeFileSync('./data_import/materials.json', JSON.stringify(materials));
 | 
			
		||||
    await saveMaterials();
 | 
			
		||||
  }
 | 
			
		||||
  else if (0) {  // samples
 | 
			
		||||
    await importCsv();
 | 
			
		||||
    await allSamples();
 | 
			
		||||
    await saveSamples();
 | 
			
		||||
  }
 | 
			
		||||
  else if (0) {  // DPT
 | 
			
		||||
    await allDpts();
 | 
			
		||||
  }
 | 
			
		||||
  else if (1) {  // KF/VZ
 | 
			
		||||
    await importCsv();
 | 
			
		||||
    await allKfVz();
 | 
			
		||||
  }
 | 
			
		||||
  else if (0) {  // pdf test
 | 
			
		||||
    console.log(await readPdf('N28_BN22-O010_2018-03-08.pdf'));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function importCsv() {
 | 
			
		||||
  await new Promise(resolve => {
 | 
			
		||||
    fs.createReadStream(metadata)
 | 
			
		||||
      .pipe(iconv.decodeStream('win1252'))
 | 
			
		||||
      .pipe(csv())
 | 
			
		||||
      .on('data', (row) => {
 | 
			
		||||
        data.push(row);
 | 
			
		||||
      })
 | 
			
		||||
      .on('end', () => {
 | 
			
		||||
        console.info('CSV file successfully processed');
 | 
			
		||||
        resolve();
 | 
			
		||||
      });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function allDpts() {
 | 
			
		||||
  let res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/template/measurements',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const measurement_template = res.data.find(e => e.name === 'spectrum')._id;
 | 
			
		||||
  res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/samples?status=all',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const sampleIds = {};
 | 
			
		||||
  res.data.forEach(sample => {
 | 
			
		||||
    sampleIds[sample.number] = sample._id;
 | 
			
		||||
  });
 | 
			
		||||
  const regex = /.*?_(.*?)_(\d+|\d+_\d+).DPT/;
 | 
			
		||||
  const dpts = fs.readdirSync(dptFiles);
 | 
			
		||||
  for (let i in dpts) {
 | 
			
		||||
    const regexRes = regex.exec(dpts[i])
 | 
			
		||||
    if (regexRes && sampleIds[regexRes[1]]) {  // found matching sample
 | 
			
		||||
      console.log(dpts[i]);
 | 
			
		||||
      const f = fs.readFileSync(dptFiles + '\\' + dpts[i], 'utf-8');
 | 
			
		||||
      const data = {
 | 
			
		||||
        sample_id: sampleIds[regexRes[1]],
 | 
			
		||||
        values: {},
 | 
			
		||||
        measurement_template
 | 
			
		||||
      };
 | 
			
		||||
      data.values.dpt = f.split('\r\n').map(e => e.split(','));
 | 
			
		||||
      await axios({
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: 'http://localhost:3000/measurement/new',
 | 
			
		||||
        auth: {
 | 
			
		||||
          username: 'admin',
 | 
			
		||||
          password: 'Abc123!#'
 | 
			
		||||
        },
 | 
			
		||||
        data
 | 
			
		||||
      }).catch(err => {
 | 
			
		||||
        console.log(dpts[i]);
 | 
			
		||||
        console.error(err.response.data);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function allKfVz() {
 | 
			
		||||
  let res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/template/measurements',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const kf_template = res.data.find(e => e.name === 'kf')._id;
 | 
			
		||||
  const vz_template = res.data.find(e => e.name === 'vz')._id;
 | 
			
		||||
  res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/samples?status=all',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const sampleIds = {};
 | 
			
		||||
  res.data.forEach(sample => {
 | 
			
		||||
    sampleIds[sample.number] = sample._id;
 | 
			
		||||
  });
 | 
			
		||||
  for (let index in data) {
 | 
			
		||||
    console.info(`${index}/${data.length}`);
 | 
			
		||||
    let sample = data[index];
 | 
			
		||||
    if (sample['Sample number'] !== '') {
 | 
			
		||||
      if (sample['KF in Gew%']) {
 | 
			
		||||
        await axios({
 | 
			
		||||
          method: 'post',
 | 
			
		||||
          url: 'http://localhost:3000/measurement/new',
 | 
			
		||||
          auth: {
 | 
			
		||||
            username: 'admin',
 | 
			
		||||
            password: 'Abc123!#'
 | 
			
		||||
          },
 | 
			
		||||
          data: {
 | 
			
		||||
            sample_id: sampleIds[sample['Sample number']],
 | 
			
		||||
            measurement_template: kf_template,
 | 
			
		||||
            values: {
 | 
			
		||||
              'weight %': sample['KF in Gew%'],
 | 
			
		||||
              'standard deviation': sample['Stabwn']
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
          console.log(sample['Sample number']);
 | 
			
		||||
          console.error(err.response.data);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      if (sample['VZ (ml/g)']) {
 | 
			
		||||
        await axios({
 | 
			
		||||
          method: 'post',
 | 
			
		||||
          url: 'http://localhost:3000/measurement/new',
 | 
			
		||||
          auth: {
 | 
			
		||||
            username: 'admin',
 | 
			
		||||
            password: 'Abc123!#'
 | 
			
		||||
          },
 | 
			
		||||
          data: {
 | 
			
		||||
            sample_id: sampleIds[sample['Sample number']],
 | 
			
		||||
            measurement_template: vz_template,
 | 
			
		||||
            values: {
 | 
			
		||||
              vz: sample['VZ (ml/g)']
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
          console.log(sample['Sample number']);
 | 
			
		||||
          console.error(err.response.data);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function allSamples() {
 | 
			
		||||
  let res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/materials?status=all',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const dbMaterials = {}
 | 
			
		||||
  res.data.forEach(m => {
 | 
			
		||||
    dbMaterials[m.name] = m;
 | 
			
		||||
  })
 | 
			
		||||
  res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: 'http://localhost:3000/samples?status=all',
 | 
			
		||||
    auth: {
 | 
			
		||||
      username: 'admin',
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const sampleColors = {};
 | 
			
		||||
  res.data.forEach(sample => {
 | 
			
		||||
    sampleColors[sample.number] = sample.color;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  for (let index in data) {
 | 
			
		||||
    console.info(`${index}/${data.length}`);
 | 
			
		||||
    let sample = data[index];
 | 
			
		||||
    if (sample['Sample number'] !== '' && sample['Supplier'] !== '' && sample['Granulate/Part'] !== '') {  // TODO: wait for decision about samples without suppliers/color/type
 | 
			
		||||
      const material = dbMaterials[trim(sample['Material name'])];
 | 
			
		||||
      if (!material) { // could not find material, skipping sample
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      console.log(sample['Material name']);
 | 
			
		||||
      console.log(material._id);
 | 
			
		||||
      samples.push({
 | 
			
		||||
        number: sample['Sample number'],
 | 
			
		||||
        type: sample['Granulate/Part'],
 | 
			
		||||
        batch: sample['Charge/batch granulate/part'] || '',
 | 
			
		||||
        material_id: material._id,
 | 
			
		||||
        notes: {
 | 
			
		||||
          comment: sample['Comments']
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      const si = samples.length - 1;
 | 
			
		||||
      if (sample['Material number'] !== '' && material.numbers.find(e => e.number === sample['Material number'])) {  // TODO: fix because of false material/material number
 | 
			
		||||
        samples[si].color = material.numbers.find(e => e.number === sample['Material number']).color;
 | 
			
		||||
      }
 | 
			
		||||
      else if (sample['Color'] && sample['Color'] !== '') {
 | 
			
		||||
        samples[si].color = material.numbers.find(e => e.color.indexOf(sample['Color']) >= 0).color;
 | 
			
		||||
      }
 | 
			
		||||
      else if (sampleColors[sample['Sample number'].split('_')[0]]) {  // derive color from main sample for kf/vz
 | 
			
		||||
        samples[si].color = sampleColors[sample['Sample number'].split('_')[0]];
 | 
			
		||||
      }
 | 
			
		||||
      else {  // TODO: no color information at all
 | 
			
		||||
        samples.pop();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveSamples() {
 | 
			
		||||
  for (let i in samples) {
 | 
			
		||||
    console.info(`${i}/${samples.length}`);
 | 
			
		||||
    await axios({
 | 
			
		||||
      method: 'post',
 | 
			
		||||
      url: 'http://localhost:3000/sample/new',
 | 
			
		||||
      auth: {
 | 
			
		||||
        username: 'admin',
 | 
			
		||||
        password: 'Abc123!#'
 | 
			
		||||
      },
 | 
			
		||||
      data: samples[i]
 | 
			
		||||
    }).catch(err => {
 | 
			
		||||
      console.log(samples[i]);
 | 
			
		||||
      console.error(err.response.data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  console.info('saved all samples');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function allMaterials() {
 | 
			
		||||
  for (let index in data) {
 | 
			
		||||
    let sample = data[index];
 | 
			
		||||
    if (sample['Sample number'] !== '' && sample['Supplier'] !== '') {  // TODO: wait for decision about supplierless samples
 | 
			
		||||
      sample['Material name'] = trim(sample['Material name']);
 | 
			
		||||
      if (materials.hasOwnProperty(sample['Material name'])) {  // material already found at least once
 | 
			
		||||
        if (sample['Material number'] !== '') {
 | 
			
		||||
          if (materials[sample['Material name']].numbers.length === 0 || !materials[sample['Material name']].numbers.find(e => e.number === stripSpaces(sample['Material number']))) {  // new material number
 | 
			
		||||
            if (materials[sample['Material name']].numbers.find(e => e.color === sample['Color'] && e.number === '')) {  // color already in list, only number missing
 | 
			
		||||
              materials[sample['Material name']].numbers.find(e => e.color === sample['Color'] && e.number === '').number = stripSpaces(sample['Material number']);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              materials[sample['Material name']].numbers.push({color: sample['Color'], number: stripSpaces(sample['Material number'])});
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        else if (sample['Color'] !== '') {
 | 
			
		||||
          if (!materials[sample['Material name']].numbers.find(e => e.color === stripSpaces(sample['Color']))) {  // new material color
 | 
			
		||||
            materials[sample['Material name']].numbers.push({color: sample['Color'], number: ''});
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else {  // new material
 | 
			
		||||
        console.info(`${index}/${data.length}  ${sample['Material name']}`);
 | 
			
		||||
        materials[sample['Material name']] = {
 | 
			
		||||
          name: sample['Material name'],
 | 
			
		||||
          supplier: sample['Supplier'],
 | 
			
		||||
          group: sample['Material']
 | 
			
		||||
        };
 | 
			
		||||
        let tmp = /M(\d+)/.exec(sample['Reinforcing material']);
 | 
			
		||||
        materials[sample['Material name']].mineral = tmp ? tmp[1] : 0;
 | 
			
		||||
        tmp = /GF(\d+)/.exec(sample['Reinforcing material']);
 | 
			
		||||
        materials[sample['Material name']].glass_fiber = tmp ? tmp[1] : 0;
 | 
			
		||||
        tmp = /CF(\d+)/.exec(sample['Reinforcing material']);
 | 
			
		||||
        materials[sample['Material name']].carbon_fiber = tmp ? tmp[1] : 0;
 | 
			
		||||
        materials[sample['Material name']].numbers = await numbersFetch(sample);
 | 
			
		||||
        console.log(materials[sample['Material name']]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveMaterials() {
 | 
			
		||||
  const mKeys = Object.keys(materials)
 | 
			
		||||
  for (let i in mKeys) {
 | 
			
		||||
    await axios({
 | 
			
		||||
      method: 'post',
 | 
			
		||||
      url: 'http://localhost:3000/material/new',
 | 
			
		||||
      auth: {
 | 
			
		||||
        username: 'admin',
 | 
			
		||||
        password: 'Abc123!#'
 | 
			
		||||
      },
 | 
			
		||||
      data: materials[mKeys[i]]
 | 
			
		||||
    }).catch(err => {
 | 
			
		||||
      console.log(materials[mKeys[i]]);
 | 
			
		||||
      console.error(err.response.data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  console.info('saved all materials');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function numbersFetch(sample) {
 | 
			
		||||
  let nm = [];
 | 
			
		||||
  let res = [];
 | 
			
		||||
  if (sample['Material number']) {  // sample has a material number
 | 
			
		||||
    nm = normMaster[stripSpaces(sample['Material number'])]? [normMaster[stripSpaces(sample['Material number'])]] : [];
 | 
			
		||||
  }
 | 
			
		||||
  else {  // try finding via material name
 | 
			
		||||
    nm = Object.keys(normMaster).filter(e => normMaster[e].nameSpaceless === stripSpaces(sample['Material name'])).map(e => normMaster[e]);
 | 
			
		||||
  }
 | 
			
		||||
  if (nm.length > 0) {
 | 
			
		||||
    for (let i in nm) {
 | 
			
		||||
      // if (!fs.readdirSync(nmDocs).find(e => e.indexOf(nm[i].doc.replace(/ /g, '_')) >= 0)) {  // document not loaded
 | 
			
		||||
      //   await getNormMasterDoc(nm[i].url.replace(/ /g, '%20'));
 | 
			
		||||
      // }
 | 
			
		||||
      // if (!fs.readdirSync(nmDocs).find(e => e.indexOf(nm[i].doc.replace(/ /g, '_')) >= 0)) {  // document not loaded
 | 
			
		||||
      //   console.info('Retrying download...');
 | 
			
		||||
      //   await getNormMasterDoc(nm[i].url.replace(/ /g, '%20'), 2.2);
 | 
			
		||||
      // }
 | 
			
		||||
      // if (!fs.readdirSync(nmDocs).find(e => e.indexOf(nm[i].doc.replace(/ /g, '_')) >= 0)) {  // document not loaded
 | 
			
		||||
      //   console.info('Retrying download again...');
 | 
			
		||||
      //   await getNormMasterDoc(nm[i].url.replace(/ /g, '%20'), 5);
 | 
			
		||||
      // }
 | 
			
		||||
      if (fs.readdirSync(nmDocs).find(e => e.indexOf(nm[i].doc.replace(/ /g, '_')) >= 0)) {  // document loaded
 | 
			
		||||
        res = await readPdf(fs.readdirSync(nmDocs).find(e => e.indexOf(nm[i].doc.replace(/ /g, '_')) >= 0));
 | 
			
		||||
      }
 | 
			
		||||
      if (res.length > 0) {  // no results
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      else if (i + 1 >= nm.length) {
 | 
			
		||||
        console.error('Download failed!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (res.length === 0) {  // no results
 | 
			
		||||
    if (sample['Color'] !== '' || sample['Material number'] !== '') {
 | 
			
		||||
      return [{color: sample['Color'], number: sample['Material number']}];
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return [];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    if (!res.find(e => e.number === sample['Material number'])) {  // sometimes norm master does not include sample number even if listed
 | 
			
		||||
      res.push({color: sample['Color'], number: sample['Material number']});
 | 
			
		||||
    }
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getNormMaster(fetchAgain = false) {
 | 
			
		||||
  if (fetchAgain) {
 | 
			
		||||
    console.info('fetching norm master...');
 | 
			
		||||
    const res = await axios({
 | 
			
		||||
      method: 'get',
 | 
			
		||||
      url: 'http://rb-normen.bosch.com/cgi-bin/searchRBNorm4TradeName'
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    console.info('finding documents...');
 | 
			
		||||
    let match;
 | 
			
		||||
    // const regex = /<tr>.*?<td>.*?<\/span>(.*?)<\/td><td>(\d+)<\/td>.*?<a href="(.*?)"/gm;
 | 
			
		||||
    const regex = /<tr>.*?<td>.*?<\/span>(.*?)<\/td><td>(\d+)<\/td><td>40.*?<a href="(.*?)".*?<\/a>(.*?)<\/td>/gm;  // only valid materials
 | 
			
		||||
    do {
 | 
			
		||||
      match = regex.exec(res.data);
 | 
			
		||||
      if (match) {
 | 
			
		||||
        normMaster[match[2]] = {name: match[1], nameSpaceless: stripSpaces(match[1]), number: match[2], url: match[3], doc: match[4]};
 | 
			
		||||
      }
 | 
			
		||||
    } while (match);
 | 
			
		||||
    fs.writeFileSync('./data_import/normMaster.json', JSON.stringify(normMaster));
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    normMaster = JSON.parse(fs.readFileSync('./data_import/normMaster.json'), 'utf-8');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getNormMasterDoc(url, timing = 1) {
 | 
			
		||||
  console.log(url);
 | 
			
		||||
  return new Promise(async resolve => {
 | 
			
		||||
    const options = new chrome.Options();
 | 
			
		||||
    options.setUserPreferences({
 | 
			
		||||
      "download.default_directory": nmDocs,
 | 
			
		||||
      "download.prompt_for_download": false,
 | 
			
		||||
      "download.directory_upgrade": true,
 | 
			
		||||
      "plugins.always_open_pdf_externally": true
 | 
			
		||||
    });
 | 
			
		||||
    let driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build();
 | 
			
		||||
    let timeout = 7000 * timing;
 | 
			
		||||
    try {
 | 
			
		||||
      await driver.get(url);
 | 
			
		||||
      if (await driver.getCurrentUrl() !== 'https://rb-wam-saml.bosch.com/tfim/sps/normmaster/saml20/login') {  // got document selection page
 | 
			
		||||
        timeout = 11000 * timing;
 | 
			
		||||
        await driver.executeScript('Array.prototype.slice.call(document.querySelectorAll(\'.functionlink\')).filter(e => e.innerText === \'English\')[0].click()').catch(() => {timeout = 0; });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    finally {
 | 
			
		||||
      setTimeout(async () => {  // wait until download is finished
 | 
			
		||||
        await driver.quit();
 | 
			
		||||
        resolve();
 | 
			
		||||
      }, timeout);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function readPdf(file) {
 | 
			
		||||
  return new Promise(async resolve => {
 | 
			
		||||
    const countdown = 100;  // value for text timeout
 | 
			
		||||
    let table = 0;      // > 0 when in correct table area
 | 
			
		||||
    let rows = [];      // found table rows
 | 
			
		||||
    let lastY = 0;      // y of last row
 | 
			
		||||
    let lastX = 0;      // right x of last item
 | 
			
		||||
    let lastText = '';  // text of last item
 | 
			
		||||
    let lastLastText = '';  // text of last last item
 | 
			
		||||
    await new pdfReader.PdfReader().parseFileItems(nmDocs + '\\' + file, (err, item) => {
 | 
			
		||||
      if (item && item.text) {
 | 
			
		||||
        if ((stripSpaces(lastLastText + lastText + item.text).toLowerCase().indexOf('colordesignationsupplier') >= 0) || (stripSpaces(lastLastText + lastText + item.text).toLowerCase().indexOf('colordesignatiomsupplier') >= 0)) {  // table area starts
 | 
			
		||||
          table = countdown;
 | 
			
		||||
        }
 | 
			
		||||
        if (table > 0) {
 | 
			
		||||
          // console.log(item);
 | 
			
		||||
          // console.log(item.y - lastY);
 | 
			
		||||
          // console.log(item.text);
 | 
			
		||||
          if (item.y - lastY > 0.8 && Math.abs(item.x - lastX) > 5) {  // new row
 | 
			
		||||
            lastY = item.y;
 | 
			
		||||
            rows.push(item.text);
 | 
			
		||||
          }
 | 
			
		||||
          else {  // still the same row row
 | 
			
		||||
            rows[rows.length - 1] += (item.x - lastX > 1.1 ? '$' : '') + item.text;  // push to row, detect if still same cell
 | 
			
		||||
          }
 | 
			
		||||
          lastX = (item.w * 0.055) + item.x;
 | 
			
		||||
 | 
			
		||||
          if (/\d \d\d\d \d\d\d \d\d\d/.test(item.text)) {
 | 
			
		||||
            table = countdown;
 | 
			
		||||
          }
 | 
			
		||||
          table --;
 | 
			
		||||
          if (table <= 0 || item.text.toLowerCase().indexOf('release document') >= 0 || item.text.toLowerCase().indexOf('normative references') >= 0) {  // table area ended
 | 
			
		||||
            table = -1;
 | 
			
		||||
            // console.log(rows);
 | 
			
		||||
            rows = rows.filter(e => /^\d{10}/m.test(stripSpaces(e)));  // filter non-table rows
 | 
			
		||||
            resolve(rows.map(e => {return {color: e.split('$')[3], number: stripSpaces(e.split('$')[0])}; }));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        lastLastText = lastText;
 | 
			
		||||
        lastText = item.text;
 | 
			
		||||
      }
 | 
			
		||||
      if (!item && table !== -1) {  // document ended
 | 
			
		||||
        rows = rows.filter(e => /^\d{10}/m.test(stripSpaces(e)));  // filter non-table rows
 | 
			
		||||
        resolve(rows.map(e => {return {color: e.split('$')[3], number: stripSpaces(e.split('$')[0])}; }));
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function stripSpaces(s) {
 | 
			
		||||
  return s ? s.replace(/ /g,'') : '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function trim(s) {
 | 
			
		||||
  return s.replace(/(^\s+|\s+$)/gm, '');
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										505
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										505
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,7 +11,8 @@
 | 
			
		||||
    "start": "node index.js",
 | 
			
		||||
    "dev": "nodemon -e ts,yaml --exec \"tsc && node dist/index.js || exit 1\"",
 | 
			
		||||
    "loadDev": "node dist/test/loadDev.js",
 | 
			
		||||
    "coverage": "tsc && nyc --reporter=html --reporter=text mocha dist/**/**.spec.js --timeout 5000"
 | 
			
		||||
    "coverage": "tsc && nyc --reporter=html --reporter=text mocha dist/**/**.spec.js --timeout 5000",
 | 
			
		||||
    "import": "node data_import/import.js"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [],
 | 
			
		||||
  "author": "",
 | 
			
		||||
@@ -45,9 +46,13 @@
 | 
			
		||||
    "@types/node": "^13.1.6",
 | 
			
		||||
    "@types/qs": "^6.9.1",
 | 
			
		||||
    "@types/serve-static": "^1.13.3",
 | 
			
		||||
    "csv-parser": "^2.3.3",
 | 
			
		||||
    "iconv-lite": "^0.6.0",
 | 
			
		||||
    "mocha": "^7.1.2",
 | 
			
		||||
    "nodemon": "^2.0.3",
 | 
			
		||||
    "nyc": "^15.0.1",
 | 
			
		||||
    "pdfreader": "^1.0.7",
 | 
			
		||||
    "selenium-webdriver": "^4.0.0-alpha.7",
 | 
			
		||||
    "should": "^13.2.3",
 | 
			
		||||
    "supertest": "^4.0.2",
 | 
			
		||||
    "tslint": "^5.20.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import mongoose from 'mongoose';
 | 
			
		||||
// TODO: generate output for ML in format DPT -> data, implement filtering, field selection
 | 
			
		||||
// TODO: generate csv
 | 
			
		||||
// TODO: write script for data import
 | 
			
		||||
// TODO: allowed types: tension rod, part, granulate, other
 | 
			
		||||
 | 
			
		||||
describe('/sample', () => {
 | 
			
		||||
  let server;
 | 
			
		||||
@@ -18,6 +19,7 @@ describe('/sample', () => {
 | 
			
		||||
  afterEach(done => TestHelper.afterEach(server, done));
 | 
			
		||||
  after(done => TestHelper.after(done));
 | 
			
		||||
 | 
			
		||||
  // TODO: sort, added date filter, has measurements/condition filter
 | 
			
		||||
  describe('GET /samples', () => {
 | 
			
		||||
    it('returns all samples', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
@@ -30,7 +32,7 @@ describe('/sample', () => {
 | 
			
		||||
        const json = require('../test/db.json');
 | 
			
		||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length);
 | 
			
		||||
        should(res.body).matchEach(sample => {
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
          should(sample).have.property('_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('number').be.type('string');
 | 
			
		||||
          should(sample).have.property('type').be.type('string');
 | 
			
		||||
@@ -41,6 +43,7 @@ describe('/sample', () => {
 | 
			
		||||
          should(sample).have.property('material_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('note_id');
 | 
			
		||||
          should(sample).have.property('user_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('added').be.type('string');
 | 
			
		||||
        });
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
@@ -56,7 +59,7 @@ describe('/sample', () => {
 | 
			
		||||
        const json = require('../test/db.json');
 | 
			
		||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length);
 | 
			
		||||
        should(res.body).matchEach(sample => {
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
          should(sample).have.property('_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('number').be.type('string');
 | 
			
		||||
          should(sample).have.property('type').be.type('string');
 | 
			
		||||
@@ -67,6 +70,7 @@ describe('/sample', () => {
 | 
			
		||||
          should(sample).have.property('material_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('note_id');
 | 
			
		||||
          should(sample).have.property('user_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('added').be.type('string');
 | 
			
		||||
        });
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
@@ -82,7 +86,7 @@ describe('/sample', () => {
 | 
			
		||||
        const json = require('../test/db.json');
 | 
			
		||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.new).length);
 | 
			
		||||
        should(res.body).matchEach(sample => {
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
          should(sample).have.property('_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('number').be.type('string');
 | 
			
		||||
          should(sample).have.property('type').be.type('string');
 | 
			
		||||
@@ -92,10 +96,114 @@ describe('/sample', () => {
 | 
			
		||||
          should(sample).have.property('material_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('note_id');
 | 
			
		||||
          should(sample).have.property('user_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('added').be.type('string');
 | 
			
		||||
        });
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('uses the given page size', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&page-size=3',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).have.lengthOf(3);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns results starting after last-id', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&last-id=400000000000000000000002',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body[0]).have.property('_id', '400000000000000000000003');
 | 
			
		||||
        should(res.body[1]).have.property('_id', '400000000000000000000004');
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns the right page number', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&to-page=2&page-size=2',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body[0]).have.property('_id', '400000000000000000000006');
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('works with negative page numbers', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&to-page=-1&page-size=2&last-id=400000000000000000000004',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body[0]).have.property('_id', '400000000000000000000001');
 | 
			
		||||
        should(res.body[1]).have.property('_id', '400000000000000000000002');
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns an empty array for a page number out of range', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&to-page=100&page-size=2',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).have.lengthOf(0);
 | 
			
		||||
        should(res.body).be.eql([]);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns an empty array for a page number out of negative range', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?status=all&to-page=-100&page-size=3&last-id=400000000000000000000004',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).have.lengthOf(0);
 | 
			
		||||
        should(res.body).be.eql([]);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a negative page size', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?page-size=-3',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"page-size" must be larger than or equal to 1'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an invalid last-id', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?last-id=40000000000h000000000002',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"last-id" with value "40000000000h000000000002" fails to match the required pattern: /[0-9a-f]{24}/'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a to-page without page-size', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: '/samples?to-page=3',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"to-page" missing required peer "page-size"'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an invalid state name', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'get',
 | 
			
		||||
@@ -127,7 +235,7 @@ describe('/sample', () => {
 | 
			
		||||
        let asyncCounter = res.body.length;
 | 
			
		||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.new).length);
 | 
			
		||||
        should(res.body).matchEach(sample => {
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
          should(sample).have.property('_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('number').be.type('string');
 | 
			
		||||
          should(sample).have.property('type').be.type('string');
 | 
			
		||||
@@ -140,6 +248,7 @@ describe('/sample', () => {
 | 
			
		||||
          should(sample).have.property('material_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('note_id');
 | 
			
		||||
          should(sample).have.property('user_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('added').be.type('string');
 | 
			
		||||
          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
			
		||||
            should(data).have.property('status',globals.status.new);
 | 
			
		||||
            if (--asyncCounter === 0) {
 | 
			
		||||
@@ -161,7 +270,7 @@ describe('/sample', () => {
 | 
			
		||||
        let asyncCounter = res.body.length;
 | 
			
		||||
        should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === -1).length);
 | 
			
		||||
        should(res.body).matchEach(sample => {
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
          should(sample).have.property('_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('number').be.type('string');
 | 
			
		||||
          should(sample).have.property('type').be.type('string');
 | 
			
		||||
@@ -174,6 +283,7 @@ describe('/sample', () => {
 | 
			
		||||
          should(sample).have.property('material_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('note_id');
 | 
			
		||||
          should(sample).have.property('user_id').be.type('string');
 | 
			
		||||
          should(sample).have.property('added').be.type('string');
 | 
			
		||||
          SampleModel.findById(sample._id).lean().exec((err, data) => {
 | 
			
		||||
            should(data).have.property('status',globals.status.deleted);
 | 
			
		||||
            if (--asyncCounter === 0) {
 | 
			
		||||
@@ -269,7 +379,7 @@ describe('/sample', () => {
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('PUT /sample/{id}', () => {
 | 
			
		||||
  describe('PUT /sample/{id}', () => {  // TODO: fix tests, work on /samples
 | 
			
		||||
    it('returns the right sample', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
@@ -277,7 +387,7 @@ describe('/sample', () => {
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {},
 | 
			
		||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
			
		||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('keeps unchanged properties', done => {
 | 
			
		||||
@@ -289,7 +399,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', notes: {}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'});
 | 
			
		||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
			
		||||
          if (err) return done (err);
 | 
			
		||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
			
		||||
@@ -316,7 +426,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {type: 'granulate'}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'});
 | 
			
		||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
			
		||||
          if (err) return done (err);
 | 
			
		||||
          should(data).have.property('status',globals.status.validated);
 | 
			
		||||
@@ -333,7 +443,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'});
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'});
 | 
			
		||||
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
 | 
			
		||||
          if (err) return done (err);
 | 
			
		||||
          should(data).have.property('status',globals.status.validated);
 | 
			
		||||
@@ -350,7 +460,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {notes: {comment: 'Stoff gesperrt', sample_references: []}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002'});
 | 
			
		||||
        should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'});
 | 
			
		||||
        SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => {
 | 
			
		||||
          if (err) return done (err);
 | 
			
		||||
          should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v');
 | 
			
		||||
@@ -627,7 +737,7 @@ describe('/sample', () => {
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {condition: {}},
 | 
			
		||||
        res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
			
		||||
        res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an old version of a condition template', done => {
 | 
			
		||||
@@ -647,7 +757,7 @@ describe('/sample', () => {
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
 | 
			
		||||
        res: {_id: '400000000000000000000004', number: '32', type: 'granulate', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003'}
 | 
			
		||||
        res: {_id: '400000000000000000000004', number: '32', type: 'granulate', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003', added: '2004-01-10T13:37:04.000Z'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an changing back to an empty condition', done => {
 | 
			
		||||
@@ -694,7 +804,7 @@ describe('/sample', () => {
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {},
 | 
			
		||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
			
		||||
        res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects requests from a read user', done => {
 | 
			
		||||
@@ -1085,7 +1195,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done (err);
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
        should(res.body).have.property('_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('number', 'Rng37');
 | 
			
		||||
        should(res.body).have.property('color', 'black');
 | 
			
		||||
@@ -1095,6 +1205,8 @@ describe('/sample', () => {
 | 
			
		||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
			
		||||
        should(res.body).have.property('note_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('user_id', '000000000000000000000002');
 | 
			
		||||
        should(res.body).have.property('added').be.type('string');
 | 
			
		||||
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
@@ -1198,7 +1310,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done (err);
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
        should(res.body).have.property('_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('number', 'Fe1');
 | 
			
		||||
        should(res.body).have.property('color', 'black');
 | 
			
		||||
@@ -1207,6 +1319,8 @@ describe('/sample', () => {
 | 
			
		||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
			
		||||
        should(res.body).have.property('note_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('user_id', '000000000000000000000004');
 | 
			
		||||
        should(res.body).have.property('added').be.type('string');
 | 
			
		||||
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1500);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
@@ -1219,7 +1333,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done (err);
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
        should(res.body).have.property('_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('number', 'Rng37');
 | 
			
		||||
        should(res.body).have.property('color', 'black');
 | 
			
		||||
@@ -1229,6 +1343,8 @@ describe('/sample', () => {
 | 
			
		||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
			
		||||
        should(res.body).have.property('note_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('user_id', '000000000000000000000002');
 | 
			
		||||
        should(res.body).have.property('added').be.type('string');
 | 
			
		||||
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
@@ -1271,7 +1387,7 @@ describe('/sample', () => {
 | 
			
		||||
        req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done (err);
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
 | 
			
		||||
        should(res.body).have.property('_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('number', 'Rng34');
 | 
			
		||||
        should(res.body).have.property('color', 'black');
 | 
			
		||||
@@ -1281,6 +1397,8 @@ describe('/sample', () => {
 | 
			
		||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
			
		||||
        should(res.body).have.property('note_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('user_id', '000000000000000000000003');
 | 
			
		||||
        should(res.body).have.property('added').be.type('string');
 | 
			
		||||
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -25,22 +25,43 @@ router.get('/samples', (req, res, next) => {
 | 
			
		||||
  const {error, value: filters} = SampleValidate.query(req.query);
 | 
			
		||||
  if (error) return res400(error, res);
 | 
			
		||||
 | 
			
		||||
  let conditions;
 | 
			
		||||
 | 
			
		||||
  let status;
 | 
			
		||||
  if (filters.hasOwnProperty('status')) {
 | 
			
		||||
    if(filters.status === 'all') {
 | 
			
		||||
      conditions = {$or: [{status: globals.status.validated}, {status: globals.status.new}]}
 | 
			
		||||
      status = {$or: [{status: globals.status.validated}, {status: globals.status.new}]}
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      conditions = {status: globals.status[filters.status]};
 | 
			
		||||
      status = {status: globals.status[filters.status]};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {  // default
 | 
			
		||||
    conditions = {status: globals.status.validated};
 | 
			
		||||
    status = {status: globals.status.validated};
 | 
			
		||||
  }
 | 
			
		||||
  const query = SampleModel.find(status);
 | 
			
		||||
 | 
			
		||||
  if (filters['page-size']) {
 | 
			
		||||
    query.limit(filters['page-size']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  SampleModel.find(conditions).lean().exec((err, data) => {
 | 
			
		||||
  if (filters['last-id']) {
 | 
			
		||||
    if (filters['to-page'] && filters['to-page'] < 0) {
 | 
			
		||||
      query.lte('_id', mongoose.Types.ObjectId(filters['last-id']));  // TODO: consider sorting
 | 
			
		||||
      query.sort({_id: -1});
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      query.gt('_id', mongoose.Types.ObjectId(filters['last-id']));  // TODO: consider sorting
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (filters['to-page']) {
 | 
			
		||||
    query.skip(Math.abs(filters['to-page']) * filters['page-size']);  // TODO: check order for negative numbers
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  query.lean().exec((err, data) => {
 | 
			
		||||
    if (err) return next(err);
 | 
			
		||||
    if (filters['to-page'] && filters['to-page'] < 0) {
 | 
			
		||||
      data.reverse();
 | 
			
		||||
    }
 | 
			
		||||
    res.json(_.compact(data.map(e => SampleValidate.output(e))));  // validate all and filter null values from validation errors
 | 
			
		||||
  })
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,11 @@ export default class SampleValidate {
 | 
			
		||||
            Joi.date()
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
    })
 | 
			
		||||
    }),
 | 
			
		||||
 | 
			
		||||
    added: Joi.date()
 | 
			
		||||
      .iso()
 | 
			
		||||
      .min('1970-01-01T00:00:00.000Z')
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
			
		||||
@@ -85,6 +89,7 @@ export default class SampleValidate {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static output (data, param = 'refs') {  // validate output and strip unwanted properties, returns null if not valid
 | 
			
		||||
    data.added = data._id.getTimestamp();
 | 
			
		||||
    data = IdValidate.stringify(data);
 | 
			
		||||
    let joiObject;
 | 
			
		||||
    if (param === 'refs') {
 | 
			
		||||
@@ -97,7 +102,8 @@ export default class SampleValidate {
 | 
			
		||||
        condition: this.sample.condition,
 | 
			
		||||
        material_id: IdValidate.get(),
 | 
			
		||||
        note_id: IdValidate.get().allow(null),
 | 
			
		||||
        user_id: IdValidate.get()
 | 
			
		||||
        user_id: IdValidate.get(),
 | 
			
		||||
        added: this.sample.added
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    else if(param === 'details') {
 | 
			
		||||
@@ -123,7 +129,10 @@ export default class SampleValidate {
 | 
			
		||||
 | 
			
		||||
  static query (data) {
 | 
			
		||||
    return Joi.object({
 | 
			
		||||
      status: Joi.string().valid('validated', 'new', 'all')
 | 
			
		||||
    }).validate(data);
 | 
			
		||||
      status: Joi.string().valid('validated', 'new', 'all'),
 | 
			
		||||
      'last-id': IdValidate.get(),
 | 
			
		||||
      'to-page': Joi.number().integer(),
 | 
			
		||||
      'page-size': Joi.number().integer().min(1)
 | 
			
		||||
    }).with('to-page', 'page-size').validate(data);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user