From e1ae16944e36a306304b737984f9aca0bf188ae1 Mon Sep 17 00:00:00 2001 From: ville rantanen Date: Thu, 25 Jul 2013 14:17:13 +0300 Subject: [PATCH] CSV editor based on sc --- csvedit.py | 267 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100755 csvedit.py diff --git a/csvedit.py b/csvedit.py new file mode 100755 index 0000000..912f19e --- /dev/null +++ b/csvedit.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python +# +# Copyright 2011 Ville Rantanen +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# + +'''SC based CSV editor.''' + +__author__ = "Ville Rantanen" + +__version__ = "0.1" + +import sys,os +import csv +from argparse import ArgumentParser +import unicodedata, re +import subprocess +import shutil + +def which(program): + import os + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + +class SCReader: + """ Class for reading SC files. + + """ + def __init__(self,fileobject): + self.file=fileobject + self.parserre=re.compile('.* ([A-Z]+)([0-9]+) = (.*)') + + def _parse_row(self,string): + col=None + row=None + content=None + m=self.parserre.match(string.strip()) + if m: + col=self.alpha_to_column(m.group(1)) + row=self.try_int(m.group(2)) + content=m.group(3) + return col,row,content + + def try_int(self,string): + + try: + return int(string) + except: + return string + + def alpha_to_column(self,alpha): + ''' Returns a column number from spreadsheet column alphabet ''' + n=0 + o=0 + for char in alpha[::-1]: + o+=(ord(char.upper())-64)*(26**n) + n+=1 + return int(o-1) + + + def next(self): + ''' Returns the next row in the table, three items: column, row, content''' + return self._parse_row(self.reader.next()) + + def __iter__(self): + for row in self.file: + yield self._parse_row(row) + +class SCWriter: + """ Class for writing SC files. + + """ + def __init__(self,fileobject): + self.file=fileobject + self.row=0 + self.col=0 + self.col_lengths=[] + + def parse_row(self,string): + + self.col=0 + for el in row: + self.write_element(self.row,self.col,el) + if len(self.col_lengths) <= self.col: + self.col_lengths.append(max(len(el),8)) + else: + self.col_lengths[self.col]=max(len(el),self.col_lengths[self.col]) + self.col+=1 + self.row+=1 + + def write_element(self,row,col,content): + + colalpha=self.column_to_alpha(col) + content=content.strip('"') + + if self.is_num(content): + self.file.write('let '+colalpha+str(row)+' = ' + str(self.to_num(content))+'\n') + else: + self.file.write('rightstring '+colalpha+str(row)+' = "' + content + '"\n') + + + def column_to_alpha(self,column): + ''' Returns a column alphabet from column number ''' + o=chr(column+64+1) + if column>25: + return self.column_to_alpha((column / 26) -1) + self.column_to_alpha(column % 26); + return o + + def write_row(self,row): + ''' Writes a row as a SC file part ''' + self.parse_row(row) + + def write_formats(self): + + for col in range(len(self.col_lengths)): + self.file.write('format '+self.column_to_alpha(col)+' '+str(self.col_lengths[col])+' 2 0\n') + + def is_num(self,string): + ''' returns the True if string can be converted to number safely ''' + try: + num=int(string) + return True + except: + pass + + try: + num=float(string) + return True + except: + pass + + return False + + def to_num(self,string): + ''' returns the number in the correct data type if string can be converted to number safely ''' + try: + num=int(string) + return num + except: + pass + + try: + num=float(string) + return num + except: + pass + + return string + +def setup_options(): + ''' Setup the command line options ''' + + parser=ArgumentParser() + parser.add_argument("-v",action='version', version=__version__) + parser.add_argument("--version",action='version', version=__version__) + parser.add_argument("-b",action="store_false",dest="backup",default=True, + help="Do not create a backup file.") + parser.add_argument("-i",type=str,dest="delimiter",default="\t", + help="Input delimiter for the CSV, default: [tab]") + parser.add_argument("-D",action="store_true",dest="debug",default=False, + help="Debug mode, i.e. do not delete the SC file.") + parser.add_argument("csv",type=str,action="store", + help="CSV file to edit") + options=parser.parse_args() + return options + +def csv_write(screader,fileout): + ''' writes a CSV from SCReader iterator ''' + content=[] + rows=0 + cols=0 + for row in screader: + if row[0]!=None: + content.append(row) + rows=max(row[1],rows) + cols=max(row[0],cols) + table=[] + for r in range(rows+1): + table.append([]) + for c in range(cols+1): + table[r].append('') + for e in content: + table[e[1]][e[0]]=e[2] + for row in table: + fileout.write('\t'.join(row)+'\n') + + +if not which('sc'): + print('You don\'t seem to have "sc" installed!') + sys.exit(1) + +opts=setup_options() + +f_bkp=opts.csv+'.bkp' +f_sc=opts.csv+'.sc' +f_sc_tmp=opts.csv+'.sc.tmp' + +# copy a backup file +if opts.backup: + shutil.copyfile(opts.csv, f_bkp) + +# Convert CSV -> SC +f_sc_w=open(f_sc,'wt') +f_csv_r=open(opts.csv,'rt') +csv_reader = csv.reader(f_csv_r, + delimiter=opts.delimiter, + doublequote=False, + escapechar='\\', + quoting=csv.QUOTE_NONE) +sc_writer=SCWriter(f_sc_w) +for row in csv_reader: + if len(row)>0: + sc_writer.write_row(row) +sc_writer.write_formats() +f_sc_w.close() +f_csv_r.close() + +# Launch sc +subprocess.call(['sc',f_sc]) +# Calculate values + +process = subprocess.Popen(['sc','-v','-P','%',f_sc], shell=False, stdout=subprocess.PIPE) +f_cs_tmp_w=open(f_sc_tmp,'wt') +for l in process.stdout.readlines(): + f_cs_tmp_w.write(l) +f_cs_tmp_w.close() + +# Convert SC -> CSV +f_sc_r=open(f_sc_tmp,'rt') +f_csv_w=open(opts.csv,'wt') +sc_reader=SCReader(f_sc_r) +csv_write(sc_reader,f_csv_w) +f_sc_r.close() +f_csv_w.close() + +# Delete SC if not in debug mode +if not opts.debug: + os.remove(f_sc) + os.remove(f_sc_tmp) + if os.path.isfile(f_sc+'~'): + os.remove(f_sc+'~') + +