Skip to content

Commit

Permalink
Support importing original claim with fact check (#1928)
Browse files Browse the repository at this point in the history
* Support importing original claim along with fact-check

This update enhances the Check API by allowing the
`createProjectMedia` GraphQL mutation to import original claims
alongside fact-checks. The new functionality supports creating
media from file URLs, regular URLs, and text. A new argument,
`set_original_claim`, is introduced to handle the original claim data.

- Added `set_original_claim` argument to the `Create` mutation in `project_media_mutations.rb`.
- Declared `set_original_claim` as an accessor in `project_media.rb`.
- Added `create_original_claim` callback before validation on create in `project_media.rb`.
- Implemented `create_original_claim` method in `project_media_creators.rb`
to handle URLs (creating `UploadedImage`, `UploadedVideo`, `UploadedAudio`,
or `Link` media) and text (creating `Claim` media).
- Added unit tests in `project_media_test.rb` to cover all media types: link,
image, video, audio, and text.

* Increase code coverage and fix whitespace issues

Increase code coverage and fix whitespace issues flagged by CodeQL

* Update GraphQL relay schema

Update GraphQL relay schema

* Add reviewer feedback

* Split original claim tests into multiple tests

* Print test name during setup and teardown

* Move original claim tests to new files

Move original claim tests to new files. Created graphql_controller_11_test.rb and project_media_7_test.rb
  • Loading branch information
jayjay-w authored Jul 3, 2024
1 parent 941da62 commit 278e15d
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 1 deletion.
1 change: 1 addition & 0 deletions app/graph/mutations/project_media_mutations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class Create < Mutations::CreateMutation
argument :set_tags, JsonStringType, required: false, camelize: false
argument :set_title, GraphQL::Types::String, required: false, camelize: false
argument :set_status, GraphQL::Types::String, required: false, camelize: false # Status identifier (for example, "in_progress")
argument :set_original_claim, GraphQL::Types::String, required: false, camelize: false
end

class Update < Mutations::UpdateMutation
Expand Down
58 changes: 58 additions & 0 deletions app/models/concerns/project_media_creators.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
require 'active_support/concern'
require 'open-uri'
require 'uri'

module ProjectMediaCreators
extend ActiveSupport::Concern
Expand Down Expand Up @@ -28,6 +30,62 @@ def create_annotation
end
end

def create_original_claim
claim = self.set_original_claim.strip
if claim.match?(/\A#{URI::DEFAULT_PARSER.make_regexp(['http', 'https'])}\z/)
uri = URI.parse(claim)
content_type = fetch_content_type(uri)

case content_type
when /^image\//
self.media = create_media_from_url('UploadedImage', claim)
when /^video\//
self.media = create_media_from_url('UploadedVideo', claim)
when /^audio\//
self.media = create_media_from_url('UploadedAudio', claim)
else
self.media = create_link_media(claim)
end
else
self.media = create_claim_media(claim)
end
end

def fetch_content_type(uri)
response = Net::HTTP.get_response(uri)
response['content-type']
end

def create_media_from_url(type, url)
klass = type.constantize
file = download_file(url)
m = klass.new
m.file = file
m.save!
m
end

def download_file(url)
raise "Invalid URL when creating media from original claim attribute" unless url =~ /\A#{URI::DEFAULT_PARSER.make_regexp(['http', 'https'])}\z/

file = Tempfile.new(['download', File.extname(url)])
file.binmode
file.write(URI(url).open.read)
file.rewind
file
end

def create_claim_media(text)
Claim.create!(quote: text)
end

def create_link_media(url)
team = self.team || Team.current
pender_key = team.get_pender_key if team
url_from_pender = Link.normalized(url, pender_key)
Link.find_by(url: url_from_pender) || Link.create!(url: url, pender_key: pender_key)
end

def set_quote_metadata
media = self.media
case media.type
Expand Down
3 changes: 2 additions & 1 deletion app/models/project_media.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class ProjectMedia < ApplicationRecord
attr_accessor :quote, :quote_attributions, :file, :media_type, :set_annotation, :set_tasks_responses, :previous_project_id, :cached_permissions, :is_being_created, :related_to_id, :skip_rules, :set_claim_description, :set_claim_context, :set_fact_check, :set_tags, :set_title, :set_status
attr_accessor :quote, :quote_attributions, :file, :media_type, :set_annotation, :set_tasks_responses, :previous_project_id, :cached_permissions, :is_being_created, :related_to_id, :skip_rules, :set_claim_description, :set_claim_context, :set_fact_check, :set_tags, :set_title, :set_status, :set_original_claim

belongs_to :media
has_one :claim_description
Expand Down Expand Up @@ -32,6 +32,7 @@ class ProjectMedia < ApplicationRecord
validates_presence_of :custom_title, if: proc { |pm| pm.title_field == 'custom_title' }

before_validation :set_team_id, :set_channel, :set_project_id, on: :create
before_validation :create_original_claim, if: proc { |pm| pm.set_original_claim.present? }, on: :create
after_create :create_annotation, :create_metrics_annotation, :send_slack_notification, :create_relationship, :create_team_tasks, :create_claim_description_and_fact_check, :create_tags
after_create :add_source_creation_log, unless: proc { |pm| pm.source_id.blank? }
after_commit :apply_rules_and_actions_on_create, :set_quote_metadata, :notify_team_bots_create, on: [:create]
Expand Down
1 change: 1 addition & 0 deletions lib/relay.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2671,6 +2671,7 @@ input CreateProjectMediaInput {
set_annotation: String
set_claim_description: String
set_fact_check: JsonStringType
set_original_claim: String
set_status: String
set_tags: JsonStringType
set_tasks_responses: JsonStringType
Expand Down
12 changes: 12 additions & 0 deletions public/relay.json
Original file line number Diff line number Diff line change
Expand Up @@ -16109,6 +16109,18 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "set_original_claim",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
Expand Down
118 changes: 118 additions & 0 deletions test/controllers/graphql_controller_11_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
require_relative '../test_helper'

class GraphqlController11Test < ActionController::TestCase
def setup
@controller = Api::V1::GraphqlController.new
TestDynamicAnnotationTables.load!

@u = create_user
@t = create_team
create_team_user team: @t, user: @u, role: 'admin'
end

def teardown
User.unstub(:current)
Team.unstub(:current)
User.current = nil
Team.current = nil
end

test "should create media with various types using set_original_claim" do
p = create_project team: @t
authenticate_with_user(@u)

# Test for creating media with plain text original claim
query_plain_text = <<~GRAPHQL
mutation {
createProjectMedia(input: { project_id: #{p.id}, set_original_claim: "This is an original claim" }) {
project_media {
id
}
}
}
GRAPHQL

post :create, params: { query: query_plain_text, team: @t.slug }
assert_response :success
data = JSON.parse(response.body)['data']['createProjectMedia']
assert_not_nil data['project_media']['id']

# Prepare mock responses for URLs
pender_url = CheckConfig.get('pender_url_private') + '/api/medias'
url_types = {
audio: 'http://example.com/audio.mp3',
image: 'http://example.com/image.png',
video: 'http://example.com/video.mp4',
generic: 'http://example.com'
}

url_types.each do |type, url|
response_body = '{"type":"media","data":{"url":"' + url + '","type":"item"}}'
WebMock.stub_request(:get, pender_url).with({ query: { url: url } }).to_return(body: response_body)
end

# Test for creating media with audio URL original claim
query_audio = <<~GRAPHQL
mutation {
createProjectMedia(input: { project_id: #{p.id}, set_original_claim: "#{url_types[:audio]}" }) {
project_media {
id
}
}
}
GRAPHQL

post :create, params: { query: query_audio, team: @t.slug }
assert_response :success
data = JSON.parse(response.body)['data']['createProjectMedia']
assert_not_nil data['project_media']['id']

# Test for creating media with image URL original claim
query_image = <<~GRAPHQL
mutation {
createProjectMedia(input: { project_id: #{p.id}, set_original_claim: "#{url_types[:image]}" }) {
project_media {
id
}
}
}
GRAPHQL

post :create, params: { query: query_image, team: @t.slug }
assert_response :success
data = JSON.parse(response.body)['data']['createProjectMedia']
assert_not_nil data['project_media']['id']

# Test for creating media with video URL original claim
query_video = <<~GRAPHQL
mutation {
createProjectMedia(input: { project_id: #{p.id}, set_original_claim: "#{url_types[:video]}" }) {
project_media {
id
}
}
}
GRAPHQL

post :create, params: { query: query_video, team: @t.slug }
assert_response :success
data = JSON.parse(response.body)['data']['createProjectMedia']
assert_not_nil data['project_media']['id']

# Test for creating media with generic URL original claim
query_generic = <<~GRAPHQL
mutation {
createProjectMedia(input: { project_id: #{p.id}, set_original_claim: "#{url_types[:generic]}" }) {
project_media {
id
}
}
}
GRAPHQL

post :create, params: { query: query_generic, team: @t.slug }
assert_response :success
data = JSON.parse(response.body)['data']['createProjectMedia']
assert_not_nil data['project_media']['id']
end
end
70 changes: 70 additions & 0 deletions test/models/project_media_7_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
require_relative '../test_helper'
require 'tempfile'

class ProjectMedia7Test < ActiveSupport::TestCase
def setup
require 'sidekiq/testing'
Sidekiq::Testing.fake!
super
create_team_bot login: 'keep', name: 'Keep'
create_verification_status_stuff
end

test "should create media from original claim URL as Link" do
setup_elasticsearch

# Mock Pender response for Link
link_url = 'https://example.com'
pender_url = CheckConfig.get('pender_url_private') + '/api/medias'
link_response = {
type: 'media',
data: {
url: link_url,
type: 'item'
}
}.to_json
WebMock.stub_request(:get, pender_url).with(query: { url: link_url }).to_return(body: link_response)
pm_link = create_project_media(set_original_claim: link_url)
assert_equal 'Link', pm_link.media.type
assert_equal link_url, pm_link.media.url
end

test "should create media from original claim URL as UploadedImage" do
Tempfile.create(['test_image', '.jpg']) do |file|
file.write(File.read(File.join(Rails.root, 'test', 'data', 'rails.png')))
file.rewind
image_url = "http://example.com/#{file.path.split('/').last}"
WebMock.stub_request(:get, image_url).to_return(body: file.read, headers: { 'Content-Type' => 'image/jpeg' })
pm_image = create_project_media(set_original_claim: image_url)
assert_equal 'UploadedImage', pm_image.media.type
end
end

test "should create media from original claim URL as UploadedVideo" do
Tempfile.create(['test_video', '.mp4']) do |file|
file.write(File.read(File.join(Rails.root, 'test', 'data', 'rails.mp4')))
file.rewind
video_url = "http://example.com/#{file.path.split('/').last}"
WebMock.stub_request(:get, video_url).to_return(body: file.read, headers: { 'Content-Type' => 'video/mp4' })
pm_video = create_project_media(set_original_claim: video_url)
assert_equal 'UploadedVideo', pm_video.media.type
end
end

test "should create media from original claim URL as UploadedAudio" do
Tempfile.create(['test_audio', '.mp3']) do |file|
file.write(File.read(File.join(Rails.root, 'test', 'data', 'rails.mp3')))
file.rewind
audio_url = "http://example.com/#{file.path.split('/').last}"
WebMock.stub_request(:get, audio_url).to_return(body: file.read, headers: { 'Content-Type' => 'audio/mp3' })
pm_audio = create_project_media(set_original_claim: audio_url)
assert_equal 'UploadedAudio', pm_audio.media.type
end
end

test "should create media from original claim text as Claim" do
pm_claim = create_project_media(set_original_claim: 'This is a claim.')
assert_equal 'Claim', pm_claim.media.type
assert_equal 'This is a claim.', pm_claim.media.quote
end
end

0 comments on commit 278e15d

Please sign in to comment.