Upload test_khazna.py
Browse files- tests/test_khazna.py +47 -0
tests/test_khazna.py
CHANGED
|
@@ -42,6 +42,7 @@ DB = [
|
|
| 42 |
ro("a4", "Solar desalination membranes", 2023, typ="thesis", abstract="solar desalination", ku=("Ali", "Hassan", "p2")),
|
| 43 |
ro("a5", "Graphene oxide for desalination", 2022, abstract="graphene membranes desalination", ku=("Jane", "Smith", "p1"), ext=("x1", "x2")),
|
| 44 |
ro("a6", "Urban PM2.5 sensor readings", 2021, typ="dataset", abstract="pollution sensor readings Abu Dhabi", ext=()),
|
|
|
|
| 45 |
]
|
| 46 |
EXT_ORGS = {"x1": {"uuid": "x1", "name": {"en_GB": "Massachusetts Institute of Technology"}, "type": {"term": {"en_GB": "University"}},
|
| 47 |
"address": {"country": {"term": {"en_GB": "United States"}}}},
|
|
@@ -61,6 +62,12 @@ PERSONS = {
|
|
| 61 |
"staffOrganizationAssociations": [{"jobTitle": {"term": {"en_GB": "Associate Professor"}}, "organization": {"uuid": "o1"}}],
|
| 62 |
"keywordGroups": [{"keywords": [{"freeKeywords": ["membranes", "desalination"]}]}]},
|
| 63 |
"p2": {"uuid": "p2", "name": {"firstName": "Ali", "lastName": "Hassan"}, "visibility": {"key": "FREE"}},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
}
|
| 65 |
ORGS = {"o1": {"uuid": "o1", "name": {"en_GB": "Department of Chemical Engineering"}, "type": {"term": {"en_GB": "Department"}}, "visibility": {"key": "FREE"}},
|
| 66 |
"o2": {"uuid": "o2", "name": {"en_GB": "Center for Membranes and Advanced Water Technology"}, "type": {"term": {"en_GB": "Research Centre"}}, "visibility": {"key": "FREE"}}}
|
|
@@ -269,6 +276,7 @@ def test_endpoint_scopes_widget_ratelimit_and_dataset_403():
|
|
| 269 |
assert d["intent"] == "expert_search" and d["search_results"][0]["kind"] == "person"
|
| 270 |
assert "email" not in json.dumps(d)
|
| 271 |
assert "/en/persons/?search=desalination" in d["portal_search_url"]
|
|
|
|
| 272 |
d = tc.post("/agent", json={"question": "membranes", "scope": "organisations"}).json()
|
| 273 |
assert d["search_results"][0]["kind"] == "organisation"
|
| 274 |
DATASETS_ENABLED["on"] = False
|
|
@@ -429,3 +437,42 @@ def test_agent_new_scopes_end_to_end():
|
|
| 429 |
assert d["search_results"][0]["kind"] == "collaborator" and d["search_results"][0]["joint_outputs"] == 2
|
| 430 |
d = tc.post("/agent", json={"question": "hydrogen storage", "scope": "publications"}).json()
|
| 431 |
assert {f["scope"] for f in d["follow_up_suggestions"]} == {"persons", "theses", "projects", "collaborations"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
ro("a4", "Solar desalination membranes", 2023, typ="thesis", abstract="solar desalination", ku=("Ali", "Hassan", "p2")),
|
| 43 |
ro("a5", "Graphene oxide for desalination", 2022, abstract="graphene membranes desalination", ku=("Jane", "Smith", "p1"), ext=("x1", "x2")),
|
| 44 |
ro("a6", "Urban PM2.5 sensor readings", 2021, typ="dataset", abstract="pollution sensor readings Abu Dhabi", ext=()),
|
| 45 |
+
ro("a7", "Deep learning for seismic imaging", 2024, abstract="seismic inversion with neural networks", ku=("Kumar", "Padmanabhan", "k1"), ext=()),
|
| 46 |
]
|
| 47 |
EXT_ORGS = {"x1": {"uuid": "x1", "name": {"en_GB": "Massachusetts Institute of Technology"}, "type": {"term": {"en_GB": "University"}},
|
| 48 |
"address": {"country": {"term": {"en_GB": "United States"}}}},
|
|
|
|
| 62 |
"staffOrganizationAssociations": [{"jobTitle": {"term": {"en_GB": "Associate Professor"}}, "organization": {"uuid": "o1"}}],
|
| 63 |
"keywordGroups": [{"keywords": [{"freeKeywords": ["membranes", "desalination"]}]}]},
|
| 64 |
"p2": {"uuid": "p2", "name": {"firstName": "Ali", "lastName": "Hassan"}, "visibility": {"key": "FREE"}},
|
| 65 |
+
# several "Kumar"s in different name positions, plus a prefix-only match
|
| 66 |
+
"k1": {"uuid": "k1", "name": {"firstName": "Kumar", "lastName": "Padmanabhan"}, "visibility": {"key": "FREE"},
|
| 67 |
+
"staffOrganizationAssociations": [{"jobTitle": {"term": {"en_GB": "Professor"}}, "organization": {"uuid": "o1"}}]},
|
| 68 |
+
"k2": {"uuid": "k2", "name": {"firstName": "Rajesh", "lastName": "Kumar"}, "visibility": {"key": "FREE"}},
|
| 69 |
+
"k3": {"uuid": "k3", "name": {"firstName": "Anil Kumar", "lastName": "Sharma"}, "visibility": {"key": "FREE"}},
|
| 70 |
+
"k4": {"uuid": "k4", "name": {"firstName": "Kumaran", "lastName": "Iyer"}, "visibility": {"key": "FREE"}},
|
| 71 |
}
|
| 72 |
ORGS = {"o1": {"uuid": "o1", "name": {"en_GB": "Department of Chemical Engineering"}, "type": {"term": {"en_GB": "Department"}}, "visibility": {"key": "FREE"}},
|
| 73 |
"o2": {"uuid": "o2", "name": {"en_GB": "Center for Membranes and Advanced Water Technology"}, "type": {"term": {"en_GB": "Research Centre"}}, "visibility": {"key": "FREE"}}}
|
|
|
|
| 276 |
assert d["intent"] == "expert_search" and d["search_results"][0]["kind"] == "person"
|
| 277 |
assert "email" not in json.dumps(d)
|
| 278 |
assert "/en/persons/?search=desalination" in d["portal_search_url"]
|
| 279 |
+
assert "searchBy" not in d["portal_search_url"]
|
| 280 |
d = tc.post("/agent", json={"question": "membranes", "scope": "organisations"}).json()
|
| 281 |
assert d["search_results"][0]["kind"] == "organisation"
|
| 282 |
DATASETS_ENABLED["on"] = False
|
|
|
|
| 437 |
assert d["search_results"][0]["kind"] == "collaborator" and d["search_results"][0]["joint_outputs"] == 2
|
| 438 |
d = tc.post("/agent", json={"question": "hydrogen storage", "scope": "publications"}).json()
|
| 439 |
assert {f["scope"] for f in d["follow_up_suggestions"]} == {"persons", "theses", "projects", "collaborations"}
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def test_match_people_positions_and_prefix():
|
| 443 |
+
from pure_client import normalize_person
|
| 444 |
+
people = [normalize_person(PERSONS[k], PORTAL) for k in ("k1", "k2", "k3", "k4")]
|
| 445 |
+
matches, full = pipeline.match_people("KUMAR", people)
|
| 446 |
+
assert [p["uuid"] for p in matches] == ["k2", "k3", "k1"] or {p["uuid"] for p in matches} == {"k1", "k2", "k3"}
|
| 447 |
+
assert matches[0]["uuid"] == "k2" # surname matches are listed first
|
| 448 |
+
assert "k4" not in {p["uuid"] for p in matches} # 'Kumaran' only used when no exact match exists
|
| 449 |
+
assert full == []
|
| 450 |
+
m, f = pipeline.match_people("Kumar Padmanabhan", people)
|
| 451 |
+
assert [p["uuid"] for p in f] == ["k1"]
|
| 452 |
+
m, f = pipeline.match_people("Kumara", people) # prefix fallback
|
| 453 |
+
assert [p["uuid"] for p in m] == ["k4"]
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def test_ambiguous_name_asks_to_choose_then_explores_choice():
|
| 457 |
+
os.environ["PURE_API_KEY"] = "test-key"
|
| 458 |
+
import app as appmod
|
| 459 |
+
from fastapi.testclient import TestClient
|
| 460 |
+
with TestClient(appmod.app) as tc:
|
| 461 |
+
appmod.pure = client()
|
| 462 |
+
appmod._hits.clear()
|
| 463 |
+
d = tc.post("/agent", json={"question": "Kumar", "scope": "persons"}).json()
|
| 464 |
+
assert d["intent"] == "author_clarify" and d["clarify"] is True
|
| 465 |
+
assert d["portal_search_url"] == "https://khazna.ku.ac.ae/en/persons/?search=Kumar"
|
| 466 |
+
assert {x["uuid"] for x in d["search_results"]} == {"k1", "k2", "k3"}
|
| 467 |
+
assert all(x["choose"] for x in d["search_results"]) and "3 KU researchers" in d["answer"]
|
| 468 |
+
assert d["search_results"][[x["uuid"] for x in d["search_results"]].index("k1")]["organisations"][0]["name"].startswith("Department")
|
| 469 |
+
# the user picks one → that exact person, no second clarification
|
| 470 |
+
d2 = tc.post("/agent", json={"question": d["original_question"], "scope": "persons", "person_uuid": "k1"}).json()
|
| 471 |
+
assert d2["intent"] == "author_search" and d2["person"]["uuid"] == "k1"
|
| 472 |
+
assert [x["uuid"] for x in d2["search_results"]] == ["a7"]
|
| 473 |
+
# a full unique name goes straight through
|
| 474 |
+
d3 = tc.post("/agent", json={"question": "Kumar Padmanabhan", "scope": "persons"}).json()
|
| 475 |
+
assert d3["intent"] == "author_search" and d3["person"]["uuid"] == "k1"
|
| 476 |
+
# a non-matching / single name still works as before
|
| 477 |
+
d4 = tc.post("/agent", json={"question": "Jane Smith", "scope": "persons"}).json()
|
| 478 |
+
assert d4["person"]["name"] == "Jane Smith"
|