...
[iramuteq] / tableau.py
1 # -*- coding: utf-8 -*-
2 #Author: Pierre Ratinaud
3 #Copyright (c) 2010 Pierre Ratinaud
4 #License: GNU/GPL
5
6 import codecs
7 import sys
8 import xlrd
9 import ooolib
10 import os
11 import tempfile
12 import re
13 import htmlentitydefs
14 import shelve
15 from functions import DoConf
16 from uuid import uuid4
17 from chemins import PathOut
18 import logging
19
20 log = logging.getLogger('iramuteq.tableau')
21
22 ##
23 # Removes HTML or XML character references and entities from a text string.
24 #
25 # @param text The HTML (or XML) source text.
26 # @return The plain text, as a Unicode string, if necessary.
27
28 def unescape(text):
29     def fixup(m):
30         #apos is not in the dictionnary
31         htmlentitydefs.name2codepoint['apos'] = ord("'")
32         text = m.group(0)
33         if text[:2] == "&#":
34             # character reference
35             try:
36                 if text[:3] == "&#x":
37                     return unichr(int(text[3:-1], 16))
38                 else:
39                     return unichr(int(text[2:-1]))
40             except ValueError:
41                 pass
42         else:
43             try:
44                 text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
45             except KeyError:
46                 pass
47         return text # leave as is
48     return re.sub("&#?\w+;", fixup, text)
49
50 def UpdateDico(Dico, word, line):
51     if word in Dico :
52         Dico[word][0] += 1
53         Dico[word][1].append(line)
54     else:
55         Dico[word] = [1, [line]]
56         
57 def copymatrix(tableau):
58     log.info('copy matrix')
59     copymat = Tableau(tableau.parent, parametres = tableau.parametres)
60     copymat.linecontent = tableau.linecontent
61     copymat.csvtable = tableau.csvtable
62     copymat.pathout = tableau.pathout
63     copymat.colnames = tableau.colnames
64     copymat.rownb = tableau.rownb
65     copymat.colnb = tableau.colnb
66     if copymat.csvtable is None :
67         copymat.open()
68     return copymat
69
70 class Tableau() :
71     def __init__(self, parent, filename = '', filetype = 'csv', encodage = 'utf-8', parametres = None) :
72         self.parent = parent
73         if parametres is None :
74             self.parametres = DoConf(self.parent.ConfigPath['matrix']).getoptions('matrix')
75             self.parametres['pathout'] = PathOut(filename, 'matrix').mkdirout()
76             self.parametres['originalpath'] = filename
77             self.parametres['filetype'] = filetype
78             self.parametres['encodage'] = encodage
79             #self.parametre['pathout'] = os.path.dirname(os.path.abspath(filename))
80             self.parametres['mineff'] = 3
81             self.parametres['syscoding'] = sys.getdefaultencoding()
82             self.parametres['type'] = 'matrix'
83             self.parametres['matrix_name'] = os.path.basename(filename)
84             self.parametres['uuid'] = str(uuid4())
85             self.parametres['shelves'] = os.path.join(self.parametres['pathout'], 'shelve.db')
86             self.parametres['ira'] = os.path.join(self.parametres['pathout'], 'Matrix.ira')
87         else :
88             self.parametres = parametres
89         self.pathout = PathOut(filename = filename, dirout = self.parametres['pathout'])
90         self.csvtable = None
91         self.sups = {}
92         self.actives = {}
93         self.listactives = None
94         self.content = []
95         self.linecontent = []
96         self.isbinary = False
97         self.binary = []
98         self.firstrowiscolnames = True
99         self.colnames = []
100         self.firstcolisrownames = True
101         self.rownames = []
102         self.colnb = 0
103         self.rownb = 0
104         self.classes = []
105         #self.parametres = self.parametre
106
107     def read_tableau(self, fileout) :
108         d=shelve.open(fileout)
109         #self.parametres = d['parametres']
110         #if 'syscoding' not in self.parametres :
111         #    self.parametres['syscoding'] = sys.getdefaultencoding()
112         self.actives = d['actives']
113         self.sups = d['sups']
114         self.classes = d['classes']
115         self.listactives = d['listactives']
116         if 'listet' in d :
117             self.listet = d['listet']
118         if 'selected_col' in d :
119             self.selected_col = d['selected_col']
120         if 'datas' in d :
121             self.datas = d['datas']
122         if 'lchi' in d :
123             self.lchi = d['lchi']
124         if 'content' in d :
125             self.content = d['content']
126         d.close()
127     
128     def open(self):
129         print 'open matrix'
130         self.read_csvfile()
131         self.colnames = self.csvtable[0][1:]
132         self.rownb = len(self.linecontent)
133         self.colnb = len(self.linecontent[0])
134
135     def save_tableau(self, fileout) :
136         d=shelve.open(fileout)
137         d['parametres'] = self.parametres
138         d['actives'] = self.actives
139         d['sups'] = self.sups
140         d['classes'] = self.classes
141         d['listactives'] = self.listactives
142         if 'listet' in dir(self) :
143             d['listet'] = self.listet
144         if 'selected_col' in dir(self) :
145             d['selected_col'] = self.selected_col
146         if 'datas' in dir(self) :
147             d['datas'] = self.datas
148         if 'lchi' in dir(self) :
149             d['lchi'] = self.lchi
150         d['content'] = self.content
151         d.close()
152
153     def make_content(self) :
154         self.pathout.createdir(self.parametres['pathout'])
155         if self.parametres['filetype'] == 'csv' :
156             self.read_csv()
157         elif self.parametres['filetype'] == 'xls' :
158             self.read_xls()
159         elif self.parametres['filetype'] == 'ods' :
160             self.read_ods()
161         self.parametres['csvfile'] = os.path.join(self.parametres['pathout'], 'csvfile.csv')
162         self.make_tmpfile()
163         DoConf().makeoptions(['matrix'],[self.parametres], self.parametres['ira'])
164         self.parent.history.addMatrix(self.parametres)
165
166     def read_xls(self) :
167         #FIXME : encodage
168         #print '############## ENCODING IN EXCEL #######################'
169         #datafile = xlrd.open_workbook(self.parametre['filename'], encoding_override="azerazerazer")
170         datafile = xlrd.open_workbook(self.parametres['originalpath'])
171         datatable = datafile.sheet_by_index(self.parametres['sheetnb']-1)
172         self.linecontent = [[str(datatable.cell_value(rowx = i, colx = j)).replace(u'"','').replace(u';',' ').replace(u'\n',' ').replace('\r', ' ').replace('\t', ' ').strip() for j in range(datatable.ncols)] for i in range(datatable.nrows)]
173
174     def read_ods(self) :
175         doc = ooolib.Calc(opendoc=self.parametres['originalpath'])
176         doc.set_sheet_index(0)
177         (cols, rows) = doc.get_sheet_dimensions()
178         for row in range(1, rows + 1):
179             ligne = []
180             for col in range(1, cols + 1):
181                 data = doc.get_cell_value(col, row)
182                 if data is not None :
183                     ligne.append(unescape(data[1].replace(u'"','').replace(u';',' ').replace(u'\n', ' ').replace('\t', ' ').strip()))
184                 else :
185                     ligne.append('')
186             self.linecontent.append(ligne)
187
188     def read_csv(self) :
189         with codecs.open(self.parametres['originalpath'], 'r', self.parametres['encodage']) as f :
190             content = f.read() 
191         self.linecontent = [line.split(self.parametres['colsep']) for line in content.splitlines()]
192         self.linecontent = [[val.replace(u'"','').replace(u';',' ').replace('\t', ' ').strip() for val in line] for line in self.linecontent]
193
194     def write_csvfile(self) :
195         with open(self.parametres['csvfile'], 'w') as f :
196             f.write('\n'.join(['\t'.join(line) for line in self.csvtable]))
197
198     def make_tmpfile(self) :
199         self.rownb = len(self.linecontent)
200         self.colnb = len(self.linecontent[0])
201         if self.firstrowiscolnames :
202             self.colnames = self.linecontent[0]
203             self.linecontent.pop(0)
204             self.rownb -= 1
205         else :
206             self.colnames = ['_'.join([u'colonne', `i`]) for i in range(self.colnb)]
207         if self.firstcolisrownames :
208             self.rownames = [row[0] for row in self.linecontent]
209             self.linecontent = [row[1:] for row in self.linecontent]
210             self.colnb -= 1
211             self.idname = self.colnames[0]
212             self.colnames.pop(0)
213             self.check_rownames()
214         else :
215             self.rownames = [`i` for i in range(self.rownb)]
216             self.idname = u'identifiant'
217         self.csvtable = [[self.idname] + self.colnames] + [[self.rownames[i]] + self.linecontent[i] for i in range(len(self.rownames))] 
218         self.write_csvfile()
219
220     def read_csvfile(self):
221         with codecs.open(self.parametres['csvfile'], 'r', self.parametres['syscoding']) as f:
222             self.csvtable = [line.split('\t') for line in f.read().splitlines()]
223         self.linecontent = [line[1:] for line in self.csvtable]
224         self.linecontent.pop(0)
225         
226     def extractfrommod(self, col, val):
227         return ([''] + self.colnames) + [line for line in self.csvtable[1:] if line[col + 1] == val]
228
229     def splitfromvar(self, col):
230         newtabs = {}
231         for line in self.csvtable[1:] :
232             mod = line[col+1]
233             if mod in newtabs :
234                 newtabs[mod].append(line)
235             else :
236                 newtabs[mod] = [line]
237         for mod in newtabs :
238             newtabs[mod].insert(0, [''] + self.colnames)
239         return newtabs
240
241     def check_rownames(self) :
242         if len(self.rownames) == len(list(set(self.rownames))) :
243             print u'row names ok'
244         else :
245             print u'les noms de lignes ne sont pas uniques, ils sont remplaces'
246             self.rownames = [`i` for i in range(self.rownb)]
247
248     def make_unique_list(self) :
249         return list(set([val for line in self.linecontent for val in line if val.strip() != '']))
250
251     def make_dico(self, selcol) :
252         dico = {}
253         for i, line in enumerate(selcol) :
254             for forme in line:
255                 if forme.strip() != '' :
256                     UpdateDico(dico, forme, i)
257         return dico
258     
259     def select_col(self, listcol) :
260         dc = dict(zip(listcol, listcol))
261         selcol = [[val for i, val in enumerate(row) if i in dc] for row in self.linecontent]
262         return selcol
263     
264     def countmultiple(self, liscol):
265         return self.make_dico(self.select_col(liscol))
266
267     def getactlistfromselection(self, listact) :
268         selcol = self.select_col(listact)
269         self.actives = self.make_dico(selcol)
270         return [[val, self.actives[val][0]] for val in self.actives]       
271
272     def make_listactives(self) :
273         self.listactives = [val for val in self.actives if val != 'NA' and self.actives[val] >= self.parametres['mineff']]
274     
275     def write01(self, fileout, dico, linecontent) :
276         if self.listactives is None :
277             self.listactives = [val for val in dico if val != 'NA' and dico[val] >= self.parametres['mineff']]
278         out = [['0' for forme in self.listactives] for line in linecontent]
279         for i, forme in enumerate(self.listactives) :
280             for line in dico[forme][1] :
281                 out[line][i] = '1'
282         #out = [[self.rownames[i]] + out[i] for i in range(len(linecontent))] 
283         #out.insert(0,[self.idname] + self.listactives)
284         out.insert(0, self.listactives)
285         with open(fileout, 'w') as f :
286             f.write('\n'.join([';'.join(line) for line in out]))
287
288     def make_01_from_selection(self, listact, listsup = None, dowrite = True) :
289         selcol = self.select_col(listact)
290         self.actives = self.make_dico(selcol)
291         self.write01(self.pathout['mat01.csv'], self.actives, selcol)
292         if listsup is not None :
293             selcol = self.select_col(listsup)
294             self.sups = self.make_dico(selcol)
295
296     def make_01_alc_format(self, fileout) :
297         for i, ligne in enumerate(self.linecontent) :
298             for forme in ligne:
299                 if len(forme) >= 1:
300                     if forme[0] == u'*':
301                         UpdateDico(self.sups, forme, i)
302                     else:
303                         UpdateDico(self.actives, forme, i)        
304         self.listactives = [val for val in self.actives if self.actives[val][0] >= self.parametres['mineff']]
305         table = [['0' for i in range(len(self.listactives))] for j in range(self.rownb)]
306         for i, val in enumerate(self.listactives) :
307             for j, line in enumerate(self.linecontent) :
308                 if val in line :
309                     table[j][i] = '1'
310         #table = [[self.rownames[i]] + table[i] for i in range(len(self.rownames))]
311         #table.insert(0, [self.idname] + self.listactives)
312         table.insert(0, self.listactives)
313         with open(fileout, 'w') as f:
314             f.write('\n'.join([';'.join(line) for line in table]))
315
316     def printtable(self, filename, Table, sep = ';'):
317         with open(filename, 'w') as f :
318             f.write('\n'.join([sep.join(line) for line in Table]))
319     
320     def buildprofil(self) :
321         with open(self.pathout['uce'], 'rU') as filein :
322             content = filein.readlines()
323         content.pop(0)
324         lsucecl = []
325         dicocl = {}
326         for i, line in enumerate(content) :
327             line = line.replace('\n', '').replace('"', '').split(';')
328             UpdateDico(dicocl, line[1], i)
329             lsucecl.append([int(line[0]) - 1, int(line[1])])
330         self.classes = lsucecl
331         nlist = [[nbuce, cl] for nbuce, cl in lsucecl if cl != 0]
332         self.ucecla = len(nlist)
333         if '0' in dicocl :
334             self.clnb = len(dicocl) - 1
335         else:
336             self.clnb = len(dicocl)
337
338         tablecont = []
339         for active in self.listactives :
340             line = [active]
341             line0 = [0] * self.clnb
342             line += line0
343             for i in range(0, self.clnb) :
344                 for uce, cl in nlist:
345                     if cl == i + 1 :
346                         if active in self.linecontent[uce]:
347                             line[i + 1] += 1
348             if sum(line[1:]) > self.parametres['mineff']:
349                 tablecont.append([line[0]] + [`don` for don in line if type(don) == type(1)])
350         
351         tablecontet = []
352         for sup in self.sups :
353             line = [sup]
354             line0 = [0] * self.clnb
355             line += line0
356             for i in range(0, self.clnb) :
357                 for uce, cl in nlist:
358                     if cl == i + 1 :
359                         if sup in self.linecontent[uce]:
360                             line[i + 1] += 1
361             tablecontet.append([line[0]] + [`don` for don in line if type(don) == type(1)])
362             
363         self.printtable(self.pathout['ContEtOut'], tablecontet)
364         self.printtable(self.pathout['Contout'], tablecont)        
365
366     def get_colnames(self) :
367         return self.colnames[:]
368
369     def make_table_from_classe(self, cl, la) :
370         ln = [line[0] for line in self.classes if line[1] == cl]
371         out = [['0' for col in la] for line in ln]
372         for i, act in enumerate(la) :
373             for j, line in enumerate(ln) :
374                 if line in self.actives[act][1] :
375                     out[j][i] = '1'
376         out.insert(0,[act for act in la])
377         return out
378         
379         
380
381 #filename = 'corpus/cent3.csv'
382 #filename = 'corpus/agir2sortie.csv'
383 #tab = Tableau('',filename, encodage='utf-8')
384 #tab.parametre['csvfile'] = tab.parametre['filename']
385 #tab.parametre['sep'] = '\t'
386 #tab.firstrowiscolnames = True
387 #tab.firstcolisrownames = False
388 #tab.read_data()
389 #tab.make_01('corpus/matrice01.csv')