[{"data":1,"prerenderedAt":1693},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-python-excel-script-on-windows-task-scheduler":3,"surround:\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-python-excel-script-on-windows-task-scheduler":1686},{"id":4,"title":5,"body":6,"dateModified":1665,"datePublished":1665,"description":1666,"extension":1667,"faq":1668,"meta":1677,"navigation":296,"path":1678,"seo":1679,"slug":1682,"stem":1683,"type":1684,"__hash__":1685},"docs\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-python-excel-script-on-windows-task-scheduler\u002Findex.md","Run a Python Excel Script on Windows Task Scheduler",{"type":7,"value":8,"toc":1650},"minimark",[9,37,171,176,186,208,215,219,233,856,859,868,872,892,917,928,932,943,946,961,964,978,981,995,1001,1006,1020,1039,1048,1052,1245,1249,1291,1295,1298,1307,1312,1380,1383,1392,1403,1407,1509,1527,1531,1541,1558,1579,1589,1593,1624,1628,1646],[10,11,12,13,17,18,22,23,26,27,32,33,36],"p",{},"Windows has no ",[14,15,16],"code",{},"cron",", so the unattended scheduling you'd do on Linux happens through ",[19,20,21],"strong",{},"Task Scheduler"," instead. This page registers a Python report script to run on a fixed schedule using ",[14,24,25],{},"schtasks",", the command-line interface to Task Scheduler, so you can version the command and recreate the task on any machine. It is the Windows companion to ",[28,29,31],"a",{"href":30},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002F","Scheduling Python Excel Scripts with Cron","; the report logic is identical, only the trigger mechanism changes. By the end you'll have a task that runs whether or not anyone is logged on, writes ",[14,34,35],{},"daily_summary.xlsx",", and logs every run.",[38,39,47,48,47,52,47,56,47,66,47,73,47,78,47,83,47,89,47,93,47,96,47,99,47,103,47,107,47,111,47,115,47,120,47,124,47,128,47,132,47,137,47,143,47,147,47,151,47,154,47,158,47,161,47,167],"svg",{"viewBox":40,"role":41,"ariaLabelledBy":42,"xmlns":45,"style":46},"0 0 760 232","img",[43,44],"wts-t","wts-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[49,50,51],"title",{"id":43},"How Windows Task Scheduler runs a Python Excel report",[53,54,55],"desc",{"id":44},"A daily or weekly time trigger registered with schtasks tells Task Scheduler to launch the virtualenv python.exe by absolute path, which runs the report script and writes daily_summary.xlsx.",[57,58],"rect",{"x":59,"y":60,"width":61,"height":62,"rx":63,"fill":64,"stroke":65},"20","58","160","92","14","var(--brand-soft,rgba(91,92,240,0.12))","var(--line,#cdd5e6)",[67,68,72],"text",{"x":69,"y":70,"style":71},"100","40","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","Trigger",[67,74,77],{"x":69,"y":75,"style":76},"98","font-size:15px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Daily \u002F Weekly",[67,79,82],{"x":69,"y":80,"style":81},"121","font-size:12.5px;fill:var(--muted,#5b6780);text-anchor:middle","e.g. 07:00",[57,84],{"x":85,"y":60,"width":61,"height":62,"rx":63,"fill":86,"stroke":87,"style":88},"227","var(--surface-muted,#eef2ff)","var(--brand,#5b5cf0)","stroke-width:2px",[67,90,92],{"x":91,"y":70,"style":71},"307","Register",[67,94,25],{"x":91,"y":75,"style":95},"font-size:15px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[67,97,98],{"x":91,"y":80,"style":81},"\u002FCreate",[57,100],{"x":101,"y":60,"width":61,"height":62,"rx":63,"fill":87,"stroke":102},"434","var(--brand-strong,#4338ca)",[67,104,106],{"x":105,"y":70,"style":71},"514","Run",[67,108,110],{"x":105,"y":75,"style":109},"font-size:14.5px;font-weight:700;fill:#ffffff;text-anchor:middle","venv python.exe",[67,112,114],{"x":105,"y":80,"style":113},"font-size:12px;fill:rgba(255,255,255,0.88);text-anchor:middle","absolute path",[57,116],{"x":117,"y":60,"width":69,"height":62,"rx":63,"fill":118,"stroke":119,"style":88},"641","rgba(15,148,136,0.12)","var(--teal,#0f9488)",[67,121,123],{"x":122,"y":70,"style":71},"691","Output",[67,125,127],{"x":122,"y":75,"style":126},"font-size:13px;font-weight:700;fill:var(--teal,#0f9488);text-anchor:middle","daily_",[67,129,131],{"x":122,"y":130,"style":126},"116","summary",[67,133,136],{"x":122,"y":134,"style":135},"133","font-size:11.5px;fill:var(--muted,#5b6780);text-anchor:middle",".xlsx",[138,139],"line",{"x1":140,"y1":141,"x2":142,"y2":141,"stroke":87,"style":88},"182","104","223",[144,145],"polygon",{"points":146,"fill":87},"223,104 213,99 213,109",[138,148],{"x1":149,"y1":141,"x2":150,"y2":141,"stroke":87,"style":88},"389","430",[144,152],{"points":153,"fill":87},"430,104 420,99 420,109",[138,155],{"x1":156,"y1":141,"x2":157,"y2":141,"stroke":119,"style":88},"596","637",[144,159],{"points":160,"fill":119},"637,104 627,99 627,109",[67,162,166],{"x":163,"y":164,"style":165},"380","186","font-size:12px;fill:var(--muted,#5b6780);text-anchor:middle","Task Scheduler owns the schedule and fires whether or not anyone is logged on.",[67,168,170],{"x":163,"y":169,"style":165},"208","A .bat wrapper captures stdout and stderr to a log on every run.",[172,173,175],"h2",{"id":174},"prerequisites","Prerequisites",[177,178,179,183],"ul",{},[180,181,182],"li",{},"Windows 10\u002F11 or Windows Server with administrator access (needed to run a task while logged off).",[180,184,185],{},"Python installed with a virtualenv. Create one and install the report dependencies:",[187,188,193],"pre",{"className":189,"code":190,"language":191,"meta":192,"style":192},"language-bat shiki shiki-themes github-light github-dark","python -m venv C:\\reporting\\venv\nC:\\reporting\\venv\\Scripts\\pip install pandas openpyxl\n","bat","",[14,194,195,202],{"__ignoreMap":192},[196,197,199],"span",{"class":138,"line":198},1,[196,200,201],{},"python -m venv C:\\reporting\\venv\n",[196,203,205],{"class":138,"line":204},2,[196,206,207],{},"C:\\reporting\\venv\\Scripts\\pip install pandas openpyxl\n",[10,209,210,211,214],{},"Note the absolute path ",[14,212,213],{},"C:\\reporting\\venv\\Scripts\\python.exe",". Task Scheduler does not activate virtualenvs, so you call that interpreter directly — exactly as you'd call the venv Python by absolute path under cron.",[172,216,218],{"id":217},"a-self-contained-report-script","A self-contained report script",[10,220,221,222,225,226,228,229,232],{},"Save this as ",[14,223,224],{},"C:\\reporting\\generate_daily_report.py",". It uses absolute paths, seeds sample data if none exists, writes ",[14,227,35],{},", and exits non-zero on failure so the task's ",[19,230,231],{},"Last Run Result"," reflects an error:",[187,234,238],{"className":235,"code":236,"language":237,"meta":192,"style":192},"language-python shiki shiki-themes github-light github-dark","\"\"\"Scheduled Excel report: build data, summarize, write daily_summary.xlsx.\"\"\"\nimport sys\nimport logging\nfrom datetime import datetime\nfrom pathlib import Path\n\nimport pandas as pd\n\n# Absolute paths: Task Scheduler's working directory is C:\\Windows\\System32.\nBASE_DIR = Path(r\"C:\\reporting\")\nOUTPUT_DIR = BASE_DIR \u002F \"output\"\nLOG_DIR = BASE_DIR \u002F \"logs\"\nfor d in (OUTPUT_DIR, LOG_DIR):\n    d.mkdir(parents=True, exist_ok=True)\n\nlogging.basicConfig(\n    filename=LOG_DIR \u002F f\"report_{datetime.now():%Y%m%d}.log\",\n    level=logging.INFO,\n    format=\"%(asctime)s | %(levelname)s | %(message)s\",\n)\n\ndef main():\n    logging.info(\"Starting daily Excel report.\")\n    try:\n        # Build sample data so the run is self-contained.\n        df = pd.DataFrame({\n            \"region\": [\"North\", \"South\", \"North\", \"East\", \"South\"],\n            \"revenue\": [1200.0, 980.5, 1450.0, 610.25, 980.5],\n        })\n        summary = df.groupby(\"region\", as_index=False)[\"revenue\"].sum()\n\n        out = OUTPUT_DIR \u002F \"daily_summary.xlsx\"\n        summary.to_excel(out, index=False, engine=\"openpyxl\")\n        logging.info(\"Wrote %d rows to %s\", len(summary), out)\n    except Exception:\n        logging.exception(\"Report generation failed.\")\n        sys.exit(1)\n    logging.info(\"Done.\")\n\nif __name__ == \"__main__\":\n    main()\n","python",[14,239,240,246,256,264,278,291,298,312,317,324,359,376,391,416,443,448,454,503,519,547,552,557,570,581,590,596,607,640,672,678,711,716,732,757,785,796,807,818,828,833,850],{"__ignoreMap":192},[196,241,242],{"class":138,"line":198},[196,243,245],{"class":244},"sZZnC","\"\"\"Scheduled Excel report: build data, summarize, write daily_summary.xlsx.\"\"\"\n",[196,247,248,252],{"class":138,"line":204},[196,249,251],{"class":250},"szBVR","import",[196,253,255],{"class":254},"sVt8B"," sys\n",[196,257,259,261],{"class":138,"line":258},3,[196,260,251],{"class":250},[196,262,263],{"class":254}," logging\n",[196,265,267,270,273,275],{"class":138,"line":266},4,[196,268,269],{"class":250},"from",[196,271,272],{"class":254}," datetime ",[196,274,251],{"class":250},[196,276,277],{"class":254}," datetime\n",[196,279,281,283,286,288],{"class":138,"line":280},5,[196,282,269],{"class":250},[196,284,285],{"class":254}," pathlib ",[196,287,251],{"class":250},[196,289,290],{"class":254}," Path\n",[196,292,294],{"class":138,"line":293},6,[196,295,297],{"emptyLinePlaceholder":296},true,"\n",[196,299,301,303,306,309],{"class":138,"line":300},7,[196,302,251],{"class":250},[196,304,305],{"class":254}," pandas ",[196,307,308],{"class":250},"as",[196,310,311],{"class":254}," pd\n",[196,313,315],{"class":138,"line":314},8,[196,316,297],{"emptyLinePlaceholder":296},[196,318,320],{"class":138,"line":319},9,[196,321,323],{"class":322},"sJ8bj","# Absolute paths: Task Scheduler's working directory is C:\\Windows\\System32.\n",[196,325,327,331,334,337,340,343,347,351,354,356],{"class":138,"line":326},10,[196,328,330],{"class":329},"sj4cs","BASE_DIR",[196,332,333],{"class":250}," =",[196,335,336],{"class":254}," Path(",[196,338,339],{"class":250},"r",[196,341,342],{"class":244},"\"",[196,344,346],{"class":345},"sA_wV","C:",[196,348,350],{"class":349},"snhLl","\\r",[196,352,353],{"class":345},"eporting",[196,355,342],{"class":244},[196,357,358],{"class":254},")\n",[196,360,362,365,367,370,373],{"class":138,"line":361},11,[196,363,364],{"class":329},"OUTPUT_DIR",[196,366,333],{"class":250},[196,368,369],{"class":329}," BASE_DIR",[196,371,372],{"class":250}," \u002F",[196,374,375],{"class":244}," \"output\"\n",[196,377,379,382,384,386,388],{"class":138,"line":378},12,[196,380,381],{"class":329},"LOG_DIR",[196,383,333],{"class":250},[196,385,369],{"class":329},[196,387,372],{"class":250},[196,389,390],{"class":244}," \"logs\"\n",[196,392,394,397,400,403,406,408,411,413],{"class":138,"line":393},13,[196,395,396],{"class":250},"for",[196,398,399],{"class":254}," d ",[196,401,402],{"class":250},"in",[196,404,405],{"class":254}," (",[196,407,364],{"class":329},[196,409,410],{"class":254},", ",[196,412,381],{"class":329},[196,414,415],{"class":254},"):\n",[196,417,419,422,426,429,432,434,437,439,441],{"class":138,"line":418},14,[196,420,421],{"class":254},"    d.mkdir(",[196,423,425],{"class":424},"s4XuR","parents",[196,427,428],{"class":250},"=",[196,430,431],{"class":329},"True",[196,433,410],{"class":254},[196,435,436],{"class":424},"exist_ok",[196,438,428],{"class":250},[196,440,431],{"class":329},[196,442,358],{"class":254},[196,444,446],{"class":138,"line":445},15,[196,447,297],{"emptyLinePlaceholder":296},[196,449,451],{"class":138,"line":450},16,[196,452,453],{"class":254},"logging.basicConfig(\n",[196,455,457,460,462,464,466,469,472,475,478,481,484,486,489,491,494,497,500],{"class":138,"line":456},17,[196,458,459],{"class":424},"    filename",[196,461,428],{"class":250},[196,463,381],{"class":329},[196,465,372],{"class":250},[196,467,468],{"class":250}," f",[196,470,471],{"class":244},"\"report_",[196,473,474],{"class":329},"{",[196,476,477],{"class":254},"datetime.now():",[196,479,480],{"class":250},"%",[196,482,483],{"class":254},"Y",[196,485,480],{"class":250},[196,487,488],{"class":254},"m",[196,490,480],{"class":250},[196,492,493],{"class":254},"d",[196,495,496],{"class":329},"}",[196,498,499],{"class":244},".log\"",[196,501,502],{"class":254},",\n",[196,504,506,509,511,514,517],{"class":138,"line":505},18,[196,507,508],{"class":424},"    level",[196,510,428],{"class":250},[196,512,513],{"class":254},"logging.",[196,515,516],{"class":329},"INFO",[196,518,502],{"class":254},[196,520,522,525,527,529,532,535,538,540,543,545],{"class":138,"line":521},19,[196,523,524],{"class":424},"    format",[196,526,428],{"class":250},[196,528,342],{"class":244},[196,530,531],{"class":329},"%(asctime)s",[196,533,534],{"class":244}," | ",[196,536,537],{"class":329},"%(levelname)s",[196,539,534],{"class":244},[196,541,542],{"class":329},"%(message)s",[196,544,342],{"class":244},[196,546,502],{"class":254},[196,548,550],{"class":138,"line":549},20,[196,551,358],{"class":254},[196,553,555],{"class":138,"line":554},21,[196,556,297],{"emptyLinePlaceholder":296},[196,558,560,563,567],{"class":138,"line":559},22,[196,561,562],{"class":250},"def",[196,564,566],{"class":565},"sScJk"," main",[196,568,569],{"class":254},"():\n",[196,571,573,576,579],{"class":138,"line":572},23,[196,574,575],{"class":254},"    logging.info(",[196,577,578],{"class":244},"\"Starting daily Excel report.\"",[196,580,358],{"class":254},[196,582,584,587],{"class":138,"line":583},24,[196,585,586],{"class":250},"    try",[196,588,589],{"class":254},":\n",[196,591,593],{"class":138,"line":592},25,[196,594,595],{"class":322},"        # Build sample data so the run is self-contained.\n",[196,597,599,602,604],{"class":138,"line":598},26,[196,600,601],{"class":254},"        df ",[196,603,428],{"class":250},[196,605,606],{"class":254}," pd.DataFrame({\n",[196,608,610,613,616,619,621,624,626,628,630,633,635,637],{"class":138,"line":609},27,[196,611,612],{"class":244},"            \"region\"",[196,614,615],{"class":254},": [",[196,617,618],{"class":244},"\"North\"",[196,620,410],{"class":254},[196,622,623],{"class":244},"\"South\"",[196,625,410],{"class":254},[196,627,618],{"class":244},[196,629,410],{"class":254},[196,631,632],{"class":244},"\"East\"",[196,634,410],{"class":254},[196,636,623],{"class":244},[196,638,639],{"class":254},"],\n",[196,641,643,646,648,651,653,656,658,661,663,666,668,670],{"class":138,"line":642},28,[196,644,645],{"class":244},"            \"revenue\"",[196,647,615],{"class":254},[196,649,650],{"class":329},"1200.0",[196,652,410],{"class":254},[196,654,655],{"class":329},"980.5",[196,657,410],{"class":254},[196,659,660],{"class":329},"1450.0",[196,662,410],{"class":254},[196,664,665],{"class":329},"610.25",[196,667,410],{"class":254},[196,669,655],{"class":329},[196,671,639],{"class":254},[196,673,675],{"class":138,"line":674},29,[196,676,677],{"class":254},"        })\n",[196,679,681,684,686,689,692,694,697,699,702,705,708],{"class":138,"line":680},30,[196,682,683],{"class":254},"        summary ",[196,685,428],{"class":250},[196,687,688],{"class":254}," df.groupby(",[196,690,691],{"class":244},"\"region\"",[196,693,410],{"class":254},[196,695,696],{"class":424},"as_index",[196,698,428],{"class":250},[196,700,701],{"class":329},"False",[196,703,704],{"class":254},")[",[196,706,707],{"class":244},"\"revenue\"",[196,709,710],{"class":254},"].sum()\n",[196,712,714],{"class":138,"line":713},31,[196,715,297],{"emptyLinePlaceholder":296},[196,717,719,722,724,727,729],{"class":138,"line":718},32,[196,720,721],{"class":254},"        out ",[196,723,428],{"class":250},[196,725,726],{"class":329}," OUTPUT_DIR",[196,728,372],{"class":250},[196,730,731],{"class":244}," \"daily_summary.xlsx\"\n",[196,733,735,738,741,743,745,747,750,752,755],{"class":138,"line":734},33,[196,736,737],{"class":254},"        summary.to_excel(out, ",[196,739,740],{"class":424},"index",[196,742,428],{"class":250},[196,744,701],{"class":329},[196,746,410],{"class":254},[196,748,749],{"class":424},"engine",[196,751,428],{"class":250},[196,753,754],{"class":244},"\"openpyxl\"",[196,756,358],{"class":254},[196,758,760,763,766,769,772,775,777,779,782],{"class":138,"line":759},34,[196,761,762],{"class":254},"        logging.info(",[196,764,765],{"class":244},"\"Wrote ",[196,767,768],{"class":329},"%d",[196,770,771],{"class":244}," rows to ",[196,773,774],{"class":329},"%s",[196,776,342],{"class":244},[196,778,410],{"class":254},[196,780,781],{"class":329},"len",[196,783,784],{"class":254},"(summary), out)\n",[196,786,788,791,794],{"class":138,"line":787},35,[196,789,790],{"class":250},"    except",[196,792,793],{"class":329}," Exception",[196,795,589],{"class":254},[196,797,799,802,805],{"class":138,"line":798},36,[196,800,801],{"class":254},"        logging.exception(",[196,803,804],{"class":244},"\"Report generation failed.\"",[196,806,358],{"class":254},[196,808,810,813,816],{"class":138,"line":809},37,[196,811,812],{"class":254},"        sys.exit(",[196,814,815],{"class":329},"1",[196,817,358],{"class":254},[196,819,821,823,826],{"class":138,"line":820},38,[196,822,575],{"class":254},[196,824,825],{"class":244},"\"Done.\"",[196,827,358],{"class":254},[196,829,831],{"class":138,"line":830},39,[196,832,297],{"emptyLinePlaceholder":296},[196,834,836,839,842,845,848],{"class":138,"line":835},40,[196,837,838],{"class":250},"if",[196,840,841],{"class":329}," __name__",[196,843,844],{"class":250}," ==",[196,846,847],{"class":244}," \"__main__\"",[196,849,589],{"class":254},[196,851,853],{"class":138,"line":852},41,[196,854,855],{"class":254},"    main()\n",[10,857,858],{},"Confirm it runs from a normal prompt first:",[187,860,862],{"className":189,"code":861,"language":191,"meta":192,"style":192},"C:\\reporting\\venv\\Scripts\\python.exe C:\\reporting\\generate_daily_report.py\n",[14,863,864],{"__ignoreMap":192},[196,865,866],{"class":138,"line":198},[196,867,861],{},[172,869,871],{"id":870},"a-bat-wrapper-that-logs-stdout","A .bat wrapper that logs stdout",[10,873,874,875,878,879,882,883,887,888,891],{},"Task Scheduler can run ",[14,876,877],{},"python.exe"," directly, but a small ",[14,880,881],{},".bat"," wrapper gives you one place to capture stdout and stderr — the output that appears ",[884,885,886],"em",{},"before"," your in-script logging starts (a missing module, a syntax error). Save this as ",[14,889,890],{},"C:\\reporting\\run_report.bat",":",[187,893,895],{"className":189,"code":894,"language":191,"meta":192,"style":192},"@echo off\nREM Activate the venv, then run the report, appending all output to a log.\ncall C:\\reporting\\venv\\Scripts\\activate.bat\npython C:\\reporting\\generate_daily_report.py >> C:\\reporting\\logs\\stdout.log 2>&1\n",[14,896,897,902,907,912],{"__ignoreMap":192},[196,898,899],{"class":138,"line":198},[196,900,901],{},"@echo off\n",[196,903,904],{"class":138,"line":204},[196,905,906],{},"REM Activate the venv, then run the report, appending all output to a log.\n",[196,908,909],{"class":138,"line":258},[196,910,911],{},"call C:\\reporting\\venv\\Scripts\\activate.bat\n",[196,913,914],{"class":138,"line":266},[196,915,916],{},"python C:\\reporting\\generate_daily_report.py >> C:\\reporting\\logs\\stdout.log 2>&1\n",[10,918,919,920,923,924,927],{},"The ",[14,921,922],{},">> ... 2>&1"," redirect is the Windows equivalent of cron's ",[14,925,926],{},">> logfile 2>&1",": without it, interpreter-level errors go nowhere because Task Scheduler discards a task's console output.",[172,929,931],{"id":930},"register-the-task-with-schtasks","Register the task with schtasks",[10,933,934,935,938,939,942],{},"Open an ",[19,936,937],{},"elevated"," Command Prompt (Run as administrator). The ",[14,940,941],{},"^"," is the line-continuation character in batch — keep it on Windows, or put the whole command on one line.",[10,944,945],{},"Daily at 06:00:",[187,947,949],{"className":189,"code":948,"language":191,"meta":192,"style":192},"schtasks \u002FCreate \u002FTN \"DailyExcelReport\" \u002FSC DAILY \u002FST 06:00 ^\n  \u002FTR \"C:\\reporting\\run_report.bat\" \u002FRL HIGHEST \u002FF\n",[14,950,951,956],{"__ignoreMap":192},[196,952,953],{"class":138,"line":198},[196,954,955],{},"schtasks \u002FCreate \u002FTN \"DailyExcelReport\" \u002FSC DAILY \u002FST 06:00 ^\n",[196,957,958],{"class":138,"line":204},[196,959,960],{},"  \u002FTR \"C:\\reporting\\run_report.bat\" \u002FRL HIGHEST \u002FF\n",[10,962,963],{},"Weekdays only:",[187,965,967],{"className":189,"code":966,"language":191,"meta":192,"style":192},"schtasks \u002FCreate \u002FTN \"DailyExcelReport\" \u002FSC WEEKLY \u002FD MON,TUE,WED,THU,FRI \u002FST 07:30 ^\n  \u002FTR \"C:\\reporting\\run_report.bat\" \u002FRL HIGHEST \u002FF\n",[14,968,969,974],{"__ignoreMap":192},[196,970,971],{"class":138,"line":198},[196,972,973],{},"schtasks \u002FCreate \u002FTN \"DailyExcelReport\" \u002FSC WEEKLY \u002FD MON,TUE,WED,THU,FRI \u002FST 07:30 ^\n",[196,975,976],{"class":138,"line":204},[196,977,960],{},[10,979,980],{},"Every four hours:",[187,982,984],{"className":189,"code":983,"language":191,"meta":192,"style":192},"schtasks \u002FCreate \u002FTN \"HourlyExcelReport\" \u002FSC HOURLY \u002FMO 4 ^\n  \u002FTR \"C:\\reporting\\run_report.bat\" \u002FRL HIGHEST \u002FF\n",[14,985,986,991],{"__ignoreMap":192},[196,987,988],{"class":138,"line":198},[196,989,990],{},"schtasks \u002FCreate \u002FTN \"HourlyExcelReport\" \u002FSC HOURLY \u002FMO 4 ^\n",[196,992,993],{"class":138,"line":204},[196,994,960],{},[10,996,997,1000],{},[14,998,999],{},"\u002FF"," overwrites an existing task with the same name, which makes the command safe to rerun when you redeploy.",[1002,1003,1005],"h3",{"id":1004},"run-whether-or-not-the-user-is-logged-on","Run whether or not the user is logged on",[10,1007,1008,1009,1012,1013,1016,1017,891],{},"By default a task only fires while its user is interactively logged on. To run it regardless — the usual requirement for a server — supply credentials with ",[14,1010,1011],{},"\u002FRU"," and ",[14,1014,1015],{},"\u002FRP",", and request the highest privileges with ",[14,1018,1019],{},"\u002FRL HIGHEST",[187,1021,1023],{"className":189,"code":1022,"language":191,"meta":192,"style":192},"schtasks \u002FCreate \u002FTN \"DailyExcelReport\" \u002FSC DAILY \u002FST 06:00 ^\n  \u002FTR \"C:\\reporting\\run_report.bat\" ^\n  \u002FRU \"DOMAIN\\svc_reports\" \u002FRP \"the-password\" \u002FRL HIGHEST \u002FF\n",[14,1024,1025,1029,1034],{"__ignoreMap":192},[196,1026,1027],{"class":138,"line":198},[196,1028,955],{},[196,1030,1031],{"class":138,"line":204},[196,1032,1033],{},"  \u002FTR \"C:\\reporting\\run_report.bat\" ^\n",[196,1035,1036],{"class":138,"line":258},[196,1037,1038],{},"  \u002FRU \"DOMAIN\\svc_reports\" \u002FRP \"the-password\" \u002FRL HIGHEST \u002FF\n",[10,1040,1041,1042,1044,1045,1047],{},"Use a dedicated service account with a non-expiring password for ",[14,1043,1011],{},". If you omit ",[14,1046,1015],{},", Windows prompts for the password interactively.",[172,1049,1051],{"id":1050},"schtasks-create-field-reference","schtasks \u002FCreate field reference",[1053,1054,1055,1071],"table",{},[1056,1057,1058],"thead",{},[1059,1060,1061,1065,1068],"tr",{},[1062,1063,1064],"th",{},"Flag",[1062,1066,1067],{},"Meaning",[1062,1069,1070],{},"Example",[1072,1073,1074,1090,1105,1132,1150,1168,1186,1200,1214,1233],"tbody",{},[1059,1075,1076,1082,1085],{},[1077,1078,1079],"td",{},[14,1080,1081],{},"\u002FTN",[1077,1083,1084],{},"Task name (must be unique)",[1077,1086,1087],{},[14,1088,1089],{},"\"DailyExcelReport\"",[1059,1091,1092,1097,1100],{},[1077,1093,1094],{},[14,1095,1096],{},"\u002FTR",[1077,1098,1099],{},"Task to run — the program or .bat",[1077,1101,1102],{},[14,1103,1104],{},"\"C:\\reporting\\run_report.bat\"",[1059,1106,1107,1112,1115],{},[1077,1108,1109],{},[14,1110,1111],{},"\u002FSC",[1077,1113,1114],{},"Schedule type",[1077,1116,1117,410,1120,410,1123,410,1126,410,1129],{},[14,1118,1119],{},"DAILY",[14,1121,1122],{},"WEEKLY",[14,1124,1125],{},"HOURLY",[14,1127,1128],{},"MINUTE",[14,1130,1131],{},"ONLOGON",[1059,1133,1134,1139,1145],{},[1077,1135,1136],{},[14,1137,1138],{},"\u002FST",[1077,1140,1141,1142],{},"Start time, 24-hour ",[14,1143,1144],{},"HH:MM",[1077,1146,1147],{},[14,1148,1149],{},"06:00",[1059,1151,1152,1157,1163],{},[1077,1153,1154],{},[14,1155,1156],{},"\u002FD",[1077,1158,1159,1160,1162],{},"Days (with ",[14,1161,1122],{},")",[1077,1164,1165],{},[14,1166,1167],{},"MON,TUE,WED,THU,FRI",[1059,1169,1170,1175,1178],{},[1077,1171,1172],{},[14,1173,1174],{},"\u002FMO",[1077,1176,1177],{},"Modifier \u002F interval",[1077,1179,1180,1183,1184,1162],{},[14,1181,1182],{},"4"," (every 4 hours with ",[14,1185,1125],{},[1059,1187,1188,1192,1195],{},[1077,1189,1190],{},[14,1191,1011],{},[1077,1193,1194],{},"Run-as user account",[1077,1196,1197],{},[14,1198,1199],{},"\"DOMAIN\\svc_reports\"",[1059,1201,1202,1206,1209],{},[1077,1203,1204],{},[14,1205,1015],{},[1077,1207,1208],{},"Run-as password",[1077,1210,1211],{},[14,1212,1213],{},"\"the-password\"",[1059,1215,1216,1221,1224],{},[1077,1217,1218],{},[14,1219,1220],{},"\u002FRL",[1077,1222,1223],{},"Run level",[1077,1225,1226,1229,1230],{},[14,1227,1228],{},"HIGHEST"," or ",[14,1231,1232],{},"LIMITED",[1059,1234,1235,1239,1242],{},[1077,1236,1237],{},[14,1238,999],{},[1077,1240,1241],{},"Force-overwrite an existing task",[1077,1243,1244],{},"(no value)",[172,1246,1248],{"id":1247},"the-task-scheduler-gui-briefly","The Task Scheduler GUI, briefly",[10,1250,1251,1252,405,1254,1257,1258,1261,1262,1265,1266,1269,1270,1272,1273,1276,1277,1280,1281,1012,1284,1287,1288,1290],{},"If you prefer the GUI, open ",[19,1253,21],{},[14,1255,1256],{},"taskschd.msc",") → ",[884,1259,1260],{},"Create Task",". On the ",[19,1263,1264],{},"Actions"," tab set ",[884,1267,1268],{},"Program\u002Fscript"," to ",[14,1271,890],{},". On ",[19,1274,1275],{},"Triggers"," add a daily\u002Fweekly schedule. On the ",[19,1278,1279],{},"General"," tab tick ",[884,1282,1283],{},"Run whether user is logged on or not",[884,1285,1286],{},"Run with highest privileges",". The result is identical to the ",[14,1289,25],{}," command above — the CLI is just reproducible.",[172,1292,1294],{"id":1293},"verify-the-task","Verify the task",[10,1296,1297],{},"List the task and read its scheduling and last result:",[187,1299,1301],{"className":189,"code":1300,"language":191,"meta":192,"style":192},"schtasks \u002FQuery \u002FTN \"DailyExcelReport\" \u002FV \u002FFO LIST\n",[14,1302,1303],{"__ignoreMap":192},[196,1304,1305],{"class":138,"line":198},[196,1306,1300],{},[10,1308,919,1309,1311],{},[19,1310,231],{}," field is what tells you whether the run succeeded:",[1053,1313,1314,1322],{},[1056,1315,1316],{},[1059,1317,1318,1320],{},[1062,1319,231],{},[1062,1321,1067],{},[1072,1323,1324,1334,1347,1360,1370],{},[1059,1325,1326,1331],{},[1077,1327,1328],{},[14,1329,1330],{},"0x0",[1077,1332,1333],{},"Success",[1059,1335,1336,1341],{},[1077,1337,1338],{},[14,1339,1340],{},"0x1",[1077,1342,1343,1344,1162],{},"Incorrect function \u002F the script exited non-zero (your ",[14,1345,1346],{},"sys.exit(1)",[1059,1348,1349,1354],{},[1077,1350,1351],{},[14,1352,1353],{},"0x2",[1077,1355,1356,1357,1359],{},"File not found (check the ",[14,1358,1096],{}," path)",[1059,1361,1362,1367],{},[1077,1363,1364],{},[14,1365,1366],{},"0x41301",[1077,1368,1369],{},"Task is currently running",[1059,1371,1372,1377],{},[1077,1373,1374],{},[14,1375,1376],{},"267011",[1077,1378,1379],{},"Task has not yet run",[10,1381,1382],{},"Force an immediate run to test without waiting for the schedule:",[187,1384,1386],{"className":189,"code":1385,"language":191,"meta":192,"style":192},"schtasks \u002FRun \u002FTN \"DailyExcelReport\"\n",[14,1387,1388],{"__ignoreMap":192},[196,1389,1390],{"class":138,"line":198},[196,1391,1385],{},[10,1393,1394,1395,1398,1399,1402],{},"Then confirm ",[14,1396,1397],{},"C:\\reporting\\output\\daily_summary.xlsx"," was written and check ",[14,1400,1401],{},"C:\\reporting\\logs\\stdout.log",".",[172,1404,1406],{"id":1405},"common-pitfalls","Common pitfalls",[1053,1408,1409,1422],{},[1056,1410,1411],{},[1059,1412,1413,1416,1419],{},[1062,1414,1415],{},"Symptom",[1062,1417,1418],{},"Cause",[1062,1420,1421],{},"Fix",[1072,1423,1424,1442,1458,1477,1490],{},[1059,1425,1426,1432,1439],{},[1077,1427,1428,1431],{},[14,1429,1430],{},"FileNotFoundError"," for a file you can see",[1077,1433,1434,1435,1438],{},"Working directory is ",[14,1436,1437],{},"System32",", not your script folder",[1077,1440,1441],{},"Use absolute paths for every read and write",[1059,1443,1444,1449,1452],{},[1077,1445,1446],{},[14,1447,1448],{},"ModuleNotFoundError: pandas",[1077,1450,1451],{},"Task ran the system Python, not the venv",[1077,1453,1454,1455,1457],{},"Call ",[14,1456,213],{}," (or activate in the .bat)",[1059,1459,1460,1465,1471],{},[1077,1461,1462,1463],{},"Last Run Result ",[14,1464,1353],{},[1077,1466,1467,1468,1470],{},"Path in ",[14,1469,1096],{}," has spaces and isn't quoted",[1077,1472,1473,1474,1476],{},"Wrap the whole ",[14,1475,1096],{}," value in double quotes",[1059,1478,1479,1482,1485],{},[1077,1480,1481],{},"Task \"succeeds\" but no file appears",[1077,1483,1484],{},"Output and errors discarded",[1077,1486,1487,1488],{},"Redirect in the .bat with ",[14,1489,926],{},[1059,1491,1492,1495,1504],{},[1077,1493,1494],{},"Runs only when you're logged in",[1077,1496,1497,1498,1500,1501,1503],{},"No ",[14,1499,1011],{}," \u002F ",[14,1502,1015],{}," supplied",[1077,1505,1506,1507],{},"Add a service account and ",[14,1508,1019],{},[10,1510,1511,1512,1515,1516,1519,1520,1522,1523,1526],{},"A note on quoting: paths with spaces (",[14,1513,1514],{},"C:\\Program Files\\...",") need quotes ",[884,1517,1518],{},"inside"," the already-quoted ",[14,1521,1096],{}," string, which is awkward. Installing your project under a space-free path like ",[14,1524,1525],{},"C:\\reporting"," avoids the problem entirely.",[172,1528,1530],{"id":1529},"frequently-asked-questions","Frequently asked questions",[10,1532,1533,1536,1537,1540],{},[19,1534,1535],{},"Do I need the .bat wrapper, or can I point the task straight at python.exe?","\nYou can set ",[14,1538,1539],{},"\u002FTR \"C:\\reporting\\venv\\Scripts\\python.exe C:\\reporting\\generate_daily_report.py\""," directly. The wrapper is worth it because it gives you one tidy place to capture stdout\u002Fstderr to a log and to activate the venv if other tooling expects it.",[10,1542,1543,1546,1547,1550,1551,1554,1555,1557],{},[19,1544,1545],{},"Why does the task run fine manually but fail on schedule?","\nAlmost always the run-as context. A manual ",[14,1548,1549],{},"\u002FRun"," uses your logged-on session; the scheduled run may use a service account with a different ",[14,1552,1553],{},"PATH"," and working directory. Use absolute paths and confirm the ",[14,1556,1011],{}," account can read the script and write the output folder.",[10,1559,1560,1563,1566,1567,1569,1570,1572,1573,1576,1577,1402],{},[19,1561,1562],{},"How do I delete or change a task?",[14,1564,1565],{},"schtasks \u002FDelete \u002FTN \"DailyExcelReport\" \u002FF"," removes it. To change it, rerun ",[14,1568,98],{}," with ",[14,1571,999],{}," to overwrite, or use ",[14,1574,1575],{},"schtasks \u002FChange"," to tweak a single setting like ",[14,1578,1138],{},[10,1580,1581,1584,1585,1402],{},[19,1582,1583],{},"Can Task Scheduler email the report after it runs?","\nNot directly — its built-in email action is deprecated. Send mail from Python at the end of the job; see ",[28,1586,1588],{"href":1587},"\u002Fautomating-reporting-workflows\u002Femailing-excel-reports-with-smtplib\u002F","Emailing Excel Reports with smtplib",[172,1590,1592],{"id":1591},"conclusion","Conclusion",[10,1594,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1607,1610,1612,1613,1616,1617,1620,1621,1623],{},"Scheduling a Python Excel script on Windows comes down to four things: call the venv ",[14,1597,877],{}," by absolute path, use absolute paths inside the script (the working directory is ",[14,1600,1437],{},"), redirect output to a log in a ",[14,1603,881],{}," wrapper, and add ",[14,1606,1011],{},"\u002F",[14,1609,1015],{},[14,1611,1019],{}," so the task runs while logged off. Register it with ",[14,1614,1615],{},"schtasks \u002FCreate \u002FF",", verify with ",[14,1618,1619],{},"schtasks \u002FQuery \u002FV",", and key your monitoring off the ",[19,1622,231],{}," code.",[172,1625,1627],{"id":1626},"where-to-go-next","Where to go next",[10,1629,1630,1631,1633,1634,1638,1639,1643,1644,1402],{},"This is the Windows track of ",[28,1632,31],{"href":30},". If you'd rather schedule inside Python and stay cross-platform, see the sibling guide ",[28,1635,1637],{"href":1636},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-recurring-excel-reports-with-apscheduler\u002F","Schedule Recurring Excel Reports with APScheduler",". To strengthen the report the task produces, see ",[28,1640,1642],{"href":1641},"\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002F","Writing DataFrames to Excel with pandas",", and to deliver it, ",[28,1645,1588],{"href":1587},[1647,1648,1649],"style",{},"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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":192,"searchDepth":204,"depth":204,"links":1651},[1652,1653,1654,1655,1658,1659,1660,1661,1662,1663,1664],{"id":174,"depth":204,"text":175},{"id":217,"depth":204,"text":218},{"id":870,"depth":204,"text":871},{"id":930,"depth":204,"text":931,"children":1656},[1657],{"id":1004,"depth":258,"text":1005},{"id":1050,"depth":204,"text":1051},{"id":1247,"depth":204,"text":1248},{"id":1293,"depth":204,"text":1294},{"id":1405,"depth":204,"text":1406},{"id":1529,"depth":204,"text":1530},{"id":1591,"depth":204,"text":1592},{"id":1626,"depth":204,"text":1627},"2026-06-18","Register a Python Excel report with Windows Task Scheduler: a self-contained generator, schtasks \u002FCreate triggers, the venv interpreter, a .bat wrapper, and logging.","md",[1669,1671,1673,1675],{"q":1535,"a":1670},"You can set \u002FTR \"C:\\reporting\\venv\\Scripts\\python.exe C:\\reporting\\generate_daily_report.py\" directly. The wrapper is worth it because it gives you one tidy place to capture stdout\u002Fstderr to a log and to activate the venv if other tooling expects it.",{"q":1545,"a":1672},"Almost always the run-as context. A manual \u002FRun uses your logged-on session; the scheduled run may use a service account with a different PATH and working directory. Use absolute paths and confirm the \u002FRU account can read the script and write the output folder.",{"q":1562,"a":1674},"schtasks \u002FDelete \u002FTN \"DailyExcelReport\" \u002FF removes it. To change it, rerun \u002FCreate with \u002FF to overwrite, or use schtasks \u002FChange to tweak a single setting like \u002FST.",{"q":1583,"a":1676},"Not directly — its built-in email action is deprecated. Send mail from Python at the end of the job; see Emailing Excel Reports with smtplib.",{},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-python-excel-script-on-windows-task-scheduler",{"title":1680,"description":1681},"Run a Python Excel Script on Task Scheduler","Schedule a Python Excel report on Windows with schtasks: daily, weekly, and hourly triggers, the venv python.exe by absolute path, a logging .bat wrapper, verification.","run-python-excel-script-on-windows-task-scheduler","automating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-python-excel-script-on-windows-task-scheduler\u002Findex","long_tail","gR0fGlaEGxj_3IhtqxjoCcv9oHydT3GDoeErpBNfbMU",[1687,1690],{"title":31,"path":1688,"stem":1689,"children":-1},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron","automating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Findex",{"title":1637,"path":1691,"stem":1692,"children":-1},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-recurring-excel-reports-with-apscheduler","automating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Fschedule-recurring-excel-reports-with-apscheduler\u002Findex",1781795518799]