FE PDF and some overhaul

This commit is contained in:
Tyrel Souza 2023-10-10 17:03:36 -04:00
parent ea8f347085
commit 6e36dccc1f
No known key found for this signature in database
GPG Key ID: F3614B02ACBE438E
10 changed files with 516 additions and 152 deletions

View File

@ -1,9 +1,14 @@
<script setup> <script setup>
import {ref} from "vue"
import Sheet from './components/Sheet.vue' import Sheet from './components/Sheet.vue'
import PDF from './components/PDF/PDF.vue'
const sheet_or_pdf = ref(1)
</script> </script>
<template> <template>
<Sheet /> <Sheet v-if="sheet_or_pdf==0" />
<PDF v-if="sheet_or_pdf==1" />
</template> </template>
<style scoped> <style scoped>

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,323 @@
<template>
<div class="header">
<div class="flex-container">
<div class="column pct-25">
<img :src="BenchTopLogoUrl" />
</div>
<div class="column pct-50">
<div>Customer: {customer}</div>
<div>Onsite cal (yes/no): {onsite_cal}</div>
<div>Control Doc#: {control_doc_number}</div>
<div>Technician: {technician}</div>
</div>
<div class="column pct-25">
<img :src="Al2pCertUrl" />
</div>
</div>
</div>
<!-- EndHeader -->
<h1>Calibration Certificate</h1>
<!-- Instrument Info -->
<div class="instrument-information">
<div class="flex-container">
<div class="column pct-33">
<div><span class="ul">Instrument</span></div>
<div><span class="label">Model :</span>{Model}</div>
<div><span class="label">Serial# :</span>{Serial_no}</div>
<div><span class="label">Channel :</span>{channel}</div>
<div><span class="label">Transducer Model :</span>{t_model}</div>
<div><span class="label">Transducer Span :</span>{t_span}</div>
</div>
<div class="column pct-33">
<div><span class="ul">Primary Cal Device</span></div>
<div><span class="label">Model :</span>{Model}</div>
<div><span class="label">Serial# :</span>{Serial_no}</div>
<div><span class="label">Cal Date :</span>{Cal Date}</div>
<div><span class="label">Cal Due Date :</span>{Serial_no}</div>
<div><span class="label">Cert ID :</span>{Cert ID}</div>
</div>
<div class="column pct-33">
<div><span class="label">Cal Date :</span>{Cal Date}</div>
<div><span class="label">Cal Due Date :</span>{Serial_no}</div>
<div><span class="ul">Environmental Data</span></div>
<div><span class="label">Baro (Psi) :</span>{Baro}</div>
<div><span class="label">Temp (&deg;F):</span>{Temp}</div>
<div><span class="label">Humidity (%RH):</span>{Humidity}</div>
</div>
</div>
</div>
<br />
<!-- End InstrumentInfo -->
<!-- As Found -->
<div class="readings">
<table class="table table-striped">
<thead>
<tr>
<th>Gauge Reading</th>
<th>Accuracy (+/- FS)</th>
<th>Low Limit</th>
<th>High Limit</th>
<th>As Found (units)</th>
<th>OOT</th>
</tr>
</thead>
<tbody>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
</div>
<br />
<!-- As Left -->
<div class="readings">
<table class="table table-striped">
<thead>
<tr>
<th>Gauge Reading</th>
<th>Accuracy (+/- FS)</th>
<th>Low Limit</th>
<th>High Limit</th>
<th>As Left (units)</th>
<th>OOT</th>
</tr>
</thead>
<tbody>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>-12.500</td>
<td>0.050%</td>
<td>-12.510</td>
<td>-12.490</td>
<td>-12.500</td>
<td>0</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
</tr>
</tbody>
</table>
</div>
<!-- End Instruments-->
<div class="box">
<p>
Secondary Cal Device (For Environmental Data): EXTECH S/N A23050006 Cert #
1535483 07/24/23 (Cal Date) (Precision Psychrometer)
</p>
<p>
Uncertainty Statement: The accuracy of measurement is determined by the
standards uncertainty, with a coverage factor of k=2 (confidence of
roughly 95%).
</p>
</div>
</template>
<script setup>
import BenchTopLogoUrl from "../../assets/BenchTopLogo.png";
import Al2pCertUrl from "../../assets/al2pCert.png";
</script>
<style scoped>
h1 {
text-align: center;
font-size: 1.75rem;
font-weight: bold;
}
.ul {
font-size: 12pt;
text-decoration: underline;
}
.box {
border: 1px solid;
}
.label {
font-size: 12pt;
font-weight: bold;
padding-right: 0.25rem;
}
.flex-container {
display: flex;
justify-content: space-between;
}
.instrument-information {
font-size: 8pt;
}
.column {
border: 1px solid #ccc;
}
.pct-50 {
width: 45%;
}
.pct-33 {
width: 33%;
}
.pct-25 {
width: 25%;
}
.column img {
width: 150px;
}
tr td {
padding: 0 !important;
margin: 0 !important;
}
tr th {
padding: 0 !important;
margin: 0 !important;
width: 18%;
}
.table {
font-size: 9pt;
}
</style>

View File

@ -0,0 +1,71 @@
parseInstrumentInfo = (text) => {
const instrumentInfo = {};
const lines = text.split("\n").slice(2);
for (const line of lines) {
if (line) {
const [key, value] = line.trim().split(/\s\s+/);
instrumentInfo[key.trim()] = value.trim();
}
}
return instrumentInfo;
}
parsePorts = (text) => {
text += "\n\n"; // Ensure extra newline to match on
const pattern = /(Test Port \d)/g;
const matches = text.split(pattern).slice(1);
const portData = {};
for (let i = 0; i < matches.length; i += 2) {
const port = matches[i];
const calibration = matches[i + 1];
portData[port] = parseCalibrationData(calibration);
}
return portData;
}
const KEEP = {
"Mass Flow Trans": ["Instrument Flow", "Master Reading"],
"Pressure Transducer": ["Instrument Pressure", "Master Value"],
};
parseCalibrationData = (text) => {
const pattern = /(Mass Flow Trans|Pressure Transducer)\n([\s\S]+?)\n\n/g;
const matches = [...text.matchAll(pattern)];
const calibrationData = {};
for (const match of matches) {
const blockTitle = match[1];
const blockContent = match[2];
const lines = blockContent.trim().split("\n");
lines.shift(); // Remove "=======" line
const deviceName = lines.shift().trim().split(/\s+/).slice(-1)[0].trim();
const deviceData = { name: deviceName };
for (const line of lines) {
const [key, value] = line.trim().split(/\s\s+/);
const keyTrimmed = key.trim();
for (const start of KEEP[blockTitle]) {
if (keyTrimmed.startsWith(start)) {
deviceData[keyTrimmed] = value.trim();
}
}
}
calibrationData[blockTitle] = deviceData;
}
return calibrationData;
}
export default parseHardwareCalibration = (content, accuracy) => {
const sections = content.split("|| Hardware Calibration Report ||");
const instrumentInfo = parseInstrumentInfo(sections[0]);
const calibrationData = parsePorts(sections[1]);
return { instrument: instrumentInfo, calibration: calibrationData };
}

View File

@ -0,0 +1,111 @@
inRange = (index, value, masterValues) => {
return (
masterValues[index]["Low Limit"] <= value && value <= masterValues[index]["High Limit"]
);
}
delta = (index, value, masterValues) => {
return Math.abs(masterValues[index]["Low Limit"] - value);
}
export default parseTransducer = (content, accuracy) => {
accuracy = accuracy / 100.0; // Comes in as Percent
const transducerData = [];
// Split the content into sections based on the blank line
const sections = content.trim().split("\n\n");
for (const section of sections) {
// Split each section into lines
const lines = section.trim().split("\n");
const filteredLines = lines.filter(
(line) => !line.startsWith("==") && line !== "|| Transducer Verify Report ||"
);
filteredLines.shift();
// Extract the Transducer number and Transducer type
const transducerLine = filteredLines.shift().trim();
const [, transducerName, partNumber] = transducerLine.split(null, 2);
// Get part number and values
let value = null;
let unit = null;
let transducerType = null;
if (partNumber !== "Custom") {
value = partNumber.split(" ").pop();
partNumber = partNumber.split(" ")[1];
const match = value.match(/([0-9]+)([A-Z]+)/i);
if (match) {
[, value, unit] = match;
value = parseInt(value);
}
if (unit === "SCCM") {
transducerType = "Flow";
}
if (unit === "PSIA") {
transducerType = "Pressure";
}
}
// Create an object to store the data for each transducer
const transducerInfo = {
Accuracy: accuracy,
"Part Number": partNumber,
Value: value,
Unit: unit,
"Limit ABS": value * accuracy * 1000,
"Transducer Name": transducerName,
"Transducer Type": transducerType,
"Gauge Reading": [],
"Master Value": [],
"Verify Date": "",
"Verify Time": "",
};
// Extract other information for the transducer
for (const line of filteredLines) {
const [key, val] = line.trim().split(/\s\s+/);
if (key.includes("Verify Date")) {
transducerInfo["Verify Date"] = val;
} else if (key.includes("Verify Time")) {
transducerInfo["Verify Time"] = val;
} else {
// Toss anything else where it belongs
const [, cleanKey] = key.split(/\W\d/);
if (
cleanKey in transducerInfo ||
key.includes(`Instrument ${transducerType}`)
) {
const value = parseInt(val.split(" ")[0]) * 1000;
// special case Master to get the limits
if (cleanKey.includes("Master")) {
const hi = value + transducerInfo["Limit ABS"];
const lo = value - transducerInfo["Limit ABS"];
transducerInfo[cleanKey].push({
"Low Limit": lo,
"Master Value": value,
"High Limit": hi,
});
}
// Turn both Instrument Pressure and Instrument Flow to Gauge Reading
else if (key.includes(`Instrument ${transducerType}`)) {
transducerInfo["Gauge Reading"].push(value);
} else {
transducerInfo[cleanKey].push(value);
}
}
}
}
// Once we have the readings and master values, we can do the math
transducerInfo["Gauge Reading"] = transducerInfo["Gauge Reading"].map((v, idx) => ({
Value: v,
"In Range": inRange(idx, v, transducerInfo["Master Value"]),
Delta: delta(idx, v, transducerInfo["Master Value"]),
}));
transducerData.push(transducerInfo);
}
return transducerData;
}

View File

@ -33,6 +33,9 @@ body {
margin: 0.25rem; margin: 0.25rem;
margin-bottom: 2%; margin-bottom: 2%;
border-radius: 3px; border-radius: 3px;
/* purple
background-color: #b39ddb;
border: 1px solid #7e57c2; */
background-color: #a1cbfa; background-color: #a1cbfa;
border: 1px solid #4290e2; border: 1px solid #4290e2;
box-shadow: 0 2px 2px rgba(0,90,250,0.05), box-shadow: 0 2px 2px rgba(0,90,250,0.05),

View File

@ -1,7 +1,5 @@
import re import re
import json import json
from pprint import pprint
def in_range(index, value, master_values): def in_range(index, value, master_values):
return ( return (

View File

@ -1,147 +0,0 @@
<html lang="en">
<head>
<!--? <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/css/foundation.min.css"-->
<!--? crossorigin="anonymous">-->
<!--? <script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/js/foundation.min.js"-->
<!--? crossorigin="anonymous"></script>-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js" integrity="sha384-cuYeSxntonz0PPNlHhBs68uyIAVpIIOZZ5JqeqvYYIcEL727kskC66kF92t6Xl2V" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
<script type="importmap">
{ "imports": { "vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.41/vue.esm-browser.js" } }
</script>
<style>
.app {
margin: auto;
align: center;
width: 50%;
}
</style>
</head>
<body class="app">
<div id="app">
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
<table class="table table-striped">
<thead>
<tr><th colspan="2"><h2>Customer</h2></th></tr>
</thead>
<tbody>
<tr><td>Customer Name</td><td>{{ form.customer_name }}</td></tr>
<tr><td>On Site Calibration</td><td>{{ form.onsite_cal }}</td></tr>
<tr><td>Control Doc#</td><td>{{ form.control_doc }}</td></tr>
<tr><td>Technician</td><td>{{ form.technician }}</td></tr>
</tbody>
<thead>
<tr><th colspan="2"><h2>Instrument</h2></th></tr>
</thead>
<tbody>
<tr><td>Model:</td><td>{{ form.instrument_model }}</td></tr>
<tr><td>Calibration Date:</td><td>{{ form.instrument_calibration_date }}</td></tr>
<tr><td>Calibration Due Date:</td><td>{{ form.instrument_calibration_due_date }}</td></tr>
<tr><td>Serial Number</td><td>{{ form.serial_number }}</td></tr>
<tr><td>Channel</td><td>{{ form.channel }}</td></tr>
<tr><td>Transducer Model:</td><td>{{ form.transducer_model }}</td></tr>
<tr><td>Transducer Span:</td><td>{{ form.transducer_span }}</td></tr>
</tbody>
<thead>
<tr><th colspan="2"><h2>Primary Calibration Device</h2></th></tr>
</thead>
<tbody>
<tr><td>Model</td><td>{{ form.calibration_model }}</td></tr>
<tr><td>Serial #</td><td>{{ form.calibration_serial }}</td></tr>
<tr><td>Cal Date</td><td>{{ form.calibration_date }}</td></tr>
<tr><td>Cal Due Date</td><td>{{ form.calibration_due_date }}</td></tr>
<tr><td>Cert ID</td><td>{{ form.calibration_cert_id }}</td></tr>
</tbody>
<thead>
<tr><th colspan="2"><h2>Environment</h2></th></tr>
</thead>
<tbody>
<tr><td>Accuracy</td><td>{{ form.accuracy }}</td></tr>
<tr><td>Barometric Pressure (mbar)</td><td>{{ form.barometric_pressure }}</td></tr>
<tr><td>Temperature (&deg;F)</td><td>{{ form.temperature }}</td></tr>
<tr><td>Humidity</td><td>{{ form.humidity }}</td></tr>
</tbody>
<thead>
<tr><th colspan="2"><h2>Report Uploads</h2></th></tr>
</thead>
<tbody>
<tr><td>Report Type</td><td>{{ form.report_type }}</td></tr>
<tr><td colspan=2>One File: {{ form.both }}</td></tr>
<tr><td>As Found: {{ form.as_found }}</td><td>As Left:{{ form.as_left }}</td></tr>
<tr><td>&nbsp;</td><td align="right"><input type="submit" value="Generate PDF and Label"></td></tr>
</tbody>
</table>
</form>
</div>
{% verbatim %}
<script type="module">
import {createApp, ref, reactive, computed} from 'vue'
// Vue.config.delimiters = ['${', '}'];
const vm = createApp({
data() {
const as_found = ""
const as_left = ""
const both = ""
const report_type = ""
const accuracy = ref("0.5")
const barometric_pressure = ref(1013.25)
const temperature = ref("50")
const humidity = ref("50.0")
const show_as_found = reactive(true)
const show_as_left = reactive(true)
const show_both = reactive(true)
const show_report_type = reactive(true)
return {
accuracy,
barometric_pressure,
temperature,
humidity,
as_found,
as_left,
both,
report_type,
show_as_found,
show_as_left,
show_both,
show_report_type,
}
},
el: "#app",
methods: {
change_as_found: function () {
this.show_both = false
},
change_as_left: function () {
this.show_both = false
},
change_both: function () {
this.show_as_found = false
this.show_as_left = false
},
notifyFileInput: function (event) {
// update file name value
this.file = event.target.files[0].name;
},
},
ready: () => {
$('input[type="file"]').change(this.notifyFileInput.bind(this));
}
}).mount("#app")
</script>
{% endverbatim %}
</body>
</html>

View File

@ -1,4 +1,4 @@
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -30,4 +30,4 @@ def upload_file(request):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
else: else:
form = SheetForm() form = SheetForm()
return render(request, "sheets/upload.html", {"form": form}) return HttpResponse("hi")