jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/368800 )

Change subject: Image adaptation with namespace alias change
......................................................................


Image adaptation with namespace alias change

* Adapts an image if the image is from commons.
* Introduces NamespaceAlias request and caching mechanism.
* Image alignment classes are flipped based on language directions.
* To check language directionality, a node module language-data is used.
* Tests included.

language-data node module is a fresh node module written based
on jquery.uls by porting the data and utilities to nodejs.
It is not yet published in npm. Temporarily hosted at
github.com/santhoshtr/language-data, and wish to host under
wikimedia repo.

Bug: T170674
Change-Id: I288f67fbe94371ee8039ecdaf69984e51aa43511
---
M lib/mw/APIRequestManager.js
M lib/mw/ApiRequest.js
A lib/translationunits/MWImage.js
M lib/translationunits/index.js
M package.json
M test/adaptation/AdaptationTests.json
6 files changed, 149 insertions(+), 7 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/lib/mw/APIRequestManager.js b/lib/mw/APIRequestManager.js
index 1e8f673..e8d0739 100644
--- a/lib/mw/APIRequestManager.js
+++ b/lib/mw/APIRequestManager.js
@@ -1,4 +1,5 @@
-const TitlePairRequest = require( './TitlePairRequest.js' );
+const TitlePairRequest = require( './TitlePairRequest.js' ),
+       APIRequest = require( './ApiRequest.js' );
 
 class MWAPIRequestManager {
        constructor( appContext ) {
@@ -27,6 +28,31 @@
 
                return instance.get( title );
        }
+
+       /**
+        * For a given canonical namespace, get the namespace alias in given 
target language.
+        * @param {string} canonicalNamespace Canonical namespace. Example: 
File, Talk, Special etc.
+        *   See https://www.mediawiki.org/wiki/Help:Namespaces
+        * @param {string} targetLanguage Target language code
+        * @return {Promise}
+        */
+       getNamespaceAlias( canonicalNamespace, targetLanguage ) {
+               var instance = new APIRequest( { targetLanguage, context: 
this.context } );
+
+               // SiteInfo is cached in APIRequest, so we are not adding any 
caching here.
+               return instance.getSiteInfo( targetLanguage ).then( ( siteInfo 
) => {
+                       var namespaceId;
+                       for ( namespaceId in siteInfo.namespaces ) {
+                               if ( siteInfo.namespaces.hasOwnProperty( 
namespaceId ) ) {
+                                       if ( siteInfo.namespaces[ namespaceId 
].canonical === canonicalNamespace ) {
+                                               return siteInfo.namespaces[ 
namespaceId ].name;
+                                       }
+                               }
+                       }
+                       // Fallback
+                       return canonicalNamespace;
+               } );
+       }
 }
 
 /**
diff --git a/lib/mw/ApiRequest.js b/lib/mw/ApiRequest.js
index 845d5d0..4bda2b5 100644
--- a/lib/mw/ApiRequest.js
+++ b/lib/mw/ApiRequest.js
@@ -10,8 +10,8 @@
        /**
         * @param {Object} config Configuration options
         * @cfg {Object} context Application context
-        * @cfg {string} sourceLanguage Source language
-        * @cfg {string} targetLanguage target language
+        * @cfg {string} [sourceLanguage] Source language
+        * @cfg {string} [targetLanguage] target language
         */
        constructor( config ) {
                this.context = config.context;
diff --git a/lib/translationunits/MWImage.js b/lib/translationunits/MWImage.js
new file mode 100644
index 0000000..e3b24ca
--- /dev/null
+++ b/lib/translationunits/MWImage.js
@@ -0,0 +1,93 @@
+const cxutil = require( '../util.js' ),
+       TranslationUnit = require( './TranslationUnit.js' ),
+       languageData = require( 'language-data' ),
+       MWAPIRequestManager = require( '../mw/APIRequestManager.js' ),
+       CommonsFilePatchPrefix = '//upload.wikimedia.org/wikipedia/commons/';
+
+class MWImage extends TranslationUnit {
+       constructor( node, sourceLanguage, targetLanguage, context ) {
+               super( node, sourceLanguage, targetLanguage, context );
+       }
+
+       /**
+        * Check if an image is coming from Commons or not. Uses the URL 
pattern of the common file
+        * repository to determine whether the image is stored there.
+        * @param {string} imageSrc
+        * @return {boolean}
+        */
+       isCommonsImage( imageSrc ) {
+               return imageSrc.indexOf( CommonsFilePatchPrefix ) === 0;
+       }
+
+       /**
+        * Adapt the image's alignment settings for the target language.
+        */
+       adaptImageAlignment() {
+               var classes, targetDirection, sourceDirection, leftIndex, 
rightIndex;
+
+               sourceDirection = languageData.getDir( this.sourceLanguage );
+               targetDirection = languageData.getDir( this.targetLanguage );
+
+               if ( sourceDirection === targetDirection ) {
+                       // Source and target languages has same directionality. 
Nothing to do
+                       return;
+               }
+
+               classes = this.node.attributes.class.split( ' ' );
+               // If the image has an explicit alignment class in HTML, this 
means that it has explicit
+               // alignment defined in wiki syntax. It must be explicitly 
flipped if the target language's
+               // direction is different.
+               leftIndex = classes.indexOf( 'mw-halign-left' );
+               rightIndex = classes.indexOf( 'mw-halign-right' );
+               if ( leftIndex > -1 ) {
+                       classes[ leftIndex ] = 'mw-halign-right';
+               } else if ( rightIndex > -1 ) {
+                       classes[ rightIndex ] = 'mw-halign-left';
+               }
+
+               this.node.attributes.class = classes.join( ' ' );
+       }
+}
+
+MWImage.prototype.adapt = cxutil.async( function* () {
+       var i, len, chunk, sourceImage, imageLink, targetResource, 
namespaceAlias;
+
+       for ( i = 0, len = this.node.children.textChunks.length; i < len; i++ ) 
{
+               chunk = this.node.children.textChunks[ i ];
+               if ( chunk.tags[ 0 ].name === 'a' ) {
+                       imageLink = chunk.tags[ 0 ];
+               }
+               if ( chunk.inlineContent && chunk.inlineContent.name === 'img' 
) {
+                       sourceImage = chunk.inlineContent;
+                       break;
+               }
+       }
+
+       if ( !sourceImage ) {
+               throw new Error( 'img tag not found in the figure with 
mw:Image/Thumb for id: ' + this.node.attributes.id );
+       }
+
+       this.sourceResource = sourceImage.attributes[ 'resource' ];
+       this.adaptImageAlignment();
+
+       if ( this.isCommonsImage( sourceImage.attributes[ 'src' ] ) ) {
+               namespaceAlias = yield new MWAPIRequestManager( this.context 
).getNamespaceAlias( 'File', this.targetLanguage );
+               targetResource = this.sourceResource.replace( 
/^(\.+\/)*(.+)(:)/, '$1' + namespaceAlias + '$3' );
+               sourceImage.attributes[ 'resource' ] = imageLink.attributes[ 
'href' ] = targetResource;
+       } else {
+               // TODO: This format is not decided yet. We do need to inform 
client about failed
+               // adaptations somehow.
+               this.node.attributes[ 'data-cx' ] = JSON.stringify( {
+                       adapted: false,
+                       imageSource: this.imageSource,
+                       resource: this.sourceResource
+               } );
+       }
+       return this.node;
+} );
+
+MWImage.name = 'image';
+MWImage.matchTagNames = [ 'figure' ];
+MWImage.matchRdfaTypes = [ 'mw:Image', 'mw:Image/Thumb', 'mw:Image/Frame', 
'mw:Image/Frameless' ];
+
+module.exports = MWImage;
diff --git a/lib/translationunits/index.js b/lib/translationunits/index.js
index 38df6e2..402406f 100644
--- a/lib/translationunits/index.js
+++ b/lib/translationunits/index.js
@@ -1,3 +1,4 @@
 module.exports = {
-       MWLink: require( './MWLink.js' )
+       MWLink: require( './MWLink.js' ),
+       MWImage: require( './MWImage.js' )
 };
diff --git a/package.json b/package.json
index 8bc1bc6..9e891d5 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,6 @@
     "node": ">=4.4.6"
   },
   "dependencies": {
-    "jsonwebtoken": "~5.6.0",
-    "sax": "1.2.1",
-    "html-entities": "1.2.0",
     "bluebird": "^3.4.1",
     "body-parser": "^1.15.2",
     "bunyan": "^1.8.1",
@@ -26,9 +23,13 @@
     "core-js": "^2.4.1",
     "domino": "^1.0.25",
     "express": "^4.14.0",
+    "html-entities": "1.2.0",
     "js-yaml": "^3.6.1",
+    "jsonwebtoken": "~5.6.0",
+    "language-data": "git+https://github.com/santhoshtr/language-data#master";,
     "mediawiki-title": "^0.6.3",
     "preq": "^0.5.2",
+    "sax": "1.2.1",
     "service-runner": "^2.2.5",
     "swagger-router": "^0.4.6",
     "swagger-ui": "git+https://github.com/wikimedia/swagger-ui#master";,
diff --git a/test/adaptation/AdaptationTests.json 
b/test/adaptation/AdaptationTests.json
index a96d619..a97d3d6 100644
--- a/test/adaptation/AdaptationTests.json
+++ b/test/adaptation/AdaptationTests.json
@@ -12,5 +12,26 @@
                "to": "es",
                "source": "<p id='mwBg'><b id='mwBw'>Oxygen</b> is a <a 
rel='mw:WikiLink' href='./Chemical_element' title='Chemical element' 
id='mwCA'>chemical element</a> with symbol<span typeof='mw:Entity' id='mwCQ'> 
</span><b id='mwCg'>O</b> and <a rel='mw:WikiLink' href='./Atomic_number' 
title='Atomic number' id='mwCw'>atomic number</a> 8. It is a member of the <a 
rel='mw:WikiLink' href='./Chalcogen' title='Chalcogen' id='mwDA'>chalcogen</a> 
<a rel='mw:WikiLink' href='./Group_(periodic_table)' title='Group (periodic 
table)' id='mwDQ'>group</a> on the <a rel='mw:WikiLink' href='./Periodic_table' 
title='Periodic table' id='mwDg'>periodic table</a> and is a highly <a 
rel='mw:WikiLink' href='./Chemical_reaction' title='Chemical reaction' 
id='mwDw'>reactive</a> <a rel='mw:WikiLink' href='./Nonmetal' title='Nonmetal' 
id='mwEA'>nonmetal</a> and <a rel='mw:WikiLink' href='./Oxidizing_agent' 
title='Oxidizing agent' id='mwEQ'>oxidizing agent</a> that readily forms <a 
rel='mw:WikiLink' href='./Oxide' title='Oxide' id='mwEg'>oxides</a> with most 
elements as well as other <a rel='mw:WikiLink' href='./Chemical_compound' 
title='Chemical compound' id='mwEw'>compounds</a>. By mass, oxygen is the 
third-<a rel='mw:WikiLink' href='./Abundance_of_the_chemical_elements' 
title='Abundance of the chemical elements' id='mwFA'>most abundant element</a> 
in the universe, after <a rel='mw:WikiLink' href='./Hydrogen' title='Hydrogen' 
id='mwFQ'>hydrogen</a> and <a rel='mw:WikiLink' href='./Helium' title='Helium' 
id='mwFg'>helium</a>. At <a rel='mw:WikiLink' 
href='./Standard_temperature_and_pressure' title='Standard temperature and 
pressure' id='mwFw' class='mw-redirect'>standard temperature and pressure</a>, 
two atoms of the element <a rel='mw:WikiLink' href='./Chemical_bond' 
title='Chemical bond' id='mwGA'>bind</a> to form <a rel='mw:WikiLink' 
href='./Allotropes_of_oxygen#Dioxygen' title='Allotropes of oxygen' 
id='mwGQ'>dioxygen</a>, a colorless and odorless <a rel='mw:WikiLink' 
href='./Diatomic_molecule' title='Diatomic molecule' id='mwGg'>diatomic</a> <a 
rel='mw:WikiLink' href='./Gas' title='Gas' id='mwGw'>gas</a> with the formula. 
This is an important part of the <a rel='mw:WikiLink' 
href='./Atmosphere_of_Earth' title='Atmosphere of Earth' 
id='mwHQ'>atmosphere</a> and diatomic oxygen gas constitutes 20.8% of the <a 
rel='mw:WikiLink' href='./Earth's_atmosphere' title='Earth's atmosphere' 
id='mwHg' class='mw-redirect'>Earth's atmosphere</a>. Additionally, as oxides 
the element makes up almost half of the <a rel='mw:WikiLink' 
href='./Earth's_crust' title='Earth's crust' id='mwHw' 
class='mw-redirect'>Earth's crust</a>.</p>",
                "result": "<p id='mwBg'><b id='mwBw'>Oxygen</b> is a <a 
href='Elemento químico' id='mwCA' rel='mw:WikiLink' title='Chemical 
element'>chemical element</a> with symbol<span id='mwCQ' typeof='mw:Entity'> 
</span><b id='mwCg'>O</b> and <a href='Número atómico' id='mwCw' 
rel='mw:WikiLink' title='Atomic number'>atomic number</a> 8. It is a member of 
the <a href='Anfígeno' id='mwDA' rel='mw:WikiLink' 
title='Chalcogen'>chalcogen</a> <a 
data-cx='{&#34;adapted&#34;:false,&#34;sourceTitle&#34;:&#34;Group (periodic 
table)&#34;}' href='./Group_(periodic_table)' id='mwDQ' rel='mw:WikiLink' 
title='Group (periodic table)'>group</a> on the <a href='Tabla periódica de los 
elementos' id='mwDg' rel='mw:WikiLink' title='Periodic table'>periodic 
table</a> and is a highly <a href='Reacción química' id='mwDw' 
rel='mw:WikiLink' title='Chemical reaction'>reactive</a> <a href='No metal' 
id='mwEA' rel='mw:WikiLink' title='Nonmetal'>nonmetal</a> and <a 
href='Oxidante' id='mwEQ' rel='mw:WikiLink' title='Oxidizing agent'>oxidizing 
agent</a> that readily forms <a href='Óxido' id='mwEg' rel='mw:WikiLink' 
title='Oxide'>oxides</a> with most elements as well as other <a href='Compuesto 
químico' id='mwEw' rel='mw:WikiLink' title='Chemical compound'>compounds</a>. 
By mass, oxygen is the third-<a href='Abundancia de los elementos químicos' 
id='mwFA' rel='mw:WikiLink' title='Abundance of the chemical elements'>most 
abundant element</a> in the universe, after <a href='Hidrógeno' id='mwFQ' 
rel='mw:WikiLink' title='Hydrogen'>hydrogen</a> and <a href='Helio' id='mwFg' 
rel='mw:WikiLink' title='Helium'>helium</a>. At <a class='mw-redirect' 
href='Condiciones normalizadas de presión y temperatura' id='mwFw' 
rel='mw:WikiLink' title='Standard temperature and pressure'>standard 
temperature and pressure</a>, two atoms of the element <a href='Enlace químico' 
id='mwGA' rel='mw:WikiLink' title='Chemical bond'>bind</a> to form <a 
href='Alótropos del oxígeno' id='mwGQ' rel='mw:WikiLink' title='Allotropes of 
oxygen'>dioxygen</a>, a colorless and odorless <a href='Molécula diatómica' 
id='mwGg' rel='mw:WikiLink' title='Diatomic molecule'>diatomic</a> <a 
href='Gas' id='mwGw' rel='mw:WikiLink' title='Gas'>gas</a> with the formula. 
This is an important part of the <a href='Atmósfera terrestre' id='mwHQ' 
rel='mw:WikiLink' title='Atmosphere of Earth'>atmosphere</a> and diatomic 
oxygen gas constitutes 20.8% of the <a atmosphere='' class='mw-redirect' 
href='Tierra' id='mwHg' rel='mw:WikiLink' s='' s_atmosphere='' 
title='Earth'>Earth's atmosphere</a>. Additionally, as oxides the element makes 
up almost half of the <a class='mw-redirect' crust='' href='Tierra' id='mwHw' 
rel='mw:WikiLink' s='' s_crust='' title='Earth'>Earth's crust</a>.</p>"
+       },
+       {
+               "desc": "Image adaptation basic test",
+               "from": "en",
+               "to": "ru",
+               "source": "<figure class='mw-default-size mw-halign-left' 
typeof='mw:Image/Thumb' id='mwUQ'><a 
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
resource='./File:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 data-file-width='125' data-file-height='248' data-file-type='bitmap' 
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a 
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium' 
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink' 
href='./Detective' title='Detective' 
id='mwVg'>investigators</a>.</figcaption></figure>",
+               "result": "<figure class='mw-default-size mw-halign-left' 
id='mwUQ' typeof='mw:Image/Thumb'><a 
href='./Файл:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
data-file-height='248' data-file-type='bitmap' data-file-width='125' 
height='248' id='mwUw' 
resource='./Файл:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 width='125' /></a><figcaption id='mwVA'><a href='Филон Византийский' id='mwVQ' 
rel='mw:WikiLink' title='Philo of Byzantium'>Philo's</a> experiment inspired 
later <a href='Детектив (профессия)' id='mwVg' rel='mw:WikiLink' 
title='Detective'>investigators</a>.</figcaption></figure>"
+       },
+       {
+               "desc": "Image adaptation- source and target language 
directions differs",
+               "from": "en",
+               "to": "he",
+               "source": "<figure class='mw-default-size mw-halign-left' 
typeof='mw:Image/Thumb' id='mwUQ'><a 
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
resource='./File:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 data-file-width='125' data-file-height='248' data-file-type='bitmap' 
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a 
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium' 
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink' 
href='./Detective' title='Detective' 
id='mwVg'>investigators</a>.</figcaption></figure>",
+               "result": "<figure class='mw-default-size mw-halign-right' 
id='mwUQ' typeof='mw:Image/Thumb'><a 
href='./קובץ:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
data-file-height='248' data-file-type='bitmap' data-file-width='125' 
height='248' id='mwUw' 
resource='./קובץ:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/commons/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 width='125' /></a><figcaption id='mwVA'><a 
data-cx='{&#34;adapted&#34;:false,&#34;sourceTitle&#34;:&#34;Philo of 
Byzantium&#34;}' href='./Philo_of_Byzantium' id='mwVQ' rel='mw:WikiLink' 
title='Philo of Byzantium'>Philo's</a> experiment inspired later <a 
data-cx='{&#34;adapted&#34;:false,&#34;sourceTitle&#34;:&#34;Detective&#34;}' 
href='./Detective' id='mwVg' rel='mw:WikiLink' 
title='Detective'>investigators</a>.</figcaption></figure>"
+       },
+       {
+               "desc": "Image adaptation- image is not in commons",
+               "from": "en",
+               "to": "ml",
+               "source": "<figure class='mw-default-size mw-halign-left' 
typeof='mw:Image/Frame' id='mwUQ'><a 
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
resource='./File:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/mediawiki/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 data-file-width='125' data-file-height='248' data-file-type='bitmap' 
height='248' width='125' id='mwUw'/></a><figcaption id='mwVA'><a 
rel='mw:WikiLink' href='./Philo_of_Byzantium' title='Philo of Byzantium' 
id='mwVQ'>Philo's</a> experiment inspired later <a rel='mw:WikiLink' 
href='./Detective' title='Detective' 
id='mwVg'>investigators</a>.</figcaption></figure>",
+               "result": "<figure class='mw-default-size mw-halign-left' 
data-cx='{&#34;adapted&#34;:false,&#34;resource&#34;:&#34;./File:Philos_experiment_of_the_burning_candle.PNG&#34;}'
 id='mwUQ' typeof='mw:Image/Frame'><a 
href='./File:Philos_experiment_of_the_burning_candle.PNG' id='mwUg'><img 
alt='Drawing of a burning candle enclosed in a glass bulb.' 
data-file-height='248' data-file-type='bitmap' data-file-width='125' 
height='248' id='mwUw' 
resource='./File:Philos_experiment_of_the_burning_candle.PNG' 
src='//upload.wikimedia.org/wikipedia/mediawiki/e/e6/Philos_experiment_of_the_burning_candle.PNG'
 width='125' /></a><figcaption id='mwVA'><a 
data-cx='{&#34;adapted&#34;:false,&#34;sourceTitle&#34;:&#34;Philo of 
Byzantium&#34;}' href='./Philo_of_Byzantium' id='mwVQ' rel='mw:WikiLink' 
title='Philo of Byzantium'>Philo's</a> experiment inspired later <a 
data-cx='{&#34;adapted&#34;:false,&#34;sourceTitle&#34;:&#34;Detective&#34;}' 
href='./Detective' id='mwVg' rel='mw:WikiLink' 
title='Detective'>investigators</a>.</figcaption></figure>"
        }
 ]

-- 
To view, visit https://gerrit.wikimedia.org/r/368800
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I288f67fbe94371ee8039ecdaf69984e51aa43511
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/services/cxserver
Gerrit-Branch: master
Gerrit-Owner: Santhosh <[email protected]>
Gerrit-Reviewer: Catrope <[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

Reply via email to