[object Object]

← back to Secrets Manager

auto-save: 2026-07-21T14:38:01 (3 files) — add-blog-images.py fix-pr-naturals-schema.py publish-pr-naturals-blog.py

48f095f66b23cfdad5aa9027658d9e716471a794 · 2026-07-21 14:38:02 -0700 · Steve Abrams

Files touched

Diff

commit 48f095f66b23cfdad5aa9027658d9e716471a794
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jul 21 14:38:02 2026 -0700

    auto-save: 2026-07-21T14:38:01 (3 files) — add-blog-images.py fix-pr-naturals-schema.py publish-pr-naturals-blog.py
---
 add-blog-images.py          | 82 +++++++++++++++++++++++++++++++++++++++++++++
 fix-pr-naturals-schema.py   | 36 ++++++++++++++++++++
 publish-pr-naturals-blog.py | 51 ++++++++++++++++++++++++++++
 3 files changed, 169 insertions(+)

diff --git a/add-blog-images.py b/add-blog-images.py
new file mode 100644
index 0000000..6556598
--- /dev/null
+++ b/add-blog-images.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+"""
+add-blog-images.py — bring both AEO blog posts to >=5 images, using ONLY safe
+cdn.shopify.com re-hosted images (pure-hash filenames, no source-domain leak).
+  • Phillipe Romano Naturals (568551243827): 0 -> 6 images distributed through the body,
+    AND appends the FAQPage JSON-LD (was missing).
+  • Creatures of the Night (561218191411): 3 -> 5 via a "Pairs beautifully with" pair.
+Run:  ! python3 ~/Projects/secrets-manager/add-blog-images.py
+"""
+import re, json, urllib.request
+
+env = open('/Users/macstudio3/Projects/secrets-manager/.env').read()
+m = re.search(r'^SHOPIFY_CONTENT_TOKEN=(.+)$', env, re.M) or re.search(r'^SHOPIFY_DRAFT_TOKEN=(.+)$', env, re.M)
+tok = m.group(1).strip().strip('"').strip("'")
+S = "designer-laboratory-sandbox.myshopify.com"; A = "2024-10"; B = 9243689072
+
+def get(p):
+    r = urllib.request.Request(f"https://{S}/admin/api/{A}/{p}", headers={"X-Shopify-Access-Token": tok})
+    return json.load(urllib.request.urlopen(r, timeout=30))
+def put(p, payload):
+    r = urllib.request.Request(f"https://{S}/admin/api/{A}/{p}", data=json.dumps(payload).encode(),
+        method="PUT", headers={"X-Shopify-Access-Token": tok, "Content-Type": "application/json"})
+    return json.load(urllib.request.urlopen(r, timeout=30))
+
+def fig(url, alt):
+    return (f'<p><img src="{url}" alt="{alt}" '
+            f'style="max-width:100%;height:auto;border-radius:2px;" loading="lazy"></p>\n')
+
+# ---- Phillipe Romano Naturals: 6 safe images, anchored before known H2s + tail ----
+NAT = [
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/f72b285ffbd93d23f73a6465ebc73f26.jpg",
+  "Phillipe Romano grasscloth wallcovering in a natural woven texture"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/f4f84be58dc46b61384a5065ac13eb4f.jpg",
+  "Sisal grasscloth wallcovering in a light-brown horizontal weave"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/906e95d8479a94d541005dc00dd49cce.jpg",
+  "Mica wallcovering with a shimmering chevron herringbone texture"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/6c8118b37707729c59d3bdeed3bcc675.jpg",
+  "Painted abaca bark natural wallcovering"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/d754f160d4b0a5ae5176463564496545.jpg",
+  "Multi-color grasscloth wallcovering in a coastal palette"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/72648fc7609950488f8fd8179705e048.jpg",
+  "Fine sisal wallcovering in a soft green tone"),
+]
+a = get(f"blogs/{B}/articles/568551243827.json")['article']; body = a['body_html'] or ''
+if '<img' not in body:
+    for anchor, (u, alt) in zip(
+        ['<h2>What natural materials are in the collection?</h2>',
+         '<h2>What rooms suit natural wallcoverings?</h2>',
+         '<h2>How do you order and install Phillipe Romano Naturals?</h2>',
+         '<h2>Frequently Asked Questions</h2>'], NAT[:4]):
+        body = body.replace(anchor, fig(u, alt) + anchor, 1)
+    tail = fig(*NAT[4]) + fig(*NAT[5])
+    body = body.replace('<h2>Frequently Asked Questions</h2>', tail + '<h2>Frequently Asked Questions</h2>', 1)
+if 'FAQPage' not in body:
+    faq = json.load(open('/tmp/pr-naturals-faq.json'))['faq']
+    schema = {"@context": "https://schema.org", "@type": "FAQPage",
+              "mainEntity": [{"@type": "Question", "name": q["q"],
+                              "acceptedAnswer": {"@type": "Answer", "text": q["a"]}} for q in faq]}
+    body += '\n<script type="application/ld+json">\n' + json.dumps(schema, ensure_ascii=False, indent=2) + '\n</script>'
+r = put(f"blogs/{B}/articles/568551243827.json", {"article": {"id": 568551243827, "body_html": body}})['article']
+nb = r['body_html'] or ''
+print("PR Naturals  imgs:", len(re.findall(r'<img', nb)), "| faqschema:", 'FAQPage' in nb)
+
+# ---- Creatures of the Night: add a "Pairs beautifully with" pair (3 -> 5) ----
+PAIR = [
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/df17d36a880da0ecf941d29c92a6c7f1.jpg",
+  "Paperweave grasscloth in natural fibers on a black ground — a coordinating dark texture"),
+ ("https://cdn.shopify.com/s/files/1/0015/4117/7456/products/1debc67bc748298d8c4d8856a0e2330e.jpg",
+  "Charcoal textured wallcovering that pairs with a dark, dramatic scheme"),
+]
+a2 = get(f"blogs/{B}/articles/561218191411.json")['article']; body2 = a2['body_html'] or ''
+if 'Pairs beautifully with' not in body2:
+    block = ('<h2>Pairs beautifully with</h2>\n'
+             '<p>To carry the nocturnal mood across a room, pair Creatures of the Night with a '
+             'deep, tactile ground:</p>\n' + fig(*PAIR[0]) + fig(*PAIR[1]))
+    if '<script type="application/ld+json">' in body2:
+        body2 = body2.replace('<script type="application/ld+json">', block + '\n<script type="application/ld+json">', 1)
+    else:
+        body2 += '\n' + block
+r2 = put(f"blogs/{B}/articles/561218191411.json", {"article": {"id": 561218191411, "body_html": body2}})['article']
+nb2 = r2['body_html'] or ''
+print("Creatures    imgs:", len(re.findall(r'<img', nb2)), "| faqschema:", 'FAQPage' in nb2)
diff --git a/fix-pr-naturals-schema.py b/fix-pr-naturals-schema.py
new file mode 100644
index 0000000..4dae3af
--- /dev/null
+++ b/fix-pr-naturals-schema.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+"""
+fix-pr-naturals-schema.py — append the FAQPage JSON-LD to the already-published
+"Phillipe Romano Naturals" article (id 568551243827). The create-script sent only the
+<body>, and the schema had been placed in the <head>, so the live article has the visible
+FAQ but not the structured data. This PUTs the schema onto the end of the body.
+Run:  ! python3 ~/Projects/secrets-manager/fix-pr-naturals-schema.py
+"""
+import re, json, urllib.request
+
+env = open('/Users/macstudio3/Projects/secrets-manager/.env').read()
+m = re.search(r'^SHOPIFY_CONTENT_TOKEN=(.+)$', env, re.M) or re.search(r'^SHOPIFY_DRAFT_TOKEN=(.+)$', env, re.M)
+tok = m.group(1).strip().strip('"').strip("'")
+STORE = "designer-laboratory-sandbox.myshopify.com"; API = "2024-10"; BLOG = 9243689072; ART = 568551243827
+
+def get(p):
+    r = urllib.request.Request(f"https://{STORE}/admin/api/{API}/{p}", headers={"X-Shopify-Access-Token": tok})
+    return json.load(urllib.request.urlopen(r, timeout=30))
+def put(p, payload):
+    r = urllib.request.Request(f"https://{STORE}/admin/api/{API}/{p}", data=json.dumps(payload).encode(),
+        method="PUT", headers={"X-Shopify-Access-Token": tok, "Content-Type": "application/json"})
+    return json.load(urllib.request.urlopen(r, timeout=30))
+
+faq = json.load(open('/tmp/pr-naturals-faq.json'))['faq']
+schema = {"@context": "https://schema.org", "@type": "FAQPage",
+          "mainEntity": [{"@type": "Question", "name": q["q"],
+                          "acceptedAnswer": {"@type": "Answer", "text": q["a"]}} for q in faq]}
+schema_html = '\n<script type="application/ld+json">\n' + json.dumps(schema, ensure_ascii=False, indent=2) + '\n</script>'
+
+art = get(f"blogs/{BLOG}/articles/{ART}.json")['article']
+body = art['body_html'] or ''
+if 'FAQPage' in body:
+    print("schema already present — no change"); raise SystemExit(0)
+res = put(f"blogs/{BLOG}/articles/{ART}.json", {"article": {"id": ART, "body_html": body + schema_html}})['article']
+nb = res['body_html'] or ''
+print("FIXED OK  body chars:", len(nb), "| faq schema now:", 'FAQPage' in nb)
diff --git a/publish-pr-naturals-blog.py b/publish-pr-naturals-blog.py
new file mode 100644
index 0000000..b1f7ebd
--- /dev/null
+++ b/publish-pr-naturals-blog.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+"""
+publish-pr-naturals-blog.py — CREATE a new DW blog article:
+"Phillipe Romano Naturals: Grasscloth, Sisal & Paperweave Wallcoverings".
+
+Body is taken from the audited draft (/tmp/dw-drafts/pr-naturals-DRAFT.html, scored 96/100).
+The <h1> is stripped (the Shopify theme renders the title as the H1); the FAQPage JSON-LD is kept.
+No feature image is set — the catalog images point to the source domain (private-label leak),
+so a hero should be chosen from the Shopify media library on review.
+Creates the article PUBLISHED. Run yourself:
+    ! python3 ~/Projects/secrets-manager/publish-pr-naturals-blog.py
+"""
+import re, json, urllib.request
+
+env = open('/Users/macstudio3/Projects/secrets-manager/.env').read()
+m = re.search(r'^SHOPIFY_CONTENT_TOKEN=(.+)$', env, re.M) or re.search(r'^SHOPIFY_DRAFT_TOKEN=(.+)$', env, re.M)
+tok = m.group(1).strip().strip('"').strip("'")
+STORE = "designer-laboratory-sandbox.myshopify.com"; API = "2024-10"; BLOG = 9243689072
+
+# --- pull the audited draft body, strip <head> and the <h1> ---
+draft = open('/tmp/dw-drafts/pr-naturals-DRAFT.html').read()
+body = re.search(r'<body>(.*)</body>', draft, re.S).group(1)
+body = re.sub(r'<h1>.*?</h1>\s*', '', body, count=1, flags=re.S)  # theme renders the title as H1
+body = body.strip()
+
+TITLE = "Phillipe Romano Naturals: Grasscloth, Sisal & Paperweave Wallcoverings"
+SEO_TITLE = "Phillipe Romano Naturals: Grasscloth, Sisal & Paperweave Wallcoverings | Designer Wallcoverings"
+SEO_DESC = "Phillipe Romano Naturals: 53 patterns of grasscloth, sisal, paperweave, jute and mica wallcovering in coastal colorways. Order up to 10 free samples."
+
+payload = {"article": {
+    "title": TITLE,
+    "author": "Designer Wallcoverings",
+    "tags": "Phillipe Romano, Naturals, Grasscloth, Sisal, Paperweave, Jute, Mica, Wallcovering",
+    "body_html": body,
+    "published": True,
+    "metafields": [
+        {"namespace": "global", "key": "title_tag", "value": SEO_TITLE, "type": "single_line_text_field"},
+        {"namespace": "global", "key": "description_tag", "value": SEO_DESC, "type": "single_line_text_field"},
+    ],
+}}
+
+data = json.dumps(payload).encode()
+req = urllib.request.Request(f"https://{STORE}/admin/api/{API}/blogs/{BLOG}/articles.json",
+    data=data, method="POST",
+    headers={"X-Shopify-Access-Token": tok, "Content-Type": "application/json"})
+res = json.load(urllib.request.urlopen(req, timeout=30))['article']
+print("CREATED OK  article id:", res['id'])
+print("handle    :", res.get('handle'))
+print("published :", res.get('published_at'))
+print("live URL  : https://designerwallcoverings.com/blogs/news/" + str(res.get('handle')))
+print("body chars:", len(res.get('body_html') or ''), "| faq schema:", 'FAQPage' in (res.get('body_html') or ''))

← 95b77ef auto-save: 2026-07-21T14:07:54 (3 files) — alias-content-tok  ·  back to Secrets Manager  ·  Greenland leak cleanup: archive-duplicates script + mirror-o e6f3f31 →