This file is indexed.

/usr/share/pyshared/MoinMoin/stats/hitcounts.py is in python-moinmoin 1.9.3-1ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Hitcount Statistics

    This macro creates a hitcount chart from the data in "event.log".

    TODO: refactor to use a class, this code is ugly.
          A lot of code here is duplicated in stats.useragents.
          Maybe both can use same base class, maybe some parts are useful to other code.

    @copyright: 2002-2004 Juergen Hermann <jh@web.de>,
                2007 MoinMoin:ThomasWaldmann
    @license: GNU GPL, see COPYING for details.
"""

_debug = 0

import time

from MoinMoin import caching, wikiutil, logfile
from MoinMoin.Page import Page
from MoinMoin.logfile import eventlog

# this is a CONSTANT used for on-disk caching, it must NOT be configurable and
# not depend on request.user!
DATE_FMT = '%04d-%02d-%02d' # % (y, m, d)

def linkto(pagename, request, params=''):
    _ = request.getText

    if not request.cfg.chart_options:
        return text(pagename, request, params)

    if _debug:
        return draw(pagename, request)

    page = Page(request, pagename)

    # Create escaped query string from dict and params
    querystr = {'action': 'chart', 'type': 'hitcounts'}
    querystr = wikiutil.makeQueryString(querystr)
    querystr = wikiutil.escape(querystr)
    if params:
        querystr += '&amp;' + params

    data = {'url': page.url(request, querystr)}
    data.update(request.cfg.chart_options)
    result = ('<img src="%(url)s" width="%(width)d" height="%(height)d"'
              ' alt="hitcounts chart">') % data

    return result


def get_data(pagename, request, filterpage=None):
    cache_days, cache_views, cache_edits = [], [], []
    cache_date = 0

    # Get results from cache
    if filterpage:
        arena = Page(request, pagename)
        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='item', use_pickle=True)
    else:
        arena = 'charts'
        cache = caching.CacheEntry(request, arena, 'hitcounts', scope='wiki', use_pickle=True)

    if cache.exists():
        try:
            cache_date, cache_days, cache_views, cache_edits = cache.content()
        except:
            cache.remove() # cache gone bad

    # Get new results from the log
    log = eventlog.EventLog(request)
    try:
        new_date = log.date()
    except logfile.LogMissing:
        new_date = None

    # prepare data
    days = []
    views = []
    edits = []
    ratchet_day = None
    ratchet_time = None
    if new_date is not None:
        log.set_filter(['VIEWPAGE', 'SAVEPAGE'])
        latest = None
        for event in log.reverse():
            # don't use event_log.date()
            if latest is None:
                latest = event[0]
            event_usecs = event[0]
            if event_usecs <= cache_date:
                break
            eventpage = event[2].get('pagename', '')
            if filterpage and eventpage != filterpage:
                continue
            event_secs = wikiutil.version2timestamp(event_usecs)
            time_tuple = time.gmtime(event_secs) # must be UTC
            day = tuple(time_tuple[0:3])
            if day != ratchet_day:
                # new day
                while ratchet_time:
                    ratchet_time -= 86400 # seconds per day
                    rday = tuple(time.gmtime(ratchet_time)[0:3]) # must be UTC
                    if rday <= day:
                        break
                    days.append(DATE_FMT % rday)
                    views.append(0)
                    edits.append(0)
                days.append(DATE_FMT % day)
                views.append(0)
                edits.append(0)
                ratchet_day = day
                ratchet_time = event_secs
            if event[1] == 'VIEWPAGE':
                views[-1] += 1
            elif event[1] == 'SAVEPAGE':
                edits[-1] += 1

        days.reverse()
        views.reverse()
        edits.reverse()

    # merge the day on the end of the cache
    if cache_days and days and days[0] == cache_days[-1]:
        cache_edits[-1] += edits[0]
        cache_views[-1] += views[0]
        days, views, edits = days[1:], views[1:], edits[1:]

    # Update and save the cache
    cache_days.extend(days)
    cache_views.extend(views)
    cache_edits.extend(edits)
    if new_date is not None:
        cache.update((latest, cache_days, cache_views, cache_edits))

    return cache_days, cache_views, cache_edits


def text(pagename, request, params=''):
    from MoinMoin.util.dataset import TupleDataset, Column
    from MoinMoin.widget.browser import DataBrowserWidget
    _ = request.getText

    # check params
    filterpage = None
    if params.startswith('page='):
        filterpage = wikiutil.url_unquote(params[len('page='):])

    if request and request.values and 'page' in request.values:
        filterpage = request.values['page']

    days, views, edits = get_data(pagename, request, filterpage)

    hits = TupleDataset()
    hits.columns = [Column('day', label=_("Date"), align='left'),
                    Column('views', label=_("Views/day"), align='right'),
                    Column('edits', label=_("Edits/day"), align='right'),
                    ]

    maxentries = 30

    if maxentries < len(days):
        step = float(len(days))/ maxentries
    else:
        step = 1

    sv = 0.0
    se = 0.0
    sd = 0.0
    cnt = 0

    for i in xrange(len(days)-1, -1, -1):
        d, v, e = days[i], views[i], edits[i]
        # sum up views and edits to step days
        sd += 1
        cnt += 1
        sv += v
        se += e
        if cnt >= step:
            cnt -= step
            hits.addRow((d, "%.1f" % (sv/sd), "%.1f" % (se/sd)))
            sv = 0.0
            se = 0.0
            sd = 0.0

    table = DataBrowserWidget(request)
    table.setData(hits)
    return table.render(method="GET")


def draw(pagename, request):
    import shutil, cStringIO
    from MoinMoin.stats.chart import Chart, ChartData, Color

    _ = request.getText

    # check params
    filterpage = None
    if request and request.values and 'page' in request.values:
        filterpage = request.values['page']

    days, views, edits = get_data(pagename, request, filterpage)

    import math

    try:
        scalefactor = float(max(views))/max(edits)
    except (ZeroDivisionError, ValueError):
        scalefactor = 1.0
    else:
        scalefactor = int(10 ** math.floor(math.log10(scalefactor)))

    # scale edits up
    edits = [x * scalefactor for x in edits]

    # create image
    image = cStringIO.StringIO()
    c = Chart()
    c.addData(ChartData(views, color='green'))
    c.addData(ChartData(edits, color='red'))
    chart_title = ''
    if request.cfg.sitename:
        chart_title = "%s: " % request.cfg.sitename
    chart_title = chart_title + _('Page hits and edits')
    if filterpage:
        chart_title = _("%(chart_title)s for %(filterpage)s") % {
            'chart_title': chart_title,
            'filterpage': filterpage,
        }
    chart_title = "%s\n%sx%d" % (chart_title, _("green=view\nred=edit"), scalefactor)
    c.option(
        title=chart_title.encode('iso-8859-1', 'replace'), # gdchart can't do utf-8
        xtitle=(_('date') + ' (Server)').encode('iso-8859-1', 'replace'),
        ytitle=_('# of hits').encode('iso-8859-1', 'replace'),
        title_font=c.GDC_GIANT,
        #thumblabel = 'THUMB', thumbnail = 1, thumbval = 10,
        #ytitle_color = Color('green'),
        #yaxis2 = 1,
        #ytitle2 = '# of edits',
        #ytitle2_color = Color('red'),
        #ylabel2_color = Color('black'),
        #interpolations = 0,
        threed_depth=1.0,
        requested_yinterval=1.0,
        stack_type=c.GDC_STACK_BESIDE
    )
    c.draw(c.GDC_LINE,
        (request.cfg.chart_options['width'], request.cfg.chart_options['height']),
        image, days)

    request.content_type = 'image/gif'
    request.content_length = len(image.getvalue())

    # copy the image
    image.reset()
    shutil.copyfileobj(image, request, 8192)