diff --git a/package-lock.json b/package-lock.json index 7ef06debc2..cf3eb8029b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -623,6 +623,11 @@ "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" }, + "afinn-165": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/afinn-165/-/afinn-165-1.0.3.tgz", + "integrity": "sha512-LTwJcUGRzjQCR6uQCXMEUkoecezmWNjyWRjPm9pv0WlIYtLLaOnVsR1RvAdSMgOJPTFNlxS1IkfXxrkmV+gcyQ==" + }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", @@ -701,6 +706,14 @@ "normalize-path": "^2.1.1" } }, + "apparatus": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/apparatus/-/apparatus-0.0.10.tgz", + "integrity": "sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==", + "requires": { + "sylvester": ">= 0.0.8" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1516,9 +1529,7 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "component-emitter": { "version": "1.3.0", @@ -5005,6 +5016,14 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -5025,6 +5044,11 @@ "minimist": "^1.2.0" } }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5564,6 +5588,18 @@ "to-regex": "^3.0.1" } }, + "natural": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/natural/-/natural-0.6.3.tgz", + "integrity": "sha512-78fcEdNN6Y4pv8SOLPDhJTlUG+8IiQzNx0nYpl0k7q00K4ZZuds+wDWfSa6eeiPcSQDncvV44WWGsi70/ZP3+w==", + "requires": { + "afinn-165": "^1.0.2", + "apparatus": "^0.0.10", + "json-stable-stringify": "^1.0.1", + "sylvester": "^0.0.12", + "underscore": "^1.3.1" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5629,6 +5665,15 @@ "semver": "^6.3.0" } }, + "node-summarizer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/node-summarizer/-/node-summarizer-1.0.7.tgz", + "integrity": "sha512-kHgVyhm+k97/50ActxLFIemHLvYPwHLJfwF3tEg2TqeL5xmUCCytqRwdiO3LyBRFMUopLJjisSlkJwTk72gVbg==", + "requires": { + "natural": "^0.6.3", + "wordpos": "^1.2.0" + } + }, "nodemon": { "version": "1.19.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", @@ -7601,6 +7646,11 @@ "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, + "sylvester": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz", + "integrity": "sha1-WohEFc0tACxX56OqyZRip1zp/bQ=" + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -7903,6 +7953,11 @@ } } }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, "unherit": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz", @@ -8341,6 +8396,21 @@ } } }, + "wordnet-db": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/wordnet-db/-/wordnet-db-3.1.11.tgz", + "integrity": "sha512-gU1pmI6SaXA38fA62OoJz1LAYo2B9vnT0lIw6ZCiCel2dlIP8gJ/4EutnkVzenR5QH6X57n/TmWyXPNfiD0uMA==" + }, + "wordpos": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/wordpos/-/wordpos-1.2.0.tgz", + "integrity": "sha512-3C0pdLnYIV962dyPEmi+QNJhI+RRz2lLY1QiCycUt2qhL8Dalv1huAngfePDmNJ1WHdSAT/fjbDqtbu1KfguCw==", + "requires": { + "commander": "^2.0.0", + "underscore": ">=1.3.1", + "wordnet-db": "^3.1.6" + } + }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", diff --git a/package.json b/package.json index 78f6383b71..b32e559a66 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "express": "^4.17.1", "express-healthcheck": "^0.1.0", "feedparser-promised": "^2.0.1", + "node-summarizer": "^1.0.7", "sentiment": "^5.0.2" }, "devDependencies": { diff --git a/src/summarize.js b/src/summarize.js new file mode 100644 index 0000000000..61569545b6 --- /dev/null +++ b/src/summarize.js @@ -0,0 +1,6 @@ +const { SummarizerManager } = require('node-summarizer'); + +module.exports = async function (text, numSentences = 3) { + const summarizer = new SummarizerManager(text, numSentences); + return summarizer.getSummaryByRank().then((summaryObject) => summaryObject.summary); +}; diff --git a/test/summarize.test.js b/test/summarize.test.js new file mode 100644 index 0000000000..043573e68e --- /dev/null +++ b/test/summarize.test.js @@ -0,0 +1,22 @@ +const summarize = require('../src/summarize'); + +describe('summarize tests...', () => { + const inputData = 'Alright, we are into the last few days of Hacktoberfest and here’s my status update. Bad news first because I’m a cup half empty kind of guy: I gave up on working with PHP and Laurel because I just ran out of time and lack the drive to really focus on learning a new technology when there are so many other fun things waiting to be tinkered. The good news: I have completed my four PRs required for Hacktoberfest! The last two issues I worked on contrasted each quite a bit. One was a new component for a react-app while the other was just a simple “add a button to html page” (Sorry, I ran out of time). The latter was no fun so I’ll just talk about the react component. The component was for a web-based comic book reader called Villian. The issue involved adding a tooltip to the slider on the toolbar to indicate the handle’s value. My first iteration of the feature involved me googling how other people implemented tooltips. Turns out the base code used in the project was nearly identical to the ones from the React tutorials on their official site. So I just those code segments into the project and submitted the PR. Thought it was a easy fix, but then I was asked to properly add the tooltip as a component and seamlessly incorporate it into the app. This took some time as I was no longer just copy, pasting and editing. I had to look up some react documentation to recall how the framework operated, which took a day, and then modify my PR. I was very satisfied with my end result, the code was a lot cleaner, compact, and modular. The effort is worth the satisfaction. Before Hacktoberfest ends I will be monitoring my other PRs as they have yet been reviewed. But I have to say, I am very happy that I participated in the event. It was really fun and I actually felt like I was programming, not just doing mundane maintenance work. I believe I will be continuing to work in the open source community after this month.\n'; + let predicted; + + beforeAll(async () => { + predicted = await summarize(inputData, 3); + }); + + test('summarize returns string', () => expect(typeof predicted).toBe('string')); + + /* + node-summarizer's implementation of TextRank fails to return consistent summaries + over multiple runs with the same input data. This might be just an attribute of + the TestRank algorithm itself as it randomly traverses the word map to generate a + ranking of the most relavent sentences. Therefore, it is best to not test for + an expected summary. However, the number of sentences returned should work + but for some reason it also fails at times. More investigation is required before + the sentence count and the summary value may be tested. + */ +});