Open serpico opened 3 years ago
That could look something like this:
// template.js
const datePattern = /^\d{4}-\d{2}-\d{2}$/
const itemIdPattern = /^Q\d+$/
const ordinalPattern = /^\d+$/
module.exports = (label, year, location, start, end, follows, isFollowedBy, ordinal) => {
const championship = championships[year]
const type = types[location]
const location = locations[location]
// Validating arguments as it would be easy to get messed up given the number of arguments to pass
if (label.split(' ').length < 4) throw new Error(`that label looks too short: ${label}`)
if (!championship) throw new Error(`unknown championship: ${championship}`)
if (!type) throw new Error(`unknown type: ${type}`)
if (!location) throw new Error(`unknown location: ${location}`)
if (!datePattern.test(start)) throw new Error(`invalid start date: ${start}`)
if (!datePattern.test(end)) throw new Error(`invalid end date: ${end}`)
// Make those qualifier values optional: pass null to skip
if (follows === 'null') follows === null
if (isFollowedBy === 'null') isFollowedBy === null
if (follows && !itemIdPattern.test(follows)) throw new Error(`invalid follows item id: ${follows}`)
if (isFollowedBy && !itemIdPattern.test(isFollowedBy)) throw new Error(`invalid isFollowedBy item id: ${isFollowedBy}`)
if (!ordinalPattern.test(ordinal)) throw new Error(`invalid ordinal number: ${ordinal}`)
const qualifiers = {
P156: ordinal
}
if (follows) qualifiers.P155 = follows
if (isFollowedBy) qualifiers.P156 = isFollowedBy
const newItem = {
labels: {
en: label
},
claims: {
P31: { value: type, qualifiers },
P361: { value: championship, qualifiers },
P17: 'Q17',
P276: location,
P580: start,
P582: end,
}
}
return newItem
}
const championships = {
2019: 'Q106725472',
2018: 'Q106726771',
2017: 'Q106726795',
}
const types = {
'Okayama': 'Q106712954',
'Motegi': 'Q106712966',
}
const locations = {
'Okayama': 'Q173185',
'Motegi': 'Q1420895',
}
to be then called from the CLI like that:
wd create-entity ./template.js "2019 All Japan Road Race Championship Round 6 Okayama" 2019 Okayama 2019-08-30 2019-09-01 null Q106713038 12
I haven't tested it so it might require some adjustments ^^
@maxlath Beautiful!! I'll play with it sometimes next week most probably ( long enough so I don't ask silly questions ) and I'll get back here to tell you how it went. Thanks a lot :fire: :rocket:
I completed the const lists at the bottom as follow :
const championships = {
2021: 'Q106735845',
2020: 'Q106707657',
2019: 'Q106725472',
2018: 'Q106726771',
2017: 'Q106726795',
2016: 'Q106726816',
2015: 'Q106726831',
}
const types = {
'Autopolis': 'Q106705839',
'Autopolis 2&4' : 'Q106763105',
'MFJ GP' : 'Q106712980',
'Motegi': 'Q106712966',
'Motegi 2&4' : 'Q106763118',
'Okayama': 'Q106712954',
'Sugo' : 'Q106697764',
'Suzuka' : 'Q106737001',
'Suzuka 2&4' : 'Q106737001',
'Tsukuba' : 'Q106725499',
}
const locations = {
'Autopolis' : 'Q1748785',
'Motegi': 'Q1420895',
'Okayama': 'Q173185',
'Sugo' : 'Q917398',
'Suzuka' : 'Q174170',
'Tsukuba' : 'Q2097263',
}
Tried :
wd create-entity ./template.js "2018 All Japan Road Race Championship Round 1 Motegi" 2018 Motegi 2018-04-05 2018-04-08 null null 1
Got this output :
/home/arno/wikibase-cli-templates/create/template.js:9
const location = locations[location]
^
SyntaxError: Identifier 'location' has already been declared
at createScript (vm.js:80:10)
at Object.runInThisContext (vm.js:139:10)
at Module._compile (module.js:616:28)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at getDataFromJsModule (/usr/local/lib/node_modules/wikibase-cli/lib/object_arg_parser.js:57:20)
Replaced location by locatione in line 9 ; line 15 (!locatione) ; line 42 Re-run
wd create-entity ./template.js "2018 All Japan Road Race Championship Round 1 Motegi" 2018 Motegi 2018-04-05 2018-04-08 null null 1
Got :
Error: invalid follows item id: null
at module.exports (/home/arno/wikibase-cli-templates/create/template.js:23:54)
at getDataFromJsModule (/usr/local/lib/node_modules/wikibase-cli/lib/object_arg_parser.js:61:12)
at getData (/usr/local/lib/node_modules/wikibase-cli/lib/object_arg_parser.js:36:14)
at Command.module.exports.args [as customArgsParser] (/usr/local/lib/node_modules/wikibase-cli/lib/object_arg_parser.js:8:16)
at runOnce (/usr/local/lib/node_modules/wikibase-cli/lib/edit/edit_command.js:59:20)
at module.exports (/usr/local/lib/node_modules/wikibase-cli/lib/edit/edit_command.js:41:13)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
at Function.Module.runMain (module.js:695:11)
at startup (bootstrap_node.js:188:16)
Tried :
wd create-entity ./template.js "2018 All Japan Road Race Championship Round 1 Motegi" 2018 Motegi 2018-04-05 2018-04-08 Q106713038 Q106713038 1
as a dummy edit, to avoid coming up against the null error above again
Got :
{ permissiondenied: permissiondenied: You do not have the permissions needed to carry out this action.
at requestError (/usr/local/lib/node_modules/wikibase-cli/node_modules/wikibase-edit/lib/request/parse_response_body.js:18:15)
at module.exports (/usr/local/lib/node_modules/wikibase-cli/node_modules/wikibase-edit/lib/request/parse_response_body.js:11:33)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
url: https://www.wikidata.org/w/api.php?action=wbeditentity&format=json
response status: 200
response body: {"error":{"code":"permissiondenied","info":"You do not have the permissions needed to carry out this action.","messages":[{"name":"wikibase-api-permissiondenied","parameters":[],"html":{"*":"You do not have the permissions needed to carry out this action."}},{"name":"badaccess-groups","parameters":["*, [[Wikidata:Users|Users]]",2],"html":{"*":"The action you have requested is limited to users in one of the groups: *, <a href=\"/wiki/Wikidata:Users\" title=\"Wikidata:Users\">Users</a>."}}],"*":"See https://www.wikidata.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."},"servedby":"mw1378"}
At the beginning I remember getting the credential set up prompt, I followed the instructions, choose OAuth, copy/pasted the 4 "keys" from the meta.wikimedia webpage into the terminal ( but I didn't "tick" any of the boxes on the webpage, didn't change anything, just went to the bottom of webpage and clicked on some kind of validation button (from memory) ).
Now I'm wondering if I should have ticked some of those boxes to be able to get the proper permission...
I tried
wb config credentials https://www.wikidata.org
The 4 "keys" show up
In the "Manage connected applications" webpage of meta.wikimedia.org, for wikibase-cli-myusername I only have "revoke access" not "manage access" ( I was hoping to be able to activate the necessary permission which I might have missed at the "ticking boxes" step...
Let me know if you need me to try something else. Thanks again
So I did a few mistakes in the template >< which I could have easily tested by running the command with the --dry
flag, which prevents to actually run the edit. Now that it's done, I corrected the template to this:
// template.js
const datePattern = /^\d{4}-\d{2}-\d{2}$/
const itemIdPattern = /^Q\d+$/
const ordinalPattern = /^\d+$/
module.exports = (label, year, locationName, start, end, follows, isFollowedBy, ordinal) => {
console.error({ label, year, locationName, start, end, follows, isFollowedBy, ordinal })
const championship = championships[year]
const type = types[locationName]
const location = locations[locationName]
// Validating arguments as it would be easy to get messed up given the number of arguments to pass
if (label.split(' ').length < 4) throw new Error(`that label looks too short: ${label}`)
if (!championship) throw new Error(`unknown championship: ${championship}`)
if (!type) throw new Error(`unknown type: ${type}`)
if (!location) throw new Error(`unknown location: ${location}`)
if (!datePattern.test(start)) throw new Error(`invalid start date: ${start}`)
if (!datePattern.test(end)) throw new Error(`invalid end date: ${end}`)
// Make those qualifier values optional: pass null to skip
if (follows === 'null') follows = null
if (isFollowedBy === 'null') isFollowedBy = null
if (follows && !itemIdPattern.test(follows)) throw new Error(`invalid follows item id: ${follows}`)
if (isFollowedBy && !itemIdPattern.test(isFollowedBy)) throw new Error(`invalid isFollowedBy item id: ${isFollowedBy}`)
if (!ordinalPattern.test(ordinal)) throw new Error(`invalid ordinal number: ${ordinal}`)
const qualifiers = {
P156: ordinal
}
if (follows) qualifiers.P155 = follows
if (isFollowedBy) qualifiers.P156 = isFollowedBy
const newItem = {
labels: {
en: label
},
claims: {
P31: { value: type, qualifiers },
P361: { value: championship, qualifiers },
P17: 'Q17',
P276: location,
P580: start,
P582: end,
}
}
return newItem
}
const championships = {
2021: 'Q106735845',
2020: 'Q106707657',
2019: 'Q106725472',
2018: 'Q106726771',
2017: 'Q106726795',
2016: 'Q106726816',
2015: 'Q106726831',
}
const types = {
'Autopolis': 'Q106705839',
'Autopolis 2&4' : 'Q106763105',
'MFJ GP' : 'Q106712980',
'Motegi': 'Q106712966',
'Motegi 2&4' : 'Q106763118',
'Okayama': 'Q106712954',
'Sugo' : 'Q106697764',
'Suzuka' : 'Q106737001',
'Suzuka 2&4' : 'Q106737001',
'Tsukuba' : 'Q106725499',
}
const locations = {
'Autopolis' : 'Q1748785',
'Motegi': 'Q1420895',
'Okayama': 'Q173185',
'Sugo' : 'Q917398',
'Suzuka' : 'Q174170',
'Tsukuba' : 'Q2097263',
}
As for the permissions, I would recommand to delete the previous tokens and run:
wb config credentials https://www.wikidata.org reset
and select the following authorizations:
There was a tiny typo line 29 ( P156
instead of P1545
), once changed the item was created successfully !!! :congratulations:
I tried to make the ordinal
argument optional as well by mimicking line 21 and 22 but I couldn't ( among other things >< ) figure out how to modify the check in line 4 accordingly
Would it be possible :
ordinal
, one applying to P31
, another applying to P361
? as they're never the same, the same goes for follows
and isFollowedBy
, with the CLI command ordered as follow ( maybe following the pattern as they appears on WD ( although I know it's cosmetic, it doesn't matter how they appear/are ordered, just when one's does manual edit/comparison, for clarity/readability's sake ) :wd create-entity ./template.js "2019 All Japan Road Race Championship Round 6 Okayama" 2019 Okayama followsP31 ordinalP31 isFollowedP31 followsP361 ordinalP361 isFollowedP361 2019-08-30 2019-09-01
Test case with the current syntax :
wd create-entity ./template.js "2018 All Japan Road Race Championship Round 2 Suzuka 2&4" 2018 Suzuka 2018-04-20 2018-04-22 Q106766005 null 2
Allowed me to create Q106794957
, the value, Q106766005
, is correct for P361
but for P31
I haven't created the item for 2017 All Japan Road Race Championship Round x Suzuka 2&4 yet so even if I had a specific/different parameter in the syntax/command to add it to the CLI command, I'd be nice to have 'somevalue'
as allowed value ( see pt. 2 below ) so I can update ( manually ) as soon as I'll create it ( or more realistically somewhere down the road :-) ... )
followsP31
/isFollowedP31
/followsP361
/isFollowedP361
as well as ordinalP31
/ordinalP361
accepting 'null'
( not created - current behavior ) but also 'somevalue'
( created - appears as unknown value
) as value ( so it's faster to modify the qualifier value manually once all the necessary items are created.Following the proposed pattern, the ideal ( at least in my mind, at this point...) syntax would resemble something like :
wd create-entity ./template.js "2018 All Japan Road Race Championship Round 3 Autopolis 2&4" 2018 Autopolis somevalue somevalue Q106725510 Q106794957 3 somevalue 2018-05-11 2018-05-13
And the result should be as it it appears for Q106799195 ( created manually for the demo purpose )
I'm not sure my explanations are clear enough, so I could create the items manually as they are expected to appear for each parameter combination/CLI syntax if you feel it's necessary, it wouldn't a problem at all for me, feel free to ask if you think it may save you some time.
Thanks again for your help
could be something like this?
// template.js
const datePattern = /^\d{4}-\d{2}-\d{2}$/
const itemIdPattern = /^Q\d+$/
const ordinalPattern = /^\d+$/
module.exports = (label, year, locationName, followsP31, ordinalP31, isFollowedP31, followsP361, ordinalP361, isFollowedP361, start, end ) => {
console.error({ label, year, locationName, followsP31, ordinalP31, isFollowedP31, followsP361, ordinalP361, isFollowedP361, start, end })
const championship = championships[year]
const type = types[locationName]
const location = locations[locationName]
// Validating arguments as it would be easy to get messed up given the number of arguments to pass
if (label.split(' ').length < 4) throw new Error(`that label looks too short: ${label}`)
if (!championship) throw new Error(`unknown championship: ${championship}`)
if (!type) throw new Error(`unknown type: ${type}`)
if (!location) throw new Error(`unknown location: ${location}`)
if (!datePattern.test(start)) throw new Error(`invalid start date: ${start}`)
if (!datePattern.test(end)) throw new Error(`invalid end date: ${end}`)
// Make those qualifier values optional: pass null to skip
if (followsP31 === 'null') followsP31 = null
if (isFollowedByP31 === 'null') isFollowedByP31 = null
if (ordinalP31 === 'null') ordinalP31 = null
if (followsP31 === 'somevalue') followsP31 = { snaktype: 'somevalue' }
if (isFollowedByP31 === 'somevalue') isFollowedByP31 = { snaktype: 'somevalue' }
if (ordinalP31 === 'somevalue') ordinalP31 = { snaktype: 'somevalue' }
if (isNormalSnaktypeValue(followsP31) && !itemIdPattern.test(followsP31)) throw new Error(`invalid followsP31 item id: ${followsP31}`)
if (isNormalSnaktypeValue(isFollowedByP31) && !itemIdPattern.test(isFollowedByP31)) throw new Error(`invalid isFollowedByP31 item id: ${isFollowedByP31}`)
if (isNormalSnaktypeValue(ordinalP31) && !ordinalP31Pattern.test(ordinalP31)) throw new Error(`invalid ordinalP31 number: ${ordinalP31}`)
const qualifiersP31 = {}
if (followsP31) qualifiersP31.P155 = followsP31
if (isFollowedByP31) qualifiersP31.P156 = isFollowedByP31
if (ordinalP31) qualifiersP31.P1545 = ordinalP31
if (followsP361 === 'null') followsP361 = null
if (isFollowedByP361 === 'null') isFollowedByP361 = null
if (ordinalP361 === 'null') ordinalP361 = null
if (followsP361 === 'somevalue') followsP361 = { snaktype: 'somevalue' }
if (isFollowedByP361 === 'somevalue') isFollowedByP361 = { snaktype: 'somevalue' }
if (ordinalP361 === 'somevalue') ordinalP361 = { snaktype: 'somevalue' }
if (isNormalSnaktypeValue(followsP361) && !itemIdPattern.test(followsP361)) throw new Error(`invalid followsP361 item id: ${followsP361}`)
if (isNormalSnaktypeValue(isFollowedByP361) && !itemIdPattern.test(isFollowedByP361)) throw new Error(`invalid isFollowedByP361 item id: ${isFollowedByP361}`)
if (isNormalSnaktypeValue(ordinalP361) && !ordinalP361Pattern.test(ordinalP361)) throw new Error(`invalid ordinalP361 number: ${ordinalP361}`)
const qualifiersP361 = {}
if (followsP361) qualifiersP361.P155 = followsP361
if (isFollowedByP361) qualifiersP361.P156 = isFollowedByP361
if (ordinalP361) qualifiersP361.P1545 = ordinalP361
const newItem = {
labels: {
en: label
},
claims: {
P31: { value: type, qualifiers: qualifiersP31 },
P361: { value: championship, qualifiers: qualifiersP361 },
P17: 'Q17',
P276: location,
P580: start,
P582: end,
}
}
return newItem
}
const championships = {
2021: 'Q106735845',
2020: 'Q106707657',
2019: 'Q106725472',
2018: 'Q106726771',
2017: 'Q106726795',
2016: 'Q106726816',
2015: 'Q106726831',
}
const types = {
'Autopolis': 'Q106705839',
'Autopolis 2&4' : 'Q106763105',
'MFJ GP' : 'Q106712980',
'Motegi': 'Q106712966',
'Motegi 2&4' : 'Q106763118',
'Okayama': 'Q106712954',
'Sugo' : 'Q106697764',
'Suzuka' : 'Q106737001',
'Suzuka 2&4' : 'Q106737001',
'Tsukuba' : 'Q106725499',
}
const locations = {
'Autopolis' : 'Q1748785',
'Motegi': 'Q1420895',
'Okayama': 'Q173185',
'Sugo' : 'Q917398',
'Suzuka' : 'Q174170',
'Tsukuba' : 'Q2097263',
}
const isNormalSnaktypeValue = value => typeof value === 'string'
Below is the one I used, I just had to define ordinalP31Pattern
and ordinalP361Pattern
by copying what you did for ordinalPattern
and replace isFollowedBy
by isFollowed
and it worked splendidly !!!
There's just the qualifiers orders but it's cosmetic so don't bother it'll do like this.
Once again, thanks for bestowing your magic on this, for kicks I'll try to do a whole or even half a season manually and the same using your code, just to fully realize how much time you saved me, and if I do I'll report the numbers.
// template.js
const datePattern = /^\d{4}-\d{2}-\d{2}$/
const itemIdPattern = /^Q\d+$/
const ordinalPattern = /^\d+$/
const ordinalP31Pattern = /^\d+$/
const ordinalP361Pattern = /^\d+$/
module.exports = (label, year, locationName, followsP31, ordinalP31, isFollowedP31, followsP361, ordinalP361, isFollowedP361, start, end ) => {
console.error({ label, year, locationName, followsP31, ordinalP31, isFollowedP31, followsP361, ordinalP361, isFollowedP361, start, end })
const championship = championships[year]
const type = types[locationName]
const location = locations[locationName]
// Validating arguments as it would be easy to get messed up given the number of arguments to pass
if (label.split(' ').length < 4) throw new Error(`that label looks too short: ${label}`)
if (!championship) throw new Error(`unknown championship: ${championship}`)
if (!type) throw new Error(`unknown type: ${type}`)
if (!location) throw new Error(`unknown location: ${location}`)
if (!datePattern.test(start)) throw new Error(`invalid start date: ${start}`)
if (!datePattern.test(end)) throw new Error(`invalid end date: ${end}`)
// Make those qualifier values optional: pass null to skip
if (followsP31 === 'null') followsP31 = null
if (isFollowedP31 === 'null') isFollowedP31 = null
if (ordinalP31 === 'null') ordinalP31 = null
if (followsP31 === 'somevalue') followsP31 = { snaktype: 'somevalue' }
if (isFollowedP31 === 'somevalue') isFollowedP31 = { snaktype: 'somevalue' }
if (ordinalP31 === 'somevalue') ordinalP31 = { snaktype: 'somevalue' }
if (isNormalSnaktypeValue(followsP31) && !itemIdPattern.test(followsP31)) throw new Error(`invalid followsP31 item id: ${followsP31}`)
if (isNormalSnaktypeValue(isFollowedP31) && !itemIdPattern.test(isFollowedP31)) throw new Error(`invalid isFollowedP31 item id: ${isFollowedP31}`)
if (isNormalSnaktypeValue(ordinalP31) && !ordinalP31Pattern.test(ordinalP31)) throw new Error(`invalid ordinalP31 number: ${ordinalP31}`)
const qualifiersP31 = {}
if (followsP31) qualifiersP31.P155 = followsP31
if (isFollowedP31) qualifiersP31.P156 = isFollowedP31
if (ordinalP31) qualifiersP31.P1545 = ordinalP31
if (followsP361 === 'null') followsP361 = null
if (isFollowedP361 === 'null') isFollowedP361 = null
if (ordinalP361 === 'null') ordinalP361 = null
if (followsP361 === 'somevalue') followsP361 = { snaktype: 'somevalue' }
if (isFollowedP361 === 'somevalue') isFollowedP361 = { snaktype: 'somevalue' }
if (ordinalP361 === 'somevalue') ordinalP361 = { snaktype: 'somevalue' }
if (isNormalSnaktypeValue(followsP361) && !itemIdPattern.test(followsP361)) throw new Error(`invalid followsP361 item id: ${followsP361}`)
if (isNormalSnaktypeValue(isFollowedP361) && !itemIdPattern.test(isFollowedP361)) throw new Error(`invalid isFollowedP361 item id: ${isFollowedP361}`)
if (isNormalSnaktypeValue(ordinalP361) && !ordinalP361Pattern.test(ordinalP361)) throw new Error(`invalid ordinalP361 number: ${ordinalP361}`)
const qualifiersP361 = {}
if (followsP361) qualifiersP361.P155 = followsP361
if (isFollowedP361) qualifiersP361.P156 = isFollowedP361
if (ordinalP361) qualifiersP361.P1545 = ordinalP361
const newItem = {
labels: {
en: label
},
claims: {
P31: { value: type, qualifiers: qualifiersP31 },
P361: { value: championship, qualifiers: qualifiersP361 },
P17: 'Q17',
P276: location,
P580: start,
P582: end,
}
}
return newItem
}
const championships = {
2021: 'Q106735845',
2020: 'Q106707657',
2019: 'Q106725472',
2018: 'Q106726771',
2017: 'Q106726795',
2016: 'Q106726816',
2015: 'Q106726831',
}
const types = {
'Autopolis': 'Q106705839',
'Autopolis 2&4' : 'Q106763105',
'MFJ GP' : 'Q106712980',
'Motegi': 'Q106712966',
'Motegi 2&4' : 'Q106763118',
'Okayama': 'Q106712954',
'Sugo' : 'Q106697764',
'Suzuka' : 'Q106737001',
'Suzuka 2&4' : 'Q106737001',
'Tsukuba' : 'Q106725499',
}
const locations = {
'Autopolis' : 'Q1748785',
'Motegi': 'Q1420895',
'Okayama': 'Q173185',
'Sugo' : 'Q917398',
'Suzuka' : 'Q174170',
'Tsukuba' : 'Q2097263',
}
const isNormalSnaktypeValue = value => typeof value === 'string'
Hi,
I'd like to get started with wikibase-cli mainly to replace/supplement QuickStatements for my WD batch editing.
As an example I tried the following with QS ( I prepared it in a spreadsheet ) to create and populate an item in one "operation" :
unfortunately it doesn't work and as a workaround I used :
Then after manually copy/pasting the newly created item's Q number :
Woud you have some pointers on how to move in that direction, to familiarize myself so somewhere down the road I'd like to able to write/modify a little script in which I'd have the values for P31, P361, P276 as arguments ( with an/several "if function" example : with arg1 being the year and arg2 being Okayama ( with a list of abt 10 different values for arg2 )
etc....
and
so I can process several seasons and rounds faster, although I can't see a way to change the value for P580 and P582 other than manually...
Thanks in advance for any guidance you'd be willing to provide.