{en}Statistics Canada · Notes from the code{fr}Statistique Canada · Notes tirées du code{/}
{en}How the MCP queries Statistics Canada{fr}Comment le MCP interroge Statistique Canada{/}
{en}Statistics Canada does not have one API; it has several. This is how MapleStats MCP finds a table, turns a coordinate into a vector, slices a big table with SDMX and weights a microdata file, and what the live services taught me along the way.{fr}Statistique Canada n'a pas une API, mais plusieurs. Voici comment MapleStats MCP trouve un tableau, passe d'une coordonnée à un vecteur, découpe un grand tableau avec SDMX et pondère un fichier de microdonnées, et ce que les services en direct m'ont appris en chemin.{/}
{en}Many services, one way in{fr}Plusieurs services, une seule entrée{/}
{en}Start with the list. The Web Data Service (WDS) serves tables and time series as JSON. An SDMX REST API serves the same tables as SDMX-ML. Reference Data as a Service (RDaaS) holds classifications such as NAICS. The 2021 Census Profile has its own SDMX API on another host, the 2016 profile has a separate JSON API, and the profiles from 2001 to 2016 exist as bulk downloads. Public use microdata files (PUMFs) are ZIP archives. Then come The Daily's Atom feeds, the Data catalogue, the survey directory, a census geography service and the indicator feeds behind StatCan's home page.{fr}Commençons par la liste. Le Service de données Web (WDS) sert les tableaux et les séries chronologiques en JSON. Une API REST SDMX sert les mêmes tableaux en SDMX-ML. Le service de données de référence (RDaaS) contient les classifications comme le SCIAN. Le Profil du recensement de 2021 a sa propre API SDMX sur un autre hôte, celui de 2016 une API JSON distincte, et les profils de 2001 à 2016 n'existent qu'en téléchargement en bloc. Les fichiers de microdonnées à grande diffusion (FMGD) sont des archives ZIP. Viennent ensuite les flux Atom du Quotidien, le catalogue Données, le répertoire des enquêtes, un service de géographie du recensement et les flux d'indicateurs de la page d'accueil de Statistique Canada.{/}
{en}Each has its own identifiers, its own error habits and its own idea of what a request body looks like. An agent should not have to learn all of that before it can answer a question about prices. MapleStats wraps these services in {{statcan_tool_count}} tools, and the agent does not see those as a list either. The server shows a client three tools, plan_query, search_tools and call_tool. Every StatCan tool is found by a plain-language query to search_tools and run through call_tool, so the first request of a StatCan question is not a StatCan request at all:{fr}Chacun a ses identifiants, ses façons d'échouer et sa propre idée de ce qu'est un corps de requête. Un agent ne devrait pas avoir à apprendre tout cela avant de répondre à une question sur les prix. MapleStats regroupe ces services en {{statcan_tool_count}} outils, et l'agent ne les voit pas non plus sous forme de liste. Le serveur présente trois outils au client : plan_query, search_tools et call_tool. Chaque outil de Statistique Canada se trouve par une requête en langage courant à search_tools et s'exécute par call_tool. La première requête d'une question sur Statistique Canada n'est donc pas une requête à Statistique Canada :{/}
{en}The ranking comes from the server's own BM25 index over the tools' names and docstrings, the same index the search on this site uses. wds_search_cubes is among the results, and that is where a table hunt starts.{fr}Le classement vient de l'index BM25 du serveur, construit sur le nom et la docstring de chaque outil; c'est le même index que la recherche de ce site. wds_search_cubes figure parmi les résultats, et c'est par lui que commence la chasse au tableau.{/}
{en}From a question to a table{fr}D'une question à un tableau{/}
{en}Every StatCan table has a product id, the PID. It is the table number without its dashes: table 18-10-0004-01 is PID 18100004, because the last two digits, a view of the table, are optional. The docs://statcan/addressing resource the server ships gives the anatomy of the full ten digits: two for the subject, two for the product type, four for the sequence and two for the view.{fr}Chaque tableau de Statistique Canada a un identifiant de produit, le PID. C'est le numéro du tableau sans ses traits d'union : le tableau 18-10-0004-01 a le PID 18100004, puisque les deux derniers chiffres, qui désignent une vue du tableau, sont facultatifs. La ressource docs://statcan/addressing fournie par le serveur détaille les dix chiffres : deux pour le sujet, deux pour le type de produit, quatre pour le numéro séquentiel et deux pour la vue.{/}
{en}wds_search_cubes turns words into a PID, and it is less clever than it sounds. It downloads getAllCubesListLite, the list of every table in WDS, keeps it cached for an hour, and looks for the query as a substring of each English and French title. Here is the search for the table behind the CPI chart on the case studies page:{fr}wds_search_cubes transforme des mots en PID, et il est moins savant qu'il n'y paraît. Il télécharge getAllCubesListLite, la liste de tous les tableaux du WDS, la garde en cache une heure et cherche la requête comme sous-chaîne de chaque titre, français et anglais. Voici la recherche du tableau derrière le graphique de l'IPC dans les études de cas :{/}
{en}A substring match is blunt. "consumer price index" finds the CPI tables because their titles say so; a question worded differently from StatCan's titles can miss them, and then the agent has to try again in StatCan's words. The provenance says how much was searched, and each result carries cansim_id, the table's number in the old CANSIM system, next to release_time, the table's latest release.{fr}Une correspondance de sous-chaîne est un outil grossier. « consumer price index » trouve les tableaux de l'IPC parce que leur titre le dit; une question formulée autrement que les titres de Statistique Canada peut les manquer, et l'agent doit alors réessayer avec les mots de Statistique Canada. La provenance indique l'étendue de la recherche, et chaque résultat porte cansim_id, le numéro du tableau dans l'ancien système CANSIM, à côté de release_time, la dernière diffusion du tableau.{/}
{en}Coordinates and vectors{fr}Coordonnées et vecteurs{/}
{en}A StatCan table is a cube. Each dimension has a tree of members, and one member from each dimension picks out one series. wds_get_cube_metadata returns the dimensions. The CPI table has two: Geography, with {{sc_members_geo}} members, and Products and product groups, with {{sc_members_prod}}.{fr}Un tableau de Statistique Canada est un cube. Chaque dimension a un arbre de membres, et un membre de chaque dimension désigne une série. wds_get_cube_metadata renvoie les dimensions. Le tableau de l'IPC en a deux : Géographie, avec {{sc_members_geo}} membres, et Produits et groupes de produits, avec {{sc_members_prod}}.{/}
{en}Member ids repeat across dimensions: in both, member 2 is the root of the tree, the one with no parent (Canada, and All-items). And not every combination exists. {{sc_members_geo}} geographies times {{sc_members_prod}} products would make {{sc_combos}} series; the table has {{sc_n_series}}. A coordinate is not a free choice, then: it has to name a series StatCan publishes.{fr}Les identifiants de membres se répètent d'une dimension à l'autre : dans les deux, le membre 2 est la racine de l'arbre, celui qui n'a pas de parent (Canada, et Ensemble). Et toutes les combinaisons n'existent pas. {{sc_members_geo}} géographies fois {{sc_members_prod}} produits donneraient {{sc_combos}} séries; le tableau en compte {{sc_n_series}}. Une coordonnée n'est donc pas un choix libre : elle doit désigner une série que Statistique Canada publie.{/}
{en}A coordinate writes one member id per dimension, in dimension order, separated by dots. WDS always wants exactly ten positions, with zeros for the dimensions a table does not have. The client pads it, so asking for {{sc_coord_in}} sends {{sc_coord_out}}. What comes back is the vector: a stable id for one series, the "V" number carried over from CANSIM.{fr}Une coordonnée aligne un identifiant de membre par dimension, dans l'ordre des dimensions, séparés par des points. Le WDS exige toujours exactement dix positions, avec des zéros pour les dimensions que le tableau n'a pas. Le client complète la coordonnée : demander {{sc_coord_in}} envoie {{sc_coord_out}}. En retour vient le vecteur, un identifiant stable pour une série, le numéro « V » hérité de CANSIM.{/}
{en}With the vector, the data is one more call. This one is the request behind the CPI chart on the case studies page, the latest {{sc_latest_n}} months of v{{sc_vector}}:{fr}Avec le vecteur, les données ne sont plus qu'à un appel. Celui-ci est la requête derrière le graphique de l'IPC des études de cas : les {{sc_latest_n}} derniers mois de v{{sc_vector}}.{/}
{{sc_data_box}}{en}A value never travels alone. Each observation carries the codes that say how to read it:{fr}Une valeur ne voyage jamais seule. Chaque observation porte les codes qui disent comment la lire :{/}
scalar_factor_code- {en}The power of ten the value is expressed in: 0 for units, 3 for thousands, 6 for millions. For the CPI it is 0.{fr}La puissance de dix dans laquelle la valeur est exprimée : 0 pour les unités, 3 pour les milliers, 6 pour les millions. Pour l'IPC, c'est 0.{/}
decimals- {en}The number of decimals StatCan publishes. The value already carries StatCan's rounding.{fr}Le nombre de décimales que Statistique Canada publie. La valeur est déjà arrondie par Statistique Canada.{/}
symbol_code,status_code- {en}The symbols and status flags StatCan attaches to a value, as codes.
wds_get_code_setsdecodes them, along with scalar factors, frequencies and units of measure.{fr}Les symboles et les indicateurs d'état que Statistique Canada associe à une valeur, sous forme de codes.wds_get_code_setsles décode, tout comme les facteurs scalaires, les fréquences et les unités de mesure.{/} release_time- {en}The release timestamp WDS gives the data point. It is not always the first publication: {{sc_last_month}} carries {{sc_last_release}}, but {{sc_first_month}}, the oldest month in this recording, carries {{sc_first_release}}.{fr}L'horodatage de diffusion que le WDS donne au point de données. Ce n'est pas toujours la première publication : {{sc_last_month}} porte le {{sc_last_release}}, mais {{sc_first_month}}, le mois le plus ancien de cet enregistrement, porte le {{sc_first_release}}.{/}
{en}Then the rule I care most about: the server never applies the scalar factor. value is exactly what WDS sent. The code says so twice, in the schema's docstring ("deliberately NOT applied ... WDS never auto-applies it either") and first on the list in docs://statcan/gotchas. A value in thousands stays in thousands, with its code beside it. Scaling is one line, apply_scalar_factor(value, code) multiplies by ten to the power of the code, but that line belongs to whoever uses the number, where it can be seen. What the tool returns matches what WDS returns, digit for digit.{fr}Vient ensuite la règle qui me tient le plus à cœur : le serveur n'applique jamais le facteur scalaire. value est exactement ce que le WDS a envoyé. Le code le dit deux fois, dans la docstring du schéma (« deliberately NOT applied ... WDS never auto-applies it either ») et en tête de liste dans docs://statcan/gotchas. Une valeur en milliers reste en milliers, avec son code à côté. La mise à l'échelle tient en une ligne, apply_scalar_factor(value, code) multiplie par dix à la puissance du code, mais cette ligne revient à qui utilise le chiffre, là où elle reste visible. Ce que l'outil renvoie correspond à ce que le WDS renvoie, chiffre pour chiffre.{/}
{en}When WDS is not enough: SDMX{fr}Quand le WDS ne suffit pas : SDMX{/}
{en}WDS thinks in series: give it vectors or coordinates and it returns their observations. When a question covers a whole slice of a big table, every detailed geography or every occupation, going series by series means finding every coordinate first, and downloading the full table means taking far more than the question needs. StatCan's SDMX API slices on the server instead. A key names the members you want, dimension by dimension, and a blank position is a wildcard.{fr}Le WDS raisonne en séries : donnez-lui des vecteurs ou des coordonnées, il renvoie leurs observations. Quand une question porte sur toute une tranche d'un grand tableau, chaque géographie détaillée ou chaque profession, procéder série par série oblige à trouver d'abord chaque coordonnée, et télécharger le tableau complet revient à prendre bien plus que ce que la question demande. L'API SDMX de Statistique Canada découpe plutôt côté serveur. Une clé nomme les membres voulus, dimension par dimension, et une position vide sert de joker.{/}
{en}The key is the coordinate without its padding: one position per non-time dimension. sdmx_get_vector_data builds it from a vector by itself. It looks up the vector's coordinate through WDS, reads the table's SDMX structure to count its dimensions, and cuts the coordinate to that length, so v{{sc_vector}} becomes the key {{sc_sdmx_key}}:{fr}La clé est la coordonnée sans ses zéros de remplissage : une position par dimension autre que le temps. sdmx_get_vector_data la construit seul à partir d'un vecteur. Il cherche la coordonnée du vecteur par le WDS, lit la structure SDMX du tableau pour compter ses dimensions et coupe la coordonnée à cette longueur : v{{sc_vector}} devient la clé {{sc_sdmx_key}}.{/}
{en}Three things about this API are written into the client.{fr}Trois particularités de cette API sont inscrites dans le client.{/}
{en}It answers in XML{fr}Elle répond en XML{/}
{en}Ask for JSON with format=jsondata or an Accept header, and StatCan's SDMX endpoint still returns SDMX-ML, for data and structure alike. The constants file records this as confirmed live, against benchmark documentation that assumed SDMX-JSON, so the client parses the XML itself, with defusedxml rather than the standard library parser.{fr}Demandez du JSON avec format=jsondata ou un en-tête Accept, et le point d'accès SDMX de Statistique Canada renvoie tout de même du SDMX-ML, pour les données comme pour la structure. Le fichier de constantes note que c'est confirmé en direct, à l'encontre d'une documentation de référence qui supposait du SDMX-JSON. Le client lit donc le XML lui-même, avec defusedxml plutôt que l'analyseur de la bibliothèque standard.{/}
{en}Wildcards sample big dimensions{fr}Les jokers échantillonnent les grandes dimensions{/}
{en}Leave a dimension with more than about 30 codes blank and the answer is a sparse, unpredictable sample of its codes, not all of them. sdmx_get_key_for_dimension builds the complete version instead. It reads the dimension's codelist from the structure, keeps the leaf codes (those that are no other code's parent) and joins them with + into an explicit OR key, to splice into the key at that position.{fr}Laissez vide une dimension de plus d'une trentaine de codes, et la réponse est un échantillon clairsemé et imprévisible de ses codes, pas leur totalité. sdmx_get_key_for_dimension construit plutôt la version complète. Il lit la liste de codes de la dimension dans la structure, garde les codes terminaux (ceux qui ne sont parents d'aucun autre) et les joint par des + en une clé OR explicite, à insérer dans la clé à cette position.{/}
{en}One combination is refused{fr}Une combinaison est refusée{/}
{en}lastNObservations together with startPeriod or endPeriod gets HTTP 406. The client refuses that combination before sending anything, and if a 406 comes back anyway, the error names the usual cause instead of passing the bare status through.{fr}lastNObservations combiné à startPeriod ou à endPeriod reçoit HTTP 406. Le client refuse cette combinaison avant d'envoyer quoi que ce soit, et si un 406 revient malgré tout, l'erreur nomme la cause habituelle au lieu de transmettre le statut nu.{/}
{en}Microdata, weights and replicates{fr}Microdonnées, poids et répliques{/}
{en}Tables are aggregates. A public use microdata file is the records themselves, one row per respondent, with weights that make the sample stand for the population. StatCan ships PUMFs as ZIPs, and the codebooks inside come in several formats: CSV codebooks, Stata .dct and .do files, SPSS label files and SAS files.{fr}Les tableaux sont des agrégats. Un fichier de microdonnées à grande diffusion, ce sont les enregistrements eux-mêmes, une ligne par répondant, avec des poids qui font représenter la population par l'échantillon. Statistique Canada distribue les FMGD dans des ZIP, et les dictionnaires qu'ils contiennent prennent plusieurs formes : dictionnaires CSV, fichiers Stata .dct et .do, fichiers d'étiquettes SPSS et fichiers SAS.{/}
{en}The table APIs cannot see PUMFs at all. The one place to discover them is StatCan's Data catalogue, which statcan_reference_search_data searches. Once a file is found, statcan_pumf_get_codebook reads its codebook from inside the ZIP with HTTP range requests, so the variables and weights are known before the data file is downloaded. statcan_pumf_tabulate then does the arithmetic on the server, with DuckDB. Its first call downloads the file into a local cache, and later calls reuse it.{fr}Les API de tableaux ne voient pas du tout les FMGD. Le seul endroit où les découvrir est le catalogue Données de Statistique Canada, que statcan_reference_search_data interroge. Une fois le fichier trouvé, statcan_pumf_get_codebook lit son dictionnaire à l'intérieur du ZIP par des requêtes HTTP partielles : on connaît les variables et les poids avant de télécharger le fichier de données. statcan_pumf_tabulate fait ensuite les calculs sur le serveur, avec DuckDB. Son premier appel télécharge le fichier dans un cache local, et les appels suivants le réutilisent.{/}
{en}This is the call behind the bachelor's degree chart on the case studies page: the 2021 Census individuals file, adults aged 25 to 64 (age groups 9 to 16), and the share holding each highest credential in each province, with the codes for "not available" and "not applicable" filtered out.{fr}Voici l'appel derrière le graphique du baccalauréat des études de cas : le fichier des particuliers du recensement de 2021, les adultes de 25 à 64 ans (groupes d'âge 9 à 16), et la part de chaque plus haut diplôme dans chaque province, sans les codes « non disponible » et « sans objet ».{/}
{{sc_pumf_box}} {{sc_pumf_table}}{en}Two things decide whether a table like that is right. The first is the weight. The tool defaults to the codebook's main weight, {{sc_census_weight}} here, and it checks labels and field widths as well as names, because names mislead: in the CSWC, WTQ_05 is a question about working at night, and in the CCHS, DOHWT is a height-and-weight inclusion flag. Real weights are wide numeric fields, so a known width under four columns rules a variable out. The second is the sample under each cell. Every cell carries its unweighted count, and by default cells with fewer than 30 respondents are flagged low_count.{fr}Deux choses décident de la justesse d'un tel tableau. La première est le poids. Par défaut, l'outil prend le poids principal du dictionnaire, ici {{sc_census_weight}}, et il vérifie les étiquettes et la largeur des champs en plus des noms, car les noms trompent : dans la CSWC, WTQ_05 est une question sur le travail de nuit, et dans l'ESCC, DOHWT est un indicateur d'inclusion pour la taille et le poids. Les vrais poids sont des champs numériques larges : une largeur connue de moins de quatre colonnes écarte donc une variable. La seconde est l'échantillon sous chaque cellule. Chaque cellule indique son effectif non pondéré, et par défaut celles de moins de 30 répondants sont marquées low_count.{/}
{en}Then the standard errors. The Census file carries {{sc_replicates}} replicate weights, {{sc_replicate_first}} to {{sc_replicate_last}}. The tool computes the estimate once with the main weight and once with each replicate, sums the squared deviations of the {{sc_replicates}} replicate estimates from their mean, divides by {{sc_census_divisor}} and takes the square root. The {{sc_census_divisor}} is not a typo. It is the user guide's recipe, a Fay adjustment over {{sc_replicates}} groups, and the comment above it in tabulate.py shows the arithmetic: (240/35) × (1/240). StatCan notes that this method overestimates the error for small estimates.{fr}Viennent ensuite les erreurs-types. Le fichier du recensement contient {{sc_replicates}} poids de réplication, de {{sc_replicate_first}} à {{sc_replicate_last}}. L'outil calcule l'estimation une fois avec le poids principal et une fois avec chaque réplique, additionne les écarts au carré des {{sc_replicates}} estimations répliquées à leur moyenne, divise par {{sc_census_divisor}} et prend la racine carrée. Ce {{sc_census_divisor}} n'est pas une coquille. C'est la recette du guide de l'utilisateur, un ajustement de Fay sur {{sc_replicates}} groupes, et le commentaire qui le précède dans tabulate.py en montre le calcul : (240/35) × (1/240). Statistique Canada signale que cette méthode surestime l'erreur des petites estimations.{/}
{en}Each method is copied from its survey's user guide, with the section cited, and a file gets one only after its guide has been read, "never by analogy with another survey", in the words of the comment above the list. {{statcan_variance_count}} files have one so far:{fr}Chaque méthode est reprise du guide de l'utilisateur de l'enquête, avec la section citée, et un fichier n'en reçoit une qu'après lecture de son guide, « never by analogy with another survey », selon le commentaire qui précède la liste. {{statcan_variance_count}} fichiers en ont une pour l'instant :{/}
{{statcan_variance_table}}{en}For any other PUMF the result gives no standard error, says why, and names the replicate weights or the bootstrap file it found. The bootstrap weights in PUMFs are perturbed for confidentiality, so their standard errors are comparable to StatCan's official ones, not the same. Two case studies use the 2021 Census file this way: bachelor's degrees by province and low income across immigrant generations.{fr}Pour tout autre FMGD, le résultat ne donne pas d'erreur-type, explique pourquoi et nomme les poids de réplication ou le fichier bootstrap trouvés. Les poids bootstrap des FMGD sont perturbés pour protéger la confidentialité : leurs erreurs-types sont comparables à celles de Statistique Canada, sans être identiques. Deux études de cas utilisent ainsi le fichier du recensement de 2021 : le baccalauréat par province et le faible revenu d'une génération d'immigration à l'autre.{/}
{en}Things that bit us{fr}Ce qui nous a joué des tours{/}
{en}Most of these were found by calling the live services, not by reading documentation. The code records how each one was confirmed, next to the fix, so that a later edit does not quietly undo it.{fr}La plupart de ces pièges ont été trouvés en appelant les services en direct, pas en lisant la documentation. Le code consigne comment chacun a été confirmé, à côté du correctif, pour qu'une modification ultérieure ne le défasse pas en silence.{/}
shared/http.py
{en}The connection that hung{fr}La connexion qui restait pendue{/}
{en}Plain httpx connections to statcan.gc.ca timed out, silently. The cause is not in this code: something on StatCan's network path, a WAF or CDN, blocks TLS handshakes whose ALPN extension offers only http/1.1, which is exactly what httpx offers by default. The diagnosis reproduced the hang with a raw ssl socket narrowed to that one value and watched it go away once h2 was back on the list. The fix is one argument, http2=True. That argument now has a comment asking the next person not to delete it, h2 is a pinned dependency because of it, and the Python scripts reproduce_code writes set it too.{fr}De simples connexions httpx vers statcan.gc.ca expiraient, sans bruit. La cause n'est pas dans ce code : un élément du réseau de Statistique Canada, un pare-feu applicatif ou un CDN, bloque les négociations TLS dont l'extension ALPN n'offre que http/1.1, soit exactement ce qu'httpx offre par défaut. Le diagnostic a reproduit le blocage avec un socket ssl brut limité à cette seule valeur, puis l'a vu disparaître dès que h2 est revenu dans la liste. Le correctif tient en un argument, http2=True. Cet argument porte maintenant un commentaire qui demande à la personne suivante de ne pas le supprimer, h2 est une dépendance épinglée pour cette raison, et les scripts Python qu'écrit reproduce_code le règlent aussi.{/}
statcan/wds/client.py
{en}The overnight lock{fr}Le verrou de nuit{/}
{en}From midnight to 8:30 a.m. Eastern, while StatCan updates its data, some WDS methods answer HTTP 409. That is a schedule, not a fault, and no retry beats a schedule, so the shared retry layer leaves 409 out of the statuses it retries (429, 500, 502, 503 and 504). The WDS and SDMX clients turn it into a DataLocked error that says to try again after 8:30.{fr}De minuit à 8 h 30, heure de l'Est, pendant que Statistique Canada met ses données à jour, certaines méthodes du WDS répondent HTTP 409. C'est un horaire, pas une panne, et aucune nouvelle tentative ne vient à bout d'un horaire : la couche commune de nouvelles tentatives exclut donc 409 des statuts qu'elle réessaie (429, 500, 502, 503 et 504). Les clients WDS et SDMX en font une erreur DataLocked qui dit de réessayer après 8 h 30.{/}
statcan/wds/client.py
{en}One status, three meanings{fr}Un statut, trois sens{/}
{en}WDS answers a well-formed request for something that does not exist, such as an unknown productId, with HTTP 406, not 404. It answers 406 when a date is too short, too: reference-period ranges need full YYYY-MM-DD dates, and release ranges need YYYY-MM-DDTHH:MM. And getBulkVectorDataByRange wants its body as one flat object where every other WDS POST method takes a list; send it a list and that is a 406 as well. The client turns a WDS 406 into an InvalidInput error that asks the agent to check its identifiers.{fr}Le WDS répond à une requête bien formée qui vise un objet inexistant, un productId inconnu par exemple, par HTTP 406 et non 404. Il répond aussi 406 quand une date est trop courte : les intervalles de périodes de référence exigent des dates complètes AAAA-MM-JJ, et ceux de diffusion AAAA-MM-JJTHH:MM. Enfin, getBulkVectorDataByRange veut un corps fait d'un seul objet plat, là où toutes les autres méthodes POST du WDS prennent une liste; envoyez-lui une liste, et c'est encore un 406. Le client fait d'un 406 du WDS une erreur InvalidInput qui invite l'agent à vérifier ses identifiants.{/}
shared/json_utils.py
{en}Null is not an empty list{fr}Null n'est pas une liste vide{/}
{en}dict.get(key, []) uses its default only when the key is missing. Some WDS cubes send surveyCode and subjectCode as an explicit null instead, and a null where the schema expects a list fails validation. list_or_empty(obj, key) is obj.get(key) or [], which covers both cases, and it is now the rule for every list-typed field pulled from an external API. The numeric codes on observations get the same treatment for the same reason: int(None) raises, so decimals and the other codes are read with or 0.{fr}dict.get(key, []) n'utilise sa valeur par défaut que si la clé est absente. Certains cubes du WDS envoient plutôt surveyCode et subjectCode avec un null explicite, et un null là où le schéma attend une liste échoue à la validation. list_or_empty(obj, key) vaut obj.get(key) or [], ce qui couvre les deux cas, et c'est désormais la règle pour tout champ de type liste tiré d'une API externe. Les codes numériques des observations sont traités de la même façon, pour la même raison : int(None) lève une exception, alors decimals et les autres codes sont lus avec or 0.{/}
statcan/reference/client.py
{en}The search that ignored its keyword{fr}La recherche qui ignorait son mot-clé{/}
{en}StatCan's Reference, Analysis and Data catalogues share one Drupal search. Sent a keyword cold, it returns every document in the catalogue, unfiltered, unless the session has first visited the base page and carries the cookie that visit sets. The proof was a raw curl with a cookie jar: the same URL and query string, a different result depending only on whether the base page came first. So the client warms up each catalogue and language once. Sessions expire, though, and an expired one fails the same quiet way, so every response is checked. If the page's own search box comes back empty despite a keyword, the client warms up again and retries once, and it raises rather than return the whole catalogue as a match.{fr}Les catalogues Référence, Analyse et Données de Statistique Canada partagent un même moteur de recherche Drupal. Interrogé à froid avec un mot-clé, il renvoie tous les documents du catalogue, sans filtre, à moins que la session n'ait d'abord visité la page de base et ne porte le témoin que cette visite dépose. La preuve : un curl brut avec un fichier de témoins, la même URL et la même chaîne de requête, et un résultat différent selon la seule question de savoir si la page de base est venue d'abord. Le client prépare donc la session une fois par catalogue et par langue. Mais les sessions expirent, et une session expirée échoue de la même manière silencieuse; chaque réponse est donc vérifiée. Si la boîte de recherche de la page revient vide malgré un mot-clé, le client prépare de nouveau la session et réessaie une fois, puis lève une erreur plutôt que de présenter tout le catalogue comme résultat.{/}
AGENTS.md
{en}Two of thirty-two{fr}Deux sur trente-deux{/}
{en}The first version of the StatCan module called 2 of its 32 tools against the real API before it was called done. The others passed tests written against hand-made fixtures. A later pass called all 32 live and found 9 more bugs: wrong field names for 4 of the 10 getCodeSets categories, footnotes assumed to be strings that are really objects, two WDS methods that need a differently shaped request body, two with the opposite date requirements from the ones assumed, an RDaaS endpoint returning a list where a dict was expected, and 404 and 406 responses escaping as raw HTTP errors instead of typed ones. The fixtures could not catch any of it, because they were written from the same wrong assumptions as the code. The rule since then: before a client is done, a throwaway script calls every function it exports against the real API, and the test suite fails for any module without a live smoke test.{fr}La première version du module Statistique Canada n'appelait que 2 de ses 32 outils sur l'API réelle quand on l'a jugée terminée. Les autres passaient des tests écrits d'après des données fictives. Un passage ultérieur a appelé les 32 en direct et trouvé 9 autres bogues : des noms de champs erronés pour 4 des 10 catégories de getCodeSets, des notes supposées être des chaînes qui sont en fait des objets, deux méthodes du WDS qui exigent un corps de requête d'une autre forme, deux autres aux exigences de dates inverses de celles supposées, un point d'accès du RDaaS qui renvoie une liste là où l'on attendait un dictionnaire, et des réponses 404 et 406 qui s'échappaient en erreurs HTTP brutes au lieu d'erreurs typées. Les données fictives ne pouvaient rien en détecter, puisqu'elles reposaient sur les mêmes hypothèses erronées que le code. La règle depuis : avant qu'un client soit terminé, un script jetable appelle chacune de ses fonctions sur l'API réelle, et la suite de tests échoue pour tout module sans test de fumée en direct.{/}
{en}From a call to a script{fr}D'un appel à un script{/}
{en}An agent's answer is only as good as the check someone can run without the agent. reproduce_code takes a tool name and its arguments and writes an R, Python, Stata or Julia script that fetches the same data straight from StatCan. What the script does depends on the call:{fr}La réponse d'un agent ne vaut que la vérification qu'on peut faire sans lui. reproduce_code prend le nom d'un outil et ses arguments, et écrit un script R, Python, Stata ou Julia qui récupère les mêmes données directement chez Statistique Canada. Ce que fait le script dépend de l'appel :{/}
| {en}Call{fr}Appel{/} | {en}Script{fr}Script{/} |
|---|---|
{en}A wds_ or sdmx_ call with a product_id{fr}Un appel wds_ ou sdmx_ avec un product_id{/} | {en}Downloads the full table as CSV, to filter to the rows the tool returned; in R, cansim's get_cansim().{fr}Télécharge le tableau complet en CSV, à filtrer selon les lignes renvoyées par l'outil; en R, get_cansim() de cansim.{/} |
wds_get_data_from_vectors | {en}Sends the same WDS request for those vectors; in R, cansim's get_cansim_vector().{fr}Envoie la même requête WDS pour ces vecteurs; en R, get_cansim_vector() de cansim.{/} |
sdmx_get_vector_data | {en}Fetches the same vector through WDS, which serves it as JSON.{fr}Récupère le même vecteur par le WDS, qui le sert en JSON.{/} |
{en}A statcan_pumf_ call with a ZIP url{fr}Un appel statcan_pumf_ avec l'url d'un ZIP{/} | {en}Downloads the same ZIP, with notes on the weight variable and on reading fixed-width files with the Stata, SPSS or SAS files inside it. The weighted table itself is computed by the server.{fr}Télécharge le même ZIP, avec des notes sur la variable de poids et sur la lecture des fichiers à largeur fixe à l'aide des fichiers Stata, SPSS ou SAS qu'il contient. Le tableau pondéré lui-même est calculé par le serveur.{/} |
statcan_census_tables_get_downloads | {en}2016: the full-table CSV, and in R the Beyond 20/20 file read with canivt. 2006 and 2011: R only, with canivt.{fr}2016 : le CSV du tableau complet et, en R, le fichier Beyond 20/20 lu avec canivt. 2006 et 2011 : R seulement, avec canivt.{/} |
statcan_indicators_get_indicators | {en}Downloads the same feed and repeats the tool's filters.{fr}Télécharge le même flux et reprend les filtres de l'outil.{/} |
| {en}The Daily, and the documents and analysis catalogues{fr}Le Quotidien, et les catalogues de documents et d'analyses{/} | {en}No script: these return documents, not data.{fr}Aucun script : ils renvoient des documents, pas des données.{/} |
| {en}Any other StatCan tool{fr}Tout autre outil de Statistique Canada{/} | {en}The tool runs once while its upstream requests are recorded, and the script repeats the data request exactly.{fr}L'outil s'exécute une fois pendant que ses requêtes sont enregistrées, et le script reprend exactement la requête de données.{/} |
{en}For the CPI call above, the R script fetches the data with one cansim call, and the Python script repeats the WDS request, http2=True included. Neither breaks the scalar rule: both keep value as WDS sent it and put the scaled number in a column of its own, val_norm from cansim in R and value_normalized in Python. From the recorded scripts:{fr}Pour l'appel de l'IPC ci-dessus, le script R obtient les données en un seul appel à cansim, et le script Python reprend la requête WDS, http2=True compris. Aucun des deux n'enfreint la règle du facteur scalaire : les deux gardent value tel que le WDS l'a envoyé et placent la valeur mise à l'échelle dans une colonne à part, val_norm de cansim en R et value_normalized en Python. Extraits des scripts enregistrés :{/}
{en}Everything else, briefly{fr}Tout le reste, en bref{/}
{en}Classifications. RDaaS holds NAICS, the Standard Geographical Classification and the rest: structure, category trees, index terms, exclusions, and concordances that map codes from one version to the next. It has one gap worth knowing. Asked for the category tree of the current NAICS, 2022.1.0, it answers with an empty body, while every older version tried returns the full tree. The tool says so and points to the 2017-to-2022 concordance, whose target codes are the current NAICS codes. Index terms switch to French only through an Accept-Language header, which the client sends.{fr}Classifications. Le RDaaS contient le SCIAN, la Classification géographique type et les autres : structure, arbres de catégories, termes de l'index, exclusions et concordances qui font passer les codes d'une version à la suivante. Il a une lacune à connaître. Interrogé sur l'arbre des catégories du SCIAN actuel, 2022.1.0, il répond par un corps vide, alors que toutes les versions antérieures essayées renvoient l'arbre complet. L'outil le signale et renvoie à la concordance de 2017 à 2022, dont les codes cibles sont ceux du SCIAN actuel. Les termes de l'index ne passent au français que par un en-tête Accept-Language, que le client envoie.{/}
{en}The census. The 2021 Census Profile is an SDMX API on its own host: find a geography, from a province down to a dissemination area, and one of 2,631 characteristics, then fetch the values. It too picks its language from an Accept-Language header rather than a parameter. The 2016 profile has a separate JSON API, and the 2001 to 2016 profiles are CSV or TAB bulk downloads, which the archive tools resolve to direct links. Census geography comes from an ArcGIS REST service that returns boundaries and DGUIDs, and that answers every error with HTTP 200 and an error object inside.{fr}Le recensement. Le Profil du recensement de 2021 est une API SDMX sur son propre hôte : on trouve une géographie, de la province à l'aire de diffusion, et l'une de 2 631 caractéristiques, puis on obtient les valeurs. Lui aussi choisit sa langue par un en-tête Accept-Language plutôt que par un paramètre. Le profil de 2016 a une API JSON distincte, et les profils de 2001 à 2016 sont des téléchargements en bloc CSV ou TAB, dont les outils d'archives donnent les liens directs. La géographie du recensement vient d'un service ArcGIS REST qui fournit les limites et les DGUID, et qui répond à toute erreur par HTTP 200 avec un objet d'erreur.{/}
{en}Releases. The Daily comes from its Atom feeds, the last 100 days by subject, and from the JSON file behind the release calendar, which goes back to 14 March 2012. wds_get_changed_cube_list lists the tables that changed on a date, and statcan_delta_get_file_link finds the bulk-update ZIP for one business day.{fr}Diffusions. Le Quotidien vient de ses flux Atom, les 100 derniers jours par sujet, et du fichier JSON derrière le calendrier des diffusions, qui remonte au 14 mars 2012. wds_get_changed_cube_list liste les tableaux modifiés à une date donnée, et statcan_delta_get_file_link trouve le ZIP de mise à jour d'un jour ouvrable.{/}
{en}The full list of families, with each one's prefix and number of tools, counted from the registry when this page was built:{fr}La liste complète des familles, avec le préfixe et le nombre d'outils de chacune, comptés dans le registre au moment de générer cette page :{/}
{{statcan_families}}{en}Where to go next{fr}Pour aller plus loin{/}
- {en}All {{statcan_tool_count}} Statistics Canada tools{fr}Les {{statcan_tool_count}} outils de Statistique Canada{/}{en}Every tool's parameters, keywords and an example call.{fr}Les paramètres, les mots-clés et un exemple d'appel pour chaque outil.{/}
- {en}The case studies{fr}Les études de cas{/}{en}The CPI and Census calls on this page as charts, with the calls behind each one.{fr}Les appels de l'IPC et du recensement de cette page en graphiques, avec les appels derrière chacun.{/}
- {en}Connect MapleStats to your agent{fr}Connecter MapleStats à votre agent{/}{en}Two steps, no account or API key.{fr}Deux étapes, sans compte ni clé d'API.{/}