Implement phone normalization
Implement number filter Implement daily stats Implement put APIs
Showing
4 changed files
with
172 additions
and
29 deletions
... | @@ -66,22 +66,26 @@ func NewApp(config Config) *App { | ... | @@ -66,22 +66,26 @@ func NewApp(config Config) *App { |
66 | 66 | ||
67 | extraction.GET("/calls", app.GetCalls) | 67 | extraction.GET("/calls", app.GetCalls) |
68 | extraction.POST("/calls", app.PostCall) | 68 | extraction.POST("/calls", app.PostCall) |
69 | + extraction.PUT("/calls/:id", app.PutCall) | ||
69 | extraction.DELETE("/calls/:id", app.DeleteCall) | 70 | extraction.DELETE("/calls/:id", app.DeleteCall) |
70 | 71 | ||
71 | extraction.GET("/messages", app.GetMessages) | 72 | extraction.GET("/messages", app.GetMessages) |
72 | extraction.POST("/messages", app.PostMessage) | 73 | extraction.POST("/messages", app.PostMessage) |
74 | + extraction.PUT("/messages/:id", app.PutMessage) | ||
73 | extraction.DELETE("/messages/:id", app.DeleteMessage) | 75 | extraction.DELETE("/messages/:id", app.DeleteMessage) |
74 | 76 | ||
75 | extraction.GET("/calls/analyses", app.GetCallsAnalyses) | 77 | extraction.GET("/calls/analyses", app.GetCallsAnalyses) |
76 | 78 | ||
77 | extraction.GET("/apps/analyses", app.GetAppsAnalyses) | 79 | extraction.GET("/apps/analyses", app.GetAppsAnalyses) |
78 | extraction.POST("/apps/analyses", app.PostAppAnalysis) | 80 | extraction.POST("/apps/analyses", app.PostAppAnalysis) |
81 | + extraction.PUT("/apps/analyses/:package", app.PutAppAnalysis) | ||
79 | extraction.DELETE("/apps/analyses/:package", app.DeleteAppAnalysis) | 82 | extraction.DELETE("/apps/analyses/:package", app.DeleteAppAnalysis) |
80 | 83 | ||
81 | extraction.GET("/messages/analyses", app.GetMessagesAnalyses) | 84 | extraction.GET("/messages/analyses", app.GetMessagesAnalyses) |
82 | extraction.GET("/processes", app.GetProcesses) | 85 | extraction.GET("/processes", app.GetProcesses) |
83 | extraction.GET("/alarms", app.GetAlarms) | 86 | extraction.GET("/alarms", app.GetAlarms) |
84 | extraction.GET("/schedules", app.GetSchedules) | 87 | extraction.GET("/schedules", app.GetSchedules) |
88 | + extraction.GET("/dailies", app.GetDailyContacts) | ||
85 | 89 | ||
86 | app.echo.GET("/graph/:extractions", app.GetGraph) | 90 | app.echo.GET("/graph/:extractions", app.GetGraph) |
87 | 91 | ... | ... |
... | @@ -4,6 +4,7 @@ import ( | ... | @@ -4,6 +4,7 @@ import ( |
4 | "context" | 4 | "context" |
5 | "fmt" | 5 | "fmt" |
6 | "net/http" | 6 | "net/http" |
7 | + "strconv" | ||
7 | "time" | 8 | "time" |
8 | 9 | ||
9 | "github.com/labstack/echo/v4" | 10 | "github.com/labstack/echo/v4" |
... | @@ -16,7 +17,7 @@ type Call struct { | ... | @@ -16,7 +17,7 @@ type Call struct { |
16 | ID int `json:"id" db:"id"` | 17 | ID int `json:"id" db:"id"` |
17 | Type int `json:"type" db:"type"` | 18 | Type int `json:"type" db:"type"` |
18 | Name *string `json:"name" db:"name"` | 19 | Name *string `json:"name" db:"name"` |
19 | - Number int `json:"number" db:"number"` | 20 | + Number string `json:"number" db:"number"` |
20 | Duration int `json:"duration" db:"duration"` | 21 | Duration int `json:"duration" db:"duration"` |
21 | Date time.Time `json:"date" db:"date"` | 22 | Date time.Time `json:"date" db:"date"` |
22 | } | 23 | } |
... | @@ -25,7 +26,15 @@ func (app *App) GetCalls(c echo.Context) error { | ... | @@ -25,7 +26,15 @@ func (app *App) GetCalls(c echo.Context) error { |
25 | calls := []Call{} | 26 | calls := []Call{} |
26 | 27 | ||
27 | query := `SELECT * FROM calls WHERE extraction_no=?` | 28 | query := `SELECT * FROM calls WHERE extraction_no=?` |
28 | - app.db.Unsafe().Select(&calls, query, c.Param("no")) | 29 | + vals := []interface{}{c.Param("no")} |
30 | + | ||
31 | + number := c.QueryParam("number") | ||
32 | + if len(number) > 0 { | ||
33 | + query += " AND `number`=?" | ||
34 | + vals = append(vals, NormalizeNumber(number)) | ||
35 | + } | ||
36 | + | ||
37 | + app.db.Unsafe().Select(&calls, query, vals...) | ||
29 | 38 | ||
30 | return c.JSON(http.StatusOK, calls) | 39 | return c.JSON(http.StatusOK, calls) |
31 | } | 40 | } |
... | @@ -45,6 +54,28 @@ func (app *App) PostCall(c echo.Context) error { | ... | @@ -45,6 +54,28 @@ func (app *App) PostCall(c echo.Context) error { |
45 | return c.NoContent(http.StatusNoContent) | 54 | return c.NoContent(http.StatusNoContent) |
46 | } | 55 | } |
47 | 56 | ||
57 | +func (app *App) PutCall(c echo.Context) error { | ||
58 | + call := Call{} | ||
59 | + if err := c.Bind(&call); err != nil { | ||
60 | + return err | ||
61 | + } | ||
62 | + | ||
63 | + query := "UPDATE calls SET `id`=?, `type`=?, `name`=?, `number`=?, `duration`=?, `date`=? " + | ||
64 | + "WHERE `extraction_no`=? AND `id`=?" | ||
65 | + res, err := app.db.Exec(query, call.ID, call.Type, call.Name, call.Number, call.Duration, call.Date, c.Param("no"), c.Param("id")) | ||
66 | + if err != nil { | ||
67 | + return nil | ||
68 | + } | ||
69 | + | ||
70 | + if rows, err := res.RowsAffected(); err != nil { | ||
71 | + return err | ||
72 | + } else if rows == 0 { | ||
73 | + return echo.NewHTTPError(http.StatusNotFound, "Can not find the call") | ||
74 | + } else { | ||
75 | + return c.NoContent(http.StatusNoContent) | ||
76 | + } | ||
77 | + | ||
78 | +} | ||
48 | func (app *App) DeleteCall(c echo.Context) error { | 79 | func (app *App) DeleteCall(c echo.Context) error { |
49 | query := "DELETE FROM calls WHERE `extraction_no`=? AND `id`=?" | 80 | query := "DELETE FROM calls WHERE `extraction_no`=? AND `id`=?" |
50 | res, err := app.db.Exec(query, c.Param("no"), c.Param("id")) | 81 | res, err := app.db.Exec(query, c.Param("no"), c.Param("id")) |
... | @@ -120,6 +151,34 @@ func (app *App) PostAppAnalysis(c echo.Context) error { | ... | @@ -120,6 +151,34 @@ func (app *App) PostAppAnalysis(c echo.Context) error { |
120 | return c.NoContent(http.StatusNoContent) | 151 | return c.NoContent(http.StatusNoContent) |
121 | } | 152 | } |
122 | 153 | ||
154 | +func (app *App) PutAppAnalysis(c echo.Context) error { | ||
155 | + info := AppInfo{} | ||
156 | + if err := c.Bind(&info); err != nil { | ||
157 | + return err | ||
158 | + } | ||
159 | + | ||
160 | + query := "UPDATE apps SET `package_name`=?, `name`=?, `version`=?, " + | ||
161 | + "`wifi_usage`=?, `cellular_usage`=?, `last_used`=?, `foreground_time`=? " + | ||
162 | + "WHERE `extraction_no`=? AND `package_name`=?" | ||
163 | + res, err := app.db.Exec( | ||
164 | + query, | ||
165 | + info.PackageName, info.Name, info.Version, | ||
166 | + info.WifiUsage, info.CellularUsage, info.LastUsed, info.ForegroundTime, | ||
167 | + c.Param("no"), c.Param("package"), | ||
168 | + ) | ||
169 | + if err != nil { | ||
170 | + return err | ||
171 | + } | ||
172 | + | ||
173 | + if rows, err := res.RowsAffected(); err != nil { | ||
174 | + return err | ||
175 | + } else if rows == 0 { | ||
176 | + return echo.NewHTTPError(http.StatusNotFound, "Can not find the app") | ||
177 | + } else { | ||
178 | + return c.NoContent(http.StatusNoContent) | ||
179 | + } | ||
180 | +} | ||
181 | + | ||
123 | func (app *App) DeleteAppAnalysis(c echo.Context) error { | 182 | func (app *App) DeleteAppAnalysis(c echo.Context) error { |
124 | query := "DELETE FROM apps WHERE `extraction_no`=? AND `package_name`=?" | 183 | query := "DELETE FROM apps WHERE `extraction_no`=? AND `package_name`=?" |
125 | res, err := app.db.Exec(query, c.Param("no"), c.Param("package")) | 184 | res, err := app.db.Exec(query, c.Param("no"), c.Param("package")) |
... | @@ -149,6 +208,15 @@ func (app *App) GetMessages(c echo.Context) error { | ... | @@ -149,6 +208,15 @@ func (app *App) GetMessages(c echo.Context) error { |
149 | 208 | ||
150 | no := c.Param("no") | 209 | no := c.Param("no") |
151 | 210 | ||
211 | + query := "SELECT * FROM messages WHERE extraction_no=?" | ||
212 | + vals := []interface{}{no} | ||
213 | + | ||
214 | + number := c.QueryParam("number") | ||
215 | + if len(number) > 0 { | ||
216 | + query += " AND `address`=?" | ||
217 | + vals = append(vals, NormalizeNumber(number)) | ||
218 | + } | ||
219 | + | ||
152 | q := c.QueryParam("q") | 220 | q := c.QueryParam("q") |
153 | if len(q) > 0 { | 221 | if len(q) > 0 { |
154 | res, err := app.es.Search("messages-" + no). | 222 | res, err := app.es.Search("messages-" + no). |
... | @@ -159,19 +227,22 @@ func (app *App) GetMessages(c echo.Context) error { | ... | @@ -159,19 +227,22 @@ func (app *App) GetMessages(c echo.Context) error { |
159 | return err | 227 | return err |
160 | } | 228 | } |
161 | 229 | ||
230 | + query += " AND `id`=?" | ||
231 | + | ||
162 | for _, hit := range res.Hits.Hits { | 232 | for _, hit := range res.Hits.Hits { |
163 | message := Message{} | 233 | message := Message{} |
164 | 234 | ||
165 | - app.db.Unsafe().Get( | 235 | + err := app.db.Unsafe().Get( |
166 | &message, | 236 | &message, |
167 | - "SELECT * FROM messages WHERE extraction_no=? AND `id`=?", | 237 | + query, append(vals, hit.Id)..., |
168 | - no, hit.Id, | ||
169 | ) | 238 | ) |
170 | 239 | ||
171 | - messages = append(messages, message) | 240 | + if err == nil { |
241 | + messages = append(messages, message) | ||
242 | + } | ||
172 | } | 243 | } |
173 | } else { | 244 | } else { |
174 | - app.db.Unsafe().Select(&messages, `SELECT * FROM messages WHERE extraction_no=?`, no) | 245 | + app.db.Unsafe().Select(&messages, query, vals...) |
175 | } | 246 | } |
176 | 247 | ||
177 | return c.JSON(http.StatusOK, messages) | 248 | return c.JSON(http.StatusOK, messages) |
... | @@ -193,6 +264,29 @@ func (app *App) PostMessage(c echo.Context) error { | ... | @@ -193,6 +264,29 @@ func (app *App) PostMessage(c echo.Context) error { |
193 | return c.NoContent(http.StatusNoContent) | 264 | return c.NoContent(http.StatusNoContent) |
194 | } | 265 | } |
195 | 266 | ||
267 | +func (app *App) PutMessage(c echo.Context) error { | ||
268 | + message := Message{} | ||
269 | + if err := c.Bind(&message); err != nil { | ||
270 | + return err | ||
271 | + } | ||
272 | + | ||
273 | + query := "UPDATE messages SET `id`=?, `type`=?, `address`=?, `body`=?, `date`=? " + | ||
274 | + "WHERE `extraction_no`=? AND `id`=?" | ||
275 | + res, err := app.db.Exec(query, message.ID, message.Type, message.Address, message.Body, message.Date, c.Param("no"), c.Param("id")) | ||
276 | + | ||
277 | + if err != nil { | ||
278 | + return err | ||
279 | + } | ||
280 | + | ||
281 | + if rows, err := res.RowsAffected(); err != nil { | ||
282 | + return err | ||
283 | + } else if rows == 0 { | ||
284 | + return echo.NewHTTPError(http.StatusNotFound, "Can not find the message") | ||
285 | + } else { | ||
286 | + return c.NoContent(http.StatusNoContent) | ||
287 | + } | ||
288 | +} | ||
289 | + | ||
196 | func (app *App) DeleteMessage(c echo.Context) error { | 290 | func (app *App) DeleteMessage(c echo.Context) error { |
197 | query := "DELETE FROM messages WHERE `extraction_no`=? AND `id`=?" | 291 | query := "DELETE FROM messages WHERE `extraction_no`=? AND `id`=?" |
198 | res, err := app.db.Exec(query, c.Param("no"), c.Param("id")) | 292 | res, err := app.db.Exec(query, c.Param("no"), c.Param("id")) |
... | @@ -291,3 +385,33 @@ func (app *App) GetSchedules(c echo.Context) error { | ... | @@ -291,3 +385,33 @@ func (app *App) GetSchedules(c echo.Context) error { |
291 | 385 | ||
292 | return c.JSON(http.StatusOK, schedules) | 386 | return c.JSON(http.StatusOK, schedules) |
293 | } | 387 | } |
388 | + | ||
389 | +type DailyContact struct { | ||
390 | + Date time.Time `json:"date" db:"date"` | ||
391 | + Calls int `json:"calls" db:"calls"` | ||
392 | + Messages int `json:"messages" db:"messages"` | ||
393 | +} | ||
394 | + | ||
395 | +func (app *App) GetDailyContacts(c echo.Context) error { | ||
396 | + query := "SELECT `date`, `calls`, `messages` FROM daily_contacts WHERE `extraction_no`=?" | ||
397 | + result := []DailyContact{} | ||
398 | + if err := app.db.Select(&result, query, c.Param("no")); err != nil { | ||
399 | + fmt.Println(err) | ||
400 | + return err | ||
401 | + } | ||
402 | + | ||
403 | + return c.JSON(http.StatusOK, result) | ||
404 | +} | ||
405 | + | ||
406 | +func NormalizeNumber(number string) string { | ||
407 | + if len(number) > 0 && number[0] == '+' { | ||
408 | + return number | ||
409 | + } | ||
410 | + | ||
411 | + integer, err := strconv.Atoi(number) | ||
412 | + if err != nil { | ||
413 | + return number | ||
414 | + } | ||
415 | + | ||
416 | + return fmt.Sprintf("+82%d", integer) | ||
417 | +} | ... | ... |
... | @@ -83,7 +83,8 @@ func (app *App) PostExtractions(c echo.Context) error { | ... | @@ -83,7 +83,8 @@ func (app *App) PostExtractions(c echo.Context) error { |
83 | if err == nil { | 83 | if err == nil { |
84 | for rows.Next() { | 84 | for rows.Next() { |
85 | vals, _ := rows.SliceScan() | 85 | vals, _ := rows.SliceScan() |
86 | - _, err = tx.Exec("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...) | 86 | + vals[3] = NormalizeNumber(string(vals[3].([]byte))) |
87 | + tx.Exec("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...) | ||
87 | } | 88 | } |
88 | } | 89 | } |
89 | 90 | ||
... | @@ -117,6 +118,7 @@ func (app *App) PostExtractions(c echo.Context) error { | ... | @@ -117,6 +118,7 @@ func (app *App) PostExtractions(c echo.Context) error { |
117 | 118 | ||
118 | for rows.Next() { | 119 | for rows.Next() { |
119 | vals, _ := rows.SliceScan() | 120 | vals, _ := rows.SliceScan() |
121 | + vals[2] = NormalizeNumber(string(vals[2].([]byte))) | ||
120 | tx.Exec("INSERT INTO messages VALUES (?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...) | 122 | tx.Exec("INSERT INTO messages VALUES (?, ?, ?, ?, ?, ?)", append([]interface{}{extNo}, vals...)...) |
121 | app.es.Index().Index(idxName).Id(fmt.Sprint(vals[0])).BodyJson(echo.Map{"content": string(vals[3].([]byte))}).Do(context.Background()) | 123 | app.es.Index().Index(idxName).Id(fmt.Sprint(vals[0])).BodyJson(echo.Map{"content": string(vals[3].([]byte))}).Do(context.Background()) |
122 | } | 124 | } | ... | ... |
... | @@ -27,36 +27,49 @@ cursor = db.cursor() | ... | @@ -27,36 +27,49 @@ cursor = db.cursor() |
27 | 27 | ||
28 | ext_no = int(sys.argv[1]) | 28 | ext_no = int(sys.argv[1]) |
29 | 29 | ||
30 | -cursor.execute("SELECT `type`, `number`, `duration` FROM calls WHERE `extraction_no`=%s", (ext_no)) | 30 | +cursor.execute("SELECT `type`, `number`, `duration`, `date` FROM calls WHERE `extraction_no`=%s", (ext_no)) |
31 | calls = cursor.fetchall() | 31 | calls = cursor.fetchall() |
32 | 32 | ||
33 | -cursor.execute("SELECT `type`, `address` FROM messages WHERE `extraction_no`=%s", (ext_no)) | 33 | +cursor.execute("SELECT `type`, `address`, `date` FROM messages WHERE `extraction_no`=%s", (ext_no)) |
34 | messages = cursor.fetchall() | 34 | messages = cursor.fetchall() |
35 | 35 | ||
36 | regions = { | 36 | regions = { |
37 | - '02': 'Seoul', | 37 | + '+822': 'Seoul', |
38 | - '031': 'Gyeonggi', | 38 | + '+8231': 'Gyeonggi', |
39 | - '032': 'Incheon', | 39 | + '+8232': 'Incheon', |
40 | - '033': 'Gangwon', | 40 | + '+8233': 'Gangwon', |
41 | - '041': 'Chungnam', | 41 | + '+8241': 'Chungnam', |
42 | - '042': 'Daejeon', | 42 | + '+8242': 'Daejeon', |
43 | - '043': 'Chungbuk', | 43 | + '+8243': 'Chungbuk', |
44 | - '044': 'Sejong', | 44 | + '+8244': 'Sejong', |
45 | - '051': 'Busan', | 45 | + '+8251': 'Busan', |
46 | - '052': 'Ulsan', | 46 | + '+8252': 'Ulsan', |
47 | - '053': 'Daegu', | 47 | + '+8253': 'Daegu', |
48 | - '054': 'Gyeongbuk', | 48 | + '+8254': 'Gyeongbuk', |
49 | - '055': 'Gyeongnam', | 49 | + '+8255': 'Gyeongnam', |
50 | - '061': 'Jeonnam', | 50 | + '+8261': 'Jeonnam', |
51 | - '062': 'Gwangju', | 51 | + '+8262': 'Gwangju', |
52 | - '063': 'Jeonbuk', | 52 | + '+8263': 'Jeonbuk', |
53 | - '064': 'Jeju' | 53 | + '+8264': 'Jeju' |
54 | } | 54 | } |
55 | 55 | ||
56 | spark = SparkSession.builder.getOrCreate() | 56 | spark = SparkSession.builder.getOrCreate() |
57 | 57 | ||
58 | -cdf = spark.createDataFrame(list(calls), schema=['type', 'number', 'duration']) | 58 | +cdf = spark.createDataFrame(list(calls), schema=['type', 'number', 'duration', 'date']) |
59 | -mdf = spark.createDataFrame(list(messages), schema=['type', 'address']) | 59 | +mdf = spark.createDataFrame(list(messages), schema=['type', 'address', 'date']) |
60 | + | ||
61 | +dfdc = cdf.select(F.to_date(F.col('date')).alias('date')).groupBy('date').agg(F.count('*').alias('calls')) | ||
62 | +dfdm = mdf.select(F.to_date(F.col('date')).alias('date')).groupBy('date').agg(F.count('*').alias('messages')) | ||
63 | + | ||
64 | +dfd = dfdc.join(dfdm, ['date'], 'fullouter').select( | ||
65 | + 'date', | ||
66 | + F.coalesce('calls', F.lit(0)).alias('calls'), | ||
67 | + F.coalesce('messages', F.lit(0)).alias('messages') | ||
68 | +) | ||
69 | + | ||
70 | +for r in dfd.collect(): | ||
71 | + sql = "INSERT INTO daily_contacts VALUES (%s, %s, %s, %s)" | ||
72 | + cursor.execute(sql, (ext_no, r[0], r[1], r[2])) | ||
60 | 73 | ||
61 | result = None | 74 | result = None |
62 | for key, val in regions.items(): | 75 | for key, val in regions.items(): | ... | ... |
-
Please register or login to post a comment