Skip to content
Snippets Groups Projects
Commit 1fb74723 authored by Katja's avatar Katja
Browse files

ah

parents f1abcf08 0ba4588f
Branches
No related tags found
1 merge request!20Main
Showing
with 88 additions and 2683 deletions
.DS_Store 0 → 100644
File added
File deleted
<<<<<<< HEAD
{
"nodes": [
{
......@@ -112,3 +113,6 @@
}
]
}
=======
{"nodes": [], "links": []}
>>>>>>> upstream/main
......@@ -10,8 +10,13 @@ from dash.exceptions import PreventUpdate
from input.interface import InputInterface
import input.publication
from verarbeitung.process_main import Processing
from dash.dependencies import Input, Output, State #Loading Bar
import plotly.express as px
import dash_bootstrap_components as dbc # pip install dash-bootstrap-components for Loading Bar
app = dash.Dash(__name__)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB]) #SPACELAB https://bootswatch.com/default/ for more themes)
# List of options when inputting data and generating the graph
additional_options = ['Update Automatically','Smart Input']
......@@ -24,25 +29,47 @@ f.close()
app.layout = html.Div([
# Layer 0: For the Header and Help Function(s)
html.Div([
html.Button(id='show-info',children='Show Info',n_clicks=0),
html.Div(id='info-box')
]),
# Layer 1: For all mandatory Inputs
dbc.Button(
'show Info',
id='collapse-button',
className="me-1",
color="primary",
n_clicks=0,
),
dbc.Collapse(
dbc.Card(dbc.CardBody(html.Div(boxcontent, style={'whiteSpace': 'pre-line'}))),
id='collapse',
is_open=False,
),
# Layer 1: For the string input
html.Div([
"Input: ",
# A simple box for inputting a string.
# Value is transmitted upon pressing return or clicking out of the box.
dcc.Input(id='input-string', value='', type='text',debounce=True),
dcc.Input(id='string-input', value='', type='text',debounce=True,
style={ "width": "400px"},
),
]),
# Layer 2: For file input and recursion depths
html.Div([
"Cited-by Depth: ",
# Forward recursion. Values between 1 and 10 can be entered.
dcc.Input(id='forward-depth',value='1',type='number',min='1',max='10'),
dcc.Input(id='forward-depth',value='1',type='number',min='0',max='5',
style={ "width": "50px"},
),
"References Depth: ",
# Backward recursion. Values between 1 and 10 can be entered.
dcc.Input(id='backward-depth',value='1',type='number',min='1',max='10'),
dcc.Input(id='backward-depth',value='1',type='number',min='0',max='5',
style={"width": "50px"},
),
# Upload box. Can be used via drag-and-drop or byclicking on it to open a file viewer.
dcc.Upload(
id="upload-data",
id="file-input",
children=html.Div(
["Drag and drop or click to select a file to upload."]),
#Drag and drop or click to select a file to upload
["Drag and drop"]),
style={
"width": "30%",
"height": "60px",
......@@ -54,7 +81,8 @@ app.layout = html.Div([
"margin": "10px",
})
]),
# Layer 2: For the checklist, Remove-/Start-Buttons and input-error-message
# Layer 3: For the checklist, Remove-/Start-Buttons and error message
html.Div([
# All input DOIs are collected in this checklist.
# It is initialized to avoid error messages.
......@@ -63,13 +91,13 @@ app.layout = html.Div([
# Displays error message if 'Smart Input' is active.
html.Div(id='input-err',style={'color':'red'}),
# Clears the entire list.
html.Button(id='clear-all-button',children='Clear All'),
dbc.Button(id='clear-all-button',children='Clear All', color="primary", className="me-1"),
# Clear all selected elements.
html.Button(id='clear-selected-button',children='Clear Selected'),
dbc.Button(id='clear-selected-button',children='Clear Selected', color="primary", className="me-1"),
# Starts the process that generates a graph.
html.Button(id='start-button',children='Generate Graph')
dbc.Button(id='start-button',children='Generate Graph', color="primary", className="me-1")
]),
# Layer 3: For additional Options (e.g. Topological Sort)
# Layer 4: For additional Options
html.Div([
html.H4('Additional Options'),
# A checklist of all additional options that are listed above.
......@@ -77,25 +105,25 @@ app.layout = html.Div([
options=[{'label':k,'value':k} for k in additional_options],
value=[])
]),
# Layer 4: For the Graph
html.Div(
[html.Iframe(
# Layer 5: For the Graph and corresponding error messages
html.Div([
html.Div(id='generate-graph-error',style={'color':'red'}),
html.Iframe(
src="assets/index.html",
style={"height": "600px", "width": "100%"},
),
html.Div(id='test-output')
])
])
@app.callback(
Output('input-checklist','options'),
Output('input-checklist','value'),
Output('input-string','value'),
Output('string-input','value'),
Output('input-err','children'),
Input('input-string','value'),
Input('string-input','value'),
Input('clear-all-button','n_clicks'),
Input('clear-selected-button','n_clicks'),
Input('upload-data','contents'),
Input('file-input','contents'),
State('input-checklist','options'),
State('input-checklist','value'),
State('additional-options','value')
......@@ -105,7 +133,7 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
'''
Most important callback function. Updates the checklist that holds all inputs.
State of the checklist as input is needed so that previews entries are readded.
input-string is required as Output to clear the input box after each input.
string-input is required as Output to clear the input box after each input.
Different actions are performed depending on which input triggered the callback.
The value-attribute of input-checklist must be updates so that the values
of deleted elements no longer appear in the list of selected elements.
......@@ -140,7 +168,7 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
return all_inputs,list(),'',''
# when a new element is added via dcc.Input
if 'input-string' in changed_id:
if 'string-input' in changed_id:
# Creates a list of previously added inputs to make sure nothing is added twice
currValues = [x['value'] for x in all_inputs]
if input_value not in currValues:
......@@ -158,6 +186,9 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
# Creates a more readable string to display in the checklist
rep_str = pub.contributors[0] + ',' + pub.journal + \
',' + pub.publication_date
# Makes sure not to add the same article with different links
currLabels = [x['label'] for x in all_inputs]
if rep_str not in currLabels:
all_inputs.append({'label':rep_str, 'value':input_value})
# if 'Smart Input' is not selected, the input value is added as is,
......@@ -167,7 +198,7 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
return all_inputs,selected_inputs,'',''
# when a txt-file is uploaded
if 'upload-data.contents' in changed_id:
if 'file-input.contents' in changed_id:
if filecontents:
# Skips the info portion that is added when a file is uploaded
found = base64.b64decode(re.search(',(.+?)$', filecontents).group(1))
......@@ -187,6 +218,8 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
return all_inputs,selected_inputs,'','{}'.format(err)
rep_str = pub.contributors[0] + ',' + pub.journal + \
',' + pub.publication_date
currLabels = [x['label'] for x in all_inputs]
if rep_str not in currLabels:
all_inputs.append({'label':rep_str, 'value':input_value})
else:
all_inputs.append({'label':input_value,'value':input_value})
......@@ -197,33 +230,33 @@ def update_input_checklist(input_value,btn1,btn2,filecontents,all_inputs,
if input_value == '':
return list(),list(),'',''
@app.callback(
Output('info-box','children'),
Input('show-info','n_clicks')
Output('collapse', 'is_open'),
[Input('collapse-button', 'n_clicks')],
[State('collapse', 'is_open')],
)
def show_hide_info_box(n_clicks):
def toggle_collapse(n, is_open):
'''
This callback shows and hides the (first) info-box by, checking how often
This callback shows and hides the (first) info-box by, checking how# often
the button has been pressed. The text was loaded at the top.
:param n_clicks: number of times show-info has been clicked.
'type n_clicks: int
'''
if n_clicks % 2 == 0:
return ''
else:
return html.Div(boxcontent, style={'whiteSpace': 'pre-line'})
if n:
return not is_open
return is_open
@app.callback(
Output('test-output','children'),
Output('generate-graph-error','children'),
Input('start-button','n_clicks'),
Input('input-checklist','options'),
Input('input-checklist','value'),
Input('forward-depth','value'),
Input('backward-depth','value'),
State('additional-options','value')
)
def generate_output(n_clicks,all_inputs,selected_inputs,
forward_depth,backward_depth,additional_options):
def generate_output(n_clicks,all_inputs,forward_depth,backward_depth,additional_options):
'''
Basic structure for a callback that generates an output. This is only a
proof of concept and has noting to do with the intended output yet.
......@@ -233,8 +266,6 @@ def generate_output(n_clicks,all_inputs,selected_inputs,
:param all_inputs: all labels and values from the checklist,
regardless if they have been checked or not
:type all_inputs: list of dictionaries with 2 entries each
:param selected_inputs: values of all checked elements
:type selected_inputs: list of strings
:param forward_depth: forward recursion depth
:type forward_depth: unsigned int
:param backward_depth: backward recursion depth
......@@ -248,7 +279,15 @@ def generate_output(n_clicks,all_inputs,selected_inputs,
elif 'Update Automatically' in additional_options \
or 'start-button' in changed_id:
input_links = [x['value'] for x in all_inputs]
Processing(input_links,int(forward_depth),int(backward_depth),'assets/json_text.json')
errors = Processing(input_links,int(forward_depth),int(backward_depth),'assets/json_text.json')
if errors:
message = ['The following inputs are invalid and were not used:']
for error in errors:
message.append(html.Br())
message.append(error)
message = html.P(message)
#message = [html.P(error) for error in errors]
return message
if __name__ == '__main__':
app.run_server(debug=False)
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
# Projekt CiS-Projekt 2021/22
Input-Skripts
File deleted
https://pubs.acs.org/doi/10.1021/acs.jcim.5b00332
https://pubs.acs.org/doi/10.1021/acs.jcim.6b00709
#!/usr/bin/env python3
"""
Functions for information retrieval of articles from the ACS journal JCIM
"""
__author__ = "Florian Jochens"
__email__ = "fj@andaco.de"
__status__ = "Production"
#__copyright__ = ""
#__credits__ = ["", "", "", ""]
#__license__ = ""
#__version__ = ""
#__maintainer__ = ""
from bs4 import BeautifulSoup as bs
import requests as req
import sys
from pathlib import Path
class Publication:
#_registry = []
_citations = []
_references = []
def __init__(self, title, publication_date, contributors, doi_url,
subjects = None, num_citations = None):
#self._registry.append(self)
self.title = title
self.publication_date = publication_date
self.contributors = contributors
self.doi_url = doi_url
self.subjects = subjects
self.num_citations = num_citations
#self._citations = []
#self._references = []
class Citation:
def __init__(self, title, journal, contributors, doi_url):
self.title = title
self.journal = journal
self.contributors = contributors
self.doi_url = doi_url
class References:
def __init__(self, title, journal, contributors, doi_url):
self.title = title
self.journal = journal
self.contributors = contributors
self.doi_url = doi_url
def get_article_info(soup):
header = soup.find('div', class_ = 'article_header-left pull-left')
article_title = header.find('span', class_ = 'hlFld-Title').text
publication_date = header.find('span', class_ = 'pub-date-value').text
for link in header.find('div', class_ = 'article_header-doiurl'):
doi_url = link.get('href')
subs = header.find('div', class_ = 'article_header-taxonomy')
subjects = []
for sub in subs.find_all('a'):
subjects.append(sub.get('title'))
cons = header.find('ul', class_ = 'loa')
contributors = []
for con in cons.find_all('span', class_ = 'hlFld-ContribAuthor'):
contributors.append(con.text)
numc = header.find('div', class_ = 'articleMetrics_count')
if not numc.a:
num_citations = 0
else:
num_citations = numc.a.text
pub = Publication(article_title, publication_date, contributors, doi_url,
subjects, num_citations)
return pub
def get_download_url():
export = soup.find('div', class_ = 'cit-download-dropdown_content')
url = 'https://pubs.acs.org'
for link in export.find_all('a'):
if link.get('title') == 'Citation and references':
url += link.get('href')
print(url)
return url
def download(url): # Download citation and references file
if url.find('='):
filename = url.rsplit('=', 1)[1]
path = Path(('./files/' + filename))
if path.is_file():
print("File already exists")
else:
print("File does not exist")
def get_citation_info(pub, num_citations, soup):
pub._citations = []
details = soup.find('ol', class_ = 'cited-content_cbyCitation')
titles = []
for title in details.find_all('span',
class_ = 'cited-content_cbyCitation_article-title'):
titles.append(title.text.replace('.', ''))
journal_names = []
for name in details.find_all('span',
class_ = 'cited-content_cbyCitation_journal-name'):
journal_names.append(name.text)
doi_urls = []
for url in details.find_all('a'):
doi_urls.append(url.get('href'))
contributors = []
for contrib in details.find_all('span',
class_ = 'cited-content_cbyCitation_article-contributors'):
contributors.append(contrib.text)
for i in range(0, int(num_citations)):
pub._citations.append(Citation(titles[i], journal_names[i],
contributors[i], doi_urls[i]))
def print_pub_info(pub):
print(f'''Article title: {pub.title}
Publication date: {pub.publication_date}
DOI-URL: {pub.doi_url}
Subjects:''')
print(*(pub.subjects), sep = ", ")
print('\nContributors:')
print(*(pub.contributors), sep = ", ")
if int(pub.num_citations) > 0:
if int(pub.num_citations) == 1:
print(f'\nThis publication is cited by the following publication:\n')
else:
print(f'\nThis publication is cited by the following {pub.num_citations} publications:\n')
for citation in pub._citations:
print(f'''
Title: {citation.title}
Journal: {citation.journal}
Contributors: {citation.contributors}
DOI-URL: {citation.doi_url}
''')
else:
print('\nThis publication is not cited by any other publication.')
def input(url):
html_text = req.get(url).text
soup = bs(html_text, 'html.parser')
pub = get_article_info(soup)
if int(pub.num_citations) > 0:
get_citation_info(pub, int(pub.num_citations), soup)
return pub
#if len(sys.argv) != 2:
# sys.stderr.write('Usage: {} <url>\n'.format(sys.argv[0]))
# exit(1)
#url = sys.argv[1]
#pub = input(url)
#print_pub_info(pub)
class Publication:
#_registry = []
#_citations = []
#_references = []
def __init__(self, title, publication_date, contributors, doi_url,
subjects, num_citations):
#self._registry.append(self)
self.title = title
self.publication_date = publication_date
self.contributors = contributors
self.doi_url = doi_url
self.subjects = subjects
self.num_citations = num_citations
self.num_references = num_references
self._citations = []
self._references = []
class Citation:
def __init__(self, title, journal, contributors, doi_url):
self.title = title
self.journal = journal
self.contributors = contributors
self.doi_url = doi_url
class References:
def __init__(self, title, journal, contributors, doi_url):
self.title = title
self.journal = journal
self.contributors = contributors
self.doi_url = doi_url
#!/usr/bin/env python3
from input_fj import input, print_pub_info
import sys
if len(sys.argv) != 3:
sys.stderr.write('Usage: {} <url> <url>\n'.format(sys.argv[0]))
exit(1)
url = sys.argv[1]
url2 = sys.argv[2]
pub = input(url)
print_pub_info(pub)
pub2 = input(url2)
print_pub_info(pub2)
Article title: Feasibility of Active Machine Learning for Multiclass Compound Classification
Publication date: January 7, 2016
DOI-URL: https://doi.org/10.1021/acs.jcim.5b00332
Subjects:
Algorithms, Molecules, Drug discovery, Screening assays, Receptors
Contributors:
Tobias Lang, Florian Flachsenberg, Ulrike von Luxburg, Matthias Rarey
This publication is cited by the following 30 publications:
Title: Concepts of Artificial Intelligence for Computer-Assisted Drug Discovery
Journal: Chemical Reviews
Contributors: Xin Yang, Yifei Wang, Ryan Byrne, Gisbert Schneider, Shengyong Yang.
DOI-URL: https://doi.org/10.1021/acs.chemrev.8b00728
Title: De Novo Molecule Design by Translating from Reduced Graphs to SMILES
Journal: Journal of Chemical Information and Modeling
Contributors: Peter Pogány, Navot Arad, Sam Genway, Stephen D. Pickett.
DOI-URL: https://doi.org/10.1021/acs.jcim.8b00626
Title: Designing Algorithms To Aid Discovery by Chemical Robots
Journal: ACS Central Science
Contributors: Alon B. Henson, Piotr S. Gromski, Leroy Cronin.
DOI-URL: https://doi.org/10.1021/acscentsci.8b00176
Title: Modeling Kinase Inhibition Using Highly Confident Data Sets
Journal: Journal of Chemical Information and Modeling
Contributors: Sorin Avram, Alina Bora, Liliana Halip, Ramona Curpăn.
DOI-URL: https://doi.org/10.1021/acs.jcim.7b00729
Title: Predictive Models for Fast and Effective Profiling of Kinase Inhibitors
Journal: Journal of Chemical Information and Modeling
Contributors: Alina Bora, Sorin Avram, Ionel Ciucanu, Marius Raica, and Stefana Avram .
DOI-URL: https://doi.org/10.1021/acs.jcim.5b00646
Title: Evaluation of categorical matrix completion algorithms: toward improved active learning for drug discovery
Journal: Bioinformatics
Contributors: Huangqingbo Sun, Robert F Murphy, .
DOI-URL: https://doi.org/10.1093/bioinformatics/btab322
Title: An Artificial Intelligence Approach Based on Hybrid CNN-XGB Model to Achieve High Prediction Accuracy through Feature Extraction, Classification and Regression for Enhancing Drug Discovery in Biomedicine
Journal: International Journal of Biology and Biomedical Engineering
Contributors: Mukesh Madanan, Biju T. Sayed, Nurul Akhmal Mohd Zulkefli, Nitha C. Velayudhan.
DOI-URL: https://doi.org/10.46300/91011.2021.15.22
Title: Artificial Intelligence in Medicinal Chemistry
Journal:
Contributors: Edward Griffen, Alexander Dossetter, Andrew Leach, Shane Montague.
DOI-URL: https://doi.org/10.1002/0471266949.bmc267
Title: Practical Chemogenomic Modeling and Molecule Discovery Strategies Unveiled by Active Learning
Journal:
Contributors: J.B. Brown.
DOI-URL: https://doi.org/10.1016/B978-0-12-801238-3.11533-8
Title: Machine learning phases and criticalities without using real data for training
Journal: Physical Review B
Contributors: D.-R. Tan, F.-J. Jiang.
DOI-URL: https://doi.org/10.1103/PhysRevB.102.224434
Title: Active learning effectively identifies a minimal set of maximally informative and asymptotically performant cytotoxic structure–activity patterns in NCI-60 cell lines
Journal: RSC Medicinal Chemistry
Contributors: Takumi Nakano, Shunichi Takeda, J.B. Brown.
DOI-URL: https://doi.org/10.1039/D0MD00110D
Title: Active learning efficiently converges on rational limits of toxicity prediction and identifies patterns for molecule design
Journal: Computational Toxicology
Contributors: Ahsan Habib Polash, Takumi Nakano, Christin Rakers, Shunichi Takeda, J.B. Brown.
DOI-URL: https://doi.org/10.1016/j.comtox.2020.100129
Title: Practical considerations for active machine learning in drug discovery
Journal: Drug Discovery Today: Technologies
Contributors: Daniel Reker.
DOI-URL: https://doi.org/10.1016/j.ddtec.2020.06.001
Title: Designing compact training sets for data-driven molecular property prediction through optimal exploitation and exploration
Journal: Molecular Systems Design & Engineering
Contributors: Bowen Li, Srinivas Rangarajan.
DOI-URL: https://doi.org/10.1039/C9ME00078J
Title: Applicability Domain of Active Learning in Chemical Probe Identification: Convergence in Learning from Non-Specific Compounds and Decision Rule Clarification
Journal: Molecules
Contributors: Ahsan Habib Polash, Takumi Nakano, Shunichi Takeda, J.B. Brown.
DOI-URL: https://doi.org/10.3390/molecules24152716
Title: Capturing and applying knowledge to guide compound optimisation
Journal: Drug Discovery Today
Contributors: Matthew Segall, Tamsin Mansley, Peter Hunt, Edmund Champness.
DOI-URL: https://doi.org/10.1016/j.drudis.2019.02.004
Title: A novel graph kernel on chemical compound classification
Journal: Journal of Bioinformatics and Computational Biology
Contributors: Qiangrong Jiang, Jiajia Ma.
DOI-URL: https://doi.org/10.1142/S0219720018500269
Title: Accelerating Drug Discovery Using Convolution Neural Network Based Active Learning
Journal:
Contributors: Pengfei Liu, Kwong-Sak Leung.
DOI-URL: https://doi.org/10.1109/TENCON.2018.8650298
Title: An Adaptive Lightweight Security Framework Suited for IoT
Journal:
Contributors: Menachem Domb.
DOI-URL: https://doi.org/10.5772/intechopen.73712
Title: Adaptive mining and model building of medicinal chemistry data with a multi-metric perspective
Journal: Future Medicinal Chemistry
Contributors: JB Brown.
DOI-URL: https://doi.org/10.4155/fmc-2018-0188
Title: Chemogenomic Active Learning's Domain of Applicability on Small, Sparse qHTS Matrices: A Study Using Cytochrome P450 and Nuclear Hormone Receptor Families
Journal: ChemMedChem
Contributors: Christin Rakers, Rifat Ara Najnin, Ahsan Habib Polash, Shunichi Takeda, J.B. Brown.
DOI-URL: https://doi.org/10.1002/cmdc.201700677
Title: Automating drug discovery
Journal: Nature Reviews Drug Discovery
Contributors: Gisbert Schneider.
DOI-URL: https://doi.org/10.1038/nrd.2017.232
Title: Classifiers and their Metrics Quantified
Journal: Molecular Informatics
Contributors: J. B. Brown.
DOI-URL: https://doi.org/10.1002/minf.201700127
Title: Active Search for Computer-aided Drug Design
Journal: Molecular Informatics
Contributors: Dino Oglic, Steven A. Oatley, Simon J. F. Macdonald, Thomas Mcinally, Roman Garnett, Jonathan D. Hirst, Thomas Gärtner.
DOI-URL: https://doi.org/10.1002/minf.201700130
Title: Selection of Informative Examples in Chemogenomic Datasets
Journal:
Contributors: Daniel Reker, J. B. Brown.
DOI-URL: https://doi.org/10.1007/978-1-4939-8639-2_13
Title: The value of prior knowledge in machine learning of complex network systems
Journal: Bioinformatics
Contributors: Dana Ferranti, David Krane, David Craft, .
DOI-URL: https://doi.org/10.1093/bioinformatics/btx438
Title: Lightweight adaptive Random-Forest for IoT rule generation and execution
Journal: Journal of Information Security and Applications
Contributors: Menachem Domb, Elisheva Bonchek-Dokow, Guy Leshem.
DOI-URL: https://doi.org/10.1016/j.jisa.2017.03.001
Title: Active learning for computational chemogenomics
Journal: Future Medicinal Chemistry
Contributors: Daniel Reker, Petra Schneider, Gisbert Schneider, JB Brown.
DOI-URL: https://doi.org/10.4155/fmc-2016-0197
Title: Small Random Forest Models for Effective Chemogenomic Active Learning
Journal: Journal of Computer Aided Chemistry
Contributors: Christin Rakers, Daniel Reker, J.B. Brown.
DOI-URL: https://doi.org/10.2751/jcac.18.124
Title: Large-Scale Off-Target Identification Using Fast and Accurate Dual Regularized One-Class Collaborative Filtering and Its Application to Drug Repurposing
Journal: PLOS Computational Biology
Contributors: Hansaim Lim, Aleksandar Poleksic, Yuan Yao, Hanghang Tong, Di He, Luke Zhuang, Patrick Meng, Lei Xie, .
DOI-URL: https://doi.org/10.1371/journal.pcbi.1005135
Article title: Matched Molecular Series: Measuring SAR Similarity
Publication date: May 1, 2017
DOI-URL: https://doi.org/10.1021/acs.jcim.6b00709
Subjects:
Substituents, Mathematical methods, Structure activity relationship, Biological databases
Contributors:
Emanuel S. R. Ehmki, Christian Kramer
This publication is cited by the following 5 publications:
Title: Matched Molecular Series Analysis for ADME Property Prediction
Journal: Journal of Chemical Information and Modeling
Contributors: Mahendra Awale, Sereina Riniker, Christian Kramer.
DOI-URL: https://doi.org/10.1021/acs.jcim.0c00269
Title: Approaches using AI in medicinal chemistry
Journal:
Contributors: Christian Tyrchan, Eva Nittinger, Dea Gogishvili, Atanas Patronov, Thierry Kogej.
DOI-URL: https://doi.org/10.1016/B978-0-12-822249-2.00002-5
Title: Bioactivity Prediction Based on Matched Molecular Pair and Matched Molecular Series Methods
Journal: Current Pharmaceutical Design
Contributors: Xiaoyu Ding, Chen Cui, Dingyan Wang, Jihui Zhao, Mingyue Zheng, Xiaomin Luo, Hualiang Jiang, Kaixian Chen.
DOI-URL: https://doi.org/10.2174/1381612826666200427111309
Title: BRADSHAW: a system for automated molecular design
Journal: Journal of Computer-Aided Molecular Design
Contributors: Darren V. S. Green, Stephen Pickett, Chris Luscombe, Stefan Senger, David Marcus, Jamel Meslamani, David Brett, Adam Powell, Jonathan Masson.
DOI-URL: https://doi.org/10.1007/s10822-019-00234-8
Title: The use of matched molecular series networks for cross target structure activity relationship translation and potency prediction
Journal: MedChemComm
Contributors: Christopher E. Keefer, George Chang.
DOI-URL: https://doi.org/10.1039/C7MD00465F
# Projekt CiS-Projekt 2021/22
Citation network made with **d3.js**
## Usage
### Input
Json file **json\_text.json** in directory
```json
{
"nodes": [
{
"name": <title: string>,
"author": [<author1: string>, <author2: string>, ...],
"year": <date: tring>,
"journal": <journal: string>,
"doi": <doi: string>,
"group": <"input"/"height"/"depth">,
"citations": <citation: int>
}, ...
],
"links": [
{
"source": <doi: string>,
"target": <doi: string>
}, ...
]
}
```
### Display the Citation Network
Starting a python web server:
```sh
cd <path to file> &&python3 -m http.server <port>
```
Access to the server:
[http://0.0.0.0/:\<port\>](http://0.0.0.0/:<port>)
## Files in Directory
- **index.html**: webpage
- **cn.js**: javascript code for force-directed graph, text elements and legend
- **json_text.json**: example data
## Authors
- Katja Ehlers
- Merle Stahl
\ No newline at end of file
/**
* creates a new zoom behavior
*/
var zoom = d3.zoom().on("zoom", handle_zoom);
/**
* creates svg object and associated attributes
* applies the zoom behavior to svg
*/
var svg = d3.select("svg.graph")
.call(zoom),
width = svg.attr("width"),
height = svg.attr("height"),
perc;
/**
* scale functions that return y coordinate/color of node depending on group
*/
var color = d3.scaleOrdinal()
.domain(["Citedby", "Input", "Reference"])
.range([' #01d7c0', ' #8b90fe ', ' #a15eb2 ']),
y_scale = d3.scaleOrdinal()
.domain(["Citedby", "Input", "Reference"])
.range([0, 200, 400]),
to_remove;
/**
* creates node object and (default) radius
*/
var node,
r = 10;
/**
* creates link object
*/
var link;
/**
* creates a background with a click functionality
*/
var rect = svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", height)
.attr("width", width)
.style("fill", 'white')
.on('click', click_rect);
/**
* creates svg object (legend) and associated attributes
* transform
*/
var svg_legend = d3.select("svg.legendsvg"),
legend_position = [65,95,125],
arrow_legend_position = [0,25],
arrow_group_names = ["citation","self-citation"],
group_names = ["cited by","input","reference"],
line_type = d3.scaleOrdinal()
.domain(["line","dotted"])
.range([("8,0"),("8,8")]),
text_info = '',
text_abstract = '';
var legend = svg_legend.selectAll(".legend")
.data(legend_position)
.enter()
.append("g")
.attr("class","legend")
.attr("transform", function(d,i) {return "translate(0," + d + ")"; });
legend.append("text")
.attr("x", 80)
.attr("y", 0)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d,i) {return group_names[i]});
legend.append("circle")
.attr("r", r)
.attr("cx",30-r)
.style("fill", color);
var legend_arrow = svg_legend.selectAll(".legendarr")
.data(arrow_legend_position)
.enter()
.append("g")
.attr("class","legendarr")
.attr("transform", function(d) { return "translate(0," + d + ")"; });
legend_arrow.append("line")
.attr("x1", 10)
.attr("x2", 50)
.attr("y1", 10)
.attr("y2", 10)
.style("stroke-dasharray",line_type)
.style("stroke", '#999')
.style("stroke-width", "1px")
.style('pointer-events', 'none')
.attr('marker-end',update_marker('#999',this));
legend_arrow.append("text")
.attr("x", 80)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d,i){return arrow_group_names[i]});
/**
* creates a new simulation
* updates the positions of the links and nodes when the
state of the layout has changed (simulation has advanced by a tick)
*/
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {return d.doi;}).distance(50).strength(function(d) {
if (d.group == "Input") {return 0;}
else {return 5;}
}))
.force("collide", d3.forceCollide(function(d) {
if (d.group == "Input") {return 70;}
else {return 70;}
}).strength(0.5))
.force("charge", d3.forceManyBody().strength(0.001))
.force("center", d3.forceCenter(width/2, height/2+20))
.force("yscale", d3.forceY().strength(function(d) {
if (d.group == "Input") {return 300;}
else {return 200;}
}).y(function(d) {return y_scale(d.group)}))
.alpha(0.004)
.on("end", zoom_to);
/**
* creates group element
*/
var g = svg.append("g")
.attr("class", "everything")
/**
* loads JSON data and calls the update function
*/
d3.json("json_text.json").then(function(graph) {
update(graph.links, graph.nodes);
})
var intervalId=window.setInterval(function(){
d3.json("json_text.json").then(function(graph) {
newjson_string=JSON.stringify(graph)
var newjson = CryptoJS.MD5(newjson_string).toString();
oldjson=localStorage.getItem("oldjson")
if(newjson !== oldjson){
localStorage.setItem("oldjson", newjson);
window.location.reload()
}
})
},5000);
/**
* calls update functions for links and nodes
* adds the nodes, links and tick functionality to the simulation
* @param {object} nodes - nodes
* @param {object} links - links
*/
function update(links, nodes) {
update_links(links);
update_nodes(nodes);
simulation
.nodes(nodes)
.on("tick", handle_tick);
simulation.force("link")
.links(links);
link.attr('marker-end', function(d) {return update_marker("#999", d.target);})
.style("stroke-dasharray",function(d){return self_citation(d.source,d.target)? ("8,8"): ("1,0")});
}
/**
* initializes and shows links (edges)
* @param {object} links - links
*/
function update_links(links) {
link = g.append("g")
.selectAll(".link")
.data(links)
.enter()
.append("line")
.style("stroke-width", "1px")
.style("stroke", "#999")
.attr("class", "link");
}
/**
* initializes and shows nodes with circles, texts and a click functionality
* creates a new drag behavior and applies it to the circles
* @param {object} nodes - nodes
*/
function update_nodes(nodes) {
node = g.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", start_drag)
.on("drag", dragged)
);
node.append("circle")
.attr("class", "circle")
.attr("r", function(d) {return 1.5*r+d.citations*0.05})
.style("fill", function(d){ return color(d.group)})
.on('click', click_node);
node.append("text")
.attr("class", "text")
.style("font-size", "15px")
.style('pointer-events', 'auto')
.text(function (d) {const first_author=d.author[0].split(" ")
return first_author[first_author.length-1];})
.on('click', click_node);
}
/**
* creates arrowhead and returns its url
* @param {string} color - color of arrowhead
* @param {string} target - target node
*/
function update_marker(color, target) {
var radius = 1.5*r+target.citations*0.05;
svg.append('defs').append('marker')
.attr('id',color.replace("#", "")+radius)
.attr('viewBox','-0 -5 10 10')
.attr('refX',radius+9.5)
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',10)
.attr('markerHeight',15)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', color)
.style('stroke','none');
return "url(" + color + radius + ")";
};
/**
* sets color of circle and its links to black and removes the previous highlights
* displays overview info of node in textbox
* @param {object} node - node
*/
function click_node(node) {
d3.select(this.parentNode).raise();
fix_nodes(node);
if(to_remove){
d3.select(to_remove).selectAll(".circle").style("stroke","none")
}
to_remove = this.parentNode;
d3.select(this.parentNode).selectAll(".circle").style("stroke","black")
mark_link(node)
textbox_content(node)
reset_button_highlight()
highlight_button("overview")
}
/**
* removes the highlights of the circles and their links
*/
function click_rect() {
fix_nodes(node);
d3.selectAll(".circle").style("stroke", "none")
d3.selectAll(".link")
.style("stroke", "#999")
.attr('marker-end', function(d) {return update_marker('#999', d.target);})
text_abstract='';
text_info='';
reset_button_highlight()
document.getElementById('textbox').innerHTML = "Click node";
}
/**
* returns true if journals have a common author (self-citation)
* @param {object} source - node
* @param {object} target - node
*/
function self_citation(source,target) {
return source.author.some(item=>target.author.includes(item))
}
/**
* sets color of link (line and arrowhead) to black if it is directly connected to node
* and to grey otherwise
* @param {object} node - node
*/
function mark_link(node) {
d3.selectAll(".link")
.style("stroke", function(o) {
return is_link_for_node(node, o) ? "black" : "#DEDEDE";})
.attr('marker-end', function(o) {
return is_link_for_node(node, o) ? update_marker('#000000', o.target) : update_marker('#DEDEDE', o.target);})
}
/**
* returns true if link is directly connected to node and false if it is not
* @param {object} node - node
* @param {object} link - link
*/
function is_link_for_node(node, link) {
return link.source.index == node.index || link.target.index == node.index;
}
/**
* saves text for overview and abstract of node
* outputs node info to textbox
* @param {object} node - node
*/
function textbox_content(node) {
text_info = "Title:" + '</br>' + node.name +
'</br>' +'</br>'+"Author:"+ '</br>' +node.author+'</br>'+'</br>'+"Date:"+'</br>'
+node.year+'</br>'+'</br>'+"Journal:"+'</br>'+node.journal+'</br>'+'</br>'+"doi:"
+'</br>'+'<a href="'+node.doi+ '">'+node.doi+'</a>'+'</br>'+'</br>'+"Citations:"
+'</br>'+node.citations;
text_abstract = node.abstract;
document.getElementById('textbox').innerHTML = text_info;
}
/**
* sets color of btn to dark gray
* @param {object} btn - button
*/
function highlight_button(btn) {
reset_button_highlight();
document.getElementById(btn).style.background = "#CACACA";
}
/**
* sets color of all buttons to default light gray
*/
function reset_button_highlight() {
document.getElementById("overview").style.background = '';
document.getElementById("abstract").style.background = '';
}
/**
* displays abstract in textbox if a is true, overview text otherwise
* @param {bool} a- bool
*/
function display_abstract(a) {
if (text_abstract == '' && text_info == '') {
document.getElementById('textbox').innerHTML="Click node";
}
else {
if (a == true) {
document.getElementById('textbox').innerHTML = text_abstract;
}
else {
document.getElementById('textbox').innerHTML = text_info;
}
}
}
/**
* updates the positions of the links and nodes
*/
function handle_tick() {
link.attr("x1", function (d) {return d.source.x;})
.attr("y1", function (d) {return d.source.y;})
.attr("x2", function (d) {return d.target.x;})
.attr("y2", function (d) {return d.target.y;});
node.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});
}
/**
* initializes the dragging of the node
* @param {object} node - node
*/
function start_drag(node) {
d3.select(this).raise();
if (!d3.event.active)
simulation.alphaTarget(0.3).restart()
node.fx = node.x;
node.fy = node.y;
fix_nodes(node);
}
/**
* applies the dragging to the node
* @param {object} node - node
*/
function dragged(node) {
node.fx = d3.event.x;
node.fy = d3.event.y;
fix_nodes(node);
}
/**
* fix positions of all nodes except for the current node
* @param {object} this_node - node
*/
function fix_nodes(this_node) {
node.each(function(d) {
if (this_node != d) {
d.fx = d.x;
d.fy = d.y;
}
});
}
/**
* applies the transformation (zooming or dragging) to the g element
*/
function handle_zoom() {
d3.select('g').attr("transform", d3.event.transform);
}
/**
* transforms svg so that the zoom is adapted to the size of the graph
*/
function zoom_to() {
node_bounds = d3.selectAll("svg.graph").node().getBBox();
svg_bounds = d3.select("rect").node().getBBox();
perc_x = width/(node_bounds.width+100);
perc_y = height/(node_bounds.height+100);
perc = d3.min([perc_x, perc_y])
d3.select('svg')
.call(zoom.scaleBy, perc);
}
/**
* transforms svg so that the zoom and drag is reset
*/
function reset_view() {
d3.select('svg')
.call(zoom.scaleTo, 1)
d3.select('svg')
.call(zoom.translateTo, 0.5 * width, 0.5 * height);
d3.select('svg')
.call(zoom.scaleBy, perc);
}
/**
* save svg as png
*/
function save_svg(){
var svgString = get_svg_string(svg.node());
svg_string_to_image(svgString, 2*width, 2*height, 'png', save); // passes Blob and filesize String to the callback
function save( dataBlob, filesize ){
saveAs(dataBlob, 'D3 vis exported to PNG.png'); // FileSaver.js function
}
};
/**
* generate svgString
* @param {object} svgNode - node
*/
function get_svg_string(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = get_css_styles(svgNode);
append_css(cssStyleText, svgNode);
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function get_css_styles(parentElement) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.'+parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.'+parentElement.classList[c]);
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#'+id, selectorTextArr))
selectorTextArr.push('#' + id);
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.'+classes[c], selectorTextArr))
selectorTextArr.push('.'+classes[c]);
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch(e) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf(str) === -1 ? false : true;
}
}
function append_css(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}
/**
* convert svgString to image and export it
* @param {object} svgString - svgString
* @param {object} width - width of image
* @param {object} height - height of image
* @param {object} format - format to save image in
* @param {object} callback - callback function
*/
function svg_string_to_image( svgString, width, height, format, callback ) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,'+ btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
canvas.toBlob(function(blob) {
var filesize = Math.round(blob.length/1024) + ' KB';
if (callback) callback(blob, filesize);
});
};
image.src = imgsrc;
}
/**
* creates a new zoom behavior
*/
var zoom = d3.zoom().on("zoom", handle_zoom);
/**
* creates svg object and associated attributes
* applies the zoom behavior to svg
*/
var svg = d3.select("svg.graph")
.call(zoom),
width = svg.attr("width"),
height = svg.attr("height"),
perc;
/**
* scale functions that return y coordinate/color of node depending on group
*/
var color = d3.scaleOrdinal()
.domain(["Citedby", "Input", "Reference"])
.range([' #01d7c0', ' #8b90fe ', ' #a15eb2 ']),
y_scale = d3.scaleOrdinal()
.domain(["Citedby", "Input", "Reference"])
.range([0, 200, 400]),
to_remove;
/**
* creates node object and (default) radius
*/
var node,
r = 10;
/**
* creates link object
*/
var link;
/**
* creates a background with a click functionality
*/
var rect = svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", height)
.attr("width", width)
.style("fill", 'white')
.on('click', click_rect);
/**
* creates svg object (legend) and associated attributes
* transform
* mehr kommentare
*/
var svg_legend = d3.select("svg.legendsvg"),
legend_position = [65,95,125],
arrow_legend_position = [0,25],
arrow_group_names = ["citation","self-citation"],
group_names = ["cited by","input","reference"],
line_type = d3.scaleOrdinal()
.domain(["line","dotted"])
.range([("8,0"),("8,8")]),
text_info = '',
text_abstract = '';
var legend = svg_legend.selectAll(".legend")
.data(legend_position)
.enter()
.append("g")
.attr("class","legend")
.attr("transform", function(d,i) {return "translate(0," + d + ")"; });
legend.append("text")
.attr("x", 80)
.attr("y", 0)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d,i) {return group_names[i]});
legend.append("circle")
.attr("r", r)
.attr("cx",30-r)
.style("fill", color);
var legend_arrow = svg_legend.selectAll(".legendarr")
.data(arrow_legend_position)
.enter()
.append("g")
.attr("class","legendarr")
.attr("transform", function(d) { return "translate(0," + d + ")"; });
legend_arrow.append("line")
.attr("x1", 10)
.attr("x2", 50)
.attr("y1", 10)
.attr("y2", 10)
.style("stroke-dasharray",line_type)
.style("stroke", '#999')
.style("stroke-width", "1px")
.style('pointer-events', 'none')
.attr('marker-end',update_marker('#999',this));
legend_arrow.append("text")
.attr("x", 80)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d,i){return arrow_group_names[i]});
/**
* creates a new simulation
* updates the positions of the links and nodes when the
state of the layout has changed (simulation has advanced by a tick)
*/
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {return d.doi;}).distance(50).strength(function(d) {
if (d.group == "Input") {return 0;}
else {return 5;}
}))
.force("collide", d3.forceCollide(function(d) {
if (d.group == "Input") {return 70;}
else {return 75;}
}).strength(1))
.force("charge", d3.forceManyBody().strength(0.001))
.force("center", d3.forceCenter(width/2, height/2+20))
.alpha(0.004)
.on("end", zoom_to);
/**
* creates group element
*/
var g = svg.append("g")
.attr("class", "everything")
/**
* creates xAxis element
*/
var xAxis = d3.axisBottom()
.tickFormat(function(d) {return d;})
.ticks(10);;
/**
* draw xAxis
*/
var gX = svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0,25)")
gX.append("text")
.attr("y", 0)
.attr("x", 80)
.attr("text-anchor", "end")
.attr("stroke", "black")
.text("year");
/**
* loads JSON data and calls the update function
*/
d3.json("json_text.json").then(function(graph) {
update(graph.links, graph.nodes);
})
var intervalId=window.setInterval(function(){
d3.json("json_text.json").then(function(graph) {
newjson_string=JSON.stringify(graph)
var newjson = CryptoJS.MD5(newjson_string).toString();
oldjson=localStorage.getItem("oldjson")
if(newjson !== oldjson){
localStorage.setItem("oldjson", newjson);
window.location.reload()
}
})
},5000);
/**
* calls update functions for links and nodes
* adds the nodes, links and tick functionality to the simulation
* @param {object} nodes - nodes
* @param {object} links - links
*/
function update(links, nodes) {
updateXAxis(nodes);
update_links(links);
update_nodes(nodes);
simulation
.nodes(nodes)
.on("tick", handle_tick);
simulation.force("link")
.links(links);
link.attr('marker-end', function(d) {return update_marker("#999", d.target);})
.style("stroke-dasharray",function(d){return self_citation(d.source,d.target)? ("8,8"): ("1,0")});
}
/**
* initializes and shows xAxis
* @param {object} nodes - nodes
*/
function updateXAxis(nodes) {
years = [];
for (i = 0; i < nodes.length; i++) {
years.push(parseInt((nodes[i]["year"]).split(" ")[2]));
}
xscale = d3.scaleLinear()
.domain([d3.min(years)-1, d3.max(years)+1])
.range([50, width-50])
xAxis.scale(xscale);
gX.call(xAxis);
}
/**
* initializes and shows links (edges)
* @param {object} links - links
*/
function update_links(links) {
link = g.append("g")
.selectAll(".link")
.data(links)
.enter()
.append("line")
.style("stroke-width", "1px")
.style("stroke", "#999")
.attr("class", "link");
}
/**
* initializes and shows nodes with circles, texts and a click functionality
* creates a new drag behavior and applies it to the circles
* @param {object} nodes - nodes
*/
function update_nodes(nodes) {
node = g.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", start_drag)
.on("drag", dragged)
);
node.append("circle")
.attr("class", "circle")
.attr("r", function(d) {return 1.5*r+d.citations*0.05})
.style("fill", function(d){ return color(d.group)})
.on('click', click_node);
node.append("text")
.attr("class", "text")
.style("font-size", "15px")
.style('pointer-events', 'auto')
.text(function (d) {const first_author=d.author[0].split(" ")
return first_author[first_author.length-1];})
.on('click', click_node);
}
/**
* creates arrowhead and returns its url
* @param {string} color - color of arrowhead
* @param {string} target - target node
*/
function update_marker(color, target) {
var radius = 1.5*r+target.citations*0.05;
svg.append('defs').append('marker')
.attr('id',color.replace("#", "")+radius)
.attr('viewBox','-0 -5 10 10')
.attr('refX',radius+9.5)
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',10)
.attr('markerHeight',15)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', color)
.style('stroke','none');
return "url(" + color + radius + ")";
};
/**
* sets color of circle and its links to black and removes the previous highlights
* displays overview info of node in textbox
* @param {object} node - node
*/
function click_node(node) {
d3.select(this.parentNode).raise();
fix_nodes(node);
if(to_remove){
d3.select(to_remove).selectAll(".circle").style("stroke","none")
}
to_remove = this.parentNode;
d3.select(this.parentNode).selectAll(".circle").style("stroke","black")
mark_link(node)
textbox_content(node)
reset_button_highlight()
highlight_button("overview")
}
/**
* removes the highlights of the circles and their links
*/
function click_rect() {
fix_nodes(node);
d3.selectAll(".circle").style("stroke", "none")
d3.selectAll(".link")
.style("stroke", "#999")
.attr('marker-end', function(d) {return update_marker('#999', d.target);})
text_abstract='';
text_info='';
reset_button_highlight()
document.getElementById('textbox').innerHTML = "Click node";
}
/**
* returns true if journals have a common author (self-citation)
* @param {object} source - node
* @param {object} target - node
*/
function self_citation(source,target) {
return source.author.some(item=>target.author.includes(item))
}
/**
* sets color of link (line and arrowhead) to black if it is directly connected to node
* and to grey otherwise
* @param {object} node - node
*/
function mark_link(node) {
d3.selectAll(".link")
.style("stroke", function(o) {
return is_link_for_node(node, o) ? "black" : "#DEDEDE";})
.attr('marker-end', function(o) {
return is_link_for_node(node, o) ? update_marker('#000000', o.target) : update_marker("#DEDEDE", o.target);})
}
/**
* returns true if link is directly connected to node and false if it is not
* @param {object} node - node
* @param {object} link - link
*/
function is_link_for_node(node, link) {
return link.source.index == node.index || link.target.index == node.index;
}
/**
* saves text for overview and abstract of node
* outputs node info to textbox
* @param {object} node - node
*/
function textbox_content(node) {
text_info = "Title:" + '</br>' + node.name +
'</br>' +'</br>'+"Author:"+ '</br>' +node.author+'</br>'+'</br>'+"Date:"+'</br>'
+node.year+'</br>'+'</br>'+"Journal:"+'</br>'+node.journal+'</br>'+'</br>'+"doi:"
+'</br>'+'<a href="'+node.doi+ '">'+node.doi+'</a>'+'</br>'+'</br>'+"Citations:"
+'</br>'+node.citations;
text_abstract = node.abstract;
document.getElementById('textbox').innerHTML = text_info;
}
/**
* sets color of btn to dark gray
* @param {object} btn - button
*/
function highlight_button(btn) {
reset_button_highlight();
document.getElementById(btn).style.background = "#CACACA";
}
/**
* sets color of all buttons to default light gray
*/
function reset_button_highlight() {
document.getElementById("overview").style.background = '';
document.getElementById("abstract").style.background = '';
}
/**
* displays abstract in textbox if a is true, overview text otherwise
* @param {bool} a- bool
*/
function display_abstract(a) {
if (text_abstract == '' && text_info == '') {
document.getElementById('textbox').innerHTML="Click node";
}
else {
if (a == true) {
document.getElementById('textbox').innerHTML = text_abstract;
}
else {
document.getElementById('textbox').innerHTML = text_info;
}
}
}
/**
* updates the positions of the links and nodes
*/
function handle_tick() {
link.attr("x1", function (d) {return xscale(parseInt((d.source.year).split(" ")[2]));})
.attr("y1", function (d) {return d.source.y;})
.attr("x2", function (d) {return xscale(parseInt((d.target.year).split(" ")[2]));})
.attr("y2", function (d) {return d.target.y;});
node.attr("transform", function (d) {return "translate(" + xscale(parseInt((d.year).split(" ")[2])) + ", " + d.y + ")";});
}
/**
* initializes the dragging of the node
* @param {object} node - node
*/
function start_drag(node) {
d3.select(this).raise();
if (!d3.event.active)
simulation.alphaTarget(0.3).restart()
//node.fx = node.x;
node.fy = node.y;
fix_nodes(node);
}
/**
* applies the dragging to the node
* @param {object} node - node
*/
function dragged(node) {
//node.fx = d3.event.x;
node.fy = d3.event.y;
fix_nodes(node);
}
/**
* fix positions of all nodes except for the current node
* @param {object} this_node - node
*/
function fix_nodes(this_node) {
node.each(function(d) {
if (this_node != d) {
d.fx = d.x;
d.fy = d.y;
}
});
}
/**
* applies the transformation (zooming or dragging) to the g element
*/
function handle_zoom() {
d3.select('g').attr("transform", d3.event.transform);
var new_xScale = d3.event.transform.rescaleX(xscale)
gX.call(xAxis.scale(new_xScale));
}
/**
* transforms svg so that the zoom is adapted to the size of the graph
*/
function zoom_to() {
node_bounds = d3.selectAll("svg.graph").node().getBBox();
svg_bounds = d3.select("rect").node().getBBox();
perc_x = width/(node_bounds.width+100);
perc_y = height/(node_bounds.height+100);
perc = d3.min([perc_x, perc_y])
d3.select('svg')
.call(zoom.scaleBy, perc);
}
/**
* transforms svg so that the zoom and drag is reset
*/
function reset_view() {
d3.select('svg')
.call(zoom.scaleTo, 1)
d3.select('svg')
.call(zoom.translateTo, 0.5 * width, 0.5 * height);
d3.select('svg')
.call(zoom.scaleBy, perc);
}
/**
* save svg as png
*/
function save_svg(){
var svgString = get_svg_string(svg.node());
svg_string_to_image(svgString, 2*width, 2*height, 'png', save); // passes Blob and filesize String to the callback
function save( dataBlob, filesize ){
saveAs(dataBlob, 'D3 vis exported to PNG.png'); // FileSaver.js function
}
};
/**
* generate svgString
* @param {object} svgNode - node
*/
function get_svg_string(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = get_css_styles(svgNode);
append_css(cssStyleText, svgNode);
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function get_css_styles(parentElement) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.'+parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.'+parentElement.classList[c]);
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#'+id, selectorTextArr))
selectorTextArr.push('#' + id);
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.'+classes[c], selectorTextArr))
selectorTextArr.push('.'+classes[c]);
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch(e) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf(str) === -1 ? false : true;
}
}
function append_css(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}
/**
* convert svgString to image and export it
* @param {object} svgString - svgString
* @param {object} width - width of image
* @param {object} height - height of image
* @param {object} format - format to save image in
* @param {object} callback - callback function
*/
function svg_string_to_image( svgString, width, height, format, callback ) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,'+ btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);
canvas.toBlob(function(blob) {
var filesize = Math.round(blob.length/1024) + ' KB';
if (callback) callback(blob, filesize);
});
};
image.src = imgsrc;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- style specifications for button and div elements -->
<style type="text/css">
button {
width: 100px;
height:20px;
display: flex;
justify-content: center;
position: absolute;
left: 455px;
top: 575px;
transition-duration: 0.4s;
border-radius:3px;
border:1px solid #909090;
}
button.display{
width: 120px;
top: 0px;
margin-left: 100px;
}
.button:hover {
background-color: #CACACA;
}
button.resetZoom {
margin-left: 110px;
}
button.save {
margin-left: 220px;
}
button.abstract {
width:146px;
position:absolute;
top: 181px;
left: 1114px;
border-radius:0;
border:1px solid #909090;
}
button.overview {
width:147px;
position:absolute;
display:inline-block;
top: 181px;
left: 968px;
border-radius:0;
border:1px solid #909090;
}
div.legendbox {
width:270px;
height:170px;
padding: 10px;
/*border: 1px solid #999;*/
position: absolute;
top: 10px;
left: 968px;
display: inline-block;
margin: 0;
}
div.textbox {
width:270px;
min-height:200px;
max-height:370px;
padding: 10px;
border: 1px solid #999;
position: absolute;
top: 200px;
left: 968px;
display: inline-block;
overflow-y: scroll;
margin: 0;
}
</style>
</head>
<body>
<button id="change_graph" class="display" onclick="display()">display timeline</button>
<!-- graph -->
<svg class="graph" width="960" height="560"></svg>
<p id="oldjson"></p>
<!-- legend -->
<div class="legendbox"><svg class="legendsvg"></svg></div>
<!-- textbox -->
<div class="textbox" id="textbox">Click node</div>
<button id="overview" class="overview" onclick='display_abstract(false), highlight_button("overview")'>Overview</button>
<button id="abstract" class="abstract" onclick='display_abstract(true), highlight_button("abstract")'>Abstract</button>
<!-- buttons -->
<button onclick="location.reload()">Reload Graph</button>
<button class="resetZoom" onclick="reset_view()">Reset View</button>
<button class="save" onclick="save_svg()">Save</button>
<!-- D3 (version 5) -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<!-- scripts to save svg element as png -->
<script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
<script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/md5.js"></script>
<script type="text/javascript" >
if(window.location.hash=='#default'){
document.getElementById("change_graph").innerHTML = 'display timeline';
load_default();
}
else if(window.location.hash=='#time'){
document.getElementById("change_graph").innerHTML = 'display default';
load_time();
}
else if(window.location.hash==''){
window.location.hash = 'default';
document.getElementById("change_graph").innerHTML = 'display timeline';
load_default();
}
function display(){
if(window.location.hash=='#time'){
display_default();
}
else if(window.location.hash=='#default'){
display_time();
}
}
function display_default(){
window.location.hash = 'default';
window.location.reload();
}
function display_time(){
window.location.hash = 'time';
window.location.reload();
}
function load_default(){
var htmlHeader = document.getElementsByTagName("head")[0];
var myScript = document.createElement('script');
myScript.type = 'text/javascript';
myScript.src = 'cn.js';
myScript.id='abc';
htmlHeader.appendChild(myScript);
}
function load_time(){
var htmlHeader = document.getElementsByTagName("head")[0];
var myScript = document.createElement('script');
myScript.type = 'text/javascript';
myScript.src = 'cn2.js';
myScript.id='abc';
htmlHeader.appendChild(myScript);
}
</script>
<!-- javascript for force-directed graph
<script type="text/javascript" id="cn" src="cn.js"></script> -->
</body>
</html>
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment