Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 43 additions & 43 deletions GANReviewTool/modules/GARCloserWikicodeGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,27 @@ __TOC__`;

processDelistForArticle( wikicode ) {
const gaTemplateNames = [ 'ga icon', 'ga article', 'good article' ];
const templateFinder = new TemplateFinder( wikicode );
for ( const templateName of gaTemplateNames ) {
const template = templateFinder.firstTemplate( templateName );
if ( !template ) {
continue;
}
const { previousSibling, nextSibling } = template;
template.remove();
// handle lots of line breaks: \n\n{{templateName}}\n\n -> \n\n
let regex = new RegExp( '\\n\\n\\{\\{' + templateName + '\\}\\}\\n\\n', 'i' );
wikicode = wikicode.replace( regex, '\n\n' );

// handle normal: {{templateName}}\n -> '', {{templateName}} -> ''
regex = new RegExp( '\\{\\{' + templateName + '\\}\\}\\n?', 'i' );
wikicode = wikicode.replace( regex, '' );
if (
previousSibling && previousSibling.type === 'text' && previousSibling.data.endsWith( '\n\n' ) &&
nextSibling && nextSibling.type === 'text' && nextSibling.data.startsWith( '\n\n' )
) {
nextSibling.deleteData( 0, 2 );

// handle normal: {{templateName}}\n -> '', {{templateName}} -> ''
} else if ( nextSibling && nextSibling.type === 'text' && nextSibling.data.startsWith( '\n' ) ) {
nextSibling.deleteData( 0, 1 );
}
}
return wikicode;
return templateFinder.getWikitext();
}

processDelistForGAList( wikicode, articleToRemove ) {
Expand Down Expand Up @@ -288,8 +299,14 @@ __TOC__`;
'weapons and buildings': 'Wikipedia:Good articles/Warfare',
weapons: 'Wikipedia:Good articles/Warfare'
};
let topic = wikicode.match( /(?:\{\{Article ?history|\{\{GA\s*(?=\|)).*?\|\s*(?:sub)?topic\s*=\s*([^|}\n]+)/is )[ 1 ];
topic = topic.toLowerCase().trim();
const templateFinder = new TemplateFinder( wikicode );
const templates = templateFinder.getTemplates( [ 'Article history', 'Articlehistory', 'GA' ] );
const template = templates.find( ( t ) => t.getArgs( 'topic' ).size || t.getArgs( 'subtopic' ).size );
let topic = template.getValue( 'topic' );
if ( topic === undefined ) {
topic = template.getValue( 'subtopic' );
}
topic = topic.toLowerCase();
const gaListTitle = dictionary[ topic ];
// throw the error a little later rather than now. that way it doesn't interrupt modifying the article talk page.
return gaListTitle;
Expand Down Expand Up @@ -374,16 +391,9 @@ __TOC__`;
}

removeTemplate( templateName, wikicode ) {
const regex = new RegExp( `\\{\\{${ this.regExEscape( templateName ) }[^\\}]*\\}\\}\\n?`, 'i' );
return wikicode.replace( regex, '' );
}

regexGetFirstMatchString( regex, haystack ) {
const matches = haystack.match( regex );
if ( matches !== null && matches[ 1 ] !== undefined ) {
return matches[ 1 ];
}
return null;
const templateFinder = new TemplateFinder( wikicode );
templateFinder.deleteTemplate( templateName );
return templateFinder.getWikitext();
}

/**
Expand All @@ -392,15 +402,15 @@ __TOC__`;
convertGATemplateToArticleHistoryIfPresent( talkPageTitle, wikicode ) {
const templateFinder = new TemplateFinder( wikicode );
const hasArticleHistory = templateFinder.hasTemplate( 'Article ?history' );
const gaTemplateWikicode = this.regexGetFirstMatchString( /(\{\{GA[^}]*\}\})/i, wikicode );
if ( !hasArticleHistory && gaTemplateWikicode ) {
const gaTemplate = templateFinder.firstTemplate( 'GA' );
if ( !hasArticleHistory && gaTemplate ) {
// delete {{ga}} template
templateFinder.deleteTemplate( 'GA' );
wikicode = templateFinder.getWikitext().trim();

// parse its parameters
// example: |21:00, 12 March 2017 (UTC)|topic=Sports and recreation|page=1|oldid=769997774
const parameters = this.getParametersFromTemplateWikicode( gaTemplateWikicode );
const parameters = this.getParametersFromTemplateWikicode( gaTemplate );

// if no page specified, assume page is 1. so then the good article review link will be parsed as /GA1
const noPageSpecified = parameters.page === undefined;
Expand Down Expand Up @@ -547,28 +557,18 @@ __TOC__`;
/**
* @return {Object} Parameters, with keys being equivalent to the template parameter names. Unnamed parameters will be 1, 2, 3, etc.
*/
getParametersFromTemplateWikicode( wikicodeOfSingleTemplate ) {
wikicodeOfSingleTemplate = wikicodeOfSingleTemplate.slice( 2, -2 ); // remove {{ and }}
// TODO: explode without exploding | inside of inner templates
const strings = wikicodeOfSingleTemplate.split( '|' );
getParametersFromTemplateWikicode( template ) {
if ( typeof template === 'string' ) {
const templateFinder = new TemplateFinder( template );
template = templateFinder.firstTemplate();
}
if ( template.type !== 'template' ) {
throw new Error( `InvalidArgumentException: ${ template.type }
${ template }` );
}
const parameters = {};
let unnamedParameterCount = 1;
let i = 0;
for ( const string of strings ) {
i++;
if ( i == 1 ) {
continue; // skip the template name, this is not a parameter
}
const hasEquals = string.indexOf( '=' );
if ( hasEquals === -1 ) {
parameters[ unnamedParameterCount ] = string;
unnamedParameterCount++;
} else {
const matches = string.match( /^([^=]*)=(.*)/s ); // isolate param name and param value by looking for first equals sign
const paramName = matches[ 1 ].trim().toLowerCase();
const paramValue = matches[ 2 ].trim();
parameters[ paramName ] = paramValue;
}
for ( const parameter of template.getAllArgs() ) {
parameters[ parameter.name.toLowerCase() ] = parameter.getValue();
}
return parameters;
}
Expand Down
32 changes: 11 additions & 21 deletions GANReviewTool/modules/MassGARWikicodeGenerator.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TemplateFinder } from './TemplateFinder.js';

export class MassGARWikicodeGenerator {
hasGoodArticleTemplate( mainArticleWikicode ) {
const gaTemplateNames = [ 'ga icon', 'ga article', 'good article' ];
Expand All @@ -12,12 +14,14 @@ export class MassGARWikicodeGenerator {
}

// Check for {{Article history|currentstatus=GA}}
// TODO: currently just checks for |currentstatus=GA anywhere on the page. Could improve this algorithm if there end up being false positives.
const matches = talkPageWikicode.match( /\|\s*currentstatus\s*=\s*GA\b/i );
if ( matches ) {
return true;
const templateFinder = new TemplateFinder( talkPageWikicode );
const aliases = [ 'Articlehistory', 'Article milestones', 'Articlemilestones', 'Article History', 'ArticleHistory' ];
const articleHistoryTemplate = templateFinder.firstTemplate( aliases );
if ( !articleHistoryTemplate ) {
return false;
}
return false;
const value = articleHistoryTemplate.getValue( 'currentstatus' );
return Boolean( value ) && value.toUpperCase() === 'GA';
}

hasOpenGAR( talkPageWikicode ) {
Expand All @@ -30,21 +34,7 @@ export class MassGARWikicodeGenerator {
* @param {Array} listOfTemplates Case insensitive.
*/
_wikicodeHasTemplate( wikicode, listOfTemplates ) {
const stringForRegEx = listOfTemplates
.map( ( v ) => this._regExEscape( v ) )
.join( '|' );
const regex = new RegExp( `{{(?:${ stringForRegEx })\\b`, 'i' );
const matches = wikicode.match( regex );
if ( matches ) {
return true;
}
return false;
}

/**
* @copyright coolaj86, CC BY-SA 4.0 https://stackoverflow.com/a/6969486/3480193
*/
_regExEscape( string ) {
return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ); // $& means the whole matched string
const templateFinder = new TemplateFinder( wikicode );
return templateFinder.hasTemplate( listOfTemplates );
}
}
14 changes: 11 additions & 3 deletions GANReviewTool/modules/TemplateFinder.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,18 @@ export class TemplateFinder {
}
}

getTemplates( templateNameCaseInsensitive ) {
const templateName = `template:${ templateNameCaseInsensitive.toLowerCase().replace( / /g, '_' ) }`;
/**
* @param {string|Array} templateNamesCaseInsensitive
*/
getTemplates( templateNamesCaseInsensitive ) {
if ( typeof templateNamesCaseInsensitive === 'string' ) {
templateNamesCaseInsensitive = [ templateNamesCaseInsensitive ];
}
const templateNames = templateNamesCaseInsensitive.map(
( t ) => `template:${ t.toLowerCase().replace( / /g, '_' ) }`
);
return this.wikiPage.querySelectorAll( 'template' )
.filter( ( { name } ) => name.toLowerCase() === templateName );
.filter( ( { name } ) => templateNames.includes( name.toLowerCase() ) );
}

deleteTemplate( templateNameRegExOrArrayCaseInsensitive ) {
Expand Down
39 changes: 8 additions & 31 deletions GANReviewTool/tests/GARCloserWikicodeGenerator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ describe( 'getGAListTitleFromTalkPageWikicode(wikicode)', () => {
} );

it( 'Should be case insensitive', () => {
const wikicode = '{{aRTiClE HiStOrY|ToPiC=SpOrTs}}';
const wikicode = '{{aRTiClE HiStOrY|topic=SpOrTs}}';
const output = 'Wikipedia:Good articles/Sports and recreation';
expect( wg.getGAListTitleFromTalkPageWikicode( wikicode ) ).toBe( output );
} );
Expand Down Expand Up @@ -1459,36 +1459,6 @@ describe( 'deleteMiddleOfString(string, deleteStartPosition, deleteEndPosition)'
} );
} );

describe( 'regexGetFirstMatchString(regex, haystack)', () => {
test( 'match', () => {
const regex = /hello ([^ ]+)/;
const haystack = 'hello test goodbye';
const result = wg.regexGetFirstMatchString( regex, haystack );
expect( result ).toBe( 'test' );
} );

test( 'no match', () => {
const regex = /hello (bob)/;
const haystack = 'hello test goodbye';
const result = wg.regexGetFirstMatchString( regex, haystack );
expect( result ).toBe( null );
} );

test( 'no capture group, no match', () => {
const regex = /hello bob/;
const haystack = 'hello test goodbye';
const result = wg.regexGetFirstMatchString( regex, haystack );
expect( result ).toBe( null );
} );

test( 'no capture group, yes match', () => {
const regex = /hello bob/;
const haystack = 'hello bob';
const result = wg.regexGetFirstMatchString( regex, haystack );
expect( result ).toBe( null );
} );
} );

describe( 'convertGATemplateToArticleHistoryIfPresent(talkPageTitle, wikicode)', () => {
it( 'should default to page=1 when no page parameter', () => {
const talkPageTitle = 'Talk:Test';
Expand Down Expand Up @@ -1895,4 +1865,11 @@ describe( 'removeTemplate( templateName, wikicode )', () => {
const expected = `Test `;
expect( wg.removeTemplate( templateName, wikicode ) ).toBe( expected );
} );

it( '1 template with params + nested template', () => {
const templateName = 'GA';
const wikicode = `Test {{GA|date={{formatdate|2025-12-20}}}}`;
const expected = `Test `;
expect( wg.removeTemplate( templateName, wikicode ) ).toBe( expected );
} );
} );
7 changes: 7 additions & 0 deletions GANReviewTool/tests/MassGARWikicodeGenerator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,13 @@ describe( '_wikicodeHasTemplate( wikicode, listOfTemplates )', () => {
expect( wg._wikicodeHasTemplate( wikicode, listOfTemplates ) ).toBe( expected );
} );

it( '1 template in haystack, 0 templates to search for', () => {
const wikicode = `Test {{GA}}`;
const listOfTemplates = [];
const expected = false;
expect( wg._wikicodeHasTemplate( wikicode, listOfTemplates ) ).toBe( expected );
} );

it( '1 template in haystack, 1 template to search for', () => {
const wikicode = `Test {{GA}}`;
const listOfTemplates = [ 'GA' ];
Expand Down