#!/usr/bin/python # -*- coding: utf-8 -*- ''' Data module for a KaiSD Text Tools. (c) 2013 Ivan "Kai SD" Korystin License: GPLv3 ''' import csv, codecs, cStringIO class Data(object): ''' Empty data class. Can be used for a subclassing or procedural data creation. ''' def __init__(self, *args, **kwargs): ''' Constructor ''' self.keys = [] self.rows = [] def __getitem__(self, pair): ''' Returns a value for given key and row. ''' key = pair[0] row = pair[1] keys = self.keys rows = self.rows if key in keys: if len(rows) > row: return rows[row][keys.index(key)] else: raise BaseException('Row %i not found in data' % (row)) else: raise BaseException('Named value %s not found in data' % (key)) def __setitem__(self, pair, value): ''' Sets a value for given key and row. ''' key = pair[0] row = pair[1] keys = self.keys rows = self.rows if key in keys: if len(rows) > row: rows[row][keys.index(key)] = value else: raise BaseException('Row %i not found in data' % (row)) else: raise BaseException('Named value %s not found in data' % (key)) def __str__(self): ''' Returns data as string. ''' return str((self.keys, self.rows)) def __repr__(self): return self.__str__() def has_key(self, key): ''' Returns True if given key exists in data ''' return key in self.keys def add_rows(self, n=1): ''' Adds some empty rows to the data. ''' keys = self.keys rows = self.rows for n in xrange(0, n): row = [] for k in keys: row.append('') rows.append(row) def add_keys(self, *h): ''' Adds new keys to the data. ''' keys = self.keys rows = self.rows for i in h: keys.append(i) for r in rows: for i in h: r.append('') def del_row(self, idx): ''' Removes giver row from data ''' del self.rows[idx] def col_by_key(self, key): ''' Returns a column by header's name ''' keys = self.keys if key in keys: idx = keys.index(key) return self.col_by_idx(idx) else: raise BaseException('Named value %s not found in data' % (key)) def col_by_idx(self, idx): ''' Returns a column by header's index ''' cols = [] rows = self.rows for r in rows: if len(r) > idx: cols.append(r[idx]) return tuple(cols) def row_by_idx(self, idx): ''' Returns a row by index. ''' return tuple(self.rows[idx]) def transpose(self, key_idx = 0): ''' Returns the transposed copy of the data. key_idx - index of the column, that contains keywords (default: 0) ''' new_keys = [self.keys[key_idx]] new_keys += list(self.col_by_idx(key_idx)) new_data = Data() new_data.keys = new_keys idx = 0 for k in self.keys: if not idx == key_idx: new_row = [k] new_row += self.col_by_idx(idx) new_data.rows.append(new_row) idx += 1 return new_data def add_data(self, other): ''' Adds rows from another data table to this one. ''' sk = self.keys ok = other.keys for k in ok: if not k in sk: self.add_keys(k) for r in other.rows: new_row = [] if len(r) >= len(sk): for k in sk: if k in ok: new_row.append(r[ok.index(k)]) else: new_row.append('') self.rows.append(new_row) class CSVData(Data): ''' Class for reading CSV files. ''' class Reader: class Recoder: def __init__(self, f, encoding): self.reader = codecs.getreader(encoding)(f) def __iter__(self): return self def next(self): return self.reader.next().encode("utf-8") def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwargs): f = self.Recoder(f, encoding) self.reader = csv.reader(f, dialect=dialect, **kwargs) def next(self): row = self.reader.next() return [unicode(s, "utf-8") for s in row] def __iter__(self): return self class Writer: def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwargs): self.queue = cStringIO.StringIO() self.writer = csv.writer(self.queue, dialect=dialect, **kwargs) self.stream = f self.encoder = codecs.getincrementalencoder(encoding)() def writerow(self, row): self.writer.writerow([unicode(s).encode("utf-8") for s in row]) data = self.queue.getvalue() data = data.decode("utf-8") data = self.encoder.encode(data) self.stream.write(data) self.queue.truncate(0) def writerows(self, rows): for row in rows: self.writerow(row) def __init__(self, file, encoding='utf-8', delimiter=';', quotechar='"', **kwargs): ''' Constructor. filename - CSV table filename encoding - CSV table encoding (default: utf-8) delimiter - CSV table delimiter (default: ;) quotechar - CSV table quotechar (default: ") ''' if file: if type(file) == str: with open(file) as f: csvfile = self.Reader(f, encoding=encoding, delimiter=delimiter, quotechar=quotechar) else: csvfile = self.Reader(file, encoding=encoding, delimiter=delimiter, quotechar=quotechar) source_data = [] source_keys = None; for i in csvfile: if not source_keys: source_keys = i else: for k in xrange(0, len(i)): try: i[k] = int(i[k]) except: try: i[k] = float(i[k]) except: i[k] = i[k] source_data.append(i) self.keys = source_keys self.rows = source_data else: super(CSVData, self).__init__() def export_csv(self, filename, encoding='utf-8', delimiter=';', quotechar='"', **kwargs): ''' Saves the data to CSV file filename - CSV table filename encoding - CSV table encoding (default: utf-8) delimiter - CSV table delimiter (default: ;) quotechar - CSV table quotechar (default: ") ''' with open(filename, 'wb') as f: csvfile = self.Writer(f, encoding='utf-8', delimiter=';', quotechar='"', **kwargs) csvfile.writerow(self.keys) csvfile.writerows(self.rows)