#!/usr/bin/env python3.8
# -*- coding: utf-8 -*-

'''
        Prácticas de Python DB-API 2.0 (PEP-249) y bases de datos biológicas
        Script de inserción de entradas en formato SWISSPROT en la base de datos
'''

import sys
import re
import sqlite3 as dbi

'''
Estas variables globales contienen los parámetros de conexión a la base de datos
'''
dbname='uniprot.db'     # El nombre de la base de datos, que tendréis que cambiarlo

class SWParser(object):
        def __init__(self,filename):
                self.filename = filename
        
        def __iter__(self):
                # Estamos abriendo el fichero con el encoding 'latin-1'
                # Para text mining lo recomendable es el encoding 'utf-8'
                self.infile = open(self.filename,'r',encoding="latin-1")
                print("Procesando fichero ",self.filename)
                
                return self
        
        def __next__(self):
                # Inicialización de variables
                acc = []
                id = ''
                lastdate = ''
                description = ''
                sequence = ''
                molw = ''
                readingseq = False
                for line in self.infile:
                        # Lo primero, quitar el salto de línea
                        line = line.rstrip('\n')
                        
                        # Detección del final del registro
                        if re.search('^//',line) is not None:
                                # Cuando se ha terminado de leer un
                                # registro hay que proceder a guardar
                                # los datos en la base de datos
                                
                                if description == '':
                                        description = None
                                
                                # Impresión de comprobación
                                print("ACC: {0} ; ID: {1} ; Last: {2}".format(acc[0],id,lastdate))
                                
                                return acc,id,lastdate,description,sequence,molw
                        
                        # ¿Estoy leyendo una secuencia?
                        if readingseq:
                                # Quito todos los espacios intermedios 
                                line = re.compile(r"\s+").sub('',line)
                                
                                # Y concateno
                                sequence += line
                                
                        # Como no la estoy leyendo, busco los patrones apropiados
                        else:
                                seqmatch = re.search(r"^SQ.+[^0-9](\d+) MW",line)
                                matched = seqmatch is not None
                                
                                idmatch = None if matched else re.search(r"^ID   ([a-zA-Z0-9_]+)",line)
                                matched = matched or idmatch is not None
                                
                                dtmatch = None if matched else re.search(r"^DT   (\d{2}-[A-Z]{3}-\d{4}),",line)
                                matched = matched or dtmatch is not None
                                
                                acmatch = None if matched else re.search(r"^AC   (.+)",line)
                                matched = matched or acmatch is not None
                                
                                dematch = None if matched else re.search(r"^DE   RecName: Full=(.+);",line)
                                matched = matched or dematch is not None
                                
                                if matched:
                                        if seqmatch is not None:
                                                # Extracción del peso molecular
                                                # y comienzo de secuencia
                                                molw = seqmatch.group(1)
                                                
                                                readingseq = True
                                        elif idmatch is not None:
                                                # Identificador
                                                id = idmatch.group(1)
                                        elif dtmatch is not None:
                                                # Fecha de la última actualización
                                                lastdate = dtmatch.group(1)
                                        elif acmatch is not None:
                                                # Los accnumber, que pueden estar en varias líneas
                                                ac = acmatch.group(1)
                                                # Elimino los espacios y quito el posible último punto y coma
                                                ac = re.compile(r"\s+").sub('',ac).rstrip(';')
                                                
                                                # Rompo por los puntos y coma, y
                                                # añado a la lista de accnumber
                                                acc.extend(ac.split(';'))
                                        elif dematch is not None:
                                                # La descripción, que puede estar en varias líneas
                                                if description != '':
                                                        description += ', EC '
                                                description += dematch.group(1)
                
                # Se cierra el fichero procesado
                self.infile.close()
                
                # Y como hemos terminado, lo indicamos
                raise StopIteration


# Comprobación del número de parámetros de entrada
if __name__ == '__main__':
        # sys.argv contiene tanto el nombre del script como los parámetros
        # que se le introdujeron por línea de comandos al llamar al programa
        if len(sys.argv)>1:
                infiles = sys.argv[1:]
                
                # Apertura de la conexión con la base de datos
                try:
                        conn = dbi.connect(dbname)
                        # Obtención de un cursor para enviar operaciones a la base de datos
                        cur = conn.cursor()
                        # Esto sirve para que cada sentencia se ejecute inmediatamente
                        #conn.autocommit = True
                except dbi.Error as e:
                        print("Ha habido un problema al conectar a la base de datos: ",str(e),file=sys.stderr)
                        raise
                
                # Trabajo a nivel de transacción
                with conn:
                        # Procesamiento de cada fichero
                        for infile in infiles:
                                try:
                                        # Recuperar entradas una a una con un iterador
                                        for acc,id,lastdate,description,sequence,molw in iter(SWParser(infile)):
                                                # Preparación de las operaciones de inserción a realizar repetidamente
                                                # (Debería hacer un chequeo aquí, para comprobar que funcionan)
                                                cur.execute('INSERT INTO SWISSENTRY VALUES (?,?,?,?,?,?)',(acc[0],id,lastdate,description,sequence,molw))
                                                for accnumber in acc:
                                                                cur.execute('INSERT INTO ACCNUMBERS(main_accnumber,accnumber) VALUES (?,?)',(acc[0],accnumber))
                                except dbi.Error as e:
                                        print("Error al insertar en la base de datos: ",str(e),file=sys.stderr)
                                        raise
                                except IOError as e:
                                        print("Error de lectura de fichero {0}: {1}".format(e.errno, e.strerror),file=sys.stderr)
                                        #raise
                                except:
                                        print("Error inesperado: ", sys.exc_info()[0],file=sys.stderr)
                                        raise
                
        else:
                raise AssertionError("Debes introducir al menos un fichero con formato SW.")