view pkg/imports/dmv.go @ 2168:b868cb653c4d

Import queue: The job kind parameter when creating a new job from a string was never used.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 08 Feb 2019 15:39:17 +0100
parents a7c4005b723f
children 7c83b5277c1c
line wrap: on
line source

// 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):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package imports

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/models"
	"gemma.intevation.de/gemma/pkg/soap"
	"gemma.intevation.de/gemma/pkg/soap/erdms"
)

type DistanceMarksVirtual struct {
	// URL is the URL of the SOAP service.
	URL string `json:"url"`
	// Username is the username used to authenticate.
	Username string `json:"username"`
	// Passwort is the password to authenticate.
	Password string `json:"password"`
	// Insecure indicates if HTTPS traffic
	// should validate certificates or not.
	Insecure bool `json:"insecure"`
}

const DMVJobKind JobKind = "dmv"

type dmvJobCreator struct{}

func init() {
	RegisterJobCreator(DMVJobKind, dmvJobCreator{})
}

func (dmvJobCreator) Description() string { return "distance marks virtual" }

func (dmvJobCreator) AutoAccept() bool { return true }

func (dmvJobCreator) Create(data string) (Job, error) {
	dmv := new(DistanceMarksVirtual)
	if err := common.FromJSONString(data, dmv); err != nil {
		return nil, err
	}
	return dmv, nil
}

func (dmvJobCreator) Depends() []string {
	return []string{
		"distance_marks_virtual",
	}
}

// StageDone does nothing as there is no staging for distance marks virtual.
func (dmvJobCreator) StageDone(context.Context, *sql.Tx, int64) error { return nil }

// CleanUp does nothing as there is nothing to cleanup with distance marks virtual.
func (*DistanceMarksVirtual) CleanUp() error { return nil }

const (
	deleteDistanceMarksVirtualSQL = `
DELETE FROM waterway.distance_marks_virtual
WHERE (location_code).country_code = $1
`
	insertDistanceMarksVirtualSQL = `
INSERT INTO waterway.distance_marks_virtual (
  location_code,
  geom,
  related_enc
)
VALUES (
  ($1::char(2), $2::char(3), $3::char(5), $4::char(5), $5::int),
  ST_SetSRID(ST_MakePoint($6, $7), 4326)::geography,
  $8
) ON CONFLICT (location_code) DO UPDATE SET
  geom = ST_SetSRID(ST_MakePoint($6, $7), 4326)::geography,
  related_enc = $8
`
)

func (dmv *DistanceMarksVirtual) Do(
	ctx context.Context,
	importID int64,
	conn *sql.Conn,
	feedback Feedback,
) (interface{}, error) {

	start := time.Now()

	tx, err := conn.BeginTx(ctx, nil)
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	var country string
	err = tx.QueryRowContext(ctx, selectCurrentUserCountrySQL).Scan(&country)
	switch {
	case err == sql.ErrNoRows:
		return nil, errors.New("Cannot figure out user country")
	case err != nil:
		return nil, err
	}

	country = strings.ToUpper(country)
	feedback.Info("Using country '%s'.", country)

	var auth *soap.BasicAuth
	if dmv.Username != "" {
		auth = &soap.BasicAuth{
			Login:    dmv.Username,
			Password: dmv.Password,
		}
	}

	client := erdms.NewRefService(dmv.URL, dmv.Insecure, auth)

	request := &erdms.GetRisDataXML{
		GetRisDataXMLType: &erdms.GetRisDataXMLType{
			Subcode: erdms.NoNS{Text: country + "%"},
			Funcode: erdms.NoNS{Text: "DISMAR"},
		},
	}

	data, err := client.GetRisDataXML(request)

	if err != nil {
		log.Printf("error: %v\n", err)
		return nil, fmt.Errorf("Error requesting ERDMS service: %v", err)
	}

	if _, err := tx.ExecContext(ctx, deleteDistanceMarksVirtualSQL, country); err != nil {
		return nil, err
	}

	insertStmt, err := tx.PrepareContext(ctx, insertDistanceMarksVirtualSQL)
	if err != nil {
		return nil, err
	}
	defer insertStmt.Close()

	var ignored, features int

	for _, dr := range data.RisdataReturn {
		if dr.RisidxCode == nil {
			ignored++
			continue
		}

		code, err := models.IsrsFromString(string(*dr.RisidxCode))
		if err != nil {
			feedback.Warn("invalid ISRS code %v", err)
			ignored++
			continue
		}

		if dr.Lat == nil || dr.Lon == nil {
			feedback.Warn("missing lat/lon: %s", code)
			ignored++
			continue
		}

		if dr.Relenc == nil {
			feedback.Warn("missing relenc: %s", code)
			ignored++
			continue
		}

		if _, err := insertStmt.ExecContext(
			ctx,
			code.CountryCode,
			code.LoCode,
			code.FairwaySection,
			code.Orc,
			code.Hectometre,
			float64(*dr.Lon), float64(*dr.Lat),
			string(*dr.Relenc),
		); err != nil {
			return nil, err
		}
		features++
	}
	feedback.Info("ignored: %d", ignored)
	feedback.Info("features: %d", features)

	if features == 0 {
		return nil, errors.New("No features found")
	}

	if err = tx.Commit(); err == nil {
		feedback.Info("Refreshing distance marks (virtual) successfully took %s.",
			time.Since(start))
	} else {
		feedback.Error("Refreshing distance marks (virtual) failed after %s.",
			time.Since(start))
	}

	return nil, nil
}