Add Annotator Hands
Creates 11 annotator hand profiles and attributes dissertation references to specific hands.
1"""Add annotator hands table and tag dissertation references with hand attributions. 2 3Based on James Russell's PhD thesis analysis of six annotated copies of the HP. 4""" 5 6import sqlite3 7from pathlib import Path 8 9BASE_DIR = Path(__file__).resolve().parent.parent 10DB_PATH = BASE_DIR / "db" / "hp.db" 11 12 13HANDS_SCHEMA = """ 14CREATE TABLE IF NOT EXISTS annotator_hands ( 15 id INTEGER PRIMARY KEY, 16 hand_label TEXT NOT NULL, 17 manuscript_shelfmark TEXT NOT NULL, 18 attribution TEXT, 19 language TEXT, 20 ink_color TEXT, 21 date_range TEXT, 22 school TEXT, 23 interests TEXT, 24 description TEXT, 25 is_alchemist BOOLEAN DEFAULT 0, 26 chapter_num INTEGER, 27 UNIQUE(hand_label, manuscript_shelfmark) 28); 29 30-- Add hand_id column to dissertation_refs if not exists 31""" 32 33ALTER_REFS = """ 34ALTER TABLE dissertation_refs ADD COLUMN hand_id INTEGER REFERENCES annotator_hands(id); 35""" 36 37 38# All annotator hands identified by Russell across the six copies 39HANDS = [ 40 # BL C.60.o.12 (Ch. 6) — 1545 edition 41 { 42 'hand_label': 'A', 43 'manuscript_shelfmark': 'C.60.o.12', 44 'attribution': 'Ben Jonson', 45 'language': 'English, Latin', 46 'ink_color': None, 47 'date_range': 'c.1600-1637', 48 'school': None, 49 'interests': 'stagecraft, linguistic technicalities, inventio, visual stage design', 50 'description': ( 51 'First hand in the BL copy, convincingly attributed to Ben Jonson. ' 52 'Composed a selective summary foregrounding large-scale visual monuments ' 53 'and vehicles suitable for stage design. Extensive grammatical parsing ' 54 'through underlining (simple, double, bracket). Interested in architectural ' 55 'vocabulary and natural forms (snails, acanthus, seashells) as column models. ' 56 'Mining the HP for ideas for masque stagecraft (inventio).' 57 ), 58 'is_alchemist': False, 59 'chapter_num': 6, 60 }, 61 { 62 'hand_label': 'B', 63 'manuscript_shelfmark': 'C.60.o.12', 64 'attribution': 'Anonymous (possibly Royal Society circle)', 65 'language': 'Latin, English', 66 'ink_color': None, 67 'date_range': 'post-1641, late 17th century', 68 'school': "Jean d'Espagnet (Enchiridion Physicae Restitutae)", 69 'interests': 'alchemical allegory, Master Mercury, ideographic vocabulary, prisca sapientia', 70 'description': ( 71 "Anonymous second hand in the BL copy. Inferred alchemical formulae beneath " 72 "the narrative surface. Labelled passages and woodcuts with alchemical signs " 73 "(gold, silver, mercury, Venus, Jupiter). Follower of Jean d'Espagnet's " 74 "emphasis on mercury as catalytic bearer of solar energy. Used extensive " 75 "alchemical ideograms with Latin inflections (e.g. sun-symbol + '-ra' for " 76 "'aurata'). Consistency with Newton's Keynes MSS vocabulary suggests possible " 77 "connection to Royal Society or Cambridge. Wrote programmatic summary on " 78 "flyleaf verso: 'verus sensus intentionis huius libri est 3um' — threefold " 79 "meaning centering on 'Master Mercury'. Notes decline in Book II but alchemical " 80 "symbols persist. Also an Englishman ('frogg green')." 81 ), 82 'is_alchemist': True, 83 'chapter_num': 6, 84 }, 85 86 # Buffalo (Ch. 7) — 1499 edition 87 { 88 'hand_label': 'A', 89 'manuscript_shelfmark': 'Buffalo RBR', 90 'attribution': 'Anonymous (possibly Jesuit, St. Omer)', 91 'language': 'French', 92 'ink_color': 'black', 93 'date_range': 'pre-1739', 94 'school': None, 95 'interests': 'narrative summary', 96 'description': ( 97 'First French hand in black ink. Wrote summary of HP narrative on page ' 98 'following dedicatory letter. If the book was at St. Omer before 1739 ' 99 '(as Jesuit ex libris suggests), this may be a Jesuit annotator.' 100 ), 101 'is_alchemist': False, 102 'chapter_num': 7, 103 }, 104 { 105 'hand_label': 'B', 106 'manuscript_shelfmark': 'Buffalo RBR', 107 'attribution': 'Anonymous (possibly Jesuit, St. Omer)', 108 'language': 'French', 109 'ink_color': 'light brown', 110 'date_range': 'pre-1739', 111 'school': None, 112 'interests': 'Greek etymologies, Hebrew roots, Plinian sources', 113 'description': ( 114 'Second French hand in light brown ink. Primary interest in etymologies ' 115 'of Greek terms. Only annotator in the study to identify Hebrew roots. ' 116 'Also traces Plinian sources (Naturalis historia) for wines, laws, ' 117 'architecture. Similar ductus to Hand A suggests common origin, possibly ' 118 'another Jesuit of St. Omer.' 119 ), 120 'is_alchemist': False, 121 'chapter_num': 7, 122 }, 123 { 124 'hand_label': 'C', 125 'manuscript_shelfmark': 'Buffalo RBR', 126 'attribution': 'Anonymous', 127 'language': 'Latin', 128 'ink_color': 'brown', 129 'date_range': None, 130 'school': None, 131 'interests': 'narrative signposting', 132 'description': 'Latin hand in brown ink. Primarily interested in signposting the narrative, summarising major passages.', 133 'is_alchemist': False, 134 'chapter_num': 7, 135 }, 136 { 137 'hand_label': 'D', 138 'manuscript_shelfmark': 'Buffalo RBR', 139 'attribution': 'Anonymous', 140 'language': 'Italian, Latin', 141 'ink_color': None, 142 'date_range': None, 143 'school': None, 144 'interests': 'architecture', 145 'description': ( 146 'Italian hand responding to Hand A. In one instance crosses off A\'s comments ' 147 'and offers abbreviated summary. Primary interest in architecture. Switches to ' 148 'Latin when labelling architectural features.' 149 ), 150 'is_alchemist': False, 151 'chapter_num': 7, 152 }, 153 { 154 'hand_label': 'E', 155 'manuscript_shelfmark': 'Buffalo RBR', 156 'attribution': 'Anonymous', 157 'language': 'Latin', 158 'ink_color': None, 159 'date_range': 'late 17th-early 18th century', 160 'school': 'pseudo-Geber (Jabir ibn Hayyan)', 161 'interests': 'alchemical allegory, Sol/Luna, sulphur, chemical wedding', 162 'description': ( 163 "Final distinguishable hand. Applies alchemical reading. Overwrites Hand D " 164 "(latest hand). Follower of pseudo-Geber school emphasizing sulphur and " 165 "Sol/Luna pairings. Unlike BL alchemist, uses minimal ideograms — writes " 166 "out element names in full (e.g. 'sulphur naturae'). Labels passages with " 167 "alchemical stages. Praises Geber's 'ingenium subtile'. Reads male-female " 168 "pairings as alchemical weddings. Identifies chess match results as " 169 "silver/gold transmutation." 170 ), 171 'is_alchemist': True, 172 'chapter_num': 7, 173 }, 174 175 # Modena (Ch. 4) — 1499 edition 176 { 177 'hand_label': 'Primary', 178 'manuscript_shelfmark': 'Modena (Panini)', 179 'attribution': 'Benedetto Giovio', 180 'language': 'Italian, Latin', 181 'ink_color': None, 182 'date_range': 'early 16th century', 183 'school': None, 184 'interests': 'Plinian natural history, etymology, botany, music, medicine', 185 'description': ( 186 'Primary hand attributed to Benedetto Giovio (1471-1545). Plinian ' 187 'encyclopedic reading: extracts etymologies, botanical identifications, ' 188 'medical terms, musical modes, mineralogy, and literary cross-references ' 189 '(Boccaccio, Erasmus, Homer). Over 150 sources identified by Stichel. ' 190 'Also modified woodcuts with coloring and shading.' 191 ), 192 'is_alchemist': False, 193 'chapter_num': 4, 194 }, 195 196 # Como (Ch. 5) — 1499 edition 197 { 198 'hand_label': 'Primary', 199 'manuscript_shelfmark': 'INCUN A.5.13', 200 'attribution': 'Benedetto Giovio', 201 'language': 'Italian, Latin', 202 'ink_color': None, 203 'date_range': 'early 16th century', 204 'school': None, 205 'interests': 'Plinian botany, botanical vocabulary extraction', 206 'description': ( 207 'Also attributed to Benedetto Giovio. Same hand as Modena copy. ' 208 'Focuses specifically on botanical vocabulary extraction, treating ' 209 'the HP as a Plinian encyclopedia. Identifies plants with Plinian ' 210 'cross-references.' 211 ), 212 'is_alchemist': False, 213 'chapter_num': 5, 214 }, 215 216 # Vatican (Ch. 8) — 1499 edition 217 { 218 'hand_label': 'Primary', 219 'manuscript_shelfmark': 'Inc.Stam.Chig.II.610', 220 'attribution': 'Pope Alexander VII (Fabio Chigi)', 221 'language': 'Latin, Italian', 222 'ink_color': None, 223 'date_range': 'c.1630s-1667', 224 'school': None, 225 'interests': 'acutezze (verbal wit), architecture, Rome topography', 226 'description': ( 227 'Attributed to Fabio Chigi, later Pope Alexander VII (1599-1667). ' 228 'Combed text for examples of verbal wit (acutezze). Compared ' 229 "Poliphilo's architectural journeys with his own passages through " 230 "Rome. Interest in orthographic diagrams and textual emendation." 231 ), 232 'is_alchemist': False, 233 'chapter_num': 8, 234 }, 235 236 # Siena (Ch. 9) — 1499 edition 237 { 238 'hand_label': 'Primary', 239 'manuscript_shelfmark': 'O.III.38', 240 'attribution': 'Anonymous', 241 'language': 'Latin, Italian', 242 'ink_color': None, 243 'date_range': 'early modern', 244 'school': None, 245 'interests': 'calligraphic annotation, textual emendation, educative marginalia', 246 'description': ( 247 'Painstaking calligraphic marginalia. Multiple hands (A-D) including ' 248 "ownership marks by Francisci Fini and Friedrich Schaller. Hand C wrote " 249 "'Ad lectorem' note. Hand D engaged with dating and literary analysis." 250 ), 251 'is_alchemist': False, 252 'chapter_num': 9, 253 }, 254] 255 256 257# Rules for attributing dissertation references to hands 258# Based on thesis content analysis: signature refs that Russell explicitly 259# discusses in connection with specific hands. 260HAND_ATTRIBUTION_RULES = { 261 # BL C.60.o.12 — Ch. 6 262 # Hand A (Jonson): stagecraft, grammatical parsing, architectural vocabulary 263 ('C.60.o.12', 'A'): { 264 'signatures': { 265 'a4r', # underlining techniques (p.147) 266 'e7r', # hatching technique for shadow (p.150) 267 'b4v', # Corinthian order identification (p.151) 268 'm7v', # Corinthian column base (p.152) 269 'd4r', # snail metaphor (p.152) 270 't7v', # seashell form (p.152) 271 }, 272 'pages': range(123, 156), 273 }, 274 # Hand B (Alchemist): ideograms, mercury, elemental signs 275 ('C.60.o.12', 'B'): { 276 'signatures': { 277 'b6v', # Elephant and Obelisk ideograms (p.157) 278 'E8v', # Venus symbol replacing name (p.157) 279 'y7r', # Fons Heptagonis labeled (p.136) 280 'e1r', # Panton Tokadi / vials labeled (p.163-164) 281 'd8v', # mercury/vas references (p.164) 282 'a1r', # dawn scene alchemical annotation (p.165) 283 'a3r', # alchemical context (p.155) 284 'k5r', # alchemical labeling (p.131) 285 }, 286 'pages': range(155, 170), 287 }, 288 # Buffalo — Ch. 7 289 # Hand E (Alchemist): Geberian school, sulphur, Sol/Luna 290 ('Buffalo RBR', 'E'): { 291 'signatures': { 292 'b7r', # Geberian schema, ingenium subtile (p.187) 293 'c6v', # Venus/Bacchus/Ceres (p.187-188) 294 'h1r', # chess match/hermaphrodite (p.189-190) 295 'b5r', # alchemical context (p.190) 296 }, 297 'pages': range(185, 200), 298 }, 299 # Hand B (Etymologist): Plinian sources, etymologies 300 ('Buffalo RBR', 'B'): { 301 'signatures': { 302 'b8v', # Hebrew roots (p.172) 303 'g8r', # Plinian sources for laws (p.176) 304 'h7r', # serpent wreath correction (p.183) 305 }, 306 'pages': range(172, 185), 307 }, 308 # Hand D (Architect) 309 ('Buffalo RBR', 'D'): { 310 'signatures': { 311 'b1r', # architectural labels (p.173, 181) 312 }, 313 }, 314 # Hand A (French narrative) 315 ('Buffalo RBR', 'A'): { 316 'signatures': { 317 'A2r', # Polia name origin (p.179) 318 }, 319 }, 320} 321 322 323def main(): 324 conn = sqlite3.connect(DB_PATH) 325 cur = conn.cursor() 326 327 # Create hands table 328 print("Creating annotator_hands table...") 329 cur.executescript(HANDS_SCHEMA) 330 331 # Add hand_id column if not exists 332 try: 333 cur.execute(ALTER_REFS) 334 print(" Added hand_id column to dissertation_refs") 335 except sqlite3.OperationalError as e: 336 if 'duplicate column' in str(e).lower(): 337 print(" hand_id column already exists") 338 else: 339 raise 340 341 # Insert hands 342 print("Inserting annotator hands...") 343 for h in HANDS: 344 cur.execute( 345 """INSERT OR REPLACE INTO annotator_hands 346 (hand_label, manuscript_shelfmark, attribution, language, 347 ink_color, date_range, school, interests, description, 348 is_alchemist, chapter_num) 349 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", 350 (h['hand_label'], h['manuscript_shelfmark'], h['attribution'], 351 h['language'], h['ink_color'], h['date_range'], h['school'], 352 h['interests'], h['description'], h['is_alchemist'], 353 h['chapter_num']) 354 ) 355 conn.commit() 356 print(f" Inserted {len(HANDS)} hands") 357 358 # Attribute refs to hands based on rules 359 print("\nAttributing dissertation references to hands...") 360 attributed = 0 361 for (shelfmark, hand_label), rule in HAND_ATTRIBUTION_RULES.items(): 362 # Get hand_id 363 cur.execute( 364 "SELECT id FROM annotator_hands WHERE manuscript_shelfmark=? AND hand_label=?", 365 (shelfmark, hand_label) 366 ) 367 row = cur.fetchone() 368 if not row: 369 print(f" WARNING: hand not found: {shelfmark} / {hand_label}") 370 continue 371 hand_id = row[0] 372 373 sigs = rule.get('signatures', set()) 374 pages = rule.get('pages') 375 376 for sig in sigs: 377 # Find matching refs 378 if pages: 379 cur.execute( 380 """UPDATE dissertation_refs SET hand_id = ? 381 WHERE signature_ref = ? AND manuscript_shelfmark = ? 382 AND thesis_page BETWEEN ? AND ?""", 383 (hand_id, sig, shelfmark, pages.start, pages.stop) 384 ) 385 else: 386 cur.execute( 387 """UPDATE dissertation_refs SET hand_id = ? 388 WHERE signature_ref = ? AND manuscript_shelfmark = ?""", 389 (hand_id, sig, shelfmark) 390 ) 391 attributed += cur.rowcount 392 393 # Also attribute by chapter for copies with single known hands 394 single_hand_chapters = { 395 4: ('Modena (Panini)', 'Primary'), 396 5: ('INCUN A.5.13', 'Primary'), 397 8: ('Inc.Stam.Chig.II.610', 'Primary'), 398 9: ('O.III.38', 'Primary'), 399 } 400 for ch, (shelfmark, hand_label) in single_hand_chapters.items(): 401 cur.execute( 402 "SELECT id FROM annotator_hands WHERE manuscript_shelfmark=? AND hand_label=?", 403 (shelfmark, hand_label) 404 ) 405 row = cur.fetchone() 406 if not row: 407 continue 408 hand_id = row[0] 409 cur.execute( 410 "UPDATE dissertation_refs SET hand_id = ? WHERE chapter_num = ? AND hand_id IS NULL", 411 (hand_id, ch) 412 ) 413 attributed += cur.rowcount 414 415 conn.commit() 416 print(f" Attributed {attributed} references to hands") 417 418 # Summary 419 print("\nHand attribution summary:") 420 cur.execute(""" 421 SELECT h.hand_label, h.manuscript_shelfmark, h.attribution, h.is_alchemist, 422 COUNT(r.id) as ref_count 423 FROM annotator_hands h 424 LEFT JOIN dissertation_refs r ON r.hand_id = h.id 425 GROUP BY h.id 426 ORDER BY h.chapter_num, h.hand_label 427 """) 428 for row in cur.fetchall(): 429 label, shelf, attr, alch, count = row 430 alch_tag = " [ALCHEMIST]" if alch else "" 431 print(f" {shelf} Hand {label} ({attr}){alch_tag}: {count} refs") 432 433 # Show unattributed 434 cur.execute("SELECT COUNT(*) FROM dissertation_refs WHERE hand_id IS NULL") 435 unattr = cur.fetchone()[0] 436 print(f"\n Unattributed references: {unattr}") 437 438 conn.close() 439 print("\nDone.") 440 441 442if __name__ == "__main__": 443 main()