Skip to content

Commit

Permalink
Adding author field to fact-checks and explainers (#2217)
Browse files Browse the repository at this point in the history
Currently, our system only stores and exposes the last user who updated an article (fact-check or explainer). This PR aims to store and expose the original creator of the article in the database and make it available via the GraphQL API.

- Database: Add a new column (author_id) to store the user who originally created an article (tables fact_checks and explainers).
- Callback: Implement callbacks to set the author automatically when the article is created.
- GraphQL API: Update the schema to expose the article creator alongside the last updated user.
- Backfill Existing Data: Implement a migration rake task that determines the original creator from version history and populates author_id for existing articles.
- Tests: Add automated tests.
  • Loading branch information
caiosba authored Feb 16, 2025
1 parent ac7403d commit 7986251
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 2 deletions.
1 change: 1 addition & 0 deletions app/graph/types/explainer_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ class ExplainerType < DefaultObject
field :team, PublicTeamType, null: true
field :tags, [GraphQL::Types::String, null: true], null: true
field :trashed, GraphQL::Types::Boolean, null: true
field :author, UserType, null: true
end
1 change: 1 addition & 0 deletions app/graph/types/fact_check_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ class FactCheckType < DefaultObject
field :imported, GraphQL::Types::Boolean, null: true
field :report_status, GraphQL::Types::String, null: true
field :trashed, GraphQL::Types::Boolean, null: true
field :author, UserType, null: true
end
6 changes: 6 additions & 0 deletions app/models/concerns/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ module Article
include CheckElasticSearch

belongs_to :user
belongs_to :author, class_name: 'User', foreign_key: 'author_id', optional: true

before_validation :set_author, on: :create
before_validation :set_user
validates_presence_of :user

Expand All @@ -21,6 +23,10 @@ def text_fields
# Implement it in the child class
end

def set_author
self.author = User.current unless User.current.nil?
end

def set_user
self.user = User.current unless User.current.nil?
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/explainer.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
class Explainer < ApplicationRecord
include Article

has_paper_trail on: [:create, :update], ignore: [:updated_at, :created_at], if: proc { |_x| User.current.present? }, versions: { class_name: 'Version' }

belongs_to :team

has_annotations
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20250214142854_add_author_to_articles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddAuthorToArticles < ActiveRecord::Migration[6.1]
def change
add_reference :fact_checks, :author, index: true
add_reference :explainers, :author, index: true
add_reference :claim_descriptions, :author, index: true
end
end
10 changes: 8 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2025_01_24_155814) do
ActiveRecord::Schema.define(version: 2025_02_14_142854) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -242,6 +242,8 @@
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "team_id"
t.bigint "author_id"
t.index ["author_id"], name: "index_claim_descriptions_on_author_id"
t.index ["project_media_id"], name: "index_claim_descriptions_on_project_media_id", unique: true
t.index ["team_id"], name: "index_claim_descriptions_on_team_id"
t.index ["user_id"], name: "index_claim_descriptions_on_user_id"
Expand Down Expand Up @@ -346,7 +348,9 @@
t.datetime "updated_at", precision: 6, null: false
t.string "tags", default: [], array: true
t.boolean "trashed", default: false
t.bigint "author_id"
t.index "date_trunc('day'::text, created_at)", name: "explainer_created_at_day"
t.index ["author_id"], name: "index_explainers_on_author_id"
t.index ["created_at"], name: "index_explainers_on_created_at"
t.index ["tags"], name: "index_explainers_on_tags", using: :gin
t.index ["team_id"], name: "index_explainers_on_team_id"
Expand All @@ -369,7 +373,9 @@
t.string "rating"
t.boolean "imported", default: false
t.boolean "trashed", default: false
t.bigint "author_id"
t.index "date_trunc('day'::text, created_at)", name: "fact_check_created_at_day"
t.index ["author_id"], name: "index_fact_checks_on_author_id"
t.index ["claim_description_id"], name: "index_fact_checks_on_claim_description_id", unique: true
t.index ["created_at"], name: "index_fact_checks_on_created_at"
t.index ["imported"], name: "index_fact_checks_on_imported"
Expand Down Expand Up @@ -1008,6 +1014,6 @@
add_foreign_key "requests", "feeds"

create_trigger :enforce_relationships, sql_definition: <<-SQL
CREATE TRIGGER enforce_relationships BEFORE INSERT ON public.relationships FOR EACH ROW EXECUTE FUNCTION validate_relationships()
CREATE TRIGGER enforce_relationships BEFORE INSERT ON public.relationships FOR EACH ROW EXECUTE PROCEDURE validate_relationships()
SQL
end
2 changes: 2 additions & 0 deletions lib/relay.idl
Original file line number Diff line number Diff line change
Expand Up @@ -8261,6 +8261,7 @@ type Dynamic_annotation_verification_statusEdge {
Explainer type
"""
type Explainer implements Node {
author: User
created_at: String
dbid: Int
description: String
Expand Down Expand Up @@ -8413,6 +8414,7 @@ type ExtractTextPayload {
FactCheck type
"""
type FactCheck implements Node {
author: User
claim_description: ClaimDescription
created_at: String
dbid: Int
Expand Down
64 changes: 64 additions & 0 deletions lib/tasks/migrate/20250214142854_set_author_for_articles.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# rake check:migrate:set_author_for_articles
ActiveRecord::Base.logger = nil
namespace :check do
namespace :migrate do
task set_author_for_articles: :environment do
started = Time.now.to_i

# Initially set the author for records that were not changed since they were created
[ClaimDescription, FactCheck, Explainer].each do |model|
query = model.where('created_at = updated_at').where(author_id: nil).order('id ASC')
puts "[#{Time.now}] Total of instances of model #{model} to be updated: #{query.count}."
i = 0
query.in_batches(of: 1000) do |batch|
i += 1
batch.update_all('author_id = user_id')
puts "[#{Time.now}] Updated author for batch ##{i} of instances of model #{model}."
end
end

# For claims and fact-checks, look into the versions table to update the records that were updated since they were created
Team.all.order('id ASC').find_each do |team|
puts "[#{Time.now}] Updating article authors for workspace #{team.slug}..."
claims = ClaimDescription.where(author_id: nil, team_id: team.id).order('id ASC')
claims_count = claims.count
puts "[#{Time.now}] Total of claims to be updated: #{claims_count}."
i = 0
claims.find_each do |claim|
i += 1

# Update claim
claim_author = Version.from_partition(team.id).where(item_type: 'ClaimDescription', event: 'create', item_id: claim.id).last&.whodunnit
# Skip if no author found from the versions table
if claim_author.to_i == 0
puts "[#{Time.now}] Skipping claim #{claim.id} because no author was found from the versions table."
else
claim.update_column(:author_id, claim_author.to_i)
puts "[#{Time.now}] [#{i}/#{claims_count}] Updated author to be #{claim_author} for claim #{claim.id}."
end

# Update fact-check
fact_check = claim.fact_check
# Skip if no fact-check found for claim
if fact_check.nil?
puts "[#{Time.now}] [#{i}/#{claims_count}] Skipping, no fact-check found for claim #{claim.id}."
next
end
fact_check_author = Version.from_partition(team.id).where(item_type: 'FactCheck', event: 'create', item_id: fact_check.id).last&.whodunnit
# Skip if no author found from the versions table
if fact_check_author.to_i == 0
puts "[#{Time.now}] Skipping fact-check #{fact_check.id} because no author was found from the versions table."
else
fact_check.update_column(:author_id, fact_check_author.to_i)
puts "[#{Time.now}] [#{i}/#{claims_count}] Updated author to be #{fact_check_author} for fact-check #{fact_check.id}."
end
end

puts "[#{Time.now}] Updated article authors for workspace #{team.slug}."
end

minutes = ((Time.now.to_i - started) / 60).to_i
puts "[#{Time.now}] Done in #{minutes} minutes."
end
end
end
28 changes: 28 additions & 0 deletions public/relay.json
Original file line number Diff line number Diff line change
Expand Up @@ -44673,6 +44673,20 @@
"name": "Explainer",
"description": "Explainer type",
"fields": [
{
"name": "author",
"description": null,
"args": [

],
"type": {
"kind": "OBJECT",
"name": "User",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "created_at",
"description": null,
Expand Down Expand Up @@ -45485,6 +45499,20 @@
"name": "FactCheck",
"description": "FactCheck type",
"fields": [
{
"name": "author",
"description": null,
"args": [

],
"type": {
"kind": "OBJECT",
"name": "User",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "claim_description",
"description": null,
Expand Down
2 changes: 2 additions & 0 deletions test/lib/team_statistics_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

class TeamStatisticsTest < ActiveSupport::TestCase
def setup
Sidekiq::Testing.fake!
WebMock.stub_request(:post, /#{CheckConfig.get('alegre_host')}/).to_return(body: '{}')
Explainer.delete_all
@team = create_team
@team.set_languages = ['en', 'pt']
Expand Down
8 changes: 8 additions & 0 deletions test/models/explainer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,12 @@ def teardown
ex = create_explainer language: nil
assert_equal 'en', ex.reload.language
end

test "should set author" do
u = create_user is_admin: true
User.current = u
ex = create_explainer
User.current = nil
assert_equal u, ex.author
end
end
8 changes: 8 additions & 0 deletions test/models/fact_check_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -733,4 +733,12 @@ def setup
fc = create_fact_check
assert_kind_of TiplineSearchResult, fc.as_tipline_search_result
end

test "should set author" do
u = create_user is_admin: true
User.current = u
fc = create_fact_check
User.current = nil
assert_equal u, fc.author
end
end

0 comments on commit 7986251

Please sign in to comment.