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