view client/src/components/map/contextbox/ImportSoundingresults.vue @ 1520:6ad1f431bc85

Fixed date of latest measurement in bottlenecks list. As the time zone conversion done by geoserver leads to unexpected results for date fields if the local timezone differs from UTC, I replaced the date column in the bottleneck_overview view with text. As the transport format used (JSON) does handle dates as strings anyway we do not loose any information by doing so...
author Sascha Wilde <wilde@intevation.de>
date Thu, 06 Dec 2018 15:37:06 +0100
parents 4af7eaca44a1
children
line wrap: on
line source

<template>
  <div>
    <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center">
      <font-awesome-icon icon="upload" class="mr-2"></font-awesome-icon>
      <translate>Import Soundingresults</translate>
    </h6>
    <div v-if="editState" class="ml-auto mr-auto mt-4 w-95">
      <div class="d-flex flex-column">
        <div class="d-flex flex-row">
          <div class="mt-1 text-left w-50 ml-2 mr-4">
            <small class="text-muted">
              <translate>Bottleneck</translate>
            </small>
            <select v-model="bottleneck" class="custom-select">
              <option
                v-for="bottleneck in availableBottlenecks"
                :key="bottleneck"
                >{{ bottleneck }}</option
              >
            </select>
            <span class="text-danger">
              <small v-if="!bottleneck">
                <translate>Please select a bottleneck</translate>
              </small>
            </span>
          </div>
          <div class="d-flex flex-column mt-1 text-left w-50 mr-2">
            <small class="text-muted">
              <translate>Projection</translate>&nbsp;(EPSG)
            </small>
            <input
              class="form-control"
              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>
        <div class="d-flex flex-row">
          <div class="mt-1 text-left w-50 ml-2 mr-4">
            <small class="text-muted">
              <translate>Depthreference</translate>
            </small>
            <select
              v-model="depthReference"
              class="custom-select"
              id="depthreference"
            >
              <option
                v-for="option in this.$options.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="mt-1 text-left w-50 mr-2">
            <small class="text-muted"> <translate>Date</translate> </small>
            <input
              id="importdate"
              type="date"
              class="form-control"
              placeholder="Date of import"
              aria-label="bottleneck"
              aria-describedby="bottlenecklabel"
              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="ml-2 mt-2 text-left">
        <small v-for="(message, index) in messages" :key="index">
          {{ message }}
        </small>
      </div>
    </div>
    <div class="w-95 ml-auto mr-auto mt-4 mb-4">
      <div v-if="uploadState" class="d-flex flex-row input-group mb-4">
        <div class="custom-file">
          <input
            accept=".zip"
            type="file"
            @change="fileSelected"
            class="custom-file-input"
            id="uploadFile"
          />
          <label class="custom-file-label" for="uploadFile">
            {{ uploadLabel }}
          </label>
        </div>
      </div>
      <div class="buttons text-right">
        <a
          v-if="editState"
          download="meta.json"
          :href="dataLink"
          class="btn btn-outline-info pull-left"
        >
          <translate>Download Meta.json</translate>
        </a>
        <button
          v-if="editState"
          @click="deleteTempData"
          class="btn btn-danger"
          type="button"
        >
          <translate>Cancel Upload</translate>
        </button>
        <button
          :disabled="disableUploadButton"
          @click="submit"
          class="btn btn-info"
          type="button"
        >
          {{ uploadState ? Upload : Confirm }}
        </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.js";
import { mapState } from "vuex";

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

export default {
  name: "imports",
  data() {
    return {
      importState: IMPORTSTATE.UPLOAD,
      depthReference: "",
      bottleneck: "",
      projection: "",
      importDate: "",
      uploadLabel: this.$gettext("choose .zip- file"),
      uploadFile: null,
      disableUpload: false,
      token: null,
      messages: []
    };
  },
  methods: {
    initialState() {
      this.importState = IMPORTSTATE.UPLOAD;
      this.depthReference = "";
      this.bottleneck = "";
      this.projection = "";
      this.importDate = "";
      this.uploadLabel = this.$gettext("choose .zip- file");
      this.uploadFile = null;
      this.disableUpload = false;
      this.token = null;
      this.messages = [];
    },
    fileSelected(e) {
      const files = e.target.files || e.dataTransfer.files;
      if (!files) return;
      this.uploadLabel = files[0].name;
      this.uploadFile = files[0];
    },
    deleteTempData() {
      HTTP.delete("/imports/soundingresult-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}`
          });
        });
    },
    submit() {
      if (!this.uploadFile || this.disableUpload) return;
      if (this.importState === IMPORTSTATE.UPLOAD) {
        this.upload();
      } else {
        this.confirm();
      }
    },
    upload() {
      let formData = new FormData();
      formData.append("soundingresult", this.uploadFile);
      HTTP.post("/imports/soundingresult-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"];
            this.bottleneck = bottleneck;
            this.depthReference = depthReference;
            this.importDate = new Date(date).toISOString().split("T")[0];
            this.projection = epsg;
          }
          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);
      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("", this.projection);

      HTTP.post("/imports/soundingresult", 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
          });
          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"]),
    disableUploadButton() {
      if (this.importState === IMPORTSTATE.UPLOAD) return this.disableUpload;
      if (
        !this.bottleneck ||
        !this.importDate ||
        !this.depthReference ||
        !this.projection
      )
        return true;
      return this.disableUpload;
    },
    availableBottlenecks() {
      return this.bottlenecks.map(x => x.properties.name);
    },
    editState() {
      return this.importState === IMPORTSTATE.EDIT;
    },
    uploadState() {
      return this.importState === IMPORTSTATE.UPLOAD;
    },
    Upload() {
      return this.$gettext("Upload");
    },
    Confirm() {
      return this.$gettext("Confirm");
    },
    dataLink() {
      return (
        "data:text/json;charset=utf-8," +
        encodeURIComponent(
          JSON.stringify({
            depthReference: this.depthReference,
            bottleneck: this.bottleneck,
            date: this.importDate
          })
        )
      );
    }
  },
  depthReferenceOptions: [
    "",
    // "NAP",
    // "KP",
    // "FZP",
    // "ADR",
    // "TAW",
    // "PUL",
    // "NGM",
    // "ETRS",
    // "POT",
    // "LDC",
    // "HDC",
    // "ZPG",
    // "GLW",
    // "HSW",
    // "LNW",
    // "HNW",
    // "IGN",
    // "WGS",
    "RN" //,
    // "HBO"
  ]
};
</script>

<style lang="scss" scoped>
.projectionLabel {
  margin-left: $small-offset;
}

.depthreferencelabel {
  margin-left: $small-offset;
}

.offset-r {
  margin-right: $small-offset;
}

.buttons button {
  margin-left: $offset !important;
}

.label-text {
  width: 5rem;
  text-align: left;
  line-height: 2.25rem;
}
</style>