Implement lifecycle workspace publication and audit delivery
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a10780-6d41-7952-9718-dfbc1f41236f
This commit is contained in:
parent
ee472acc87
commit
5fec7de4cb
21 changed files with 1022 additions and 78 deletions
|
|
@ -17,6 +17,7 @@ from .disposition import DispositionRefused, Verb
|
|||
from .policy import PolicyError
|
||||
from .provenance import HumanControlNotDischargeable
|
||||
from .review import ReviewError
|
||||
from .lifecycle_service import LifecycleError
|
||||
from .store import Conflict, EvidenceUnavailable, StoreError
|
||||
from .ui import STYLES, document, error_page, overview_section, review_page
|
||||
|
||||
|
|
@ -38,9 +39,10 @@ def _one(query: str, *, limit=8192, fields=16) -> dict[str, str]:
|
|||
|
||||
|
||||
class App:
|
||||
def __init__(self, login: KeyCapeLogin, review=None, *, readiness=lambda: False):
|
||||
def __init__(self, login: KeyCapeLogin, review=None, *, readiness=lambda: False, lifecycle=None):
|
||||
self.login = login
|
||||
self.review, self.readiness = review, readiness
|
||||
self.lifecycle = lifecycle
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
path = environ.get("PATH_INFO", "/")
|
||||
|
|
@ -84,6 +86,11 @@ class App:
|
|||
}
|
||||
status, body, content_type = exc.status, error_page(messages.get(exc.code,
|
||||
"The review could not complete its required checks. Reload after the service is available.")), "text/html"
|
||||
except LifecycleError as exc:
|
||||
status, body, content_type = exc.status, document("Decision unavailable",
|
||||
'<h1>Decision unavailable</h1><p>The request could not complete its required checks. '
|
||||
'Sign in again if your session expired, or reload the decision before retrying.</p>'
|
||||
'<p><a href="/decisions">Return to the workspace</a></p>'), "text/html"
|
||||
except DispositionRefused as exc:
|
||||
status, body, content_type = 409, error_page({
|
||||
"G_ACK": "Record all required highlight acknowledgments before accepting or declining.",
|
||||
|
|
@ -125,6 +132,14 @@ class App:
|
|||
return 303, "", "text/plain", [("Location", "/"), _cookie(FLOW_COOKIE, "", 0),
|
||||
_cookie(SESSION_COOKIE, new_sid, 900)]
|
||||
session = self.login.session(sid)
|
||||
if self.lifecycle is not None and (path == "/decisions" or path.startswith("/decisions/")):
|
||||
if session is None:
|
||||
raise LifecycleError(401, "identity_expired")
|
||||
from .lifecycle_ui import LifecycleWorkspace
|
||||
params = _one(environ.get("QUERY_STRING", ""))
|
||||
form = self._form(environ, session) if method == "POST" else None
|
||||
status, body, extra = LifecycleWorkspace(self.lifecycle).dispatch(method, path, params, session, form)
|
||||
return status, body, "application/octet-stream" if isinstance(body, bytes) else "text/html", extra
|
||||
if self.review is not None and (path == "/review" or path.startswith("/presentations/")):
|
||||
if session is None:
|
||||
raise ReviewError(401, "session_expired")
|
||||
|
|
@ -188,6 +203,7 @@ class App:
|
|||
if method == "GET" and path == "/":
|
||||
if session:
|
||||
content = (f"<p>Signed in as {html.escape(session.subject)}.</p>"
|
||||
+ ('<p><a href="/decisions">Open the decision workspace</a></p>' if self.lifecycle is not None else '')
|
||||
+ (self._overview(session) if self.review is not None else '')
|
||||
+ ('<h2>Open a decision review</h2><p>Enter the memo identifier supplied with your review request.</p>'
|
||||
'<form method="get" action="/review"><label>Memo identifier <input name="memo_id" required maxlength="256"></label>'
|
||||
|
|
@ -244,7 +260,8 @@ def main():
|
|||
runtime = Runtime.from_file(os.environ["INFD_REVIEW_CONFIG"])
|
||||
runtime.pump.start()
|
||||
app = App(login, runtime.controller if runtime else None,
|
||||
readiness=runtime.pump.ready if runtime else lambda: False)
|
||||
readiness=runtime.pump.ready if runtime else lambda: False,
|
||||
lifecycle=runtime.lifecycle if runtime else None)
|
||||
try:
|
||||
serve(app, host=host, port=8080, threads=4)
|
||||
finally:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue