1#!/usr/bin/env python
2# -*- mode: python; coding: utf-8; -*-
3# ---------------------------------------------------------------------------##
4#
5# Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
6# Copyright (C) 2003 Mt. Hood Playing Card Co.
7# Copyright (C) 2005-2009 Skomoroh
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21#
22# ---------------------------------------------------------------------------##
23
24import time
25
26from pysollib.gamedb import GI
27from pysollib.mfxutil import format_time
28from pysollib.mygettext import _
29
30from six.moves import range
31
32# ************************************************************************
33# *
34# ************************************************************************
35
36
37class PysolStatsFormatter:
38
39    def getStatHeader(self):
40        return (_("Game"),
41                _("Played"),
42                _("Won"),
43                _("Lost"),
44                _('Playing time'),
45                _('Moves'),
46                _("% won"))
47
48    def getStatResults(self, player, sort_by='name'):
49        app = self.app
50        #
51        sort_functions = {
52            'name':    app.getGamesIdSortedByName,
53            'played':  app.getGamesIdSortedByPlayed,
54            'won':     app.getGamesIdSortedByWon,
55            'lost':    app.getGamesIdSortedByLost,
56            'time':    app.getGamesIdSortedByPlayingTime,
57            'moves':   app.getGamesIdSortedByMoves,
58            'percent': app.getGamesIdSortedByPercent,
59            }
60        sort_func = sort_functions[sort_by]
61        g = sort_func(player=player)
62        t_won, tlost, tgames, ttime, tmoves = 0, 0, 0, 0, 0
63        for id in g:
64            won, lost, time, moves = app.stats.getFullStats(player, id)
65            tot = won + lost
66            if tot > 0 or id == app.game.id:
67                # yield only played games
68                name = app.getGameTitleName(id)
69                t_won, tlost = t_won + won, tlost + lost
70                ttime, tmoves = ttime+time, tmoves+moves
71                perc = "%.1f" % (100.0 * won / tot) if tot > 0 else '0.0'
72                t = format_time(time)
73                m = str(round(moves, 1))
74                yield [name, won+lost, won, lost, t, m, perc, id]
75                tgames += 1
76        # summary
77        won, lost = t_won, tlost
78        if won + lost > 0:
79            if won > 0:
80                time = format_time(ttime/tgames)
81                moves = round(tmoves/tgames, 1)
82            else:
83                time = format_time(0)
84                moves = 0
85            perc = "%.1f" % (100.0*won/(won+lost))
86        else:
87            perc = "0.0"
88        self.total_games = len(g)
89        self.played_games = tgames
90        self.won_games = won
91        self.lost_games = lost
92        self.avrg_time = time
93        self.avrg_moves = moves
94        self.percent = perc
95        # yield (_("Total (%d out of %d games)") % (tgames, len(g)),
96        #       won+lost, won, lost, time, moves, perc, '')
97
98    def getStatSummary(self):
99        return self.total_games, \
100               self.played_games, \
101               self.won_games, \
102               self.lost_games, \
103               self.avrg_time, \
104               self.avrg_moves, \
105               self.percent
106
107    def getLogHeader(self):
108        return _("Game"), _("Game number"), _("Started at"), _("Status")
109
110    def getLogResults(self, player, prev_games):
111        t_won, tlost = 0, 0
112        for pg in prev_games:
113            if not isinstance(pg, tuple):
114                continue
115            if len(pg) == 5:
116                pg = pg + ("", None, None, 1)
117            elif len(pg) == 7:
118                pg = pg + (None, 1)
119            elif len(pg) == 8:
120                pg = pg + (1,)
121            if len(pg) < 8:
122                continue
123            gameid = pg[0]
124            if not isinstance(gameid, int):
125                continue
126            gi = self.app.getGameInfo(gameid)
127            if not gi:
128                gi = self.app.getGameInfo(GI.PROTECTED_GAMES.get(gameid))
129            if gi:
130                name = gi.name
131            else:
132                name = _("** UNKNOWN %d **") % gameid
133            f = pg[1]
134            if len(f) == 16:
135                # gamenumber = "%s-%s-%s-%s" % \
136                #   (f[0:4], f[4:8], f[8:12], f[12:16])
137                gamenumber = "%s-%s-%s" % (f[4:8], f[8:12], f[12:16])
138            elif len(f) <= 20:
139                gamenumber = f
140            else:
141                gamenumber = _("** ERROR **")
142            date = time.strftime("%Y-%m-%d  %H:%M", time.localtime(pg[3]))
143            if pg[2] >= 0:
144                won = pg[2] > 0
145                t_won, tlost = t_won + won, tlost + (1 - won)
146            status = "*error*"
147            if -2 <= pg[2] <= 2:
148                status = (_("Loaded"), _("Not won"), _("Lost"),
149                          _("Won"), _("Perfect"))[pg[2]+2]
150            # writer.plog(name, gamenumber, date, status, gameid=gameid,
151            #   won=pg[2])
152            yield [name, gamenumber, date, status, pg[2], gameid]
153
154    #
155    #
156    #
157
158    def writeStats(self, player, sort_by='name'):
159        pass
160
161    def writeFullLog(self, player):
162        pass
163
164    def writeSessionLog(self, player):
165        pass
166
167
168class FileStatsFormatter(PysolStatsFormatter):
169
170    def __init__(self, app, file):
171        self.app = app
172        self.file = file
173
174    def p(self, s):
175        self.file.write(s)
176
177    def nl(self, count=1):
178        self.p("\n" * count)
179
180    def pheader(self, s):
181        self.p(s)
182
183    def pstats(self, *args, **kwargs):
184        s = "%-30s %7s %7s %7s %7s %7s %7s\n" % args
185        self.p(s)
186
187    def plog(self, gamename, gamenumber, date, status, gameid=-1, won=-1):
188        self.p("%-25s %-20s  %17s  %s\n" %
189               (gamename, gamenumber, date, status))
190
191    def writeHeader(self, header, pagewidth=72):
192        date = time.ctime(time.time())
193        date = time.strftime("%Y-%m-%d  %H:%M", time.localtime(time.time()))
194        blanks = max(pagewidth - len(header) - len(date), 1)
195        self.pheader(header + " "*blanks + date + "\n")
196        self.pheader("-" * pagewidth + "\n")
197        self.pheader("\n")
198
199    def writeStats(self, player, sort_by='name'):
200        if player is None:
201            player = _('Demo')
202        header = _("Statistics for %(player)s") % {'player': player}
203        self.writeHeader(header, 62)
204        header = self.getStatHeader()
205        self.pstats(*header)
206        self.nl()
207        for result in self.getStatResults(player, sort_by):
208            gameid = result.pop()
209            self.pstats(gameid=gameid, *result)
210        self.nl()
211        total, played, won, lost, time, moves, perc = self.getStatSummary()
212        self.pstats(_("Total (%(played)d out of %(total)d games)") %
213                    {'played': played, 'total': total},
214                    won+lost, won, lost, time, moves, perc)
215        self.nl(2)
216        return played
217
218    def writeLog(self, player, header, prev_games):
219        if not player or not prev_games:
220            return 0
221        self.writeHeader(header, 71)
222        header = self.getLogHeader()
223        self.plog(*header)
224        self.nl()
225        for result in self.getLogResults(player, prev_games):
226            gameid = result.pop()
227            won = result.pop()
228            self.plog(gameid=gameid, won=won, *result)
229        self.nl(2)
230        return 1
231
232    def writeFullLog(self, player):
233        if player is None:
234            player = _('Demo')
235        header = _("Full log for %(player)s") % {'player': player}
236        prev_games = self.app.stats.prev_games.get(player)
237        return self.writeLog(player, header, prev_games)
238
239    def writeSessionLog(self, player):
240        if player is None:
241            player = _('Demo')
242        header = _("Session log for %(player)s") % {'player': player}
243        prev_games = self.app.stats.session_games.get(player)
244        return self.writeLog(player, header, prev_games)
245
246
247# ************************************************************************
248# *
249# ************************************************************************
250
251class ProgressionFormatter:
252
253    def __init__(self, app, player, gameid):
254
255        all_results = {}      # key: (year, month, day);  value: [played, won]
256        self.all_results = all_results
257        game_results = {}
258        self.game_results = game_results
259        games = app.stats.prev_games.get(player)
260        if not games:
261            return
262        for g in games:
263            id = g[0]
264            status = g[2]
265            start_time = g[3]
266            t = time.localtime(start_time)[:3]
267            if t not in all_results:
268                all_results[t] = [0, 0]
269            all_results[t][0] += 1
270            if status > 0:
271                all_results[t][1] += 1
272            if id == gameid:
273                if t not in game_results:
274                    game_results[t] = [0, 0]
275                game_results[t][0] += 1
276                if status > 0:
277                    game_results[t][1] += 1
278        # from pprint import pprint; pprint(all_results)
279
280    def norm_time(self, t):
281        if len(t) == 3:
282            t = list(t)+[0, 0, 0, -1, -1, -1]
283        return list(time.localtime(time.mktime(tuple(t))))
284
285    def getResults(self, interval, all_games=True):
286        if all_games:
287            results = self.all_results
288        else:
289            results = self.game_results
290        t = list(time.localtime())
291        if interval == 'week':
292            t[2] -= 7
293            lt = self.norm_time(t)
294            marks = None
295            delta = 1
296            format = '%d.%m'
297        elif interval == 'month':
298            tt = t[:]
299            t[1] -= 1
300            lt = self.norm_time(t)
301            marks = [lt[:3], tt[:3]]
302            tt[2] -= 10
303            marks.append(self.norm_time(tt)[:3])
304            tt[2] -= 10
305            marks.append(self.norm_time(tt)[:3])
306            delta = 1
307            format = '%d.%m'
308        elif interval == 'year':
309            tt = t[:]
310            t[0] -= 1
311            lt = self.norm_time(t)
312            marks = [lt[:3], tt[:3]]
313            for i in range(5):
314                tt[1] -= 2
315                marks.append(self.norm_time(tt)[:3])
316            delta = 7
317            format = '%d.%m.%y'
318        else:                           # all
319            tt = t[:]
320            tt[1] -= 1
321            tt = self.norm_time(tt)
322            if results:
323                lt = self.norm_time(min(results.keys()))
324                lt = min(lt, tt)        # min 1 month
325            else:
326                lt = tt
327            dt = time.time()-time.mktime(tuple(lt))
328            if dt > 63072000:           # 2 years
329                d = 6
330            elif dt > 31536000:         # 1 year
331                d = 4
332            elif dt > 10512000:         # 4 month
333                d = 2
334            else:
335                d = 1
336            marks = [lt[:3], t[:3]]
337            while t > lt:
338                t[1] -= d
339                t = self.norm_time(t)
340                marks.append(t[:3])
341            delta = 7
342            format = '%d.%m.%y'
343
344        res = []
345        ct = list(time.localtime())
346        while lt <= ct:
347            # assert type(lt) is type(ct)
348            played = 0
349            won = 0
350            text = None
351            for i in range(delta):
352                if (not marks) or ct[:3] in marks:
353                    text = time.strftime(format, tuple(ct))
354                t = tuple(ct[:3])
355                if t in results:
356                    played += results[t][0]
357                    won += results[t][1]
358                ct[2] -= 1
359                ct = self.norm_time(ct)
360            res.append((text, played, won))
361        res.reverse()
362        # from pprint import pprint; pprint(res)
363        return res
364