Skip to content

Commit 21ec75f

Browse files
committed
Column mapping for event log support added
Closes #3
1 parent 90c451c commit 21ec75f

12 files changed

+318
-76
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ RUN apt-get update && apt-get install -y \
77
WORKDIR /srv/webapp
88
ADD build/linux-amd64/ .
99
ADD run_analysis.bash .
10+
ADD run_analysis_columns.bash .
1011

1112
EXPOSE 8080
1213
CMD ["/srv/webapp/waiting-time-backend", "-host", "localhost", "-port", "8080"]

app/app.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
// Version: 1.0.0
99
//
1010
// Consumes:
11-
// - application/json
11+
// - application/json
1212
//
1313
// Produces:
14-
// - application/json
14+
// - application/json
1515
//
1616
// swagger:meta
1717
package app
@@ -310,7 +310,7 @@ func (app *Application) jobResultFromPath(filePath string) (*model.JobResult, er
310310
return result, err
311311
}
312312

313-
func (app *Application) newJobFromRequestBody(body io.ReadCloser) (*model.Job, error) {
313+
func (app *Application) newJobFromRequestBody(body io.ReadCloser, columnMapping map[string]string) (*model.Job, error) {
314314
defer func() {
315315
if err := body.Close(); err != nil {
316316
app.logger.Printf("error closing request body: %s", err.Error())
@@ -375,5 +375,6 @@ func (app *Application) newJobFromRequestBody(body io.ReadCloser) (*model.Job, e
375375
EventLogFromRequestBody: true,
376376
CreatedAt: time.Now(),
377377
Dir: jobDir,
378+
ColumnMapping: columnMapping,
378379
}, nil
379380
}

app/app_runAnalysis_unix.go

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package app
55
import (
66
"bytes"
77
"context"
8+
"encoding/json"
89
"errors"
910
"fmt"
1011
"github.com/AutomatedProcessImprovement/waiting-time-backend/model"
@@ -20,12 +21,44 @@ func (app *Application) runAnalysis(ctx context.Context, eventLogName string, jo
2021
return err
2122
}
2223

23-
eventLogPath := path.Join(jobDir, eventLogName)
24-
scriptName := "run_analysis.bash"
24+
// custom column mapping if it was provided with the API request
25+
26+
var columnMapping string
27+
28+
if job.ColumnMapping != nil {
29+
b, err := json.Marshal(job.ColumnMapping)
30+
if err != nil {
31+
return fmt.Errorf("error marshalling column mapping: %s", err.Error())
32+
}
33+
columnMapping = string(b)
34+
}
35+
36+
// analysis script name
37+
38+
var scriptName string
39+
2540
if app.config.DevelopmentMode {
26-
scriptName = "run_analysis_dev.bash"
41+
if columnMapping == "" {
42+
scriptName = "run_analysis_dev.bash"
43+
} else {
44+
scriptName = "run_analysis_dev_columns.bash"
45+
}
46+
} else if columnMapping == "" {
47+
scriptName = "run_analysis.bash"
48+
} else {
49+
scriptName = "run_analysis_columns.bash"
50+
}
51+
52+
// shell command
53+
54+
var args string
55+
56+
eventLogPath := path.Join(jobDir, eventLogName)
57+
if columnMapping == "" {
58+
args = fmt.Sprintf("bash %s %s %s", scriptName, eventLogPath, jobDir)
59+
} else {
60+
args = fmt.Sprintf("bash %s %s %s %q", scriptName, eventLogPath, jobDir, columnMapping)
2761
}
28-
args := fmt.Sprintf("bash %s %s %s", scriptName, eventLogPath, jobDir)
2962

3063
cmd := exec.CommandContext(ctx, "sh", "-c", args)
3164

@@ -44,7 +77,7 @@ func (app *Application) runAnalysis(ctx context.Context, eventLogName string, jo
4477
case <-ctx.Done():
4578
// NOTE: unix specific code
4679
if err = syscall.Kill(-1*cmd.Process.Pid, syscall.SIGKILL); err != nil {
47-
app.logger.Printf("Error cancelling job: %s", err.Error())
80+
app.logger.Printf("Cannot cancel the job: %s. But it might be okay if the job finished successfully", err.Error())
4881
}
4982
}
5083
}()

app/app_runAnalysis_windows.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,41 @@ func (app *Application) runAnalysis(ctx context.Context, eventLogName string, jo
1717
return err
1818
}
1919

20-
eventLogPath := path.Join(jobDir, eventLogName)
21-
scriptName := "run_analysis.bash"
20+
// custom column mapping if it was provided with the API request
21+
22+
var columnMapping string
23+
24+
if job.ColumnMapping != nil {
25+
b, err := json.Marshal(job.ColumnMapping)
26+
if err != nil {
27+
return fmt.Errorf("error marshalling column mapping: %s", err.Error())
28+
}
29+
columnMapping = string(b)
30+
}
31+
32+
// analysis script name
33+
34+
var scriptName string
35+
2236
if app.config.DevelopmentMode {
37+
// NOTE: we don't use columns mapping in development mode, modify the log accordingly
2338
scriptName = "run_analysis_dev.bash"
39+
} else if columnMapping == "" {
40+
scriptName = "run_analysis.bash"
41+
} else {
42+
scriptName = "run_analysis_columns.bash"
43+
}
44+
45+
// shell command
46+
47+
var args string
48+
49+
eventLogPath := path.Join(jobDir, eventLogName)
50+
if columnMapping == "" {
51+
args = fmt.Sprintf("bash %s %s %s", scriptName, eventLogPath, jobDir)
52+
} else {
53+
args = fmt.Sprintf("bash %s %s %s %q", scriptName, eventLogPath, jobDir, columnMapping)
2454
}
25-
args := fmt.Sprintf("bash %s %s %s", scriptName, eventLogPath, jobDir)
2655

2756
cmd := exec.CommandContext(ctx, "sh", "-c", args)
2857

@@ -38,7 +67,7 @@ func (app *Application) runAnalysis(ctx context.Context, eventLogName string, jo
3867
case <-ctx.Done():
3968
// NOTE: Windows specific code. Not sure if it kills child processes
4069
if err = cmd.Process.Kill(); err != nil {
41-
app.logger.Printf("Error cancelling job: %s", err.Error())
70+
app.logger.Printf("Cannot cancel the job: %s. But it might be okay if the job finished successfully", err.Error())
4271
}
4372
}
4473
}()

app/handlers.go

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,16 @@ func SwaggerJSON(app *Application) http.HandlerFunc {
5454
// description: Callback request
5555
// required: true
5656
// schema:
57-
// $ref: '#/definitions/ApiCallbackRequest'
57+
// $ref: '#/definitions/ApiCallbackRequest'
5858
//
5959
// Responses:
60-
// default:
61-
// schema:
62-
// $ref: '#/definitions/ApiResponseError'
63-
// 200:
64-
// schema:
65-
// $ref: '#/definitions/ApiCallbackRequest'
60+
//
61+
// default:
62+
// schema:
63+
// $ref: '#/definitions/ApiResponseError'
64+
// 200:
65+
// schema:
66+
// $ref: '#/definitions/ApiCallbackRequest'
6667
func SampleCallback(app *Application) http.HandlerFunc {
6768
return func(w http.ResponseWriter, r *http.Request) {
6869
var payload model.ApiCallbackRequest
@@ -85,9 +86,10 @@ func SampleCallback(app *Application) http.HandlerFunc {
8586
//
8687
// ---
8788
// Responses:
88-
// default:
89-
// schema:
90-
// $ref: '#/definitions/ApiJobsResponse'
89+
//
90+
// default:
91+
// schema:
92+
// $ref: '#/definitions/ApiJobsResponse'
9193
func GetJobs(app *Application) http.HandlerFunc {
9294
return func(w http.ResponseWriter, r *http.Request) {
9395
apiResponse := model.ApiJobsResponse{Jobs: app.queue.Jobs}
@@ -114,15 +116,16 @@ func GetJobs(app *Application) http.HandlerFunc {
114116
// description: Description of a job
115117
// required: true
116118
// schema:
117-
// $ref: '#/definitions/ApiRequest'
119+
// $ref: '#/definitions/ApiRequest'
118120
//
119121
// Responses:
120-
// default:
121-
// schema:
122-
// $ref: '#/definitions/ApiResponseError'
123-
// 200:
124-
// schema:
125-
// $ref: '#/definitions/ApiSingleJobResponse'
122+
//
123+
// default:
124+
// schema:
125+
// $ref: '#/definitions/ApiResponseError'
126+
// 200:
127+
// schema:
128+
// $ref: '#/definitions/ApiSingleJobResponse'
126129
func PostJob(app *Application) http.HandlerFunc {
127130
return func(w http.ResponseWriter, r *http.Request) {
128131
// Read the event log from the request body
@@ -142,7 +145,7 @@ func PostJob(app *Application) http.HandlerFunc {
142145
}
143146
_ = r.Body.Close()
144147

145-
job, err := model.NewJob(apiRequest.EventLogURL_, apiRequest.CallbackEndpointURL_, app.config.ResultsDir)
148+
job, err := model.NewJob(apiRequest.EventLogURL_, apiRequest.CallbackEndpointURL_, apiRequest.ColumnMapping, app.config.ResultsDir)
146149
if err != nil {
147150
message := fmt.Sprintf("cannot create a job; %s", err)
148151
reply(w, http.StatusBadRequest, model.ApiResponseError{Error: message}, app.logger)
@@ -168,7 +171,9 @@ func PostJob(app *Application) http.HandlerFunc {
168171

169172
func PostJobFromBody(app *Application) http.HandlerFunc {
170173
return func(w http.ResponseWriter, r *http.Request) {
171-
job, err := app.newJobFromRequestBody(r.Body)
174+
columnMapping := columnMappingFromRequest(r)
175+
176+
job, err := app.newJobFromRequestBody(r.Body, columnMapping)
172177
if err != nil {
173178
message := fmt.Sprintf("failed to create a job from the request body; %s", err)
174179
reply(w, http.StatusBadRequest, model.ApiResponseError{Error: message}, app.logger)
@@ -198,12 +203,13 @@ func PostJobFromBody(app *Application) http.HandlerFunc {
198203
//
199204
// ---
200205
// Responses:
201-
// default:
202-
// schema:
203-
// $ref: '#/definitions/ApiResponseError'
204-
// 200:
205-
// schema:
206-
// $ref: '#/definitions/ApiJobsResponse'
206+
//
207+
// default:
208+
// schema:
209+
// $ref: '#/definitions/ApiResponseError'
210+
// 200:
211+
// schema:
212+
// $ref: '#/definitions/ApiJobsResponse'
207213
func DeleteJobs(app *Application) http.HandlerFunc {
208214
return func(w http.ResponseWriter, r *http.Request) {
209215
err := app.queue.Clear()
@@ -249,12 +255,13 @@ func DeleteJobs(app *Application) http.HandlerFunc {
249255
// type: string
250256
//
251257
// Responses:
252-
// default:
253-
// schema:
254-
// $ref: '#/definitions/ApiResponseError'
255-
// 200:
256-
// schema:
257-
// $ref: '#/definitions/ApiSingleJobResponse'
258+
//
259+
// default:
260+
// schema:
261+
// $ref: '#/definitions/ApiResponseError'
262+
// 200:
263+
// schema:
264+
// $ref: '#/definitions/ApiSingleJobResponse'
258265
func GetJobByID(app *Application) http.HandlerFunc {
259266
return func(w http.ResponseWriter, r *http.Request) {
260267
var apiResponse model.ApiSingleJobResponse
@@ -292,12 +299,13 @@ func GetJobByID(app *Application) http.HandlerFunc {
292299
// type: string
293300
//
294301
// Responses:
295-
// default:
296-
// schema:
297-
// $ref: '#/definitions/ApiResponseError'
298-
// 200:
299-
// schema:
300-
// $ref: '#/definitions/ApiSingleJobResponse'
302+
//
303+
// default:
304+
// schema:
305+
// $ref: '#/definitions/ApiResponseError'
306+
// 200:
307+
// schema:
308+
// $ref: '#/definitions/ApiSingleJobResponse'
301309
func CancelJobByID(app *Application) http.HandlerFunc {
302310
return func(w http.ResponseWriter, r *http.Request) {
303311
vars := mux.Vars(r)
@@ -345,3 +353,52 @@ func checkError(err error, message string, logger *log.Logger) {
345353
}
346354
logger.Printf("%s; %s", message, err)
347355
}
356+
357+
func columnMappingFromRequest(r *http.Request) map[string]string {
358+
vars := mapFromRawQuery(r.URL.RawQuery)
359+
360+
var (
361+
caseID string
362+
activity string
363+
resource string
364+
startTime string
365+
endTime string
366+
ok bool
367+
)
368+
369+
columnMapping := make(map[string]string)
370+
371+
caseID, ok = vars["case"]
372+
if ok {
373+
columnMapping["case"] = caseID
374+
}
375+
activity, ok = vars["activity"]
376+
if ok {
377+
columnMapping["activity"] = activity
378+
}
379+
resource, ok = vars["resource"]
380+
if ok {
381+
columnMapping["resource"] = resource
382+
}
383+
startTime, ok = vars["start_timestamp"]
384+
if ok {
385+
columnMapping["start_timestamp"] = startTime
386+
}
387+
endTime, ok = vars["end_timestamp"]
388+
if ok {
389+
columnMapping["end_timestamp"] = endTime
390+
}
391+
392+
return columnMapping
393+
}
394+
395+
func mapFromRawQuery(query string) map[string]string {
396+
queryMap := make(map[string]string)
397+
for _, pair := range strings.Split(query, "&") {
398+
values := strings.Split(pair, "=")
399+
if len(values) == 2 {
400+
queryMap[values[0]] = values[1]
401+
}
402+
}
403+
return queryMap
404+
}

app/queue_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func TestQueue_Clear(t *testing.T) {
1212
const resultsDir = "../assets/results"
1313
var rootFS = os.DirFS(resultsDir)
1414

15-
j, err := model.NewJob(nil, nil, resultsDir)
15+
j, err := model.NewJob(nil, nil, nil, resultsDir)
1616
if err != nil {
1717
t.Fatal(err)
1818
}

0 commit comments

Comments
 (0)