[eCAPRIS status collector] Create view to combine Moped internal notes, Moped status updates, and eCapris status updates#1607
Conversation
…ecapris author emails
✅ Deploy Preview for atd-moped-main ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| -- Update moped_note_types to have source column | ||
| ALTER TABLE moped_note_types | ||
| ADD COLUMN source text NOT NULL DEFAULT 'moped'; | ||
|
|
||
| -- Insert eCapris status note type that will be used in notes view | ||
| INSERT INTO moped_note_types (name, slug, source) | ||
| VALUES | ||
| ('eCapris Status Update', 'ecapris_status_update', 'ecapris'); | ||
|
|
||
| COMMENT ON COLUMN moped_note_types.source IS 'Source of the note type, e.g., Moped or eCapris applications'; |
There was a problem hiding this comment.
The reasoning behind adding the eCapris note type to our lookup table is that our React code renders radio buttons and filter buttons based off values in this table. The new source column helps us control when these elements render - for example, radios to choose new note type could be limited to Moped-sourced types only.
I realize that changes the concept of this table from "Moped note types" to "types of notes that can be displayed in Moped." It is unlikely that we will see other types, and I'm hoping the source column is enough to help us remember how this works in the future.
| moped_phases.phase_key AS phase_key, | ||
| moped_proj_notes.is_deleted, | ||
| moped_proj_notes.phase_id, | ||
| TRUE AS is_editable, |
There was a problem hiding this comment.
The intent of is_editable in this view is to help us know when to edit components like the pencil and trash icons since eCapris statuses will not be editable in Moped. This would work along with the existing business logic in the React code that note authors can only edit their own notes and statuses.
| COALESCE( | ||
| (moped_users.first_name || ' ' || moped_users.last_name), | ||
| CASE | ||
| WHEN ecapris_subproject_statuses.reviewed_by_name LIKE '%,%' | ||
| THEN TRIM(SPLIT_PART(ecapris_subproject_statuses.reviewed_by_name, ',', 2)) || ' ' || TRIM(SPLIT_PART(ecapris_subproject_statuses.reviewed_by_name, ',', 1)) | ||
| ELSE LOWER(ecapris_subproject_statuses.reviewed_by_email) | ||
| END | ||
| ) AS author, |
There was a problem hiding this comment.
I was originally going to use reviewed_by_email as a fallback to eCapris statuses that didn't match an existing Moped user, but this column is not always populated in the eCapris database.
I could not find any statuses without reviewed_by_name or statuses where the format was not Last, First but I added a check just in case we see something different in the future.
| ('M' || moped_proj_notes.project_note_id) AS id, | ||
| moped_proj_notes.project_note_id AS original_id, |
There was a problem hiding this comment.
The prefixed ids serve as unique keys for rendering lists of notes. The original_id is needed to know which editable notes and statuses to mutate.
| NULL AS phase_name, | ||
| NULL AS phase_key, |
There was a problem hiding this comment.
eCapris statuses do have "phases" with their own lookup values. No stakeholders have asked for us to attempt to map them to Moped phases, and it doesn't seem like a great idea to me to put effort into this.
Either way, our React code doesn't render a status badge when these are null which is what we want to happen.
johnclary
left a comment
There was a problem hiding this comment.
Mike, I'm blocked on replicating the DB at the moment, so I'm going to submit these handful of comments in case they're helpful. I didn't see your comments until I had reviewed. This looks great! Will come back to test it soon.
| -- Most recent migration: moped-database/migrations/default/1748534889272_create_notes_view/up.sql | ||
|
|
||
| CREATE OR REPLACE VIEW combined_project_notes AS SELECT | ||
| 'M'::text || moped_proj_notes.project_note_id AS id, |
There was a problem hiding this comment.
is the intention to render this ID in the UI? or merely have unique ID for every row in the view? my first reaction is is to suggest you use moped_ and ecapris_ as prefixes to make it self-documenting.
There was a problem hiding this comment.
yes, the intent is to have a unique id that we can depend on for React keys, and I think that adding the full names is a great call. I'll update 🙏
| WHEN ecapris_subproject_statuses.reviewed_by_name ~~ '%,%'::text THEN (TRIM(BOTH FROM SPLIT_PART(ecapris_subproject_statuses.reviewed_by_name, ','::text, 2)) || ' '::text) || TRIM(BOTH FROM SPLIT_PART(ecapris_subproject_statuses.reviewed_by_name, ','::text, 1)) | ||
| ELSE LOWER(ecapris_subproject_statuses.reviewed_by_email) | ||
| END | ||
| ) AS author, |
There was a problem hiding this comment.
If it's possible to include comments in these files it'd be nice to have some sample strings of what you're parsing here.
There was a problem hiding this comment.
I can't remember if comments persist inside of these views after the bot handles them or not so I'll give it a go, and we'll find out. 🚀
There was a problem hiding this comment.
Nope, the comments didn't make it through. Maybe an improvement that we could look into down the road with a future VZ SQL bot. 🤖
| moped_note_types.slug AS note_type_slug, | ||
| null::text AS phase_name, | ||
| null::text AS phase_key, | ||
| false AS is_deleted, |
There was a problem hiding this comment.
I can't recall if you mentioned whether or not ecapris statuses may ever be deleted upstream?
There was a problem hiding this comment.
Not sure if I mentioned or not but they are not deleted upstream. We should be covered handling edits only.
| null::text AS phase_key, | ||
| false AS is_deleted, | ||
| null::integer AS phase_id, | ||
| false AS is_editable, |
There was a problem hiding this comment.
Nice! this view is looking great—I can see how this is going to feed nicely into the UI code 🚀
| NULL AS phase_name, | ||
| NULL AS phase_key, |
…_filter Disable eCAPRIS Notes Filter
…pris_false Update should_sync_ecapris_statuses to false for projects without eCAPRIS IDs
… text when switched on
Handle eCAPRIS sync activity in log when there is no eCAPRIS subproject id
…ty_log Show eCapris status syncing updates in project activity log
…atuses Show eCapris statuses in Notes tab
Associated issues
Closes cityofaustin/atd-data-tech#22700, cityofaustin/atd-data-tech#22217
This PR creates a new view to collect all types of notes that can be displayed in the Notes tab into one single timeline. The view handles the mapping of eCapris columns that we want to show in the UI to conform to the existing schema of Moped notes and status updates. It also joins in note type and note phase data since these details are needed for the current note features.
It also updates the
project_list_viewto consider whether a project has an eCapris subproject ID and if syncing is enabled. This will prevent eCapris statuses from showing on projects where the PMs have opted out of eCapris statuses (toggle is off in Notes tab) without any additional business logic in the React code or AGOL ETL.These are the places that we are concerned about seeing status updates:
project_list_viewwhich handles whether to include eCapris statuses or notproject_list_viewwhich handles whether to include eCapris statuses or notThis feels like a good way to wrangle the concept of notes in a single place - I'm open to other ideas too!
Testing
URL to test:
Local
Steps to test:
# Get only Moped-type notes query GetCombinedProjectNotes { combined_project_notes_view(where: {project_id: {_eq: 331}}, order_by: {created_at: desc}) { author created_at ecapris_subproject_id id is_editable note_type_name phase_id project_id project_note created_by_user_id original_id note_type_slug phase_key phase_name } }# Get the latest status from the project_list_view query GetProjectListLatestStatus{ project_list_view(where: {project_id: {_eq: 331}}){ project_id project_status_update } }Ship list