view client/src/components/importconfiguration/types/Soundingresults.vue @ 3712:9eab897f66b6

import_soundingresult: form parameter for beamtype to boolean
author Thomas Junk <thomas.junk@intevation.de>
date Thu, 20 Jun 2019 12:44:19 +0200
parents ee7dc0bea083
children 73589ed481f5
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>
    </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
    };
  },
  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.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
        );

      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,
              projection: this.projection
            })
          )
        );
      }
    },
    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: "multibeam",
    SINGLEBEAM: "singlebeam"
  },
  UPLOADLABEL: "choose a .zip or .txt file",
  on: "on",
  off: "off"
};
</script>