Skip to content

Phase 1: Create Tag objects for all 10 controlled vocabulary types - #40

Merged
mekarpeles merged 2 commits into
Open-Book-Genome-Project:mainfrom
Chisomnwa:phase-a/tag-creation
Jul 6, 2026
Merged

Phase 1: Create Tag objects for all 10 controlled vocabulary types#40
mekarpeles merged 2 commits into
Open-Book-Genome-Project:mainfrom
Chisomnwa:phase-a/tag-creation

Conversation

@Chisomnwa

@Chisomnwa Chisomnwa commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Part of the Phase 1 work for #14.

What this does

Creates OL Tag objects for every entry in all 10 controlled vocabulary.json files and writes the returned OLxxxT keys back. This is a one-time job — after this PR, the vocabulary files permanently store the Tag key for each term, which the migration script (Phase B) will use to populate work.genres.

In other words, this PR:

  • Adds scripts/create_tags.py — reads entries from all 10 controlled vocabulary.json files, creates a Tag via POST /api/new for each, captures the auto-generated OLxxxT key, and writes it back to the file
  • Adds a key field to every entry in every controlled vocabulary.json (198 entries total)

Files changed

File Change
scripts/create_tags.py New — batch tag creation script
tag_types/audience/vocabulary.json Added key field to all 9 entries (OL94T–OL102T)
tag_types/content_features/vocabulary.json Added key field to all 17 entries (OL103T–OL119T)
tag_types/content_formats/vocabulary.json Added key field to all 24 entries (OL120T–OL143T)
tag_types/content_warnings/vocabulary.json Added key field to all 18 entries (OL144T–OL161T)
tag_types/genres/vocabulary.json Added key field to all 21 entries (OL162T–OL182T)
tag_types/literary_form/vocabulary.json Added key field to both entries (OL183T–OL184T)
tag_types/literary_themes/vocabulary.json Added key field to all 29 entries (OL185T–OL213T)
tag_types/literary_tropes/vocabulary.json Added key field to all 28 entries (OL214T–OL240T, OL291T)
tag_types/moods/vocabulary.json Added key field to all 26 entries (OL241T–OL266T)
tag_types/subgenres/vocabulary.json Added key field to all 24 entries (OL267T–OL290T)

Verification

  • 198 Tag objects created across all 10 controlled types
  • Each Tag verified by fetching its .json endpoint
  • create_tags.py skips entries that already have a key (safe to re-run)
  • One tag (Time Skip) hit a ModSecurity rule on name — created as TIME_SKIP then renamed via PUT

Notes for future contributors

  • Virtual environment: If you use a venv, you must pip install -e /path/to/openlibrary-client inside it, not just system-wide.
  • save_many vs /api/new: save_many updates existing docs (needs a key). /api/new creates new ones (auto-generates key).
  • Tag structure: Always use "type": {"key": "/type/tag"} (dict), not "type": "/type/tag" (string).
  • ModSecurity: Some tag names may trigger rules. Bypass by using a temporary name, then update via PUT.
  • Content-Type: Always set ol.session.headers.update({"Content-Type": "application/json"}).

Reviewers

@mekarpeles

Comment thread scripts/create_tags.py Outdated
@mekarpeles

Copy link
Copy Markdown
Collaborator

Looks good overall! A questions about comments in our tag generation script

@Chisomnwa

Chisomnwa commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Hi @mekarpeles, thank you for the review! I have uncommented the main parts of the script. Everything is okay now.

@Chisomnwa Chisomnwa changed the title feat: create Tag objects for all 10 controlled vocabulary types Phase A: Create Tag objects for all 10 controlled vocabulary types Jul 4, 2026
Comment thread scripts/create_tags.py
Comment on lines +35 to +58
def create_tag(name, tag_type, slug, description):
"""
Send a single tag to Open Library's /api/new endpoint.
Returns the auto-generated key (e.g. /tags/OL123T) or None on failure.
"""
payload = json.dumps([{
"type": {"key": "/type/tag"},
"name": name,
"tag_type": tag_type,
"slugs": [slug],
"tag_description": description,
}])
headers = {
"Content-Type": "application/json",
"Opt": '"http://openlibrary.org/dev/docs/api"; ns=42',
"42-comment": f"create tag: {slug} ({tag_type})",
}
r = ol.session.post("https://openlibrary.org/api/new", data=payload, headers=headers)
if r.status_code == 200:
# /api/new returns a list of keys, one per input doc
return r.json()[0]
else:
print(f" Failed ({r.status_code}) for {name}: {r.text[:200]}")
return None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should ultimately be DRY'd (Do Not Repeat ourselves) up into the openlibrary-client as there's a lot of boiler plate here and this is a common task that will be done over again. Not a blocker for this PR.

Comment thread scripts/create_tags.py
print(f" Wrote keys to {os.path.relpath(vf, os.path.join(TAG_TYPES_DIR, '..'))}")

# Small delay so we don't hammer the API
time.sleep(0.5)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timeouts based on ms are fragile as the load on the server is ever-changing. This would be better as a configurable parameter.

Comment thread scripts/create_tags.py
if modified:
with open(vf, "w") as f:
json.dump(data, f, indent=2)
print(f" Wrote keys to {os.path.relpath(vf, os.path.join(TAG_TYPES_DIR, '..'))}")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally logging would be preferable to print statements -- this would give us control over where the IO goes (e.g. into sentry or some other aggregation program) and lets us also log different types like errors v. info.

@mekarpeles
mekarpeles merged commit c441633 into Open-Book-Genome-Project:main Jul 6, 2026
1 check passed
@Chisomnwa Chisomnwa changed the title Phase A: Create Tag objects for all 10 controlled vocabulary types Phase 1: Create Tag objects for all 10 controlled vocabulary types Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants