planvortex 0.5.0__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {planvortex-0.5.0 → planvortex-0.6.0}/CHANGELOG.md +40 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/PKG-INFO +4 -4
- {planvortex-0.5.0 → planvortex-0.6.0}/README.md +3 -3
- {planvortex-0.5.0 → planvortex-0.6.0}/openapi/planvortex.openapi.json +24 -14
- {planvortex-0.5.0 → planvortex-0.6.0}/pyproject.toml +1 -1
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/errors.py +15 -6
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_generated/models.py +20 -9
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_shapes.py +2 -1
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_version.py +1 -1
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/types.py +13 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_core.py +12 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_connect.py +1 -1
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_publish.py +1 -1
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_organizations.py +2 -2
- {planvortex-0.5.0 → planvortex-0.6.0}/.gitignore +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/LICENSE +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/scripts/build_docs.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/scripts/check_packaging.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/scripts/generate_models.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/scripts/generate_sync.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/scripts/route_coverage.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_client.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_client_sync.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/auth.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/auth_sync.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/files.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/http.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/http_sync.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/pagination.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/query.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_core/transport.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/_generated/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/py.typed +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/accounts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/ai_plans.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/apps.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/base.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/catalog.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/clients.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/comments.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/contacts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/dashboard.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/integrations.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/messages.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/organizations.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/products.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/publications.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources/uploads.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/accounts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/ai_plans.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/apps.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/base.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/catalog.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/clients.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/comments.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/contacts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/dashboard.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/integrations.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/messages.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/organizations.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/products.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/publications.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/resources_sync/uploads.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/src/planvortex/webhooks.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/conftest.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/contrato.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/__init__.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/conftest.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_auth.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_catalog.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_connect_flow.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_errors.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_publish.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/live/test_read.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_accounts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_ai_plans.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_apps.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_catalog.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_client.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_clients.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_comments.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_contacts.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_dashboard.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_comments.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_schedule.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_example_webhooks.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_files.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_generate_models.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_generate_sync.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_integrations.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_messages.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_models.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_openapi_freshness.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_packaging.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_pagination.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_products.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_publications.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_query_parity.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_readme.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_route_coverage.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_shapes_parity.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_types.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_uploads.py +0 -0
- {planvortex-0.5.0 → planvortex-0.6.0}/tests/test_webhooks.py +0 -0
|
@@ -4,6 +4,46 @@ All notable changes to this package are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.6.0] - 2026-09-03
|
|
8
|
+
|
|
9
|
+
**Publications are unlimited on every plan**, so the quota that said otherwise is gone from the
|
|
10
|
+
types.
|
|
11
|
+
|
|
12
|
+
It was never a number this package invented: `PlanData["publications"]` came from the spec, and the
|
|
13
|
+
server stopped charging for it on 02-09-2026. What stayed behind was worse than a wrong number — it
|
|
14
|
+
was a key mypy kept promising, so `limits["publications"]` type-checked and raised `KeyError` at
|
|
15
|
+
runtime.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **BREAKING (types only): `PlanData` no longer has a `publications` key.** The count moved to the
|
|
20
|
+
new `PlanUseData`, which is what `actual_use` has always been in practice: the same shape as a
|
|
21
|
+
plan plus `publications`, and `NotRequired` because a brand-new organization has spent nothing.
|
|
22
|
+
Nothing changes on the wire.
|
|
23
|
+
- `OrganizationUse["actual_use"]` is a `PlanUseData`; `actual_asigned` stays a `PlanData`, which is
|
|
24
|
+
the real asymmetry: what a child organization was handed cannot include a metric.
|
|
25
|
+
- The `publish` example prints publications as what they are — a monthly count with no ceiling —
|
|
26
|
+
instead of `12 de 100`. What throttles publishing is rate (per hour and account, and the
|
|
27
|
+
per-network daily caps in `GET /social_limits`), not the plan.
|
|
28
|
+
- **The `publication` error family now reaches 979, not 960.** Everything above the old ceiling was
|
|
29
|
+
falling outside every range and arriving as a bare `PlanVortexError`: the Bluesky, Discord,
|
|
30
|
+
Telegram and Threads codes (961-977) and, more to the point, the two rate brakes that replaced the
|
|
31
|
+
monthly quota — **978** (publishing too fast on this account) and **979** (that network's daily
|
|
32
|
+
cap). `except PublicationError` now catches them.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
**The public API is on every plan now, the free one included** — it used to be a Custom-plan
|
|
37
|
+
feature — and the types follow the three things that came with opening it.
|
|
38
|
+
|
|
39
|
+
- `PlanData["apps"]`: how many apps the plan allows (1 on Free, 2 on Basic, 5 on Pro, 10 on Custom).
|
|
40
|
+
Each app is a `client_id` with a secret, so each one is a key to the whole API. Unlike accounts,
|
|
41
|
+
users or storage it is **not** split between organizations — an app belongs to the client.
|
|
42
|
+
- Three error codes, and the two ranges that were hiding them: **545** (the plan's API rate limit,
|
|
43
|
+
which arrives as a `429` with `Retry-After`) and **546** (the account's email must be verified to
|
|
44
|
+
create an app) are `AuthError`, so that range now runs to 546; **1308** (no more apps fit in this
|
|
45
|
+
plan) is a `PlanLimitError`, so that one runs to 1308.
|
|
46
|
+
|
|
7
47
|
## [0.5.0] - 2026-09-02
|
|
8
48
|
|
|
9
49
|
**Threads is the twelfth network**, and the types now know it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: planvortex
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Official Python client for the PlanVortex API
|
|
5
5
|
Project-URL: Homepage, https://planvortex.com/developers
|
|
6
6
|
Project-URL: Documentation, https://taliasoftworks.github.io/PlanVortexPython/
|
|
@@ -242,15 +242,15 @@ memorising numbers:
|
|
|
242
242
|
|
|
243
243
|
| Codes | Family | Exception |
|
|
244
244
|
|---|---|---|
|
|
245
|
-
| 500-
|
|
245
|
+
| 500-546 | `auth` | `AuthError` |
|
|
246
246
|
| 601-612 | `user` | `UserError` |
|
|
247
247
|
| 700-715 | `account` | `AccountError` |
|
|
248
248
|
| 800-810 | `file` | `FileError` |
|
|
249
|
-
| 900-
|
|
249
|
+
| 900-979 | `publication` | `PublicationError` |
|
|
250
250
|
| 1000-1003 | `general` | `PlanVortexError` |
|
|
251
251
|
| 1100-1111 | `organization` | `OrganizationError` |
|
|
252
252
|
| 1200-1207 | `role` | `PlanVortexError` |
|
|
253
|
-
| 1300-
|
|
253
|
+
| 1300-1308, 1400-1408 | `plan_limit` | `PlanLimitError` |
|
|
254
254
|
| 1500-1512 | `messaging` | `MessagingError` |
|
|
255
255
|
| 1600-1601 | `contact` | `ContactError` |
|
|
256
256
|
| 1900-1906 | `payment` | `PlanVortexError` |
|
|
@@ -212,15 +212,15 @@ memorising numbers:
|
|
|
212
212
|
|
|
213
213
|
| Codes | Family | Exception |
|
|
214
214
|
|---|---|---|
|
|
215
|
-
| 500-
|
|
215
|
+
| 500-546 | `auth` | `AuthError` |
|
|
216
216
|
| 601-612 | `user` | `UserError` |
|
|
217
217
|
| 700-715 | `account` | `AccountError` |
|
|
218
218
|
| 800-810 | `file` | `FileError` |
|
|
219
|
-
| 900-
|
|
219
|
+
| 900-979 | `publication` | `PublicationError` |
|
|
220
220
|
| 1000-1003 | `general` | `PlanVortexError` |
|
|
221
221
|
| 1100-1111 | `organization` | `OrganizationError` |
|
|
222
222
|
| 1200-1207 | `role` | `PlanVortexError` |
|
|
223
|
-
| 1300-
|
|
223
|
+
| 1300-1308, 1400-1408 | `plan_limit` | `PlanLimitError` |
|
|
224
224
|
| 1500-1512 | `messaging` | `MessagingError` |
|
|
225
225
|
| 1600-1601 | `contact` | `ContactError` |
|
|
226
226
|
| 1900-1906 | `payment` | `PlanVortexError` |
|
|
@@ -2449,7 +2449,6 @@
|
|
|
2449
2449
|
"name": "Client X",
|
|
2450
2450
|
"actual_plan": {
|
|
2451
2451
|
"accounts": 4,
|
|
2452
|
-
"publications": 150,
|
|
2453
2452
|
"users": 1,
|
|
2454
2453
|
"space": 0.25,
|
|
2455
2454
|
"integrations": 1,
|
|
@@ -2625,7 +2624,6 @@
|
|
|
2625
2624
|
"value": {
|
|
2626
2625
|
"actual_plan": {
|
|
2627
2626
|
"accounts": 8,
|
|
2628
|
-
"publications": 300,
|
|
2629
2627
|
"users": 3,
|
|
2630
2628
|
"space": 1,
|
|
2631
2629
|
"integrations": 3,
|
|
@@ -6024,7 +6022,6 @@
|
|
|
6024
6022
|
"value": {
|
|
6025
6023
|
"actual_plan": {
|
|
6026
6024
|
"accounts": 8,
|
|
6027
|
-
"publications": 300,
|
|
6028
6025
|
"users": 3,
|
|
6029
6026
|
"space": 1,
|
|
6030
6027
|
"integrations": 3,
|
|
@@ -6343,7 +6340,6 @@
|
|
|
6343
6340
|
"name": "Client X - Spain",
|
|
6344
6341
|
"actual_plan": {
|
|
6345
6342
|
"accounts": 3,
|
|
6346
|
-
"publications": 100,
|
|
6347
6343
|
"users": 1,
|
|
6348
6344
|
"space": 0.25,
|
|
6349
6345
|
"integrations": 0,
|
|
@@ -10702,7 +10698,7 @@
|
|
|
10702
10698
|
"actual_use": {
|
|
10703
10699
|
"allOf": [
|
|
10704
10700
|
{
|
|
10705
|
-
"$ref": "#/components/schemas/
|
|
10701
|
+
"$ref": "#/components/schemas/PlanUseData"
|
|
10706
10702
|
}
|
|
10707
10703
|
],
|
|
10708
10704
|
"description": "Current consumption across every organization of the client. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent this calendar month."
|
|
@@ -12079,7 +12075,7 @@
|
|
|
12079
12075
|
"actual_use": {
|
|
12080
12076
|
"allOf": [
|
|
12081
12077
|
{
|
|
12082
|
-
"$ref": "#/components/schemas/
|
|
12078
|
+
"$ref": "#/components/schemas/PlanUseData"
|
|
12083
12079
|
}
|
|
12084
12080
|
],
|
|
12085
12081
|
"description": "What the organization and its children are consuming right now."
|
|
@@ -12265,7 +12261,7 @@
|
|
|
12265
12261
|
},
|
|
12266
12262
|
"code": {
|
|
12267
12263
|
"type": "integer",
|
|
12268
|
-
"description": "PlanVortex error code. Ranges: 500-
|
|
12264
|
+
"description": "PlanVortex error code. Ranges: 500-546 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-979 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1308 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations."
|
|
12269
12265
|
},
|
|
12270
12266
|
"data": {
|
|
12271
12267
|
"additionalProperties": true,
|
|
@@ -12943,7 +12939,7 @@
|
|
|
12943
12939
|
"actual_use": {
|
|
12944
12940
|
"allOf": [
|
|
12945
12941
|
{
|
|
12946
|
-
"$ref": "#/components/schemas/
|
|
12942
|
+
"$ref": "#/components/schemas/PlanUseData"
|
|
12947
12943
|
}
|
|
12948
12944
|
],
|
|
12949
12945
|
"description": "Current consumption. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent in the current calendar month; the rest is what exists right now."
|
|
@@ -13385,7 +13381,6 @@
|
|
|
13385
13381
|
"description": "The resources a plan grants. On a client it is what was contracted; on an organization, the slice of it that was assigned. The sum across all the organizations of a client can never exceed what the client has contracted.",
|
|
13386
13382
|
"required": [
|
|
13387
13383
|
"accounts",
|
|
13388
|
-
"publications",
|
|
13389
13384
|
"users",
|
|
13390
13385
|
"space",
|
|
13391
13386
|
"integrations"
|
|
@@ -13395,10 +13390,6 @@
|
|
|
13395
13390
|
"type": "integer",
|
|
13396
13391
|
"description": "Social accounts that may be connected."
|
|
13397
13392
|
},
|
|
13398
|
-
"publications": {
|
|
13399
|
-
"type": "integer",
|
|
13400
|
-
"description": "Publications that may be sent per month."
|
|
13401
|
-
},
|
|
13402
13393
|
"users": {
|
|
13403
13394
|
"type": "integer",
|
|
13404
13395
|
"description": "Users with access."
|
|
@@ -13409,7 +13400,11 @@
|
|
|
13409
13400
|
},
|
|
13410
13401
|
"integrations": {
|
|
13411
13402
|
"type": "integer",
|
|
13412
|
-
"description": "Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it
|
|
13403
|
+
"description": "Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it has its own allowance in `apps`."
|
|
13404
|
+
},
|
|
13405
|
+
"apps": {
|
|
13406
|
+
"type": "integer",
|
|
13407
|
+
"description": "Apps this plan allows: 1 on Free, 2 on Basic, 5 on Pro, 10 on Custom. Each one is a `client_id` with a secret, so each one is a key to the whole public API — which every plan has, Free included. Unlike accounts, users or storage this is NOT split between organizations: an app belongs to the client. On a use payload it is how many exist right now."
|
|
13413
13408
|
},
|
|
13414
13409
|
"twitter_credits": {
|
|
13415
13410
|
"type": "integer",
|
|
@@ -13433,6 +13428,21 @@
|
|
|
13433
13428
|
}
|
|
13434
13429
|
}
|
|
13435
13430
|
},
|
|
13431
|
+
"PlanUseData": {
|
|
13432
|
+
"type": "object",
|
|
13433
|
+
"description": "What is being consumed right now. Same shape as PlanData plus `publications`, which is counted but neither charged nor split between organizations.",
|
|
13434
|
+
"allOf": [
|
|
13435
|
+
{
|
|
13436
|
+
"$ref": "#/components/schemas/PlanData"
|
|
13437
|
+
}
|
|
13438
|
+
],
|
|
13439
|
+
"properties": {
|
|
13440
|
+
"publications": {
|
|
13441
|
+
"type": "integer",
|
|
13442
|
+
"description": "Publications created in the current calendar month. A METRIC, not a quota: publications are unlimited on every plan, so there is no limit to compare it against. What throttles publishing is rate, not plan — see the per-hour and per-network daily caps."
|
|
13443
|
+
}
|
|
13444
|
+
}
|
|
13445
|
+
},
|
|
13436
13446
|
"ProductsProduct": {
|
|
13437
13447
|
"allOf": [
|
|
13438
13448
|
{
|
|
@@ -9,7 +9,7 @@ name = "planvortex"
|
|
|
9
9
|
# entonces `project.version` desapareceria del pyproject y el paso del workflow de release que
|
|
10
10
|
# compara el tag con la version —que lo lee con `tomllib`— dejaria de encontrarla. Se quedan las dos
|
|
11
11
|
# y las ata `tests/test_packaging.py`, que falla si se separan.
|
|
12
|
-
version = "0.
|
|
12
|
+
version = "0.6.0"
|
|
13
13
|
description = "Official Python client for the PlanVortex API"
|
|
14
14
|
readme = "README.md"
|
|
15
15
|
requires-python = ">=3.10"
|
|
@@ -27,15 +27,22 @@ class ErrorRange(NamedTuple):
|
|
|
27
27
|
# Los mismos 16 rangos que publica `swagger/common.json`, `PLANVORTEX_ERROR_RANGES` del paquete de
|
|
28
28
|
# Node y el apendice A del roadmap. SI SE TOCA UN RANGO, SE TOCAN LOS CINCO SITIOS.
|
|
29
29
|
PLANVORTEX_ERROR_RANGES: tuple[ErrorRange, ...] = (
|
|
30
|
-
|
|
30
|
+
# Hasta 546, no 544: la API publica se abrio a todos los planes y trajo dos codigos nuevos
|
|
31
|
+
# —545 (ritmo por plan, que sale 429) y 546 (correo sin verificar al crear una app)—.
|
|
32
|
+
ErrorRange(500, 546, "auth"),
|
|
31
33
|
ErrorRange(601, 612, "user"),
|
|
32
34
|
ErrorRange(700, 715, "account"),
|
|
33
35
|
ErrorRange(800, 810, "file"),
|
|
34
|
-
|
|
36
|
+
# El techo sube con el catalogo del servidor, y subir tarde no da un error: da un consejo
|
|
37
|
+
# equivocado. Los codigos de Bluesky, Discord, Telegram y Threads (961-977) y los dos frenos
|
|
38
|
+
# de ritmo de la fase de publicaciones ilimitadas (978, 979) nacieron por encima de 960 y
|
|
39
|
+
# caian fuera de toda familia.
|
|
40
|
+
ErrorRange(900, 979, "publication"),
|
|
35
41
|
ErrorRange(1000, 1003, "general"),
|
|
36
42
|
ErrorRange(1100, 1111, "organization"),
|
|
37
43
|
ErrorRange(1200, 1207, "role"),
|
|
38
|
-
|
|
44
|
+
# 1308 es el tope de APPS del plan, que nacio al quitar el candado de precio de la API.
|
|
45
|
+
ErrorRange(1300, 1308, "plan_limit"),
|
|
39
46
|
ErrorRange(1400, 1408, "plan_limit"),
|
|
40
47
|
ErrorRange(1500, 1512, "messaging"),
|
|
41
48
|
ErrorRange(1600, 1601, "contact"),
|
|
@@ -101,7 +108,7 @@ class PlanVortexError(Exception):
|
|
|
101
108
|
|
|
102
109
|
|
|
103
110
|
class AuthError(PlanVortexError):
|
|
104
|
-
"""500-
|
|
111
|
+
"""500-546 — tokens, client apps, permissions. Includes 501 and 522, the expired-token ones."""
|
|
105
112
|
|
|
106
113
|
|
|
107
114
|
class UserError(PlanVortexError):
|
|
@@ -117,7 +124,9 @@ class FileError(PlanVortexError):
|
|
|
117
124
|
|
|
118
125
|
|
|
119
126
|
class PublicationError(PlanVortexError):
|
|
120
|
-
"""900-
|
|
127
|
+
"""900-979 — publications, including per-network limits (characters, images, duration) and the
|
|
128
|
+
two rate brakes: 978 (publishing too fast on this account) and 979 (that network's daily cap).
|
|
129
|
+
"""
|
|
121
130
|
|
|
122
131
|
|
|
123
132
|
class OrganizationError(PlanVortexError):
|
|
@@ -125,7 +134,7 @@ class OrganizationError(PlanVortexError):
|
|
|
125
134
|
|
|
126
135
|
|
|
127
136
|
class PlanLimitError(PlanVortexError):
|
|
128
|
-
"""1300-
|
|
137
|
+
"""1300-1308 and 1400-1408 — the plan's quota, on the client or on the organization.
|
|
129
138
|
|
|
130
139
|
This is the error an integrator actually wants to tell apart: it is not fixed by retrying, it is
|
|
131
140
|
fixed by changing plan. That is why both ranges share a class.
|
|
@@ -1072,7 +1072,7 @@ class Error1(TypedDict):
|
|
|
1072
1072
|
"""
|
|
1073
1073
|
code: int
|
|
1074
1074
|
"""
|
|
1075
|
-
PlanVortex error code. Ranges: 500-
|
|
1075
|
+
PlanVortex error code. Ranges: 500-546 auth, tokens and client apps · 601-612 user · 700-715 social accounts · 800-810 files · 900-979 publications · 1000-1003 general · 1100-1111 organizations · 1200-1207 roles · 1300-1308 client plan · 1400-1408 organization plan · 1500-1512 messaging · 1600-1601 contacts · 1900-1906 payments · 2000-2099 products · 2100-2199 AI plans · 2200-2299 integrations.
|
|
1076
1076
|
"""
|
|
1077
1077
|
data: NotRequired[dict[str, Any]]
|
|
1078
1078
|
"""
|
|
@@ -1553,10 +1553,6 @@ class PlanData(TypedDict):
|
|
|
1553
1553
|
"""
|
|
1554
1554
|
Social accounts that may be connected.
|
|
1555
1555
|
"""
|
|
1556
|
-
publications: int
|
|
1557
|
-
"""
|
|
1558
|
-
Publications that may be sent per month.
|
|
1559
|
-
"""
|
|
1560
1556
|
users: int
|
|
1561
1557
|
"""
|
|
1562
1558
|
Users with access.
|
|
@@ -1567,7 +1563,11 @@ class PlanData(TypedDict):
|
|
|
1567
1563
|
"""
|
|
1568
1564
|
integrations: int
|
|
1569
1565
|
"""
|
|
1570
|
-
Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it
|
|
1566
|
+
Connections to a third-party tool material is pulled from (Google Drive, an RSS feed). Not the same thing as an app: an app is API access, and it has its own allowance in `apps`.
|
|
1567
|
+
"""
|
|
1568
|
+
apps: NotRequired[int]
|
|
1569
|
+
"""
|
|
1570
|
+
Apps this plan allows: 1 on Free, 2 on Basic, 5 on Pro, 10 on Custom. Each one is a `client_id` with a secret, so each one is a key to the whole public API — which every plan has, Free included. Unlike accounts, users or storage this is NOT split between organizations: an app belongs to the client. On a use payload it is how many exist right now.
|
|
1571
1571
|
"""
|
|
1572
1572
|
twitter_credits: NotRequired[int]
|
|
1573
1573
|
"""
|
|
@@ -1591,6 +1591,17 @@ class PlanData(TypedDict):
|
|
|
1591
1591
|
"""
|
|
1592
1592
|
|
|
1593
1593
|
|
|
1594
|
+
class PlanUseData(PlanData):
|
|
1595
|
+
"""
|
|
1596
|
+
What is being consumed right now. Same shape as PlanData plus `publications`, which is counted but neither charged nor split between organizations.
|
|
1597
|
+
"""
|
|
1598
|
+
|
|
1599
|
+
publications: NotRequired[int]
|
|
1600
|
+
"""
|
|
1601
|
+
Publications created in the current calendar month. A METRIC, not a quota: publications are unlimited on every plan, so there is no limit to compare it against. What throttles publishing is rate, not plan — see the per-hour and per-network daily caps.
|
|
1602
|
+
"""
|
|
1603
|
+
|
|
1604
|
+
|
|
1594
1605
|
class ProductsProductCatalog(TypedDict):
|
|
1595
1606
|
"""
|
|
1596
1607
|
A Meta commerce catalogue, as the network returns it.
|
|
@@ -2399,7 +2410,7 @@ class DashboardDashboardPublicationRef(TypedDict):
|
|
|
2399
2410
|
|
|
2400
2411
|
|
|
2401
2412
|
class DashboardPlanUse(TypedDict):
|
|
2402
|
-
actual_use:
|
|
2413
|
+
actual_use: PlanUseData
|
|
2403
2414
|
"""
|
|
2404
2415
|
What the organization and its children are consuming right now.
|
|
2405
2416
|
"""
|
|
@@ -2641,7 +2652,7 @@ class Organization(TypedDict):
|
|
|
2641
2652
|
"""
|
|
2642
2653
|
The slice of the client's plan assigned to this organization. **Absent when nothing was assigned**, and then the organization shares whatever its nearest parent with a plan has — or, failing that, the client's unassigned remainder. Ask `GET /organizations/{id_organization}/limits` for the effective numbers instead of reading this.
|
|
2643
2654
|
"""
|
|
2644
|
-
actual_use: NotRequired[
|
|
2655
|
+
actual_use: NotRequired[PlanUseData]
|
|
2645
2656
|
"""
|
|
2646
2657
|
Current consumption. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent in the current calendar month; the rest is what exists right now.
|
|
2647
2658
|
"""
|
|
@@ -2957,7 +2968,7 @@ class ClientsClient(TypedDict):
|
|
|
2957
2968
|
"""
|
|
2958
2969
|
Read-only view of the client's own AI provider configuration (BYOK). Written via `PUT /clients/{id_client}/ai-settings`. API keys are stored encrypted and are NEVER returned: each scope only exposes provider, model and has_api_key.
|
|
2959
2970
|
"""
|
|
2960
|
-
actual_use: NotRequired[
|
|
2971
|
+
actual_use: NotRequired[PlanUseData]
|
|
2961
2972
|
"""
|
|
2962
2973
|
Current consumption across every organization of the client. **Only present with `getUse=true`.** `twitter_credits` and `ai_credits` are what has been spent this calendar month.
|
|
2963
2974
|
"""
|
|
@@ -31,6 +31,7 @@ from planvortex._generated.models import (
|
|
|
31
31
|
DashboardPublicationsSummary,
|
|
32
32
|
Organization,
|
|
33
33
|
PlanData,
|
|
34
|
+
PlanUseData,
|
|
34
35
|
Publication,
|
|
35
36
|
Upload,
|
|
36
37
|
)
|
|
@@ -118,7 +119,7 @@ class OrganizationUse(TypedDict):
|
|
|
118
119
|
with ``get_use=True``.
|
|
119
120
|
"""
|
|
120
121
|
|
|
121
|
-
actual_use: NotRequired[
|
|
122
|
+
actual_use: NotRequired[PlanUseData]
|
|
122
123
|
actual_asigned: NotRequired[PlanData]
|
|
123
124
|
|
|
124
125
|
|
|
@@ -12,7 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
import platform
|
|
14
14
|
|
|
15
|
-
VERSION = "0.
|
|
15
|
+
VERSION = "0.6.0"
|
|
16
16
|
|
|
17
17
|
# Sin barra final, y con el `/v1.0.0` dentro: la version de la API es parte de la ruta, no una
|
|
18
18
|
# cabecera, asi que una `base_url` sin ella da 404 en todo.
|
|
@@ -289,6 +289,18 @@ CAREFUL WITH THE SUM: what is assigned to a client's organizations can never exc
|
|
|
289
289
|
has contracted — asking for more comes back as a 1400-1408.
|
|
290
290
|
"""
|
|
291
291
|
|
|
292
|
+
PlanUseData: TypeAlias = _models.PlanUseData
|
|
293
|
+
"""What is being CONSUMED right now: a :data:`PlanData` plus ``publications``.
|
|
294
|
+
|
|
295
|
+
``publications`` is a METRIC, not a quota — publications are unlimited on every plan — so there is
|
|
296
|
+
no ceiling to compare it against: do not draw it as a bar. What throttles whoever publishes too
|
|
297
|
+
much is RATE (a per-hour cap per account, and the per-network daily caps in ``GET /social_limits``),
|
|
298
|
+
not the plan.
|
|
299
|
+
|
|
300
|
+
Not to be confused with :data:`PlanUse`, which is the dashboard's whole block: consumption, what
|
|
301
|
+
was handed down, and the limits together.
|
|
302
|
+
"""
|
|
303
|
+
|
|
292
304
|
Organization: TypeAlias = _models.Organization
|
|
293
305
|
"""An organization: the container of accounts, publications and files. They nest.
|
|
294
306
|
|
|
@@ -1115,6 +1127,7 @@ __all__ = [
|
|
|
1115
1127
|
"PersistentMenu",
|
|
1116
1128
|
"PlanData",
|
|
1117
1129
|
"PlanUse",
|
|
1130
|
+
"PlanUseData",
|
|
1118
1131
|
"PlannerTemplate",
|
|
1119
1132
|
"PlannerTemplateField",
|
|
1120
1133
|
"PlannerTemplateFieldType",
|
|
@@ -305,7 +305,19 @@ def test_cada_rango_del_catalogo_cae_en_su_clase() -> None:
|
|
|
305
305
|
"""
|
|
306
306
|
assert error_class_for_code(500) is error_class_for_code(544)
|
|
307
307
|
assert error_class_for_code(900).__name__ == "PublicationError"
|
|
308
|
+
# El rango llega hasta 979, no hasta 960: por encima del 960 estan las cuatro ultimas redes
|
|
309
|
+
# (Bluesky, Discord, Telegram y Threads) y los DOS FRENOS DE RITMO que sustituyeron al cupo
|
|
310
|
+
# mensual de publicaciones. Fuera de familia, el 978 llegaba como un error generico y quien lo
|
|
311
|
+
# recibia no tenia forma de saber que la respuesta es esperar.
|
|
312
|
+
assert error_class_for_code(975).__name__ == "PublicationError"
|
|
313
|
+
assert error_class_for_code(978).__name__ == "PublicationError"
|
|
314
|
+
assert error_class_for_code(979).__name__ == "PublicationError"
|
|
308
315
|
assert error_class_for_code(1300).__name__ == "PlanLimitError"
|
|
316
|
+
# 1308 es el tope de APPS del plan, y 545/546 los dos codigos que trajo abrir la API publica
|
|
317
|
+
# a todos los planes. Los tres nacieron por encima del techo que tenian sus rangos.
|
|
318
|
+
assert error_class_for_code(1308).__name__ == "PlanLimitError"
|
|
319
|
+
assert error_class_for_code(545).__name__ == "AuthError"
|
|
320
|
+
assert error_class_for_code(546).__name__ == "AuthError"
|
|
309
321
|
assert error_class_for_code(1408).__name__ == "PlanLimitError"
|
|
310
322
|
assert error_class_for_code(2299).__name__ == "IntegrationError"
|
|
311
323
|
# Los siete de las plantillas del planificador. No hay nada que registrar para que caigan aqui:
|
|
@@ -92,7 +92,7 @@ def _camino(
|
|
|
92
92
|
url=f"{BASE_URL}/clients/cli1/organizations?limit=1",
|
|
93
93
|
json={"organizations": [ORGANIZACION], "total": 1},
|
|
94
94
|
)
|
|
95
|
-
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "
|
|
95
|
+
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
|
|
96
96
|
httpx_mock.add_response(
|
|
97
97
|
url=f"{ORG}?getUse=true",
|
|
98
98
|
json={
|
|
@@ -77,7 +77,7 @@ def _camino(httpx_mock: HTTPXMock, *, publicacion: dict[str, Any] | None = None)
|
|
|
77
77
|
url=f"{BASE_URL}/clients/cli1/organizations?limit=1",
|
|
78
78
|
json={"organizations": [ORGANIZACION], "total": 1},
|
|
79
79
|
)
|
|
80
|
-
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "
|
|
80
|
+
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
|
|
81
81
|
httpx_mock.add_response(
|
|
82
82
|
url=f"{ORG}?getUse=true",
|
|
83
83
|
json={"organization": {**ORGANIZACION, "actual_use": {"accounts": 1, "publications": 12}}},
|
|
@@ -73,9 +73,9 @@ def test_las_hijas_se_listan_se_iteran_y_se_crean(cliente: ClienteDePrueba, http
|
|
|
73
73
|
|
|
74
74
|
def test_limits_llega_sin_sobre(cliente: ClienteDePrueba, httpx_mock: HTTPXMock) -> None:
|
|
75
75
|
"""La cascada ya resuelta, y en crudo. Es lo que hay que mirar, no `actual_plan`."""
|
|
76
|
-
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "
|
|
76
|
+
httpx_mock.add_response(url=f"{ORG}/limits", json={"accounts": 5, "users": 3})
|
|
77
77
|
|
|
78
|
-
assert cliente.esperar(cliente.pv.organizations.limits("org1")) == {"accounts": 5, "
|
|
78
|
+
assert cliente.esperar(cliente.pv.organizations.limits("org1")) == {"accounts": 5, "users": 3}
|
|
79
79
|
assert ruta(unica(httpx_mock)) == "/organizations/org1/limits"
|
|
80
80
|
|
|
81
81
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|