Skip to content

Commit

Permalink
Finished pdf generation server
Browse files Browse the repository at this point in the history
  • Loading branch information
rishadbaniya committed Nov 23, 2022
1 parent 06e0f11 commit 23e8eaa
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 132 deletions.
Binary file removed backend/pdf_generation/abc.pdf
Binary file not shown.
319 changes: 187 additions & 132 deletions backend/pdf_generation/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
const puppeteer = require('puppeteer');
const express = require('express');
const bodyParser = require('body-parser')
const fs = require('fs');

const PDF_DIR = './pdf';
const PORT = 8001;
const APP = express();

/* The request comes in following JSON Schema
*
* {
* "id" : 1 // ID of the user
* "data" : [] or [{
* "transactionHistory" : [] or [{
* "operation" : BALANCE_IN // Represents the type of operation, can have values of BALANCE_IN, TRANSFER_IN, TRANSFER_OUT, PURCHASE
* "items" : [
* ], // PURCHASE items, [] in case of BALANCE_IN, TRANSFER_IN, TRANSFER_OUT
Expand All @@ -19,146 +28,192 @@
*
*/

const puppeteer = require('puppeteer');
const express = require('express');
const PORT = 9000;
const APP = express();

(async function runBrowser(){
// let BROWSER = await puppeteer.launch({ headless: true });
//
// APP.post("/generate_pdf", (req, res) => {
//
// req = JSON.parse(req);
// if (req == []}{
// html = "<h1> NO TRANSACTIONS </h1>"
// }else{
// x = ``;
// }
// getPdf(html, BROWSER, id).then(() => {
// html = ```
//
// <html>
// <body>
// <table style="border:1px white">
// <tr>
// <th>S.no</th>
// <th>Acitivties</th>
// <th>Item</th>
// <th>Rate</th>
// <th>Quantity</th>
// <th>Amount</th>
// </tr>
// <tr>
// <th>1</th>
// <th>Tranfer out</th>
// <th>-</th>
// <th>-</th>
// <th>-</th>
// <th>2000</th>
// </tr>
//
// </table>
// </body>
// </html>
// ```
// });
// });
// APP.listen(PORT);
})();

(async function getPdf(htmlContent, browser, id, datas){
let BROWSER = await puppeteer.launch({ headless: true });
let html = String.raw`
<html>
<style>${STYLE}</style>
<body>
<table>
${TABLE_HEADING}
<tbody>
${generateTableRows(datas)}
</tbody>
</table>
</body>
</html>
`;
const page = await BROWSER.newPage();
await page.setContent(html);
await page.pdf({ path: './abc.pdf', format: 'A4' });
await BROWSER.close();
})();

const generateHtml = (data) => {

const STYLE=String.raw`
html{
-webkit-print-color-adjust: exact;
}
table {
width: 750px;
border-collapse: collapse;
margin:50px auto;
}
.in{
background-color : #A3F4A3;
}
let allRows = "";
data.map((d, i) => {
if(d.transaction === "BUY"){
allRows += TRANSACTION.BUY(d);
}else if(d.transaction === "TRANSFER_IN"){
allRows += TRANSACTION.TRANSFER_IN(d);
}else if(d.transaction === "TRANSFER_OUT"){
allRows += TRANSACTION.TRANSFER_OUT(d);
}
});

.out{
background-color : #FEADAD;
}

th {
background: #3498db;
color: white;
font-weight: bold;
}
const HTML = String.raw`<html>
<style>
html {
-webkit-print-color-adjust: exact;
}
table {
width: 750px;
border-collapse: collapse;
margin:50px auto;
}
th{
background: #3498db;
color: white;
font-weight: bold;
text-align : center;
}
td{
text-align : center;
}
.in{
background-color : #A3F4A3;
}
.out{
background-color : #FEADAD;
}
td, th {
padding: 10px;
border: 1px solid #ccc;
text-align: left;
font-size: 10px;
}
`;

const TABLE_HEADING = String.raw`
<thead>
<tr>
<th>S.N</th>
<th>Date</th>
<th>Operation</th>
<th>Items</th>
<th>Rate</th>
<th>Quantity</th>
<th>Amount</th>
<th>Total</th>
</tr>
</thead>
`;

const generateTableRows = (data) => {
let ALL_ROWS = "";
data.map((d) => {
const {operation, items, rates, quantities, totals, total_amount} = d;
const IN_OR_OUT = (operation == "BALANCE_IN" || operation == "TANSFER_IN") ? "in" : "out";
ALL_ROWS += String.raw`
<tr class="${IN_OR_OUT}">
<td data-column="SN">James</td>
<td data-column="Last Name">Matman</td>
<td data-column="Job Title">Chief Sandwich Eater</td>
<td data-column="Twitter">@james</td>
</tr>`
});
}
/*
* JSON Data format as req:
* []
* Empty array that represents
*
*
*
*
*
*
*/
.bill_table {
width: 100% !important;
border-collapse: collapse;
margin: 0px !important;
}
</style>
<body>
<table>
<tr>
<th>Transaction</th>
<th>Date</th>
<th>Bill</th>
<th>Balance Before</th>
<th>Balance After</th>
</tr>
${allRows}
</table>
</body>
</html>`;
return HTML;
}

// It returns a table row
const TRANSACTION = {
BUY : (data) => {
let date = new Date(0);
date.setUTCSeconds(parseInt(data.date));
date = date.toLocaleString();

const bill = JSON.parse(data.bill);
let billRows = "";
bill.map((_d) => {
billRows += String.raw`
<tr>
<td>${_d.item}</td>
<td>${_d.rate}</td>
<td>${_d.quantity}</td>
<td>${_d.amount}</td>
</tr>
`;
});

const tableRow = String.raw`
<tr class="out">
<td>BUY</td>
<td>${date}</td>
<td>
<table class="bill_table">
<tr>
<th>Item</th>
<th>Rate</th>
<th>Quantity</th>
<th>Amount</th>
</tr>
${billRows}
</table>
</td>
<td>${data.balance_before}</td>
<td>${data.balance_after}</td>
</tr>
`;
return tableRow;
},

TRANSFER_IN : (data) => {

let date = new Date(0);
date.setUTCSeconds(parseInt(data.date));
date = date.toLocaleString();

const tableRow = String.raw`
<tr class="in">
<td>TRANSFER_IN</td>
<td>${date}</td>
<td>${data.bill}</td>
<td>${data.balance_before}</td>
<td>${data.balance_after}</td>
</tr>
`;
return tableRow;
},
TRANSFER_OUT : (data) => {

let date = new Date(0);
date.setUTCSeconds(parseInt(data.date));
date = date.toLocaleString();

const tableRow = String.raw`
<tr class="out">
<td>TRANSFER_OUT</td>
<td>${date}</td>
<td>${data.bill}</td>
<td>${data.balance_before}</td>
<td>${data.balance_after}</td>
</tr>
`;
return tableRow;

},
};

(async function runBrowser(){
if (!fs.existsSync(PDF_DIR)){
fs.mkdirSync(PDF_DIR);
}

APP.use(bodyParser.text({
verify: (req, res, buf) => {
req.rawBody = buf
}
}));

APP.use(express.static('pdf'))
APP.post("/", (req, res) => {
const body = req.body;
let id = body.substr(0, body.indexOf('{'));
id = parseInt(id);
let transactionHistory = `[${body.substr(body.indexOf('{'))}]`;
try{
transactionHistory = JSON.parse(transactionHistory);
let html = generateHtml(transactionHistory);
(async() => {
// Had to open and close browser continuosly in every request, kinda tedious...
// gotta recommend to create once instance and then create multiple pages of it
let BROWSER = await puppeteer.launch({ headless: true });
let page = await BROWSER.newPage();
await page.setContent(html)
await page.pdf({ path: `pdf/${id}_transaction_history.pdf`, format: 'A4' });
await BROWSER.close();
res.send("OK");
})();
}catch(e){
res.send("ERR");
}

});

APP.listen(PORT);
})();


1 change: 1 addition & 0 deletions backend/pdf_generation/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/pdf_generation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.1",
"dompurify": "^2.4.0",
"express": "^4.18.2",
"jsdom": "^20.0.2",
Expand Down
Binary file not shown.

0 comments on commit 23e8eaa

Please sign in to comment.