[my-place] Refactoring: Tizen C++ convention - File names chenge.
[platform/core/context/context-provider.git] / src / my-place / place / PlacesDetector.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <sstream>
18 #include <Types.h>
19 #include <Json.h>
20 #include "../utils/Similarity.h"
21 #include "PlacesDetector.h"
22 #include "PlaceCateger.h"
23 #include "../utils/Median.h"
24 #ifdef TIZEN_ENGINEER_MODE
25 #include "../utils/Gmap.h"
26 #endif /* TIZEN_ENGINEER_MODE */
27 #include "../facade/UserPlacesTypes.h"
28 #include <fstream>
29 #include <algorithm>
30 #include "../facade/UserPlacesParams.h"
31 #include "../utils/DebugUtils.h"
32
33 #define __DELETE_PLACES_QUERY "DELETE FROM " PLACE_TABLE
34
35 #ifdef TIZEN_ENGINEER_MODE
36 #define __USER_PLACES_FILE "/tmp/user_places.txt" // TODO: Only for debug purposes -> Remove in final solution
37 #endif /* TIZEN_ENGINEER_MODE */
38
39 #define __GET_VISITS_QUERY "SELECT "\
40         VISIT_COLUMN_END_TIME ", "\
41         VISIT_COLUMN_START_TIME ", "\
42         VISIT_COLUMN_WIFI_APS ", "\
43         VISIT_COLUMN_LOCATION_VALID ", "\
44         VISIT_COLUMN_LOCATION_LATITUDE ", "\
45         VISIT_COLUMN_LOCATION_LONGITUDE ", "\
46         VISIT_COLUMN_LOCATION_ACCURACY ", "\
47         VISIT_COLUMN_CATEG_HOME ", "\
48         VISIT_COLUMN_CATEG_WORK ", "\
49         VISIT_COLUMN_CATEG_OTHER \
50         " FROM " VISIT_TABLE
51
52 #define __GET_PLACES_QUERY "SELECT "\
53         PLACE_COLUMN_CATEG_ID ", "\
54         PLACE_COLUMN_CATEG_CONFIDENCE ", "\
55         PLACE_COLUMN_NAME ", "\
56         PLACE_COLUMN_LOCATION_VALID ", "\
57         PLACE_COLUMN_LOCATION_LATITUDE ", "\
58         PLACE_COLUMN_LOCATION_LONGITUDE ", "\
59         PLACE_COLUMN_LOCATION_ACCURACY ", "\
60         PLACE_COLUMN_WIFI_APS ", "\
61         PLACE_COLUMN_CREATE_DATE \
62         " FROM " PLACE_TABLE
63
64 #define __GET_WIFI_APS_MAP_QUERY "SELECT "\
65         WIFI_APS_MAP_COLUMN_MAC ", "\
66         WIFI_APS_MAP_COLUMN_NETWORK_NAME \
67         " FROM " WIFI_APS_MAP_TABLE
68
69 #define __PLACE_TABLE_COLUMNS \
70         PLACE_COLUMN_CATEG_ID " INTEGER, "\
71         PLACE_COLUMN_CATEG_CONFIDENCE " REAL, "\
72         PLACE_COLUMN_NAME " TEXT, "\
73         PLACE_COLUMN_LOCATION_VALID " INTEGER, "\
74         PLACE_COLUMN_LOCATION_LATITUDE " REAL, "\
75         PLACE_COLUMN_LOCATION_LONGITUDE " REAL, "\
76         PLACE_COLUMN_LOCATION_ACCURACY " REAL, "\
77         PLACE_COLUMN_WIFI_APS " STRING, "\
78         PLACE_COLUMN_CREATE_DATE " timestamp"
79
80 bool ctx::PlacesDetector::onTimerExpired(int timerId)
81 {
82         _D("");
83         __dbDeletePlaces();
84         __dbDeleteOldEntries();
85         std::vector<Json> records = __dbGetVisits();
86         Visits visits = __visitsFromJsons(records);
87         __dbGetWifiAPsMap();
88         __processVisits(visits);
89         __wifiAPsMap.clear();
90         return true;
91 }
92
93 std::vector<ctx::Json> ctx::PlacesDetector::__dbGetVisits()
94 {
95         std::vector<Json> records;
96         bool ret = __dbManager->executeSync(__GET_VISITS_QUERY, &records);
97         _D("load visits execute query result: %s", ret ? "SUCCESS" : "FAIL");
98         return records;
99 }
100
101 std::vector<ctx::Json> ctx::PlacesDetector::__dbGetPlaces()
102 {
103         std::vector<Json> records;
104         bool ret = __dbManager->executeSync(__GET_PLACES_QUERY, &records);
105         _D("load places execute query result: %s", ret ? "SUCCESS" : "FAIL");
106         return records;
107 }
108
109 void ctx::PlacesDetector::__dbGetWifiAPsMap()
110 {
111         std::vector<Json> records;
112         bool ret = __dbManager->executeSync(__GET_WIFI_APS_MAP_QUERY, &records);
113         _D("load Wifi APs map (size = %d), result: %s", records.size(), ret ? "SUCCESS" : "FAIL");
114         std::string mac = "";
115         std::string networkName = "";
116         for (Json record : records) {
117                 record.get(NULL, WIFI_APS_MAP_COLUMN_MAC, &mac);
118                 record.get(NULL, WIFI_APS_MAP_COLUMN_NETWORK_NAME, &networkName);
119                 __wifiAPsMap.insert(std::pair<std::string, std::string>(mac, networkName));
120         }
121 }
122
123 double ctx::PlacesDetector::__doubleValueFromJson(Json &row, const char* key)
124 {
125         double value;
126         row.get(NULL, key, &value);
127         _D("__doubleValueFromJson, key:%s, value: %lf", key, value);
128         return value;
129 }
130
131 ctx::Categs ctx::PlacesDetector::__visitCategsFromJson(Json &row)
132 {
133         Categs categs;
134         categs[PLACE_CATEG_ID_HOME] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_HOME);
135         categs[PLACE_CATEG_ID_WORK] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_WORK);
136         categs[PLACE_CATEG_ID_OTHER] = __doubleValueFromJson(row, VISIT_COLUMN_CATEG_OTHER);
137         return categs;
138 }
139
140 void ctx::PlacesDetector::__visitLocationFromJson(Json &row, ctx::Visit &visit)
141 {
142         int locationValidInt;
143         row.get(NULL, VISIT_COLUMN_LOCATION_VALID, &locationValidInt);
144         visit.locationValid = (bool) locationValidInt;
145         row.get(NULL, VISIT_COLUMN_LOCATION_LATITUDE, &(visit.location.latitude));
146         row.get(NULL, VISIT_COLUMN_LOCATION_LONGITUDE, &(visit.location.longitude));
147         row.get(NULL, VISIT_COLUMN_LOCATION_ACCURACY, &(visit.location.accuracy));
148 }
149
150 ctx::Visit ctx::PlacesDetector::__visitFromJson(Json &row)
151 {
152         int startTime;
153         int endTime;
154         std::string str;
155         row.get(NULL, VISIT_COLUMN_START_TIME, &startTime);
156         row.get(NULL, VISIT_COLUMN_END_TIME, &endTime);
157         row.get(NULL, VISIT_COLUMN_WIFI_APS, &str);
158
159         std::stringstream ss;
160         ss << str;
161         std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
162         ss >> *macSet;
163
164         Interval interval(startTime, endTime);
165         Categs categs = __visitCategsFromJson(row);
166
167         Visit visit(interval, macSet, categs);
168
169         __visitLocationFromJson(row, visit);
170
171         return visit;
172 }
173
174 ctx::Visits ctx::PlacesDetector::__visitsFromJsons(std::vector<Json>& records)
175 {
176         Visits visits;
177         _D("db_result: number of all visits: %d", records.size());
178
179         for (Json &row : records) {
180                 Visit visit = __visitFromJson(row);
181                 visits.push_back(visit);
182         }
183         _D("number of all visits in vector: %d", visits.size());
184         return visits;
185 }
186
187 std::shared_ptr<ctx::Place> ctx::PlacesDetector::__placeFromJson(Json &row)
188 {
189         std::shared_ptr<Place> place = std::make_shared<Place>();
190         __placeCategoryFromJson(row, *place);
191         row.get(NULL, PLACE_COLUMN_NAME, &(place->name));
192         __placeLocationFromJson(row, *place);
193         __placeWifiAPsFromJson(row, *place);
194         __placeCreateDateFromJson(row, *place);
195         _D("db_result: categId: %d; place: name: %s; locationValid: %d; latitude: %lf, longitude: %lf, createDate: %d", place->categId, place->name.c_str(), place->locationValid, place->location.latitude, place->location.longitude, place->createDate);
196         return place;
197 }
198
199 void ctx::PlacesDetector::__placeCategoryFromJson(Json &row, ctx::Place &place)
200 {
201         int categId;
202         row.get(NULL, PLACE_COLUMN_CATEG_ID, &categId);
203         // This is due to the fact the JSON module API interface doesn't handle enum
204         place.categId = static_cast<PlaceCategId>(categId);
205 }
206
207 void ctx::PlacesDetector::__placeLocationFromJson(Json &row, ctx::Place &place)
208 {
209         int locationValidInt;
210         row.get(NULL, PLACE_COLUMN_LOCATION_VALID, &locationValidInt);
211         place.locationValid = (bool) locationValidInt;
212         row.get(NULL, PLACE_COLUMN_LOCATION_LATITUDE, &(place.location.latitude));
213         row.get(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, &(place.location.longitude));
214         row.get(NULL, PLACE_COLUMN_LOCATION_ACCURACY, &(place.location.accuracy));
215 }
216
217 void ctx::PlacesDetector::__placeWifiAPsFromJson(Json &row, ctx::Place &place)
218 {
219         std::string wifiAps;
220         row.get(NULL, PLACE_COLUMN_WIFI_APS, &wifiAps);
221         std::stringstream ss;
222         ss << wifiAps;
223         std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
224         ss >> *macSet;
225         for (ctx::Mac mac : *macSet) {
226                 place.wifiAps.insert(std::pair<std::string, std::string>(mac, __wifiAPsMap[mac]));
227         }
228 }
229
230 void ctx::PlacesDetector::__placeCreateDateFromJson(Json &row, ctx::Place &place)
231 {
232         int createDate;
233         row.get(NULL, PLACE_COLUMN_CREATE_DATE, &(createDate));
234         // This is due to the fact the JSON module API interface doesn't handle time_t
235         place.createDate = static_cast<time_t>(createDate);
236 }
237
238 std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::__placesFromJsons(std::vector<Json>& records)
239 {
240         std::vector<std::shared_ptr<Place>> places;
241         _D("db_result: number of all places: %d", records.size());
242
243         for (Json &row : records) {
244                 std::shared_ptr<Place> place = __placeFromJson(row);
245                 places.push_back(place);
246         }
247         _D("number of all places in vector: %d", places.size());
248         return places;
249 }
250
251 void ctx::PlacesDetector::__reduceOutliers(ctx::Visits &visits)
252 {
253         int size = visits.size();
254         visits.erase(std::remove_if(
255                                         visits.begin(),
256                                         visits.end(),
257                                         [](Visit v)->bool {
258                                                 int minutes = (v.interval.end - v.interval.start) / 60;
259                                                 return (minutes < PLACES_DETECTOR_MIN_VISIT_DURATION_MINUTES)
260                                                                 || (minutes > PLACES_DETECTOR_MAX_VISIT_DURATION_MINUTES);
261                                         }),
262                                 visits.end());
263         int newSize = visits.size();
264         if (size != newSize)
265                 _D("Visits number from %d to %d (to short and to long reduction)", size, newSize);
266 }
267
268 void ctx::PlacesDetector::__processVisits(ctx::Visits &visits)
269 {
270         __reduceOutliers(visits);
271
272         _D("__testMode = %d", __testMode);
273         auto components = __mergeVisits(visits);
274         std::vector<std::shared_ptr<Place>> newDetectedPlaces;
275 #ifdef TIZEN_ENGINEER_MODE
276         std::vector<Visits> placesVisits; // TODO: remove from final solution.
277 #endif /* TIZEN_ENGINEER_MODE */
278         for (std::shared_ptr<graph::Component> component : *components) {
279                 // Small places outliers reduction
280                 if (!__testMode && component->size() < PLACES_DETECTOR_MIN_VISITS_PER_BIG_PLACE)
281                         continue;
282                 std::shared_ptr<Visits> merged = std::make_shared<Visits>();
283                 for (graph::Node i : *component) {
284                         merged->push_back(visits[i]);
285                 }
286                 std::shared_ptr<Place> place = __placeFromMergedVisits(*merged);
287                 if (place->categId == PLACE_CATEG_ID_NONE)
288                         continue;
289                 newDetectedPlaces.push_back(place);
290                 if (!__testMode)
291                         __dbInsertPlace(*place);
292
293 #ifdef TIZEN_ENGINEER_MODE
294                 { // TODO: Only for debug -> remove in final solution
295                         Visits placeVisits;
296                         for (graph::Node i : *component) {
297                                 placeVisits.push_back(visits[i]);
298                         }
299                         placesVisits.push_back(placeVisits);
300                 }
301 #endif /* TIZEN_ENGINEER_MODE */
302         }
303
304         __detectedPlacesUpdate(newDetectedPlaces);
305
306 #ifdef TIZEN_ENGINEER_MODE
307         { // Print to file TODO: Only for debug -> remove in final solution
308                 std::ofstream out(__USER_PLACES_FILE);
309                 for (size_t i = 0; i < newDetectedPlaces.size(); i++) {
310                         DebugUtils::printPlace2Stream(*newDetectedPlaces[i], out);
311                         Visits placeVisits = placesVisits[i];
312                         for (Visit visit : placeVisits) {
313                                 visit.printShort2Stream(out);
314                         }
315                 }
316                 out.close();
317                 Gmap::writeMap(newDetectedPlaces);
318         }
319 #endif /* TIZEN_ENGINEER_MODE */
320 }
321
322 /*
323  * Replace old places by new ones.
324  */
325 void ctx::PlacesDetector::__detectedPlacesUpdate(std::vector<std::shared_ptr<Place>> &newPlaces)
326 {
327         _D("");
328         // XXX: In case of thread safety issues use std::mutex to protect places list.
329         __detectedPlaces = newPlaces;
330 }
331
332 void ctx::PlacesDetector::__mergeLocation(const Visits &visits, Place &place)
333 {
334         std::vector<double> latitudes;
335         std::vector<double> longitudes;
336         std::vector<double> accuracy;
337         place.locationValid = false;
338         for (const Visit& visit : visits) {
339                 if (visit.locationValid) {
340                         latitudes.push_back(visit.location.latitude);
341                         longitudes.push_back(visit.location.longitude);
342                         accuracy.push_back(visit.location.accuracy);
343                         place.locationValid = true;
344                 }
345         }
346         if (place.locationValid) {
347                 place.location = medianLocation(latitudes, longitudes, accuracy);
348                 _D("place location set: lat=%.8f, lon=%.8f, acc=%.8f",
349                                 place.location.latitude,
350                                 place.location.longitude,
351                                 place.location.accuracy);
352         } else {
353                 _D("place location not set");
354         }
355 }
356
357 std::shared_ptr<ctx::Place> ctx::PlacesDetector::__placeFromMergedVisits(Visits &mergedVisits)
358 {
359         std::shared_ptr<Place> place = std::make_shared<Place>();
360         place->createDate = std::time(nullptr);
361         std::vector<std::shared_ptr<MacSet>> macSets;
362         for (const Visit &visit : mergedVisits) {
363                 macSets.push_back(visit.macSet);
364         }
365         std::shared_ptr<MacSet> allMacs = macSetsUnion(macSets);
366         for (ctx::Mac mac : *allMacs) {
367                 place->wifiAps.insert(std::pair<std::string, std::string>(mac, __wifiAPsMap[mac]));
368         }
369         __mergeLocation(mergedVisits, *place);
370
371         PlaceCateger::categorize(mergedVisits, *place);
372
373         return place;
374 }
375
376 void ctx::PlacesDetector::__reduceOutliers(std::shared_ptr<ctx::graph::Components> &cc)
377 {
378         int size = cc->size();
379         cc->erase(std::remove_if(cc->begin(),
380                                                          cc->end(),
381                                                          [](std::shared_ptr<graph::Component> &c)->bool {
382                                                                  return c->size() < PLACES_DETECTOR_MIN_VISITS_PER_PLACE;
383                                                          }),
384                           cc->end());
385         int newSize = cc->size();
386         if (size != newSize)
387                 _D("Connected components from %d to %d (min visit per place)", size, newSize);
388 }
389
390 std::shared_ptr<ctx::graph::Components> ctx::PlacesDetector::__mergeVisits(const std::vector<Visit> &visits)
391 {
392         auto graph = __graphFromVisits(visits);
393         auto cc = graph::connectedComponents(*graph);
394         __reduceOutliers(cc);
395         return cc;
396 }
397
398 std::shared_ptr<ctx::graph::Graph> ctx::PlacesDetector::__graphFromVisits(const std::vector<Visit> &visits)
399 {
400         std::shared_ptr<graph::Graph> graph = std::make_shared<graph::Graph>();
401         graph->resize(visits.size());
402         for (size_t i = 0; i < visits.size(); i++) {
403                 (*graph)[i] = std::make_shared<graph::NeighbourNodes>();
404                 for (size_t j = 0; j < i; j++) {
405                         if (similarity::isJoint(*visits[i].macSet, *visits[j].macSet)) {
406                                 (*graph)[i]->insert(j);
407                                 (*graph)[j]->insert(i);
408                         }
409                 }
410         }
411         return graph;
412 }
413
414 void ctx::PlacesDetector::__dbDeletePlaces()
415 {
416         std::vector<Json> records;
417         bool ret = __dbManager->executeSync(__DELETE_PLACES_QUERY, &records);
418         _D("delete places execute query result: %s", ret ? "SUCCESS" : "FAIL");
419 }
420
421 void ctx::PlacesDetector::__dbDeleteOldEntries()
422 {
423         time_t currentTime;
424         time(&currentTime);
425         time_t thresholdTime = currentTime - PLACES_DETECTOR_RETENTION_SECONDS;
426         __dbDeleteOlderVisitsThan(thresholdTime);
427         __dbDeleteOlderWifiAPsThan(thresholdTime);
428 }
429
430 void ctx::PlacesDetector::__dbDeleteOlderVisitsThan(time_t threshold)
431 {
432         std::stringstream query;
433         query << "DELETE FROM " << VISIT_TABLE;
434         query << " WHERE " << VISIT_COLUMN_END_TIME << " < " << threshold;
435         // query << " AND 0"; // XXX: Always false condition. Uncomment it for not deleting any visit during development.
436         std::vector<Json> records;
437         bool ret = __dbManager->executeSync(query.str().c_str(), &records);
438         _D("deleting visits older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
439 }
440
441 void ctx::PlacesDetector::__dbDeleteOlderWifiAPsThan(time_t threshold)
442 {
443         std::stringstream query;
444         query << "DELETE FROM " << WIFI_APS_MAP_TABLE;
445         query << " WHERE " << WIFI_APS_MAP_COLUMN_INSERT_TIME << " < " << threshold;
446         std::vector<Json> records;
447         bool ret = __dbManager->executeSync(query.str().c_str(), &records);
448         _D("deleting Wifi APs older than: %d, result: %s", threshold, ret ? "SUCCESS" : "FAIL");
449 }
450
451 ctx::PlacesDetector::PlacesDetector(bool testMode):
452         __testMode(testMode),
453         __dbManager(testMode ? nullptr : new DatabaseManager())
454 {
455         if (testMode)
456                 return;
457         __dbCreateTable();
458         std::vector<Json> records = __dbGetPlaces();
459         __dbGetWifiAPsMap();
460         std::vector<std::shared_ptr<Place>> dbPlaces = __placesFromJsons(records);
461         __wifiAPsMap.clear();
462         __detectedPlacesUpdate(dbPlaces);
463 }
464
465 void ctx::PlacesDetector::__dbCreateTable()
466 {
467         bool ret = __dbManager->createTable(0, PLACE_TABLE, __PLACE_TABLE_COLUMNS);
468         _D("db: place Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
469 }
470
471 void ctx::PlacesDetector::__dbInsertPlace(const Place &place)
472 {
473         Json data;
474         data.set(NULL, PLACE_COLUMN_CATEG_ID, place.categId);
475         data.set(NULL, PLACE_COLUMN_CATEG_CONFIDENCE, place.categConfidence);
476         data.set(NULL, PLACE_COLUMN_NAME, place.name);
477
478         data.set(NULL, PLACE_COLUMN_LOCATION_VALID, place.locationValid);
479         data.set(NULL, PLACE_COLUMN_LOCATION_LATITUDE, place.location.latitude);
480         data.set(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, place.location.longitude);
481         data.set(NULL, PLACE_COLUMN_LOCATION_ACCURACY, place.location.accuracy);
482         std::string wifiAps;
483         for (std::pair<std::string, std::string> ap : place.wifiAps) {
484                 wifiAps.append(ap.first);
485                 wifiAps.append(",");
486         }
487         wifiAps = wifiAps.substr(0, wifiAps.size()-1);
488         data.set(NULL, PLACE_COLUMN_WIFI_APS, wifiAps);
489         data.set(NULL, PLACE_COLUMN_CREATE_DATE, static_cast<int>(place.createDate));
490
491         int64_t rowId;
492         bool ret = __dbManager->insertSync(PLACE_TABLE, data, &rowId);
493         _D("insert place execute query result: %s", ret ? "SUCCESS" : "FAIL");
494 }
495
496 std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::getPlaces()
497 {
498         // XXX: In case of thread safety issues use std::mutex to protect places list.
499         return __detectedPlaces;
500 }