comparison pkg/controllers/importqueue.go @ 4791:1fef4679b07a

Added an endpoint GET api/imports/export to export imports.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 25 Oct 2019 13:03:50 +0200
parents 47922c1a088d
children d727641911a5
comparison
equal deleted inserted replaced
4790:6f3730196ebb 4791:1fef4679b07a
14 package controllers 14 package controllers
15 15
16 import ( 16 import (
17 "context" 17 "context"
18 "database/sql" 18 "database/sql"
19 "encoding/csv"
19 "encoding/json" 20 "encoding/json"
20 "fmt" 21 "fmt"
21 "log" 22 "log"
22 "net/http" 23 "net/http"
23 "strconv" 24 "strconv"
55 EXISTS(SELECT 1 FROM import.import_logs 56 EXISTS(SELECT 1 FROM import.import_logs
56 WHERE kind = 'warn'::log_type and import_id = imports.id) AS has_warnings 57 WHERE kind = 'warn'::log_type and import_id = imports.id) AS has_warnings
57 FROM import.imports 58 FROM import.imports
58 WHERE 59 WHERE
59 ` 60 `
61 selectExportSQL = `
62 SELECT
63 imports.id AS id,
64 state::varchar,
65 enqueued,
66 changed,
67 kind,
68 username,
69 (SELECT country FROM users.list_users lu
70 WHERE lu.username = import.imports.username) AS country,
71 signer,
72 EXISTS(SELECT 1 FROM import.import_logs
73 WHERE kind = 'warn'::log_type and import_id = imports.id) AS has_warnings,
74 data
75 FROM import.imports
76 WHERE
77 `
60 selectEnqueuedSQL = ` 78 selectEnqueuedSQL = `
61 SELECT enqueued FROM import.imports 79 SELECT enqueued FROM import.imports
62 WHERE 80 WHERE
63 ` 81 `
64 selectImportSummarySQL = ` 82 selectImportSummarySQL = `
87 type filledStmt struct { 105 type filledStmt struct {
88 stmt strings.Builder 106 stmt strings.Builder
89 args []interface{} 107 args []interface{}
90 } 108 }
91 109
92 func buildFilters(req *http.Request) (*filledStmt, *filledStmt, *filledStmt, error) { 110 func buildFilters(projection string, req *http.Request) (*filledStmt, *filledStmt, *filledStmt, error) {
93 111
94 var l, a, b filterAnd 112 var l, a, b filterAnd
95 113
96 var noBefore, noAfter bool 114 var noBefore, noAfter bool
97 115
173 fa.stmt.WriteString(selectEnqueuedSQL) 191 fa.stmt.WriteString(selectEnqueuedSQL)
174 fb.stmt.WriteString(selectEnqueuedSQL) 192 fb.stmt.WriteString(selectEnqueuedSQL)
175 193
176 var counting bool 194 var counting bool
177 195
178 switch count := strings.ToLower(req.FormValue("count")); count { 196 if projection != "" {
179 case "1", "t", "true": 197 fl.stmt.WriteString(projection)
180 counting = true 198 } else {
181 fl.stmt.WriteString(selectImportsCountSQL) 199 switch count := strings.ToLower(req.FormValue("count")); count {
182 default: 200 case "1", "t", "true":
183 fl.stmt.WriteString(selectImportsSQL) 201 counting = true
202 fl.stmt.WriteString(selectImportsCountSQL)
203 default:
204 fl.stmt.WriteString(selectImportsSQL)
205 }
184 } 206 }
185 207
186 if len(l) == 0 { 208 if len(l) == 0 {
187 fl.stmt.WriteString(" TRUE ") 209 fl.stmt.WriteString(" TRUE ")
188 } else { 210 } else {
229 return nil 251 return nil
230 } 252 }
231 return &models.ImportTime{Time: when.UTC()} 253 return &models.ImportTime{Time: when.UTC()}
232 } 254 }
233 255
256 func exportImports(rw http.ResponseWriter, req *http.Request) {
257
258 list, _, _, err := buildFilters(selectExportSQL, req)
259 if err != nil {
260 http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
261 return
262 }
263
264 rw.Header().Add("Content-Type", "text/csv")
265 out := csv.NewWriter(rw)
266
267 record := []string{
268 "#id",
269 "#kind",
270 "#enqueued",
271 "#changed",
272 "#user",
273 "#country",
274 "#signer",
275 "#state",
276 "#warnings",
277 "#source",
278 }
279
280 if err := out.Write(record); err != nil {
281 // Too late for HTTP status message.
282 log.Printf("error: %v\n", err)
283 return
284 }
285
286 conn := mw.GetDBConn(req)
287 ctx := req.Context()
288 var rows *sql.Rows
289 if rows, err = conn.QueryContext(ctx, list.stmt.String(), list.args...); err != nil {
290 log.Printf("error: %v\n", err)
291 return
292 }
293 defer rows.Close()
294
295 stringString := func(s sql.NullString) string {
296 if s.Valid {
297 return s.String
298 }
299 return ""
300 }
301
302 // Extract some meta infos from the import.
303 type Description interface {
304 Description() (string, error)
305 }
306
307 for rows.Next() {
308 var (
309 id int64
310 state string
311 enqueued time.Time
312 changed time.Time
313 kind string
314 user string
315 country string
316 signer sql.NullString
317 warnings bool
318 data string
319 description string
320 )
321 if err = rows.Scan(
322 &id,
323 &state,
324 &enqueued,
325 &changed,
326 &kind,
327 &user,
328 &country,
329 &signer,
330 &warnings,
331 &data,
332 ); err != nil {
333 return
334 }
335
336 // Do some introspection on the job to be more verbose.
337 if jc := imports.FindJobCreator(imports.JobKind(kind)); jc != nil {
338 job := jc.Create()
339 if err := common.FromJSONString(data, job); err != nil {
340 log.Printf("error: %v\n", err)
341 } else if desc, ok := job.(Description); ok {
342 if description, err = desc.Description(); err != nil {
343 log.Printf("error: %v\n", err)
344 }
345 }
346 }
347
348 record[0] = strconv.FormatInt(id, 10)
349 record[1] = kind
350 record[2] = enqueued.UTC().Format(common.TimeFormat)
351 record[3] = changed.UTC().Format(common.TimeFormat)
352 record[4] = user
353 record[5] = country
354 record[6] = stringString(signer)
355 record[7] = state
356 record[8] = strconv.FormatBool(warnings)
357 record[9] = strings.Replace(description, ",", "|", -1)
358
359 if err := out.Write(record); err != nil {
360 log.Printf("error: %v\n", err)
361 return
362 }
363 }
364
365 out.Flush()
366 if err := out.Error(); err != nil {
367 log.Printf("error: %v\n", err)
368 }
369
370 if err = rows.Err(); err != nil {
371 log.Printf("error: %v\n", err)
372 return
373 }
374 }
375
234 func listImports(req *http.Request) (jr mw.JSONResult, err error) { 376 func listImports(req *http.Request) (jr mw.JSONResult, err error) {
235 377
236 var list, before, after *filledStmt 378 var list, before, after *filledStmt
237 379
238 if list, before, after, err = buildFilters(req); err != nil { 380 if list, before, after, err = buildFilters("", req); err != nil {
239 return 381 return
240 } 382 }
241 383
242 ctx := req.Context() 384 ctx := req.Context()
243 385