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 https://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)


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
332
333
# File 'lib/wikidatum/client.rb', line 278

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)


369
370
371
# File 'lib/wikidatum/client.rb', line 369

def allow_ip_edits?
  @allow_ip_edits
end

#authenticated?Boolean

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

Returns:

  • (Boolean)


360
361
362
363
364
# File 'lib/wikidatum/client.rb', line 360

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)


376
377
378
# File 'lib/wikidatum/client.rb', line 376

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)


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

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:, follow_redirects: false) ⇒ 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.

  • follow_redirects (Boolean) (defaults to: false)

    Whether to follow a redirect if it occurs. Will only follow the first redirect no matter what.

Returns:

Raises:

  • (ArgumentError)


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

def item(id:, follow_redirects: false)
  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}", follow_redirects: follow_redirects)

  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)


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

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)


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

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