gdbcore 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gdbcore-0.1.0/PKG-INFO +39 -0
- gdbcore-0.1.0/README.md +19 -0
- gdbcore-0.1.0/gdbcore/__init__.py +0 -0
- gdbcore-0.1.0/gdbcore/apoc_load_queries.py +186 -0
- gdbcore-0.1.0/gdbcore/csv_formatter.py +292 -0
- gdbcore-0.1.0/gdbcore/cypher_load_queries.py +169 -0
- gdbcore-0.1.0/gdbcore/dump_to_nelists.py +285 -0
- gdbcore-0.1.0/gdbcore/helpers/__init__.py +0 -0
- gdbcore-0.1.0/gdbcore/helpers/utils.py +848 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_cypher.py +644 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_gt.py +117 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_nelist.py +258 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_neo4j.py +262 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_nx.py +91 -0
- gdbcore-0.1.0/gdbcore/helpers/utils_obonet.py +632 -0
- gdbcore-0.1.0/gdbcore/list_builder.py +564 -0
- gdbcore-0.1.0/gdbcore/neo4j_bulk_import.py +134 -0
- gdbcore-0.1.0/gdbcore/nx_builder.py +186 -0
- gdbcore-0.1.0/gdbcore/obo_to_csv.py +124 -0
- gdbcore-0.1.0/pyproject.toml +28 -0
gdbcore-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: gdbcore
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Angel L. P.
|
|
6
|
+
Author-email: 59593766+angelphanth@users.noreply.github.com
|
|
7
|
+
Requires-Python: >=3.11,<3.13
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: matplotlib (>=3.8.1,<4.0.0)
|
|
12
|
+
Requires-Dist: neo4j (>=5.14.1,<6.0.0)
|
|
13
|
+
Requires-Dist: networkx (>=3.1,<4.0)
|
|
14
|
+
Requires-Dist: obonet (>=1.0.0,<2.0.0)
|
|
15
|
+
Requires-Dist: pandas (>=2.1.1,<3.0.0)
|
|
16
|
+
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
17
|
+
Requires-Dist: scipy (>=1.12.0,<2.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Work In Progress
|
|
21
|
+
|
|
22
|
+
Overall goal: Create KG dbs (LPGs) from node and edge property lists.
|
|
23
|
+
|
|
24
|
+
Current status:
|
|
25
|
+
- Creates / formats node and edge property lists from CSVs.
|
|
26
|
+
- Produces a Networkx graph and .graphml file
|
|
27
|
+
- Produces a Neo4j Graph database (3 venues)
|
|
28
|
+
- Connector to query Neo4j via python
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Installation and Getting started
|
|
32
|
+
|
|
33
|
+
1. download requirements
|
|
34
|
+
`poetry install`
|
|
35
|
+
|
|
36
|
+
2. activate env
|
|
37
|
+
e.g. `poetry shell`
|
|
38
|
+
|
|
39
|
+
|
gdbcore-0.1.0/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Work In Progress
|
|
2
|
+
|
|
3
|
+
Overall goal: Create KG dbs (LPGs) from node and edge property lists.
|
|
4
|
+
|
|
5
|
+
Current status:
|
|
6
|
+
- Creates / formats node and edge property lists from CSVs.
|
|
7
|
+
- Produces a Networkx graph and .graphml file
|
|
8
|
+
- Produces a Neo4j Graph database (3 venues)
|
|
9
|
+
- Connector to query Neo4j via python
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Installation and Getting started
|
|
13
|
+
|
|
14
|
+
1. download requirements
|
|
15
|
+
`poetry install`
|
|
16
|
+
|
|
17
|
+
2. activate env
|
|
18
|
+
e.g. `poetry shell`
|
|
19
|
+
|
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
- The purpose of this script is to create a Neo4j database (db) from node and edge lists
|
|
3
|
+
in the form of data csv files with an associated header csv file.
|
|
4
|
+
- The process will be:
|
|
5
|
+
- init log
|
|
6
|
+
- load config parameters from config/config.yaml
|
|
7
|
+
- establish neo4j python driver connection
|
|
8
|
+
- move the files to *db import folder* from source folder specified in config.yaml -> db setup -> source folder
|
|
9
|
+
- **to find this folder location you can use helpers.utils_neo4j.Neo4jConnection.abs_import_path()
|
|
10
|
+
- if a file from source folder already exists in the db import folder then it will be skipped NOT OVERWRITTEN
|
|
11
|
+
- TODO
|
|
12
|
+
|
|
13
|
+
NEO4J REQUIREMENT:
|
|
14
|
+
- have a running DBMS instance
|
|
15
|
+
|
|
16
|
+
FILE FORMAT REQUIREMENTS: (will include example file later and script to check/format the file sources)
|
|
17
|
+
- the data files and their matching header file must have the SAME
|
|
18
|
+
filenaming except for the suffices of '-header.csv' and 'data.csv'
|
|
19
|
+
OR '-header.csv' and 'data1.csv', 'data2.csv'
|
|
20
|
+
- if node files have 'node' somehwere in the base file name
|
|
21
|
+
- if edge files have 'edge somewhere in the base file name
|
|
22
|
+
- also see here for column naming requirements: https://neo4j.com/docs/operations-manual/current/tutorial/neo4j-admin-import/
|
|
23
|
+
- for node lists:
|
|
24
|
+
- the primary id column/property should be named 'id:ID'
|
|
25
|
+
- should include a :LABEL column with label
|
|
26
|
+
- only one node label type per files (e.g., if a db is going to have Gene and Protein nodes then
|
|
27
|
+
they should have their own set of data+header files)
|
|
28
|
+
- for edge lists:
|
|
29
|
+
- should hav :START_ID, :END_ID, and :TYPE columns at least
|
|
30
|
+
- only one edge label type per file
|
|
31
|
+
|
|
32
|
+
this whole area is TODO
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
# import libraries
|
|
38
|
+
import sys
|
|
39
|
+
|
|
40
|
+
# import modules
|
|
41
|
+
from gdbcore.helpers.utils import (
|
|
42
|
+
config_loader,
|
|
43
|
+
copy_recursively,
|
|
44
|
+
filter_filepaths,
|
|
45
|
+
get_args,
|
|
46
|
+
get_logger,
|
|
47
|
+
normalize_url,
|
|
48
|
+
)
|
|
49
|
+
from gdbcore.helpers.utils_cypher import apoc_load_csv_edges, apoc_load_csv_nodes
|
|
50
|
+
from gdbcore.helpers.utils_neo4j import Neo4jConnection
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
|
|
54
|
+
## GET ARGS
|
|
55
|
+
args = get_args(
|
|
56
|
+
prog_name="neo4j-data-loader",
|
|
57
|
+
others=dict(description="uses apoc plugin to load nodes and rels"),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
## START LOG FILE
|
|
61
|
+
logger = get_logger()
|
|
62
|
+
logger.info(f"Arguments: {args}")
|
|
63
|
+
|
|
64
|
+
## LOAD CONFIG PARAMETERS
|
|
65
|
+
# getting the config filepath
|
|
66
|
+
config_filepath = args.config
|
|
67
|
+
# log it
|
|
68
|
+
logger.info(f"Path to config file: {config_filepath}")
|
|
69
|
+
# load config params
|
|
70
|
+
logger.info("Loading config params ... ")
|
|
71
|
+
config = config_loader(config_filepath)["apoc_load_queries"]
|
|
72
|
+
logger.info(f"Configuration: {config}")
|
|
73
|
+
# following db set up
|
|
74
|
+
username = config["neo4j"]["username"]
|
|
75
|
+
password = config["neo4j"]["password"]
|
|
76
|
+
db_name = config["db setup"]["db_name"]
|
|
77
|
+
host = config["neo4j"]["host"]
|
|
78
|
+
bolt_port = config["neo4j"]["bolt port"]
|
|
79
|
+
http_port = config["neo4j"]["http port"]
|
|
80
|
+
sep = config["db setup"]["sep"]
|
|
81
|
+
quotechar = config["db setup"]["quotechar"]
|
|
82
|
+
fill_na = config["db setup"]["fill_na"]
|
|
83
|
+
num_transactions = config["neo4j"]["num_transactions"]
|
|
84
|
+
source_folder = config["db setup"]["source folder"]
|
|
85
|
+
import_path = config['neo4j']['import_path']
|
|
86
|
+
logger.info(f"Configuration: {config}")
|
|
87
|
+
|
|
88
|
+
## MAIN
|
|
89
|
+
# get and check uris
|
|
90
|
+
uri = normalize_url(host, bolt_port, scheme='bolt')
|
|
91
|
+
http_uri = normalize_url(host, http_port)
|
|
92
|
+
|
|
93
|
+
## CONNECT TO NEO4J
|
|
94
|
+
connection = Neo4jConnection(uri=uri, user=username, pwd=password)
|
|
95
|
+
# log it
|
|
96
|
+
logger.info(f"Neo4j connection established. neo4j vers: {connection.neo4j_version}")
|
|
97
|
+
|
|
98
|
+
# retrieve import path
|
|
99
|
+
if import_path:
|
|
100
|
+
neo_import_path = import_path
|
|
101
|
+
else:
|
|
102
|
+
neo_import_path = connection.abs_import_path()
|
|
103
|
+
|
|
104
|
+
logger.info(f"DB import filepath: {neo_import_path}")
|
|
105
|
+
## MOVE FILES FROM source_folder TO DB IMPORT FOLDER
|
|
106
|
+
logger.info(f"Copying files from {source_folder} to {neo_import_path}")
|
|
107
|
+
copy_recursively(src=source_folder, dest=neo_import_path)
|
|
108
|
+
|
|
109
|
+
## RETRIEVE NODE AND EDGE LISTS (DATA AND HEADER CSVs)
|
|
110
|
+
logger.info("Getting filepaths to node and edge lists")
|
|
111
|
+
# getting header files to iterate through
|
|
112
|
+
node_head_files = filter_filepaths(
|
|
113
|
+
neo_import_path, identifiers=["-header.csv", "node"]
|
|
114
|
+
)
|
|
115
|
+
logger.info(f"Identified NODE header files: {node_head_files}")
|
|
116
|
+
# getting data files
|
|
117
|
+
node_data_files = filter_filepaths(neo_import_path, identifiers=["-data", "node"])
|
|
118
|
+
logger.info(f"Identified NODE data files: {node_data_files}")
|
|
119
|
+
edge_head_files = filter_filepaths(
|
|
120
|
+
neo_import_path, identifiers=["-header.csv", "edge"]
|
|
121
|
+
)
|
|
122
|
+
logger.info(f"Identified EDGE header files: {edge_head_files}")
|
|
123
|
+
# getting data files
|
|
124
|
+
edge_data_files = filter_filepaths(neo_import_path, identifiers=["-data", "edge"])
|
|
125
|
+
logger.info(f"Identified EDGE data files: {edge_data_files}")
|
|
126
|
+
|
|
127
|
+
## GENERATING NODE CYPHER LOADING QUERIES
|
|
128
|
+
node_queries = apoc_load_csv_nodes(
|
|
129
|
+
header_files=node_head_files,
|
|
130
|
+
data_files=node_data_files,
|
|
131
|
+
num_transactions=num_transactions,
|
|
132
|
+
sep=sep,
|
|
133
|
+
quotechar=quotechar,
|
|
134
|
+
fill_na=fill_na,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
## GENERATING EDGE CYPHER LOADING QUERIES
|
|
138
|
+
edge_queries = apoc_load_csv_edges(
|
|
139
|
+
header_files=edge_head_files,
|
|
140
|
+
data_files=edge_data_files,
|
|
141
|
+
num_transactions=num_transactions,
|
|
142
|
+
sep=sep,
|
|
143
|
+
quotechar=quotechar,
|
|
144
|
+
fill_na=fill_na,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if import_path:
|
|
148
|
+
local_node_queries = [x.replace(import_path, "file://") for x in node_queries]
|
|
149
|
+
local_edge_queries = [x.replace(import_path, "file://") for x in edge_queries]
|
|
150
|
+
else:
|
|
151
|
+
# change filename to file:///
|
|
152
|
+
local_node_queries = connection.to_local_prefix(node_queries)
|
|
153
|
+
# change filenames to file:///
|
|
154
|
+
local_edge_queries = connection.to_local_prefix(edge_queries)
|
|
155
|
+
|
|
156
|
+
# log it
|
|
157
|
+
logger.info("NODE QUERIES:")
|
|
158
|
+
for q in local_node_queries:
|
|
159
|
+
logger.info(q)
|
|
160
|
+
# log it
|
|
161
|
+
logger.info("EDGE QUERIES:")
|
|
162
|
+
for q in local_edge_queries:
|
|
163
|
+
logger.info(q)
|
|
164
|
+
|
|
165
|
+
## CREATE NODES IN NEO4J DB
|
|
166
|
+
logger.info(f"Creating nodes in {db_name}")
|
|
167
|
+
for q in local_node_queries:
|
|
168
|
+
# run query in neo4j
|
|
169
|
+
stdout = connection.query(query=q, db=db_name)
|
|
170
|
+
# logit
|
|
171
|
+
logger.info(stdout)
|
|
172
|
+
|
|
173
|
+
## CREATE EDGES IN NEO4J DB
|
|
174
|
+
logger.info(f"Creating edges in {db_name}")
|
|
175
|
+
for q in local_edge_queries:
|
|
176
|
+
# run query in neo4j
|
|
177
|
+
stdout = connection.query(query=q, db=db_name)
|
|
178
|
+
# logit
|
|
179
|
+
logger.info(stdout)
|
|
180
|
+
|
|
181
|
+
# logit
|
|
182
|
+
connection.close()
|
|
183
|
+
logger.info("Run Complete")
|
|
184
|
+
|
|
185
|
+
else:
|
|
186
|
+
print("File imported instead of executed. Not completed.")
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The purpose of this script is to take node and edge lists (separate files) and create initial node and edge lists as csvs
|
|
3
|
+
|
|
4
|
+
Options to rename mandatory columns and remove some columns.
|
|
5
|
+
|
|
6
|
+
Any additional cleaning of the node and edge lists can be done any way you prefer outside of this script.
|
|
7
|
+
|
|
8
|
+
Requirements:
|
|
9
|
+
- node and edge lists should be separated csvs
|
|
10
|
+
- don't have excess files in the src foldr
|
|
11
|
+
- filenaming:
|
|
12
|
+
- if source files have a separate header the corresponding header should have '-header' in the filename
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# imports
|
|
17
|
+
import os
|
|
18
|
+
import pandas as pd
|
|
19
|
+
|
|
20
|
+
# import modules
|
|
21
|
+
from gdbcore.helpers.utils import (
|
|
22
|
+
get_args,
|
|
23
|
+
get_logger,
|
|
24
|
+
config_loader,
|
|
25
|
+
filter_filepaths,
|
|
26
|
+
get_line_count,
|
|
27
|
+
warn_folder,
|
|
28
|
+
assert_nonempty_keys,
|
|
29
|
+
assert_nonempty_vals,
|
|
30
|
+
group_files,
|
|
31
|
+
find_matches,
|
|
32
|
+
get_header,
|
|
33
|
+
)
|
|
34
|
+
from gdbcore.helpers.utils_nelist import IntSorter
|
|
35
|
+
from gdbcore.helpers.utils_cypher import cypher_upper, cypher_camelcase
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
|
|
40
|
+
## GET ARGS
|
|
41
|
+
args = get_args(
|
|
42
|
+
prog_name="csv-formatter",
|
|
43
|
+
others=dict(description="not sure anymore"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
## START LOG FILE
|
|
47
|
+
logger = get_logger()
|
|
48
|
+
logger.info(f"Arguments: {args}")
|
|
49
|
+
|
|
50
|
+
## LOAD CONFIG PARAMETERS
|
|
51
|
+
# getting the config filepath
|
|
52
|
+
config_filepath = args.config
|
|
53
|
+
# log it
|
|
54
|
+
logger.info(f"Path to config file: {config_filepath}")
|
|
55
|
+
# load config params
|
|
56
|
+
logger.info("Loading config params ... ")
|
|
57
|
+
config = config_loader(config_filepath)
|
|
58
|
+
assert_nonempty_keys(config)
|
|
59
|
+
assert_nonempty_vals(config)
|
|
60
|
+
logger.info(f"Configuration: {config}")
|
|
61
|
+
|
|
62
|
+
# assign to variables for use in script
|
|
63
|
+
chunksize = config["chunksize"]
|
|
64
|
+
# incoming file conditions
|
|
65
|
+
src_fpath = config["source"]["folder path"]
|
|
66
|
+
src_sep_header = config["source"]["separate header"]
|
|
67
|
+
src_multi_csv = config["source"]["multi_csv"]
|
|
68
|
+
src_sep = config["source"]["sep"]
|
|
69
|
+
src_quotechar = config["source"]["quotechar"]
|
|
70
|
+
# column names corresponding to
|
|
71
|
+
assert_nonempty_keys(config["source"]["col_names"])
|
|
72
|
+
assert_nonempty_vals(config["source"]["col_names"])
|
|
73
|
+
src_node_ids = config["source"]["col_names"]["node_ids"]
|
|
74
|
+
src_node_labels = config["source"]["col_names"]["node_labels"]
|
|
75
|
+
src_edge_labels = config["source"]["col_names"]["edge_labels"]
|
|
76
|
+
src_start_ids = config["source"]["col_names"]["start_ids"]
|
|
77
|
+
src_end_ids = config["source"]["col_names"]["end_ids"]
|
|
78
|
+
src_exclude_cols = config["source"]["col_names"]["exclude list"]
|
|
79
|
+
# outcoming file settings
|
|
80
|
+
out_fpath = config["output"]["folder path"]
|
|
81
|
+
out_prefix = config["output"]["prefix"]
|
|
82
|
+
out_sep_header = config["output"]["separate header"]
|
|
83
|
+
out_multi_csv = config["output"]["multi_csv"]
|
|
84
|
+
out_sep = config["output"]["sep"]
|
|
85
|
+
out_quotechar = config["output"]["quotechar"]
|
|
86
|
+
# column names corresponding to
|
|
87
|
+
assert_nonempty_keys(config["output"]["col_names"])
|
|
88
|
+
assert_nonempty_vals(config["output"]["col_names"])
|
|
89
|
+
out_node_ids = config["output"]["col_names"]["node_ids"]
|
|
90
|
+
out_node_labels = config["output"]["col_names"]["node_labels"]
|
|
91
|
+
out_edge_labels = config["output"]["col_names"]["edge_labels"]
|
|
92
|
+
out_start_ids = config["output"]["col_names"]["start_ids"]
|
|
93
|
+
out_end_ids = config["output"]["col_names"]["end_ids"]
|
|
94
|
+
out_max_rows = config["output"]["max_rows"]
|
|
95
|
+
# additional db specific formating
|
|
96
|
+
out_cypher = config["output"]["cypher_formatting"]
|
|
97
|
+
|
|
98
|
+
# warning
|
|
99
|
+
message = f"""
|
|
100
|
+
Warning: There are existing files in the output path {out_fpath}.
|
|
101
|
+
There is a risk of duplicating nodes and edges being appended to these files.
|
|
102
|
+
"""
|
|
103
|
+
logger.info(warn_folder(folderpath=out_fpath, warning_message=message))
|
|
104
|
+
|
|
105
|
+
# getting filepaths
|
|
106
|
+
datafiles = filter_filepaths(
|
|
107
|
+
src_fpath, identifiers=[".csv"], exclude=["-header", ".graphml"]
|
|
108
|
+
)
|
|
109
|
+
headfiles = filter_filepaths(
|
|
110
|
+
src_fpath, identifiers=["-header", ".csv"], exclude=[".graphml"]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# grouping them to accomodate multi_csv (this may not be necessary anymore)
|
|
114
|
+
|
|
115
|
+
grouped_files = group_files(
|
|
116
|
+
source_folder=datafiles,
|
|
117
|
+
sub=("\d*.csv", ""),
|
|
118
|
+
)
|
|
119
|
+
logger.info(group_files)
|
|
120
|
+
|
|
121
|
+
# node files first
|
|
122
|
+
for group in grouped_files:
|
|
123
|
+
|
|
124
|
+
# get the group of files
|
|
125
|
+
filelist = grouped_files[group]
|
|
126
|
+
logger.info(filelist)
|
|
127
|
+
|
|
128
|
+
# get the original base filename
|
|
129
|
+
og_fname = os.path.splitext(os.path.basename(group))[0]
|
|
130
|
+
|
|
131
|
+
# preparing the new filenames
|
|
132
|
+
filename_start = f"{out_prefix}_{og_fname}"
|
|
133
|
+
logger.info(filename_start)
|
|
134
|
+
|
|
135
|
+
# preparing header and data filenames
|
|
136
|
+
header_fname = f"{filename_start}-header.csv"
|
|
137
|
+
data_fname_prefix = f"{filename_start}-data"
|
|
138
|
+
# preparing header and data file output paths
|
|
139
|
+
header_fpath = os.path.join(out_fpath, header_fname)
|
|
140
|
+
data_fpath_start = os.path.join(out_fpath, data_fname_prefix)
|
|
141
|
+
|
|
142
|
+
for file in filelist:
|
|
143
|
+
|
|
144
|
+
logger.info(f"Searching {file} ...")
|
|
145
|
+
|
|
146
|
+
# if the source files have separate header files
|
|
147
|
+
if src_sep_header:
|
|
148
|
+
# get the matching header
|
|
149
|
+
header_file = find_matches(file, area=headfiles)
|
|
150
|
+
header = get_header(
|
|
151
|
+
header_file[0], sep=src_sep, quotechar=src_quotechar
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
reader = pd.read_csv(
|
|
155
|
+
file,
|
|
156
|
+
names=[x for x in header if x not in src_exclude_cols],
|
|
157
|
+
chunksize=chunksize,
|
|
158
|
+
sep=src_sep,
|
|
159
|
+
usecols=lambda x: x not in src_exclude_cols,
|
|
160
|
+
)
|
|
161
|
+
# otherwise don't attach header
|
|
162
|
+
else:
|
|
163
|
+
reader = pd.read_csv(
|
|
164
|
+
file,
|
|
165
|
+
header=0,
|
|
166
|
+
chunksize=chunksize,
|
|
167
|
+
sep=src_sep,
|
|
168
|
+
usecols=lambda x: x not in src_exclude_cols,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
for chunk in reader:
|
|
172
|
+
|
|
173
|
+
if out_multi_csv:
|
|
174
|
+
# getting the latest data file
|
|
175
|
+
# first get all data files for that node/edge type
|
|
176
|
+
data_list = filter_filepaths(
|
|
177
|
+
fpath=out_fpath, identifiers=[data_fname_prefix]
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if len(data_list) > 0:
|
|
181
|
+
# sort by digit suffix using utils.IntSorter
|
|
182
|
+
dl_sorter = IntSorter(
|
|
183
|
+
regex=f"{data_fpath_start}(\d+).csv", default_val=0
|
|
184
|
+
)
|
|
185
|
+
# getting the latest file (biggest digit suffix)
|
|
186
|
+
sorted_dl = dl_sorter.sort(
|
|
187
|
+
a_list=data_list,
|
|
188
|
+
num_reverse=True,
|
|
189
|
+
str_reverse=False,
|
|
190
|
+
str_lower=False,
|
|
191
|
+
)
|
|
192
|
+
latest_fname = sorted_dl[0]
|
|
193
|
+
# what's the latest file number?
|
|
194
|
+
latest_fnum = dl_sorter.get_int(latest_fname)
|
|
195
|
+
|
|
196
|
+
# create a new file if the latest file reaches max_rows
|
|
197
|
+
if get_line_count(latest_fname) >= out_max_rows:
|
|
198
|
+
current_data_fname = (
|
|
199
|
+
f"{data_fpath_start}{latest_fnum + 1}.csv"
|
|
200
|
+
)
|
|
201
|
+
print(f"Creating {current_data_fname}...")
|
|
202
|
+
else:
|
|
203
|
+
current_data_fname = latest_fname
|
|
204
|
+
else:
|
|
205
|
+
# if no data files create the first one
|
|
206
|
+
current_data_fname = f"{data_fpath_start}1.csv"
|
|
207
|
+
print(f"Creating {current_data_fname}...")
|
|
208
|
+
else:
|
|
209
|
+
# if not writing node/edges to multiple csvs, then use suffix 1 always
|
|
210
|
+
current_data_fname = f"{data_fpath_start}1.csv"
|
|
211
|
+
|
|
212
|
+
# prep rename dict
|
|
213
|
+
src_v_out = {
|
|
214
|
+
src_node_ids: out_node_ids,
|
|
215
|
+
src_node_labels: out_node_labels,
|
|
216
|
+
src_edge_labels: out_edge_labels,
|
|
217
|
+
src_start_ids: out_start_ids,
|
|
218
|
+
src_end_ids: out_end_ids,
|
|
219
|
+
}
|
|
220
|
+
# rename columns?
|
|
221
|
+
chunk = chunk.rename(columns=src_v_out)
|
|
222
|
+
|
|
223
|
+
# cypher formatting?
|
|
224
|
+
if out_cypher:
|
|
225
|
+
# for nodes
|
|
226
|
+
try:
|
|
227
|
+
# CamelCase node labels
|
|
228
|
+
chunk[out_node_labels] = chunk[out_node_labels].apply(
|
|
229
|
+
cypher_camelcase,
|
|
230
|
+
lower_first=False,
|
|
231
|
+
)
|
|
232
|
+
except Exception:
|
|
233
|
+
logger.info(
|
|
234
|
+
f"CamelCase of {out_node_labels} not completed. May be due to labels not in this csv - column names: {chunk.columns}"
|
|
235
|
+
)
|
|
236
|
+
# for edges
|
|
237
|
+
try:
|
|
238
|
+
# UPPER_CASE edge labels
|
|
239
|
+
chunk[out_edge_labels] = chunk[out_edge_labels].apply(
|
|
240
|
+
cypher_upper
|
|
241
|
+
)
|
|
242
|
+
except Exception:
|
|
243
|
+
logger.info(
|
|
244
|
+
f"UPPER_CASE of {out_edge_labels} not completed. May be due to labels not in this csv - column names: {chunk.columns}"
|
|
245
|
+
)
|
|
246
|
+
# camelCase property names
|
|
247
|
+
chunk = chunk.rename(
|
|
248
|
+
columns=lambda x: (
|
|
249
|
+
cypher_camelcase(x, lower_first=True)
|
|
250
|
+
if x not in src_v_out.values()
|
|
251
|
+
else x
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# append to outfiles
|
|
256
|
+
if out_sep_header:
|
|
257
|
+
chunk.to_csv(
|
|
258
|
+
current_data_fname,
|
|
259
|
+
mode="a",
|
|
260
|
+
header=False,
|
|
261
|
+
index=False,
|
|
262
|
+
sep=out_sep,
|
|
263
|
+
)
|
|
264
|
+
# WRITING HEADER FILE
|
|
265
|
+
# checking if header already exists
|
|
266
|
+
if (os.path.exists(header_fpath) == False) or get_line_count(
|
|
267
|
+
header_fpath
|
|
268
|
+
) < 1:
|
|
269
|
+
# if doesn't exist then create it
|
|
270
|
+
logger.info(f"Creating {header_fpath}...")
|
|
271
|
+
# write header to header file
|
|
272
|
+
chunk.head(0).to_csv(
|
|
273
|
+
header_fpath,
|
|
274
|
+
mode="w",
|
|
275
|
+
header=True,
|
|
276
|
+
index=False,
|
|
277
|
+
sep=out_sep,
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
chunk.to_csv(
|
|
281
|
+
current_data_fname,
|
|
282
|
+
mode="a",
|
|
283
|
+
header=True,
|
|
284
|
+
index=False,
|
|
285
|
+
sep=out_sep,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# done statement
|
|
289
|
+
logger.info("Run Complete.")
|
|
290
|
+
|
|
291
|
+
else:
|
|
292
|
+
print("File imported instead of executed. Not completed.")
|