[{"data":1,"prerenderedAt":3681},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel":3674},{"id":4,"title":5,"body":6,"dateModified":3651,"datePublished":3651,"description":3652,"extension":3653,"faq":3654,"meta":3665,"navigation":251,"path":3666,"seo":3667,"slug":3670,"stem":3671,"type":3672,"__hash__":3673},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel\u002Findex.md","Add a Table of Contents Sheet with Hyperlinks in Excel",{"type":7,"value":8,"toc":3639},"minimark",[9,19,186,191,220,223,568,574,578,585,755,758,988,995,1017,1021,1024,1968,1988,1999,2003,2079,2082,2382,2397,2400,2450,2454,2462,2544,2719,2726,2730,2863,2867,2874,2881,3440,3454,3464,3528,3531,3535,3541,3545,3561,3570,3582,3588,3594,3598,3635],[10,11,12,13,18],"p",{},"A workbook with twelve sheets has a navigation problem: the tab bar shows six of them, the names are truncated, and a reader looking for \"the regional breakdown\" clicks through four sheets to find it. An index sheet fixes it — a front page listing every sheet with a one-line description and a clickable link. openpyxl builds one in a loop, and the two things worth getting right are the internal-link syntax and generating the list rather than typing it. This guide covers both. It extends ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002F","Building Multi-Sheet Excel Dashboards",".",[20,21,30,31,30,35,30,39,30,46,30,55,30,64,30,71,30,78,30,84,30,89,30,94,30,99,30,102,30,105,30,109,30,112,30,115,30,119,30,122,30,127,30,132,30,137,30,143,30,147,30,152,30,157,30,161,30,164,30,169,30,173,30,176,30,178,30,182],"svg",{"viewBox":22,"role":23,"ariaLabel":24,"ariaLabelledBy":25,"xmlns":28,"style":29},"0 0 800 254","img","An index sheet listing four sheets with descriptions, row counts and clickable links, and each target sheet carrying a back-link to the index.",[26,27],"toc-t","toc-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:800px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[32,33,34],"title",{"id":26},"An index sheet and the back-links that complete it",[36,37,38],"desc",{"id":27},"On the left an index sheet lists four sheets, each row carrying the sheet name as a blue underlined link, a short description and a row count. Arrows show a reader jumping from the index to the Regional sheet. On the right, the Regional sheet carries a back-link in cell A1 reading \"back to index\", so navigation works in both directions rather than requiring the reader to find the tab again.",[40,41],"rect",{"x":42,"y":42,"width":43,"height":44,"fill":45},"0","800","254","#ffffff",[40,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":45,"stroke":53,"style":54},"14","24","410","212","12","var(--line,#cdd5e6)","stroke-width:2px",[40,56],{"x":57,"y":58,"width":59,"height":60,"rx":61,"fill":62,"stroke":63,"style":54},"36","42","366","32","5","#ebebfd","var(--brand,#5b5cf0)",[65,66,70],"text",{"x":67,"y":68,"style":69},"219","64","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Contents — Regional Revenue, August 2026",[40,72],{"x":57,"y":73,"width":74,"height":75,"rx":76,"fill":77,"stroke":53},"82","150","26","4","#f0f2f5",[65,79,83],{"x":80,"y":81,"style":82},"111","100","font-size:10px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Summary",[65,85,88],{"x":86,"y":81,"style":87},"200","font-size:9.5px;fill:var(--muted,#5b6780)","headline figures · 6 rows",[40,90],{"x":57,"y":91,"width":74,"height":75,"rx":76,"fill":92,"stroke":93,"style":54},"112","#d9f4f1","var(--teal,#0f9488)",[65,95,98],{"x":80,"y":96,"style":97},"130","font-size:10px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Regional",[65,100,101],{"x":86,"y":96,"style":87},"by region · 24 rows",[40,103],{"x":57,"y":104,"width":74,"height":75,"rx":76,"fill":77,"stroke":53},"142",[65,106,108],{"x":80,"y":107,"style":82},"160","Detail",[65,110,111],{"x":86,"y":107,"style":87},"every order · 1,482 rows",[40,113],{"x":57,"y":114,"width":74,"height":75,"rx":76,"fill":77,"stroke":53},"172",[65,116,118],{"x":80,"y":117,"style":82},"190","Q3 Detail",[65,120,121],{"x":86,"y":117,"style":87},"quoted name · 402 rows",[65,123,126],{"x":67,"y":124,"style":125},"222","font-size:10px;fill:var(--muted,#5b6780);text-anchor:middle","generated from the sheets that exist",[128,129],"path",{"d":130,"fill":131,"stroke":93,"style":54},"M190 125 H 440 V 76 H 470","none",[133,134],"polygon",{"points":135,"fill":136},"478,76 466,70 466,82","#0f9488",[65,138,142],{"x":139,"y":140,"style":141},"330","118","font-size:9px;fill:var(--teal-ink,#0b6157);text-anchor:middle","#'Regional'!A1",[40,144],{"x":145,"y":49,"width":146,"height":51,"rx":52,"fill":45,"stroke":53,"style":54},"486","300",[40,148],{"x":149,"y":150,"width":151,"height":75,"rx":76,"fill":92,"stroke":93,"style":54},"508","60","256",[65,153,156],{"x":154,"y":155,"style":97},"636","78","← back to index",[65,158,160],{"x":154,"y":58,"style":159},"font-size:11px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","the Regional sheet",[40,162],{"x":149,"y":163,"width":151,"height":49,"rx":76,"fill":62,"stroke":63},"96",[65,165,168],{"x":154,"y":166,"style":167},"113","font-size:9.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","region · units · revenue",[40,170],{"x":149,"y":171,"width":151,"height":172,"rx":76,"fill":45,"stroke":53},"124","20",[40,174],{"x":149,"y":175,"width":151,"height":172,"rx":76,"fill":45,"stroke":53},"148",[40,177],{"x":149,"y":114,"width":151,"height":172,"rx":76,"fill":45,"stroke":53},[128,179],{"d":180,"fill":131,"stroke":63,"style":181},"M600 88 V 108 H 440 V 130 H 196","stroke-width:2px;stroke-dasharray:4 4",[133,183],{"points":184,"fill":185},"188,130 200,124 200,136","#5b5cf0",[187,188,190],"h2",{"id":189},"prerequisites","Prerequisites",[192,193,198],"pre",{"className":194,"code":195,"language":196,"meta":197,"style":197},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[199,200,201],"code",{"__ignoreMap":197},[202,203,206,210,214,217],"span",{"class":204,"line":205},"line",1,[202,207,209],{"class":208},"sMTad","pip",[202,211,213],{"class":212},"srMev"," install",[202,215,216],{"class":212}," pandas",[202,218,219],{"class":212}," openpyxl\n",[10,221,222],{},"A multi-sheet workbook to index:",[192,224,228],{"className":225,"code":226,"language":227,"meta":197,"style":197},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nsheets = {\n    \"Summary\": pd.DataFrame({\"metric\": [\"revenue\", \"units\"],\n                             \"value\": [41614.15, 3204]}),\n    \"Regional\": pd.DataFrame({\"region\": [\"North\", \"South\", \"West\", \"East\"],\n                              \"revenue\": [5150.0, 4268.5, 3511.25, 2980.1]}),\n    \"Q3 Detail\": pd.DataFrame({\"order\": range(1, 51),\n                               \"amount\": [12.5 * i for i in range(1, 51)]}),\n    \"_raw\": pd.DataFrame({\"order\": range(1, 501), \"amount\": range(1, 501)}),\n}\n\nwith pd.ExcelWriter(\"dashboard.xlsx\", engine=\"xlsxwriter\") as writer:\n    for name, frame in sheets.items():\n        frame.to_excel(writer, sheet_name=name, index=False)\n","python",[199,229,230,246,253,265,292,312,345,373,404,443,487,493,498,529,543],{"__ignoreMap":197},[202,231,232,236,240,243],{"class":204,"line":205},[202,233,235],{"class":234},"s-kum","import",[202,237,239],{"class":238},"skGVy"," pandas ",[202,241,242],{"class":234},"as",[202,244,245],{"class":238}," pd\n",[202,247,249],{"class":204,"line":248},2,[202,250,252],{"emptyLinePlaceholder":251},true,"\n",[202,254,256,259,262],{"class":204,"line":255},3,[202,257,258],{"class":238},"sheets ",[202,260,261],{"class":234},"=",[202,263,264],{"class":238}," {\n",[202,266,268,271,274,277,280,283,286,289],{"class":204,"line":267},4,[202,269,270],{"class":212},"    \"Summary\"",[202,272,273],{"class":238},": pd.DataFrame({",[202,275,276],{"class":212},"\"metric\"",[202,278,279],{"class":238},": [",[202,281,282],{"class":212},"\"revenue\"",[202,284,285],{"class":238},", ",[202,287,288],{"class":212},"\"units\"",[202,290,291],{"class":238},"],\n",[202,293,295,298,300,304,306,309],{"class":204,"line":294},5,[202,296,297],{"class":212},"                             \"value\"",[202,299,279],{"class":238},[202,301,303],{"class":302},"sP0c6","41614.15",[202,305,285],{"class":238},[202,307,308],{"class":302},"3204",[202,310,311],{"class":238},"]}),\n",[202,313,315,318,320,323,325,328,330,333,335,338,340,343],{"class":204,"line":314},6,[202,316,317],{"class":212},"    \"Regional\"",[202,319,273],{"class":238},[202,321,322],{"class":212},"\"region\"",[202,324,279],{"class":238},[202,326,327],{"class":212},"\"North\"",[202,329,285],{"class":238},[202,331,332],{"class":212},"\"South\"",[202,334,285],{"class":238},[202,336,337],{"class":212},"\"West\"",[202,339,285],{"class":238},[202,341,342],{"class":212},"\"East\"",[202,344,291],{"class":238},[202,346,348,351,353,356,358,361,363,366,368,371],{"class":204,"line":347},7,[202,349,350],{"class":212},"                              \"revenue\"",[202,352,279],{"class":238},[202,354,355],{"class":302},"5150.0",[202,357,285],{"class":238},[202,359,360],{"class":302},"4268.5",[202,362,285],{"class":238},[202,364,365],{"class":302},"3511.25",[202,367,285],{"class":238},[202,369,370],{"class":302},"2980.1",[202,372,311],{"class":238},[202,374,376,379,381,384,387,390,393,396,398,401],{"class":204,"line":375},8,[202,377,378],{"class":212},"    \"Q3 Detail\"",[202,380,273],{"class":238},[202,382,383],{"class":212},"\"order\"",[202,385,386],{"class":238},": ",[202,388,389],{"class":302},"range",[202,391,392],{"class":238},"(",[202,394,395],{"class":302},"1",[202,397,285],{"class":238},[202,399,400],{"class":302},"51",[202,402,403],{"class":238},"),\n",[202,405,407,410,412,415,418,421,424,426,429,432,434,436,438,440],{"class":204,"line":406},9,[202,408,409],{"class":212},"                               \"amount\"",[202,411,279],{"class":238},[202,413,414],{"class":302},"12.5",[202,416,417],{"class":234}," *",[202,419,420],{"class":238}," i ",[202,422,423],{"class":234},"for",[202,425,420],{"class":238},[202,427,428],{"class":234},"in",[202,430,431],{"class":302}," range",[202,433,392],{"class":238},[202,435,395],{"class":302},[202,437,285],{"class":238},[202,439,400],{"class":302},[202,441,442],{"class":238},")]}),\n",[202,444,446,449,451,453,455,457,459,461,463,466,469,472,474,476,478,480,482,484],{"class":204,"line":445},10,[202,447,448],{"class":212},"    \"_raw\"",[202,450,273],{"class":238},[202,452,383],{"class":212},[202,454,386],{"class":238},[202,456,389],{"class":302},[202,458,392],{"class":238},[202,460,395],{"class":302},[202,462,285],{"class":238},[202,464,465],{"class":302},"501",[202,467,468],{"class":238},"), ",[202,470,471],{"class":212},"\"amount\"",[202,473,386],{"class":238},[202,475,389],{"class":302},[202,477,392],{"class":238},[202,479,395],{"class":302},[202,481,285],{"class":238},[202,483,465],{"class":302},[202,485,486],{"class":238},")}),\n",[202,488,490],{"class":204,"line":489},11,[202,491,492],{"class":238},"}\n",[202,494,496],{"class":204,"line":495},12,[202,497,252],{"emptyLinePlaceholder":251},[202,499,501,504,507,510,512,516,518,521,524,526],{"class":204,"line":500},13,[202,502,503],{"class":234},"with",[202,505,506],{"class":238}," pd.ExcelWriter(",[202,508,509],{"class":212},"\"dashboard.xlsx\"",[202,511,285],{"class":238},[202,513,515],{"class":514},"sa561","engine",[202,517,261],{"class":234},[202,519,520],{"class":212},"\"xlsxwriter\"",[202,522,523],{"class":238},") ",[202,525,242],{"class":234},[202,527,528],{"class":238}," writer:\n",[202,530,532,535,538,540],{"class":204,"line":531},14,[202,533,534],{"class":234},"    for",[202,536,537],{"class":238}," name, frame ",[202,539,428],{"class":234},[202,541,542],{"class":238}," sheets.items():\n",[202,544,546,549,552,554,557,560,562,565],{"class":204,"line":545},15,[202,547,548],{"class":238},"        frame.to_excel(writer, ",[202,550,551],{"class":514},"sheet_name",[202,553,261],{"class":234},[202,555,556],{"class":238},"name, ",[202,558,559],{"class":514},"index",[202,561,261],{"class":234},[202,563,564],{"class":302},"False",[202,566,567],{"class":238},")\n",[10,569,570,571,573],{},"Note ",[199,572,118],{}," — a sheet name with a space, which is the case that breaks naive link building.",[187,575,577],{"id":576},"step-1-understand-the-link-syntax","Step 1 — Understand the link syntax",[10,579,580,581,584],{},"An internal hyperlink is a location string starting with ",[199,582,583],{},"#",":",[192,586,588],{"className":225,"code":587,"language":227,"meta":197,"style":197},"from openpyxl import load_workbook\n\nwb = load_workbook(\"dashboard.xlsx\")\nws = wb.create_sheet(\"Contents\", 0)\n\nws[\"A1\"] = \"Summary\"\nws[\"A1\"].hyperlink = \"#Summary!A1\"          # works: no space in the name\n\nws[\"A2\"] = \"Q3 Detail\"\nws[\"A2\"].hyperlink = \"#Q3 Detail!A1\"        # broken: unquoted space\nws[\"A3\"] = \"Q3 Detail\"\nws[\"A3\"].hyperlink = \"#'Q3 Detail'!A1\"      # works: quoted\n\nwb.save(\"dashboard_linked.xlsx\")\n",[199,589,590,603,607,621,640,644,660,678,682,696,712,725,741,745],{"__ignoreMap":197},[202,591,592,595,598,600],{"class":204,"line":205},[202,593,594],{"class":234},"from",[202,596,597],{"class":238}," openpyxl ",[202,599,235],{"class":234},[202,601,602],{"class":238}," load_workbook\n",[202,604,605],{"class":204,"line":248},[202,606,252],{"emptyLinePlaceholder":251},[202,608,609,612,614,617,619],{"class":204,"line":255},[202,610,611],{"class":238},"wb ",[202,613,261],{"class":234},[202,615,616],{"class":238}," load_workbook(",[202,618,509],{"class":212},[202,620,567],{"class":238},[202,622,623,626,628,631,634,636,638],{"class":204,"line":267},[202,624,625],{"class":238},"ws ",[202,627,261],{"class":234},[202,629,630],{"class":238}," wb.create_sheet(",[202,632,633],{"class":212},"\"Contents\"",[202,635,285],{"class":238},[202,637,42],{"class":302},[202,639,567],{"class":238},[202,641,642],{"class":204,"line":294},[202,643,252],{"emptyLinePlaceholder":251},[202,645,646,649,652,655,657],{"class":204,"line":314},[202,647,648],{"class":238},"ws[",[202,650,651],{"class":212},"\"A1\"",[202,653,654],{"class":238},"] ",[202,656,261],{"class":234},[202,658,659],{"class":212}," \"Summary\"\n",[202,661,662,664,666,669,671,674],{"class":204,"line":347},[202,663,648],{"class":238},[202,665,651],{"class":212},[202,667,668],{"class":238},"].hyperlink ",[202,670,261],{"class":234},[202,672,673],{"class":212}," \"#Summary!A1\"",[202,675,677],{"class":676},"s-wDw","          # works: no space in the name\n",[202,679,680],{"class":204,"line":375},[202,681,252],{"emptyLinePlaceholder":251},[202,683,684,686,689,691,693],{"class":204,"line":406},[202,685,648],{"class":238},[202,687,688],{"class":212},"\"A2\"",[202,690,654],{"class":238},[202,692,261],{"class":234},[202,694,695],{"class":212}," \"Q3 Detail\"\n",[202,697,698,700,702,704,706,709],{"class":204,"line":445},[202,699,648],{"class":238},[202,701,688],{"class":212},[202,703,668],{"class":238},[202,705,261],{"class":234},[202,707,708],{"class":212}," \"#Q3 Detail!A1\"",[202,710,711],{"class":676},"        # broken: unquoted space\n",[202,713,714,716,719,721,723],{"class":204,"line":489},[202,715,648],{"class":238},[202,717,718],{"class":212},"\"A3\"",[202,720,654],{"class":238},[202,722,261],{"class":234},[202,724,695],{"class":212},[202,726,727,729,731,733,735,738],{"class":204,"line":495},[202,728,648],{"class":238},[202,730,718],{"class":212},[202,732,668],{"class":238},[202,734,261],{"class":234},[202,736,737],{"class":212}," \"#'Q3 Detail'!A1\"",[202,739,740],{"class":676},"      # works: quoted\n",[202,742,743],{"class":204,"line":500},[202,744,252],{"emptyLinePlaceholder":251},[202,746,747,750,753],{"class":204,"line":531},[202,748,749],{"class":238},"wb.save(",[202,751,752],{"class":212},"\"dashboard_linked.xlsx\"",[202,754,567],{"class":238},[10,756,757],{},"Excel's rule is that a sheet name containing anything other than letters, digits and underscores must be wrapped in single quotes. Rather than remembering when, quote whenever the name is not a plain identifier — and escape any apostrophe inside it by doubling it:",[192,759,761],{"className":225,"code":760,"language":227,"meta":197,"style":197},"import re\n\nPLAIN = re.compile(r\"^[A-Za-z_][A-Za-z0-9_]*$\")\n\ndef sheet_ref(name, cell=\"A1\"):\n    \"\"\"A safe internal-link reference for any sheet name.\"\"\"\n    if PLAIN.match(name):\n        return f\"#{name}!{cell}\"\n    escaped = name.replace(\"'\", \"''\")\n    return f\"#'{escaped}'!{cell}\"\n\nprint(sheet_ref(\"Summary\"))       # #Summary!A1\nprint(sheet_ref(\"Q3 Detail\"))     # #'Q3 Detail'!A1\nprint(sheet_ref(\"Bob's data\"))    # #'Bob''s data'!A1\n",[199,762,763,770,774,817,821,840,845,856,889,909,937,941,958,973],{"__ignoreMap":197},[202,764,765,767],{"class":204,"line":205},[202,766,235],{"class":234},[202,768,769],{"class":238}," re\n",[202,771,772],{"class":204,"line":248},[202,773,252],{"emptyLinePlaceholder":251},[202,775,776,779,782,785,788,791,794,798,801,804,807,810,813,815],{"class":204,"line":255},[202,777,778],{"class":302},"PLAIN",[202,780,781],{"class":234}," =",[202,783,784],{"class":238}," re.compile(",[202,786,787],{"class":234},"r",[202,789,790],{"class":212},"\"",[202,792,793],{"class":302},"^[",[202,795,797],{"class":796},"sSjpA","A-Za-z_",[202,799,800],{"class":302},"][",[202,802,803],{"class":796},"A-Za-z0-9_",[202,805,806],{"class":302},"]",[202,808,809],{"class":234},"*",[202,811,812],{"class":302},"$",[202,814,790],{"class":212},[202,816,567],{"class":238},[202,818,819],{"class":204,"line":267},[202,820,252],{"emptyLinePlaceholder":251},[202,822,823,826,830,833,835,837],{"class":204,"line":294},[202,824,825],{"class":234},"def",[202,827,829],{"class":828},"s_Opv"," sheet_ref",[202,831,832],{"class":238},"(name, cell",[202,834,261],{"class":234},[202,836,651],{"class":212},[202,838,839],{"class":238},"):\n",[202,841,842],{"class":204,"line":314},[202,843,844],{"class":212},"    \"\"\"A safe internal-link reference for any sheet name.\"\"\"\n",[202,846,847,850,853],{"class":204,"line":347},[202,848,849],{"class":234},"    if",[202,851,852],{"class":302}," PLAIN",[202,854,855],{"class":238},".match(name):\n",[202,857,858,861,864,867,870,873,876,879,881,884,886],{"class":204,"line":375},[202,859,860],{"class":234},"        return",[202,862,863],{"class":234}," f",[202,865,866],{"class":212},"\"#",[202,868,869],{"class":796},"{",[202,871,872],{"class":238},"name",[202,874,875],{"class":796},"}",[202,877,878],{"class":212},"!",[202,880,869],{"class":796},[202,882,883],{"class":238},"cell",[202,885,875],{"class":796},[202,887,888],{"class":212},"\"\n",[202,890,891,894,896,899,902,904,907],{"class":204,"line":406},[202,892,893],{"class":238},"    escaped ",[202,895,261],{"class":234},[202,897,898],{"class":238}," name.replace(",[202,900,901],{"class":212},"\"'\"",[202,903,285],{"class":238},[202,905,906],{"class":212},"\"''\"",[202,908,567],{"class":238},[202,910,911,914,916,919,921,924,926,929,931,933,935],{"class":204,"line":445},[202,912,913],{"class":234},"    return",[202,915,863],{"class":234},[202,917,918],{"class":212},"\"#'",[202,920,869],{"class":796},[202,922,923],{"class":238},"escaped",[202,925,875],{"class":796},[202,927,928],{"class":212},"'!",[202,930,869],{"class":796},[202,932,883],{"class":238},[202,934,875],{"class":796},[202,936,888],{"class":212},[202,938,939],{"class":204,"line":489},[202,940,252],{"emptyLinePlaceholder":251},[202,942,943,946,949,952,955],{"class":204,"line":495},[202,944,945],{"class":302},"print",[202,947,948],{"class":238},"(sheet_ref(",[202,950,951],{"class":212},"\"Summary\"",[202,953,954],{"class":238},"))       ",[202,956,957],{"class":676},"# #Summary!A1\n",[202,959,960,962,964,967,970],{"class":204,"line":500},[202,961,945],{"class":302},[202,963,948],{"class":238},[202,965,966],{"class":212},"\"Q3 Detail\"",[202,968,969],{"class":238},"))     ",[202,971,972],{"class":676},"# #'Q3 Detail'!A1\n",[202,974,975,977,979,982,985],{"class":204,"line":531},[202,976,945],{"class":302},[202,978,948],{"class":238},[202,980,981],{"class":212},"\"Bob's data\"",[202,983,984],{"class":238},"))    ",[202,986,987],{"class":676},"# #'Bob''s data'!A1\n",[10,989,990,991,994],{},"Setting ",[199,992,993],{},"hyperlink"," creates the link but leaves the cell looking like ordinary text. Style it too, or readers will not know it is clickable:",[192,996,998],{"className":225,"code":997,"language":227,"meta":197,"style":197},"ws[\"A1\"].style = \"Hyperlink\"          # Excel's built-in style\n",[199,999,1000],{"__ignoreMap":197},[202,1001,1002,1004,1006,1009,1011,1014],{"class":204,"line":205},[202,1003,648],{"class":238},[202,1005,651],{"class":212},[202,1007,1008],{"class":238},"].style ",[202,1010,261],{"class":234},[202,1012,1013],{"class":212}," \"Hyperlink\"",[202,1015,1016],{"class":676},"          # Excel's built-in style\n",[187,1018,1020],{"id":1019},"step-2-generate-the-index","Step 2 — Generate the index",[10,1022,1023],{},"Walking the workbook means the index cannot drift out of date, which a typed list always eventually does:",[192,1025,1027],{"className":225,"code":1026,"language":227,"meta":197,"style":197},"from openpyxl import load_workbook\nfrom openpyxl.styles import Alignment, Font, PatternFill\n\nDESCRIPTIONS = {\n    \"Summary\": \"Headline figures for the period\",\n    \"Regional\": \"Revenue and variance by region\",\n    \"Q3 Detail\": \"Every order in the quarter\",\n}\n\ndef add_contents(path, dest, title=\"Contents\", skip_prefixes=(\"_\",),\n                 descriptions=None, index_name=\"Contents\"):\n    \"\"\"Build an index sheet listing every sheet, with links and row counts.\"\"\"\n    descriptions = descriptions or {}\n    wb = load_workbook(path)\n\n    if index_name in wb.sheetnames:\n        del wb[index_name]\n    index = wb.create_sheet(index_name, 0)\n\n    index[\"A1\"] = title\n    index.merge_cells(\"A1:C1\")\n    index[\"A1\"].font = Font(size=14, bold=True, color=\"4338CA\")\n    index[\"A1\"].alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n    index[\"A1\"].fill = PatternFill(\"solid\", start_color=\"EBEBFD\",\n                                   end_color=\"EBEBFD\")\n    index.row_dimensions[1].height = 26\n\n    header = Font(bold=True, color=\"FFFFFF\")\n    header_fill = PatternFill(\"solid\", start_color=\"4338CA\",\n                              end_color=\"4338CA\")\n    for column, label in zip(\"ABC\", (\"Sheet\", \"Contents\", \"Rows\")):\n        cell = index[f\"{column}3\"]\n        cell.value = label\n        cell.font = header\n        cell.fill = header_fill\n\n    row = 4\n    for ws in wb.worksheets:\n        if ws.title == index_name or ws.title.startswith(skip_prefixes):\n            continue\n        if ws.sheet_state != \"visible\":\n            continue\n\n        link = index.cell(row=row, column=1, value=ws.title)\n        link.hyperlink = sheet_ref(ws.title)\n        link.style = \"Hyperlink\"\n\n        index.cell(row=row, column=2,\n                   value=descriptions.get(ws.title, \"\"))\n        index.cell(row=row, column=3,\n                   value=max(ws.max_row - 1, 0)).number_format = \"#,##0\"\n        row += 1\n\n    index.column_dimensions[\"A\"].width = 22\n    index.column_dimensions[\"B\"].width = 46\n    index.column_dimensions[\"C\"].width = 12\n    index.freeze_panes = \"A4\"\n    index.sheet_view.showGridLines = False\n\n    wb.active = 0\n    wb.save(dest)\n    return row - 4\n\ncount = add_contents(\"dashboard.xlsx\", \"dashboard_indexed.xlsx\",\n                     descriptions=DESCRIPTIONS)\nprint(f\"indexed {count} sheet(s)\")\n",[199,1028,1029,1039,1051,1055,1064,1076,1087,1098,1102,1106,1133,1152,1157,1173,1183,1187,1200,1209,1224,1229,1244,1255,1299,1333,1363,1375,1391,1396,1423,1445,1457,1493,1522,1533,1544,1555,1560,1571,1584,1603,1609,1626,1631,1636,1671,1682,1693,1698,1719,1736,1756,1787,1799,1804,1821,1836,1851,1862,1873,1878,1889,1895,1907,1912,1932,1944],{"__ignoreMap":197},[202,1030,1031,1033,1035,1037],{"class":204,"line":205},[202,1032,594],{"class":234},[202,1034,597],{"class":238},[202,1036,235],{"class":234},[202,1038,602],{"class":238},[202,1040,1041,1043,1046,1048],{"class":204,"line":248},[202,1042,594],{"class":234},[202,1044,1045],{"class":238}," openpyxl.styles ",[202,1047,235],{"class":234},[202,1049,1050],{"class":238}," Alignment, Font, PatternFill\n",[202,1052,1053],{"class":204,"line":255},[202,1054,252],{"emptyLinePlaceholder":251},[202,1056,1057,1060,1062],{"class":204,"line":267},[202,1058,1059],{"class":302},"DESCRIPTIONS",[202,1061,781],{"class":234},[202,1063,264],{"class":238},[202,1065,1066,1068,1070,1073],{"class":204,"line":294},[202,1067,270],{"class":212},[202,1069,386],{"class":238},[202,1071,1072],{"class":212},"\"Headline figures for the period\"",[202,1074,1075],{"class":238},",\n",[202,1077,1078,1080,1082,1085],{"class":204,"line":314},[202,1079,317],{"class":212},[202,1081,386],{"class":238},[202,1083,1084],{"class":212},"\"Revenue and variance by region\"",[202,1086,1075],{"class":238},[202,1088,1089,1091,1093,1096],{"class":204,"line":347},[202,1090,378],{"class":212},[202,1092,386],{"class":238},[202,1094,1095],{"class":212},"\"Every order in the quarter\"",[202,1097,1075],{"class":238},[202,1099,1100],{"class":204,"line":375},[202,1101,492],{"class":238},[202,1103,1104],{"class":204,"line":406},[202,1105,252],{"emptyLinePlaceholder":251},[202,1107,1108,1110,1113,1116,1118,1120,1123,1125,1127,1130],{"class":204,"line":445},[202,1109,825],{"class":234},[202,1111,1112],{"class":828}," add_contents",[202,1114,1115],{"class":238},"(path, dest, title",[202,1117,261],{"class":234},[202,1119,633],{"class":212},[202,1121,1122],{"class":238},", skip_prefixes",[202,1124,261],{"class":234},[202,1126,392],{"class":238},[202,1128,1129],{"class":212},"\"_\"",[202,1131,1132],{"class":238},",),\n",[202,1134,1135,1138,1140,1143,1146,1148,1150],{"class":204,"line":489},[202,1136,1137],{"class":238},"                 descriptions",[202,1139,261],{"class":234},[202,1141,1142],{"class":302},"None",[202,1144,1145],{"class":238},", index_name",[202,1147,261],{"class":234},[202,1149,633],{"class":212},[202,1151,839],{"class":238},[202,1153,1154],{"class":204,"line":495},[202,1155,1156],{"class":212},"    \"\"\"Build an index sheet listing every sheet, with links and row counts.\"\"\"\n",[202,1158,1159,1162,1164,1167,1170],{"class":204,"line":500},[202,1160,1161],{"class":238},"    descriptions ",[202,1163,261],{"class":234},[202,1165,1166],{"class":238}," descriptions ",[202,1168,1169],{"class":234},"or",[202,1171,1172],{"class":238}," {}\n",[202,1174,1175,1178,1180],{"class":204,"line":531},[202,1176,1177],{"class":238},"    wb ",[202,1179,261],{"class":234},[202,1181,1182],{"class":238}," load_workbook(path)\n",[202,1184,1185],{"class":204,"line":545},[202,1186,252],{"emptyLinePlaceholder":251},[202,1188,1190,1192,1195,1197],{"class":204,"line":1189},16,[202,1191,849],{"class":234},[202,1193,1194],{"class":238}," index_name ",[202,1196,428],{"class":234},[202,1198,1199],{"class":238}," wb.sheetnames:\n",[202,1201,1203,1206],{"class":204,"line":1202},17,[202,1204,1205],{"class":234},"        del",[202,1207,1208],{"class":238}," wb[index_name]\n",[202,1210,1212,1215,1217,1220,1222],{"class":204,"line":1211},18,[202,1213,1214],{"class":238},"    index ",[202,1216,261],{"class":234},[202,1218,1219],{"class":238}," wb.create_sheet(index_name, ",[202,1221,42],{"class":302},[202,1223,567],{"class":238},[202,1225,1227],{"class":204,"line":1226},19,[202,1228,252],{"emptyLinePlaceholder":251},[202,1230,1232,1235,1237,1239,1241],{"class":204,"line":1231},20,[202,1233,1234],{"class":238},"    index[",[202,1236,651],{"class":212},[202,1238,654],{"class":238},[202,1240,261],{"class":234},[202,1242,1243],{"class":238}," title\n",[202,1245,1247,1250,1253],{"class":204,"line":1246},21,[202,1248,1249],{"class":238},"    index.merge_cells(",[202,1251,1252],{"class":212},"\"A1:C1\"",[202,1254,567],{"class":238},[202,1256,1258,1260,1262,1265,1267,1270,1273,1275,1277,1279,1282,1284,1287,1289,1292,1294,1297],{"class":204,"line":1257},22,[202,1259,1234],{"class":238},[202,1261,651],{"class":212},[202,1263,1264],{"class":238},"].font ",[202,1266,261],{"class":234},[202,1268,1269],{"class":238}," Font(",[202,1271,1272],{"class":514},"size",[202,1274,261],{"class":234},[202,1276,48],{"class":302},[202,1278,285],{"class":238},[202,1280,1281],{"class":514},"bold",[202,1283,261],{"class":234},[202,1285,1286],{"class":302},"True",[202,1288,285],{"class":238},[202,1290,1291],{"class":514},"color",[202,1293,261],{"class":234},[202,1295,1296],{"class":212},"\"4338CA\"",[202,1298,567],{"class":238},[202,1300,1302,1304,1306,1309,1311,1314,1317,1319,1322,1324,1327,1329,1331],{"class":204,"line":1301},23,[202,1303,1234],{"class":238},[202,1305,651],{"class":212},[202,1307,1308],{"class":238},"].alignment ",[202,1310,261],{"class":234},[202,1312,1313],{"class":238}," Alignment(",[202,1315,1316],{"class":514},"horizontal",[202,1318,261],{"class":234},[202,1320,1321],{"class":212},"\"center\"",[202,1323,285],{"class":238},[202,1325,1326],{"class":514},"vertical",[202,1328,261],{"class":234},[202,1330,1321],{"class":212},[202,1332,567],{"class":238},[202,1334,1336,1338,1340,1343,1345,1348,1351,1353,1356,1358,1361],{"class":204,"line":1335},24,[202,1337,1234],{"class":238},[202,1339,651],{"class":212},[202,1341,1342],{"class":238},"].fill ",[202,1344,261],{"class":234},[202,1346,1347],{"class":238}," PatternFill(",[202,1349,1350],{"class":212},"\"solid\"",[202,1352,285],{"class":238},[202,1354,1355],{"class":514},"start_color",[202,1357,261],{"class":234},[202,1359,1360],{"class":212},"\"EBEBFD\"",[202,1362,1075],{"class":238},[202,1364,1366,1369,1371,1373],{"class":204,"line":1365},25,[202,1367,1368],{"class":514},"                                   end_color",[202,1370,261],{"class":234},[202,1372,1360],{"class":212},[202,1374,567],{"class":238},[202,1376,1378,1381,1383,1386,1388],{"class":204,"line":1377},26,[202,1379,1380],{"class":238},"    index.row_dimensions[",[202,1382,395],{"class":302},[202,1384,1385],{"class":238},"].height ",[202,1387,261],{"class":234},[202,1389,1390],{"class":302}," 26\n",[202,1392,1394],{"class":204,"line":1393},27,[202,1395,252],{"emptyLinePlaceholder":251},[202,1397,1399,1402,1404,1406,1408,1410,1412,1414,1416,1418,1421],{"class":204,"line":1398},28,[202,1400,1401],{"class":238},"    header ",[202,1403,261],{"class":234},[202,1405,1269],{"class":238},[202,1407,1281],{"class":514},[202,1409,261],{"class":234},[202,1411,1286],{"class":302},[202,1413,285],{"class":238},[202,1415,1291],{"class":514},[202,1417,261],{"class":234},[202,1419,1420],{"class":212},"\"FFFFFF\"",[202,1422,567],{"class":238},[202,1424,1426,1429,1431,1433,1435,1437,1439,1441,1443],{"class":204,"line":1425},29,[202,1427,1428],{"class":238},"    header_fill ",[202,1430,261],{"class":234},[202,1432,1347],{"class":238},[202,1434,1350],{"class":212},[202,1436,285],{"class":238},[202,1438,1355],{"class":514},[202,1440,261],{"class":234},[202,1442,1296],{"class":212},[202,1444,1075],{"class":238},[202,1446,1448,1451,1453,1455],{"class":204,"line":1447},30,[202,1449,1450],{"class":514},"                              end_color",[202,1452,261],{"class":234},[202,1454,1296],{"class":212},[202,1456,567],{"class":238},[202,1458,1460,1462,1465,1467,1470,1472,1475,1478,1481,1483,1485,1487,1490],{"class":204,"line":1459},31,[202,1461,534],{"class":234},[202,1463,1464],{"class":238}," column, label ",[202,1466,428],{"class":234},[202,1468,1469],{"class":302}," zip",[202,1471,392],{"class":238},[202,1473,1474],{"class":212},"\"ABC\"",[202,1476,1477],{"class":238},", (",[202,1479,1480],{"class":212},"\"Sheet\"",[202,1482,285],{"class":238},[202,1484,633],{"class":212},[202,1486,285],{"class":238},[202,1488,1489],{"class":212},"\"Rows\"",[202,1491,1492],{"class":238},")):\n",[202,1494,1496,1499,1501,1504,1507,1509,1511,1514,1516,1519],{"class":204,"line":1495},32,[202,1497,1498],{"class":238},"        cell ",[202,1500,261],{"class":234},[202,1502,1503],{"class":238}," index[",[202,1505,1506],{"class":234},"f",[202,1508,790],{"class":212},[202,1510,869],{"class":796},[202,1512,1513],{"class":238},"column",[202,1515,875],{"class":796},[202,1517,1518],{"class":212},"3\"",[202,1520,1521],{"class":238},"]\n",[202,1523,1525,1528,1530],{"class":204,"line":1524},33,[202,1526,1527],{"class":238},"        cell.value ",[202,1529,261],{"class":234},[202,1531,1532],{"class":238}," label\n",[202,1534,1536,1539,1541],{"class":204,"line":1535},34,[202,1537,1538],{"class":238},"        cell.font ",[202,1540,261],{"class":234},[202,1542,1543],{"class":238}," header\n",[202,1545,1547,1550,1552],{"class":204,"line":1546},35,[202,1548,1549],{"class":238},"        cell.fill ",[202,1551,261],{"class":234},[202,1553,1554],{"class":238}," header_fill\n",[202,1556,1558],{"class":204,"line":1557},36,[202,1559,252],{"emptyLinePlaceholder":251},[202,1561,1563,1566,1568],{"class":204,"line":1562},37,[202,1564,1565],{"class":238},"    row ",[202,1567,261],{"class":234},[202,1569,1570],{"class":302}," 4\n",[202,1572,1574,1576,1579,1581],{"class":204,"line":1573},38,[202,1575,534],{"class":234},[202,1577,1578],{"class":238}," ws ",[202,1580,428],{"class":234},[202,1582,1583],{"class":238}," wb.worksheets:\n",[202,1585,1587,1590,1593,1596,1598,1600],{"class":204,"line":1586},39,[202,1588,1589],{"class":234},"        if",[202,1591,1592],{"class":238}," ws.title ",[202,1594,1595],{"class":234},"==",[202,1597,1194],{"class":238},[202,1599,1169],{"class":234},[202,1601,1602],{"class":238}," ws.title.startswith(skip_prefixes):\n",[202,1604,1606],{"class":204,"line":1605},40,[202,1607,1608],{"class":234},"            continue\n",[202,1610,1612,1614,1617,1620,1623],{"class":204,"line":1611},41,[202,1613,1589],{"class":234},[202,1615,1616],{"class":238}," ws.sheet_state ",[202,1618,1619],{"class":234},"!=",[202,1621,1622],{"class":212}," \"visible\"",[202,1624,1625],{"class":238},":\n",[202,1627,1629],{"class":204,"line":1628},42,[202,1630,1608],{"class":234},[202,1632,1634],{"class":204,"line":1633},43,[202,1635,252],{"emptyLinePlaceholder":251},[202,1637,1639,1642,1644,1647,1650,1652,1655,1657,1659,1661,1663,1666,1668],{"class":204,"line":1638},44,[202,1640,1641],{"class":238},"        link ",[202,1643,261],{"class":234},[202,1645,1646],{"class":238}," index.cell(",[202,1648,1649],{"class":514},"row",[202,1651,261],{"class":234},[202,1653,1654],{"class":238},"row, ",[202,1656,1513],{"class":514},[202,1658,261],{"class":234},[202,1660,395],{"class":302},[202,1662,285],{"class":238},[202,1664,1665],{"class":514},"value",[202,1667,261],{"class":234},[202,1669,1670],{"class":238},"ws.title)\n",[202,1672,1674,1677,1679],{"class":204,"line":1673},45,[202,1675,1676],{"class":238},"        link.hyperlink ",[202,1678,261],{"class":234},[202,1680,1681],{"class":238}," sheet_ref(ws.title)\n",[202,1683,1685,1688,1690],{"class":204,"line":1684},46,[202,1686,1687],{"class":238},"        link.style ",[202,1689,261],{"class":234},[202,1691,1692],{"class":212}," \"Hyperlink\"\n",[202,1694,1696],{"class":204,"line":1695},47,[202,1697,252],{"emptyLinePlaceholder":251},[202,1699,1701,1704,1706,1708,1710,1712,1714,1717],{"class":204,"line":1700},48,[202,1702,1703],{"class":238},"        index.cell(",[202,1705,1649],{"class":514},[202,1707,261],{"class":234},[202,1709,1654],{"class":238},[202,1711,1513],{"class":514},[202,1713,261],{"class":234},[202,1715,1716],{"class":302},"2",[202,1718,1075],{"class":238},[202,1720,1722,1725,1727,1730,1733],{"class":204,"line":1721},49,[202,1723,1724],{"class":514},"                   value",[202,1726,261],{"class":234},[202,1728,1729],{"class":238},"descriptions.get(ws.title, ",[202,1731,1732],{"class":212},"\"\"",[202,1734,1735],{"class":238},"))\n",[202,1737,1739,1741,1743,1745,1747,1749,1751,1754],{"class":204,"line":1738},50,[202,1740,1703],{"class":238},[202,1742,1649],{"class":514},[202,1744,261],{"class":234},[202,1746,1654],{"class":238},[202,1748,1513],{"class":514},[202,1750,261],{"class":234},[202,1752,1753],{"class":302},"3",[202,1755,1075],{"class":238},[202,1757,1759,1761,1763,1766,1769,1772,1775,1777,1779,1782,1784],{"class":204,"line":1758},51,[202,1760,1724],{"class":514},[202,1762,261],{"class":234},[202,1764,1765],{"class":302},"max",[202,1767,1768],{"class":238},"(ws.max_row ",[202,1770,1771],{"class":234},"-",[202,1773,1774],{"class":302}," 1",[202,1776,285],{"class":238},[202,1778,42],{"class":302},[202,1780,1781],{"class":238},")).number_format ",[202,1783,261],{"class":234},[202,1785,1786],{"class":212}," \"#,##0\"\n",[202,1788,1790,1793,1796],{"class":204,"line":1789},52,[202,1791,1792],{"class":238},"        row ",[202,1794,1795],{"class":234},"+=",[202,1797,1798],{"class":302}," 1\n",[202,1800,1802],{"class":204,"line":1801},53,[202,1803,252],{"emptyLinePlaceholder":251},[202,1805,1807,1810,1813,1816,1818],{"class":204,"line":1806},54,[202,1808,1809],{"class":238},"    index.column_dimensions[",[202,1811,1812],{"class":212},"\"A\"",[202,1814,1815],{"class":238},"].width ",[202,1817,261],{"class":234},[202,1819,1820],{"class":302}," 22\n",[202,1822,1824,1826,1829,1831,1833],{"class":204,"line":1823},55,[202,1825,1809],{"class":238},[202,1827,1828],{"class":212},"\"B\"",[202,1830,1815],{"class":238},[202,1832,261],{"class":234},[202,1834,1835],{"class":302}," 46\n",[202,1837,1839,1841,1844,1846,1848],{"class":204,"line":1838},56,[202,1840,1809],{"class":238},[202,1842,1843],{"class":212},"\"C\"",[202,1845,1815],{"class":238},[202,1847,261],{"class":234},[202,1849,1850],{"class":302}," 12\n",[202,1852,1854,1857,1859],{"class":204,"line":1853},57,[202,1855,1856],{"class":238},"    index.freeze_panes ",[202,1858,261],{"class":234},[202,1860,1861],{"class":212}," \"A4\"\n",[202,1863,1865,1868,1870],{"class":204,"line":1864},58,[202,1866,1867],{"class":238},"    index.sheet_view.showGridLines ",[202,1869,261],{"class":234},[202,1871,1872],{"class":302}," False\n",[202,1874,1876],{"class":204,"line":1875},59,[202,1877,252],{"emptyLinePlaceholder":251},[202,1879,1881,1884,1886],{"class":204,"line":1880},60,[202,1882,1883],{"class":238},"    wb.active ",[202,1885,261],{"class":234},[202,1887,1888],{"class":302}," 0\n",[202,1890,1892],{"class":204,"line":1891},61,[202,1893,1894],{"class":238},"    wb.save(dest)\n",[202,1896,1898,1900,1903,1905],{"class":204,"line":1897},62,[202,1899,913],{"class":234},[202,1901,1902],{"class":238}," row ",[202,1904,1771],{"class":234},[202,1906,1570],{"class":302},[202,1908,1910],{"class":204,"line":1909},63,[202,1911,252],{"emptyLinePlaceholder":251},[202,1913,1915,1918,1920,1923,1925,1927,1930],{"class":204,"line":1914},64,[202,1916,1917],{"class":238},"count ",[202,1919,261],{"class":234},[202,1921,1922],{"class":238}," add_contents(",[202,1924,509],{"class":212},[202,1926,285],{"class":238},[202,1928,1929],{"class":212},"\"dashboard_indexed.xlsx\"",[202,1931,1075],{"class":238},[202,1933,1935,1938,1940,1942],{"class":204,"line":1934},65,[202,1936,1937],{"class":514},"                     descriptions",[202,1939,261],{"class":234},[202,1941,1059],{"class":302},[202,1943,567],{"class":238},[202,1945,1947,1949,1951,1953,1956,1958,1961,1963,1966],{"class":204,"line":1946},66,[202,1948,945],{"class":302},[202,1950,392],{"class":238},[202,1952,1506],{"class":234},[202,1954,1955],{"class":212},"\"indexed ",[202,1957,869],{"class":796},[202,1959,1960],{"class":238},"count",[202,1962,875],{"class":796},[202,1964,1965],{"class":212}," sheet(s)\"",[202,1967,567],{"class":238},[10,1969,1970,1971,1974,1975,1979,1980,1983,1984,1987],{},"Three choices worth noting. Skipping sheets whose name starts with ",[199,1972,1973],{},"_"," gives you a convention for working sheets that should not appear — the same convention used for hiding them in ",[14,1976,1978],{"href":1977},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Fhide-sheets-rows-and-columns-with-openpyxl\u002F","hiding sheets, rows and columns",". Deleting an existing index before rebuilding makes the function idempotent, so a re-run does not produce ",[199,1981,1982],{},"Contents1",". And ",[199,1985,1986],{},"wb.active = 0"," opens the workbook on the index, which is the point of having one.",[10,1989,1990,1991,1994,1995,1998],{},"The row count uses ",[199,1992,1993],{},"ws.max_row - 1"," to exclude the header — and inherits ",[199,1996,1997],{},"max_row","'s habit of overreporting when stray formatting extends the used range, so treat it as indicative rather than exact.",[187,2000,2002],{"id":2001},"step-3-add-back-links","Step 3 — Add back-links",[20,2004,30,2010,30,2013,30,2016,30,2019,30,2024,30,2028,30,2033,30,2037,30,2041,30,2046,30,2051,30,2054,30,2056,30,2059,30,2063,30,2068,30,2070,30,2073],{"viewBox":2005,"role":23,"ariaLabel":2006,"ariaLabelledBy":2007,"xmlns":28,"style":29},"0 0 800 226","Sheet name quoting rules: plain identifiers need no quotes, names with spaces or punctuation must be quoted, and an apostrophe inside a quoted name must be doubled.",[2008,2009],"quote-t","quote-d",[32,2011,2012],{"id":2008},"When a sheet name needs quoting in a link",[36,2014,2015],{"id":2009},"Three cases. A plain identifier such as Summary needs no quotes and links as hash Summary exclamation A1. A name containing a space such as Q3 Detail must be wrapped in single quotes or Excel cannot resolve it and the link silently does nothing. A name containing an apostrophe needs both the surrounding quotes and the inner apostrophe doubled. Quoting unconditionally whenever the name is not a plain identifier avoids having to remember which case applies.",[40,2017],{"x":42,"y":42,"width":43,"height":2018,"fill":45},"226",[65,2020,2023],{"x":86,"y":2021,"style":2022},"30","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","sheet name",[65,2025,2027],{"x":2026,"y":2021,"style":2022},"580","link location",[40,2029],{"x":2021,"y":2030,"width":2031,"height":58,"rx":2032,"fill":92,"stroke":93,"style":54},"46","340","7",[65,2034,83],{"x":86,"y":2035,"style":2036},"72","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[40,2038],{"x":2039,"y":2030,"width":2040,"height":58,"rx":2032,"fill":45,"stroke":53,"style":54},"396","374",[65,2042,2045],{"x":2043,"y":2035,"style":2044},"583","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","#Summary!A1",[40,2047],{"x":2021,"y":2048,"width":2031,"height":58,"rx":2032,"fill":2049,"stroke":2050,"style":54},"98","#fdefd8","var(--gold,#b4740a)",[65,2052,118],{"x":86,"y":171,"style":2053},"font-size:11.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[40,2055],{"x":2039,"y":2048,"width":2040,"height":58,"rx":2032,"fill":45,"stroke":2050,"style":54},[65,2057,2058],{"x":2043,"y":171,"style":2044},"#'Q3 Detail'!A1",[40,2060],{"x":2021,"y":74,"width":2031,"height":58,"rx":2032,"fill":2061,"stroke":2062,"style":54},"#fee8f2","var(--accent,#f43f8f)",[65,2064,2067],{"x":86,"y":2065,"style":2066},"176","font-size:11.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Bob's data",[40,2069],{"x":2039,"y":74,"width":2040,"height":58,"rx":2032,"fill":45,"stroke":2062,"style":54},[65,2071,2072],{"x":2043,"y":2065,"style":2044},"#'Bob''s data'!A1",[65,2074,2078],{"x":2075,"y":2076,"style":2077},"400","214","font-size:10.5px;fill:var(--muted,#5b6780);text-anchor:middle","quote unconditionally unless the name is a plain identifier",[10,2080,2081],{},"Navigation should work both ways. A link in A1 of every sheet costs one loop:",[192,2083,2085],{"className":225,"code":2084,"language":227,"meta":197,"style":197},"from openpyxl import load_workbook\nfrom openpyxl.styles import Font\n\ndef add_back_links(path, dest, index_name=\"Contents\", label=\"← back to index\"):\n    \"\"\"Put a link to the index in A1 of every other sheet.\"\"\"\n    wb = load_workbook(path)\n    if index_name not in wb.sheetnames:\n        raise KeyError(f\"no sheet named {index_name!r}\")\n\n    added = 0\n    for ws in wb.worksheets:\n        if ws.title == index_name:\n            continue\n\n        # Make room so the link never overwrites data.\n        ws.insert_rows(1)\n        cell = ws.cell(row=1, column=1, value=label)\n        cell.hyperlink = sheet_ref(index_name)\n        cell.style = \"Hyperlink\"\n        cell.font = Font(size=10, bold=True, underline=\"single\",\n                         color=\"4338CA\")\n        ws.freeze_panes = \"A3\"          # keep the link and header visible\n        added += 1\n\n    wb.save(dest)\n    return added\n",[199,2086,2087,2097,2108,2112,2136,2141,2149,2163,2192,2196,2205,2215,2226,2230,2234,2239,2248,2280,2290,2299,2334,2345,2358,2367,2371,2375],{"__ignoreMap":197},[202,2088,2089,2091,2093,2095],{"class":204,"line":205},[202,2090,594],{"class":234},[202,2092,597],{"class":238},[202,2094,235],{"class":234},[202,2096,602],{"class":238},[202,2098,2099,2101,2103,2105],{"class":204,"line":248},[202,2100,594],{"class":234},[202,2102,1045],{"class":238},[202,2104,235],{"class":234},[202,2106,2107],{"class":238}," Font\n",[202,2109,2110],{"class":204,"line":255},[202,2111,252],{"emptyLinePlaceholder":251},[202,2113,2114,2116,2119,2122,2124,2126,2129,2131,2134],{"class":204,"line":267},[202,2115,825],{"class":234},[202,2117,2118],{"class":828}," add_back_links",[202,2120,2121],{"class":238},"(path, dest, index_name",[202,2123,261],{"class":234},[202,2125,633],{"class":212},[202,2127,2128],{"class":238},", label",[202,2130,261],{"class":234},[202,2132,2133],{"class":212},"\"← back to index\"",[202,2135,839],{"class":238},[202,2137,2138],{"class":204,"line":294},[202,2139,2140],{"class":212},"    \"\"\"Put a link to the index in A1 of every other sheet.\"\"\"\n",[202,2142,2143,2145,2147],{"class":204,"line":314},[202,2144,1177],{"class":238},[202,2146,261],{"class":234},[202,2148,1182],{"class":238},[202,2150,2151,2153,2155,2158,2161],{"class":204,"line":347},[202,2152,849],{"class":234},[202,2154,1194],{"class":238},[202,2156,2157],{"class":234},"not",[202,2159,2160],{"class":234}," in",[202,2162,1199],{"class":238},[202,2164,2165,2168,2171,2173,2175,2178,2180,2183,2186,2188,2190],{"class":204,"line":375},[202,2166,2167],{"class":234},"        raise",[202,2169,2170],{"class":302}," KeyError",[202,2172,392],{"class":238},[202,2174,1506],{"class":234},[202,2176,2177],{"class":212},"\"no sheet named ",[202,2179,869],{"class":796},[202,2181,2182],{"class":238},"index_name",[202,2184,2185],{"class":234},"!r",[202,2187,875],{"class":796},[202,2189,790],{"class":212},[202,2191,567],{"class":238},[202,2193,2194],{"class":204,"line":406},[202,2195,252],{"emptyLinePlaceholder":251},[202,2197,2198,2201,2203],{"class":204,"line":445},[202,2199,2200],{"class":238},"    added ",[202,2202,261],{"class":234},[202,2204,1888],{"class":302},[202,2206,2207,2209,2211,2213],{"class":204,"line":489},[202,2208,534],{"class":234},[202,2210,1578],{"class":238},[202,2212,428],{"class":234},[202,2214,1583],{"class":238},[202,2216,2217,2219,2221,2223],{"class":204,"line":495},[202,2218,1589],{"class":234},[202,2220,1592],{"class":238},[202,2222,1595],{"class":234},[202,2224,2225],{"class":238}," index_name:\n",[202,2227,2228],{"class":204,"line":500},[202,2229,1608],{"class":234},[202,2231,2232],{"class":204,"line":531},[202,2233,252],{"emptyLinePlaceholder":251},[202,2235,2236],{"class":204,"line":545},[202,2237,2238],{"class":676},"        # Make room so the link never overwrites data.\n",[202,2240,2241,2244,2246],{"class":204,"line":1189},[202,2242,2243],{"class":238},"        ws.insert_rows(",[202,2245,395],{"class":302},[202,2247,567],{"class":238},[202,2249,2250,2252,2254,2257,2259,2261,2263,2265,2267,2269,2271,2273,2275,2277],{"class":204,"line":1202},[202,2251,1498],{"class":238},[202,2253,261],{"class":234},[202,2255,2256],{"class":238}," ws.cell(",[202,2258,1649],{"class":514},[202,2260,261],{"class":234},[202,2262,395],{"class":302},[202,2264,285],{"class":238},[202,2266,1513],{"class":514},[202,2268,261],{"class":234},[202,2270,395],{"class":302},[202,2272,285],{"class":238},[202,2274,1665],{"class":514},[202,2276,261],{"class":234},[202,2278,2279],{"class":238},"label)\n",[202,2281,2282,2285,2287],{"class":204,"line":1211},[202,2283,2284],{"class":238},"        cell.hyperlink ",[202,2286,261],{"class":234},[202,2288,2289],{"class":238}," sheet_ref(index_name)\n",[202,2291,2292,2295,2297],{"class":204,"line":1226},[202,2293,2294],{"class":238},"        cell.style ",[202,2296,261],{"class":234},[202,2298,1692],{"class":212},[202,2300,2301,2303,2305,2307,2309,2311,2314,2316,2318,2320,2322,2324,2327,2329,2332],{"class":204,"line":1231},[202,2302,1538],{"class":238},[202,2304,261],{"class":234},[202,2306,1269],{"class":238},[202,2308,1272],{"class":514},[202,2310,261],{"class":234},[202,2312,2313],{"class":302},"10",[202,2315,285],{"class":238},[202,2317,1281],{"class":514},[202,2319,261],{"class":234},[202,2321,1286],{"class":302},[202,2323,285],{"class":238},[202,2325,2326],{"class":514},"underline",[202,2328,261],{"class":234},[202,2330,2331],{"class":212},"\"single\"",[202,2333,1075],{"class":238},[202,2335,2336,2339,2341,2343],{"class":204,"line":1246},[202,2337,2338],{"class":514},"                         color",[202,2340,261],{"class":234},[202,2342,1296],{"class":212},[202,2344,567],{"class":238},[202,2346,2347,2350,2352,2355],{"class":204,"line":1257},[202,2348,2349],{"class":238},"        ws.freeze_panes ",[202,2351,261],{"class":234},[202,2353,2354],{"class":212}," \"A3\"",[202,2356,2357],{"class":676},"          # keep the link and header visible\n",[202,2359,2360,2363,2365],{"class":204,"line":1301},[202,2361,2362],{"class":238},"        added ",[202,2364,1795],{"class":234},[202,2366,1798],{"class":302},[202,2368,2369],{"class":204,"line":1335},[202,2370,252],{"emptyLinePlaceholder":251},[202,2372,2373],{"class":204,"line":1365},[202,2374,1894],{"class":238},[202,2376,2377,2379],{"class":204,"line":1377},[202,2378,913],{"class":234},[202,2380,2381],{"class":238}," added\n",[10,2383,2384,2387,2388,2392,2393,18],{},[199,2385,2386],{},"insert_rows(1)"," is what stops the back-link overwriting a header. It does shift every row down by one, so run this ",[2389,2390,2391],"strong",{},"before"," anything that depends on row positions — and remember that inserting rows does not rewrite formulas, as covered in ",[14,2394,2396],{"href":2395},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Finsert-and-delete-rows-and-columns-with-openpyxl\u002F","inserting and deleting rows and columns",[10,2398,2399],{},"Where the sheets already carry a title row, write the link into an unused cell to the right instead:",[192,2401,2403],{"className":225,"code":2402,"language":227,"meta":197,"style":197},"ws.cell(row=1, column=ws.max_column + 2, value=label).hyperlink = \\\n    sheet_ref(index_name)\n",[199,2404,2405,2445],{"__ignoreMap":197},[202,2406,2407,2410,2412,2414,2416,2418,2420,2422,2425,2428,2431,2433,2435,2437,2440,2442],{"class":204,"line":205},[202,2408,2409],{"class":238},"ws.cell(",[202,2411,1649],{"class":514},[202,2413,261],{"class":234},[202,2415,395],{"class":302},[202,2417,285],{"class":238},[202,2419,1513],{"class":514},[202,2421,261],{"class":234},[202,2423,2424],{"class":238},"ws.max_column ",[202,2426,2427],{"class":234},"+",[202,2429,2430],{"class":302}," 2",[202,2432,285],{"class":238},[202,2434,1665],{"class":514},[202,2436,261],{"class":234},[202,2438,2439],{"class":238},"label).hyperlink ",[202,2441,261],{"class":234},[202,2443,2444],{"class":238}," \\\n",[202,2446,2447],{"class":204,"line":248},[202,2448,2449],{"class":238},"    sheet_ref(index_name)\n",[187,2451,2453],{"id":2452},"step-4-order-the-index-the-way-readers-think","Step 4 — Order the index the way readers think",[10,2455,2456,2457,2461],{},"Workbook order is how the tabs appear, and the index should usually match — but the ",[2458,2459,2460],"em",{},"useful"," order is often not the order the sheets were written in.",[20,2463,30,2469,30,2472,30,2475,30,2478,30,2482,30,2488,30,2492,30,2496,30,2498,30,2501,30,2503,30,2506,30,2509,30,2513,30,2516,30,2519,30,2524,30,2527,30,2529,30,2531,30,2533,30,2535,30,2537,30,2539,30,2541],{"viewBox":2464,"role":23,"ariaLabel":2465,"ariaLabelledBy":2466,"xmlns":28,"style":29},"0 0 800 238","Two orderings of the same index: creation order buries the summary among detail sheets, while a curated order puts the summary first and groups the detail below.",[2467,2468],"ord-t","ord-d",[32,2470,2471],{"id":2467},"Creation order versus reading order",[36,2473,2474],{"id":2468},"Two versions of the same index. In creation order the sheets appear as they were written — a detail sheet, then lookups, then the summary near the bottom — so the reader has to scan for the sheet they want. In a curated order the summary sits first, the regional breakdown second, and the detail sheets group below, matching the order a reader actually wants them. Reordering the workbook itself also fixes the tab order and the PDF page order.",[40,2476],{"x":42,"y":42,"width":43,"height":2477,"fill":45},"238",[40,2479],{"x":48,"y":75,"width":2480,"height":2481,"rx":48,"fill":2049,"stroke":2050,"style":54},"368","192",[65,2483,2487],{"x":2484,"y":2485,"style":2486},"198","54","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","creation order",[40,2489],{"x":2030,"y":2490,"width":2491,"height":49,"rx":76,"fill":45,"stroke":53},"68","304",[65,2493,108],{"x":2484,"y":2494,"style":2495},"85","font-size:10px;fill:var(--text,#172033);text-anchor:middle",[40,2497],{"x":2030,"y":163,"width":2491,"height":49,"rx":76,"fill":45,"stroke":53},[65,2499,2500],{"x":2484,"y":166,"style":2495},"Lookups",[40,2502],{"x":2030,"y":171,"width":2491,"height":49,"rx":76,"fill":45,"stroke":53},[65,2504,118],{"x":2484,"y":2505,"style":2495},"141",[40,2507],{"x":2030,"y":2508,"width":2491,"height":49,"rx":76,"fill":2049,"stroke":2050,"style":54},"152",[65,2510,83],{"x":2484,"y":2511,"style":2512},"169","font-size:10px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[65,2514,2515],{"x":2484,"y":86,"style":2077},"the reader scans for what they want",[40,2517],{"x":2518,"y":75,"width":2480,"height":2481,"rx":48,"fill":92,"stroke":93,"style":54},"418",[65,2520,2523],{"x":2521,"y":2485,"style":2522},"602","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","curated order",[40,2525],{"x":2526,"y":2490,"width":2491,"height":49,"rx":76,"fill":92,"stroke":93,"style":54},"450",[65,2528,83],{"x":2521,"y":2494,"style":97},[40,2530],{"x":2526,"y":163,"width":2491,"height":49,"rx":76,"fill":45,"stroke":93},[65,2532,98],{"x":2521,"y":166,"style":2495},[40,2534],{"x":2526,"y":171,"width":2491,"height":49,"rx":76,"fill":45,"stroke":93},[65,2536,118],{"x":2521,"y":2505,"style":2495},[40,2538],{"x":2526,"y":2508,"width":2491,"height":49,"rx":76,"fill":45,"stroke":93},[65,2540,108],{"x":2521,"y":2511,"style":2495},[65,2542,2543],{"x":2521,"y":86,"style":2077},"and the tabs and PDF pages follow",[192,2545,2547],{"className":225,"code":2546,"language":227,"meta":197,"style":197},"from openpyxl import load_workbook\n\nPREFERRED = [\"Contents\", \"Summary\", \"Regional\", \"Q3 Detail\", \"Detail\"]\n\ndef reorder_sheets(wb, preferred=PREFERRED):\n    \"\"\"Put the named sheets first, in that order; leave the rest after.\"\"\"\n    known = [wb[name] for name in preferred if name in wb.sheetnames]\n    rest = [ws for ws in wb.worksheets if ws not in known]\n    wb._sheets = known + rest\n    wb.active = 0\n    return [ws.title for ws in wb.worksheets]\n",[199,2548,2549,2559,2563,2595,2599,2615,2620,2650,2680,2695,2703],{"__ignoreMap":197},[202,2550,2551,2553,2555,2557],{"class":204,"line":205},[202,2552,594],{"class":234},[202,2554,597],{"class":238},[202,2556,235],{"class":234},[202,2558,602],{"class":238},[202,2560,2561],{"class":204,"line":248},[202,2562,252],{"emptyLinePlaceholder":251},[202,2564,2565,2568,2570,2573,2575,2577,2579,2581,2584,2586,2588,2590,2593],{"class":204,"line":255},[202,2566,2567],{"class":302},"PREFERRED",[202,2569,781],{"class":234},[202,2571,2572],{"class":238}," [",[202,2574,633],{"class":212},[202,2576,285],{"class":238},[202,2578,951],{"class":212},[202,2580,285],{"class":238},[202,2582,2583],{"class":212},"\"Regional\"",[202,2585,285],{"class":238},[202,2587,966],{"class":212},[202,2589,285],{"class":238},[202,2591,2592],{"class":212},"\"Detail\"",[202,2594,1521],{"class":238},[202,2596,2597],{"class":204,"line":267},[202,2598,252],{"emptyLinePlaceholder":251},[202,2600,2601,2603,2606,2609,2611,2613],{"class":204,"line":294},[202,2602,825],{"class":234},[202,2604,2605],{"class":828}," reorder_sheets",[202,2607,2608],{"class":238},"(wb, preferred",[202,2610,261],{"class":234},[202,2612,2567],{"class":302},[202,2614,839],{"class":238},[202,2616,2617],{"class":204,"line":314},[202,2618,2619],{"class":212},"    \"\"\"Put the named sheets first, in that order; leave the rest after.\"\"\"\n",[202,2621,2622,2625,2627,2630,2632,2635,2637,2640,2643,2645,2647],{"class":204,"line":347},[202,2623,2624],{"class":238},"    known ",[202,2626,261],{"class":234},[202,2628,2629],{"class":238}," [wb[name] ",[202,2631,423],{"class":234},[202,2633,2634],{"class":238}," name ",[202,2636,428],{"class":234},[202,2638,2639],{"class":238}," preferred ",[202,2641,2642],{"class":234},"if",[202,2644,2634],{"class":238},[202,2646,428],{"class":234},[202,2648,2649],{"class":238}," wb.sheetnames]\n",[202,2651,2652,2655,2657,2660,2662,2664,2666,2669,2671,2673,2675,2677],{"class":204,"line":375},[202,2653,2654],{"class":238},"    rest ",[202,2656,261],{"class":234},[202,2658,2659],{"class":238}," [ws ",[202,2661,423],{"class":234},[202,2663,1578],{"class":238},[202,2665,428],{"class":234},[202,2667,2668],{"class":238}," wb.worksheets ",[202,2670,2642],{"class":234},[202,2672,1578],{"class":238},[202,2674,2157],{"class":234},[202,2676,2160],{"class":234},[202,2678,2679],{"class":238}," known]\n",[202,2681,2682,2685,2687,2690,2692],{"class":204,"line":406},[202,2683,2684],{"class":238},"    wb._sheets ",[202,2686,261],{"class":234},[202,2688,2689],{"class":238}," known ",[202,2691,2427],{"class":234},[202,2693,2694],{"class":238}," rest\n",[202,2696,2697,2699,2701],{"class":204,"line":445},[202,2698,1883],{"class":238},[202,2700,261],{"class":234},[202,2702,1888],{"class":302},[202,2704,2705,2707,2710,2712,2714,2716],{"class":204,"line":489},[202,2706,913],{"class":234},[202,2708,2709],{"class":238}," [ws.title ",[202,2711,423],{"class":234},[202,2713,1578],{"class":238},[202,2715,428],{"class":234},[202,2717,2718],{"class":238}," wb.worksheets]\n",[10,2720,2721,2722,18],{},"Reordering the workbook rather than just the index rows fixes three things at once: the tab order, the index order, and the page order if the workbook is later exported, as in ",[14,2723,2725],{"href":2724},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002Fconvert-only-selected-sheets-of-a-workbook-to-pdf\u002F","converting only selected sheets to PDF",[187,2727,2729],{"id":2728},"common-pitfalls-and-fixes","Common pitfalls and fixes",[2731,2732,2733,2749],"table",{},[2734,2735,2736],"thead",{},[2737,2738,2739,2743,2746],"tr",{},[2740,2741,2742],"th",{},"Symptom",[2740,2744,2745],{},"Cause",[2740,2747,2748],{},"Fix",[2750,2751,2752,2766,2784,2799,2812,2825,2838,2852],"tbody",{},[2737,2753,2754,2758,2761],{},[2755,2756,2757],"td",{},"Link does nothing",[2755,2759,2760],{},"Sheet name with a space, unquoted",[2755,2762,2763,2764,18],{},"Wrap it: ",[199,2765,2058],{},[2737,2767,2768,2771,2777],{},[2755,2769,2770],{},"Link looks like plain text",[2755,2772,2773,2774,2776],{},"Only ",[199,2775,993],{}," set",[2755,2778,2779,2780,2783],{},"Apply the ",[199,2781,2782],{},"Hyperlink"," style.",[2737,2785,2786,2789,2794],{},[2755,2787,2788],{},"Link opens a browser",[2755,2790,2791,2792],{},"Missing the leading ",[199,2793,583],{},[2755,2795,2796,2797,18],{},"Internal links must start with ",[199,2798,583],{},[2737,2800,2801,2806,2809],{},[2755,2802,2803,2805],{},[199,2804,1982],{}," after a re-run",[2755,2807,2808],{},"Existing index not removed",[2755,2810,2811],{},"Delete it before recreating.",[2737,2813,2814,2817,2820],{},[2755,2815,2816],{},"Back-link overwrote a header",[2755,2818,2819],{},"Written into an occupied A1",[2755,2821,2822,2824],{},[199,2823,2386],{}," first.",[2737,2826,2827,2830,2835],{},[2755,2828,2829],{},"Row counts far too high",[2755,2831,2832,2834],{},[199,2833,1997],{}," reflects the used range",[2755,2836,2837],{},"Treat the count as indicative.",[2737,2839,2840,2843,2849],{},[2755,2841,2842],{},"Workbook opens on the wrong sheet",[2755,2844,2845,2848],{},[199,2846,2847],{},"wb.active"," not set",[2755,2850,2851],{},"Set it to the index's position.",[2737,2853,2854,2857,2860],{},[2755,2855,2856],{},"Link to a sheet with an apostrophe fails",[2755,2858,2859],{},"Apostrophe not escaped",[2755,2861,2862],{},"Double it inside the quotes.",[187,2864,2866],{"id":2865},"performance-and-scale-notes","Performance and scale notes",[10,2868,2869,2870,2873],{},"Building an index is cheap — one sheet, one row per sheet. The cost is ",[199,2871,2872],{},"load_workbook",", which parses the whole file, so adding an index to a 60 MB workbook pays the full parse for a few dozen cells of output.",[10,2875,2876,2877,2880],{},"Two ways to avoid that. ",[2389,2878,2879],{},"Build the index during the original write",", when the sheet names and row counts are already known, so no second load is needed:",[192,2882,2884],{"className":225,"code":2883,"language":227,"meta":197,"style":197},"import pandas as pd\n\ndef write_with_contents(sheets, path, descriptions=None):\n    \"\"\"Write every sheet and an index, in one pass.\"\"\"\n    descriptions = descriptions or {}\n    order = [\"Contents\", *sheets]\n\n    with pd.ExcelWriter(path, engine=\"xlsxwriter\") as writer:\n        book = writer.book\n        index = book.add_worksheet(\"Contents\")\n        writer.sheets[\"Contents\"] = index\n\n        title = book.add_format({\"bold\": True, \"font_size\": 14,\n                                 \"font_color\": \"#4338CA\",\n                                 \"bg_color\": \"#EBEBFD\", \"align\": \"center\"})\n        header = book.add_format({\"bold\": True, \"bg_color\": \"#4338CA\",\n                                  \"font_color\": \"#FFFFFF\"})\n        link = book.add_format({\"font_color\": \"blue\", \"underline\": 1})\n\n        index.merge_range(\"A1:C1\", \"Contents\", title)\n        for column, label in enumerate((\"Sheet\", \"Contents\", \"Rows\")):\n            index.write(2, column, label, header)\n\n        for position, (name, frame) in enumerate(sheets.items(), start=3):\n            index.write_url(position, 0, f\"internal:'{name}'!A1\", link, name)\n            index.write(position, 1, descriptions.get(name, \"\"))\n            index.write_number(position, 2, len(frame))\n            frame.to_excel(writer, sheet_name=name, index=False, startrow=1)\n\n            sheet = writer.sheets[name]\n            sheet.write_url(0, 0, \"internal:'Contents'!A1\", link,\n                            \"← back to index\")\n            sheet.freeze_panes(2, 0)\n\n        index.set_column(\"A:A\", 22)\n        index.set_column(\"B:B\", 46)\n        index.set_column(\"C:C\", 12)\n        index.freeze_panes(3, 0)\n        index.hide_gridlines(2)\n\n    return path\n",[199,2885,2886,2896,2900,2916,2921,2933,2951,2955,2975,2985,2999,3013,3017,3045,3057,3079,3105,3117,3144,3148,3162,3189,3199,3203,3226,3252,3266,3281,3309,3313,3323,3342,3349,3362,3366,3381,3394,3407,3420,3429,3433],{"__ignoreMap":197},[202,2887,2888,2890,2892,2894],{"class":204,"line":205},[202,2889,235],{"class":234},[202,2891,239],{"class":238},[202,2893,242],{"class":234},[202,2895,245],{"class":238},[202,2897,2898],{"class":204,"line":248},[202,2899,252],{"emptyLinePlaceholder":251},[202,2901,2902,2904,2907,2910,2912,2914],{"class":204,"line":255},[202,2903,825],{"class":234},[202,2905,2906],{"class":828}," write_with_contents",[202,2908,2909],{"class":238},"(sheets, path, descriptions",[202,2911,261],{"class":234},[202,2913,1142],{"class":302},[202,2915,839],{"class":238},[202,2917,2918],{"class":204,"line":267},[202,2919,2920],{"class":212},"    \"\"\"Write every sheet and an index, in one pass.\"\"\"\n",[202,2922,2923,2925,2927,2929,2931],{"class":204,"line":294},[202,2924,1161],{"class":238},[202,2926,261],{"class":234},[202,2928,1166],{"class":238},[202,2930,1169],{"class":234},[202,2932,1172],{"class":238},[202,2934,2935,2938,2940,2942,2944,2946,2948],{"class":204,"line":314},[202,2936,2937],{"class":238},"    order ",[202,2939,261],{"class":234},[202,2941,2572],{"class":238},[202,2943,633],{"class":212},[202,2945,285],{"class":238},[202,2947,809],{"class":234},[202,2949,2950],{"class":238},"sheets]\n",[202,2952,2953],{"class":204,"line":347},[202,2954,252],{"emptyLinePlaceholder":251},[202,2956,2957,2960,2963,2965,2967,2969,2971,2973],{"class":204,"line":375},[202,2958,2959],{"class":234},"    with",[202,2961,2962],{"class":238}," pd.ExcelWriter(path, ",[202,2964,515],{"class":514},[202,2966,261],{"class":234},[202,2968,520],{"class":212},[202,2970,523],{"class":238},[202,2972,242],{"class":234},[202,2974,528],{"class":238},[202,2976,2977,2980,2982],{"class":204,"line":406},[202,2978,2979],{"class":238},"        book ",[202,2981,261],{"class":234},[202,2983,2984],{"class":238}," writer.book\n",[202,2986,2987,2990,2992,2995,2997],{"class":204,"line":445},[202,2988,2989],{"class":238},"        index ",[202,2991,261],{"class":234},[202,2993,2994],{"class":238}," book.add_worksheet(",[202,2996,633],{"class":212},[202,2998,567],{"class":238},[202,3000,3001,3004,3006,3008,3010],{"class":204,"line":489},[202,3002,3003],{"class":238},"        writer.sheets[",[202,3005,633],{"class":212},[202,3007,654],{"class":238},[202,3009,261],{"class":234},[202,3011,3012],{"class":238}," index\n",[202,3014,3015],{"class":204,"line":495},[202,3016,252],{"emptyLinePlaceholder":251},[202,3018,3019,3022,3024,3027,3030,3032,3034,3036,3039,3041,3043],{"class":204,"line":500},[202,3020,3021],{"class":238},"        title ",[202,3023,261],{"class":234},[202,3025,3026],{"class":238}," book.add_format({",[202,3028,3029],{"class":212},"\"bold\"",[202,3031,386],{"class":238},[202,3033,1286],{"class":302},[202,3035,285],{"class":238},[202,3037,3038],{"class":212},"\"font_size\"",[202,3040,386],{"class":238},[202,3042,48],{"class":302},[202,3044,1075],{"class":238},[202,3046,3047,3050,3052,3055],{"class":204,"line":531},[202,3048,3049],{"class":212},"                                 \"font_color\"",[202,3051,386],{"class":238},[202,3053,3054],{"class":212},"\"#4338CA\"",[202,3056,1075],{"class":238},[202,3058,3059,3062,3064,3067,3069,3072,3074,3076],{"class":204,"line":545},[202,3060,3061],{"class":212},"                                 \"bg_color\"",[202,3063,386],{"class":238},[202,3065,3066],{"class":212},"\"#EBEBFD\"",[202,3068,285],{"class":238},[202,3070,3071],{"class":212},"\"align\"",[202,3073,386],{"class":238},[202,3075,1321],{"class":212},[202,3077,3078],{"class":238},"})\n",[202,3080,3081,3084,3086,3088,3090,3092,3094,3096,3099,3101,3103],{"class":204,"line":1189},[202,3082,3083],{"class":238},"        header ",[202,3085,261],{"class":234},[202,3087,3026],{"class":238},[202,3089,3029],{"class":212},[202,3091,386],{"class":238},[202,3093,1286],{"class":302},[202,3095,285],{"class":238},[202,3097,3098],{"class":212},"\"bg_color\"",[202,3100,386],{"class":238},[202,3102,3054],{"class":212},[202,3104,1075],{"class":238},[202,3106,3107,3110,3112,3115],{"class":204,"line":1202},[202,3108,3109],{"class":212},"                                  \"font_color\"",[202,3111,386],{"class":238},[202,3113,3114],{"class":212},"\"#FFFFFF\"",[202,3116,3078],{"class":238},[202,3118,3119,3121,3123,3125,3128,3130,3133,3135,3138,3140,3142],{"class":204,"line":1211},[202,3120,1641],{"class":238},[202,3122,261],{"class":234},[202,3124,3026],{"class":238},[202,3126,3127],{"class":212},"\"font_color\"",[202,3129,386],{"class":238},[202,3131,3132],{"class":212},"\"blue\"",[202,3134,285],{"class":238},[202,3136,3137],{"class":212},"\"underline\"",[202,3139,386],{"class":238},[202,3141,395],{"class":302},[202,3143,3078],{"class":238},[202,3145,3146],{"class":204,"line":1226},[202,3147,252],{"emptyLinePlaceholder":251},[202,3149,3150,3153,3155,3157,3159],{"class":204,"line":1231},[202,3151,3152],{"class":238},"        index.merge_range(",[202,3154,1252],{"class":212},[202,3156,285],{"class":238},[202,3158,633],{"class":212},[202,3160,3161],{"class":238},", title)\n",[202,3163,3164,3167,3169,3171,3174,3177,3179,3181,3183,3185,3187],{"class":204,"line":1246},[202,3165,3166],{"class":234},"        for",[202,3168,1464],{"class":238},[202,3170,428],{"class":234},[202,3172,3173],{"class":302}," enumerate",[202,3175,3176],{"class":238},"((",[202,3178,1480],{"class":212},[202,3180,285],{"class":238},[202,3182,633],{"class":212},[202,3184,285],{"class":238},[202,3186,1489],{"class":212},[202,3188,1492],{"class":238},[202,3190,3191,3194,3196],{"class":204,"line":1257},[202,3192,3193],{"class":238},"            index.write(",[202,3195,1716],{"class":302},[202,3197,3198],{"class":238},", column, label, header)\n",[202,3200,3201],{"class":204,"line":1301},[202,3202,252],{"emptyLinePlaceholder":251},[202,3204,3205,3207,3210,3212,3214,3217,3220,3222,3224],{"class":204,"line":1335},[202,3206,3166],{"class":234},[202,3208,3209],{"class":238}," position, (name, frame) ",[202,3211,428],{"class":234},[202,3213,3173],{"class":302},[202,3215,3216],{"class":238},"(sheets.items(), ",[202,3218,3219],{"class":514},"start",[202,3221,261],{"class":234},[202,3223,1753],{"class":302},[202,3225,839],{"class":238},[202,3227,3228,3231,3233,3235,3237,3240,3242,3244,3246,3249],{"class":204,"line":1365},[202,3229,3230],{"class":238},"            index.write_url(position, ",[202,3232,42],{"class":302},[202,3234,285],{"class":238},[202,3236,1506],{"class":234},[202,3238,3239],{"class":212},"\"internal:'",[202,3241,869],{"class":796},[202,3243,872],{"class":238},[202,3245,875],{"class":796},[202,3247,3248],{"class":212},"'!A1\"",[202,3250,3251],{"class":238},", link, name)\n",[202,3253,3254,3257,3259,3262,3264],{"class":204,"line":1377},[202,3255,3256],{"class":238},"            index.write(position, ",[202,3258,395],{"class":302},[202,3260,3261],{"class":238},", descriptions.get(name, ",[202,3263,1732],{"class":212},[202,3265,1735],{"class":238},[202,3267,3268,3271,3273,3275,3278],{"class":204,"line":1393},[202,3269,3270],{"class":238},"            index.write_number(position, ",[202,3272,1716],{"class":302},[202,3274,285],{"class":238},[202,3276,3277],{"class":302},"len",[202,3279,3280],{"class":238},"(frame))\n",[202,3282,3283,3286,3288,3290,3292,3294,3296,3298,3300,3303,3305,3307],{"class":204,"line":1398},[202,3284,3285],{"class":238},"            frame.to_excel(writer, ",[202,3287,551],{"class":514},[202,3289,261],{"class":234},[202,3291,556],{"class":238},[202,3293,559],{"class":514},[202,3295,261],{"class":234},[202,3297,564],{"class":302},[202,3299,285],{"class":238},[202,3301,3302],{"class":514},"startrow",[202,3304,261],{"class":234},[202,3306,395],{"class":302},[202,3308,567],{"class":238},[202,3310,3311],{"class":204,"line":1425},[202,3312,252],{"emptyLinePlaceholder":251},[202,3314,3315,3318,3320],{"class":204,"line":1447},[202,3316,3317],{"class":238},"            sheet ",[202,3319,261],{"class":234},[202,3321,3322],{"class":238}," writer.sheets[name]\n",[202,3324,3325,3328,3330,3332,3334,3336,3339],{"class":204,"line":1459},[202,3326,3327],{"class":238},"            sheet.write_url(",[202,3329,42],{"class":302},[202,3331,285],{"class":238},[202,3333,42],{"class":302},[202,3335,285],{"class":238},[202,3337,3338],{"class":212},"\"internal:'Contents'!A1\"",[202,3340,3341],{"class":238},", link,\n",[202,3343,3344,3347],{"class":204,"line":1495},[202,3345,3346],{"class":212},"                            \"← back to index\"",[202,3348,567],{"class":238},[202,3350,3351,3354,3356,3358,3360],{"class":204,"line":1524},[202,3352,3353],{"class":238},"            sheet.freeze_panes(",[202,3355,1716],{"class":302},[202,3357,285],{"class":238},[202,3359,42],{"class":302},[202,3361,567],{"class":238},[202,3363,3364],{"class":204,"line":1535},[202,3365,252],{"emptyLinePlaceholder":251},[202,3367,3368,3371,3374,3376,3379],{"class":204,"line":1546},[202,3369,3370],{"class":238},"        index.set_column(",[202,3372,3373],{"class":212},"\"A:A\"",[202,3375,285],{"class":238},[202,3377,3378],{"class":302},"22",[202,3380,567],{"class":238},[202,3382,3383,3385,3388,3390,3392],{"class":204,"line":1557},[202,3384,3370],{"class":238},[202,3386,3387],{"class":212},"\"B:B\"",[202,3389,285],{"class":238},[202,3391,2030],{"class":302},[202,3393,567],{"class":238},[202,3395,3396,3398,3401,3403,3405],{"class":204,"line":1562},[202,3397,3370],{"class":238},[202,3399,3400],{"class":212},"\"C:C\"",[202,3402,285],{"class":238},[202,3404,52],{"class":302},[202,3406,567],{"class":238},[202,3408,3409,3412,3414,3416,3418],{"class":204,"line":1573},[202,3410,3411],{"class":238},"        index.freeze_panes(",[202,3413,1753],{"class":302},[202,3415,285],{"class":238},[202,3417,42],{"class":302},[202,3419,567],{"class":238},[202,3421,3422,3425,3427],{"class":204,"line":1586},[202,3423,3424],{"class":238},"        index.hide_gridlines(",[202,3426,1716],{"class":302},[202,3428,567],{"class":238},[202,3430,3431],{"class":204,"line":1605},[202,3432,252],{"emptyLinePlaceholder":251},[202,3434,3435,3437],{"class":204,"line":1611},[202,3436,913],{"class":234},[202,3438,3439],{"class":238}," path\n",[10,3441,3442,3443,3446,3447,3450,3451,3453],{},"xlsxwriter's ",[199,3444,3445],{},"write_url"," uses ",[199,3448,3449],{},"internal:"," rather than a leading ",[199,3452,583],{},", and quoting the sheet name is required for the same reason. Writing the index first also means it is sheet zero, so the workbook opens on it with no extra call.",[10,3455,3456,3459,3460,3463],{},[2389,3457,3458],{},"Read only the metadata when you must load an existing file."," ",[199,3461,3462],{},"read_only=True"," gives you sheet names and dimensions without materialising cells:",[192,3465,3467],{"className":225,"code":3466,"language":227,"meta":197,"style":197},"from openpyxl import load_workbook\n\nwb = load_workbook(\"dashboard.xlsx\", read_only=True)\ninventory = {ws.title: ws.max_row for ws in wb.worksheets}\nwb.close()\n",[199,3468,3469,3479,3483,3504,3523],{"__ignoreMap":197},[202,3470,3471,3473,3475,3477],{"class":204,"line":205},[202,3472,594],{"class":234},[202,3474,597],{"class":238},[202,3476,235],{"class":234},[202,3478,602],{"class":238},[202,3480,3481],{"class":204,"line":248},[202,3482,252],{"emptyLinePlaceholder":251},[202,3484,3485,3487,3489,3491,3493,3495,3498,3500,3502],{"class":204,"line":255},[202,3486,611],{"class":238},[202,3488,261],{"class":234},[202,3490,616],{"class":238},[202,3492,509],{"class":212},[202,3494,285],{"class":238},[202,3496,3497],{"class":514},"read_only",[202,3499,261],{"class":234},[202,3501,1286],{"class":302},[202,3503,567],{"class":238},[202,3505,3506,3509,3511,3514,3516,3518,3520],{"class":204,"line":267},[202,3507,3508],{"class":238},"inventory ",[202,3510,261],{"class":234},[202,3512,3513],{"class":238}," {ws.title: ws.max_row ",[202,3515,423],{"class":234},[202,3517,1578],{"class":238},[202,3519,428],{"class":234},[202,3521,3522],{"class":238}," wb.worksheets}\n",[202,3524,3525],{"class":204,"line":294},[202,3526,3527],{"class":238},"wb.close()\n",[10,3529,3530],{},"Use that to plan the index, then do the single write pass that produces it. And note the practical ceiling on the whole idea: an index earns its place from about four sheets upwards, and stops helping beyond about thirty — at that point the workbook is a database, and the answer is a filterable table rather than a longer list of links.",[187,3532,3534],{"id":3533},"conclusion","Conclusion",[10,3536,3537,3538,3540],{},"An index sheet turns a pile of tabs into a document with a front page. The syntax is a location starting with ",[199,3539,583],{},", with the sheet name in single quotes whenever it is not a plain identifier — and an apostrophe inside it doubled. Style the cells so readers can see they are links, put a back-link in every sheet so navigation works both ways, and generate the list by walking the workbook so it cannot drift out of date. Order the sheets the way a reader wants them rather than the order they were written, and build the index during the original write when you can, so no second parse is needed.",[187,3542,3544],{"id":3543},"frequently-asked-questions","Frequently asked questions",[10,3546,3547,3550,3551,3553,3554,3556,3557,3560],{},[2389,3548,3549],{},"What is the syntax for a link to another sheet?","\nA location beginning with ",[199,3552,583],{}," and the sheet reference, such as ",[199,3555,2045],{},". openpyxl writes it through ",[199,3558,3559],{},"cell.hyperlink",", and Excel treats it as an internal jump rather than a web link.",[10,3562,3563,3566,3567,3569],{},[2389,3564,3565],{},"How do I link to a sheet whose name has a space?","\nWrap the sheet name in single quotes inside the reference — ",[199,3568,2058],{},". Without the quotes Excel cannot resolve the reference and the link does nothing.",[10,3571,3572,3575,3576,3578,3579,3581],{},[2389,3573,3574],{},"Why does my link look like plain text?","\nSetting ",[199,3577,3559],{}," creates the link but does not style it. Apply the built-in ",[199,3580,2782],{}," style, or set a blue underlined font yourself, so readers can see it is clickable.",[10,3583,3584,3587],{},[2389,3585,3586],{},"Should every sheet link back to the index?","\nYes, in cell A1 or just above the data. A workbook where you can reach the index from anywhere is far easier to navigate than one where you have to find the tab.",[10,3589,3590,3593],{},[2389,3591,3592],{},"Can I generate the index automatically?","\nYes, and you should. Walk the workbook's sheets, skip the ones you do not want listed, and build a row per sheet with its name, a description and its row count — so the index cannot drift out of date.",[187,3595,3597],{"id":3596},"related","Related",[3599,3600,3601,3608,3615,3622,3628],"ul",{},[3602,3603,3604,3605,3607],"li",{},"Up to the parent: ",[14,3606,17],{"href":16}," — the workbook this indexes.",[3602,3609,3610,3614],{},[14,3611,3613],{"href":3612},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002F","Add a Summary Sheet to an Excel Report with Python"," — the sheet the index should list first.",[3602,3616,3617,3621],{},[14,3618,3620],{"href":3619},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File"," — the single-pass write the index fits into.",[3602,3623,3624,3627],{},[14,3625,3626],{"href":1977},"Hide Sheets, Rows and Columns with openpyxl"," — keeping working sheets out of the index.",[3602,3629,3630,3634],{},[14,3631,3633],{"href":3632},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Frename-reorder-and-delete-excel-sheets-with-openpyxl\u002F","Rename, Reorder and Delete Excel Sheets with openpyxl"," — the ordering operations used here.",[3636,3637,3638],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":197,"searchDepth":248,"depth":248,"links":3640},[3641,3642,3643,3644,3645,3646,3647,3648,3649,3650],{"id":189,"depth":248,"text":190},{"id":576,"depth":248,"text":577},{"id":1019,"depth":248,"text":1020},{"id":2001,"depth":248,"text":2002},{"id":2452,"depth":248,"text":2453},{"id":2728,"depth":248,"text":2729},{"id":2865,"depth":248,"text":2866},{"id":3533,"depth":248,"text":3534},{"id":3543,"depth":248,"text":3544},{"id":3596,"depth":248,"text":3597},"2026-08-15","Give a multi-sheet workbook a front door — an index sheet with internal hyperlinks, row counts and back-links, generated from whatever sheets the workbook happens to contain.","md",[3655,3657,3659,3661,3663],{"q":3549,"a":3656},"A location beginning with a hash and the sheet reference, such as hash Summary exclamation A1. openpyxl writes it through cell.hyperlink, and Excel treats it as an internal jump rather than a web link.",{"q":3565,"a":3658},"Wrap the sheet name in single quotes inside the reference — hash apostrophe Q3 Detail apostrophe exclamation A1. Without the quotes Excel cannot resolve the reference and the link does nothing.",{"q":3574,"a":3660},"Setting cell.hyperlink creates the link but does not style it. Apply the built-in Hyperlink style, or set a blue underlined font yourself, so readers can see it is clickable.",{"q":3586,"a":3662},"Yes, in cell A1 or just above the data. A workbook where you can reach the index from anywhere is far easier to navigate than one where you have to find the tab.",{"q":3592,"a":3664},"Yes, and you should. Walk the workbook's sheets, skip the ones you do not want listed, and build a row per sheet with its name, a description and its row count — so the index cannot drift out of date.",{},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel",{"title":3668,"description":3669},"Excel Table of Contents Sheet with Links (Python)","Build an index sheet in openpyxl: internal hyperlinks with the hash-quote syntax, escaping sheet names with spaces, back-links on every sheet, and a generated summary.","add-a-table-of-contents-sheet-with-hyperlinks-in-excel","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel\u002Findex","how-to","6F8cAV7hXAm17NYaPBwyDoQIU6jnUblFuGx_GvznI34",[3675,3678],{"title":17,"path":3676,"stem":3677,"children":-1},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Findex",{"title":3613,"path":3679,"stem":3680,"children":-1},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002Findex",1786800029137]