Module:ConvertIB/sandbox
Appearance
From Wikipedia, the free encyclopedia
This is the module sandbox page for Module:ConvertIB (diff).
See also the companion subpage for test cases (run).
See also the companion subpage for test cases (run).
Warning This Lua module is used on approximately 761,000 pages, or roughly 1% of all pages .
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them.
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them.
A Lua module that wraps {{convert }}, designed for infoboxes. It implements:
- {{convinfobox }}
- {{Infobox settlement/areadisp }}
- {{Infobox settlement/lengthdisp }}
- {{Infobox settlement/densdisp }}
Usage
[edit ]{{#invoke:ConvertIB|convert}}
- Like {{convinfobox }}, accepts alternating series of pairs of [blank|value], unit . When a unit has a non-blank value, it will get converted to all other units that do have blank values
- Accepts all named parameters that {{convert }} does
- Accepts groups of multiple units (e.g., "5 ft 6 in") that {{convert }} does
{{#invoke:ConvertIB|area}}
- Implements {{Infobox settlement/areadisp }}, automatically converting area units, using the output order specified by MOS:UNIT
{{#invoke:ConvertIB|length}}
- Implements {{Infobox settlement/lengthdisp }}, automatically converting length units, using the output order specified by MOS:UNIT
{{#invoke:ConvertIB|density}}
- Implements {{Infobox settlement/densdisp }}, parsing population and area, producting density in inhabitants per square km and square mile.
The above documentation is transcluded from Module:ConvertIB/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Add categories to the /doc subpage. Subpages of this module.
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Add categories to the /doc subpage. Subpages of this module.
require('strict') localp={} localgetArgs=require('Module:Arguments').getArgs -- Function to pull out values and units from numeric args -- Returns: -- values: list of numeric values, or "false" if no numeric argument is given -- units: list of units (str) -- value: if there is a last numeric value unpaired with a unit, it becomes the precision -- anyValue: whether there is a non-false value in the values list localfunctionparseValuesUnits(args) localvalues={} localunits={} localindx=1 localvalue=nil localanyValue=false -- loop through numeric arguments in pairs whileargs[indx]orargs[indx+1]do value=args[indx] anyValue=anyValueorvalue -- if there is a unit, save in output lists ifargs[indx+1]then table.insert(values,valueorfalse) table.insert(units,args[indx+1]) value=nil end indx=indx+2 end returnvalues,units,value,anyValue end -- Function to identify multiple units and rewrite them as new input or output groups -- Args: -- values, units: numeric values and units, as lists with same length -- Returns: -- newValues, newUnits: same lists rewritten localfunctionparseMultiples(values,units) localnewValues={} localnewUnits={} locali=1 -- we will search for multiples with up to 4 entries (depending on length) localmaxMultiple=math.min(4,#units-1) localvalueFound=false-- flag to suppress second (and later) input values --- Hack for handling "stone": check if only value supplied is "lb" localonlyPounds=true fori=1,#unitsdo ifvalues[i]andunits[i]~='lb'then onlyPounds=false break end end localmultiple=mw.loadData('Module:ConvertIB/data').multiple -- sweep through units whilei<=#unitsdo -- determine index of last possible unit that could contain a multiple locallast_unit=math.min(i+maxMultiple-1,#units) localmultipleFound=false -- try from longest multiple down to double multiple (prefer longest ones) forj=last_unit,i+1,-1do localkey=table.concat({unpack(units,i,j)},'') ifmultiple[key]then -- we found a multiple unit multipleFound=true -- Hack for "stone": add either 'lb' or multiple unit string to output units -- depending on whether 'lb' was the only unit string with a value ifmw.ustring.sub(key,1,2)=='st'then table.insert(newValues,false) table.insert(newUnits,onlyPoundsandkeyor'lb') end -- if there are any value in the span of the multiple, -- then the multiple is an input -- assume all missing values after the first are zero localfirstValueFound=false fork=i,jdo firstValueFound=notvalueFoundand(firstValueFoundorvalues[k]) iffirstValueFoundthen table.insert(newValues,values[k]or0) table.insert(newUnits,units[k]) end end valueFound=valueFoundorfirstValueFound -- if no values in the span of the multiple, -- then the multiple is an output. Insert combined string as output unit ifnotfirstValueFoundthen table.insert(newValues,false) table.insert(newUnits,key) end i=j+1 break end end --- If no multiple unit was found, insert value[i] and unit[i] into rewritten lists ifnotmultipleFoundthen ifvalueFoundthen table.insert(newValues,false)-- skip writing value if it is a duplicate else table.insert(newValues,values[i]) valueFound=values[i] end table.insert(newUnits,units[i]) i=i+1 end end returnnewValues,newUnits end -- Call {{convert}} with args localfunctioncallConvert(args) localframe=mw.getCurrentFrame() returnframe:expandTemplate{title='Convert',args=args} end -- Implement {{convinfobox}} functionp._convert(args) -- find all values and units in numeric args (and the precision, if it exists) localvalues,units,precision,anyValue=parseValuesUnits(args) -- bail if no values at all ifnotanyValuethen returnnil end -- rewrite values and units if multiple units are found values,units=parseMultiples(values,units) -- sort input and outputs into different buckets localinput_values={} localinput_units={} localoutput_units={} fori=1,#unitsdo ifvalues[i]then table.insert(input_values,values[i]) table.insert(input_units,units[i]) else table.insert(output_units,units[i]) end end -- bail if nothing to convert if#input_values==0or#output_units==0then returnnil end -- assemble argument list to {{convert}} localinnerArgs={} -- First, pass all input unit(s) fori,vinipairs(input_values)do table.insert(innerArgs,v) table.insert(innerArgs,input_units[i]) end -- Then the output unit(s) [concatenated as single argument] table.insert(innerArgs,table.concat(output_units,"+")) ifprecisionthen table.insert(innerArgs,precision)-- last non-nil value contains precision end -- now handle all non-numeric arguments, passing to {{convert}} innerArgs.abbr='on'-- abbr=on by default fork,vinpairs(args)do ifnottonumber(k)then innerArgs[k]=v end end returncallConvert(innerArgs) end localfunctionimpUnitPref(pref,country) --- lower case all arguments pref=prefandmw.ustring.lower(pref) country=countryandmw.ustring.lower(country) --- determine imperial unit by going thru arguments in priority order ifprefandpref~='dunam'then localimpPref=mw.loadData('Module:ConvertIB/data').impPref returnimpPref[pref] elseifcountrythen returnmw.ustring.find(country,"united states",1,true) ormw.ustring.find(country,"united kingdom",1,true) end returnfalse end locallargeUnits={km2=true,mi2=true,sqmi=true,km=true,mi=true} -- Implement {{Infobox settlement/areadisp}} functionp._area(args) localpref=args['pref'] localcountry=args['name'] localimpus=impUnitPref(pref,country) localdunam=args['dunam']orargs['dunum'] locallink=args['link'] localinnerArgs={} innerArgs.abbr='on' innerArgs.order='out' for_,unitinipairs({'km2','mi2','sqmi','ha','acre'})do ifargs[unit]then table.insert(innerArgs,args[unit]) table.insert(innerArgs,unit) iflargeUnits[unit]then table.insert(innerArgs,impusand'sqmi km2'or'km2 sqmi') else table.insert(innerArgs,impusand'acre ha'or'ha acre') end returncallConvert(innerArgs) end end ifdunamthen table.insert(innerArgs,dunam) table.insert(innerArgs,'dunam') pref=prefandmw.ustring.lower(pref) localorder=pref=='dunam'and'dunam 'or'' dunam=mw.getContentLanguage():parseFormattedNumber(dunam) ifimpusthen order=order..(dunamanddunam<2589and'acre ha'or'sqmi km2') else order=order..(dunamanddunam<1000and'ha acre'or'km2 sqmi') end table.insert(innerArgs,order) localyesNo=require('Module:Yesno') ifyesNo(link,true)andlink~='none'then innerArgs.lk='in' end returncallConvert(innerArgs) end returnnil end -- Implement {{Infobox settlement/lengthdisp}} functionp._length(args) localpref=args['pref'] ifpref=='dunam'then-- ignore dunam pref for this function pref=nil end localcountry=args['name'] localimpus=impUnitPref(pref,country) localinnerArgs={} innerArgs.abbr='on' innerArgs.order='out' for_,unitinipairs({'km','mi','m','ft'})do ifargs[unit]then table.insert(innerArgs,args[unit]) table.insert(innerArgs,unit) iflargeUnits[unit]then table.insert(innerArgs,impusand'mi km'or'km mi') else table.insert(innerArgs,impusand'ft m'or'm ft') end returncallConvert(innerArgs) end end returnnil end locallang localfunctionparseNumeric(s) localnum_str=string.match(tostring(s),'^[+-]?%d[%d,]*%.?%d*e?[+-]?%d*') ifnotnum_strthen returnnil end lang=langormw.getContentLanguage() returnlang:parseFormattedNumber(num_str) end --Compute number of significant digits in a numeric string localfunctioncomputeSigFig(s) localnum_str=string.match(tostring(s),'^[+-]?%d[%d,]*%.?%d*') ifnotnum_strthen return0 end -- Strip leading signs num_str=string.gsub(num_str,'^[+-]','') -- Strip commas num_str=string.gsub(num_str,',','') -- Strip leading zeros num_str=string.gsub(num_str,'^0*','') ifnum_str==''then return0 end -- If there's a decimal point, all trailing zeros are significant. ifstring.find(num_str,'%.')then return#string.gsub(num_str,'%.','')-- Count all digits after removing decimal end -- If no decimal point, trailing zeros are not significant. -- Count all digits up to the last non-zero one. num_str=string.gsub(num_str,'0+$','') return#num_str end -- Implement {{Infobox settlement/densdisp}} -- Returns table: -- density = computed value if no error -- error = error string if error. -- These are only errors detected in this code, {{convert}} does its own error handling functionp._density(args) localresult={} localpref=args['pref'] ifpref=='dunam'then-- ignore dunam pref for this function pref=nil end localcountry=args['name'] localper_km2=args['/km2'] localper_mi2=args['/mi2']orargs['/sqmi'] localimpus=impUnitPref(pref,country,per_km2,per_mi2) localper_km2_value=parseNumeric(per_km2) localper_mi2_value=parseNumeric(per_mi2) localinnerArgs={} innerArgs.abbr='on' ifper_km2_valueorper_mi2_valuethen innerArgs.order='out' ifper_km2_valuethen table.insert(innerArgs,per_km2_value) table.insert(innerArgs,'/km2') else table.insert(innerArgs,per_mi2_value) table.insert(innerArgs,'/sqmi') end table.insert(innerArgs,impusand'/sqmi /km2'or'/km2 /sqmi') result.density=callConvert(innerArgs) returnresult end ifper_km2~='auto'andper_mi2~='auto'then -- automatic computation not requested, fail silently returnresult end ifnotargs['pop']then -- fail silently if no population given returnresult end localareaSigFig localareaValue localareaUnit for_,unitinipairs({'km2','mi2','sqmi','ha','acre','dunam','dunum'})do localvalue=parseNumeric(args[unit]) ifvaluethen ifvalue<=0then result.error=unit.." value not positive" returnresult end areaValue=value areaUnit=unit areaSigFig=computeSigFig(args[unit]) break elseifargs[unit]then result.error="Malformed "..unit.." value" returnresult end end ifnotareaSigFigthen -- fail silently if no area given returnresult end ifareaSigFig==0then result.error="Malformed area string" returnresult end localpopValue=parseNumeric(args['pop']) ifnotpopValuethen result.error="Malformed population value" returnresult end ifpopValue<0then result.error="Negative population value" returnresult end table.insert(innerArgs,popValue/areaValue) table.insert(innerArgs,'/'..areaUnit) localpopSigFig=computeSigFig(args['pop']) localsigFig=popSigFig<areaSigFigandpopSigFigorareaSigFig ifsigFig<2then sigFig=2 end innerArgs.sigfig=sigFig innerArgs.disp='out' table.insert(innerArgs,'/km2') localmetric=callConvert(innerArgs) innerArgs[3]='/sqmi' localimperial=callConvert(innerArgs) ifimpusthen result.density=string.format("%s (%s)",imperial,metric) else result.density=string.format("%s (%s)",metric,imperial) end returnresult end functionp.convert(frame) localargs=getArgs(frame) returnp._convert(args)or"" end functionp.area(frame) localargs=getArgs(frame) returnp._area(args)or"" end functionp.length(frame) localargs=getArgs(frame) returnp._length(args)or"" end functionp.density(frame) localargs=getArgs(frame) localresult=p._density(args) ifresult.densitythen returnresult.density end ifresult.errorthen localwarning=require('Module:If_preview')._warning localresult=warning({result.error}) ifmw.title.getCurrentTitle().namespace==0then result=result..'[[Category:Pages using infobox settlement with bad density arguments]]' end returnresult end return'' end returnp