Mercurial > gemma
changeset 2247:e6fba449aa3c
merged pdf-export in default
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Wed, 13 Feb 2019 16:41:00 +0100 |
parents | 2cba13653a58 |
children | cce158db02b0 |
files | client/src/assets/application.scss client/src/components/Pdftool.vue client/src/components/systemconfiguration/PDFTemplates.vue client/src/components/systemconfiguration/Systemconfiguration.vue client/src/store/application.js pkg/controllers/printtemplates.go |
diffstat | 6 files changed, 160 insertions(+), 193 deletions(-) [+] |
line wrap: on
line diff
--- a/client/src/assets/application.scss Wed Feb 13 16:13:44 2019 +0100 +++ b/client/src/assets/application.scss Wed Feb 13 16:41:00 2019 +0100 @@ -131,11 +131,10 @@ } .list-fade-enter-active, .list-fade-leave-active { - transition: transform .3s; + transition: opacity .3s; } .list-fade-enter, .list-fade-leave-to { opacity: 0; - transform: translateY(20px); } .pointer {
--- a/client/src/components/Pdftool.vue Wed Feb 13 16:13:44 2019 +0100 +++ b/client/src/components/Pdftool.vue Wed Feb 13 16:41:00 2019 +0100 @@ -57,35 +57,18 @@ </select> </div> </div> - <!-- - <small class="d-block my-2"> - <input - type="radio" - id="pdfexport-downloadtype-download" - value="download" - v-model="form.downloadType" - selected - /> - <label for="pdfexport-downloadtype-download" class="ml-1 mr-2"> - <translate>Download</translate> - </label> - <input - type="radio" - id="pdfexport-downloadtype-open" - value="open" - v-model="form.downloadType" - /> - <label for="pdfexport-downloadtype-open" class="ml-1"> - <translate>Open in new window</translate> - </label> - </small> - --> <button @click="download" type="button" :disabled="!readyToGenerate" class="btn btn-sm btn-info d-block w-100 mt-2" > + <font-awesome-icon + v-if="!readyToGenerate" + class="mr-1" + icon="spinner" + spin + /> <translate>Generate PDF</translate> </button> </div> @@ -114,6 +97,8 @@ import "@/lib/font-linbiolinum.js"; import { getPointResolution } from "ol/proj.js"; import locale2 from "locale2"; +import { HTTP } from "../lib/http"; +import { displayError } from "@/lib/errors.js"; var paperSizes = { // in millimeter, landscape [width, height] @@ -132,6 +117,7 @@ downloadType: "download", resolution: "80" }, + templateData: null, pdf: { doc: null, width: null, @@ -153,14 +139,28 @@ // applied to the rest of the form. applyTemplateToForm() { if (this.form.template) { - this.form.format = this.form.template.properties.format; - this.form.paperSize = this.form.template.properties.paperSize; - this.form.resolution = this.form.template.properties.resolution; + HTTP.get("/templates/print/" + this.form.template.name, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } + }) + .then(response => { + this.templateData = response.data.template_data; + this.form.format = this.templateData.properties.format; + this.form.paperSize = this.templateData.properties.paperSize; + this.form.resolution = this.templateData.properties.resolution; + }) + .catch(e => { + const { status, data } = e.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); } }, download() { - let template = this.form.template; - // disable button while working on it this.readyToGenerate = false; @@ -237,15 +237,15 @@ this.pdf.height ); - if (template) { + if (this.templateData) { this.pdf.doc.setFont("linbiolinum", "normal"); let defaultFontSize = 11, - defaultRounding = 0, + defaultRounding = 2, defaultTextColor = "black", defaultBgColor = "white", defaultPadding = 3, defaultOffset = { x: 0, y: 0 }; - template.elements.forEach(e => { + this.templateData.elements.forEach(e => { switch (e.type) { case "text": { this.addText( @@ -673,7 +673,7 @@ this.getLayerByName("Bottleneck isolines").isVisible ) { let width = 54; - let height = 13; + let height = 17; let padding = 3; // x/y defaults to offset for topleft corner (normal x/y coordinates) @@ -704,11 +704,13 @@ this.pdf.doc.setFontStyle("bold"); this.pdf.doc.text(x + padding + w, y + padding, str); + let survey = this.selectedSurvey; + str = this.$gettext("Survey date") + ": "; w = this.pdf.doc.getTextWidth(str); this.pdf.doc.setFontStyle("italic"); this.pdf.doc.text(x + padding, y + padding + 3, str); - str = this.selectedSurvey.date_info; + str = survey.date_info; this.pdf.doc.setFontStyle("normal"); this.pdf.doc.text(x + padding + w, y + padding + 3, str); @@ -716,9 +718,22 @@ w = this.pdf.doc.getTextWidth(str); this.pdf.doc.setFontStyle("italic"); this.pdf.doc.text(x + padding, y + padding + 6, str); - str = this.selectedSurvey.gauge_objname; + str = survey.gauge_objname; this.pdf.doc.setFontStyle("normal"); this.pdf.doc.text(x + padding + w, y + padding + 6, str); + + str = this.$gettext("Depth relativ to") + ": "; + w = this.pdf.doc.getTextWidth(str); + this.pdf.doc.setFontStyle("italic"); + this.pdf.doc.text(x + padding, y + padding + 9, str); + this.pdf.doc.setFontStyle("normal"); + str = survey.depth_reference + " = "; + if (survey.hasOwnProperty("waterlevel_value")) { + str += survey.waterlevel_value + " cm"; + } else { + str += "?"; + } + this.pdf.doc.text(x + padding + w, y + padding + 9, str); } }, replacePlaceholders(text) { @@ -741,6 +756,7 @@ mounted() { this.$store.dispatch("application/loadPdfTemplates").then(() => { this.form.template = this.pdfTemplates[0]; + this.applyTemplateToForm(); }); } };
--- a/client/src/components/systemconfiguration/PDFTemplates.vue Wed Feb 13 16:13:44 2019 +0100 +++ b/client/src/components/systemconfiguration/PDFTemplates.vue Wed Feb 13 16:41:00 2019 +0100 @@ -4,36 +4,25 @@ <h5><translate>PDF-Templates</translate></h5> <input @change="upload" - id="uploadPDFTemplates" - ref="uploadPDFTemplates" + id="uploadPDFTemplate" + ref="uploadPDFTemplate" type="file" style="visibility:hidden" /> - <button - class="btn btn-sm btn-info" - @click="$refs.uploadPDFTemplates.click()" - > - <font-awesome-icon icon="spinner" class="fa-spin" v-if="uploading" /> - <font-awesome-icon icon="plus" v-else /> - </button> </div> - <div class="d-flex mt-1"> + <div class="mt-1"> <table class="table table-sm"> <thead> <tr> <th>Name</th> - <th>Description</th> <th>Date</th> - <th>Country</th> <th></th> </tr> </thead> <transition-group name="list-fade" tag="tbody"> <tr v-for="template in pdfTemplates" :key="template.name"> <td>{{ template.name }}</td> - <td>{{ template.description }}</td> - <td>{{ template.date }}</td> - <td></td> + <td>{{ template.time }}</td> <td class="text-right"> <button class="btn btn-sm btn-info mr-2"> <font-awesome-icon icon="download" /> @@ -51,6 +40,18 @@ </tr> </transition-group> </table> + <button + class="btn btn-sm btn-info" + @click="$refs.uploadPDFTemplate.click()" + > + <font-awesome-icon + icon="spinner" + class="fa-spin fa-fw" + v-if="uploading" + /> + <font-awesome-icon icon="upload" class="fa-fw" v-else /> + <translate>Upload new template</translate> + </button> </div> <div @@ -126,10 +127,10 @@ this.uploading = true; this.$store .dispatch( - "application/uploadPDFTemplates", - this.$refs.uploadPDFTemplates.files + "application/uploadPDFTemplate", + this.$refs.uploadPDFTemplate.files ) - .then(() => { + .finally(() => { this.uploading = false; }); }, @@ -137,6 +138,9 @@ this.showDeleteTemplatePrompt = false; this.$store.dispatch("application/removePDFTemplate", template); } + }, + mounted() { + this.$store.dispatch("application/loadPdfTemplates"); } }; </script>
--- a/client/src/components/systemconfiguration/Systemconfiguration.vue Wed Feb 13 16:13:44 2019 +0100 +++ b/client/src/components/systemconfiguration/Systemconfiguration.vue Wed Feb 13 16:41:00 2019 +0100 @@ -31,7 +31,7 @@ <translate>Send</translate> </a> </div> - <!-- <PDFTemplates /> --> + <PDFTemplates /> </div> <!-- card-body --> </div>
--- a/client/src/store/application.js Wed Feb 13 16:13:44 2019 +0100 +++ b/client/src/store/application.js Wed Feb 13 16:41:00 2019 +0100 @@ -15,7 +15,8 @@ */ import { version } from "../../package.json"; -// import { HTTP } from "../lib/http"; +import { HTTP } from "../lib/http"; +import { displayError } from "@/lib/errors.js"; // initial state const init = () => { @@ -112,146 +113,93 @@ }, actions: { loadPdfTemplates({ commit }) { - return new Promise(resolve => { - // pretend we do something async - setTimeout(function() { - commit("pdfTemplates", [ - { - name: "Default Template", - properties: { - format: "landscape", - resolution: "120", - paperSize: "a4" - }, - elements: [ - { - type: "textbox", - position: "bottomleft", - offset: { x: 2, y: 2 }, - width: 100, - fontSize: 8, - text: "Date of publication: {date} - generated by: {user}" - }, - { - type: "bottleneck", - position: "topleft", - offset: { x: 2, y: 2 } - }, - { - type: "legend", - position: "topleft", - offset: { x: 2, y: 18 } - }, - { - type: "scalebar", - position: "bottomright", - offset: { x: 2, y: 2 } - }, - { - type: "northarrow", - position: "topright", - offset: { x: 10, y: 5 }, - size: 2 - } - ] - }, - { - name: "Full Template", - properties: { - format: "landscape", - resolution: "120", - paperSize: "a4" - }, - elements: [ - { - type: "box", - position: "topleft", - offset: { x: 20, y: 2 }, - width: 60, - height: 25, - color: "green" - }, - { - type: "text", - position: "topleft", - offset: { x: 23, y: 5 }, - width: 56, - fontSize: 10, - color: "greenyellow", - text: - "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua." - }, - { - type: "textbox", - position: "bottomleft", - offset: { x: 2, y: 2 }, - width: 90, - padding: 3, - fontSize: 7, - color: "gray", - background: "white", - text: "Date of publication: {date} - generated by: {user}" - }, - { - type: "image", - format: "PNG", - position: "bottomleft", - offset: { x: 2, y: 10 }, - width: 90, - height: 12, - border: 2 - }, - { - type: "bottleneck", - position: "topright", - offset: { x: 2, y: 2 } - }, - { - type: "legend", - position: "topright", - offset: { x: 2, y: 18 } - }, - { - type: "scalebar", - position: "bottomright", - offset: { x: 2, y: 2 } - }, - { - type: "northarrow", - position: "topleft", - offset: { x: 8, y: 4 }, - size: 2 - } - ] - } - ]); - resolve(); - }, 500); + return new Promise((resolve, reject) => { + HTTP.get("/templates/print", { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } + }) + .then(response => { + commit("pdfTemplates", response.data); + resolve(); + }) + .catch(e => { + reject(e); + const { status, data } = e.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); }); }, - uploadPDFTemplates({ state, commit }, files) { + uploadPDFTemplate({ dispatch }, files) { return new Promise((resolve, reject) => { - setTimeout(() => { - const reader = new FileReader(); - reader.onload = event => { - let templates = state.pdfTemplates; - templates.push(JSON.parse(event.target.result)); - commit("pdfTemplates", templates); - // TODO: send template to server - }; - reader.onerror = error => reject(error); - reader.readAsText(files[0]); - resolve(); - }, 1000); + const reader = new FileReader(); + reader.onload = event => { + let template; + try { + template = JSON.parse(event.target.result); + } catch (e) { + displayError({ + title: "Format Error", + message: "Uploaded file does not contain valid json data." + }); + reject(e); + } + if (template.name) { + HTTP.post( + "/templates/print/" + template.name, + { + template_name: template.name, + template_data: template + }, + { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } + } + ) + .then(response => { + dispatch("loadPdfTemplates"); + resolve(response); + }) + .catch(e => { + reject(e); + const { status, data } = e.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); + } else { + reject(); + displayError({ + title: "Format Error", + message: "The provided template has no name property." + }); + } + }; + reader.onerror = error => reject(error); + reader.readAsText(files[0]); }); }, removePDFTemplate({ state, commit }, template) { - let templates = state.pdfTemplates; - let removeIndex = templates.findIndex(t => t.name === template.name); - if (removeIndex !== -1) { - templates.splice(removeIndex, 1); - commit("pdfTemplates", templates); - } + HTTP.delete("/templates/print/" + template.name, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "text/xml; charset=UTF-8" + } + }).then(() => { + let templates = state.pdfTemplates; + let removeIndex = templates.findIndex(t => t.name === template.name); + if (removeIndex !== -1) { + templates.splice(removeIndex, 1); + commit("pdfTemplates", templates); + } + }); } } };
--- a/pkg/controllers/printtemplates.go Wed Feb 13 16:13:44 2019 +0100 +++ b/pkg/controllers/printtemplates.go Wed Feb 13 16:41:00 2019 +0100 @@ -38,13 +38,13 @@ ORDER BY date_info DESC` hasPrintTemplateSQL = ` -SELECT true FROM users.templates WHERE name = $1` +SELECT true FROM users.templates WHERE template_name = $1` deletePrintTemplateSQL = ` -DELETE FROM users.templates WHERE name = $1` +DELETE FROM users.templates WHERE template_name = $1` selectPrintTemplateSQL = ` -SELECT template_data FROM users.templates WHERE name = $1` +SELECT template_data FROM users.templates WHERE template_name = $1` insertPrintTemplateSQL = ` INSERT INTO users.templates (template_name, template_data)