#!/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('^//',lineis 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.errnoe.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.")