File: statistics.py

package info (click to toggle)
solfege 2.0.4-4
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 3,200 kB
  • ctags: 1,844
  • sloc: python: 12,160; xml: 5,458; ansic: 1,486; makefile: 562; sh: 233
file content (313 lines) | stat: -rw-r--r-- 11,580 bytes parent folder | download
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# GNU Solfege - eartraining for GNOME
# Copyright (C) 2000, 2001, 2002, 2003  Tom Cato Amundsen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import sys
if __name__ == '__main__':
    import pygtk
    pygtk.require('2.0')
    sys.path.append(".")

import time, os, pickle
import shutil
import utils
import configureoutput

# version 1, the first version was introduced with Solfege 2.0
statistics_version = 1

STATISTICS_SAVE_DIR = os.path.join(os.path.expanduser('~'), '.solfege')
if not os.path.exists(STATISTICS_SAVE_DIR):
    for ver in '1.9', '1.4':
        if os.path.exists(os.path.join(os.path.expanduser('~'), '.solfege%s' % ver)):
            shutil.copytree(os.path.join(os.path.expanduser('~'), '.solfege%s' % ver),
                            STATISTICS_SAVE_DIR)
            break
    if not os.path.exists(STATISTICS_SAVE_DIR):
        os.mkdir(STATISTICS_SAVE_DIR)
    f = file(os.path.join(STATISTICS_SAVE_DIR, 'statistics_version'), 'w')
    print >> f, statistics_version
    f.close()

def get_statistics_version():
    """
    Return as an int, the version number the statistics stored in
    STATISTICS_SAVE_DIR has.
    """
    try:
        f = file(os.path.join(STATISTICS_SAVE_DIR, 'statistics_version'), 'r')
    except IOError, e:
        print >> sys.stderr, "While opening the file $HOME/.solfege/statistics_version:"
        print >> sys.stderr, e
        return 0
    s = f.read()
    f.close()
    try:
        return int(s)
    except ValueError, e:
        print >> sys.stderr, "While parsing the content of $HOME/.solfege/statistics_version:"
        print >> sys.stderr, e
        return 0

statistics_on_disk_version = get_statistics_version()
if statistics_on_disk_version > statistics_version:
    usable_statistics = None
    print >> sys.stderr, """The statistics stored in $HOME/.solfege have been created by a newer version of
Solfege, and that release has changed the way statistics are saved. If you want
to record statistics, you must either use that newer version of Solfege, or
delete (or rename) the directory $HOME/.solfege\n"""
elif statistics_on_disk_version == 0:
    usable_statistics = None
    print >> sys.stderr, """
Something is strange with the statistics stored in $HOME/.solfege.
"""
else:
    usable_statistics = 1
YEAR = 0
MONTH = 1
DAY = 2
HOURS = 3
MINUTES = 4
SECONDS = 5
WEEKDAY = 6
JULIANDAY = 7

class AbstractStatistics:
    def __init__(self, teacher):
        self.m_t = teacher
        self.m_session_stat = {}
        self.m_today_stat = {}
        self.m_last7_stat = {}
        self.m_total_stat = {}
        self.m_dicts = {'session' : self.m_session_stat,
                        'today'   : self.m_today_stat,
                        'last7'   : self.m_last7_stat,
                        'total'   : self.m_total_stat}
        self.m_savepath = os.path.join(STATISTICS_SAVE_DIR, self.m_t.m_exname)
        if usable_statistics:
            if not os.path.exists(self.m_savepath):
                os.mkdir(self.m_savepath)
    def _add(self, question, answer):
        for D in [self.m_session_stat, self.m_today_stat,
                  self.m_last7_stat, self.m_total_stat]:
            if not D.has_key(question):
                D[question] = {}
            if not D[question].has_key(answer):
                D[question][answer] = 0
            D[question][answer] = D[question][answer] + 1
    def add_correct(self, answer):
        self._add(answer, answer)
    def add_wrong(self, question, answer):
        self._add(question, answer)
    def merge_stat_dicts(self, A, B):
        "Add content of B to A"
        for k in B.keys():
            if not A.has_key(k):
                A[k] = {}
            for n in B[k].keys():
                if not A[k].has_key(n):
                    A[k][n] = 0
                A[k][n] = A[k][n] + B[k][n]
        return A
    def load_statistics(self, datadir):
        if not usable_statistics:
            return
        for filename in os.listdir(datadir):
            t = int(filename)
            lt = time.localtime(t)
            f = open(os.path.join(datadir, filename), 'r')
            D = pickle.load(f)
            f.close()
            now = int(time.time())
            lt_today = time.localtime(time.time())
            if lt[YEAR] == lt_today[YEAR] and \
                    lt[JULIANDAY] == lt_today[JULIANDAY]:
                self.m_today_stat = self.merge_stat_dicts(self.m_today_stat, D)
            if now - t < 60*60*24*7: # 7 days
                self.m_last7_stat = self.merge_stat_dicts(self.m_last7_stat, D)
            self.m_total_stat = self.merge_stat_dicts(self.m_total_stat, D)
    def get(self):
        """Will return a 0 <= value <= 1.0 that say how many percent is
        correct in this session
        """
        c = t = 0
        for k in self.m_session_stat.keys():
            if self.m_session_stat[k].has_key(k):
                c = c + self.m_session_stat[k][k]
            t = t + self.get_num_guess(self.m_session_stat, k)
        if t > 0:
            return 1.0 * c / t
        else:
            return 0.0
    def display(self):
        print
        v = self.m_session_stat.keys()
        v.sort()
        for x in v:
            print x, self.m_session_stat[x]
    def get_keys(self, all=0):
        """
        by default it returns the keys for all questions that have
        been asked. If 'all' is true, it also includes the keys for
        all the wrong answers.
        """
        keys = []
        for st in self.m_session_stat, self.m_today_stat, self.m_last7_stat, self.m_total_stat:
            for k in st.keys():
                if k not in keys:
                    keys.append(k)
                if all:
                    for wk in self.m_total_stat[k].keys():
                        if wk not in keys:
                            keys.append(wk)
        keys.sort()
        return keys
    def get_num_guess(self, st, key):
        if not st.has_key(key):
            return 0
        t = 0
        for i in st[key].keys():
            t = t + st[key][i]
        return t
    def get_percentage_correct(self, st, key):
        """
        This was added to be used for harmonic-interval.
        """
        if not st.has_key(key):
            return 0.0
        if st[key].has_key(key):
            num_correct = st[key][key]
        else:
            num_correct = 0
        total = 0
        for n in st[key].keys():
            total = total + st[key][n]
        return num_correct * 100.0 / total
    def key_to_pretty_name(self, k):
        return k
    def get_label_style(self):
        return 'normal'
    def reset_session(self):
        self.m_session_stat = {}


class Statistics(AbstractStatistics):
    def __init__(self, teacher):
        AbstractStatistics.__init__(self, teacher)
        self.load_statistics(self.m_savepath)
    def save_data(self):
        if not usable_statistics:
            return
        if self.m_session_stat:
            f = open(os.path.join(self.m_savepath, str(int(time.time()))), 'w')
            pickle.dump(self.m_session_stat, f)
            f.close()


class LessonStatistics(AbstractStatistics):
    def __init__(self, teacher):
        AbstractStatistics.__init__(self, teacher)
        self.m_cur_collection = self.m_cur_file = None
        self.lessonfile_changed(self.m_t.get_string('lessoncollection'),
                                self.m_t.get_string('lessonfile'))
    def get_label_style(self):
        return self.m_t.m_P.header.labelformat
    def lessonfile_changed(self, new_collection, new_file):
        if not new_file:
            return
        if self.m_cur_file and self.m_session_stat != {}:
            self.save_data()

        self.m_session_stat = {}
        self.m_today_stat = {}
        self.m_last7_stat = {}
        self.m_total_stat = {}
        self.m_cur_collection = new_collection
        self.m_cur_file = new_file
        if not usable_statistics:
            return
        self.create_statistics_dir()

        # if the lessonfile has changed, we have to
        # delete all statistics just to be save.
        if self.get_hash_of_statistics() != self.get_hash_of_lessonfile():
            p = os.path.join(self.m_savepath,
                             self.m_cur_collection,
                             self.m_cur_file)
            if os.path.isfile("%s_hash" % p):
                os.remove("%s_hash" % p)
            for f in os.listdir(p):
                os.remove(os.path.join(p, f))
        self.load_statistics(os.path.join(self.m_savepath,
                                          self.m_cur_collection,
                                          self.m_cur_file))
    def save_data(self):
        if not usable_statistics:
            return
        if self.m_session_stat == {}:
            return
        #save the hashvalue for the lessonfile this statistics was made with
        f = open(os.path.join(self.m_savepath,
                              self.m_cur_collection,
                              '%s_hash' % self.m_cur_file), 'w')
        f.write(str(self.get_hash_of_lessonfile()))
        f.close()
        f = open(os.path.join(self.m_savepath,
                              self.m_cur_collection,
                              self.m_cur_file,
                              str(int(time.time()))), 'w')
        pickle.dump(self.m_session_stat, f)
        f.close()
    def create_statistics_dir(self):
        dir = os.path.join(self.m_savepath, self.m_cur_collection)
        if not os.path.exists(dir):
            os.mkdir(dir)

        dir = os.path.join(dir, self.m_cur_file)
        if not os.path.exists(dir):
            os.mkdir(dir)
    def get_hash_of_statistics(self):
        # !wrong function name!
        # get the hash for the content of the lessonfile that was used last
        # time statistics was saved
        if os.path.isfile(os.path.join(self.m_savepath,
                                       self.m_cur_collection,
                                       '%s_hash' % self.m_cur_file)):
            s = open(os.path.join(self.m_savepath,
                                         self.m_cur_collection,
                                         '%s_hash' % self.m_cur_file)).read()
            try:
                return int(s)
            except:
                return 0
        else:
            return 0
    def get_hash_of_lessonfile(self):
        return hash(open(os.path.expanduser(os.path.join(
            self.m_t.get_string('lessoncollections/%s' % self.m_cur_collection),
            self.m_cur_file))).read())


class IntervalStatistics(Statistics):
    def key_to_pretty_name(self, key):
        return utils.int_to_intervalname(key, 1, 1)

class HarmonicIntervalStatistics(Statistics):
    def key_to_pretty_name(self, key):
        return utils.int_to_intervalname(key, 1, 0)

if __name__ == '__main__':
    print "statistics_version:", get_statistics_version()