@@ -44,6 +44,167 @@ with RMPClient() as client:
4444 print (rating.date, rating.quality, rating.comment)
4545```
4646
47+ ## How it works
48+
49+ ### Package architecture
50+
51+ ``` mermaid
52+ flowchart TB
53+ subgraph Your code
54+ User["Your script / app"]
55+ end
56+
57+ subgraph rmp_client [rmp_client package]
58+ Client["RMPClient\n(client.py)"]
59+ Config["RMPClientConfig\n(config.py)"]
60+ Models["Models\n(School, Professor, Rating)\n(models.py)"]
61+ Errors["RMPError hierarchy\n(errors.py)"]
62+ end
63+
64+ subgraph HTTP layer
65+ HttpCtx["HttpClientContext\n(http.py)"]
66+ Http["HttpClient\n(retries, headers)"]
67+ Bucket["TokenBucket\n(rate_limit.py)"]
68+ end
69+
70+ subgraph External
71+ API["RMP GraphQL API\n(ratemyprofessors.com)"]
72+ end
73+
74+ User --> Client
75+ Client --> Config
76+ Client --> HttpCtx
77+ HttpCtx --> Http
78+ Http --> Bucket
79+ Http --> API
80+ Client --> Models
81+ Client --> Errors
82+ ```
83+
84+ ### Request flow
85+
86+ ``` mermaid
87+ sequenceDiagram
88+ participant User
89+ participant RMPClient
90+ participant HttpClient
91+ participant TokenBucket
92+ participant httpx
93+ participant RMP API
94+
95+ User->>RMPClient: e.g. get_professor(id) or iter_professors_for_school(school_id)
96+ RMPClient->>RMPClient: Build GraphQL-style payload
97+ RMPClient->>HttpClient: post_json(path, payload)
98+ HttpClient->>TokenBucket: consume()
99+ TokenBucket-->>HttpClient: (blocks until token available)
100+ HttpClient->>httpx: POST base_url, json=payload
101+ httpx->>RMP API: HTTPS request
102+ RMP API-->>httpx: JSON response
103+ httpx-->>HttpClient: response
104+ HttpClient->>HttpClient: Retry on 5xx / HTTP error
105+ HttpClient-->>RMPClient: dict (parsed JSON)
106+ RMPClient->>RMPClient: Parse into Professor / Rating / etc.
107+ RMPClient-->>User: Professor, Rating, or list
108+ ```
109+
110+ ### Data models
111+
112+ ``` mermaid
113+ erDiagram
114+ School ||--o{ Professor : "has"
115+ Professor ||--o{ Rating : "has"
116+
117+ School {
118+ string id
119+ string name
120+ string city
121+ string state
122+ string country
123+ }
124+
125+ Professor {
126+ string id
127+ string name
128+ string department
129+ float overall_rating
130+ int num_ratings
131+ School school
132+ }
133+
134+ Rating {
135+ date date
136+ string comment
137+ float quality
138+ float difficulty
139+ string course_raw
140+ }
141+
142+ ProfessorSearchResult {
143+ Professor[] professors
144+ int page
145+ int page_size
146+ bool has_next_page
147+ }
148+
149+ ProfessorRatingsPage {
150+ Professor professor
151+ Rating[] ratings
152+ bool has_next_page
153+ string next_cursor
154+ }
155+ ```
156+
157+ ### Extras and ingestion pipeline
158+
159+ ``` mermaid
160+ flowchart LR
161+ subgraph RMPClient
162+ iter_professors["iter_professors_for_school"]
163+ iter_ratings["iter_professor_ratings"]
164+ end
165+
166+ subgraph extras [rmp_client.extras]
167+ dedupe["dedupe\n(normalize_comment,\n is_valid_comment)"]
168+ sentiment["sentiment\n(analyze_sentiment)"]
169+ course_codes["course_codes\n(build_course_mapping)"]
170+ end
171+
172+ subgraph Your pipeline [Your pipeline e.g. ingest_supabase]
173+ filter["Filter comments"]
174+ store["Supabase / DB"]
175+ end
176+
177+ iter_professors --> iter_ratings
178+ iter_ratings --> filter
179+ filter --> dedupe
180+ dedupe --> sentiment
181+ iter_ratings --> course_codes
182+ sentiment --> store
183+ course_codes --> store
184+ ```
185+
186+ ### CI/CD (publish to PyPI)
187+
188+ ``` mermaid
189+ flowchart LR
190+ subgraph On any push
191+ T[Run tests\npytest]
192+ end
193+
194+ subgraph On main push
195+ B[Build wheel + sdist]
196+ TestPyPI[Publish to TestPyPI]
197+ end
198+
199+ subgraph On release published
200+ PyPI[Publish to PyPI]
201+ end
202+
203+ T --> B
204+ B --> TestPyPI
205+ B --> PyPI
206+ ```
207+
47208## Extras
48209
49210Optional helpers live under ` rmp_client.extras ` :
0 commit comments