view client/src/components/importconfiguration/types/Soundingresults.vue @ 4231:6f31a99cd92d

clinet: fix translations process and update source strings * move strings for translations from *.po files to the component itself to let gettext() mark only the strings without the html elements. (make makemessages complains to have html elements in the .po files and stops the process).
author Fadi Abbud <fadi.abbud@intevation.de>
date Wed, 21 Aug 2019 11:13:12 +0200
parents 5020128dc5a0
children 008bc1ae8897
line wrap: on
line source

<template>
  <div>
    <div v-if="editState" class="mb-2 p-2">
      <div
        v-for="(message, index) in messages"
        :key="index"
        class="alert alert-warning small"
      >
        {{ message }}
      </div>
      <div class="d-flex">
        <div class="text-left mr-3">
          <small class="text-muted">
            <translate>Bottleneck</translate>
          </small>
          <select class="form-control font-weight-bold" v-model="bottleneck">
            <option :value="null">{{ placeholder }}</option>
            <optgroup
              v-for="(bottlenecksForCountry, cc) in orderedBottlenecks"
              :key="cc"
              :label="cc"
            >
              <option
                v-for="bn in bottlenecksForCountry"
                :key="bn.properties.objnam"
                :value="bn"
              >
                {{ bn.properties.objnam }}
              </option>
            </optgroup>
          </select>
          <span class="text-danger">
            <small v-if="!bottleneck">
              <translate>Please select a bottleneck</translate>
            </small>
          </span>
        </div>
        <div class="text-left ml-2 mr-1">
          <small class="text-muted">
            <translate>Projection</translate>&nbsp;(EPSG)
          </small>
          <input
            class="form-control form-control-sm"
            v-model="projection"
            value="4326"
            placeholder="e.g. 4326"
            type="number"
          />
          <span class="text-left text-danger">
            <small v-if="!projection">
              <translate>Please enter a projection</translate>
            </small>
          </span>
        </div>
        <div class="text-left ml-1">
          <small class="text-muted">
            <translate>BeamType</translate>
          </small>
          <select
            v-model="beamType"
            class="custom-select custom-select-sm"
            id="beamtype"
          >
            <option
              v-for="option in Object.keys($options.BEAMTYPES)"
              :key="option"
              >{{ $options.BEAMTYPES[option] }}</option
            >
          </select>
        </div>
      </div>
      <div class="d-flex flex-row w-100 mt-3">
        <div class="mr-2 text-left">
          <small class="text-muted">
            <translate>Depthreference</translate>
          </small>
          <select
            v-model="depthReference"
            class="custom-select custom-select-sm"
            id="depthreference"
          >
            <option
              v-for="option in this.depthReferenceOptions"
              :key="option"
              >{{ option }}</option
            >
          </select>
          <span class="text-left text-danger">
            <small v-if="!depthReference">
              <translate>Please enter a reference</translate>
            </small>
          </span>
        </div>
        <div class="ml-3 text-left">
          <small class="text-muted"> <translate>Date</translate> </small>
          <input
            id="importdate"
            type="date"
            class="form-control form-control-sm"
            placeholder="Date of import"
            v-model="importDate"
          />
          <span class="text-left text-danger">
            <small v-if="!importDate">
              <translate>Please enter a date</translate>
            </small>
          </span>
        </div>
        <div class="ml-3 text-left d-flex flex-column">
          <div class="text-left">
            <small class="text-muted">
              <translate>Negate Z values in XYZ files</translate>
            </small>
          </div>
          <div class="ml-4 mt-2 text-left">
            <input
              id="negatez"
              type="checkbox"
              class="form-check-input"
              v-model="negateZ"
            />
          </div>
        </div>
      </div>
    </div>
    <div class="mt-2">
      <div v-if="uploadState" class="input-group px-2">
        <div :key="1" class="flex-column mr-4">
          <div class="flex-row text-left">
            <small class="text-muted">
              <translate>Email Notification</translate>
            </small>
          </div>
          <div class="flex-flex-row text-left">
            <toggle-button
              v-model="eMailNotification"
              class="mt-2"
              :speed="100"
              :labels="{
                checked: this.$options.on,
                unchecked: this.$options.off
              }"
              :width="60"
              :height="30"
            />
          </div>
        </div>
        <div class="custom-file mt-4">
          <input
            accept=".zip,.txt"
            type="file"
            @change="fileSelected"
            class="custom-file-input"
            id="uploadFile"
          />
          <label class="pointer custom-file-label" for="uploadFile">
            {{ uploadLabel }}
          </label>
        </div>
      </div>
      <div
        class="d-flex justify-content-between mt-2 p-2 border-top"
        v-if="editState"
      >
        <button
          :key="1"
          @click="deleteTempData()"
          class="btn btn-sm btn-warning"
        >
          Back
        </button>
        <span>
          <a
            download="meta.json"
            :href="dataLink"
            :class="[
              'btn btn-sm btn-outline-info',
              { disabled: !bottleneck || !importDate || !depthReference }
            ]"
          >
            <translate>Download Meta.json</translate>
          </a>
          <button
            :disabled="disableUploadButton"
            @click="confirm"
            class="btn btn-sm btn-info ml-2"
            type="button"
          >
            <translate>Confirm</translate>
          </button>
        </span>
      </div>
      <div v-if="uploadState" class="d-flex mt-2 p-2 border-top">
        <button :key="2" @click="back()" class="btn btn-sm btn-warning">
          Back
        </button>
      </div>
    </div>
  </div>
</template>

<script>
/* This is Free Software under GNU Affero General Public License v >= 3.0
 * without warranty, see README.md and license for details.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 * License-Filename: LICENSES/AGPL-3.0.txt
 *
 * Copyright (C) 2018 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Markus Kottländer <markus.kottlaender@intevation.de>
 */
import { HTTP } from "@/lib/http";
import { displayError, displayInfo } from "@/lib/errors";
import { mapState, mapGetters } from "vuex";

const IMPORTSTATE = { UPLOAD: "UPLOAD", EDIT: "EDIT" };

export default {
  data() {
    return {
      beamType: this.$options.BEAMTYPES.MULTIBEAM,
      importState: IMPORTSTATE.UPLOAD,
      depthReference: "",
      bottleneck: "",
      projection: "4326",
      importDate: "",
      uploadLabel: this.$gettext(this.$options.UPLOADLABEL),
      uploadFile: null,
      disableUpload: false,
      token: null,
      messages: [],
      eMailNotification: false,
      negateZ: false
    };
  },
  methods: {
    back() {
      this.$store.commit("importschedule/setListMode");
    },
    initialState() {
      this.importState = IMPORTSTATE.UPLOAD;
      this.depthReference = "";
      this.bottleneck = null;
      this.projection = "4326";
      this.importDate = "";
      this.uploadLabel = this.$gettext(this.$options.UPLOADLABEL);
      this.uploadFile = null;
      this.disableUpload = false;
      this.token = null;
      this.eMailNotification = false;
      this.messages = [];
    },
    fileSelected(e) {
      const files = e.target.files || e.dataTransfer.files;
      if (!files) return;
      this.uploadLabel = files[0].name;
      this.uploadFile = files[0];
      this.upload();
    },
    deleteTempData() {
      HTTP.delete("/imports/sr-upload/" + this.token, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token")
        }
      })
        .then(() => {
          this.initialState();
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    },
    upload() {
      let formData = new FormData();
      formData.append("soundingresult", this.uploadFile);
      if (this.eMailNotification) {
        formData.append("send-email", this.eMailNotification);
      }
      HTTP.post("/imports/sr-upload", formData, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-Type": "multipart/form-data"
        }
      })
        .then(response => {
          if (response.data.meta) {
            const { bottleneck, date, epsg } = response.data.meta;
            const depthReference = response.data.meta["depth-reference"];
            const singlebeam = response.data.meta["single-beam"];
            this.negateZ = response.data.meta["negate-z"];
            this.bottleneck = this.bottlenecks.find(
              bn => bn.properties.objnam === bottleneck
            );
            this.depthReference = depthReference;
            this.importDate = new Date(date).toISOString().split("T")[0];
            this.projection = epsg;
            this.beamType = singlebeam
              ? this.$options.BEAMTYPES.SINGLEBEAM
              : this.$options.BEAMTYPES.MULTIBEAM;
          }
          this.importState = IMPORTSTATE.EDIT;
          this.token = response.data.token;
          this.messages = response.data.messages;
        })
        .catch(error => {
          const { status, data } = error.response;
          const messages = data.messages ? data.messages.join(", ") : "";
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${messages}`
          });
        });
    },
    confirm() {
      let formData = new FormData();
      formData.append("token", this.token);
      if (this.bottleneck)
        formData.append("bottleneck", this.bottleneck.properties.objnam);
      if (this.importDate)
        formData.append("date", this.importDate.split("T")[0]);
      if (this.depthReference)
        formData.append("depth-reference", this.depthReference);
      if (this.projection) formData.append("epsg", this.projection);
      if (this.beamType)
        formData.append(
          "single-beam",
          this.beamType === this.$options.BEAMTYPES.SINGLEBEAM
        );
      formData.append("negate-z", this.negateZ == true);
      HTTP.post("/imports/sr", formData, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-Type": "multipart/form-data"
        }
      })
        .then(() => {
          displayInfo({
            title: this.$gettext("Import"),
            message:
              this.$gettext("Starting import for ") +
              this.bottleneck.properties.objnam
          });
          this.initialState();
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    }
  },
  mounted() {
    this.$store.dispatch("bottlenecks/loadBottlenecks");
  },
  watch: {
    showContextBox() {
      if (!this.showContextBox && this.token) this.deleteTempData();
    }
  },
  computed: {
    ...mapState("application", ["showContextBox"]),
    ...mapState("bottlenecks", ["bottlenecks"]),
    ...mapState("user", ["user"]),
    ...mapGetters("usermanagement", ["userCountries"]),
    ...mapGetters("user", ["isSysAdmin"]),
    importSoundingresultsLabel() {
      return this.$gettext("Import Soundingresults");
    },
    disableUploadButton() {
      if (this.importState === IMPORTSTATE.UPLOAD) return this.disableUpload;
      if (
        !this.bottleneck ||
        !this.importDate ||
        !this.depthReference ||
        !this.projection
      )
        return true;
      return this.disableUpload;
    },
    availableBottlenecks() {
      const userCountrycode = this.userCountries[this.user];
      if (userCountrycode === "global" || this.isSysAdmin)
        return this.bottlenecks;
      return this.bottlenecks.filter(
        bn => bn.properties.responsible_country === userCountrycode
      );
    },
    placeholder() {
      return this.$gettext("Select bottleneck");
    },
    orderedBottlenecks() {
      let groupedBottlenecks = {},
        orderedGroups = {};

      // group bottlenecks by cc
      this.availableBottlenecks.forEach(bn => {
        let cc = bn.properties.responsible_country;
        if (groupedBottlenecks.hasOwnProperty(cc)) {
          groupedBottlenecks[cc].push(bn);
        } else {
          groupedBottlenecks[cc] = [bn];
        }
      });

      // order groups by cc
      Object.keys(groupedBottlenecks)
        .sort()
        .forEach(cc => (orderedGroups[cc] = groupedBottlenecks[cc]));

      return orderedGroups;
    },
    editState() {
      return this.importState === IMPORTSTATE.EDIT;
    },
    uploadState() {
      return this.importState === IMPORTSTATE.UPLOAD;
    },
    Upload() {
      return this.$gettext("Upload");
    },
    Confirm() {
      return this.$gettext("Confirm");
    },
    dataLink() {
      if (
        this.bottleneck &&
        this.depthReference &&
        this.importDate &&
        this.beamType &&
        this.projection
      ) {
        return (
          "data:text/json;charset=utf-8," +
          encodeURIComponent(
            JSON.stringify({
              "depth-reference": this.depthReference,
              bottleneck: this.bottleneck.properties.objnam,
              date: this.importDate,
              "single-beam":
                this.beamType === this.$options.BEAMTYPES.SINGLEBEAM,
              epsg: Number(this.projection),
              "negate-z": this.negateZ == true
            })
          )
        );
      }
    },
    depthReferenceOptions() {
      if (this.bottleneck) {
        const referenceLevels = JSON.parse(
          this.bottleneck.properties.reference_water_levels
        );
        const result = Object.keys(referenceLevels);
        if (!referenceLevels["ZPG"]) result.push("ZPG"); // ZPG should always be available
        return result;
      }
      return [];
    }
  },
  BEAMTYPES: {
    MULTIBEAM: "multi-beam",
    SINGLEBEAM: "single-beam"
  },
  UPLOADLABEL: "choose a .zip or .txt file",
  on: "on",
  off: "off"
};
</script>