gitdata-lib 0.0.10__tar.gz → 0.0.12__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.
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/MANIFEST.in +3 -1
- {gitdata-lib-0.0.10/gitdata_lib.egg-info → gitdata_lib-0.0.12}/PKG-INFO +11 -8
- gitdata_lib-0.0.12/README.md +5 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/__init__.py +2 -9
- gitdata_lib-0.0.12/gitdata/__version__.py +1 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/buckets.py +91 -11
- gitdata_lib-0.0.12/gitdata/cli/__init__.py +70 -0
- gitdata_lib-0.0.12/gitdata/cli/gitdata_get.py +22 -0
- gitdata_lib-0.0.12/gitdata/cli/gitdata_scan.py +94 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/config.py +0 -1
- gitdata_lib-0.0.12/gitdata/connectors/__init__.py +5 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/connectors/common.py +129 -6
- gitdata_lib-0.0.12/gitdata/connectors/fake.py +68 -0
- gitdata_lib-0.0.12/gitdata/connectors/http.py +59 -0
- gitdata_lib-0.0.12/gitdata/connectors/local.py +92 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/common.py +0 -1
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/mysql.py +6 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/mysql_setup_test_database.sql +0 -2
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/sqlite3.py +4 -0
- gitdata_lib-0.0.12/gitdata/database/sqlite3_setup.sql +16 -0
- gitdata_lib-0.0.12/gitdata/digester.py +94 -0
- gitdata_lib-0.0.12/gitdata/graphs.py +278 -0
- gitdata_lib-0.0.12/gitdata/repositories.py +110 -0
- gitdata_lib-0.0.12/gitdata/sql.py +235 -0
- gitdata_lib-0.0.12/gitdata/stores/__init__.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/stores/common.py +12 -9
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/stores/entities.py +1 -8
- gitdata_lib-0.0.12/gitdata/stores/facts.py +297 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/utils.py +30 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12/gitdata_lib.egg-info}/PKG-INFO +11 -8
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata_lib.egg-info/SOURCES.txt +12 -9
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata_lib.egg-info/entry_points.txt +0 -1
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata_lib.egg-info/requires.txt +1 -1
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/requirements.txt +1 -1
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/setup.py +1 -1
- gitdata-lib-0.0.10/README.md +0 -6
- gitdata-lib-0.0.10/gitdata/__version__.py +0 -1
- gitdata-lib-0.0.10/gitdata/cli/__init__.py +0 -39
- gitdata-lib-0.0.10/gitdata/connectors/__init__.py +0 -5
- gitdata-lib-0.0.10/gitdata/connectors/console.py +0 -18
- gitdata-lib-0.0.10/gitdata/connectors/csv.py +0 -26
- gitdata-lib-0.0.10/gitdata/connectors/http.py +0 -20
- gitdata-lib-0.0.10/gitdata/connectors/json.py +0 -56
- gitdata-lib-0.0.10/gitdata/connectors/local.py +0 -41
- gitdata-lib-0.0.10/gitdata/connectors/location.py +0 -35
- gitdata-lib-0.0.10/gitdata/connectors/rows.py +0 -15
- gitdata-lib-0.0.10/gitdata/connectors/system.py +0 -29
- gitdata-lib-0.0.10/gitdata/connectors/zip.py +0 -19
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/CHANGELOG.md +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/LICENSE +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/assets/README.md +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/connectors/gitlab.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/__init__.py +0 -0
- /gitdata-lib-0.0.10/gitdata/stores/entity_store.sql → /gitdata_lib-0.0.12/gitdata/database/mysql_setup.sql +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/postgresql.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/database/sqlite3_setup_test_data.sql +0 -0
- {gitdata-lib-0.0.10/gitdata/stores → gitdata_lib-0.0.12/gitdata/ext}/__init__.py +0 -0
- /gitdata-lib-0.0.10/gitdata/database/sqlite3_setup.sql → /gitdata_lib-0.0.12/gitdata/ext/connectors/__init__.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/json.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/queues.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata/solutions.py +0 -0
- /gitdata-lib-0.0.10/gitdata/stores/records.py → /gitdata_lib-0.0.12/gitdata/stores/tables.py +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata_lib.egg-info/dependency_links.txt +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/gitdata_lib.egg-info/top_level.txt +0 -0
- {gitdata-lib-0.0.10 → gitdata_lib-0.0.12}/setup.cfg +0 -0
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
include README.md LICENSE CHANGELOG.md requirements.txt
|
|
2
|
-
recursive-include gitdata *.py *.html *.pug *.sass *.sql *.ini *.js *.css *.gitinclude *.md *.json *.wsgi *.eot *.svg *.ttf *.woff *.woff2 *.png *.ico *.map *.gif LICENSE
|
|
2
|
+
recursive-include gitdata *.py *.html *.pug *.sass *.sql *.ini *.js *.css *.gitinclude *.md *.json *.wsgi *.eot *.svg *.ttf *.woff *.woff2 *.png *.ico *.map *.gif LICENSE
|
|
3
|
+
recursive-exclude gitdata/ext *.py
|
|
4
|
+
recursive-include gitdata/ext __init__.py
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: gitdata-lib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.12
|
|
4
4
|
Summary: Data extraction and analysis library
|
|
5
5
|
Home-page: https://github.com/gitdata/gitdata-lib
|
|
6
6
|
Author: DSI Labs
|
|
7
7
|
Author-email: support@gitdata.com
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
10
8
|
Classifier: Development Status :: 1 - Planning
|
|
11
9
|
Classifier: Environment :: Console
|
|
12
10
|
Classifier: Intended Audience :: Developers
|
|
@@ -22,12 +20,17 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
22
20
|
Classifier: Topic :: Database :: Front-Ends
|
|
23
21
|
Description-Content-Type: text/markdown
|
|
24
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: python-decouple>=3.4
|
|
24
|
+
Requires-Dist: Unipath>=1.1
|
|
25
|
+
Requires-Dist: PyMySQL==0.10.1
|
|
26
|
+
Requires-Dist: psycopg2-binary==2.9.1
|
|
27
|
+
Requires-Dist: python-dotenv
|
|
28
|
+
Requires-Dist: requests
|
|
29
|
+
Requires-Dist: docopt
|
|
30
|
+
Requires-Dist: faker
|
|
25
31
|
|
|
26
32
|
# GitData Lib
|
|
27
33
|
Data Wrangling for Everyone.
|
|
28
34
|
|
|
29
|
-
GitData is
|
|
30
|
-
with a rich set of commands that provide ways to gather, manage and query data in
|
|
31
|
-
an unusually straightforward way.
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
GitData is a fast, scalable, distributed data exploration system
|
|
36
|
+
with a rich set of commands that provide ways to gather, manage and query data in an unusually straightforward way.
|
|
@@ -8,14 +8,7 @@
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from .utils import Record
|
|
11
|
-
from .stores.
|
|
11
|
+
from .stores.tables import table_of
|
|
12
12
|
from .stores.entities import store_of
|
|
13
13
|
from .__version__ import __version__
|
|
14
|
-
from .connectors
|
|
15
|
-
|
|
16
|
-
def fetch(ref):
|
|
17
|
-
"""Fetch data
|
|
18
|
-
|
|
19
|
-
Stub for fetch data function.
|
|
20
|
-
"""
|
|
21
|
-
return Record(HttpConnector().get(ref))
|
|
14
|
+
from .connectors import fetch
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.12'
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
stores blobs of data
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import io
|
|
7
8
|
import os
|
|
8
|
-
import
|
|
9
|
+
import shutil
|
|
9
10
|
|
|
11
|
+
import gitdata
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
def mkdir_p(path):
|
|
@@ -19,10 +21,6 @@ def mkdir_p(path):
|
|
|
19
21
|
os.makedirs(path)
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
def new_id():
|
|
23
|
-
"""Generate a random unique id"""
|
|
24
|
-
return uuid.uuid4().hex
|
|
25
|
-
|
|
26
24
|
class BaseBucket:
|
|
27
25
|
"""BaseBucket"""
|
|
28
26
|
|
|
@@ -30,7 +28,7 @@ class BaseBucket:
|
|
|
30
28
|
class FileBucket(BaseBucket):
|
|
31
29
|
"""File Bucket
|
|
32
30
|
|
|
33
|
-
FileBucket can store and
|
|
31
|
+
FileBucket can store and retrieve blobs of binary data. This is
|
|
34
32
|
useful for storing images or files as attachments where you want
|
|
35
33
|
to put the binary data somewhere and be able to retreive it later
|
|
36
34
|
if asked. The id returned is generally stored in a database or
|
|
@@ -46,8 +44,7 @@ class FileBucket(BaseBucket):
|
|
|
46
44
|
in the site.ini configuration file.
|
|
47
45
|
|
|
48
46
|
>>> import gitdata, tempfile
|
|
49
|
-
>>>
|
|
50
|
-
>>> path = str(dir)
|
|
47
|
+
>>> path = tempfile.TemporaryDirectory().name
|
|
51
48
|
>>> ids = ['id_%04d' % (9-i) for i in range(10)]
|
|
52
49
|
>>> new_id = ids.pop
|
|
53
50
|
|
|
@@ -83,9 +80,22 @@ class FileBucket(BaseBucket):
|
|
|
83
80
|
>>> for item_id in bucket.keys():
|
|
84
81
|
... bucket.delete(item_id)
|
|
85
82
|
|
|
83
|
+
>>> bucket = FileBucket(path, new_id)
|
|
84
|
+
>>> item_id = bucket.puts(io.BytesIO(b'some data'))
|
|
85
|
+
>>> item_id
|
|
86
|
+
'id_0004'
|
|
87
|
+
>>> s = bucket.gets(item_id)
|
|
88
|
+
>>> s.read()
|
|
89
|
+
b'some data'
|
|
90
|
+
>>> s.close()
|
|
91
|
+
>>> bucket.exists(item_id)
|
|
92
|
+
True
|
|
93
|
+
>>> bucket.delete(item_id)
|
|
94
|
+
>>> bucket.exists(item_id)
|
|
95
|
+
False
|
|
86
96
|
"""
|
|
87
97
|
|
|
88
|
-
def __init__(self, path, id_factory=
|
|
98
|
+
def __init__(self, path, id_factory=gitdata.utils.new_uid):
|
|
89
99
|
self.path = os.path.realpath(path)
|
|
90
100
|
self.id_factory = id_factory
|
|
91
101
|
mkdir_p(self.path)
|
|
@@ -95,7 +105,7 @@ class FileBucket(BaseBucket):
|
|
|
95
105
|
pathname = os.path.join(self.path, item_id)
|
|
96
106
|
if os.path.exists(pathname):
|
|
97
107
|
raise Exception('duplicate item')
|
|
98
|
-
f = open(
|
|
108
|
+
f = open(pathname, 'wb')
|
|
99
109
|
try:
|
|
100
110
|
f.write(item)
|
|
101
111
|
finally:
|
|
@@ -109,6 +119,26 @@ class FileBucket(BaseBucket):
|
|
|
109
119
|
with open(os.path.join(pathname), 'rb') as f:
|
|
110
120
|
return f.read()
|
|
111
121
|
|
|
122
|
+
def puts(self, item):
|
|
123
|
+
item_id = self.id_factory()
|
|
124
|
+
pathname = os.path.join(self.path, item_id)
|
|
125
|
+
if os.path.exists(pathname):
|
|
126
|
+
raise Exception('duplicate item')
|
|
127
|
+
f = open(pathname, 'wb')
|
|
128
|
+
try:
|
|
129
|
+
shutil.copyfileobj(item, f)
|
|
130
|
+
finally:
|
|
131
|
+
f.close()
|
|
132
|
+
return item_id
|
|
133
|
+
|
|
134
|
+
def gets(self, item_id, default=None):
|
|
135
|
+
if not isinstance(item_id, str):
|
|
136
|
+
return default
|
|
137
|
+
pathname = os.path.join(self.path, item_id)
|
|
138
|
+
if not os.path.exists(pathname) and default is not None:
|
|
139
|
+
return default
|
|
140
|
+
return open(os.path.join(pathname), 'rb')
|
|
141
|
+
|
|
112
142
|
def delete(self, item_id):
|
|
113
143
|
pathname = os.path.join(self.path, item_id)
|
|
114
144
|
os.remove(pathname)
|
|
@@ -120,5 +150,55 @@ class FileBucket(BaseBucket):
|
|
|
120
150
|
def keys(self):
|
|
121
151
|
return os.listdir(self.path)
|
|
122
152
|
|
|
153
|
+
def clear(self):
|
|
154
|
+
for key in self.keys():
|
|
155
|
+
self.delete(key)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class MemoryBucket(BaseBucket):
|
|
159
|
+
"""Memory Bucket
|
|
160
|
+
|
|
161
|
+
Memory based bucket.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self, id_factory=gitdata.utils.new_uid):
|
|
165
|
+
self.id_factory = id_factory
|
|
166
|
+
self.items = {}
|
|
167
|
+
|
|
168
|
+
def put(self, item):
|
|
169
|
+
item_id = self.id_factory()
|
|
170
|
+
if item_id in self.items:
|
|
171
|
+
raise Exception('duplicate item')
|
|
172
|
+
self.items[item_id] = item
|
|
173
|
+
return item_id
|
|
174
|
+
|
|
175
|
+
def get(self, item_id, default=None):
|
|
176
|
+
value = self.items.get(item_id, default)
|
|
177
|
+
return value
|
|
178
|
+
|
|
179
|
+
def puts(self, item):
|
|
180
|
+
item_id = self.id_factory()
|
|
181
|
+
if item_id in self.items:
|
|
182
|
+
raise Exception('duplicate item')
|
|
183
|
+
self.items[item_id] = item.read()
|
|
184
|
+
return item_id
|
|
185
|
+
|
|
186
|
+
def gets(self, item_id, default=None):
|
|
187
|
+
value = self.items.get(item_id)
|
|
188
|
+
return io.BytesIO(value) if value else default
|
|
189
|
+
# return io.BytesIO(self.items.get(item_id, default))
|
|
190
|
+
|
|
191
|
+
def delete(self, item_id):
|
|
192
|
+
self.items.pop(item_id)
|
|
193
|
+
|
|
194
|
+
def exists(self, item_id):
|
|
195
|
+
return item_id in self.items
|
|
196
|
+
|
|
197
|
+
def keys(self):
|
|
198
|
+
return list(self.items.keys())
|
|
199
|
+
|
|
200
|
+
def clear(self):
|
|
201
|
+
self.items.clear()
|
|
202
|
+
|
|
123
203
|
|
|
124
|
-
Bucket = FileBucket
|
|
204
|
+
Bucket = FileBucket
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
usage: gitdata [options] <command> [<args>...]
|
|
3
|
+
|
|
4
|
+
options:
|
|
5
|
+
-h, --help show help
|
|
6
|
+
-V, --version print version
|
|
7
|
+
-d, --debug debug
|
|
8
|
+
|
|
9
|
+
The most commonly used gitdata commands are:
|
|
10
|
+
fetch fetch data to the local reposotiry
|
|
11
|
+
get get data
|
|
12
|
+
scan scan data
|
|
13
|
+
|
|
14
|
+
See 'gitdata help <command>' for more information on a specific command.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
from docopt import docopt
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
import gitdata
|
|
23
|
+
from gitdata.utils import trim
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
root_logger = logging.getLogger()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def print_help(doc):
|
|
30
|
+
"""Print help text"""
|
|
31
|
+
print(trim(doc))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
"""CLI main"""
|
|
36
|
+
|
|
37
|
+
if len(sys.argv) == 1:
|
|
38
|
+
print_help(__doc__)
|
|
39
|
+
sys.exit()
|
|
40
|
+
|
|
41
|
+
args = docopt(
|
|
42
|
+
__doc__,
|
|
43
|
+
version='gitdata version {}'.format(gitdata.__version__),
|
|
44
|
+
options_first=True
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if args['--debug']:
|
|
48
|
+
print(args)
|
|
49
|
+
root_logger.setLevel(logging.DEBUG)
|
|
50
|
+
|
|
51
|
+
argv = [args['<command>']] + args['<args>']
|
|
52
|
+
|
|
53
|
+
if args['<command>'] == 'get':
|
|
54
|
+
from gitdata.cli.gitdata_get import get, __doc__ as doc
|
|
55
|
+
args = docopt(doc, argv=argv)
|
|
56
|
+
get(args)
|
|
57
|
+
|
|
58
|
+
elif args['<command>'] == 'scan':
|
|
59
|
+
from gitdata.cli.gitdata_scan import scan_to_console, __doc__ as doc
|
|
60
|
+
args = docopt(doc, argv=argv)
|
|
61
|
+
scan_to_console(args)
|
|
62
|
+
|
|
63
|
+
elif args['<command>'] in ['help', None]:
|
|
64
|
+
if args['<args>'] == ['get']:
|
|
65
|
+
from gitdata.cli.gitdata_get import __doc__ as doc
|
|
66
|
+
exit(doc)
|
|
67
|
+
exit(__doc__)
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
exit("%r is not a gitdata command. See 'gitdata help'." % args['<command>'])
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
usage: gitdata get [options] [<ref>...]
|
|
3
|
+
|
|
4
|
+
options:
|
|
5
|
+
-h, --help
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pprint import pprint
|
|
9
|
+
|
|
10
|
+
import gitdata.connectors.common
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get(args):
|
|
14
|
+
if args['<ref>']:
|
|
15
|
+
for ref in args['<ref>']:
|
|
16
|
+
print('getting', ref)
|
|
17
|
+
pprint(
|
|
18
|
+
gitdata.connectors.common.get(ref),
|
|
19
|
+
)
|
|
20
|
+
else:
|
|
21
|
+
print(__doc__)
|
|
22
|
+
print(args)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
usage: gitdata scan [options] [<ref>...]
|
|
3
|
+
|
|
4
|
+
options:
|
|
5
|
+
--limit <n>, -n <n> limit number of records displayed
|
|
6
|
+
--name-columns provide synthetic column names (handy for csv files that do not provide a header)
|
|
7
|
+
-h, --help
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import csv
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
import gitdata
|
|
14
|
+
from gitdata.connectors.common import load, connect
|
|
15
|
+
from gitdata.connectors.http import HttpConnector
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def as_blobs(ref):
|
|
19
|
+
if os.path.isfile(ref):
|
|
20
|
+
with open(ref, 'rb') as f:
|
|
21
|
+
return [f.read()]
|
|
22
|
+
|
|
23
|
+
if ref.startswith('http'):
|
|
24
|
+
http_connector = HttpConnector()
|
|
25
|
+
result = http_connector.get(ref)
|
|
26
|
+
if result:
|
|
27
|
+
return [result['blob'].read()]
|
|
28
|
+
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def as_tables(ref, args):
|
|
33
|
+
for blob in as_blobs(ref):
|
|
34
|
+
rows = []
|
|
35
|
+
try:
|
|
36
|
+
reader = csv.reader(blob.decode('utf8').splitlines())
|
|
37
|
+
except UnicodeDecodeError:
|
|
38
|
+
reader = csv.reader(blob.decode('Latin1').splitlines())
|
|
39
|
+
for row in reader:
|
|
40
|
+
rows.append(row)
|
|
41
|
+
yield gitdata.Record(
|
|
42
|
+
name=ref,
|
|
43
|
+
filename=ref,
|
|
44
|
+
rows=rows,
|
|
45
|
+
columns=[gitdata.Record(name=f'{chr(n+65)}', type='str') for n in range(len(rows[0]))]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def scan(ref, args):
|
|
50
|
+
|
|
51
|
+
def scan_table(table):
|
|
52
|
+
header = gitdata.Record(
|
|
53
|
+
name=table.name,
|
|
54
|
+
row_count=len(table.rows),
|
|
55
|
+
column_count=len(table.rows[0])
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
limit = args['--limit'] and int(args['--limit']) or 5
|
|
59
|
+
|
|
60
|
+
if args['--name-columns']:
|
|
61
|
+
rows = [[t.name for t in table.columns]] + table.rows[:limit]
|
|
62
|
+
else:
|
|
63
|
+
rows = table.rows[:limit]
|
|
64
|
+
|
|
65
|
+
sample = gitdata.utils.ItemList(rows)
|
|
66
|
+
return str(header) + '\n' + str(sample) + str(list())
|
|
67
|
+
|
|
68
|
+
return ''.join(scan_table(table) for table in as_tables(ref, args))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def console(output):
|
|
72
|
+
if output:
|
|
73
|
+
print(output)
|
|
74
|
+
print()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def scan_to_console(args):
|
|
78
|
+
if args['<ref>']:
|
|
79
|
+
for ref in args['<ref>']:
|
|
80
|
+
console(scan(ref, args))
|
|
81
|
+
# connection = connect(ref)
|
|
82
|
+
# if connection:
|
|
83
|
+
# print(connection)
|
|
84
|
+
# for table in connection.get_tables():
|
|
85
|
+
# print(f'scanning {table.name}')
|
|
86
|
+
# else:
|
|
87
|
+
# print(f'unable to connect to {ref}')
|
|
88
|
+
|
|
89
|
+
# g = load(ref)
|
|
90
|
+
# print('scanning', ref)
|
|
91
|
+
# print(repr(g))
|
|
92
|
+
# print(g)
|
|
93
|
+
else:
|
|
94
|
+
print(__doc__)
|
|
@@ -2,20 +2,25 @@
|
|
|
2
2
|
common connector
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import datetime
|
|
6
|
+
import getpass
|
|
5
7
|
import inspect
|
|
6
8
|
import importlib
|
|
7
9
|
import logging
|
|
8
10
|
import pkgutil
|
|
11
|
+
import platform
|
|
9
12
|
|
|
10
13
|
import gitdata.connectors
|
|
14
|
+
import gitdata.ext.connectors
|
|
15
|
+
import gitdata.graphs
|
|
11
16
|
import gitdata.solutions
|
|
12
17
|
|
|
13
|
-
|
|
14
18
|
logger = logging.getLogger(__name__)
|
|
15
19
|
|
|
16
20
|
|
|
17
21
|
def get_connectors():
|
|
18
22
|
"""generate connectors"""
|
|
23
|
+
|
|
19
24
|
path = gitdata.connectors.__path__
|
|
20
25
|
for _, name, _ in pkgutil.iter_modules(path):
|
|
21
26
|
if name != 'common':
|
|
@@ -30,6 +35,19 @@ def get_connectors():
|
|
|
30
35
|
if found:
|
|
31
36
|
yield obj
|
|
32
37
|
|
|
38
|
+
path = gitdata.ext.connectors.__path__
|
|
39
|
+
for _, name, _ in pkgutil.iter_modules(path):
|
|
40
|
+
module = importlib.import_module('gitdata.ext.connectors.' + name)
|
|
41
|
+
for _, obj in inspect.getmembers(module):
|
|
42
|
+
if obj == BaseConnector:
|
|
43
|
+
continue
|
|
44
|
+
try:
|
|
45
|
+
found = issubclass(obj, BaseConnector)
|
|
46
|
+
except TypeError:
|
|
47
|
+
found = False
|
|
48
|
+
if found:
|
|
49
|
+
yield obj
|
|
50
|
+
|
|
33
51
|
|
|
34
52
|
def get_connector_graph():
|
|
35
53
|
"""generate connector graph"""
|
|
@@ -103,6 +121,15 @@ class BaseConnector:
|
|
|
103
121
|
def connect(self, ref):
|
|
104
122
|
"""Connect to a ref"""
|
|
105
123
|
|
|
124
|
+
def extract(self, ref):
|
|
125
|
+
"""Extract facts from a ref"""
|
|
126
|
+
|
|
127
|
+
def get_tables(self):
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
def get_blobs(self):
|
|
131
|
+
return []
|
|
132
|
+
|
|
106
133
|
# def explore(self, data):
|
|
107
134
|
# """Explore a graph
|
|
108
135
|
# """
|
|
@@ -113,16 +140,112 @@ def connect(ref):
|
|
|
113
140
|
"""Return a connection to a ref"""
|
|
114
141
|
|
|
115
142
|
for connector in get_connectors():
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
connection.connect(ref)
|
|
143
|
+
connection = connector().connect(ref)
|
|
144
|
+
if connection:
|
|
119
145
|
return connection
|
|
120
146
|
|
|
121
147
|
|
|
122
148
|
# connectors = {
|
|
123
149
|
# connector.__name__: connector for connector in get_connectors()}
|
|
124
150
|
|
|
125
|
-
|
|
126
|
-
|
|
127
151
|
# if 'System' in connectors:
|
|
128
152
|
# return connectors['System']()
|
|
153
|
+
|
|
154
|
+
def add_connection_metadata(facts):
|
|
155
|
+
facts.update(
|
|
156
|
+
dict(
|
|
157
|
+
collected=datetime.datetime.now(),
|
|
158
|
+
node=platform.node(),
|
|
159
|
+
user=getpass.getuser()
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def fetch(ref):
|
|
165
|
+
"""Fetch data
|
|
166
|
+
|
|
167
|
+
Fetch the facts in a datasource and store them.
|
|
168
|
+
"""
|
|
169
|
+
connectors = get_connectors()
|
|
170
|
+
for connector in connectors:
|
|
171
|
+
if hasattr(connector, 'get'):
|
|
172
|
+
facts = connector().get(ref)
|
|
173
|
+
if facts:
|
|
174
|
+
add_connection_metadata(facts)
|
|
175
|
+
return facts
|
|
176
|
+
|
|
177
|
+
def get(ref):
|
|
178
|
+
"""Get Data
|
|
179
|
+
|
|
180
|
+
Get the facts from a datasource.
|
|
181
|
+
"""
|
|
182
|
+
connectors = get_connectors()
|
|
183
|
+
for connector in connectors:
|
|
184
|
+
if hasattr(connector, 'get'):
|
|
185
|
+
facts = connector().get(ref)
|
|
186
|
+
if facts:
|
|
187
|
+
add_connection_metadata(facts)
|
|
188
|
+
return facts
|
|
189
|
+
|
|
190
|
+
class Bunch:
|
|
191
|
+
"""a handy bunch of variables"""
|
|
192
|
+
tables = []
|
|
193
|
+
|
|
194
|
+
def __init__(self, **kwargs):
|
|
195
|
+
self.__dict__.update(kwargs)
|
|
196
|
+
|
|
197
|
+
def __str__(self):
|
|
198
|
+
return f'{self.__class__.__name__}\n' + '\n '.join(
|
|
199
|
+
f'{k:{(20-len(k))*"."}}: {v!r}'
|
|
200
|
+
for k, v in self.__dict__.items()
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
class State(dict):
|
|
204
|
+
"""State of Extraction"""
|
|
205
|
+
|
|
206
|
+
def find(self, kind):
|
|
207
|
+
"""Return things of a kind"""
|
|
208
|
+
cache = self.setdefault('_cache', [])
|
|
209
|
+
for thing in self.setdefault(kind, []):
|
|
210
|
+
thing_id = id(thing)
|
|
211
|
+
if thing_id not in cache:
|
|
212
|
+
cache.append(thing_id)
|
|
213
|
+
yield thing
|
|
214
|
+
|
|
215
|
+
def add(self, kind, value):
|
|
216
|
+
"""Add a value of a certain kind"""
|
|
217
|
+
self.setdefault(kind, []).append(value)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def extract(ref):
|
|
221
|
+
"""Extract a reference
|
|
222
|
+
|
|
223
|
+
Extract data from a reference.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
connectors = get_connectors()
|
|
227
|
+
|
|
228
|
+
# ref = State(ref=refs)
|
|
229
|
+
|
|
230
|
+
extracting = True
|
|
231
|
+
while extracting:
|
|
232
|
+
extracting = any(
|
|
233
|
+
connector().extract(ref)
|
|
234
|
+
for connector in connectors
|
|
235
|
+
)
|
|
236
|
+
# print(extracting)
|
|
237
|
+
|
|
238
|
+
return Bunch(
|
|
239
|
+
# name=ref.get('name', ref['ref']),
|
|
240
|
+
# ref=ref['ref'],
|
|
241
|
+
# tables=ref['tables'],
|
|
242
|
+
# text=ref['text'],
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def load(ref):
|
|
247
|
+
"""Get a Graph
|
|
248
|
+
"""
|
|
249
|
+
graph = gitdata.graphs.Graph()
|
|
250
|
+
graph.add(get(ref))
|
|
251
|
+
return graph
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mock data
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import faker
|
|
6
|
+
|
|
7
|
+
from gitdata.connectors.common import BaseConnector
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FakeConnector(BaseConnector):
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def person(self):
|
|
14
|
+
fake = faker.Faker()
|
|
15
|
+
person = fake.profile()
|
|
16
|
+
first_name, last_name = person['name'].split(maxsplit=1)
|
|
17
|
+
return dict(
|
|
18
|
+
first_name=first_name,
|
|
19
|
+
last_name=last_name,
|
|
20
|
+
birthdate=person['birthdate'],
|
|
21
|
+
sex=person['sex']
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def people(self):
|
|
26
|
+
while True:
|
|
27
|
+
yield self.person
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def address(self):
|
|
31
|
+
fake = faker.Faker()
|
|
32
|
+
return dict(
|
|
33
|
+
street=fake.street_address(),
|
|
34
|
+
city=fake.city(),
|
|
35
|
+
state=fake.state(),
|
|
36
|
+
postal_code=fake.postcode(),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def addresses(self):
|
|
41
|
+
while True:
|
|
42
|
+
yield self.address
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def location(self):
|
|
46
|
+
fake = faker.Faker()
|
|
47
|
+
lat, lng, name, country_code, timezone = fake.location_on_land()
|
|
48
|
+
return dict(
|
|
49
|
+
latitude=lat,
|
|
50
|
+
longitude=lng,
|
|
51
|
+
name=name,
|
|
52
|
+
country_code=country_code,
|
|
53
|
+
timezone=timezone,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def locations(self):
|
|
58
|
+
while True:
|
|
59
|
+
yield self.location
|
|
60
|
+
|
|
61
|
+
def get(self, ref):
|
|
62
|
+
if ref.startswith('fake'):
|
|
63
|
+
print('ref is', ref)
|
|
64
|
+
return dict(
|
|
65
|
+
people=self.people,
|
|
66
|
+
addresses=self.addresses,
|
|
67
|
+
locations=self.locations,
|
|
68
|
+
)
|