windows
[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         print(self.actives, self.parametres['mineff'])
294         self.listactives = [val for val in self.actives if val != 'NA' and self.actives[val][0] >= self.parametres['mineff']]
295
296     def write01(self, fileout, dico, linecontent) :
297         if self.listactives is None :
298             self.listactives = [val for val in dico if val != 'NA' and dico[val][0] >= self.parametres['mineff']]
299         out = [['0' for forme in self.listactives] for line in linecontent]
300         for i, forme in enumerate(self.listactives) :
301             for line in dico[forme][1] :
302                 out[line][i] = '1'
303         #out = [[self.rownames[i]] + out[i] for i in range(len(linecontent))] 
304         #out.insert(0,[self.idname] + self.listactives)
305         out.insert(0, self.listactives)
306         with open(fileout, 'w') as f :
307             f.write('\n'.join([';'.join(line) for line in out]))
308
309     def make_01_from_selection(self, listact, listsup = None, dowrite = True) :
310         selcol = self.select_col(listact)
311         self.actives = self.make_dico(selcol)
312         self.write01(self.pathout['mat01.csv'], self.actives, selcol)
313         if listsup is not None :
314             selcol = self.select_col(listsup)
315             self.sups = self.make_dico(selcol)
316
317     def make_01_alc_format(self, fileout) :
318         for i, ligne in enumerate(self.linecontent) :
319             for forme in ligne:
320                 if len(forme) >= 1:
321                     if forme[0] == '*':
322                         UpdateDico(self.sups, forme, i)
323                     else:
324                         UpdateDico(self.actives, forme, i)        
325         self.listactives = [val for val in self.actives if self.actives[val][0] >= self.parametres['mineff']]
326         table = [['0' for i in range(len(self.listactives))] for j in range(self.rownb)]
327         for i, val in enumerate(self.listactives) :
328             for j, line in enumerate(self.linecontent) :
329                 if val in line :
330                     table[j][i] = '1'
331         #table = [[self.rownames[i]] + table[i] for i in range(len(self.rownames))]
332         #table.insert(0, [self.idname] + self.listactives)
333         table.insert(0, self.listactives)
334         with open(fileout, 'w') as f:
335             f.write('\n'.join([';'.join(line) for line in table]))
336
337     def printtable(self, filename, Table, sep = ';'):
338         with open(filename, 'w') as f :
339             f.write('\n'.join([sep.join(line) for line in Table]))
340
341     def buildprofil(self) :
342         with open(self.pathout['uce'], 'r') as filein :
343             content = filein.readlines()
344         content.pop(0)
345         lsucecl = []
346         dicocl = {}
347         for i, line in enumerate(content) :
348             line = line.replace('\n', '').replace('"', '').split(';')
349             UpdateDico(dicocl, line[1], i)
350             lsucecl.append([int(line[0]) - 1, int(line[1])])
351         self.classes = lsucecl
352         nlist = [[nbuce, cl] for nbuce, cl in lsucecl if cl != 0]
353         self.ucecla = len(nlist)
354         if '0' in dicocl :
355             self.clnb = len(dicocl) - 1
356         else:
357             self.clnb = len(dicocl)
358         tablecont = []
359         for active in self.listactives :
360             line = [active]
361             line0 = [0] * self.clnb
362             line += line0
363             for i in range(0, self.clnb) :
364                 for uce, cl in nlist:
365                     if cl == i + 1 :
366                         if active in self.linecontent[uce]:
367                             line[i + 1] += 1
368             if sum(line[1:]) > self.parametres['mineff']:
369                 tablecont.append([line[0]] + [repr(don) for don in line if type(don) == type(1)])
370         tablecontet = []
371         for sup in self.sups :
372             line = [sup]
373             line0 = [0] * self.clnb
374             line += line0
375             for i in range(0, self.clnb) :
376                 for uce, cl in nlist:
377                     if cl == i + 1 :
378                         if sup in self.linecontent[uce]:
379                             line[i + 1] += 1
380             tablecontet.append([line[0]] + [repr(don) for don in line if type(don) == type(1)])
381             
382         self.printtable(self.pathout['ContEtOut'], tablecontet)
383         self.printtable(self.pathout['Contout'], tablecont)        
384
385     def get_colnames(self) :
386         return self.colnames[:]
387
388     def make_table_from_classe(self, cl, la) :
389         ln = [line[0] for line in self.classes if line[1] == cl]
390         out = [['0' for col in la] for line in ln]
391         for i, act in enumerate(la) :
392             for j, line in enumerate(ln) :
393                 if line in self.actives[act][1] :
394                     out[j][i] = '1'
395         out.insert(0,[act for act in la])
396         return out