> adduser demo
# (set a password, confirm it, and enter through the prompts)
> usermod -aG sudo demo
> su demo
> cd /home/demo
> sudo git clone https://github.com/Lscheinman/FioriFlask3.git
> cd FioriFlask3
> sudo docker-compose build
Step 7/12 : RUN mkdir -p $INSTALL_PATH
---> Using cache
---> 01a7b54e104d
Step 8/12 : WORKDIR $INSTALL_PATH
---> Using cache
---> 6989d9e16454
Step 9/12 : COPY requirements.txt requirements.txt
---> Using cache
---> 8ed25fecb0dc
Step 10/12 : RUN pip install -r requirements.txt
---> Using cache
---> 8125b33f3cb4
Step 11/12 : COPY . .
---> Using cache
---> 33c26d58b6ae
Step 12/12 : CMD gunicorn -b 0.0.0.0:8000 --access-logfile - "fioriapp.app:create_app()"
---> Using cache
---> 975581bc144f
Successfully built 975581bc144f
Successfully tagged fioriapp_celery:latest
> sudo docker-compose up
orientdb_1 | +-----------------------+------+-----------------------+-----+---------+---------------+---------------+-----------------------+
orientdb_1 | |Name |Status|Databases |Conns|StartedOn|Binary |HTTP |UsedMemory |
orientdb_1 | +-----------------------+------+-----------------------+-----+---------+---------------+---------------+-----------------------+
orientdb_1 | |node1551968111025(*)(@)|ONLINE|test=ONLINE (MASTER) |0 |12:51:22 |172.19.0.2:2424|172.19.0.2:2480|361.18MB/3.83GB (9.20%)|
orientdb_1 | | | |Dialogs=ONLINE (MASTER)| | | | | |
orientdb_1 | +-----------------------+------+-----------------------+-----+---------+---------------+---------------+-----------------------+
orientdb_1 | [OHazelcastPlugin]
orientdb_1 | 2019-03-28 12:51:27:805 WARNI Authenticated clients can execute any kind of code into the server by using the following allowed languages: [sql] [OServerSideScriptInterpreter]
orientdb_1 | 2019-03-28 12:51:27:806 INFO OrientDB Studio available at http://172.19.0.2:2480/studio/index.html [OServer]
website_1 | [OrientModel_init__2019-03-28 12:51:32] localhost failed
website_1 | [OrientModel_init__2019-03-28 12:51:33] successfully connected to 172.19.0.2
website_1 | [Extractor_init_2019-03-28 12:51:33] Running diagnostics on ODB
website_1 | [OrientModel_open_db_2019-03-28 12:51:33] Dialogs opened
website_1 | [OrientModel_check_classes_2019-03-28 12:51:33] All 4 classes found
website_1 | [OrientModel_fill_index_2019-03-28 12:51:33] filling index...
website_1 | [OrientModel_fill_index_2019-03-28 12:51:33] ...of 8 vertices...
website_1 | [Extractor_init_2019-03-28 12:51:33] Following files in data
#fioriapp/blueprints/dialogs/__init__.py
from fioriapp.blueprints.dialogs.views import dialogs
#requirements.txt
...
pandas==0.24.2
pyorient==1.5.5
#fioriapp/blueprints/dialogs/models.py
import pyorient #NEW LIBRARY FOR ORIENT DB CLIENT
import os, time, string
import pandas as pd #NEW LIBRARY FOR DATA CLEANSING
import json
import click
from threading import Thread
from datetime import datetime
from difflib import SequenceMatcher
def clean(content):
"""
Utility function for returning cleaned strings into a normalized format for keys
:param content:
:return:
"""
try:
content = content.lower().translate(str.maketrans('', '', string.punctuation)).replace(" ", "")
except Exception as e:
click.echo('%s %s' % (get_datetime(), str(e)))
content = None
return content
def get_datetime():
"""
Utility function for returning a common standard datetime
:return:
"""
return datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
#fioriapp/blueprints/dialogs/models.py
...
class OrientModel():
def __init__(self):
"""
Set up the OrientDB specifically for graphing conversations
Start with a work around for connecting to Dockerized ODB.
1) Wait for ODB to setup and start with sleep
2) Cycle through potential addresses and try connecting to each, breaking when one works
"""
time.sleep(10)
#Line improved by Remi Astier to get away from hard coded values
possible_hosts = socket.gethostbyname_ex(socket.gethostname())[-1]
if len(possible_hosts) > 0:
hostname = possible_hosts[0][:possible_hosts[0].rfind('.')]
i = 1
while i < 10:
possible_hosts.append("%s.%d" % (hostname, i))
i+=1
possible_hosts.append("localhost")
self.user = "root"
self.pswd = "root"
self.stderr = False
self.db_name = "Dialogs"
for h in possible_hosts:
self.client = pyorient.OrientDB("%s" % h, 2424)
try:
self.session_id = self.client.connect(self.user, self.pswd)
click.echo('[OrientModel_init__%s] successfully connected to %s' % (get_datetime(), h))
break
except:
click.echo('[OrientModel_init__%s] %s failed' % (get_datetime(), h))
#fioriapp/blueprints/dialogs/models.py
...
class OrientModel():
...
def initialize_db(self):
"""
Build the schema in OrientDB using the models established in __init__
1) Create the DB if it hasn't been created
2) Open it if it is not already
3) Cycle through the model configuration
4) Use a rule that if 'id' is part of the model, then it should have an index
:return:
"""
click.echo('[OrientModel_initialize_db_%s] Starting process...' % (get_datetime()))
if self.checks['created'] == False:
self.create_db()
if self.checks['open_db'] == False:
self.open_db()
sql = ""
for m in self.models:
sql = sql+"create class %s extends %s;\n" % (m, self.models[m]['class'])
for k in self.models[m].keys():
if k != 'class':
sql = sql+"create property %s.%s %s;\n" % (m, k, self.models[m][k])
if 'id' in str(k):
sql = sql + "create index %s_%s on %s (%s) UNIQUE ;\n" % (m, k, m, k)
sql = sql + "create sequence idseq type ordered;"
click.echo('[OrientModel_initialize_db_%s]'
' Initializing db with following batch statement'
'\n*************** SQL ***************\n'
'%s\n*************** SQL ***************\n' % (get_datetime(), sql))
self.checks['initialized'] = True
#fioriapp/blueprints/dialogs/models.py
...
class DataPrep():
def __init__(self):
"""
Class to deal with the application's back end folder structure. It knows to find the data and upload paths
which will be used to orchestrate interactions between extractions and database transactions.
"""
self.path = os.getcwd()
self.data = os.path.join(self.path, "data")
self.upload = os.path.join(self.data, "upload")
self.acceptable_files = ['csv', 'txt', 'xls', 'xlsx']
self.files = []
def get_folders(self):
for f in os.listdir(self.data):
if os.path.isdir(os.path.join(self.data, f)):
for sub1 in os.listdir(os.path.join(self.data, f)):
if os.path.isdir(os.path.join(self.data, f, sub1)):
for sub2 in os.listdir(os.path.join(self.data, f, sub1)):
if os.path.isfile(os.path.join(self.data, f, sub1, sub2)):
self.files.append(os.path.join(self.data, f, sub1, sub2))
elif os.path.isfile(os.path.join(self.data, f, sub1)):
self.files.append(os.path.join(self.data, f, sub1))
elif os.path.isfile(os.path.join(self.data, f)):
self.files.append(os.path.join(self.data, f))
#fioriapp/blueprints/dialogs/models.py
...
class Extractor():
def __init__(self):
self.odb = OrientModel()
click.echo('[Extractor_init_%s] Running diagnostics on ODB' % (get_datetime()))
self.odb.run_diagnostics()
if self.odb.checks['created'] == False:
self.odb.create_db()
if self.odb.checks['open_db'] == False:
self.odb.open_db()
if self.odb.checks['initialized'] == False:
self.odb.initialize_db()
self.dp = DataPrep()
self.dp.get_folders()
self.report_every = 100
self.last_report_dtg = 0
self.last_lap = 0
# Set up to look at the headers of files and determine the mapping to a common Dialog extraction pattern
self.acceptable_headers = (
{'content': ['posts', 'text'],
'tags': ['type'],
'd_to': ['to'],
'd_from': ['from'],
'd_id': ['dialogueID']
}
)
click.echo('[Extractor_init_%s] Following files in data' % (get_datetime()))
for f in self.dp.list_files()['files']:
click.echo('\t\t%s' % f)
if self.odb.checks['demo_data'] == False:
self.odb.checks['demo_data'] = self.set_demo_data()
#fioriapp/blueprints/dialogs/models.py
...
class Queries:
def __init__(self):
self.ex = Extractor()
def create_duo(self, **kwargs):
"""
Create a conversation between a from and to entity. The conversation is a simple exchange but each entity can
have multiple lines which is why we have from_lines and to_lines in which a connection is made between the last
thing said by the from entity and the first thing said by the to entity.
:param kwargs:
:return:
"""
data = {'tags': kwargs['tags']}
from_lines = self.ex.ex_segs_from_lines(data, kwargs['nfrom'], False)
from_id = from_lines[-1]
to_lines = self.ex.ex_segs_from_lines(data, kwargs['nto'], False)
to_id = clean(to_lines[0])
if from_id not in self.ex.odb.cache:
try:
self.ex.odb.create_content_node(content=kwargs['nfrom'], tags=kwargs['tags'])
except Exception as e:
if 'RecordDuplicatedException' in str(e):
pass
else:
click.echo('%s UNKNOWN ERROR in create_duo %s' % (get_datetime(), str(e)))
self.ex.odb.cache.append(from_id)
if to_id not in self.ex.odb.cache:
try:
self.ex.odb.create_content_node(content=kwargs['nto'], tags=kwargs['tags'])
except Exception as e:
if 'RecordDuplicatedException' in str(e):
pass
else:
click.echo('%s UNKNOWN ERROR in create_duo %s' % (get_datetime(), str(e)))
self.ex.odb.cache.append(to_id)
self.ex.odb.create_edge(rtype='Response', nfrom=from_id, nto=to_id, tags=kwargs['tags'])
return {
'cont_id': list(set(from_lines + to_lines)),
'message': 'Dialog created from %s to %s' % (from_id, to_id)
}
#fioriapp/blueprints/dialogs/views.py
...
def layout_graph(data):
"""
1) Prepare the data list for the graph as a JSON based on data received from the Queries model class in the form:
'a_content': response['d'][0].oRecordData['a_content'],
'a_pid': response['d'][0].oRecordData['a_pid'],
'a_tags': response['d'][0].oRecordData['a_tags'],
'a_create_date': response['d'][0].oRecordData['a_create_date'],
'v_in': [],
'v_out': []
2) Add some additional metrics to provide additional testing opportunity such as random lat/long, string length
3) Create a report which summarizes the data
:param data:
:return:
"""
graph = {
"nodes": [],
"lines": [],
"keys": [],
"report": {'total_nodes': 0,
'avg_str_len': 0,
'tot_str_len': 0}
}
for r in data:
nodes = Q.ex.odb.get_node(cont_id=r)
if r not in graph['keys']:
graph['keys'].append(r)
graph['nodes'].append({
'key': nodes['a_pid'],
'title': str(nodes['a_content'])[:10],
'icon': "sap-icon://message-popup",
'attributes': [
{"label": "Content",
"value": nodes['a_content']},
{"label": "ID",
"value": nodes['a_pid']},
{"label": "Key",
"value": r},
{"label": "Tags",
"value": nodes['a_tags']},
{"label": "Created on",
"value": nodes['a_create_date']},
{"label": "Length",
"value": len(nodes['a_content'])},
{"label": "Geo_lat",
"value": get_random_lat()},
{"label": "Geo_lon",
"value": get_random_lat()}
]
})
graph['report']['total_nodes'] += 1
graph['report']['tot_str_len'] += len(nodes['a_content'])
for rel in nodes['v_in']:
graph['lines'].append({
'from': rel['pid'],
'to': nodes['a_pid']
})
if rel['cont_id'] not in graph['keys']:
graph['report']['total_nodes'] += 1
graph['nodes'].append({
'key': rel['pid'],
'title': str(rel['content'])[:10],
'icon': "sap-icon://message-popup",
'attributes': [
{"label": "Content",
"value": rel['content']},
{"label": "ID",
"value": rel['pid']},
{"label": "Key",
"value": rel['cont_id']},
{"label": "Tags",
"value": rel['tags']},
{"label": "Created on",
"value": rel['create_date']},
{"label": "Length",
"value": len(rel['content'])},
{"label": "Geo_lat",
"value": get_random_lat()},
{"label": "Geo_lon",
"value": get_random_lon()}
]
})
graph['keys'].append(rel['cont_id'])
graph['report']['tot_str_len'] += len(rel['content'])
for rel in nodes['v_out']:
graph['lines'].append({
'from': nodes['a_pid'],
'to': rel['pid']
})
graph['report']['total_nodes'] += 1
if rel['cont_id'] not in graph['keys']:
graph['nodes'].append({
'key': rel['pid'],
'title': str(rel['content'])[:10],
'icon': "sap-icon://message-popup",
'attributes': [
{"label": "Content",
"value": rel['content']},
{"label": "ID",
"value": rel['pid']},
{"label": "Key",
"value": rel['cont_id']},
{"label": "Tags",
"value": rel['tags']},
{"label": "Created on",
"value": rel['create_date']},
{"label": "Length",
"value": len(rel['content'])},
{"label": "Geo_lat",
"value": get_random_lat()},
{"label": "Geo_lon",
"value": get_random_lon()}
]
})
graph['keys'].append(rel['cont_id'])
graph['report']['tot_str_len'] += len(rel['content'])
graph['report']['avg_str_len'] = graph['report']['tot_str_len'] / graph['report']['total_nodes']
return graph
"routes": [
{
"pattern": "",
"name": "home",
"target": ["home"]
},
{
"pattern": "FlexibleColumnLayout",
"name": "DashboardAnalytics",
"target": ["FlexibleColumnLayout"]
},
{
"pattern": "FlexibleColumnLayout",
"name": "FlexibleColumnLayout",
"target": ["FlexibleColumnLayout"]
},
{
"pattern": "Dialogs",
"name": "Dialogs",
"target": ["Dialogs"]
}
],
"targets": {
"home": {
"viewName": "Home",
"viewId": "home",
"viewLevel": 1,
"title": "{i18n>title}"
},
"FlexibleColumnLayout": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewName": "FlexibleColumnLayout"
},
"Dialogs": {
"viewType": "XML",
"transition": "slide",
"clearControlAggregation": false,
"viewName": "Dialogs"
}
//fioriapp/static/controller/Dialogs.controller.js
...
onInit: function() {
this.oModelSettings = new JSONModel({
maxIterations: 200,
maxTime: 500,
initialTemperature: 200,
coolDownStep: 1
});
this.getView().setModel(this.oModelSettings, "settings");
this.getView().setModel(sap.ui.getCore().getModel("DialogsModel"), "DialogsModel");
//Initialize the Graph network
this.oGraph = this.byId("graph");
this.oGraph._fZoomLevel = 0.75;
this.demoGraph();
},
...
getResponse: function(sType) {
MessageToast.show("Getting a response");
sap.ui.core.BusyIndicator.show(0);
var oThis = this;
var oData = {
'rtype': oThis.byId('Dialog.get_response.rtype').getSelectedItem().getKey(),
'phrase': oThis.byId('Dialog.get_response.input').getValue(),
'rel_text': oThis.byId('Dialog.get_response.tags').getValue()
};
jQuery.ajax({
url : "/Dialogs/get_response",
type : "POST",
dataType : "json",
async : true,
data : oData,
success : function(response){
MessageToast.show(response.data.message);
oThis.makeGraph(response.graph);
sap.ui.core.BusyIndicator.hide();
},
error: function(response){
console.log(response);
}
});
console.log(sType);
},
updateGraph: function(oData, curModel){
for (var i = 0; i < oData.nodes.length; i++){
if(!((curModel.keys.indexOf(oData.nodes[i].attributes[2].value) > -1))){
curModel.oData.nodes.push(oData.nodes[i]);
curModel.keys.push(oData.nodes[i].attributes[2].value);
}
}
for (var i = 0; i < oData.lines.length; i++){
if(!((curModel.oData.lines.indexOf(oData.lines[i]) > -1))){
curModel.oData.lines.push(oData.lines[i]);
}
}
var oModel = new JSONModel(curModel.oData);
oModel['keys'] = curModel.keys;
this.getView().setModel(oModel);
},
<!--fioriapp/static/view/Fragments/Dialogs/NetworkGraph.fragment.xml
<core:FragmentDefinition
xmlns="sap.suite.ui.commons.networkgraph"
xmlns:l="sap.ui.layout"
xmlns:core="sap.ui.core"
xmlns:layout="sap.suite.ui.commons.networkgraph.layout"
xmlns:m="sap.m">
<l:FixFlex>
<l:fixContent>
<m:FlexBox fitContainer="true" renderType="Bare" wrap="Wrap" id="graphWrapper2">
<m:items>
<Graph
nodes="{/nodes}"
lines="{/lines}"
groups="{/groups}"
id="graph">
<layoutData>
<m:FlexItemData/>
</layoutData>
<layoutAlgorithm>
<layout:ForceBasedLayout
>
</layout:ForceBasedLayout>
</layoutAlgorithm>
<nodes>
<Node
height="{settings>/height}"
key="{key}"
title="{title}"
icon="{icon}"
group="{group}"
attributes="{path:'attributes', templateShareable:true}"
shape="{shape}"
status="{status}"
x="{x}"
y="{y}">
<attributes>
<ElementAttribute
label="{label}"
value="{value}"/>
</attributes>
</Node>
</nodes>
<lines>
<Line
from="{from}"
to="{to}"
status="{status}"
>
</Line>
</lines>
<groups>
<Group
key="{key}"
title="{title}">
</Group>
</groups>
</Graph>
</m:items>
</m:FlexBox>
</l:fixContent>
</l:FixFlex>
</core:FragmentDefinition>
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
36 | |
25 | |
17 | |
13 | |
8 | |
7 | |
6 | |
6 | |
6 | |
6 |