Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💩 🔧 Enable suspending data collection for certain subgroups #1016

Merged
merged 2 commits into from
Feb 3, 2025

Conversation

shankari
Copy link
Contributor

@shankari shankari commented Feb 3, 2025

This implements a hack for e-mission/e-mission-docs#1104
The request in the issue was to implement the ability to suspend and resume
data collection.

As a first step for implementing this, we:

  • add a new section to the opcode in the dynamic config that lists the
    suspended subgroups, and
  • skip all calls to usercache/put for users that are in suspended subgroup

This is a hack because the real fix is to stop data collection on the phone.

But even with this implementation:

  • I have copied over the implementation of getSubgroupFromToken from the phone and manually ported it over to python. Instead, we should have all the implementations for parsing the opcode in e-mission-common and just use them here
  • ideally, we would check for the subgroup being suspended in the putIntoCache method. But, by design, we only see the opcode in the auth module; the rest of the code works only with UUIDs. We should figure out a way to return the non-random parts of the opcode (program and subgroup) as a context so we can do additional validation downstream if needed. We can return this as a context variable, or store it into the profile and have the code check it from there.

@JGreenlee @TeachMeTW we should discuss if we want to continue with this hack on the server, or just go to the principled fix, in which case, most of these 💩 changes can be reverted.

Before this, the dynamic config was read in `resolve_auth` and only used to
choose the authentication method if it was dynamic. However, as we make more
parts of the server also controlled via the dynamic config, we will want to
make the dynamic config more broadly available.

This change pulls out the dynamic config into a separate function, invokes it
before resolving the auth, and passes it in the `resolve_auth` function.

A potential cleanup would be to pull it up even further (into
`emission.core.dynamic_config`, similar to
`emission/core/backwards_compat_config.py`) so that it could be used more
easily in other modules as well.

Testing done:

auth_mode = skip

```
2025-02-02 12:43:15,973:DEBUG:8313653312:Using auth method skip
2025-02-02 12:45:30,066:DEBUG:6329921536:START POST /profile/create
2025-02-02 12:45:30,068:DEBUG:6329921536:Called createUserProfile
2025-02-02 12:45:30,074:DEBUG:6329921536:methodName = skip, returning <class 'emission.net.aut
h.skip.SkipMethod'>
2025-02-02 12:45:30,075:DEBUG:6329921536:Using the skip method to verify id token nrelop_dev-e
mulator-study_default_123 of length 37
2025-02-02 12:45:30,075:DEBUG:6329921536:userEmail = nrelop_dev-emulator-study_default_123
...
2025-02-02 12:45:30,103:DEBUG:6329921536:END POST /profile/create  0.0375370979309082
```

auth_mode = dynamic, but token not in token list

```
ValueError: Invalid token nrelop_dev-emulator-study_default_123, not found in list of length 0

2025-02-02 12:49:37,234:DEBUG:8313653312:auth_method is dynamic, using dynamic config to find
the actual auth method
2025-02-02 12:49:37,234:DEBUG:8313653312:is a program set auth_method to token_list
2025-02-02 12:49:37,235:DEBUG:8313653312:Using auth method token_list
2025-02-02 12:50:07,735:DEBUG:6353514496:START POST /profile/create
2025-02-02 12:50:07,737:DEBUG:6353514496:Called createUserProfile
et.auth.token_list.TokenListMethod'>
2025-02-02 12:50:07,744:DEBUG:6370340864:methodName = token_list, returning <class 'emission.n
et.auth.token_list.TokenListMethod'>
2025-02-02 12:50:07,746:DEBUG:6353514496:Using the TokenListMethod to verify id token nrelop_d
ev-emulator-study_default_123 of length 37 against list []...
2025-02-02 12:50:07,749:DEBUG:6353514496:END POST /profile/create  0.015098094940185547
```

auth_mode = dynamic, token in token list

```
./e-mission-py.bash bin/auth/insert_tokens.py --single nrelop_dev-emulator-study_default_123
Config file not found, returning a copy of the environment variables instead...
Retrieved config: {'DB_HOST': None, 'DB_RESULT_LIMIT': None}
URL not formatted, defaulting to "Stage_database"
Connecting to database URL localhost

2025-02-02 12:53:31,260:DEBUG:8313653312:attempting to resolve auth_method
2025-02-02 12:53:31,261:DEBUG:8313653312:STUDY_CONFIG is stage-program
2025-02-02 12:53:31,261:DEBUG:8313653312:About to download config from https://raw.githubuserc
ontent.com/e-mission/nrel-openpath-deploy-configs/main/configs/stage-program.nrel-op.json
2025-02-02 12:53:31,368:DEBUG:8313653312:Successfully downloaded config with version 1 for Sta
ging environment for testing programs only and data collection URL https://openpath-stage.nrel
.gov/api/
2025-02-02 12:53:31,369:DEBUG:8313653312:auth_method is dynamic, using dynamic config to find the actual auth method
2025-02-02 12:53:31,369:DEBUG:8313653312:is a program set auth_method to token_list
2025-02-02 12:53:31,369:DEBUG:8313653312:Using auth method token_list
2025-02-02 12:54:15,996:DEBUG:13186920448:START POST /profile/create
2025-02-02 12:54:15,997:DEBUG:13186920448:Called createUserProfile
2025-02-02 12:54:16,001:DEBUG:13186920448:methodName = token_list, returning <class 'emission.net.auth.token_list.TokenListMethod'>
2025-02-02 12:54:16,011:DEBUG:13203746816:Using the TokenListMethod to verify id token nrelop_dev-emulator-study_default_123 of length 37 against list ['nrelop_dev-emulator-study_default_123']...
2025-02-02 12:54:16,011:DEBUG:13203746816:Found match for token nrelop_dev-emulator-study_default_123 of length 37
2025-02-02 12:54:16,014:DEBUG:13203746816:retUUID = eb4a7aae-f2d4-4480-ba85-c568a45591b5
2025-02-02 12:54:16,016:DEBUG:13186920448:Looked up user = <emission.core.wrapper.user.User object at 0x3105a3820>
2025-02-02 12:54:16,016:DEBUG:13186920448:Returning result {'uuid': 'eb4a7aae-f2d4-4480-ba85-c568a45591b5'}
2025-02-02 12:54:16,017:DEBUG:13186920448:END POST /profile/create  0.020717620849609375
```
@shankari
Copy link
Contributor Author

shankari commented Feb 3, 2025

Testing done:

After changing the default config

STUDY_CONFIG = os.getenv('STUDY_CONFIG', "dev-emulator-study")

and changing the dev-emulator-study config to have the new-style opcode with
a suspended subgroup

    "opcode": {
        "autogen": true,
        "subgroups": [
          "test",
          "default"
        ],
        "suspended_subgroups": ["default"]
    },

The suspended subgroup skipped all put messages

2025-02-02 18:57:35,723:DEBUG:6363951104:START POST /usercache/put
2025-02-02 18:57:35,724:DEBUG:6363951104:Called userCache.put
2025-02-02 18:57:35,742:DEBUG:6363951104:subgroup default found in list ['test', 'default']
2025-02-02 18:57:35,743:INFO:6363951104:Received put message for subgroup default in suspended
_subgroups=['default'], returning uuid = None
2025-02-02 18:57:35,743:DEBUG:6363951104:retUUID = None
2025-02-02 18:57:35,743:DEBUG:6380777472:START POST /usercache/get
2025-02-02 18:57:35,743:DEBUG:6363951104:END POST /usercache/put  0.023092985153198242

The non-suspended subgroup used the default implementation of put

2025-02-02 19:15:42,723:DEBUG:13035925504:START POST /usercache/put
2025-02-02 19:15:42,726:DEBUG:13035925504:Called userCache.put
2025-02-02 19:15:42,750:DEBUG:13035925504:subgroup test found in list ['test', 'default']
2025-02-02 19:15:42,759:DEBUG:13035925504:Updated result for user = feb70456-abf4-444b-8848-1515fc3470cf, key = stats/client_time, write_ts = 1738551626.711804 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a034de46756f8244ede842'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 19:15:42,761:DEBUG:13035925504:Updated result for user = feb70456-abf4-444b-8848-1515fc3470cf, key = stats/client_time, write_ts = 1738551626.71355 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a034de46756f8244ede844'), 'ok': 1.0, 'updatedExisting': False}

After modifying the config to remove suspended_subgroups (default case)

    "opcode": {
        "autogen": true,
        "subgroups": [
          "test",
          "default"
        ],
    },
2025-02-02 21:08:26,082:DEBUG:13052751872:START POST /usercache/put
2025-02-02 21:08:26,082:DEBUG:13052751872:Called userCache.put
2025-02-02 21:08:26,088:DEBUG:13052751872:subgroup default found in list ['test', 'default']
2025-02-02 21:08:26,088:DEBUG:13052751872:methodName = skip, returning <class 'emission.net.auth.skip.SkipMethod'>
2025-02-02 21:08:26,088:DEBUG:13052751872:Using the skip method to verify id token nrelop_dev-emulator-study_default_123 of length 37

2025-02-02 21:08:26,110:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = background/location, write_ts = 1738559116.0185199 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3a88'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 21:08:26,112:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = background/filtered_location, write_ts = 1738559116.020091 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3a8a'), 'ok': 1.0, 'updatedExisting': False}
...
2025-02-02 21:08:26,930:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = stats/client_nav_event, write_ts = 1738559306.0461679 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3da8'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 21:08:26,930:DEBUG:13052751872:END POST /usercache/put eb4a7aae-f2d4-4480-ba85-c568a45591b5 0.849830150604248

@shankari shankari force-pushed the suspend_certain_subgroups branch from 6c51207 to bc0aeb1 Compare February 3, 2025 06:16
This implements a hack for e-mission/e-mission-docs#1104
The request in the issue was to implement the ability to suspend and resume
data collection.

As a first step for implementing this, we:
- add a new section to the `opcode` in the dynamic config that lists the
  suspended subgroups, and
- skip all calls to `usercache/put` for users that are in suspended subgroup

This is a hack because the real fix is to stop data collection on the phone.

But even with this implementation:
- I have copied over the implementation of `getSubgroupFromToken` from the
  phone and manually ported it over to python. Instead, we should have all the
  implementations for parsing the opcode in `e-mission-common` and just use them here
- ideally, we would check for the subgroup being suspended in the
  `putIntoCache` method. But, by design, we only see the opcode in the auth
  module; the rest of the code works only with UUIDs. We should figure out a
  way to return the non-random parts of the opcode (program and subgroup) as a
  context so we can do additional validation downstream if needed. We can
  return this as a context variable, or store it into the profile and have the
  code check it from there.

Testing done:

After changing the default config

```
STUDY_CONFIG = os.getenv('STUDY_CONFIG', "dev-emulator-study")
```

and changing the `dev-emulator-study` config to have the new-style opcode with
a suspended subgroup

```
    "opcode": {
        "autogen": true,
        "subgroups": [
          "test",
          "default"
        ],
        "suspended_subgroups": ["default"]
    },
```

The suspended subgroup skipped all put messages

```
2025-02-02 18:57:35,723:DEBUG:6363951104:START POST /usercache/put
2025-02-02 18:57:35,724:DEBUG:6363951104:Called userCache.put
2025-02-02 18:57:35,742:DEBUG:6363951104:subgroup default found in list ['test', 'default']
2025-02-02 18:57:35,743:INFO:6363951104:Received put message for subgroup default in suspended
_subgroups=['default'], returning uuid = None
2025-02-02 18:57:35,743:DEBUG:6363951104:retUUID = None
2025-02-02 18:57:35,743:DEBUG:6380777472:START POST /usercache/get
2025-02-02 18:57:35,743:DEBUG:6363951104:END POST /usercache/put  0.023092985153198242
```

The non-suspended subgroup used the default implementation of put

```
2025-02-02 19:15:42,723:DEBUG:13035925504:START POST /usercache/put
2025-02-02 19:15:42,726:DEBUG:13035925504:Called userCache.put
2025-02-02 19:15:42,750:DEBUG:13035925504:subgroup test found in list ['test', 'default']
2025-02-02 19:15:42,759:DEBUG:13035925504:Updated result for user = feb70456-abf4-444b-8848-1515fc3470cf, key = stats/client_time, write_ts = 1738551626.711804 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a034de46756f8244ede842'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 19:15:42,761:DEBUG:13035925504:Updated result for user = feb70456-abf4-444b-8848-1515fc3470cf, key = stats/client_time, write_ts = 1738551626.71355 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a034de46756f8244ede844'), 'ok': 1.0, 'updatedExisting': False}
```

After modifying the config to remove suspended_subgroups (default case)

```
    "opcode": {
        "autogen": true,
        "subgroups": [
          "test",
          "default"
        ],
    },
```

```
2025-02-02 21:08:26,082:DEBUG:13052751872:START POST /usercache/put
2025-02-02 21:08:26,082:DEBUG:13052751872:Called userCache.put
2025-02-02 21:08:26,088:DEBUG:13052751872:subgroup default found in list ['test', 'default']
2025-02-02 21:08:26,088:DEBUG:13052751872:methodName = skip, returning <class 'emission.net.auth.skip.SkipMethod'>
2025-02-02 21:08:26,088:DEBUG:13052751872:Using the skip method to verify id token nrelop_dev-emulator-study_default_123 of length 37

2025-02-02 21:08:26,110:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = background/location, write_ts = 1738559116.0185199 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3a88'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 21:08:26,112:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = background/filtered_location, write_ts = 1738559116.020091 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3a8a'), 'ok': 1.0, 'updatedExisting': False}
...
2025-02-02 21:08:26,930:DEBUG:13052751872:Updated result for user = eb4a7aae-f2d4-4480-ba85-c568a45591b5, key = stats/client_nav_event, write_ts = 1738559306.0461679 = {'n': 1, 'nModified': 0, 'upserted': ObjectId('67a04f4a46756f8244ee3da8'), 'ok': 1.0, 'updatedExisting': False}
2025-02-02 21:08:26,930:DEBUG:13052751872:END POST /usercache/put eb4a7aae-f2d4-4480-ba85-c568a45591b5 0.849830150604248
```
@shankari shankari force-pushed the suspend_certain_subgroups branch from bc0aeb1 to cbb29e4 Compare February 3, 2025 06:36
@shankari
Copy link
Contributor Author

shankari commented Feb 3, 2025

@mattwigway the hack has been merged now. It will be on staging for a couple of days, and then we will move it to production, likely by mid-week. After it is in production, you can choose to edit the config and suspend the subgroup at any time.

@shankari shankari merged commit aaedcd3 into e-mission:master Feb 3, 2025
5 checks passed
@mattwigway
Copy link

mattwigway commented Feb 3, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants