9e84b6fc13d60a1beb638f18050cb1fed5835410 hiram Sun May 24 12:04:05 2026 -0700 add a "hide/show completed" toggle and no longer need to protect via VPN IP with .htaccess now refs #31811 diff --git src/hg/utils/otto/userRequests/ottoRequestView.cgi src/hg/utils/otto/userRequests/ottoRequestView.cgi index 0a81e3b954f..dc7d51122da 100644 --- src/hg/utils/otto/userRequests/ottoRequestView.cgi +++ src/hg/utils/otto/userRequests/ottoRequestView.cgi @@ -289,55 +289,62 @@ 'font-size:12px}\n' 'th{background:#eee;text-align:left;position:sticky;top:0}\n' 'tr:nth-child(even){background:#f8f8f8}\n' 'td.comment{max-width:28em;white-space:pre-wrap;' 'word-break:break-word}\n' 'tr.s7 td{background:#ffe0e0}\n' 'tr.s8 td{background:#e0f0e0;color:#555}\n' 'select,button{font-size:12px}\n' '.banner{padding:.5em;margin:.4em 0;border-radius:4px}\n' '.info {background:#dfd;border:1px solid #5a5}\n' '.error{background:#fdd;border:1px solid #a55}\n' '.legend{font-size:15px;color:#333;margin:.4em 0}\n' '.legend code{background:#eee;padding:0 3px;font-size:14px}\n' '.refreshBtn{font-size:14px;padding:3px 10px;margin-left:6px;' 'cursor:pointer}\n' + '.toggleBtn{font-size:14px;padding:3px 10px;margin-left:6px;' + 'cursor:pointer;background:#f0f0f0;border:1px solid #ccc}\n' + '.hide-complete tr.s8{display:none}\n' '</style></head><body>\n') out(f'<h2>{DB}.{TABLE}</h2>\n') if galaxyStatus: staleNote = ' <b>[stale]</b>' if galaxyStatus.get('stale') else '' out('<div class="legend">Galaxy queue: ' f'<b>{galaxyStatus.get("running", "?")}</b> running · ' f'<b>{galaxyStatus.get("queued", "?")}</b> queued · ' f'<b>{galaxyStatus.get("new", "?")}</b> new ' f'<small>(as of {html.escape(galaxyStatus.get("ts", ""))})</small>' f'{staleNote}</div>\n') else: out('<div class="legend">Galaxy queue: ' '<i>status unavailable</i></div>\n') if info: out(f'<div class="banner info">{html.escape(info)}</div>\n') if error: out(f'<div class="banner error">{html.escape(error)}</div>\n') out('<div class="legend">status: ') out(' · '.join(f'<code>{k}</code>={html.escape(v)}' for k, v in STATUS_NAMES.items())) + # Count completed rows for the toggle button label + completed_count = sum(1 for r in rows if len(r) > 7 and r[7] == '8') out(f' · <b>{len(rows)}</b> row(s)' '<button class="refreshBtn" type="button" ' - 'onclick="location.reload()">refresh</button></div>\n') + 'onclick="location.reload()">refresh</button>' + f'<button class="toggleBtn" type="button" id="toggleComplete" ' + f'onclick="toggleCompleted()">hide completed ({completed_count})</button></div>\n') out(f'<div class="legend">cron times: 9,20,31,42,53 for ottoRequestWatch.sh, and 4,26,46 for ottoRequestPush and 1,8,15,22,29,36,43,50,57 for the first acknowledgement</div>\n') out('<table class="sortable">\n<tr>') for c in COLS: out(f'<th>{c}</th>') out('<th title="% of fromDb covered by chains to toDb / ' '% of toDb covered by chains to fromDb">' 'coverage<br><small>from / to</small></th>' '<th>elapsed</th><th>set status</th></tr>\n') reqIdx = COLS.index('requestTime') doneIdx = COLS.index('completeTime') fromIdx = COLS.index('fromDb') toIdx = COLS.index('toDb') @@ -383,34 +390,59 @@ out(f'<td>{html.escape(elapsed)}</td>') # reset form out('<td><form method="post" ' 'onsubmit="return confirm(\'Reset status of id=' + html.escape(rid) + ' to \' + this.status.value + \'?\')">' '<input type="hidden" name="action" value="resetStatus">' f'<input type="hidden" name="id" value="{html.escape(rid)}">' '<select name="status">') for k in sorted(STATUS_NAMES): sel = ' selected' if k == stnum else '' out(f'<option value="{k}"{sel}>{k}</option>') out('</select> <button type="submit">set</button></form></td>') out('</tr>\n') out('</table>\n') out('<script src="/js/sorttable.js"></script>\n') + out('<script>\n' + 'function toggleCompleted() {\n' + ' var table = document.querySelector("table");\n' + ' var btn = document.getElementById("toggleComplete");\n' + ' var isHidden = table.classList.contains("hide-complete");\n' + ' if (isHidden) {\n' + ' table.classList.remove("hide-complete");\n' + f' btn.textContent = "hide completed ({completed_count})";\n' + ' localStorage.setItem("hideCompleted", "false");\n' + ' } else {\n' + ' table.classList.add("hide-complete");\n' + ' btn.textContent = "show completed";\n' + ' localStorage.setItem("hideCompleted", "true");\n' + ' }\n' + '}\n' + '// Restore toggle state from localStorage\n' + 'window.addEventListener("load", function() {\n' + ' if (localStorage.getItem("hideCompleted") === "true") {\n' + ' var table = document.querySelector("table");\n' + ' var btn = document.getElementById("toggleComplete");\n' + ' table.classList.add("hide-complete");\n' + ' btn.textContent = "show completed";\n' + ' }\n' + '});\n' + '</script>\n') out('</body></html>\n') def main(): - checkIp() +# checkIp() # POST/Redirect/GET: handle the write, then 303 to a GET of the same URL # so a browser reload doesn't re-submit the form and re-run the UPDATE. if os.environ.get('REQUEST_METHOD', 'GET') == 'POST': form = cgi.FieldStorage() action = form.getfirst('action', '') if action == 'resetStatus': info, error = doResetStatus(form) else: info, error = None, f"unknown action: {action!r}" params = {} if info: params['info'] = info if error: params['error'] = error qs = ('?' + urllib.parse.urlencode(params)) if params else '' sys.stdout.write(f"Status: 303 See Other\r\n"