diff --git a/.github/workflows/test-overpass.yml b/.github/workflows/test-overpass.yml index 934075e4f..5f1297a61 100644 --- a/.github/workflows/test-overpass.yml +++ b/.github/workflows/test-overpass.yml @@ -21,7 +21,7 @@ jobs: # Runs TestOverpass.py to check for API functionality - name: Test Overpass env: - GEOFABRIK_OVERPASS_KEY: '${{ secrets.OVERPASS_API }}' + GEOFABRIK_OVERPASS_KEY: '${{ secrets.GEOFABRIK_OVERPASS_KEY }}' run: | echo Testing overpass! chmod +x emission/individual_tests/setup_and_test_overpass.sh diff --git a/emission/analysis/intake/cleaning/clean_and_resample.py b/emission/analysis/intake/cleaning/clean_and_resample.py index 3666cd938..85d1df378 100644 --- a/emission/analysis/intake/cleaning/clean_and_resample.py +++ b/emission/analysis/intake/cleaning/clean_and_resample.py @@ -254,6 +254,7 @@ def get_filtered_place(raw_place): reverse_geocoded_json = eco.Geocoder.get_json_reverse(filtered_place_data.location.coordinates[1], filtered_place_data.location.coordinates[0]) if reverse_geocoded_json is not None: + filtered_place_data['geocoded_address'] = reverse_geocoded_json['address'] filtered_place_data.display_name = format_result(reverse_geocoded_json) except KeyError as e: logging.info("nominatim result does not have an address, skipping") diff --git a/emission/core/wrapper/cleanedplace.py b/emission/core/wrapper/cleanedplace.py index 11975dd17..9dd552340 100644 --- a/emission/core/wrapper/cleanedplace.py +++ b/emission/core/wrapper/cleanedplace.py @@ -12,6 +12,7 @@ class Cleanedplace(ecwp.Place): props = ecwp.Place.props props.update( {"raw_places": ecwb.WrapperBase.Access.WORM, # raw places that were combined to from this cleaned place + "geocoded_address": ecwb.WrapperBase.Access.WORM, # the 'address' field of the OSM reverse geocoding result "display_name": ecwb.WrapperBase.Access.WORM # The human readable name for this place }) diff --git a/emission/core/wrapper/user.py b/emission/core/wrapper/user.py index 5bd84ed40..d8f2a2b5a 100644 --- a/emission/core/wrapper/user.py +++ b/emission/core/wrapper/user.py @@ -127,10 +127,14 @@ def getSettings(self): @staticmethod def createProfile(uuid, ts): - initProfileObj = {'user_id': uuid, + initProfileObj = { + 'user_id': uuid, 'source':'Shankari', 'update_ts': ts, - 'mpg_array': [defaultMpg]} + 'mpg_array': [defaultMpg], + 'mode': {}, + 'purpose': {} + } writeResultProfile = get_profile_db().update_one( {'user_id': uuid}, {'$set': initProfileObj}, @@ -225,3 +229,76 @@ def unregister(userEmail): get_uuid_db().delete_one({'user_email': userEmail}) get_profile_db().delete_one({'user_id': uuid}) return uuid + + def getUserCustomLabel(self, key): + user = get_profile_db().find_one({'user_id': self.uuid}) + if key in user: + labels = user[key] + filteredLabels = {key: value for key, value in labels.items() if value.get('isActive', False)} + sortedLabels = dict(sorted(filteredLabels.items(), key=lambda x: (x[1]["frequency"]), reverse=True)) + return list(sortedLabels) + else: + return [] + + def insertUserCustomLabel(self, inserted_label): + from datetime import datetime + user = get_profile_db().find_one({'user_id': self.uuid}) + key = inserted_label['key'] + label = inserted_label['label'] + items = user[key] if key in user else {} + + # if label exists in database, chage it as 'active' label + if label in items: + items[label]['isActive'] = True + else: + items[label] = { + 'createdAt': datetime.now(), + 'frequency': 0, + 'isActive': True, + } + + get_profile_db().update_one({'user_id': self.uuid}, {'$set': {key: items}}) + return self.getUserCustomLabel(key) + + def updateUserCustomLabel(self, updated_label): + from datetime import datetime + user = get_profile_db().find_one({'user_id': self.uuid}) + key = updated_label['key'] + items = user[key] if key in user else {} + old_label = updated_label['old_label'] + new_label = updated_label['new_label'] + is_new_label_must_added = updated_label['is_new_label_must_added'] + # when a user changed a label to an exsiting customized label + if new_label in items: + updated_frequency = items[new_label]['frequency'] + 1 + items[new_label]['frequency'] = updated_frequency + items[new_label]['isActive'] = True + + # when a user added a new customized label + if is_new_label_must_added and not new_label in items: + items[new_label] = { + 'createdAt': datetime.now(), + 'frequency': 1, + 'isActive': True, + } + + # when a user chaged a label from an exsiting customized label + if old_label in items: + updated_frequency = items[old_label]['frequency'] - 1 + items[old_label]['frequency'] = updated_frequency + + get_profile_db().update_one({'user_id': self.uuid}, {'$set': {key: items}}) + return self.getUserCustomLabel(key) + + def deleteUserCustomLabel(self, deleted_label): + user = get_profile_db().find_one({'user_id': self.uuid}) + key = deleted_label['key'] + label = deleted_label['label'] + items = user[key] if key in user else {} + + if label in items: + items[label]['isActive'] = False + + get_profile_db().update_one({'user_id': self.uuid}, {'$set': {key: items}}) + return self.getUserCustomLabel(key) + \ No newline at end of file diff --git a/emission/individual_tests/TestNominatim.py b/emission/individual_tests/TestNominatim.py index a7f75b958..1ad31a9d7 100644 --- a/emission/individual_tests/TestNominatim.py +++ b/emission/individual_tests/TestNominatim.py @@ -62,9 +62,13 @@ def test_geofabrik_and_nominatim(self): def test_get_filtered_place(self): fake_place_raw = self.fake_place fake_place_data = clean.get_filtered_place(fake_place_raw).__getattr__("data") - actual_result = fake_place_data.__getattr__("display_name") - expected_result = "Dorrance Street, Providence" - self.assertEqual(expected_result, actual_result) + actual_display_name = fake_place_data.__getattr__("display_name") + expected_display_name = "Dorrance Street, Providence" + self.assertEqual(expected_display_name, actual_display_name) + actual_geocoded_address = fake_place_data["geocoded_address"] + expected_geocoded_address = {'road': 'Dorrance Street', 'city': 'Providence', 'postcode': '02903'} + for k in expected_geocoded_address: + self.assertEqual(expected_geocoded_address[k], actual_geocoded_address[k]) #Testing make_url_geo, which creates a query URL from the input string. def test_make_url_geo(self): diff --git a/emission/net/api/cfc_webapp.py b/emission/net/api/cfc_webapp.py index a5e0d1dd9..0fb3b10ef 100644 --- a/emission/net/api/cfc_webapp.py +++ b/emission/net/api/cfc_webapp.py @@ -306,6 +306,47 @@ def getUserProfile(): user = User.fromUUID(user_uuid) return user.getProfile() +@post('/customlabel/get') +def getUserCustomLabels(): + logging.debug("Called getUserCustomLabels") + keys = request.json['keys'] + user_uuid = getUUID(request) + user = User.fromUUID(user_uuid) + to_return = {} + for key in keys: + to_return[key] = user.getUserCustomLabel(key) + return to_return + +@post('/customlabel/insert') +def insertUserCustomLabel(): + logging.debug("Called insertUserCustomLabel") + inserted_label = request.json['inserted_label'] + user_uuid = getUUID(request) + user = User.fromUUID(user_uuid) + to_return = user.insertUserCustomLabel(inserted_label) + logging.debug("Successfully inserted label for user %s" % user_uuid) + return { 'label' : to_return } + +@post('/customlabel/update') +def updateUserCustomLabel(): + logging.debug("Called updateUserCustomLabel") + updated_label = request.json['updated_label'] + user_uuid = getUUID(request) + user = User.fromUUID(user_uuid) + to_return = user.updateUserCustomLabel(updated_label) + logging.debug("Successfully updated label label for user %s" % user_uuid) + return { 'label' : to_return } + +@post('/customlabel/delete') +def deleteUserCustomLabel(): + logging.debug("Called deleteUserCustomLabel") + deleted_label = request.json['deleted_label'] + user_uuid = getUUID(request) + user = User.fromUUID(user_uuid) + to_return = user.deleteUserCustomLabel(deleted_label) + logging.debug("Successfully deleted label for user %s" % user_uuid) + return { 'label' : to_return } + @post('/result/metrics/') def summarize_metrics(time_type): _fill_aggregate_backward_compat(request) diff --git a/emission/tests/coreTests/wrapperTests/TestUserCustomLabel.py b/emission/tests/coreTests/wrapperTests/TestUserCustomLabel.py new file mode 100644 index 000000000..a0aa30842 --- /dev/null +++ b/emission/tests/coreTests/wrapperTests/TestUserCustomLabel.py @@ -0,0 +1,86 @@ +from __future__ import division +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import absolute_import +# Standard imports +from future import standard_library +standard_library.install_aliases() +from builtins import * +import unittest + +# Our imports +from emission.core.wrapper.user import User +import emission.tests.common as etc +import emission.core.get_database as edb + +import emission.tests.common as etc + +class TestUserCustomMode(unittest.TestCase): + + def setUp(self): + self.user = User.register('fake@fake.com') + + def testInitialGetUserCustomLabels(self): + self.assertListEqual(self.user.getUserCustomLabel('mode'), []) + self.assertListEqual(self.user.getUserCustomLabel('purpose'), []) + + def testInsertCustomLabel(self): + inserted_mode = { + 'key' : 'mode', + 'label' : 'mode1', + } + mode = self.user.insertUserCustomLabel(inserted_mode) + self.assertListEqual(mode, ['mode1']) + + inserted_purpose = { + 'key' : 'purpose', + 'label' : 'purpose1', + } + purpose = self.user.insertUserCustomLabel(inserted_purpose) + self.assertListEqual(purpose, ['purpose1']) + + def tesUpdateUserCustomLabel(self): + self.testInsertCustomLabel() + updated_mode = { + 'key' : 'mode', + 'old_label' : '', + 'new_label' : 'mode2', + 'is_new_label_must_added': True + } + mode = self.user.updateUserCustomLabel(updated_mode) + self.assertListEqual(mode, ['mode2', 'mode1']) + + updated_purpose = { + 'key' : 'purpose', + 'old_label' : '', + 'new_label' : 'purpose2', + 'is_new_label_must_added': True + } + purpose = self.user.updateUserCustomLabel(updated_purpose) + self.assertListEqual(purpose, ['purpose2', 'purpose1']) + + def testDeleteUserCustomMode(self): + self.tesUpdateUserCustomLabel() + deleted_mode = { + 'key' : 'mode', + 'label' : 'mode2', + } + mode = self.user.deleteUserCustomLabel(deleted_mode) + + self.assertListEqual(mode, ['mode1']) + + deleted_purpose = { + 'key' : 'purpose', + 'label' : 'purpose2', + } + purpose = self.user.deleteUserCustomLabel(deleted_purpose) + self.assertListEqual(purpose, ['purpose1']) + + def tearDown(self): + etc.dropAllCollections(edb._get_current_db()) + + +if __name__ == '__main__': + etc.configLogging() + unittest.main() + diff --git a/setup/environment36.yml b/setup/environment36.yml index 1f1f57a77..11e2640c3 100644 --- a/setup/environment36.yml +++ b/setup/environment36.yml @@ -19,7 +19,7 @@ dependencies: - scipy=1.10.0 - utm=0.7.0 - pip: - - git+https://github.com/JGreenlee/e-mission-common@0.5.2 + - git+https://github.com/JGreenlee/e-mission-common@0.5.3 - pyfcm==1.5.4 - pygeocoder==1.2.5 - pymongo==4.3.3