view pkg/imports/dsr.go @ 5670:b75d0b303328

Various fixes and improvements of gauges import: - Allow update of erased data (and thereby set erased to false) - Fix source_organization to work with ERDMS2 - Give ISRS of new and updated gauges in summary - Fixed reference of null pointers if revlevels are missing - Fixed reference of null pointer on update errors - Added ISRS to reference_code warning
author Sascha Wilde <wilde@sha-bang.de>
date Fri, 08 Dec 2023 17:29:56 +0100
parents f2204f91d286
children 6270951dda28
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) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>
//  * Sascha Wilde <wilde@intevation.de>

package imports

import (
	"context"
	"database/sql"
	"errors"

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

// DeleteSoundingResult is a Job to delete a sounding result from the database.
type DeleteSoundingResult struct {
	BottleneckID string      `json:"bottleneck-id"`
	Date         models.Date `json:"date-info"`
}

// Description gives a short info about relevant facts of this import.
func (dsr *DeleteSoundingResult) Description([]string) (string, error) {
	return dsr.BottleneckID + "|" + dsr.Date.Format(common.DateFormat), nil
}

// DSRJobKind is the import queue type identifier.
const DSRJobKind JobKind = "dsr"

type dsrJobCreator struct{}

func init() { RegisterJobCreator(DSRJobKind, dsrJobCreator{}) }

func (dsrJobCreator) Description() string { return "delete sounding result" }

func (dsrJobCreator) AutoAccept() bool { return false }

func (dsrJobCreator) Create() Job { return new(DeleteSoundingResult) }

func (dsrJobCreator) Depends() [2][]string {
	// Same as import.
	return srJobCreator{}.Depends()
}

const (
	dsrFindSQL = `
SELECT id FROM waterway.sounding_results
  WHERE bottleneck_id = $1
  AND date_info = $2
  AND staging_done
`
	dsrStageDoneSQL = `
DELETE FROM waterway.sounding_results
WHERE id IN (
  SELECT key from import.track_imports
  WHERE import_id = $1 AND
        deletion AND
        relation = 'waterway.sounding_results'::regclass)`
)

// StageDone finally removes the sounding result from the database.
func (dsrJobCreator) StageDone(
	ctx context.Context,
	tx *sql.Tx,
	id int64,
	_ Feedback,
) error {
	result, err := tx.ExecContext(ctx, dsrStageDoneSQL, id)
	if err != nil {
		return err
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return err
	}
	if rows == 0 {
		return errors.New("deletion failed. " +
			"Propably Data outside the area of responsibility." +
			"Or the data was already deleted by another user")
	}
	return err
}

// CleanUp of a sounding result delete import is a NOP.
func (*DeleteSoundingResult) CleanUp() error { return nil }

// Do prepares the deletion of the sounding result.
func (dsr *DeleteSoundingResult) Do(
	ctx context.Context,
	importID int64,
	conn *sql.Conn,
	feedback Feedback,
) (interface{}, error) {

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

	feedback.Info("SR: bottleneck-id: %v, date-info: %v",
		dsr.BottleneckID, dsr.Date.Time)

	var id int64
	switch err := tx.QueryRowContext(ctx, dsrFindSQL,
		dsr.BottleneckID, dsr.Date.Time).Scan(&id); err {
	case sql.ErrNoRows:
		return nil, UnchangedError("Sounding result does not exist")
	case nil:
		// Continue
	default:
		return nil, err
	}

	feedback.Info("Prepare deletion of sounding result with id %d", id)

	if _, err := tx.ExecContext(
		ctx,
		trackImportDeletionSQL,
		importID,
		"waterway.sounding_results",
		id,
		true,
	); err != nil {
		return nil, err
	}

	if err := tx.Commit(); err != nil {
		return nil, err
	}

	return dsr, nil
}