Observation now updates the scoring fields.
This commit is contained in:
parent
710c07a10f
commit
f7b44e14a5
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"exec": {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
// JavaScript Document
|
||||||
|
|
||||||
|
console.log("LIBRARY: libPDFscripts.js")
|
||||||
|
//Use this function as a start - to pass the path, then datasource to update with pdfbytes, then download -- Maybe combine into one file?
|
||||||
|
//wich is more efficient?
|
||||||
|
|
||||||
|
let popover = new bootstrap.Popover(popoverTrigger, {
|
||||||
|
content: "This dropdown is disabled.",
|
||||||
|
placement: "top",
|
||||||
|
trigger: "manual"
|
||||||
|
});
|
||||||
|
|
||||||
|
//filepath = /PDF/XXX.pdf, dataSource = datastore.var
|
||||||
|
// *** Use of 'rest paramater' syntax to add extra datasources at the end.
|
||||||
|
//return array of datasources i.e [datastore,data_view1] and pass that array to updatePdfFields
|
||||||
|
//Consider default values = filepath = '/PDF/masterTemplate.pdf'
|
||||||
|
async function toBase64(filePath, ...dataSources) {
|
||||||
|
console.log(filePath);
|
||||||
|
const pdfBytes = await fetch(filePath).then((res) => res.arrayBuffer());
|
||||||
|
|
||||||
|
// console.log(dataSources); // should be only 2,
|
||||||
|
updatePdfFields(pdfBytes, dataSources);
|
||||||
|
|
||||||
|
//return pdfBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updatePdfFields(pdfBytes, dataSources) {
|
||||||
|
// Load the PDF document
|
||||||
|
console.log("pdfBytes.size =", pdfBytes.byteLength);
|
||||||
|
console.log(dataSources);
|
||||||
|
const pdfDoc = await PDFLib.PDFDocument.load(pdfBytes);
|
||||||
|
const form = pdfDoc.getForm();
|
||||||
|
|
||||||
|
// Loop over each data source passed to the function
|
||||||
|
dataSources.forEach((dataSource) => {
|
||||||
|
// Loop over each record in the current data source
|
||||||
|
dataSource.forEach((record) => {
|
||||||
|
// Loop over each key-value pair in the record
|
||||||
|
Object.entries(record).forEach(([fieldName, fieldValue]) => {
|
||||||
|
console.log(fieldName, " => ", fieldValue);
|
||||||
|
|
||||||
|
// Find the form field in the PDF by name
|
||||||
|
/** !!! BLOCK 1 Start
|
||||||
|
const formField = form.getTextField(fieldName);
|
||||||
|
// If the field exists in the PDF, update it with the value
|
||||||
|
if (formField) {
|
||||||
|
formField.setText(String(fieldValue));
|
||||||
|
}
|
||||||
|
**/ // BLOCK 1 END
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Serialize the PDF back to bytes
|
||||||
|
const updatedPdfBytes = await pdfDoc.save();
|
||||||
|
|
||||||
|
// Trigger download of the updated PDF
|
||||||
|
downloadPdf(updatedPdfBytes, "UpdatedDocument.pdf");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to trigger download of the updated PDF
|
||||||
|
function downloadPdf(pdfBytes, fileName) {
|
||||||
|
// Create a Blob from the PDF bytes
|
||||||
|
const blob = new Blob([pdfBytes], { type: "application/pdf" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
// Create a link element for the download
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = fileName;
|
||||||
|
|
||||||
|
// Append the link to the document, trigger a click, and remove it afterward
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
// Release the object URL to free up memory
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage Example
|
||||||
|
async function main() {
|
||||||
|
// Load your PDF as bytes (e.g., using fetch)
|
||||||
|
const pdfBytes = await fetch("path/to/your.pdf").then((res) =>
|
||||||
|
res.arrayBuffer()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sample data sources from Wappler
|
||||||
|
const dataSource1 = [{ fieldName1: "Value1", fieldName2: "Value2" }];
|
||||||
|
const dataSource2 = [
|
||||||
|
{ fieldName3: "AnotherValue1", fieldName4: "AnotherValue2" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Call the function with multiple data sources
|
||||||
|
await updatePdfFields(pdfBytes, dataSource1, dataSource2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomFilename() {
|
||||||
|
const characters =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let filename = "";
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * characters.length);
|
||||||
|
filename += characters[randomIndex];
|
||||||
|
}
|
||||||
|
console.log("Random Filename: ", filename);
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,6 +40,27 @@ dmx.config({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"local": {}
|
"local": {}
|
||||||
|
},
|
||||||
|
"data_view2": {
|
||||||
|
"meta": [
|
||||||
|
{
|
||||||
|
"name": "$id",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"name": "numSection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"name": "pointValue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"name": "description"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputType": "array"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -49,101 +49,83 @@ async function fetchPdfBytes(filePath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update form fields in the PDF
|
// Update form fields in the PDF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async function updatePdfFields(
|
async function updatePdfFields(
|
||||||
pdfBytes,
|
pdfBytes,
|
||||||
dataSources
|
dataSources,
|
||||||
|
//skipMissingFields = false
|
||||||
|
|
||||||
) {
|
) {
|
||||||
console.log("Datasources Count:", dataSources.length)
|
console.log("Datasources Count:", dataSources.length)
|
||||||
console.log(JSON.stringify(dataSources, null, 2));
|
console.log(JSON.stringify(dataSources, null, 2));
|
||||||
|
|
||||||
try {
|
|
||||||
const pdfDoc = await PDFLib.PDFDocument.load(pdfBytes);
|
|
||||||
const form = pdfDoc.getForm();
|
|
||||||
|
|
||||||
dataSources.forEach((dataSource, dsIndex) => {
|
|
||||||
console.log(`Processing dataSource[${dsIndex}]:`, dataSource);
|
|
||||||
|
|
||||||
if (!Array.isArray(dataSource)) {
|
// try {
|
||||||
console.warn(`dataSource[${dsIndex}] is not an array. Skipping.`);
|
const pdfDoc = await PDFLib.PDFDocument.load(pdfBytes);
|
||||||
|
const form = pdfDoc.getForm();
|
||||||
|
|
||||||
|
|
||||||
|
dataSources.forEach((dataSource, dsIndex) => {
|
||||||
|
console.log(`Processing dataSource[${dsIndex}]:`, dataSource);
|
||||||
|
|
||||||
|
if (!Array.isArray(dataSource)) {
|
||||||
|
console.warn(`dataSource[${dsIndex}] is not an array. Skipping.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ii = 0
|
||||||
|
dataSource.forEach((record, recIndex) => {
|
||||||
|
console.log(`Processing record[${recIndex}]:`, record);
|
||||||
|
|
||||||
|
if (!record || typeof record !== 'object') {
|
||||||
|
console.warn(`record[${recIndex}] is undefined or not an object. Skipping.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataSource.forEach((record, recIndex) => {
|
// Extract numSection and pointValue dynamically or other 'key' components.
|
||||||
console.log(`Processing record[${recIndex}]:`, record);
|
const { numSection } = record;
|
||||||
|
const pointValue = record.pointValue || record.Points; // Fallback to Points if pointValue is missing
|
||||||
if (!record || typeof record !== 'object') {
|
|
||||||
console.warn(`record[${recIndex}] is undefined or not an object. Skipping.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract numSection and pointValue dynamically or other 'key' components.
|
|
||||||
const { numSection } = record;
|
|
||||||
const pointValue = record.pointValue || record.Points; // Fallback to Points if pointValue is missing
|
|
||||||
|
|
||||||
|
|
||||||
if (numSection && pointValue) {
|
if (numSection && pointValue) {
|
||||||
console.log(`Updating PDF field: ${numSection} with value: ${pointValue}`);
|
console.log(`Updating PDF field: ${numSection} with value: ${pointValue}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Use numSection as the dynamic field name
|
// Use numSection as the dynamic field name
|
||||||
const formField = form.getTextField(numSection);
|
const formField = form.getTextField(numSection);
|
||||||
|
|
||||||
if (formField) {
|
if (formField) {
|
||||||
formField.setText(String(pointValue));
|
ii++
|
||||||
console.log(`Field "${numSection}" updated with value "${pointValue}".`);
|
formField.setText(String(pointValue));
|
||||||
} else {
|
console.log(`Field "${numSection}" updated with value "${pointValue}".`);
|
||||||
console.warn(`Field "${numSection}" not found in PDF.`);
|
} else {
|
||||||
}
|
console.warn(`Field "${numSection}" not found in PDF.`);
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error updating field "${numSection}":`, err);
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (err) {
|
||||||
console.warn(`Missing numSection or pointValue in record[${recIndex}].`);
|
console.error(`Error updating field "${numSection}":`, err);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
console.warn(`Missing numSection or pointValue in record[${recIndex}].`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
console.log(`${ii} Form field updates complete.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
return await pdfDoc.save(); /// MOVE TO the Main PROCESS and DOWNLOD PDF function or as last call. (need await??) Maybe make it an actual 'ready' to download button.
|
||||||
|
|
||||||
dataSources.forEach((dataSource) => {
|
|
||||||
//console.log("Current Datasource", dataSource)
|
|
||||||
dataSource.forEach((record) => {
|
|
||||||
console.log(record)
|
|
||||||
//console.log("Working in: ", record)
|
|
||||||
Object.entries(record).forEach(([fieldName, fieldValue]) => {
|
|
||||||
dd(`Field: ${fieldName} Value: ${fieldValue}`) //debugger
|
|
||||||
try {
|
|
||||||
console.log(`----------------> form ${fieldName}`)
|
|
||||||
const formField = form.getTextField(fieldName);
|
|
||||||
if (formField) {
|
|
||||||
// console.info(`Field "${fieldName}" OK`);
|
|
||||||
dd(`${fieldName} --> ${fieldValue} OK`);
|
|
||||||
formField.setText(String(fieldValue));
|
|
||||||
} else if (!skipMissingFields) {
|
|
||||||
//dd(`Skipping Field: ${fieldName} NOT FOUND`)
|
|
||||||
console.warn(`Field "${fieldName}" not found.`);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error updating field "${fieldName}":`); //console.error(`Error updating field "${fieldName}":`, err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
// console.log(JSON.stringify(dataSources, null, 2));
|
|
||||||
|
|
||||||
return await pdfDoc.save();
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error updating PDF fields:", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to trigger download of the updated PDF
|
// Function to trigger download of the updated PDF
|
||||||
function downloadPdf(pdfBytes, fileName) {
|
function downloadPdf(pdfBytes, fileName) {
|
||||||
|
|
||||||
const blob = new Blob([pdfBytes], { type: "application/pdf" });
|
const blob = new Blob([pdfBytes], { type: "application/pdf" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
|
@ -151,7 +133,7 @@ function downloadPdf(pdfBytes, fileName) {
|
||||||
href: url,
|
href: url,
|
||||||
download: fileName,
|
download: fileName,
|
||||||
});
|
});
|
||||||
|
console.log("DOWNLOAD Filled PDF")
|
||||||
link.click();
|
link.click();
|
||||||
URL.revokeObjectURL(url); // Clean up the URL immediately
|
URL.revokeObjectURL(url); // Clean up the URL immediately
|
||||||
}
|
}
|
||||||
|
|
@ -164,7 +146,7 @@ function generateRandomFilename(length = 8, prefix = "", suffix = "") {
|
||||||
);
|
);
|
||||||
const filename = `${prefix}${array.join("")}${suffix}`;
|
const filename = `${prefix}${array.join("")}${suffix}`;
|
||||||
|
|
||||||
dd("Random Filename:", filename);
|
//dd("Random Filename:", filename);
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,43 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<base href="/">
|
||||||
<title>Untitled Document</title>
|
<meta charset="UTF-8">
|
||||||
<!-- New Wappler Page -->
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>PDF Form Fields</title>
|
||||||
|
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<h1>Hardcoded PDF Form Fields</h1>
|
||||||
|
<ul id="field-list"></ul>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const filePath = "/PDF/Template-EDV-ERT_v4.1.pdf"
|
||||||
|
|
||||||
|
const pdfBytes = await fetchPdfBytes(filePath);
|
||||||
|
// Function to load a PDF and list all form fields
|
||||||
|
async function listPdfFields(pdfBytes) {
|
||||||
|
const pdfDoc = await PDFLib.PDFDocument.load(pdfBytes);
|
||||||
|
const form = pdfDoc.getForm();
|
||||||
|
const fields = form.getFields();
|
||||||
|
const fieldList = document.getElementById('field-list');
|
||||||
|
fieldList.innerHTML = '';
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
const type = field.constructor.name;
|
||||||
|
const name = field.getName();
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
listItem.textContent = `Field: ${name}, Type: ${type}`;
|
||||||
|
fieldList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the hardcoded PDF from a URL (this can be a server-hosted or local file)
|
||||||
|
fetch('/PDF/.pdf')
|
||||||
|
.then(response => response.arrayBuffer())
|
||||||
|
.then(pdfBytes => listPdfFields(pdfBytes))
|
||||||
|
.catch(error => console.error('Error loading PDF:', error));
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
columns: [
|
columns: [
|
||||||
{table: "da", column: "db_signatureData"}
|
{table: "da", column: "db_signatureData"}
|
||||||
],
|
],
|
||||||
table: {name: "da"},
|
table: {name: "db_training", alias: "da"},
|
||||||
primary: "_id",
|
primary: "_id",
|
||||||
joins: [],
|
joins: [],
|
||||||
wheres: {
|
wheres: {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<!-- Wappler include head-page="layouts/main" fontawesome_5="cdn" bootstrap5="local" is="dmx-app" id="observationPage" appConnect="local" components="{dmxBootstrap5TableGenerator:{},dmxMasonry:{},dmxFormatter:{},dmxBootstrap5Popovers:{},dmxBootstrap5Toasts:{},dmxDataTraversal:{},dmxStateManagement:{},dmxDatastore:{},dmxValidator:{},dmxBootstrap5Offcanvas:{},dmxBootstrap5Modal:{},dmxBootstrap5Navigation:{},dmxBootstrap5Tooltips:{},dmxBootbox5:{},dmxNotifications:{},dmxPouchDB:{},dmxBootstrap5Alert:{},dmxBootstrap5Collapse:{}}" jquery_slim_35="cdn" moment_2="cdn" -->
|
<!-- Wappler include head-page="layouts/main" fontawesome_5="cdn" bootstrap5="local" is="dmx-app" id="observationPage" appConnect="local" components="{dmxBootstrap5TableGenerator:{},dmxMasonry:{},dmxFormatter:{},dmxBootstrap5Popovers:{},dmxBootstrap5Toasts:{},dmxDataTraversal:{},dmxStateManagement:{},dmxDatastore:{},dmxValidator:{},dmxBootstrap5Offcanvas:{},dmxBootstrap5Modal:{},dmxBootstrap5Navigation:{},dmxBootstrap5Tooltips:{},dmxBootbox5:{},dmxNotifications:{},dmxPouchDB:{},dmxBootstrap5Alert:{},dmxBootstrap5Collapse:{}}" jquery_slim_35="cdn" moment_2="cdn" -->
|
||||||
|
<dmx-data-view id="data_view2" dmx-bind:data="datastore1.data" filter="!numSection.isEmpty()&&!pointValue.isEmpty()"></dmx-data-view>
|
||||||
<dmx-datastore id="datastore1"></dmx-datastore>
|
<dmx-datastore id="datastore1"></dmx-datastore>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.9.1/jquery.timepicker.min.css" integrity="sha512-UimcIlYKETYXXjgBbgq45ZXCLXI+d1O43cZXVTdhtCYYGomJVM2Ahz+L19UEWBIH4f/A/qmlvCaOWiaNMcaw3w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-timepicker/1.9.1/jquery.timepicker.min.css" integrity="sha512-UimcIlYKETYXXjgBbgq45ZXCLXI+d1O43cZXVTdhtCYYGomJVM2Ahz+L19UEWBIH4f/A/qmlvCaOWiaNMcaw3w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
|
@ -309,7 +310,12 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="container"></div>
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="container" dmx-show="toggle1.checked">
|
<div class="container" dmx-show="toggle1.checked">
|
||||||
<div class="row" dmx-hide="offcanvas1.CKBinput2.checked">
|
<div class="row" dmx-hide="offcanvas1.CKBinput2.checked">
|
||||||
<div class="col-5">
|
<div class="col-5">
|
||||||
|
|
@ -360,7 +366,7 @@
|
||||||
<td>
|
<td>
|
||||||
<!--select id="selPoints" class="form-select" name="sPoints" dmx-on:changed="datastore1.upsert({numSection: numSection, $id: numSection, description: txtSection},{numSection: numSection, Points: value, description: txtSection})" dmx-bind:id="numSection" dmx-bind:value="selectedValue"-->
|
<!--select id="selPoints" class="form-select" name="sPoints" dmx-on:changed="datastore1.upsert({numSection: numSection, $id: numSection, description: txtSection},{numSection: numSection, Points: value, description: txtSection})" dmx-bind:id="numSection" dmx-bind:value="selectedValue"-->
|
||||||
|
|
||||||
<select id="selPoints" class="form-select" name="sPoints" dmx-bind:id="numSection" dmx-bind:value="selectedValue" dmx-on:changed="datastore1.upsert({numSection: numSection},{numSection: numSection, pointValue: selectedValue, description: txtSection})">
|
<select id="selPoints" class="form-select" name="sPoints" dmx-bind:id="" dmx-bind:value="selectedValue" dmx-on:changed="datastore1.upsert({numSection: numSection},{numSection: numSection, pointValue: selectedValue, description: txtSection})">
|
||||||
|
|
||||||
<option value="0">0 points</option>
|
<option value="0">0 points</option>
|
||||||
<option value="1">1 point</option>
|
<option value="1">1 point</option>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue