Spaces:
Runtime error
Runtime error
Download scripts/kgraph.py from derangedsaturnian/NasaKnowledgeEngine2025: direct link, hf CLI and curl.
- Browser
- Download file 7.64 kB
-
https://huggingface.co/spaces/derangedsaturnian/NasaKnowledgeEngine2025/resolve/main/scripts/kgraph.py
- Command line
-
hf download hf://spaces/derangedsaturnian/NasaKnowledgeEngine2025/scripts/kgraph.py
-
curl -L -o kgraph.py https://huggingface.co/spaces/derangedsaturnian/NasaKnowledgeEngine2025/resolve/main/scripts/kgraph.py
7.64 kB
| from neo4j import GraphDatabase | |
| from dotenv import load_dotenv | |
| import os | |
| import spacy | |
| import sqlite3 | |
| import shutil | |
| from pathlib import Path | |
| original_db = Path("data/nasa_data.db") | |
| runtime_db = Path("/tmp/nasa_data.db") | |
| if not runtime_db.exists(): | |
| shutil.copy(original_db, runtime_db) | |
| print(f"copied {original_db} → {runtime_db}") | |
| else: | |
| print(f"usiing existing runtime DB at {runtime_db}") | |
| nlp = spacy.load("en_core_web_sm") | |
| load_dotenv() | |
| NEO4J_URI = os.getenv("NEO4J_URI") | |
| NEO4J_USER = os.getenv("NEO4J_USERNAME") | |
| NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD") | |
| print("connecting to Neo4j at:", NEO4J_URI) | |
| driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD)) | |
| driver.verify_connectivity() | |
| with driver.session() as session: | |
| result = session.run("RETURN 'Neo4j connection successful!' AS msg") | |
| print(result.single()["msg"]) | |
| def extract_entities(text): | |
| """return (entity_text, entity_type) from text using spacy NER""" | |
| doc = nlp(text) | |
| return [(ent.text, ent.label_) for ent in doc.ents] | |
| def extract_keywords(text): | |
| """return important nouns/proper nouns from text""" | |
| doc = nlp(text) | |
| return [token.lemma_.capitalize() for token in doc if token.pos_ in ("NOUN", "PROPN") and len(token.text) > 3] | |
| def load_data(): | |
| """laoding projects, publications, links, PIs, authors and abstracts from SQLite""" | |
| db_path = "/tmp/nasa_data.db" | |
| conn = sqlite3.connect(db_path) | |
| cur = conn.cursor() | |
| cur.execute("SELECT Task_id, [Project Title] FROM projects") | |
| proj = cur.fetchall() | |
| cur.execute("SELECT pmc_id, title FROM publications") | |
| pub = cur.fetchall() | |
| cur.execute("SELECT project_id, pmc_id, link_type, confidence FROM links") | |
| links = cur.fetchall() | |
| cur.execute("SELECT Task_id, PI_Name FROM projects") | |
| pis = cur.fetchall() | |
| cur.execute("SELECT pmc_id, authors FROM publications") | |
| authors = cur.fetchall() | |
| cur.execute("SELECT pmc_id, abstract FROM publications") | |
| abstracts = cur.fetchall() | |
| conn.close() | |
| return proj, pub, links, pis, authors, abstracts | |
| def clear_old_data(session): | |
| """optionallly clear old nodes and relationships for a clean rebuild""" | |
| session.run("MATCH ()-[r:LINKS_TO]->() DELETE r") | |
| session.run("MATCH (t:Topic) DETACH DELETE t") | |
| session.run("MATCH (e:Entity) DETACH DELETE e") | |
| def create_links(session, proj, pub, links): | |
| """create proj and pub nodes and LINKS_TO relationships""" | |
| # proj | |
| for proj_id, title in proj: | |
| session.run(""" | |
| MERGE (p:Project {id: $id}) | |
| SET p.title = $title | |
| """, id=str(proj_id), title=title) | |
| # pub | |
| for pmc_id, title in pub: | |
| session.run(""" | |
| MERGE (pub:Publication {pmc_id: $pmc_id}) | |
| SET pub.title = $title | |
| """, pmc_id=str(pmc_id).upper(), title=title) | |
| # LINKS_TO relationships | |
| for proj_id, pmc_id, link_type, conf in links: | |
| if conf < 0.5: | |
| continue | |
| session.run(""" | |
| MATCH (p:Project {id: $proj_id}) | |
| MATCH (pub:Publication {pmc_id: $pmc_id}) | |
| MERGE (p)-[r:LINKS_TO]->(pub) | |
| SET r.type = $link_type, | |
| r.confidence = $confidence | |
| """, proj_id=str(proj_id), pmc_id=str(pmc_id).upper(), link_type=link_type, confidence=conf) | |
| def add_people_relationships(session, pis, authors): | |
| """add person nodes and link them as HAS_PI or HAS_AUTHOR""" | |
| # PIs | |
| for proj_id, pi_name in pis: | |
| if not pi_name: | |
| continue | |
| session.run("MERGE (person:Person {name: $pi_name})", pi_name=pi_name) | |
| session.run(""" | |
| MATCH (p:Project {id: $proj_id}) | |
| MATCH (person:Person {name: $pi_name}) | |
| MERGE (p)-[:HAS_PI]->(person) | |
| """, proj_id=str(proj_id), pi_name=pi_name) | |
| # authors | |
| for pmc_id, author_str in authors: | |
| if not author_str: | |
| continue | |
| for author in [a.strip() for a in author_str.split(",") if a.strip()]: | |
| session.run("MERGE (person:Person {name: $author})", author=author) | |
| session.run(""" | |
| MATCH (pub:Publication {pmc_id: $pmc_id}) | |
| MATCH (person:Person {name: $author}) | |
| MERGE (pub)-[:HAS_AUTHOR]->(person) | |
| """, pmc_id=str(pmc_id).upper(), author=author) | |
| def add_coauthor_rel(session): | |
| session.run(""" | |
| MATCH (a:Person)<-[:HAS_AUTHOR]-(pub:Publication)-[:HAS_AUTHOR]->(b:Person) | |
| WHERE a <> b | |
| MERGE (a)-[r:CO_AUTHOR_WITH]-(b) | |
| ON CREATE SET r.count = 1 | |
| ON MATCH SET r.count = r.count + 1 | |
| """) | |
| def add_pi_author_overlap(session): | |
| session.run(""" | |
| MATCH (p:Project)-[:HAS_PI]->(pi:Person) | |
| MATCH (pub:Publication)-[:HAS_AUTHOR]->(pi) | |
| MERGE (p)-[:PI_IS_AUTHOR_OF]->(pub) | |
| """) | |
| def add_project_similarity(session): | |
| session.run(""" | |
| MATCH (p1:Project)-[:LINKS_TO]->(pub:Publication)<-[:LINKS_TO]-(p2:Project) | |
| WHERE p1 <> p2 | |
| MERGE (p1)-[r:RELATED_TO_PROJECT]->(p2) | |
| ON CREATE SET r.shared_pubs = 1 | |
| ON MATCH SET r.shared_pubs = r.shared_pubs + 1 | |
| """) | |
| def add_author_project_links(session): | |
| session.run(""" | |
| MATCH (p:Project)-[:LINKS_TO]->(pub:Publication)-[:HAS_AUTHOR]->(a:Person) | |
| MERGE (a)-[r:WORKS_ON]->(p) | |
| ON CREATE SET r.via_publications = 1 | |
| ON MATCH SET r.via_publications = r.via_publications + 1 | |
| """) | |
| def add_topic_nodes(session, projects, publications): | |
| for proj_id, title in projects: | |
| if not title: | |
| continue | |
| for kw in extract_keywords(title): | |
| session.run(""" | |
| MERGE (t:Topic {name: $topic}) | |
| WITH t | |
| MATCH (p:Project {id: $proj_id}) | |
| MERGE (p)-[:HAS_TOPIC]->(t) | |
| """, topic=kw, proj_id=str(proj_id)) | |
| for pmc_id, title in publications: | |
| if not title: | |
| continue | |
| for kw in extract_keywords(title): | |
| session.run(""" | |
| MERGE (t:Topic {name: $topic}) | |
| WITH t | |
| MATCH (pub:Publication {pmc_id: $pmc_id}) | |
| MERGE (pub)-[:HAS_TOPIC]->(t) | |
| """, topic=kw, pmc_id=str(pmc_id).upper()) | |
| def add_publication_text(session, abstracts): | |
| for pmc_id, abstract in abstracts: | |
| if not abstract: | |
| continue | |
| session.run(""" | |
| MATCH (pub:Publication {pmc_id: $pmc_id}) | |
| SET pub.abstract = $abstract | |
| """, pmc_id=str(pmc_id).upper(), abstract=abstract) | |
| def add_entities_from_text(session, abstracts): | |
| for pmc_id, abstract in abstracts: | |
| if not abstract: | |
| continue | |
| entities = extract_entities(abstract) | |
| for entity_text, entity_type in entities: | |
| session.run(""" | |
| MERGE (e:Entity {name: $name, type: $type}) | |
| WITH e | |
| MATCH (pub:Publication {pmc_id: $pmc_id}) | |
| MERGE (pub)-[:MENTIONS_ENTITY]->(e) | |
| """, name=entity_text, type=entity_type, pmc_id=str(pmc_id).upper()) | |
| proj, pub, links, pis, authors, abstracts = load_data() | |
| with driver.session() as session: | |
| clear_old_data(session) | |
| create_links(session, proj, pub, links) | |
| add_people_relationships(session, pis, authors) | |
| add_coauthor_rel(session) | |
| add_pi_author_overlap(session) | |
| add_project_similarity(session) | |
| add_author_project_links(session) | |
| add_topic_nodes(session, proj, pub) | |
| add_publication_text(session, abstracts) | |
| add_entities_from_text(session, abstracts) | |
| driver.close() | |
| print("data pushed to Neo4j successfully!") | |