-
Notifications
You must be signed in to change notification settings - Fork 119
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
Enhance _get_and_store_range
Function to Include Trip Statistics and Last API Call Tracking
#993
Conversation
Now I only have one concern: the current iteration does not use UUID in the docs_cursor = edb.get_timeseries_db().find({
"metadata.key": "stats/server_api_time",
"user_id" : user_id
}) Thoughts? |
@JGreenlee Comments addressed, I split the function into a different file as suggested -- though it may be overkill. Seeking thoughts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good refactor! I agree that the smaller functions are overkill.
Sometimes it's just a matter of finding the balance between tidiness and complexity.
@JGreenlee Comments addressed |
e4e8ed9
to
8faafe1
Compare
@JGreenlee Addressed and rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks good to me now, but I think we should include a unit test for this pipeline stage since it now stores a full set of stats.
I also didn't see a breakdown of manual testing. (Have you tested this locally in conjunction with e-mission/op-admin-dashboard#153 to verify that the new stats show up in the table?)
dfb5afc
to
f4c57f0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you have questions
self.assertIn("last_call_ts", profile, "User profile should contain 'last_call_ts'.") | ||
|
||
expected_total_trips = - | ||
expected_labeled_trips = - |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0 if you don't load any user inputs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JGreenlee load any user inputs how so? Like loading from the example or what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The real_examples
files like shankari_2015-aug-27
only have raw sensed data; i.e. what the phone automatically picked up while traveling
For some of the days, e.g. shankari_2016-06-20
we have an additional file shankari_2016-06-20.user_inputs
, which does not contain sensed data but it has "manual inputs" or "user inputs". These entries represent labels or survey responses that the user has recorded while using the app.
For a more in-depth test you could load both shankari_2016-06-20
and shankari_2016-06-20.user_inputs
and expected_labeled_trips
would be something other than 0
@JGreenlee I realized my code was NOT pushed and you may have reviewed the wrong thing.... my bad, I will take your notes into account with the latest. |
179594b
to
965cc17
Compare
@JGreenlee Comments addressed, ready for re-review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there!
# Retrieve the user profile | ||
profile = edb.get_profile_db().find_one({"user_id": self.UUID}) | ||
if profile is None: | ||
# Initialize the profile if it does not exist | ||
edb.get_profile_db().insert_one({"user_id": self.UUID}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if you take this out? Does something not work without it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I take this out, the profile is empty.
Traceback (most recent call last):
File "/Users/rsimpson/e-mission-server/emission/tests/analysisTests/intakeTests/TestUserStat.py", line 81, in testGetAndStoreUserStats
self.assertIsNotNone(profile, "User profile should exist after storing stats.")
AssertionError: unexpectedly None : User profile should exist after storing stats.
Meaning for some reason the entry is not being inserted. Does that hint to an issue with my additions or existing?
b01bd17
to
965cc17
Compare
@JGreenlee Addressed, see response -- in need of clarification |
a7008f8
to
3ef0517
Compare
|
||
TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' | ||
|
||
def count_trips(ts: esta.TimeSeries, key_list: list, extra_query_list: Optional[list] = None) -> int: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: I had an earlier comment here that was resolved but I don't think was addressed. I don't see the point of this function when it is just a wrapper around ts.find_entries_count
with the exact same params and no added functionality
In the event that a suggested change doesn't work for you or you have a good reason to do otherwise, that's fine; just reply and we can discuss
Or, if you just missed my comment, make sure not to resolve conversations until you have made a change that addresses them. When I have my code reviewed I like to react with thumbs up and reply "TODO" so I can keep track of what I have seen vs what I have actually implemented and pushed up
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I did address it, but I've been trying to rebase and do all this github branch magic and maybe it got reverted -- will do react and/or reply on future comments before resolving
OR I believe a better option is for the reviewer to resolve the comment? what are your thoughts on that approach in addition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TeachMeTW Code looks good but the new tests appear to be failing in GH Actions
Do they pass for you locally?
@JGreenlee Yes the tests pass locally for me. I believe the error is not related to my code changes. |
I don't know why it's passing locally and not here, but it is related to this PR because the test that's failing is the one you added. It's complaining about timestamps that look a little bit off. |
Shall I add delta/assert if almost equal? |
No we should investigate why it's different. What happens if you checkout master and run that test data through the pipeline? |
@JGreenlee I checkout master and run the test data through pipeline and its the same as the branch NOT the CI. I believe running ALL the tests must affect the ts. I tried to test runAllTest.sh but it crashes my console/vscode, perhaps you could verify |
I will try to verify tomorrow. I will also pass this along in the review process for Shankari's input. If |
Feel free to use either option. I will not merge until this is resolved. |
We do not understand why the pipeline start ts comes up as
I am confident that opcode = 'nrelop_dev-emulator-study_test'
day = 'shankari_2015-aug-27'
!./e-mission-py.bash bin/debug/purge_user.py -e $opcode
!./e-mission-py.bash bin/debug/load_timeline_for_day_and_user.py emission/tests/data/real_examples/$day $opcode
!./e-mission-py.bash bin/debug/intake_single_user.py -e $opcode
import emission.storage.timeseries.abstract_timeseries as esta
import emission.core.wrapper.user as ecwu
import pandas as pd
pd.set_option('display.float_format', str)
user_id = ecwu.User.fromEmail(opcode).uuid
ts = esta.TimeSeries.get_time_series(user_id)
ts.get_data_df("analysis/confirmed_trip") I can see that the first trip has start_ts of |
This specific problem has happened once before. I used Github's search feature with "org:e-mission 1440688707.867" and got a hit: #509 (comment) Reading through that PR, we still cannot pinpoint what the cause was. |
Commenting out all the test cases in TestTripSegmentation and replacing it with a dummy test with a print statment still invokes the same errors. Might be something with setup and teardown, might be not as I cleared everything and still the same error. Maybe its the pipeline -- not sure for now. I also discovered TestSectionSegmentation with TestGetUserStats has a similar error result. |
We should do both. |
Add per-operation timing to segment_current_trips using ect.Timer Added total, labeled, and last call Modified total and labeled trips to match op-admin implementation Modified last call to mirror op-admin implementation
Addressed comments, reduced overkill on refactor Forgot to add last_call_ts
8022a1f
to
cadb13e
Compare
@shankari Modified and pushed accordingly. Will make an issue as suggested. |
@TeachMeTW I figured out what was causing it and made a PR #1003 |
Nice find! |
a612c77
to
957c8fa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to roll forward and merge to unblock us; @TeachMeTW please address these comments in a follow-on PR
|
||
def _get_and_store_range(user_id, trip_key): | ||
ts = esta.TimeSeries.get_time_series(user_id) | ||
start_ts = ts.get_first_value_for_field(trip_key, "data.start_ts", pymongo.ASCENDING) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record, and to assist in understanding #1003 (comment)
this should be the start of the first trip.
Why is not the first location if the location points were received before?
The background sensed objects don't have a data.start_ts
, they are associated with data.ts
So this must find a trip-like object; since they are the only ones who have a start_ts. So it would really be the start timestamp of the first procssed trip.
The segmentation/raw_trip
is created with a start that corresponds to the start transition (as we do for draft trips). In the clean_and_resample
stage, we "fill in" the trip trajectory by interpolating to the end of the previous trip. So this should return the start_ts
from a cleaned/confirmed/composite trip.
@JGreenlee for visibility
@@ -107,7 +107,7 @@ def getRealExampleEmail(testObj): | |||
def fillExistingUUID(testObj): | |||
userObj = ecwu.User.fromEmail(getRealExampleEmail(testObj)) | |||
print("Setting testUUID to %s" % userObj.uuid) | |||
testObj.testUUID = userObj.uuir | |||
testObj.testUUID = userObj.uuid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to make sense, but I am not sure how the the tests were working without this. It looks like I changed this ~ 3 years ago
a45d4e3
The change was almost certainly an unintentional typo but the tests have been passing consistently since then
total_trips = ts.find_entries_count(key_list=["analysis/confirmed_trip"]) | ||
labeled_trips = ts.find_entries_count( | ||
key_list=["analysis/confirmed_trip"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cleanup: is there a reason this should be hardcoded instead of being trip_key
? In addition to being cleaner, this will also likely result in better performance, since repeated queries on the same objects are likely to hit the cache.
@@ -25,7 +26,7 @@ def add_pipeline_range(process_number, uuid_list, skip_if_no_new_data): | |||
continue | |||
|
|||
try: | |||
epi._get_and_store_range(uuid, "analysis/composite_trip") | |||
eaurs.get_and_store_user_stats(uuid, "analysis/composite_trip") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be changed. The scripts in historical
intended to be a record of the changes that we made to the database over time. At the time that the script was created
commit e98218e5f2505225284fa7a73088b8b2861dcc65
Author: Shankari <shankari@eecs.berkeley.edu>
Date: Wed Aug 16 07:13:49 2023 -0700
🦕 Migration script to iterate over all users and fill in the pipeline ranges
the function was get_and_store_range
Open to discussion about how to deal with historical migrations, but am reverting this change now.
@TeachMeTW there is now a merge conflict after I have pulled from master. I think this may be because your original base did not include You need to pull changes from master periodically, particularly when you are making changes to the same files |
- Introduced comprehensive test suite to validate functionality of the `get_and_store_user_stats` method. - Covered core scenarios including: - Correct data aggregation and storage. - Handling cases with no trips or missing data. - Verifying behavior with partial data and multiple invocations. - handling of invalid UUID inputs. - setup and teardown to ensure test isolation and clean up user data. - Included data insertion for confirmed trips, composite trips, and server API timestamps to simulate realistic scenarios. This initial test suite establishes a baseline for ensuring reliability of the `get_and_store_user_stats` function. remove Modified test Refactored the test and simplified it; validated all new user stats added store to intake Updated Based on requested changes Removed unnecessary wrapper Changed to Aug 21 for the time being
148136c
to
ae549f2
Compare
@JGreenlee @TeachMeTW this needs to be refactored further for it to work properly.
That's fine as long as we were only caching pipeline state, because it wouldn't change if the pipeline was skipped |
In e-mission#993 we expanded `_get_and_store_range` to store entries other than the pipeline range, notably the `last_call_ts`. However, adding it to the end of the pipeline causes it to be skipped most of the time. We have an early return from `run_intake_pipeline_for_user` if there is no new data, so the end of the pipeline (the code in e-mission#993) is effectively in the "else" That's fine as long as we were only caching pipeline state, because it wouldn't change if the pipeline was skipped But now that we are caching the last_call_ts the current location will not work. Concretely, if the app is contacting the server but is not uploading any data, last_call_ts will never be changed. Since this is a fix that I am implementing on a holiday weekend, I have not added unit tests to check this. @TeachMeTW please enhance the unit tests to verify that the stats are generated in both cases: (i) when there is new data and (ii) when there is no new data.
I have fixed this in #1005, @TeachMeTW please update the unit tests as outlined there |
In e-mission#993 we expanded `_get_and_store_range` to store entries other than the pipeline range, notably the `last_call_ts`. However, adding it to the end of the pipeline causes it to be skipped most of the time. We have an early return from `run_intake_pipeline_for_user` if there is no new data, so the end of the pipeline (the code in e-mission#993) is effectively in the "else" That's fine as long as we were only caching pipeline state, because it wouldn't change if the pipeline was skipped But now that we are caching the last_call_ts the current location will not work. Concretely, if the app is contacting the server but is not uploading any data, last_call_ts will never be changed. Since this is a fix that I am implementing on a holiday weekend, I have not added unit tests to check this. @TeachMeTW please enhance the unit tests to verify that the stats are generated in both cases: (i) when there is new data and (ii) when there is no new data. I have also changed the two tests that were running `epi.run_intake_pipeline_for_user` and checking the profile for results, since that is not run in that function any more. I am a bit surprised that `TestUserStat` was calling `epi.run_intake_pipeline_for_user` although `etc.runIntakePipeline` was enhanced to run the new step. @TeachMeTW Why was the enhancement needed? We need to figure out which method to call and be consistent about it as part of adding new tests (@JGreenlee, @TeachMeTW)
Overview
Enhances
_get_and_store_range
by addingtotal_trips
,labeled_trips
, andlast_call
fields to the user profile, enabling better insights into user activities and API usage.Changes
Extended User Profile Updates:
total_trips
: Total number of trips recorded for the user.labeled_trips
: Number of labeled/annotated trips.Integrated Last API Call Tracking:
stats/server_api_time
to retrieve timestamps of the lastGET
andPUT
API calls.last_call
in the user's profile in UNIX format.Testing
Please review and provide feedback or approval.