Deploy AIOS web (React glide grid + FastAPI slice)
Browse files- RELEASES.json +1 -1
- VERSION +1 -1
- api/routes_publish.py +20 -17
RELEASES.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
{
|
| 2 |
-
"current": "
|
| 3 |
"releases": [
|
| 4 |
{
|
| 5 |
"version": "v53",
|
|
|
|
| 1 |
{
|
| 2 |
+
"current": "76fb302",
|
| 3 |
"releases": [
|
| 4 |
{
|
| 5 |
"version": "v53",
|
VERSION
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
|
|
|
|
| 1 |
+
76fb302
|
api/routes_publish.py
CHANGED
|
@@ -302,8 +302,8 @@ def _may_administer_view(session: Session, table_key: str, view_id: str):
|
|
| 302 |
"no rows at all. Publish a copy without the cohort lock.")
|
| 303 |
if _needs_a_reader(cfg.get("filters")):
|
| 304 |
raise err(400, "reader_scoped",
|
| 305 |
-
"this view filters on a cohort
|
| 306 |
-
"
|
| 307 |
"the page would show no rows at all. Publish a copy filtered on columns.")
|
| 308 |
return view, mode
|
| 309 |
|
|
@@ -319,14 +319,18 @@ PUBLIC_ROW_CAP = 5000
|
|
| 319 |
def _needs_a_reader(tree) -> bool:
|
| 320 |
"""Does this filter tree contain a leaf that only a SIGNED-IN reader could resolve?
|
| 321 |
|
| 322 |
-
β COHORTS
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
|
| 331 |
β So such a view is refused AT THE MINT, where a person is standing in front of the answer.
|
| 332 |
That is R6's second sentence: a limit that genuinely cannot be removed is REPORTED with its
|
|
@@ -346,9 +350,7 @@ def _needs_a_reader(tree) -> bool:
|
|
| 346 |
return False
|
| 347 |
if node.get("kind") == "group" or isinstance(node.get("children"), list):
|
| 348 |
return any(walk(n) for n in (node.get("children") or []))
|
| 349 |
-
|
| 350 |
-
return True
|
| 351 |
-
return node.get("op") in filter_eval.RANK_OPS
|
| 352 |
|
| 353 |
return walk(tree)
|
| 354 |
|
|
@@ -561,12 +563,13 @@ def _public_view(rt, table_key: str, view: dict) -> dict:
|
|
| 561 |
rows_src.append(r)
|
| 562 |
rows_src.sort(key=lambda r: r["pid"])
|
| 563 |
|
| 564 |
-
# The view's own row selection, through the SHARED evaluator.
|
| 565 |
-
#
|
| 566 |
-
#
|
| 567 |
ctx = filter_eval.EvalCtx(today=time.strftime("%Y-%m-%d"))
|
| 568 |
keep = set(filter_eval.visible_pids(cfg.get("filters"), rows_src, fields, ctx,
|
| 569 |
-
member_pids=cfg.get("memberPids")
|
|
|
|
| 570 |
chosen = [r for r in rows_src if r.get("pid") in keep]
|
| 571 |
|
| 572 |
# ββ COORDINATES RIDE A MAP WHEN THEY ARE COORDINATES β NOT WHEN THEY ARE VISIBLE, AND NOT
|
|
|
|
| 302 |
"no rows at all. Publish a copy without the cohort lock.")
|
| 303 |
if _needs_a_reader(cfg.get("filters")):
|
| 304 |
raise err(400, "reader_scoped",
|
| 305 |
+
"this view filters on a cohort or a measure rule, and each is resolved for the "
|
| 306 |
+
"person reading it β a public link has no reader, so "
|
| 307 |
"the page would show no rows at all. Publish a copy filtered on columns.")
|
| 308 |
return view, mode
|
| 309 |
|
|
|
|
| 319 |
def _needs_a_reader(tree) -> bool:
|
| 320 |
"""Does this filter tree contain a leaf that only a SIGNED-IN reader could resolve?
|
| 321 |
|
| 322 |
+
β COHORTS AND MEASURE RULES ARE NOT PROPERTIES OF THE VIEW. Each is a SET the host computes
|
| 323 |
+
for the person asking β `filter_eval.EvalCtx`'s own docstring: *"each is an answer a single
|
| 324 |
+
ROW cannot compute β¦ absent, the condition matches NOTHING rather than everything"*. That
|
| 325 |
+
default is right (it fails closed) and it is unusable here: an anonymous page whose every row
|
| 326 |
+
was silently filtered out is indistinguishable from a broken link, and the reader has nobody
|
| 327 |
+
to ask.
|
| 328 |
+
|
| 329 |
+
β Rank slices are deliberately NOT included. `filter_eval.visible_pids` resolves their
|
| 330 |
+
membership from the server row payload before evaluating it, so a public `topN`/`bottomN`
|
| 331 |
+
link has one deterministic answer independent of its reader. `evaluate` remains fail-closed
|
| 332 |
+
for a bare row context; this route takes the whole-payload entry point precisely so it can
|
| 333 |
+
answer the set question rather than silently publish an empty page.
|
| 334 |
|
| 335 |
β So such a view is refused AT THE MINT, where a person is standing in front of the answer.
|
| 336 |
That is R6's second sentence: a limit that genuinely cannot be removed is REPORTED with its
|
|
|
|
| 350 |
return False
|
| 351 |
if node.get("kind") == "group" or isinstance(node.get("children"), list):
|
| 352 |
return any(walk(n) for n in (node.get("children") or []))
|
| 353 |
+
return filter_eval._is_cohort(node) or filter_eval._is_measure(node)
|
|
|
|
|
|
|
| 354 |
|
| 355 |
return walk(tree)
|
| 356 |
|
|
|
|
| 563 |
rows_src.append(r)
|
| 564 |
rows_src.sort(key=lambda r: r["pid"])
|
| 565 |
|
| 566 |
+
# The view's own row selection, through the SHARED whole-payload evaluator. It resolves rank
|
| 567 |
+
# sets from `rows_src` before evaluating, while `_needs_a_reader` has already refused the
|
| 568 |
+
# cohort/measure leaves this anonymous context cannot answer.
|
| 569 |
ctx = filter_eval.EvalCtx(today=time.strftime("%Y-%m-%d"))
|
| 570 |
keep = set(filter_eval.visible_pids(cfg.get("filters"), rows_src, fields, ctx,
|
| 571 |
+
member_pids=cfg.get("memberPids"),
|
| 572 |
+
group_by=cfg.get("groupBy")))
|
| 573 |
chosen = [r for r in rows_src if r.get("pid") in keep]
|
| 574 |
|
| 575 |
# ββ COORDINATES RIDE A MAP WHEN THEY ARE COORDINATES β NOT WHEN THEY ARE VISIBLE, AND NOT
|