Merge branch 'contribution-guide' into 'master'
authorJan Vales (Someone) <jan.vales@tuwien.ac.at>
Tue, 11 Dec 2018 13:52:11 +0000 (14:52 +0100)
committerJan Vales (Someone) <jan.vales@tuwien.ac.at>
Tue, 11 Dec 2018 13:52:11 +0000 (14:52 +0100)
Resolve "Contribution Guide"

Closes #26

See merge request aic18/G6T4!19

23 files changed:
.gitattributes [new file with mode: 0644]
camunda-overlay/README.md [new file with mode: 0644]
camunda-overlay/camunda-deploy.sh [deleted file]
camunda-overlay/camunda.py [new file with mode: 0755]
camunda-overlay/forms/download-pdf.html
camunda-overlay/sentiment-analysis.bpmn
docker-compose.yml
provision.sh
service-analysis/Dockerfile
service-analysis/README.md
service-analysis/requirements.txt [new file with mode: 0644]
service-analysis/sentiment_analysis.py
service-fallback/README.md [new file with mode: 0644]
service-reporting/Controllers/PdfController.cs
service-reporting/Dockerfile
service-reporting/Models/Term.cs [moved from service-reporting/Models/Tweet.cs with 69% similarity]
service-reporting/README.md [new file with mode: 0644]
service-reporting/Utility/TemplateGenerator.cs
service-reporting/readme.md [deleted file]
service-twitter/src/main/java/at/aic18/g6t4/servicetwitter/configuration/Twitter4jProperties.java
service-twitter/src/main/java/at/aic18/g6t4/servicetwitter/exception/SearchException.java [deleted file]
service-twitter/src/main/java/at/aic18/g6t4/servicetwitter/service/TwitterService.java
service-twitter/src/main/resources/application.yml

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..5e014fe
--- /dev/null
@@ -0,0 +1,6 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Declare files that will always have LF line endings on checkout
+*.sh text eol=lf
+mvnw text eol=lf
diff --git a/camunda-overlay/README.md b/camunda-overlay/README.md
new file mode 100644 (file)
index 0000000..66b0767
--- /dev/null
@@ -0,0 +1,22 @@
+# Camunda process manager
+
+Run ```./camunda.py``` to just deploy the process.
+
+## Flags
+
+```--autoclick 1```: will deploy the process and run it with three terms.
+Result should be available in Camunda Tasklist -> All Tasks
+
+```--autoclick 2```: will deploy the process, run it with three terms, and tries to download the pdf
+
+```--no-deploy```: do not deploy the process, probably only useful with the autoclick-flag
+
+```--no-cleanup```: do not remove old deployments, keep tasks running (might be confusing if there are multiple deployments)
+
+## Default login
+
+URI: http://localhost:8080/camunda/
+
+User: demo
+
+Pass: demo
diff --git a/camunda-overlay/camunda-deploy.sh b/camunda-overlay/camunda-deploy.sh
deleted file mode 100755 (executable)
index 60d4d63..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-
-camunda="http://localhost:8080"
-camunda_deployment="${camunda}/engine-rest/deployment"
-deployment_name="Sentiment Analysis"
-
-# find old deployments named ${deployment_name}
-old_ids=`curl -s ${camunda_deployment}/ | python3 -c "import sys, json; doc = json.load(sys.stdin); items = [deployment['id'] for deployment in doc if deployment['name'] == '${deployment_name}']; print (*items, sep = '\n')"`
-# delete all old deployments
-for old_id in ${old_ids}; do
-       curl -s -X DELETE "${camunda_deployment}/${old_id}?cascade=true"
-done
-
-# deploy new verson
-curl -i "${camunda_deployment}/create" \
-        -H "Expect:" \
-        -F "deployment-name=${deployment_name}" \
-        -F "sentiment-analysis.bpmn=@sentiment-analysis.bpmn" \
-        -F "input-forms.html=@forms/input-terms.html" \
-        -F "download-pdf.html=@forms/download-pdf.html"
diff --git a/camunda-overlay/camunda.py b/camunda-overlay/camunda.py
new file mode 100755 (executable)
index 0000000..8ecaa0c
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import json
+import requests
+import argparse
+from pprint import pprint
+
+CAMUNDA="http://localhost:8080/engine-rest/"
+
+def get_current_deployments(key = 'sentiment-analysis'):
+    res = requests.get(CAMUNDA + "deployment")
+    #return [proc for proc in res.json() if proc['name'] == key]
+    return res.json()
+
+def cleanup_old_deployments(key='sentiment-analysis'):
+    print ("Cleaning up old deployments")
+    for deployment in get_current_deployments(key):
+        res = requests.delete(CAMUNDA + "deployment/" + deployment['id'] + "?cascade=true&skipCustomListeners=true")
+        if (res.status_code == 204):
+            print ("Cleaned up deployment {}".format(deployment['id']))
+        else:
+            print ("Error cleaning old deployment {}: Code: {}".format(deployment['id'], res.status_code))
+            try:
+                pprint(res.json())
+            except:
+                pprint(res.content)
+
+def create_deployment(cleanup=False):
+    parameters = [
+            ("deployment-name", "sentiment-analysis"),
+            #("enable-duplicate-filtering", "true"),
+            #("deploy-changed-only", "true"),
+            ("file1", open("sentiment-analysis.bpmn")),
+            ("file2", open("forms/input-terms.html")),
+            ("file3", open("forms/download-pdf.html")),
+            ]
+    if cleanup:
+        cleanup_old_deployments()
+    res = requests.post(CAMUNDA + "deployment/create", files=parameters)
+    if (res.status_code == 200):
+        print ("Successfully deployed Sentiment Analysis")
+    else:
+        pprint ("Status Code: {}".format(res.status_code))
+        try:
+            pprint(res.json())
+        except:
+            pprint(res.content)
+
+def submit_terms(terms):
+    termlist = [{'term': term} for term in terms]
+    # submit to camunda
+    params = {
+            'variables': {
+                'terms': {
+                    'value': json.dumps(termlist),
+                    'type': "json",
+                    'valueInfo': {
+                        'serializationDataFormat': "application/json",
+                        }
+                    }
+                }
+            }
+    res = requests.post(CAMUNDA + "process-definition/key/sentiment-analysis/start", json=params)
+    if (res.status_code == 200):
+        print ("Successfully started Sentiment Analysis")
+    else:
+        pprint ("Status Code: {}".format(res.status_code))
+        try:
+            pprint(res.json())
+        except:
+            try:
+                import xml.dom.minidom
+                content = res.content.decode('utf-8').replace('<!doctype html>', '')
+                print(xml.dom.minidom.parseString(content).toprettyxml())
+            except:
+                pprint(res.content)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--no-deploy', dest='deploy', default=True, action='store_false', help="Do not run the deployment step")
+    parser.add_argument('--no-cleanup', dest='cleanup', default=True, action='store_false', help="Initial deploy does not need cleanup")
+    parser.add_argument('--autoclick', dest='autoclick', type=int, default=0, help="How many steps to autoclick")
+    args = parser.parse_args()
+
+    if args.deploy:
+        # initialize camunda process
+        create_deployment(cleanup=args.cleanup)
+
+    if args.autoclick >= 1:
+        # start clicking
+        submit_terms(["voting", "phonegate", "35c3"])
index 3fc7e0759ac10132900dd6ae0f1794c79e9443d5..4a50a185b8b91226a586bf082044dfe6076299b2 100644 (file)
@@ -1 +1,3 @@
-<p>There is no pdf right now.</p>
+<form name="downloadForm" role="form">
+       <p>Download: <a cam-file-download="reportPDF"></a></p>
+</form>
index 29e6337acdd96d5932eb9a56143a913711caf71c..00637579c8db3ba092c477c344c0c8a71b83bbae 100644 (file)
 <?xml version="1.0" encoding="UTF-8"?>
 <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_0co5an7" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="2.0.3">
-  <bpmn:collaboration id="Collaboration_1x8eoy3">
-    <bpmn:participant id="Participant_0gzc3m9" name="Sentiment Analysis" processRef="Sentiment_Analysis" />
+  <bpmn:collaboration id="Collaboration">
+    <bpmn:participant id="Sentiment_Analysis" name="Sentiment Analysis" processRef="sentiment-analysis" />
   </bpmn:collaboration>
-  <bpmn:process id="Sentiment_Analysis" name="Sentiment Analysis" isExecutable="true" camunda:versionTag="0.0.1">
+  <bpmn:process id="sentiment-analysis" name="Sentiment Analysis" isExecutable="true" camunda:versionTag="0.0.1">
     <bpmn:laneSet id="LaneSet_1u08y0e" />
-    <bpmn:sequenceFlow id="SequenceFlow_0ez6lnk" sourceRef="Task_1tosag4" targetRef="Task_1e7059p" />
-    <bpmn:sequenceFlow id="SequenceFlow_0f33zny" sourceRef="Task_14xmt44" targetRef="IntermediateCatchEvent_050nd2m" />
-    <bpmn:sequenceFlow id="SequenceFlow_11xavl5" sourceRef="StartEvent_1t6nxib" targetRef="Task_14xmt44" />
-    <bpmn:sequenceFlow id="SequenceFlow_0tu3w40" sourceRef="IntermediateThrowEvent_1u3snd0" targetRef="EndEvent_0bmuv13" />
-    <bpmn:sequenceFlow id="SequenceFlow_0ra8hjs" sourceRef="IntermediateThrowEvent_09eb9xc" targetRef="Task_1tosag4" />
-    <bpmn:sequenceFlow id="SequenceFlow_0yqo6md" sourceRef="IntermediateThrowEvent_1pwlz5c" targetRef="ExclusiveGateway_0onybdl" />
-    <bpmn:sequenceFlow id="SequenceFlow_0e9hshn" sourceRef="IntermediateThrowEvent_0d8ns1p" targetRef="Task_1kanz3j" />
-    <bpmn:sequenceFlow id="SequenceFlow_12s5p9s" sourceRef="IntermediateThrowEvent_1rxh4ec" targetRef="Task_1n6iu8h" />
-    <bpmn:sequenceFlow id="SequenceFlow_0fct5f2" sourceRef="IntermediateThrowEvent_192yjst" targetRef="Task_1cfglzc" />
-    <bpmn:sequenceFlow id="SequenceFlow_1inwzje" sourceRef="IntermediateThrowEvent_0zbs0zv" targetRef="Task_1rt5mbt" />
-    <bpmn:sequenceFlow id="SequenceFlow_1gdqfur" sourceRef="Task_1kanz3j" targetRef="IntermediateThrowEvent_0zbs0zv" />
-    <bpmn:sequenceFlow id="SequenceFlow_0jebdb9" sourceRef="Task_1e7059p" targetRef="IntermediateThrowEvent_1u3snd0" />
-    <bpmn:sequenceFlow id="SequenceFlow_03ohp0o" sourceRef="Task_1tosag4" targetRef="IntermediateThrowEvent_1rxh4ec" />
-    <bpmn:sequenceFlow id="SequenceFlow_0okj60f" name="All terms are analyzed" sourceRef="ExclusiveGateway_0onybdl" targetRef="IntermediateThrowEvent_09eb9xc">
-      <bpmn:documentation>All terms are analyzed</bpmn:documentation>
-      <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${terms}==[]</bpmn:conditionExpression>
-    </bpmn:sequenceFlow>
-    <bpmn:sequenceFlow id="SequenceFlow_10v2c9r" sourceRef="ExclusiveGateway_0onybdl" targetRef="IntermediateThrowEvent_0d8ns1p" />
-    <bpmn:sequenceFlow id="SequenceFlow_1gbx7mw" sourceRef="Task_1kanz3j" targetRef="IntermediateThrowEvent_1pwlz5c" />
+    <bpmn:sequenceFlow id="SequenceFlow_14nqu0e" sourceRef="service-report" targetRef="Task_1e7059p" />
+    <bpmn:sequenceFlow id="SequenceFlow_0jebdb9" sourceRef="Task_1e7059p" targetRef="EndEvent_0bmuv13" />
+    <bpmn:sequenceFlow id="SequenceFlow_15dip2q" sourceRef="StartEvent_1t6nxib" targetRef="terms_loop" />
+    <bpmn:sequenceFlow id="SequenceFlow_1j2y6tv" sourceRef="terms_loop" targetRef="service-report" />
+    <bpmn:startEvent id="StartEvent_1t6nxib" camunda:formKey="embedded:deployment:input-terms.html">
+      <bpmn:extensionElements>
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="javascript">// make variable global
+execution.setVariable("results", S('[]'))</camunda:script>
+        </camunda:executionListener>
+      </bpmn:extensionElements>
+      <bpmn:outgoing>SequenceFlow_15dip2q</bpmn:outgoing>
+    </bpmn:startEvent>
+    <bpmn:subProcess id="terms_loop">
+      <bpmn:extensionElements>
+        <camunda:executionListener event="start">
+          <camunda:script scriptFormat="javascript">// make variables local to loop
+execution.setVariable("analysis", 0, "terms_loop");
+execution.setVariable('tweets', S('[]'), "terms_loop");</camunda:script>
+        </camunda:executionListener>
+        <camunda:executionListener event="end">
+          <camunda:script scriptFormat="javascript">var results = execution.getVariable("results");
+var term = execution.getVariable("term");
+var analysis = execution.getVariable("analysis");
+var termStr = term.prop('term').value();
+
+// this is for dictionary
+//results.prop(termStr, analysis);
+// this is for list
+var item = {};
+item[termStr] = analysis;
+results.append(item)
+
+execution.setVariable("results", S(results))</camunda:script>
+        </camunda:executionListener>
+      </bpmn:extensionElements>
+      <bpmn:incoming>SequenceFlow_15dip2q</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_1j2y6tv</bpmn:outgoing>
+      <bpmn:multiInstanceLoopCharacteristics camunda:collection="${terms.elements()}" camunda:elementVariable="term" />
+      <bpmn:serviceTask id="service-analysis" name="Analysis Service">
+        <bpmn:extensionElements>
+          <camunda:connector>
+            <camunda:inputOutput>
+              <camunda:inputParameter name="url">http://analysis:8081/</camunda:inputParameter>
+              <camunda:inputParameter name="method">POST</camunda:inputParameter>
+              <camunda:inputParameter name="headers">
+                <camunda:map>
+                  <camunda:entry key="Accept">application/json</camunda:entry>
+                  <camunda:entry key="Content-Type">application/json</camunda:entry>
+                </camunda:map>
+              </camunda:inputParameter>
+              <camunda:inputParameter name="payload">${tweets.toString()}</camunda:inputParameter>
+              <camunda:outputParameter name="analysis">
+                <camunda:script scriptFormat="Javascript">var response = connector.getVariable("response");
+response.trim()</camunda:script>
+              </camunda:outputParameter>
+            </camunda:inputOutput>
+            <camunda:connectorId>http-connector</camunda:connectorId>
+          </camunda:connector>
+        </bpmn:extensionElements>
+        <bpmn:incoming>SequenceFlow_0g6bfz6</bpmn:incoming>
+        <bpmn:outgoing>SequenceFlow_1bz7zx2</bpmn:outgoing>
+      </bpmn:serviceTask>
+      <bpmn:serviceTask id="service-twitter" name="Twitter Service">
+        <bpmn:extensionElements>
+          <camunda:connector>
+            <camunda:inputOutput>
+              <camunda:inputParameter name="url">
+                <camunda:script scriptFormat="Javascript">var term = execution.getVariable("term");
+'http://twitter:8084/search/tweets.json?q='+term.prop('term').value()</camunda:script>
+              </camunda:inputParameter>
+              <camunda:inputParameter name="method">GET</camunda:inputParameter>
+              <camunda:inputParameter name="headers">
+                <camunda:map>
+                  <camunda:entry key="Accept">application/json</camunda:entry>
+                </camunda:map>
+              </camunda:inputParameter>
+              <camunda:outputParameter name="tweets">
+                <camunda:script scriptFormat="Javascript">var response = connector.getVariable("response");
+S(response);</camunda:script>
+              </camunda:outputParameter>
+            </camunda:inputOutput>
+            <camunda:connectorId>http-connector</camunda:connectorId>
+          </camunda:connector>
+        </bpmn:extensionElements>
+        <bpmn:incoming>SequenceFlow_0f6v4j4</bpmn:incoming>
+        <bpmn:outgoing>SequenceFlow_0g6bfz6</bpmn:outgoing>
+      </bpmn:serviceTask>
+      <bpmn:startEvent id="StartEvent_parsing_term">
+        <bpmn:outgoing>SequenceFlow_0f6v4j4</bpmn:outgoing>
+      </bpmn:startEvent>
+      <bpmn:sequenceFlow id="SequenceFlow_0f6v4j4" sourceRef="StartEvent_parsing_term" targetRef="service-twitter" />
+      <bpmn:sequenceFlow id="SequenceFlow_0g6bfz6" sourceRef="service-twitter" targetRef="service-analysis" />
+      <bpmn:sequenceFlow id="SequenceFlow_1bz7zx2" sourceRef="service-analysis" targetRef="EndEvent_parsing_term" />
+      <bpmn:endEvent id="EndEvent_parsing_term">
+        <bpmn:incoming>SequenceFlow_1bz7zx2</bpmn:incoming>
+      </bpmn:endEvent>
+    </bpmn:subProcess>
     <bpmn:endEvent id="EndEvent_0bmuv13">
-      <bpmn:incoming>SequenceFlow_0tu3w40</bpmn:incoming>
-    </bpmn:endEvent>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_1u3snd0">
       <bpmn:incoming>SequenceFlow_0jebdb9</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0tu3w40</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
+    </bpmn:endEvent>
+    <bpmn:serviceTask id="service-report" name="Report Service">
+      <bpmn:extensionElements>
+        <camunda:connector>
+          <camunda:inputOutput>
+            <camunda:inputParameter name="url">http://reporting:8083/generatePDF/</camunda:inputParameter>
+            <camunda:inputParameter name="method">POST</camunda:inputParameter>
+            <camunda:inputParameter name="headers">
+              <camunda:map>
+                <camunda:entry key="Accept">application/pdf</camunda:entry>
+                <camunda:entry key="Content-Type">application/json</camunda:entry>
+              </camunda:map>
+            </camunda:inputParameter>
+            <camunda:inputParameter name="payload">${results.toString()}</camunda:inputParameter>
+            <camunda:outputParameter name="reportPDF">
+              <camunda:script scriptFormat="javascript">var response = connector.getVariable("response");
+print ("response: ")
+print (response)
+var file = Java.type('org.camunda.bpm.engine.variable.Variables').fileValue("pdfTest").file(response.getBytes("utf-8")).mimeType('application/pdf').create()
+//response.getBytes("utf-8")
+file</camunda:script>
+            </camunda:outputParameter>
+          </camunda:inputOutput>
+          <camunda:connectorId>http-connector</camunda:connectorId>
+        </camunda:connector>
+      </bpmn:extensionElements>
+      <bpmn:incoming>SequenceFlow_1j2y6tv</bpmn:incoming>
+      <bpmn:outgoing>SequenceFlow_14nqu0e</bpmn:outgoing>
+    </bpmn:serviceTask>
     <bpmn:userTask id="Task_1e7059p" name="download pdf" camunda:formKey="embedded:deployment:download-pdf.html">
-      <bpmn:incoming>SequenceFlow_0ez6lnk</bpmn:incoming>
+      <bpmn:incoming>SequenceFlow_14nqu0e</bpmn:incoming>
       <bpmn:outgoing>SequenceFlow_0jebdb9</bpmn:outgoing>
     </bpmn:userTask>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_1rxh4ec">
-      <bpmn:incoming>SequenceFlow_03ohp0o</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_12s5p9s</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_0zbs0zv">
-      <bpmn:incoming>SequenceFlow_1gdqfur</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_1inwzje</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:startEvent id="StartEvent_1t6nxib" camunda:formKey="embedded:deployment:input-terms.html">
-      <bpmn:outgoing>SequenceFlow_11xavl5</bpmn:outgoing>
-    </bpmn:startEvent>
-    <bpmn:task id="Task_1n6iu8h" name="Report Service">
-      <bpmn:incoming>SequenceFlow_12s5p9s</bpmn:incoming>
-    </bpmn:task>
-    <bpmn:task id="Task_1rt5mbt" name="Twitter Service">
-      <bpmn:incoming>SequenceFlow_1inwzje</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0h1h7gs</bpmn:outgoing>
-    </bpmn:task>
-    <bpmn:sequenceFlow id="SequenceFlow_0h1h7gs" sourceRef="Task_1rt5mbt" targetRef="IntermediateCatchEvent_1h9izac" />
-    <bpmn:sequenceFlow id="SequenceFlow_0bzf3di" sourceRef="Task_1slpwnp" targetRef="IntermediateThrowEvent_192yjst" />
-    <bpmn:task id="Task_1cfglzc" name="Analysis Service">
-      <bpmn:incoming>SequenceFlow_0fct5f2</bpmn:incoming>
-    </bpmn:task>
-    <bpmn:scriptTask id="Task_1tosag4" name="Generate Report">
-      <bpmn:incoming>SequenceFlow_0ra8hjs</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0ez6lnk</bpmn:outgoing>
-      <bpmn:outgoing>SequenceFlow_03ohp0o</bpmn:outgoing>
-      <bpmn:script>""</bpmn:script>
-    </bpmn:scriptTask>
-    <bpmn:scriptTask id="Task_1slpwnp" name="forward tweets to analyzer">
-      <bpmn:incoming>SequenceFlow_07bm292</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0bzf3di</bpmn:outgoing>
-      <bpmn:script>""</bpmn:script>
-    </bpmn:scriptTask>
-    <bpmn:intermediateCatchEvent id="IntermediateCatchEvent_1h9izac">
-      <bpmn:incoming>SequenceFlow_0h1h7gs</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_07bm292</bpmn:outgoing>
-      <bpmn:timerEventDefinition id="TimerEventDefinition_0i6ti1o">
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:sequenceFlow id="SequenceFlow_07bm292" sourceRef="IntermediateCatchEvent_1h9izac" targetRef="Task_1slpwnp" />
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_1pwlz5c">
-      <bpmn:incoming>SequenceFlow_1gbx7mw</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0yqo6md</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:exclusiveGateway id="ExclusiveGateway_0onybdl">
-      <bpmn:incoming>SequenceFlow_0yqo6md</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0okj60f</bpmn:outgoing>
-      <bpmn:outgoing>SequenceFlow_10v2c9r</bpmn:outgoing>
-    </bpmn:exclusiveGateway>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_0d8ns1p">
-      <bpmn:incoming>SequenceFlow_10v2c9r</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0e9hshn</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_09eb9xc">
-      <bpmn:incoming>SequenceFlow_0okj60f</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0ra8hjs</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:intermediateCatchEvent id="IntermediateThrowEvent_192yjst">
-      <bpmn:incoming>SequenceFlow_0bzf3di</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0fct5f2</bpmn:outgoing>
-      <bpmn:timerEventDefinition>
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:scriptTask id="Task_1kanz3j" name="Load Twitter Data">
-      <bpmn:incoming>SequenceFlow_0e9hshn</bpmn:incoming>
-      <bpmn:incoming>SequenceFlow_11ge7w6</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_1gdqfur</bpmn:outgoing>
-      <bpmn:outgoing>SequenceFlow_1gbx7mw</bpmn:outgoing>
-      <bpmn:script>""</bpmn:script>
-    </bpmn:scriptTask>
-    <bpmn:sequenceFlow id="SequenceFlow_11ge7w6" sourceRef="IntermediateCatchEvent_050nd2m" targetRef="Task_1kanz3j" />
-    <bpmn:intermediateCatchEvent id="IntermediateCatchEvent_050nd2m">
-      <bpmn:incoming>SequenceFlow_0f33zny</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_11ge7w6</bpmn:outgoing>
-      <bpmn:timerEventDefinition id="TimerEventDefinition_0nq95t6">
-        <bpmn:timeDuration xsi:type="bpmn:tFormalExpression">PT1M</bpmn:timeDuration>
-      </bpmn:timerEventDefinition>
-    </bpmn:intermediateCatchEvent>
-    <bpmn:scriptTask id="Task_14xmt44" name="Input terms automated" scriptFormat="groovy" camunda:resultVariable="terms_groovy">
-      <bpmn:incoming>SequenceFlow_11xavl5</bpmn:incoming>
-      <bpmn:outgoing>SequenceFlow_0f33zny</bpmn:outgoing>
-      <bpmn:script>for ( i in terms) {
-  println "line found: " + i
-}
-nt = {}
-nt["term"] = "newterm"
-terms += nt
-return ""</bpmn:script>
-    </bpmn:scriptTask>
-    <bpmn:association id="Association_1bcr79m" sourceRef="Task_1cfglzc" targetRef="TextAnnotation_08dib8t" />
-    <bpmn:textAnnotation id="TextAnnotation_08dib8t">
-      <bpmn:text>collect all analyze results</bpmn:text>
-    </bpmn:textAnnotation>
   </bpmn:process>
   <bpmndi:BPMNDiagram id="BPMNDiagram_1">
-    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1x8eoy3">
-      <bpmndi:BPMNShape id="Participant_0gzc3m9_di" bpmnElement="Participant_0gzc3m9">
-        <dc:Bounds x="93" y="-237" width="1444" height="543" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="EndEvent_0bmuv13_di" bpmnElement="EndEvent_0bmuv13">
-        <dc:Bounds x="1393" y="185" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="StartEvent_1t6nxib_di" bpmnElement="StartEvent_1t6nxib">
-        <dc:Bounds x="137" y="185" width="36" height="36" />
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration">
+      <bpmndi:BPMNShape id="Participant_0gzc3m9_di" bpmnElement="Sentiment_Analysis">
+        <dc:Bounds x="0" y="0" width="1084" height="202" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_03ohp0o_di" bpmnElement="SequenceFlow_03ohp0o">
-        <di:waypoint x="1070" y="163" />
-        <di:waypoint x="1070" y="132" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0okj60f_di" bpmnElement="SequenceFlow_0okj60f">
-        <di:waypoint x="701" y="203" />
-        <di:waypoint x="875" y="203" />
-        <bpmndi:BPMNLabel>
-          <dc:Bounds x="753" y="189" width="62" height="27" />
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_10v2c9r_di" bpmnElement="SequenceFlow_10v2c9r">
-        <di:waypoint x="676" y="228" />
-        <di:waypoint x="676" y="277" />
-        <di:waypoint x="566" y="277" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_1gbx7mw_di" bpmnElement="SequenceFlow_1gbx7mw">
-        <di:waypoint x="477" y="203" />
-        <di:waypoint x="530" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="ExclusiveGateway_0pzjnfx_di" bpmnElement="ExclusiveGateway_0onybdl" isMarkerVisible="true">
-        <dc:Bounds x="651" y="178" width="50" height="50" />
+      <bpmndi:BPMNShape id="SubProcess_1skl2rb_di" bpmnElement="terms_loop" isExpanded="true">
+        <dc:Bounds x="135" y="19" width="443" height="163" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_0jebdb9_di" bpmnElement="SequenceFlow_0jebdb9">
-        <di:waypoint x="1286" y="203" />
-        <di:waypoint x="1320" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="UserTask_0iooc2g_di" bpmnElement="Task_1e7059p">
-        <dc:Bounds x="1186" y="163" width="100" height="80" />
+      <bpmndi:BPMNShape id="ServiceTask_01bq7a6_di" bpmnElement="service-analysis">
+        <dc:Bounds x="381" y="60" width="100" height="80" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_1dguuge_di" bpmnElement="IntermediateThrowEvent_0zbs0zv">
-        <dc:Bounds x="409" y="96" width="36" height="36" />
+      <bpmndi:BPMNShape id="ServiceTask_1o2usc9_di" bpmnElement="service-twitter">
+        <dc:Bounds x="230" y="60" width="100" height="80" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_1gdqfur_di" bpmnElement="SequenceFlow_1gdqfur">
-        <di:waypoint x="427" y="163" />
-        <di:waypoint x="427" y="132" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_1inwzje_di" bpmnElement="SequenceFlow_1inwzje">
-        <di:waypoint x="427" y="96" />
-        <di:waypoint x="427" y="66" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0fct5f2_di" bpmnElement="SequenceFlow_0fct5f2">
-        <di:waypoint x="804" y="26" />
-        <di:waypoint x="843" y="26" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_1vje2lu_di" bpmnElement="IntermediateThrowEvent_192yjst">
-        <dc:Bounds x="768" y="8" width="36" height="36" />
+      <bpmndi:BPMNShape id="StartEvent_1nx6b6f_di" bpmnElement="StartEvent_parsing_term">
+        <dc:Bounds x="156" y="82" width="36" height="36" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_12s5p9s_di" bpmnElement="SequenceFlow_12s5p9s">
-        <di:waypoint x="1070" y="96" />
-        <di:waypoint x="1070" y="66" />
+      <bpmndi:BPMNEdge id="SequenceFlow_0f6v4j4_di" bpmnElement="SequenceFlow_0f6v4j4">
+        <di:waypoint x="192" y="100" />
+        <di:waypoint x="230" y="100" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0e9hshn_di" bpmnElement="SequenceFlow_0e9hshn">
-        <di:waypoint x="530" y="277" />
-        <di:waypoint x="427" y="277" />
-        <di:waypoint x="427" y="243" />
+      <bpmndi:BPMNEdge id="SequenceFlow_0g6bfz6_di" bpmnElement="SequenceFlow_0g6bfz6">
+        <di:waypoint x="330" y="100" />
+        <di:waypoint x="381" y="100" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0yqo6md_di" bpmnElement="SequenceFlow_0yqo6md">
-        <di:waypoint x="566" y="203" />
-        <di:waypoint x="651" y="203" />
+      <bpmndi:BPMNEdge id="SequenceFlow_1bz7zx2_di" bpmnElement="SequenceFlow_1bz7zx2">
+        <di:waypoint x="481" y="100" />
+        <di:waypoint x="516" y="100" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0ra8hjs_di" bpmnElement="SequenceFlow_0ra8hjs">
-        <di:waypoint x="911" y="203" />
-        <di:waypoint x="1020" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0tu3w40_di" bpmnElement="SequenceFlow_0tu3w40">
-        <di:waypoint x="1356" y="203" />
-        <di:waypoint x="1393" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_15xr8tf_di" bpmnElement="IntermediateThrowEvent_0d8ns1p">
-        <dc:Bounds x="530" y="259" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_16kp9qx_di" bpmnElement="IntermediateThrowEvent_1pwlz5c">
-        <dc:Bounds x="530" y="185" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_1k8uu2s_di" bpmnElement="IntermediateThrowEvent_09eb9xc">
-        <dc:Bounds x="875" y="185" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_0l6h0iz_di" bpmnElement="IntermediateThrowEvent_1rxh4ec">
-        <dc:Bounds x="1052" y="96" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_14dhjgv_di" bpmnElement="IntermediateThrowEvent_1u3snd0">
-        <dc:Bounds x="1320" y="185" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="Task_19kl2q9_di" bpmnElement="Task_1rt5mbt">
-        <dc:Bounds x="377" y="-14" width="100" height="80" />
+      <bpmndi:BPMNShape id="StartEvent_1t6nxib_di" bpmnElement="StartEvent_1t6nxib">
+        <dc:Bounds x="56" y="83" width="36" height="36" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="Task_1walrai_di" bpmnElement="Task_1cfglzc">
-        <dc:Bounds x="843" y="-14" width="100" height="80" />
+      <bpmndi:BPMNShape id="ServiceTask_1gaqtzh_di" bpmnElement="service-report">
+        <dc:Bounds x="619" y="61" width="100" height="80" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="Task_0pek2di_di" bpmnElement="Task_1n6iu8h">
-        <dc:Bounds x="1020" y="-14" width="100" height="80" />
+      <bpmndi:BPMNShape id="UserTask_0iooc2g_di" bpmnElement="Task_1e7059p">
+        <dc:Bounds x="766" y="61" width="100" height="80" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="ScriptTask_1ui5yru_di" bpmnElement="Task_14xmt44">
-        <dc:Bounds x="203" y="163" width="100" height="80" />
+      <bpmndi:BPMNShape id="EndEvent_0bmuv13_di" bpmnElement="EndEvent_0bmuv13">
+        <dc:Bounds x="1024" y="83" width="36" height="36" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_11xavl5_di" bpmnElement="SequenceFlow_11xavl5">
-        <di:waypoint x="173" y="203" />
-        <di:waypoint x="203" y="203" />
+      <bpmndi:BPMNEdge id="SequenceFlow_14nqu0e_di" bpmnElement="SequenceFlow_14nqu0e">
+        <di:waypoint x="719" y="101" />
+        <di:waypoint x="766" y="101" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0f33zny_di" bpmnElement="SequenceFlow_0f33zny">
-        <di:waypoint x="303" y="203" />
-        <di:waypoint x="323" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0ez6lnk_di" bpmnElement="SequenceFlow_0ez6lnk">
-        <di:waypoint x="1120" y="203" />
-        <di:waypoint x="1186" y="203" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0h1h7gs_di" bpmnElement="SequenceFlow_0h1h7gs">
-        <di:waypoint x="477" y="26" />
-        <di:waypoint x="530" y="26" />
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="SequenceFlow_0bzf3di_di" bpmnElement="SequenceFlow_0bzf3di">
-        <di:waypoint x="726" y="26" />
-        <di:waypoint x="768" y="26" />
+      <bpmndi:BPMNEdge id="SequenceFlow_0jebdb9_di" bpmnElement="SequenceFlow_0jebdb9">
+        <di:waypoint x="866" y="101" />
+        <di:waypoint x="1024" y="101" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="ScriptTask_1wgv8e0_di" bpmnElement="Task_1slpwnp">
-        <dc:Bounds x="626" y="-14" width="100" height="80" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="TextAnnotation_08dib8t_di" bpmnElement="TextAnnotation_08dib8t">
-        <dc:Bounds x="908" y="112" width="100" height="40" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="Association_1bcr79m_di" bpmnElement="Association_1bcr79m">
-        <di:waypoint x="918" y="66" />
-        <di:waypoint x="946" y="112" />
+      <bpmndi:BPMNEdge id="SequenceFlow_15dip2q_di" bpmnElement="SequenceFlow_15dip2q">
+        <di:waypoint x="92" y="101" />
+        <di:waypoint x="135" y="101" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="ScriptTask_0h8sco9_di" bpmnElement="Task_1tosag4">
-        <dc:Bounds x="1020" y="163" width="100" height="80" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_1h9izac_di" bpmnElement="IntermediateCatchEvent_1h9izac">
-        <dc:Bounds x="530" y="8" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_07bm292_di" bpmnElement="SequenceFlow_07bm292">
-        <di:waypoint x="566" y="26" />
-        <di:waypoint x="626" y="26" />
+      <bpmndi:BPMNEdge id="SequenceFlow_1j2y6tv_di" bpmnElement="SequenceFlow_1j2y6tv">
+        <di:waypoint x="578" y="101" />
+        <di:waypoint x="619" y="101" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNShape id="ScriptTask_1yoc45k_di" bpmnElement="Task_1kanz3j">
-        <dc:Bounds x="377" y="163" width="100" height="80" />
+      <bpmndi:BPMNShape id="EndEvent_07g109m_di" bpmnElement="EndEvent_parsing_term">
+        <dc:Bounds x="516" y="82" width="36" height="36" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="IntermediateCatchEvent_050nd2m_di" bpmnElement="IntermediateCatchEvent_050nd2m">
-        <dc:Bounds x="323" y="185" width="36" height="36" />
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge id="SequenceFlow_11ge7w6_di" bpmnElement="SequenceFlow_11ge7w6">
-        <di:waypoint x="359" y="203" />
-        <di:waypoint x="377" y="203" />
-      </bpmndi:BPMNEdge>
     </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>
 </bpmn:definitions>
index 8e7d00e3bf421251291167e2958e7943d4a57cc5..58b4096ef8e8f8a1c703b6f3c99c67767641b5c9 100644 (file)
@@ -30,7 +30,7 @@ services:
     build: ./service-reporting
     container_name: reporting
     ports:
-      - "8083:80"
+      - "8083:8083"
     restart: always
 
   service-twitter:
index 90c70999077d7fc0619484d8490a8a9110134011..eeb2084993258fa520de8dffb7bc2c0a5c984b28 100755 (executable)
@@ -5,14 +5,21 @@ cd /vagrant
 ###############################
 # Get Docker + Docker-Compose #
 ###############################
-apt-get update
-apt-get install -y -q apt-transport-https vim
+# Check for updates only once per boot.
+dpkg-query -W docker-ce docker-compose &>/dev/null || rm -f /tmp/apt_run_this_boot
 
-wget -q "https://download.docker.com/linux/debian/gpg" -O - | apt-key add
-echo "deb https://download.docker.com/linux/debian stretch stable" > /etc/apt/sources.list.d/docker_com.list
+if [ ! -f /tmp/apt_run_this_boot ]; then
+    apt-get update
+    apt-get install -y -q apt-transport-https vim
 
-apt-get update
-apt-get install -y -q docker-ce docker-compose
+    wget -q "https://download.docker.com/linux/debian/gpg" -O - | apt-key add
+    echo "deb https://download.docker.com/linux/debian stretch stable" > /etc/apt/sources.list.d/docker_com.list
+
+    apt-get update
+    apt-get install -y -q docker-ce docker-compose
+
+    date -Isec > /tmp/apt_run_this_boot
+fi
 
 #############################
 # remove running containers #
index 976abd116501890f71027063d4323dba04e06127..ad239ec90879ddb796656346ce9b6524ae2908eb 100644 (file)
@@ -1,13 +1,11 @@
-FROM   python:3
+FROM   python:3.7-slim
 LABEL  maintainer="Sebastian Steiner"
 
-RUN            pip install flask
-RUN            pip install flask_restful
-RUN            pip install indicoio
-
 WORKDIR        /app
 COPY   . /app/
 
+RUN            pip install -r requirements.txt
+
 RUN            chmod a+x *.py
 
-CMD            ["python3", "./sentiment_analysis.py"]
+CMD            ["python3.7", "./sentiment_analysis.py"]
index 59f472fe374df1d18402bd5c7aa85a9349462792..fb414facdeaefcf0091aa27e6bf9e16b4cbbaf37 100644 (file)
@@ -2,8 +2,43 @@
 
 
 ## API
+Provides a REST-API to get the average sentiment of a given list of texts
+The service uses the the sentiment analysis service indico.
 
-You can post a json string to the REST API on http://localhost:8081/.
-The json string needs to contain name-value key 'text'.
+`GET`: `/` Displays the expected input for a POST
 
-The service uses the online sentiment analysis tool indico to get the sentiment for each text, calculates the average sentiment value and returns it.
+`POST`: `/` Calculates the average sentiment for a given list of texts
+- param: Term[] as Content-Tye: `application/json`
+example body of request:
+```json
+[
+       {
+               'text': 'text 1',
+       },
+       {       
+               'text': 'text 2',
+       },
+       {       
+               'text': 'text 3',
+       }
+]
+```
+JSON string may contain more value-key pairs than 'text', but 'text' is needed for the sentiment analysis. Everything else will be ignored.
+
+## API errors
+- `409`: input string does not meet specifications
+- `503`: indico sentiment analysis not available
+
+## run with docker
+- `docker build -t sentiment_analysis .`
+- `docker run -d -p YOUR_PORT:8081 sentiment_analysis`
+
+## run local
+### requirements
+- python 3.7
+- pip
+  -> indicoio
+  -> flask
+  -> flask_restfut
+### commands
+- `python3.7 sentiment_analysis.py`
diff --git a/service-analysis/requirements.txt b/service-analysis/requirements.txt
new file mode 100644 (file)
index 0000000..206be2f
--- /dev/null
@@ -0,0 +1,3 @@
+flask
+flask_restful
+indicoio
index d9836af06928d75fa545db42a613e99748a288e6..933ce9c3114804a85a6f64487bfafd4cf5b703cc 100644 (file)
@@ -2,30 +2,39 @@
 import json
 import indicoio
 
-from flask import Flask, request
-from flask_restful import Resource, Api
+from flask import Flask, request, jsonify
+from flask_restful import Resource, Api, output_json, abort
 
 app = Flask(__name__)
 api = Api(app)
 
 indicoio.config.api_key = '525f16078717a430f9dac17cdc9dbaa3'
 
+input_error_409 = 'Input must be a JSON string. JSON objects must contain contain key text.'
+service_error_503 = 'The sentiment analysis service is currently unavailable.'
+
 class Sentiment_Analysis(Resource):
        def get(self):
-               return "correct usage: curl -H 'content-type: application/json' -X POST http://localhost:8081 -d '$your JSON here$'"
+               return "POST a JSON string as content-type: application/json. JSON objects must contain key 'text'."
                
        def post(self):
-               tweets = request.json
+               if not request.json:
+                       return abort(409, message=input_error_409)
+               texts = request.json
+               text_array = []
                value = 0
-               for tweet in tweets:
-                       #possible performance improvement: batch sentiment: indicoio.sentiment($list_of_texts$)
-                       sentiment_value = indicoio.sentiment(tweet['text'])
-                       value += sentiment_value
-                       #returns number between 0 and 1. 
-                       #it is a probability representing the likelihood that the analyzed text is positive or negative
-                       #values > 0.5 indicate positive sentiment
-                       #values < 0.5 indicate negative sentiment
-               return value/len(tweets)
+               for text in texts:
+                       if 'text' not in text:
+                               return abort(409, message=input_error_409)
+                       text_array.append(text['text'])
+               try:
+                       for sentiment_value in indicoio.sentiment(text_array):
+                               value += sentiment_value
+               except:
+                       return make_error(503, message=service_error_503)
+               sentiment = value/len(text_array)
+               data = {'sentiment': sentiment}
+               return output_json(json.dumps(data), 200)
                                
 api.add_resource(Sentiment_Analysis, '/')
 
diff --git a/service-fallback/README.md b/service-fallback/README.md
new file mode 100644 (file)
index 0000000..d3a44f8
--- /dev/null
@@ -0,0 +1,3 @@
+# Fallback service
+
+tbd
\ No newline at end of file
index a47cbb1281898a0aca5a8390b6b496ac8e8bea86..726b8978f5bcd4a695e7f3c98448c51fac6630d7 100644 (file)
@@ -2,6 +2,7 @@
 using DinkToPdf.Contracts;
 using Microsoft.AspNetCore.Mvc;
 using System.IO;
+using System;
 using PdfService.Utility;
 using PdfService.Models;
 namespace PdfService.Controllers
@@ -34,11 +35,11 @@ namespace PdfService.Controllers
             var objectSettings = new ObjectSettings
             {
                 PagesCount = true,
-                HtmlContent = TemplateGenerator.GetHTMLString(new Tweet[]{
-                    new Tweet("Test", "Good"),
-                    new Tweet("Test2", "Bad"),
-                    new Tweet("Another Tweet", "Bad"),
-                    new Tweet("Another bad Tweet", "Bad")
+                HtmlContent = TemplateGenerator.GetHTMLString(new Term[]{
+                    new Term("Good Term", 0.9),
+                    new Term("Term 1", 0.5),
+                    new Term("Another Term", 0.3),
+                    new Term("Another bad Tweet", 0.1)
                     }),
                 WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet =  Path.Combine(Directory.GetCurrentDirectory(), "assets", "bulma.min.css") }
             };
@@ -56,7 +57,7 @@ namespace PdfService.Controllers
         // GET /generatePDF
         [Route("generatePDF")]
         [HttpPost]
-        public ActionResult GeneratePdf([FromBody] Tweet[] tweets)
+        public ActionResult GeneratePdf([FromBody] Term[] terms)
         {
             // check parameters
             if (!ModelState.IsValid)
@@ -64,9 +65,9 @@ namespace PdfService.Controllers
                 return BadRequest(ModelState);
             }
 
-            // check for emptyString
-            foreach(Tweet tweet in tweets) {
-                if(tweet.Name.Length<1 || tweet.Sentiment.Length<1){
+            // check parameter if they have correct form
+            foreach(Term term in terms) {
+                if(term.Name.Length<1 || term.Sentiment<0.0 || term.Sentiment>1.0){
                     return BadRequest(ModelState);
                 }
             }
@@ -83,7 +84,7 @@ namespace PdfService.Controllers
             var objectSettings = new ObjectSettings
             {
                 PagesCount = true,
-                HtmlContent = TemplateGenerator.GetHTMLString(tweets),
+                HtmlContent = TemplateGenerator.GetHTMLString(terms),
                 WebSettings = { DefaultEncoding = "utf-8", UserStyleSheet =  Path.Combine(Directory.GetCurrentDirectory(), "assets", "bulma.min.css") }
             };
  
@@ -92,9 +93,12 @@ namespace PdfService.Controllers
                 GlobalSettings = globalSettings,
                 Objects = { objectSettings }
             };
+            
             var file = _converter.Convert(pdf);
-            return File(file, "application/pdf");
+            if(Request.Headers["Accept"] == "application/base64")
+                return Content(Convert.ToBase64String(file));
+            else
+                return File(file, "application/pdf");
         }
 
     }
index c372a86268a66c353f174f71bb42fd6f4f981a65..080fad49232e75fcdb1b14d5c2f59a51994d836b 100644 (file)
@@ -1,6 +1,7 @@
 FROM microsoft/dotnet:2.2.100-preview3-sdk AS build-env
 WORKDIR /app
 
+
 # Copy csproj and restore as distinct layers
 COPY *.csproj ./
 RUN dotnet restore
@@ -14,6 +15,10 @@ FROM microsoft/dotnet:2.2.0-preview3-aspnetcore-runtime
 WORKDIR /app
 COPY --from=build-env /app/out .
 
+# set ports
+EXPOSE 8083            
+ENV ASPNETCORE_URLS=http://+:8083
+
 # copy pdf library
 # COPY libwkhtmltox.* ./
 # install additional libaries
@@ -29,7 +34,6 @@ RUN apt-get update \
         --location \
         https://github.com/rdvojmoc/DinkToPdf/raw/v1.0.8/v0.12.4/64%20bit/libwkhtmltox.so
 
-               
 # copy assets
 COPY assets/*.css ./assets/            
 ENTRYPOINT ["dotnet", "PdfService.dll"]
\ No newline at end of file
similarity index 69%
rename from service-reporting/Models/Tweet.cs
rename to service-reporting/Models/Term.cs
index 6889d1453297de3c5d89479e4374381af0c4312d..bb6289036aa49bfa77c966bd8bc63d8120de2461 100644 (file)
@@ -3,13 +3,13 @@ namespace PdfService.Models
 {
     // represents the data got from sentiment analysis.
     // TODO: adapt to real data from sentiment analysis
-    public class Tweet
+    public class Term
     {
-        public Tweet(string Name, string Sentiment) {
+        public Term(string Name, double Sentiment) {
             this.Name = Name;
             this.Sentiment = Sentiment;
         }
         public string Name { get; set; }
-        public string Sentiment { get; set; }
+        public double Sentiment { get; set; }
     }
 }
\ No newline at end of file
diff --git a/service-reporting/README.md b/service-reporting/README.md
new file mode 100644 (file)
index 0000000..49640ff
--- /dev/null
@@ -0,0 +1,44 @@
+# PDF Service
+
+Provides a REST-API to generate PDF reports for terms and their sentiment analysis results. The service uses an .NET Core wrapper for the wkhtmltopdf library to generate pdf out of html code.
+
+`GET`: `/` Shows a demo page  
+- param: none
+- return: pdf file
+
+`POST`: `/generatePDF/` Generates a pdf report for the given terms
+- Header: `Content-Type`: `application/json`  
+(optional) `Accept`: `application/base64` if the pdf should be in base64 instead of `application/pdf`  
+- Body of request: Term[] e.g.
+```json
+[
+  {
+    "name": "term 1",
+    "sentiment": 0.1
+  },
+  {
+    "name": "term 2",
+    "sentiment": 0.3
+  },
+  {
+    "name": "term 3",
+    "sentiment": 0.7
+  }
+]
+``` 
+- return: pdf file
+
+### Term model
+Term(string `Name`, double `Sentiment`)
+
+## run with docker
+
+- `docker build -t service-reporting .`    
+- `docker run -p YOUR_PORT:8083 service-reporting:latest`
+
+## run local
+### requirements
+- .net core 2.2
+- download [libwkhtml library](https://github.com/rdvojmoc/DinkToPdf/tree/master/v0.12.4) depending on your operating system and put it into `service-reporting` root folder
+### commands
+- `dotnet run`
\ No newline at end of file
index fa2ff9f0660fc6bab52f4694ce7ae3fec446ca37..5a7127399f646661aaf375b1ebddf2138f52dde9 100644 (file)
@@ -5,7 +5,7 @@ namespace PdfService.Utility
 {
     public static class TemplateGenerator
     {
-        public static string GetHTMLString(Tweet[] tweets)
+        public static string GetHTMLString(Term[] terms)
         {
             var sb = new StringBuilder();
             sb.Append(@"
@@ -25,21 +25,28 @@ namespace PdfService.Utility
                                     </div>
                                 </div>
                                 </section>
-                                <div class='notification'>
-                                    Generated report only contains mocked data. Will be changed later.
-                                </div> 
-                                <div class='container' style='margin-left:3em'>
+                                <div class='container' style='margin-left:3em; margin-right:3em'>
 
                                 ");
  
-            foreach (var tweet in tweets)
+            foreach (Term term in terms)
             {
+                string color = "";
+                if(term.Sentiment < 0.33)
+                    color = "is-danger";
+                else if(term.Sentiment < 0.66)
+                    color = "is-warning";
+                else 
+                    color = "is-success";
+
                 sb.AppendFormat(@"<div class='container' style='margin-top:3em;'>  
                             <p class='title is-5'>{0}</p>
                     <div class='content'>
-                    {1}
+                    Sentiment Analysis Result:
+                        <div class='column'><progress class='progress {2}' value='{1}' max='100'>{1}%</progress></div>
+                    
                     </div>
-                </div>", tweet.Name, tweet.Sentiment);
+                </div>", term.Name, System.Math.Ceiling(term.Sentiment*100), color);
             }
  
             sb.Append(@" </div>
diff --git a/service-reporting/readme.md b/service-reporting/readme.md
deleted file mode 100644 (file)
index 4e70534..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# PDF Service
-
-Provides an API interface to generate PDF reports for tweets and their sentiment analysis results. The service uses an .NET Core wrapper for the wkhtmltopdf library to generate pdf out of html code.
-
-`GET`: `/` Shows a demo page  
-- param: none
-- return: pdf file
-
-`POST`: `/generatePDF/` Generates a pdf report for the given tweets
-- param: Tweet[] as Content-Type: `application/json`  
-- return: pdf file
-
-### Tweet model
-Tweet(string `Name`, string `Sentiment`)
-
-## run with docker
-
-- `docker build -t service-reporting .`    
-- `docker run -p YOUR_PORT:80 service-reporting:latest`
-
-## run local
-### requirements
-- .net core 2.2
-- download [libwkhtml library](https://github.com/rdvojmoc/DinkToPdf/tree/master/v0.12.4) depending on your operating system and put it into `service-reporting` root folder
-### commands
-- `dotnet run`
\ No newline at end of file
index 3196f98c9150c35507f1245f27371ebde6cd8c8e..8e2ae24fb11069a356b57a5ccb5cfb1aaa9200fd 100644 (file)
@@ -1,5 +1,7 @@
 package at.aic18.g6t4.servicetwitter.configuration;
 
+import lombok.Getter;
+import lombok.Setter;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.validation.annotation.Validated;
 
@@ -8,23 +10,15 @@ import javax.validation.constraints.NotEmpty;
 
 @ConfigurationProperties(prefix = "twitter4j")
 @Validated
+@Getter
+@Setter
 public class Twitter4jProperties {
 
     @Valid private final OAuth oauth = new OAuth();
     private boolean debug = false;
 
-    public OAuth getOauth() {
-        return oauth;
-    }
-
-    public boolean isDebug() {
-        return debug;
-    }
-
-    public void setDebug(boolean debug) {
-        this.debug = debug;
-    }
-
+    @Getter
+    @Setter
     public static class OAuth {
 
         @NotEmpty private String consumerKey;
@@ -32,38 +26,6 @@ public class Twitter4jProperties {
         @NotEmpty private String accessToken;
         @NotEmpty private String accessTokenSecret;
 
-        public String getConsumerKey() {
-            return consumerKey;
-        }
-
-        public void setConsumerKey(String consumerKey) {
-            this.consumerKey = consumerKey;
-        }
-
-        public String getConsumerSecret() {
-            return consumerSecret;
-        }
-
-        public void setConsumerSecret(String consumerSecret) {
-            this.consumerSecret = consumerSecret;
-        }
-
-        public String getAccessToken() {
-            return accessToken;
-        }
-
-        public void setAccessToken(String accessToken) {
-            this.accessToken = accessToken;
-        }
-
-        public String getAccessTokenSecret() {
-            return accessTokenSecret;
-        }
-
-        public void setAccessTokenSecret(String accessTokenSecret) {
-            this.accessTokenSecret = accessTokenSecret;
-        }
-
     }
 
 }
diff --git a/service-twitter/src/main/java/at/aic18/g6t4/servicetwitter/exception/SearchException.java b/service-twitter/src/main/java/at/aic18/g6t4/servicetwitter/exception/SearchException.java
deleted file mode 100644 (file)
index b69d7ab..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-package at.aic18.g6t4.servicetwitter.exception;
-
-public class SearchException extends Exception {
-
-    public SearchException() {
-        super();
-    }
-
-    public SearchException(String message) {
-        super(message);
-    }
-
-    public SearchException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public SearchException(Throwable cause) {
-        super(cause);
-    }
-
-}
index d676835fd0b8570f1bb4ca360d0556f323b1ac3e..f10c2dc1ff6eeae1566699be083c924cdea7f472 100644 (file)
@@ -18,7 +18,8 @@ public class TwitterService {
     }
 
     public List<Tweet> searchTweets(String queryString) throws TwitterException {
-        Query query = new Query(queryString);
+        String excludeRetweets = " exclude:retweets";
+        Query query = new Query(queryString + excludeRetweets);
         query.count(10);
         QueryResult queryResult = twitter.search(query);
         List<Status> statusList = queryResult.getTweets();
index 6941e324e4b1a2b1e077af487ac39a5167882ae0..ac49b95b5459a867fcc931041d4fd1564d1e2342 100644 (file)
@@ -1,4 +1,4 @@
-debug: true
+debug: false
 
 spring:
   jackson:
@@ -8,7 +8,7 @@ server:
   port: 8084
 
 twitter4j:
-  debug: true
+  debug: false
   oauth:
     consumer-key: "DnmBShVqvJ2xfnRjkAWtq644Z"
     consumer-secret: "YbP2oAdU9IyuYMAUxbyJn1NNKZ91jnOz1CpNKMSCjCR0Pu8JlJ"