changes ...

main
Steve Nyemba 3 weeks ago
parent 773a7faa6f
commit a8756b9947

@ -43,8 +43,6 @@
"order": { "order": {
"menu": [ "menu": [
"docs", "docs",
"wizard",
"setup",
"about" "about"
] ]
}, },

@ -1 +1,80 @@
These are basic <b>python functions</b> with a single argument (data:pd.DataFrame). The functions can be used as a pipeline to be called in the context of pre/post processing. <style>
.double-page {
display:grid;
grid-template-columns: 49% 1% 49%;
gap:8px;
}
</style>
<script>
$(document).ready(()=>{
_layout = {on:{load:{'visit-us':['www/html/visit-us.html']}}}
bootup.init('',_layout)
})
</script>
<div>
<div class="large-text">Plugins</div>
Plugins are native python functions, that are integrated into <b>{{layout.header.title}}</b> and called as pre/post processing.
<div class="source-code">
$ transport plugins --help
</div>
<div class="double-page">
<div>
<b>0. Write a plugin function with a decorator</b>
<p>
Plugins are native python functions, that take in a single parameter. The following example should be save in a file <b>my-plugin.py</b>
</p>
<div class="source-code">
<b>import</b> transport
<br><b>import</b> numpy <b>as</b> np
<br>_index = 0
<br><b>@</b>transport.Plugin(name='autoincrement')
<br><b>def</b> _incr (_data):
<div style="margin-left:16px">
global _index
<br>_data['_id'] = _index + np.arange(_data.shape[0])
<br>_index = _data.shape[0]
<br><b>return</b> _data
</div>
</div>
<div style="border-top:4px dotted #CAD5E0; margin:8px;"></div>
<b>1. Register & test the plugin</b>
<p>
The plugin utility will make a copy of the file and allow it to be reused against any supported database techology.
More information is available when running
</p>
<div class="source-code">
$ transport plugins add myplugin my-plugin.py
</div>
<p>
Once registered it is important to see if the function can be tested
</p>
<div class="source-code">$ plugin-ix registry list --folder ~/.data-transport</div>
</div>
<div style="border-left:4px dotted #cad4e0"></div>
<div>
<b>Using our first plugin</b>
<p>
Plugins are used as pipelines i.e you can add more than one and they will execute accordingly in the order in which they are expressed.
</p>
<div class="source-code">
<b>import</b> transport
<br>
<br>dbreader = transport.get.reader(label="address-db",plugins=["_incr@myplugin"])
<br>_df = dbreader.read()
</div>
<br>
The code above shows how a simple plugin function can be applied to a data
<br><i class="fa-solid fa-minus"></i> address-db, is the database label that points to the url with data
<br><i class="fa-solid fa-minus"></i> myplugin, points to a copy of "incr" in "my-plugin.py"
<p>
<div id="visit-us"></div>
</p>
</div>
</div>
</div>

@ -4,11 +4,11 @@
}) })
</script> </script>
<div> <div>
<div class="bold">What is the registry</div> <div class="large-text">Initialize registry</div>
<p> <b>data-transport</b> uses a registry to store database authentication information and referenced by a human readable label. <p> <b>data-transport</b> uses a registry to store database authentication information and referenced by a human readable label.
<ul style="list-style: none;"> <ul style="list-style: none;">
<li> <i class="fa-solid fa-minus"></i> The code uses the labels instead of username,passwords ...</li> <li> <i class="fa-solid fa-minus"></i> The labels reference are recommended instead of having username,passwords ... hard coded</li>
<li> <i class="fa-solid fa-minus"></i> This makes sharing notebooks (Jupyter, Zeppelin, ...) without dissipating sensitive information</li> <li> <i class="fa-solid fa-minus"></i> This makes sharing notebooks (Jupyter, Zeppelin, ...) without dissipating sensitive information, making this a solution to improve collaboration</li>
</ul> </ul>
</p> </p>
<div class="bold">Initialize Registry</div> <div class="bold">Initialize Registry</div>
@ -51,21 +51,21 @@
<b>In code</b> <b>In code</b>
<p> <p>
<div class="source-code"> <div class="source-code">
import transport <b>import</b> transport
import io <br><b>import</b> io
import json <br><b>import</b> json
# <br>#
# transport.registry.exists() <br># transport.registry.exists()
_email = 'steve@the-phi.com' <br>_email = 'steve@the-phi.com'
transport.registry.init(_email) <br>transport.registry.init(_email)
# <br><br>#
# Adding the entry to the registry now that is initialized <br># Adding the entry to the registry now that is initialized
_authStr = {"provider":"http", <br>_authStr = {"provider":"http",
"url":"https://raw.githubusercontent.com/codeforamerica/ohana-api/master/data/sample-csv/addresses.csv" "url":"https://raw.githubusercontent.com/codeforamerica/ohana-api/master/data/sample-csv/addresses.csv"
} }
file = io.StringIO(json.dumps(_authStr)) <br>file = io.StringIO(json.dumps(_authStr))
transport.registry.set('address-db',file) <br>transport.registry.set('address-db',file)
</div> </div>
</p> </p>
</div> </div>

@ -1,61 +1,50 @@
<style>
</style>
<script> <script>
$(document).ready(function(){ $(document).ready(function(){
qcms.source_code() // qcms.source_code()
// $('.source-code').each((_index)=>{ _layout = {on:{load:{'visit-us':['www/html/visit-us.html']}}}
// var _code = $('.source-code')[_index] bootup.init('',_layout)
// var lines = $(_code).text().split('\n')
// $(_code).empty()
// lines.forEach((line)=>{
// line = line.replace(/import/g,'<b>import</b>').replace(/def/g,'<b>def</b>').replace(/\#/g,'<i style="color:darkgray">#').replace(/#.+(\n$)/g,'</i>')
// line = line.replace(/print/g,'<b>print</b>')
// $(_code).append( $('<div></div>').html(line))
// })
// })
}) })
</script> </script>
<div style="display:grid; grid-template-columns: calc(50% - 4px) 4px 50%; gap:8px;"> <div style="display:grid; grid-template-columns:49% auto 49%; gap:8px; width:100%">
<div> <div>
<div class="bold">Collaborative development</div> <div class="bold">Collaborative development</div>
<p> <p>
<b>0.</b> In this scenario we assume the registry has been initialized and that an entry has been added (CLI). <b>0.</b> In this scenario we assume the registry has been initialized and that an entry has been added (CLI).
<div class="source-code"> <div class="source-code">
# transport registry add --help <b>$ </b> transport registry add address-db ./http-auth.json
<br>$ transport registry add address-db http-auth.json </div>
</div>
</p>
The python code would look like the following :
<div class="source-code" >
<b>import</b> transport
<pre>
#
# We are assuming here that the label books-db is an entry in the registry
dbreader = transport.get.reader(label='address-db') # <i>No database credentials</i> </p>
_df = dbreader.read(sql="SELECT * FROM books where postal_code like '946%' ") The python code would look like the following :
print (_df.head()) <br></br><div class="source-code" >
</pre> <b>import</b> transport
<br><i>#
<br># we are using "address-db" to refrence the content of http-auth.json file
</i>
<br>
<br>dbreader = transport.get.reader(label='address-db')
<br>_df = dbreader.read()
<br><b>print</b> (_df.head())
</div> </div>
<p style="border-top:4px dotted #CAD5E0"> <p style="border-top:4px dotted #CAD5E0; padding-top:8px;">
<b>1.</b> Alternatively it is possible to directly use the authentication file dubbed "<b>auth-file</b>". <b>1.</b> Alternatively it is possible to directly use the authentication file dubbed "<b>auth-file</b>".
<div class="source-code" > <div class="source-code" >
<b>import</b> transport <b>import</b> transport
<pre> <i>
# <br>#
# We are assuming here that the label books-db is an entry in the registry <br># <b>http-auth.json</b> contians connectivity parameters.
</i>
dbreader = transport.get.reader(auth_file='/home/me/http-auth.json') # <i>No database credentials</i> <br><br>dbreader = transport.get.reader(auth_file='http-auth.json')
_df = dbreader.read(sql="SELECT * FROM books where postal_code like '946%' ") <br>_df = dbreader.read()
print (_df.head()) <br><b>print</b> (_df.head())
</pre>
</div> </div>
</p> </p>
@ -64,20 +53,20 @@ print (_df.head())
<div> <div>
<div class="bold">Non-collaborative development</div> <div class="bold">Non-collaborative development</div>
<p> <p>
In this scenario, we are using connectivity parameters in the code. We do <b>NOT recommend</b> this if the code will be used/shared. In this scenario, we are using connectivity parameters in the code. This scenario is suited for <b>non-collaborative</b> environments.
</p> </p>
<div class="source-code"> <div class="source-code">
<b>import</b> transport <b>import</b> transport
<pre>
# <br>#
# In this scenario we are loading an SQLite3+ database <br># In this scenario we are loading an SQLite3+ database
url= "https://raw.githubusercontent.com/codeforamerica/ohana-api/master/data/sample-csv/addresses.csv" <br>url= "https://raw.githubusercontent.com/codeforamerica/ohana-api/master/data/sample-csv/addresses.csv"
_args = {"provider":"http","database"} <br>_args = {"provider":"http","url":url}
dbreader = transport.get.reader(**_args) # <i>No database credentials</i> <br><br>dbreader = transport.get.reader(**_args)
_df = dbreader.read(sql="SELECT * FROM books where postal_code like '946%' ") <br>_df = dbreader.read()
print (_df.head()) <br><b>print</b> (_df.head())
</pre>
</div> </div>
@ -85,6 +74,7 @@ print (_df.head())
<div class="bold">Learn more</div> <div class="bold">Learn more</div>
It is possible to <b>initialize the registry</b>; <b>run ETL</b> from your code as well as from the command line (CLI). We compiled this in notebooks available in our code repository It is possible to <b>initialize the registry</b>; <b>run ETL</b> from your code as well as from the command line (CLI). We compiled this in notebooks available in our code repository
</p> </p>
<div id="visit-us" class="visit-us"></div>
</div> </div>
</div> </div>

@ -0,0 +1,83 @@
<style>
.configurator {
width:70%; margin-left:15%;
}
.configurator input[type=text] {
padding:8px;
outline:0px;
border:0px;
font-family:sans-serif; font-size:18px;
background-color: #f3f3f3f3;;
font-weight: normal; color:#000000; line-height: 1.5;
}
.configurator .search-box {
display:grid;
grid-template-columns: auto 64px; gap:8px;
background-color: #f3f3f3f3;;
align-items: center;
align-content:center;
}
.configurator .source .item-select {display:grid;
grid-template-columns: 50% 25% 25%;
padding:4px; gap:8px;
margin:4px;
/* text-transform: capitalize; */
}
</style>
<script>
var _setup = {}
_setup.render = {}
_setup.render.source = function (_data) {
var _select = $('.configurator .source')[0]
_data.forEach(_item=>{
var _label = jx.dom.get.instance('DIV')
var _provider = jx.dom.get.instance('DIV')
var _plugins = jx.dom.get.instance('DIV')
_label.innerHTML = _item.label
_provider.innerHTML = _item.provider
_plugins.innerHTML = JSON.stringify(_item.plugins)
_provider.align='left'
_provider.className = ''
var _pane = jx.dom.get.instance('DIV')
_pane.appendChild(_label)
_pane.appendChild(_provider)
_pane.appendChild(_plugins)
_pane.data = _item
_pane.className = 'item-select active'
_select.appendChild(_pane)
})
}
_setup.init = function (){
var uri='{{system.context}}/api/info/registry'
var http = HttpClient.instance()
http.get(uri,function (x){
_setup.render.source ( JSON.parse(x.responseText))
})
}
$(document).ready(function(){
_setup.init()
})
</script>
<div class="configurator">
<div class="tabs">
<input type="radio" id="etl-tab" name="setup-tabs"/>
<label for="etl-tab">ETL : CLI</label>
<input type="radio" id="store-tab" name="setup-tabs"/>
<label for="store-tab">Read/Write</label>
</div>
<div class="tab-content">
<div class="tab-content-container">
<div>
<h3>Source: Database Technology</h3>
<div class="small">Select a database technology as a <b>source</b></div>
<div class="search-box">
<input type="text" class="source-label" placeholder="Name of the label"/>
<div class="border-left"><div class="active" align="center"><i class="fa-solid fa-times"></i></div></div>
</div>
<div class="source border-top"></div>
</div>
</div>
</div>
</div>

@ -0,0 +1,199 @@
<script>
var _meta ={"postgresql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"redshift":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "mysql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"mariadb":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "bigquery":{"private_key":1,"dataset":1,"table":1},"mongodb":{"db":1,"collection":1,"host":0,"port":0,"username":0,"password":0}, "netezza":{"host":1,"port":1,"username":1,"password":1, "database":1,"table":1}, "sqlite":{"path":1,"table":1}, "sqlserver":{"host":0,"port":0,"username":1,"password":1,"database":1, "table":1}
,"databricks":{"host":1,"token":1,"schema":1,"cluster_path":1,"table":1},"couchdb":{"url":0,"dbname":1,"doc":1,"username":0,"password":0},"cloudant":{"url":0,"dbname":1,"doc":1,"username":0,"password":0}
}
var wizard = {}
wizard.init = function (){
Object.keys(_meta).forEach( function (_name){
var _div = jx.dom.get.instance('DIV')
_div.innerHTML = _name
_div.data = _meta[_name]
_div.data.provider = _name
_div.data.context = 'write'
_div.className = 'search-item active'
_div.placeholder = 'use '+_name+' provider'
_div.onclick = function (){
wizard.write_code(this.data)
$('.wizard-code').slideDown()
}
jx.dom.append('search-meta-items',_div)
})
jx.dom.set.value('search-provider','sqlite')
wizard.search()
}
wizard.write_code = function (_info){
$('.provider').html(_info.provider)
$('#auth-file').html( JSON.stringify(_info))
}
wizard.search = function (){
var term = jx.dom.get.value('search-provider').trim()
wizard.show(term)
}
wizard.clear = function (){
jx.dom.set.value('search-provider','')
wizard.show('')
jx.dom.set.focus('search-provider')
}
wizard.show = function (term){
_count = 0
$('.wizard-code').slideUp()
if (term == ''){
$('.search-item').slideDown()
}else{
var nodes = jx.dom.get.children('search-meta-items')
var _regx = new RegExp(term,"ig")
jx.utils.patterns.visitor(nodes,function (node){
if (node.data.provider.match(_regx)){
$(node).slideDown()
_count += 1
}else{
$(node).slideUp()
}
})
}
$('.found-items').html(_count)
}
wizard.supported_providers = function (){
var uri = '{{system.context}}/api/info/about' ;
var http = HttpClient.instance()
http.get(uri,function(x){
var _object = JSON.parse(x.responseText)
jx.dom.set.value('wiz-vendors',_object.supported)
var layout = {on:{load:{'contact-wizard':['www/html/contact.html']}}}
bootup.init('{{system.context}}',layout)
})
}
$(document).ready(function(){
_node = jx.dom.get.instance('wz-clear-search')
_node.onclick = wizard.clear
_node = jx.dom.get.instance('search-provider')
_node.onkeyup=wizard.search
$('.wizard-code').slideUp()
wizard.supported_providers()
wizard.init()
})
</script>
<style>
.wizard-frame input {
background-color:#f3f3f3; width:96%;
padding:8px; border:4px solid transparent; outline: 0;
color:#000000;
}
.wizard-frame input:focus {
border-left-color: #4682b4;
}
.search-meta-frame {
width:calc(100% - 64px) ;
margin-top:4px;
}
#search-meta{
height:100px; overflow: hidden; overflow-y: auto;
padding:8px;
}
#search-meta .search-item {padding:4px; text-transform: capitalize;}
.wizard-code .provider {text-transform: uppercase;}
.supported-vendors table {width:100%; padding:4px; border:1px solid #CAD5E0;}
.supported-vendors thead tr {
background-color: #4682B4;
color: #ffffff;
text-align: left;
}
.supported-vendors tbody tr {
border-bottom: 1px solid #dddddd;
}
.supported-vendors tbody tr:nth-of-type(even) {
background-color: #f3f3f3;
}
</style>
<div class="wizard-frame" style="display:grid; grid-template-columns: 650px auto; gap:8px;">
<div>
<div class="large-text">Wizard: auth file generator</div>
<p>
<div>This wizard generates an <b>auth-file</b>. It is a template file to be used to setup a <b>data-transport</b> database connectivity to help with best practice when it comes to sensitive information in code.
<ul>
<i class="fa-solid fa-minus"></i> search for the database provider / vendors
<br><i class="fa-solid fa-minus"></i> click on the vendor and copy the generated code to a file
</ul>
</div>
</p>
<p>
<div style="display:grid; grid-template-columns: auto 48px; gap:4px">
<div>
<input id="search-provider" type="text" placeholder="[database provider]" onkeyup="wizard.search()">
</div>
<div>
<div id="wz-clear-search" class="active" align="center"><i class="fa-solid fa-trash"></i></div>
</div>
</div>
<div class="search-meta-frame border-round border">
<div align="right">
<span class="found-items">0</span><span> found</span>
</div>
<div id="search-meta"><div id="search-meta-items"></div></div>
</div>
</p>
<p>
<div style="height:150px;">
<div class="wizard-code">
<div class="provider bold"></div>
<div id="auth-file" class="source-code" style="text-wrap: wrap; overflow: hidden; overflow-x: auto;"> &nbsp;</div>
<br><b>Note :</b>
<br><i class="fa-solid fa-minus"> </i> Copy the code above to the <b>auth-file</b> and fill with appropriate values
<br><i class="fa-solid fa-minus"> </i> Attributes with <b>zero</b> i.e <b>0</b> are optional
</div>
</div>
</p>
<p>
<div id="contact-wizard"></div>
</p>
</div>
<div>
<div class="large-text">Prerequisites</div>
<ul class="border-left">
<li type="square">Familiarity with JSON format</li>
<li type="square">Understand your current database security access policy
<br><i class="fa-solid fa-minus"> </i> Insure your policy (permissions) match your use case
<br>
</li>
</ul>
<div class="large-text">Thing to know</div>
<ul class="border-left">
<li type="square">Values assigned to attributes
<br><i class="fa-solid fa-minus"> </i> value of <b>one</b> i.e <b>1</b> suggests a value must be provided
<br><i class="fa-solid fa-minus"> </i> value of <b>zero</b> i.e <b>0</b> suggests the attribute is optional and can be removed
</li>
<li type="square">
Supported databases (or database providers) to use in search
<div id="wiz-vendors" class="supported-vendors"></div>
</li>
</ul>
</div>
</div>

@ -1,28 +1,36 @@
<script> <script>
$(document).ready(function(){ $(document).ready(function(){
var uri = '{{system.context}}/api/info/about' var uri = '{{system.context}}/api/info/about'
var http = HttpClient.instance() var http = HttpClient.instance()
http.get(uri,function(x){ http.get(uri,function(x){
_info = JSON.parse(x.responseText) _info = JSON.parse(x.responseText)
Object.keys(_info).forEach(_id=>{ Object.keys(_info).forEach(_id=>{
if ($('.'+_id).length == 1){ try{
value = _info[_id] var value = _info[_id]
$('.'+_id).html(value.replace(/\n/g,'<br>')) $('.'+_id).html(value.replace(/\n/g,'<br>'))
}catch(e){
} }
}) })
var _layout = {on:{load:{'visit-us':['www/html/visit-us.html']}}}
// bootup.init('',_layout)
qcms.page.loader('.legal',_layout)
}) })
}) })
</script> </script>
<style>.legal {height:80vh; line-height: 1.5; font-family:sans-serif; font-size:14px; font-weight:lighter}</style> <style>.legal {height:75vh; line-height: 1.5; font-family:sans-serif; font-size:14px; font-weight:lighter}</style>
<div class="legal"> <div class="legal">
<div style="background-color: #f3f3f3;padding:8px;"> <div style="background-color: #f3f3f3;padding:8px;">
<span class="author bold"></span>, version <span class="version"></span> <span class="author bold"></span>, version <span class="version"></span>
</div> </div>
<p> <p>
<div style="display:grid; grid-template-columns: 60% auto; gap:8px;"> <div style="display:grid; grid-template-columns: 52% 48%; gap:8px;">
<div> <div>
<h3>MIT License</h3> <h3>MIT License</h3>
<div class="license"></div> <div class="license"></div>
@ -34,4 +42,9 @@
</div> </div>
</p> </p>
<p>
<div style="border-top:4px dotted #CAD5E0;"></div>
<br>
<div id="visit-us" align="center"></div>
</p>
</div> </div>

@ -0,0 +1,199 @@
<style>
.notebook-pane {
display:grid; grid-template-columns: 40% 60%; gap:8px;;
font-family: sans-serif; line-height: 1.5; padding:8px; font-size:14px;
}
.search-box {display:grid; grid-template-columns: auto 64px; gap:8px; }
.search-box input {padding:8px; border:4px solid transparent; outline: 0; background-color: #f3f3f3; width:99%;}
.search-box input:focus {border-left-color: #4682b4;}
.notebook-search-results-pane {
height:105x;
}
.notebook-search-results {
height:105px; overflow: hidden; overflow-y: auto;
}
.notebook-pane .right-pane .notebook{
height:65vh;
padding:8px;
overflow: hidden ;
}
.notebook-pane .right-pane .notebook iframe{
height:98%;
width:100%;
}
.notebook-pane .right-pane{
display:grid;
grid-template-rows: 32px auto; gap:4px;
}
</style>
<script>
var notebook = {}
notebook.meta ={"postgresql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"redshift":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "mysql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"mariadb":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "bigquery":{"private_key":1,"dataset":1,"table":1},"mongodb":{"db":1,"collection":1,"host":0,"port":0,"username":0,"password":0}, "netezza":{"host":1,"port":1,"username":1,"password":1, "database":1,"table":1}, "sqlite":{"path":1,"table":1}, "sqlserver":{"host":0,"port":0,"username":1,"password":1,"database":1, "table":1}
,"databricks":{"host":1,"token":1,"schema":1,"cluster_path":1,"table":1},"couchdb":{"url":0,"dbname":1,"doc":1,"username":0,"password":0},"cloudant":{"url":0,"dbname":1,"doc":1,"username":0,"password":0}
}
notebook.meta.etl = "transport generate ./my-etl-config.json<br>transport apply ./my-etl-config.json"
notebook.meta.s3 = {"access_key":1,"secret_key":1, "file":1, "bucket":1, "region":1}
notebook.meta['mssqlserver'] = notebook.meta.sqlserver
notebook.init = function (){
var uri = '{{system.context}}/api/info/_data'
var http = HttpClient.instance()
http.post(uri,function(x){
if(x.status == 200 && x.readyState == 4){
var _data = JSON.parse(x.responseText)
notebook.render(_data)
notebook.search('sqlite')
$('.notebook-pane input').val('sqlite')
$('.notebook-pane .left-pane .sqlite').click()
}else{
//
// Find a way to alert of the error
//
;
}
})
}
notebook.render = function (data){
data.forEach(_item =>{
var _div = jx.dom.get.instance('DIV')
_div.innerHTML = _item.label
_div.data = _item
_div.onclick = function (){
$('.notebook-pane .label').html(this.data.label)
$('.notebook-pane .left-pane .vendor-info').html(this.data.doc)
$('.notebook-pane .left-pane .vendor-url').html(this.data.url)
_uri = 'www/html/docs/html/'+this.data.provider.trim()+'.html'
// _context = '{{system.context}}'
// if (_context != '') {
// _uri = _context + '/'+_uri
// }
frame = jx.dom.get.instance('IFRAME')
frame.frameBorder = 0
frame.className = ''
frame.src = _uri //'{{system.context}}'+'/www/html/docs/html/'+this.data.provider.trim()+'.html'
$('.notebook-pane .right-pane .notebook').empty()
$('.notebook-pane .right-pane .notebook').append(frame)
$('.notebook-pane .search-box input').focus()
_info = notebook.meta[this.data.provider]
$('.notebook-pane .left-pane .source-code').html(JSON.stringify(_info))
} //-- binding
_div.className = 'active notebook-item '+_item.provider
$('.notebook-search-results').append(_div)
})
// $('.notebook-search-results .notebook-item').slideUp()
}
notebook.search = function (term) {
_count = 0
if (term == ''){
$('.notebook-search-results .notebook-item').slideDown()
}else{
var nodes = jx.dom.get.children('notebook-search-results')
var _regx = new RegExp(term,"ig")
jx.utils.patterns.visitor(nodes,function (node){
if (node.data.provider.match(_regx) || node.data.label.match(_regx)) {
$(node).slideDown()
_count += 1
}else{
$(node).slideUp()
}
})
}
$('.notebook-pane .found').html(_count)
}
notebook.find = function (){
var term = $('.search-box input').val().trim()
notebook.search(term)
}
notebook.clear = function (){
$('.notebook-pane .search-box input').val('')
notebook.search('')
$('.notebook-pane .search-box input').focus()
$('.notebook-pane .right-pane .notebook').empty()
$('.notebook-pane .label').html('')
$('.notebook-pane .left-pane .vendor-info').html('')
$('.notebook-pane .left-pane .vendor-url').html('')
$('.notebook-pane .source-code').html('&nbsp;')
// $('.notebook-pane .search-box input').focus()
}
notebook.open_wizard =function(){
try{
_args = {{layout.overwrite.wizard}}
menu.events._dialog(_args,'{{context}}')
}catch(e){}
}
$(document).ready(function (){
notebook.init()
$('.search-box .active').on('click',notebook.clear)
$('.search-box input').on('keypress',notebook.find)
})
</script>
<div class="notebook-pane">
<div class="left-pane">
<div class="search-box border-round border">
<div class="border-right"><input type="search" placeholder="[provider or vendor]"/></div>
<div class="active" align="center"><i class="fa-solid fa-trash"></i></div>
</div>
<p><div class="border-round border notebook-search-results-pane">
<div align="right" class="">
<span class="found"></span><span> Found </span>
</div>
<div id = "notebook-search-results" class="notebook-search-results"></div>
</div>
<div style="height:125px">
<ul>
<i class="fa-solid fa-minus "></i> Preview notebooks with database providers/vendors
<br><i class="fa-solid fa-minus "></i> The notebooks show how to use <b>data-transport</b> as a library
<br><i class="fa-solid fa-minus "></i> <span class="vendor-info"></span>
<br><i class="fa-solid fa-minus "></i> <span class="vendor-url"></span>
</ul>
</div>
<div class="border-top">&nbsp;</div>
<p>
<h3>Generated: auth-file <span class="label" style="text-transform: uppercase;"></span></h3>
<ul>
<i class="fa-solid fa-minus "></i> An <b>auth-file</b> is a file used to store database parameters.
<br><i class="fa-solid fa-minus "></i> Copy the code above to the auth-file and fill with appropriate values
<br><i class="fa-solid fa-minus "></i> Attributes with zero i.e 0 are optional
</ul>
<div class="source-code"></div>
<ul>
<b>Note:</b>
<br><i class="fa-solid fa-minus "></i> The database providers/vendors above is exhaustive
<br><i class="fa-solid fa-minus "></i> Generate files for database provider/vendors <span class="bold active" onclick="notebook.open_wizard()">click here</span>
</ul>
</p>
</p>
</div>
<div class="right-pane">
<div>
<div style="text-transform: capitalize;">
<div class="label bold" style="font-size:20px; text-transform: uppercase;"></div>
<div>Jupyter Notebook preview</div>
</div>
</div>
<p><div class="notebook border border-round"></div>
</div>
</div>

@ -1,199 +1,58 @@
<style> <style>
.notebook-pane { iframe {
display:grid; grid-template-columns: 40% 60%; gap:8px;; border:1px solid transparent;
font-family: sans-serif; line-height: 1.5; padding:8px; font-size:14px; width:100%; height:100%; margin:8px;}
} .page-split {display:grid; grid-template-columns: 25% 4px auto; gap:8px; height:75vh;}
.search-box {display:grid; grid-template-columns: auto 64px; gap:8px; } .page-split ul {margin:4px}
.search-box input {padding:8px; border:4px solid transparent; outline: 0; background-color: #f3f3f3; width:99%;}
.search-box input:focus {border-left-color: #4682b4;}
.notebook-search-results-pane {
height:105x;
}
.notebook-search-results {
height:105px; overflow: hidden; overflow-y: auto;
}
.notebook-pane .right-pane .notebook{
height:65vh;
padding:8px;
overflow: hidden ;
}
.notebook-pane .right-pane .notebook iframe{
height:98%;
width:100%;
}
.notebook-pane .right-pane{
display:grid;
grid-template-rows: 32px auto; gap:4px;
}
</style> </style>
<script> <script>
var _layout = {on:{load:{'visit-us':['www/html/visit-us.html','www/html/visit-us.html']}}}
var notebook = {} bootup.init('',_layout)
notebook.meta ={"postgresql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"redshift":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "mysql":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0},"mariadb":{"host":0,"port":0,"database":1,"table":1,"username":0,"password":0}, "bigquery":{"private_key":1,"dataset":1,"table":1},"mongodb":{"db":1,"collection":1,"host":0,"port":0,"username":0,"password":0}, "netezza":{"host":1,"port":1,"username":1,"password":1, "database":1,"table":1}, "sqlite":{"path":1,"table":1}, "sqlserver":{"host":0,"port":0,"username":1,"password":1,"database":1, "table":1} var notebook = {}
,"databricks":{"host":1,"token":1,"schema":1,"cluster_path":1,"table":1},"couchdb":{"url":0,"dbname":1,"doc":1,"username":0,"password":0},"cloudant":{"url":0,"dbname":1,"doc":1,"username":0,"password":0} notebook.open = function (_id){
$('.notebook iframe').attr('src',`www/html/docs/html/${_id}.html`)
}
notebook.meta.etl = "transport generate ./my-etl-config.json<br>transport apply ./my-etl-config.json"
notebook.meta.s3 = {"access_key":1,"secret_key":1, "file":1, "bucket":1, "region":1}
notebook.meta['mssqlserver'] = notebook.meta.sqlserver
notebook.init = function (){
var uri = '{{system.context}}/api/info/_data'
var http = HttpClient.instance()
http.post(uri,function(x){
if(x.status == 200 && x.readyState == 4){
var _data = JSON.parse(x.responseText)
notebook.render(_data)
notebook.search('sqlite')
$('.notebook-pane input').val('sqlite')
$('.notebook-pane .left-pane .sqlite').click()
}else{
//
// Find a way to alert of the error
//
;
}
})
}
notebook.render = function (data){
data.forEach(_item =>{
var _div = jx.dom.get.instance('DIV')
_div.innerHTML = _item.label
_div.data = _item
_div.onclick = function (){
$('.notebook-pane .label').html(this.data.label)
$('.notebook-pane .left-pane .vendor-info').html(this.data.doc)
$('.notebook-pane .left-pane .vendor-url').html(this.data.url)
_uri = 'www/html/docs/html/'+this.data.provider.trim()+'.html'
// _context = '{{system.context}}'
// if (_context != '') {
// _uri = _context + '/'+_uri
// }
frame = jx.dom.get.instance('IFRAME')
frame.frameBorder = 0
frame.className = ''
frame.src = _uri //'{{system.context}}'+'/www/html/docs/html/'+this.data.provider.trim()+'.html'
$('.notebook-pane .right-pane .notebook').empty()
$('.notebook-pane .right-pane .notebook').append(frame)
$('.notebook-pane .search-box input').focus()
_info = notebook.meta[this.data.provider]
$('.notebook-pane .left-pane .source-code').html(JSON.stringify(_info))
} //-- binding
_div.className = 'active notebook-item '+_item.provider
$('.notebook-search-results').append(_div)
})
// $('.notebook-search-results .notebook-item').slideUp()
}
notebook.search = function (term) {
_count = 0
if (term == ''){
$('.notebook-search-results .notebook-item').slideDown()
}else{
var nodes = jx.dom.get.children('notebook-search-results')
var _regx = new RegExp(term,"ig")
jx.utils.patterns.visitor(nodes,function (node){
if (node.data.provider.match(_regx) || node.data.label.match(_regx)) {
$(node).slideDown()
_count += 1
}else{
$(node).slideUp()
}
})
} }
$('.notebook-pane .found').html(_count)
}
notebook.find = function (){
var term = $('.search-box input').val().trim()
notebook.search(term)
}
notebook.clear = function (){
$('.notebook-pane .search-box input').val('')
notebook.search('')
$('.notebook-pane .search-box input').focus()
$('.notebook-pane .right-pane .notebook').empty()
$('.notebook-pane .label').html('')
$('.notebook-pane .left-pane .vendor-info').html('')
$('.notebook-pane .left-pane .vendor-url').html('')
$('.notebook-pane .source-code').html('&nbsp;')
// $('.notebook-pane .search-box input').focus()
}
notebook.open_wizard =function(){
try{
_args = {{layout.overwrite.wizard}}
menu.events._dialog(_args,'{{context}}')
}catch(e){}
}
$(document).ready(function (){
notebook.init()
$('.search-box .active').on('click',notebook.clear)
$('.search-box input').on('keypress',notebook.find)
})
</script> </script>
<div class="notebook-pane"> <div class="notebook page-split">
<div>
<div class="left-pane"> <div class="large-text">Notebooks</div>
<div class="search-box border-round border"> <p class="small">
<div class="border-right"><input type="search" placeholder="[provider or vendor]"/></div> Click on a link below to preview a notebook that illustrates how {{layout.header.title}} can be used.
<div class="active" align="center"><i class="fa-solid fa-trash"></i></div> </p>
</div>
<p><div class="border-round border notebook-search-results-pane">
<div align="right" class="">
<span class="found"></span><span> Found </span>
</div>
<div id = "notebook-search-results" class="notebook-search-results"></div>
</div>
<div style="height:125px">
<ul>
<i class="fa-solid fa-minus "></i> Preview notebooks with database providers/vendors
<br><i class="fa-solid fa-minus "></i> The notebooks show how to use <b>data-transport</b> as a library
<br><i class="fa-solid fa-minus "></i> <span class="vendor-info"></span>
<br><i class="fa-solid fa-minus "></i> <span class="vendor-url"></span>
</ul>
</div>
<div class="border-top">&nbsp;</div>
<p>
<h3>Generated: auth-file <span class="label" style="text-transform: uppercase;"></span></h3>
<ul>
<i class="fa-solid fa-minus "></i> An <b>auth-file</b> is a file used to store database parameters.
<br><i class="fa-solid fa-minus "></i> Copy the code above to the auth-file and fill with appropriate values
<br><i class="fa-solid fa-minus "></i> Attributes with zero i.e 0 are optional
</ul>
<div class="source-code"></div>
<ul> <ul>
<b>Note:</b> <div class="active" onclick="notebook.open('sqlite')">
<br><i class="fa-solid fa-minus "></i> The database providers/vendors above is exhaustive <i class="fa-solid fa-minus"></i> sqlite
<br><i class="fa-solid fa-minus "></i> Generate files for database provider/vendors <span class="bold active" onclick="notebook.open_wizard()">click here</span> </div>
<div class="active" onclick="notebook.open('bigquery')">
<i class="fa-solid fa-minus"></i> bigquery
</div>
<div class="active" onclick="notebook.open('mssqlserver')">
<i class="fa-solid fa-minus"></i> mssqlserver
</div>
<div class="active" onclick="notebook.open('mysql')">
<i class="fa-solid fa-minus"></i> mysql
</div>
<div class="active" onclick="notebook.open('postgresql')">
<i class="fa-solid fa-minus"></i> postgresql
</div>
<div class="active" onclick="notebook.open('s3')">
<i class="fa-solid fa-minus"></i> AWS S3
</div>
<div class="active" onclick="notebook.open('mongodb')">
<div ><i class="fa-solid fa-minus"></i> mongodb</div>
</div>
</ul> </ul>
</p> <p>
</p> <br>
</div> <div style="border-top:4px dotted #CAD5E0"></div>
<div class="right-pane"> <br>
<div> <div id="visit-us"></div>
<div style="text-transform: capitalize;"> </p>
<div class="label bold" style="font-size:20px; text-transform: uppercase;"></div>
<div>Jupyter Notebook preview</div>
</div> </div>
</div>
<p><div class="notebook border border-round"></div> <div style="border-left:4px dotted #CAD5E0;"></div>
</div> <iframe src="www/html/docs/html/mongodb.html"></iframe>
</div> </div>

@ -1,166 +0,0 @@
<script>
_layout = {on:{load:{'plugin-intro':['www/html/_notes/plugins-intro.html']}}}
var _plugins = {}
_plugins.open = function (_id){
$('.tab-content').slideUp(function (){
})
_id = '.'+_id
$(_id).slideDown()
}
_plugins.copy = function(_node) {
var _code = $(_node.parentNode).text().trim().replace(/ {8}/g,'').replace(/ {4}/g,'\t').replace(/\r/g,'\n')
navigator.clipboard.writeText(_code);
$(_node).empty()
$(_node).html('<i class="fa-solid fa-check"></i>')
setTimeout(function(){
$(_node).empty()
$(_node).html('<i class="fa-regular fa-copy"></i>')
},750)
}
$(document).ready(function(){
bootup.init('{{system.context}}',_layout)
// _plugins.open('makeplug-src')
var label = $('.process-steps .tabs label')[0]
label.click()
})
</script>
<style>
/* .source-code {background-color:#f3f3f3; color:black;} */
.source-code b {color: yellow;}
.split-1 {display:grid; grid-template-columns: 45% auto; gap:4px}
.split-2 {display:grid; grid-template-columns: 55% auto; gap:4px}
.process-steps .list { font-size:16px; padding:4px; margin-left:8px; background-color: #f3f3f3;}
.process-steps .list div {margin:8px;}
/* .fa-copy {float:right; cursor:pointer; padding:4px; margin:4px; background-color: transparent;} */
/* .fa-copy:hover {color:#4682b4;} */
</style>
<div style="min-height:650px;">
<div class="split-2">
<div>
<h3>Plugins: Usage & Development</h3>
<p>
<div id="plugin-intro" >
</div>
</p>
<p>
<h3>Plugins: Registry</h3>
The plugins registry is a registry of plugins intended to be used in pre/post processing. This feature comes in handy :
<ul>
<div><i class="fa-solid fa-minus"></i> During ETL: Cleanup data, adding columns enforcing data-typing, removing/encrypting PHI ... </div>
<div><i class="fa-solid fa-minus"></i> In a collaborative environment (Jupyter-x; Zeppelin; AWS Service Workbench)</div>
</ul>
</p>
<p>
<h3>Plugins: Architecture & Design</h3>
Plugins are designed around <b class="active" onclick="window.open('https://en.wikipedia.org/wiki/Plug-in_(computing)','_doc')">plugin architecture</b> using <b class="active" onclick="window.open('https://en.wikipedia.org/wiki/Iterator_pattern','doc')">Iterator design-pattern</b>. In that respect and function as a <b>pipeline</b> i.e executed sequentially in the order in which they are expressed in the parameter. Effectively the output of one function will be the input to the next.
<p>
<div class="border figure" align="center" >
<img src="www/html/_assets/images/plugins-components.png">
<div class="small border-top" align="center" style="margin-top:4px; padding-top:4px;">Data Transport UML Plugin Component View</div>
</div>
</p>
</p>
</div>
<div class="process-steps" >
<h3>Quick Start</h3>
<div class="tabs">
<input type="radio" name="step" id="makeplug">
<label for="makeplug" onclick="_plugins.open('makeplug-src')">1. Make Plugin</label>
<input type="radio" name="step" id="regplug">
<label for="regplug" onclick="_plugins.open('regplug-src')">2. Register Plugin</label>
<input type="radio" name="step" id="useplug">
<label for="useplug" onclick="_plugins.open('useplug-src')">3. Use The Plugin</label>
</div>
<div class="tab-content makeplug-src">
<p>
<div class="split-x">
<div>
<ul class="list">
<div><i class="fa-solid fa-minus"></i> The code here shows a function that will be registered as <b><i>"autoincrement"</i></b>.</div>
<div><i class="fa-solid fa-minus"></i> The data, will always be a <b>pandas.DataFrame</b></b></div>
<div><i class="fa-solid fa-minus"></i> For the sake of this example the file will be <b>my-plugin.py</b></div>
</ul>
</div>
<div class="source-code"><i class="fa-regular fa-copy" onclick="_plugins.copy(this)"></i>
<b>import</b> transport
<div><b>import</b> numpy <b>as</b> np</div>
<br>
<br>_index = 0
<br><div>@transport.Plugin(name='autoincrement')</div>
<div><b>def</b> _incr (_data):</div>
<ul>
<div><b>global</b> _index</div>
<div>_data['_id'] = _index + np.arange(_data.shape[0])</div>
<div>_index = _data.shape[0]</div>
<div><b>return</b> _data</div>
</ul>
</div>
</div>
</p>
</div>
<!-- register plugin-->
<div class="tab-content regplug-src">
<p>
<div class="">
<div>
<b>data-transport</b> comes with a built-in command line interface (CLI). It allows plugins to be registered and reused.
<ul class="list">
<div><i class="fa-solid fa-minus"></i> Registered functions are stored in <b><i>$HOME/.data-transport/plugins/code</i></b></div>
<div><i class="fa-solid fa-minus"></i> Any updates to <b>my-plugin.py</b> will require re-registering the file</div>
<div><i class="fa-solid fa-minus"></i> Additional plugin registry functions (list, test) are available </div>
</ul>
</div>
<div>
<div class="source-code"><i class="fa-regular fa-copy" onclick="_plugins.copy(this)"></i>
<b>$</b> transport plugin-add demo ./my-plugin.py
</div>
<p>
The following command allows <b>data-transport</b> to determine what is knows about the function i.e <b>real name</b> and <b>name</b> to be used in code.
<div class="source-code"><i class="fa-regular fa-copy" onclick="_plugins.copy(this)"></i>
<b>$</b> transport plugin-test demo.autoincrement
</div>
</p>
</div>
</div>
</p>
</div>
<!-- useplug-src -->
<div class="tab-content useplug-src">
<p>
Once registered, the plugins are ready for use within code or configuration file (auth-file).
<div class="source-code"><i class="fa-regular fa-copy" onclick="_plugins.copy(this)"></i><b>import</b> transport
<div><b>from</b> transport <b>import</b> providers</div>
_args = {
<div>"provider":providers.HTTP,</div>
<div>"url":"https://raw.githubusercontent.com/codeforamerica/ohana-api/master/data/sample-csv/addresses.csv",</div>
<div>"plugins":["demo@autoincrement"]</div>
}
<div>reader = transport.get.reader(**_args)</div>
<div>_data = reader.read()</div>
<div>print (_data.head())</div>
</div>
</p>
</div>
</div>
</div>
</div>

@ -2,7 +2,7 @@
.docs { .docs {
display: grid; display: grid;
grid-template-columns: 30% auto; grid-template-columns: 25% auto;
grid-template-rows: min-content; grid-template-rows: min-content;
@ -52,10 +52,16 @@
var uri = qcms.context+'/page' var uri = qcms.context+'/page'
var http = HttpClient.instance() var http = HttpClient.instance()
uri = uri + '?'+_path uri = uri + '?'+_path
http.get(uri, function (x){ http.get(uri, function (x){
$('.docs .full-docs').empty()
$('.docs .full-docs').html(x.responseText) $('.docs .full-docs').html(x.responseText)
var script = $('.docs .full-docs script') // var script = $('.docs .full-docs script')
eval($(script).text()) // script.each( (_index)=>{
// eval( $(script[_index]).text())
// })
}) })
} }
@ -100,11 +106,17 @@
</div> </div>
<div class="src-code"> <div class="src-code">
<div class="active bold" onclick="qstart.open('www/html/about/license.html')">3. Source code</div> <div class="active bold" onclick="qstart.open('www/html/about/license.html')">3. License & features</div>
<ul class="small"> <ul class="small">
{{layout.header.title}} is available uner MIT license and available on github. Contribute, share at will ;-) {{layout.header.title}} is available uner MIT license and available on github. Contribute, share at will ;-)
</ul> </ul>
</div> </div>
<div class="src-code">
<div class="active bold" onclick="qstart.open('www/html/_notes/plugins-intro.html')">4. Plugins</div>
<ul class="small">
How to use and integrate plugins for pre/post processing.
</ul>
</div>
</div> </div>
</div> </div>

@ -1,186 +0,0 @@
<style>
.terminal {
display:grid; grid-template-columns: 55% 45%; gap:8px; font-weight: lighter;
}
hr{border:0px; border-top:3px dotted #CAD5E0; width:50%; margin-left:25%}
.source-code pre {font-size:12px; font-weight:lighter; font-family: courier;}
</style>
<script>
layout = {on:{load:{'install':['www/html/_notes/install.html'],'documentation':['www/html/_notes/documentation.html']}}}
// bootup.init('{{system.context}}',layout)
var label = $('.terminal .tabs label')[0]
label.click()
// menu.events._openTabs('.terminal .tab-content','etl-conf-tab')
// menu.events._openTabs('.make-config','.manual')
label = $('.terminal .etl-conf-tab .tabs label')[0]
label.click()
</script>
<div class="terminal">
<div>
<p>
<h3>ETL: Introduction</h3>
Extract Load & Transform (ETL) consists in copying data from one database to one or many others. This can be done in two different ways:
<ul>
<div><i class="fa-solid fa-minus"></i> Command Line Interface (CLI), driven by JSON configuration</div>
<div><i class="fa-solid fa-minus"></i> Or within custom python code</div>
</ul>
The ETL process will take advantage of registries for <b>plugins</b> and <b>labeled database connectivity</b> to perform <b>pre</b>/<b>post</b> processing tasks.
</p>
<hr>
<p>
<h3>ETL: Command Line Interface</h3>
<p>
The configuration file needed to run the ETL is a JSON formatted file where each entry contains:
<ul>
<div><i class="fa-solid fa-minus"></i> <b>source</b> with the content of an <b>auth-file</b></div>
<div><i class="fa-solid fa-minus"></i> <b>target</b> with <b>list</b> of elements of an <b>auth-file</b></div>
</ul>
<!-- The auth-file<span class="active bold" onclick="menu.events._dialog({type:'dialog',uri:'www/html/wizard/wizard.html',title:'Auth-file Generator'},'{{system.context}}')">generator</span>, shows how the auth-file entry is structured. -->
<div>
The <b>CLI</b> (transport), is capable of generating a demo ETL :
<ul>
<div><i class="fa-solid fa-minus"></i> with <b>source</b>: reads CSV data from github</b></div>
<div><i class="fa-solid fa-minus"></i> and <b>target</b>: writes the data to CSV & SQLite3 database</div>
</ul>
<div class="source-code"><i class="fa-solid fa-copy" onclick="_plugins.copy(this)"></i>
$ transport generate ./demo-etl.json
</div>
</div>
</p>
</p>
<br>
<hr>
<br><div align="center" class="border figure"><img src="www/html/_images/uml-activity.png">
<div class="small border-top" style="margin-top:4px; padding-top:4px">
Data-transport UML Extract-Load-Transform (ETL) Workflow
</div>
</div>
</div>
<div>
<div>
<div class="tabs" >
<input type="radio" name="etl" id="etl-conf"/>
<label for="etl-conf" onclick="menu.events._openTabs('.terminal .tab-content','etl-conf-tab')">1. Configuration</label>
<input type="radio" name="etl" id="etl-exe"/>
<label for="etl-exe" onclick="menu.events._openTabs('.terminal .tab-content','etl-exe-tab')">2. Run ETL CLI</label>
<input type="radio" name="etl" id="etl-code"/>
<label for="etl-code" onclick="menu.events._openTabs('.terminal .tab-content','etl-code-tab')">ETL: Custom Code</label>
<input type="radio" name="etl" id="none" disabled>
<label for="none" style="grid-column:4">&nbsp;</label>
</div>
</div>
<p>
<div class="tab-content">
<div class="etl-exe-tab">
<p>
The command-line interface should be instructed to run the ETL by calling the <b>apply</b> function.
</p>
<p>
<div class="source-code">
$ transport apply ./demo-etl.json
</div>
</p>
<p>
Additional parameters can be invoked by providing the <b>--help</b> switch
</p>
<p>
<div class="source-code">
$ transport apply --help
</div>
</p>
</div>
<div class="etl-code-tab"></div>
<div class="etl-conf-tab">
The following examples shows simple configuration files that do NOT require any database to be installed. Feel free to change and edit at your own discression.
<br>
<p>
<h3>Example # 1: Basic ETL</h3>
<div class="tabs" style="margin:0px; padding:0px; background-color:#ffffff;grid-template-columns: 50% 50%; display:grid;" align="center">
<input type="radio" name="mk-conf" id="mk-man">
<label for="mk-man" onclick="menu.events._openTabs('.make-config','.manual')" style="background-color:#ffffff; border-radius:0px;">Manual</label>
<input type="radio" name="mk-conf" id="mk-gen">
<label for="mk-gen" onclick="menu.events._openTabs('.make-config','.generated')" style="background-color:#ffffff; border-radius:0px;">Generate</label>
</div>
<div style="border-top:0px; min-height:400px;">
<div class="make-config">
<div class="generated">
<p>
<b>data-transport</b> comes with a CLI integrated that will
<ul>
<div><i class="fa-solid fa-minus"></i> <b>generate</b> an EL configuration file</div>
<div class=" source-code"><i class="fa-solid fa-copy" onclick="_plugins.copy(this)"></i>
<span>$ transport generate ./demo-etl.json</span>
</div>
</ul>
<div><i class="fa-solid fa-minus"></i> <b>NOTE:</b>The configuration file supports <b>labels</b> and/or <b>plugins</b>, these would have to be done manually</div>
</p>
</div>
<div class="manual">
<p>Copy the content and save it to a file <b>"demo-etl.json"</b></p>
<div class="source-code" style="text-overflow: ellipsis;">
<i class="fa-solid fa-copy" onclick="code.copy(this)"></i>
<pre>[{
"source": {
"provider": "http",
"url": "https://github.com/codeforamerica/ohana-api/blob/master/data/sample-csv/addresses.csv"
},
"target": [
{"provider": "files", "path": "addresses.csv", "delimiter": ","},
{"provider": "sqlite3", "database": "sample.db3", "table": "addresses"}
]}]</pre>
</div>
</div>
</div>
</div>
</p>
<hr>
<p>
<h3>Example # 2: ETL With Plugins</h3>
<p>Copy the content and save it to a file <b>"demo-etl.json"</b></p>
<div class="source-code" style="text-overflow: ellipsis;">
<i class="fa-solid fa-copy" onclick="code.copy(this)"></i>
<pre >[{
"source": {
"provider": "http",
"plugins":["demo@autoincrement"],
"url": "https://github.com/codeforamerica/ohana-api/blob/master/data/sample-csv/addresses.csv"
},
"target": [
{"provider": "files", "path": "addresses.csv", "delimiter": ","},
{"provider": "sqlite3", "database": "sample.db3", "table": "addresses"}
]}]</pre>
</div>
</p>
</div>
</div>
</p>
<!-- <div class="border-round border">
<h3 style="border-color: transparent;">UML Activity Diagram - ETL</h3>
<br><div class="border-top">
<ul>
<i class="fa-solid fa-minus"> </i> The diagram shows <b>1-to-many</b> database support
<br><i class="fa-solid fa-minus"> </i> The ETL job is specified by JSON configuration file
</ul>
</div>
</div>
<br><div id="documentation" class="border-round border" style="min-height:250px"></div> -->
</div>
</div>

@ -0,0 +1,17 @@
<style>
.visit-us-action-frame {display:grid; grid-template-columns: auto 250px auto;}
</style>
<div>
We encourage you to visit the github repository for examples and various ways to use <b>{{layout.header.title}}</b>
<p>
<div class="visit-us-action-frame">
<div></div>
<div onclick="window.open('https://github.com/lnyemba/data-transport')">
<div align="center" class="border-round border"><div class="active bold">Github</div></div>
</div>
<div></div>
</div>
</p>
</div>
Loading…
Cancel
Save