Skip to content

Commit 89f5356

Browse files
kfettichgitbook-bot
authored andcommitted
GitBook: [#3] Developer docs initial commit
1 parent 7001861 commit 89f5356

24 files changed

+576
-36
lines changed

.gitbook/assets/image (1).png

156 KB
Loading

.gitbook/assets/image (2).png

69.9 KB
Loading

.gitbook/assets/image (3).png

69.9 KB
Loading

.gitbook/assets/image (4).png

65.2 KB
Loading

.gitbook/assets/image.png

98.7 KB
Loading

README.md

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,3 @@
1-
# Project Overview
1+
# Architecture
22

3-
## [The Philadelphia Animal Welfare Society (PAWS)](https://github.com/CodeForPhilly/paws-data-pipeline/blob/master/phillypaws.org)
4-
5-
As the city's largest animal rescue partner and no-kill animal shelter, the [Philadelphia Animal Welfare Society (PAWS)](https://github.com/CodeForPhilly/paws-data-pipeline/blob/master/phillypaws.org) is working to make Philadelphia a place where every healthy and treatable pet is guaranteed a home. Since inception over 10 years ago, PAWS has rescued and placed 27,000+ animals in adoptive and foster homes, and has worked to prevent pet homelessness by providing 86,000+ low-cost spay/neuter services and affordable vet care to 227,000+ clinic patients. PAWS is funded 100% through donations, with 91 cents of every dollar collected going directly to the animals. Therefore, PAWS' rescue work (including 3 shelters and all rescue and animal care programs), administration and development efforts are coordinated by only about 70 staff members complemented by over 1500 volunteers.
6-
7-
This project seeks to provide PAWS with an easy-to-use and easy-to-support tool to extract data from multiple source systems, confirm accuracy and appropriateness, clean/validate data where necessary (a data hygiene and wrangling step), and then load relevant data into one or more repositories to facilitate (1) a highly-accurate and rich 360-degree view of PAWS constituents (Salesforce is a likely candidate target system; already in use at PAWS) and (2) flexible ongoing data analysis and insights discovery (e.g. a data lake / data warehouse).
8-
9-
Through all of its operational and service activities, PAWS accumulates data regarding donations, adoptions, fosters, volunteers, merchandise sales, event attendees (to name a few), each in their own system and/or manual (Google Sheet) tally. This vital data that can drive insights remains siloed and is usually difficult to extract, manipulate, and analyze. Taking all of this data, making it readily available, and drawing inferences through analysis can drive many benefits:
10-
11-
PAWS operations can be better informed and use data-driven decisions to guide programs and maximize effectiveness; Supporters can be further engaged by suggesting additional opportunities for involvement based upon pattern analysis; Multi-dimensional supporters can be consistently (and accurately) acknowledged for all the ways they support PAWS (i.e. a volunteer who donates and also fosters kittens), not to mention opportunities to further tap the potential of these enthusiastic supporters.
12-
13-
### [The Data Pipeline](https://codeforphilly.org/projects/paws\_data\_pipeline)
14-
15-
Through all of its operational and service activities, PAWS accumulates data regarding donations, adoptions, fosters, volunteers, merchandise sales, event attendees (to name a few), each in their own system and/or manual tally. This vital data that can drive insights remains siloed and is usually difficult to extract, manipulate, and analyze.
16-
17-
This project provides PAWS with an easy-to-use and easy-to-support tool to extract constituent data from multiple source systems, standardize extracted data, match constituents across data sources,\
18-
load relevant data into Salesforce, and run an automation in Salesforce to produce an RFM score. Through these processes, the PAWS data pipeline has laid the groundwork for facilitating an up-to-date 360-degree view of PAWS constituents, and flexible ongoing data analysis and insights discovery.
19-
20-
### Uses
21-
22-
* The pipeline can inform the PAWS development team of new constiuents through volunteer or foster engagegement
23-
* Instead of manually matching constituents from volunteering, donations and foster/adoptions, PAWS staff only need to upload the volunteer dataset into the pipeline, and the pipeline handles the matching
24-
* Volunteer and Foster data are automatically loaded into the constituent's SalesForce profile
25-
* An RFM score is calculated for each constituent using the most recent data
26-
* Data analyses can use the output of the PDP matching logic to join datasets from different sources; PAWS can benefit from such analyses in the following ways:
27-
* PAWS operations can be better informed and use data-driven decisions to guide programs and maximize effectiveness;
28-
* Supporters can be further engaged by suggesting additional opportunities for involvement based upon pattern analysis;
29-
* Multi-dimensional supporters can be consistently (and accurately) acknowledged for all the ways they support PAWS (i.e. a volunteer who donates and also fosters kittens), not to mention opportunities to further tap the potential of these enthusiastic supporters.
30-
31-
### [Code of Conduct](https://codeforphilly.org/pages/code\_of\_conduct)
32-
33-
###
34-
35-
##
3+
This section contains the architecture for the PAWS data pipeline project.

SUMMARY.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11
# Table of contents
22

3-
* [Project Overview](README.md)
4-
* [End User Manual](end-user-manual.md)
3+
* [Architecture](README.md)
4+
* [Async on the cheap (for MVP)](architecture/async-on-the-cheap-for-mvp.md)
5+
* [Execution status stages](architecture/execution-status-stages.md)
6+
* [User management and authorization](architecture/user-management-and-authorization.md)
7+
* [RFM](architecture/rfm.md)
8+
* [Data Flow](architecture/data-flow.md)
9+
* [Database Schema](architecture/database-schema.md)
10+
* [Setup](setup/README.md)
11+
* [Getting Started](setup/getting-started.md)
12+
* [Local Setup](setup/local-setup.md)
13+
* [Kubernetes Setup](setup/kubernetes-setup.md)
14+
* [Accessing APIs without React](setup/accessing-apis-without-react.md)
15+
* [Deployment](deployment/README.md)
16+
* [Using GitHub actions](deployment/using-github-actions.md)
17+
* [Deploying PDP within the Code for Philly cluster](deployment/deploying-pdp-within-the-code-for-philly-cluster.md)
18+
* [Kubernetes logs](deployment/kubernetes-logs.md)
19+
* [Troubleshooting](troubleshooting/README.md)
20+
* [Dups Problem](troubleshooting/dups-problem.md)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Async on the cheap (for MVP)
2+
3+
### Introduction
4+
5+
It's recognized \[1, 2] that the best way to handle long-running tasks is to use a task queue, allowing separation of the middle layer (API server) and the execution server. But as we're trying to get an MVP out for feedback, it's not unreasonable to use a less-than-perfect solution for the interim. Here's a few ideas for discussion:
6+
7+
### _Continue to treat execute() as synchronous but stream back status information_
8+
9+
We've been operating (at the API server) with a model of _receive request, do work, return() with data_. But both Flask and JS support streaming data in chunks from server to client:\
10+
Flask: [Streaming Contents](https://flask.palletsprojects.com/en/1.1.x/patterns/streaming/)\
11+
JS: [Using readable streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams\_API/Using\_readable\_streams)\
12+
\
13+
From the Flask side, the data it streams back would be status updates (_e.g._, every 100 rows processed) which the React client would use to update the display. When the server sends back "complete", React displays a nice completion message and the user proceeds to the 360 view.
14+
15+
#### **Evaluation**
16+
17+
Doesn't appear to require much heavy lifting at server or client (we would need to figure out how to feed the generator on the server) but may be a bit brittle; if there's any kind of network hiccup (or user reloads the page?) the stream would be broken and we wouldn't be able to tell the user anything useful.
18+
19+
### _Client aborts Fetch, polls status API until completion_
20+
21+
In this idea, instead of waiting for the execute() Fetch to complete, the React client uses an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort) to cancel the pending Fetch. It then starts polling the API execution status endpoint, displaying updates until that endpoint reports that the operation is complete.
22+
23+
**Evaluation**
24+
25+
Using SQLAlchemy's `engine.dispose()`, and two uWSGI processes. I've got `/api/get_execution_status/<job_id>` working correctly. I'd probably want to have it find the latest job
26+
27+
![](https://user-images.githubusercontent.com/11001850/112061042-4ceb9580-8b34-11eb-8dc7-fb9eede44d7d.png)
28+
29+
instead of having to specify it (although we could use the streaming model above to send back the job\_id). We need to figure what side-effects there might be to cancelling the fetch. I presume the browser would drop the connection; will Flask assume it can kill the request?\
30+
The client could check status when the page loads to see if there's a running job so it would be more robust in the face of network issues or reloads.
31+
32+
\[1] [https://flask.palletsprojects.com/en/1.1.x/patterns/celery/](https://flask.palletsprojects.com/en/1.1.x/patterns/celery/)\
33+
\[2] [https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xxii-background-jobs](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xxii-background-jobs)
34+

architecture/data-flow.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Data Flow
2+
3+
![](../.gitbook/assets/image.png)
4+
5+
[flow chart](https://app.lucidchart.com/invitations/accept/0602fccf-18f9-48d4-84ff-ffe5f0b03e7a)
6+
7+
**ShelterLuv People**: This data is being pulled via a script that calls ShelterLuv and saves data as a csv into a Dropbox folder via an "app". It is set up to use config + cron job, although this is not yet active in deployment. Every time it pulls data, it pulls everything because the API doesn't support pagination. To configure automation, the config file needs to contain the app ID

architecture/database-schema.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Database Schema
2+
3+
TODO: fix link
4+
5+
[https://app.diagrams.net/#G1X4KbjYf7vcrfbeJLfyCj8xUPp8zGcV2k](https://app.diagrams.net/#G1X4KbjYf7vcrfbeJLfyCj8xUPp8zGcV2k)
6+
7+
[ Add a custom footer](https://github.com/CodeForPhilly/paws-data-pipeline/wiki/\_new?wiki%5Bname%5D=\_Footer)
8+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Execution status stages
2+
3+
The execution\_status table will be updated for a given job\_id through the stages in the diagram.
4+
5+
![](<../.gitbook/assets/image (1).png>)

architecture/rfm.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# RFM
2+
3+
## RFM Data Flows
4+
5+
![](<../.gitbook/assets/image (2).png>)
6+
7+
## RFM Database Tables
8+
9+
![](<../.gitbook/assets/image (4).png>)
10+
11+
## RFME Bin Logic
12+
13+
### Recency:
14+
15+
If a person's last donation was:
16+
17+
* the last 180 days: R = 5,
18+
* 180-365 days ago: R = 4
19+
* 365 - 728 days ago: R = 3,
20+
* 728 - 1093 days ago: R = 2
21+
* More than 0: R = 1
22+
* Never given: R = 0
23+
24+
### Frequency:
25+
26+
If in the last 24 months someone has made a total of
27+
28+
* 24 or more donations: F = 5,
29+
* 12 - 23 donations: F = 4
30+
* 3 - 11 donations: F = 3
31+
* 2 donations: F = 2;
32+
* 1 donation: F = 1
33+
* 0 donations: F = 0
34+
35+
### Monetary value:
36+
37+
If someone's cumulative giving in the past 24 months is
38+
39+
* $2001 ore more: M = 5
40+
* $501 - $2000: M = 4
41+
* $250 - $500: M = 3
42+
* $101 - $249: M = 2
43+
* $25 - $100 - $50: M = 1
44+
* $0 - 25: M = 0
45+
46+
### the impact labels are as follows:
47+
48+
* High impact: (F+M)/2 is between 4-5
49+
* Low impact: (F+M)/2 is between 1-3
50+
51+
### the engagement labels are as follows:
52+
53+
* engaged: R = 5
54+
* slipping: R is 3-4
55+
* disengaged: R is 1-2
56+
57+
### CAN WE INTEGRATE SCORING FOR FOSTERS/VOLUNTEERS?
58+
59+
"RFME" (E FOR ENGAGEMENT)
60+
61+
* volunteered or fostered in the past 30 days: E = 5
62+
* volunteered or fostered in the past 6 months days: E = 4
63+
* volunteered or fostered in the past year: E = 3
64+
* volunteered or fostered in the past 2 years: E = 2
65+
* volunteered or fostered ever: E = 1
66+
* volunteered or fostered never: E = 0
67+
68+
(modified from Lauren's request of: E = 5 (CURRENT), E = 4 (WITHIN THE PAST YEAR), E = 3 (WITHIN THE PAST TWO YEARS), E = 2 (EVER), E = 0 (NEVER), because "1" value was missing and needed more specific definition of "current")
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# User management and authorization
2+
3+
### Intro
4+
5+
Because the 360 view gives access to sensitive personal information, we need to ensure that only authorized users can access PDP pages.
6+
7+
### Roles
8+
9+
There are three authorization levels/user roles:
10+
11+
* User: Can use the **Common API** to view 360 data but not make any changes
12+
* Editor: User role plus can use the **Editor API** to manually link existing contacts
13+
* Admin: Editor role plus can use the **Admin API** to upload data and manage users
14+
15+
### Login
16+
17+
Upon login, the user API shall return a JSON Web \[Access] Token (JWT) with a limited lifetime\[1]. The JWT includes the user's role.
18+
19+
### Authorization
20+
21+
The React client shall render only resources that are authorized by the current user's role. The React client shall present the JWT (using the **Authorization: Bearer** header) to the API server when making a request.\
22+
The API server shall verify that user represented by the JWT is authorized to access the requested API endpoint. The server API shall return a 403 status if the user is not authorized to access the endpoint.
23+
24+
### Implementation
25+
26+
User roles are stored in the database `pdp_user_roles` table and per-user data is stored in the `pdp_users` table.
27+
28+
### API
29+
30+
**No authorization required**
31+
32+
| Endpoint | Description |
33+
| --------------------- | --------------------------------- |
34+
| `/api/user/test` | Liveness test, always returns 200 |
35+
| `/api/user/test_fail` | Always fails with 401 |
36+
| `/api/user/login` | Login |
37+
38+
**Valid JWT required**
39+
40+
| Endpoint | Description |
41+
| --------------------- | ------------------------------------------- |
42+
| `/api/user/test_auth` | Returns 200 if valid JWT presented |
43+
| `/api/user/logout` | Logout (optional, as client can delete JWT) |
44+
45+
**Admin role required**
46+
47+
| Endpoint | Description |
48+
| -------------------------------- | ------------------------------ |
49+
| `/api/admin/user/create` | Create user |
50+
| `/api/admin/user/get_user_count` | Get count of all users in DB |
51+
| `/api/admin/user/get_users` | Get list of users with details |
52+
53+
54+
55+
\[1] _We need to decide on a lifetime that provides an appropriate balance between convenience and security. An expired Access token will require the user to login again. There is a Refresh-type token that allows automatic renewal of Access tokens without requiring the user to log in but the power of this kind of token poses additional security concerns._

deployment/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Deployment
2+
3+
This section contains deployment instructions for the PAWS data pipeline project.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Deploying PDP within the Code for Philly cluster
2+
3+
## PDP hosting
4+
5+
The PAWS Data Pipeline runs on a Kubernetes cluster donated by [Linode](https://github.com/CodeForPhilly/paws-data-pipeline/wiki/www.linode.com) to the Code for Philly (CfP) project and is managed by the CfP [civic-cloud](https://forum.codeforphilly.org/c/public-development/civic-cloud/17) team.
6+
7+
The code and configurations for the various projects running on the cluster are managed using [hologit](https://github.com/JarvusInnovations/hologit) which
8+
9+
> _lets you declaratively define virtual sub-branches (called holobranches) within any Git branch that mix together content from their host branch, content from other repositories/branches, and executable-driven transformations._\[1]
10+
11+
The pieces for the sandbox clusters can be found in the `.holo` directory in the PDP repository and the [sandbox](https://github.com/CodeForPhilly/cfp-sandbox-cluster) or [live](https://github.com/CodeForPhilly/cfp-live-cluster) cluster repos as appropriate.
12+
13+
The branch (within the PDP repo) that holds the `.holo` directory is specified at [paws-data-pipeline.toml](https://github.com/CodeForPhilly/cfp-sandbox-cluster/blob/main/.holo/sources/paws-data-pipeline.toml).
14+
15+
RBAC roles and rights are defined at [admins](https://github.com/CodeForPhilly/cfp-sandbox-cluster/blob/main/admins/paws-data-pipeline.yaml).
16+
17+
### Updating deployed code
18+
19+
To deploy new code,
20+
21+
* Bump the image tag versions in **paws-data-pipeline/src/helm-chart/values.yaml** to the value you'll use for this deployment (e.g. v.2.3.4)
22+
* Commit to master, tag with the above value, push to GitHub with --follow-tags
23+
* Open a PR against [cfp-sandbox-cluster/.holo/sources/paws-data-pipeline.toml](https://github.com/CodeForPhilly/cfp-sandbox-cluster/blob/main/.holo/sources/paws-data-pipeline.toml) setting ref = "refs/tags/v2.3.4"
24+
* The sysadmin folks hang out at [https://forum.codeforphilly.org/c/project-support-center/sysadmin/20](https://forum.codeforphilly.org/c/project-support-center/sysadmin/20) and you can ask for help there
25+
26+
### Ingress controller
27+
28+
CfP uses the [ingress-nginx](https://kubernetes.github.io/ingress-nginx) ingress controller (_not to be confused with an entirely different project called **nginx-ingress**_)
29+
30+
The list of settings can be found here: [Settings](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/)\
31+
To update settings, edit [release-values.yaml](https://github.com/CodeForPhilly/cfp-sandbox-cluster/blob/main/paws-data-pipeline/release-values.yaml) and create a pull request.
32+
33+
SSL cert configuration can also be found in [release-values.yaml](https://github.com/CodeForPhilly/cfp-sandbox-cluster/blob/main/paws-data-pipeline/release-values.yaml)
34+
35+
36+
37+
1. _“Any sufficiently advanced technology is indistinguishable from magic.”_ Arthur C. Clarke

deployment/kubernetes-logs.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Kubernetes logs
2+
3+
Database logs are visible by attaching to paws-datapipeline-db and viewing `/var/lib/postgresql/data/log/`
4+
5+
Since Kubernetes performs liveness tests, there are a lot of test lines in the logs which you'll want to filter out
6+
7+
* On paws-datapipeline-server, filter on "that don't match" `/api/user/test`
8+
* On paws-datapipeline-client, filter on "that don't match" `GET /`

deployment/using-github-actions.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Using GitHub actions
2+
3+
To run the CI/CD action:
4+
5+
* Ensure you have `release-containers.yml` in `/paws-data-pipeline/.github/workflows`
6+
* Tag your code: `git tag -fa v1.4 -m "Still testing Actions"`
7+
* Push with `git push -f --tags`
8+
9+
Check the [Actions](https://github.com/CodeForPhilly/paws-data-pipeline/actions) page to see the progress.

setup/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Setup
2+
3+
This section contains setup instructions for the PAWS data pipeline project.

setup/accessing-apis-without-react.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Accessing APIs without React
2+
3+
As of [c863c77](https://github.com/CodeForPhilly/paws-data-pipeline/commit/c863c77cfb79901f65936a851834ec298aec5ec1) , a valid JWT is needed to access API endpoints. If you don't want to use the normal route (React) there are a few options:
4+
5+
* Programmatically through JS: See examples in Login.js, Admin.js, Search.js
6+
* Programmatically through Python: See /server/test\_api.py
7+
* Using Postman
8+
9+
### Using Postman
10+
11+
To use Postman:
12+
13+
* Get a valid JWT, which is returned by /api/user/login
14+
* You can do this through Postman or by capturing the returned `access_token`value using browser devtools
15+
16+
![Postman\_login](https://user-images.githubusercontent.com/11001850/114760059-f0dbf180-9d2c-11eb-83d9-27ea69ceaa66.png)
17+
18+
* Tell Postman to use that value for API calls
19+
* Copy the value
20+
* Edit the collection (three dots when hovering to right of collection name, Edit)
21+
22+
![Postman\_view\_more](https://user-images.githubusercontent.com/11001850/114760490-592ad300-9d2d-11eb-935b-2a67220e903c.png)
23+
24+
* Choose Authorization, Type: Bearer Token
25+
* Paste value into the Token field, save
26+
27+
![Postman\_token](https://user-images.githubusercontent.com/11001850/114760547-69db4900-9d2d-11eb-8e2c-779060b81205.png)
28+
29+
Start issuing API calls
30+
31+
### Error codes
32+
33+
401 - Bad login credentials\
34+
403 - Tried to access an Admin endpoint with user-level credentials\
35+
422 - JWT value was corrupted/failed validation

0 commit comments

Comments
 (0)