jenkins-bot has submitted this change and it was merged.
Change subject: Use LinearDoc for segmentation
......................................................................
Use LinearDoc for segmentation
Use LinearDoc to allow apply segmentation at a structural level.
Ensure valid, well-formed HTML output.
Include inter-segment whitespace with the previous segment.
Treat references as zero-width annotations; do not segment inside them.
Do not give link IDs to links inside a reference.
LinearDoc.js
* Add segment IDs to HTML output
* Add link IDs to HTML output
* Output attributes in lexicographic order
* Add Normalizer class for test data
CXSegmenter.js
* Rewrite completely, using LinearDoc.js
Segmenter{Default,En,Hi}.js
* Basic plaintext segmenters that work well with LinearDoc segmentation
CXSegmenter.test.js
* Normalize expected/actual output before comparison
tests/segmentation/data/result*.html
* Rewrite for LinearDoc segmentation (remove some hardcoded limitations)
* Add some new tests
* Disable one test ('Mr. D. Jones'); add a new one ('The UK. The US')
ContentTranslationService.js
* Pass the source language
Change-Id: Iee6bfa499c9f23269a290df5de5e027e48746971
---
M ContentTranslationService.js
M lineardoc/LinearDoc.js
M segmentation/CXSegmenter.js
A segmentation/SegmenterDefault.js
A segmentation/SegmenterEn.js
A segmentation/SegmenterHi.js
M tests/segmentation/CXSegmenter.test.js
M tests/segmentation/SegmentationTests.json
M tests/segmentation/data/result-10.html
M tests/segmentation/data/result-11.html
M tests/segmentation/data/result-12.html
M tests/segmentation/data/result-13.html
M tests/segmentation/data/result-15.html
A tests/segmentation/data/result-16.html
A tests/segmentation/data/result-17.html
M tests/segmentation/data/result-7.html
M tests/segmentation/data/result-8.html
M tests/segmentation/data/result-9.html
M tests/segmentation/data/result-debian-1.html
M tests/segmentation/data/result-ends-with-references-missing-letters.html
A tests/segmentation/data/test-16.html
A tests/segmentation/data/test-17.html
M tests/segmentation/data/test-7.html
23 files changed, 432 insertions(+), 147 deletions(-)
Approvals:
Santhosh: Looks good to me, approved
jenkins-bot: Verified
diff --git a/ContentTranslationService.js b/ContentTranslationService.js
index 3d6e0c7..017f868 100644
--- a/ContentTranslationService.js
+++ b/ContentTranslationService.js
@@ -67,7 +67,7 @@
var segmenter, segmentedContent;
try {
logger.debug( 'Page fetched' );
- segmenter = new CXSegmenter( data );
+ segmenter = new CXSegmenter( data, sourceLanguage );
segmenter.segment();
segmentedContent = segmenter.getSegmentedContent();
} catch ( error ) {
diff --git a/lineardoc/LinearDoc.js b/lineardoc/LinearDoc.js
index 54aba8f..4337314 100644
--- a/lineardoc/LinearDoc.js
+++ b/lineardoc/LinearDoc.js
@@ -59,9 +59,15 @@
* @return {string} Html representation of open tag
*/
function getOpenTagHtml( tag ) {
- var attr, html;
+ var html, attributes, attr, i, len;
html = [ '<' + esc( tag.name ) ];
+ attributes = [];
for ( attr in tag.attributes ) {
+ attributes.push( attr );
+ }
+ attributes.sort();
+ for ( i = 0, len = attributes.length; i < len; i++ ) {
+ attr = attributes[i];
html.push( ' ' + esc( attr ) + '="' + escAttr( tag.attributes[
attr ] ) + '"' );
}
if ( tag.isSelfClosing ) {
@@ -69,6 +75,20 @@
}
html.push( '>' );
return html.join( '' );
+}
+
+/**
+ * Clone a SAX open tag
+ * @private
+ * @param {Object} tag Tag to clone
+ * @return {Object} Cloned tag
+ */
+function cloneOpenTag( tag ) {
+ var attr, newTag = { name: tag.name, attributes: {} };
+ for ( attr in tag.attributes ) {
+ newTag.attributes[attr] = tag.attributes[attr];
+ }
+ return newTag;
}
/**
@@ -119,7 +139,13 @@
* @return {boolean} Whether the tag is a mediawiki reference span
*/
function isReference( tag ) {
- return tag.name === 'span' && tag.attributes.typeof ===
'mw:Extension/ref';
+ if ( tag.name === 'span' && tag.attributes.typeof ===
'mw:Extension/ref' ) {
+ return true;
+ } else if ( tag.name === 'span' && tag.attributes.class === 'reference'
) {
+ // TODO: This is in the tests, but is it correct behaviour?
+ return true;
+ }
+ return false;
}
/**
@@ -173,6 +199,55 @@
// non-annotation inline tags
'img', 'br'
] ) );
+
+/**
+ * Find the boundaries that lie in each chunk
+ *
+ * Boundaries lying between chunks lie in the latest chunk possible.
+ * Boundaries at the start of the first chunk, or the end of the last, are not
included.
+ * Therefore zero-width chunks never have any boundaries
+ *
+ * @function
+ * @param {number[]} boundaries Boundary offsets
+ * @param chunks Chunks to which the boundaries apply
+ * @param {Function} getLength Function returning the length of a chunk
+ * @returns {Object} Array of {chunk: ch, boundaries: [...]}
+ */
+function getChunkBoundaryGroups( boundaries, chunks, getLength ) {
+ var i, len, groupBoundaries, chunk, chunkLength, boundary,
+ groups = [],
+ offset = 0,
+ boundaryPtr = 0;
+
+ // Get boundaries in order, disregarding the start of the first chunk
+ boundaries = boundaries.slice();
+ boundaries.sort( function ( a, b ) { return a - b; } );
+ while ( boundaries[boundaryPtr] === 0 ) {
+ boundaryPtr++;
+ }
+ for( i = 0, len = chunks.length; i < len; i++ ) {
+ groupBoundaries = [];
+ chunk = chunks[i];
+ chunkLength = getLength( chunk );
+ while ( true ) {
+ boundary = boundaries[boundaryPtr];
+ if ( boundary === undefined || boundary > offset +
chunkLength - 1 ) {
+ // beyond the interior of this chunk
+ break;
+ }
+ // inside the interior of this chunk
+ groupBoundaries.push( boundary );
+ boundaryPtr++;
+ }
+ offset += chunkLength;
+ groups.push( {
+ chunk: chunk,
+ boundaries: groupBoundaries
+ } );
+ // Continue even if past boundaries: need to add remaining
chunks
+ }
+ return groups;
+}
/**
* A chunk of uniformly-annotated inline text
@@ -311,77 +386,104 @@
}
/**
+ * Set link IDs in-place on text chunks
+ *
+ * @private
+ * @param {TextChunk[]} textChunks Consecutive text chunks
+ * @param {Function} getNextId function accepting 'link' and returning next ID
+ */
+function setLinkIdsInPlace( textChunks, getNextId ) {
+ var i, iLen, j, jLen, tags, tag, href;
+ for ( i = 0, iLen = textChunks.length; i < iLen; i++ ) {
+ tags = textChunks[ i ].tags;
+ for ( j = 0, jLen = tags.length; j < jLen; j++ ) {
+ tag = tags[ j ];
+ if (
+ tag.name === 'a' &&
+ tag.attributes.href !== undefined &&
+ tag.attributes[ 'data-linkid' ] === undefined
+ ) {
+ // Hack: copy href, then remove it, then re-add
it, so that
+ // attributes appear in alphabetical order (ugh)
+ href = tag.attributes.href;
+ delete tag.attributes.href;
+ tag.attributes.class = 'cx-link';
+ tag.attributes[ 'data-linkid' ] = getNextId(
'link' );
+ tag.attributes.href = href;
+ }
+ }
+ }
+}
+
+/**
* Segment the text block into sentences
* @method
* @param {Function} getBoundaries Function taking plaintext, returning offset
array
+ * @param {Function} getNextId Function taking 'segment'|'link', returning
next ID
* @return {TextBlock} Segmented version, with added span tags
*/
-TextBlock.prototype.segment = function ( getBoundaries ) {
- var i, len, textChunk, boundary, relOffset,
- allTextChunks = [],
- currentTextChunks = [],
- charCount = 0,
- boundaries = getBoundaries( this.getPlainText() ),
- bPtr = 0,
- segId = 1;
+TextBlock.prototype.segment = function ( getBoundaries, getNextId ) {
+ var allTextChunks, currentTextChunks, groups, i, iLen, group, offset,
textChunk, j, jLen,
+ leftPart, rightPart, boundaries, relOffset;
+ // Setup: currentTextChunks for current segment, and allTextChunks for
all segments
+ allTextChunks = [];
+ currentTextChunks = [];
function flushChunks() {
- if ( currentTextChunks.length > 0 ) {
- allTextChunks.push.apply( allTextChunks, addCommonTag(
- currentTextChunks, {
- name: 'span',
- attributes: {
- class: 'seg s' + segId++
- }
- }
- ) );
- currentTextChunks = [];
+ var modifiedTextChunks;
+ if ( currentTextChunks.length === 0 ) {
+ return;
}
+ modifiedTextChunks = addCommonTag(
+ currentTextChunks,
+ {
+ name: 'span',
+ attributes: {
+ 'class': 'cx-segment',
+ 'data-segmentid': getNextId( 'segment' )
+ }
+ }
+ );
+ setLinkIdsInPlace( modifiedTextChunks, getNextId );
+ allTextChunks.push.apply( allTextChunks, modifiedTextChunks );
+ currentTextChunks = [];
}
- for ( i = 0, len = this.textChunks.length; i < len; i++ ) {
- textChunk = this.textChunks[ i ];
- // Move boundary pointer to the boundary after the start of
this chunk
- // (or beyond the end of the array if there is no such boundary)
- // Notice that if the chunk is zero-length, bPtr may already be
exactly
- // at the end of the chunk, in which case bPtr won't move
- while ( bPtr < boundaries.length && boundaries[ bPtr ] <
charCount ) {
- bPtr++;
- }
- boundary = boundaries[ bPtr ];
+ // for each chunk, split at any boundaries that occur inside the chunk
+ groups = getChunkBoundaryGroups(
+ getBoundaries( this.getPlainText() ),
+ this.textChunks,
+ function ( textChunk ) { return textChunk.text.length; }
+ );
- // Get offset relative to the start of this text chunk
- relOffset = ( boundary === undefined ) ? undefined : boundary -
charCount;
- if ( relOffset === 0 ) {
- // Boundary exactly at start of text
- if ( textChunk.text.length === 0 ) {
- // Zero-width: chunk lies before segment
boundary
- // Don't flush chunks yet: another zero-width
chunk may come
- currentTextChunks.push( textChunk );
- } else {
- // Non-zero width: chunk lies after segment
boundary, so flush
+ offset = 0;
+ for ( i = 0, iLen = groups.length; i < iLen; i++ ) {
+ group = groups[i];
+ textChunk = group.chunk;
+ boundaries = group.boundaries;
+ for ( j = 0, jLen = boundaries.length; j < jLen; j++ ) {
+ relOffset = boundaries[j] - offset;
+ if ( relOffset === 0 ) {
flushChunks();
- currentTextChunks.push( textChunk );
+ } else {
+ leftPart = new TextChunk(
+ textChunk.text.substring( 0, relOffset
),
+ textChunk.tags.slice()
+ );
+ rightPart = new TextChunk(
+ textChunk.text.substring( relOffset ),
+ textChunk.tags.slice(),
+ textChunk.inlineElement
+ );
+ currentTextChunks.push( leftPart );
+ offset += relOffset;
+ flushChunks();
+ textChunk = rightPart;
}
- } else if ( relOffset < textChunk.text.length ) {
- // Boundary strictly inside the text: split chunk
- // Add pre-split chunk, then flush block
- currentTextChunks.push( new TextChunk(
- textChunk.text.substring( 0, relOffset ),
- textChunk.tags.slice()
- ) );
- flushChunks();
- // Add post-split chunk, including ref if any
- currentTextChunks.push( new TextChunk(
- textChunk.text.substring( relOffset ),
- textChunk.tags.slice(),
- textChunk.inlineElement
- ) );
- } else {
- // No boundary, or boundary after chunk: add whole chunk
- currentTextChunks.push( textChunk );
}
- charCount += textChunk.text.length;
+ // Even if the textChunk is zero-width, it may have references
+ currentTextChunks.push( textChunk );
+ offset += textChunk.text.length;
}
flushChunks();
return new TextBlock( allTextChunks );
@@ -460,15 +562,33 @@
* @return {Doc} Segmented version of document TODO: warning: *shallow copied*.
*/
Doc.prototype.segment = function ( getBoundaries ) {
- var i, len, item, textBlock,
- newDoc = new Doc();
+ var i, len, item, tag, textBlock,
+ newDoc = new Doc(),
+ nextId = 0;
+
+ // TODO: return different counters depending on type
+ function getNextId( type ) {
+ if ( type === 'segment' || type === 'link' || type === 'block'
) {
+ return '' + nextId++;
+ } else {
+ throw new Error( 'Unknown ID type: ' + type );
+ }
+ }
+
for ( i = 0, len = this.items.length; i < len; i++ ) {
item = this.items[ i ];
- if ( this.items[ i ].type !== 'textblock' ) {
+ if ( this.items[ i ].type === 'open' ) {
+ tag = cloneOpenTag( item.item );
+ tag.attributes.id = getNextId( 'block' );
+ newDoc.addItem( item.type, tag );
+ } else if ( this.items[ i ].type !== 'textblock' ) {
newDoc.addItem( item.type, item.item );
} else {
textBlock = item.item;
- newDoc.addItem( 'textblock', textBlock.segment(
getBoundaries ) );
+ newDoc.addItem(
+ 'textblock',
+ textBlock.segment( getBoundaries, getNextId )
+ );
}
}
return newDoc;
@@ -733,11 +853,48 @@
}
};
+/**
+ * Parser to normalize XML
+ * @class
+ * @constructor
+ */
+function Normalizer() {
+ SAXParser.call( this, false, { lowercase: true } );
+}
+util.inherits( Normalizer, SAXParser );
+
+Normalizer.prototype.init = function () {
+ this.doc = [];
+ this.tags = [];
+};
+
+Normalizer.prototype.onopentag = function ( tag ) {
+ this.tags.push( tag );
+ this.doc.push( getOpenTagHtml( tag ) );
+};
+
+Normalizer.prototype.onclosetag = function ( tagName ) {
+ var tag = this.tags.pop();
+ if ( tag.name !== tagName ) {
+ throw new Error( 'Unmatched tags: ' + tag.name + ' !== ' +
tagName );
+ }
+ this.doc.push( getCloseTagHtml( tag ) );
+};
+
+Normalizer.prototype.ontext = function ( text ) {
+ this.doc.push( esc( text ) );
+};
+
+Normalizer.prototype.getHtml = function () {
+ return this.doc.join( '' );
+};
+
module.exports = {
findAll: findAll,
Doc: Doc,
TextBlock: TextBlock,
TextChunk: TextChunk,
Builder: Builder,
- Parser: Parser
+ Parser: Parser,
+ Normalizer: Normalizer
};
diff --git a/segmentation/CXSegmenter.js b/segmentation/CXSegmenter.js
index 3ba9e5e..9155ab1 100644
--- a/segmentation/CXSegmenter.js
+++ b/segmentation/CXSegmenter.js
@@ -9,52 +9,40 @@
'use strict';
-var CXParserFactory = require( __dirname + '/CXParserFactory.js'
).CXParserFactory,
- $ = require( 'jquery' );
+var LinearDoc = require( '../lineardoc/LinearDoc' ),
+ logger = require( __dirname + '/../utils/Logger.js' ),
+ getBoundariesDefault = require( './SegmenterDefault' ).getBoundaries,
+ getBoundariesEn = require( './SegmenterEn' ).getBoundaries,
+ getBoundariesHi = require( './SegmenterHi' ).getBoundaries;
+
+function getBoundaryFunction( language ) {
+ if ( language === 'en' ) {
+ return getBoundariesEn;
+ } else if ( language === 'hi' ) {
+ return getBoundariesHi;
+ } else {
+ logger.warn( 'Using fallback boundary function for language: '
+ JSON.stringify( language ) );
+ return getBoundariesDefault;
+ }
+}
function CXSegmenter( content, language ) {
+ this.parser = new LinearDoc.Parser();
+ this.parser.init();
+ this.getBoundaries = getBoundaryFunction( language );
this.content = content;
- this.segments = {};
- this.segmentedContent = null;
- this.links = {};
- this.parser = ( new CXParserFactory() ).getParser( language || 'en' );
+ this.originalDoc = null;
+ this.segmentedDoc = null;
}
CXSegmenter.prototype.segment = function () {
- this.parse();
- this.extractSegments();
-};
-
-CXSegmenter.prototype.parse = function () {
- this.parser.parse( this.content );
- this.links = this.parser.links;
- this.segmentedContent = this.parser.segmentedContent;
-};
-
-CXSegmenter.prototype.getLinks = function () {
- return this.links;
-};
-
-CXSegmenter.prototype.extractSegments = function () {
- var segmenter = this,
- $container = $( '<div>' ).html( this.segmentedContent );
-
- $container.find( '.cx-segment' ).each( function ( index, section ) {
- var $section = $( section ),
- segmentId = $section.data( 'segmentid' );
-
- segmenter.segments[segmentId] = {
- source: $section.html()
- };
- } );
-};
-
-CXSegmenter.prototype.getSegments = function () {
- return this.segments;
+ this.parser.write( this.content );
+ this.originalDoc = this.parser.builder.doc;
+ this.segmentedDoc = this.originalDoc.segment( this.getBoundaries );
};
CXSegmenter.prototype.getSegmentedContent = function () {
- return this.segmentedContent;
+ return this.segmentedDoc.getHtml();
};
module.exports.CXSegmenter = CXSegmenter;
diff --git a/segmentation/SegmenterDefault.js b/segmentation/SegmenterDefault.js
new file mode 100644
index 0000000..782ce75
--- /dev/null
+++ b/segmentation/SegmenterDefault.js
@@ -0,0 +1,37 @@
+var findAll = require( '../lineardoc/LinearDoc' ).findAll;
+
+/**
+ * Test a possible sentence boundary match
+ *
+ * @param {string} text The plaintext to segment
+ * @param {Object} match The possible boundary match (returned by regex.exec)
+ * @return {number|null} The boundary offset, or null if not a sentence
boundary
+ */
+
+function findBoundary( text, match ) {
+ var tail = text.slice( match.index + 1, text.length );
+ // Trailing non-final punctuation: not a sentence boundary
+ if ( tail.match( /^[,;:]/ ) ) {
+ return null;
+ }
+ // Next word character is number or lower-case: not a sentence boundary
+ if ( tail.match( /^\W*[0-9a-z]/ ) ) {
+ return null;
+ }
+ // Include any closing punctuation and trailing space
+ return match.index + 1 + tail.match( /^['”"’]*\s*/ )[0].length;
+}
+
+/**
+ * Find English sentence boundaries
+ *
+ * @param {string} text The plaintext to segment
+ * @returns {number[]} Sentence boundary offsets
+ */
+function getBoundaries( text ) {
+ // Regex to find possible English sentence boundaries.
+ // Must not use a shared regex instance (re.lastIndex is used)
+ return findAll( text, /[.!?]/g, findBoundary );
+}
+
+module.exports = { getBoundaries: getBoundaries };
diff --git a/segmentation/SegmenterEn.js b/segmentation/SegmenterEn.js
new file mode 100644
index 0000000..800e6bd
--- /dev/null
+++ b/segmentation/SegmenterEn.js
@@ -0,0 +1,37 @@
+var findAll = require( '../lineardoc/LinearDoc' ).findAll;
+
+/**
+ * Test a possible English sentence boundary match
+ *
+ * @param {string} text The plaintext to segment
+ * @param {Object} match The possible boundary match (returned by regex.exec)
+ * @return {number|null} The boundary offset, or null if not a sentence
boundary
+ */
+
+function findBoundary( text, match ) {
+ var tail = text.slice( match.index + 1, text.length );
+ // Trailing non-final punctuation: not a sentence boundary
+ if ( tail.match( /^[,;:]/ ) ) {
+ return null;
+ }
+ // Next word character is number or lower-case: not a sentence boundary
+ if ( tail.match( /^\W*[0-9a-z]/ ) ) {
+ return null;
+ }
+ // Include any closing punctuation and trailing space
+ return match.index + 1 + tail.match( /^['”"’]*\s*/ )[0].length;
+}
+
+/**
+ * Find English sentence boundaries
+ *
+ * @param {string} text The plaintext to segment
+ * @returns {number[]} Sentence boundary offsets
+ */
+function getBoundaries( text ) {
+ // Regex to find possible English sentence boundaries.
+ // Must not use a shared regex instance (re.lastIndex is used)
+ return findAll( text, /[.!?]/g, findBoundary );
+}
+
+module.exports = { getBoundaries: getBoundaries };
diff --git a/segmentation/SegmenterHi.js b/segmentation/SegmenterHi.js
new file mode 100644
index 0000000..8b37888
--- /dev/null
+++ b/segmentation/SegmenterHi.js
@@ -0,0 +1,28 @@
+var findAll = require( '../lineardoc/LinearDoc' ).findAll;
+
+/**
+ * Test a possible Hindi sentence boundary match
+ *
+ * @param {string} text The plaintext to segment
+ * @param {Object} match The possible boundary match (returned by regex.exec)
+ * @return {number|null} The boundary offset, or null if not a sentence
boundary
+ */
+function findBoundary( text, match ) {
+ var tail = text.slice( match.index + 1, text.length );
+ // Include any trailing space
+ return match.index + 1 + tail.match( /^\s*/ )[0].length;
+}
+
+/**
+ * Find Hindi sentence boundaries
+ *
+ * @param {string} text The plaintext to segment
+ * @returns {number[]} Sentence boundary offsets
+ */
+function getBoundaries( text ) {
+ // Regex to find possible Hindi sentence boundaries.
+ // Must not use a shared regex instance (re.lastIndex is used)
+ return findAll( text, /[।!?]/g, findBoundary );
+}
+
+module.exports = { getBoundaries: getBoundaries };
diff --git a/tests/segmentation/CXSegmenter.test.js
b/tests/segmentation/CXSegmenter.test.js
index ee70d91..f1b2aea 100644
--- a/tests/segmentation/CXSegmenter.test.js
+++ b/tests/segmentation/CXSegmenter.test.js
@@ -1,27 +1,49 @@
QUnit.module( 'CXSegmenter' );
var fs = require( 'fs' );
+
+function normalize( html ) {
+ var normalizer = new CX.LinearDoc.Normalizer();
+ normalizer.init();
+ normalizer.write( html.replace( /(\r\n|\n|\t|\r)/gm, '' ) );
+ return normalizer.getHtml();
+}
+
QUnit.test( 'Segmentation tests', function ( assert ) {
- var i, len, lang, test, tests, segmenter, result,
- count = 0,testData,expectedResultData,
+ var i, len, lang, test, tests, segmenter, result, testData,
expectedResultData,
+ count = 0,
+ skipCount = 0,
allTests = require( './SegmentationTests.json' );
for ( lang in allTests ) {
- count += allTests[ lang ].length;
+ for ( i = 0, len = allTests[ lang ].length; i < len; i++ ) {
+ if ( allTests[ lang ][ i ].skip ) {
+ skipCount += 1;
+ } else {
+ count += 1;
+ }
+ }
}
QUnit.expect( count );
for ( lang in allTests ) {
tests = allTests[ lang ];
for ( i = 0, len = allTests[ lang ].length; i < len; i++ ) {
test = tests[ i ];
+ if ( test.skip ) {
+ continue;
+ }
testData = fs.readFileSync( __dirname + '/data/' +
test.source, 'utf8' );
+
segmenter = new CX.Segmenter( testData, lang );
segmenter.segment();
- result = segmenter.getSegmentedContent();
- result = result.replace( /(\r\n|\n|\t|\r)/gm, '' );
- expectedResultData = fs.readFileSync( __dirname +
'/data/' + test.result, 'utf8' );
- expectedResultData = expectedResultData.replace(
/(\r\n|\n|\t|\r)/gm, '' );
- assert.strictEqual( result, expectedResultData,
test.desc || '' );
+ result = normalize( segmenter.getSegmentedContent() );
+ expectedResultData = normalize(
+ fs.readFileSync( __dirname + '/data/' +
test.result, 'utf8' )
+ );
+ assert.strictEqual( result, expectedResultData,
test.source + ': ' + test.desc || '' );
}
}
+ if ( skipCount > 0 ) {
+ console.warn( 'Skipped ' + skipCount + ' tests' );
+ }
} );
diff --git a/tests/segmentation/SegmentationTests.json
b/tests/segmentation/SegmentationTests.json
index aec60ca..527a1b5 100644
--- a/tests/segmentation/SegmentationTests.json
+++ b/tests/segmentation/SegmentationTests.json
@@ -13,7 +13,8 @@
{
"desc": "Exclamation, punctuation test",
"source": "test-2.html",
- "result": "result-2.html"
+ "result": "result-2.html",
+ "skip": true
},
{
"desc": "Multiple sentences",
@@ -66,7 +67,7 @@
"result": "result-12.html"
},
{
- "desc": "References can appear after period and space.
Example: Hydrogen is a gas. [1] It is .... In this case we dont have any choice
than considering [1] as part of second sentence",
+ "desc": "References can appear after period and space.
Example: Hydrogen is a gas. [1] It is ...",
"source": "test-13.html",
"result": "result-13.html"
},
@@ -81,9 +82,19 @@
"result": "result-15.html"
},
{
+ "desc": "Three sentences in a single text node",
+ "source": "test-16.html",
+ "result": "result-16.html"
+ },
+ {
"desc": "Paragraph ending with reference and already
having reference in between. The second reference should not be identified as
repeating reference. If identified as repeating reference, 7 letters from last
word will be missing.",
"source":
"test-ends-with-references-missing-letters.html",
"result":
"result-ends-with-references-missing-letters.html"
+ },
+ {
+ "desc": "Sentences ending with abbreviations",
+ "source": "test-17.html",
+ "result": "result-17.html"
}
],
"hi": [
diff --git a/tests/segmentation/data/result-10.html
b/tests/segmentation/data/result-10.html
index 313e71c..39d3ef6 100644
--- a/tests/segmentation/data/result-10.html
+++ b/tests/segmentation/data/result-10.html
@@ -1,6 +1,6 @@
<p id="0">
<span class="cx-segment" data-segmentid="1">Sentence one
<span class="reference">
- <a class="cx-link" data-linkid="2" href="#">1</a>
+ <a href="#">1</a>
</span>and rest of sentence</span>
</p>
diff --git a/tests/segmentation/data/result-11.html
b/tests/segmentation/data/result-11.html
index 969c3c1..ebb8480 100644
--- a/tests/segmentation/data/result-11.html
+++ b/tests/segmentation/data/result-11.html
@@ -1,12 +1,12 @@
<p id="0">
<span class="cx-segment" data-segmentid="1">
Hydrogen's atomic number is one.<span class="reference">
- <a class="cx-link" data-linkid="2" href="#">1</a>
+ <a href="#">1</a>
</span>
</span>
- <span class="cx-segment" data-segmentid="3">Hydrogen is a gas
+ <span class="cx-segment" data-segmentid="2">Hydrogen is a gas
<span class="reference">
- <a class="cx-link" data-linkid="4" href="#">2</a>
+ <a href="#">2</a>
</span>and it is
</span>
</p>
diff --git a/tests/segmentation/data/result-12.html
b/tests/segmentation/data/result-12.html
index 7c0156a..25c6e70 100644
--- a/tests/segmentation/data/result-12.html
+++ b/tests/segmentation/data/result-12.html
@@ -1,11 +1,11 @@
<p id="0">
<span class="cx-segment" data-segmentid="1">Sentence one <span
class="reference">
- <a class="cx-link" data-linkid="2" href="#">1</a>
+ <a href="#">1</a>
</span>
<span class="reference">
- <a class="cx-link" data-linkid="3" href="#">2</a>
+ <a href="#">2</a>
</span>
<span class="reference">
- <a class="cx-link" data-linkid="4" href="#">3</a>
+ <a href="#">3</a>
</span> and rest of sentence</span>
</p>
diff --git a/tests/segmentation/data/result-13.html
b/tests/segmentation/data/result-13.html
index 26ec46d..fafff5f 100644
--- a/tests/segmentation/data/result-13.html
+++ b/tests/segmentation/data/result-13.html
@@ -1,7 +1,8 @@
<p id="0">
- <span class="cx-segment" data-segmentid="1">Sentence one. </span>
- <span class="cx-segment" data-segmentid="2">
- <span class="reference">
- <a class="cx-link" data-linkid="3"
href="#">reference</a>
- </span> Starts with reference</span>
+ <span class="cx-segment" data-segmentid="1">Sentence one.
+ <span class="reference">
+ <a href="#">reference</a>
+ </span>
+ </span>
+ <span class="cx-segment" data-segmentid="2">Starts with reference</span>
</p>
diff --git a/tests/segmentation/data/result-15.html
b/tests/segmentation/data/result-15.html
index b36bb5b..42ab78a 100644
--- a/tests/segmentation/data/result-15.html
+++ b/tests/segmentation/data/result-15.html
@@ -1 +1 @@
-<p id="0"><span class="cx-segment" data-segmentid="1">When the GNU project
first started they "had an <a class="cx-link" data-linkid="2"
href="/wiki/Emacs" title="Emacs">Emacs</a> text editor with <a class="cx-link"
data-linkid="3" href="/wiki/Lisp_(programming_language)" title="Lisp
(programming language)">Lisp</a> for writing editor commands, a source level <a
class="cx-link" data-linkid="4" href="/wiki/Debugger"
title="Debugger">debugger</a>, a <a class="cx-link" data-linkid="5"
href="/wiki/Yacc" title="Yacc" data-original-title="">yacc</a>-compatible <a
class="cx-link" data-linkid="6" href="/wiki/Parsing" title="Parsing">parser</a>
generator, and a <a class="cx-link" data-linkid="7"
href="/wiki/Linker_(computing)" title="Linker (computing)">linker</a>".<span
id="cite_ref-4" class="reference"><a class="cx-link" data-linkid="8"
href="#cite_note-4">[4]</a></span></span><span class="cx-segment"
data-segmentid="9"> The GNU system required its own C compiler and tools to be
free software, so that these also had to be developed. </span><span
class="cx-segment" data-segmentid="10">By June 1987 the project had accumulated
and developed free software for an assembler, an almost finished portable
optimizing C compiler (<a class="cx-link" data-linkid="11"
href="/wiki/GNU_Compiler_Collection" title="GNU Compiler Collection"
data-original-title="">GCC</a>), an editor (<a class="cx-link" data-linkid="12"
href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various Unix utilities
(such as <code>ls</code>, <code>grep</code>, <code>awk</code>,
<code>make</code> and <code>ld</code>).<span id="cite_ref-5"
class="reference"><a class="cx-link" data-linkid="13" href="#cite_note-5"
title="" data-original-title="">[5]</a></span></span><span class="cx-segment"
data-segmentid="14"> They had an initial kernel that needed more
updates.</span></p>
+<p id="0"><span class="cx-segment" data-segmentid="1">When the GNU project
first started they "had an <a class="cx-link" data-linkid="2"
href="/wiki/Emacs" title="Emacs">Emacs</a> text editor with <a class="cx-link"
data-linkid="3" href="/wiki/Lisp_(programming_language)" title="Lisp
(programming language)">Lisp</a> for writing editor commands, a source level <a
class="cx-link" data-linkid="4" href="/wiki/Debugger"
title="Debugger">debugger</a>, a <a class="cx-link" data-linkid="5"
href="/wiki/Yacc" title="Yacc" data-original-title="">yacc</a>-compatible <a
class="cx-link" data-linkid="6" href="/wiki/Parsing" title="Parsing">parser</a>
generator, and a <a class="cx-link" data-linkid="7"
href="/wiki/Linker_(computing)" title="Linker (computing)">linker</a>".<span
id="cite_ref-4" class="reference"><a href="#cite_note-4">[4]</a></span>
</span><span class="cx-segment" data-segmentid="8">The GNU system required its
own C compiler and tools to be free software, so that these also had to be
developed. </span><span class="cx-segment" data-segmentid="9">By June 1987 the
project had accumulated and developed free software for an assembler, an almost
finished portable optimizing C compiler (<a class="cx-link" data-linkid="10"
href="/wiki/GNU_Compiler_Collection" title="GNU Compiler Collection"
data-original-title="">GCC</a>), an editor (<a class="cx-link" data-linkid="11"
href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various Unix utilities
(such as <code>ls</code>, <code>grep</code>, <code>awk</code>,
<code>make</code> and <code>ld</code>).<span id="cite_ref-5"
class="reference"><a href="#cite_note-5" title=""
data-original-title="">[5]</a></span> </span><span class="cx-segment"
data-segmentid="12">They had an initial kernel that needed more
updates.</span></p>
diff --git a/tests/segmentation/data/result-16.html
b/tests/segmentation/data/result-16.html
new file mode 100644
index 0000000..4e80057
--- /dev/null
+++ b/tests/segmentation/data/result-16.html
@@ -0,0 +1 @@
+<p id="0"><span class="cx-segment" data-segmentid="1">Yes. </span><span
class="cx-segment" data-segmentid="2">No. </span><span class="cx-segment"
data-segmentid="3">Maybe.</span></p>
diff --git a/tests/segmentation/data/result-17.html
b/tests/segmentation/data/result-17.html
new file mode 100644
index 0000000..7906c02
--- /dev/null
+++ b/tests/segmentation/data/result-17.html
@@ -0,0 +1 @@
+<p id="0"><span class="cx-segment" data-segmentid="1">Some in the UK.
</span><span class="cx-segment" data-segmentid="2">Others in the US.</span></p>
diff --git a/tests/segmentation/data/result-7.html
b/tests/segmentation/data/result-7.html
index 9e104ec..f4a5e67 100644
--- a/tests/segmentation/data/result-7.html
+++ b/tests/segmentation/data/result-7.html
@@ -1,7 +1,7 @@
<figure id="0">
<span class="cx-segment" data-segmentid="1">
<a class="cx-link" data-linkid="2" href="#">
- <img src="img.png"></img>
+ <img src="img.png"/>
</a>
</span>
<figcaption id="3">
diff --git a/tests/segmentation/data/result-8.html
b/tests/segmentation/data/result-8.html
index 6996f4d..301387c 100644
--- a/tests/segmentation/data/result-8.html
+++ b/tests/segmentation/data/result-8.html
@@ -1,8 +1,8 @@
<p id="0">
<span class="cx-segment" data-segmentid="1">Sentence one.
<span class="reference">
- <a class="cx-link" data-linkid="2"
href="#">reference</a>
+ <a href="#">reference</a>
</span>
- </span>
- <span class="cx-segment" data-segmentid="3"> Starts with
reference</span>
+ </span>
+ <span class="cx-segment" data-segmentid="2">Starts with reference</span>
</p>
diff --git a/tests/segmentation/data/result-9.html
b/tests/segmentation/data/result-9.html
index 91f99b4..e86493c 100644
--- a/tests/segmentation/data/result-9.html
+++ b/tests/segmentation/data/result-9.html
@@ -1,14 +1,14 @@
<p id="0">
<span class="cx-segment" data-segmentid="1">Sentence one.
<span class="reference">
- <a class="cx-link" data-linkid="2" href="#">1</a>
+ <a href="#">1</a>
</span>
<span class="reference">
- <a class="cx-link" data-linkid="3" href="#">2</a>
+ <a href="#">2</a>
</span>
<span class="reference">
- <a class="cx-link" data-linkid="4" href="#">3</a>
+ <a href="#">3</a>
</span>
- </span>
- <span class="cx-segment" data-segmentid="5"> Starts with
reference</span>
+ </span>
+ <span class="cx-segment" data-segmentid="2">Starts with reference</span>
</p>
diff --git a/tests/segmentation/data/result-debian-1.html
b/tests/segmentation/data/result-debian-1.html
index afdde28..b8cb1c8 100644
--- a/tests/segmentation/data/result-debian-1.html
+++ b/tests/segmentation/data/result-debian-1.html
@@ -7,18 +7,18 @@
<a class="cx-link" data-linkid="4" rel="mw:WikiLink"
href="./Xfce">Xfce</a>and
<a class="cx-link" data-linkid="5" rel="mw:WikiLink"
href="./LXDE">LXDE</a>.
<span about="#mwt72" class="reference">
- <a class="cx-link" data-linkid="6"
href="#cite_note-25">[25]</a>
+ <a href="#cite_note-25">[25]</a>
</span>
<span about="#mwt72" class="reference">
- <a class="cx-link" data-linkid="7"
href="#cite_note-25">[26]</a>
+ <a href="#cite_note-25">[26]</a>
</span>
- </span>
- <span class="cx-segment" data-segmentid="8"> Less common
- <a class="cx-link" data-linkid="9" rel="mw:WikiLink"
href="./Window_manager">window managers</a>such as
- <a class="cx-link" data-linkid="10" rel="mw:WikiLink"
href="./Enlightenment_(window_manager)">Enlightenment</a>,
- <a class="cx-link" data-linkid="11" rel="mw:WikiLink"
href="./Openbox">Openbox</a>,
- <a class="cx-link" data-linkid="12" rel="mw:WikiLink"
href="./Fluxbox">Fluxbox</a>,
- <a class="cx-link" data-linkid="13" rel="mw:WikiLink"
href="./GNUstep">GNUstep</a>,
- <a class="cx-link" data-linkid="14" rel="mw:WikiLink"
href="./IceWM">IceWM</a>,
- <a class="cx-link" data-linkid="15" rel="mw:WikiLink"
href="./Window_Maker">Window Maker</a>and others can also be installed.</span>
+ </span>
+ <span class="cx-segment" data-segmentid="6">Less common
+ <a class="cx-link" data-linkid="7" rel="mw:WikiLink"
href="./Window_manager">window managers</a>such as
+ <a class="cx-link" data-linkid="8" rel="mw:WikiLink"
href="./Enlightenment_(window_manager)">Enlightenment</a>,
+ <a class="cx-link" data-linkid="9" rel="mw:WikiLink"
href="./Openbox">Openbox</a>,
+ <a class="cx-link" data-linkid="10" rel="mw:WikiLink"
href="./Fluxbox">Fluxbox</a>,
+ <a class="cx-link" data-linkid="11" rel="mw:WikiLink"
href="./GNUstep">GNUstep</a>,
+ <a class="cx-link" data-linkid="12" rel="mw:WikiLink"
href="./IceWM">IceWM</a>,
+ <a class="cx-link" data-linkid="13" rel="mw:WikiLink"
href="./Window_Maker">Window Maker</a>and others can also be installed.</span>
</p>
diff --git
a/tests/segmentation/data/result-ends-with-references-missing-letters.html
b/tests/segmentation/data/result-ends-with-references-missing-letters.html
index 4af0e04..a7e516f 100644
--- a/tests/segmentation/data/result-ends-with-references-missing-letters.html
+++ b/tests/segmentation/data/result-ends-with-references-missing-letters.html
@@ -1 +1 @@
-<p id="0"><span class="cx-segment" data-segmentid="1">By June 1987 the project
had accumulated and developed free software for an assembler, an almost
finished portable optimizing C compiler (<a class="cx-link" data-linkid="2"
href="/wiki/GNU_Compiler_Collection" title="GNU Compiler Collection"
data-original-title="">GCC</a>), an editor (<a class="cx-link" data-linkid="3"
href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various Unix utilities
(such as ls, grep, awk, make and ld).<span id="cite_ref-5" class="reference"><a
class="cx-link" data-linkid="4" href="#cite_note-5" title=""
data-original-title="">[5]</a></span></span><span class="cx-segment"
data-segmentid="5"> They had an initial kernel that needed more updates.
</span><span class="cx-segment" data-segmentid="6">By June 1987 the project had
accumulated and developed free software for an assembler, an almost finished
portable optimizing C compiler (<a class="cx-link" data-linkid="7"
href="/wiki/GNU_Compiler_Collection" title="GNU Compiler Collection"
data-original-title="">GCC</a>), an editor (<a class="cx-link" data-linkid="8"
href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various Unix utilities
(such as ls, grep, awk, make and ld.<span id="cite_ref-5" class="reference"><a
class="cx-link" data-linkid="9" href="#cite_note-5" title=""
data-original-title="">[6]</a></span></span></p>
+<p id="0"><span class="cx-segment" data-segmentid="1">By June 1987 the project
had accumulated and developed free software for an assembler, an almost
finished portable optimizing C compiler (<a class="cx-link" data-linkid="2"
href="/wiki/GNU_Compiler_Collection" title="GNU Compiler Collection"
data-original-title="">GCC</a>), an editor (<a class="cx-link" data-linkid="3"
href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various Unix utilities
(such as ls, grep, awk, make and ld).<span id="cite_ref-5" class="reference"><a
href="#cite_note-5" title="" data-original-title="">[5]</a></span> </span><span
class="cx-segment" data-segmentid="4">They had an initial kernel that needed
more updates. </span><span class="cx-segment" data-segmentid="5">By June 1987
the project had accumulated and developed free software for an assembler, an
almost finished portable optimizing C compiler (<a class="cx-link"
data-linkid="6" href="/wiki/GNU_Compiler_Collection" title="GNU Compiler
Collection" data-original-title="">GCC</a>), an editor (<a class="cx-link"
data-linkid="7" href="/wiki/Emacs" title="Emacs">GNU Emacs</a>), and various
Unix utilities (such as ls, grep, awk, make and ld.<span id="cite_ref-5"
class="reference"><a href="#cite_note-5" title=""
data-original-title="">[6]</a></span></span></p>
diff --git a/tests/segmentation/data/test-16.html
b/tests/segmentation/data/test-16.html
new file mode 100644
index 0000000..9fb51f0
--- /dev/null
+++ b/tests/segmentation/data/test-16.html
@@ -0,0 +1 @@
+<p>Yes. No. Maybe.</p>
diff --git a/tests/segmentation/data/test-17.html
b/tests/segmentation/data/test-17.html
new file mode 100644
index 0000000..26d3af2
--- /dev/null
+++ b/tests/segmentation/data/test-17.html
@@ -0,0 +1 @@
+<p>Some in the UK. Others in the US.</p>
diff --git a/tests/segmentation/data/test-7.html
b/tests/segmentation/data/test-7.html
index fda9595..f6f3340 100644
--- a/tests/segmentation/data/test-7.html
+++ b/tests/segmentation/data/test-7.html
@@ -1,6 +1,6 @@
<figure>
<a href="#">
- <img src="img.png">
+ <img src="img.png"/>
</a>
<figcaption>Figure caption</figcaption>
</figure>
--
To view, visit https://gerrit.wikimedia.org/r/138532
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iee6bfa499c9f23269a290df5de5e027e48746971
Gerrit-PatchSet: 8
Gerrit-Project: mediawiki/services/cxserver
Gerrit-Branch: master
Gerrit-Owner: Divec <[email protected]>
Gerrit-Reviewer: Divec <[email protected]>
Gerrit-Reviewer: Santhosh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits