hugohm/utils/csveditor.py

170 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
from argparse import ArgumentParser
import atexit
import csv
import os
import readline
import sys
from cmd import Cmd
def init_readline():
histfile = os.path.join(os.path.expanduser("~"), ".hmwebot_history")
try:
readline.read_history_file(histfile)
h_len = readline.get_current_history_length()
except FileNotFoundError:
open(histfile, 'wb').close()
h_len = 0
def save(prev_h_len, histfile):
new_h_len = readline.get_current_history_length()
readline.set_history_length(1000)
readline.append_history_file(new_h_len - prev_h_len, histfile)
atexit.register(save, h_len, histfile)
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt) # or raw_input in Python 2
finally:
readline.set_startup_hook()
class CSVEditor:
def __init__(self, filename):
self.filename = filename
with open(filename, 'r') as f:
reader = csv.DictReader(f)
self.fieldnames = reader.fieldnames
self.data = [data for data in reader]
def get_list(self, title_field):
return [(idx, data[title_field]) for idx, data in enumerate(self.data)]
def get(self, idx):
try:
return self.data[idx]
except IndexError:
return None
def show(self, idx):
line = self.get(idx)
if line is None:
return "Índice fuera de rango"
output = [
f"{field}: {value}"
for field, value in line.items()
]
return "\n".join(output)
def edit(self, idx, item):
self.data[idx] = item
def delete(self, idx):
if idx in self.data:
del self.data[idx]
def _edit(self, item):
editing = True
while editing:
for fieldname in self.fieldnames:
item[fieldname] = rlinput(f"{fieldname}: ", prefill=item.get(fieldname, ''))
editing = rlinput('Terminado (s/N)?').lower() == 'n'
def edit(self, idx):
item = self.get(idx)
if item is None:
return
self._edit(item)
self.data[idx] = item
def add(self):
item = {}
self._edit(item)
self.data.append(item)
def write(self):
with open(self.filename, 'w') as f:
writer = csv.DictWriter(f, fieldnames=self.fieldnames)
writer.writeheader()
writer.writerows(self.data)
class CSVEditorCmd(Cmd):
"Editor interactivo de ficheros csv"
def __init__(self, filename, title_field=None):
super().__init__()
self.csveditor = CSVEditor(filename)
self.title_field = title_field
if title_field is None:
self.title_field = self.csveditor.fieldnames[0]
self.do_help('')
def do_list(self, arg):
"Listar el contenido del csv"
for idx, item in self.csveditor.get_list(self.title_field):
print(f"{idx}) {item}")
def do_show(self, arg):
"Mostrar la línea con el índice indicado"
try:
idx = int(arg)
except ValueError:
print("Tienes que indicar un número")
return
print(self.csveditor.show(idx))
def do_add(self, arg):
"Añadir una nueva entrada"
self.csveditor.add()
def do_edit(self, arg):
"Editar la entrada con el índice indicado"
try:
idx = int(arg)
except ValueError:
print("Tienes que indicar un número")
return
self.csveditor.edit(idx)
def do_delete(self, arg):
"Quitar la entrada con el índice indicado"
try:
idx = int(arg)
except ValueError:
print("Tienes que indicar un número")
return
self.csveditor.delete(idx)
def do_save(self, arg):
"Guardar la lista actualizada"
self.csveditor.write()
def do_quit(self, arg):
"Terminar"
return True
do_EOF = do_quit
parser = ArgumentParser(
description='Editor sencillo de ficheros CSV')
parser.add_argument('csvfile', help='Fichero que hay que editar.')
parser.add_argument('-t', '--title', help='Columna principal del CSV, por defecto la primera.')
def main():
init_readline()
# bot = Bot()
args = parser.parse_args()
cmd = CSVEditorCmd(args.csvfile, args.title)
cmd.cmdloop()
if __name__ == '__main__':
main()