wikiranger 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/exe/wikiranger +18 -3
- data/lib/wikiranger.rb +5 -0
- data/lib/wikiranger/report.rb +45 -0
- data/lib/wikiranger/version.rb +1 -1
- data/lib/wikiranger/wikipedia.rb +1 -1
- data/lib/wikiranger/wikipedia/user_contribution.rb +1 -1
- data/templates/default.html.erb +181 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee56f0d0a12d1f38a39c3b63b17408da3d0870b9
|
4
|
+
data.tar.gz: 9e2ae0ebae414dd022dfe9f14ddf786494e4a256
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cff48a388b7bcc366394c62969d2223d1593f05edd6088b8e6011579ebf1fa583f75c12e98369fd692b293364ab6fc27c592af42be72996107d72b515713ef0
|
7
|
+
data.tar.gz: ee3a860d27cd930481df8786432cd3292d1f79a7799e6bf9f0597b7ba1e7d94c1cc3512250fe302eb522742170f27a126f21962ed11724e55db92a637e57b2e6
|
data/Gemfile.lock
CHANGED
data/exe/wikiranger
CHANGED
@@ -4,6 +4,7 @@ require "wikiranger"
|
|
4
4
|
|
5
5
|
options = {
|
6
6
|
:csv => nil,
|
7
|
+
:html => nil,
|
7
8
|
:threads => 5,
|
8
9
|
:top => 10,
|
9
10
|
:api_base_url => Wikiranger::Wikipedia::DEFAULT_API_BASE_URI
|
@@ -13,6 +14,14 @@ begin
|
|
13
14
|
OptionParser.new do |opts|
|
14
15
|
opts.banner = "Usage: #{$0} [options] cidr [cidr2] ... [cidrN]"
|
15
16
|
|
17
|
+
opts.on("--html DESTINATION", "Write Wiki contribution HTML report file") do |v|
|
18
|
+
if File.exists?(v)
|
19
|
+
puts "ERROR: File #{v} does already exist".red
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
options[:html] = v
|
23
|
+
end
|
24
|
+
|
16
25
|
opts.on("--csv DESTINATION", "Write Wiki contribution data to CSV file") do |v|
|
17
26
|
if File.exists?(v)
|
18
27
|
puts "ERROR: File #{v} does already exist".red
|
@@ -97,9 +106,15 @@ if contributions.count.zero?
|
|
97
106
|
exit
|
98
107
|
end
|
99
108
|
|
109
|
+
if options[:html]
|
110
|
+
report = Wikiranger::Report.new(contributions)
|
111
|
+
report.generate(options[:html])
|
112
|
+
puts "[+] Wrote Wiki contribution HTML report to #{options[:html].bold}"
|
113
|
+
end
|
114
|
+
|
100
115
|
if options[:csv]
|
101
116
|
CSV.open(options[:csv], "wb") do |csv|
|
102
|
-
csv << ["user", "page_id", "rev_id", "parent_id", "title", "timestamp", "comment", "size"]
|
117
|
+
csv << ["user", "page_id", "rev_id", "parent_id", "title", "timestamp", "comment", "size", "size_diff"]
|
103
118
|
contributions.each do |contribution|
|
104
119
|
csv << contribution.to_csv_array
|
105
120
|
end
|
@@ -120,10 +135,10 @@ contributions.each do |contribution|
|
|
120
135
|
end
|
121
136
|
|
122
137
|
if !top_pages.key?(contribution.pageid)
|
123
|
-
top_pages[contribution.pageid] = [contribution.
|
138
|
+
top_pages[contribution.pageid] = [contribution.sizediff.abs, contribution.title]
|
124
139
|
else
|
125
140
|
contribution_bytes, title = top_pages[contribution.pageid]
|
126
|
-
top_pages[contribution.pageid] = [contribution_bytes + contribution.
|
141
|
+
top_pages[contribution.pageid] = [contribution_bytes + contribution.sizediff.abs, title]
|
127
142
|
end
|
128
143
|
end
|
129
144
|
|
data/lib/wikiranger.rb
CHANGED
@@ -6,6 +6,8 @@ require "optparse"
|
|
6
6
|
require "ipaddr"
|
7
7
|
require "date"
|
8
8
|
require "csv"
|
9
|
+
require "erb"
|
10
|
+
require "cgi"
|
9
11
|
|
10
12
|
require "colorize"
|
11
13
|
require "ruby-progressbar"
|
@@ -15,8 +17,11 @@ require "wikiranger/util"
|
|
15
17
|
require "wikiranger/thread_pool"
|
16
18
|
require "wikiranger/wikipedia"
|
17
19
|
require "wikiranger/wikipedia/user_contribution"
|
20
|
+
require "wikiranger/report"
|
18
21
|
|
19
22
|
module Wikiranger
|
23
|
+
WIKIRANGER_ROOT = File.expand_path(File.join(File.dirname(__FILE__), "..")).freeze
|
24
|
+
|
20
25
|
BANNER = " _ _ _\n" +
|
21
26
|
" _ _ _|_| |_|_|___ ___ ___ ___ ___ ___\n" +
|
22
27
|
"| | | | | '_| | _| .'| | . | -_| _|\n" +
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Wikiranger
|
2
|
+
class Report
|
3
|
+
def initialize(contributions)
|
4
|
+
@contributions = contributions.sort_by { |c| c.timestamp_object }.reverse
|
5
|
+
@contributors = contributions.map(&:user).uniq.sort_by { |ip| ip.split(".").map(&:to_i) }
|
6
|
+
@pages = contributions.map { |c| [c.pageid, c.title] }.uniq.sort_by { |p| p.last }
|
7
|
+
@contribs_per_contributor = @contributors.map do |c|
|
8
|
+
[c, contributions.count { |contrib| contrib.user == c }]
|
9
|
+
end.to_h
|
10
|
+
@contribs_per_page = @pages.map do |p|
|
11
|
+
[p.first, contributions.count { |contrib| contrib.pageid == p.first }]
|
12
|
+
end.to_h
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate(destination)
|
16
|
+
report = load_template
|
17
|
+
b = binding
|
18
|
+
File.open(destination, "w") do |f|
|
19
|
+
f.write(report.result(b))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def load_template
|
26
|
+
ERB.new(File.read(File.join(Wikiranger::WIKIRANGER_ROOT, "templates", "default.html.erb")))
|
27
|
+
end
|
28
|
+
|
29
|
+
def h(unsafe)
|
30
|
+
CGI.escapeHTML(unsafe.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
def contributor_url(contribution)
|
34
|
+
"https://en.m.wikipedia.org/wiki/Special:Contributions/#{h(contribution.user)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def contribution_url(contribution)
|
38
|
+
"https://en.m.wikipedia.org/wiki/Special:MobileDiff/#{h(contribution.revid)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def page_url(contribution)
|
42
|
+
"https://en.wikipedia.org/?curid=#{h(contribution.pageid)}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/wikiranger/version.rb
CHANGED
data/lib/wikiranger/wikipedia.rb
CHANGED
@@ -12,7 +12,7 @@ module Wikiranger
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def user_contributions(user)
|
15
|
-
uri = "#{api_base_uri}/w/api.php?action=query&format=json&list=usercontribs&uclimit=max&ucuser=#{URI.escape(user)}&ucdir=older"
|
15
|
+
uri = "#{api_base_uri}/w/api.php?action=query&format=json&list=usercontribs&uclimit=max&ucuser=#{URI.escape(user)}&ucdir=older&ucprop=&ucprop=ids|title|timestamp|comment|size|sizediff"
|
16
16
|
response = request(uri)
|
17
17
|
if response.code.to_i != 200
|
18
18
|
unexpected_response!("Unexpected response code: #{response.code} when retrieving user contributions for #{user}")
|
@@ -13,7 +13,7 @@ module Wikiranger
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def to_csv_array
|
16
|
-
[self.user, self.pageid, self.revid, self.parentid, self.title, self.timestamp, self.comment, self.size]
|
16
|
+
[self.user, self.pageid, self.revid, self.parentid, self.title, self.timestamp, self.comment, self.size, self.sizediff]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
<title>Wikiranger HTML Report</title>
|
8
|
+
<style type="text/css">
|
9
|
+
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:0;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}
|
10
|
+
|
11
|
+
body {
|
12
|
+
font-family: Atlas Grotesk Web,helvetica neue,helvetica,arial,sans-serif;
|
13
|
+
color: #494a4b;
|
14
|
+
background-color: #f5f5f5;
|
15
|
+
-webkit-font-smoothing: antialiased;
|
16
|
+
-moz-osx-font-smoothing: grayscale;
|
17
|
+
overflow-y: scroll;
|
18
|
+
padding-bottom: 50px;
|
19
|
+
}
|
20
|
+
|
21
|
+
h1,h2,h3,h4,h5,h6 {
|
22
|
+
color: #494a4b;
|
23
|
+
font-weight: 400;
|
24
|
+
}
|
25
|
+
|
26
|
+
p {
|
27
|
+
margin:0 0 1em 0;
|
28
|
+
line-height:1.5;
|
29
|
+
font-size:17px;
|
30
|
+
}
|
31
|
+
|
32
|
+
a {
|
33
|
+
color: inherit;
|
34
|
+
text-decoration: underline;
|
35
|
+
}
|
36
|
+
|
37
|
+
a:hover {
|
38
|
+
color: #000;
|
39
|
+
}
|
40
|
+
|
41
|
+
fieldset {
|
42
|
+
margin-bottom: 20px;
|
43
|
+
}
|
44
|
+
|
45
|
+
legend {
|
46
|
+
font-weight: bold;
|
47
|
+
padding-left: 5px;
|
48
|
+
padding-right: 5px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.hidden {
|
52
|
+
display: none;
|
53
|
+
}
|
54
|
+
|
55
|
+
.content {
|
56
|
+
padding: 0px 20px 0px 20px;
|
57
|
+
}
|
58
|
+
|
59
|
+
.logo {
|
60
|
+
background-color: #2a2730;
|
61
|
+
color: #99979c;
|
62
|
+
margin: 0px 0px 50px 0px;
|
63
|
+
font-weight: bold;
|
64
|
+
}
|
65
|
+
|
66
|
+
.logo a {
|
67
|
+
color: #99979c;
|
68
|
+
}
|
69
|
+
|
70
|
+
.logo weak {
|
71
|
+
font-weight: normal;
|
72
|
+
}
|
73
|
+
|
74
|
+
table#contributions th {
|
75
|
+
text-align: left;
|
76
|
+
}
|
77
|
+
|
78
|
+
table#contributions tbody tr:hover {
|
79
|
+
background-color: #eaeaea;
|
80
|
+
}
|
81
|
+
|
82
|
+
.col-time {
|
83
|
+
width: 150px;
|
84
|
+
}
|
85
|
+
|
86
|
+
.diff-remove {
|
87
|
+
color: #dc2e16;
|
88
|
+
}
|
89
|
+
|
90
|
+
.diff-add {
|
91
|
+
color: #00af88;
|
92
|
+
}
|
93
|
+
</style>
|
94
|
+
</head>
|
95
|
+
<body>
|
96
|
+
<pre class="logo">
|
97
|
+
<div class="content">
|
98
|
+
_ _ _
|
99
|
+
_ _ _|_| |_|_|___ ___ ___ ___ ___ ___
|
100
|
+
| | | | | '_| | _| .'| | . | -_| _|
|
101
|
+
|_____|_|_,_|_|_| |__,|_|_|_ |___|_| v<%= Wikiranger::VERSION %>
|
102
|
+
<weak>by <a href="https://twitter.com/michenriksen" target="_blank">@michenriksen</a></weak> |___|
|
103
|
+
|
104
|
+
</div>
|
105
|
+
</pre>
|
106
|
+
<div class="content">
|
107
|
+
<fieldset>
|
108
|
+
<legend>Filtering options</legend>
|
109
|
+
<select id="select_contributor">
|
110
|
+
<option value="" selected="selected">From all contributors</option>
|
111
|
+
<% @contributors.each do |contributor| %>
|
112
|
+
<option value="<%=h contributor %>"><%=h contributor %> (<%=h @contribs_per_contributor[contributor] || '0' %>)</option>
|
113
|
+
<% end %>
|
114
|
+
</select>
|
115
|
+
<select id="select_page">
|
116
|
+
<option value="" selected="selected">From all pages</option>
|
117
|
+
<% @pages.each do |page| %>
|
118
|
+
<option value="<%=h page.first %>"><%=h page.last %> (<%=h @contribs_per_page[page.first] || '0' %>)</option>
|
119
|
+
<% end %>
|
120
|
+
</select>
|
121
|
+
<select id="select_changetype">
|
122
|
+
<option value="" selected="selected">Removals and additions</option>
|
123
|
+
<option value="removal">Removals</option>
|
124
|
+
<option value="addition">Additions</option>
|
125
|
+
</select>
|
126
|
+
</fieldset>
|
127
|
+
<table cellspacing="0" id="contributions" class="contribution-table">
|
128
|
+
<thead>
|
129
|
+
<tr>
|
130
|
+
<th class="col-time">Time</th>
|
131
|
+
<th>Change</th>
|
132
|
+
<th>Contributor</th>
|
133
|
+
<th>Page</th>
|
134
|
+
<th>Comment</th>
|
135
|
+
</tr>
|
136
|
+
</thead>
|
137
|
+
<tbody>
|
138
|
+
<% @contributions.each do |contribution| %>
|
139
|
+
<tr data-user="<%=h contribution.user %>" data-page-id="<%=h contribution.pageid %>" data-change-type="<%=h contribution.sizediff < 0 ? 'removal' : 'addition' %>">
|
140
|
+
<td><%=h contribution.timestamp_object.strftime("%b %e %Y, %H:%M") %></td>
|
141
|
+
<td class="<%= contribution.sizediff < 0 ? 'diff-remove' : 'diff-add' %>"><%=h contribution.sizediff%></td>
|
142
|
+
<td><a href="<%= contributor_url(contribution) %>" target="_blank"><%=h contribution.user %></a></td>
|
143
|
+
<td><a href="<%= page_url(contribution) %>" target="_blank"><%=h contribution.title %></a></td>
|
144
|
+
<td><a href="<%= contribution_url(contribution) %>" target="_blank"><%=h contribution.comment.empty? ? 'no edit summary' : contribution.comment %></a></td>
|
145
|
+
</tr>
|
146
|
+
<% end %>
|
147
|
+
</tbody>
|
148
|
+
</table>
|
149
|
+
</div>
|
150
|
+
<script type="text/javascript">
|
151
|
+
function filterContributions() {
|
152
|
+
var contributor = document.getElementById("select_contributor").value;
|
153
|
+
var page = document.getElementById("select_page").value;
|
154
|
+
var changeType = document.getElementById("select_changetype").value;
|
155
|
+
var contributions = document.querySelectorAll('#contributions tbody tr');
|
156
|
+
|
157
|
+
Array.prototype.forEach.call(contributions, function(el, i) {
|
158
|
+
el.classList.remove("hidden");
|
159
|
+
if (contributor !== "" && el.getAttribute("data-user") !== contributor ) {
|
160
|
+
el.classList.add("hidden");
|
161
|
+
return;
|
162
|
+
}
|
163
|
+
|
164
|
+
if (page !== "" && el.getAttribute("data-page-id") !== page) {
|
165
|
+
el.classList.add("hidden");
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
|
169
|
+
if (changeType !== "" && el.getAttribute("data-change-type") !== changeType) {
|
170
|
+
el.classList.add("hidden");
|
171
|
+
return;
|
172
|
+
}
|
173
|
+
});
|
174
|
+
}
|
175
|
+
|
176
|
+
document.getElementById("select_contributor").addEventListener("change", filterContributions);
|
177
|
+
document.getElementById("select_page").addEventListener("change", filterContributions);
|
178
|
+
document.getElementById("select_changetype").addEventListener("change", filterContributions);
|
179
|
+
</script>
|
180
|
+
</body>
|
181
|
+
</html>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wikiranger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Henriksen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-progressbar
|
@@ -99,11 +99,13 @@ files:
|
|
99
99
|
- bin/setup
|
100
100
|
- exe/wikiranger
|
101
101
|
- lib/wikiranger.rb
|
102
|
+
- lib/wikiranger/report.rb
|
102
103
|
- lib/wikiranger/thread_pool.rb
|
103
104
|
- lib/wikiranger/util.rb
|
104
105
|
- lib/wikiranger/version.rb
|
105
106
|
- lib/wikiranger/wikipedia.rb
|
106
107
|
- lib/wikiranger/wikipedia/user_contribution.rb
|
108
|
+
- templates/default.html.erb
|
107
109
|
- wikiranger.gemspec
|
108
110
|
homepage: https://github.com/michenriksen/wikiranger
|
109
111
|
licenses:
|