541 lines
11 KiB
Plaintext
541 lines
11 KiB
Plaintext
Der [[Date Determinator]] ist ein MediaWiki-Bot, welcher Terminfindungen vereinfachen sollen. Die Eingabe passiert als YAML, die Ausgabe kann vom Bot periodisch als hybsche Tabelle erstellt werden.
|
|
|
|
|
|
=Beispiele=
|
|
|
|
==CodingNight zu dritt==
|
|
|
|
'''Eingabe:'''
|
|
BEGIN DATA "Test"
|
|
Cool Hacker:
|
|
24.5.: nein
|
|
23.5.: ja
|
|
Astro:
|
|
23.5.: ja
|
|
22.5.: weiss noch nicht
|
|
1337 Cracker:
|
|
24.5.: jo
|
|
23.5.: yezz!
|
|
END DATA
|
|
|
|
'''Ausgabe:'''
|
|
<!-- BEGIN TABLE "Test" -->
|
|
{| border="1" cellpadding="2" cellspacing="0" style="border-collapse:collapse;"
|
|
|-
|
|
!
|
|
!22.5.
|
|
!23.5.
|
|
!24.5.
|
|
|-
|
|
|[[User:Cool Hacker|Cool Hacker]]
|
|
| |
|
|
| bgcolor="#7fff7f" | ja
|
|
| bgcolor="#ff7f7f" | nein
|
|
|-
|
|
|[[User:1337 Cracker|1337 Cracker]]
|
|
| |
|
|
| bgcolor="#7fff7f" | yezz!
|
|
| bgcolor="#7fff7f" | jo
|
|
|-
|
|
|[[User:Astro|Astro]]
|
|
| bgcolor="#bfbfbf" | weiss noch nicht
|
|
| bgcolor="#7fff7f" | ja
|
|
| |
|
|
|-
|
|
|'''sum(ja)'''
|
|
|'''0'''
|
|
|'''3'''
|
|
|'''1'''
|
|
|}
|
|
<!-- END TABLE -->
|
|
|
|
==Realitätsabgleich 2==
|
|
|
|
<!-- BEGIN DATA "R2"
|
|
Astro:
|
|
16.6.: j
|
|
17.6.: j
|
|
18.6. N: j
|
|
18.6. A: n
|
|
19.6. N: j
|
|
19.6. A: j
|
|
20.6.: j
|
|
21.6.: n
|
|
22.6.: n
|
|
23.6.: j
|
|
24.6.: j
|
|
25.6.: j
|
|
nulli:
|
|
16.6.: n
|
|
17.6.: j
|
|
18.6. N: j
|
|
18.6. A: j
|
|
19.6. N: j
|
|
19.6. A: n
|
|
20.6.: n
|
|
21.6.: n
|
|
22.6.: n
|
|
23.6.: n
|
|
24.6.: n
|
|
25.6.: n
|
|
Caldrin:
|
|
16.6.: n
|
|
17.6.: j
|
|
18.6. N: n
|
|
18.6. A: n
|
|
19.6. N: j
|
|
19.6. A: n
|
|
20.6.: n
|
|
21.6.: n
|
|
22.6.: n
|
|
23.6.: n
|
|
24.6.: j
|
|
25.6.: n
|
|
matthias:
|
|
16.6.: j
|
|
17.6.: n
|
|
18.6. N: j
|
|
18.6. A: j
|
|
19.6. N: j
|
|
19.6. A: j
|
|
20.6.: j
|
|
21.6.: j
|
|
22.6.: n
|
|
23.6.: j
|
|
24.6.: j
|
|
25.6.: j
|
|
fukami:
|
|
16.6.: n
|
|
17.6.: j
|
|
18.6. N: n
|
|
18.6. A: n
|
|
19.6. N: n
|
|
19.6. A: n
|
|
20.6.: n
|
|
21.6.: n
|
|
22.6.: n
|
|
23.6.: n
|
|
24.6.: n
|
|
25.6.: n
|
|
toidinamai:
|
|
16.6.: j
|
|
17.6.: j
|
|
18.6. N: j
|
|
18.6. A: j
|
|
19.6. N: j
|
|
19.6. A: j
|
|
20.6.: j
|
|
21.6.: j
|
|
22.6.: j
|
|
23.6.: j
|
|
24.6.: j
|
|
25.6.: j
|
|
|
|
END DATA -->
|
|
|
|
<!-- BEGIN TABLE "R2" -->
|
|
{| border="1" cellpadding="2" cellspacing="0" style="border-collapse:collapse;"
|
|
|-
|
|
!
|
|
!16.6.
|
|
!17.6.
|
|
!18.6. A
|
|
!18.6. N
|
|
!19.6. N
|
|
!19.6. A
|
|
!20.6.
|
|
!21.6.
|
|
!22.6.
|
|
!23.6.
|
|
!24.6.
|
|
!25.6.
|
|
|-
|
|
|[[User:matthias|matthias]]
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
|-
|
|
|[[User:fukami|fukami]]
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
|-
|
|
|[[User:Caldrin|Caldrin]]
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
|-
|
|
|[[User:nulli|nulli]]
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
|-
|
|
|[[User:toidinamai|toidinamai]]
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
|-
|
|
|[[User:Astro|Astro]]
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#ff7f7f" | n
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
| bgcolor="#7fff7f" | j
|
|
|-
|
|
|'''sum(ja)'''
|
|
|'''3'''
|
|
|'''5'''
|
|
|'''3'''
|
|
|'''4'''
|
|
|'''5'''
|
|
|'''3'''
|
|
|'''3'''
|
|
|'''2'''
|
|
|'''1'''
|
|
|'''3'''
|
|
|'''4'''
|
|
|'''3'''
|
|
|}
|
|
<!-- END TABLE -->
|
|
|
|
=Source=
|
|
|
|
Open-source, yada-yada!
|
|
|
|
==date_determinator.rb==
|
|
<pre>
|
|
WIKI_USER = 'AstRobot'
|
|
WIKI_PASSWORD = '...'
|
|
PAGE = 'Benutzer:Astro/Date_Determinator'
|
|
WIKI = 'http://wiki.c3d2.de/wikipgedia'
|
|
HTTP_USER = 'eris'
|
|
HTTP_PASSWORD = '...'
|
|
|
|
require 'yaml'
|
|
require 'mediawiki'
|
|
|
|
include MediaWiki
|
|
|
|
class DateUser
|
|
attr_reader :name
|
|
|
|
def initialize(name, hsh)
|
|
@name = name
|
|
@hsh = hsh
|
|
end
|
|
|
|
def dates
|
|
@hsh.keys
|
|
end
|
|
|
|
def day(d)
|
|
@hsh[d]
|
|
end
|
|
|
|
def day_class(d)
|
|
if day(d) == nil
|
|
nil
|
|
elsif day(d) =~ /^[yj]/i
|
|
true
|
|
elsif day(d) =~ /^n/i
|
|
false
|
|
else
|
|
""
|
|
end
|
|
end
|
|
|
|
def day_style(d)
|
|
case day_class(d)
|
|
when nil then ""
|
|
when true then 'bgcolor="#7fff7f"'
|
|
when false then 'bgcolor="#ff7f7f"'
|
|
else 'bgcolor="#bfbfbf"'
|
|
end
|
|
end
|
|
end
|
|
|
|
class DateData
|
|
def initialize(yaml)
|
|
@users = []
|
|
@error = nil
|
|
begin
|
|
YAML::load(yaml).each { |user,dates|
|
|
@users << DateUser.new(user, dates)
|
|
}
|
|
rescue Exception => e
|
|
puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
|
@error = e.to_s
|
|
end
|
|
end
|
|
|
|
def table
|
|
return @error if @error
|
|
|
|
s = ''
|
|
|
|
##
|
|
# Collect dates
|
|
##
|
|
dates = []
|
|
dates_yes = {}
|
|
@users.each { |user| dates += user.dates }
|
|
|
|
##
|
|
# Sort dates
|
|
##
|
|
dates.uniq!
|
|
dates.sort! { |a,b|
|
|
if a =~ /^(\d+)\.(\d+)\./
|
|
a_day = $1
|
|
a_month = $2
|
|
if b =~ /^(\d+)\.(\d+)\./
|
|
b_day = $1
|
|
b_month = $2
|
|
if a_month.to_i == b_month.to_i
|
|
a_day.to_i <=> b_day.to_i
|
|
else
|
|
a_month.to_i <=> b_month.to_i
|
|
end
|
|
else
|
|
a <=> b
|
|
end
|
|
else
|
|
a <=> b
|
|
end
|
|
}
|
|
|
|
##
|
|
# Construct header
|
|
##
|
|
s += "{| border=\"1\" cellpadding=\"2\" cellspacing=\"0\" style=\"border-collapse:collapse;\"\n|-\n! \n"
|
|
dates.each { |date|
|
|
s += "!#{date}\n"
|
|
dates_yes[date] = 0
|
|
}
|
|
|
|
##
|
|
# Construct rows
|
|
##
|
|
@users.each { |user|
|
|
s += "|-\n|[[User:#{user.name}|#{user.name}]]\n"
|
|
dates.each { |date|
|
|
s += "| #{user.day_style(date)} | #{user.day(date)}\n"
|
|
dates_yes[date] += 1 if user.day_class(date) == true
|
|
}
|
|
}
|
|
|
|
##
|
|
# Build summary
|
|
##
|
|
s += "|-\n|'''sum(ja)'''\n"
|
|
dates.each { |date|
|
|
s += "|'''#{dates_yes[date]}'''\n"
|
|
}
|
|
s += "|}"
|
|
end
|
|
end
|
|
|
|
|
|
|
|
wiki = Wiki.new(WIKI, HTTP_USER, HTTP_PASSWORD)
|
|
wiki.login(WIKI_USER, WIKI_PASSWORD)
|
|
page = wiki.article(PAGE)
|
|
|
|
datasets = {}
|
|
current_data_name = nil
|
|
current_data_yaml = ''
|
|
page.text.split(/\n/).each { |line|
|
|
if line =~ /BEGIN DATA "(.+?)"/
|
|
current_data_name = $1
|
|
current_data_yaml = ''
|
|
elsif line =~ /END DATA/ and current_data_name
|
|
datasets[current_data_name] = DateData.new(current_data_yaml)
|
|
current_data_name = nil
|
|
current_data_yaml = ''
|
|
elsif current_data_name
|
|
current_data_yaml += "#{line}\n"
|
|
end
|
|
}
|
|
|
|
|
|
text_old = page.text.dup
|
|
|
|
signature = /(<!-- BEGIN TABLE ")(.+?)(" -->)(.+?)(<!-- END TABLE -->)/m
|
|
page.text.gsub!(signature) { |part|
|
|
begin1,name,begin2,obsolete,end1 = part.match(signature).to_a[1..-1]
|
|
table = datasets[name] ? datasets[name].table : "DATA #{name} not found!"
|
|
"#{begin1}#{name}#{begin2}\n#{table}\n#{end1}"
|
|
}
|
|
|
|
page.submit('Date Determinator run') if page.text != text_old
|
|
</pre>
|
|
|
|
==mediawiki.rb==
|
|
<pre>
|
|
require 'http-access2'
|
|
require 'cgi'
|
|
require 'rexml/document'
|
|
|
|
module MediaWiki
|
|
class Wiki
|
|
##
|
|
# HTTPAccess2 object, must be accessible by the Wiki's
|
|
# Article children
|
|
attr_accessor :http
|
|
|
|
def initialize(url, auth_name=nil, auth_password=nil)
|
|
@url = (url =~ /\/$/) ? url : "#{url}/"
|
|
|
|
@http = HTTPAccess2::Client.new(nil, 'Ruby-MediaWiki::Wiki/0.1')
|
|
|
|
add_auth(url, auth_name, auth_password) if auth_name and auth_password
|
|
end
|
|
|
|
def add_auth(uri, auth_name, auth_password)
|
|
@http.set_basic_auth(uri, auth_name, auth_password)
|
|
end
|
|
|
|
def login(username, password)
|
|
postdata = "wpName=#{CGI::escape(username)}&wpPassword=#{CGI::escape(password)}&wpLoginattempt="
|
|
result = @http.post_content(article_url('Special:Userlogin'), postdata)
|
|
if result =~ /<p class='error'>/
|
|
raise "Unable to authenticate as #{username}"
|
|
end
|
|
end
|
|
|
|
##
|
|
# Return a new article with the given name
|
|
# name:: [String] Article name
|
|
# result:: [Article]
|
|
def article(name)
|
|
Article.new(self, name)
|
|
end
|
|
|
|
##
|
|
# Return
|
|
def article_url(name)
|
|
"#{@url}index.php?title=#{CGI::escape(name)}"
|
|
end
|
|
|
|
##
|
|
# Return a hash of special pages:
|
|
# 'Link title' => 'Article name'
|
|
def specialpages
|
|
result = {}
|
|
Article.new(self, 'Special:Specialpages', false).xhtml.each_element('ul/li/a') { |a|
|
|
result[a.attributes['title']] = a.text
|
|
}
|
|
result
|
|
end
|
|
end
|
|
|
|
class Article
|
|
attr_accessor :name, :text
|
|
|
|
def initialize(wiki, name, load_text=true)
|
|
@wiki = wiki
|
|
@name = name
|
|
|
|
@text = nil
|
|
@xhtml = nil
|
|
@xhtml_cached = false
|
|
@wp_edittoken = nil
|
|
@wp_edittime = nil
|
|
|
|
reload if load_text
|
|
end
|
|
|
|
def xhtml
|
|
unless @xhtml_cached
|
|
xhtml_reload
|
|
end
|
|
@xhtml
|
|
end
|
|
|
|
def xhtml_reload
|
|
html = @wiki.http.get_content("#{@wiki.article_url(@name)}")
|
|
html.scan(/<!-- start content -->(.+)<!-- end content -->/m) { |content,|
|
|
@xhtml = REXML::Document.new("<xhtml>#{content}</xhtml>").root
|
|
}
|
|
|
|
@xhtml_cached = true
|
|
end
|
|
|
|
def reload
|
|
puts "Loading #{@wiki.article_url(@name)}&action=edit"
|
|
doc = REXML::Document.new(@wiki.http.get_content("#{@wiki.article_url(@name)}&action=edit")).root
|
|
@name = doc.elements['//span[@class="editHelp"]/a'].attributes['title']
|
|
form = doc.elements['//form[@name="editform"]']
|
|
@text = form.elements['textarea[@name="wpTextbox1"]'].text
|
|
begin
|
|
@wp_edittoken = form.elements['input[@name="wpEditToken"]'].attributes['value']
|
|
@wp_edittime = form.elements['input[@name="wpEdittime"]'].attributes['value']
|
|
rescue NoMethodError
|
|
# wpEditToken might be missing, that's ok
|
|
end
|
|
end
|
|
|
|
##
|
|
# TODO: minor_edit, watch_this
|
|
def submit(summary, minor_edit=false, watch_this=false)
|
|
puts "Posting to #{@wiki.article_url(@name)}&action=submit"
|
|
postdata = "wpTextbox1=#{CGI::escape(@text)}&wpSummary=#{CGI::escape(summary)}&wpSave=1&wpEditToken=#{@wp_edittoken}&wpEdittime=#{@wp_edittime}"
|
|
result = @wiki.http.post_content("#{@wiki.article_url(@name)}&action=submit", postdata)
|
|
# TODO: Was edit successful? (We received the document anyways)
|
|
end
|
|
end
|
|
end
|
|
</pre>
|