├── .gitignore ├── .gitmodules ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── env └── default-rest.env ├── logstash └── logstash.conf └── usecases ├── bbc ├── annotations.js ├── fulltext.js ├── map.js ├── nominatim.js └── rss.js ├── slackbot ├── search.js ├── slack-reply.js └── slack-source.js └── twitter ├── render.js ├── sentiment.js └── twitter.js /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | rest.env 3 | docker-compose-deploy.yml 4 | Makefile 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "exynize-rest"] 2 | path = exynize-rest 3 | url = git@github.com:Exynize/exynize-rest.git 4 | [submodule "exynize-ui"] 5 | path = exynize-ui 6 | url = git@github.com:Exynize/exynize-ui.git 7 | [submodule "exynize-runner"] 8 | path = exynize-runner 9 | url = git@github.com:Exynize/exynize-runner.git 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Exynize platform, including all of its components and all of its derivates, is available under different licensing 4 | options designed to accommodate the needs of various users. 5 | 6 | ## Community license (GPL-3.0) 7 | 8 | Exynize platform licensed under the GNU General Public License v3 (GPL-3.0) is appropriate for the development 9 | of applications based on Exynize platform provided you can comply with the terms and conditions 10 | of the GNU General Public License v3 (GPL-3.0). 11 | 12 | ## Commercial license 13 | 14 | Exynize platform licensed under Commercial license is appropriate for development of proprietary/commercial 15 | software where you do not want to share any source code with third parties or otherwise cannot comply with the terms 16 | of the GPL-3.0. 17 | To obtain the commercial license please contact team@exynize.com 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exynize platform 2 | 3 | > Exynize platform - easy creation of complex processing pipelines for your data. 4 | 5 | ## About Exynize platform 6 | 7 | Exynize platform aims to simplifying the workflow and allow rapid creation of data processing pipelines and visualisations. 8 | Current version of the platform allows: 9 | - constructing pipelines right in your browsers with very little effort, 10 | - writing processing component as if you was dealing with a single data item, 11 | - re-using existing processing modules in new pipelines, 12 | - creating real-time processing and visualisation without thinking about doing real-time at all, 13 | - spending time on doing actual work, not fiddling with scaffolding. 14 | 15 | More info on the platform as well as some demoes of its capabilities can be found in the following article on Medium 16 | > [Building data processing and visualisation pipelines in the browser with Exynize](https://medium.com/the-data-experience/building-data-processing-and-visualisation-pipelines-in-the-browser-with-exynize-372ab15e848c#.cq73g7k7q) 17 | 18 | ## Getting started 19 | 20 | This is a deployment repository for Exynize platform. 21 | It contains docker-compose file that can be used to easily setup your own copy of exynize platform. 22 | 23 | If you are interested in separate platform components, then can be found in other repositories: 24 | 25 | - [Exynize UI](https://github.com/Exynize/exynize-ui) 26 | - [Exynize REST](https://github.com/Exynize/exynize-rest) 27 | - [Exynize Runner](https://github.com/Exynize/exynize-runner) 28 | 29 | ### Requirements 30 | 31 | For Exynize platform to function properly, you'll need to have following things installed: 32 | 33 | - Docker v1.10 or later 34 | - Docker-compose 1.6 or later 35 | 36 | ### Installation 37 | 38 | 1. Clone the repository and cd into new folder: `git clone git@github.com:Exynize/exynize-platform.git && cd exynize-platform` 39 | 2. Execute `git submodule init && git submodule update` to get latest sources for platform components 40 | 3. Execute `docker-compose up` to start Exynize platform 41 | 4. Navigate to `http://your.docker.address` using browser to see the platform UI 42 | 43 | ### Configuration 44 | 45 | To apply your custom config you need to do the following steps: 46 | 47 | 1. Copy `./env/default-rest.env` and edit variables to your liking 48 | 2. Add new entry with your file to `rest.env_file` section in `docker-compose.yml`, it should look like this: 49 | ```yml 50 | rest: 51 | build: exynize-rest 52 | depends_on: 53 | - rdb 54 | - rabbit 55 | links: 56 | - rdb 57 | - rabbit 58 | volumes: 59 | - ./data/static:/opt/app/src/static 60 | environment: 61 | - NODE_ENV=production 62 | env_file: 63 | - ./env/default-rest.env 64 | - ./my-rest.env 65 | ``` 66 | 3. Restart the containers 67 | 68 | ### RethinkDB admin UI 69 | 70 | By default you can access RethinkDB UI at `http://your.docker.address:8080`. 71 | This can be disabled by commenting out `rdb.ports` entry in `docker-compose.yml`. 72 | 73 | ### RabbitMQ admin UI 74 | 75 | By default you can access RabbitMQ admin UI at `http://your.docker.address:8081`. 76 | This can be disabled by commenting out `rabbit.ports` entry in `docker-compose.yml`. 77 | 78 | ## License 79 | 80 | Dual licensed under GPL-3.0 and commercial license. 81 | See LICENSE.md file for more details. 82 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | # Exynize UI 4 | ui: 5 | build: exynize-ui 6 | ports: 7 | - "80:3000" 8 | environment: 9 | - NODE_ENV=production 10 | logging: 11 | driver: gelf 12 | options: 13 | gelf-address: udp://localhost:12201 14 | tag: "{{.ImageName}}/{{.Name}}/{{.ID}}" 15 | # Exynize REST 16 | rest: 17 | build: exynize-rest 18 | depends_on: 19 | - logstash 20 | - rdb 21 | - rabbit 22 | volumes: 23 | - ./data/static:/opt/app/src/static 24 | environment: 25 | - NODE_ENV=production 26 | env_file: 27 | - ./env/default-rest.env 28 | logging: 29 | driver: gelf 30 | options: 31 | gelf-address: udp://localhost:12201 32 | tag: "{{.ImageName}}/{{.Name}}/{{.ID}}" 33 | # Exynize runner 34 | runner: 35 | build: exynize-runner 36 | depends_on: 37 | - logstash 38 | - rabbit 39 | environment: 40 | - NODE_ENV=production 41 | - RABBITMQ_NODENAME=rabbit 42 | logging: 43 | driver: gelf 44 | options: 45 | gelf-address: udp://localhost:12201 46 | tag: "{{.ImageName}}/{{.Name}}/{{.ID}}" 47 | 48 | # database 49 | rdb: 50 | image: rethinkdb 51 | volumes: 52 | - ./data/db:/data 53 | ports: 54 | - "8080:8080" 55 | # message bus 56 | rabbit: 57 | image: rabbitmq:management 58 | ports: 59 | - "8081:15672" 60 | 61 | # ELK logging stack 62 | elasticsearch: 63 | image: elasticsearch 64 | command: elasticsearch -Des.network.host=0.0.0.0 65 | logstash: 66 | image: logstash 67 | command: logstash -f /etc/logstash/conf.d/logstash.conf 68 | volumes: 69 | - ./logstash:/etc/logstash/conf.d 70 | ports: 71 | - "12201:12201" 72 | - "12201:12201/udp" 73 | kibana: 74 | image: kibana 75 | ports: 76 | - "5601:5601" 77 | environment: 78 | - ELASTICSEARCH_URL=http://elasticsearch:9200 79 | -------------------------------------------------------------------------------- /env/default-rest.env: -------------------------------------------------------------------------------- 1 | EXYNIZE_DB_HOST=rdb 2 | # auth password salt 3 | EXYNIZE_AUTH_SALT=set-your-salt-here 4 | # JWT secret 5 | EXYNIZE_JWT_SECRET=set-you-secret-here 6 | # hostname 7 | EXYNIZE_HOST=docker.dev 8 | # rabbit node 9 | RABBITMQ_NODENAME=rabbit 10 | # mail config 11 | EXYNIZE_MAIL_VALIDATION=0 12 | EXYNIZE_MAIL_HOST=mail.server.net 13 | EXYNIZE_MAIL_PORT=465 14 | EXYNIZE_MAIL_SECURE=true 15 | EXYNIZE_MAIL_USER=bot@server.com 16 | EXYNIZE_MAIL_PASS=password 17 | -------------------------------------------------------------------------------- /logstash/logstash.conf: -------------------------------------------------------------------------------- 1 | input { 2 | gelf {} 3 | } 4 | 5 | filter { 6 | if [short_message] =~ "error" { 7 | mutate { replace => { type => "error" } } 8 | } else if [short_message] =~ "info" { 9 | mutate { replace => { type => "info" } } 10 | } else { 11 | mutate { replace => { type => "other" } } 12 | } 13 | } 14 | 15 | output { 16 | elasticsearch { 17 | hosts => ['elasticsearch'] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /usecases/bbc/annotations.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import request from 'request'; 3 | 4 | // FOX NLP tool API url 5 | const foxUrl = 'http://fox-demo.aksw.org/call/ner/entities'; 6 | 7 | export default (data) => Rx.Observable.create(obs => { 8 | const json = { 9 | input: data.text, 10 | type: 'text', 11 | task: 'ner', 12 | output: 'JSON-LD', 13 | }; 14 | request({ 15 | method: 'POST', 16 | url: foxUrl, 17 | headers: { 18 | 'Content-Type': 'application/json', 19 | }, 20 | body: JSON.stringify(json), 21 | }, (err, res, body) => { 22 | if (err) { 23 | obs.onError(err); 24 | return; 25 | } 26 | 27 | if (res && res.statusCode !== 200) { 28 | obs.onError(`Error code: ${res.statusCode}, ${res.statusMessage}`); 29 | return; 30 | } 31 | 32 | const result = JSON.parse(body); 33 | const entries = result['@graph'] ? result['@graph'] : []; 34 | const annotations = entries.map(it => ({ 35 | types: it['@type'] ? it['@type'] 36 | .map(t => t.indexOf(':') !== -1 ? t.split(':')[1] : t) 37 | .map(t => t.toLowerCase()) 38 | .map(_.capitalize) 39 | .filter(t => t !== 'Annotation') : [], 40 | name: it['ann:body'], 41 | beginIndex: typeof it.beginIndex === 'string' ? [it.beginIndex] : it.beginIndex, 42 | endIndex: typeof it.endIndex === 'string' ? [it.endIndex] : it.endIndex, 43 | })); 44 | data.annotations = annotations; 45 | obs.onNext(data); 46 | obs.onCompleted(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /usecases/bbc/fulltext.js: -------------------------------------------------------------------------------- 1 | import request from 'superagent'; 2 | import cheerio from 'cheerio'; 3 | 4 | const cleanText = text => text 5 | .replace(/[\n\r\t]+/g, ' ') 6 | .replace(/\s+/g, ' ') 7 | .replace(/(\w)\.([A-Z0-9_])/g, '$1. $2'); 8 | 9 | const cleanHtml = html => html 10 | .replace(/[\n\r\t]+/g, ' ') 11 | .replace(//g, ' ') 12 | .replace(//g, ' ') 13 | .replace(/\s+/g, ' '); 14 | 15 | export default (data) => { 16 | return Rx.Observable.create(obs => { 17 | const {link} = data; 18 | request 19 | .get(link) 20 | .end((err, res) => { 21 | if (err) { 22 | return obs.onError(err); 23 | } 24 | 25 | const $ = cheerio.load(res.text); 26 | $('script').remove(); 27 | $('object').remove(); 28 | // try to extract only article text 29 | let obj = $('.story-body__inner'); 30 | if (!obj || !obj.length) { 31 | obj = $('body'); 32 | } 33 | // cleanup 34 | $('figure', obj).remove(); 35 | // get html and text 36 | const resHtml = cleanHtml(obj.html()); // BBC news selector 37 | const resText = cleanText(obj.text()); 38 | 39 | // assign to data 40 | data.text = resText; 41 | data.html = resHtml; 42 | 43 | // send 44 | obs.onNext(data); 45 | obs.onCompleted(); 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /usecases/bbc/map.js: -------------------------------------------------------------------------------- 1 | import L from 'leaflet'; 2 | import 'leaflet/dist/leaflet.css'; 3 | 4 | const styleGray = '#cccccc'; 5 | const styleGreen = '#5cb85c'; 6 | const styleRed = '#d9534f'; 7 | 8 | const mapConfig = { 9 | minZoom: 2, 10 | maxZoom: 20, 11 | layers: [ 12 | L.tileLayer( 13 | 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', 14 | { 15 | attribution: '© OpenStreetMap' + 16 | ' contributors, CC-BY-SA', 17 | } 18 | ) 19 | ], 20 | attributionControl: false, 21 | }; 22 | 23 | const popup = (it) => ` 24 | ${it.title} 25 |
{tweet.text}
19 |