--[[ __ __ _ _ ____ _ | \/ | ___ __| |_ _| | ___ _ / ___|_ __ ___ __ _| |_ ___ _ __ | |\/| |/ _ \ / _` | | | | |/ _ (_) | | '__/ _ \/ _` | __/ _ \| '__| | | | | (_) | (_| | |_| | | __/_| |___| | | __/ (_| | || (_) | | |_| |_|\___/ \__,_|\__,_|_|\___(_)\____|_| \___|\__,_|\__\___/|_|This module is intended to be the engine behind "Template:Creator".Please do not modify this code without applying the changes first at"Module:Creator/sandbox" and testing at "Module:Creator/testcases".Authors and maintainers:* User:Jarekt - original versionHandling of the fields ============================================================================== |field name | property | pull | push | missing | mismatch | redundant ============================================================================== |Name | label | 1 | 0 | | | |Alternative names | aliases | 1 | 0 | | | | |P742,P1477,| | | | | | |P1782,P1787| | | | | |Sortkey | P734,P735 | 1 | 0 | | | 1 |Birthdate | P569 | 1 | 1 | 1 | 1 | 1 |Deathdate | P570 | 1 | 1 | 1 | 1 | 1 |Birthloc | P19 | 1 | 1 | 1 | 1 | 1 |Deathloc | P20 | 1 | 1 | 1 | 1 | 1 |Workperiod |P2031,P2032| 1 | | | | | | P1317 | | | | | |Workloc | P937 | 1 | 1 | 1 | 1 | 1 |Image | P18 | 1 | 1 | 1 | 1 | 1 |Homecat | P373 | 1 | 1 | 1 | 1 | 1 |Nationality | P27, P172 | 1 | | 1 | 1 | 1 |Gender | P21 | 1 | 1 | 1 | 1 | 1 |Occupation | P106 | 1 | | 1 | | 1 |Linkback | P1472 | 1 | 1 | 1 | 1 | 1 |Wikisource | sitelinks | 1 | 0 | | 0 | 1 |Wikiquote | sitelinks | 1 | 0 | | 0 | 1 =============================================================================== pull - can we pull data from wikidata ? - 1 - commons then wikidata - - not implemented yet - 0 - will not implement push - upload to wikidata through quick statements? missing - detect if missing on Wikidata mismatch - detect mismatch between wikidata and commons redundant - detect if redundant identical values on wikidata and commons]]localgetLabel=require("Module:Wikidata label")._getLabel-- used for creation of name based on wikidatalocalWikidata_label=require("Module:Wikidata label")-- used for creation of name based on wikidatalocalgetDate=require("Module:Wikidata date")._date-- used for processing of date propertieslocalqualifierDate=require("Module:Wikidata date")._qualifierDate-- used for processing of date qualifierslocalauthorityControl=require("Module:Authority control")._authorityControl-- used for formatting of Authority control rowlocalalterName=require("Module:Name")._name-- used for adding "option" fields to "name"localCity=require("Module:City")-- used to add wikidata bases links to names of placeslocalISOdate=require("Module:ISOdate")-- used for internationalization of dateslocalNationAndOccupation=require("Module:NationAndOccupation")._NationAndOccupationlocalvalid_date=require("Module:Calendar")._valid_datelocallabels=require("Module:I18n/creator")localTagQS=require('Module:TagQS')localcore=require("Module:core")localmessageBox=require("Module:Message box")-- ==================================================-- === Internal functions ===========================-- ==================================================localfunctionempty2nil(str)ifstr==''thenreturnnilelsereturnstr;endendlocalfunctionintersect(A,B)-- find intersection of tables A and Blocalret={}for_,ainipairs(Aor{})dofor_,binipairs(Bor{})doifa==bthentable.insert(ret,b)endendendreturnretendlocalfunctionisodate2timestamp(dateStr)-- convert isodate to timestamp used by quick statementslocaltStamp=nilifstring.match(dateStr,"^%d%d%d%d$")then-- if YYYY formattStamp='+'..dateStr..'-00-00T00:00:00Z/9'elseifstring.match(dateStr,"^%d%d%d%d%-%d%d$")then-- if YYYY-MM formattStamp='+'..dateStr..'-00T00:00:00Z/10'elseifstring.match(dateStr,"^%d%d%d%d%-%d%d%-%d%d$")then-- if YYYY-MM-DD formattStamp='+'..dateStr..'T00:00:00Z/11'endreturntStampendlocalfunctionfilepage_warningbox(text,lang,qCode)localboxArgs={}boxArgs.type='content'boxArgs.text=string.format(core.langSwitch(labels[text],lang),qCode)returnmessageBox.main("mbox",boxArgs);end-- ====================================================================-- This function is responsible for producing HTML of a single row of the template-- At this stage all the fields are already filed. There is either one or two fields-- INPUTS:-- * param1 and param2 - structures for 2 fields containing fields:-- - tag - I18n tag used for localization of the field name. Usually name of page in MediaWiki namespace which was imported from translatewiki.org.-- Alternative is to pass already translated field name.-- - field - field content-- - id - ID tag added to HTML's <td> cell. if IDs of 2 fields ar the same than we ignore the second one-- - wrapper - some fields need a <span class=...> wrapper around the field content-- ====================================================================localfunctionBuild_html_row(param1,param2,args)localtag,headerCell,cell2,cell3localfield1=args[param1.field]localfield2=args[param2.field]iffield1==''thenfield1=nil;endiffield2==''thenfield2=nil;endifnot(field1orfield2orargs.demo)thenreturnnilendiffield2thentag=param2.tagelsetag=param1.tagend-- use different tag based on presence of field2ifstring.sub(tag,1,10)=='wm-license'thentag=mw.message.new(tag):inLanguage(args.lang):plain()-- label message in args.lang languageendheaderCell=string.format('<th scope="row">%s</th>\n',tag)ifparam1.id==param2.idthen-- 2 cell rowcell2=string.format('<td colspan="2" class="fullwidth" id="%s">'..param1.wrapper..'</td>',param1.id,field1or'')cell3=''else-- 3 cell rowcell2=string.format('<td class="halfwidth" id="%s">\n%s</td>',param1.id,field1or'')cell3=string.format('<td class="halfwidth" id="%s">\n%s</td>',param2.id,field2or'')endreturnstring.format('<tr>\n%s%s%s</tr>\n',headerCell,cell2,cell3)end-- ====================================================================-- === This function is just responsible for producing HTML of the ===-- === template. At this stage all the fields are already filed ===-- ====================================================================localfunctionBuild_html(args,cats)localfield-- Top line with Creator name, lifespan and link icons -field=string.format('<span class="fn" id="creator"><bdi>%s</bdi></span> %s',args.nameor'missing name',args.lifespanor'')ifargs.linkbackthenfield=string.format('%s [[File:Blue pencil.svg|15px|link=Creator:%s|class=skin-invert]]',field,args.linkback)endifargs.wikidatathen-- Wikidata Linkfield=string.format('%s [[File:Wikidata-logo.svg|20px|wikidata:%s|link=wikidata:%s]]',field,args.wikidata,args.wikidata)endifargs.wikisourcethen--Wikisource linkfield=string.format('%s [[File:Wikisource-logo.svg|15px|%s|link=%s]]',field,args.wikisource,args.wikisource)endifargs.wikiquotethen--Wikiquote linkfield=string.format('%s [[File:Wikiquote-logo.svg|15px|%s|link=%s]]',field,args.wikiquote,args.wikiquote)endifargs.QSthen-- quick_statement link to upload missing info to wikidatafield=string.format('%s %s',field,args.QS)end-- Provide our own collapsible toggle in the th, which is image based-- This avoids depending on English for anonymous users, but it's a bit of a hack.localarrowtoggle=string.format(' <div class="mw-collapsible-toggle %s"><span class="mw-collapsible-arrowtoggle skin-invert"> </span></div>',(args.collapseorargs.namespace==6)and'mw-collapsible-toggle-collapsed'or'mw-collapsible-toggle-expanded');localline=string.format('<th colspan="4">%s%s</th>',field,arrowtoggle)localresults={}table.insert(results,string.format('<tr>\n%s\n</tr>\n',line))-- add other fieldslocalparam={{tag='wm-license-creator-alternative-names',field='alternative_names',id='fileinfotpl_creator_alt-name_value',wrapper='<div class="nickname">\n%s</div>'},{tag='wm-license-creator-description',field='description',id='fileinfotpl_creator_desc_value',wrapper='%s'},{tag='wm-license-creator-date-of-birth',field='birthdate',id='fileinfotpl_creator_birthdate_value',wrapper='%s'},{tag='wm-license-creator-date-of-birth-and-death',field='deathdate',id='fileinfotpl_creator_deathdate_value',wrapper='%s'},{tag='wm-license-creator-location-of-birth',field='birthloc',id='fileinfotpl_creator_birthloc_value',wrapper='%s'},{tag='wm-license-creator-location-of-birth-and-death',field='deathloc',id='fileinfotpl_creator_deathloc_value',wrapper='%s'},{tag='wm-license-creator-work-period',field='workperiod',id='fileinfotpl_creator_work-period_value',wrapper='%s'},{tag='wm-license-creator-work-location',field='workloc',id='fileinfotpl_creator_work-location',wrapper='<div class="locality">\n%s</div>'},{tag=args.authority_tag,field='authority',id='fileinfotpl_creator_authority_value',wrapper='%s'},{tag='wm-license-artwork-references',field='references',id='fileinfotpl_creator_references',wrapper='<div>\n%s</div>'}}table.insert(results,Build_html_row(param[1],param[1],args))table.insert(results,Build_html_row(param[2],param[2],args))table.insert(results,Build_html_row(param[3],param[4],args))table.insert(results,Build_html_row(param[5],param[6],args))table.insert(results,Build_html_row(param[7],param[7],args))table.insert(results,Build_html_row(param[8],param[8],args))table.insert(results,Build_html_row(param[9],param[9],args))table.insert(results,Build_html_row(param[10],param[10],args))-- Image on the Leftifnotargs.imageandargs.demothenargs.image='Silver - replace this image male.svg'endifargs.imagethen--Wikiquote linkfield=string.format('[[File:%s|120x360px|alt=%s|class=photo]]',args.image,args.nameor'')localn=#results-- number of rows belowline=string.format('<td rowspan="%i" style="width:120px" id="fileinfotpl_creator_image"><span class="wpImageAnnotatorControl wpImageAnnotatorOff">%s</span></td>',n,field)table.insert(results,2,string.format('<tr>\n%s\n</tr>\n',line))endresults=table.concat(results)-- Template styles-- We should make this sandbox awarelocaltemplatestyles=mw.getCurrentFrame():extensionTag{name='templatestyles',args={src='Module:Creator/styles.css'}}-- build tablelocaldir=mw.language.new(args.lang):getDir()localtext_align=((dir=='ltr')and'left')or'right'localcollapsed=''ifargs.collapseorargs.namespace==6thencollapsed='mw-collapsed'endlocalstyle=string.format('class="commons-creator-table mw-collapsible %s mw-content-%s" dir="%s" lang="%s"',collapsed,dir,dir,args.lang)results=string.format('<table %s>\n%s\n</table>\n',style,results)results=string.format('<div class="commons-creator vcard">\n%s\n%s\n</div>\n',templatestyles,results)-- add references and documentation which are only visible in creator namespaceifargs.namespace==100orargs.demothenlocalbox=''ifargs.wikidataandstring.match(cats,'missing linkback')thenbox=filepage_warningbox('missing_linkback',args.lang,args.wikidata)elseifargs.wikidataandstring.match(cats,'without home category')thenbox=filepage_warningbox('missing_homecat',args.lang,args.wikidata)endlocaldoc=mw.getCurrentFrame():expandTemplate{title='documentation',args={'Template:Creator/documentation'}}results=results..box..doc-- add documentation to pages in creator namespaceendreturnresultsend-- ===========================================================================-- === This function is responsible for adding maintenance categories ===-- === which are not related to wikidata ===-- === INPUTS: ===-- === * args - merged data from the local arguments and Wikidata ===-- ===========================================================================localfunctionadd_maintenance_categories(args)localcats=''-- categories-- ====================================================-- === automatic tagging of pages in all namespaces ===-- ====================================================ifargs.type==''orargs.type=='person'then-- add an empty template which can be used as a tag in PetScanlocaldod=args.deathyearorargs.deathdate-- date of deathlocaldob=args.birthyearorargs.birthdate-- date of birthlocald=os.date('!*t')-- current date tablelocalyear=tonumber(d.year)-- current yearlocalpma=nil-- years since deathifdodthendod=tonumber(ISOdate._ISOyear(dod))ifdodthenpma=year-dodendendifdobandnotpmathendob=tonumber(ISOdate._ISOyear(dob))ifdobthenpma=year-dob-100-- Assumes max 100 lifespanendend-- Add empty tag templates to track different casesifpmaandpma>100thenmw.getCurrentFrame():expandTemplate{title='Works of authors who died more than 100 years ago'}elseifpmaandpma>70thenmw.getCurrentFrame():expandTemplate{title='Works of authors who died more than 70 years ago'}elseif(dodordobor0)>year-65thenmw.getCurrentFrame():expandTemplate{title='Works of authors who died less than 65 years ago'}endend-- ============================================================-- === automatic categorization of pages in File: namespace ===-- ============================================================ifargs.namespace==6thenifnotargs.imagethenmw.getCurrentFrame():expandTemplate{title='Creator template without image'}-- add the template tagendreturncatsend-- ===============================================================-- === automatic categorization of pages in Creator: namespace ===-- ===============================================================ifargs.namespace~=100thenreturncatsend-- add [[Category:Creator templates]] categorycats=cats..string.format('\n[[Category:Creator templates|%s]]',args.sortkeyor' ')-- check for key informationifnotargs.linkbackandnotargs.wikidatathencats=cats..'\n[[Category:Creator templates without linkback]]'endifnotargs.namethencats=cats..'\n[[Category:Creator templates without name]]'end-- add homecat categoryifargs.homecatthencats=cats..string.format('\n[[Category:%s]]',args.homecat)end-- add type categoryifargs.typethenlocallut={['commons user']='\n[[Category:User creator templates]]',['corporation']='\n[[Category:Corporate creator templates]]',['group']='\n[[Category:Group creator templates]]',}cats=cats..(lut[args.type]or'')ifargs.type=='commons user'thenreturncats-- for commons user do not add other maintenance categoriesendend-- ===============================================================-- === automatic categorization of pages in Creator: namespace ===-- === all pages except: 'commons user' ===-- ===============================================================-- check for imageifnotargs.imagethencats=cats..'\n[[Category:Creator templates without images]]'end-- check for wikidata q-codeifnotargs.wikidatathencats=cats..'\n[[Category:Creator templates without Wikidata link]]'end-- check for homecatifnotargs.homecatthencats=cats..'\n[[Category:Creator templates without home category]]'elselocalhc=mw.title.new('Category:'..args.homecat)ifnothc.existsthencats=cats..'\n[[Category:Creator templates with non-existing home categories]]'endhc=mw.title.new('Creator:'..args.homecat)ifhc:localUrl()~=mw.title.getCurrentTitle():localUrl()thencats=cats..'\n[[Category:Creator templates with non-matching home categories]]'endendreturncatsend-- ===========================================================================-- === This function is responsible for adding maintenance categories ===-- === to pages in category namespace ===-- === INPUTS: ===-- === * args - local inputs from the creator template page ===-- ===========================================================================localfunctionadd_categories_to_category_namespace(args)localcatsifargs.namespace~=14or(args.homecatandmw.title.new('Category:'..args.homecat):localUrl()~=mw.title.getCurrentTitle():localUrl())thenreturn''-- if not a home category than exitendlocalsortkey="|"..(args.sortkeyor'')if#sortkey==1thensortkey=''endcats=string.format('\n[[Category:Creator template home categories%s]]',sortkey)-- check for wikidata q-codeifnotargs.wikidatathencats=cats..'\n[[Category:Creator template home categories without Wikidata link]]'endifargs.command=='autocategorize'then-- add basic categories to the creator pagecats=string.format('%s\n[[Category:People by name%s]]',cats,sortkey)ifargs.deathyearthencats=string.format('%s\n[[Category:%i deaths%s]]',cats,args.deathyear,sortkey)endifargs.birthyearthencats=string.format('%s\n[[Category:%i births%s]]',cats,args.birthyear,sortkey)endendreturncatsend-- ===========================================================================-- === This function is responsible for adding maintenance categories ===-- === to pages in creator namespace which are related to wikidata ===-- === INPUTS: ===-- === * args0 - local inputs from the creator template page ===-- === * args1 - merge of local and wikidata metadata ===-- === * data - data pulled from Wikidata ===-- ===========================================================================localfunctionadd_categories_to_creator_namespace(args0,args1,data)localcats=''-- categorieslocalqsTable={}-- table to store QuickStatementslocalcomp={}-- outcome of argument vs. wikidata comparison-- two forms of QuickStatements command with and without quoteslocalqsCommand={'%s|%s|%s','%s|%s|"%s"'}-- compare Linkback to the actual page name. Many "Linkbacks" are created with-- tool which produces & and ' instead of "&" and "'"ifargs0.linkbackthenlocallinkback=args0.linkbacklinkback=mw.ustring.gsub(linkback,''',"'")linkback=mw.ustring.gsub(linkback,'&',"&")iflinkback~=args0.pagenamethencats=cats..'\n[[Category:Creator templates with mismatching linkback]]'endend-- add [[Category:Creator templates with unknown parameter]] category, if some parameter not on the following list is usedlocalfields={'name','alternative_names','sortkey','birthdate','deathdate','birthloc','deathloc','workperiod','workloc','collapse','image','homecat','nationality','gender','occupation','description','authority','type','wikisource','wikiquote','command','namespace','linkback','wikidata','lang','pagename','reference','references','lifespan','birthyear','deathyear','option'}localset={}for_,fieldinipairs(fields)doset[field]=trueendforfield,_inpairs(args0)doifnotset[field]thencats=string.format('%s\n[[Category:Creator templates with unknown parameter|%s]]',cats,field)endend-- add [[Category:Wikidata based creator templates]] and [[Category:Creator templates with Wikidata link: local linkback]]localval={wikidata=1,option=0,linkback=0,lang=0,namespace=0,pagename=0,type=0,command=0}localhash=0;forfield,_inpairs(args0)dohash=hash+(val[field]or10)endifhash==1thencats=string.format('%s\n[[Category:Creator templates based only on Wikidata|%s]]',cats,args1.sortkeyor'')end-- if no q-code but we have "create" input argument then create new itemifnotargs0.wikidataandargs0.command=='create item'thenlocaldescriptiontable.insert(qsTable,'CREATE')table.insert(qsTable,'LAST|P31|Q5|S143|Q24731821')-- instance of humantable.insert(qsTable,'LAST|Len|"'..args0.pagename..'"')-- english labelifargs0.nationalityandargs0.occupationthenlocallang=args0.langargs0.lang='en';description,_,_=NationAndOccupation(args0)args0.lang=langifargs1.birthyearandargs1.deathyearthendescription=string.format('%s (%s-%s)',description,args1.birthyear,args1.deathyear)endtable.insert(qsTable,'LAST|Den|"'..description..'"')-- english descriptionendargs0.wikidata='LAST'end-- skip the rest if no q-codeifnotargs0.wikidatathenreturncats,args1end-- mark parameters as "local" if they are present in creator templatelocalfields={'name','birthdate','deathdate','birthyear','deathyear','birthloc','deathloc','image','homecat','nationality','gender','occupation','description','authority','wikisource','wikiquote','sortkey'}for_,fieldinipairs(fields)doifargs0[field]thencomp[field]='local'endend-- redundant if commons creator template and wikidata have those fields, without checking valueslocalfields={'wikiquote','wikisource'}for_,fieldinipairs(fields)doifargs0[field]anddata[field]thencomp[field]='redundant'endend-- ==================================================-- === time fields =================================-- ==================================================localfields={birthdate='P569',deathdate='P570'}locala1,a2,d1,d2,dyforfield,propinpairs(fields)doa1=args0[field]-- original creator template value often in iso (YYYY or YYYY-MM-DD) formata2=args1[field]-- translated creator template valued1=data[field..'_']-- wikidata value in iso (YYYY or YYYY-MM-DD) formatd2=data[field]-- translated wikidata valuedy=tostring(data[string.gsub(field,'date','year')])-- wikidata year valueifa1andnot(string.match(a1,"^%d%d%d%d$")orstring.match(a1,"^%d%d%d%d%-%d%d$")orstring.match(a1,"^%d%d%d%d%-%d%d%-%d%d$"))then-- if YYYY or YYYY-MM-DD formata1=nil-- delete if not in iso formatendifa1then-- local date in iso formif(a1==d1)or(a2anda2==d2)or(#a1==4anda1==dy)thencomp[field]='redundant'-- matching iso value, translated value and commons-year matching wikidata dateelseifd1anda1~=d1thencomp[field]='mismatching'elseifnotd2then-- missing on Wikidatacomp[field]='item missing'end-- create QS string so the Commons value can be uploded to Wikidataif(comp[field]=='item missing')or(#a1>4andd1and#d1==4andstring.sub(a1,1,4)==d1)thenlocalval=isodate2timestamp(a1)ifvalthentable.insert(qsTable,string.format(qsCommand[1],args0.wikidata,prop,val))endendendend-- ==================================================-- === birthloc / deathloc place fields ============-- ==================================================localfields={birthloc='P19',deathloc='P20'}forfield,propinpairs(fields)dolocala1,a2,d1,d2,dy,_a2=args0[field]-- creator template valued1=data[field]-- wikidata q-codeifa2thena1,_=City.qCode(a2)-- q-code for original creator template valueendifd1thend2=getLabel(d1,'en','-')-- get english labeldy=City._city(d1,args0.lang)..core.editAtWikidata(args0.wikidata,prop,args0.lang)endif(a1anda1==d1)or(a2anda2==d2)thencomp[field]='redundant'-- matching q-code and nameelseif(a1andd1anda1~=d1)or(a2andd2anda2~=d2)thencomp[field]='mismatching'elseifa1andnotd2then-- missing on Wikidatacomp[field]='item missing'table.insert(qsTable,string.format(qsCommand[1],args0.wikidata,prop,a1))elseifa2andnotd2thencomp[field]='item missing'enddata[field..'_']=d1args0[field..'_']=a1data[field]=dyend-- ==================================================-- === workloc field ===============================-- ==================================================if(args0.worklocandargs0.workloc==data.workloc_en)thencomp.workloc='redundant'-- matching q-code and nameelseifargs0.worklocandnotdata.worklocthen-- missing on Wikidatacomp.workloc='item missing'end-- ==================================================-- === nationality and occupation ==================-- ==================================================localfields={nationality='nationality_',occupation='occupationEN'}data.nationality_=data.nationalityforfield,field_inpairs(fields)dolocala1,a2,ada1=args1[field_]-- creator template valued1=data[field_]-- wikidata q-codead=intersect(a1,d1)if(a1andd1and#a1==#adand(#d1==#adorfield=='occupation'))then-- for nationality all values on Commons must be the same as on Wikidata-- for occupation all commons values have to be on Wikidata but wikidata can have more than thatcomp[field]='redundant'elseif(a1andd1and#a1>#ad)thencomp[field]='mismatching'-- some commons values are not on Wikidataelseifa1andnotd1then-- missing on Wikidatacomp[field]='item missing'endend-- ==================================================-- === gender ===============================-- ==================================================ifargs0.genderthen-- look up q-codes of genderlocalGenderLut={male='Q6581097',female='Q6581072'}a1=GenderLut[mw.ustring.lower(args0.gender)]-- look up q-code for each genderd1=GenderLut[data.gender]-- wikidata q-codeifa1andd1anda1~=d1thencomp.gender='mismatching'elseifa1andd1anda1==d1thencomp.gender='redundant'elseifa1andnotd1thencomp.gender='item missing'table.insert(qsTable,string.format(qsCommand[1],args0.wikidata,'P21',a1))endend-- ==================================================-- === odds and ends ===============================-- ==================================================ifargs0.imagethenargs0.image_=mw.uri.decode(args0.image,"WIKI")endargs0.linkback_=args0.pagename;args0.homecat_=args0.homecat;localfields={image='P18',linkback='P1472',homecat='P373'}forfield,propinpairs(fields)doa1=args0[field..'_']-- creator template valued1=data[field]-- wikidata q-codeifa1andd1anda1~=d1thencomp[field]='mismatching'elseifa1andd1anda1==d1thencomp[field]='redundant'elseifa1andnotd1thencomp[field]='item missing'table.insert(qsTable,string.format(qsCommand[2],args0.wikidata,prop,a1))endendifcomp.linkback=='redundant'and(hash~=1ornotargs0.linkback)thencomp.linkback=nilendifargs0.sortkeyanddata.sortkeyandargs0.sortkey==data.sortkeythencomp.sortkey='redundant'endifargs0.descriptionandargs1.description_==args0.descriptionthen-- description is "French painter" while nationality is FR and occupation is "painter"comp.description='redundant'end-- ==================================================-- === alter look of some fields ===-- ==================================================localfields={'birthloc','deathloc','birthdate','deathdate'}for_,fieldinipairs(fields)doif(comp[field]=='mismatching')or(comp[field]=='local'anddata[field])thenargs1[field]=string.format('<div style=\"background-color:PeachPuff\">%s</div> <br/>%s',args1[field],data[field])elseif(comp[field]=='redundant')thenargs1[field]=string.format('<div style=\"background-color:Thistle\">%s</div>',args1[field])elseif(comp[field]=='item missing'andargs1[field])thenargs1[field]=string.format('<div style=\"background-color:PeachPuff\">%s</div>',args1[field])endend-- ==================================================-- === Create categories and QuickStatement codes ===-- ==================================================-- create categories based on comp structureforfield,outcomeinpairs(comp)docats=string.format('%s\n[[Category:Creator templates with Wikidata link: %s %s]]',cats,outcome,field)end-- convert QS table to a stringlocalQS=''-- quick_statements final stringif#qsTable>0thenlocaltoday='+'..os.date('!%F')..'T00:00:00Z/11'-- today's date in QS formatlocalurl=mw.title.getCurrentTitle():canonicalUrl()localsource='|S143|Q24731821|S813|'..today..'|S4656|"'..url..'"'localqsWrapper=' [[File:Commons_to_Wikidata_QuickStatements.svg|15px|link=%s]]'QS=table.concat(qsTable,source..'||')..source-- combine multiple statements into a single command separated by ||QS=mw.ustring.gsub(QS,' ',"%%20")QS=mw.ustring.gsub(mw.uri.encode(QS),'%%2520','%%20')QS='https://quickstatements.toolforge.org/#/v1='..QS-- create full URL linkQS=string.format(qsWrapper,QS)cats=cats..'\n[[Category:Creator templates with Wikidata link: quick statements]]'endargs1.QS=QS-- tag invalid dates that use YYYY-MM-DD formatif(args0.deathdateand(notvalid_date(args0.deathdate)))or(args0.birthdateand(notvalid_date(args0.birthdate)))thencats=cats..'\n[[Category:Pages with incorrect date]]'endreturncats,args1end-- ===========================================================================-- === Harvest wikidata properties matching creator template fields ==========-- ===========================================================================localfunctiongetPropertyQual(entity,prop,qualifiers,lang,offset)localRes={}ifentity.claimsandentity.claims[prop]thenfork,statementinipairs(entity:getBestStatements(prop))doif(statement.mainsnak.snaktype=="value")thenlocalres={}-- table with fields: key, value, P... (qualifiers)localjdn=k-- "Julian day number" will be used as a key for sorting events; initializelocalval=statement.mainsnak.datavalue.value.idval=getLabel(val,lang)res.value=valforiQual,qualinipairs(qualifiers)doifstatement.qualifiersandstatement.qualifiers[qual]thenlocalsnak=statement.qualifiers[qual][1]if(snak.snaktype=="value"andsnak.datatype=='time')thenval=qualifierDate(snak,lang)ifiQual==1then-- first qualifier in the qualifiers list will be used as a sorting valuejdn=val.jdnendval=val.strendres[qual]=valendendres.key=jdntable.insert(Res,res)endendendlocaltableComp=function(rec1,rec2)returnrec1.key<rec2.keyendtable.sort(Res,tableComp)returnResend-- ===========================================================================localfunctionget_work_location(entity,lang)-- work_location (P937) / 'P580', 'P582' (time properties)localprop=getPropertyQual(entity,'P937',{'P580','P582','P585'},lang)localX={}for_,pinipairs(prop)dolocalstr=p.valueifp.P580orp.P582thenstr=string.format("%s (%s–%s)",p.value,p.P580or'',p.P582or'')elseifp.P585thenstr=string.format("%s (%s)",p.value,p.P585)elsestr=p.valueendtable.insert(X,str)endif#X>0thenreturntable.concat(X,"; ")endreturnnilend-- ===========================================================================localfunctioneditAtWikidata(data,qCode,property,lang)forprop,fieldinpairs(property)doifdata[field]thendata[field]=data[field]..core.editAtWikidata(qCode,prop,lang)endendreturndataend-- ===========================================================================localfunctionharvest_wikidata(qCode,lang,namespace,pagename)localstr,dlocaldata={}-- structure similar to "args" but filled with wikidata datalocalcats=''localentity=nilifmw.wikibaseandqCodethenentity=mw.wikibase.getEntity(qCode)ifnotentitythencats='[[Category:Creator templates with bad Wikidata link|invalid]]'elseifentity.id~=qCodethencats='[[Category:Creator templates with redirected Wikidata link]]'qCode=entity.idendendifnotentitythenreturndata,catsend-- ===========================================================================-- === Step 1: time properties-- ===========================================================================-- harvest time properties: translated date and year numberlocald1=getDate(entity,'P569',lang)locald2=getDate(entity,'P570',lang)locald3=getDate(entity,'P1636',lang)locald4=getDate(entity,'P4602',lang)data.birthdate,data.birthdate_,data.birthyear=d1.str,d1.iso,d1.yeardata.deathdate,data.deathdate_,data.deathyear=d2.str,d2.iso,d2.yeardata.baptism,data.baptismyear=d3.str,d3.yeardata.burial,data.burialyear=d4.str,d4.yeardata=editAtWikidata(data,qCode,{P569='birthdate',P570='deathdate'},lang)-- baptism date as birth dateifnotdata.birthdateanddata.baptismthendata.birthdate=mw.getCurrentFrame():expandTemplate{title='Lifetime date',args={'baptism',data.baptism,lang=lang}}..core.editAtWikidata(qCode,'P1636',lang)data.birthyear=data.baptismyearend-- burial date as death dateifnotdata.birthdateanddata.baptismthendata.deathdate=mw.getCurrentFrame():expandTemplate{title='Lifetime date',args={'buried',data.burial,lang=lang}}..core.editAtWikidata(qCode,'P4602',lang)data.deathyear=data.burialyearenddata.birthyear=tostring(data.birthyearor'')data.deathyear=tostring(data.deathyearor'')-- work periodlocalproperty={P2031='workperiod1',P2032='workperiod2',P1317='floruit'}forprop,fieldinpairs(property)dod1=getDate(entity,prop,lang)ifd1.strthendata[field..'_']=d1.strdata[field]=d1.str..core.editAtWikidata(qCode,prop,lang)endendifdata.workperiod1ordata.workperiod2thendata.workperiod=(data.workperiod1or'')..'–'..(data.workperiod2or'')data.workperiod_=(data.workperiod1_or'')..'–'..(data.workperiod2_or'')elseifdata.floruitthendata.workperiod=data.floruitdata.workperiod_=data.floruit_end-- work locationdata.workloc=get_work_location(entity,lang)ifdata.worklocthendata.workloc=data.workloc..core.editAtWikidata(qCode,'P937',lang)enddata.workloc_en=get_work_location(entity,'en')-- lifespan displayed after nameifdata.birthyear~=''ordata.deathyear~=''then-- quick localization of the two numberslocall=mw.language.new(lang)localby=tonumber(data.birthyear)by=byandl:formatNum(by,{noCommafy='y'})ordata.birthyearlocaldy=tonumber(data.deathyear)dy=dyandl:formatNum(dy,{noCommafy='y'})ordata.deathyeardata.lifespan=string.format('(%s–%s)',by,dy)elseifdata.workperiod_then-- create from work period (without "edit at wikidata")data.lifespan=string.format('([[d:Q36424|fl.]] %s)',data.workperiod_)end-- ===========================================================================-- === Step 2: simple string and Q-code properties-- ===========================================================================-- harvest string and Q-code propertieslocalproperty={P18='image',P19='birthloc',P20='deathloc',P109='signature',P373='homecat',P734='lastname',P735='firstname',P742='pseudonym',P1472='linkback',P1477='birthname',P1782='courtesyname',P1787='artname'}forprop,fieldinpairs(property)doifentity.claimsandentity.claims[prop]then-- if we have wikidata item and item has the property-- capture single "best" Wikidata valuefor_,statementinpairs(entity:getBestStatements(prop))doif(statement.mainsnak.snaktype=='value')thenlocalv=statement.mainsnak.datavalue.valueifv.idthenv=v.idenddata[field]=vendendendend-- if homecat missing than look for it among sitelinksifnotdata.homecatthenlocalsitelinkifentity.sitelinksandentity.sitelinks.commonswikithensitelink=entity.sitelinks.commonswiki.titleendifnotsitelinkthen-- check for "topic's main category" sitelinkslocalcat_id=(core.parseStatements(entity:getBestStatements("P910"),nil)or{nil})[1]ifcat_idthensitelink=Wikidata_label._sitelinks(cat_id,"commons")['']endendifsitelinkandmw.ustring.sub(sitelink,1,9)=='Category:'thendata.homecat=mw.ustring.sub(sitelink,10)endend-- get "sortkey" fieldifnotdata.sortkeythenlocallastname,firstname,name_partifdata.lastnamethenlastname=getLabel(data.lastname,lang,'-')elseifnamespace==100thenname_part=mw.text.split(pagename,'%(')name_part=mw.text.trim(name_part[1])name_part=mw.text.split(name_part,' ')lastname=name_part[#name_part]elselastname="ZZZ"enddata.lastname=lastnameifdata.firstnamethenfirstname=getLabel(data.firstname,lang,'-')elsefirstname=data.linkbackor''enddata.sortkey=lastname..', '..firstnameend-- convert genderdata.gender_=data.genderifdata.gender=='Q6581097'ordata.gender=='Q2449503'thendata.gender='male'endifdata.gender=='Q6581072'ordata.gender=='Q1052281'thendata.gender='female'enddata.image=data.imageordata.signature-- =================================================================================-- === Step 5: name, wikisource, wikiquote, alternative_names and authority control-- =================================================================================-- get name fielddata.name=getLabel(entity,lang,'wikipedia')-- create name based on wikidata label-- prepare fallback list of languageslocallangList=mw.language.getFallbacksFor(lang)table.insert(langList,1,lang)-- get alternative nameslocalaltNameTable={}localaltNameTableFromProperties={}localnameTypes={birthname='birth',pseudonym='pseudonym',courtesyname='courtesy name',artname='artist'}fort,operationinpairs(nameTypes)doifdata[t]thenifdata[t].textthendata[t]=data[t].textendlocalnameStr=alterName(operation,data[t],lang)table.insert(altNameTable,nameStr)table.insert(altNameTableFromProperties,nameStr)endendfor_,lnginipairs(langList)dolocalaliasTable=Wikidata_label._aliases(entity,lng)if#aliasTable>0and#aliasTable<8then-- skip aliases if more than 8 of themfor_,aliasinpairs(aliasTable)dolocalisSpecialName=falsefort,_inpairs(nameTypes)doifalias==data[t]thenisSpecialName=trueendendifnotisSpecialNamethentable.insert(altNameTable,alias)endenddata.alternative_names=table.concat(altNameTable,'; ')breakelsedata.alternative_names=table.concat(altNameTableFromProperties,'; ')endend-- get wikisource and wikiquote linklocalprojects={s='wikisource',q='wikiquote'}forcode,projectinpairs(projects)dolocalsitelinks=Wikidata_label._sitelinks(entity,project)ifsitelinksthenlocallng,_=next(sitelinks)-- get language of the first sitelinktable.insert(langList,lng)-- and add it to the listso there is at least one lang with sitelink on the listfor_,languageinipairs(langList)dolocalsitelink=sitelinks[language]ifsitelinkthendata[project]=string.format('%s:%s:%s',code,language,sitelink)breakendendendend-- get authority control templatelocalAC_catslocalnIdent=nil-- number of authority control identifiers to display (nil means unlimited)ifnamespace==6thennIdent=5-- limit number of identifiers in file namespace for clarityenddata.authority,AC_cats=authorityControl(entity,{wikidata=qCode},lang,nIdent)ifnot(namespace==2ornamespace==6ornamespace==828ormath.fmod(namespace,2)==1)thencats=cats..AC_cats-- lets not add authorityControl categories to user pages, files, modules or talk pages and concentrate on templates and categories insteadendreturndata,catsend-- ==================================================-- === External functions ===========================-- ==================================================localp={}-- ===========================================================================-- === Version of the function to be called from other LUA codes-- ===========================================================================functionp._creator(args)locallang=args.lang-- user's languagelocalcats=''-- categorieslocalstr,data-- look up title infolocaltitle=mw.title.getCurrentTitle()args.namespace=title.namespace-- get page namespaceargs.pagename=title.text-- get {{PAGENAME}}-- ===========================================================================-- === Step 1: clean up of template arguments "args"-- ===========================================================================args.type=string.lower(args.typeor'person')-- if 'type' field is not specified than set to "person"ifargs.linkbackthenargs.linkback=string.sub(args.linkback,9)end-- clean up "gender" fieldifstring.sub(args.genderor'',1,1)=='m'thenargs.gender='male'endifstring.sub(args.genderor'',1,1)=='f'thenargs.gender='female'end--make a copy of args structure to capture raw inputslocalargs0={}-- original argsforname,valueinpairs(args)doargs0[name]=valueend--get birthyear and deathyear from full datesifargs.birthdatethenargs.birthyear=empty2nil(ISOdate._ISOyear(args.birthdate))args.birthdate=ISOdate._ISOdate(args.birthdate,lang)endifargs.deathdatethenargs.deathyear=empty2nil(ISOdate._ISOyear(args.deathdate))args.deathdate=ISOdate._ISOdate(args.deathdate,lang)end-- ===========================================================================-- === Step 2: one by one merge wikidata and creator data-- ===========================================================================data,cats=harvest_wikidata(args.wikidata,lang,args.namespace,args.pagename)localdescription,args1,data1=NationAndOccupation(args)localfields={'nationality','occupation','gender','occupationEN'}for_,fieldinipairs(fields)doargs[field]=args1[field]data[field]=data1[field]endargs.nationality_=args.nationality-- mass merge (prioritize local values)fields={'name','alternative_names','sortkey','birthdate','deathdate','birthloc','deathloc','workperiod','image','homecat','nationality','gender','occupation','authority','wikisource','wikiquote','workloc','linkback','lifespan','birthyear','deathyear','collapse'}for_,fieldinipairs(fields)doargs[field]=args[field]ordata[field]end-- process "name" fieldifargs.optionandargs.option~=''then-- modify name based on "option" parameterlocalbase_name=args.name-- call [[module:Name]] with the taskargs.name=alterName(args.option,args.name,lang)ifargs.name=="name not supported"thenargs.name=base_namecats=cats..'\n[[Category:Bad use of creator template - option]]'endend-- process places fields-- locations can be words or q -codes. Add linkslocalloc_fields={birthloc='P19',deathloc='P20'}forfield,propinpairs(loc_fields)doargs[field]=City._city(args[field],lang)ifnotargs0[field]anddata[field]then-- no value from Commons, but value from Wikidataargs[field]=args[field]..core.editAtWikidata(args.wikidata,prop,lang)endendifargs.worklocandnotstring.find(args.workloc,' ')thenargs.workloc=City._city(args.workloc,lang)-- single word workloc will get a linkend-- lifespan displayed after nameifargs.lifespanthenargs.lifespan=string.gsub(args.lifespan,'-','–')-- use special dashend-- process "Authority Control" fieldargs.authority_tag=getLabel("Q36524",args.lang,"wikipedia","ucfirst")-- process "description" field-- Add phrase like "French painter" to the description fielddescription=mw.text.trim(description)ifdescriptionand#description>0thenifargs.descriptionthenargs.description_=descriptionargs.description=description..'<br/>'..args.descriptionelseargs.description=descriptionendend-- use Normalization Form D to convert string with accented characters to more sort friendly format-- See http://unicode.org/reports/tr15/ for examplesargs.sortkey=mw.ustring.toNFD(args.sortkeyor'')-- references are only shown in ''Creator'' namespaceifargs.namespace~=100thenargs.references=nilend-- convert all empty strings to nilsfor_,fieldinipairs(fields)doifargs[field]==''thenargs[field]=nil;endend-- ===========================================================================-- === Step 3: create maintenance categories and render html of the table-- ===========================================================================ifargs.namespace==14and(args.type==''orargs.type=='person')thencats=cats..add_categories_to_category_namespace(args)endcats=cats..add_maintenance_categories(args)-- If creator namespace and "person" template than add maintenance categoriesargs.QS=nil;ifargs.namespace==100and(args.type==''orargs.type=='person')thenstr,args=add_categories_to_creator_namespace(args0,args,data)cats=cats..strendlocalresults=Build_html(args,cats)returnresults,catsend-- ===========================================================================-- === Version of the function to be called from template namespace-- ===========================================================================functionp.creator(frame)-- switch to lowercase parameters to make them case independentlocalargs=core.getArgs(frame)-- alias field namesargs.references=args.referencesorargs.reference-- two alternative names for references-- parse args.option field, which is passed through individual Creator template (page in Creator namespace)localoptions=mw.text.split(args.optionor'','/')-- individual keywords can be separated by "/"args.option=nilfor_,optioninpairs(options)doifoption=='autocategorize'thenargs.command=option-- some "options" are to modify the name and some are commands to do thingselseifoption=='collapse'thenargs.collapse=1-- some "options" are to modify the name and some are commands to do thingselseif#option>3thenargs.option=optionendendifargs.wikidata=="create"thenargs.command="create item"args.wikidata=nilend-- Create invisible language independent marking in format similar to QuickStatements code based on Wikidata and OptionlocalQS=''ifargs.wikidatathenargs.wikidata=string.upper(args.wikidata)endifargs.wikidataandstring.match(args.wikidataor'',"^Q%d+$")then-- invisible language independent markingifnotargs.optionthen-- no "option" modifierQS=TagQS.createTag('creator','P170',args.wikidata)elseargs.option=mw.ustring.gsub(mw.ustring.lower(args.option),'_',' ')-- normalize input string-- Not handled options: "studio of", "or follower", "or workshop", "and workshop", "formerly attributed to"localqual1={['workshop of']='P1774',['follower of']='P1775',['circle of']='P1776',['manner of']='P1777',['school of']='P1780',['after']='P1877'}-- ['possibly']='P1779',localqual2={['probably']='Q56644435',['presumably']='Q18122778',['possibly']='Q30230067',['attributed to']='Q230768'}ifqual1[args.option]then-- use anonymous = Q4233718 with qualifierQS=TagQS.createTag('creator','P170',string.format('Q4233718,%s,%s',qual1[args.option],args.wikidata))elseifqual2[args.option]then-- use "nature of statement" (P5102) qualifierQS=TagQS.createTag('creator','P170',string.format('%s,P5102,%s',args.wikidata,qual2[args.option]))endendend-- call the inner "core" functionlocalresults,cats=p._creator(args)returnresults..QS..catsendreturnp