Advertisement
Guest User

Untitled

a guest
Jul 18th, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.38 KB | None | 0 0
  1. let username = '',
  2. password = '';
  3.  
  4. let archiver = require('archiver'),
  5. btoa = require('btoa'),
  6. fs = require('fs'),
  7. os = require('os'),
  8. handlebars = require('handlebars'),
  9. request = require('request'),
  10. _ = require('lodash'),
  11. moment = require('moment'),
  12. wkhtmltopdf = require('wkhtmltopdf'),
  13. rimraf = require('rimraf'),
  14. clientProperties,
  15. serverProperties;
  16.  
  17. moment.locale('ru');
  18.  
  19. handlebars.registerHelper('format', (date, format) => moment(date).format(format));
  20.  
  21. handlebars.registerHelper('condition', (condition, v1, v2) => condition ? v1 : v2);
  22.  
  23. handlebars.registerHelper('shift', (employee, date, model, isImpersonal, options) => {
  24. let shift;
  25. if (isImpersonal) {
  26. shift = _.find(model.shifts, e => {
  27. return e.positionIndex === employee.positionIndex && moment(e.startDate).isSame(date, 'day');
  28. });
  29. } else {
  30. shift = _.find(model.shifts, e => {
  31. return e.employeePositionId === employee.id && moment(e.startDate).isSame(date, 'day');
  32. });
  33. }
  34. if(shift) {
  35. this.shift = shift;
  36. return options.fn(this);
  37. }
  38. });
  39.  
  40. handlebars.registerHelper('comment', (date, comments) => {
  41. let comment = _.find(comments, e => moment(e.date).isSame(date, 'day'));
  42. if(comment) {
  43. return comment.text;
  44. }
  45. });
  46.  
  47. handlebars.registerHelper('shifts', (employee, date, type, model, isImpersonal, options) => {
  48. let typesArray, prop = isImpersonal ? 'positionIndex' : 'employeePositionId';
  49. switch (type) {
  50. case 'OPEN':
  51. typesArray = ['OPEN', 'OUTSIDE_OPEN', 'FULL_DAY', 'FULL_DAY_OUTSIDE'];
  52. break;
  53. case 'CLOSE':
  54. typesArray = ['CLOSE', 'OUTSIDE_CLOSE', 'FULL_DAY', 'FULL_DAY_OUTSIDE'];
  55. break;
  56. }
  57. this.shifts = _.filter(model.shifts, e => {
  58. return ((employee && _.isNumber(employee[isImpersonal ? prop : 'id'])) ? e[prop] === employee[isImpersonal ? prop : 'id'] : true) &&
  59. (typesArray ? _.some(typesArray, type => {return type === e.type;}) : true) &&
  60. (date ? moment(e.startDate).isSame(date, 'day') : true);
  61. });
  62. return options.fn(this);
  63. });
  64.  
  65. handlebars.registerHelper('scheduleRequest', (employeeId, date, scheduleRequests, options) => {
  66. let scheduleRequest = _.find(scheduleRequests, e => {
  67. return (e.employeeId === employeeId && e.status === 'APPROVED' && e.type !== 'PARTIAL_ABSENCE' && e.type !== 'SHIFT' &&
  68. moment(date).isBetween(e.startDate, e.endDate, 'day', '[]'));
  69. });
  70. if(scheduleRequest) {
  71. this.scheduleRequest = scheduleRequest;
  72. return options.fn(this);
  73. }
  74. });
  75.  
  76. handlebars.registerHelper('shiftClass', type => {
  77. switch (type) {
  78. case 'OPEN':
  79. return 'mdl-color--green-300';
  80. case 'MIDDLE':
  81. return 'mdl-color--amber-200';
  82. case 'CLOSE':
  83. return 'mdl-color--teal-200';
  84. case 'FULL_DAY':
  85. return 'mdl-color--purple-200';
  86. case 'FULL_DAY_OUTSIDE':
  87. return 'mdl-color--purple-400';
  88. case 'OUTSIDE_OPEN':
  89. return 'mdl-color--green-500';
  90. case 'OUTSIDE_CLOSE':
  91. return 'mdl-color--teal-400';
  92. case 'OUTSIDE':
  93. return 'mdl-color--blue-grey-400';
  94. }
  95. });
  96.  
  97. handlebars.registerHelper('daysOff', (shifts, monthArray) => _.size(monthArray) - _.size(shifts));
  98.  
  99. handlebars.registerHelper('size', array => _.size(array));
  100.  
  101. handlebars.registerHelper('workload', shifts => {
  102. let sum = 0;
  103. _.forEach(shifts, shift => {
  104. sum += (hoursBetween(shift.startDate, shift.endDate) - 1);
  105. });
  106. return sum;
  107. });
  108.  
  109. handlebars.registerHelper('timeRange', (startDate, endDate, lunch) => {
  110. let date = moment().startOf('day').add(endDate - startDate - lunch * 60000);
  111. return date.format(date.minutes() > 0 ? 'H:mm' : 'H');
  112. });
  113.  
  114. function link(template, valuesObject = {}) {
  115. let link;
  116.  
  117. if (!template) {
  118. return '';
  119. }
  120.  
  121. link = template
  122. .replace(/{\?(.)+}/g, '')
  123. .replace(/{([A-Za-z0-9])+}/g, str => {
  124. return valuesObject[str.slice(1, str.length - 1)]
  125. });
  126.  
  127. return link;
  128. }
  129.  
  130. function getMonthArray(date = new Date) {
  131. let array = [];
  132.  
  133. for (let c = 1, day; c <= moment(date).daysInMonth(); c++) {
  134. day = moment(date).date(c).startOf('day').toDate();
  135. array.push({
  136. date : day,
  137. active: (day.getDay() !== 0 && day.getDay() !== 6)
  138. });
  139. }
  140.  
  141. return array;
  142. }
  143.  
  144. function hoursBetween(startDate, endDate) {
  145. return Math.round( (moment(endDate).valueOf() - moment(startDate).valueOf()) / (1000 * 60 * 60 ) );
  146. }
  147.  
  148. function tryParseJSON (jsonString){
  149. try {
  150. return JSON.parse(jsonString);
  151. }
  152. catch (e) {
  153. console.error("Not a valid JSON")
  154. }
  155. return false;
  156. }
  157.  
  158. // params:
  159. // org-unit-ids
  160. // position-type-ids
  161. // date
  162. // zip
  163. // locale
  164. // impersonal
  165. // chief-id
  166.  
  167. exports.render = (req, res, next, properties) => {
  168. req.setTimeout(6*60*60*1000);
  169.  
  170. let orgUnitIds = _.uniq(_.split(_.get(req, 'query[\'org-unit-ids\']'), ',')),
  171. positionTypeIds = _.uniq(_.split(_.get(req, 'query[\'position-type-ids\']'), ',')),
  172. date = moment(_.get(req, 'query.date')),
  173. isZip = _.get(req, 'query.zip') === 'true',
  174. showMinutes = _.get(req, 'query.minutes') === 'true',
  175. isImpersonal = _.get(req, 'query.impersonal') === 'true',
  176. from = date.clone().startOf('month').format('YYYY-MM-DD'),
  177. to = date.clone().endOf('month').format('YYYY-MM-DD'),
  178. locale = _.get(req, 'query.locale'),
  179. chiefId = _.get(req, 'query.chief-id'),
  180. orgUnits = [],
  181. isLocalhost = req.headers.host.split(':')[0] === 'localhost',
  182. jar = request.jar(),
  183. isClose = false,
  184. scheduleRequests,
  185. scheduleRequestTypes;
  186.  
  187. if (!orgUnitIds || !date) {
  188. res.send('ERROR');
  189. return;
  190. }
  191.  
  192. clientProperties = require(properties.client);
  193. serverProperties = require(properties.server);
  194. locale && moment.locale(locale);
  195.  
  196. !isLocalhost && jar.setCookie(request.cookie(req.headers.cookie), clientProperties.api.host);
  197.  
  198. request.get({
  199. url : clientProperties.api.host + ':' + clientProperties.api.port + clientProperties.api.path + clientProperties.api.version,
  200. headers: isLocalhost ? {'Authorization': 'Basic ' + btoa(username + ':' + password)} : null,
  201. jar : isLocalhost ? null : jar
  202. }, function (error, response, body) {
  203. if (error) {
  204. console.log(error);
  205. res.send('Can not be authenticated');
  206. return;
  207. }
  208.  
  209. let data = tryParseJSON(body);
  210.  
  211. if (response.statusCode === 401) {
  212. res.send(_.get(data, '[0].message'));
  213. return;
  214. }
  215. if (response.statusCode !== 200) {
  216. res.send('Unacceptable status code');
  217. return;
  218. }
  219.  
  220. let links = _.get(data, '_links'),
  221. length = orgUnitIds.length;
  222.  
  223. if (!links) {
  224. res.send('Can not get links');
  225. return;
  226. }
  227.  
  228. isLocalhost && jar.setCookie(request.cookie(_.get(response.headers, 'set-cookie[0]')), clientProperties.api.host);
  229.  
  230. req.on('close', () => {
  231. isClose = true;
  232. });
  233.  
  234. function renderPdf() {
  235. switch (os.type()) {
  236. case 'Linux':
  237. wkhtmltopdf.command = serverProperties.binPath + '/wkhtmltopdf/linux/wkhtmltopdf';
  238. break;
  239.  
  240. case 'Darwin':
  241. wkhtmltopdf.command = serverProperties.binPath + '/wkhtmltopdf/macosx/wkhtmltopdf';
  242. break;
  243.  
  244. case 'Windows_NT':
  245. wkhtmltopdf.command = serverProperties.binPath + '/wkhtmltopdf/windows/wkhtmltopdf.exe';
  246. break;
  247. }
  248.  
  249. _.forEach(orgUnits, orgUnit => {
  250. orgUnit.pages = _.chunk(orgUnit[isImpersonal ? 'positionTypes' : 'employeePositions'], orgUnit.comments ? 9 : 12);
  251. });
  252.  
  253. let orgUnit_ = [];
  254. _.forEach(orgUnits, orgUnit => {
  255. _.size(orgUnit.employeePositions) > 0 && orgUnit_.push(orgUnit);
  256. });
  257. orgUnits = orgUnit_;
  258.  
  259. let template = fs.readFileSync(__dirname + '/template.html', 'utf8'),
  260. options = {
  261. orientation: 'landscape',
  262. 'B' : 0,
  263. 'L' : 0,
  264. 'R' : 0,
  265. 'T' : 0,
  266. zoom : serverProperties.zoomFactor
  267. },
  268. // zoom: os.type() === 'Linux' ? 0.5 : 0.75
  269. scope = {
  270. monthArray : getMonthArray(date),
  271. date : _.upperFirst(date.format('MMMM YYYY')),
  272. scheduleRequests: scheduleRequests,
  273. isImpersonal : isImpersonal,
  274. base : clientProperties.client.host + ':' + clientProperties.client.port,
  275. };
  276.  
  277. if (isZip) {
  278. res.setHeader('content-type', 'application/zip');
  279. let archive = archiver('zip', {
  280. zlib: {level: 0}
  281. });
  282.  
  283. let dirName = serverProperties.tmpPath + '/' +_.random(1e10, 1e15);
  284. fs.mkdirSync(dirName);
  285.  
  286. let limit_ = serverProperties.processesLimit;
  287.  
  288. function a(i) {
  289. if (orgUnits.length !== i + 1) return;
  290. // console.log('a')
  291. _.forEach(orgUnits, (orgUnit, i) => {
  292. archive.file(dirName + '/' + i + '.pdf', {name: orgUnit.orgUnit.name + '.pdf'});
  293. });
  294. archive.finalize();
  295. archive.pipe(res);
  296. archive.on('end', () => {
  297. rimraf(dirName, () => {});
  298. });
  299. }
  300.  
  301. function f(i) {
  302. if (isClose) {
  303. setTimeout(() => rimraf(dirName, () => {}), 10000);
  304. return;
  305. }
  306. // console.log('f ' + i)
  307. if (orgUnits.length >= i + 1) {
  308. scope.orgUnits = [orgUnits[i]];
  309. options.output = dirName + '/' + i + '.pdf';
  310. if (--limit_ === 0) {
  311. wkhtmltopdf(handlebars.compile(template)(scope), _.clone(options), () => {
  312. f(i + 1);
  313. a(i)
  314. });
  315. limit_ = serverProperties.processesLimit;
  316. } else {
  317. wkhtmltopdf(handlebars.compile(template)(scope), _.clone(options), () => a(i));
  318. f(i + 1);
  319. }
  320. }
  321. }
  322.  
  323. f(0);
  324.  
  325. } else {
  326. res.setHeader('content-type', 'application/pdf');
  327. scope.orgUnits = orgUnits;
  328. let fileName = serverProperties.tmpPath + '/' + _.random(1e10, 1e15) + '.pdf';
  329. options.output = fileName;
  330. wkhtmltopdf(handlebars.compile(template)(scope), options, () => {
  331. fs.createReadStream(fileName).pipe(res);
  332. setTimeout(() => fs.unlinkSync(fileName), 100);
  333. });
  334. }
  335. }
  336.  
  337. function after() {
  338. if (isClose) return;
  339.  
  340. _.forEach(orgUnitIds, orgUnitId => {
  341. let model = {
  342. orgUnit : null,
  343. employeePositions : null,
  344. positionTypes : null,
  345. chief : null,
  346. comments : null,
  347. shifts : null,
  348. roster : null,
  349. pages : null,
  350. productionCalendarInfo: null,
  351. isScheduleTemplateUsed: null
  352. };
  353. request.get({
  354. url: _.get(links, 'orgUnits.href') + '/' + orgUnitId,
  355. jar: jar
  356. }, (error, response, orgUnit) => {
  357. if (error || response.statusCode !== 200) {
  358. --length === 0 && renderPdf();
  359. return;
  360. }
  361. model.orgUnit = tryParseJSON(orgUnit);
  362.  
  363. let resources = 0;
  364.  
  365. function after() {
  366. if (isClose) return;
  367. // console.log('ou ' + (length - 1))
  368. orgUnits.push(model);
  369. --length === 0 && renderPdf();
  370. }
  371.  
  372. !isImpersonal && (chiefId ? true : _.get(model.orgUnit, '_links.chief-position.href')) && ++resources &&
  373. request.get({
  374. url: chiefId ? (_.get(links, 'positions.href') + '/' + chiefId) : _.get(model.orgUnit, '_links.chief-position.href'),
  375. jar: jar
  376. }, (error, response, body) => {
  377. if (error || response.statusCode !== 200) {
  378. --resources === 0 && after();
  379. return;
  380. }
  381. let position = tryParseJSON(body);
  382. if (_.get(position, '_links.employee.href')) {
  383. request.get({
  384. url: _.get(position, '_links.employee.href'),
  385. jar: jar
  386. }, (error, response, body) => {
  387. if (error || response.statusCode !== 200) {
  388. --resources === 0 && after();
  389. return;
  390. }
  391. model.chief = tryParseJSON(body);
  392. --resources === 0 && after();
  393. });
  394. } else {
  395. --resources === 0 && after();
  396. }
  397. });
  398. ++resources &&
  399. request.get({
  400. url: link(_.get(model.orgUnit, '_links.productionCalendar.href')) + '?' + 'date=' + from,
  401. jar: jar
  402. }, (error, response, body) => {
  403. if (error || response.statusCode !== 200) {
  404. --resources === 0 && after();
  405. return;
  406. }
  407. request.get({
  408. url: link(_.get(tryParseJSON(body), '_links.productionCalendarInfo.href')),
  409. jar: jar
  410. }, (error, response, body) => {
  411. if (error || response.statusCode !== 200) {
  412. --resources === 0 && after();
  413. return;
  414. }
  415. model.productionCalendarInfo = tryParseJSON(body);
  416. model.productionCalendarInfo.monthWorkDayCount = _.get(model.productionCalendarInfo, `monthWorkDayCount[${date.month()}]`);
  417. model.productionCalendarInfo.monthOffDayCount = _.get(model.productionCalendarInfo, `monthOffDayCount[${date.month()}]`);
  418. model.productionCalendarInfo.monthHourCount = _.get(model.productionCalendarInfo, `monthHourCount[${date.month()}]`);
  419. --resources === 0 && after();
  420. });
  421. });
  422. ++resources &&
  423. request.get({
  424. url: link(_.get(links, 'comments.href'), {organizationUnitId: orgUnitId}) + '?' + 'from=' + from + '&' + 'to=' + to + '&' + 'size=' + 1e5,
  425. jar: jar
  426. }, (error, response, body) => {
  427. if (error || response.statusCode !== 200) {
  428. --resources === 0 && after();
  429. return;
  430. }
  431. model.comments = _.get(tryParseJSON(body), '_embedded.comments');
  432. --resources === 0 && after();
  433. });
  434. !isImpersonal && ++resources &&
  435. request.get({
  436. url: _.get(model.orgUnit, '_links.employeePositions.href') + '?' + 'from=' + from + '&' + 'to=' + to + '&' + 'size=' + 1e5 + '&' + 'include-chief=true',
  437. jar: jar
  438. }, (error, response, body) => {
  439. if (error || response.statusCode !== 200) {
  440. --resources === 0 && after();
  441. return;
  442. }
  443. model.employeePositions = _.filter(_.get(tryParseJSON(body), '_embedded.employeePositions'), e => {
  444. if (_.get(positionTypeIds, '[0]') !== '') {
  445. return _.some(positionTypeIds, id => id === _.last(_.split(_.get(e, '_embedded.position._links.positionType.href'), '/')));
  446. }
  447. return true;
  448. });
  449. --resources === 0 && after();
  450. });
  451. ++resources && request.get({
  452. url: link(_.get(model.orgUnit, '_links.rosters.href') + '?' + 'from=' + from + '&' + 'to=' + to),
  453. jar: jar
  454. }, (error, response, body) => {
  455. if (error || response.statusCode !== 200) {
  456. --resources === 0 && after();
  457. return;
  458. }
  459. let rosters = _.get(tryParseJSON(body), '_embedded.rosters');
  460. model.roster = _.find(rosters, {'active': true});
  461. model.isScheduleTemplateUsed = _.get(model, 'roster.scheduleTemplateUsed');
  462. if (model.roster) {
  463. resources--;
  464. isImpersonal && ++resources &&
  465. request.get({
  466. url: link(_.get(model.roster, '_links.positionTypes.href')) + '?' + 'from=' + from + '&' + 'to=' + to,
  467. jar: jar
  468. }, function (error, response, body) {
  469. if (error || response.statusCode !== 200) {
  470. --resources === 0 && after();
  471. return;
  472. }
  473. model.positionTypes = _.unionBy([], _.get(tryParseJSON(body), '_embedded.positionTypes'), 'positionIndex');
  474. --resources === 0 && after();
  475. });
  476. ++resources &&
  477. request.get({
  478. url: link(_.get(model.roster, '_links.shifts.href')) + '?' + 'from=' + from + '&' + 'to=' + to,
  479. jar: jar
  480. }, function (error, response, body) {
  481. if (error || response.statusCode !== 200) {
  482. --resources === 0 && after();
  483. return;
  484. }
  485. model.shifts = _.get(tryParseJSON(body), '_embedded.shifts');
  486. model.shifts && _.forEach(model.shifts, e => {
  487. e.startDate = moment(_.get(e, 'dateTimeInterval.startDateTime'));
  488. e.endDate = moment(_.get(e, 'dateTimeInterval.endDateTime'));
  489. e.haveMinutes = e.startDate.minutes() > 0 || e.endDate.minutes();
  490. delete e.dateTimeInterval;
  491. });
  492. --resources === 0 && after();
  493. });
  494. } else {
  495. --resources === 0 && after();
  496. }
  497. });
  498. });
  499. });
  500. }
  501.  
  502. isImpersonal ? after() : request.get({
  503. url : _.get(links, 'scheduleRequestTypes.href'),
  504. headers: {
  505. 'Accept': 'application/json'
  506. },
  507. jar : jar
  508. }, (error, response, body) => {
  509. if (error || response.statusCode !== 200) {
  510. res.send('Can not get scheduleRequestTypes');
  511. return;
  512. }
  513. scheduleRequestTypes = tryParseJSON(body);
  514. request.get({
  515. url: link(_.get(links, 'scheduleRequests.href')) + '?' + 'from=' + from + '&' + 'to=' + to + '&' + 'size=' + 1e5 + '&' + 'org-unit-ids=' + orgUnitIds.join(','),
  516. jar: jar
  517. }, (error, response, body) => {
  518. if (error || response.statusCode !== 200) {
  519. res.send('Can not get scheduleRequests');
  520. return;
  521. }
  522. scheduleRequests = _.get(tryParseJSON(body), '_embedded.scheduleRequests');
  523. _.forEach(scheduleRequests, e => {
  524. e.startDate = moment(_.get(e, 'dateTimeInterval.startDateTime'));
  525. e.endDate = moment(_.get(e, 'dateTimeInterval.endDateTime'));
  526. e.text = _.get(_.find(scheduleRequestTypes, ['value', e.type]), 'extended.shortName');
  527. delete e.dateTimeInterval;
  528. });
  529. after();
  530. });
  531. });
  532.  
  533. });
  534.  
  535. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement