5
5
This macro creates a hitcount chart from the data in "event.log".
7
TODO: refactor to use a class, this code is ugly. A lot of code
8
here is duplicated in stats.useragents. Maybe both can use same
9
base class, maybe some parts are useful to other code.
7
TODO: refactor to use a class, this code is ugly.
8
A lot of code here is duplicated in stats.useragents.
9
Maybe both can use same base class, maybe some parts are useful to other code.
11
@copyright: 2002-2004 by J�rgen Hermann <jh@web.de>
11
@copyright: 2002-2004 Juergen Hermann <jh@web.de>,
12
2007 MoinMoin:ThomasWaldmann
12
13
@license: GNU GPL, see COPYING for details.
17
from MoinMoin import caching, config, wikiutil
20
from MoinMoin import caching, wikiutil, logfile
18
21
from MoinMoin.Page import Page
19
from MoinMoin.util import MoinMoinNoFooter
20
from MoinMoin.formatter.text_html import Formatter
21
from MoinMoin.logfile import eventlog, logfile
22
from MoinMoin.logfile import eventlog
24
# this is a CONSTANT used for on-disk caching, it must NOT be configurable and
25
# not depend on request.user!
26
DATE_FMT = '%04d-%02d-%02d' # % (y, m, d)
24
28
def linkto(pagename, request, params=''):
25
29
_ = request.getText
27
31
if not request.cfg.chart_options:
28
request.formatter = Formatter(request)
29
32
return text(pagename, request, params)
39
42
querystr = wikiutil.escape(querystr)
41
44
querystr += '&' + params
43
# TODO: remove escape=0 in 2.0
44
data = {'url': page.url(request, querystr, escape=0)}
46
data = {'url': page.url(request, querystr)}
45
47
data.update(request.cfg.chart_options)
46
48
result = ('<img src="%(url)s" width="%(width)d" height="%(height)d"'
47
49
' alt="hitcounts chart">') % data
52
54
def get_data(pagename, request, filterpage=None):
55
cache_days, cache_views, cache_edits = [], [], []
54
58
# Get results from cache
56
60
arena = Page(request, pagename)
61
cache = caching.CacheEntry(request, arena, 'hitcounts', scope='item', use_pickle=True)
60
cache_days, cache_views, cache_edits = [], [], []
62
cache = caching.CacheEntry(request, arena, 'hitcounts')
64
cache = caching.CacheEntry(request, arena, 'hitcounts', scope='wiki', use_pickle=True)
65
cache_date, cache_days, cache_views, cache_edits = eval(cache.content())
68
cache_date, cache_days, cache_views, cache_edits = cache.content()
67
70
cache.remove() # cache gone bad
81
84
ratchet_time = None
82
85
if new_date is not None:
83
86
log.set_filter(['VIEWPAGE', 'SAVEPAGE'])
84
88
for event in log.reverse():
85
#print ">>>", wikiutil.escape(repr(event)), "<br>"
87
if event[0] <= cache_date:
89
# don't use event_log.date()
92
event_usecs = event[0]
93
if event_usecs <= cache_date:
89
# XXX Bug: event[2].get('pagename') -> u'Aktuelle%C4nderungen' 8(
90
eventpage = event[2].get('pagename','')
95
eventpage = event[2].get('pagename', '')
91
96
if filterpage and eventpage != filterpage:
93
time_tuple = request.user.getTime(wikiutil.version2timestamp(event[0]))
98
event_secs = wikiutil.version2timestamp(event_usecs)
99
time_tuple = time.gmtime(event_secs) # must be UTC
94
100
day = tuple(time_tuple[0:3])
95
101
if day != ratchet_day:
97
103
while ratchet_time:
99
rday = tuple(request.user.getTime(ratchet_time)[0:3])
100
if rday <= day: break
101
days.append(request.user.getFormattedDate(ratchet_time))
104
ratchet_time -= 86400 # seconds per day
105
rday = tuple(time.gmtime(ratchet_time)[0:3]) # must be UTC
108
days.append(DATE_FMT % rday)
104
days.append(request.user.getFormattedDate(wikiutil.version2timestamp(event[0])))
111
days.append(DATE_FMT % day)
107
114
ratchet_day = day
108
ratchet_time = wikiutil.version2timestamp(event[0])
115
ratchet_time = event_secs
109
116
if event[1] == 'VIEWPAGE':
110
views[-1] = views[-1] + 1
111
118
elif event[1] == 'SAVEPAGE':
112
edits[-1] = edits[-1] + 1
126
133
cache_views.extend(views)
127
134
cache_edits.extend(edits)
128
135
if new_date is not None:
129
cache.update("(%r, %r, %r, %r)" %
130
(new_date, cache_days, cache_views, cache_edits))
136
cache.update((latest, cache_days, cache_views, cache_edits))
132
138
return cache_days, cache_views, cache_edits
141
147
filterpage = None
142
148
if params.startswith('page='):
143
params = params[len('page='):]
149
params = params[len('page='):]
150
params = wikiutil.url_unquote(params, want_unicode=False)
144
151
filterpage = wikiutil.decodeUserInput(params)
146
if request and request.form and request.form.has_key('page'):
153
if request and request.form and 'page' in request.form:
147
154
filterpage = request.form['page'][0]
149
156
days, views, edits = get_data(pagename, request, filterpage)
151
158
hits = TupleDataset()
152
159
hits.columns = [Column('day', label=_("Date"), align='left'),
153
Column('views', label=_("Views/day") , align='right'),
154
Column('edits', label=_("Edits/day") , align='right')
160
Column('views', label=_("Views/day"), align='right'),
161
Column('edits', label=_("Edits/day"), align='right'),
160
167
step = float(len(days))/ maxentries
169
for i in xrange(len(days)-1,-1,-1):
170
d,v,e = days[i], views[i], edits[i]
171
# sum up views and edits to step days
176
for i in xrange(len(days)-1, -1, -1):
177
d, v, e = days[i], views[i], edits[i]
178
# sum up views and edits to step days
195
202
filterpage = None
196
if request and request.form and request.form.has_key('page'):
203
if request and request.form and 'page' in request.form:
197
204
filterpage = request.form['page'][0]
199
206
days, views, edits = get_data(pagename, request, filterpage)
204
211
scalefactor = float(max(views))/max(edits)
205
212
except (ZeroDivisionError, ValueError):
208
215
scalefactor = int(10 ** math.floor(math.log10(scalefactor)))
211
edits = map(lambda x: x*scalefactor, edits)
218
edits = [x * scalefactor for x in edits]
214
221
image = cStringIO.StringIO()
216
223
c.addData(ChartData(views, color='green'))
217
224
c.addData(ChartData(edits, color='red'))
219
if request.cfg.sitename: chart_title = "%s: " % request.cfg.sitename
226
if request.cfg.sitename:
227
chart_title = "%s: " % request.cfg.sitename
220
228
chart_title = chart_title + _('Page hits and edits')
221
if filterpage: chart_title = _("%(chart_title)s for %(filterpage)s") % {
222
'chart_title': chart_title, 'filterpage': filterpage}
230
chart_title = _("%(chart_title)s for %(filterpage)s") % {
231
'chart_title': chart_title,
232
'filterpage': filterpage,
223
234
chart_title = "%s\n%sx%d" % (chart_title, _("green=view\nred=edit"), scalefactor)
225
title = chart_title.encode('iso-8859-1', 'replace'), # gdchart can't do utf-8
226
xtitle = (_('date') + ' (Server)').encode('iso-8859-1', 'replace'),
227
ytitle = _('# of hits').encode('iso-8859-1', 'replace'),
228
title_font = c.GDC_GIANT,
236
title=chart_title.encode('iso-8859-1', 'replace'), # gdchart can't do utf-8
237
xtitle=(_('date') + ' (Server)').encode('iso-8859-1', 'replace'),
238
ytitle=_('# of hits').encode('iso-8859-1', 'replace'),
239
title_font=c.GDC_GIANT,
229
240
#thumblabel = 'THUMB', thumbnail = 1, thumbval = 10,
230
241
#ytitle_color = Color('green'),
233
244
#ytitle2_color = Color('red'),
234
245
#ylabel2_color = Color('black'),
235
246
#interpolations = 0,
237
requested_yinterval = 1.0,
238
stack_type = c.GDC_STACK_BESIDE
248
requested_yinterval=1.0,
249
stack_type=c.GDC_STACK_BESIDE
240
251
c.draw(c.GDC_LINE,
241
252
(request.cfg.chart_options['width'], request.cfg.chart_options['height']),
246
256
"Content-Type: image/gif",
247
257
"Content-Length: %d" % len(image.getvalue()),
249
request.http_headers(headers)
259
request.emit_http_headers(headers)
253
263
shutil.copyfileobj(image, request, 8192)
254
raise MoinMoinNoFooter