-
Notifications
You must be signed in to change notification settings - Fork 5
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
Geocoding on activist update #197
Conversation
Thanks for this! I will review later today. |
Me too, ty and will check it out!
Le mar. 2 mai 2023, 09:49, jakehobbs ***@***.***> a écrit :
… Thanks for this! I will review later today.
—
Reply to this email directly, view it on GitHub
<#197 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAS6DL7KDYYOF5CEM33GVILXEE3J3ANCNFSM6AAAAAAXSUA7TI>
.
You are receiving this because your review was requested.Message ID:
***@***.***>
|
Yes, maintaining package-lock.json changes in version control ensures reproducible builds. package.json only specifies a range of versions allowed to build the project, not exact versions. |
model/activist.go
Outdated
@@ -1131,12 +1146,14 @@ func UpdateActivistData(db *sqlx.DB, activist ActivistExtra, userEmail string) ( | |||
// TODO: make this work for other chapters too, maybe based on Chapters table mailing list info? | |||
// or maybe just passing chapter Name or ID to the signup service? | |||
if origActivist.ChapterID == 47 { | |||
activistInfoChanged := activist.Name != origActivist.Name || | |||
activistAddressChanged := activist.StreetAddress != origActivist.StreetAddress || |
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.
looks like StreetAddress is not used by the mailing list, so this should not trigger a call to update the mailing list.
model/activist.go
Outdated
activist.City != origActivist.City || | ||
activist.State != origActivist.State | ||
activistInfoChanged := activistAddressChanged || | ||
activist.Location != origActivist.Location || |
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.
should location be considered in activistAddressChanged instead? it look like it contains the Zip which is used by the mailing list.
but then Zip is not used by the geo API, so maybe this logic should not be shared between geo api logic and mailing list logic, in which case using names like "isMailingListInfoChanged" and "isGeoInfoChanged" would better convey the intent.
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.
Yeah I don't think zip is used for geocoding so I'll do the latter
model/activist.go
Outdated
@@ -1155,7 +1172,23 @@ func UpdateActivistData(db *sqlx.DB, activist ActivistExtra, userEmail string) ( | |||
fmt.Println("ERROR updating activist on mailing list:", err.Error()) | |||
} | |||
} | |||
fmt.Println("Updated: ", activist.Location, activist.City, activist.State) | |||
if activistAddressChanged && activist.StreetAddress != "" && activist.City != "" && activist.State != "" { |
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.
please split the body of this 'if' into a separate function (e.g. called GetCoordinatesFromAddress
) in a separate file (single responsibility principle). i suggest the function take a single string parameter for the address and return the location and any error.
additionally, but optionally, the 'if' itself could be its own function called updateActivistCoordinates
which would allow early returns in case of error rather than nesting the happy path inside of an if
block.
model/activist.go
Outdated
request := "https://maps.googleapis.com/maps/api/geocode/json?address=" + full_address + "&key=" + config.GooglePlacesAPIKey | ||
resp, err := http.Get(request) | ||
if err != nil { | ||
fmt.Println("Error geocoding activist location", err) |
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.
if there is an error might be better to break out of this if
somehow (or return early if you split this into another function)
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.
On this note, I think we'd probably prefer to silently fail (& log it) but still go through w/ saving the activist in the db if the gecocoding service fails for w/e reason.
I feel it's pretty important that activist data is still able to be updated even if the geocoding service is failing.
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.
Thinking about this further... maybe it would be ideal to just fire off a goroutine here that handles the things that need to happen in the background, such as updating mailing list and coords? B/c we don't really want the user to have to wait around for those things to happen before the activist is saved in the db. (This was a bit of a flaw in the original logic for updating the mailing list.)
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.
probably the coords should still be looked synchronously because the result is used in the database update? if so, could the signup be made async in a separate PR so Jeff's change isn't held up?
just wondering, is the async idea to make it faster when an admin of ADB clicks save to update a user? did you notice the adb signup was slow?
maybe whatever is simplest/easiest is best for this probably rarely used feature. but some possible improvements mainly as thought experiments:
- it would be nice to know if the signup operations fails, maybe by an email notification to our team, perhaps directly or by monitoring error logs.
- it would be nice to ensure async operations complete even if the web server container is requested to shut down or even if an error in the main function causes it to return and shut down. perhaps there would be a work queue that the signup gets added to, and the main function can wait for the queue on the last line before exiting.
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.
Fair points. Yeah, I was thinking the main reason would be so it saves faster, but I that might be a bit of an over optimization for now. I def won't block on it.
We have log drains in AWS and can set up Cloudwatch alerts for whatever.
The signup service has its own queue. That being said, I do think the background jobs that run in ADB could use a huge overhaul at some point if we have bandwidth. Right now they only run within a single instance of the app (via an environment variable that enables them). It would be great to implement a more robust job system.
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.
Maybe walking through how the signup service works at the next meeting would be useful. A lot of stuff feeds in and out of it, and would be good for others to know how it works.
model/activist.go
Outdated
@@ -441,6 +444,18 @@ type GetActivistOptions struct { | |||
UpcomingFollowupsOnly bool `json:"upcoming_followups_only"` | |||
} | |||
|
|||
type GeocodeResponse struct { |
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.
please a comment indicating that this structure corresponds to Google's geo coding API with a link to that API's documentation
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 took a brief look over this and p much agree w/ what Alex pointed out. Left one comment on one of his comments, but I don't have anything else to add other than that.
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.
Thanks for the updates! Just got a few more suggestions.
geoInfoChanged := activist.City != origActivist.City || | ||
activist.State != origActivist.State || | ||
activist.StreetAddress != origActivist.StreetAddress | ||
mailingListInfoChanged := activist.Name != origActivist.Name || | ||
activist.Email != origActivist.Email || | ||
activist.Phone != origActivist.Phone || | ||
activist.Location != origActivist.Location || |
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.
would be nice to move this down a couple lines underneath the state != state check to keep things in the same order that they are used later on, to more easily verify correctness at a glance.
model/activist.go
Outdated
@@ -1131,14 +1145,17 @@ func UpdateActivistData(db *sqlx.DB, activist ActivistExtra, userEmail string) ( | |||
// TODO: make this work for other chapters too, maybe based on Chapters table mailing list info? | |||
// or maybe just passing chapter Name or ID to the signup service? | |||
if origActivist.ChapterID == 47 { | |||
activistInfoChanged := activist.Name != origActivist.Name || | |||
geoInfoChanged := activist.City != origActivist.City || |
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.
please declare this where it is used, just above if geoInfoChanged && ...
fmt.Printf("No geocoding results found for address %v. Not updating Lat and Lng", full_address) | ||
return nil | ||
} else { | ||
return &Location{Lat: geocode_response.Results[0].Geometry.Location.Lat, Lng: geocode_response.Results[0].Geometry.Location.Lng} |
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.
If you wouldn't mind checking if Results[0] and Geometry and Location are non-nil, I think that could prevent a segfault. Even if the API always returns non-nil now, I worry this could change and we wouldn't want the server to crash - just log an error and carry on.
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'm a beginner at Go, but from my understanding this wouldn't be possible? Constructing a GeocodeResponse from json that doesn't contain these fields would just result in the default values being included for lat and lng, which would be 0 and 0
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.
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.
Oh got it. My bad :) I'm a Go beginner too. So a struct inside struct has a default value of the 0 value of the inner struct, as opposed to a struct pointer inside a struct which would be nil.
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.
Looks good to me. Let's see if Jake has any suggestions since it's the first PR in this codebase for either of us, I think.
Can you select "squash" merge strategy rather than "merge"? Otherwise it will clutter the main branch log since the many commits in this branch are all for one logical change. If it lets you type a commit message, perhaps use something slightly more precise like "Geocode activist address on update"
resp, err := http.Get(request) | ||
if err != nil { | ||
fmt.Println("Error geocoding activist location", err) | ||
return nil |
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.
optional: return the error (and print it in the caller) to allow callers to handle errors as needed, thereby making this function more reusable
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.
yeah i agree returning the error would be good as best practice, then the caller could just choose to ignore it if you don't want to actually handle it.
fmt.Println("Error geocoding activist location", err) | ||
return nil | ||
} | ||
defer resp.Body.Close() |
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.
consider adding a blank line between the HTTP fetching logic and GeocodeResponse parsing logic.
fmt.Printf("No geocoding results found for address %v. Not updating Lat and Lng", full_address) | ||
return nil | ||
} else { | ||
return &Location{Lat: geocode_response.Results[0].Geometry.Location.Lat, Lng: geocode_response.Results[0].Geometry.Location.Lng} |
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.
Oh got it. My bad :) I'm a Go beginner too. So a struct inside struct has a default value of the 0 value of the inner struct, as opposed to a struct pointer inside a struct which would be nil.
resp, err := http.Get(request) | ||
if err != nil { | ||
fmt.Println("Error geocoding activist location", err) | ||
return nil |
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.
yeah i agree returning the error would be good as best practice, then the caller could just choose to ignore it if you don't want to actually handle it.
Attempt at geocoding addresses with google API on activist update
Tested locally on a variety of addresses. Google Geocoding API has a bit undefined behavior when entering bogus addresses but seems to work well with real addresses.
Not sure if I should include package-lock.json changes in my commits?