Spaces:
Running
Running
Abid Ali Awan commited on
Commit ·
679be47
1
Parent(s): f6fe0ea
Remove education level from research profile
Browse files- README.md +2 -2
- output/jupyter-notebook/scholarscope-beginner-guide.ipynb +2 -4
- src/olostep_client.py +1 -2
- src/schemas.py +0 -2
- src/ui.py +3 -14
- tests/test_dataset_store.py +0 -1
- tests/test_live_research_behavior.py +0 -3
- tests/test_olostep_client.py +0 -1
- tests/test_schemas.py +1 -2
README.md
CHANGED
|
@@ -15,11 +15,11 @@ short_description: Find funds with AI.
|
|
| 15 |
|
| 16 |
**Find funds with AI.**
|
| 17 |
|
| 18 |
-
ScholarScope is an AI-powered grant and scholarship research agent. Users provide
|
| 19 |
|
| 20 |
## How It Works
|
| 21 |
|
| 22 |
-
1. Validate and normalize the
|
| 23 |
2. Build three short, complementary searches from the profile.
|
| 24 |
3. Run those searches concurrently without scraping every result.
|
| 25 |
4. Remove duplicate, social-media, and known aggregator links, then cheaply rank the remaining URLs.
|
|
|
|
| 15 |
|
| 16 |
**Find funds with AI.**
|
| 17 |
|
| 18 |
+
ScholarScope is an AI-powered grant and scholarship research agent. Users provide five profile fields—origin, what they are applying for, field of study, funding type, and preferences—then receive ranked opportunities with eligibility analysis and direct source links.
|
| 19 |
|
| 20 |
## How It Works
|
| 21 |
|
| 22 |
+
1. Validate and normalize the five profile fields.
|
| 23 |
2. Build three short, complementary searches from the profile.
|
| 24 |
3. Run those searches concurrently without scraping every result.
|
| 25 |
4. Remove duplicate, social-media, and known aggregator links, then cheaply rank the remaining URLs.
|
output/jupyter-notebook/scholarscope-beginner-guide.ipynb
CHANGED
|
@@ -130,7 +130,6 @@
|
|
| 130 |
" 'applying_for': \"Master's degree\",\n",
|
| 131 |
" 'field_of_study': 'Computer science or data science',\n",
|
| 132 |
" 'funding_type': 'Scholarship',\n",
|
| 133 |
-
" 'education_level': \"Bachelor's degree completed\",\n",
|
| 134 |
" 'preferences': 'Fully funded and open to international applicants'}"
|
| 135 |
]
|
| 136 |
},
|
|
@@ -145,7 +144,6 @@
|
|
| 145 |
" \"applying_for\": \"Master's degree\",\n",
|
| 146 |
" \"field_of_study\": \"Computer science or data science\",\n",
|
| 147 |
" \"funding_type\": \"Scholarship\",\n",
|
| 148 |
-
" \"education_level\": \"Bachelor's degree completed\",\n",
|
| 149 |
" \"preferences\": \"Fully funded and open to international applicants\",\n",
|
| 150 |
"}\n",
|
| 151 |
"\n",
|
|
@@ -188,7 +186,7 @@
|
|
| 188 |
"\n",
|
| 189 |
"required_fields = (\n",
|
| 190 |
" \"origin\", \"applying_for\", \"field_of_study\",\n",
|
| 191 |
-
" \"funding_type\", \"
|
| 192 |
")\n",
|
| 193 |
"missing_fields = [name for name in required_fields if not profile.get(name)]\n",
|
| 194 |
"\n",
|
|
@@ -238,7 +236,7 @@
|
|
| 238 |
")\n",
|
| 239 |
"\n",
|
| 240 |
"queries = [\n",
|
| 241 |
-
" f'current official {base}
|
| 242 |
" f'direct provider {base} {profile[\"preferences\"]} eligibility funding',\n",
|
| 243 |
" f'university government foundation {profile[\"funding_type\"]} '\n",
|
| 244 |
" f'{profile[\"applying_for\"]} {profile[\"field_of_study\"]} '\n",
|
|
|
|
| 130 |
" 'applying_for': \"Master's degree\",\n",
|
| 131 |
" 'field_of_study': 'Computer science or data science',\n",
|
| 132 |
" 'funding_type': 'Scholarship',\n",
|
|
|
|
| 133 |
" 'preferences': 'Fully funded and open to international applicants'}"
|
| 134 |
]
|
| 135 |
},
|
|
|
|
| 144 |
" \"applying_for\": \"Master's degree\",\n",
|
| 145 |
" \"field_of_study\": \"Computer science or data science\",\n",
|
| 146 |
" \"funding_type\": \"Scholarship\",\n",
|
|
|
|
| 147 |
" \"preferences\": \"Fully funded and open to international applicants\",\n",
|
| 148 |
"}\n",
|
| 149 |
"\n",
|
|
|
|
| 186 |
"\n",
|
| 187 |
"required_fields = (\n",
|
| 188 |
" \"origin\", \"applying_for\", \"field_of_study\",\n",
|
| 189 |
+
" \"funding_type\", \"preferences\",\n",
|
| 190 |
")\n",
|
| 191 |
"missing_fields = [name for name in required_fields if not profile.get(name)]\n",
|
| 192 |
"\n",
|
|
|
|
| 236 |
")\n",
|
| 237 |
"\n",
|
| 238 |
"queries = [\n",
|
| 239 |
+
" f'current official {base} application deadline',\n",
|
| 240 |
" f'direct provider {base} {profile[\"preferences\"]} eligibility funding',\n",
|
| 241 |
" f'university government foundation {profile[\"funding_type\"]} '\n",
|
| 242 |
" f'{profile[\"applying_for\"]} {profile[\"field_of_study\"]} '\n",
|
src/olostep_client.py
CHANGED
|
@@ -189,10 +189,9 @@ def _build_queries(profile: UserProfile) -> list[str]:
|
|
| 189 |
f"{profile.funding_type} for students from {profile.origin} "
|
| 190 |
f"applying for {profile.applying_for} in {profile.field_of_study}"
|
| 191 |
)
|
| 192 |
-
education = f"with {profile.education_level}"
|
| 193 |
preferences = profile.preferences or "international applicants"
|
| 194 |
return [
|
| 195 |
-
f"current official {base}
|
| 196 |
f"direct provider {base} {preferences} eligibility funding",
|
| 197 |
f"university government foundation {profile.funding_type} "
|
| 198 |
f"{profile.applying_for} {profile.field_of_study} {profile.origin} {preferences}",
|
|
|
|
| 189 |
f"{profile.funding_type} for students from {profile.origin} "
|
| 190 |
f"applying for {profile.applying_for} in {profile.field_of_study}"
|
| 191 |
)
|
|
|
|
| 192 |
preferences = profile.preferences or "international applicants"
|
| 193 |
return [
|
| 194 |
+
f"current official {base} application deadline",
|
| 195 |
f"direct provider {base} {preferences} eligibility funding",
|
| 196 |
f"university government foundation {profile.funding_type} "
|
| 197 |
f"{profile.applying_for} {profile.field_of_study} {profile.origin} {preferences}",
|
src/schemas.py
CHANGED
|
@@ -12,7 +12,6 @@ class UserProfile(BaseModel):
|
|
| 12 |
applying_for: str = Field(min_length=2, max_length=120)
|
| 13 |
field_of_study: str = Field(min_length=2, max_length=180)
|
| 14 |
funding_type: str = Field(min_length=2, max_length=120)
|
| 15 |
-
education_level: str = Field(min_length=2, max_length=120)
|
| 16 |
preferences: str = Field(default="", max_length=800)
|
| 17 |
|
| 18 |
@field_validator("*", mode="before")
|
|
@@ -28,7 +27,6 @@ class UserProfile(BaseModel):
|
|
| 28 |
self.applying_for,
|
| 29 |
self.field_of_study,
|
| 30 |
self.funding_type,
|
| 31 |
-
self.education_level,
|
| 32 |
self.preferences,
|
| 33 |
]
|
| 34 |
return " ".join(part for part in parts if part)
|
|
|
|
| 12 |
applying_for: str = Field(min_length=2, max_length=120)
|
| 13 |
field_of_study: str = Field(min_length=2, max_length=180)
|
| 14 |
funding_type: str = Field(min_length=2, max_length=120)
|
|
|
|
| 15 |
preferences: str = Field(default="", max_length=800)
|
| 16 |
|
| 17 |
@field_validator("*", mode="before")
|
|
|
|
| 27 |
self.applying_for,
|
| 28 |
self.field_of_study,
|
| 29 |
self.funding_type,
|
|
|
|
| 30 |
self.preferences,
|
| 31 |
]
|
| 32 |
return " ".join(part for part in parts if part)
|
src/ui.py
CHANGED
|
@@ -21,7 +21,6 @@ class ResearchRequest(BaseModel):
|
|
| 21 |
applying_for: str
|
| 22 |
field_of_study: str
|
| 23 |
funding_type: str
|
| 24 |
-
education_level: str
|
| 25 |
preferences: str = ""
|
| 26 |
|
| 27 |
|
|
@@ -791,16 +790,6 @@ FRONTEND_HTML = r"""
|
|
| 791 |
</select>
|
| 792 |
</label>
|
| 793 |
<label class="field full"><span>Field of study</span><input name="field_of_study" value="Computer science or data science" required></label>
|
| 794 |
-
<label class="field"><span>Current education level</span>
|
| 795 |
-
<select name="education_level">
|
| 796 |
-
<option>High school</option>
|
| 797 |
-
<option selected>Bachelor's degree completed</option>
|
| 798 |
-
<option>Master's degree completed</option>
|
| 799 |
-
<option>PhD</option>
|
| 800 |
-
<option>Postdoctoral</option>
|
| 801 |
-
<option>Professional</option>
|
| 802 |
-
</select>
|
| 803 |
-
</label>
|
| 804 |
<fieldset class="field">
|
| 805 |
<span>Funding type — pick any</span>
|
| 806 |
<div class="checkgroup">
|
|
@@ -896,9 +885,9 @@ FRONTEND_HTML = r"""
|
|
| 896 |
let apiReady = null;
|
| 897 |
|
| 898 |
const EXAMPLE_PROFILES = {
|
| 899 |
-
"ai-pakistan": { origin: "Pakistan", applying_for: "Master's degree", field_of_study: "Artificial intelligence", funding_type: "Scholarship",
|
| 900 |
-
"health-africa": { origin: "Kenya", applying_for: "Postdoctoral research", field_of_study: "Public health", funding_type: "Research grant",
|
| 901 |
-
"cs-canada": { origin: "Canada", applying_for: "PhD", field_of_study: "Computer science", funding_type: "Fellowship",
|
| 902 |
};
|
| 903 |
|
| 904 |
const escapeHtml = (v) => String(v ?? "").replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
|
|
|
| 21 |
applying_for: str
|
| 22 |
field_of_study: str
|
| 23 |
funding_type: str
|
|
|
|
| 24 |
preferences: str = ""
|
| 25 |
|
| 26 |
|
|
|
|
| 790 |
</select>
|
| 791 |
</label>
|
| 792 |
<label class="field full"><span>Field of study</span><input name="field_of_study" value="Computer science or data science" required></label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
<fieldset class="field">
|
| 794 |
<span>Funding type — pick any</span>
|
| 795 |
<div class="checkgroup">
|
|
|
|
| 885 |
let apiReady = null;
|
| 886 |
|
| 887 |
const EXAMPLE_PROFILES = {
|
| 888 |
+
"ai-pakistan": { origin: "Pakistan", applying_for: "Master's degree", field_of_study: "Artificial intelligence", funding_type: "Scholarship", preferences: "Fully funded, responsible AI, international applicants" },
|
| 889 |
+
"health-africa": { origin: "Kenya", applying_for: "Postdoctoral research", field_of_study: "Public health", funding_type: "Research grant", preferences: "African institutions and implementation research" },
|
| 890 |
+
"cs-canada": { origin: "Canada", applying_for: "PhD", field_of_study: "Computer science", funding_type: "Fellowship", preferences: "AI safety, systems, and human-centered computing" }
|
| 891 |
};
|
| 892 |
|
| 893 |
const escapeHtml = (v) => String(v ?? "").replace(/[&<>"']/g, (c) => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
tests/test_dataset_store.py
CHANGED
|
@@ -9,7 +9,6 @@ def test_dataset_store_memory_fallback() -> None:
|
|
| 9 |
applying_for="Master's degree",
|
| 10 |
field_of_study="AI",
|
| 11 |
funding_type="Scholarship",
|
| 12 |
-
education_level="Bachelor's degree completed",
|
| 13 |
preferences="Fully funded",
|
| 14 |
)
|
| 15 |
opportunity = Opportunity(title="AI Grant", source_url="https://example.org/grant")
|
|
|
|
| 9 |
applying_for="Master's degree",
|
| 10 |
field_of_study="AI",
|
| 11 |
funding_type="Scholarship",
|
|
|
|
| 12 |
preferences="Fully funded",
|
| 13 |
)
|
| 14 |
opportunity = Opportunity(title="AI Grant", source_url="https://example.org/grant")
|
tests/test_live_research_behavior.py
CHANGED
|
@@ -14,7 +14,6 @@ def test_live_mode_returns_empty_state_when_no_pages(monkeypatch) -> None:
|
|
| 14 |
applying_for="Master's degree",
|
| 15 |
field_of_study="Artificial intelligence",
|
| 16 |
funding_type="Scholarship",
|
| 17 |
-
education_level="Bachelor's degree completed",
|
| 18 |
preferences="Fully funded",
|
| 19 |
)
|
| 20 |
)
|
|
@@ -34,7 +33,6 @@ def test_missing_live_keys_returns_configuration_error(monkeypatch) -> None:
|
|
| 34 |
applying_for="Master's degree",
|
| 35 |
field_of_study="Artificial intelligence",
|
| 36 |
funding_type="Scholarship",
|
| 37 |
-
education_level="Bachelor's degree completed",
|
| 38 |
preferences="Fully funded",
|
| 39 |
)
|
| 40 |
)
|
|
@@ -62,7 +60,6 @@ def test_live_mode_uses_real_source_fallback_when_ranker_fails(monkeypatch) -> N
|
|
| 62 |
applying_for="Master's degree",
|
| 63 |
field_of_study="Artificial intelligence",
|
| 64 |
funding_type="Scholarship",
|
| 65 |
-
education_level="Bachelor's degree completed",
|
| 66 |
preferences="Fully funded",
|
| 67 |
)
|
| 68 |
)
|
|
|
|
| 14 |
applying_for="Master's degree",
|
| 15 |
field_of_study="Artificial intelligence",
|
| 16 |
funding_type="Scholarship",
|
|
|
|
| 17 |
preferences="Fully funded",
|
| 18 |
)
|
| 19 |
)
|
|
|
|
| 33 |
applying_for="Master's degree",
|
| 34 |
field_of_study="Artificial intelligence",
|
| 35 |
funding_type="Scholarship",
|
|
|
|
| 36 |
preferences="Fully funded",
|
| 37 |
)
|
| 38 |
)
|
|
|
|
| 60 |
applying_for="Master's degree",
|
| 61 |
field_of_study="Artificial intelligence",
|
| 62 |
funding_type="Scholarship",
|
|
|
|
| 63 |
preferences="Fully funded",
|
| 64 |
)
|
| 65 |
)
|
tests/test_olostep_client.py
CHANGED
|
@@ -10,7 +10,6 @@ def make_profile(funding_type: str = "Scholarship") -> UserProfile:
|
|
| 10 |
applying_for="Master's degree",
|
| 11 |
field_of_study="AI",
|
| 12 |
funding_type=funding_type,
|
| 13 |
-
education_level="Bachelor's degree completed",
|
| 14 |
preferences="Fully funded and open to international applicants",
|
| 15 |
)
|
| 16 |
|
|
|
|
| 10 |
applying_for="Master's degree",
|
| 11 |
field_of_study="AI",
|
| 12 |
funding_type=funding_type,
|
|
|
|
| 13 |
preferences="Fully funded and open to international applicants",
|
| 14 |
)
|
| 15 |
|
tests/test_schemas.py
CHANGED
|
@@ -10,14 +10,13 @@ def make_profile() -> UserProfile:
|
|
| 10 |
applying_for="Master's degree",
|
| 11 |
field_of_study="AI",
|
| 12 |
funding_type="Scholarship",
|
| 13 |
-
education_level="Bachelor's degree completed",
|
| 14 |
preferences="Fully funded",
|
| 15 |
)
|
| 16 |
|
| 17 |
|
| 18 |
def test_profile_validation_requires_detail() -> None:
|
| 19 |
with pytest.raises(ValidationError):
|
| 20 |
-
UserProfile(origin="P", applying_for="MS", field_of_study="AI", funding_type="Grant"
|
| 21 |
|
| 22 |
|
| 23 |
def test_dataset_record_creation() -> None:
|
|
|
|
| 10 |
applying_for="Master's degree",
|
| 11 |
field_of_study="AI",
|
| 12 |
funding_type="Scholarship",
|
|
|
|
| 13 |
preferences="Fully funded",
|
| 14 |
)
|
| 15 |
|
| 16 |
|
| 17 |
def test_profile_validation_requires_detail() -> None:
|
| 18 |
with pytest.raises(ValidationError):
|
| 19 |
+
UserProfile(origin="P", applying_for="MS", field_of_study="AI", funding_type="Grant")
|
| 20 |
|
| 21 |
|
| 22 |
def test_dataset_record_creation() -> None:
|