canonical mapping
CANONICAL_SCHEMA.md in ~/Projects/Stats APIs is the wire contract. LeetCode implements it in models/canonical/ with PLATFORM = "leetcode" and CATEGORY = "dsa".
Mapper comment in canonical_mapper.py: legacy routes call the pure *_from converters on the object they already fetched, so they do not GraphQL twice. build_* fetchers are for clients that only want one section.
| Converter | Input | Empty behavior |
|---|---|---|
profile_from |
profile response | Profile(username=...), countryFlag=None, verified=False |
stats_from |
stats + topics | zeros, topicAnalysis may still have bars |
contests_from |
contest ranking | empty Contests(), history attended only, rating 0 becomes None |
rating_from |
same contests | points with a non-null rating |
heatmap_from |
heatmap response | empty Heatmap() |
badges_from |
badges | empty list, level always None |
summary_from |
a full Card |
rollup totals |
byDifficulty on LeetCode is easy / medium / hard. Schema keys school, basic, fundamental stay 0 so GFG can fill them without a second shape.
build_card fetches contests once, then profile, stats plus skillStats, rating from those contests, heatmap, badges. The summary route first calls get_user_stats as an existence check, then build_card, so stats GraphQL runs twice there. Wasteful, leftover, still the path.
Envelope:
{
"status": "success",
"message": "retrieved",
"platform": "leetcode",
"username": "khan-tashif",
"cached": false,
"data": {}
}
cached on the JSON is set by the mapper, usually false. Redis hits are visible as X-Cache: HIT on the HTTP response, not as that field flipping. Do not trust data.cached as a cache probe.