├── .eslintignore ├── doc ├── source │ ├── images │ │ ├── bot.png │ │ ├── deploy.png │ │ ├── rss-1.png │ │ ├── rss-2.png │ │ ├── env_vars.png │ │ ├── rss_feed.png │ │ ├── slack-0.png │ │ ├── slack-00.png │ │ ├── slack-1.png │ │ ├── slack-2.png │ │ ├── slack-3.png │ │ ├── trouble-1.png │ │ ├── trouble-2.png │ │ ├── architecture.png │ │ ├── edit-configmap.png │ │ ├── architecture_old.png │ │ ├── disco-get-creds.png │ │ ├── edit-deployment.png │ │ ├── sample-output-query.png │ │ ├── toolchain-pipeline.png │ │ ├── edit-deployment-yaml.png │ │ └── sample-output-trending.png │ ├── cf.md │ ├── local.md │ └── openshift.md ├── news-feed-trending-topics.md ├── slack-bot-integration.md └── promises-over-callback.md ├── public ├── images │ └── feed-icon.png ├── js │ ├── search │ │ └── bundle.js │ └── trending │ │ └── bundle.js └── css │ └── application.css ├── .travis.yml ├── ACKNOWLEDGEMENTS.md ├── env.sample ├── manifest.yml ├── .eslintrc.yml ├── CONTRIBUTING.md ├── server ├── watson-discovery-service.js ├── query-builder.test.js ├── express.js ├── query-builder.js └── index.js ├── app.js ├── .gitignore ├── src ├── shared │ ├── utils.js │ └── Query │ │ └── index.js ├── search │ ├── Briefing │ │ └── index.js │ ├── index.js │ ├── Sentiment │ │ └── index.js │ ├── TopStories │ │ └── index.js │ ├── layouts │ │ └── default.js │ ├── Search │ │ └── index.js │ └── main.js ├── trending │ ├── index.js │ ├── Cloud │ │ └── index.js │ ├── layouts │ │ └── default.js │ ├── main.js │ └── taxonomy.js └── home.js ├── package.json ├── DEVELOPING.md ├── MAINTAINERS.md ├── README.md └── LICENSE /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /doc/source/images/bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/bot.png -------------------------------------------------------------------------------- /doc/source/images/deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/deploy.png -------------------------------------------------------------------------------- /doc/source/images/rss-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/rss-1.png -------------------------------------------------------------------------------- /doc/source/images/rss-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/rss-2.png -------------------------------------------------------------------------------- /public/images/feed-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/public/images/feed-icon.png -------------------------------------------------------------------------------- /doc/source/images/env_vars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/env_vars.png -------------------------------------------------------------------------------- /doc/source/images/rss_feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/rss_feed.png -------------------------------------------------------------------------------- /doc/source/images/slack-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/slack-0.png -------------------------------------------------------------------------------- /doc/source/images/slack-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/slack-00.png -------------------------------------------------------------------------------- /doc/source/images/slack-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/slack-1.png -------------------------------------------------------------------------------- /doc/source/images/slack-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/slack-2.png -------------------------------------------------------------------------------- /doc/source/images/slack-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/slack-3.png -------------------------------------------------------------------------------- /doc/source/images/trouble-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/trouble-1.png -------------------------------------------------------------------------------- /doc/source/images/trouble-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/trouble-2.png -------------------------------------------------------------------------------- /doc/source/images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/architecture.png -------------------------------------------------------------------------------- /doc/source/images/edit-configmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/edit-configmap.png -------------------------------------------------------------------------------- /doc/source/images/architecture_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/architecture_old.png -------------------------------------------------------------------------------- /doc/source/images/disco-get-creds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/disco-get-creds.png -------------------------------------------------------------------------------- /doc/source/images/edit-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/edit-deployment.png -------------------------------------------------------------------------------- /doc/source/images/sample-output-query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/sample-output-query.png -------------------------------------------------------------------------------- /doc/source/images/toolchain-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/toolchain-pipeline.png -------------------------------------------------------------------------------- /doc/source/images/edit-deployment-yaml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/edit-deployment-yaml.png -------------------------------------------------------------------------------- /doc/source/images/sample-output-trending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/watson-discovery-news/HEAD/doc/source/images/sample-output-trending.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | dist: trusty 3 | node_js: 4 | - "node" 5 | - "lts/*" 6 | cache: 7 | directories: 8 | - node_modules 9 | script: npm run test && npm run lint -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | 3 | * Much of the initial work for the search and trending portions of the application were completed by [Ankur Patel](https://github.com/ankurp). 4 | -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | # Copy this file to .env and replace the credentials with 2 | # your own before starting the app. 3 | 4 | # Watson Discovery 5 | DISCOVERY_URL= 6 | DISCOVERY_APIKEY= 7 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | declared-services: 3 | discovery-news-service: 4 | label: discovery 5 | plan: lite 6 | applications: 7 | - path: . 8 | name: watson-discovery-news 9 | buildpack: sdk-for-nodejs 10 | memory: 512M 11 | instances: 1 12 | random-route: false 13 | services: 14 | - discovery-news-service 15 | env: 16 | OPTIMIZE_MEMORY: true 17 | SLACK_BOT_TOKEN: placeholder 18 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true 3 | es6: true 4 | node: true 5 | jest: true 6 | extends: 7 | - 'eslint:recommended' 8 | - 'plugin:react/recommended' 9 | parserOptions: 10 | ecmaFeatures: 11 | experimentalObjectRestSpread: true 12 | jsx: true 13 | sourceType: module 14 | plugins: 15 | - react 16 | rules: 17 | indent: 18 | - error 19 | - 2 20 | linebreak-style: 21 | - error 22 | - unix 23 | quotes: 24 | - error 25 | - single 26 | semi: 27 | - error 28 | - always 29 | no-console: 30 | - warn -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This is an open source project, and we appreciate your help! 4 | 5 | We use the GitHub issue tracker to discuss new features and non-trivial bugs. 6 | 7 | In addition to the issue tracker, [#journeys on 8 | Slack](https://dwopen.slack.com) is the best way to get into contact with the 9 | project's maintainers. 10 | 11 | To contribute code, documentation, or tests, please submit a pull request to 12 | the GitHub repository. Generally, we expect two maintainers to review your pull 13 | request before it is approved for merging. For more details, see the 14 | [MAINTAINERS](MAINTAINERS.md) page. 15 | 16 | Contributions are subject to the [Developer Certificate of Origin, Version 1.1](https://developercertificate.org/) and the [Apache License, Version 2](https://www.apache.org/licenses/LICENSE-2.0.txt). 17 | -------------------------------------------------------------------------------- /public/js/search/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import ReactDOM from 'react-dom'; 19 | import Main from '../../../src/search/main'; 20 | 21 | ReactDOM.render(
, document.querySelector('main')); 22 | -------------------------------------------------------------------------------- /public/js/trending/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import ReactDOM from 'react-dom'; 19 | import Main from '../../../src/trending/main'; 20 | 21 | ReactDOM.render(
, document.querySelector('main')); 22 | -------------------------------------------------------------------------------- /server/watson-discovery-service.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const DiscoveryV1 = require('ibm-watson/discovery/v1'); 18 | 19 | var discovery; 20 | const version_date = '2020-12-01'; 21 | 22 | discovery = new DiscoveryV1({ 23 | version: version_date 24 | }); 25 | 26 | discovery.environmentId = 'system'; 27 | discovery.collectionId = 'news-en'; 28 | 29 | module.exports = discovery; 30 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | require('dotenv').config({ silent: true }); 18 | 19 | const server = require('./server'); 20 | const port = process.env.PORT || process.env.VCAP_APP_PORT || 3000; 21 | 22 | server.then(app => { 23 | app.listen(port, () => { 24 | // eslint-disable-next-line no-console 25 | console.log('Watson Discovery News Server running on port: %d', port); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Virtual Studio code 49 | .vscode/ 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # Mac files 64 | .DS_Store 65 | -------------------------------------------------------------------------------- /src/shared/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const parseData = data => ({ 18 | topics: data.result.aggregations[0].results, 19 | rawData: data 20 | }); 21 | 22 | const topicStory = item => item.aggregations[0].hits.hits[0]; 23 | 24 | const objectWithoutProperties = (object, properties) => { 25 | 'use strict'; 26 | 27 | var obj = {}; 28 | var keys = Object.keys(object); 29 | keys.forEach(key => { 30 | if (!~properties.indexOf(key)) { 31 | obj[key] = object[key]; 32 | } 33 | }); 34 | return obj; 35 | }; 36 | 37 | module.exports = { 38 | parseData, 39 | topicStory, 40 | objectWithoutProperties 41 | }; 42 | -------------------------------------------------------------------------------- /src/search/Briefing/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import PropTypes from 'prop-types'; 19 | 20 | const Briefing = props => ( 21 |
22 |
23 |
24 |

Briefing from the Top News

25 |
26 |
27 |
28 | {props.items.map(summary => ( 29 |
30 |

{summary.title}

31 |

{summary.text}

32 |
33 |
34 | ))} 35 |
36 |
37 |
38 | ); 39 | 40 | Briefing.propTypes = { 41 | items: PropTypes.array.isRequired 42 | }; 43 | 44 | module.exports = Briefing; 45 | -------------------------------------------------------------------------------- /src/trending/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const React = require('react'); 18 | const DefaultLayout = require('./layouts/default'); 19 | const Main = require('./main'); 20 | 21 | function objectWithoutProperties (object, properties) { 22 | 'use strict'; 23 | 24 | var obj = {}; 25 | var keys = Object.keys(object); 26 | keys.forEach((key) => { 27 | if (!~properties.indexOf(key)) { 28 | obj[key] = object[key]; 29 | } 30 | }); 31 | return obj; 32 | } 33 | 34 | class Application extends React.Component { 35 | render() { 36 | const props = objectWithoutProperties(this.props, ['settings', '_locals', 'cache']); 37 | 38 | return ( 39 | 40 |
41 | 42 | ); 43 | } 44 | } 45 | 46 | module.exports = Application; 47 | -------------------------------------------------------------------------------- /src/search/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const React = require('react'); 18 | const PropTypes = require('prop-types'); 19 | const DefaultLayout = require('./layouts/default'); 20 | const Main = require('./main'); 21 | const objectWithoutProperties = require('../shared/utils').objectWithoutProperties; 22 | 23 | class Application extends React.Component { 24 | render() { 25 | const props = objectWithoutProperties(this.props, ['settings', '_locals', 'cache']); 26 | 27 | return ( 28 | 33 |
34 | 35 | ); 36 | } 37 | } 38 | 39 | Application.propTypes = { 40 | data: PropTypes.object, 41 | searchQuery: PropTypes.string 42 | }; 43 | 44 | module.exports = Application; 45 | -------------------------------------------------------------------------------- /src/shared/Query/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Tabs, Pane, Code } from 'watson-react-components'; 4 | 5 | const Query = props => ( 6 |
7 |
8 |

{props.title}

9 |
10 |
11 | 12 | 13 |
14 |
15 |
16 | 17 | {typeof props.query === 'object' ? 18 | JSON.stringify(props.query, null, 2) : props.query} 19 | 20 | 21 | {props.response ? 22 | 23 |
24 |
25 |
26 | 27 | {typeof props.response === 'object' ? 28 | JSON.stringify(props.response, null, 2) : props.response} 29 | 30 | : 31 | 32 | } 33 | 34 |
35 | ); 36 | 37 | Query.propTypes = { 38 | query: PropTypes.oneOfType([ 39 | PropTypes.object, 40 | PropTypes.string, // json string 41 | ]).isRequired, 42 | response: PropTypes.oneOfType([ 43 | PropTypes.object, 44 | PropTypes.string, // json string 45 | ]), 46 | title: PropTypes.string.isRequired, 47 | }; 48 | 49 | export default Query; 50 | -------------------------------------------------------------------------------- /server/query-builder.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import queryBuilder from './query-builder'; 18 | import moment from 'moment'; 19 | 20 | beforeEach(() => { 21 | queryBuilder.setCollectionId('collection'); 22 | queryBuilder.setEnvironmentId('environment'); 23 | }); 24 | 25 | describe('Query builder returns params for discovery service', () => { 26 | test('when opts are NOT passed', () => { 27 | expect(queryBuilder.trending()).toEqual({ 28 | environmentId: 'environment', 29 | collectionId: 'collection', 30 | return: 'enriched_title.entities.text', 31 | aggregation: 'term(enriched_title.entities.text,count:20).top_hits(1)', 32 | filter: `crawl_date>${moment().subtract(24,'h').toISOString().slice(0, -5)}` 33 | }); 34 | }); 35 | 36 | test('when opts are passed', () => { 37 | expect(queryBuilder.trending({ 38 | filter: 'enriched_text.categories.label:"test"' 39 | })).toEqual({ 40 | environmentId: 'environment', 41 | collectionId: 'collection', 42 | return: 'enriched_title.entities.text', 43 | aggregation: 'term(enriched_title.entities.text,count:20).top_hits(1)', 44 | filter: `enriched_text.categories.label:"test",crawl_date>${moment().subtract(24,'h').toISOString().slice(0, -5)}` 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /doc/source/cf.md: -------------------------------------------------------------------------------- 1 | # Run on IBM Cloud with Cloud Foundry 2 | 3 | This document shows how to run the `watson-discovery-news` application using Cloud Foundry on IBM Cloud. 4 | 5 | > **NOTE**: This app cannot be deployed to IBM Cloud with Cloud Foundry **if** you are using a free trial IBM Cloud account. This type of account is classified as a `Lite` accoount, and has a limit of 256 MB of instantaneous runtime memory available for your Cloud Foundry apps. The `watson-discovery-news` app requires 512 MB. 6 | > 7 | > If you're using a Lite account, you can get more memory by upgrading to a billable account (limit is 2 GB). From the IBM Cloud console, go to `Manage` > `Account`, and select `Account settings`. For more information about Lite account features, see [Lite account](https://cloud.ibm.com/docs/account?topic=account-accounts#liteaccount). 8 | 9 | ## Steps 10 | 11 |

12 | 13 | Deploy to IBM Cloud 14 | 15 |

16 | 17 | 1. Click the `Deploy to IBM Cloud` button and hit `Create` on the next prompt. This will automatically create the services and application for you. Create an IBM Cloud API key if required. 18 | 19 | ![deploy](images/deploy.png) 20 | 21 | 2. From the Toolchains view, click on the Delivery Pipeline to watch while the app is deployed. Here you'll be able to see logs about the deployment. 22 | 23 | ![toolchain-pipeline](images/toolchain-pipeline.png) 24 | 25 | 3. To see the app and services that were created use the [IBM Cloud dashboard](https://cloud.ibm.com). The app is named `watson-discovery-news` with a unique suffix. The following services are created: 26 | 27 | * discovery-news-service 28 | 29 | [![return](https://raw.githubusercontent.com/IBM/pattern-utils/master/deploy-buttons/return.png)](https://github.com/IBM/watson-discovery-news#deployment-options) 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "watson-discovery-news", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "app.js", 6 | "license": "Apache-2.0", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/IBM/watson-discovery-news.git" 10 | }, 11 | "scripts": { 12 | "start": "node --max_old_space_size=512 app.js", 13 | "start:watch": "nodemon app.js", 14 | "bootstrap": "cp env.sample .env", 15 | "test": "jest", 16 | "lint": "eslint .", 17 | "lint:fix": "eslint . --fix" 18 | }, 19 | "dependencies": { 20 | "@babel/core": "^7.12.9", 21 | "@babel/preset-env": "^7.12.7", 22 | "@babel/preset-react": "^7.12.7", 23 | "axios": "^0.21.2", 24 | "babel-core": "^7.0.0-bridge.0", 25 | "babel-jest": "^26.6.3", 26 | "babel-loader": "^8.2.2", 27 | "babelify": "^10.0.0", 28 | "cf-deployment-tracker-client": "*", 29 | "cfenv": "^1.2.3", 30 | "dotenv": "^8.2.0", 31 | "express": "^4.17.1", 32 | "express-browserify": "^1.0.3", 33 | "express-react-views": "^0.11.0", 34 | "ibm-watson": "^5.7.1", 35 | "isomorphic-fetch": "^3.0.0", 36 | "moment": "^2.29.1", 37 | "nodemon": "^2.0.6", 38 | "prop-types": "^15.7.2", 39 | "query-string": "^6.13.7", 40 | "react": "^17.0.1", 41 | "react-dom": "^17.0.1", 42 | "recharts": "^1.8.5", 43 | "rss": "^1.2.2", 44 | "shell-quote": "^1.6.1", 45 | "uglifyify": "^5.0.2", 46 | "vcap_services": "^0.2.0", 47 | "watson-react-components": "^0.6.19" 48 | }, 49 | "engines": { 50 | "node": ">=14.0.0" 51 | }, 52 | "babel": { 53 | "presets": [ 54 | "@babel/env", 55 | "@babel/react" 56 | ], 57 | "ignore": [ 58 | "node_modules" 59 | ] 60 | }, 61 | "devDependencies": { 62 | "enzyme": "^3.11.0", 63 | "enzyme-adapter-react-16": "^1.15.5", 64 | "eslint": "^7.14.0", 65 | "eslint-plugin-import": "^2.22.1", 66 | "eslint-plugin-react": "^7.21.5", 67 | "jest": "^26.6.3", 68 | "jest-cli": "^26.6.3" 69 | }, 70 | "jest": { 71 | "verbose": true, 72 | "testURL": "http://localhost/" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /server/express.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const path = require('path'); 18 | const express = require('express'); 19 | const expressBrowserify = require('express-browserify'); 20 | 21 | const app = express(); 22 | 23 | app.set('views', path.join(__dirname, '..', 'src')); 24 | app.set('view engine', 'js'); 25 | app.engine('js', require('express-react-views').createEngine()); 26 | 27 | // Middlewares 28 | app.use('/css', express.static(path.resolve(__dirname, '..', 'public/css'))); 29 | app.use('/images', express.static(path.resolve(__dirname, '..', 'public/images'))); 30 | app.use(express.static(path.join(__dirname, '..', 'node_modules/watson-react-components/dist'))); 31 | 32 | const isDev = (app.get('env') === 'development'); 33 | const trendingBrowserifyier = expressBrowserify(path.resolve(__dirname, '..', 'public/js/trending/bundle.js'), { 34 | watch: isDev, 35 | debug: isDev, 36 | extension: ['js'], 37 | transform: ['babelify'], 38 | }); 39 | const searchBrowserifyier = expressBrowserify(path.resolve(__dirname, '..', 'public/js/search/bundle.js'), { 40 | watch: isDev, 41 | debug: isDev, 42 | extension: ['js'], 43 | transform: ['babelify'], 44 | }); 45 | 46 | if (!isDev) { 47 | trendingBrowserifyier.browserify.transform('uglifyify', { global: true }); 48 | searchBrowserifyier.browserify.transform('uglifyify', { global: true }); 49 | } 50 | 51 | // Client Side Bundle route 52 | app.get('/js/trending/bundle.js', trendingBrowserifyier); 53 | app.get('/js/search/bundle.js', searchBrowserifyier); 54 | 55 | module.exports = app; 56 | -------------------------------------------------------------------------------- /server/query-builder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | const util = require('util'); 18 | const moment = require('moment'); 19 | const aggregations = { 20 | search: 'term(enriched_text.sentiment.document.label)', 21 | trending: 'term(enriched_title.entities.text,count:20).top_hits(1)' 22 | }; 23 | 24 | module.exports = { 25 | aggregations, 26 | setEnvironmentId(environmentId) { 27 | this.environment_id = environmentId; 28 | }, 29 | setCollectionId(collectionId) { 30 | this.collection_id = collectionId; 31 | }, 32 | search(queryOpts) { 33 | const params = Object.assign({ 34 | environmentId: this.environment_id, 35 | collectionId: this.collection_id, 36 | count: 10, 37 | // sort: 'result_metadata.score', 38 | return: 'title,text,url,host,crawl_date,result_metadata.score,id,enriched_text.entities.text,enriched_text.sentiment.document.label', 39 | aggregation: aggregations.search 40 | }, queryOpts); 41 | 42 | console.log('Discovery Search Query Params: '); 43 | console.log(util.inspect(params, false, null)); 44 | 45 | return params; 46 | }, 47 | trending(queryOpts = {}) { 48 | const { filter } = queryOpts; 49 | const timeAndSourceFilter = `crawl_date>${moment().subtract(24,'h').toISOString().slice(0, -5)}`; 50 | 51 | const params = Object.assign({ 52 | environmentId: this.environment_id, 53 | collectionId: this.collection_id, 54 | return: 'enriched_title.entities.text', 55 | aggregation: aggregations.trending 56 | }, queryOpts, { 57 | filter: filter ? `${filter},${timeAndSourceFilter}` : timeAndSourceFilter 58 | }); 59 | 60 | console.log('Discovery Trending Query Params: '); 61 | console.log(util.inspect(params, false, null)); 62 | return params; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/trending/Cloud/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import PropTypes from 'prop-types'; 19 | import { Colors } from 'watson-react-components'; 20 | import { topicStory } from '../../shared/utils'; 21 | 22 | const MAX_SIZE = 50; 23 | const MIN_SIZE = 16; 24 | let largest; 25 | let ratio; 26 | let computeSize; 27 | 28 | const getSentimentColor = item => { 29 | switch (topicStory(item).enriched_text.sentiment.document.label) { 30 | case 'negative': return Colors.red_50; 31 | case 'positive': return Colors.green_50; 32 | default: return Colors.gray_50; 33 | } 34 | }; 35 | 36 | const Cloud = props => { 37 | largest = props.data ? 38 | props.data.reduce((prev, cur) => (cur.matching_results > prev ? cur.matching_results : prev), 0) : 39 | 0; 40 | ratio = MAX_SIZE / largest; 41 | computeSize = (value) => Math.max(MIN_SIZE, value * ratio); 42 | return ( 43 |
44 | { 45 | props.data ? 46 | props.data.map((item, index) => 47 | 60 | {item.key} 61 | ) : 62 | [] 63 | } 64 |
65 | ); 66 | }; 67 | 68 | Cloud.propTypes = { 69 | data: PropTypes.array.isRequired, 70 | }; 71 | 72 | module.exports = Cloud; 73 | -------------------------------------------------------------------------------- /DEVELOPING.md: -------------------------------------------------------------------------------- 1 | # Project Structure 2 | 3 | Below is a general overview of the most important directories and files in the project. 4 | 5 | ``` 6 | . 7 | ├── env.sample # Sample environment variable files that needs to be copied to .env file 8 | ├── app.js # Entry point of the application 9 | ├── manifest.yml # Configuration used to deploy app to Bluemix 10 | ├── package.json# Config file containing dependencies and scripts and babel config 11 | ├── public # Public folder contains CSS and JS served on the webpage 12 | │   ├── css 13 | │   ├── images 14 | │   └── js 15 | │   ├── search 16 | │   │   └── bundle.js # Entry point for code run in the browser for search page 17 | │   └── trending 18 | │   └── bundle.js # Entry point for code run in the browser for trending page 19 | ├── server # Contains code specific to the server 20 | │   ├── express.js # File that configures express 21 | │   ├── index.js # Configures the endpoint for Discovery API and create express server 22 | │   ├── query-builder.js # Helper file which helps generate the query params passed to Discovery API 23 | │   └── watson-discovery-service.js # Helper file to promisify Waston SDK APIs 24 | ├── src # Views that get rendered by the server and the client bundle.js 25 | │   ├── home.js # Component that renders the home page 26 | │   ├── search # Folder containing code related to /search page 27 | │   │   ├── Briefing # Briefing Component 28 | │   │   ├── Search # Search Component 29 | │   │   ├── Sentiment # Sentiment Component 30 | │   │   ├── TopStories # TopStories and Story Component 31 | │   │   ├── index.js # HTML view that is rendered for /search/ 32 | │   │   ├── layouts # Layout for search page 33 | │   │   │   └── default.js 34 | │   │   └── main.js # Main component of Search which is rendered as HTML server side and contains client side code 35 | │   ├── shared # Folder containing shared code across pages 36 | │   │   ├── Query # Component that renders the query performed 37 | │   │   └── utils.js # Helper file containing utility functions shared in the project 38 | │   └── trending # Folder containing code related to /trending page 39 | │   ├── Cloud # Component that renders topics trending 40 | │   ├── index.js # HTML view that is rendered for /trending/ 41 | │   ├── layouts # Layout for trending page 42 | │   │   └── default.js 43 | │   ├── main.js # Main component of Trending which is rendered as HTML server side and contains 44 | └── └── taxonomy.js # File containing taxonomy that are listed in the /trending/ page 45 | ``` 46 | -------------------------------------------------------------------------------- /src/search/Sentiment/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import PropTypes from 'prop-types'; 19 | import { BarChart, XAxis, YAxis, Bar, CartesianGrid, Legend } from 'recharts'; 20 | import { Colors } from 'watson-react-components'; 21 | 22 | class Sentiment extends React.Component { 23 | 24 | constructor(...args) { 25 | super(...args); 26 | this.handleResize = this.handleResize.bind(this); 27 | this.state = { width: 0 }; 28 | } 29 | 30 | handleResize() { 31 | this.setState({ 32 | width: this.el.getBoundingClientRect().width - 64 33 | }); 34 | } 35 | 36 | componentDidMount() { 37 | this.setState({ 38 | width: this.el.getBoundingClientRect().width - 64 39 | }); 40 | window.addEventListener('resize', this.handleResize); 41 | } 42 | 43 | componentWillUnmount() { 44 | window.removeEventListener('resize', this.handleResize); 45 | } 46 | 47 | render() { 48 | return ( 49 |
{ this.el = el; }}> 50 |
51 |
52 |

Sentiment Expressed

53 |
54 |
55 |
Below is the total count of the number of news articles that have negative, neutral or positive sentiment 56 | expressed in the article related to your search query above.
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 |
69 |
70 | ); 71 | } 72 | } 73 | 74 | Sentiment.propTypes = { 75 | data: PropTypes.object.isRequired 76 | }; 77 | 78 | module.exports = Sentiment; 79 | -------------------------------------------------------------------------------- /src/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); you may not 5 | * use this file except in compliance with the License. You may obtain a copy of 6 | * the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations under 14 | * the License. 15 | */ 16 | 17 | import { Header, Jumbotron, Footer } from 'watson-react-components'; 18 | const React = require('react'); 19 | 20 | class Application extends React.Component { 21 | getDescription() { 22 | return ( 23 |
24 |
25 | This web app showcases multiple ways to utilize the Watson Discovery Service to query data collections, such as Watson News. 26 |
27 |
28 | Watson News is a pre-enriched dataset that is updated continuosly with over 300,000 news articles and blogs a day. 29 |
30 |
31 |
32 |
Select from one of the following examples:
33 | 41 |
42 | ); 43 | } 44 | 45 | render() { 46 | return ( 47 | 48 | 49 | Watson Discovery 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
62 | 71 |