~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/stats/hitcounts.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mfrom: (0.9.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080622211713-fpo2zrq3s5dfecxg
Tags: 1.7.0-3
Simplify /etc/moin/wikilist format: "USER URL" (drop unneeded middle
CONFIG_DIR that was wrongly advertised as DATA_DIR).  Make
moin-mass-migrate handle both formats and warn about deprecation of
the old one.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
 
5
5
    This macro creates a hitcount chart from the data in "event.log".
6
6
 
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.
10
10
 
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.
13
14
"""
14
15
 
15
16
_debug = 0
16
17
 
17
 
from MoinMoin import caching, config, wikiutil
 
18
import time
 
19
 
 
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
22
23
 
 
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)
23
27
 
24
28
def linkto(pagename, request, params=''):
25
29
    _ = request.getText
26
30
 
27
31
    if not request.cfg.chart_options:
28
 
        request.formatter = Formatter(request)
29
32
        return text(pagename, request, params)
30
33
 
31
34
    if _debug:
39
42
    querystr = wikiutil.escape(querystr)
40
43
    if params:
41
44
        querystr += '&amp;' + params
42
 
    
43
 
    # TODO: remove escape=0 in 2.0
44
 
    data = {'url': page.url(request, querystr, escape=0)}
 
45
 
 
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
50
52
 
51
53
 
52
54
def get_data(pagename, request, filterpage=None):
53
 
    
 
55
    cache_days, cache_views, cache_edits = [], [], []
 
56
    cache_date = 0
 
57
 
54
58
    # Get results from cache
55
59
    if filterpage:
56
60
        arena = Page(request, pagename)
 
61
        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='item', use_pickle=True)
57
62
    else:
58
63
        arena = 'charts'
59
 
    
60
 
    cache_days, cache_views, cache_edits = [], [], []
61
 
    cache_date = 0
62
 
    cache = caching.CacheEntry(request, arena, 'hitcounts')
 
64
        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='wiki', use_pickle=True)
 
65
 
63
66
    if cache.exists():
64
67
        try:
65
 
            cache_date, cache_days, cache_views, cache_edits = eval(cache.content())
 
68
            cache_date, cache_days, cache_views, cache_edits = cache.content()
66
69
        except:
67
70
            cache.remove() # cache gone bad
68
71
 
72
75
        new_date = log.date()
73
76
    except logfile.LogMissing:
74
77
        new_date = None
75
 
        
 
78
 
76
79
    # prepare data
77
80
    days = []
78
81
    views = []
81
84
    ratchet_time = None
82
85
    if new_date is not None:
83
86
        log.set_filter(['VIEWPAGE', 'SAVEPAGE'])
 
87
        latest = None
84
88
        for event in log.reverse():
85
 
            #print ">>>", wikiutil.escape(repr(event)), "<br>"
86
 
    
87
 
            if event[0] <=  cache_date:
 
89
            # don't use event_log.date()
 
90
            if latest is None:
 
91
                latest = event[0]
 
92
            event_usecs = event[0]
 
93
            if event_usecs <= cache_date:
88
94
                break
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:
92
97
                continue
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:
96
102
                # new day
97
103
                while ratchet_time:
98
 
                    ratchet_time -= 86400
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
 
106
                    if rday <= day:
 
107
                        break
 
108
                    days.append(DATE_FMT % rday)
102
109
                    views.append(0)
103
110
                    edits.append(0)
104
 
                days.append(request.user.getFormattedDate(wikiutil.version2timestamp(event[0])))
 
111
                days.append(DATE_FMT % day)
105
112
                views.append(0)
106
113
                edits.append(0)
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
 
117
                views[-1] += 1
111
118
            elif event[1] == 'SAVEPAGE':
112
 
                edits[-1] = edits[-1] + 1
113
 
    
 
119
                edits[-1] += 1
 
120
 
114
121
        days.reverse()
115
122
        views.reverse()
116
123
        edits.reverse()
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))
131
137
 
132
138
    return cache_days, cache_views, cache_edits
133
139
 
140
146
    # check params
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)
145
 
    
146
 
    if request and request.form and request.form.has_key('page'):
 
152
 
 
153
    if request and request.form and 'page' in request.form:
147
154
        filterpage = request.form['page'][0]
148
155
 
149
156
    days, views, edits = get_data(pagename, request, filterpage)
150
157
 
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'),
155
162
                    ]
156
163
 
157
164
    maxentries = 30
160
167
        step = float(len(days))/ maxentries
161
168
    else:
162
169
        step = 1
163
 
        
 
170
 
164
171
    sv = 0.0
165
172
    se = 0.0
166
173
    sd = 0.0
167
174
    cnt = 0
168
175
 
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
172
179
        sd += 1
173
180
        cnt += 1
174
181
        sv += v
193
200
 
194
201
    # check params
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]
198
205
 
199
206
    days, views, edits = get_data(pagename, request, filterpage)
200
207
 
201
208
    import math
202
 
    
 
209
 
203
210
    try:
204
211
        scalefactor = float(max(views))/max(edits)
205
212
    except (ZeroDivisionError, ValueError):
207
214
    else:
208
215
        scalefactor = int(10 ** math.floor(math.log10(scalefactor)))
209
216
 
210
 
    #scale edits up
211
 
    edits = map(lambda x: x*scalefactor, edits)
 
217
    # scale edits up
 
218
    edits = [x * scalefactor for x in edits]
212
219
 
213
220
    # create image
214
221
    image = cStringIO.StringIO()
216
223
    c.addData(ChartData(views, color='green'))
217
224
    c.addData(ChartData(edits, color='red'))
218
225
    chart_title = ''
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}
 
229
    if filterpage:
 
230
        chart_title = _("%(chart_title)s for %(filterpage)s") % {
 
231
            'chart_title': chart_title,
 
232
            'filterpage': filterpage,
 
233
        }
223
234
    chart_title = "%s\n%sx%d" % (chart_title, _("green=view\nred=edit"), scalefactor)
224
235
    c.option(
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'),
231
242
        #yaxis2 = 1,
233
244
        #ytitle2_color = Color('red'),
234
245
        #ylabel2_color = Color('black'),
235
246
        #interpolations = 0,
236
 
        threed_depth = 1.0,
237
 
        requested_yinterval = 1.0,
238
 
        stack_type = c.GDC_STACK_BESIDE
 
247
        threed_depth=1.0,
 
248
        requested_yinterval=1.0,
 
249
        stack_type=c.GDC_STACK_BESIDE
239
250
    )
240
251
    c.draw(c.GDC_LINE,
241
252
        (request.cfg.chart_options['width'], request.cfg.chart_options['height']),
242
253
        image, days)
243
254
 
244
 
    # send HTTP headers
245
255
    headers = [
246
256
        "Content-Type: image/gif",
247
257
        "Content-Length: %d" % len(image.getvalue()),
248
258
    ]
249
 
    request.http_headers(headers)
 
259
    request.emit_http_headers(headers)
250
260
 
251
261
    # copy the image
252
262
    image.reset()
253
263
    shutil.copyfileobj(image, request, 8192)
254
 
    raise MoinMoinNoFooter
255
264