Commit 8d9cb03
authored
Add Tumblr error handling, response validation, and typed SocialPostingVertical (#83)
Closes #22
Closes #45
Closes #62
The Tumblr integration returned raw JSON, and we were not validating
what Tumblr sent back, and any API failure would surface as a cryptic
Python error instead of something useful. This brings it up to the same
standard as Strava.
- Added `TumblrAPIError` so Tumblr failures are identifiable and have
useful messages. Before, a 401 from Tumblr would just be a generic
`HTTPError`, and a malformed response would crash with something like
`AttributeError: 'NoneType' object has no attribute 'get'`. Now we get
`TumblrAPIError: Tumblr API error: ...` with the status code and a
description of what went wrong.
- Both `user/info` and `user/dashboard` responses are now checked for
the expected structure before anything tries to read from them. If
Tumblr returns 200 but the payload is weird (missing keys, wrong types),
it raises `TumblrAPIError` right away instead of silently producing
garbage data.
- Added `parse_social_posting_vertical` that turns a raw Tumblr post
into a `SocialPostingVertical` model — mapping things like post ID, URL,
timestamp, tags, note count, text blocks, and media URLs into the
standard schema. Same approach Strava already uses.
- `fetch_social_posting_vertical` now returns `(parsed_verticals,
raw_posts)` instead of just a list of dicts. Matches Strava's pattern so
consumers get both structured data and raw JSON.
- The updated testsuite covers error wrapping, validation, and parsing.
Pagination #8 is out of scope here.
---
These new features don't change anything in the demoing approach.
```bash
from pardner.services.tumblr import TumblrTransferService
from core.models import DonatedPost, ServiceAccount
import json
# Get the most recent donation
sa = ServiceAccount.objects.order_by('-completed_donation_at').first()
post = DonatedPost.objects.filter(service_account=sa).first()
# Parse through the new parser
svc = TumblrTransferService('x', 'x', 'http://localhost')
vertical = svc.parse_social_posting_vertical(post.raw_data)
# See the structured output
print(json.dumps(vertical.model_dump(), indent=2, default=str))
```
<details>
<summary>Structured output example</summary>
```json
{
"pardner_object_id": "e6971577c78b49c6963cabdf6cebf529",
"service_object_id": "810233790391877632",
"creator_user_id": "t:iE-yd_-VKiQ4tRh4kkzLeg",
"data_owner_id": "",
"service": "Tumblr",
"vertical_name": "social_posting",
"created_at": "2026-03-05 08:25:56",
"url": "https://angelswouldnthelpyou.tumblr.com/post/810233790391877632/david-lynch-and-sheryl-lee-twin-peaks-fire-walk",
"abstract": "David Lynch and Sheryl Lee \nTwin Peaks Fire Walk With Me 1992",
"associated_media": [
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s1280x1920/ae567fc16c7d32e5359142ead8c14c8c43225c70.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s640x960/82e3c61df944a7acdbee8da566ad5001411912b8.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s540x810/f1a2d05bade2e5d0a9e6cc17050fac2475214b49.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s500x750/d6fbe2d7c7d4746aab542717af43e8aeaabd84dc.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s400x600/9528b99630774c979fd9afe77f41a09f27eef657.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s250x400/c8b9576de6f9f79b52c5fcf5952a42115b8d2031.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s100x200/62762543123ff8cad1d4467159d8d73ad827ed05.jpg"
},
{
"media_type": "image",
"url": "https://64.media.tumblr.com/d020bdb6ba1834edc05117b2f7ef3f84/37d16fd99f65962b-60/s75x75_c1/1e79db2eb2321ea7f1bdcdd309634206d38819b4.jpg"
}
],
"interaction_count": 16,
"keywords": [
"twin peaks fire walk with me",
"david lynch",
"sheryl lee",
"laura palmer",
"twin peaks",
"cinema"
],
"shared_content": [],
"status": "public",
"text": "David Lynch and Sheryl Lee \n\nTwin Peaks Fire Walk With Me 1992",
"title": null
}
```
</details>
> The parsed `SocialPostingVertical` models are not being saved to the
database right now. This is intentional.
>
> 1. The `DonatedPost` model only has a `raw_data` JSONField. There's no
column or table for structured vertical data
> 2. I wanted to preserve the site's access to raw Tumblr JSON and
follow the Strava pattern of returning both
> 3. The site currently does `_, raw_posts = ...` — the `_` discards the
parsed verticals and only `raw_posts` gets stored1 parent 2ec9a6b commit 8d9cb03
5 files changed
Lines changed: 396 additions & 39 deletions
File tree
- src/pardner
- services
- tests/test_transfer_services
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
189 | | - | |
| 189 | + | |
190 | 190 | | |
191 | 191 | | |
192 | 192 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
1 | 3 | | |
2 | 4 | | |
3 | 5 | | |
| |||
26 | 28 | | |
27 | 29 | | |
28 | 30 | | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| 3 | + | |
3 | 4 | | |
4 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
5 | 8 | | |
6 | 9 | | |
| 10 | + | |
7 | 11 | | |
8 | 12 | | |
9 | 13 | | |
| |||
63 | 67 | | |
64 | 68 | | |
65 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
66 | 230 | | |
67 | 231 | | |
68 | 232 | | |
| |||
75 | 239 | | |
76 | 240 | | |
77 | 241 | | |
78 | | - | |
79 | | - | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
80 | 246 | | |
81 | 247 | | |
82 | 248 | | |
83 | | - | |
84 | | - | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
85 | 263 | | |
86 | 264 | | |
87 | 265 | | |
| |||
95 | 273 | | |
96 | 274 | | |
97 | 275 | | |
98 | | - | |
| 276 | + | |
99 | 277 | | |
100 | 278 | | |
101 | 279 | | |
102 | 280 | | |
103 | 281 | | |
104 | 282 | | |
105 | 283 | | |
106 | | - | |
| 284 | + | |
107 | 285 | | |
108 | | - | |
| 286 | + | |
109 | 287 | | |
110 | 288 | | |
111 | 289 | | |
| |||
115 | 293 | | |
116 | 294 | | |
117 | 295 | | |
118 | | - | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
119 | 300 | | |
120 | | - | |
121 | | - | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
122 | 304 | | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
130 | 309 | | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
0 commit comments