Ma Suhyeon

Implement phone normalization

Implement number filter
Implement daily stats
Implement put APIs
...@@ -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():
......