Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# RateMyProfessors API Client (Python)

[![PyPI](https://img.shields.io/pypi/v/ratemyprofessors-client?color=10b981)](https://pypi.org/project/ratemyprofessors-client/) [![downloads](https://img.shields.io/pepy/dt/ratemyprofessors-client)](https://pypi.org/project/ratemyprofessors-client/) [![docs](https://img.shields.io/badge/docs-website-10b981)](https://amaanjaved1.github.io/Rate-My-Professors-API-Client/)

A typed, retrying, rate-limited **unofficial** client for [RateMyProfessors](https://www.ratemyprofessors.com).

> **Disclaimer:** This library is unofficial and may break if RMP changes their internal API. Use responsibly and respect rate limits.
> **Looking for TypeScript?** Check out the [TypeScript version](https://github.com/amaanjaved1/rate-my-professors-client-ts).

## Requirements

Expand Down Expand Up @@ -114,8 +116,8 @@ from rmp_client import (
)
```

- `normalize_comment(text)` — Normalize text for deduplication (lowercase, collapse whitespace)
- `is_valid_comment(text, min_len=10)` — Check if a comment is non-empty and meets a minimum length
- `normalize_comment(text, *, strip_html=True, strip_punctuation=False)` — Normalize text for deduplication (trim, strip HTML, lowercase, collapse whitespace; optionally strip punctuation)
- `is_valid_comment(text, *, min_len=10)` — Validate a comment and return a `ValidationResult` with diagnostics (empty, too short, all caps, excessive repeats, no alpha)
- `clean_course_label(raw)` — Clean scraped course labels (remove counts, normalize whitespace)
- `build_course_mapping(scraped, valid)` — Map scraped labels to known course codes
- `analyze_sentiment(text)` — Compute sentiment label from text (uses TextBlob)
76 changes: 76 additions & 0 deletions docs/configuration.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Configuration &mdash; RMP Client</title>
<link rel="stylesheet" href="style.css" />
<link id="hljs-theme" rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github-dark.min.css" />
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>
<script>
(function(){var t=localStorage.getItem('rmp-docs-theme');var d=t==='dark'||(!t&&window.matchMedia('(prefers-color-scheme:dark)').matches);if(d)document.documentElement.classList.add('dark')})();
</script>
</head>
<body>
<header class="topbar">
<div class="topbar-right">
<a href="https://github.com/amaanjaved1/Rate-My-Professors-API-Client" class="topbar-btn" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
</a>
<button class="topbar-btn" id="theme-toggle" aria-label="Toggle theme"></button>
</div>
</header>

<aside class="sidebar">
<div class="sidebar-brand">RMP API Docs</div>
<nav><ul>
<li><a href="index.html">Home</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="reference.html">API Reference</a></li>
<li><a href="configuration.html" class="active">Configuration</a></li>
<li><a href="extras.html">Extras</a></li>
</ul></nav>
</aside>

<main class="content">
<h1>Configuration</h1>
<p>The client is configured via <code>RMPClientConfig</code>. All fields have sensible defaults.</p>
<pre><code class="language-python">from rmp_client import RMPClientConfig, RMPClient

config = RMPClientConfig(
base_url="https://www.ratemyprofessors.com/graphql",
timeout_seconds=10.0,
max_retries=3,
rate_limit_per_minute=60,
)
with RMPClient(config) as client:
...</code></pre>

<h2 id="available-options">Available Options <a href="#available-options" class="anchor">#</a></h2>
<table>
<tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr>
<tr><td><code>base_url</code></td><td><code>str</code></td><td><code>https://...graphql</code></td><td>GraphQL endpoint URL</td></tr>
<tr><td><code>timeout_seconds</code></td><td><code>float</code></td><td><code>10.0</code></td><td>HTTP request timeout</td></tr>
<tr><td><code>max_retries</code></td><td><code>int</code></td><td><code>3</code></td><td>Number of retry attempts for failed requests</td></tr>
<tr><td><code>rate_limit_per_minute</code></td><td><code>int</code></td><td><code>60</code></td><td>Max requests per minute (token bucket)</td></tr>
<tr><td><code>user_agent</code></td><td><code>str</code></td><td>Firefox UA</td><td>User-Agent header</td></tr>
<tr><td><code>default_headers</code></td><td><code>Mapping[str, str]</code></td><td>UA + Accept-Language</td><td>Default headers for all requests</td></tr>
</table>

<h2 id="rate-limiting">Rate Limiting <a href="#rate-limiting" class="anchor">#</a></h2>
<p>The client uses a token-bucket algorithm. Tokens replenish continuously. Each request consumes one token. If no tokens are available, the request blocks until one becomes available.</p>
<pre><code class="language-python">config = RMPClientConfig(rate_limit_per_minute=30)</code></pre>

<h2 id="retries">Retries <a href="#retries" class="anchor">#</a></h2>
<p>On 5xx errors or network failures, the client retries up to <code>max_retries</code> times. 4xx errors are <strong>not</strong> retried. After exhausting retries, a <code>RetryError</code> is raised.</p>
<pre><code class="language-python">config = RMPClientConfig(max_retries=5)</code></pre>

<h2 id="timeouts">Timeouts <a href="#timeouts" class="anchor">#</a></h2>
<p>The <code>timeout_seconds</code> value applies to each individual HTTP request (connect + read).</p>
<pre><code class="language-python">config = RMPClientConfig(timeout_seconds=30.0)</code></pre>
</main>

<aside class="toc"><div class="toc-title">On this page</div><ul id="toc-list"></ul></aside>
<script src="script.js"></script>
</body>
</html>
52 changes: 0 additions & 52 deletions docs/configuration.md

This file was deleted.

101 changes: 101 additions & 0 deletions docs/extras.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Extras &mdash; RMP Client</title>
<link rel="stylesheet" href="style.css" />
<link id="hljs-theme" rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github-dark.min.css" />
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>
<script>
(function(){var t=localStorage.getItem('rmp-docs-theme');var d=t==='dark'||(!t&&window.matchMedia('(prefers-color-scheme:dark)').matches);if(d)document.documentElement.classList.add('dark')})();
</script>
</head>
<body>
<header class="topbar">
<div class="topbar-right">
<a href="https://github.com/amaanjaved1/Rate-My-Professors-API-Client" class="topbar-btn" target="_blank" rel="noopener" aria-label="GitHub">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/></svg>
</a>
<button class="topbar-btn" id="theme-toggle" aria-label="Toggle theme"></button>
</div>
</header>

<aside class="sidebar">
<div class="sidebar-brand">RMP API Docs</div>
<nav><ul>
<li><a href="index.html">Home</a></li>
<li><a href="usage.html">Usage</a></li>
<li><a href="reference.html">API Reference</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="extras.html" class="active">Extras</a></li>
</ul></nav>
</aside>

<main class="content">
<h1>Extras</h1>
<p>Helpers for ingestion pipelines. Import them from <code>rmp_client</code>:</p>
<pre><code class="language-python">from rmp_client import (
analyze_sentiment, normalize_comment,
is_valid_comment, build_course_mapping,
clean_course_label,
)</code></pre>

<h2 id="sentiment">Sentiment <a href="#sentiment" class="anchor">#</a></h2>
<p>Compute a sentiment score and label from comment text (uses TextBlob internally).</p>
<pre><code class="language-python">result = analyze_sentiment("Great prof, explains concepts clearly.")
print(result.score, result.label) # e.g. 0.65 "positive"</code></pre>

<h2 id="helpers">Helpers <a href="#helpers" class="anchor">#</a></h2>

<h3 id="normalize_comment">normalize_comment</h3>
<div class="method-sig">normalize_comment(text: str, *, strip_html: bool = True, strip_punctuation: bool = False) -&gt; str</div>
<p>Normalizes a comment for comparison or deduplication. Trims whitespace, strips HTML tags (opt-out), lowercases, and collapses runs of whitespace. Optionally strips punctuation for looser matching.</p>
<table>
<tr><th>Parameter</th><th>Type</th><th>Default</th><th>Description</th></tr>
<tr><td><code>text</code></td><td><code>str</code></td><td>&mdash;</td><td>Comment text</td></tr>
<tr><td><code>strip_html</code></td><td><code>bool</code></td><td><code>True</code></td><td>Remove HTML tags</td></tr>
<tr><td><code>strip_punctuation</code></td><td><code>bool</code></td><td><code>False</code></td><td>Remove all punctuation</td></tr>
</table>
<pre><code class="language-python">a = normalize_comment(" Great Professor! ")
b = normalize_comment("great professor!")
assert a == b # True

normalize_comment("&lt;b&gt;Loved&lt;/b&gt; this class") # "loved this class"
normalize_comment("Hello, world!", strip_punctuation=True) # "hello world"</code></pre>

<h3 id="is_valid_comment">is_valid_comment</h3>
<div class="method-sig">is_valid_comment(text: str, *, min_len: int = 10) -&gt; ValidationResult</div>
<p>Validates a comment and returns detailed diagnostics. Checks for empty text, insufficient length, all-caps, excessive repeated characters, and absence of alphabetic characters.</p>
<table>
<tr><th>Parameter</th><th>Type</th><th>Default</th><th>Description</th></tr>
<tr><td><code>text</code></td><td><code>str</code></td><td>&mdash;</td><td>Comment text</td></tr>
<tr><td><code>min_len</code></td><td><code>int</code></td><td><code>10</code></td><td>Minimum character length</td></tr>
</table>
<p><strong>Returns:</strong> <code>ValidationResult</code> with <code>valid</code> (bool) and <code>issues</code> (list of <code>CommentIssue</code>).</p>
<p>Each issue has a <code>code</code> (<code>"empty"</code>, <code>"too_short"</code>, <code>"all_caps"</code>, <code>"excessive_repeats"</code>, <code>"no_alpha"</code>) and a human-readable <code>message</code>.</p>
<pre><code class="language-python">result = is_valid_comment("Good")
# ValidationResult(valid=False, issues=[CommentIssue(code="too_short", ...)])

result = is_valid_comment("Great class, learned a lot")
# ValidationResult(valid=True, issues=[])

result = is_valid_comment("WORST PROF EVER!!!")
# ValidationResult(valid=False, issues=[CommentIssue(code="all_caps", ...)])</code></pre>

<h2 id="course-code-helpers">Course Code Helpers <a href="#course-code-helpers" class="anchor">#</a></h2>
<p>Map scraped RMP course labels to your course catalog.</p>
<pre><code class="language-python">scraped = ["ANAT 215 (12)", "phys115"]
valid = ["ANAT 215", "PHYS 115"]

mapping = build_course_mapping(scraped, valid)
# {"ANAT 215 (12)": "ANAT 215", "phys115": "PHYS 115"}

cleaned = clean_course_label("MATH 101 (5)")
# "MATH 101"</code></pre>
</main>

<aside class="toc"><div class="toc-title">On this page</div><ul id="toc-list"></ul></aside>
<script src="script.js"></script>
</body>
</html>
48 changes: 0 additions & 48 deletions docs/extras.md

This file was deleted.

Loading
Loading