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