Mercurial > gemma
view pkg/controllers/srimports.go @ 5637:7f9600ab5461
merge
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 14 Jun 2023 08:45:11 +0200 |
parents | 5f47eeea988d |
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) 2018 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann <sascha.teichmann@intevation.de> package controllers import ( "archive/zip" "encoding/hex" "fmt" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "time" "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/auth" "gemma.intevation.de/gemma/pkg/common" "gemma.intevation.de/gemma/pkg/config" "gemma.intevation.de/gemma/pkg/imports" "gemma.intevation.de/gemma/pkg/log" "gemma.intevation.de/gemma/pkg/misc" "gemma.intevation.de/gemma/pkg/models" mw "gemma.intevation.de/gemma/pkg/middleware" ) const ( soundingResultName = "soundingresult" maxSoundingResultSize = 50 * 1024 * 1024 ) func fetchSoundingResult(req *http.Request) (string, error) { // Check first if we have a token. if token := req.FormValue("token"); token != "" { if _, err := hex.DecodeString(token); err != nil { return "", err } dir := config.TmpDir() if dir == "" { dir = os.TempDir() } // XXX: This should hopefully be race-free enough. now := time.Now().Format("2006-15-04-05") dst := filepath.Join(dir, soundingResultName+"-"+token+"-"+now) if err := misc.UnmakeTempFile(token, dst); err != nil { return "", err } return dst, nil } return misc.StoreUploadedFileCheck( req, soundingResultName, "sr.zip", maxSoundingResultSize, true, ) } func fetchSoundingResultMetaOverrides(sr *imports.SoundingResult, req *http.Request) error { if v := req.FormValue("epsg"); v != "" { epsg, err := strconv.ParseUint(v, 10, 32) if err != nil { return err } srid := uint(epsg) sr.EPSG = &srid } if v := req.FormValue("date"); v != "" { date, err := time.Parse(common.DateFormat, v) if err != nil { return err } sr.Date = &models.Date{Time: date} } if v := req.FormValue("depth-reference"); v != "" { sr.DepthReference = &v } if v := req.FormValue("bottleneck"); v != "" { sr.Bottleneck = &v } if v := req.FormValue("negate-z"); v != "" { var negateZ bool switch strings.ToLower(v) { case "true", "1": negateZ = true case "false", "0": negateZ = false default: return fmt.Errorf("unknown negate-z '%s'", v) } sr.NegateZ = &negateZ } // Kept this in for compat. if v := req.FormValue("single-beam"); v != "" { var surveyType models.SurveyType switch strings.ToLower(v) { case "true", "1", "singlebeam", "single-beam": surveyType = models.SurveyTypeSingleBeam case "false", "0", "multibeam", "multi-beam": surveyType = models.SurveyTypeMultiBeam default: return fmt.Errorf("unknown single-beam '%s'", v) } sr.SurveyType = &surveyType } if v := req.FormValue("survey-type"); v != "" { var surveyType models.SurveyType switch strings.ToLower(v) { case "2", "marking": surveyType = models.SurveyTypeMarking case "true", "1", "singlebeam", "single-beam", "single": surveyType = models.SurveyTypeSingleBeam case "false", "0", "multibeam", "multi-beam", "multi": surveyType = models.SurveyTypeMultiBeam default: return fmt.Errorf("unknown survey-type '%s'", v) } sr.SurveyType = &surveyType } return nil } func importSoundingResult(rw http.ResponseWriter, req *http.Request) { sr := new(imports.SoundingResult) if err := fetchSoundingResultMetaOverrides(sr, req); err != nil { log.Errorf("%v\n", err) http.Error(rw, "error: "+err.Error(), http.StatusBadRequest) return } dir, err := fetchSoundingResult(req) if err != nil { log.Errorf("%v\n", err) http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) return } sr.Dir = dir serialized, err := common.ToJSONString(sr) if err != nil { log.Errorf("%v\n", err) http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) return } session, _ := auth.GetSession(req) sendEmail := req.FormValue("send-email") != "" jobID, err := imports.AddJob( imports.SRJobKind, time.Time{}, // due nil, // trys nil, // retry wait session.User, sendEmail, serialized) if err != nil { log.Errorf("%v\n", err) http.Error(rw, "error: "+err.Error(), http.StatusInternalServerError) return } log.Infof("added import #%d to queue\n", jobID) result := struct { ID int64 `json:"id"` }{ ID: jobID, } mw.SendJSON(rw, http.StatusCreated, &result) } func loadMeta(f *zip.File) (*models.SoundingResultMeta, error) { r, err := f.Open() if err != nil { return nil, err } defer r.Close() var m models.SoundingResultMeta return &m, m.Decode(r) } func uploadSoundingResult(req *http.Request) (jr mw.JSONResult, err error) { var dir string if dir, err = misc.StoreUploadedFileCheck( req, soundingResultName, "sr.zip", maxSoundingResultSize, true, ); err != nil { return } var messages []string srFile := filepath.Join(dir, "sr.zip") var zr *zip.ReadCloser var once sync.Once closeOnce := func() { zr.Close() } var isZIP bool if zr, err = zip.OpenReader(srFile); err != nil { messages = append(messages, fmt.Sprintf("%v - "+ "Trying TXT file mode.", err)) } else { isZIP = true defer once.Do(closeOnce) } var result struct { Token string `json:"token,omitempty"` Meta interface{} `json:"meta,omitempty"` Messages []string `json:"messages,omitempty"` } find := func(ext string) *zip.File { return common.FindInZIP(zr, ext) } var noXYZ bool if isZIP { if zr != nil { noXYZ = find(".xyz") == nil && find(".txt") == nil } if noXYZ { messages = append(messages, "no .xyz or .txt file found.") } if mj := find("meta.json"); mj == nil { messages = append(messages, "no 'meta.json' file found.") } else { if meta, err := loadMeta(mj); err != nil { messages = append(messages, fmt.Sprintf("'meta.json' found but invalid: %v", err)) } else { errs := meta.Validate(req.Context(), mw.JSONConn(req)) for _, err := range errs { messages = append(messages, fmt.Sprintf("invalid 'meta.json': %v", err)) } result.Meta = meta } } } if zr != nil { once.Do(closeOnce) } code := http.StatusCreated // If there are no XYZ data we cant help the user anyway. if noXYZ { code = http.StatusBadRequest if err2 := os.RemoveAll(dir); err2 != nil { log.Errorf("%v\n", err2) } } else if result.Token, err = misc.MakeTempFile(dir); err != nil { if err2 := os.RemoveAll(dir); err2 != nil { log.Errorf("%v\n", err2) } return } result.Messages = messages jr = mw.JSONResult{ Code: code, Result: &result, } return } func deleteSoundingUpload(rw http.ResponseWriter, req *http.Request) { token := mux.Vars(req)["token"] if _, err := hex.DecodeString(token); err != nil { http.Error(rw, "Invalid token", http.StatusBadRequest) return } if err := misc.DeleteTempFile(token); err != nil { http.Error(rw, fmt.Sprintf("error: %v", err), http.StatusInternalServerError) return } result := struct { Message string `json:"message"` }{ Message: fmt.Sprintf("Token %s deleted.", token), } mw.SendJSON(rw, http.StatusOK, &result) }