Initial commit
Browse files- .gitignore +132 -0
- LICENSE +21 -0
- Procfile +1 -0
- app.py +23 -0
- apps/google.py +95 -0
- apps/grant.py +108 -0
- apps/h_index.py +67 -0
- apps/home.py +16 -0
- apps/journal.py +310 -0
- apps/orcid.py +146 -0
- apps/organization.py +153 -0
- apps/publication.py +122 -0
- apps/researcher.py +240 -0
- data/journals.json +280 -0
- data/journals.xlsx +0 -0
- multiapp.py +71 -0
- packages.txt +0 -0
- postBuild +6 -0
- requirements.txt +14 -0
- setup.sh +8 -0
- streamlit_app.py +61 -0
- streamlit_call.py +14 -0
.gitignore
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
data/*.csv
|
| 12 |
+
.vscode/
|
| 13 |
+
private/
|
| 14 |
+
build/
|
| 15 |
+
develop-eggs/
|
| 16 |
+
dist/
|
| 17 |
+
downloads/
|
| 18 |
+
eggs/
|
| 19 |
+
.eggs/
|
| 20 |
+
lib/
|
| 21 |
+
lib64/
|
| 22 |
+
parts/
|
| 23 |
+
sdist/
|
| 24 |
+
var/
|
| 25 |
+
wheels/
|
| 26 |
+
pip-wheel-metadata/
|
| 27 |
+
share/python-wheels/
|
| 28 |
+
*.egg-info/
|
| 29 |
+
.installed.cfg
|
| 30 |
+
*.egg
|
| 31 |
+
MANIFEST
|
| 32 |
+
|
| 33 |
+
# PyInstaller
|
| 34 |
+
# Usually these files are written by a python script from a template
|
| 35 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 36 |
+
*.manifest
|
| 37 |
+
*.spec
|
| 38 |
+
|
| 39 |
+
# Installer logs
|
| 40 |
+
pip-log.txt
|
| 41 |
+
pip-delete-this-directory.txt
|
| 42 |
+
|
| 43 |
+
# Unit test / coverage reports
|
| 44 |
+
htmlcov/
|
| 45 |
+
.tox/
|
| 46 |
+
.nox/
|
| 47 |
+
.coverage
|
| 48 |
+
.coverage.*
|
| 49 |
+
.cache
|
| 50 |
+
nosetests.xml
|
| 51 |
+
coverage.xml
|
| 52 |
+
*.cover
|
| 53 |
+
*.py,cover
|
| 54 |
+
.hypothesis/
|
| 55 |
+
.pytest_cache/
|
| 56 |
+
|
| 57 |
+
# Translations
|
| 58 |
+
*.mo
|
| 59 |
+
*.pot
|
| 60 |
+
|
| 61 |
+
# Django stuff:
|
| 62 |
+
*.log
|
| 63 |
+
local_settings.py
|
| 64 |
+
db.sqlite3
|
| 65 |
+
db.sqlite3-journal
|
| 66 |
+
|
| 67 |
+
# Flask stuff:
|
| 68 |
+
instance/
|
| 69 |
+
.webassets-cache
|
| 70 |
+
|
| 71 |
+
# Scrapy stuff:
|
| 72 |
+
.scrapy
|
| 73 |
+
|
| 74 |
+
# Sphinx documentation
|
| 75 |
+
docs/_build/
|
| 76 |
+
|
| 77 |
+
# PyBuilder
|
| 78 |
+
target/
|
| 79 |
+
|
| 80 |
+
# Jupyter Notebook
|
| 81 |
+
.ipynb_checkpoints
|
| 82 |
+
|
| 83 |
+
# IPython
|
| 84 |
+
profile_default/
|
| 85 |
+
ipython_config.py
|
| 86 |
+
|
| 87 |
+
# pyenv
|
| 88 |
+
.python-version
|
| 89 |
+
|
| 90 |
+
# pipenv
|
| 91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 94 |
+
# install all needed dependencies.
|
| 95 |
+
#Pipfile.lock
|
| 96 |
+
|
| 97 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
| 98 |
+
__pypackages__/
|
| 99 |
+
|
| 100 |
+
# Celery stuff
|
| 101 |
+
celerybeat-schedule
|
| 102 |
+
celerybeat.pid
|
| 103 |
+
|
| 104 |
+
# SageMath parsed files
|
| 105 |
+
*.sage.py
|
| 106 |
+
|
| 107 |
+
# Environments
|
| 108 |
+
.env
|
| 109 |
+
.venv
|
| 110 |
+
env/
|
| 111 |
+
venv/
|
| 112 |
+
ENV/
|
| 113 |
+
env.bak/
|
| 114 |
+
venv.bak/
|
| 115 |
+
|
| 116 |
+
# Spyder project settings
|
| 117 |
+
.spyderproject
|
| 118 |
+
.spyproject
|
| 119 |
+
|
| 120 |
+
# Rope project settings
|
| 121 |
+
.ropeproject
|
| 122 |
+
|
| 123 |
+
# mkdocs documentation
|
| 124 |
+
/site
|
| 125 |
+
|
| 126 |
+
# mypy
|
| 127 |
+
.mypy_cache/
|
| 128 |
+
.dmypy.json
|
| 129 |
+
dmypy.json
|
| 130 |
+
|
| 131 |
+
# Pyre type checker
|
| 132 |
+
.pyre/
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2021 Qiusheng Wu
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
Procfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
web: sh setup.sh && streamlit run streamlit_app.py
|
app.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import scholarpy
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from multiapp import MultiApp
|
| 4 |
+
from apps import grant, home, journal, orcid, publication, researcher
|
| 5 |
+
|
| 6 |
+
st.set_page_config(layout="wide")
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
apps = MultiApp()
|
| 12 |
+
|
| 13 |
+
# Add all your application here
|
| 14 |
+
|
| 15 |
+
apps.add_app("Home", home.app)
|
| 16 |
+
apps.add_app("Grant", grant.app)
|
| 17 |
+
apps.add_app("Journal", journal.app)
|
| 18 |
+
apps.add_app("Publication", publication.app)
|
| 19 |
+
apps.add_app("Researcher", researcher.app)
|
| 20 |
+
apps.add_app("ORCID", orcid.app)
|
| 21 |
+
|
| 22 |
+
# The main app
|
| 23 |
+
apps.run()
|
apps/google.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import scholarpy
|
| 3 |
+
import tempfile
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import leafmap.foliumap as leafmap
|
| 7 |
+
import plotly.express as px
|
| 8 |
+
from scholarly import scholarly
|
| 9 |
+
|
| 10 |
+
# if "dsl" not in st.session_state:
|
| 11 |
+
# st.session_state["dsl"] = scholarpy.Dsl()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def app():
|
| 15 |
+
|
| 16 |
+
st.title("Search Google Scholar")
|
| 17 |
+
|
| 18 |
+
row1_col1, row1_col2 = st.columns([1, 1])
|
| 19 |
+
placeholder = st.empty()
|
| 20 |
+
|
| 21 |
+
with row1_col1:
|
| 22 |
+
name = st.text_input("Enter a researcher name:", "")
|
| 23 |
+
|
| 24 |
+
if name:
|
| 25 |
+
placeholder.text("Searching...")
|
| 26 |
+
if name not in st.session_state:
|
| 27 |
+
authors = scholarpy.get_author_list(name)
|
| 28 |
+
st.session_state[name] = authors
|
| 29 |
+
else:
|
| 30 |
+
authors = st.session_state[name]
|
| 31 |
+
placeholder.empty()
|
| 32 |
+
|
| 33 |
+
if len(authors) == 0:
|
| 34 |
+
with row1_col1:
|
| 35 |
+
st.write("No results found")
|
| 36 |
+
else:
|
| 37 |
+
with row1_col1:
|
| 38 |
+
st.write("Found {} results:".format(len(authors)))
|
| 39 |
+
|
| 40 |
+
author = st.selectbox("Select a researcher:", authors)
|
| 41 |
+
|
| 42 |
+
if author:
|
| 43 |
+
placeholder.text("Retrieving data...")
|
| 44 |
+
id = author.split("|")[1].strip()
|
| 45 |
+
if id not in st.session_state:
|
| 46 |
+
record = scholarpy.get_author_record(id=id)
|
| 47 |
+
st.session_state[id] = record
|
| 48 |
+
else:
|
| 49 |
+
record = st.session_state[id]
|
| 50 |
+
basics = scholarpy.get_author_basics(
|
| 51 |
+
record=record, return_df=True)
|
| 52 |
+
out_csv = os.path.join(tempfile.gettempdir(), "basics.csv")
|
| 53 |
+
basics.to_csv(out_csv, sep="\t", index=False)
|
| 54 |
+
df = pd.read_csv(out_csv, sep="\t")
|
| 55 |
+
with row1_col1:
|
| 56 |
+
st.header("Basic information")
|
| 57 |
+
markdown = f"""Google Scholar Profile: <https://scholar.google.com/citations?user={id}>"""
|
| 58 |
+
st.markdown(markdown)
|
| 59 |
+
if "url_picture" in record and len(record["url_picture"]) > 0:
|
| 60 |
+
st.image(record["url_picture"])
|
| 61 |
+
st.dataframe(df)
|
| 62 |
+
leafmap.st_download_button(
|
| 63 |
+
"Download data", df, csv_sep="\t")
|
| 64 |
+
|
| 65 |
+
pubs = scholarpy.get_author_pubs(record=record, return_df=True)
|
| 66 |
+
with row1_col1:
|
| 67 |
+
st.header("Publications")
|
| 68 |
+
st.text(f"Total number of publications: {len(pubs)}")
|
| 69 |
+
st.dataframe(pubs)
|
| 70 |
+
leafmap.st_download_button(
|
| 71 |
+
"Download data", pubs, csv_sep="\t")
|
| 72 |
+
|
| 73 |
+
pubs_stats, pubs_fig = scholarpy.author_pubs_by_year(
|
| 74 |
+
record=record, return_plot=True)
|
| 75 |
+
citations_stats, citations_fig = scholarpy.author_citations_by_year(
|
| 76 |
+
record=record, return_plot=True)
|
| 77 |
+
|
| 78 |
+
with row1_col2:
|
| 79 |
+
st.header("Plots")
|
| 80 |
+
st.plotly_chart(pubs_fig)
|
| 81 |
+
leafmap.st_download_button("Download data", pubs_stats,
|
| 82 |
+
file_name="data.csv", csv_sep="\t")
|
| 83 |
+
st.plotly_chart(citations_fig)
|
| 84 |
+
leafmap.st_download_button(
|
| 85 |
+
"Download data", citations_stats, file_name="data.csv", csv_sep="\t")
|
| 86 |
+
if len(record["coauthors"]) > 0:
|
| 87 |
+
st.header("Co-authors")
|
| 88 |
+
st.text(
|
| 89 |
+
"Co-authors listed on Google Scholar profile only.")
|
| 90 |
+
coauthors = scholarpy.get_author_coauthors(
|
| 91 |
+
record=record, return_df=True)
|
| 92 |
+
st.dataframe(coauthors)
|
| 93 |
+
leafmap.st_download_button(
|
| 94 |
+
"Download data", coauthors, file_name="data.csv", csv_sep="\t")
|
| 95 |
+
placeholder.empty()
|
apps/grant.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import scholarpy
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import leafmap.foliumap as leafmap
|
| 6 |
+
import plotly.express as px
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def app():
|
| 13 |
+
st.title("Search Grants")
|
| 14 |
+
dsl = st.session_state["dsl"]
|
| 15 |
+
|
| 16 |
+
(
|
| 17 |
+
row1_col1,
|
| 18 |
+
row1_col2,
|
| 19 |
+
row1_col3,
|
| 20 |
+
row1_col4,
|
| 21 |
+
row1_col5,
|
| 22 |
+
) = st.columns([1, 0.5, 1, 1, 1])
|
| 23 |
+
|
| 24 |
+
(
|
| 25 |
+
row2_col1,
|
| 26 |
+
row2_col2,
|
| 27 |
+
row2_col3,
|
| 28 |
+
row2_col4,
|
| 29 |
+
row2_col5,
|
| 30 |
+
) = st.columns([1, 0.5, 1, 1, 1])
|
| 31 |
+
|
| 32 |
+
with row1_col1:
|
| 33 |
+
keywords = st.text_input("Enter a keyword to search for")
|
| 34 |
+
|
| 35 |
+
with row1_col2:
|
| 36 |
+
exact_match = st.checkbox("Exact match", True)
|
| 37 |
+
|
| 38 |
+
with row1_col3:
|
| 39 |
+
scope = st.selectbox(
|
| 40 |
+
"Select a search scope",
|
| 41 |
+
[
|
| 42 |
+
"concepts",
|
| 43 |
+
"full_data",
|
| 44 |
+
"investigators",
|
| 45 |
+
"title_abstract_only",
|
| 46 |
+
"title_only",
|
| 47 |
+
],
|
| 48 |
+
index=4,
|
| 49 |
+
)
|
| 50 |
+
with row1_col4:
|
| 51 |
+
years = st.slider("Select the start and end year:", 1950, 2030, (2010, 2025))
|
| 52 |
+
|
| 53 |
+
with row1_col5:
|
| 54 |
+
limit = st.slider("Select the number of grants to return", 1, 1000, 100)
|
| 55 |
+
|
| 56 |
+
if keywords:
|
| 57 |
+
result = dsl.search_grants_by_keyword(
|
| 58 |
+
keywords,
|
| 59 |
+
exact_match,
|
| 60 |
+
scope,
|
| 61 |
+
start_year=years[0],
|
| 62 |
+
end_year=years[1],
|
| 63 |
+
limit=limit,
|
| 64 |
+
)
|
| 65 |
+
df = scholarpy.json_to_df(result)
|
| 66 |
+
if limit > result.count_total:
|
| 67 |
+
limit = result.count_total
|
| 68 |
+
markdown = f"""
|
| 69 |
+
Returned grants: {limit} (total = {result.count_total})
|
| 70 |
+
|
| 71 |
+
"""
|
| 72 |
+
with row2_col1:
|
| 73 |
+
st.markdown(markdown)
|
| 74 |
+
|
| 75 |
+
with row2_col2:
|
| 76 |
+
filter = st.checkbox("Apply a filter")
|
| 77 |
+
|
| 78 |
+
if filter:
|
| 79 |
+
countries = []
|
| 80 |
+
for row in df.itertuples():
|
| 81 |
+
countries.append(eval(row.funder_countries)[0]["name"])
|
| 82 |
+
df["funder_country"] = countries
|
| 83 |
+
with row2_col3:
|
| 84 |
+
filter_by = st.selectbox(
|
| 85 |
+
"Select a filter",
|
| 86 |
+
[
|
| 87 |
+
"funder_country",
|
| 88 |
+
"funding_org_name",
|
| 89 |
+
"funding_org_acronym",
|
| 90 |
+
"research_org_name",
|
| 91 |
+
],
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
df["funding_org_acronym"] = df["funding_org_acronym"].astype(str)
|
| 95 |
+
df["research_org_name"] = df["research_org_name"].astype(str)
|
| 96 |
+
options = df[filter_by].unique()
|
| 97 |
+
options.sort()
|
| 98 |
+
|
| 99 |
+
with row2_col4:
|
| 100 |
+
selected = st.selectbox("Select a filter value", options)
|
| 101 |
+
df = df[df[filter_by] == selected]
|
| 102 |
+
|
| 103 |
+
with row2_col5:
|
| 104 |
+
st.write("")
|
| 105 |
+
|
| 106 |
+
if df is not None:
|
| 107 |
+
st.dataframe(df)
|
| 108 |
+
leafmap.st_download_button("Download data", df, csv_sep="\t")
|
apps/h_index.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import dimcli
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import scholarpy
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@st.cache
|
| 13 |
+
def the_H_function(sorted_citations_list, n=1):
|
| 14 |
+
"""from a list of integers [n1, n2 ..] representing publications citations,
|
| 15 |
+
return the max list-position which is >= integer
|
| 16 |
+
|
| 17 |
+
eg
|
| 18 |
+
>>> the_H_function([10, 8, 5, 4, 3]) => 4
|
| 19 |
+
>>> the_H_function([25, 8, 5, 3, 3]) => 3
|
| 20 |
+
>>> the_H_function([1000, 20]) => 2
|
| 21 |
+
"""
|
| 22 |
+
if sorted_citations_list and sorted_citations_list[0] >= n:
|
| 23 |
+
return the_H_function(sorted_citations_list[1:], n + 1)
|
| 24 |
+
else:
|
| 25 |
+
return n - 1
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def dim_login(key=None, endpoint=None):
|
| 29 |
+
|
| 30 |
+
if key is None:
|
| 31 |
+
KEY = os.environ.get("DIM_TOKEN")
|
| 32 |
+
|
| 33 |
+
if endpoint is None:
|
| 34 |
+
ENDPOINT = "https://app.dimensions.ai"
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
dimcli.login(key=KEY, endpoint=ENDPOINT)
|
| 38 |
+
dsl = dimcli.Dsl()
|
| 39 |
+
return dsl
|
| 40 |
+
except:
|
| 41 |
+
raise Exception("Failed to login to Dimensions")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@st.cache
|
| 45 |
+
def get_pubs_df(dsl, researcher_id):
|
| 46 |
+
|
| 47 |
+
q = """search publications where researchers.id = "{}" return publications[id+title+doi+times_cited] sort by times_cited limit 1000"""
|
| 48 |
+
|
| 49 |
+
pubs = dsl.query(q.format(researcher_id))
|
| 50 |
+
return pubs.as_dataframe()
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@st.cache
|
| 54 |
+
def get_citations(df):
|
| 55 |
+
return list(df.fillna(0)["times_cited"])
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def app():
|
| 59 |
+
|
| 60 |
+
dsl = st.session_state["dsl"]
|
| 61 |
+
|
| 62 |
+
researchER_id = st.text_input("Enter researcher ID:", "ur.013632443777.66")
|
| 63 |
+
df = get_pubs_df(dsl, researchER_id)
|
| 64 |
+
st.dataframe(df)
|
| 65 |
+
citations = get_citations(df)
|
| 66 |
+
h_index = the_H_function(citations)
|
| 67 |
+
st.write(f"H-index: {h_index}")
|
apps/home.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def app():
|
| 5 |
+
st.title("Home")
|
| 6 |
+
st.text(
|
| 7 |
+
"Welcome to the Scholar Web App. Click on the left sidebar menu to explore."
|
| 8 |
+
)
|
| 9 |
+
markdown = """
|
| 10 |
+
Disclaimer: The data records are pulled from the [Dimensions.ai database](https://app.dimensions.ai), which might not be the most complete bibliometric database.
|
| 11 |
+
We plan to incorporate [Scopus](https://www.scopus.com) and [Google Scholar](https://scholar.google.com) in the near future. Don't be surprised if you see that
|
| 12 |
+
your publication records are not the same as your Google Scholar profile. This is a very preliminary version. A lot more features will be added in the future.
|
| 13 |
+
We would welcome any feedback. Please send feedback to Qiusheng Wu ([email protected]).
|
| 14 |
+
"""
|
| 15 |
+
st.info(markdown)
|
| 16 |
+
st.image("https://i.imgur.com/ZNUJ9fF.gif")
|
apps/journal.py
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import dimcli
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import plotly.express as px
|
| 6 |
+
import streamlit as st
|
| 7 |
+
import scholarpy
|
| 8 |
+
import leafmap.foliumap as leafmap
|
| 9 |
+
|
| 10 |
+
if "dsl" not in st.session_state:
|
| 11 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 12 |
+
|
| 13 |
+
# create output data folder
|
| 14 |
+
FOLDER_NAME = "data"
|
| 15 |
+
if not (os.path.exists(FOLDER_NAME)):
|
| 16 |
+
os.mkdir(FOLDER_NAME)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def save(df, filename_dot_csv):
|
| 20 |
+
df.to_csv(FOLDER_NAME + "/" + filename_dot_csv, index=False)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def read(filename_dot_csv):
|
| 24 |
+
df = pd.read_csv(FOLDER_NAME + "/" + filename_dot_csv)
|
| 25 |
+
return df
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@st.cache
|
| 29 |
+
def get_token():
|
| 30 |
+
|
| 31 |
+
return os.environ.get("DIM_TOKEN")
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
@st.cache
|
| 35 |
+
def get_journals():
|
| 36 |
+
|
| 37 |
+
with open("data/journals.json") as f:
|
| 38 |
+
journals = json.load(f)
|
| 39 |
+
|
| 40 |
+
return journals
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@st.cache
|
| 44 |
+
def read_excel(sheet_name):
|
| 45 |
+
|
| 46 |
+
df = pd.read_excel(
|
| 47 |
+
"data/journals.xlsx", sheet_name=sheet_name, index_col=False, engine="openpyxl"
|
| 48 |
+
)
|
| 49 |
+
df.set_index("Rank", inplace=True)
|
| 50 |
+
return df
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def app():
|
| 54 |
+
|
| 55 |
+
st.title("Search Journals")
|
| 56 |
+
dsl = st.session_state["dsl"]
|
| 57 |
+
search_type = st.radio(
|
| 58 |
+
"Select a search type",
|
| 59 |
+
["Search by journal title", "List Google Scholar journal categories"],
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
if search_type == "Search by journal title":
|
| 63 |
+
row1_col1, row1_col2, row1_col3, _ = st.columns([1, 1, 2, 1])
|
| 64 |
+
with row1_col1:
|
| 65 |
+
name = st.text_input("Enter a journal title")
|
| 66 |
+
|
| 67 |
+
with row1_col2:
|
| 68 |
+
exact_match = st.checkbox("Exact match")
|
| 69 |
+
|
| 70 |
+
with row1_col3:
|
| 71 |
+
options = [
|
| 72 |
+
"book",
|
| 73 |
+
"book_series",
|
| 74 |
+
"proceeding",
|
| 75 |
+
"journal",
|
| 76 |
+
"preprint_platform",
|
| 77 |
+
]
|
| 78 |
+
types = st.multiselect(
|
| 79 |
+
"Select journal types", options, ["journal", "book_series"]
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
if name:
|
| 83 |
+
result = dsl.search_journal_by_title(name, exact_match=exact_match)
|
| 84 |
+
if result is not None:
|
| 85 |
+
titles = result.as_dataframe()
|
| 86 |
+
titles = titles[titles["type"].isin(types)]
|
| 87 |
+
titles.sort_values("title", inplace=True)
|
| 88 |
+
else:
|
| 89 |
+
titles = pd.DataFrame()
|
| 90 |
+
# titles = titles.astype({"start_year": int})
|
| 91 |
+
if not titles.empty:
|
| 92 |
+
|
| 93 |
+
markdown = f"""
|
| 94 |
+
Returned Journals: {len(titles)}
|
| 95 |
+
|
| 96 |
+
"""
|
| 97 |
+
st.markdown(markdown)
|
| 98 |
+
|
| 99 |
+
st.dataframe(titles)
|
| 100 |
+
titles["uid"] = (
|
| 101 |
+
titles["id"] + " | " + titles["type"] + " | " + titles["title"]
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
row2_col1, row2_col2, row2_col3, row2_col4, row2_col5 = st.columns(
|
| 105 |
+
[2.4, 1, 0.6, 1, 1]
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
with row2_col1:
|
| 109 |
+
title = st.selectbox(
|
| 110 |
+
"Select a journal title", titles["uid"].values.tolist()
|
| 111 |
+
)
|
| 112 |
+
|
| 113 |
+
with row2_col2:
|
| 114 |
+
keyword = st.text_input("Enter a keyword to search for")
|
| 115 |
+
|
| 116 |
+
with row2_col3:
|
| 117 |
+
exact_match = st.checkbox("Exact match", True)
|
| 118 |
+
|
| 119 |
+
with row2_col4:
|
| 120 |
+
scope = st.selectbox(
|
| 121 |
+
"Select a search scope",
|
| 122 |
+
[
|
| 123 |
+
"authors",
|
| 124 |
+
"concepts",
|
| 125 |
+
"full_data",
|
| 126 |
+
"full_data_exact",
|
| 127 |
+
"title_abstract_only",
|
| 128 |
+
"title_only",
|
| 129 |
+
],
|
| 130 |
+
index=5,
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
with row2_col5:
|
| 134 |
+
years = st.slider(
|
| 135 |
+
"Select the start and end year:", 1950, 2021, (1980, 2021)
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
if title:
|
| 139 |
+
journal_id = title.split(" | ")[0]
|
| 140 |
+
if keyword:
|
| 141 |
+
pubs = dsl.search_pubs_by_keyword(
|
| 142 |
+
keyword, exact_match, scope, years[0], years[1], journal_id
|
| 143 |
+
)
|
| 144 |
+
else:
|
| 145 |
+
pubs = dsl.search_pubs_by_journal_id(
|
| 146 |
+
journal_id, years[0], years[1]
|
| 147 |
+
)
|
| 148 |
+
pubs_df = pubs.as_dataframe()
|
| 149 |
+
if pubs_df is not None and (not pubs_df.empty):
|
| 150 |
+
st.write(
|
| 151 |
+
f"Total number of pulications: {pubs.count_total:,}. Display {min(pubs.count_total, 1000)} publications below."
|
| 152 |
+
)
|
| 153 |
+
try:
|
| 154 |
+
st.dataframe(pubs_df)
|
| 155 |
+
except Exception as e:
|
| 156 |
+
st.dataframe(scholarpy.json_to_df(pubs))
|
| 157 |
+
# st.error("An error occurred: " + str(e))
|
| 158 |
+
leafmap.st_download_button(
|
| 159 |
+
"Download data", pubs_df, csv_sep="\t"
|
| 160 |
+
)
|
| 161 |
+
else:
|
| 162 |
+
st.text("No results found")
|
| 163 |
+
|
| 164 |
+
elif search_type == "List Google Scholar journal categories":
|
| 165 |
+
|
| 166 |
+
st.markdown(
|
| 167 |
+
"""
|
| 168 |
+
The journal categories are adopted from [Google Scholar](https://scholar.google.com/citations?view_op=top_venues&hl=en&inst=9897619243961157265).
|
| 169 |
+
See the list of journals [here](https://docs.google.com/spreadsheets/d/1uCEi3TsJCWl9QEZimvjlM8wjt7hNq3QvMqHGeT44HXQ/edit?usp=sharing).
|
| 170 |
+
"""
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
st.session_state["orcids"] = None
|
| 174 |
+
# dsl = st.session_state["dsl"]
|
| 175 |
+
# token = get_token()
|
| 176 |
+
# dimcli.login(key=token, endpoint="https://app.dimensions.ai")
|
| 177 |
+
# dsl = dimcli.Dsl()
|
| 178 |
+
|
| 179 |
+
categories = get_journals()
|
| 180 |
+
|
| 181 |
+
row1_col1, row1_col2, _, row1_col3 = st.columns([1, 1, 0.05, 1])
|
| 182 |
+
|
| 183 |
+
with row1_col1:
|
| 184 |
+
category = st.selectbox("Select a category:", categories.keys())
|
| 185 |
+
|
| 186 |
+
if category:
|
| 187 |
+
with row1_col2:
|
| 188 |
+
journal = st.selectbox("Select a journal:", categories[category].keys())
|
| 189 |
+
|
| 190 |
+
with row1_col3:
|
| 191 |
+
years = st.slider(
|
| 192 |
+
"Select the start and end year:", 1950, 2021, (1980, 2021)
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
if journal:
|
| 196 |
+
pubs = read_excel(sheet_name=category)
|
| 197 |
+
with st.expander("Show journal metrics"):
|
| 198 |
+
st.dataframe(pubs)
|
| 199 |
+
|
| 200 |
+
journal_id = categories[category][journal]
|
| 201 |
+
if journal_id is not None and str(journal_id).startswith("jour"):
|
| 202 |
+
q_template = """search publications where
|
| 203 |
+
journal.id="{}" and
|
| 204 |
+
year>={} and
|
| 205 |
+
year<={}
|
| 206 |
+
return publications[id+title+doi+year+authors+type+pages+journal+issue+volume+altmetric+times_cited]
|
| 207 |
+
limit 1000"""
|
| 208 |
+
q = q_template.format(journal_id, years[0], years[1])
|
| 209 |
+
else:
|
| 210 |
+
q_template = """search publications where
|
| 211 |
+
journal.title="{}" and
|
| 212 |
+
year>={} and
|
| 213 |
+
year<={}
|
| 214 |
+
return publications[id+title+doi+year+authors+type+pages+journal+issue+volume+altmetric+times_cited]
|
| 215 |
+
limit 1000"""
|
| 216 |
+
q = q_template.format(journal, years[0], years[1])
|
| 217 |
+
|
| 218 |
+
pubs = dsl.query(q)
|
| 219 |
+
if pubs.count_total > 0:
|
| 220 |
+
st.header("Publications")
|
| 221 |
+
st.write(
|
| 222 |
+
f"Total number of pulications: {pubs.count_total:,}. Display 1,000 publications below."
|
| 223 |
+
)
|
| 224 |
+
df_pubs = pubs.as_dataframe()
|
| 225 |
+
save(df_pubs, "publications.csv")
|
| 226 |
+
df_pubs = read("publications.csv")
|
| 227 |
+
st.dataframe(df_pubs)
|
| 228 |
+
|
| 229 |
+
st.header("Authors")
|
| 230 |
+
authors = pubs.as_dataframe_authors()
|
| 231 |
+
st.write(
|
| 232 |
+
f"Total number of authors of the 1,000 pubs shown above: {authors.shape[0]:,}"
|
| 233 |
+
)
|
| 234 |
+
save(authors, "authors.csv")
|
| 235 |
+
df_authors = read("authors.csv")
|
| 236 |
+
st.dataframe(df_authors)
|
| 237 |
+
|
| 238 |
+
df_authors_orcid = df_authors[~df_authors["orcid"].isna()]
|
| 239 |
+
# st.dataframe(df_authors_orcid)
|
| 240 |
+
orcids = list(set(df_authors_orcid["orcid"].values.tolist()))
|
| 241 |
+
orcids = [i[2:21] for i in orcids]
|
| 242 |
+
orcids.sort()
|
| 243 |
+
# st.write(orcids)
|
| 244 |
+
st.session_state["orcids"] = orcids
|
| 245 |
+
|
| 246 |
+
st.header("Affiliations")
|
| 247 |
+
affiliations = pubs.as_dataframe_authors_affiliations()
|
| 248 |
+
st.write(
|
| 249 |
+
f"Total number of affiliations of the 1,000 pubs shown above: {affiliations.shape[0]:,}"
|
| 250 |
+
)
|
| 251 |
+
save(affiliations, "affiliations.csv")
|
| 252 |
+
df_affiliations = read("affiliations.csv")
|
| 253 |
+
st.dataframe(df_affiliations)
|
| 254 |
+
|
| 255 |
+
researchers = authors.query("researcher_id!=''")
|
| 256 |
+
#
|
| 257 |
+
df_researchers = pd.DataFrame(
|
| 258 |
+
{
|
| 259 |
+
"measure": [
|
| 260 |
+
"Authors in total (non unique)",
|
| 261 |
+
"Authors with a researcher ID",
|
| 262 |
+
"Authors with a researcher ID (unique)",
|
| 263 |
+
],
|
| 264 |
+
"count": [
|
| 265 |
+
len(authors),
|
| 266 |
+
len(researchers),
|
| 267 |
+
researchers["researcher_id"].nunique(),
|
| 268 |
+
],
|
| 269 |
+
}
|
| 270 |
+
)
|
| 271 |
+
fig_researchers = px.bar(
|
| 272 |
+
df_researchers,
|
| 273 |
+
x="measure",
|
| 274 |
+
y="count",
|
| 275 |
+
title=f"Author Research ID stats for {journal} ({years[0]}-{years[1]})",
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
orcids = authors.query("orcid!=''")
|
| 279 |
+
#
|
| 280 |
+
df_orcids = pd.DataFrame(
|
| 281 |
+
{
|
| 282 |
+
"measure": [
|
| 283 |
+
"Authors in total (non unique)",
|
| 284 |
+
"Authors with a ORCID",
|
| 285 |
+
"Authors with a ORCID (unique)",
|
| 286 |
+
],
|
| 287 |
+
"count": [
|
| 288 |
+
len(authors),
|
| 289 |
+
len(orcids),
|
| 290 |
+
orcids["orcid"].nunique(),
|
| 291 |
+
],
|
| 292 |
+
}
|
| 293 |
+
)
|
| 294 |
+
fig_orcids = px.bar(
|
| 295 |
+
df_orcids,
|
| 296 |
+
x="measure",
|
| 297 |
+
y="count",
|
| 298 |
+
title=f"Author ORCID stats for {journal} ({years[0]}-{years[1]})",
|
| 299 |
+
)
|
| 300 |
+
|
| 301 |
+
st.header("Stats")
|
| 302 |
+
|
| 303 |
+
row2_col1, row1_col2 = st.columns(2)
|
| 304 |
+
with row2_col1:
|
| 305 |
+
st.plotly_chart(fig_researchers)
|
| 306 |
+
with row1_col2:
|
| 307 |
+
st.plotly_chart(fig_orcids)
|
| 308 |
+
|
| 309 |
+
else:
|
| 310 |
+
st.warning("No publications found")
|
apps/orcid.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from geemap.common import ee_initialize
|
| 2 |
+
import requests
|
| 3 |
+
import folium
|
| 4 |
+
import scholarpy
|
| 5 |
+
import streamlit as st
|
| 6 |
+
import geemap.foliumap as geemap
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def get_orcid_data(orcid, info_type=None):
|
| 13 |
+
"""Retrieve ORCID data based on an ORCID and information type.
|
| 14 |
+
|
| 15 |
+
Args:
|
| 16 |
+
orcid (str): The ORCID to retrieve data for, e.g., 0000-0001-5437-4073
|
| 17 |
+
info_type (str): The type of information to retrieve, e.g., educations, employments, works
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
dict: The ORCID data as a dictionary.
|
| 21 |
+
"""
|
| 22 |
+
headers = {
|
| 23 |
+
"Accept": "application/vnd.orcid+json",
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
if info_type is not None:
|
| 27 |
+
url = f"https://pub.orcid.org/v3.0/{orcid}/{info_type}"
|
| 28 |
+
else:
|
| 29 |
+
url = f"https://pub.orcid.org/v3.0/{orcid}"
|
| 30 |
+
|
| 31 |
+
response = requests.get(url, headers=headers)
|
| 32 |
+
return response.json()
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_education_data(orcid):
|
| 36 |
+
|
| 37 |
+
result = get_orcid_data(orcid, "educations")
|
| 38 |
+
affiliations = result["affiliation-group"]
|
| 39 |
+
info_dict = {}
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
|
| 43 |
+
for affiliation in affiliations:
|
| 44 |
+
summary = affiliation["summaries"][0]["education-summary"]
|
| 45 |
+
name = summary["source"]["source-name"]["value"]
|
| 46 |
+
role = summary["role-title"]
|
| 47 |
+
|
| 48 |
+
organization = summary["organization"]["name"]
|
| 49 |
+
start_year = summary["start-date"]["year"]["value"]
|
| 50 |
+
end_year = summary["end-date"]["year"]["value"]
|
| 51 |
+
# start_date = (
|
| 52 |
+
# summary["start-date"]["year"]["value"]
|
| 53 |
+
# + "-"
|
| 54 |
+
# + summary["start-date"]["month"]["value"]
|
| 55 |
+
# + "-"
|
| 56 |
+
# + summary["start-date"]["day"]["value"]
|
| 57 |
+
# )
|
| 58 |
+
# end_date = (
|
| 59 |
+
# summary["end-date"]["year"]["value"]
|
| 60 |
+
# + "-"
|
| 61 |
+
# + summary["end-date"]["month"]["value"]
|
| 62 |
+
# + "-"
|
| 63 |
+
# + summary["end-date"]["day"]["value"]
|
| 64 |
+
# )
|
| 65 |
+
city = summary["organization"]["address"]["city"]
|
| 66 |
+
region = summary["organization"]["address"]["region"]
|
| 67 |
+
country = summary["organization"]["address"]["country"]
|
| 68 |
+
address_list = [city, region, country]
|
| 69 |
+
address = ", ".join([i for i in address_list if i])
|
| 70 |
+
|
| 71 |
+
# address = city + ", " + region + ", " + country
|
| 72 |
+
coords = geemap.geocode(address)[0]
|
| 73 |
+
lat = coords.lat
|
| 74 |
+
lng = coords.lng
|
| 75 |
+
|
| 76 |
+
info_dict[role] = {
|
| 77 |
+
"name": name,
|
| 78 |
+
"organization": organization,
|
| 79 |
+
"start_year": start_year,
|
| 80 |
+
"end_year": end_year,
|
| 81 |
+
"city": city,
|
| 82 |
+
"region": region,
|
| 83 |
+
"country": country,
|
| 84 |
+
"address": address,
|
| 85 |
+
"lat": lat,
|
| 86 |
+
"lng": lng,
|
| 87 |
+
}
|
| 88 |
+
except:
|
| 89 |
+
pass
|
| 90 |
+
|
| 91 |
+
return info_dict
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def app():
|
| 95 |
+
|
| 96 |
+
dsl = st.session_state["dsl"]
|
| 97 |
+
st.title("Retrieve ORCID Data")
|
| 98 |
+
m = geemap.Map(center=(20, 0), zoom=2, ee_initialize=False)
|
| 99 |
+
|
| 100 |
+
row1_col1, row1_col2 = st.columns(2)
|
| 101 |
+
|
| 102 |
+
with row1_col1:
|
| 103 |
+
name = st.text_input("Enter a researcher name", "")
|
| 104 |
+
|
| 105 |
+
if name:
|
| 106 |
+
orcids = dsl.search_orcid_by_name(name, return_list=True)
|
| 107 |
+
with row1_col2:
|
| 108 |
+
if orcids is not None:
|
| 109 |
+
selected = st.selectbox("Select an ORCID", orcids)
|
| 110 |
+
else:
|
| 111 |
+
selected = None
|
| 112 |
+
st.write("No ORCID found.")
|
| 113 |
+
|
| 114 |
+
# orcids = ["0000-0001-5437-4073", "0000-0001-6157-5519"]
|
| 115 |
+
# if st.session_state.get("orcids", []) is not None:
|
| 116 |
+
# orcids = orcids + st.session_state.get("orcids", [])
|
| 117 |
+
# selected_orcid = st.selectbox("Select an ORCID:", orcids)
|
| 118 |
+
|
| 119 |
+
# with row1_col2:
|
| 120 |
+
# orcid = st.text_input("Enter an ORCID:", selected_orcid)
|
| 121 |
+
|
| 122 |
+
row2_col1, row2_col2 = st.columns([1, 1])
|
| 123 |
+
|
| 124 |
+
if selected is not None:
|
| 125 |
+
orcid = selected.split("|")[1].strip()
|
| 126 |
+
education_data = get_education_data(orcid)
|
| 127 |
+
roles = list(education_data.keys())
|
| 128 |
+
|
| 129 |
+
for role in roles:
|
| 130 |
+
popup = f"<b>Name: </b>{education_data[role]['name']}<br><b>Organization: </b>{education_data[role]['organization']}<br><b>Degree: </b>{role}"
|
| 131 |
+
marker = folium.Marker(
|
| 132 |
+
[education_data[role]["lat"], education_data[role]["lng"]],
|
| 133 |
+
popup=popup,
|
| 134 |
+
)
|
| 135 |
+
marker.add_to(m)
|
| 136 |
+
|
| 137 |
+
with row2_col1:
|
| 138 |
+
markdown = f"""ORCID URL: <https://orcid.org/{orcid}>"""
|
| 139 |
+
st.markdown(markdown)
|
| 140 |
+
if len(education_data) > 0:
|
| 141 |
+
st.write("Education:")
|
| 142 |
+
st.write(education_data)
|
| 143 |
+
else:
|
| 144 |
+
st.write("No education data found.")
|
| 145 |
+
with row2_col2:
|
| 146 |
+
m.to_streamlit()
|
apps/organization.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import scholarpy
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import leafmap.foliumap as leafmap
|
| 6 |
+
import plotly.express as px
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def app():
|
| 13 |
+
|
| 14 |
+
st.title("Search Organizations")
|
| 15 |
+
dsl = st.session_state["dsl"]
|
| 16 |
+
row1_col1, row1_col2 = st.columns([1, 1])
|
| 17 |
+
|
| 18 |
+
with row1_col1:
|
| 19 |
+
name = st.text_input("Enter an organization name:", "")
|
| 20 |
+
|
| 21 |
+
if name:
|
| 22 |
+
orgs = dsl.search_org_by_name(
|
| 23 |
+
name, exact_match=False, return_list=True)
|
| 24 |
+
|
| 25 |
+
if orgs is not None:
|
| 26 |
+
with row1_col1:
|
| 27 |
+
selected_org = st.selectbox("Select a organization id:", orgs)
|
| 28 |
+
org_id = selected_org.split("|")[0].strip()
|
| 29 |
+
|
| 30 |
+
id_info = dsl.search_org_by_id(org_id)
|
| 31 |
+
|
| 32 |
+
info_df = scholarpy.json_to_df(id_info, transpose=True)
|
| 33 |
+
info_df.rename(
|
| 34 |
+
columns={info_df.columns[0]: "Type",
|
| 35 |
+
info_df.columns[1]: "Value"},
|
| 36 |
+
inplace=True,
|
| 37 |
+
)
|
| 38 |
+
with row1_col1:
|
| 39 |
+
st.header("Organization Information")
|
| 40 |
+
if not info_df.empty:
|
| 41 |
+
st.dataframe(info_df)
|
| 42 |
+
leafmap.st_download_button(
|
| 43 |
+
"Download data", info_df, csv_sep="\t"
|
| 44 |
+
)
|
| 45 |
+
else:
|
| 46 |
+
st.text("No information found")
|
| 47 |
+
|
| 48 |
+
with row1_col2:
|
| 49 |
+
years = st.slider(
|
| 50 |
+
"Select the start and end year:", 1950, 2030, (1980, 2021))
|
| 51 |
+
st.header("Publications by year")
|
| 52 |
+
|
| 53 |
+
pubs, fig = dsl.org_pubs_annual_stats(
|
| 54 |
+
org_id, start_year=years[0], end_year=years[1], return_plot=True)
|
| 55 |
+
|
| 56 |
+
st.text(
|
| 57 |
+
f'Total number of publications: {pubs["count"].sum():,}')
|
| 58 |
+
|
| 59 |
+
if fig is not None:
|
| 60 |
+
st.plotly_chart(fig)
|
| 61 |
+
|
| 62 |
+
leafmap.st_download_button(
|
| 63 |
+
"Download data",
|
| 64 |
+
pubs,
|
| 65 |
+
file_name="data.csv",
|
| 66 |
+
csv_sep="\t",
|
| 67 |
+
)
|
| 68 |
+
else:
|
| 69 |
+
st.text("No publications found")
|
| 70 |
+
|
| 71 |
+
with row1_col1:
|
| 72 |
+
st.header("Top funders")
|
| 73 |
+
|
| 74 |
+
funder_count = st.slider(
|
| 75 |
+
"Select the number of funders:", 1, 100, 20)
|
| 76 |
+
|
| 77 |
+
funders, fig = dsl.org_grant_funders(
|
| 78 |
+
org_id, limit=funder_count, return_plot=True)
|
| 79 |
+
st.text(
|
| 80 |
+
f'Total funding amount: ${funders["funding"].sum():,}')
|
| 81 |
+
if fig is not None:
|
| 82 |
+
st.plotly_chart(fig)
|
| 83 |
+
leafmap.st_download_button(
|
| 84 |
+
"Download data",
|
| 85 |
+
funders,
|
| 86 |
+
file_name="data.csv",
|
| 87 |
+
csv_sep="\t",
|
| 88 |
+
)
|
| 89 |
+
else:
|
| 90 |
+
st.text("No funders found")
|
| 91 |
+
|
| 92 |
+
with row1_col2:
|
| 93 |
+
st.header("The number of grants by year")
|
| 94 |
+
grants, fig_count, fig_amount = dsl.org_grants_annual_stats(
|
| 95 |
+
org_id, start_year=years[0], end_year=years[1], return_plot=True)
|
| 96 |
+
|
| 97 |
+
st.plotly_chart(fig_count)
|
| 98 |
+
st.plotly_chart(fig_amount)
|
| 99 |
+
leafmap.st_download_button(
|
| 100 |
+
"Download data",
|
| 101 |
+
grants,
|
| 102 |
+
file_name="data.csv",
|
| 103 |
+
csv_sep="\t",
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
with row1_col1:
|
| 107 |
+
st.header("List of grants")
|
| 108 |
+
st.text("Only the first 1000 grants are shown")
|
| 109 |
+
result = dsl.search_grants_by_org(
|
| 110 |
+
org_id, start_year=years[0], end_year=years[1])
|
| 111 |
+
df = result.as_dataframe()
|
| 112 |
+
if not df.empty:
|
| 113 |
+
st.dataframe(df)
|
| 114 |
+
leafmap.st_download_button(
|
| 115 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
with row1_col1:
|
| 119 |
+
st.header("Publications most cited in last 2 years")
|
| 120 |
+
result = dsl.org_pubs_most_cited(org_id, recent=True, limit=100)
|
| 121 |
+
df = scholarpy.json_to_df(result, transpose=False)
|
| 122 |
+
if not df.empty:
|
| 123 |
+
st.dataframe(df)
|
| 124 |
+
leafmap.st_download_button(
|
| 125 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
with row1_col2:
|
| 129 |
+
st.header("Publications most cited - all time")
|
| 130 |
+
result = dsl.org_pubs_most_cited(org_id, recent=False, limit=100)
|
| 131 |
+
df = scholarpy.json_to_df(result, transpose=False)
|
| 132 |
+
if not df.empty:
|
| 133 |
+
st.dataframe(df)
|
| 134 |
+
leafmap.st_download_button(
|
| 135 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
df, area_fig, journal_fig = dsl.org_pubs_top_areas(org_id, return_plot=True)
|
| 139 |
+
if not df.empty:
|
| 140 |
+
with row1_col1:
|
| 141 |
+
st.header("Research areas of most cited publications")
|
| 142 |
+
st.plotly_chart(area_fig)
|
| 143 |
+
# leafmap.st_download_button(
|
| 144 |
+
# "Download data", df, file_name="data.csv", csv_sep="\t"
|
| 145 |
+
# )
|
| 146 |
+
with row1_col2:
|
| 147 |
+
st.header("Journals of most cited publications")
|
| 148 |
+
st.plotly_chart(journal_fig)
|
| 149 |
+
leafmap.st_download_button(
|
| 150 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 151 |
+
)
|
| 152 |
+
else:
|
| 153 |
+
st.text("No organizations found")
|
apps/publication.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import scholarpy
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import leafmap.foliumap as leafmap
|
| 6 |
+
import plotly.express as px
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def app():
|
| 13 |
+
st.title("Search Publications")
|
| 14 |
+
dsl = st.session_state["dsl"]
|
| 15 |
+
|
| 16 |
+
(
|
| 17 |
+
row1_col1,
|
| 18 |
+
row1_col2,
|
| 19 |
+
row1_col3,
|
| 20 |
+
row1_col4,
|
| 21 |
+
row1_col5,
|
| 22 |
+
) = st.columns([1, 0.7, 1, 1, 1])
|
| 23 |
+
|
| 24 |
+
row2_col1, row2_col2, row2_col3, row2_col4, row2_col5 = st.columns(
|
| 25 |
+
[1, 0.7, 1, 1, 1]
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
with row1_col1:
|
| 29 |
+
keywords = st.text_input("Enter a keyword to search for")
|
| 30 |
+
|
| 31 |
+
with row1_col2:
|
| 32 |
+
exact_match = st.checkbox("Exact match", True)
|
| 33 |
+
|
| 34 |
+
with row1_col3:
|
| 35 |
+
scope = st.selectbox(
|
| 36 |
+
"Select a search scope",
|
| 37 |
+
[
|
| 38 |
+
"authors",
|
| 39 |
+
"concepts",
|
| 40 |
+
"full_data",
|
| 41 |
+
"full_data_exact",
|
| 42 |
+
"title_abstract_only",
|
| 43 |
+
"title_only",
|
| 44 |
+
],
|
| 45 |
+
index=5,
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
with row1_col4:
|
| 49 |
+
years = st.slider("Select the start and end year:", 1950, 2030, (1980, 2022))
|
| 50 |
+
|
| 51 |
+
with row1_col5:
|
| 52 |
+
limit = st.slider("Select the number of publications to return", 1, 1000, 100)
|
| 53 |
+
|
| 54 |
+
if keywords:
|
| 55 |
+
result = dsl.search_pubs_by_keyword(
|
| 56 |
+
keywords,
|
| 57 |
+
exact_match,
|
| 58 |
+
scope,
|
| 59 |
+
start_year=years[0],
|
| 60 |
+
end_year=years[1],
|
| 61 |
+
limit=limit,
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
df = scholarpy.json_to_df(result)
|
| 65 |
+
affiliations = result.as_dataframe_authors_affiliations()
|
| 66 |
+
country_df = affiliations.groupby(['pub_id'])['aff_country'].unique()
|
| 67 |
+
df = df.merge(country_df, left_on='id', right_on='pub_id')
|
| 68 |
+
|
| 69 |
+
countries = [c[c.astype(bool)].size for c in df['aff_country']]
|
| 70 |
+
df['country_count'] = countries
|
| 71 |
+
|
| 72 |
+
journal_counts = df.copy()["journal.title"].value_counts()
|
| 73 |
+
if limit > result.count_total:
|
| 74 |
+
limit = result.count_total
|
| 75 |
+
markdown = f"""
|
| 76 |
+
Returned Publications: {limit} (total = {result.count_total})
|
| 77 |
+
|
| 78 |
+
"""
|
| 79 |
+
|
| 80 |
+
with row2_col1:
|
| 81 |
+
st.markdown(markdown)
|
| 82 |
+
|
| 83 |
+
with row2_col2:
|
| 84 |
+
filter = st.checkbox("Filter by journal")
|
| 85 |
+
|
| 86 |
+
if filter:
|
| 87 |
+
df["journal.title"] = df["journal.title"].astype(str)
|
| 88 |
+
journals = df["journal.title"].unique()
|
| 89 |
+
journals.sort()
|
| 90 |
+
with row2_col3:
|
| 91 |
+
journal = st.selectbox("Select a journal", journals)
|
| 92 |
+
df = df[df["journal.title"] == journal]
|
| 93 |
+
|
| 94 |
+
with row2_col4:
|
| 95 |
+
st.write("")
|
| 96 |
+
|
| 97 |
+
with row2_col5:
|
| 98 |
+
st.write("")
|
| 99 |
+
|
| 100 |
+
if df is not None:
|
| 101 |
+
st.dataframe(df)
|
| 102 |
+
leafmap.st_download_button("Download data", df, csv_sep="\t")
|
| 103 |
+
|
| 104 |
+
summary = pd.DataFrame(
|
| 105 |
+
{"Journal": journal_counts.index, "Count": journal_counts}
|
| 106 |
+
).reset_index(drop=True)
|
| 107 |
+
markdown = f"""
|
| 108 |
+
- Total number of journals: **{len(summary)}**
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
row3_col1, row3_col2 = st.columns([1, 1])
|
| 112 |
+
|
| 113 |
+
with row3_col1:
|
| 114 |
+
st.markdown(markdown)
|
| 115 |
+
st.dataframe(summary)
|
| 116 |
+
leafmap.st_download_button("Download data", summary, csv_sep="\t")
|
| 117 |
+
|
| 118 |
+
with row3_col2:
|
| 119 |
+
fig = px.box(df, x='year', y='country_count', title='Country Counts')
|
| 120 |
+
st.plotly_chart(fig)
|
| 121 |
+
|
| 122 |
+
|
apps/researcher.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import scholarpy
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import leafmap.foliumap as leafmap
|
| 6 |
+
import plotly.express as px
|
| 7 |
+
|
| 8 |
+
if "dsl" not in st.session_state:
|
| 9 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@st.cache(allow_output_mutation=True)
|
| 13 |
+
def get_geonames():
|
| 14 |
+
return scholarpy.get_geonames()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def json_to_df(json_data, transpose=False):
|
| 18 |
+
df = json_data.as_dataframe()
|
| 19 |
+
if not df.empty:
|
| 20 |
+
if transpose:
|
| 21 |
+
df = df.transpose()
|
| 22 |
+
|
| 23 |
+
out_csv = leafmap.temp_file_path(".csv")
|
| 24 |
+
df.to_csv(out_csv, index=transpose)
|
| 25 |
+
df = pd.read_csv(out_csv)
|
| 26 |
+
os.remove(out_csv)
|
| 27 |
+
return df
|
| 28 |
+
else:
|
| 29 |
+
return None
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def annual_pubs(pubs, col="year"):
|
| 33 |
+
if pubs is not None:
|
| 34 |
+
df = pubs[col].value_counts().sort_index()
|
| 35 |
+
df2 = pd.DataFrame({"year": df.index, "publications": df.values})
|
| 36 |
+
return df2
|
| 37 |
+
else:
|
| 38 |
+
return None
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def annual_collaborators(pubs, col="year"):
|
| 42 |
+
if pubs is not None:
|
| 43 |
+
df = pubs.groupby([col]).sum()
|
| 44 |
+
df2 = pd.DataFrame(
|
| 45 |
+
{"year": df.index, "collaborators": df["authors_count"].values}
|
| 46 |
+
)
|
| 47 |
+
fig = px.bar(
|
| 48 |
+
df2,
|
| 49 |
+
x="year",
|
| 50 |
+
y="collaborators",
|
| 51 |
+
)
|
| 52 |
+
return fig
|
| 53 |
+
else:
|
| 54 |
+
return None
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def annual_citations(pubs, col="year"):
|
| 58 |
+
if pubs is not None:
|
| 59 |
+
df = pubs.groupby([col]).sum()
|
| 60 |
+
df2 = pd.DataFrame(
|
| 61 |
+
{"year": df.index, "citations": df["times_cited"].values})
|
| 62 |
+
fig = px.bar(
|
| 63 |
+
df2,
|
| 64 |
+
x="year",
|
| 65 |
+
y="citations",
|
| 66 |
+
)
|
| 67 |
+
return fig
|
| 68 |
+
else:
|
| 69 |
+
return None
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def the_H_function(sorted_citations_list, n=1):
|
| 73 |
+
"""from a list of integers [n1, n2 ..] representing publications citations,
|
| 74 |
+
return the max list-position which is >= integer
|
| 75 |
+
|
| 76 |
+
eg
|
| 77 |
+
>>> the_H_function([10, 8, 5, 4, 3]) => 4
|
| 78 |
+
>>> the_H_function([25, 8, 5, 3, 3]) => 3
|
| 79 |
+
>>> the_H_function([1000, 20]) => 2
|
| 80 |
+
"""
|
| 81 |
+
if sorted_citations_list and sorted_citations_list[0] >= n:
|
| 82 |
+
return the_H_function(sorted_citations_list[1:], n + 1)
|
| 83 |
+
else:
|
| 84 |
+
return n - 1
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def app():
|
| 88 |
+
|
| 89 |
+
st.title("Search Researchers")
|
| 90 |
+
dsl = st.session_state["dsl"]
|
| 91 |
+
row1_col1, row1_col2 = st.columns([1, 1])
|
| 92 |
+
|
| 93 |
+
with row1_col1:
|
| 94 |
+
name = st.text_input("Enter a researcher name:", "")
|
| 95 |
+
|
| 96 |
+
if name:
|
| 97 |
+
|
| 98 |
+
ids, names = dsl.search_researcher_by_name(name, return_list=True)
|
| 99 |
+
if ids.count_total > 0:
|
| 100 |
+
# options = ids.as_dataframe()["id"].values.tolist()
|
| 101 |
+
with row1_col1:
|
| 102 |
+
name = st.selectbox("Select a researcher id:", names)
|
| 103 |
+
|
| 104 |
+
if name:
|
| 105 |
+
id = name.split("|")[1].strip()
|
| 106 |
+
id_info = dsl.search_researcher_by_id(id, return_df=False)
|
| 107 |
+
|
| 108 |
+
info_df = json_to_df(id_info, transpose=True)
|
| 109 |
+
info_df.rename(
|
| 110 |
+
columns={info_df.columns[0]: "Type",
|
| 111 |
+
info_df.columns[1]: "Value"},
|
| 112 |
+
inplace=True,
|
| 113 |
+
)
|
| 114 |
+
with row1_col1:
|
| 115 |
+
st.header("Researcher Information")
|
| 116 |
+
if not info_df.empty:
|
| 117 |
+
st.dataframe(info_df)
|
| 118 |
+
leafmap.st_download_button(
|
| 119 |
+
"Download data", info_df, csv_sep="\t"
|
| 120 |
+
)
|
| 121 |
+
else:
|
| 122 |
+
st.text("No information found")
|
| 123 |
+
|
| 124 |
+
pubs = dsl.search_pubs_by_researcher_id(id)
|
| 125 |
+
df = json_to_df(pubs)
|
| 126 |
+
# annual_df = annual_pubs(df)
|
| 127 |
+
if df is not None:
|
| 128 |
+
df1, df2 = dsl.researcher_annual_stats(
|
| 129 |
+
pubs, geonames_df=get_geonames()
|
| 130 |
+
)
|
| 131 |
+
df3 = scholarpy.collaborator_locations(df2)
|
| 132 |
+
|
| 133 |
+
with row1_col2:
|
| 134 |
+
st.header("Researcher statistics")
|
| 135 |
+
columns = ["pubs", "collaborators",
|
| 136 |
+
"institutions", "cities"]
|
| 137 |
+
selected_columns = st.multiselect(
|
| 138 |
+
"Select attributes to display:", columns, columns
|
| 139 |
+
)
|
| 140 |
+
if selected_columns:
|
| 141 |
+
fig = scholarpy.annual_stats_barplot(
|
| 142 |
+
df1, selected_columns)
|
| 143 |
+
st.plotly_chart(fig)
|
| 144 |
+
leafmap.st_download_button(
|
| 145 |
+
"Download data",
|
| 146 |
+
df1,
|
| 147 |
+
file_name="data.csv",
|
| 148 |
+
csv_sep="\t",
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
st.header("Map of collaborator institutions")
|
| 152 |
+
markdown = f"""
|
| 153 |
+
- Total number of collaborator institutions: **{len(df3)}**
|
| 154 |
+
"""
|
| 155 |
+
st.markdown(markdown)
|
| 156 |
+
m = leafmap.Map(
|
| 157 |
+
center=[0, 0],
|
| 158 |
+
zoom_start=1,
|
| 159 |
+
latlon_control=False,
|
| 160 |
+
draw_control=False,
|
| 161 |
+
measure_control=False,
|
| 162 |
+
locate_control=True,
|
| 163 |
+
)
|
| 164 |
+
m.add_points_from_xy(df3)
|
| 165 |
+
m.to_streamlit(height=420)
|
| 166 |
+
leafmap.st_download_button(
|
| 167 |
+
"Download data",
|
| 168 |
+
df3,
|
| 169 |
+
file_name="data.csv",
|
| 170 |
+
csv_sep="\t",
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
st.header("Publication counts with collaborators")
|
| 174 |
+
collaborators = dsl.search_researcher_collaborators(
|
| 175 |
+
id, pubs)
|
| 176 |
+
markdown = f"""
|
| 177 |
+
- Total number of collaborators: **{len(collaborators)}**
|
| 178 |
+
"""
|
| 179 |
+
st.markdown(markdown)
|
| 180 |
+
st.dataframe(collaborators)
|
| 181 |
+
leafmap.st_download_button(
|
| 182 |
+
"Download data",
|
| 183 |
+
collaborators,
|
| 184 |
+
file_name="data.csv",
|
| 185 |
+
csv_sep="\t",
|
| 186 |
+
)
|
| 187 |
+
else:
|
| 188 |
+
st.text("No publications found")
|
| 189 |
+
|
| 190 |
+
with row1_col1:
|
| 191 |
+
st.header("Publications")
|
| 192 |
+
if df is not None:
|
| 193 |
+
citations = df["times_cited"].values.tolist()
|
| 194 |
+
citations.sort(reverse=True)
|
| 195 |
+
h_index = the_H_function(citations)
|
| 196 |
+
markdown = f"""
|
| 197 |
+
- Total number of publications: **{len(df)}**
|
| 198 |
+
- Total number of citations: **{df["times_cited"].sum()}**
|
| 199 |
+
- i10-index: **{len(df[df["times_cited"]>=10])}**
|
| 200 |
+
- h-index: **{h_index}**
|
| 201 |
+
"""
|
| 202 |
+
st.markdown(markdown)
|
| 203 |
+
st.dataframe(df)
|
| 204 |
+
leafmap.st_download_button(
|
| 205 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
if "journal.title" in df.columns:
|
| 209 |
+
st.header("Publication counts by journal")
|
| 210 |
+
journals = df["journal.title"].value_counts()
|
| 211 |
+
summary = pd.DataFrame(
|
| 212 |
+
{"Journal": journals.index, "Count": journals}
|
| 213 |
+
).reset_index(drop=True)
|
| 214 |
+
markdown = f"""
|
| 215 |
+
- Total number of journals: **{len(summary)}**
|
| 216 |
+
"""
|
| 217 |
+
st.markdown(markdown)
|
| 218 |
+
st.dataframe(summary)
|
| 219 |
+
leafmap.st_download_button(
|
| 220 |
+
"Download data",
|
| 221 |
+
summary,
|
| 222 |
+
file_name="data.csv",
|
| 223 |
+
csv_sep="\t",
|
| 224 |
+
)
|
| 225 |
+
else:
|
| 226 |
+
st.text("No journal publications")
|
| 227 |
+
|
| 228 |
+
else:
|
| 229 |
+
st.text("No publications found")
|
| 230 |
+
|
| 231 |
+
grants = dsl.search_grants_by_researcher(id)
|
| 232 |
+
df = grants.as_dataframe()
|
| 233 |
+
if not df.empty:
|
| 234 |
+
st.header("Grants")
|
| 235 |
+
st.dataframe(df)
|
| 236 |
+
leafmap.st_download_button(
|
| 237 |
+
"Download data", df, file_name="data.csv", csv_sep="\t"
|
| 238 |
+
)
|
| 239 |
+
else:
|
| 240 |
+
st.text("No results found.")
|
data/journals.json
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"All Categories": {
|
| 3 |
+
"Nature": "jour.1018957",
|
| 4 |
+
"The New England Journal of Medicine": "jour.1014075",
|
| 5 |
+
"Science": "jour.1346339",
|
| 6 |
+
"IEEE/CVF Conference on Computer Vision and Pattern Recognition": null,
|
| 7 |
+
"The Lancet": "jour.1077219",
|
| 8 |
+
"Advanced Materials": "jour.1129018",
|
| 9 |
+
"Cell": "jour.1019114",
|
| 10 |
+
"Nature Communications": null,
|
| 11 |
+
"Chemical Reviews": "jour.1077147",
|
| 12 |
+
"International Conference on Learning Representations": null,
|
| 13 |
+
"JAMA": "jour.1081531",
|
| 14 |
+
"Neural Information Processing Systems": null,
|
| 15 |
+
"Proceedings of the National Academy of Sciences": null,
|
| 16 |
+
"Journal of the American Chemical Society": null,
|
| 17 |
+
"Angewandte Chemie": null,
|
| 18 |
+
"Chemical Society Reviews": null,
|
| 19 |
+
"Nucleic Acids Research": null,
|
| 20 |
+
"Renewable and Sustainable Energy Reviews": null,
|
| 21 |
+
"Journal of Clinical Oncology": null,
|
| 22 |
+
"Physical Review Letters": null,
|
| 23 |
+
"Advanced Energy Materials": null,
|
| 24 |
+
"Nature Medicine": null,
|
| 25 |
+
"International Conference on Machine Learning": null,
|
| 26 |
+
"Energy & Environmental Science": null,
|
| 27 |
+
"ACS nullo": null,
|
| 28 |
+
"Scientific Reports": null,
|
| 29 |
+
"European Conference on Computer Vision": null,
|
| 30 |
+
"The Lancet Oncology": null,
|
| 31 |
+
"Advanced Functional Materials": null,
|
| 32 |
+
"PLoS ONE": null,
|
| 33 |
+
"IEEE/CVF International Conference on Computer Vision": null,
|
| 34 |
+
"Nature Genetics": null,
|
| 35 |
+
"Journal of Cleaner Production": null,
|
| 36 |
+
"Nature Materials": null,
|
| 37 |
+
"Science of The Total Environment": null,
|
| 38 |
+
"Circulation": "jour.1009570",
|
| 39 |
+
"BMJ": "jour.1017377",
|
| 40 |
+
"Journal of the American College of Cardiology": null,
|
| 41 |
+
"Applied Catalysis B: Environmental": null,
|
| 42 |
+
"Science Advances": null,
|
| 43 |
+
"nullo Letters": null,
|
| 44 |
+
"Nature Energy": null,
|
| 45 |
+
"ACS Applied Materials & Interfaces": null,
|
| 46 |
+
"Journal of Materials Chemistry A": null,
|
| 47 |
+
"IEEE Access": null,
|
| 48 |
+
"Nature Biotechnology": null,
|
| 49 |
+
"nullo Energy": null,
|
| 50 |
+
"Nature Methods": null,
|
| 51 |
+
"Nature nullotechnology": null,
|
| 52 |
+
"Cochrane Database of Systematic Reviews": null,
|
| 53 |
+
"The Astrophysical Journal": null,
|
| 54 |
+
"The Lancet Infectious Diseases": null,
|
| 55 |
+
"Applied Energy": null,
|
| 56 |
+
"European Heart Journal": null,
|
| 57 |
+
"Blood": "jour.1085025",
|
| 58 |
+
"American Economic Review": null,
|
| 59 |
+
"Immunity": "jour.1112054",
|
| 60 |
+
"Meeting of the Association for Computational Linguistics (ACL)": null,
|
| 61 |
+
"AAAI Conference on Artificial Intelligence": null,
|
| 62 |
+
"Gastroenterology": "jour.1017616",
|
| 63 |
+
"Neuron": "jour.1098485",
|
| 64 |
+
"Journal of High Energy Physics": null,
|
| 65 |
+
"IEEE Communications Surveys & Tutorials": null,
|
| 66 |
+
"Nature Neuroscience": null,
|
| 67 |
+
"Computers in Human Behavior": null,
|
| 68 |
+
"Chemical engineering journal": null,
|
| 69 |
+
"ACS Catalysis": null,
|
| 70 |
+
"Nature Reviews. Molecular Cell Biology": null,
|
| 71 |
+
"International Journal of Molecular Sciences": null,
|
| 72 |
+
"IEEE Transactions on Pattern Analysis and Machine Intelligence": null,
|
| 73 |
+
"Environmental Science & Technology": null,
|
| 74 |
+
"Monthly Notices of the Royal Astronomical Society": null,
|
| 75 |
+
"Cell Metabolism": null,
|
| 76 |
+
"Nature Physics": null,
|
| 77 |
+
"Physical Review D": null,
|
| 78 |
+
"Accounts of Chemical Research": null,
|
| 79 |
+
"Nature Photonics": null,
|
| 80 |
+
"Nature Climate Change": null,
|
| 81 |
+
"Chemistry of Materials": null,
|
| 82 |
+
"Molecular Cell": null,
|
| 83 |
+
"Clinical Infectious Diseases": null,
|
| 84 |
+
"Morbidity and Mortality Weekly Report": null,
|
| 85 |
+
"Nature Reviews Immunology": null,
|
| 86 |
+
"Gut": "jour.1077125",
|
| 87 |
+
"Annals of Oncology": null,
|
| 88 |
+
"Cell Reports": null,
|
| 89 |
+
"Journal of Business Research": null,
|
| 90 |
+
"Clinical Cancer Research": null,
|
| 91 |
+
"Frontiers in Microbiology": null,
|
| 92 |
+
"Journal of Hepatology": null,
|
| 93 |
+
"eLife": "jour.1046517",
|
| 94 |
+
"Bioinformatics": "jour.1345383",
|
| 95 |
+
"The Journal of Clinical Investigation": null,
|
| 96 |
+
"Science Translational Medicine": null,
|
| 97 |
+
"Water Research": null,
|
| 98 |
+
"Frontiers in Immunology": null,
|
| 99 |
+
"Small": "jour.1034711",
|
| 100 |
+
"Nature Immunology": null,
|
| 101 |
+
"JAMA Oncology": null,
|
| 102 |
+
"The Lancet Neurology": null
|
| 103 |
+
},
|
| 104 |
+
"Business, Economics & Management": {
|
| 105 |
+
"American Economic Review": "jour.1056580",
|
| 106 |
+
"Journal of Business Research": "jour.1028262",
|
| 107 |
+
"Tourism Management": null,
|
| 108 |
+
"Journal of Business Ethics": null,
|
| 109 |
+
"Journal of Finullcial Economics": null,
|
| 110 |
+
"The Quarterly Journal of Economics": null,
|
| 111 |
+
"The Review of Finullcial Studies": null,
|
| 112 |
+
"Technological Forecasting and Social Change": null,
|
| 113 |
+
"International Journal of Information Management": null,
|
| 114 |
+
"Management Science": null,
|
| 115 |
+
"Journal of Political Economy": null,
|
| 116 |
+
"International Journal of Production Economics": null,
|
| 117 |
+
"The Journal of Finullce": null,
|
| 118 |
+
"Journal of Management": null,
|
| 119 |
+
"Strategic Management Journal": null,
|
| 120 |
+
"World Development": null,
|
| 121 |
+
"Journal of Retailing and Consumer Services": null,
|
| 122 |
+
"Academy of Management Journal": null,
|
| 123 |
+
"International Journal of Project Management": null,
|
| 124 |
+
"Energy Economics": null
|
| 125 |
+
},
|
| 126 |
+
"Chemical & Material Sciences": {
|
| 127 |
+
"Advanced Materials": "jour.1129018",
|
| 128 |
+
"Chemical Reviews": null,
|
| 129 |
+
"Journal of the American Chemical Society": null,
|
| 130 |
+
"Angewandte Chemie": null,
|
| 131 |
+
"Chemical Society Reviews": null,
|
| 132 |
+
"Advanced Energy Materials": null,
|
| 133 |
+
"Energy & Environmental Science": null,
|
| 134 |
+
"ACS nullo": null,
|
| 135 |
+
"Advanced Functional Materials": null,
|
| 136 |
+
"Nature Materials": null,
|
| 137 |
+
"Applied Catalysis B: Environmental": null,
|
| 138 |
+
"nullo Letters": null,
|
| 139 |
+
"Nature Energy": null,
|
| 140 |
+
"ACS Applied Materials & Interfaces": null,
|
| 141 |
+
"Journal of Materials Chemistry A": null,
|
| 142 |
+
"nullo Energy": null,
|
| 143 |
+
"Nature nullotechnology": null,
|
| 144 |
+
"Chemical engineering journal": null,
|
| 145 |
+
"ACS Catalysis": null,
|
| 146 |
+
"Accounts of Chemical Research": null
|
| 147 |
+
},
|
| 148 |
+
"Engineering & Computer Science": {
|
| 149 |
+
"IEEE/CVF Conference on Computer Vision and Pattern Recognition": null,
|
| 150 |
+
"Advanced Materials": "jour.1129018",
|
| 151 |
+
"International Conference on Learning Representations": null,
|
| 152 |
+
"Neural Information Processing Systems": null,
|
| 153 |
+
"Renewable and Sustainable Energy Reviews": null,
|
| 154 |
+
"Advanced Energy Materials": null,
|
| 155 |
+
"International Conference on Machine Learning": null,
|
| 156 |
+
"Energy & Environmental Science": null,
|
| 157 |
+
"ACS nullo": null,
|
| 158 |
+
"European Conference on Computer Vision": null,
|
| 159 |
+
"Advanced Functional Materials": null,
|
| 160 |
+
"IEEE/CVF International Conference on Computer Vision": null,
|
| 161 |
+
"Journal of Cleaner Production": null,
|
| 162 |
+
"Nature Materials": null,
|
| 163 |
+
"Applied Catalysis B: Environmental": null,
|
| 164 |
+
"nullo Letters": null,
|
| 165 |
+
"Nature Energy": null,
|
| 166 |
+
"ACS Applied Materials & Interfaces": null,
|
| 167 |
+
"Journal of Materials Chemistry A": null,
|
| 168 |
+
"IEEE Access": null
|
| 169 |
+
},
|
| 170 |
+
"Health & Medical Sciences": {
|
| 171 |
+
"The New England Journal of Medicine": "jour.1014075",
|
| 172 |
+
"The Lancet": "jour.1077219",
|
| 173 |
+
"Cell": "jour.1019114",
|
| 174 |
+
"JAMA": "jour.1081531",
|
| 175 |
+
"Proceedings of the National Academy of Sciences": null,
|
| 176 |
+
"Journal of Clinical Oncology": null,
|
| 177 |
+
"Nature Medicine": null,
|
| 178 |
+
"The Lancet Oncology": null,
|
| 179 |
+
"PLoS ONE": null,
|
| 180 |
+
"Nature Genetics": null,
|
| 181 |
+
"Circulation": "jour.1009570",
|
| 182 |
+
"BMJ": "jour.1017377",
|
| 183 |
+
"Journal of the American College of Cardiology": null,
|
| 184 |
+
"Cochrane Database of Systematic Reviews": null,
|
| 185 |
+
"The Lancet Infectious Diseases": null,
|
| 186 |
+
"European Heart Journal": null,
|
| 187 |
+
"Blood": "jour.1085025",
|
| 188 |
+
"Immunity": "jour.1112054",
|
| 189 |
+
"Gastroenterology": "jour.1017616",
|
| 190 |
+
"Neuron": "jour.1098485"
|
| 191 |
+
},
|
| 192 |
+
"Humanities, Literature & Arts": {
|
| 193 |
+
"Digital Journalism": null,
|
| 194 |
+
"Journal of Communication": null,
|
| 195 |
+
"Journalism Studies": null,
|
| 196 |
+
"International Journal of Communication": null,
|
| 197 |
+
"Journalism": "jour.1138763",
|
| 198 |
+
"System": "jour.1137434",
|
| 199 |
+
"The Modern Language Journal": null,
|
| 200 |
+
"Media, Culture & Society": null,
|
| 201 |
+
"Synthese": "jour.1284232",
|
| 202 |
+
"Political Communication": null,
|
| 203 |
+
"Applied Linguistics": null,
|
| 204 |
+
"Language Learning": null,
|
| 205 |
+
"Public Opinion Quarterly": null,
|
| 206 |
+
"TESOL Quarterly": null,
|
| 207 |
+
"Journalism Practice": null,
|
| 208 |
+
"Feminist Media Studies": null,
|
| 209 |
+
"Studies in Second Language Acquisition": null,
|
| 210 |
+
"English Language Teaching": null,
|
| 211 |
+
"Language Teaching": null,
|
| 212 |
+
"Race Ethnicity and Education": null
|
| 213 |
+
},
|
| 214 |
+
"Life Sciences & Earth Sciences": {
|
| 215 |
+
"Nature": "jour.1018957",
|
| 216 |
+
"Science": "jour.1346339",
|
| 217 |
+
"Cell": "jour.1019114",
|
| 218 |
+
"Nature Communications": null,
|
| 219 |
+
"Proceedings of the National Academy of Sciences": null,
|
| 220 |
+
"Nucleic Acids Research": null,
|
| 221 |
+
"Scientific Reports": null,
|
| 222 |
+
"PLoS ONE": null,
|
| 223 |
+
"Nature Genetics": null,
|
| 224 |
+
"Science of The Total Environment": null,
|
| 225 |
+
"Science Advances": null,
|
| 226 |
+
"Nature Biotechnology": null,
|
| 227 |
+
"Nature Methods": null,
|
| 228 |
+
"Neuron": "jour.1098485",
|
| 229 |
+
"Nature Reviews. Molecular Cell Biology": null,
|
| 230 |
+
"International Journal of Molecular Sciences": null,
|
| 231 |
+
"Environmental Science & Technology": null,
|
| 232 |
+
"Cell Metabolism": null,
|
| 233 |
+
"Nature Climate Change": null,
|
| 234 |
+
"Molecular Cell": null
|
| 235 |
+
},
|
| 236 |
+
"Physics & Mathematics": {
|
| 237 |
+
"Physical Review Letters": null,
|
| 238 |
+
"The Astrophysical Journal": null,
|
| 239 |
+
"Journal of High Energy Physics": null,
|
| 240 |
+
"Monthly Notices of the Royal Astronomical Society": null,
|
| 241 |
+
"Nature Physics": null,
|
| 242 |
+
"Physical Review D": null,
|
| 243 |
+
"Nature Photonics": null,
|
| 244 |
+
"Physical Review B": null,
|
| 245 |
+
"Physical Review X": null,
|
| 246 |
+
"Astronomy & Astrophysics": null,
|
| 247 |
+
"The European Physical Journal C": null,
|
| 248 |
+
"Journal of Molecular Liquids": null,
|
| 249 |
+
"IEEE Transactions on Automatic Control": null,
|
| 250 |
+
"International Journal of Heat and Mass Transfer": null,
|
| 251 |
+
"Physics Letters B": null,
|
| 252 |
+
"IEEE Transactions on Geoscience and Remote Sensing": null,
|
| 253 |
+
"Reviews of Modern Physics": null,
|
| 254 |
+
"IEEE Transactions on Signal Processing": null,
|
| 255 |
+
"Geophysical Research Letters": null,
|
| 256 |
+
"Optica": "jour.1050828"
|
| 257 |
+
},
|
| 258 |
+
"Social Sciences": {
|
| 259 |
+
"Journal of Business Ethics": null,
|
| 260 |
+
"Computers & Education": null,
|
| 261 |
+
"Research Policy": null,
|
| 262 |
+
"New Media & Society": null,
|
| 263 |
+
"American Journal of Public Health": null,
|
| 264 |
+
"Global Environmental Change": null,
|
| 265 |
+
"Nature Human Behaviour": null,
|
| 266 |
+
"Health Affairs": null,
|
| 267 |
+
"Social Science & Medicine": null,
|
| 268 |
+
"Teaching and Teacher Education": null,
|
| 269 |
+
"Energy Research & Social Science": null,
|
| 270 |
+
"Information, Communication & Society": null,
|
| 271 |
+
"Land Use Policy": null,
|
| 272 |
+
"Academic Medicine": null,
|
| 273 |
+
"Studies in Higher Education": null,
|
| 274 |
+
"American Journal of Political Science": null,
|
| 275 |
+
"Review of Educational Research": null,
|
| 276 |
+
"Annals of Tourism Research": null,
|
| 277 |
+
"Cities": "jour.1027483",
|
| 278 |
+
"Business Strategy and the Environment": null
|
| 279 |
+
}
|
| 280 |
+
}
|
data/journals.xlsx
ADDED
|
Binary file (25.6 kB). View file
|
|
|
multiapp.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Frameworks for running multiple Streamlit applications as a single app.
|
| 2 |
+
"""
|
| 3 |
+
import streamlit as st
|
| 4 |
+
|
| 5 |
+
# app_state = st.experimental_get_query_params()
|
| 6 |
+
# app_state = {k: v[0] if isinstance(v, list) else v for k, v in app_state.items()} # fetch the first item in each query string as we don't have multiple values for each query string key in this example
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MultiApp:
|
| 10 |
+
"""Framework for combining multiple streamlit applications.
|
| 11 |
+
Usage:
|
| 12 |
+
def foo():
|
| 13 |
+
st.title("Hello Foo")
|
| 14 |
+
def bar():
|
| 15 |
+
st.title("Hello Bar")
|
| 16 |
+
app = MultiApp()
|
| 17 |
+
app.add_app("Foo", foo)
|
| 18 |
+
app.add_app("Bar", bar)
|
| 19 |
+
app.run()
|
| 20 |
+
It is also possible keep each application in a separate file.
|
| 21 |
+
import foo
|
| 22 |
+
import bar
|
| 23 |
+
app = MultiApp()
|
| 24 |
+
app.add_app("Foo", foo.app)
|
| 25 |
+
app.add_app("Bar", bar.app)
|
| 26 |
+
app.run()
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
def __init__(self):
|
| 30 |
+
self.apps = []
|
| 31 |
+
|
| 32 |
+
def add_app(self, title, func):
|
| 33 |
+
"""Adds a new application.
|
| 34 |
+
Parameters
|
| 35 |
+
----------
|
| 36 |
+
func:
|
| 37 |
+
the python function to render this app.
|
| 38 |
+
title:
|
| 39 |
+
title of the app. Appears in the dropdown in the sidebar.
|
| 40 |
+
"""
|
| 41 |
+
self.apps.append({"title": title, "function": func})
|
| 42 |
+
|
| 43 |
+
def run(self):
|
| 44 |
+
app_state = st.experimental_get_query_params()
|
| 45 |
+
app_state = {
|
| 46 |
+
k: v[0] if isinstance(v, list) else v for k, v in app_state.items()
|
| 47 |
+
} # fetch the first item in each query string as we don't have multiple values for each query string key in this example
|
| 48 |
+
|
| 49 |
+
# st.write('before', app_state)
|
| 50 |
+
|
| 51 |
+
titles = [a["title"] for a in self.apps]
|
| 52 |
+
functions = [a["function"] for a in self.apps]
|
| 53 |
+
default_radio = titles.index(app_state["page"]) if "page" in app_state else 0
|
| 54 |
+
|
| 55 |
+
st.sidebar.title("Navigation")
|
| 56 |
+
|
| 57 |
+
title = st.sidebar.radio("Go To", titles, index=default_radio, key="radio")
|
| 58 |
+
|
| 59 |
+
app_state["page"] = st.session_state.radio
|
| 60 |
+
# st.write('after', app_state)
|
| 61 |
+
|
| 62 |
+
st.experimental_set_query_params(**app_state)
|
| 63 |
+
# st.experimental_set_query_params(**st.session_state.to_dict())
|
| 64 |
+
functions[titles.index(title)]()
|
| 65 |
+
|
| 66 |
+
st.sidebar.title("About")
|
| 67 |
+
st.sidebar.info(
|
| 68 |
+
"""
|
| 69 |
+
The web app URL: <https://scholar.gishub.org>. If you have any questions regarding this web app, please contact [Qiusheng Wu](https://wetlands.io) ([email protected]).
|
| 70 |
+
"""
|
| 71 |
+
)
|
packages.txt
ADDED
|
File without changes
|
postBuild
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# enable nbserverproxy
|
| 2 |
+
jupyter serverextension enable --sys-prefix nbserverproxy
|
| 3 |
+
# streamlit launches at startup
|
| 4 |
+
mv streamlit_call.py ${NB_PYTHON_PREFIX}/lib/python*/site-packages/
|
| 5 |
+
# enable streamlit extension
|
| 6 |
+
jupyter serverextension enable --sys-prefix streamlit_call
|
requirements.txt
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dimcli
|
| 2 |
+
geopandas
|
| 3 |
+
# jupyter-server-proxy
|
| 4 |
+
# keplergl
|
| 5 |
+
# nbserverproxy
|
| 6 |
+
openpyxl
|
| 7 |
+
streamlit
|
| 8 |
+
streamlit-option-menu
|
| 9 |
+
leafmap
|
| 10 |
+
scholarpy
|
| 11 |
+
geemap
|
| 12 |
+
# git+https://github.com/giswqs/leafmap
|
| 13 |
+
# git+https://github.com/giswqs/scholarpy
|
| 14 |
+
# git+https://github.com/giswqs/geemap
|
setup.sh
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mkdir -p ~/.streamlit/
|
| 2 |
+
echo "\
|
| 3 |
+
[server]\n\
|
| 4 |
+
headless = true\n\
|
| 5 |
+
port = $PORT\n\
|
| 6 |
+
enableCORS = false\n\
|
| 7 |
+
\n\
|
| 8 |
+
" > ~/.streamlit/config.toml
|
streamlit_app.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import scholarpy
|
| 2 |
+
import streamlit as st
|
| 3 |
+
from streamlit_option_menu import option_menu
|
| 4 |
+
from apps import grant, home, google, journal, orcid, organization, publication, researcher
|
| 5 |
+
|
| 6 |
+
st.set_page_config(page_title="Scholar Web App",
|
| 7 |
+
page_icon="chart_with_upwards_trend",
|
| 8 |
+
layout="wide")
|
| 9 |
+
|
| 10 |
+
# A dictionary of apps in the format of {"App title": "App icon"}
|
| 11 |
+
# More icons can be found here: https://icons.getbootstrap.com
|
| 12 |
+
|
| 13 |
+
apps = {"home": {"title": "Home", "icon": "house"},
|
| 14 |
+
"grant": {"title": "Grant", "icon": "coin"},
|
| 15 |
+
"journal": {"title": "Journal", "icon": "journals"},
|
| 16 |
+
"publication": {"title": "Publication", "icon": "journal"},
|
| 17 |
+
"researcher": {"title": "Researcher", "icon": "person-circle"},
|
| 18 |
+
"orcid": {"title": "ORCID", "icon": "person-square"},
|
| 19 |
+
"organization": {"title": "Organization", "icon": "building"},
|
| 20 |
+
"google": {"title": "Google Scholar", "icon": "google"},
|
| 21 |
+
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
titles = [app["title"] for app in apps.values()]
|
| 26 |
+
icons = [app["icon"] for app in apps.values()]
|
| 27 |
+
params = st.experimental_get_query_params()
|
| 28 |
+
|
| 29 |
+
if "page" in params:
|
| 30 |
+
default_index = int(titles.index(params["page"][0].lower()))
|
| 31 |
+
else:
|
| 32 |
+
default_index = 0
|
| 33 |
+
|
| 34 |
+
with st.sidebar:
|
| 35 |
+
selected = option_menu(
|
| 36 |
+
"Main Menu",
|
| 37 |
+
options=titles,
|
| 38 |
+
icons=icons,
|
| 39 |
+
menu_icon="cast",
|
| 40 |
+
default_index=default_index,
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
# st.sidebar.title("About")
|
| 44 |
+
st.sidebar.info(
|
| 45 |
+
"""
|
| 46 |
+
**Web App URL:**
|
| 47 |
+
<https://scholar.streamlit.app>
|
| 48 |
+
|
| 49 |
+
**Contact:**
|
| 50 |
+
- [Qiusheng Wu](https://geography.utk.edu/about-us/faculty/dr-qiusheng-wu)
|
| 51 |
+
"""
|
| 52 |
+
)
|
| 53 |
+
st.image("https://i.imgur.com/2WhANKg.png")
|
| 54 |
+
|
| 55 |
+
if "dsl" not in st.session_state:
|
| 56 |
+
st.session_state["dsl"] = scholarpy.Dsl()
|
| 57 |
+
|
| 58 |
+
for app in apps:
|
| 59 |
+
if apps[app]["title"] == selected:
|
| 60 |
+
eval(f"{app}.app()")
|
| 61 |
+
break
|
streamlit_call.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from subprocess import Popen
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def load_jupyter_server_extension(nbapp):
|
| 5 |
+
"""serve the streamlit app"""
|
| 6 |
+
Popen(
|
| 7 |
+
[
|
| 8 |
+
"streamlit",
|
| 9 |
+
"run",
|
| 10 |
+
"streamlit_app.py",
|
| 11 |
+
"--browser.serverAddress=0.0.0.0",
|
| 12 |
+
"--server.enableCORS=False",
|
| 13 |
+
]
|
| 14 |
+
)
|