[{"data":1,"prerenderedAt":2698},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph":3,"surround:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph":2670,"ancestors:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph":2679,"children:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph":2684,"related:\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph":2685},{"id":4,"title":5,"body":6,"dateModified":2638,"datePublished":2638,"description":2639,"extension":2640,"faq":2641,"keyword":2650,"kind":2651,"meta":2652,"navigation":126,"path":2660,"seo":2661,"slug":2664,"stem":2665,"topics":2666,"type":2668,"__hash__":2669},"docs\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph\u002Findex.md","Add, Rename and Delete Worksheets with Microsoft Graph",{"type":7,"value":8,"toc":2627},"minimark",[9,24,29,58,78,82,742,776,780,783,1095,1120,1249,1253,1550,1602,1732,1736,1742,2306,2328,2493,2497,2532,2536,2562,2566,2572,2578,2584,2590,2594,2623],[10,11,12,13,17,18,23],"p",{},"Workbooks that live in OneDrive or SharePoint and are updated by automation often change shape over time: a new sheet for each month, last month's ",[14,15,16],"em",{},"Current"," sheet renamed to its period, sheets older than a year hidden or removed. Downloading the file, changing it with openpyxl and uploading it again would work, but it risks overwriting edits people make in Excel for the web in the meantime and drops features openpyxl does not support. Microsoft Graph's workbook API changes the sheets in place, the same way a person would in the browser. This page lists a workbook's worksheets, adds one, renames, reorders and hides sheets, deletes old ones, groups the changes in a workbook session, validates names against Excel's rules before sending them, and puts it all together as a monthly rotation job — tested against a mocked Graph API so the requests can be checked without a tenant. It belongs to ",[19,20,22],"a",{"href":21},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002F","Automating Excel Online with Microsoft Graph",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install requests responses\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," requests",[40,56,57],{"class":50}," responses\n",[10,59,60,61,64,65,68,69,73,74,77],{},"You need a Graph token with ",[37,62,63],{},"Files.ReadWrite"," (or ",[37,66,67],{},"Files.ReadWrite.All"," for application access) and the drive and item IDs of the workbook; getting a token is covered in ",[19,70,72],{"href":71},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation\u002F","Authenticate to Microsoft Graph with MSAL for Excel Automation",". The outputs on this page come from a mock of the Graph workbook API built with the ",[37,75,76],{},"responses"," library that keeps a list of sheets and applies each request to it, so the sequence of calls and their effect can be shown and checked without a tenant.",[25,79,81],{"id":80},"step-1-a-small-client-with-a-workbook-session","Step 1 — A small client with a workbook session",[30,83,87],{"className":84,"code":85,"language":86,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import os\nimport requests\nfrom urllib.parse import quote\n\nGRAPH = \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\nBOOK = f\"{GRAPH}\u002Fdrives\u002Fb!reports-drive\u002Fitems\u002F01TRACKER\u002Fworkbook\"\n\nclass Workbook:\n    def __init__(self, token):\n        self.s = requests.Session()\n        self.s.headers[\"Authorization\"] = f\"Bearer {token}\"\n\n    def __enter__(self):\n        r = self.s.post(f\"{BOOK}\u002FcreateSession\", json={\"persistChanges\": True}, timeout=60)\n        r.raise_for_status()\n        self.s.headers[\"workbook-session-id\"] = r.json()[\"id\"]\n        return self\n\n    def __exit__(self, *exc):\n        self.s.post(f\"{BOOK}\u002FcloseSession\", timeout=60)\n\n    def call(self, method, path, **kw):\n        r = self.s.request(method, f\"{BOOK}\u002F{path}\", timeout=60, **kw)\n        if r.status_code >= 400:\n            err = r.json()[\"error\"]\n            raise RuntimeError(f\"{method} {path}: {err['code']}: {err['message']}\")\n        return r.json() if r.content else None\n\n    def sheets(self):\n        return self.call(\"GET\", \"worksheets\")[\"value\"]\n\n    def ref(self, name):\n        return \"worksheets\u002F\" + quote(f\"'{name}'\")\n\nwith Workbook(os.environ[\"GRAPH_TOKEN\"]) as wb:\n    print([(s[\"position\"], s[\"name\"]) for s in wb.sheets()])\n","python",[37,88,89,99,107,121,128,141,167,172,184,196,211,242,247,258,320,326,349,358,363,380,410,415,433,482,499,514,576,596,601,611,637,642,653,684,689,710],{"__ignoreMap":35},[40,90,91,95],{"class":42,"line":43},[40,92,94],{"class":93},"s-kum","import",[40,96,98],{"class":97},"skGVy"," os\n",[40,100,102,104],{"class":42,"line":101},2,[40,103,94],{"class":93},[40,105,106],{"class":97}," requests\n",[40,108,110,113,116,118],{"class":42,"line":109},3,[40,111,112],{"class":93},"from",[40,114,115],{"class":97}," urllib.parse ",[40,117,94],{"class":93},[40,119,120],{"class":97}," quote\n",[40,122,124],{"class":42,"line":123},4,[40,125,127],{"emptyLinePlaceholder":126},true,"\n",[40,129,131,135,138],{"class":42,"line":130},5,[40,132,134],{"class":133},"sP0c6","GRAPH",[40,136,137],{"class":93}," =",[40,139,140],{"class":50}," \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\"\n",[40,142,144,147,149,152,155,159,161,164],{"class":42,"line":143},6,[40,145,146],{"class":133},"BOOK",[40,148,137],{"class":93},[40,150,151],{"class":93}," f",[40,153,154],{"class":50},"\"",[40,156,158],{"class":157},"sSjpA","{",[40,160,134],{"class":133},[40,162,163],{"class":157},"}",[40,165,166],{"class":50},"\u002Fdrives\u002Fb!reports-drive\u002Fitems\u002F01TRACKER\u002Fworkbook\"\n",[40,168,170],{"class":42,"line":169},7,[40,171,127],{"emptyLinePlaceholder":126},[40,173,175,178,181],{"class":42,"line":174},8,[40,176,177],{"class":93},"class",[40,179,180],{"class":46}," Workbook",[40,182,183],{"class":97},":\n",[40,185,187,190,193],{"class":42,"line":186},9,[40,188,189],{"class":93},"    def",[40,191,192],{"class":133}," __init__",[40,194,195],{"class":97},"(self, token):\n",[40,197,199,202,205,208],{"class":42,"line":198},10,[40,200,201],{"class":133},"        self",[40,203,204],{"class":97},".s ",[40,206,207],{"class":93},"=",[40,209,210],{"class":97}," requests.Session()\n",[40,212,214,216,219,222,225,227,229,232,234,237,239],{"class":42,"line":213},11,[40,215,201],{"class":133},[40,217,218],{"class":97},".s.headers[",[40,220,221],{"class":50},"\"Authorization\"",[40,223,224],{"class":97},"] ",[40,226,207],{"class":93},[40,228,151],{"class":93},[40,230,231],{"class":50},"\"Bearer ",[40,233,158],{"class":157},[40,235,236],{"class":97},"token",[40,238,163],{"class":157},[40,240,241],{"class":50},"\"\n",[40,243,245],{"class":42,"line":244},12,[40,246,127],{"emptyLinePlaceholder":126},[40,248,250,252,255],{"class":42,"line":249},13,[40,251,189],{"class":93},[40,253,254],{"class":133}," __enter__",[40,256,257],{"class":97},"(self):\n",[40,259,261,264,266,269,272,275,277,279,281,283,286,289,293,295,297,300,303,306,309,312,314,317],{"class":42,"line":260},14,[40,262,263],{"class":97},"        r ",[40,265,207],{"class":93},[40,267,268],{"class":133}," self",[40,270,271],{"class":97},".s.post(",[40,273,274],{"class":93},"f",[40,276,154],{"class":50},[40,278,158],{"class":157},[40,280,146],{"class":133},[40,282,163],{"class":157},[40,284,285],{"class":50},"\u002FcreateSession\"",[40,287,288],{"class":97},", ",[40,290,292],{"class":291},"sa561","json",[40,294,207],{"class":93},[40,296,158],{"class":97},[40,298,299],{"class":50},"\"persistChanges\"",[40,301,302],{"class":97},": ",[40,304,305],{"class":133},"True",[40,307,308],{"class":97},"}, ",[40,310,311],{"class":291},"timeout",[40,313,207],{"class":93},[40,315,316],{"class":133},"60",[40,318,319],{"class":97},")\n",[40,321,323],{"class":42,"line":322},15,[40,324,325],{"class":97},"        r.raise_for_status()\n",[40,327,329,331,333,336,338,340,343,346],{"class":42,"line":328},16,[40,330,201],{"class":133},[40,332,218],{"class":97},[40,334,335],{"class":50},"\"workbook-session-id\"",[40,337,224],{"class":97},[40,339,207],{"class":93},[40,341,342],{"class":97}," r.json()[",[40,344,345],{"class":50},"\"id\"",[40,347,348],{"class":97},"]\n",[40,350,352,355],{"class":42,"line":351},17,[40,353,354],{"class":93},"        return",[40,356,357],{"class":133}," self\n",[40,359,361],{"class":42,"line":360},18,[40,362,127],{"emptyLinePlaceholder":126},[40,364,366,368,371,374,377],{"class":42,"line":365},19,[40,367,189],{"class":93},[40,369,370],{"class":133}," __exit__",[40,372,373],{"class":97},"(self, ",[40,375,376],{"class":93},"*",[40,378,379],{"class":97},"exc):\n",[40,381,383,385,387,389,391,393,395,397,400,402,404,406,408],{"class":42,"line":382},20,[40,384,201],{"class":133},[40,386,271],{"class":97},[40,388,274],{"class":93},[40,390,154],{"class":50},[40,392,158],{"class":157},[40,394,146],{"class":133},[40,396,163],{"class":157},[40,398,399],{"class":50},"\u002FcloseSession\"",[40,401,288],{"class":97},[40,403,311],{"class":291},[40,405,207],{"class":93},[40,407,316],{"class":133},[40,409,319],{"class":97},[40,411,413],{"class":42,"line":412},21,[40,414,127],{"emptyLinePlaceholder":126},[40,416,418,420,424,427,430],{"class":42,"line":417},22,[40,419,189],{"class":93},[40,421,423],{"class":422},"s_Opv"," call",[40,425,426],{"class":97},"(self, method, path, ",[40,428,429],{"class":93},"**",[40,431,432],{"class":97},"kw):\n",[40,434,436,438,440,442,445,447,449,451,453,455,458,460,463,465,467,469,471,473,475,477,479],{"class":42,"line":435},23,[40,437,263],{"class":97},[40,439,207],{"class":93},[40,441,268],{"class":133},[40,443,444],{"class":97},".s.request(method, ",[40,446,274],{"class":93},[40,448,154],{"class":50},[40,450,158],{"class":157},[40,452,146],{"class":133},[40,454,163],{"class":157},[40,456,457],{"class":50},"\u002F",[40,459,158],{"class":157},[40,461,462],{"class":97},"path",[40,464,163],{"class":157},[40,466,154],{"class":50},[40,468,288],{"class":97},[40,470,311],{"class":291},[40,472,207],{"class":93},[40,474,316],{"class":133},[40,476,288],{"class":97},[40,478,429],{"class":93},[40,480,481],{"class":97},"kw)\n",[40,483,485,488,491,494,497],{"class":42,"line":484},24,[40,486,487],{"class":93},"        if",[40,489,490],{"class":97}," r.status_code ",[40,492,493],{"class":93},">=",[40,495,496],{"class":133}," 400",[40,498,183],{"class":97},[40,500,502,505,507,509,512],{"class":42,"line":501},25,[40,503,504],{"class":97},"            err ",[40,506,207],{"class":93},[40,508,342],{"class":97},[40,510,511],{"class":50},"\"error\"",[40,513,348],{"class":97},[40,515,517,520,523,526,528,530,532,535,537,540,542,544,546,548,551,554,557,559,561,563,565,568,570,572,574],{"class":42,"line":516},26,[40,518,519],{"class":93},"            raise",[40,521,522],{"class":133}," RuntimeError",[40,524,525],{"class":97},"(",[40,527,274],{"class":93},[40,529,154],{"class":50},[40,531,158],{"class":157},[40,533,534],{"class":97},"method",[40,536,163],{"class":157},[40,538,539],{"class":157}," {",[40,541,462],{"class":97},[40,543,163],{"class":157},[40,545,302],{"class":50},[40,547,158],{"class":157},[40,549,550],{"class":97},"err[",[40,552,553],{"class":50},"'code'",[40,555,556],{"class":97},"]",[40,558,163],{"class":157},[40,560,302],{"class":50},[40,562,158],{"class":157},[40,564,550],{"class":97},[40,566,567],{"class":50},"'message'",[40,569,556],{"class":97},[40,571,163],{"class":157},[40,573,154],{"class":50},[40,575,319],{"class":97},[40,577,579,581,584,587,590,593],{"class":42,"line":578},27,[40,580,354],{"class":93},[40,582,583],{"class":97}," r.json() ",[40,585,586],{"class":93},"if",[40,588,589],{"class":97}," r.content ",[40,591,592],{"class":93},"else",[40,594,595],{"class":133}," None\n",[40,597,599],{"class":42,"line":598},28,[40,600,127],{"emptyLinePlaceholder":126},[40,602,604,606,609],{"class":42,"line":603},29,[40,605,189],{"class":93},[40,607,608],{"class":422}," sheets",[40,610,257],{"class":97},[40,612,614,616,618,621,624,626,629,632,635],{"class":42,"line":613},30,[40,615,354],{"class":93},[40,617,268],{"class":133},[40,619,620],{"class":97},".call(",[40,622,623],{"class":50},"\"GET\"",[40,625,288],{"class":97},[40,627,628],{"class":50},"\"worksheets\"",[40,630,631],{"class":97},")[",[40,633,634],{"class":50},"\"value\"",[40,636,348],{"class":97},[40,638,640],{"class":42,"line":639},31,[40,641,127],{"emptyLinePlaceholder":126},[40,643,645,647,650],{"class":42,"line":644},32,[40,646,189],{"class":93},[40,648,649],{"class":422}," ref",[40,651,652],{"class":97},"(self, name):\n",[40,654,656,658,661,664,667,669,672,674,677,679,682],{"class":42,"line":655},33,[40,657,354],{"class":93},[40,659,660],{"class":50}," \"worksheets\u002F\"",[40,662,663],{"class":93}," +",[40,665,666],{"class":97}," quote(",[40,668,274],{"class":93},[40,670,671],{"class":50},"\"'",[40,673,158],{"class":157},[40,675,676],{"class":97},"name",[40,678,163],{"class":157},[40,680,681],{"class":50},"'\"",[40,683,319],{"class":97},[40,685,687],{"class":42,"line":686},34,[40,688,127],{"emptyLinePlaceholder":126},[40,690,692,695,698,701,704,707],{"class":42,"line":691},35,[40,693,694],{"class":93},"with",[40,696,697],{"class":97}," Workbook(os.environ[",[40,699,700],{"class":50},"\"GRAPH_TOKEN\"",[40,702,703],{"class":97},"]) ",[40,705,706],{"class":93},"as",[40,708,709],{"class":97}," wb:\n",[40,711,713,716,719,722,725,728,730,733,736,739],{"class":42,"line":712},36,[40,714,715],{"class":133},"    print",[40,717,718],{"class":97},"([(s[",[40,720,721],{"class":50},"\"position\"",[40,723,724],{"class":97},"], s[",[40,726,727],{"class":50},"\"name\"",[40,729,703],{"class":97},[40,731,732],{"class":93},"for",[40,734,735],{"class":97}," s ",[40,737,738],{"class":93},"in",[40,740,741],{"class":97}," wb.sheets()])\n",[10,743,744,745,748,749,752,753,756,757,760,761,764,765,768,769,771,772,23],{},"A ",[14,746,747],{},"workbook session"," — ",[37,750,751],{},"createSession"," with ",[37,754,755],{},"persistChanges: True"," — groups the following requests on the server: Excel for the web opens the workbook once instead of for every call, which is faster, and the changes are saved together. Every request in the session carries the ",[37,758,759],{},"workbook-session-id"," header. Worksheets can be addressed by ID or by name; names are put in single quotes and URL-encoded, so ",[37,762,763],{},"2026-09"," or a name with spaces is safe in the path. The tracker workbook starts with an ",[14,766,767],{},"Instructions"," sheet, a ",[14,770,16],{}," sheet and twelve monthly sheets from September 2025 to August 2026. Sessions in more depth are covered in ",[19,773,775],{"href":774},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Frecalculate-excel-online-workbooks-with-graph-sessions\u002F","Recalculate Excel Online Workbooks with Graph Sessions",[25,777,779],{"id":778},"step-2-validate-names-before-sending-them","Step 2 — Validate names before sending them",[10,781,782],{},"Excel restricts sheet names, and Graph rejects invalid ones. Checking locally gives a clearer error and saves a round trip:",[30,784,786],{"className":84,"code":785,"language":86,"meta":35,"style":35},"import re\n\ndef check_sheet_name(name, existing):\n    problems = []\n    if not 1 \u003C= len(name) \u003C= 31:\n        problems.append(\"must be 1–31 characters\")\n    if re.search(r\"[\\[\\]:*?\u002F\\\\]\", name):\n        problems.append(\"may not contain [ ] : * ? \u002F \\\\\")\n    if name.startswith(\"'\") or name.endswith(\"'\"):\n        problems.append(\"may not start or end with an apostrophe\")\n    if name.lower() == \"history\":\n        problems.append(\"'History' is reserved\")\n    if name.lower() in {n.lower() for n in existing}:\n        problems.append(\"already exists (names are case-insensitive)\")\n    return problems\n\nexisting = [\"Instructions\", \"Current\", \"2026-08\"]\nfor candidate in [\"2026-09\", \"Sales: Sept\", \"current\", \"A very long sheet name for September 2026\"]:\n    print(f\"{candidate!r:\u003C46} {check_sheet_name(candidate, existing) or 'ok'}\")\n",[37,787,788,795,799,810,820,848,858,890,903,927,936,951,960,981,990,998,1002,1027,1059],{"__ignoreMap":35},[40,789,790,792],{"class":42,"line":43},[40,791,94],{"class":93},[40,793,794],{"class":97}," re\n",[40,796,797],{"class":42,"line":101},[40,798,127],{"emptyLinePlaceholder":126},[40,800,801,804,807],{"class":42,"line":109},[40,802,803],{"class":93},"def",[40,805,806],{"class":422}," check_sheet_name",[40,808,809],{"class":97},"(name, existing):\n",[40,811,812,815,817],{"class":42,"line":123},[40,813,814],{"class":97},"    problems ",[40,816,207],{"class":93},[40,818,819],{"class":97}," []\n",[40,821,822,825,828,831,834,837,840,843,846],{"class":42,"line":130},[40,823,824],{"class":93},"    if",[40,826,827],{"class":93}," not",[40,829,830],{"class":133}," 1",[40,832,833],{"class":93}," \u003C=",[40,835,836],{"class":133}," len",[40,838,839],{"class":97},"(name) ",[40,841,842],{"class":93},"\u003C=",[40,844,845],{"class":133}," 31",[40,847,183],{"class":97},[40,849,850,853,856],{"class":42,"line":143},[40,851,852],{"class":97},"        problems.append(",[40,854,855],{"class":50},"\"must be 1–31 characters\"",[40,857,319],{"class":97},[40,859,860,862,865,868,870,873,877,880,883,885,887],{"class":42,"line":169},[40,861,824],{"class":93},[40,863,864],{"class":97}," re.search(",[40,866,867],{"class":93},"r",[40,869,154],{"class":50},[40,871,872],{"class":133},"[",[40,874,876],{"class":875},"s_b0D","\\[\\]",[40,878,879],{"class":157},":*?\u002F",[40,881,882],{"class":875},"\\\\",[40,884,556],{"class":133},[40,886,154],{"class":50},[40,888,889],{"class":97},", name):\n",[40,891,892,894,897,899,901],{"class":42,"line":174},[40,893,852],{"class":97},[40,895,896],{"class":50},"\"may not contain [ ] : * ? \u002F ",[40,898,882],{"class":157},[40,900,154],{"class":50},[40,902,319],{"class":97},[40,904,905,907,910,913,916,919,922,924],{"class":42,"line":186},[40,906,824],{"class":93},[40,908,909],{"class":97}," name.startswith(",[40,911,912],{"class":50},"\"'\"",[40,914,915],{"class":97},") ",[40,917,918],{"class":93},"or",[40,920,921],{"class":97}," name.endswith(",[40,923,912],{"class":50},[40,925,926],{"class":97},"):\n",[40,928,929,931,934],{"class":42,"line":198},[40,930,852],{"class":97},[40,932,933],{"class":50},"\"may not start or end with an apostrophe\"",[40,935,319],{"class":97},[40,937,938,940,943,946,949],{"class":42,"line":213},[40,939,824],{"class":93},[40,941,942],{"class":97}," name.lower() ",[40,944,945],{"class":93},"==",[40,947,948],{"class":50}," \"history\"",[40,950,183],{"class":97},[40,952,953,955,958],{"class":42,"line":244},[40,954,852],{"class":97},[40,956,957],{"class":50},"\"'History' is reserved\"",[40,959,319],{"class":97},[40,961,962,964,966,968,971,973,976,978],{"class":42,"line":249},[40,963,824],{"class":93},[40,965,942],{"class":97},[40,967,738],{"class":93},[40,969,970],{"class":97}," {n.lower() ",[40,972,732],{"class":93},[40,974,975],{"class":97}," n ",[40,977,738],{"class":93},[40,979,980],{"class":97}," existing}:\n",[40,982,983,985,988],{"class":42,"line":260},[40,984,852],{"class":97},[40,986,987],{"class":50},"\"already exists (names are case-insensitive)\"",[40,989,319],{"class":97},[40,991,992,995],{"class":42,"line":322},[40,993,994],{"class":93},"    return",[40,996,997],{"class":97}," problems\n",[40,999,1000],{"class":42,"line":328},[40,1001,127],{"emptyLinePlaceholder":126},[40,1003,1004,1007,1009,1012,1015,1017,1020,1022,1025],{"class":42,"line":351},[40,1005,1006],{"class":97},"existing ",[40,1008,207],{"class":93},[40,1010,1011],{"class":97}," [",[40,1013,1014],{"class":50},"\"Instructions\"",[40,1016,288],{"class":97},[40,1018,1019],{"class":50},"\"Current\"",[40,1021,288],{"class":97},[40,1023,1024],{"class":50},"\"2026-08\"",[40,1026,348],{"class":97},[40,1028,1029,1031,1034,1036,1038,1041,1043,1046,1048,1051,1053,1056],{"class":42,"line":360},[40,1030,732],{"class":93},[40,1032,1033],{"class":97}," candidate ",[40,1035,738],{"class":93},[40,1037,1011],{"class":97},[40,1039,1040],{"class":50},"\"2026-09\"",[40,1042,288],{"class":97},[40,1044,1045],{"class":50},"\"Sales: Sept\"",[40,1047,288],{"class":97},[40,1049,1050],{"class":50},"\"current\"",[40,1052,288],{"class":97},[40,1054,1055],{"class":50},"\"A very long sheet name for September 2026\"",[40,1057,1058],{"class":97},"]:\n",[40,1060,1061,1063,1065,1067,1069,1071,1074,1077,1079,1081,1084,1086,1089,1091,1093],{"class":42,"line":365},[40,1062,715],{"class":133},[40,1064,525],{"class":97},[40,1066,274],{"class":93},[40,1068,154],{"class":50},[40,1070,158],{"class":157},[40,1072,1073],{"class":97},"candidate",[40,1075,1076],{"class":93},"!r:\u003C46",[40,1078,163],{"class":157},[40,1080,539],{"class":157},[40,1082,1083],{"class":97},"check_sheet_name(candidate, existing) ",[40,1085,918],{"class":93},[40,1087,1088],{"class":50}," 'ok'",[40,1090,163],{"class":157},[40,1092,154],{"class":50},[40,1094,319],{"class":97},[10,1096,1097,1098,1101,1102,748,1105,1108,1109,1111,1112,1115,1116,23],{},"Sheet names are limited to 31 characters, may not contain ",[37,1099,1100],{},"[ ] : * ? \u002F \\",", may not begin or end with an apostrophe, and must be unique ",[14,1103,1104],{},"ignoring case",[37,1106,1107],{},"current"," clashes with ",[37,1110,16],{},". ",[14,1113,1114],{},"History"," is reserved by Excel. The same rules apply to sheets created with openpyxl, as covered in ",[19,1117,1119],{"href":1118},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-invalid-worksheet-name-errors\u002F","Fix Invalid Worksheet Name Errors",[1121,1122,1130,1131,1130,1135,1130,1139,1130,1147,1130,1155,1130,1162,1130,1169,1130,1172,1130,1176,1130,1181,1130,1186,1130,1188,1130,1193,1130,1195,1130,1198,1130,1201,1130,1205,1130,1207,1130,1211,1130,1213,1130,1216,1130,1219,1130,1222,1130,1224,1130,1228,1130,1230,1130,1232,1130,1235,1130,1239,1130,1241,1130,1245,1130,1247],"svg",{"viewBox":1123,"role":1124,"ariaLabelledBy":1125,"xmlns":1128,"style":1129},"4 0 752 208","img",[1126,1127],"gw-names-t","gw-names-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[1132,1133,1134],"title",{"id":1126},"Excel's rules for sheet names",[1136,1137,1138],"desc",{"id":1127},"A sheet name must be 1 to 31 characters, may not contain square brackets, colon, asterisk, question mark or slashes, may not start or end with an apostrophe, may not be History, and must be unique regardless of case.",[1140,1141],"rect",{"x":1142,"y":1143,"width":1144,"height":1145,"fill":1146},"4","0","752","208","#ffffff",[1140,1148],{"x":1149,"y":1150,"width":1151,"height":1152,"rx":1143,"fill":1146,"stroke":1153,"style":1154},"20.0","16.0","152.8","40.0","var(--line,#cdd5e6)","stroke-width:1px",[1140,1156],{"x":1157,"y":1150,"width":1158,"height":1152,"rx":1143,"fill":1159,"stroke":1160,"style":1161},"172.8","283.6","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.2px",[1163,1164,1168],"text",{"x":1165,"y":1166,"style":1167},"314.6","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","example",[1140,1170],{"x":1171,"y":1150,"width":1158,"height":1152,"rx":1143,"fill":1159,"stroke":1160,"style":1161},"456.4",[1163,1173,1175],{"x":1174,"y":1166,"style":1167},"598.2","result",[1140,1177],{"x":1149,"y":1178,"width":1151,"height":1179,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"56.0","34.0","#e7ebef",[1163,1182,763],{"x":1183,"y":1184,"style":1185},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start",[1140,1187],{"x":1157,"y":1178,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1189,1192],{"x":1165,"y":1190,"style":1191},"77.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","fine",[1140,1194],{"x":1171,"y":1178,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1196,1197],{"x":1174,"y":1190,"style":1191},"ok",[1140,1199],{"x":1149,"y":1200,"width":1151,"height":1179,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"90.0",[1163,1202,1204],{"x":1183,"y":1203,"style":1185},"112.0","Sales: Sept",[1140,1206],{"x":1157,"y":1200,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1208,1210],{"x":1165,"y":1209,"style":1191},"111.0","contains a colon",[1140,1212],{"x":1171,"y":1200,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1214,1215],{"x":1174,"y":1209,"style":1191},"rejected",[1140,1217],{"x":1149,"y":1218,"width":1151,"height":1179,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"124.0",[1163,1220,1107],{"x":1183,"y":1221,"style":1185},"146.0",[1140,1223],{"x":1157,"y":1218,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1225,1227],{"x":1165,"y":1226,"style":1191},"145.0","same as Current",[1140,1229],{"x":1171,"y":1218,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1231,1215],{"x":1174,"y":1226,"style":1191},[1140,1233],{"x":1149,"y":1234,"width":1151,"height":1179,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"158.0",[1163,1236,1238],{"x":1183,"y":1237,"style":1185},"180.0","40-character name",[1140,1240],{"x":1157,"y":1234,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1242,1244],{"x":1165,"y":1243,"style":1191},"179.0","over 31 characters",[1140,1246],{"x":1171,"y":1234,"width":1158,"height":1179,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1248,1215],{"x":1174,"y":1243,"style":1191},[25,1250,1252],{"id":1251},"step-3-add-rename-move-hide-and-delete","Step 3 — Add, rename, move, hide and delete",[30,1254,1256],{"className":84,"code":1255,"language":86,"meta":35,"style":35},"with Workbook(os.environ[\"GRAPH_TOKEN\"]) as wb:\n    names = [s[\"name\"] for s in wb.sheets()]\n    assert not check_sheet_name(\"2026-09\", names)\n    wb.call(\"PATCH\", wb.ref(\"Current\"), json={\"name\": \"2026-09\"})                 # last month's working sheet keeps its period\n    new = wb.call(\"POST\", \"worksheets\u002Fadd\", json={\"name\": \"Current\"})            # a fresh working sheet\n    wb.call(\"PATCH\", wb.ref(\"Current\"), json={\"position\": 1})                     # straight after Instructions\n    wb.call(\"PATCH\", wb.ref(\"2025-09\"), json={\"visibility\": \"Hidden\"})            # older than a year: hide\n    try:\n        wb.call(\"POST\", \"worksheets\u002Fadd\", json={\"name\": \"Current\"})\n    except RuntimeError as e:\n        print(\"second add:\", e)\n    print([(s[\"position\"], s[\"name\"], s[\"visibility\"][0]) for s in wb.sheets()][:5], \"…\")\n",[37,1257,1258,1272,1295,1310,1345,1383,1414,1446,1453,1481,1494,1507],{"__ignoreMap":35},[40,1259,1260,1262,1264,1266,1268,1270],{"class":42,"line":43},[40,1261,694],{"class":93},[40,1263,697],{"class":97},[40,1265,700],{"class":50},[40,1267,703],{"class":97},[40,1269,706],{"class":93},[40,1271,709],{"class":97},[40,1273,1274,1277,1279,1282,1284,1286,1288,1290,1292],{"class":42,"line":101},[40,1275,1276],{"class":97},"    names ",[40,1278,207],{"class":93},[40,1280,1281],{"class":97}," [s[",[40,1283,727],{"class":50},[40,1285,224],{"class":97},[40,1287,732],{"class":93},[40,1289,735],{"class":97},[40,1291,738],{"class":93},[40,1293,1294],{"class":97}," wb.sheets()]\n",[40,1296,1297,1300,1302,1305,1307],{"class":42,"line":109},[40,1298,1299],{"class":93},"    assert",[40,1301,827],{"class":93},[40,1303,1304],{"class":97}," check_sheet_name(",[40,1306,1040],{"class":50},[40,1308,1309],{"class":97},", names)\n",[40,1311,1312,1315,1318,1321,1323,1326,1328,1330,1332,1334,1336,1338,1341],{"class":42,"line":123},[40,1313,1314],{"class":97},"    wb.call(",[40,1316,1317],{"class":50},"\"PATCH\"",[40,1319,1320],{"class":97},", wb.ref(",[40,1322,1019],{"class":50},[40,1324,1325],{"class":97},"), ",[40,1327,292],{"class":291},[40,1329,207],{"class":93},[40,1331,158],{"class":97},[40,1333,727],{"class":50},[40,1335,302],{"class":97},[40,1337,1040],{"class":50},[40,1339,1340],{"class":97},"})                 ",[40,1342,1344],{"class":1343},"s-wDw","# last month's working sheet keeps its period\n",[40,1346,1347,1350,1352,1355,1358,1360,1363,1365,1367,1369,1371,1373,1375,1377,1380],{"class":42,"line":130},[40,1348,1349],{"class":97},"    new ",[40,1351,207],{"class":93},[40,1353,1354],{"class":97}," wb.call(",[40,1356,1357],{"class":50},"\"POST\"",[40,1359,288],{"class":97},[40,1361,1362],{"class":50},"\"worksheets\u002Fadd\"",[40,1364,288],{"class":97},[40,1366,292],{"class":291},[40,1368,207],{"class":93},[40,1370,158],{"class":97},[40,1372,727],{"class":50},[40,1374,302],{"class":97},[40,1376,1019],{"class":50},[40,1378,1379],{"class":97},"})            ",[40,1381,1382],{"class":1343},"# a fresh working sheet\n",[40,1384,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405,1408,1411],{"class":42,"line":143},[40,1386,1314],{"class":97},[40,1388,1317],{"class":50},[40,1390,1320],{"class":97},[40,1392,1019],{"class":50},[40,1394,1325],{"class":97},[40,1396,292],{"class":291},[40,1398,207],{"class":93},[40,1400,158],{"class":97},[40,1402,721],{"class":50},[40,1404,302],{"class":97},[40,1406,1407],{"class":133},"1",[40,1409,1410],{"class":97},"})                     ",[40,1412,1413],{"class":1343},"# straight after Instructions\n",[40,1415,1416,1418,1420,1422,1425,1427,1429,1431,1433,1436,1438,1441,1443],{"class":42,"line":169},[40,1417,1314],{"class":97},[40,1419,1317],{"class":50},[40,1421,1320],{"class":97},[40,1423,1424],{"class":50},"\"2025-09\"",[40,1426,1325],{"class":97},[40,1428,292],{"class":291},[40,1430,207],{"class":93},[40,1432,158],{"class":97},[40,1434,1435],{"class":50},"\"visibility\"",[40,1437,302],{"class":97},[40,1439,1440],{"class":50},"\"Hidden\"",[40,1442,1379],{"class":97},[40,1444,1445],{"class":1343},"# older than a year: hide\n",[40,1447,1448,1451],{"class":42,"line":174},[40,1449,1450],{"class":93},"    try",[40,1452,183],{"class":97},[40,1454,1455,1458,1460,1462,1464,1466,1468,1470,1472,1474,1476,1478],{"class":42,"line":186},[40,1456,1457],{"class":97},"        wb.call(",[40,1459,1357],{"class":50},[40,1461,288],{"class":97},[40,1463,1362],{"class":50},[40,1465,288],{"class":97},[40,1467,292],{"class":291},[40,1469,207],{"class":93},[40,1471,158],{"class":97},[40,1473,727],{"class":50},[40,1475,302],{"class":97},[40,1477,1019],{"class":50},[40,1479,1480],{"class":97},"})\n",[40,1482,1483,1486,1488,1491],{"class":42,"line":198},[40,1484,1485],{"class":93},"    except",[40,1487,522],{"class":133},[40,1489,1490],{"class":93}," as",[40,1492,1493],{"class":97}," e:\n",[40,1495,1496,1499,1501,1504],{"class":42,"line":213},[40,1497,1498],{"class":133},"        print",[40,1500,525],{"class":97},[40,1502,1503],{"class":50},"\"second add:\"",[40,1505,1506],{"class":97},", e)\n",[40,1508,1509,1511,1513,1515,1517,1519,1521,1523,1526,1528,1530,1532,1534,1536,1539,1542,1545,1548],{"class":42,"line":244},[40,1510,715],{"class":133},[40,1512,718],{"class":97},[40,1514,721],{"class":50},[40,1516,724],{"class":97},[40,1518,727],{"class":50},[40,1520,724],{"class":97},[40,1522,1435],{"class":50},[40,1524,1525],{"class":97},"][",[40,1527,1143],{"class":133},[40,1529,703],{"class":97},[40,1531,732],{"class":93},[40,1533,735],{"class":97},[40,1535,738],{"class":93},[40,1537,1538],{"class":97}," wb.sheets()][:",[40,1540,1541],{"class":133},"5",[40,1543,1544],{"class":97},"], ",[40,1546,1547],{"class":50},"\"…\"",[40,1549,319],{"class":97},[10,1551,1552,1553,1556,1557,1559,1560,1563,1564,752,1566,1569,1570,1111,1572,1575,1576,288,1579,1582,1583,1586,1587,1589,1590,1592,1593,1595,1596,1598,1599,1601],{},"Renaming is a ",[37,1554,1555],{},"PATCH"," with the new ",[37,1558,676],{},"; the sheet keeps its content, formatting and any formulas pointing at it, which Excel updates to the new name. ",[37,1561,1562],{},"worksheets\u002Fadd"," creates a sheet at the end, and a second ",[37,1565,1555],{},[37,1567,1568],{},"position"," moves it — positions count from zero, so 1 is straight after ",[14,1571,767],{},[37,1573,1574],{},"visibility"," takes ",[37,1577,1578],{},"Visible",[37,1580,1581],{},"Hidden"," or ",[37,1584,1585],{},"VeryHidden","; hidden sheets are kept, with all their data, out of the way of readers. Adding a sheet whose name already exists fails with an error object carrying a code and a message — the mock returns one in Graph's error format — which the client turns into one readable line. The renamed ",[14,1588,763],{}," sheet stays where ",[14,1591,16],{}," used to be, now just after the new ",[14,1594,16],{},"; if the monthly sheets should be in date order, one more ",[37,1597,1555],{}," with a ",[37,1600,1568],{}," files it where it belongs.",[1121,1603,1130,1609,1130,1612,1130,1615,1130,1620,1130,1624,1130,1628,1130,1634,1130,1638,1130,1642,1130,1646,1130,1650,1130,1654,1130,1658,1130,1662,1130,1666,1130,1669,1130,1673,1130,1675,1130,1678,1130,1680,1130,1682,1130,1684,1130,1687,1130,1689,1130,1692,1130,1694,1130,1696,1130,1699,1130,1702,1130,1704,1130,1707,1130,1712,1130,1716,1130,1718,1130,1722,1130,1724,1130,1727,1130,1729],{"viewBox":1604,"role":1124,"ariaLabelledBy":1605,"xmlns":1128,"style":1608},"65 0 629 112",[1606,1607],"gw-tabs-t","gw-tabs-d","width:100%;max-width:629px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[1132,1610,1611],{"id":1606},"The tab order after the changes",[1136,1613,1614],{"id":1607},"Instructions stays first, the new Current sheet is moved to position 1, the renamed 2026-09 sheet follows it, 2025-09 is hidden and the remaining monthly sheets follow in order.",[1140,1616],{"x":1617,"y":1143,"width":1618,"height":1619,"fill":1146},"65","629","112",[1140,1621],{"x":1622,"y":1150,"width":1183,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"81.9","24.0",[1140,1625],{"x":1626,"y":1150,"width":1627,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"113.9","98.1",[1163,1629,1633],{"x":1630,"y":1631,"style":1632},"163.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[1140,1635],{"x":1636,"y":1150,"width":1637,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"212.0","110.0",[1163,1639,1641],{"x":1640,"y":1631,"style":1632},"267.0","B",[1140,1643],{"x":1644,"y":1150,"width":1645,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"322.0","146.4",[1163,1647,1649],{"x":1648,"y":1631,"style":1632},"395.1","C",[1140,1651],{"x":1652,"y":1150,"width":1653,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"468.3","133.8",[1163,1655,1657],{"x":1656,"y":1631,"style":1632},"535.2","D",[1140,1659],{"x":1660,"y":1150,"width":1661,"height":1623,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"602.1","76.0",[1163,1663,1665],{"x":1664,"y":1631,"style":1632},"640.1","E",[1140,1667],{"x":1622,"y":1152,"width":1183,"height":1668,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"28.0",[1163,1670,1407],{"x":1671,"y":1672,"style":1632},"97.9","59.0",[1140,1674],{"x":1626,"y":1152,"width":1627,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1676,1143],{"x":1630,"y":1672,"style":1677},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle",[1140,1679],{"x":1636,"y":1152,"width":1637,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1681,1407],{"x":1640,"y":1672,"style":1677},[1140,1683],{"x":1644,"y":1152,"width":1645,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1685,1686],{"x":1648,"y":1672,"style":1677},"2",[1140,1688],{"x":1652,"y":1152,"width":1653,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1690,1691],{"x":1656,"y":1672,"style":1677},"3",[1140,1693],{"x":1660,"y":1152,"width":1661,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1695,1142],{"x":1664,"y":1672,"style":1677},[1140,1697],{"x":1622,"y":1698,"width":1183,"height":1668,"rx":1143,"fill":1180,"stroke":1153,"style":1154},"68.0",[1163,1700,1686],{"x":1671,"y":1701,"style":1632},"87.0",[1140,1703],{"x":1626,"y":1698,"width":1627,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1705,767],{"x":1630,"y":1701,"style":1706},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[1140,1708],{"x":1636,"y":1698,"width":1637,"height":1668,"rx":1143,"fill":1709,"stroke":1710,"style":1711},"#d9f4f1","var(--teal,#0f9488)","stroke-width:1.5px",[1163,1713,1715],{"x":1640,"y":1701,"style":1714},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Current (new)",[1140,1717],{"x":1644,"y":1698,"width":1645,"height":1668,"rx":1143,"fill":1159,"stroke":1160,"style":1711},[1163,1719,1721],{"x":1648,"y":1701,"style":1720},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","2026-09 (renamed)",[1140,1723],{"x":1652,"y":1698,"width":1653,"height":1668,"rx":1143,"fill":1180,"stroke":1153,"style":1711},[1163,1725,1726],{"x":1656,"y":1701,"style":1677},"2025-09 (hidden)",[1140,1728],{"x":1660,"y":1698,"width":1661,"height":1668,"rx":1143,"fill":1146,"stroke":1153,"style":1154},[1163,1730,1731],{"x":1664,"y":1701,"style":1706},"2025-10",[25,1733,1735],{"id":1734},"step-4-a-monthly-rotation-job","Step 4 — A monthly rotation job",[10,1737,1738,1739,1741],{},"Put it together: once a month, keep last month's sheet under its period name, start a fresh ",[14,1740,16],{}," sheet, hide sheets older than a year and delete those older than two:",[30,1743,1745],{"className":84,"code":1744,"language":86,"meta":35,"style":35},"from datetime import date\n\ndef months_old(name, today):\n    y, m = map(int, name.split(\"-\"))\n    return (today.year - y) * 12 + today.month - m\n\ndef rotate(token, today):\n    period = f\"{today.year}-{today.month - 1:02d}\" if today.month > 1 else f\"{today.year - 1}-12\"\n    with Workbook(token) as wb:\n        names = [s[\"name\"] for s in wb.sheets()]\n        if period in names:\n            return f\"{period} already rotated — nothing to do\"\n        wb.call(\"PATCH\", wb.ref(\"Current\"), json={\"name\": period})\n        wb.call(\"POST\", \"worksheets\u002Fadd\", json={\"name\": \"Current\"})\n        wb.call(\"PATCH\", wb.ref(\"Current\"), json={\"position\": 1})\n        for s in wb.sheets():\n            if re.fullmatch(r\"\\d{4}-\\d{2}\", s[\"name\"]):\n                age = months_old(s[\"name\"], today)\n                if age > 24:\n                    wb.call(\"DELETE\", wb.ref(s[\"name\"]))\n                elif age > 12 and s[\"visibility\"] == \"Visible\":\n                    wb.call(\"PATCH\", wb.ref(s[\"name\"]), json={\"visibility\": \"Hidden\"})\n        return [s[\"name\"] for s in wb.sheets() if s[\"visibility\"] == \"Visible\"]\n\ncalls.clear()\nprint(rotate(os.environ[\"GRAPH_TOKEN\"], date(2026, 11, 1)))\nprint(rotate(os.environ[\"GRAPH_TOKEN\"], date(2026, 11, 1)))\nprint([c[:2] for c in calls])\n",[37,1746,1747,1759,1763,1773,1797,1825,1829,1839,1906,1918,1939,1951,1970,1993,2019,2045,2057,2092,2107,2122,2138,2166,2193,2226,2230,2235,2263,2285],{"__ignoreMap":35},[40,1748,1749,1751,1754,1756],{"class":42,"line":43},[40,1750,112],{"class":93},[40,1752,1753],{"class":97}," datetime ",[40,1755,94],{"class":93},[40,1757,1758],{"class":97}," date\n",[40,1760,1761],{"class":42,"line":101},[40,1762,127],{"emptyLinePlaceholder":126},[40,1764,1765,1767,1770],{"class":42,"line":109},[40,1766,803],{"class":93},[40,1768,1769],{"class":422}," months_old",[40,1771,1772],{"class":97},"(name, today):\n",[40,1774,1775,1778,1780,1783,1785,1788,1791,1794],{"class":42,"line":123},[40,1776,1777],{"class":97},"    y, m ",[40,1779,207],{"class":93},[40,1781,1782],{"class":133}," map",[40,1784,525],{"class":97},[40,1786,1787],{"class":133},"int",[40,1789,1790],{"class":97},", name.split(",[40,1792,1793],{"class":50},"\"-\"",[40,1795,1796],{"class":97},"))\n",[40,1798,1799,1801,1804,1807,1810,1812,1815,1817,1820,1822],{"class":42,"line":130},[40,1800,994],{"class":93},[40,1802,1803],{"class":97}," (today.year ",[40,1805,1806],{"class":93},"-",[40,1808,1809],{"class":97}," y) ",[40,1811,376],{"class":93},[40,1813,1814],{"class":133}," 12",[40,1816,663],{"class":93},[40,1818,1819],{"class":97}," today.month ",[40,1821,1806],{"class":93},[40,1823,1824],{"class":97}," m\n",[40,1826,1827],{"class":42,"line":143},[40,1828,127],{"emptyLinePlaceholder":126},[40,1830,1831,1833,1836],{"class":42,"line":169},[40,1832,803],{"class":93},[40,1834,1835],{"class":422}," rotate",[40,1837,1838],{"class":97},"(token, today):\n",[40,1840,1841,1844,1846,1848,1850,1852,1855,1857,1859,1861,1864,1866,1868,1871,1873,1875,1878,1880,1883,1885,1888,1890,1892,1894,1897,1899,1901,1903],{"class":42,"line":174},[40,1842,1843],{"class":97},"    period ",[40,1845,207],{"class":93},[40,1847,151],{"class":93},[40,1849,154],{"class":50},[40,1851,158],{"class":157},[40,1853,1854],{"class":97},"today.year",[40,1856,163],{"class":157},[40,1858,1806],{"class":50},[40,1860,158],{"class":157},[40,1862,1863],{"class":97},"today.month ",[40,1865,1806],{"class":93},[40,1867,830],{"class":133},[40,1869,1870],{"class":93},":02d",[40,1872,163],{"class":157},[40,1874,154],{"class":50},[40,1876,1877],{"class":93}," if",[40,1879,1819],{"class":97},[40,1881,1882],{"class":93},">",[40,1884,830],{"class":133},[40,1886,1887],{"class":93}," else",[40,1889,151],{"class":93},[40,1891,154],{"class":50},[40,1893,158],{"class":157},[40,1895,1896],{"class":97},"today.year ",[40,1898,1806],{"class":93},[40,1900,830],{"class":133},[40,1902,163],{"class":157},[40,1904,1905],{"class":50},"-12\"\n",[40,1907,1908,1911,1914,1916],{"class":42,"line":186},[40,1909,1910],{"class":93},"    with",[40,1912,1913],{"class":97}," Workbook(token) ",[40,1915,706],{"class":93},[40,1917,709],{"class":97},[40,1919,1920,1923,1925,1927,1929,1931,1933,1935,1937],{"class":42,"line":198},[40,1921,1922],{"class":97},"        names ",[40,1924,207],{"class":93},[40,1926,1281],{"class":97},[40,1928,727],{"class":50},[40,1930,224],{"class":97},[40,1932,732],{"class":93},[40,1934,735],{"class":97},[40,1936,738],{"class":93},[40,1938,1294],{"class":97},[40,1940,1941,1943,1946,1948],{"class":42,"line":213},[40,1942,487],{"class":93},[40,1944,1945],{"class":97}," period ",[40,1947,738],{"class":93},[40,1949,1950],{"class":97}," names:\n",[40,1952,1953,1956,1958,1960,1962,1965,1967],{"class":42,"line":244},[40,1954,1955],{"class":93},"            return",[40,1957,151],{"class":93},[40,1959,154],{"class":50},[40,1961,158],{"class":157},[40,1963,1964],{"class":97},"period",[40,1966,163],{"class":157},[40,1968,1969],{"class":50}," already rotated — nothing to do\"\n",[40,1971,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990],{"class":42,"line":249},[40,1973,1457],{"class":97},[40,1975,1317],{"class":50},[40,1977,1320],{"class":97},[40,1979,1019],{"class":50},[40,1981,1325],{"class":97},[40,1983,292],{"class":291},[40,1985,207],{"class":93},[40,1987,158],{"class":97},[40,1989,727],{"class":50},[40,1991,1992],{"class":97},": period})\n",[40,1994,1995,1997,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017],{"class":42,"line":260},[40,1996,1457],{"class":97},[40,1998,1357],{"class":50},[40,2000,288],{"class":97},[40,2002,1362],{"class":50},[40,2004,288],{"class":97},[40,2006,292],{"class":291},[40,2008,207],{"class":93},[40,2010,158],{"class":97},[40,2012,727],{"class":50},[40,2014,302],{"class":97},[40,2016,1019],{"class":50},[40,2018,1480],{"class":97},[40,2020,2021,2023,2025,2027,2029,2031,2033,2035,2037,2039,2041,2043],{"class":42,"line":322},[40,2022,1457],{"class":97},[40,2024,1317],{"class":50},[40,2026,1320],{"class":97},[40,2028,1019],{"class":50},[40,2030,1325],{"class":97},[40,2032,292],{"class":291},[40,2034,207],{"class":93},[40,2036,158],{"class":97},[40,2038,721],{"class":50},[40,2040,302],{"class":97},[40,2042,1407],{"class":133},[40,2044,1480],{"class":97},[40,2046,2047,2050,2052,2054],{"class":42,"line":328},[40,2048,2049],{"class":93},"        for",[40,2051,735],{"class":97},[40,2053,738],{"class":93},[40,2055,2056],{"class":97}," wb.sheets():\n",[40,2058,2059,2062,2065,2067,2069,2072,2075,2077,2079,2082,2084,2087,2089],{"class":42,"line":351},[40,2060,2061],{"class":93},"            if",[40,2063,2064],{"class":97}," re.fullmatch(",[40,2066,867],{"class":93},[40,2068,154],{"class":50},[40,2070,2071],{"class":133},"\\d",[40,2073,2074],{"class":93},"{4}",[40,2076,1806],{"class":50},[40,2078,2071],{"class":133},[40,2080,2081],{"class":93},"{2}",[40,2083,154],{"class":50},[40,2085,2086],{"class":97},", s[",[40,2088,727],{"class":50},[40,2090,2091],{"class":97},"]):\n",[40,2093,2094,2097,2099,2102,2104],{"class":42,"line":360},[40,2095,2096],{"class":97},"                age ",[40,2098,207],{"class":93},[40,2100,2101],{"class":97}," months_old(s[",[40,2103,727],{"class":50},[40,2105,2106],{"class":97},"], today)\n",[40,2108,2109,2112,2115,2117,2120],{"class":42,"line":365},[40,2110,2111],{"class":93},"                if",[40,2113,2114],{"class":97}," age ",[40,2116,1882],{"class":93},[40,2118,2119],{"class":133}," 24",[40,2121,183],{"class":97},[40,2123,2124,2127,2130,2133,2135],{"class":42,"line":382},[40,2125,2126],{"class":97},"                    wb.call(",[40,2128,2129],{"class":50},"\"DELETE\"",[40,2131,2132],{"class":97},", wb.ref(s[",[40,2134,727],{"class":50},[40,2136,2137],{"class":97},"]))\n",[40,2139,2140,2143,2145,2147,2149,2152,2155,2157,2159,2161,2164],{"class":42,"line":412},[40,2141,2142],{"class":93},"                elif",[40,2144,2114],{"class":97},[40,2146,1882],{"class":93},[40,2148,1814],{"class":133},[40,2150,2151],{"class":93}," and",[40,2153,2154],{"class":97}," s[",[40,2156,1435],{"class":50},[40,2158,224],{"class":97},[40,2160,945],{"class":93},[40,2162,2163],{"class":50}," \"Visible\"",[40,2165,183],{"class":97},[40,2167,2168,2170,2172,2174,2176,2179,2181,2183,2185,2187,2189,2191],{"class":42,"line":417},[40,2169,2126],{"class":97},[40,2171,1317],{"class":50},[40,2173,2132],{"class":97},[40,2175,727],{"class":50},[40,2177,2178],{"class":97},"]), ",[40,2180,292],{"class":291},[40,2182,207],{"class":93},[40,2184,158],{"class":97},[40,2186,1435],{"class":50},[40,2188,302],{"class":97},[40,2190,1440],{"class":50},[40,2192,1480],{"class":97},[40,2194,2195,2197,2199,2201,2203,2205,2207,2209,2212,2214,2216,2218,2220,2222,2224],{"class":42,"line":435},[40,2196,354],{"class":93},[40,2198,1281],{"class":97},[40,2200,727],{"class":50},[40,2202,224],{"class":97},[40,2204,732],{"class":93},[40,2206,735],{"class":97},[40,2208,738],{"class":93},[40,2210,2211],{"class":97}," wb.sheets() ",[40,2213,586],{"class":93},[40,2215,2154],{"class":97},[40,2217,1435],{"class":50},[40,2219,224],{"class":97},[40,2221,945],{"class":93},[40,2223,2163],{"class":50},[40,2225,348],{"class":97},[40,2227,2228],{"class":42,"line":484},[40,2229,127],{"emptyLinePlaceholder":126},[40,2231,2232],{"class":42,"line":501},[40,2233,2234],{"class":97},"calls.clear()\n",[40,2236,2237,2240,2243,2245,2248,2251,2253,2256,2258,2260],{"class":42,"line":516},[40,2238,2239],{"class":133},"print",[40,2241,2242],{"class":97},"(rotate(os.environ[",[40,2244,700],{"class":50},[40,2246,2247],{"class":97},"], date(",[40,2249,2250],{"class":133},"2026",[40,2252,288],{"class":97},[40,2254,2255],{"class":133},"11",[40,2257,288],{"class":97},[40,2259,1407],{"class":133},[40,2261,2262],{"class":97},")))\n",[40,2264,2265,2267,2269,2271,2273,2275,2277,2279,2281,2283],{"class":42,"line":578},[40,2266,2239],{"class":133},[40,2268,2242],{"class":97},[40,2270,700],{"class":50},[40,2272,2247],{"class":97},[40,2274,2250],{"class":133},[40,2276,288],{"class":97},[40,2278,2255],{"class":133},[40,2280,288],{"class":97},[40,2282,1407],{"class":133},[40,2284,2262],{"class":97},[40,2286,2287,2289,2292,2294,2296,2298,2301,2303],{"class":42,"line":598},[40,2288,2239],{"class":133},[40,2290,2291],{"class":97},"([c[:",[40,2293,1686],{"class":133},[40,2295,224],{"class":97},[40,2297,732],{"class":93},[40,2299,2300],{"class":97}," c ",[40,2302,738],{"class":93},[40,2304,2305],{"class":97}," calls])\n",[10,2307,2308,2309,2311,2312,2315,2316,2318,2319,2321,2322,2324,2325,2327],{},"On 1 November the job turns ",[14,2310,16],{}," into ",[14,2313,2314],{},"2026-10",", starts a new ",[14,2317,16],{}," sheet after the instructions and hides the month that has just passed a year old. Run again the same day it finds ",[14,2320,2314],{}," already present and does nothing — the job is safe to rerun, which matters for anything triggered by a scheduler. The call log shows exactly what was sent, all within one session: the rename, the add, the move and one ",[37,2323,1555],{}," hiding ",[14,2326,1731],{},", then on the second run only the session request before the job stops. Because the mock records every request, the same log is the basis of a unit test for the rotation logic — assert the sequence of calls for a given date and starting set of sheets, and the job's behaviour on the first of each month is pinned down without touching a real workbook. Deleting is the only irreversible step; keeping deletion at two years and hiding at one gives a long window to notice a mistake, and the workbook's version history in OneDrive covers the rest.",[1121,2329,1130,2334,1130,2337,1130,2340,1130,2343,1130,2349,1130,2359,1130,2365,1130,2369,1130,2373,1130,2378,1130,2383,1130,2388,1130,2395,1130,2400,1130,2403,1130,2406,1130,2410,1130,2413,1130,2416,1130,2423,1130,2429,1130,2433,1130,2437,1130,2440,1130,2444,1130,2447,1130,2450,1130,2457,1130,2462,1130,2465,1130,2468,1130,2472,1130,2475,1130,2478,1130,2484,1130,2488,1130,2490],{"viewBox":2330,"role":1124,"ariaLabelledBy":2331,"xmlns":1128,"style":1129},"4 0 752 160",[2332,2333],"gw-rotate-t","gw-rotate-d",[1132,2335,2336],{"id":2332},"The monthly sheet rotation",[1136,2338,2339],{"id":2333},"Open a workbook session, check whether last month has already been rotated, rename Current to last month's period, add a new Current sheet and move it after Instructions, hide monthly sheets older than a year and delete those older than two years, then close the session.",[1140,2341],{"x":1142,"y":1143,"width":1144,"height":2342,"fill":1146},"160",[1140,2344],{"x":1149,"y":1150,"width":2345,"height":2346,"rx":2347,"fill":1180,"stroke":1153,"style":2348},"115.2","128.0","12","stroke-width:2px",[2350,2351,2353],"g",{"transform":2352},"translate(65.6,37.8) scale(1.000)",[462,2354],{"d":2355,"fill":2356,"stroke":2357,"style":2358},"M8 11a4 4 0 1 0 .01 0z M11 13l9-9 M17 7l2 2 M15 9l2 2","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[1163,2360,2364],{"x":2361,"y":2362,"style":2363},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Open",[1163,2366,2368],{"x":2361,"y":2367,"style":2363},"101.8","session",[1163,2370,2372],{"x":2361,"y":2371,"style":1191},"121.2","persistChanges",[42,2374],{"x1":2375,"y1":2376,"x2":2377,"y2":2376,"stroke":1160,"style":2348},"140.2","80.0","162.2",[2379,2380],"polygon",{"points":2381,"fill":2382},"166.2,80.0 157.2,85.0 157.2,75.0","#5b5cf0",[1140,2384],{"x":2385,"y":1150,"width":2345,"height":2346,"rx":2347,"fill":2386,"stroke":2387,"style":2348},"171.2","#fdefd8","var(--gold,#b4740a)",[2350,2389,2391],{"transform":2390},"translate(216.8,37.8) scale(1.000)",[462,2392],{"d":2393,"fill":2356,"stroke":2394,"style":2358},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","var(--gold-ink,#7a4e06)",[1163,2396,2399],{"x":2397,"y":2362,"style":2398},"228.8","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Already",[1163,2401,2402],{"x":2397,"y":2367,"style":2398},"done?",[1163,2404,2405],{"x":2397,"y":2371,"style":1191},"safe to rerun",[42,2407],{"x1":2408,"y1":2376,"x2":2409,"y2":2376,"stroke":1160,"style":2348},"291.4","313.4",[2379,2411],{"points":2412,"fill":2382},"317.4,80.0 308.4,85.0 308.4,75.0",[1140,2414],{"x":2415,"y":1150,"width":2345,"height":2346,"rx":2347,"fill":1159,"stroke":1160,"style":2348},"322.4",[2350,2417,2419],{"transform":2418},"translate(368.0,29.0) scale(1.000)",[462,2420],{"d":2421,"fill":2356,"stroke":2422,"style":2358},"M20 11a8 8 0 1 0-2.3 5.7 M20 4v7h-7","var(--brand-strong,#4338ca)",[1163,2424,2428],{"x":2425,"y":2426,"style":2427},"380.0","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Rename +",[1163,2430,2432],{"x":2425,"y":2431,"style":2427},"93.0","add",[1163,2434,2436],{"x":2425,"y":2435,"style":1191},"112.5","Current →",[1163,2438,2314],{"x":2425,"y":2439,"style":1191},"130.0",[42,2441],{"x1":2442,"y1":2376,"x2":2443,"y2":2376,"stroke":1160,"style":2348},"442.6","464.6",[2379,2445],{"points":2446,"fill":2382},"468.6,80.0 459.6,85.0 459.6,75.0",[1140,2448],{"x":2449,"y":1150,"width":2345,"height":2346,"rx":2347,"fill":1709,"stroke":1710,"style":2348},"473.6",[2350,2451,2453],{"transform":2452},"translate(519.2,37.8) scale(1.000)",[462,2454],{"d":2455,"fill":2356,"stroke":2456,"style":2358},"M4 7h16 M10 11v6 M14 11v6 M6 7l1 13h10l1-13 M9 7V4h6v3","var(--teal-ink,#0b6157)",[1163,2458,2461],{"x":2459,"y":2362,"style":2460},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Hide > 12,",[1163,2463,2464],{"x":2459,"y":2367,"style":2460},"delete > 24",[1163,2466,2467],{"x":2459,"y":2371,"style":1191},"by period name",[42,2469],{"x1":2470,"y1":2376,"x2":2471,"y2":2376,"stroke":1160,"style":2348},"593.8","615.8",[2379,2473],{"points":2474,"fill":2382},"619.8,80.0 610.8,85.0 610.8,75.0",[1140,2476],{"x":2477,"y":1150,"width":2345,"height":2346,"rx":2347,"fill":1180,"stroke":1153,"style":2348},"624.8",[2350,2479,2481],{"transform":2480},"translate(670.4,37.8) scale(1.000)",[462,2482],{"d":2483,"fill":2356,"stroke":2357,"style":2358},"M5 12.5l4.5 4.5L19 7",[1163,2485,2487],{"x":2486,"y":2362,"style":2363},"682.4","Close",[1163,2489,2368],{"x":2486,"y":2367,"style":2363},[1163,2491,2492],{"x":2486,"y":2371,"style":1191},"changes saved",[25,2494,2496],{"id":2495},"common-pitfalls","Common pitfalls",[2498,2499,2500,2508,2514,2520,2526],"ul",{},[2501,2502,2503,2507],"li",{},[2504,2505,2506],"strong",{},"Download, edit, upload."," It can overwrite edits made online; change the workbook through the API.",[2501,2509,2510,2513],{},[2504,2511,2512],{},"Names unquoted in the path."," Quote and URL-encode sheet names, or use IDs.",[2501,2515,2516,2519],{},[2504,2517,2518],{},"Invalid or duplicate names."," Validate locally; names are unique ignoring case.",[2501,2521,2522,2525],{},[2504,2523,2524],{},"No session for many changes."," Group calls in a workbook session.",[2501,2527,2528,2531],{},[2504,2529,2530],{},"Deleting too eagerly."," Hide first; delete only well past retention.",[25,2533,2535],{"id":2534},"conclusion","Conclusion",[10,2537,2538,2539,2542,2543,2546,2547,2549,2550,288,2552,2554,2555,2557,2558,2561],{},"Microsoft Graph manages worksheets in place: ",[37,2540,2541],{},"GET worksheets"," lists them, ",[37,2544,2545],{},"POST worksheets\u002Fadd"," creates one, ",[37,2548,1555],{}," changes ",[37,2551,676],{},[37,2553,1568],{}," and ",[37,2556,1574],{},", and ",[37,2559,2560],{},"DELETE"," removes one. Group the calls in a workbook session, address sheets by quoted, URL-encoded name or by ID, check names against Excel's rules first, and build rotation jobs that check what has already been done so they are safe to rerun.",[25,2563,2565],{"id":2564},"frequently-asked-questions","Frequently asked questions",[10,2567,2568,2571],{},[2504,2569,2570],{},"How do I add a worksheet with Microsoft Graph?","\nPOST to \u002Fworkbook\u002Fworksheets\u002Fadd on the drive item with a JSON body containing the new sheet's name.",[10,2573,2574,2577],{},[2504,2575,2576],{},"How do I rename or move a worksheet through Graph?","\nPATCH the worksheet, addressed by ID or quoted name, with a body containing name for a rename or position for a move.",[10,2579,2580,2583],{},[2504,2581,2582],{},"How do I hide a worksheet with Microsoft Graph?","\nPATCH the worksheet with visibility set to Hidden, or VeryHidden to hide it from Excel's Unhide list.",[10,2585,2586,2589],{},[2504,2587,2588],{},"Why use a workbook session for several changes?","\nA session keeps the workbook open on the server between calls, which is faster and saves the changes together.",[25,2591,2593],{"id":2592},"related","Related",[2498,2595,2596,2602,2609,2616],{},[2501,2597,2598,2599,2601],{},"Up one level: ",[19,2600,22],{"href":21}," — Excel in the cloud from Python.",[2501,2603,2604,2608],{},[19,2605,2607],{"href":2606},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fread-and-write-excel-online-ranges-with-microsoft-graph\u002F","Read and Write Excel Online Ranges with Microsoft Graph"," — filling the new sheet.",[2501,2610,2611,2615],{},[19,2612,2614],{"href":2613},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph\u002F","Copy a Template Workbook in SharePoint with Microsoft Graph"," — whole new workbooks instead of sheets.",[2501,2617,2618,2622],{},[19,2619,2621],{"href":2620},"\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 same for local files.",[2624,2625,2626],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":35,"searchDepth":101,"depth":101,"links":2628},[2629,2630,2631,2632,2633,2634,2635,2636,2637],{"id":27,"depth":101,"text":28},{"id":80,"depth":101,"text":81},{"id":778,"depth":101,"text":779},{"id":1251,"depth":101,"text":1252},{"id":1734,"depth":101,"text":1735},{"id":2495,"depth":101,"text":2496},{"id":2534,"depth":101,"text":2535},{"id":2564,"depth":101,"text":2565},{"id":2592,"depth":101,"text":2593},"2026-10-02","Manage the sheets of an Excel workbook in OneDrive or SharePoint from Python with Microsoft Graph: list worksheets, add a sheet, rename and reorder it, hide old ones and delete those past their retention — all inside a workbook session — with sheet names validated before the request, and a monthly rotation job tested against a mocked API.","md",[2642,2644,2646,2648],{"q":2570,"a":2643},"POST to \u002Fworkbook\u002Fworksheets\u002Fadd on the drive item with a JSON body containing the new sheet's name.",{"q":2576,"a":2645},"PATCH the worksheet, addressed by ID or quoted name, with a body containing name for a rename or position for a move.",{"q":2582,"a":2647},"PATCH the worksheet with visibility set to Hidden, or VeryHidden to hide it from Excel's Unhide list.",{"q":2588,"a":2649},"A session keeps the workbook open on the server between calls, which is faster and saves the changes together.","add rename delete worksheets microsoft graph",null,{"breadcrumb":2653},[2654,2656,2659],{"name":2655,"item":457},"Home",{"name":2657,"item":2658},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":22,"item":21},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph",{"title":2662,"description":2663},"Add, Rename and Delete Excel Worksheets with Microsoft Graph","Worksheet management in Excel Online from Python: GET worksheets, worksheets\u002Fadd, PATCH name, position and visibility, DELETE, workbook sessions, Excel sheet-name rules and a monthly sheet rotation, tested with responses.","add-rename-and-delete-worksheets-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-rename-and-delete-worksheets-with-microsoft-graph\u002Findex",[2667],"excel-online","how-to","xiv1sI6mlZtm5uTIlWvyd4T8nbXgpN2OY1GIggRf28c",[2671,2675],{"title":2672,"path":2673,"stem":2674,"children":-1},"Add a Chart to an Excel Online Workbook with Microsoft Graph","\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-a-chart-to-an-excel-online-workbook-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fadd-a-chart-to-an-excel-online-workbook-with-microsoft-graph\u002Findex",{"title":2676,"path":2677,"stem":2678,"children":-1},"Append Rows to an Excel Online Table with Microsoft Graph","\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fappend-rows-to-an-excel-online-table-with-microsoft-graph","automating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fappend-rows-to-an-excel-online-table-with-microsoft-graph\u002Findex",[2680,2682],{"path":2681,"title":22},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph",{"path":2683,"title":2657},"\u002Fautomating-reporting-workflows",[],[2686,2689,2690,2691,2693,2696],{"path":2687,"title":2688,"kind":2668},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002Fsend-an-excel-report-with-microsoft-graph-api","Send an Excel Report with the Microsoft Graph API",{"path":2673,"title":2672,"kind":2668},{"path":2677,"title":2676,"kind":2668},{"path":2692,"title":72,"kind":2668},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fauthenticate-to-microsoft-graph-with-msal-for-excel-automation",{"path":2694,"title":2695,"kind":2668},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcall-excel-worksheet-functions-through-microsoft-graph","Call Excel Worksheet Functions Through Microsoft Graph",{"path":2697,"title":2614,"kind":2668},"\u002Fautomating-reporting-workflows\u002Fautomating-excel-online-with-microsoft-graph\u002Fcopy-a-template-workbook-in-sharepoint-with-microsoft-graph",1790972118869]