Utilisateur:Piglobot/Code
user_category.rb
modifierrequire 'job'
class UserCategory < Piglobot::Job
def initialize(*args)
super
@name = "[[Utilisateur:Piglobot/Utilisateurs catégorisés dans main|Utilisateurs catégorisés dans main]]"
end
def process
20.times do
step_and_sleep
end
end
def step_and_sleep
step
sleep 2
end
def step
@done = false
@data ||= {}
if @data[:done]
@done = true
log("Toutes les catégories ont été traitées")
return
end
categories = @data[:categories]
if categories.nil?
@data[:categories] = @wiki.all_pages("14").select { |page|
valid_category?(page)
}
@data[:empty] = []
@data[:one] = []
@changed = true
else
category = nil
loop do
category = categories.shift
break if category.nil? or category !~ /^Utilisateur/
end
@changed = false
process_category(category)
if categories.empty?
[
[@data[:empty], "Catégories vides"],
[@data[:one], "Catégories avec une seule page"],
].each do |pages, title|
@wiki.post("Utilisateur:Piglobot/#{title}", pages.map { |page| "* [[:#{page}]]\n" }.join, "Mise à jour")
end
post_user_categories(@data[:users]) if @data[:users]
@done = true
@data[:done] = true
elsif categories.size % 1000 == 0
notice("#{categories.size} catégories à traiter (dernière : [[:#{category}]])")
end
end
end
def valid_category?(name)
name !~ /^Catégorie:Utilisateur/
end
def process_category(name)
if valid_category?(name)
process_valid_category(name)
else
log("Catégorie ignorée : #{name}")
end
end
def process_valid_category(name)
pages = @wiki.category(name.split(":", 2).last)
log("#{pages.size} pages dans #{name}")
if pages.size == 0
@data[:empty] << name
elsif pages.size == 1
@data[:one] << name
end
pages.delete_if { |page| page !~ /^Utilisateur:/ and page !~ /^Discussion Utilisateur:/ }
if pages.empty?
log "Aucune page utilisateur dans #{name}"
else
log "#{pages.size} pages utilisateur dans #{name}"
add_user_category(name, pages)
end
end
def post_user_categories(categories)
list_page = "Utilisateur:Piglobot/Utilisateurs catégorisés dans main"
text = ""
categories.sort_by { |name, pages| name.downcase }.each do |name, pages|
text << "== [[:#{name}]] ==\n" + pages.map { |page| "* [[:#{page}]]\n" }.join + "\n"
end
@wiki.append(list_page, text, "Mise à jour")
@changed = true
end
def add_user_category(name, pages)
@data[:users] ||= {}
@data[:users][name] = pages
end
end
user_category_spec.rb
modifierrequire 'user_category'
describe UserCategory do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@job = UserCategory.new(@bot)
end
it "should step 20 times at each process" do
@job.should_receive(:step_and_sleep).with().exactly(20).times
@job.process
end
it "should step and sleep" do
@job.should_receive(:step).ordered
@job.should_receive(:sleep).ordered.with(2)
@job.step_and_sleep
end
it "should retreive categories" do
@wiki.should_receive(:all_pages).with("14").and_return(["foo", "bar", "baz"])
@job.should_receive(:valid_category?).ordered.with("foo").and_return(true)
@job.should_receive(:valid_category?).ordered.with("bar").and_return(true)
@job.should_receive(:valid_category?).ordered.with("baz").and_return(false)
@job.step
@job.data.should == { :categories => ["foo", "bar"], :empty => [], :one => [] }
@job.changed?.should == true
@job.done?.should == false
end
it "should process next category" do
@job.data = { :categories => ["foo", "bar"] }
@wiki.should_not_receive(:all_pages)
@job.should_receive(:process_category).with("foo")
@job.step
@job.data.should == { :categories => ["bar"] }
@job.changed?.should == false
@job.done?.should == false
end
it "should be done and save special categories when out of category" do
initial_data = { :categories => ["foo"], :empty => ["foo", "bar"], :one => ["baz", "bob"], :users => { "Catégorie:cat" => ["foo", "Utilisateur:bob/panda"], "bar" => ["baz"] } }
@job.data = initial_data.dup
@wiki.should_not_receive(:all_pages)
@job.should_receive(:process_category).with("foo")
@job.should_not_receive(:notice)
@wiki.should_receive(:post).with("Utilisateur:Piglobot/Catégories vides", "* [[:foo]]\n* [[:bar]]\n", "Mise à jour")
@wiki.should_receive(:post).with("Utilisateur:Piglobot/Catégories avec une seule page", "* [[:baz]]\n* [[:bob]]\n", "Mise à jour")
page = "Utilisateur:Piglobot/Utilisateurs catégorisés dans main"
text = [
"== [[:bar]] ==",
"* [[:baz]]",
"",
"== [[:Catégorie:cat]] ==",
"* [[:foo]]",
"* [[:Utilisateur:bob/panda]]",
"",
].map { |x| x + "\n" }.join
@wiki.should_receive(:append).with(page, text, "Mise à jour")
@job.step
done_data = initial_data.dup
done_data[:done] = true
@job.data.should == done_data
@job.done?.should == true
end
it "should do nothing on step when done" do
@job.data = { :done => true }
@wiki.should_not_receive(:all_pages)
@job.should_not_receive(:process_category)
@job.should_receive(:log).with("Toutes les catégories ont été traitées")
@job.step
@job.done?.should == true
end
[
[100, false],
[1000, true],
[1, false],
[9, false],
[10, false],
[99, false],
[999, false],
[1001, false],
].each do |count, notice|
it "should #{notice ? '' : 'not' } notice when on #{count} categories remaining" do
@job.data = { :categories => ["foo", "bar"] + ["baz"] * (count - 1) }
@job.should_receive(:process_category).with("foo")
if notice
@job.should_receive(:notice).with("#{count} catégories à traiter (dernière : [[:foo]])")
else
@job.should_not_receive(:notice)
end
@job.step
end
end
[
"Catégorie:Utilisateur/foo",
"Catégorie:Utilisateur bar",
"Catégorie:Utilisateur",
].each do |category|
it "should know category #{category} is invalid" do
@job.valid_category?(category).should == false
end
end
[
"Catégorie:foo",
"Catégorie:bar",
"Catégorie:foo Utilisateur",
].each do |category|
it "should know category #{category} is valid" do
@job.valid_category?(category).should == true
end
end
it "should not process invalid category" do
@job.should_receive(:valid_category?).with("Catégorie:cat").and_return(false)
@job.should_receive(:log).with("Catégorie ignorée : Catégorie:cat")
@job.process_category("Catégorie:cat")
@job.changed?.should == false
end
it "should process valid category" do
@job.should_receive(:valid_category?).with("Catégorie:cat").and_return(true)
@job.should_receive(:process_valid_category).with("Catégorie:cat")
@job.process_category("Catégorie:cat")
end
it "should detect user pages on valid category" do
@wiki.should_receive(:category).with("cat").and_return(["foo", "Utilisateur:foo", "bar", "Utilisateur:bob/panda", "Discussion Utilisateur:test/test"])
@job.should_receive(:log).with("5 pages dans Catégorie:cat")
@job.should_receive(:log).with("3 pages utilisateur dans Catégorie:cat")
@job.should_receive(:add_user_category).with("Catégorie:cat", ["Utilisateur:foo", "Utilisateur:bob/panda", "Discussion Utilisateur:test/test"])
@job.process_valid_category("Catégorie:cat")
end
it "should do nothing when no user page in category" do
@wiki.should_receive(:category).with("cat").and_return(["foo", "foo Utilisateur:foo", "bar"])
@job.should_receive(:log).with("3 pages dans Catégorie:cat")
@job.should_receive(:log).with("Aucune page utilisateur dans Catégorie:cat")
@job.process_valid_category("Catégorie:cat")
end
it "should create user category data" do
@job.data = {}
@job.add_user_category("Catégorie:cat", ["foo", "Utilisateur:bob/panda"])
@job.data.should == { :users => { "Catégorie:cat" => ["foo", "Utilisateur:bob/panda"] } }
@job.changed?.should == false
end
it "should append new user category" do
@job.data = { :users => { "foo" => ["bar"] }}
@job.add_user_category("cat", ["baz", "bob"])
@job.data.should == { :users => { "foo" => ["bar"], "cat" => ["baz", "bob"] } }
end
it "should save empty category" do
@job.data = {}
@job.data[:empty] = mock("empty list")
@job.data[:empty].should_receive(:<<).with("Catégorie:foo")
@wiki.should_receive(:category).with("foo").and_return([])
@job.should_receive(:log).twice
@job.process_valid_category("Catégorie:foo")
end
it "should save category with one item" do
@job.data = {}
@job.data[:one] = mock("empty list")
@job.data[:one].should_receive(:<<).with("Catégorie:foo")
@wiki.should_receive(:category).with("foo").and_return(["foo"])
@job.should_receive(:log).twice
@job.process_valid_category("Catégorie:foo")
end
end
change.rb
modifierrequire 'job'
require 'ruby-mediawiki/lib/mediawiki/minibrowser'
class Change < Piglobot::Job
attr_accessor :raw_data, :currencies
def initialize(*args)
super
@name = "{{m|Change}}"
end
def process
get_raw_data
parse_raw_data
publish_data
end
def get_raw_data
uri = URI.parse("http://xurrency.com/")
browser = MediaWiki::MiniBrowser.new(uri)
@raw_data = browser.get_content("/usd/feed")
end
def parse_raw_data
regexp = %r{<title xml:lang="en"><!\[CDATA\[1 USD = ([\d\.]+) ([A-Z]+)\]\]></title>}
@currencies = {}
@raw_data.scan(regexp).each do |value, name|
@currencies[name] = value
end
end
def publish_data
missing = false
names = %w( EUR GBP JPY CAD CHF )
hash = {}
names.each do |name|
value = @currencies[name]
if value
hash[name] = value
else
notice("[[Modèle:Change/#{name}]] : Aucune donnée")
missing = true
end
end
if missing
notice("Mise à jour annulée car il manque des données")
else
comment = "[[Utilisateur:Piglobot/Travail#Change|Mise à jour automatique]]"
hash.each do |name, value|
@wiki.post("Modèle:Change/#{name}", value, comment)
end
now = Time.now
monthes = %w( janvier février mars avril mai juin juillet août septembre octobre novembre décembre )
now = "#{now.day} #{monthes[now.month - 1]} #{now.year}"
@wiki.post("Modèle:Change/Màj", now, comment)
end
end
end
change_spec.rb
modifierrequire 'change'
describe Change do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@job = Change.new(@bot)
end
it "should process" do
@job.should_receive(:get_raw_data).with()
@job.should_receive(:parse_raw_data).with()
@job.should_receive(:publish_data).with()
@job.process
end
it "should get_raw_data" do
url = URI.parse("http://xurrency.com/")
@browser = mock("browser")
MediaWiki::MiniBrowser.should_receive(:new).with(url).and_return(@browser)
@browser.should_receive(:get_content).with("/usd/feed").and_return("content")
@job.get_raw_data
@job.raw_data.should == "content"
end
it "should parse_raw_data" do
@job.raw_data = File.read("sample_currency.xhtml")
@job.parse_raw_data
{
"EUR" => "0.6818",
"GBP" => "0.4747",
"JPY" => "113.6364",
"CAD" => "0.9293",
"CHF" => "1.1320",
}.each do |name, value|
{ name => @job.currencies[name] }.should == { name => value }
end
end
it "should publish_data" do
@job.currencies = {
"EUR" => "0.6818",
"GBP" => "0.4747",
"JPY" => "113.6364",
"CAD" => "0.9293",
"CHF" => "1.1320",
}
@job.currencies.each do |name, value|
@wiki.should_receive(:post).with("Modèle:Change/#{name}", value, "[[Utilisateur:Piglobot/Travail#Change|Mise à jour automatique]]")
end
Time.should_receive(:now).with().and_return(Time.local(2007, 11, 10, 1, 51, 12))
@wiki.should_receive(:post).with("Modèle:Change/Màj", "10 novembre 2007", "[[Utilisateur:Piglobot/Travail#Change|Mise à jour automatique]]")
@job.publish_data
end
it "should notice when a currency is missing" do
@job.currencies = {}
%w( EUR GBP JPY CAD CHF ).each do |name|
@job.should_receive(:notice).with("[[Modèle:Change/#{name}]] : Aucune donnée")
end
@job.should_receive(:notice).with("Mise à jour annulée car il manque des données")
@job.publish_data
end
it "should not update when one data is missing" do
@job.currencies = {
"EUR" => "0.6818",
"JPY" => "113.6364",
"CAD" => "0.9293",
"CHF" => "1.1320",
}
%w( GBP ).each do |name|
@job.should_receive(:notice).with("[[Modèle:Change/#{name}]] : Aucune donnée")
end
@job.should_receive(:notice).with("Mise à jour annulée car il manque des données")
@job.publish_data
end
it "should always be done" do
@job.done?.should == true
end
it "should have a better name" do
@job.name.should == "{{m|Change}}"
end
end
infobox_rewriter.rb
modifierclass Piglobot
class InfoboxRewriter < Job
attr_accessor :links, :infobox
def done?
false
end
def data_id
@infobox
end
def initialize(bot, infobox, links)
super(bot)
@infobox = infobox
@links = links
@editor = Piglobot::Editor.new(bot)
end
def process
@editor.setup(@infobox)
data = @data
changes = false
infobox = @infobox
links = @links
articles = data
if articles and !articles.empty?
article = articles.shift
if article =~ /:/
comment = "Article ignoré car il n'est pas dans le bon espace de nom"
text = "[[#{article}]] : #{comment}"
Piglobot::Tools.log(text)
else
text = @wiki.get(article)
begin
@editor.current_article = article
box = @editor.parse_infobox(text)
if box
result = @editor.write_infobox(box)
if result != text
comment = "[[Utilisateur:Piglobot/Travail##{infobox}|Correction automatique]] de l'[[Modèle:#{infobox}|#{infobox}]]"
@wiki.post(article,
result,
comment)
changes = true
else
text = "[[#{article}]] : Aucun changement nécessaire dans l'#{infobox}"
Piglobot::Tools.log(text)
end
else
@bot.notice("#{infobox} non trouvée dans l'article", article)
changes = true
end
rescue => e
@bot.notice(e.message, article)
changes = true
end
end
else
articles = []
links.each do |link|
articles += @wiki.links(link)
end
articles.uniq!
articles.delete_if { |name| name =~ /:/ and name !~ /::/ }
data = articles
@bot.notice("#{articles.size} articles à traiter pour #{infobox}")
changes = true
end
@changed = changes
@data = data
end
end
class InfoboxSoftware < InfoboxRewriter
def initialize(bot)
super(bot, "Infobox Logiciel", ["Modèle:Infobox Logiciel"])
end
end
class InfoboxProtectedArea < InfoboxRewriter
def initialize(bot)
super(bot,
"Infobox Aire protégée",
["Modèle:Infobox Aire protégée", "Modèle:Infobox aire protégée"]
)
end
end
end
infobox_rewriter_spec.rb
modifierrequire 'infobox_rewriter'
describe Piglobot, " on rewriter jobs" do
[
["Infobox Logiciel", Piglobot::InfoboxSoftware],
["Infobox Aire protégée", Piglobot::InfoboxProtectedArea],
].each do |job, klass|
it "should find job class for job #{job.inspect}" do
@wiki = mock("wiki")
Piglobot::Wiki.should_receive(:new).with().and_return(@wiki)
@bot = Piglobot.new
@bot.job_class(job).should == klass
end
end
end
module RandomTemplate
module_function
def random_name
chars = ((0..25).map { |i| [?a + i, ?A + i] }.flatten.map { |c| c.chr } + [" ", "_", "-"]).flatten
@infobox_name ||= (1..(rand(20)+1)).map { chars[rand(chars.size)] }.join
end
end
describe Piglobot::InfoboxRewriter do
before do
@data = nil
@bot = mock("bot")
@wiki = mock("wiki")
@editor = mock("editor")
@bot.should_receive(:wiki).and_return(@wiki)
Piglobot::Editor.should_receive(:new).with(@bot).and_return(@editor)
@name = RandomTemplate.random_name
@link = "link"
@links = [@link]
@rewriter = Piglobot::InfoboxRewriter.new(@bot, @name, @links)
end
it "should never be done" do
@rewriter.done?.should == false
end
it "should get infobox links when data is empty" do
@wiki.should_receive(:links).with(@link).and_return(["Foo", "Bar", "Baz"])
@bot.should_receive(:notice).with("3 articles à traiter pour #@name")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == ["Foo", "Bar", "Baz"]
end
it "should get infobox multiple links when data is empty" do
@rewriter.links = ["First", "Second"]
@wiki.should_receive(:links).with("First").and_return(["Foo", "Bar", "Baz"])
@wiki.should_receive(:links).with("Second").and_return(["A", "Bar", "C", "D"])
@bot.should_receive(:notice).with("6 articles à traiter pour #@name")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.sort.should == ["Foo", "Bar", "Baz", "A", "C", "D"].sort
end
it "should send infobox links to InfoboxEditor" do
@rewriter.data = ["Article 1", "Article 2"]
@wiki.should_receive(:get).with("Article 1").and_return("foo")
infobox = mock("infobox")
@editor.should_receive(:current_article=).with("Article 1").ordered
@editor.should_receive(:parse_infobox).with("foo").ordered.and_return(infobox)
@editor.should_receive(:write_infobox).with(infobox).ordered.and_return("result")
comment = "[[Utilisateur:Piglobot/Travail##@name|Correction automatique]] de l'[[Modèle:#@name|#@name]]"
@wiki.should_receive(:post).with("Article 1", "result", comment)
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == ["Article 2"]
end
it "should not write infobox if none found" do
@rewriter.data = ["Article 1", "Article 2"]
@wiki.should_receive(:get).with("Article 1").and_return("foo")
@editor.should_receive(:current_article=).with("Article 1")
@editor.should_receive(:parse_infobox).with("foo").and_return(nil)
@bot.should_receive(:notice).with("#@name non trouvée dans l'article", "Article 1")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == ["Article 2"]
end
it "should not write infobox if nothing changed" do
@rewriter.data = ["Article 1", "Article 2"]
@wiki.should_receive(:get).with("Article 1").and_return("foo")
infobox = mock("infobox")
@editor.should_receive(:current_article=).with("Article 1")
@editor.should_receive(:parse_infobox).with("foo").and_return(infobox)
@editor.should_receive(:write_infobox).with(infobox).and_return("foo")
text = "[[Article 1]] : Aucun changement nécessaire dans l'#@name"
Piglobot::Tools.should_receive(:log).with(text).once
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == false
@rewriter.data.should == ["Article 2"]
end
it "should log parsing error" do
@rewriter.data = ["Article 1", "Article 2"]
@wiki.should_receive(:get).with("Article 1").and_return("foo")
infobox = mock("infobox")
@editor.should_receive(:current_article=).with("Article 1")
@editor.should_receive(:parse_infobox).with("foo").and_raise(Piglobot::ErrorPrevention.new("error message"))
@bot.should_receive(:notice).with("error message", "Article 1")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == ["Article 2"]
end
it "should get infobox links when list is empty" do
@rewriter.data = []
@wiki.should_receive(:links).with(@link).and_return(["A", "B"])
@bot.should_receive(:notice).with("2 articles à traiter pour #@name")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == ["A", "B"]
end
it "should ignore links in namespace" do
@rewriter.data = []
@wiki.should_receive(:links).with(@link).and_return(["A", "B", "C:D", "E:F", "G::H", "I:J"])
expected = ["A", "B", "G::H"]
@bot.should_receive(:notice).with("#{expected.size} articles à traiter pour #@name")
@editor.should_receive(:setup).with(@name)
@rewriter.process
@rewriter.changed?.should == true
@rewriter.data.should == expected
end
end
describe Piglobot::InfoboxSoftware do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).and_return(@wiki, @wiki)
@job = Piglobot::InfoboxSoftware.new(@bot)
end
it "should have infobox" do
@job.infobox.should == "Infobox Logiciel"
end
it "should have links" do
@job.links.should == ["Modèle:Infobox Logiciel"]
end
end
describe Piglobot, " on real case" do
it "should continue Infobox Logiciel" do
@wiki = mock("wiki")
Piglobot::Wiki.should_receive(:new).and_return(@wiki)
@bot = Piglobot.new
@bot.job = "Infobox Logiciel"
File.should_receive(:read).with("data.yaml").and_return({
"Foo" => "Bar",
"Infobox Logiciel" => ["Blender", "GNU Emacs"],
"Infobox Aire protégée" => ["Foo"],
}.to_yaml)
@wiki.should_receive(:get).with("Blender").and_return("{{Infobox Logiciel | name = Blender }}\nBlender...")
@wiki.should_receive(:post) do |article, content, comment|
article.should == "Blender"
content.should == "{{Infobox Logiciel\n| nom = Blender\n}}\nBlender..."
comment.should =~ /Correction automatique/
comment.should =~ /Infobox Logiciel/
end
file = mock("file")
File.should_receive(:open).with("data.yaml.new", "w").and_yield(file)
file.should_receive(:write).with({
"Foo" => "Bar",
"Infobox Logiciel" => ["GNU Emacs"],
"Infobox Aire protégée" => ["Foo"],
}.to_yaml)
File.should_receive(:rename).with("data.yaml.new", "data.yaml")
@bot.process
end
end
editor_spec.rb
modifier=begin
Copyright (c) 2007 by Piglop
This file is part of Piglobot.
Piglobot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Piglobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Piglobot. If not, see <http://www.gnu.org/licenses/>.
=end
require 'piglobot'
require 'helper'
describe Piglobot::Editor, " with default values", :shared => true do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@editor = Piglobot::Editor.new(@bot)
@editor.bot.should == @bot
@editor.wiki.should == @wiki
@template_names = []
@filters = []
@template_name = nil
@name_changes = {}
@removable_parameters = []
end
it "should have template_names" do
@editor.template_names.should == @template_names
end
it "should have filters" do
@editor.filters.should == @filters
end
it "should have template_name" do
@editor.template_name.should == @template_name
end
it "should have name_changes" do
@editor.name_changes.should == @name_changes
end
it "should have removable_parameters" do
@editor.removable_parameters.should == @removable_parameters
end
end
describe Piglobot::Editor, " with real default values" do
it_should_behave_like "Piglobot::Editor with default values"
end
describe Piglobot::Editor, " working on Infobox Logiciel" do
it_should_behave_like "Piglobot::Editor with default values"
before do
@editor.setup "Infobox Logiciel"
@template_names = [
"Infobox Logiciel",
"Logiciel simple",
"Logiciel_simple",
"Logiciel",
"Infobox Software",
"Infobox_Software",
]
@filters = [
:rename_parameters,
:remove_open_source,
:remove_almost_empty,
:remove_firefox,
:rewrite_dates,
]
@template_name = "Infobox Logiciel"
@name_changes = {
"dernière_version" => "dernière version",
"date_de_dernière_version" => "date de dernière version",
"version_avancée" => "version avancée",
"date_de_version_avancée" => "date de version avancée",
"os" => "environnement",
"site_web" => "site web",
"name" => "nom",
"screenshot" => "image",
"caption" => "description",
"developer" => "développeur",
"latest release version" => "dernière version",
"latest release date" => "date de dernière version",
"latest preview version" => "dernière version avancée",
"latest preview date" => "date de dernière version avancée",
"latest_release_version" => "dernière version",
"latest_release_date" => "date de dernière version",
"latest_preview_version" => "dernière version avancée",
"latest_preview_date" => "date de dernière version avancée",
"platform" => "environnement",
"operating system" => "environnement",
"operating_system" => "environnement",
"language" => "langue",
"genre" => "type",
"license" => "licence",
"website" => "site web",
}
end
end
describe Piglobot::Editor, " working on Infobox Aire protégée" do
it_should_behave_like "Piglobot::Editor with default values"
before do
@editor.setup "Infobox Aire protégée"
@template_names = [
"Infobox Aire protégée",
"Infobox aire protégée",
"Infobox_aire protégée",
]
@filters = [
:rename_parameters,
:remove_parameters,
:rewrite_dates,
:rename_image_protected_area,
:rewrite_coordinates,
:rewrite_area,
]
@template_name = "Infobox Aire protégée"
@name_changes = {
"name" => "nom",
"iucn_category" => "catégorie iucn",
"locator_x" => "localisation x",
"locator_y" => "localisation y",
"top_caption" => "légende image",
"location" => "situation",
"localisation" => "situation",
"nearest_city" => "ville proche",
"area" => "superficie",
"established" => "création",
"visitation_num" => "visiteurs",
"visitation_year" => "visiteurs année",
"governing_body" => "administration",
"web_site" => "site web",
"comments" => "remarque",
"caption" => "légende carte",
"base_width" => "largeur carte",
"bot_image" => "image pied",
"bot_caption" => "légende pied",
}
@removable_parameters = ["back_color", "label"]
end
it "should parse and write real case" do
text = File.read("parc_national_des_arches.txt")
result = File.read("parc_national_des_arches_result.txt")
infobox = @editor.parse_infobox(text)
infobox[:parameters].should include(["name", "Arches"])
@editor.write_infobox(infobox).should == result
end
it "should rewrite template name" do
box = { :before => "", :after => "", :parameters => "" }
@editor.write_infobox(box).should == "{{Infobox Aire protégée}}"
end
end
describe Piglobot::Editor, " parsing Infobox Logiciel" do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@editor = Piglobot::Editor.new(@bot)
@infobox = {
:name => "Infobox Logiciel",
:before => "",
:after => "",
:parameters => [],
}
@editor.template_names = ["Infobox Logiciel"]
end
it "should parse empty infobox" do
@editor.parse_infobox("{{Infobox Logiciel}}").should == @infobox
end
it "should return nil on empty string" do
@editor.parse_infobox("").should == nil
end
it "should return nil on 'foo'" do
@editor.parse_infobox("foo").should == nil
end
it "should return nil on '{{foo}}'" do
@editor.parse_infobox("{{foo}}").should == nil
end
it "should return nil on '{{Infobox Logiciel}'" do
@editor.parse_infobox("{{Infobox Logiciel}").should == nil
end
it "should return nil on 'Infobox Logiciel'" do
@editor.parse_infobox("Infobox Logiciel").should == nil
end
it "should keep text before infobox on parsing" do
@infobox[:before] = "text before"
@editor.parse_infobox("text before{{Infobox Logiciel}}").should == @infobox
end
it "should keep text after infobox on parsing" do
@infobox[:after] = "text after"
@editor.parse_infobox("{{Infobox Logiciel}}text after").should == @infobox
end
it "should allow line breaks before and after" do
@infobox[:before] = "\nfoo\n\nbar\n"
@infobox[:after] = "bob\n\nmock\n\n"
text = "#{@infobox[:before]}{{Infobox Logiciel}}#{@infobox[:after]}"
@editor.parse_infobox(text).should == @infobox
end
it "should parse simple parameter" do
text = "{{Infobox Logiciel | nom = Nom }}"
@infobox[:parameters] = [["nom", "Nom"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse multiple parameters" do
text = "{{Infobox Logiciel | nom = Nom | foo = bar }}"
@infobox[:parameters] = [["nom", "Nom"], ["foo", "bar"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters on multiple lines" do
text = "{{Infobox Logiciel\n|\n nom = \nNom\nsuite\n | foo\n = \nbar\n\nbaz\n\n }}"
@infobox[:parameters] = [["nom", "Nom\nsuite"], ["foo", "bar\n\nbaz"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters with pipes" do
text = "{{Infobox Logiciel | logo = [[Image:Logo.svg|80px]] | foo = bar }}"
@infobox[:parameters] = [["logo", "[[Image:Logo.svg|80px]]"], ["foo", "bar"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters with template" do
text = "{{Infobox Logiciel | date = {{Date|12|janvier|2008}} | foo = bar }}"
@infobox[:parameters] = [["date", "{{Date|12|janvier|2008}}"], ["foo", "bar"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters with ref" do
text = "{{Infobox Logiciel | dernière version = 1.12<ref>[http://foo.com/bar ref]</ref>}}"
@infobox[:parameters] = [["dernière version", "1.12<ref>[http://foo.com/bar ref]</ref>"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters with new lines" do
text = "{{Infobox Logiciel | nom = foo\n\n bar\n | foo = bar }}"
@infobox[:parameters] = [["nom", "foo\n\n bar"], ["foo", "bar"]]
@editor.parse_infobox(text).should == @infobox
end
it "should parse parameters with weird new lines" do
text = "{{Infobox Logiciel |\nnom = foo |\nimage = |\n}}"
@infobox[:parameters] = [["nom", "foo"], ["image", ""]]
@editor.parse_infobox(text).should == @infobox
end
[
["Logiciel simple", ["Logiciel simple"]],
["logiciel simple", ["Logiciel simple"]],
["foo", ["foo", "bar"]],
["foo", ["bar", "foo"]],
["f", ["f"]],
["f", ["F"]],
["foo", ["Foo"]],
["Foo", ["foo"]],
].each do |template, template_names|
it "should find #{template.inspect} using template_names #{template_names.inspect}" do
@editor.template_names = template_names
text = "{{#{template} | bob = mock }}"
@infobox[:parameters] = [["bob", "mock"]]
@infobox[:name] = template
@editor.parse_infobox(text).should == @infobox
end
end
[
["Logiciel Simple", ["Logiciel simple"]],
["foo", ["bar"]],
["foo", ["bar", "baz"]],
["foo", ["fooo"]],
["foo", ["fo"]],
["foo", ["foO"]],
["foO", ["foo"]],
].each do |template, template_names|
it "should not find #{template.inspect} using template_names #{template_names.inspect}" do
@editor.template_names = template_names
text = "{{#{template} | bob = mock }}"
@infobox[:parameters] = [["bob", "mock"]]
@editor.parse_infobox(text).should == nil
end
end
it "should have default template_names" do
@editor.setup
@editor.template_names.should == []
end
it "should parse mono.sample" do
text = File.read("mono.sample")
@infobox[:parameters] = [
["nom", "Mono"],
["logo", "[[Image:Mono project logo.svg|80px]]"],
["image", ""],
["description", ""],
["développeur", "[[Novell]]"],
["dernière version", "1.2.5.1"],
["date de dernière version", "{{Date|20|septembre|2007}}"],
["version avancée", ""],
["date de version avancée", ""],
["environnement", "[[Multiplate-forme]]"],
["langue", ""],
["type", ""],
["licence", "[[Licence Publique Générale|GPL]], [[Licence publique générale limitée GNU|LGPL]] ou [[X11]]"],
["site web", "[http://www.mono-project.com www.mono-project.com]"],
]
@editor.parse_infobox(text)[:parameters].should == @infobox[:parameters]
end
it "should parse limewire.sample" do
text = File.read("limewire.sample")
@editor.template_names = ["Logiciel_simple"]
box = @editor.parse_infobox(text)
box.should_not == nil
@editor.write_infobox(box).should_not == text
end
it "should raise an error when an html comment is over parameters (name => name)" do
text = "{{Infobox Logiciel |\nnom = foo \n |\n <!-- image = | --> a = b\n}}"
lambda { @editor.parse_infobox(text) }.should raise_error(Piglobot::ErrorPrevention,
"L'infobox contient un commentaire qui dépasse un paramètre")
end
it "should raise an error when an html comment is over parameters (value => value)" do
text = "{{Infobox Logiciel |\nnom = foo \n<!-- |\nimage = --> | a = b\n}}"
lambda { @editor.parse_infobox(text) }.should raise_error(Piglobot::ErrorPrevention,
"L'infobox contient un commentaire qui dépasse un paramètre")
end
it "should not raise an error when an html comment is only in value" do
text = "{{Infobox Logiciel |\nnom= foo \n |\nimage = <!-- comment --> | <!-- a --> = b\n}}"
lambda { @editor.parse_infobox(text) }.should_not raise_error
end
it "should raise an error when an parameter has no name" do
text = "{{Infobox Logiciel |\nnom = foo \n |\n bar | a = b\n}}"
lambda { @editor.parse_infobox(text) }.should raise_error(Piglobot::ErrorPrevention,
"L'infobox contient un paramètre sans nom")
end
end
describe Piglobot::Editor, " writing Infobox Logiciel" do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@editor = Piglobot::Editor.new(@bot)
@infobox = {
:before => "",
:after => "",
:parameters => [],
}
@editor.template_name = "Infobox Logiciel"
end
it "should write empty infobox" do
@editor.write_infobox(@infobox).should == "{{Infobox Logiciel}}"
end
it "should write infobox with surrounding text" do
@infobox[:before] = "before"
@infobox[:after] = "after"
@editor.write_infobox(@infobox).should == "before{{Infobox Logiciel}}after"
end
it "should write infobox with parameters" do
@infobox[:parameters] = [["nom", "value"], ["other name", "other value"]]
@editor.write_infobox(@infobox).should ==
"{{Infobox Logiciel\n| nom = value\n| other name = other value\n}}"
end
it "should write infobox with new lines in parameter" do
@infobox[:parameters] = [["nom", "first line\n second line\nthird line"]]
@editor.write_infobox(@infobox).should ==
"{{Infobox Logiciel\n| nom = first line\n second line\nthird line\n}}"
end
it "should remove [[open source]] from type" do
params = [["type", "foo ([[open source]])"]]
@editor.remove_open_source(params)
params.should == [["type", "foo"]]
end
it "should remove [[open source]] and spaces from type" do
params = [["type", "foo ([[open source]])"]]
@editor.remove_open_source(params)
params.should == [["type", "foo"]]
end
[
"?",
"??",
"-",
].each do |text|
it "should remove values containing only #{text.inspect}" do
params = [["foo", text], ["bar", "uh?"], ["baz", "--"]]
@editor.remove_almost_empty(params)
params.should == [["foo", ""], ["bar", "uh?"], ["baz", "--"]]
end
end
it "should write unnammed parameters" do
@infobox[:parameters] = [["foo", "foo"], ["", "bar"], [nil, "baz"]]
@editor.write_infobox(@infobox).should ==
"{{Infobox Logiciel\n| foo = foo\n| = bar\n| baz\n}}"
end
it "should remove values like {{{latest preview date|}}}" do
params = [["foo", "{{{foo bar|}}}"], ["bar", "{{{bar}}}"], ["baz", "foo {{{bar|}}}"]]
@editor.remove_almost_empty(params)
params.should == [["foo", ""], ["bar", "{{{bar}}}"], ["baz", "foo {{{bar|}}}"]]
end
it "should remove notice about firefox screenshot" do
params = [["image", "foo <!-- Ne pas changer la capture d'écran, sauf grand changement. Et utilisez la page d'accueil de Wikipédia pour la capture, pas la page de Firefox. Prenez une capture à une taille « normale » (de 800*600 à 1024*780), désactiver les extensions et prenez le thème par défaut. -->bar"]]
@editor.remove_firefox(params)
params.should == [["image", "foo bar"]]
end
it "should remove notice about firefox screenshot with newline and spaces" do
params = [["image", "<!--
* Ne pas changer la capture d'écran, sauf grand changement.
* Utiliser la page d'accueil de Wikipédia pour la capture, pas la page de Firefox.
* Prendre une capture à une taille « normale » (de 800*600 à 1024*780).
* Désactiver les extensions et prendre le thème par défaut.
-->bar"]]
@editor.remove_firefox(params)
params.should == [["image", "bar"]]
end
%w(janvier février mars avril mai juin
juillet août septembre octobre novembre décembre).map { |month|
[month, month.capitalize]
}.flatten.each do |month|
emonth = month.downcase
{
"[[1er #{month}]] [[1998]]" => "{{date|1|#{emonth}|1998}}",
"[[18 #{month}]] [[2005]]" => "{{date|18|#{emonth}|2005}}",
"[[31 #{month}]] [[2036]]" => "{{date|31|#{emonth}|2036}}",
"[[04 #{month}]] [[1950]]" => "{{date|4|#{emonth}|1950}}",
"a[[04 #{month}]] [[1950]]" => "a[[04 #{month}]] [[1950]]",
"[[04 #{month}]] [[1950]]b" => "[[04 #{month}]] [[1950]]b",
"04 #{month} 1950" => "{{date|4|#{emonth}|1950}}",
"04 #{month}? 1950" => "04 #{month}? 1950",
"le 04 #{month} 1950" => "le 04 #{month} 1950",
"[[04 fevrier]] [[1950]]" => "[[04 fevrier]] [[1950]]",
"[[004 #{month}]] [[1950]]" => "[[004 #{month}]] [[1950]]",
"[[4 #{month}]] [[19510]]" => "[[4 #{month}]] [[19510]]",
"4 #{month} [[1951]]" => "{{date|4|#{emonth}|1951}}",
"[[18 #{month}]], [[2005]]" => "{{date|18|#{emonth}|2005}}",
"[[18 #{month}]] foo [[2005]]" => "[[18 #{month}]] foo [[2005]]",
"01 [[#{month}]] [[2005]]" => "{{date|1|#{emonth}|2005}}",
"07 [[#{month} (mois)|#{month}]] [[2005]]" => "{{date|7|#{emonth}|2005}}",
"[[#{month}]] [[2003]]" => "[[#{month}]] [[2003]]",
"[[#{month} (mois)|#{month}]] [[2003]]" => "[[#{month} (mois)|#{month}]] [[2003]]",
"{{1er #{month}}} [[2007]]" => "{{date|1|#{emonth}|2007}}",
"1{{er}} #{month} 1928" => "{{date|1|#{emonth}|1928}}",
"{{Date|1|#{emonth}|1928}}" => "{{Date|1|#{emonth}|1928}}",
"{{date|1|#{emonth}|1928}}" => "{{date|1|#{emonth}|1928}}",
}.each do |text, result|
it "should rewrite_date #{text.inspect} to #{result.inspect}" do
@editor.rewrite_date(text).should == result
end
end
end
it "should apply filters" do
params = [["foo", "bar"]]
@editor.should_receive(:fake_filter).with(params) do |parameters|
@editor.infobox.should == @infobox
parameters.replace [["a", "b"]]
end
@editor.filters = [:fake_filter]
@infobox[:parameters] = params
@editor.write_infobox(@infobox).should ==
"{{Infobox Logiciel\n| a = b\n}}"
end
it "should call rewrite_date with all values and replace with result" do
params = [
["foo", "bar"],
["baz", "baz2"],
]
@editor.should_receive(:rewrite_date).with("bar").and_return("1")
@editor.should_receive(:rewrite_date).with("baz2").and_return("baz2")
@editor.rewrite_dates(params)
params.should == [
["foo", "1"],
["baz", "baz2"],
]
end
it "should rename_parameters with name_changes" do
params = [["foo", "foo"], ["bar baz", "value"]]
@editor.name_changes = { "foo" => "new foo", "bar baz" => "bob" }
@editor.rename_parameters(params)
params.should == [["new foo", "foo"], ["bob", "value"]]
end
["infobox aire protégée", "Infobox aire protégée", "infobox_aire protégée", "Infobox_aire protégée"].each do |name|
it "should rename image on protected_area when template is #{name}" do
params = [["image", "map"], ["top_image", "illustration"]]
@editor.infobox = { :name => name }
@editor.rename_image_protected_area(params)
params.should == [["carte", "map"], ["image", "illustration"]]
end
it "should rename image on protected_area when template is #{name}, inverted order" do
params = [["top_image", "illustration"], ["image", "map"]]
@editor.infobox = { :name => name }
@editor.rename_image_protected_area(params)
params.should == [["image", "illustration"], ["carte", "map"]]
end
end
it "should not rename image on protected_area when already done" do
params = [["carte", "map"], ["image", "illustration"]]
@editor.infobox = { :name => "Infobox Aire protégée" }
@editor.rename_image_protected_area(params)
params.should == [["carte", "map"], ["image", "illustration"]]
end
it "should not rename image on protected_area when already done, inverted order" do
params = [["image", "illustration"], ["carte", "map"]]
@editor.infobox = { :name => "Infobox Aire protégée" }
@editor.rename_image_protected_area(params)
params.should == [["image", "illustration"], ["carte", "map"]]
end
it "should rewrite coordinates" do
params = [
["foo", "bar"],
["lat_degrees", "1"],
["lat_minutes", "2"],
["lat_seconds", "3"],
["lat_direction", "S"],
["long_degrees", "5"],
["long_minutes", "6"],
["long_seconds", "7"],
["long_direction", "E"],
["bar", "baz"]
]
@editor.rewrite_coordinates(params)
params.should == [["foo", "bar"], ["coordonnées", "{{coord|1|2|3|S|5|6|7|E}}"], ["bar", "baz"]]
end
it "should rewrite coordinates without seconds" do
params = [
["foo", "bar"],
["lat_degrees", "1"],
["lat_minutes", "2"],
["lat_seconds", ""],
["lat_direction", "N"],
["long_degrees", "5"],
["long_minutes", "6"],
["long_seconds", ""],
["long_direction", "W"],
["bar", "baz"]
]
@editor.rewrite_coordinates(params)
params.should == [["foo", "bar"], ["coordonnées", "{{coord|1|2|N|5|6|W}}"], ["bar", "baz"]]
end
it "should rewrite coordinates without data" do
params = [
["foo", "bar"],
["lat_degrees", ""],
["lat_minutes", ""],
["lat_seconds", ""],
["lat_direction", ""],
["long_degrees", ""],
["long_minutes", ""],
["long_seconds", ""],
["long_direction", ""],
["bar", "baz"]
]
@editor.rewrite_coordinates(params)
params.should == [["foo", "bar"], ["coordonnées", "<!-- {{coord|...}} -->"], ["bar", "baz"]]
end
it "should not rewrite anything when no coordinates" do
params = [
["alat_degrees", "1"],
["alat_minutes", "2"],
["alat_seconds", "3"],
["alat_direction", "4"],
["along_degrees", "5"],
["along_minutes", "6"],
["along_seconds", "7"],
["along_direction", "8"],
]
@editor.rewrite_coordinates(params)
params.should == [
["alat_degrees", "1"],
["alat_minutes", "2"],
["alat_seconds", "3"],
["alat_direction", "4"],
["along_degrees", "5"],
["along_minutes", "6"],
["along_seconds", "7"],
["along_direction", "8"],
]
end
[
{ "lat_direction" => "" },
{ "long_direction" => "" },
{ "lat_minutes" => "" },
{ "lat_direction" => "", "long_direction" => "" },
{ "lat_direction" => "O" },
{ "long_direction" => "O" },
{ "lat_direction" => "1" },
{ "long_direction" => "F" },
].each do |new_values|
it "should notice on invalid coordinates #{new_values.inspect}" do
params = [
["lat_degrees", "1"],
["lat_minutes", "2"],
["lat_seconds", "3"],
["lat_direction", "4"],
["long_degrees", "5"],
["long_minutes", "6"],
["long_seconds", "7"],
["long_direction", "8"],
]
new_values.each do |k,v|
params.map! do |name, value|
if name == k
[name, v]
else
[name, value]
end
end
end
old_params = YAML.load(params.to_yaml)
@bot.should_receive(:notice).with("Coordonnées invalides")
@editor.rewrite_coordinates(params)
params.should == old_params
end
end
%w( area superficie ).each do |name|
[
["{{unité|4800|km|2}}", "4800"],
["{{unité|150|km|2}}", "150"],
["{{formatnum:2219799}} acres<br />{{formatnum:8983}} km²", "8983"],
["{{unité|761266|acres}}<br />{{unité|3081|km|2}}", "3081"],
["76 519 acres<br />310 km²", "310"],
["741,5 km²", "741.5"],
["35 835 acres<br />145 km²", "145"],
["10 878 km²", "10878"],
["337 598 acres<br />1 366,21 km²", "1366.21"],
["789 745 acres<br />3 196 km²", "3196"],
["{{formatnum:922561}} acres ({{formatnum:3734}} km²)", "3734"],
["112 511 acres<br />455 km²", "455"],
["163 513 ha (1,635 km²)", "1635"],
["{{formatnum:3400}} km²", "3400"],
["{{formatnum:13762}} km²", "13762"],
["244 km²", "244"],
["22 470 ha", "224.7"],
["590 ha", "5.9"],
["1 438 km<sup>2<sup>", "1438"],
["12345", "12345"],
["123.45", "123.45"],
["181,414 ha", "1814.14"],
["12.000 ha", "120"],
["565,69 ha", "5.66"],
["{{unité|5.6569|km|2}}", "5.66"],
["105 447 ha (cœur)", "1054.47", " (cœur)"],
["6.7 km² de terres", "6.7", " de terres"],
["497,3 km² en 2005", "497.3", " en 2005"],
["591 [[km²]]", "591"],
["{{formatnum:458}} km{{2}}", "458"],
["{{unité|0.12|km|2}}", "0.12"],
["12 ha", "0.12"],
].each do |value, result, extra|
expected = "{{unité|#{result}|km|2}}#{extra}"
it "should rewrite #{name} with #{value.inspect} to #{expected.inspect}" do
params = [[name, value], ["foo", "bar"]]
@editor.rewrite_area(params)
params.should == [[name, expected], ["foo", "bar"]]
end
end
[
["8,9 ha", "89000"],
["3 ha", "30000"],
["4,67 ha", "46700"],
["{{unité|0.0467|km|2}}", "46700"],
["{{unité|0.023|km|2}}", "23000"],
["{{unité|34567|m|2}}", "34567"],
].each do |value, result, extra|
expected = "{{unité|#{result}|m|2}}#{extra}"
it "should rewrite #{name} with #{value.inspect} to #{expected.inspect}" do
params = [[name, value], ["foo", "bar"]]
@editor.rewrite_area(params)
params.should == [[name, expected], ["foo", "bar"]]
end
end
[
["17 300 ha (zone centrale)<br/>16 200 ha (zone périphérique)",
"{{unité|173.0|km|2}} (zone centrale)<br/>{{unité|162.0|km|2}} (zone périphérique)"],
["", "<!-- {{unité|...|km|2}} -->"],
["[[km²]]", "<!-- {{unité|...|km|2}} -->"],
["<!-- {{unité|...|km|2}} -->", "<!-- {{unité|...|km|2}} -->"],
#["22 015 km² <br>''parc:'' 5 900 km²<br>''réserve de parc:''16 115 km²",
# "{{unité|22015|km|2}} <br>''parc:'' {{unité|5900|km|2}}<br>''réserve de parc:''{{unité|16115|km|2}}"],
#["{{unité|22015|km|2}} <br>''parc:'' 5 900 km²<br>''réserve de parc:''16 115 km²",
# "{{unité|22015|km|2}} <br>''parc:'' {{unité|5900|km|2}}<br>''réserve de parc:''{{unité|16115|km|2}}"],
].each do |value, result|
it "should rewrite #{name} with #{value.inspect} to #{result.inspect}" do
params = [[name, value], ["foo", "bar"]]
@editor.rewrite_area(params)
params.should == [[name, result], ["foo", "bar"]]
end
end
[
"foo",
"?",
"36 m",
"foo {{formatnum:12}} km²",
"36 hab",
"foo 3 ha",
].each do |value|
it "should raise an ErrorPrevention on rewrite #{name} with #{value.inspect}" do
params = [[name, value]]
@bot.should_receive(:notice).with("Superficie non gérée : <nowiki>#{value}</nowiki>")
@editor.rewrite_area(params)
params.should == [[name, value]]
end
end
end
it "should have default name_changes" do
@editor.name_changes.should == {}
end
it "should use template_name" do
@infobox[:parameters] = [
["foo", "bar"],
]
@editor.template_name = "foo"
@editor.write_infobox(@infobox).should ==
"{{foo\n| foo = bar\n}}"
end
it "should have a default template_name" do
@editor.template_name.should == "Infobox Logiciel"
end
it "should remove parameters" do
@editor.removable_parameters = ["foo", "baz", "bob"]
params = [["foo", "bar"], ["bar", "baz"], ["baz", ""]]
@editor.remove_parameters(params)
params.should == [["bar", "baz"]]
end
end
editor.rb
modifier=begin
Copyright (c) 2007 by Piglop
This file is part of Piglobot.
Piglobot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Piglobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Piglobot. If not, see <http://www.gnu.org/licenses/>.
=end
class Piglobot::Editor
attr_accessor :name_changes, :template_names, :template_name, :filters, :removable_parameters
attr_accessor :infobox, :bot, :wiki, :current_article
def initialize(bot)
@bot = bot
@wiki = bot.wiki
@name_changes = {}
@template_names = []
@template_name = nil
@filters = []
@removable_parameters = []
end
def setup(action = nil)
case action
when "Infobox Logiciel"
@template_names = ["Infobox Logiciel",
"Logiciel simple",
"Logiciel_simple",
"Logiciel",
"Infobox Software",
"Infobox_Software",
]
@name_changes = {
"dernière_version" => "dernière version",
"date_de_dernière_version" => "date de dernière version",
"version_avancée" => "version avancée",
"date_de_version_avancée" => "date de version avancée",
"os" => "environnement",
"site_web" => "site web",
"name" => "nom",
"screenshot" => "image",
"caption" => "description",
"developer" => "développeur",
"latest release version" => "dernière version",
"latest release date" => "date de dernière version",
"latest preview version" => "dernière version avancée",
"latest preview date" => "date de dernière version avancée",
"latest_release_version" => "dernière version",
"latest_release_date" => "date de dernière version",
"latest_preview_version" => "dernière version avancée",
"latest_preview_date" => "date de dernière version avancée",
"platform" => "environnement",
"operating system" => "environnement",
"operating_system" => "environnement",
"language" => "langue",
"genre" => "type",
"license" => "licence",
"website" => "site web",
}
@filters = [
:rename_parameters,
:remove_open_source,
:remove_almost_empty,
:remove_firefox,
:rewrite_dates,
]
@template_name = "Infobox Logiciel"
when "Infobox Aire protégée"
@template_names = [
"Infobox Aire protégée",
"Infobox aire protégée",
"Infobox_aire protégée",
]
@name_changes = {
}
@filters = [
:rename_parameters,
:remove_parameters,
:rewrite_dates,
:rename_image_protected_area,
:rewrite_coordinates,
:rewrite_area,
]
@template_name = "Infobox Aire protégée"
@name_changes = {
"name" => "nom",
"iucn_category" => "catégorie iucn",
"locator_x" => "localisation x",
"locator_y" => "localisation y",
"top_caption" => "légende image",
"location" => "situation",
"localisation" => "situation",
"nearest_city" => "ville proche",
"area" => "superficie",
"established" => "création",
"visitation_num" => "visiteurs",
"visitation_year" => "visiteurs année",
"governing_body" => "administration",
"web_site" => "site web",
"comments" => "remarque",
"caption" => "légende carte",
"base_width" => "largeur carte",
"bot_image" => "image pied",
"bot_caption" => "légende pied",
}
@removable_parameters = ["back_color", "label"]
else
@template_names = []
end
end
def parse_infobox(text)
parser = Piglobot::Parser.new
parser.template_names = @template_names.map { |name|
[name, name[0].chr.swapcase + name[1..-1]]
}.flatten
parser.find_template(text)
end
def rename_parameters(parameters)
changes = @name_changes
parameters.map! { |name, value|
if changes.has_key? name
name = changes[name]
end
[name, value]
}
end
def rename_image_protected_area(parameters)
if @infobox[:name] == "Infobox aire protégée" or @infobox[:name] == "infobox aire protégée" or
@infobox[:name] == "Infobox_aire protégée" or @infobox[:name] == "infobox_aire protégée"
parameters.map! { |name, value|
name = "carte" if name == "image"
[name, value]
}
parameters.map! { |name, value|
name = "image" if name == "top_image"
[name, value]
}
end
end
def rewrite_coordinates(params)
names = %w(lat_degrees lat_minutes lat_seconds lat_direction long_degrees long_minutes long_seconds long_direction)
hash = {}
found_any = false
names.each do |name|
arg = params.find { |n, v| n == name }
if arg
arg = arg.last
arg = nil if arg.empty?
hash[name.intern] = arg
found_any = true
end
end
if found_any
coord = nil
if hash[:lat_degrees] and hash[:lat_minutes] and %w(N S).include?(hash[:lat_direction]) and
hash[:long_degrees] and hash[:long_minutes] and %w(E W).include?(hash[:long_direction])
if hash[:lat_seconds] and hash[:long_seconds]
coord = "{{" + [
"coord",
hash[:lat_degrees],
hash[:lat_minutes],
hash[:lat_seconds],
hash[:lat_direction],
hash[:long_degrees],
hash[:long_minutes],
hash[:long_seconds],
hash[:long_direction],
].join("|") + "}}"
else
coord = "{{" + [
"coord",
hash[:lat_degrees],
hash[:lat_minutes],
hash[:lat_direction],
hash[:long_degrees],
hash[:long_minutes],
hash[:long_direction],
].join("|") + "}}"
end
elsif hash.values.all? { |arg| arg == nil }
coord = "<!-- {{coord|...}} -->"
else
@bot.notice("Coordonnées invalides")
end
if coord
done = false
params.map! { |n, v|
if names.include? n and !done
done = true
["coordonnées", coord]
else
[n, v]
end
}
params.delete_if { |n, v| names.include? n }
end
end
end
def rewrite_area(params)
params.map! do |name, value|
if name == "area" or name == "superficie"
extra = nil
found = true
n = /[\d,.\s]+/
case value
when "", "[[km²]]", "<!-- {{unité|...|km|2}} -->"
value = "<!-- {{unité|...|km|2}} -->"
found = false
when /\A([\d\.]+)\Z/
value = $1
when /\A(#{n}) km<sup>2<\/?sup>\Z/
value = $1
when /\A\{\{formatnum:(#{n})\}\} km²\Z/
value = $1
when /\A#{n} ha \((#{n}) km²\)\Z/
value = $1
when /\A#{n} acres<br \/>(#{n}) km²\Z/
value = $1
when /\A\{\{unité\|(#{n})\|km\|2\}\}\Z/
value = $1.to_f
value = (value * 100).round / 100.0 unless value < 0.1
if value == value.to_i
value = value.to_i
end
when /\A\{\{unité\|(#{n})\|m\|2\}\}\Z/
value = $1.to_f / 1000000
when /\A\{\{unité\|#{n}\|acres\}\}<br \/>\{\{unité\|(#{n})\|km\|2\}\}\Z/
value = $1
when /\A\{\{formatnum:#{n}\}\} acres \(\{\{formatnum:(#{n})\}\} km²\)\Z/
value = $1
when /\A\{\{formatnum:#{n}\}\} acres<br \/>\{\{formatnum:(#{n})\}\} km²\Z/
value = $1
when /\A(#{n}) ha (.+?)<br\/>(#{n}) ha (.+?)\Z/
v1 = $1
t1 = $2
v2 = $3
t2 = $4
v1, v2 = [v1, v2].map { |v|
v = v.tr(" ", "").to_i * 0.01
v = v.to_s
v.gsub!(/,(\d{3})/, "\\1")
v.sub!(/,/, ".")
v.gsub!(/ /, "")
v = "{{unité|#{v}|km|2}}"
}
value = "#{v1} #{t1}<br/>#{v2} #{t2}"
found = false
when /\A(#{n}) ha( .+)?\Z/
extra = $2
value = $1.tr(" ", "").gsub(/,(\d{3})/, "\\1").gsub(/\.(\d{3})/, "\\1").sub(/,/, ".").to_f * 0.01
value = (value * 100).round / 100.0 unless value < 0.1
if value == value.to_i
value = value.to_i
end
when /\A(#{n}) km²( .+)?\Z/
value = $1
extra = $2
when /\A(#{n}) \[\[km²\]\]\Z/
value = $1
when /\A\{\{formatnum:(#{n})\}\} km\{\{2\}\}\Z/
value = $1
else
@bot.notice("Superficie non gérée : <nowiki>#{value}</nowiki>")
found = false
end
if found
if value.to_f < 0.1
value *= 1000000
value = value.to_i if value.to_s =~ /\.0\Z/
unit = "m"
else
unit = "km"
end
value = value.to_s
value.gsub!(/,(\d{3})/, "\\1")
value.sub!(/,/, ".")
value.gsub!(/ /, "")
value = "{{unité|#{value}|#{unit}|2}}"
value << extra if extra
end
end
[name, value]
end
end
def remove_parameters(params)
params.delete_if { |name, value|
@removable_parameters.include? name
}
end
def gsub_value(parameters, param_name, regexp, replacement)
parameters.map! { |name, value|
if param_name == :any or name == param_name
value = value.gsub regexp, replacement
end
[name, value]
}
end
def rewrite_date(value)
if value =~ /\A\{\{(1er) (.+)\}\} \[\[(\d{4})\]\]\Z/ or
value =~ /\A(1)\{\{er\}\} (.+) (\d{4})\Z/ or
value =~ /\A\[\[(.+) (.+)\]\],? \[\[(\d{4})\]\]\Z/ or
value =~ /\A(.+) (.+) (\d{4})\Z/ or
value =~ /\A(.+) \[\[(.+) \(mois\)\|.+\]\] \[\[(\d{4})\]\]\Z/ or
value =~ /\A(.+) \[\[(.+)\]\] \[\[(\d{4})\]\]\Z/ or
value =~ /\A(.+) (.+) \[\[(\d{4})\]\]\Z/
if $3
day = $1
month = $2
year = $3
else
day = ""
month = $1
year = $2
end
if ((day =~ /\A\d+\Z/ and day.size <= 2) or day == "1er" or day.empty?) and
%w(janvier février mars avril mai juin juillet août septembre
octobre novembre décembre).map { |m|
[m, m.capitalize]
}.flatten.include? month
day = "1" if day == "1er"
day.sub! /\A0+/, ""
value = "{{date|#{day}|#{month.downcase}|#{year}}}"
end
end
value
end
def rewrite_dates(parameters)
parameters.map! { |name, value|
value = rewrite_date(value)
[name, value]
}
end
def remove_firefox(parameters)
firefox_text = "<!-- Ne pas changer la capture d'écran, sauf grand changement. Et utilisez la page d'accueil de Wikipédia pour la capture, pas la page de Firefox. Prenez une capture à une taille « normale » (de 800*600 à 1024*780), désactiver les extensions et prenez le thème par défaut. -->"
gsub_value(parameters, "image", /\A(.*)#{Regexp.escape(firefox_text)}(.*)\Z/, '\1\2')
firefox_text = "<!--
* Ne pas changer la capture d'écran, sauf grand changement.
* Utiliser la page d'accueil de Wikipédia pour la capture, pas la page de Firefox.
* Prendre une capture à une taille « normale » (de 800*600 à 1024*780).
* Désactiver les extensions et prendre le thème par défaut.
-->"
gsub_value(parameters, "image", /\A#{Regexp.escape(firefox_text)}(.*)\Z/, '\1')
end
def remove_open_source(parameters)
gsub_value(parameters, "type", /(.+?) +\(\[\[open source\]\]\)$/, '\1')
end
def remove_almost_empty(parameters)
gsub_value(parameters, :any, /\A\?\??\Z/, "")
gsub_value(parameters, :any, /\A-\Z/, "")
gsub_value(parameters, :any, /\A\{\{\{.+\|\}\}\}\Z/, "")
end
def write_infobox(box)
if box[:parameters].empty?
args = ""
else
parameters = box[:parameters]
@infobox = box
@filters.each do |method|
send(method, parameters)
end
args = "\n" + parameters.map { |name, value|
if name.nil?
"| #{value}\n"
elsif name.empty?
"| = #{value}\n"
else
"| #{name} = #{value}\n"
end
}.join
end
"#{box[:before]}{{#{@template_name}#{args}}}#{box[:after]}"
end
end
suivi_portail_informatique.rb
modifierclass SuiviPortailInformatique < Piglobot::Job
def initialize(*args)
super
@name = "[[Projet:Informatique/Suivi]]"
end
def process
pages = @wiki.links("Modèle:Portail informatique")
now = Time.now
date = Piglobot::Tools.write_date(now)
bot = "{{u|Piglobot}}"
text = "<noinclude><small>''Liste des articles référencés par le projet « Informatique ». Mise à jour le #{date} par #{bot}.''</small></noinclude>\n"
text << pages.map { |page| "* [[:#{page}]]\n" }.join
@wiki.post("Projet:Informatique/Suivi", text, "Mise à jour automatique")
end
end
suivi_portail_informatique_spec.rb
modifierrequire 'suivi_portail_informatique'
describe SuiviPortailInformatique do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).and_return(@wiki)
@job = SuiviPortailInformatique.new(@bot)
end
it "should be a job" do
@job.should be_kind_of(Piglobot::Job)
end
it "should have a better name" do
@job.name.should == "[[Projet:Informatique/Suivi]]"
end
it "should retreive links and post them" do
@wiki.should_receive(:links).with("Modèle:Portail informatique").and_return(["foo", "bar"])
time = mock("time")
Time.should_receive(:now).with().and_return(time)
Piglobot::Tools.should_receive(:write_date).with(time).and_return("<date>")
text = ""
text << "<noinclude><small>''"
text << "Liste des articles référencés par le projet « Informatique ». "
text << "Mise à jour le <date> par {{u|Piglobot}}."
text << "''</small></noinclude>\n"
text << "* [[:foo]]\n"
text << "* [[:bar]]\n"
@wiki.should_receive(:post) do |page, content, comment|
page.should == "Projet:Informatique/Suivi"
content.should == text
comment.should == "Mise à jour automatique"
end
@job.process
@job.done?.should == true
end
end
job_lann_spec.rb
modifierrequire 'job_lann'
describe Piglobot, " on clean job" do
it "should know LANN job" do
@wiki = mock("wiki")
Piglobot::Wiki.should_receive(:new).with().and_return(@wiki)
@bot = Piglobot.new
@bot.job_class("LANN").should == LANN
end
it "should know AàC job" do
@wiki = mock("wiki")
Piglobot::Wiki.should_receive(:new).with().and_return(@wiki)
@bot = Piglobot.new
@bot.job_class("AàC").should == AaC
end
end
describe "Page cleaner", :shared => true do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@parser = mock("parser")
@bot.should_receive(:wiki) { @wiki }
end
it "should be a job" do
@job.should be_kind_of(Piglobot::Job)
end
it "should have a name" do
@job.name.should == @name
end
it "should have a category" do
@job.category.should == @category
end
it "should have a title" do
@job.title.should == @title
end
it "should have a done_model" do
@job.done_model.should == @done_model
end
it "should have a empty comment" do
@job.empty_comment.should == @empty_comment
end
[
nil,
{ :pages => [] },
].each do |initial_data|
it "should get pages when data is #{initial_data.inspect}" do
@job.data = initial_data
@job.should_receive(:get_pages).with()
@job.should_receive(:remove_bad_names).with()
@job.should_receive(:remove_cited).with()
@job.should_receive(:remove_already_done).with() do
@job.pages = ["foo", "bar", "baz"]
end
@job.should_receive(:notice).with("3 pages à traiter")
@job.process
@job.data[:pages].should == ["foo", "bar", "baz"]
@job.data[:done].should == []
@job.done?.should == false
end
end
it "should process first page if pages filled" do
@job.data = { :pages => ["foo", "bar"] }
@job.should_receive(:process_page).with("foo")
@job.process
@job.data.should == { :pages => ["bar"], :done => [] }
@job.done?.should == false
end
it "should be done when pages are empty" do
@job.data = { :pages => ["bar"] }
@job.should_receive(:process_page).with("bar")
@job.should_receive(:notice).with("Aucune page blanchie")
@job.process
@job.data.should == { :pages => [], :done => [] }
@job.done?.should == true
end
it "should notice pages done" do
@job.data = { :pages => ["bar"], :done => ["foo", "Discussion foo", "baz"] }
@job.should_receive(:process_page).with("bar")
@job.should_receive(:notice).with("Pages blanchies : [[foo]], [[Discussion foo]], [[baz]]")
@job.process
@job.data.should == { :pages => [], :done => [] }
@job.done?.should == true
end
it "should get pages" do
items = ["Foo", "Bar", "Baz:Baz"]
@wiki.should_receive(:category).with(@category).and_return(items)
@job.should_receive(:log).with("3 articles dans la catégorie")
@job.should_receive(:notice).with("3 pages dans la [[:Catégorie:#@category]]")
@job.get_pages
@job.pages.should == items
end
it "should remove bad names" do
@job.pages = [
"#{@title}Foo",
"Foo",
"Liste des articles non neutres/Foo",
"#{@title}Baz",
"#{@title}",
"#{@title}Bar",
"Modèle:Wikipédia:Liste des articles non neutres/Foo",
]
@job.should_receive(:log).with("3 articles avec un nom valide")
@job.should_receive(:notice).with("3 pages avec un nom valide")
@job.remove_bad_names
@job.pages.should == [
"#{@title}Foo",
"#{@title}Baz",
"#{@title}Bar",
]
end
def parser_should_return(content, links)
parser = mock("parser")
Piglobot::Parser.should_receive(:new).with().and_return(parser)
parser.should_receive(:internal_links).with(content).and_return(links)
end
it "should remove already done" do
@job.pages = ["Foo", "Bar", "Baz"]
@wiki.should_receive(:links).with(@done_model).and_return(["Foo", "bar", "Baz"])
@job.should_receive(:log).with("1 articles non traités")
@job.should_receive(:notice).with("1 pages ne contenant pas le [[#{@done_model}]]")
@job.remove_already_done
@job.pages.should == ["Bar"]
end
def time_travel(*now)
Time.should_receive(:now).and_return(Time.local(*now))
end
def next_history_date(page, *now)
@wiki.should_receive(:history).with(page, 1).and_return([
{ :author => "author2", :date => Time.local(*now), :oldid => "oldid2" }
])
end
def next_history_empty(page)
@wiki.should_receive(:history).with(page, 1).and_return([])
end
it "should detect active page when page history is recent" do
time_travel(2007, 10, 3, 23, 56, 12)
next_history_date("foo", 2007, 9, 26, 23, 57, 0)
@job.active?("foo").should == true
end
it "should detect active page when page history is old but talk history is recent" do
time_travel(2007, 10, 3, 23, 56, 12)
next_history_date("foo", 2007, 9, 26, 23, 56, 0)
next_history_date("Discussion foo", 2007, 9, 26, 23, 57, 0)
@job.active?("foo").should == true
end
it "should detect inactive page when both histories are old" do
time_travel(2007, 10, 3, 23, 56, 12)
next_history_date("foo", 2007, 9, 26, 23, 56, 0)
next_history_date("Discussion foo", 2007, 9, 25, 23, 57, 0)
@job.active?("foo").should == false
end
it "should detect inactive page when page history is old and no talk page" do
time_travel(2007, 10, 3, 23, 56, 12)
next_history_date("foo", 2007, 9, 26, 23, 56, 0)
next_history_empty("Discussion foo")
@job.active?("foo").should == false
end
it "should raise an error if page history is empty (but shouldn't happend)" do
time_travel(2007, 10, 3, 23, 56, 12)
next_history_empty("foo")
lambda { @job.active?("foo") }.should raise_error(RuntimeError, "La page n'existe pas")
end
it "should not empty page if active" do
@job.should_receive(:active?).with("foo").and_return(true)
@job.should_not_receive(:notice).with("[[foo]] non blanchie car active")
@job.should_receive(:log).with("[[foo]] ignorée car active")
@job.should_not_receive(:empty_page)
@job.process_page("foo")
@job.changed?.should == false
end
it "should not empty page on error" do
e = RuntimeError.new("error")
e.set_backtrace(["foo", "bar"])
@job.should_receive(:active?).with("foo").and_raise(e)
@job.should_receive(:notice).with("[[foo]] non blanchie car une erreur s'est produite : error")
@job.should_receive(:log).with("Erreur pour [[foo]] : error\nfoo\nbar")
@job.should_not_receive(:empty_page)
@job.process_page("foo")
@job.changed?.should == true
end
it "should empty page if inactive" do
@job.should_receive(:active?).with("foo").and_return(false)
@job.should_receive(:empty_page).with("foo").and_return(true)
@job.should_receive(:empty_talk_page).with("foo").and_return(true)
@job.process_page("foo")
@job.changed?.should == true
@job.data[:done].should == ["foo", "Discussion foo"]
end
it "should append emptied pages to done" do
@job.data = { :done => ["bar"] }
@job.should_receive(:active?).and_return(false)
@job.should_receive(:empty_page).with("foo").and_return(true)
@job.should_receive(:empty_talk_page).with("foo").and_return(false)
@job.process_page("foo")
@job.data[:done].should == ["bar", "foo"]
end
it "should empty talk page" do
page = "Wikipédia:Liste des articles non neutres/Bar"
@wiki.should_receive(:history).with("Discussion " + page, 1).and_return([
{ :author => "author2", :date => Time.now, :oldid => "123456" }
])
content = "{{Blanchiment de courtoisie}}"
comment = @empty_comment
@job.should_receive(:log).with("Blanchiment de [[Discussion #{page}]]")
@wiki.should_receive(:post).with("Discussion " + page, content, comment)
@job.empty_talk_page(page).should == true
end
it "should not empty inexistant talk page" do
page = "Wikipédia:Liste des articles non neutres/Bar"
@wiki.should_receive(:history).with("Discussion " + page, 1).and_return([])
@job.should_receive(:log).with("Blanchiment inutile de [[Discussion #{page}]]")
@job.empty_talk_page(page).should == false
end
end
describe LANN do
it_should_behave_like "Page cleaner"
before do
@job = LANN.new(@bot)
@name = "[[WP:LANN]]"
@category = "Wikipédia:Archives Articles non neutres"
@title = "Wikipédia:Liste des articles non neutres/"
@done_model = "Modèle:Archive LANN"
@empty_comment = "[[Utilisateur:Piglobot/Travail#Blanchiment LANN|Blanchiment automatique de courtoisie]]"
end
it "should remove cited" do
links = ["/Foo", "Bar"]
@job.pages = ["Wikipédia:Liste des articles non neutres/Foo", "Wikipédia:Liste des articles non neutres/Bar"]
@wiki.should_receive(:get).with("Wikipédia:Liste des articles non neutres").and_return("content")
parser_should_return("content", links)
@job.should_receive(:log).with("1 articles non cités")
@job.should_receive(:notice).with("1 pages non mentionnées dans [[WP:LANN]]")
@job.remove_cited
@job.pages.should == ["Wikipédia:Liste des articles non neutres/Bar"]
end
it "should raise an error if none are cited" do
@job.pages = ["Foo", "Bar"]
@wiki.should_receive(:get).with("Wikipédia:Liste des articles non neutres").and_return("content")
parser_should_return("content", ["Baz", "Bob"])
lambda { @job.remove_cited }.should raise_error(Piglobot::ErrorPrevention, "Aucune page de la catégorie n'est cité dans [[WP:LANN]]")
end
it "should empty page with oldid" do
page = "Wikipédia:Liste des articles non neutres/Bar"
@wiki.should_receive(:history).with(page, 1).and_return([
{ :author => "author2", :date => Time.now, :oldid => "123456" }
])
content = "{{subst:Blanchiment LANN | article = [[:Bar]] | oldid = 123456 }}"
comment = "[[Utilisateur:Piglobot/Travail#Blanchiment LANN|Blanchiment automatique de courtoisie]]"
@job.should_receive(:log).with("Blanchiment de [[#{page}]]")
@wiki.should_receive(:post).with(page, content, comment)
@job.empty_page(page).should == true
end
end
describe AaC do
it_should_behave_like "Page cleaner"
before do
@job = AaC.new(@bot)
@name = "[[WP:AàC]]"
@category = "Archives Appel à commentaires"
@title = "Wikipédia:Appel à commentaires/"
@done_model = "Modèle:Blanchiment de courtoisie"
@empty_comment = "[[Utilisateur:Piglobot/Travail#Blanchiment AàC|Blanchiment automatique de courtoisie]]"
end
it "should remove cited" do
parser = mock("parser")
Piglobot::Parser.should_receive(:new).with().and_return(parser)
links1 = ["page 1", "page 23"]
links2 = ["page 5", "page 3"]
@job.pages = ["page 1", "page 2", "page 3", "page 4"]
@wiki.should_receive(:get).with("Wikipédia:Appel à commentaires/Article").and_return("content")
parser.should_receive(:internal_links).with("content").and_return(links1)
@wiki.should_receive(:get).with("Wikipédia:Appel à commentaires/Utilisateur").and_return("content2")
parser.should_receive(:internal_links).with("content2").and_return(links2)
@job.should_receive(:log).with("2 articles non cités")
@job.should_receive(:notice).with("2 pages non mentionnées dans les pages de maintenance")
@job.remove_cited
@job.pages.should == ["page 2", "page 4"]
end
it "should empty page" do
page = "Wikipédia:Appel à commentaire/Utilisateur/Bar"
content = "{{subst:Blanchiment Appel à Commentaire}}"
comment = "[[Utilisateur:Piglobot/Travail#Blanchiment AàC|Blanchiment automatique de courtoisie]]"
@job.should_receive(:log).with("Blanchiment de [[#{page}]]")
@wiki.should_receive(:post).with(page, content, comment)
@job.empty_page(page).should == true
end
end
job_lann.rb
modifierclass LANN < Piglobot::Job
attr_accessor :pages, :category, :title, :cite_pages, :done_model, :empty_comment
def initialize(*args)
super
@name = "[[WP:LANN]]"
@category = "Wikipédia:Archives Articles non neutres"
@title = "Wikipédia:Liste des articles non neutres/"
@done_model = "Modèle:Archive LANN"
@empty_comment = "[[Utilisateur:Piglobot/Travail#Blanchiment LANN|Blanchiment automatique de courtoisie]]"
end
def done?
@done
end
def process
if @data.nil? or @data[:pages].empty?
get_pages
remove_bad_names
remove_cited
remove_already_done
notice("#{@pages.size} pages à traiter")
else
@pages = @data[:pages]
page = @pages.shift
process_page page
end
if @pages.empty?
@done = true
if @data and @data[:done] and !@data[:done].empty?
pages = @data[:done].map { |page| "[[#{page}]]" }.join(", ")
notice("Pages blanchies : " + pages)
@data[:done] = []
else
notice("Aucune page blanchie")
end
else
@done = false
end
done_pages = []
done_pages = (@data[:done] || []) if @data
@data = { :pages => @pages, :done => done_pages }
end
def get_pages
@pages = @wiki.category(@category)
log "#{@pages.size} articles dans la catégorie"
notice("#{@pages.size} pages dans la [[:Catégorie:#@category]]")
end
def remove_bad_names
@pages.delete_if { |name| name !~ /\A#{Regexp.escape @title}./ }
log "#{pages.size} articles avec un nom valide"
notice("#{@pages.size} pages avec un nom valide")
end
def remove_cited
lann = @wiki.get("Wikipédia:Liste des articles non neutres")
parser = Piglobot::Parser.new
links = parser.internal_links(lann)
links.map! do |link|
if link =~ %r{\A/}
"Wikipédia:Liste des articles non neutres" + link
else
nil
end
end
old_pages = @pages
@pages -= links
if @pages.size == old_pages.size
raise Piglobot::ErrorPrevention, "Aucune page de la catégorie n'est cité dans [[WP:LANN]]"
end
log "#{pages.size} articles non cités"
notice("#{@pages.size} pages non mentionnées dans [[WP:LANN]]")
end
def remove_already_done
links = @wiki.links(@done_model)
@pages -= links
log "#{pages.size} articles non traités"
notice("#{@pages.size} pages ne contenant pas le [[#{@done_model}]]")
end
def active?(page)
now = Time.now
limit = now - 7 * 24 * 3600
history = @wiki.history(page, 1)
raise "La page n'existe pas" if history.empty?
date = history.first[:date]
if date > limit
true
else
talk_history = @wiki.history("Discussion " + page, 1)
return false if talk_history.empty?
talk_date = talk_history.first[:date]
talk_date > limit
end
end
def process_page(page)
begin
if active? page
log("[[#{page}]] ignorée car active")
@changed = false
else
done = []
done = @data[:done] if @data and @data[:done]
if empty_page(page)
done << page
end
if empty_talk_page(page)
done << "Discussion #{page}"
end
@data ||= {}
@data[:done] = done
@changed = true
end
rescue => e
log("Erreur pour [[#{page}]] : #{e.message}\n#{e.backtrace.join("\n")}")
notice("[[#{page}]] non blanchie car une erreur s'est produite : #{e.message}")
@changed = true
end
end
def empty_page(page)
log("Blanchiment de [[#{page}]]")
history = @wiki.history(page, 1)
oldid = history.first[:oldid]
article = page.sub(%r{\A.+?/}, "")
content = "{{subst:Blanchiment LANN | article = [[:#{article}]] | oldid = #{oldid} }}"
comment = "[[Utilisateur:Piglobot/Travail#Blanchiment LANN|Blanchiment automatique de courtoisie]]"
@wiki.post(page, content, comment)
true
end
def empty_talk_page(page)
talk_page = "Discussion " + page
history = @wiki.history(talk_page, 1)
if history.empty?
log("Blanchiment inutile de [[#{talk_page}]]")
false
else
log("Blanchiment de [[#{talk_page}]]")
oldid = history.first[:oldid]
content = "{{Blanchiment de courtoisie}}"
comment = @empty_comment
@wiki.post(talk_page, content, comment)
true
end
end
end
class AaC < LANN
def initialize(*args)
super
@name = "[[WP:AàC]]"
@category = "Archives Appel à commentaires"
@title = "Wikipédia:Appel à commentaires/"
@done_model = "Modèle:Blanchiment de courtoisie"
@empty_comment = "[[Utilisateur:Piglobot/Travail#Blanchiment AàC|Blanchiment automatique de courtoisie]]"
end
def remove_cited
parser = Piglobot::Parser.new
content = @wiki.get("Wikipédia:Appel à commentaires/Article")
links = parser.internal_links(content)
content = @wiki.get("Wikipédia:Appel à commentaires/Utilisateur")
links += parser.internal_links(content)
old_pages = @pages
@pages -= links
log "#{pages.size} articles non cités"
notice("#{@pages.size} pages non mentionnées dans les pages de maintenance")
end
def empty_page(page)
log("Blanchiment de [[#{page}]]")
content = "{{subst:Blanchiment Appel à Commentaire}}"
comment = "[[Utilisateur:Piglobot/Travail#Blanchiment AàC|Blanchiment automatique de courtoisie]]"
@wiki.post(page, content, comment)
true
end
end
homonym_prevention.rb
modifierclass Piglobot
class HomonymPrevention < Job
def data_id
"Homonymes"
end
def done?
false
end
def process
changes = false
data = @data
data ||= {}
china = data["Chine"] || {}
china = {} if china.is_a?(Array)
last = china["Last"] || {}
new = china["New"] || []
if last.empty?
last = @wiki.links("Chine")
Piglobot::Tools.log("#{last.size} liens vers la page d'homonymie [[Chine]]")
else
current = @wiki.links("Chine")
new.delete_if do |old_new|
if current.include? old_new
false
else
Piglobot::Tools.log("Le lien vers [[Chine]] dans [[#{old_new}]] a été supprimé avant d'être traité")
true
end
end
current_new = current - last
last = current
current_new.each do |new_name|
Piglobot::Tools.log("Un lien vers [[Chine]] a été ajouté dans [[#{new_name}]]")
end
new += current_new
end
china["Last"] = last
china["New"] = new if new
data["Chine"] = china
@changed = changes
@data = data
end
end
end
homonym_prevention_spec.rb
modifierrequire 'homonym_prevention'
describe Piglobot, " on HomonymPrevention job" do
it "should know job" do
@wiki = mock("wiki")
Piglobot::Wiki.should_receive(:new).with().and_return(@wiki)
@bot = Piglobot.new
@bot.job_class("Homonymes").should == Piglobot::HomonymPrevention
end
end
describe Piglobot::HomonymPrevention do
before do
@bot = mock("bot")
@wiki = mock("wiki")
@bot.should_receive(:wiki).with().and_return(@wiki)
@job = Piglobot::HomonymPrevention.new(@bot)
end
it "should never be done" do
@job.done?.should == false
end
it "should find new links" do
@job.data = { "Chine" => {"Last" => ["a", "b", "c"], "New" => [] }}
@wiki.should_receive(:links, "Chine").and_return(["a", "b", "d", "c", "e"])
Piglobot::Tools.should_receive(:log).with("Un lien vers [[Chine]] a été ajouté dans [[d]]")
Piglobot::Tools.should_receive(:log).with("Un lien vers [[Chine]] a été ajouté dans [[e]]")
@job.process
@job.changed?.should == false
@job.data.should == { "Chine" => {"Last" => ["a", "b", "d", "c", "e"], "New" => ["d", "e"] }}
end
it "should keep new links" do
@job.data = { "Chine" => {"Last" => ["a", "b"], "New" => ["b"] }}
@wiki.should_receive(:links, "Chine").and_return(["a", "b"])
Piglobot::Tools.should_not_receive(:log)
@job.process
@job.data.should == { "Chine" => {"Last" => ["a", "b"], "New" => ["b"] }}
end
it "should ignore removed pending links" do
@job.data = { "Chine" => {"Last" => ["a", "b"], "New" => ["b"] }}
@wiki.should_receive(:links, "Chine").and_return(["a"])
Piglobot::Tools.should_receive(:log).with("Le lien vers [[Chine]] dans [[b]] a été supprimé avant d'être traité")
@job.process
@job.data.should == { "Chine" => {"Last" => ["a"], "New" => [] }}
end
end
tools_spec.rb
modifierrequire 'piglobot'
require 'helper'
=begin
Copyright (c) 2007 by Piglop
This file is part of Piglobot.
Piglobot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Piglobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Piglobot. If not, see <http://www.gnu.org/licenses/>.
=end
describe Piglobot::Tools do
it "should convert file to wiki" do
result = Piglobot::Tools.file_to_wiki("filename", "file\ncontent", "lang")
result.should == ([
"== filename ==",
'<syntaxhighlight lang="lang">',
'file',
'content',
'<' + '/source>',
'',
].map { |line| line + "\n" }.join)
end
it "should use Kernel.puts and append to piglobot.log on log" do
time = mock("time")
Time.should_receive(:now).with().and_return(time)
time.should_receive(:strftime).with("%Y-%m-%d %H:%M:%S").and_return("time string")
log_line = "time string: text"
Kernel.should_receive(:puts).with(log_line)
f = mock("file")
File.should_receive(:open).with("piglobot.log", "a").and_yield(f)
f.should_receive(:puts).with(log_line).once
Piglobot::Tools.log("text")
end
[
["25 octobre 2007 à 17:17", Time.local(2007, 10, 25, 17, 17, 0)],
["25 août 2006 à 09:16", Time.local(2006, 8, 25, 9, 16, 0)],
["12 juillet 2006 à 08:40", Time.local(2006, 7, 12, 8, 40, 0)],
["10 novembre 2002 à 19:12", Time.local(2002, 11, 10, 19, 12, 0)],
["1 décembre 2002 à 11:39", Time.local(2002, 12, 1, 11, 39, 0)],
].each do |text, time|
it "should parse time #{text.inspect}" do
Piglobot::Tools.parse_time(text).should == time
end
end
[
"décembre 2002 à 11:39",
"10 novembre 2002 19:12",
"10 plop 2002 à 19:12",
"10 octobre 2002",
"foo 1 décembre 2002 à 11:39",
"1 décembre 2002 à 11:39 foo",
].each do |text|
it "should not parse time #{text.inspect}" do
lambda { Piglobot::Tools.parse_time(text) }.should raise_error(ArgumentError, "Invalid time: #{text.inspect}")
end
end
months = %w(janvier février mars avril mai juin juillet août septembre octobre novembre décembre)
months.each_with_index do |month, i|
expected_month = i + 1
it "should parse month #{month.inspect}" do
Piglobot::Tools.parse_time("25 #{month} 2007 à 17:17").should ==
Time.local(2007, expected_month, 25, 17, 17, 0)
end
end
months.each_with_index do |month, i|
time = Time.local(2007, i + 1, 3, 11, 23, 3)
Piglobot::Tools.write_date(time).should == "{{date|3|#{month}|2007}}"
end
end
tools.rb
modifier=begin
Copyright (c) 2007 by Piglop
This file is part of Piglobot.
Piglobot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Piglobot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Piglobot. If not, see <http://www.gnu.org/licenses/>.
=end
module Piglobot::Tools
module_function
def file_to_wiki(filename, content, lang)
result = ""
result << "== #{filename} ==\n"
result << "<syntaxhighlight lang=\"#{lang}\">\n"
result << content + "\n"
result << '<' + "/source>\n"
result << "\n"
result
end
def log(text)
time = Time.now.strftime("%Y-%m-%d %H:%M:%S")
line = "#{time}: #{text}"
Kernel.puts line
File.open("piglobot.log", "a") { |f|
f.puts line
}
end
MONTHS = %w(janvier février mars avril mai juin
juillet août septembre octobre novembre décembre)
def parse_time(text)
if text =~ /\A(\d+) (\S+) (\d{4}) à (\d{2}):(\d{2})\Z/
month = MONTHS.index($2)
if month
return Time.local($3.to_i, month + 1, $1.to_i, $4.to_i, $5.to_i, 0)
end
end
raise ArgumentError, "Invalid time: #{text.inspect}"
end
def write_date(time)
day = time.day
month = MONTHS[time.month - 1]
year = time.year
"{{date|#{day}|#{month}|#{year}}}"
end
end