Skip to content

Commit

Permalink
Add a publication panel for each edge.
Browse files Browse the repository at this point in the history
  • Loading branch information
yjcyxky committed Mar 17, 2024
1 parent e85eaf4 commit 44541a4
Show file tree
Hide file tree
Showing 11 changed files with 449 additions and 32 deletions.
3 changes: 2 additions & 1 deletion src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
pub mod route;
pub mod schema;
pub mod auth;
pub mod auth;
pub mod req;
155 changes: 155 additions & 0 deletions src/api/req.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use anyhow;
use poem_openapi::{types::ToJSON, Object};
use reqwest;
use serde::{Deserialize, Serialize};

const CONSENSUS_API: &str = "https://consensus.app/api/paper_search/";
const CONSENSUS_DETAIL_API: &str = "https://consensus.app/api/papers/details/";

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Object)]
pub struct PublicationRecords {
pub records: Vec<Publication>,
pub total: u64,
pub page: u64,
pub page_size: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Object)]
pub struct Publication {
pub authors: Vec<String>,
pub citation_count: Option<u64>,
pub summary: String,
pub journal: String,
pub title: String,
pub year: Option<u64>,
pub doc_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Object)]
pub struct PublicationDetail {
pub authors: Vec<String>,
pub citation_count: Option<u64>,
pub summary: String,
pub journal: String,
pub title: String,
pub year: Option<u64>,
pub doc_id: String,
pub article_abstract: Option<String>,
pub doi: Option<String>,
pub provider_url: Option<String>,
}

impl Publication {
pub async fn fetch_publication(
id: &str,
) -> Result<PublicationDetail, anyhow::Error> {
let api_token = match std::env::var("CONSENSUS_API_TOKEN") {
Ok(token) => token,
Err(_) => {
return Err(anyhow::anyhow!("CONSENSUS_API_TOKEN not found"));
}
};

let url = format!("{}{}", CONSENSUS_DETAIL_API, id);
let cookie = format!("_session={}", api_token);
let client = reqwest::Client::new();
let res = client.get(&url).header("Cookie", cookie).send().await?;

if res.status().is_success() {
let body = res.text().await?;
let json: serde_json::Value = serde_json::from_str(&body)?;
let authors = json["authors"].as_array().unwrap();
let mut authors_vec = Vec::new();
for author in authors {
authors_vec.push(author.as_str().unwrap().to_string());
}
let citation_count = json["citation_count"].as_u64();
let summary = json["abstract_takeaway"].as_str().unwrap().to_string();
// Such as { "journal": { "title": "The Journal of biological chemistry","scimago_quartile": 1 }}
let journal = json["journal"]["title"].as_str().unwrap().to_string();
let title = json["title"].as_str().unwrap().to_string();
let year = json["year"].as_u64();
let doc_id = json["id"].as_str().unwrap().to_string();
let article_abstract = json["abstract"].as_str().map(|s| s.to_string());
let doi = json["doi"].as_str().map(|s| s.to_string());
let provider_url = json["provider_url"].as_str().map(|s| s.to_string());

Ok(PublicationDetail {
authors: authors_vec,
citation_count: citation_count,
summary: summary,
journal: journal,
title: title,
year: year,
doc_id: doc_id,
article_abstract: article_abstract,
doi: doi,
provider_url: provider_url,
})
} else {
Err(anyhow::anyhow!("Failed to fetch publication"))
}
}

pub async fn fetch_publications(
query_str: &str,
page: Option<u64>,
page_size: Option<u64>,
) -> Result<PublicationRecords, anyhow::Error> {
let api_token = match std::env::var("CONSENSUS_API_TOKEN") {
Ok(token) => token,
Err(_) => {
return Err(anyhow::anyhow!("CONSENSUS_API_TOKEN not found"));
}
};

// We only need to fetch the top 10 results currently.
let total = 10;
let page = 0;
let page_size = 10;

let mut records = Vec::new();
let url = format!(
"{}?query={}&page={}&size={}",
CONSENSUS_API, query_str, page, page_size
);
let cookie = format!("_session={}", api_token);
let client = reqwest::Client::new();
let res = client.get(&url).header("Cookie", cookie).send().await?;

if res.status().is_success() {
let body = res.text().await?;
let json: serde_json::Value = serde_json::from_str(&body)?;
let items = json["papers"].as_array().unwrap();
for item in items {
let authors = item["authors"].as_array().unwrap();
let mut authors_vec = Vec::new();
for author in authors {
authors_vec.push(author.as_str().unwrap().to_string());
}
let citation_count = item["citation_count"].as_u64();
let summary = item["display_text"].as_str().unwrap().to_string();
let journal = item["journal"].as_str().unwrap().to_string();
let title = item["title"].as_str().unwrap().to_string();
let year = item["year"].as_u64();
let doc_id = item["doc_id"].as_str().unwrap().to_string();
records.push(Publication {
authors: authors_vec,
citation_count: citation_count,
summary: summary,
journal: journal,
title: title,
year: year,
doc_id: doc_id,
});
}
}

Ok(PublicationRecords {
records: records,
total: total,
page: page,
page_size: page_size,
})
}
}
61 changes: 55 additions & 6 deletions src/api/route.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! This module defines the routes of the API.
use crate::api::auth::{CustomSecurityScheme, USERNAME_PLACEHOLDER};
use crate::api::req::Publication;
use crate::api::schema::{
ApiTags, DeleteResponse, GetEntityColorMapResponse, GetGraphResponse, GetPromptResponse,
GetRecordsResponse, GetRelationCountResponse, GetStatisticsResponse, GetWholeTableResponse,
NodeIdsQuery, Pagination, PaginationQuery, PostResponse, PredictedNodeQuery, PromptList,
SubgraphIdQuery,
GetPublicationsResponse, GetRecordsResponse, GetRelationCountResponse, GetStatisticsResponse,
GetWholeTableResponse, NodeIdsQuery, Pagination, PaginationQuery, PostResponse,
PredictedNodeQuery, PromptList, SubgraphIdQuery,
};
use crate::model::core::{
Entity, Entity2D, EntityMetadata, KnowledgeCuration, RecordResponse, Relation, RelationCount,
Expand All @@ -24,10 +25,58 @@ use poem_openapi::{param::Path, param::Query, payload::Json, OpenApi};
use std::sync::Arc;
use validator::Validate;

use super::schema::GetPublicationDetailResponse;

pub struct BiomedgpsApi;

#[OpenApi(prefix_path = "/api/v1")]
impl BiomedgpsApi {
/// Call `/api/v1/publications/:id` to fetch a publication.
#[oai(
path = "/publications/:id",
method = "get",
tag = "ApiTags::KnowledgeGraph",
operation_id = "fetchPublication"
)]
async fn fetch_publication(&self, id: Path<String>) -> GetPublicationDetailResponse {
let id = id.0;
match Publication::fetch_publication(&id).await {
Ok(publication) => GetPublicationDetailResponse::ok(publication),
Err(e) => {
let err = format!("Failed to fetch publication: {}", e);
warn!("{}", err);
return GetPublicationDetailResponse::bad_request(err);
}
}
}

/// Call `/api/v1/publications` with query params to fetch publications.
#[oai(
path = "/publications",
method = "get",
tag = "ApiTags::KnowledgeGraph",
operation_id = "fetchPublications"
)]
async fn fetch_publications(
&self,
query_str: Query<String>,
page: Query<Option<u64>>,
page_size: Query<Option<u64>>,
) -> GetPublicationsResponse {
let query_str = query_str.0;
let page = page.0;
let page_size = page_size.0;

match Publication::fetch_publications(&query_str, page, page_size).await {
Ok(records) => GetPublicationsResponse::ok(records),
Err(e) => {
let err = format!("Failed to fetch publications: {}", e);
warn!("{}", err);
return GetPublicationsResponse::bad_request(err);
}
}
}

/// Call `/api/v1/statistics` with query params to fetch all entity & relation metadata.
#[oai(
path = "/statistics",
Expand Down Expand Up @@ -522,7 +571,7 @@ impl BiomedgpsApi {
page,
page_size,
Some("id ASC"),
None
None,
)
.await
{
Expand Down Expand Up @@ -707,7 +756,7 @@ impl BiomedgpsApi {
page,
page_size,
Some("score ASC"),
None
None,
)
.await
{
Expand Down Expand Up @@ -827,7 +876,7 @@ impl BiomedgpsApi {
page,
page_size,
Some("embedding_id ASC"),
None
None,
)
.await
{
Expand Down
54 changes: 54 additions & 0 deletions src/api/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize};
use validator::Validate;
use validator::ValidationErrors;

use super::req::{PublicationDetail, PublicationRecords};

#[derive(Tags)]
pub enum ApiTags {
KnowledgeGraph,
Expand Down Expand Up @@ -211,6 +213,58 @@ impl<
}
}

#[derive(ApiResponse)]
pub enum GetPublicationDetailResponse {
#[oai(status = 200)]
Ok(Json<PublicationDetail>),

#[oai(status = 400)]
BadRequest(Json<ErrorMessage>),

#[oai(status = 404)]
NotFound(Json<ErrorMessage>),
}

impl GetPublicationDetailResponse {
pub fn ok(publication_detail: PublicationDetail) -> Self {
Self::Ok(Json(publication_detail))
}

pub fn bad_request(msg: String) -> Self {
Self::BadRequest(Json(ErrorMessage { msg }))
}

pub fn not_found(msg: String) -> Self {
Self::NotFound(Json(ErrorMessage { msg }))
}
}

#[derive(ApiResponse)]
pub enum GetPublicationsResponse {
#[oai(status = 200)]
Ok(Json<PublicationRecords>),

#[oai(status = 400)]
BadRequest(Json<ErrorMessage>),

#[oai(status = 404)]
NotFound(Json<ErrorMessage>),
}

impl GetPublicationsResponse {
pub fn ok(publication_records: PublicationRecords) -> Self {
Self::Ok(Json(publication_records))
}

pub fn bad_request(msg: String) -> Self {
Self::BadRequest(Json(ErrorMessage { msg }))
}

pub fn not_found(msg: String) -> Self {
Self::NotFound(Json(ErrorMessage { msg }))
}
}

#[derive(ApiResponse)]
pub enum GetRecordsResponse<
S: Serialize
Expand Down
7 changes: 6 additions & 1 deletion src/bin/biomedgps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ struct Opt {
#[structopt(name = "database-url", short = "d", long = "database-url")]
database_url: Option<String>,

/// Pool size for database connection.
#[structopt(name = "pool-size", short = "s", long = "pool-size")]
pool_size: Option<u32>,

/// Graph Database url, such as neo4j:://user:pass@host:port. We will always use the default database.
/// You can also set it with env var: NEO4J_URL.
#[structopt(name = "neo4j-url", short = "g", long = "neo4j-url")]
Expand Down Expand Up @@ -239,7 +243,8 @@ async fn main() -> Result<(), std::io::Error> {
database_url.unwrap()
};

let pool = connect_db(&database_url, 10).await;
let pool_size = args.pool_size.unwrap_or(10);
let pool = connect_db(&database_url, pool_size).await;
let arc_pool = Arc::new(pool);
let shared_rb = AddData::new(arc_pool.clone());

Expand Down
2 changes: 1 addition & 1 deletion studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"antd": "5.7.0",
"antd-schema-form": "^4.5.1",
"axios": "^1.1.2",
"biominer-components": "0.3.13",
"biominer-components": "0.3.15",
"classnames": "^2.3.0",
"handlebars": "^4.7.7",
"handsontable": "^12.1.3",
Expand Down
Loading

0 comments on commit 44541a4

Please sign in to comment.