https://de.wikipedia.org/w/index.php?action=history&feed=atom&title=Module%3AConvert%2Fwikidata Modul:Convert/wikidata - Versionsgeschichte 2025-06-09T06:39:40Z Versionsgeschichte dieser Seite in Wikipedia MediaWiki 1.45.0-wmf.4 https://de.wikipedia.org/w/index.php?title=Modul:Convert/wikidata&diff=178363191&oldid=prev Nov3rd17: aus engl.Wikipedia 2018-06-16T08:48:42Z <p>aus engl.Wikipedia</p> <p><b>Neue Seite</b></p><div>-- Functions to access Wikidata for Module:Convert.<br /> <br /> local Collection = {}<br /> Collection.__index = Collection<br /> do<br /> function Collection:add(item)<br /> if item ~= nil then<br /> self.n = self.n + 1<br /> self[self.n] = item<br /> end<br /> end<br /> function Collection:join(sep)<br /> return table.concat(self, sep)<br /> end<br /> function Collection:remove(pos)<br /> if self.n &gt; 0 and (pos == nil or (0 &lt; pos and pos &lt;= self.n)) then<br /> self.n = self.n - 1<br /> return table.remove(self, pos)<br /> end<br /> end<br /> function Collection:sort(comp)<br /> table.sort(self, comp)<br /> end<br /> function Collection.new()<br /> return setmetatable({n = 0}, Collection)<br /> end<br /> end<br /> <br /> local function strip_to_nil(text)<br /> -- If text is a non-empty string, return its trimmed content,<br /> -- otherwise return nothing (empty string or not a string).<br /> if type(text) == &#039;string&#039; then<br /> return text:match(&#039;(%S.-)%s*$&#039;)<br /> end<br /> end<br /> <br /> local function frequency_unit(value, unit_table)<br /> -- For use when converting m to Hz.<br /> -- Return true, s where s = name of unit&#039;s default output unit,<br /> -- or return false, t where t is an error message table.<br /> -- However, for simplicity a valid result is always returned.<br /> local unit<br /> if unit_table._symbol == &#039;m&#039; then<br /> -- c = speed of light in a vacuum = 299792458 m/s<br /> -- frequency = c / wavelength<br /> local w = value * (unit_table.scale or 1)<br /> local f = 299792458 / w -- if w == 0, f = math.huge which works here<br /> if f &gt;= 1e12 then<br /> unit = &#039;THz&#039;<br /> elseif f &gt;= 1e9 then<br /> unit = &#039;GHz&#039;<br /> elseif f &gt;= 1e6 then<br /> unit = &#039;MHz&#039;<br /> elseif f &gt;= 1e3 then<br /> unit = &#039;kHz&#039;<br /> else<br /> unit = &#039;Hz&#039;<br /> end<br /> end<br /> return true, unit or &#039;Hz&#039;<br /> end<br /> <br /> local function wavelength_unit(value, unit_table)<br /> -- Like frequency_unit but for use when converting Hz to m.<br /> local unit<br /> if unit_table._symbol == &#039;Hz&#039; then<br /> -- Using 0.9993 rather than 1 avoids rounding which would give results<br /> -- like converting 300 MHz to 100 cm instead of 1 m.<br /> local w = 1 / (value * (unit_table.scale or 1)) -- Hz scale is inverted<br /> if w &gt;= 0.9993e6 then<br /> unit = &#039;Mm&#039;<br /> elseif w &gt;= 0.9993e3 then<br /> unit = &#039;km&#039;<br /> elseif w &gt;= 0.9993 then<br /> unit = &#039;m&#039;<br /> elseif w &gt;= 0.9993e-2 then<br /> unit = &#039;cm&#039;<br /> elseif w &gt;= 0.9993e-3 then<br /> unit = &#039;mm&#039;<br /> else<br /> unit = &#039;um&#039;<br /> end<br /> end<br /> return true, unit or &#039;m&#039;<br /> end<br /> <br /> local specials = {<br /> frequency = { frequency_unit },<br /> wavelength = { wavelength_unit },<br /> --------------------------------------------------------------------------------<br /> -- Following is a removed experiment to show two values as a range<br /> -- using &#039;-&#039; as the separator.<br /> -- frequencyrange = { frequency_unit, &#039;-&#039; },<br /> -- wavelengthrange = { wavelength_unit, &#039;-&#039; },<br /> }<br /> <br /> local function make_unit(units, parms, uid)<br /> -- Return a unit code for convert or nil if unit unknown.<br /> -- If necessary, add a dummy unit to parms so convert will use it<br /> -- for the input without attempting a conversion since nothing<br /> -- useful is available (for example, with unit volt).<br /> local unit = units[uid]<br /> if type(unit) ~= &#039;table&#039; then<br /> return nil<br /> end<br /> local ucode = unit.ucode<br /> if ucode and not unit.si then<br /> return ucode -- a unit known to convert<br /> end<br /> parms.opt_ignore_error = true<br /> ucode = ucode or unit._ucode -- must be a non-empty string<br /> local ukey, utable<br /> if unit.si then<br /> local base = units[unit.si]<br /> ukey = base.symbol -- must be a non-empty string<br /> local n1 = base.name1<br /> local n2 = base.name2<br /> if not n1 then<br /> n1 = ukey<br /> n2 = n2 or n1 -- do not append &#039;s&#039;<br /> end<br /> utable = {<br /> _symbol = ukey,<br /> _name1 = n1,<br /> _name2 = n2,<br /> link = unit.link or base.link,<br /> utype = n1,<br /> prefixes = 1,<br /> }<br /> else<br /> ukey = ucode<br /> utable = {<br /> symbol = ucode, -- must be a non-empty string<br /> name1 = unit.name1, -- if nil, uses symbol<br /> name2 = unit.name2, -- if nil, uses name1..&#039;s&#039;<br /> link = unit.link, -- if nil, uses name1<br /> utype = unit.name1 or ucode,<br /> }<br /> end<br /> utable.scale = 1<br /> utable.default = &#039;&#039;<br /> utable.defkey = &#039;&#039;<br /> utable.linkey = &#039;&#039;<br /> utable.bad_mcode = &#039;&#039;<br /> parms.unittable = { [ukey] = utable }<br /> return ucode<br /> end<br /> <br /> local function matches_qualifier(statement, qual)<br /> -- Return:<br /> -- false, nil : if statement does not match specification<br /> -- true, nil : if matches, and statement has no qualifier<br /> -- true, sq : if matches, where sq is the statement&#039;s qualifier<br /> -- A match means that no qualifier was specified (qual == nil), or that<br /> -- the statement has a qualifier matching the specification.<br /> -- If a match occurs, the caller needs the statement&#039;s qualifier (if any)<br /> -- so statements that duplicate the qualifier are not used, after the first.<br /> -- Then, if convert is showing all values for a property such as the diameter<br /> -- of a telescope&#039;s mirror (diameters of primary and secondary mirrors), it<br /> -- will not show alternative values that could in principle be present for the<br /> -- same item (telescope) and property (diameter) and qualifier (primary/secondary).<br /> local target = (statement.qualifiers or {}).P518 -- P518 is &quot;applies to part&quot;<br /> if type(target) == &#039;table&#039; then<br /> for _, q in ipairs(target) do<br /> if type(q) == &#039;table&#039; then<br /> local value = (q.datavalue or {}).value<br /> if value then<br /> if qual == nil or qual == value.id then<br /> return true, value.id<br /> end<br /> end<br /> end<br /> end<br /> end<br /> if qual == nil then<br /> return true, nil -- only occurs if statement has no qualifier<br /> end<br /> return false, nil -- statement&#039;s qualifier is not relevant because statement will be skipped<br /> end<br /> <br /> local function get_statements(parms, pid)<br /> -- Get specified item and return a list of tables with each statement for property pid.<br /> -- Each table is of form {statqual=sq, stmt=statement} where sq = statement qualifier (nil if none).<br /> -- Statements are in Wikidata&#039;s order except that those with preferred rank<br /> -- are first, then normal rank. Any other rank is ignored.<br /> local stored = {} -- qualifiers of statements that are first for the qualifier, and will be returned<br /> local qid = strip_to_nil(parms.qid) -- nil for current page&#039;s item, or an item id (expensive)<br /> local qual = strip_to_nil(parms.qual) -- nil or id of wanted P518 (applies to part) item in qualifiers<br /> local result = Collection.new()<br /> local entity = mw.wikibase.getEntity(qid)<br /> if type(entity) == &#039;table&#039; then<br /> local statements = (entity.claims or {})[pid]<br /> if type(statements) == &#039;table&#039; then<br /> for _, rank in ipairs({ &#039;preferred&#039;, &#039;normal&#039; }) do<br /> for _, statement in ipairs(statements) do<br /> if type(statement) == &#039;table&#039; and rank == statement.rank then<br /> local is_match, statqual = matches_qualifier(statement, qual)<br /> if is_match then<br /> result:add({ statqual = statqual, stmt = statement })<br /> end<br /> end<br /> end<br /> end<br /> end<br /> end<br /> return result<br /> end<br /> <br /> local function input_from_property(tdata, parms, pid)<br /> -- Given that pid is a Wikidata property identifier like &#039;P123&#039;,<br /> -- return a collection of {amount, ucode} pairs (two strings)<br /> -- for each matching item/property, or return nothing.<br /> --------------------------------------------------------------------------------<br /> -- There appear to be few restrictions on how Wikidata is organized so it is<br /> -- very likely that any decision a module makes about how to handle data<br /> -- will be wrong for some cases at some time. This meets current requirements.<br /> -- For each qualifier (or if no qualitifer), if there are any preferred<br /> -- statements, use them and ignore any normal statements.<br /> -- For each qualifier, for the preferred statements if any, or for<br /> -- the normal statements (but not both):<br /> -- * Accept each statement if it has no qualifier (this will not occur<br /> -- if qual=x is specified because other code already ensures that in that<br /> -- case, only statements with a qualifier matching x are considered).<br /> -- * Ignore any statements after the first if it has a qualifier.<br /> -- The rationale is that for the diameter at [[South Pole Telescope]], want<br /> -- convert to show the diameters for both the primary and secondary mirrors<br /> -- if the convert does not specify which diameter is wanted.<br /> -- However, if convert is given the wanted qualifier, only one value<br /> -- (_the_ diameter) is wanted. For simplicity/consistency, that is also done<br /> -- even if no qual=x is specified. Unclear what should happen.<br /> -- For the wavelength at [[Nançay Radio Telescope]], want to show all three<br /> -- values, and the values have no qualifiers.<br /> --------------------------------------------------------------------------------<br /> local result = Collection.new()<br /> local done = {}<br /> local skip_normal<br /> for _, t in ipairs(get_statements(parms, pid)) do<br /> local statement = t.stmt<br /> if statement.mainsnak and statement.mainsnak.datatype == &#039;quantity&#039; then<br /> local value = (statement.mainsnak.datavalue or {}).value<br /> if value then<br /> local amount = value.amount<br /> if amount then<br /> amount = tostring(amount) -- in case amount is ever a number<br /> if amount:sub(1, 1) == &#039;+&#039; then<br /> amount = amount:sub(2)<br /> end<br /> local unit = value.unit<br /> if type(unit) == &#039;string&#039; then<br /> unit = unit:match(&#039;Q%d+$&#039;) -- unit item id is at end of URL<br /> local ucode = make_unit(tdata.wikidata_units, parms, unit)<br /> if ucode then<br /> local skip<br /> if t.statqual then<br /> if done[t.statqual] then<br /> skip = true<br /> else<br /> done[t.statqual] = true<br /> end<br /> else<br /> if statement.rank == &#039;preferred&#039; then<br /> skip_normal = true<br /> elseif skip_normal then<br /> skip = true<br /> end<br /> end<br /> if not skip then<br /> result:add({ amount, ucode })<br /> end<br /> end<br /> end<br /> end<br /> end<br /> end<br /> end<br /> return result<br /> end<br /> <br /> local function input_from_text(tdata, parms, text, insert2)<br /> -- Given string should be of form &quot;&lt;value&gt;&lt;space&gt;&lt;unit&gt;&quot; or<br /> -- &quot;&lt;value1&gt;&lt;space&gt;ft&lt;space&gt;&lt;value2&gt;&lt;space&gt;in&quot; for a special case (feet and inches).<br /> -- Return true if values/units were extracted and inserted, or return nothing.<br /> text = text:gsub(&#039;&amp;nbsp;&#039;, &#039; &#039;):gsub(&#039;%s+&#039;, &#039; &#039;)<br /> local pos = text:find(&#039; &#039;, 1, true)<br /> if pos then<br /> -- Leave checking of value to convert which can handle fractions.<br /> local value = text:sub(1, pos - 1)<br /> local uid = text:sub(pos + 1)<br /> if uid:sub(1, 3) == &#039;ft &#039; and uid:sub(-3) == &#039; in&#039; then<br /> -- Special case for enwiki to allow {{convert|input=5 ft 10+1/2 in}}<br /> insert2(uid:sub(4, -4), &#039;in&#039;)<br /> insert2(value, &#039;ft&#039;)<br /> else<br /> insert2(value, make_unit(tdata.wikidata_units, parms, uid) or uid)<br /> end<br /> return true<br /> end<br /> end<br /> <br /> local function adjustparameters(tdata, parms, index)<br /> -- For Module:Convert, adjust parms (a table of {{convert}} parameters).<br /> -- Return true if successful or return false, t where t is an error message table.<br /> -- This is intended mainly for use in infoboxes where the input might be<br /> -- &lt;value&gt;&lt;space&gt;&lt;unit&gt; or<br /> -- &lt;wikidata-property-id&gt;<br /> -- If successful, insert values and units in parms, before given index.<br /> local text = parms.input -- should be a trimmed, non-empty string<br /> local pid = text:match(&#039;^P%d+$&#039;)<br /> local sep = &#039;,&#039;<br /> local special = specials[parms[index]]<br /> if special then<br /> parms.out_unit = special[1]<br /> sep = special[2] or sep<br /> table.remove(parms, index)<br /> end<br /> local function quit()<br /> return false, pid and { &#039;cvt_no_output&#039; } or { &#039;cvt_bad_input&#039;, text }<br /> end<br /> local function insert2(first, second)<br /> table.insert(parms, index, second)<br /> table.insert(parms, index, first)<br /> end<br /> if pid then<br /> parms.input_text = &#039;&#039; -- output an empty string if an error occurs<br /> local result = input_from_property(tdata, parms, pid)<br /> if result.n == 0 then<br /> return quit()<br /> end<br /> local ucode<br /> for i, t in ipairs(result) do<br /> -- Convert requires each input unit to be identical.<br /> if i == 1 then<br /> ucode = t[2]<br /> elseif ucode ~= t[2] then<br /> return quit()<br /> end<br /> end<br /> local item = ucode<br /> if item == parms[index] then<br /> -- Remove specified output unit if it is the same as the Wikidata unit.<br /> -- For example, {{convert|input=P2044|km}} with property &quot;12 km&quot;.<br /> table.remove(parms, index)<br /> end<br /> for i = result.n, 1, -1 do<br /> insert2(result[i][1], item)<br /> item = sep<br /> end<br /> return true<br /> else<br /> if input_from_text(tdata, parms, text, insert2) then<br /> return true<br /> end<br /> end<br /> return quit()<br /> end<br /> <br /> --------------------------------------------------------------------------------<br /> --- List units and check syntax of definitions ---------------------------------<br /> --------------------------------------------------------------------------------<br /> local specifications = {<br /> -- seq = sequence in which fields are displayed<br /> base = {<br /> title = &#039;SI base units&#039;,<br /> fields = {<br /> symbol = { seq = 2, mandatory = true },<br /> name1 = { seq = 3, mandatory = true },<br /> name2 = { seq = 4 },<br /> link = { seq = 5 },<br /> },<br /> noteseq = 6,<br /> header = &#039;{| class=&quot;wikitable&quot;\n!si !!symbol !!name1 !!name2 !!link !!note&#039;,<br /> item = &#039;|-\n|%s ||%s ||%s ||%s ||%s ||%s&#039;,<br /> footer = &#039;|}&#039;,<br /> },<br /> alias = {<br /> title = &#039;Aliases for convert&#039;,<br /> fields = {<br /> ucode = { seq = 2, mandatory = true },<br /> si = { seq = 3 },<br /> },<br /> noteseq = 4,<br /> header = &#039;{| class=&quot;wikitable&quot;\n!alias !!ucode !!base !!note&#039;,<br /> item = &#039;|-\n|%s ||%s ||%s ||%s&#039;,<br /> footer = &#039;|}&#039;,<br /> },<br /> known = {<br /> title = &#039;Units known to convert&#039;,<br /> fields = {<br /> ucode = { seq = 2, mandatory = true },<br /> label = { seq = 3, mandatory = true },<br /> },<br /> noteseq = 4,<br /> header = &#039;{| class=&quot;wikitable&quot;\n!qid !!ucode !!label !!note&#039;,<br /> item = &#039;|-\n|%s ||%s ||%s ||%s&#039;,<br /> footer = &#039;|}&#039;,<br /> },<br /> unknown = {<br /> title = &#039;Units not known to convert&#039;,<br /> fields = {<br /> _ucode = { seq = 2, mandatory = true },<br /> si = { seq = 3 },<br /> name1 = { seq = 4 },<br /> name2 = { seq = 5 },<br /> link = { seq = 6 },<br /> label = { seq = 7, mandatory = true },<br /> },<br /> noteseq = 8,<br /> header = &#039;{| class=&quot;wikitable&quot;\n!qid !!_ucode !!base !!name1 !!name2 !!link !!label !!note&#039;,<br /> item = &#039;|-\n|%s ||%s ||%s ||%s ||%s ||%s ||%s ||%s&#039;,<br /> footer = &#039;|}&#039;,<br /> },<br /> }<br /> <br /> local function listunits(tdata, ulookup)<br /> -- For Module:Convert, make wikitext to list the built-in Wikidata units.<br /> -- Return true, wikitext if successful or return false, t where t is an<br /> -- error message table. Currently, an error return never occurs.<br /> -- The syntax of each unit definition is checked and a note is added if<br /> -- a problem is detected.<br /> local function safe_cells(t)<br /> -- This is not currently needed, but in case definitions ever use wikitext<br /> -- like &#039;[[kilogram|kg]]&#039;, escape the text so it works in a table cell.<br /> local result = {}<br /> for i, v in ipairs(t) do<br /> if v:find(&#039;|&#039;, 1, true) then<br /> v = v:gsub(&#039;(%[%[[^%[%]]-)|(.-%]%])&#039;, &#039;%1\0%2&#039;) -- replace pipe in piped link with a zero byte<br /> v = v:gsub(&#039;|&#039;, &#039;&amp;#124;&#039;) -- escape &#039;|&#039;<br /> v = v:gsub(&#039;%z&#039;, &#039;|&#039;) -- restore pipe in piped link<br /> end<br /> result[i] = v:gsub(&#039;{&#039;, &#039;&amp;#123;&#039;) -- escape &#039;{&#039;<br /> end<br /> return unpack(result)<br /> end<br /> local wdunits = tdata.wikidata_units<br /> local speckeys = { &#039;base&#039;, &#039;alias&#039;, &#039;unknown&#039;, &#039;known&#039; }<br /> for _, sid in ipairs(speckeys) do<br /> specifications[sid].units = Collection.new()<br /> end<br /> local keys = Collection.new()<br /> for k, v in pairs(wdunits) do<br /> keys:add(k)<br /> end<br /> table.sort(keys)<br /> local note_count = 0<br /> for _, key in ipairs(keys) do<br /> local unit = wdunits[key]<br /> local ktext, sid<br /> if key:match(&#039;^Q%d+$&#039;) then<br /> ktext = &#039;[[d:&#039; .. key .. &#039;|&#039; .. key .. &#039;]]&#039;<br /> if unit.ucode then<br /> sid = &#039;known&#039;<br /> else<br /> sid = &#039;unknown&#039;<br /> end<br /> elseif unit.ucode then<br /> ktext = key<br /> sid = &#039;alias&#039;<br /> else<br /> ktext = key<br /> sid = &#039;base&#039;<br /> end<br /> local result = { ktext }<br /> local spec = specifications[sid]<br /> local fields = spec.fields<br /> local note = Collection.new()<br /> for k, v in pairs(unit) do<br /> if fields[k] then<br /> local seq = fields[k].seq<br /> if result[seq] then<br /> note:add(&#039;duplicate &#039; .. k) -- cannot happen since keys are unique<br /> else<br /> result[seq] = v<br /> end<br /> else<br /> note:add(&#039;invalid &#039; .. k)<br /> end<br /> end<br /> for k, v in pairs(fields) do<br /> local value = result[v.seq]<br /> if value then<br /> if k == &#039;si&#039; and not wdunits[value] then<br /> note:add(&#039;need si &#039; .. value)<br /> end<br /> if k == &#039;label&#039; then<br /> local wdl = mw.wikibase.label(key)<br /> if wdl ~= value then<br /> note:add(&#039;label changed to &#039; .. tostring(wdl))<br /> end<br /> end<br /> else<br /> result[v.seq] = &#039;&#039;<br /> if v.mandatory then<br /> note:add(&#039;missing &#039; .. k)<br /> end<br /> end<br /> end<br /> local text<br /> if note.n &gt; 0 then<br /> note_count = note_count + 1<br /> text = &#039;*&#039; .. note:join(&#039;&lt;br /&gt;&#039;)<br /> end<br /> result[spec.noteseq] = text or &#039;&#039;<br /> spec.units:add(result)<br /> end<br /> local results = Collection.new()<br /> if note_count &gt; 0 then<br /> local text = note_count .. (note_count == 1 and &#039; note&#039; or &#039; notes&#039;)<br /> results:add(&quot;&#039;&#039;&#039;Search for * to see &quot; .. text .. &quot;&#039;&#039;&#039;\n&quot;)<br /> end<br /> for _, sid in ipairs(speckeys) do<br /> local spec = specifications[sid]<br /> results:add(&quot;&#039;&#039;&#039;&quot; .. spec.title .. &quot;&#039;&#039;&#039;&quot;)<br /> results:add(spec.header)<br /> local fmt = spec.item<br /> for _, unit in ipairs(spec.units) do<br /> results:add(string.format(fmt, safe_cells(unit)))<br /> end<br /> results:add(spec.footer)<br /> end<br /> return true, results:join(&#039;\n&#039;)<br /> end<br /> <br /> return { _adjustparameters = adjustparameters, _listunits = listunits }</div> Nov3rd17