From b9d7ab333d76e1d9c00664e43f972694c754f210 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Wed, 21 Oct 2026 10:48:57 +0100 Subject: [PATCH 1/2] leaderboard eligibility --- .claude/settings.json | 5 + CLAUDE.md | 29 ++ assets/features/leaderboards/eligibility.png | Bin 0 -> 57547 bytes es/features/leaderboards.mdx | 50 ++- es/features/users.mdx | 7 + features/leaderboards.mdx | 50 ++- features/users.mdx | 9 +- openapi/admin.json | 96 +++++- openapi/application.json | 301 ++++++++++++++++--- 9 files changed, 494 insertions(+), 53 deletions(-) create mode 100644 .claude/settings.json create mode 100644 CLAUDE.md create mode 100644 assets/features/leaderboards/eligibility.png diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..3781ac7d --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "permissions": { + "deny": ["Read(./.env)", "Read(./.env.local)", "Read(./.env.*.local)"] + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..2ed1662e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,29 @@ +# Trophy Docs + +Mintlify documentation site for Trophy. Repo-root paths are the canonical English source; `es/**` is generated by the translation workflow. See `README.md` for Lingo and MCP setup (Cursor-specific steps there). + +## Writing style + +- Never abbreviate "leaderboard" to "board" in docs copy, comments or commit messages. Always write "leaderboard". + +## Localization Workflow + +- Treat repo-root docs paths as the canonical English source and keep identical relative file paths/filenames across all locales. +- For planned doc changes, default scope to English source files only. Do not plan or execute manual edits to non-English locale content (for example `es/**`) unless the user explicitly asks for localized updates; rely on the existing translation workflow to propagate English changes. +- Keep all locale navigation in `docs.json` under `navigation.languages`; do not reintroduce `navigation.global` or top-level `navbar`. +- Keep SEO metatags in English unless explicitly requested otherwise. +- Do not translate code snippets, inline code, URLs, route slugs, or API identifiers. +- Shared media lives under repo-root `assets/`. Default-language pages are at repo-root section paths (for example `
/.mdx`) and targets are under locale directories (for example `es/
/.mdx`), so relative `src` paths need one more `../` under each locale segment than in English. `scripts/localize-internal-links.mjs` rewrites markdown `](/...)` and JSX `href="/..."` only (absolute navigation paths). Relative repo-root `assets/` `src` and MDX `import` paths are normalized by `scripts/localize-mdx-paths.mjs` after Lingo. +- `scripts/localize-mdx-paths.mjs` rewrites: `/components/*.jsx` for the active locale; `from ".../snippets/..."`; and JSX `src=".../assets/..."` so `../` depth matches the file’s path. Run after Lingo in `translate:generate` and translate-on-main; CI checks via `translate:localize-mdx-paths:check`. +- Shared MDX snippets live under repo-root `snippets/` (not per-locale). Import with relative paths from each page; do not add shared MDX snippets to Lingo `i18n.json` buckets unless you intend to translate them. +- Locale-aware React components that can contain text live under `components/*.jsx` for default language and `/components/*.jsx` for targets, and are manually maintained per locale (do not add them to Lingo `i18n.json` buckets; do not keep these in shared `snippets/`). +- Endpoint and webhook MDX must pin a **repo-local** OpenAPI file in frontmatter (do **not** put remote OpenAPI URLs in frontmatter, and do **not** set tab/group-level `openapi` in `docs.json` navigation—tab `openapi` auto-populates endpoints and duplicates MDX nav entries; remote URLs in frontmatter are not reliably resolved by Mintlify). Specs live under `openapi/application.json` (Application API + webhooks) and `openapi/admin.json` (Admin API), also listed under `api.openapi` in `docs.json`. Forms: `openapi: "/openapi/application.json GET /path"`, `openapi: "/openapi/application.json webhook "`, `openapi: "/openapi/admin.json METHOD /path"`. Keep those lines identical across locales; Lingo must not alter them. +- English **nav titles** for those pages come from OpenAPI **`summary`** via `scripts/sync-openapi-titles.mjs` (`npm run translate:sync-openapi-titles`). It runs at the start of `translate:generate` and on the translate-on-main workflow. Lingo translates the `title` field for target locales; the script only fills a missing target `title` with English (bootstrap)—it does not overwrite existing translations. +- Mintlify custom heading IDs use markdown `## Title {#slug}` (see Mintlify docs). Slugs are aligned from English source root files via `scripts/sync-heading-anchors.mjs`. Never translate or alter `{#…}`; sync runs after Lingo in PIT/CI. Do not merge heading-count drift between source root and target locales without fixing structure first. +- For Mintlify `` components that may be deep-linked, always set explicit `id` values (do not rely on title-derived hashes) and keep those `id`s identical across locales. +- Do not use fragile OpenAPI parameter hash links like `#parameter-*` in docs links; link to the endpoint page and reference parameter names inline in prose/code. +- Treat `lingo/brand-voice.md` as the source of truth for **brand voice** (tone and style) only. Do not add structural or tooling rules there (for example Mintlify `{#slug}` handling); those stay in this rule, README, and optionally in the Lingo engine **Instructions** field—not brand voice. +- Do not add script-based brand voice sync; sync brand voice on demand via Lingo MCP tools from chat. +- Treat `lingo/glossary.csv` as the glossary source of truth; use MCP sync with canonical key `sourceLocale|targetLocale|type|sourceText`. +- For glossary sync, always do dry-run first, show create/update/delete counts, then apply on confirmation. +- When adding locales, update both `docs.json` language blocks and `i18n.json` `locale.targets`. diff --git a/assets/features/leaderboards/eligibility.png b/assets/features/leaderboards/eligibility.png new file mode 100644 index 0000000000000000000000000000000000000000..64554a99c0f7a968f6a132e83a53c44433a36d62 GIT binary patch literal 57547 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 z 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 q@|xn-5Km3M#fOz-eaQ zZQY&>I&QW-5gY;PPSp{oRjJ9o#R~=IC^we4HSSQ+{{i1~VjEE=?*k!ceZR!KG;1h~ z0&Ym=Ed3%F4@#~)Btl7!ysMb1knU-X8T)+dP+J77tzgpdxqh+DH6N#OXapda;EiWh zl2vD;Pl +## Elegibilidad {#eligibility} + +La elegibilidad controla qué usuarios pueden participar en una clasificación. Úsala cuando una clasificación deba estar abierta solo a un grupo específico de usuarios, por ejemplo, clientes de pago, una región concreta o una cohorte beta. + +La elegibilidad se basa en [atributos de usuario personalizados](/es/features/users#custom-user-attributes). Un usuario solo puede participar en una clasificación, y aparecer en su ranking, si sus atributos cumplen todas las reglas de elegibilidad que definas. Las clasificaciones sin reglas de elegibilidad están abiertas a todos los usuarios. + +Para definir reglas de elegibilidad, ve a la página de configuración de la clasificación y añade uno o más atributos de usuario, junto con los valores que deben tener los usuarios, en el campo **Atributos de usuario** de la sección **Elegibilidad**. + + + + + + + Cuando defines varias reglas de elegibilidad, un usuario debe cumplirlas todas + para ser elegible. + + +Los usuarios que no son elegibles no reciben puntuación y no aparecen en los rankings, incluidas las ejecuciones históricas. Las posiciones se calculan solo entre los usuarios elegibles, por lo que no quedan huecos donde habría estado un usuario no elegible. Si los atributos de un usuario cambian y ya no cumplen las reglas, Trophy lo elimina de la clasificación. + + + Las reglas de elegibilidad solo se pueden cambiar mientras una clasificación está `Inactive`. Consulta + [estados de la clasificación](#leaderboard-statuses). + + +### Elegibilidad frente a desgloses {#eligibility-vs-breakdowns} + +Los [desgloses](#ranking-breakdowns) y la elegibilidad usan atributos de usuario, pero cumplen funciones distintas: + +- **Los desgloses** dividen a los participantes en grupos más pequeños. Todos los usuarios siguen clasificados, pero dentro de su propio grupo. +- **La elegibilidad** decide quién puede participar. Los usuarios que no cumplen las reglas quedan excluidos por completo. + +Puedes usar ambas opciones en la misma clasificación, por ejemplo, para limitar una clasificación a los usuarios de pago y luego agruparlos por país. + ## Fechas de Inicio y Fin {#start-end-dates} Usa fechas de inicio y fin para controlar la ventana dentro de la cual las clasificaciones están clasificando activamente a los usuarios. @@ -211,6 +249,8 @@ Si una clasificación ya tiene un número de participantes que coincide con su m La única excepción a esto es cuando se utilizan [atributos de desglose](#ranking-breakdowns) para agrupar a los participantes en cohortes más pequeñas. Al usar atributos de desglose, el límite de participantes se aplica a cada grupo, no de manera general. +Si una clasificación tiene reglas de [elegibilidad](#eligibility), el límite de participantes solo cuenta a los usuarios elegibles. + Si necesitas un límite de participantes mayor, considera usar [atributos de desglose](#ranking-breakdowns) para agrupar a los participantes en cohortes más pequeñas. O contáctanos en [support@trophy.so](mailto:support@trophy.so) y estaremos encantados de aumentar tus valores predeterminados. @@ -255,7 +295,7 @@ Para crear una clasificación, dirígete a la [página de clasificaciones](https - Guarda los cambios y dirígete a la página de configuración para establecer [fechas de inicio y fin](#start-end-dates), [programaciones de clasificaciones recurrentes](#repeating-leaderboards) y más. + Guarda los cambios y ve a la página de configuración para definir la [elegibilidad](#eligibility), las [fechas de inicio y fin](#start-end-dates), las [programaciones de clasificaciones recurrentes](#repeating-leaderboards) y más. @@ -365,6 +405,14 @@ Este gráfico es un histograma de las puntuaciones de los usuarios en una clasif {" "} + + Sí. Añade reglas de [elegibilidad](#eligibility) a la clasificación mediante atributos + de usuario personalizados, y solo los usuarios con los valores de atributo correspondientes podrán + participar y aparecer en la clasificación. + + +{" "} + Trophy admite la ejecución de clasificaciones repetitivas en cualquier número arbitrario de días. Así que una clasificación semanal sería simplemente una clasificación que se repite cada 7 diff --git a/es/features/users.mdx b/es/features/users.mdx index 501d226e..9e0b8f8e 100644 --- a/es/features/users.mdx +++ b/es/features/users.mdx @@ -187,6 +187,13 @@ Los atributos personalizados de usuario pueden utilizarse para personalizar logr > Controla qué usuarios reciben correos electrónicos gamificados. + + Limita las clasificaciones a grupos específicos de usuarios. + +## Eligibility {#eligibility} + +Eligibility controls which users are allowed to take part in a leaderboard at all. Use it when a leaderboard should only be open to a specific group of users, for example paying customers, a particular region or a beta cohort. + +Eligibility is based on [custom user attributes](/features/users#custom-user-attributes). A user can only take part in, and appear in the rankings of, a leaderboard if their attributes match every eligibility rule you set. Leaderboards with no eligibility rules are open to all users. + +To set eligibility rules, head to the leaderboard configuration page and add one or more user attributes and the values users must have in the **User attributes** field of the **Eligibility** section. + + + + + + + When you set multiple eligibility rules, a user must match all of them to be + eligible. + + +Users who aren't eligible are not scored and don't appear in rankings, including historical runs. Ranks are calculated across eligible users only, so there are no gaps where ineligible users would have been. If a user's attributes change and they no longer match, Trophy removes them from the leaderboard. + + + Eligibility rules can only be changed while a leaderboard is `Inactive`. See + [leaderboard statuses](#leaderboard-statuses). + + +### Eligibility vs Breakdowns {#eligibility-vs-breakdowns} + +[Breakdowns](#ranking-breakdowns) and eligibility both use user attributes but do different jobs: + +- **Breakdowns** split participants into smaller groups. Every user is still ranked, just within their own group. +- **Eligibility** decides who can participate. Users who don't match are excluded entirely. + +You can use both on the same leaderboard, for example to limit a leaderboard to paying users and then group them by country. + ## Start & End Dates {#start-end-dates} Use start and end dates to control the window within which leaderboards are actively ranking users. @@ -213,6 +251,8 @@ If a leaderboard already has a number of participants that matches its configure The only exception to this is when using [breakdown attributes](#ranking-breakdowns) to group participants into smaller cohorts. When using breakdown attributes the participant limit applies to each group, not overall. +If a leaderboard has [eligibility](#eligibility) rules, the participant limit only counts eligible users. + If you require a larger participant limit, considering using [breakdown attributes](#ranking-breakdowns) to group participants into smaller cohorts. Or please contact us at [support@trophy.so](mailto:support@trophy.so) and we'll be happy to increase your defaults. @@ -257,7 +297,7 @@ To create a leaderboard, head to the [leaderboards page](https://app.trophy.so/l - Hit save and head to the configure page to set up [start & end dates](#start-end-dates), [repeating leaderboard schedules](#repeating-leaderboards) and more. + Hit save and head to the configure page to set up [eligibility](#eligibility), [start & end dates](#start-end-dates), [repeating leaderboard schedules](#repeating-leaderboards) and more. @@ -367,6 +407,14 @@ This chart is a histogram of users' scores in a particular leaderboard. This is {" "} + + Yes. Add [eligibility](#eligibility) rules to the leaderboard using custom + user attributes, and only users with matching attribute values will be able + to take part and appear in the rankings. + + +{" "} + Trophy supports running repeating leaderboards on any arbitrary number of days. So a weekly leaderboard would just be a leaderboard that repeats every 7 diff --git a/features/users.mdx b/features/users.mdx index ff5dbc64..d8384a51 100644 --- a/features/users.mdx +++ b/features/users.mdx @@ -185,11 +185,18 @@ Custom user attributes can be used to personalize achievements, customize the wa Personalize email copy and subject lines with custom user attributes. + + Limit leaderboards to specific groups of users. + #### Data Aggregation and Filtering {#data-aggregation-and-filtering} diff --git a/openapi/admin.json b/openapi/admin.json index a412e574..f804e86a 100644 --- a/openapi/admin.json +++ b/openapi/admin.json @@ -2,13 +2,13 @@ "openapi": "3.1.0", "info": { "title": "Trophy", - "version": "1.26.0" + "version": "1.27.0" }, "x-fern-global-headers": [ { "header": "X-SDK-VERSION", "name": "sdkVersion", - "x-fern-default": "1.26.0" + "x-fern-default": "1.27.0" } ], "paths": { @@ -4884,7 +4884,13 @@ "550e8400-e29b-41d4-a716-446655440010" ], "runUnit": "month", - "runInterval": 1 + "runInterval": 1, + "userAttributes": [ + { + "attributeId": "550e8400-e29b-41d4-a716-446655440010", + "attributeValue": "premium" + } + ] }, { "id": "550e8400-e29b-41d4-a716-446655440101", @@ -4894,7 +4900,8 @@ "rankBy": "streak", "maxParticipants": 1000, "start": "2026-04-27", - "breakdownAttributes": [] + "breakdownAttributes": [], + "userAttributes": [] } ] } @@ -5314,7 +5321,8 @@ "550e8400-e29b-41d4-a716-446655440010" ], "runUnit": "month", - "runInterval": 1 + "runInterval": 1, + "userAttributes": [] } }, "Streak leaderboard": { @@ -5326,7 +5334,8 @@ "rankBy": "streak", "maxParticipants": 1000, "start": "2026-04-27", - "breakdownAttributes": [] + "breakdownAttributes": [], + "userAttributes": [] } } } @@ -8149,7 +8158,7 @@ }, "userAttributes": { "type": "array", - "description": "User attribute filters applied to the achievement. Each `attributeId` must be an active user attribute.", + "description": "User attribute filters applied to the achievement.", "items": { "type": "object", "properties": { @@ -8471,7 +8480,7 @@ "array", "null" ], - "description": "Updated user attribute filters. Send `null` to clear. Each `attributeId` must be an active user attribute.", + "description": "Updated user attribute filters. Send `null` to clear.", "items": { "type": "object", "properties": { @@ -8726,6 +8735,26 @@ "minimum": 1, "description": "The number of `runUnit`s between repeats. Required when `runUnit` is set.", "example": 1 + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters. Omit to allow every user.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } } }, "required": [ @@ -8834,6 +8863,26 @@ "runInterval": { "type": "integer", "description": "The number of recurrence units between leaderboard runs." + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } } }, "required": [ @@ -8844,7 +8893,8 @@ "rankBy", "maxParticipants", "start", - "breakdownAttributes" + "breakdownAttributes", + "userAttributes" ] }, "CreateLeaderboardsResponse": { @@ -9015,6 +9065,34 @@ "type": "integer", "minimum": 1, "description": "The updated recurrence interval." + }, + "userAttributes": { + "oneOf": [ + { + "type": "array", + "description": "Replacement user attribute filters.", + "items": { + "type": "object", + "properties": { + "attributeId": { + "type": "string", + "format": "uuid" + }, + "attributeValue": { + "type": "string" + } + }, + "required": [ + "attributeId", + "attributeValue" + ] + } + }, + { + "type": "null" + } + ], + "description": "Replacement user attribute filters, or `null` to clear them. Omit to leave the current filters unchanged. This can only be changed while the leaderboard is inactive." } }, "required": [ diff --git a/openapi/application.json b/openapi/application.json index 889318f1..9f4b847c 100644 --- a/openapi/application.json +++ b/openapi/application.json @@ -2,13 +2,13 @@ "openapi": "3.1.0", "info": { "title": "Trophy", - "version": "1.26.0" + "version": "1.27.0" }, "x-fern-global-headers": [ { "header": "X-SDK-VERSION", "name": "sdkVersion", - "x-fern-default": "1.26.0" + "x-fern-default": "1.27.0" } ], "paths": { @@ -79,7 +79,17 @@ "badgeUrl": "https://example.com/badge.png", "key": "finish-onboarding", "completions": 8, - "rarity": 80 + "rarity": 80, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + }, + { + "key": "region", + "value": "us-east" + } + ] }, { "id": "5100fe51-6bce-6j44-b0hs-bddc4e123683", @@ -141,7 +151,17 @@ "badgeUrl": "https://example.com/badge.png", "anniversaryYears": 1, "completions": 4, - "rarity": 40 + "rarity": 40, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + }, + { + "key": "region", + "value": "us-east" + } + ] } ] } @@ -242,11 +262,18 @@ "description": "Complete the onboarding process.", "badgeUrl": "https://example.com/badge.png", "key": "finish-onboarding", - "achievedAt": "2021-01-01T00:00:00Z" + "achievedAt": "2021-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, "points": { "points-system-key": { "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "points-system-key", "name": "XP", "description": null, "badgeUrl": null, @@ -262,7 +289,16 @@ "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", "type": "achievement", "achievementName": "Finish onboarding", - "points": 10 + "points": 10, + "status": "active", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -334,7 +370,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true + "subscribeToEmails": true } } } @@ -366,11 +402,11 @@ "x-codeSamples": [ { "lang": "javascript", - "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.metrics.event(\n \"words-written\",\n {\n user: {\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribedToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n },\n value: 750,\n attributes: {\n category: 'writing',\n source: 'mobile-app'\n }\n }\n);\n" + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.metrics.event(\n \"words-written\",\n {\n user: {\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribeToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n },\n value: 750,\n attributes: {\n category: 'writing',\n source: 'mobile-app'\n }\n }\n);\n" }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nuser = UpsertedUser(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n\nresponse = client.metrics.event(\n \"words-written\",\n user=user,\n value=750,\n attributes={\n \"category\": \"writing\",\n \"source\": \"mobile-app\"\n})\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nuser = UpsertedUser(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n\nresponse = client.metrics.event(\n \"words-written\",\n user=user,\n value=750,\n attributes={\n \"category\": \"writing\",\n \"source\": \"mobile-app\"\n})\n" }, { "lang": "go", @@ -422,7 +458,13 @@ "metricValue": 500, "name": "500 words written", "description": "Write 500 words in the app.", - "achievedAt": "2020-01-01T00:00:00Z" + "achievedAt": "2020-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "currentStreak": { @@ -431,11 +473,13 @@ "started": "2025-04-02", "periodStart": "2025-03-31", "periodEnd": "2025-04-05", - "expires": "2025-04-12" + "expires": "2025-04-12", + "extended": true }, "points": { "xp": { "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", + "key": "xp", "name": "XP", "description": null, "badgeUrl": null, @@ -460,7 +504,16 @@ "type": "metric", "metricName": "words written", "metricThreshold": 100, - "points": 10 + "points": 10, + "status": "active", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -477,6 +530,8 @@ "runInterval": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "metricName": "words written", "metricKey": "words-written", "threshold": 10, @@ -610,11 +665,11 @@ "x-codeSamples": [ { "lang": "javascript", - "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.users.create({\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribedToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n});\n" + "source": "import { TrophyApiClient } from '@trophyso/node';\n\nconst trophy = new TrophyApiClient({\n apiKey: 'YOUR_API_KEY'\n});\n\nconst response = await trophy.users.create({\n id: 'user-id',\n email: 'user@example.com',\n tz: 'Europe/London',\n subscribeToEmails: true,\n attributes: {\n department: 'engineering',\n role: 'developer'\n }\n});\n" }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.create(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.create(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -651,7 +706,8 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, + "control": false, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -762,7 +818,8 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, + "control": false, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -821,7 +878,7 @@ }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.identify(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.identify(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -873,6 +930,23 @@ "application/json": { "schema": { "$ref": "#/components/schemas/User" + }, + "examples": { + "Successful operation": { + "value": { + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribeToEmails": true, + "control": false, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } + } + } } } } @@ -923,7 +997,7 @@ }, { "lang": "python", - "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.update(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribedToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" + "source": "from trophy import TrophyApi\n\nclient = TrophyApi(api_key='YOUR_API_KEY')\n\nresponse = client.users.update(\n id=\"123\",\n email=\"user@example.com\",\n tz=\"Europe/London\",\n subscribeToEmails=True,\n attributes={\n \"department\": \"engineering\",\n \"role\": \"developer\"\n }\n)\n" }, { "lang": "go", @@ -975,6 +1049,23 @@ "application/json": { "schema": { "$ref": "#/components/schemas/User" + }, + "examples": { + "Successful operation": { + "value": { + "id": "user-id", + "email": "user@example.com", + "tz": "Europe/London", + "subscribeToEmails": true, + "control": false, + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z", + "attributes": { + "department": "engineering", + "role": "developer" + } + } + } } } } @@ -1392,7 +1483,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 500, "achievedAt": "2021-01-01T00:00:00Z", - "badgeUrl": "https://example.com/badge1.png" + "badgeUrl": "https://example.com/badge1.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "8a07f2d0-9c72-4de1-bf92-9530ae82b4b6", @@ -1402,7 +1499,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 1000, "achievedAt": "2021-01-02T00:00:00Z", - "badgeUrl": "https://example.com/badge2.png" + "badgeUrl": "https://example.com/badge2.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "2090d038-aa04-4048-ab2e-e2b7bf2d3b9f", @@ -1412,7 +1515,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 2000, "achievedAt": null, - "badgeUrl": "null" + "badgeUrl": "null", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -1538,7 +1647,13 @@ "name": "Novice Writer", "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 500, - "achievedAt": "2021-01-01T00:00:00Z" + "achievedAt": "2021-01-01T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "8a07f2d0-9c72-4de1-bf92-9530ae82b4b6", @@ -1546,7 +1661,13 @@ "name": "Intermediate Writer", "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 1000, - "achievedAt": "2021-01-02T00:00:00Z" + "achievedAt": "2021-01-02T00:00:00Z", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "2090d038-aa04-4048-ab2e-e2b7bf2d3b9f", @@ -1555,7 +1676,13 @@ "metricId": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", "metricValue": 2000, "achievedAt": null, - "badgeUrl": "https://example.com/badge.png" + "badgeUrl": "https://example.com/badge.png", + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -1886,7 +2013,13 @@ "achievedAt": "2021-01-01T00:00:00Z", "badgeUrl": "https://example.com/badge2.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc623", @@ -1899,7 +2032,13 @@ "achievedAt": "2021-02-01T00:00:00Z", "badgeUrl": "https://example.com/badge1.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc624", @@ -1910,7 +2049,13 @@ "achievedAt": "2021-03-01T00:00:00Z", "badgeUrl": "https://example.com/badge2.png", "completions": 100, - "rarity": 50 + "rarity": 50, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] }, { "id": "d01dcbcb-d51e-4c12-b054-dc811dcdc626", @@ -1920,7 +2065,13 @@ "achievedAt": "2021-04-01T00:00:00Z", "badgeUrl": "https://example.com/badge3.png", "completions": 40, - "rarity": 20 + "rarity": 20, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ] } @@ -2340,8 +2491,17 @@ "id": "0040fe51-6bce-4b44-b0ad-bddc4e123534", "type": "metric", "points": 10, + "status": "active", "metricName": "words written", - "metricThreshold": 1000 + "metricThreshold": 1000, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], + "created": "2021-01-01T00:00:00Z", + "updated": "2021-01-01T00:00:00Z" } } ] @@ -2814,6 +2974,8 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 2, @@ -3009,7 +3171,13 @@ "metricName": "Words Written", "achievedAt": "2024-03-15T14:30:00Z", "completions": 150, - "rarity": 15 + "rarity": 15, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "leaderboards": { @@ -3024,6 +3192,8 @@ "start": "2024-03-11", "end": "2024-03-17", "maxParticipants": 100, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 3, @@ -3119,7 +3289,13 @@ "metricName": "Words Written", "achievedAt": "2024-03-15T14:30:00Z", "completions": 150, - "rarity": 15 + "rarity": 15, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ] } ], "leaderboards": {}, @@ -3446,6 +3622,12 @@ "points": 10, "status": "active", "streakLengthThreshold": 7, + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z" }, @@ -3973,6 +4155,12 @@ "country", "plan" ], + "userAttributes": [ + { + "key": "plan-type", + "value": "premium" + } + ], "runUnit": "day", "runInterval": 7 }, @@ -3990,6 +4178,7 @@ "maxParticipants": 50, "breakdownAttribute": null, "breakdownAttributes": [], + "userAttributes": [], "runUnit": null, "runInterval": null } @@ -4165,6 +4354,8 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "breakdownAttributes": [], + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -4275,7 +4466,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4346,7 +4537,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4420,7 +4611,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4494,7 +4685,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4562,7 +4753,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4631,7 +4822,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4695,7 +4886,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -4822,7 +5013,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -5045,6 +5236,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5116,6 +5308,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5199,6 +5392,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rankings": [ @@ -5502,7 +5696,7 @@ "id": "user-id", "email": "user@example.com", "tz": "Europe/London", - "subscribedToEmails": true, + "subscribeToEmails": true, "created": "2021-01-01T00:00:00Z", "updated": "2021-01-01T00:00:00Z", "attributes": { @@ -5522,6 +5716,7 @@ "end": null, "maxParticipants": 100, "breakdownAttribute": null, + "userAttributes": [], "runUnit": "day", "runInterval": 7, "rank": 2, @@ -6410,6 +6605,29 @@ ], "description": "The interval between repetitions, relative to the start date and repetition type. Null for one-time leaderboards.", "example": 7 + }, + "userAttributes": { + "type": "array", + "description": "User attribute filters that a user must match to participate in this leaderboard and appear in its rankings.", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The key of the user attribute.", + "example": "plan-type" + }, + "value": { + "type": "string", + "description": "The required value of the user attribute.", + "example": "premium" + } + }, + "required": [ + "key", + "value" + ] + } } }, "required": [ @@ -6427,7 +6645,8 @@ "endTime", "maxParticipants", "runUnit", - "runInterval" + "runInterval", + "userAttributes" ] }, "LeaderboardResponseWithRankings": { From 816be355af1611bc306e84e804ce3fd5a54a2c15 Mon Sep 17 00:00:00 2001 From: Charlie Hopkins-Brinicombe Date: Wed, 21 Oct 2026 11:15:16 +0100 Subject: [PATCH 2/2] pr changes --- es/features/leaderboards.mdx | 4 +- features/leaderboards.mdx | 4 +- i18n.lock | 158 +++++++++++++++++++---------------- 3 files changed, 93 insertions(+), 73 deletions(-) diff --git a/es/features/leaderboards.mdx b/es/features/leaderboards.mdx index a4fc03e8..a94e8c40 100644 --- a/es/features/leaderboards.mdx +++ b/es/features/leaderboards.mdx @@ -156,7 +156,9 @@ Para definir reglas de elegibilidad, ve a la página de configuración de la cla para ser elegible. -Los usuarios que no son elegibles no reciben puntuación y no aparecen en los rankings, incluidas las ejecuciones históricas. Las posiciones se calculan solo entre los usuarios elegibles, por lo que no quedan huecos donde habría estado un usuario no elegible. Si los atributos de un usuario cambian y ya no cumplen las reglas, Trophy lo elimina de la clasificación. +Los usuarios que no son elegibles no reciben puntuación y no aparecen en los rankings. Las posiciones se calculan solo entre los usuarios elegibles, por lo que no quedan huecos donde habría estado un usuario no elegible. Si los atributos de un usuario cambian y ya no cumplen las reglas, Trophy lo elimina de la ejecución actual de la clasificación. + +Las ejecuciones históricas nunca se modifican. Una vez que una ejecución ha finalizado, sus rankings quedan fijados, independientemente de cómo cambien después los atributos de los usuarios. Las reglas de elegibilidad solo se pueden cambiar mientras una clasificación está `Inactive`. Consulta diff --git a/features/leaderboards.mdx b/features/leaderboards.mdx index 9cc0ab92..004b90bc 100644 --- a/features/leaderboards.mdx +++ b/features/leaderboards.mdx @@ -156,7 +156,9 @@ To set eligibility rules, head to the leaderboard configuration page and add one eligible. -Users who aren't eligible are not scored and don't appear in rankings, including historical runs. Ranks are calculated across eligible users only, so there are no gaps where ineligible users would have been. If a user's attributes change and they no longer match, Trophy removes them from the leaderboard. +Users who aren't eligible are not scored and don't appear in rankings. Ranks are calculated across eligible users only, so there are no gaps where ineligible users would have been. If a user's attributes change and they no longer match, Trophy removes them from the current run of the leaderboard. + +Historical runs are never changed. Once a run has finished its rankings are fixed, regardless of how users' attributes change afterwards. Eligibility rules can only be changed while a leaderboard is `Inactive`. See diff --git a/i18n.lock b/i18n.lock index a83c0fdd..9683a391 100644 --- a/i18n.lock +++ b/i18n.lock @@ -2967,7 +2967,7 @@ checksums: content/41: 05641d90a0717b85be807cc98f584c6a content/42: 95e371be1e0fec6192b97fe249708847 content/43: 3ad13a1574f2de214f4c699755082c41 - content/44: 9cc84e456ef816bb07be30921d288061 + content/44: 0300a96edf67f441577e9218fa2c898e content/45: 3fe90af4210ad4881b08f5a23dc73802 content/46: 64254f469606a3fd7f45c6a3591f8458 content/47: c6f004129299c86e60eeeeae9306858c @@ -3482,76 +3482,92 @@ checksums: content/43: 218e40ee0c9c118800a6bbc8c22a0406 content/44: 73dc7193c431ee27face3e96cca59471 content/45: abdc26659c4e48ac2a87a447c8069e21 - content/46: 2da3bce39f011d227e7439bf39000c4b - content/47: 376db24492fd179249a97399cac62c7b - content/48: 1f65a0e76a538cfda34ef69ff7316d11 - content/49: be8ee42c61c2f8412a2223346fd1e5d7 - content/50: 19ddbf4a6a27d1217a35ff1ab7082883 - content/51: 6978c15affabcb936aeac6d02d2a9201 - content/52: f53d2373b481a20fe76d332f4a102b84 - content/53: 26f14651033af5a7e0da93c5dd653916 - content/54: 84be083ce8f618a49aa37c1b5ebae0ad - content/55: 0c38bac95149de65c170e1dc15518530 - content/56: 09b8810dc95ee7576ab78014537171b0 - content/57: caea7e41aac97a6ddc795b729dea0931 - content/58: 273ebc1d83cace35c22fa22d2197272c - content/59: a613f2df863b43e86ca0da44e6f15453 - content/60: b33e18f0f1d560693bb4dbd7e1721d4f - content/61: 7f6326115eac856bb1a7ff6880f71906 - content/62: d8f1e59bb6a7b4a47013dd4ac7685903 - content/63: 6b8ea2b42e1d06e799a5a8dd20f18e4c - content/64: f2795e78ad428a0fc0d12ad8186b899a - content/65: 68fd00d1807b1c3633aa5546dc4144d5 - content/66: f6e705bf5e8338559f9cdecad266d761 - content/67: 29eba1f6a44ec24ee2b0d97faff09564 - content/68: 99c2177f3abb1ba8d08e6d45f6f7fb47 - content/69: b51af007ac57c287f3cc3e85e77d6aec - content/70: 610db0f043625ec6ace32150e8b96a66 - content/71: 258c9459be6a0fc3f535b41a89922f50 - content/72: afef5bbb2a19c4fd4958de5066bc51c1 - content/73: 830481b98e88ae69a3971df3aff5d250 - content/74: 538df8a5de4562fa88ae14bd586adc97 - content/75: 13d00a5c8b9f29080a87b703527bda75 - content/76: b0655e68a1c5d9579fbcfdd75db04fce - content/77: 71d221bcbeb83b663ee8c0a0e4975b04 - content/78: a40d8cc248a02d4ce34ae16119b14bf8 - content/79: 4e5ddca85ed818337a2757ae6bc2dc64 - content/80: a27e8a0a72073744f925f2820a1b7684 - content/81: 73aea3ef20f5f9d1626fcc73fdea597d - content/82: a01a451f7aba1a80f0af7148df811dfc - content/83: 84a204a7da04ff738ebcc5f4d3a5ea35 - content/84: 3347863434fc4a0bd8f075b971428c95 - content/85: bb2ac20c712241859473f415c26a0a11 - content/86: e180f5d5909fef8296047e0782fe8a85 - content/87: 9c2bbabcea6c0a514182170b67f38183 - content/88: 214aa9e0d35236656ba1738c8be4c3f0 - content/89: 919b58f12a4f16d63b5a35d18fdf04bb - content/90: 2ca28a1219570c10ddeaf4cb1157bb6d - content/91: a6390089f6e92430098d3d97c610c17a - content/92: 300bcdb15fbcadfde3db772fafe63524 - content/93: 3d93f05e4ce92565d044836c948352a9 - content/94: 0d08ff106c532b64677561ea2c3bad1f - content/95: 116df75dc256a5019b4809510f35677c - content/96: 162d669fb5a317e3a9deb84a1745c08c - content/97: 0ab559de8fc24b6e6cca6175444cfe9a - content/98: fab414b91a7a42cd5e37a0c5ade9945a - content/99: b87703add8d1e584a1307b28c871a0db - content/100: 2d9538daa0f3bcd2d760598097ee5d80 - content/101: 9c3265eff155d4a823fd955cde7dba35 - content/102: 689a23c317009f8944c124a0058137bc - content/103: ed10345c824c6fc4c69c882e9983b12b - content/104: fb9ce2e2edb7815efec73b696b5d5e95 - content/105: afd2cbb809e9881ac70735964981a355 - content/106: 44d8170f535788a47c7874e25dedb768 - content/107: fa9f6f1b8a7829a8e68a1d0340a84533 - content/108: b38dbc626d4fb02a18b368e626ca7cb0 - content/109: 79524f874744893afda00224a0203ea6 - content/110: 0fa45599d835da6f2e916859554673eb - content/111: 450ad7f99b79b12599856f813b9ef758 - content/112: 4f9f49c4756aa59d40fcc76ca15669a9 - content/113: f151f31b2581a7bd7f7d8635bff09dc8 - content/114: 10e8e9a59847ddd720c6d06b12c7b120 - content/115: 9799b973234bf2d10d5c971b267f55a5 + content/46: 1b2ef030b4a9c9ed76c1dae370bea77f + content/47: f16e2f4ef0731081b33838c8cb17fc34 + content/48: e839364a016e77e989922c6ac8a76a02 + content/49: 095c75f86e61d82e96e1159397765fe4 + content/50: a564b0f8fe8c5dd0d5cfa9f1897d01b2 + content/51: 8d4047550c18a4f9f5274fb479694e97 + content/52: 2dbb9d26048c96ab4d272d573dbc0591 + content/53: 8f3a2c9e3e4cff919cf089384a3e0691 + content/54: e30b47ecd8885f1c5e558937cb87f856 + content/55: 41beacaa919d50752ac1b81b3f109563 + content/56: 7d4548e2d24c1055bb6d6d03201bc285 + content/57: 5e840fe671834ee5d385c9dc4ed6ee49 + content/58: 75e7f0244426fbf3fc98265d184dd8ea + content/59: 2da3bce39f011d227e7439bf39000c4b + content/60: 376db24492fd179249a97399cac62c7b + content/61: 1f65a0e76a538cfda34ef69ff7316d11 + content/62: be8ee42c61c2f8412a2223346fd1e5d7 + content/63: 19ddbf4a6a27d1217a35ff1ab7082883 + content/64: 6978c15affabcb936aeac6d02d2a9201 + content/65: f53d2373b481a20fe76d332f4a102b84 + content/66: 26f14651033af5a7e0da93c5dd653916 + content/67: 84be083ce8f618a49aa37c1b5ebae0ad + content/68: 0c38bac95149de65c170e1dc15518530 + content/69: 09b8810dc95ee7576ab78014537171b0 + content/70: caea7e41aac97a6ddc795b729dea0931 + content/71: 273ebc1d83cace35c22fa22d2197272c + content/72: a613f2df863b43e86ca0da44e6f15453 + content/73: b33e18f0f1d560693bb4dbd7e1721d4f + content/74: 7f6326115eac856bb1a7ff6880f71906 + content/75: d8f1e59bb6a7b4a47013dd4ac7685903 + content/76: 6b8ea2b42e1d06e799a5a8dd20f18e4c + content/77: f2795e78ad428a0fc0d12ad8186b899a + content/78: 68fd00d1807b1c3633aa5546dc4144d5 + content/79: f6e705bf5e8338559f9cdecad266d761 + content/80: 29eba1f6a44ec24ee2b0d97faff09564 + content/81: 99c2177f3abb1ba8d08e6d45f6f7fb47 + content/82: b51af007ac57c287f3cc3e85e77d6aec + content/83: 610db0f043625ec6ace32150e8b96a66 + content/84: 258c9459be6a0fc3f535b41a89922f50 + content/85: d7dfcd0c7c754b5ce13f1578a4f99954 + content/86: afef5bbb2a19c4fd4958de5066bc51c1 + content/87: 830481b98e88ae69a3971df3aff5d250 + content/88: 538df8a5de4562fa88ae14bd586adc97 + content/89: 13d00a5c8b9f29080a87b703527bda75 + content/90: b0655e68a1c5d9579fbcfdd75db04fce + content/91: 71d221bcbeb83b663ee8c0a0e4975b04 + content/92: a40d8cc248a02d4ce34ae16119b14bf8 + content/93: 4e5ddca85ed818337a2757ae6bc2dc64 + content/94: a27e8a0a72073744f925f2820a1b7684 + content/95: 70417ef01650ced9aba95003d897b92f + content/96: a01a451f7aba1a80f0af7148df811dfc + content/97: 84a204a7da04ff738ebcc5f4d3a5ea35 + content/98: 3347863434fc4a0bd8f075b971428c95 + content/99: bb2ac20c712241859473f415c26a0a11 + content/100: e180f5d5909fef8296047e0782fe8a85 + content/101: 9c2bbabcea6c0a514182170b67f38183 + content/102: 214aa9e0d35236656ba1738c8be4c3f0 + content/103: 919b58f12a4f16d63b5a35d18fdf04bb + content/104: 2ca28a1219570c10ddeaf4cb1157bb6d + content/105: a6390089f6e92430098d3d97c610c17a + content/106: 300bcdb15fbcadfde3db772fafe63524 + content/107: 3d93f05e4ce92565d044836c948352a9 + content/108: 0d08ff106c532b64677561ea2c3bad1f + content/109: 116df75dc256a5019b4809510f35677c + content/110: 162d669fb5a317e3a9deb84a1745c08c + content/111: 0ab559de8fc24b6e6cca6175444cfe9a + content/112: fab414b91a7a42cd5e37a0c5ade9945a + content/113: b87703add8d1e584a1307b28c871a0db + content/114: 2d9538daa0f3bcd2d760598097ee5d80 + content/115: 9c3265eff155d4a823fd955cde7dba35 + content/116: 689a23c317009f8944c124a0058137bc + content/117: ed10345c824c6fc4c69c882e9983b12b + content/118: fb9ce2e2edb7815efec73b696b5d5e95 + content/119: afd2cbb809e9881ac70735964981a355 + content/120: 44d8170f535788a47c7874e25dedb768 + content/121: fa9f6f1b8a7829a8e68a1d0340a84533 + content/122: b38dbc626d4fb02a18b368e626ca7cb0 + content/123: 79524f874744893afda00224a0203ea6 + content/124: 0fa45599d835da6f2e916859554673eb + content/125: 450ad7f99b79b12599856f813b9ef758 + content/126: 4f9f49c4756aa59d40fcc76ca15669a9 + content/127: 917173aa35b9a8d822a0dfe9220401f7 + content/128: 4f9f49c4756aa59d40fcc76ca15669a9 + content/129: f151f31b2581a7bd7f7d8635bff09dc8 + content/130: 10e8e9a59847ddd720c6d06b12c7b120 + content/131: 9799b973234bf2d10d5c971b267f55a5 6e661270fda5f62c58626640850c56b3: meta/title: d17179a947e9687dd2338ca524b26b63 meta/description: a491b9e76be8c65506d2af60e6c6b972