diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 Binary files /dev/null and b/.DS_Store differ diff --git a/.citation_parser_ui.py.swp b/.citation_parser_ui.py.swp deleted file mode 100644 index 38c0d61d4724d853b03e358ed4916b537560e72f..0000000000000000000000000000000000000000 Binary files a/.citation_parser_ui.py.swp and /dev/null differ diff --git a/.gitignore b/.gitignore index df2eaf4579d8730d30a9b69b2e0568794c12e27f..8e7823846fbefaf18864acad411139b6a5fa4f4f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ Thumbs.db # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +*.pyc *$py.class # C extensions @@ -38,6 +39,7 @@ wheels/ *.egg-info/ .installed.cfg *.egg +.start_script.py MANIFEST # PyInstaller diff --git a/assets/json_text.json b/assets/json_text.json index b2e4c5f1d417d21f23729da7e833989a82a9eeaf..b8177a35f64f22f16224949cf9e8c88ae7fdbe6b 100644 --- a/assets/json_text.json +++ b/assets/json_text.json @@ -1 +1 @@ -{"nodes": [{"doi": "https://doi.org/10.1021/acs.jcim.6b00709", "name": "Matched Molecular Series: Measuring SAR Similarity", "author": ["Emanuel S. R. Ehmki", "Christian Kramer"], "year": "May 1, 2017", "journal": "Journal of Chemical Information and Modeling", "group": "Input", "depth": 0, "citations": 5}, {"doi": "https://doi.org/10.1021/acs.jcim.0c00269", "name": "Matched Molecular Series Analysis for ADME Property Prediction", "author": ["Mahendra Awale", "Sereina Riniker", "Christian Kramer"], "year": "May 5, 2020", "journal": "Journal of Chemical Information and Modeling", "group": "Citedby", "depth": 1, "citations": 6}, {"doi": "https://doi.org/10.1021/acs.jcim.0c00290", "name": "Identification of Bioisosteric Substituents by a Deep Neural Network", "author": ["Peter Ertl"], "year": "June 15, 2020", "journal": "Journal of Chemical Information and Modeling", "group": "Citedby", "depth": 2, "citations": 2}], "links": [{"source": "https://doi.org/10.1021/acs.jcim.0c00269", "target": "https://doi.org/10.1021/acs.jcim.6b00709"}, {"source": "https://doi.org/10.1021/acs.jcim.0c00290", "target": "https://doi.org/10.1021/acs.jcim.0c00269"}]} \ No newline at end of file +{"nodes": [], "links": []} \ No newline at end of file diff --git a/citation_parser_ui.py b/citation_parser_main.py similarity index 73% rename from citation_parser_ui.py rename to citation_parser_main.py index 7431a912ca0f5a6d4bd31a269914aa034e083348..c2b119dce5075fb6f1e11226dbf866abccd1d4fe 100644 --- a/citation_parser_ui.py +++ b/citation_parser_main.py @@ -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,39 +91,39 @@ 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. dcc.Checklist(id='additional-options', 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,7 +186,10 @@ 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 - all_inputs.append({'label':rep_str, 'value':input_value}) + # 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, # without checking for validity. @@ -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,7 +218,9 @@ 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 - all_inputs.append({'label':rep_str, 'value':input_value}) + 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}) return all_inputs,selected_inputs,'','' @@ -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) diff --git a/verarbeitung/construct_new_graph/add_citations_rec.py b/verarbeitung/construct_new_graph/add_citations_rec.py index 95afa3553e9b0927196bcb94d792b5b5be82e83d..c5f4dd0d12b02d97798f4da3aada294e78b0fe0a 100644 --- a/verarbeitung/construct_new_graph/add_citations_rec.py +++ b/verarbeitung/construct_new_graph/add_citations_rec.py @@ -164,12 +164,16 @@ def process_citations_rec(citations_pub_obj_list, search_depth, search_depth_max ''' # adds next level to nodes/edges + + new_citation_pub_obj_save_list = [] for pub in citations_pub_obj_list: - new_citation_pub_obj_list = create_graph_structure_citations(pub, search_depth, search_depth_max, cit_type, test_var) + new_citation_pub_obj_list = create_graph_structure_citations(pub, search_depth, search_depth_max, cit_type, test_var) + if len(new_citation_pub_obj_list) > 0: + new_citation_pub_obj_save_list += new_citation_pub_obj_list # If the maximum depth has not yet been reached, calls function recursivly with increased depth - if (search_depth < search_depth_max): - process_citations_rec(new_citation_pub_obj_list, search_depth+1, search_depth_max, cit_type, test_var) + if (search_depth < search_depth_max): + process_citations_rec(new_citation_pub_obj_save_list, search_depth+1, search_depth_max, cit_type, test_var) def add_citations(input_nodes, input_edges, citations_pub_obj_list, search_depth, search_depth_max, cit_type, test_var): diff --git a/verarbeitung/new_height.json b/verarbeitung/new_height.json index f96362a05cea7ad954fa28bfc22074e15e9fa1cd..75d2eb2926ce269b02134db8d9ab041148aa0443 100644 --- a/verarbeitung/new_height.json +++ b/verarbeitung/new_height.json @@ -1 +1 @@ -{"nodes": [{"doi": "doi_lg_1_i", "name": "title_lg_1_i", "author": ["contributor_lg_1_i"], "year": "date_lg_1_i", "journal": "journal_lg_1_i", "group": "Input", "depth": 0, "citations": 2}, {"doi": "doi_lg_1_d11", "name": "title_lg_1_d11", "author": ["contributor_lg_1_d11"], "year": "date_lg_1_d11", "journal": "journal_lg_1_d11", "group": "Reference", "depth": -1, "citations": 1}, {"doi": "doi_lg_1_d12", "name": "title_lg_1_d12", "author": ["contributor_lg_1_d12"], "year": "date_lg_1_d12", "journal": "journal_lg_1_d12", "group": "Reference", "depth": -1, "citations": 2}, {"doi": "doi_lg_1_h11", "name": "title_lg_1_h11", "author": ["contributor_lg_1_h11"], "year": "date_lg_1_h11", "journal": "journal_lg_1_h11", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h12", "name": "title_lg_1_h12", "author": ["contributor_lg_1_h12"], "year": "date_lg_1_h12", "journal": "journal_lg_1_h12", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h21", "name": "title_lg_1_h21", "author": ["contributor_lg_1_h21"], "year": "date_lg_1_h21", "journal": "journal_lg_1_h21", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h22", "name": "title_lg_1_h22", "author": ["contributor_lg_1_h22"], "year": "date_lg_1_h22", "journal": "journal_lg_1_h22", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h23", "name": "title_lg_1_h23", "author": ["contributor_lg_1_h23"], "year": "date_lg_1_h23", "journal": "journal_lg_1_h23", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_d21", "name": "title_lg_1_d21", "author": ["contributor_lg_1_d21"], "year": "date_lg_1_d21", "journal": "journal_lg_1_d21", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d22", "name": "title_lg_1_d22", "author": ["contributor_lg_1_d22"], "year": "date_lg_1_d22", "journal": "journal_lg_1_d22", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d23", "name": "title_lg_1_d23", "author": ["contributor_lg_1_d23"], "year": "date_lg_1_d23", "journal": "journal_lg_1_d23", "group": "Reference", "depth": -2, "citations": 2}], "links": [{"source": "doi_lg_1_i", "target": "doi_lg_1_d11"}, {"source": "doi_lg_1_i", "target": "doi_lg_1_d12"}, {"source": "doi_lg_1_h11", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h21", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_h23", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d21", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d22", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d12", "target": "doi_lg_1_d23"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_d12"}]} \ No newline at end of file +{"nodes": [{"doi": "doi_lg_1_i", "name": "title_lg_1_i", "author": ["contributor_lg_1_i"], "year": "date_lg_1_i", "journal": "journal_lg_1_i", "group": "Input", "depth": 0, "citations": 2}, {"doi": "doi_lg_1_d11", "name": "title_lg_1_d11", "author": ["contributor_lg_1_d11"], "year": "date_lg_1_d11", "journal": "journal_lg_1_d11", "group": "Reference", "depth": -1, "citations": 1}, {"doi": "doi_lg_1_d12", "name": "title_lg_1_d12", "author": ["contributor_lg_1_d12"], "year": "date_lg_1_d12", "journal": "journal_lg_1_d12", "group": "Reference", "depth": -1, "citations": 2}, {"doi": "doi_lg_1_h11", "name": "title_lg_1_h11", "author": ["contributor_lg_1_h11"], "year": "date_lg_1_h11", "journal": "journal_lg_1_h11", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h12", "name": "title_lg_1_h12", "author": ["contributor_lg_1_h12"], "year": "date_lg_1_h12", "journal": "journal_lg_1_h12", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h21", "name": "title_lg_1_h21", "author": ["contributor_lg_1_h21"], "year": "date_lg_1_h21", "journal": "journal_lg_1_h21", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h22", "name": "title_lg_1_h22", "author": ["contributor_lg_1_h22"], "year": "date_lg_1_h22", "journal": "journal_lg_1_h22", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h23", "name": "title_lg_1_h23", "author": ["contributor_lg_1_h23"], "year": "date_lg_1_h23", "journal": "journal_lg_1_h23", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_d21", "name": "title_lg_1_d21", "author": ["contributor_lg_1_d21"], "year": "date_lg_1_d21", "journal": "journal_lg_1_d21", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d22", "name": "title_lg_1_d22", "author": ["contributor_lg_1_d22"], "year": "date_lg_1_d22", "journal": "journal_lg_1_d22", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d23", "name": "title_lg_1_d23", "author": ["contributor_lg_1_d23"], "year": "date_lg_1_d23", "journal": "journal_lg_1_d23", "group": "Reference", "depth": -2, "citations": 2}], "links": [{"source": "doi_lg_1_i", "target": "doi_lg_1_d11"}, {"source": "doi_lg_1_i", "target": "doi_lg_1_d12"}, {"source": "doi_lg_1_h11", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h21", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_h23", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d12", "target": "doi_lg_1_d23"}, {"source": "doi_lg_1_d21", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d22", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_d12"}]} \ No newline at end of file diff --git a/verarbeitung/test_output.json b/verarbeitung/test_output.json index f96362a05cea7ad954fa28bfc22074e15e9fa1cd..75d2eb2926ce269b02134db8d9ab041148aa0443 100644 --- a/verarbeitung/test_output.json +++ b/verarbeitung/test_output.json @@ -1 +1 @@ -{"nodes": [{"doi": "doi_lg_1_i", "name": "title_lg_1_i", "author": ["contributor_lg_1_i"], "year": "date_lg_1_i", "journal": "journal_lg_1_i", "group": "Input", "depth": 0, "citations": 2}, {"doi": "doi_lg_1_d11", "name": "title_lg_1_d11", "author": ["contributor_lg_1_d11"], "year": "date_lg_1_d11", "journal": "journal_lg_1_d11", "group": "Reference", "depth": -1, "citations": 1}, {"doi": "doi_lg_1_d12", "name": "title_lg_1_d12", "author": ["contributor_lg_1_d12"], "year": "date_lg_1_d12", "journal": "journal_lg_1_d12", "group": "Reference", "depth": -1, "citations": 2}, {"doi": "doi_lg_1_h11", "name": "title_lg_1_h11", "author": ["contributor_lg_1_h11"], "year": "date_lg_1_h11", "journal": "journal_lg_1_h11", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h12", "name": "title_lg_1_h12", "author": ["contributor_lg_1_h12"], "year": "date_lg_1_h12", "journal": "journal_lg_1_h12", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h21", "name": "title_lg_1_h21", "author": ["contributor_lg_1_h21"], "year": "date_lg_1_h21", "journal": "journal_lg_1_h21", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h22", "name": "title_lg_1_h22", "author": ["contributor_lg_1_h22"], "year": "date_lg_1_h22", "journal": "journal_lg_1_h22", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h23", "name": "title_lg_1_h23", "author": ["contributor_lg_1_h23"], "year": "date_lg_1_h23", "journal": "journal_lg_1_h23", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_d21", "name": "title_lg_1_d21", "author": ["contributor_lg_1_d21"], "year": "date_lg_1_d21", "journal": "journal_lg_1_d21", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d22", "name": "title_lg_1_d22", "author": ["contributor_lg_1_d22"], "year": "date_lg_1_d22", "journal": "journal_lg_1_d22", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d23", "name": "title_lg_1_d23", "author": ["contributor_lg_1_d23"], "year": "date_lg_1_d23", "journal": "journal_lg_1_d23", "group": "Reference", "depth": -2, "citations": 2}], "links": [{"source": "doi_lg_1_i", "target": "doi_lg_1_d11"}, {"source": "doi_lg_1_i", "target": "doi_lg_1_d12"}, {"source": "doi_lg_1_h11", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h21", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_h23", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d21", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d22", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d12", "target": "doi_lg_1_d23"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_d12"}]} \ No newline at end of file +{"nodes": [{"doi": "doi_lg_1_i", "name": "title_lg_1_i", "author": ["contributor_lg_1_i"], "year": "date_lg_1_i", "journal": "journal_lg_1_i", "group": "Input", "depth": 0, "citations": 2}, {"doi": "doi_lg_1_d11", "name": "title_lg_1_d11", "author": ["contributor_lg_1_d11"], "year": "date_lg_1_d11", "journal": "journal_lg_1_d11", "group": "Reference", "depth": -1, "citations": 1}, {"doi": "doi_lg_1_d12", "name": "title_lg_1_d12", "author": ["contributor_lg_1_d12"], "year": "date_lg_1_d12", "journal": "journal_lg_1_d12", "group": "Reference", "depth": -1, "citations": 2}, {"doi": "doi_lg_1_h11", "name": "title_lg_1_h11", "author": ["contributor_lg_1_h11"], "year": "date_lg_1_h11", "journal": "journal_lg_1_h11", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h12", "name": "title_lg_1_h12", "author": ["contributor_lg_1_h12"], "year": "date_lg_1_h12", "journal": "journal_lg_1_h12", "group": "Citedby", "depth": 1, "citations": 2}, {"doi": "doi_lg_1_h21", "name": "title_lg_1_h21", "author": ["contributor_lg_1_h21"], "year": "date_lg_1_h21", "journal": "journal_lg_1_h21", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h22", "name": "title_lg_1_h22", "author": ["contributor_lg_1_h22"], "year": "date_lg_1_h22", "journal": "journal_lg_1_h22", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_h23", "name": "title_lg_1_h23", "author": ["contributor_lg_1_h23"], "year": "date_lg_1_h23", "journal": "journal_lg_1_h23", "group": "Citedby", "depth": 2, "citations": 0}, {"doi": "doi_lg_1_d21", "name": "title_lg_1_d21", "author": ["contributor_lg_1_d21"], "year": "date_lg_1_d21", "journal": "journal_lg_1_d21", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d22", "name": "title_lg_1_d22", "author": ["contributor_lg_1_d22"], "year": "date_lg_1_d22", "journal": "journal_lg_1_d22", "group": "Reference", "depth": -2, "citations": 2}, {"doi": "doi_lg_1_d23", "name": "title_lg_1_d23", "author": ["contributor_lg_1_d23"], "year": "date_lg_1_d23", "journal": "journal_lg_1_d23", "group": "Reference", "depth": -2, "citations": 2}], "links": [{"source": "doi_lg_1_i", "target": "doi_lg_1_d11"}, {"source": "doi_lg_1_i", "target": "doi_lg_1_d12"}, {"source": "doi_lg_1_h11", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_i"}, {"source": "doi_lg_1_h21", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h11"}, {"source": "doi_lg_1_h22", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_h23", "target": "doi_lg_1_h12"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_d11", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d12", "target": "doi_lg_1_d23"}, {"source": "doi_lg_1_d21", "target": "doi_lg_1_d22"}, {"source": "doi_lg_1_d22", "target": "doi_lg_1_d21"}, {"source": "doi_lg_1_h12", "target": "doi_lg_1_d12"}]} \ No newline at end of file