Class: Wikidatum::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/wikidatum/client.rb

Constant Summary collapse

ITEM_REGEX =
/^Q?\d+$/.freeze
PROPERTY_REGEX =
/^P?\d+$/.freeze
STATEMENT_REGEX =
/^Q?\d+\$[\w-]+$/.freeze
VALID_RANKS =
['preferred', 'normal', 'deprecated'].freeze
VALID_DATA_TYPES =
[
  'Wikidatum::DataType::CommonsMedia',
  'Wikidatum::DataType::ExternalId',
  'Wikidatum::DataType::GlobeCoordinate',
  'Wikidatum::DataType::MonolingualText',
  'Wikidatum::DataType::NoValue',
  'Wikidatum::DataType::Quantity',
  'Wikidatum::DataType::SomeValue',
  'Wikidatum::DataType::Time',
  'Wikidatum::DataType::WikibaseItem',
  'Wikidatum::DataType::WikibaseString',
  'Wikidatum::DataType::WikibaseUrl'
].freeze
CONTENT_DATA_TYPES =
[
  'Wikidatum::DataType::CommonsMedia',
  'Wikidatum::DataType::ExternalId',
  'Wikidatum::DataType::GlobeCoordinate',
  'Wikidatum::DataType::MonolingualText',
  'Wikidatum::DataType::Quantity',
  'Wikidatum::DataType::Time',
  'Wikidatum::DataType::WikibaseItem',
  'Wikidatum::DataType::WikibaseString',
  'Wikidatum::DataType::WikibaseUrl'
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user_agent:, wikibase_url: 'https://www.wikidata.org', bot: true, allow_ip_edits: false) ⇒ Wikidatum::Client

Create a new Wikidatum::Client to interact with the Wikibase REST API.

Examples:

wikidatum_client = Wikidatum::Client.new(
  user_agent: 'Bot Name',
  wikibase_url: 'https://www.wikidata.org',
  bot: true
)

Parameters:

  • user_agent (String)

    The UserAgent header to send with all requests to the Wikibase API. This will be prepended with the string “Wikidatum Ruby gem vX.X.X:”.

  • wikibase_url (String) (defaults to: 'https://www.wikidata.org')

    The root URL of the Wikibase instance we want to interact with. If not provided, will default to ‘www.wikidata.org`. Do not include a `/` at the end of the URL.

  • bot (Boolean) (defaults to: true)

    Whether requests sent by this client instance should be registered as bot requests. Defaults to ‘true`. If the user is not authenticated, or if the user is not marked with the Bot flag in the Wikibase instance, you’ll be unable to make edits with this set to ‘true`.

  • allow_ip_edits (Boolean) (defaults to: false)

    whether this client should allow non-GET requests if authentication hasn’t been provided. Defaults to false. If this is set to true, the IP address of the device from which the request was sent will be credited for the edit. Make sure not to allow these edits if you don’t want your IP address (and in many cases, a very close approximation of your physical location) exposed publicly.

Raises:

  • (ArgumentError)


82
83
84
85
86
87
88
89
90
91
# File 'lib/wikidatum/client.rb', line 82

def initialize(user_agent:, wikibase_url: 'https://www.wikidata.org', bot: true, allow_ip_edits: false)
  raise ArgumentError, "Wikibase URL must not end with a `/`, got #{wikibase_url.inspect}." if wikibase_url.end_with?('/')

  @user_agent = "Wikidatum Ruby gem v#{Wikidatum::VERSION}: #{user_agent}"
  @wikibase_url = wikibase_url
  @bot = bot
  @allow_ip_edits = allow_ip_edits

  Faraday.default_adapter = :net_http
end

Instance Attribute Details

#allow_ip_editsBoolean (readonly)

Returns whether this client should allow non-GET requests if authentication hasn’t been provided. Defaults to false.

Returns:

  • (Boolean)

    whether this client should allow non-GET requests if authentication hasn’t been provided. Defaults to false.



53
54
55
# File 'lib/wikidatum/client.rb', line 53

def allow_ip_edits
  @allow_ip_edits
end

#botBoolean (readonly)

Returns whether this client instance should identify itself as a bot when making requests.

Returns:

  • (Boolean)

    whether this client instance should identify itself as a bot when making requests.



45
46
47
# File 'lib/wikidatum/client.rb', line 45

def bot
  @bot
end

#user_agentString (readonly)

Returns the UserAgent header to send with all requests to the Wikibase API.

Returns:

  • (String)

    the UserAgent header to send with all requests to the Wikibase API.



49
50
51
# File 'lib/wikidatum/client.rb', line 49

def user_agent
  @user_agent
end

#wikibase_urlString (readonly)

Returns the root URL of the Wikibase instance we want to interact with. If not provided, will default to Wikidata.

Returns:

  • (String)

    the root URL of the Wikibase instance we want to interact with. If not provided, will default to Wikidata.



41
42
43
# File 'lib/wikidatum/client.rb', line 41

def wikibase_url
  @wikibase_url
end

Instance Method Details

#add_statement(id:, property:, value:, qualifiers: [], references: [], rank: 'normal', tags: [], comment: nil) ⇒ Boolean

Add a statement to an item.

NOTE: Adding references/qualifiers with ‘add_statement` is untested and effectively unsupported for now.

Examples:

Add a string statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P23',
  value: Wikidatum::DataType::WikibaseString.new(string: 'Foo'),
  comment: 'Adding something or another.'
)

Add a ‘no value’ statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::NoValue.new(
    type: :no_value,
    value: nil
  )
)

Add an ‘unknown value’ statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::SomeValue.new(
    type: :some_value,
    value: nil
  )
)

Add a globe coordinate statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::GlobeCoordinate.new(
    latitude: 52.51666,
    longitude: 13.3833,
    precision: 0.01666,
    globe: 'https://wikidata.org/entity/Q2'
  )
)

Add a monolingual text statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::MonolingualText.new(
    language: 'en',
    text: 'Foobar'
  )
)

Add a quantity statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::Quantity.new(
    amount: '+12',
    unit: 'https://wikidata.org/entity/Q1234'
  )
)

Add a time statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::Time.new(
    time: '+2022-08-12T00:00:00Z',
    precision: 11,
    calendar_model: 'https://wikidata.org/entity/Q1234'
  )
)

Add a Wikibase item statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::WikibaseItem.new(
    id: 'Q1234'
  )
)

Add a URL statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::WikibaseUrl.new(
    string: 'https://example.com'
  )
)

Add an External ID statement.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::ExternalId.new(
    string: '123'
  )
)

Add a statement for an image, video, or audio file from Wikimedia Commons.

wikidatum_client.add_statement(
  id: 'Q123',
  property: 'P124',
  value: Wikidatum::DataType::CommonsMedia.new(
    string: 'FooBar.jpg'
  )
)

Parameters:

Returns:

  • (Boolean)

    True if the request succeeded.

Raises:

  • (ArgumentError)


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/wikidatum/client.rb', line 276

def add_statement(id:, property:, value:, qualifiers: [], references: [], rank: 'normal', tags: [], comment: nil)
  raise ArgumentError, "#{id.inspect} is an invalid Wikibase QID. Must be an integer, a string representation of an integer, or in the format 'Q123'." unless id.is_a?(Integer) || id.match?(ITEM_REGEX)
  raise ArgumentError, "#{property.inspect} is an invalid Wikibase PID. Must be an integer, a string representation of an integer, or in the format 'P123'." unless property.is_a?(Integer) || property.match?(PROPERTY_REGEX)
  raise ArgumentError, "#{rank.inspect} is an invalid rank. Must be normal, preferred, or deprecated." unless VALID_RANKS.include?(rank.to_s)
  raise ArgumentError, "Expected an instance of one of Wikidatum::DataType's subclasses for value, but got #{value.inspect}." unless VALID_DATA_TYPES.include?(value.class.to_s)

  id = coerce_item_id(id)
  property = coerce_property_id(property)

  case value.class.to_s
  when 'Wikidatum::DataType::NoValue'
    statement_hash = {
      property: {
        id: property
      },
      value: {
        type: 'novalue'
      }
    }
  when 'Wikidatum::DataType::SomeValue'
    statement_hash = {
      property: {
        id: property
      },
      value: {
        type: 'somevalue'
      }
    }
  when *CONTENT_DATA_TYPES
    statement_hash = {
      property: {
        id: property
      },
      value: {
        type: 'value',
        content: value.marshal_dump
      }
    }
  end

  body = {
    statement: statement_hash.merge(
      {
        qualifiers: qualifiers,
        references: references,
        rank: rank.to_s
      }
    )
  }

  response = post_request("/entities/items/#{id}/statements", body, tags: tags, comment: comment)

  puts JSON.pretty_generate(response) if ENV['DEBUG']

  response.success?
end

#allow_ip_edits?Boolean

Does the current instance of Client allow anonymous IP-based edits?

Returns:

  • (Boolean)


367
368
369
# File 'lib/wikidatum/client.rb', line 367

def allow_ip_edits?
  @allow_ip_edits
end

#authenticated?Boolean

Is the current instance of Client authenticated as a Wikibase user?

Returns:

  • (Boolean)


358
359
360
361
362
# File 'lib/wikidatum/client.rb', line 358

def authenticated?
  # TODO: Make it possible for this to be true once authentication
  #   is implemented.
  false
end

#bot?Boolean

Is the current instance of Client editing as a bot?

Returns:

  • (Boolean)


374
375
376
# File 'lib/wikidatum/client.rb', line 374

def bot?
  @bot
end

#delete_statement(id:, tags: [], comment: nil) ⇒ Boolean

Delete a statement from an item.

Examples:

wikidatum_client.delete_statement(
  id: 'Q123$4543523c-1d1d-1111-1e1e-11b11111b1f1',
  comment: "Deleting this statement because it's bad."
)

Parameters:

  • id (String)

    the ID of the statemnt being deleted.

  • tags (Array<String>) (defaults to: [])
  • comment (String, nil) (defaults to: nil)

Returns:

  • (Boolean)

    True if the request succeeded.

Raises:

  • (ArgumentError)


345
346
347
348
349
350
351
352
353
# File 'lib/wikidatum/client.rb', line 345

def delete_statement(id:, tags: [], comment: nil)
  raise ArgumentError, "#{id.inspect} is an invalid Wikibase Statement ID. Must be a string in the format 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac'." unless id.match?(STATEMENT_REGEX)

  response = delete_request("/statements/#{id}", tags: tags, comment: comment)

  puts JSON.pretty_generate(response) if ENV['DEBUG']

  response.success?
end

#item(id:) ⇒ Wikidatum::Item

Get an item from the Wikibase API based on its QID.

Examples:

wikidatum_client.item(id: 'Q123')
wikidatum_client.item(id: 123)
wikidatum_client.item(id: '123')

Parameters:

  • id (String, Integer)

    Either a string or integer representation of the item’s QID, e.g. ‘“Q123”`, `“123”`, or `123`.

Returns:

Raises:

  • (ArgumentError)


103
104
105
106
107
108
109
110
111
112
113
# File 'lib/wikidatum/client.rb', line 103

def item(id:)
  raise ArgumentError, "#{id.inspect} is an invalid Wikibase QID. Must be an integer, a string representation of an integer, or in the format 'Q123'." unless id.is_a?(Integer) || id.match?(ITEM_REGEX)

  id = coerce_item_id(id)

  response = get_request("/entities/items/#{id}")

  puts JSON.pretty_generate(response) if ENV['DEBUG']

  Wikidatum::Item.marshal_load(response)
end

#labels(id:) ⇒ Array<Wikidatum::Term>

Get labels for an item from the Wikibase API based on the item’s QID.

Examples:

wikidatum_client.labels(id: 'Q123') #=> [<Wikidatum::Term lang="en" value="Foo">, <Wikidatum::Term lang="es" value="Bar">]
wikidatum_client.labels(id: 123)
wikidatum_client.labels(id: '123')

Parameters:

  • id (String, Integer)

    Either a string or integer representation of the relevant item’s QID, e.g. ‘“Q123”`, `“123”`, or `123`.

Returns:

  • (Array<Wikidatum::Term>)

    This can, theoretically, be empty if the item has no labels.

Raises:

  • (ArgumentError)


125
126
127
128
129
130
131
132
133
134
135
# File 'lib/wikidatum/client.rb', line 125

def labels(id:)
  raise ArgumentError, "#{id.inspect} is an invalid Wikibase QID. Must be an integer, a string representation of an integer, or in the format 'Q123'." unless id.is_a?(Integer) || id.match?(ITEM_REGEX)

  id = coerce_item_id(id)

  response = get_request("/entities/items/#{id}/labels")

  puts JSON.pretty_generate(response) if ENV['DEBUG']

  response.to_a.map { |lang, val| Wikidatum::Term.new(lang: lang, value: val) }
end

#statement(id:) ⇒ Wikidatum::Statement

Get a statement from the Wikibase API based on its ID.

Examples:

wikidatum_client.statement(id: 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac')

Parameters:

  • id (String)

    A string representation of the statement’s ID.

Returns:

Raises:

  • (ArgumentError)


144
145
146
147
148
149
150
151
152
# File 'lib/wikidatum/client.rb', line 144

def statement(id:)
  raise ArgumentError, "#{id.inspect} is an invalid Wikibase Statement ID. Must be a string in the format 'Q123$f004ec2b-4857-3b69-b370-e8124f5bd3ac'." unless id.match?(STATEMENT_REGEX)

  response = get_request("/statements/#{id}")

  puts JSON.pretty_generate(response) if ENV['DEBUG']

  Wikidatum::Statement.marshal_load(response)
end