diff --git a/app/assets/javascripts/components/campaign/campaign_stats_download_modal.jsx b/app/assets/javascripts/components/campaign/campaign_stats_download_modal.jsx
index 9006169d89..a66049b79f 100644
--- a/app/assets/javascripts/components/campaign/campaign_stats_download_modal.jsx
+++ b/app/assets/javascripts/components/campaign/campaign_stats_download_modal.jsx
@@ -11,6 +11,8 @@ const CampaignStatsDownloadModal = ({ match }) => {
const editorsLink = `/campaigns/${campaignSlug}/students.csv`;
const editorsByCourseLink = `/campaigns/${campaignSlug}/students.csv?course=true`;
const instructorsLink = `/campaigns/${campaignSlug}/instructors.csv?course=true`;
+ const wikidataLink = `/campaigns/${campaignSlug}/wikidata.csv`;
+
if (!show) {
return (
@@ -52,6 +54,11 @@ const CampaignStatsDownloadModal = ({ match }) => {
{I18n.t('campaign.data_instructors')}
{I18n.t('campaign.data_instructors_info')}
+
+
+ {I18n.t('campaign.data_wikidata')}
+ {I18n.t('campaign.data_wikidata_info')}
+
);
};
diff --git a/app/assets/javascripts/components/overview/course_stats_download_modal.jsx b/app/assets/javascripts/components/overview/course_stats_download_modal.jsx
index 74792a6b5f..adce6661de 100644
--- a/app/assets/javascripts/components/overview/course_stats_download_modal.jsx
+++ b/app/assets/javascripts/components/overview/course_stats_download_modal.jsx
@@ -35,7 +35,7 @@ const CourseStatsDownloadModal = createReactClass({
const wikidataCsvLink = `/course_wikidata_csv?course=${this.props.course.slug}`;
let wikidataLink;
- if (Features.wikiEd && this.props.course.home_wiki.project === 'wikidata') {
+ if (this.props.course.course_stats && this.props.course.home_wiki.project === 'wikidata') {
wikidataLink = (
<>
diff --git a/app/controllers/campaigns_controller.rb b/app/controllers/campaigns_controller.rb
index 0b73a6abf2..2881d523f2 100644
--- a/app/controllers/campaigns_controller.rb
+++ b/app/controllers/campaigns_controller.rb
@@ -11,7 +11,8 @@ class CampaignsController < ApplicationController
before_action :set_campaign, only: %i[overview programs articles users edit
update destroy add_organizer remove_organizer
remove_course courses ores_plot articles_csv
- revisions_csv alerts students instructors]
+ revisions_csv alerts students instructors
+ wikidata]
before_action :require_create_permissions, only: [:create]
before_action :require_write_permissions, only: %i[update destroy add_organizer
remove_organizer remove_course edit]
@@ -196,6 +197,10 @@ def revisions_csv
csv_of('revisions')
end
+ def wikidata
+ csv_of('wikidata')
+ end
+
private
def csv_of(type)
diff --git a/app/workers/campaign_csv_worker.rb b/app/workers/campaign_csv_worker.rb
index fab4b5c212..0634045dda 100644
--- a/app/workers/campaign_csv_worker.rb
+++ b/app/workers/campaign_csv_worker.rb
@@ -13,23 +13,29 @@ def self.generate_csv(campaign:, filename:, type:, include_course:)
def perform(campaign_id, filename, type, include_course)
campaign = Campaign.find(campaign_id)
builder = CampaignCsvBuilder.new(campaign)
- data = case type
- when 'instructors'
- campaign.users_to_csv(:instructors, course: include_course)
- when 'students'
- campaign.users_to_csv(:students, course: include_course)
- when 'courses'
- builder.courses_to_csv
- when 'articles'
- builder.articles_to_csv
- when 'revisions'
- builder.revisions_to_csv
- end
+ data = to_csv(type, campaign, builder, include_course)
write_csv(filename, data)
CsvCleanupWorker.perform_at(1.week.from_now, filename)
end
+ def to_csv(type, campaign, builder, include_course)
+ case type
+ when 'instructors'
+ campaign.users_to_csv(:instructors, course: include_course)
+ when 'students'
+ campaign.users_to_csv(:students, course: include_course)
+ when 'courses'
+ builder.courses_to_csv
+ when 'articles'
+ builder.articles_to_csv
+ when 'revisions'
+ builder.revisions_to_csv
+ when 'wikidata'
+ builder.wikidata_to_csv
+ end
+ end
+
private
def write_csv(filename, data)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 95551b3f90..55aa1aec06 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -361,6 +361,8 @@ en:
data_editors_by_course_info: List of enrolled editors and their courses/programs
data_editor_usernames: Editor usernames
data_editor_usernames_info: List of editor usernames, one per line
+ data_wikidata: Wikidata stats
+ data_wikidata_info: Detailed breakdown of Wikidata activity, based on edit summaries
default_course_type: Default Course Type
default_passcode: Default Passcode
default_passcode_explanation: "By default, new programs in this campaign should have:"
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 73731ad2aa..6a68b2c230 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -394,6 +394,8 @@ fr:
data_editors_by_course_info: Liste des rédacteurs inscrits avec leurs cours/programmes
data_editor_usernames: Noms d’utilisateur des rédacteurs
data_editor_usernames_info: Liste des noms d'utilisateur des rédacteurs, un par
+ data_wikidata: Statistiques de Wikidata
+ data_wikidata_info: Répartition détaillée de l’activité de Wikidata, d’après les résumés de modification
ligne
default_course_type: Type de cours par défaut
default_passcode: Mot de passe par défaut
diff --git a/config/routes.rb b/config/routes.rb
index 0acd185219..1384f1f816 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -244,6 +244,7 @@
get 'articles_csv'
get 'revisions_csv'
get 'alerts'
+ get 'wikidata'
put 'add_organizer'
put 'remove_organizer'
put 'remove_course'
diff --git a/lib/analytics/campaign_csv_builder.rb b/lib/analytics/campaign_csv_builder.rb
index 92090c2004..b2f5f5d8ec 100644
--- a/lib/analytics/campaign_csv_builder.rb
+++ b/lib/analytics/campaign_csv_builder.rb
@@ -5,6 +5,7 @@
require_dependency "#{Rails.root}/lib/analytics/course_articles_csv_builder"
require_dependency "#{Rails.root}/lib/analytics/course_revisions_csv_builder"
require_dependency "#{Rails.root}/app/workers/campaign_csv_worker"
+require "#{Rails.root}/lib/analytics/course_wikidata_csv_builder"
class CampaignCsvBuilder
def initialize(campaign)
@@ -44,6 +45,25 @@ def revisions_to_csv
CSV.generate { |csv| csv_data.each { |line| csv << line } }
end
+ def wikidata_to_csv
+ csv_data = [CourseWikidataCsvBuilder::CSV_HEADERS]
+ courses = @campaign.courses
+ .joins(:course_stat)
+ .where(home_wiki_id: Wiki.find_by(project: 'wikidata').id)
+ courses.find_each do |course|
+ csv_data << CourseWikidataCsvBuilder.new(course).stat_row
+ end
+
+ csv_data << sum_wiki_columns(csv_data) if courses.any?
+
+ CSV.generate { |csv| csv_data.each { |line| csv << line } }
+ end
+
+ def sum_wiki_columns(csv_data)
+ # Skip 1st header row + 1st column course name
+ csv_data[1..].transpose[1..].map(&:sum).unshift('Total')
+ end
+
class AllCourses
def self.courses
Course.all
diff --git a/lib/analytics/course_wikidata_csv_builder.rb b/lib/analytics/course_wikidata_csv_builder.rb
index 115458088a..c4547539a5 100644
--- a/lib/analytics/course_wikidata_csv_builder.rb
+++ b/lib/analytics/course_wikidata_csv_builder.rb
@@ -9,19 +9,45 @@ def initialize(course)
def generate_csv
csv_data = [CSV_HEADERS]
- stats = if @course.course_stat
- @course.course_stat.stats_hash['www.wikidata.org']
- else
- { 'total revisions' => 0 }
- end
- stats.each do |revision_type, count|
- csv_data << [revision_type, count]
- end
+ csv_data << stat_row if @course.course_stat && @course.home_wiki.project == 'wikidata'
+
CSV.generate { |csv| csv_data.each { |line| csv << line } }
end
- CSV_HEADERS = %w[
- revision_type
- count
+ def stat_row
+ hash_stats = @course
+ .course_stat.stats_hash['www.wikidata.org']
+ .merge({ 'course name' => @course.title })
+ CSV_HEADERS.map { |elmnt| hash_stats.fetch elmnt, '' }
+ end
+
+ CSV_HEADERS = [
+ 'course name',
+ 'claims created',
+ 'claims changed',
+ 'claims removed',
+ 'items created',
+ 'labels added',
+ 'labels changed',
+ 'labels removed',
+ 'descriptions added',
+ 'descriptions changed',
+ 'descriptions removed',
+ 'aliases added',
+ 'aliases changed',
+ 'aliases removed',
+ 'merged from',
+ 'merged to',
+ 'interwiki links added',
+ 'interwiki links removed',
+ 'redirects created',
+ 'reverts performed',
+ 'restorations performed',
+ 'items cleared',
+ 'qualifiers added',
+ 'other updates',
+ 'unknown',
+ 'no data',
+ 'total revisions'
].freeze
end
diff --git a/spec/controllers/campaigns_controller_spec.rb b/spec/controllers/campaigns_controller_spec.rb
index c1f18e8a19..557fb7889a 100644
--- a/spec/controllers/campaigns_controller_spec.rb
+++ b/spec/controllers/campaigns_controller_spec.rb
@@ -400,7 +400,9 @@
end
describe 'CSV actions' do
+ let(:wikidata) { Wiki.get_or_create(language: nil, project: 'wikidata') }
let(:course) { create(:course) }
+ let(:another_course) { create(:course, home_wiki: wikidata, slug: 'campaign/acourse') }
let(:campaign) { create(:campaign) }
let(:article) { create(:article) }
let(:user) { create(:user) }
@@ -408,8 +410,9 @@
let(:request_params) { { slug: campaign.slug, format: :csv } }
before do
+ stub_wiki_validation
login_as(user)
- campaign.courses << course
+ campaign.courses.push course, another_course
create(:courses_user, course: course, user: user)
end
@@ -437,6 +440,15 @@
expect(csv).to include(article.title)
expect(csv).to include('references_added')
end
+
+ it 'returns a csv of wikidata' do
+ expect(CsvCleanupWorker).to receive(:perform_at)
+ get "/campaigns/#{campaign.slug}/wikidata.csv"
+ get "/campaigns/#{campaign.slug}/wikidata.csv"
+ follow_redirect!
+ csv = response.body.force_encoding('utf-8')
+ expect(csv).to include('course name,claims created')
+ end
end
describe '#overview' do
diff --git a/spec/factories/course_stats.rb b/spec/factories/course_stats.rb
new file mode 100644
index 0000000000..ce0e72e4bd
--- /dev/null
+++ b/spec/factories/course_stats.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+# == Schema Information
+#
+# Table name: course_stats
+
+FactoryBot.define do
+ factory :course_stats, class: 'CourseStat' do
+ nil
+ end
+end
diff --git a/spec/lib/analytics/course_wikidata_csv_builder_spec.rb b/spec/lib/analytics/course_wikidata_csv_builder_spec.rb
new file mode 100644
index 0000000000..9ffb8e6135
--- /dev/null
+++ b/spec/lib/analytics/course_wikidata_csv_builder_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require "#{Rails.root}/lib/analytics/course_wikidata_csv_builder.rb"
+
+describe CourseWikidataCsvBuilder do
+ let(:wikidata) { Wiki.get_or_create(language: nil, project: 'wikidata') }
+ let(:course) { create(:course, home_wiki: wikidata) }
+ let(:builder) { described_class.new(course) }
+ let(:create_course_stat) do
+ create(:course_stats,
+ stats_hash: { 'www.wikidata.org' => { 'claims created' => 2 } }, course_id: course.id)
+ end
+
+ before { stub_wiki_validation }
+
+ context 'when no course_stat' do
+ it 'generates only headers' do
+ expect(builder.generate_csv).to start_with('course name,')
+ expect(builder.generate_csv.lines.count).to eq(1)
+ end
+ end
+
+ context 'when course_stat exists' do
+ before do
+ create_course_stat
+ end
+
+ it 'generates csv data' do
+ expect(builder.generate_csv.lines.first).to start_with('course name,claims created')
+ expect(builder.generate_csv.lines.last).to start_with(course.title + ',2')
+ expect(builder.generate_csv.lines.count).to eq 2
+ end
+ end
+end