« first day (2040 days earlier)      last day (1366 days later) » 

18:00
Una pregunta entonces el if len(blocks) == 5: se queda así o lo cambio a 6?
es que no tengo claro de qué quieres chequear realmente...
si quieres mirar que esa lista tenga 5 elementos, está bien con ==5, pero entonces el último elemento de la lista no se accede poniendo lista[5], sino lista[4]
si lo que quieres es acceder a un elemento lista[5], entonces debes comprobar antes que la lista tenga 6 elementos
porque lista[5] accede al sexto, no al quinto
(ya que el primero es el [0])
Tengo este pdf que tiene X datos, los datos que quiero obtener son los que están como en una tabla (nombre, ci, sexo, domicilio, circunscripcion, mesa, pueblo)
Eso son 7 campos
18:03
Correcto
pues para ver si están todos, deberás mirar si len == 7
pero aún si están todos, para acceder a ellos usarás índices de 0 a 6
si falta el pueblo, por ejemplo, entonces len == 6
pero mesa será el índice [5]
me explico?
if len(blocks) == 7:

            if len(blocks) == 0:
                nombre = blocks[0]['spans'][0]['text']

            if len(blocks) == 1:
                ci = blocks[1]['spans'][0]['text']

            if len(blocks) == 2:
                genero_direccion = blocks[2]['spans'][0]['text']
                gd_index = genero_direccion.find(' ')

                genero = genero_direccion[:gd_index]

                direccion = genero_direccion[gd_index+1:]

            if len(blocks) == 3:
                circunscripcion = blocks[3]['spans'][0]['text']
No, todo mal otra vez :-(
pero entendiste lo que te estuve diciendo?
Mas o menos
si una lista tiene 2 elementos, los elementos están en [0] y en [1]
¡no puedes intentar acceder a un elemento [2] si acabas de comprobar que SOLO hay dos elementos!
el elemento [2] existe sólo si la lista tiene al menos TRES elementos
esto es muy básico, y me estoy agotando de repetirlo... :-)
los arrays se numeran desde 0
si la lista tiene longitud N, el último elemento tiene índice N-1
18:07
Correcto, eso lo entiendo
pues entonces por qué haces esto?
if len(blocks) == 2:
    genero_direccion = blocks[2]['spans'][0]['text']
    gd_index = genero_direccion.find(' ')
es decir, primero te aseguras de que la lista en ese caso tenga SOLO DOS elementos
y después vas y accedes al TERCERO
al intentar blocks[2]
Todo tu código viene a decir: Si la linea tiene 1 solo elemento, entonces dame el segundo. Si tiene solo 2 elementos, entonces dame el tercero. Si tiene solo 3 elementos, entonces dame el cuarto
eso va a fallar SIEMPRE
if len(blocks) == 2:
    genero_direccion = blocks[1]['spans'][0]['text']
Vale entonces quedaría algo así
Muchachos, mi segundo curso sale dentro de pocos días :D
@JuanPerez Sí, supongo que eso es correcto. No sé si al final te saldrá o no lo que buscas, pero al menos no estás pidiendo imposibles, accediendo a elementos que no estarían
Puesta en producción mi sistema hecho en ASPnet Core MVC hacia un servidor Apache en GNU/Linux con salida a internet :D
18:16
@abulafia No me esta funcionando por ejemplo aquí if len(blocks) == 2: nunca va hacer 2 siempre será 5 si if len(blocks) == 5:
Es que aparte de todo no entendí la lógica general. Efectivamente, si len() ==5 te va a entrar en ese bloque y el resto de ifs que hay dentro no tienen mucho sentido, pues ya sabes que len es 5 y por tanto no es ni 2, ni 3, ni 4...
Solo voy a ejecutar esto:
for block in data:
        blocks = block['lines']

        print(blocks)
        print("_" * 100)
Obtengo esto:
No me deja colocar toda la salida
Ponla en un pastebin
Así me va sacando la respuesta de cada registro del pdf
@abulafia La estoy colocando un momento
@abulafia hola
18:23
@NicolásCastellanos Buenas
hay una forma de calcular a que velocidad debe girar una turbina para levantar un objeto segun su peso?
Hola Nico! :D
por ejemplo, si tengo un objeto de 1 kilo, y una turbina, a cuantas vueltas por segundo debe dar para levantar el objeto
@DanteS. Hola
y eso contando el peso de la turbina
18:25
@abulafia Listo, ahí te adjunte las 2 salidas que entrega esta linea: blocks = block['lines']
En la primera no viene la ultima columna del pdf PUEBLO INDIGENA pero en la segunda respuesta si viene
Yo creo que el verdadero problema es si te falta un dato por el medio, no si te falta el pueblo
Si solo falta el pueblo, la lista tendrá 6 elementos en vez de 7
pero si pueden faltar elementos por el medio, entonces ya no basta con mirar la longitud de la lista
porque podría tener 6 elementos pero faltar otra cosa en vez del pueblo
En teoría deberían de venir siempre las columnas (nombre, ci, sexo, domicilio, circunscripcion, mesa) el campo pueblo si puede venir vacío
Entonces por qué te líaste tanto en tu código?
basta mirar si len es 7, y tienes entonces el pueblo en [6]
No lo se xD
y si no, entonces simplemente ignoras el pueblo, y sacas el rsto de cosas del resto de campos
no necesitas mirar si len es 1 ni 2 ni 3 ni 4...
len va a ser siempre 6 (o 7 si viene el pueblo)
18:31
ya volvi
No, pero y porque cuando hago print(len(blocks)) me arroja es solo 5 o 6
Si te fijas la respuesta que te pase por pastebin en la primera respuesta 'text': se repite 5 veces nada mas pero en la segunda se repite 6 veces
@abulafia me da como resultado [['2', '3'], '9']], los números son inventado, no pongo la lista real por ser muy larga
lo que quiero es que el elemento se añada a la lista
if len(blocks) == 6:
  pueblo = sacar_pueblo(blocks[5])
else:
  pueblo = "No figura"
nombre = sacar_nombre(blocks[0])
ci = sacar_cl(blocks[1])
genero, direccion = sacar_genero_y_direccion(blocks[2])
circunscripcion = sacar_circunscripcion(blocks[3])
mesa = sacar_mesa(blocks[4])
Eso sería una especie de pseudocódigo de lo que tienes que hacer
@Christian ah, entonces [ lista.append(dato) for lista, dato in zip(lista_de_listas, lista_de_datos) ]
@abulafia ¿ Se pueden definir __slots__ a nivel de clase ? Para poner un @staticmethod
18:35
Mira lo que acabo de mirar que el sexo lo esta uniendo con el dominicilio
@JuanPerez Vale, entonces no son 7 ó 6 elementos, sino 6 ó 5. Eso encaja con el pseudocódigo que te puse antes
@Trauma No veo la relación entre esas dos cosas
@abulafia resultado [None, None, None, None]
en un momento vuelvo y pongo los datos reales
puedes definir __slots__, sí, pero no veo la relación con el staticmethod
@Christian Correcto, pero mira ahora cómo ha quedado lista_de_listas
el valor a poner
da igual, no me hace falta. Lo estaba abordando mal. Gracias :-)
@Trauma Estaba a punto de decirte que esto me olía a problema XY
18:37
no no. Bueno, no creo
no se :-)
no me caliento mas los cascos, creo que ya lo tengo
@abulafia Esto van hacer funciones: sacar_pueblo, sacar_nombre, sacar_cl, sacar_genero_y_direccion, sacar_circunscripcion, sacar_mesa ?
Yo haría funciones. El código cuanto más estructurado mejor
pero puedes también simplemente poner ahi el código que tenías para extraer esas cosas
y tomar mi código como pseudocódigo
lo importante es la estructura
el if solo sirve para saber si el pueblo está o no definido
pueblo = blocks[5]['spans'][0]['text']
el resto de cosas las sacas de los campos, sin necesidad de ningun if
Mira, lo siguiente es ya código real ejecutable, no pseudocódigo, y son una docena de líneas
def saca_texto(elem):
  return elem["spans"][0]["text"]


if len(blocks) == 6:
  pueblo = saca_texto(blocks[5])
else:
  pueblo = "No figura"
nombre = saca_texto(blocks[0])
ci = saca_ci(blocks[1])
genero, direccion = saca_texto(blocks[2]).split(" ", maxsplit=1)
circunscripcion = saca_texto(blocks[3])
mesa = saca_texto(blocks[4])
Probe así:
for block in data:
        blocks = block['lines']

        if len(blocks) == 6:
            pueblo = blocks[5]['spans'][0]['text']
        else:
            pueblo = "No figura"

        nombre = blocks[0]['spans'][0]['text']
        ci = blocks[1]['spans'][0]['text']
        sexo = blocks[2]['spans'][0]['text']
        circunscripcion = blocks[3]['spans'][0]['text']
        mesa = blocks[4]['spans'][0]['text']

        print(nombre, ci, sexo, circunscripcion, mesa, pueblo)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-3-2fe619084629> in <module>
     18         ci = blocks[1]['spans'][0]['text']
     19         circunscripcion = blocks[3]['spans'][0]['text']
---> 20         mesa = blocks[4]['spans'][0]['text']
     21
     22         print(nombre, ci, circunscripcion, mesa, pueblo)

IndexError: list index out of range
18:43
Vale, ahí tienes un caso en que faltan más campos
esa línea tiene menos de 5 campos
@abulafia o @CandidMoe El atributo _cmp es critico, tanto lo que tarda en ejecutarse como lo que tarda en invocarse.
class OrderedSet:
    __slots__ = ( '_root', '_cmp' )

    @staticmethod
    def defaultLess( op1, op2 ):
        return op1 < op2

    def __init__( self, cmp = None ):
        self._root = None
        self._cmp = OrderedSet.defaultLess if cmp is None else cmp
¿ Otra forma que genere algo mas rápido al invocarlo ?
@Trauma Qué se pretende?
Que sea rápido :-)
Es la función de ordenación de un árbol AVL
bajo qué condiciones se ejecutará ese _cmp?
quién lo llama?
Al insertar o al buscar nodos
    def __lshift__( self, val ):
        if self._root is None:
            self._root = Node( val )
        else:
            node_ptr = self._root
            node = Node( val )
            while node_ptr is not None:
                if self._cmp( node_ptr.value, node.value ):
                    if node_ptr._right is None:
                        node_ptr._right = node
                        node._parent = node_ptr
                        break
                    else:
                        node_ptr = node_ptr._right
Así inserto nodos
bst << 10 << 20 << 5 << 4 << 3
18:49
Me suena que la biblioteca estándar ya tenía una clase para eso...
¿ Que mantenga los nodos ordenados y me permita recorrerlos ?
En orden
El set no lo permite
no, el set no tiene ningun orden intrínseco
y necesito que admita el _cmp para usarlo como base de otras cosas, por ejemplo un OrderedMap
@abulafia Vale, ahí en ese caso que se podría hacer?
Estuve buscando ... hay una librería con clases nativas en python, pero creo que no se ajustaba 100% a lo que necesito
18:51
@Trauma No es esto similar a un heap queue (o una priority queue)?
Si ... pero no
Es que es la base de un OrderedMap, que a su vez será la base de un manejador de logs, que es el destino final
porque la biblioteca estándar sí trae un módulo heapq
qué es un OrderedMap? Un diccionario ordenado?
sip
collections.OrderedDict te serviría?
un set, pero cada nodo es de la forma clave, valor
18:53
cuando dices un set quieres decir que no almacena duplicados?
puedes modificar el valor todo lo que quieras, pero no la clave
@abulafia eso mismo
Bueno, ya... que no tenga claves duplicadas, supongo
una lista ordenada, sin duplicados, y con rápida inserción / búsqueda
y no es eso un OrderedDict entonces?
pues no lo se ... déjame que lo mire
18:54
aunque el orden en el diccionario te da lo mismo, a la hora de insertar o de buscar
solo afecta a la hora de recorrerlo/imprimirlo
quiero decir, la insercción y búsqueda es eficiente ya de por si tanto si está ordenado como si no (pues se basa en hashes)
solo si lo vas a querer imprimir ordenado te servirá de algo que el diccionario esté ordenado. Si no, te vale un dict normal
pero tengo que recorrer los nodos en orden
para que
xDDD
porque me hace ilu ;-p
para mantener un almacen de logs
varios módulos, cada uno con su nombre
y en su propio hilo
y el orden ese no es el orden de inserción, claro, sino un orden definido a través del cmp ese
los logs pueden coincidir en la fecha de generación
@abulafia correcto
cada log está en un archivo individual
18:57
y cuán a menudo necesitas recorrer esa estructura?
pero quiero seguir el flujo de varios módulos
@abulafia bastante :-)
porque si es solo al final, para volcarla o algo así, más te vale pasar de la ordenación durante la construcción, y usar un dict normal. Y luego ordenarlo en el momento de volcarlo
eso sería mucho mucho más eficiente que andar implementando tú árboles y similares
@abulafia no, piensa que tienes todos los logs y estás buscando algo ... no sabes a priori lo que vas a tener que buscar
quiero hacer métodos para filtar por modulo, por tag, por prioridad, fecha mínima/máxima, ...
Quieres decir buscar algo en los valores, no en las claves
correcto
la clave es la fecha de generación del log
el valor, pues una lista de cosas: modulo, prioridad, tag, ...
osea, una lista de tuplas
a ver ...
tiene buena pinta. Algo así, una lista ordenada que mantenga el orden al insertar
lo miro con detalle O-O
el tema es que estas cosas no permiten definir la función cmp esa. El enfoque sería más bien que los objetos que vayas a meter en esa lista ordenada ya implementen ellos mismos los métodos que les hagan "sortables"
__le__(), __eq__() y toda la pesca
Bueno, es es fácil y simple
es un curro, pero quizás te pueda ser útil dataclass que te lo da todo hecho
usé _cmp para poder usar tuplas y no necesitar clases ad-hoc
19:02
las tuplas ya saben compararse entre sí
y una dataclass es casi una tupla, pero con aspecto de objeto
¿ Ha si ? :-O
No sabía eso de las tuplas
a ver.. son bastante tontas
comparan el primer campo y eso ya decide la ordenación, a menos que sean iguales en cuyo caso comparan el segundo, etc.
por tanto delegan la comparación a los objetos que contienen
... no se ...
no no, copón. Las tuplas no hay que ordenarlas :-)
vamos que si A<B, entonces (A, loquesea) < (B, loquesea2)
solo filtrar ciertos campos, o ciertas tuplas por el valor de ciertos campos
19:05
me parece que estás reimplementando a pelo un diccionario o algo así
pues mas o menos
yo creo que tiene que haber un tipo estándar que te sirva
un std::set de C++
y que será miles de veces más eficiente por estar hecho en C
no sé qué es un std::set de C++
docs?
una lista ordenada, con O(log n) de tiempo de inserción y/o búsqueda
19:06
parece el bisect de python
(casi escribo bistec)
jejeje
tienes hambre ?
xDD
nos vemos amigos, que les vaya bien
El Gato se acaba de despertar O_o
Acajonao me tiene
19:07
que la fuerza os acompañe :D
Buenos dias a todos
Alguien sabe como cerrar sesion? No encuentro ese boton :c
class Crazy(object):
  def __init__(self):
    self.d = {}
    self.L = []
    self.sorted = True
  def __getitem__(self, k):
    return self.d[k]
  def __setitem__(self, k, v):
    if k not in self.d:
      self.L.append(k)
      self.sorted = False
    self.d[k] = v
  def __delitem__(self, k):
    del self.d[k]
    self.L.remove(k)
  def __iter__(self):
    if not self.sorted:
      self.L.sort()
      self.sorted = True
    return iter(self.L)
@abulafia Esta demás hacer esto:}
for block in data:
    blocks = block['lines']

    for x in blocks:
        print(x['spans'])
@Trauma ^^ Eso implementa una cosa con interfaz de diccionario, y por tanto tiempo de inserción y de búsqueda O(1)
Para verificar los indices si viene o no?
19:09
Y a la vez te permite iterar por las claves ordenadas, aunque hace trampa pues las ordena en el momento que vayas a iterar por ellas (aunque amortiza la cosa al guardar la versión ordenada)
@abulafia jejeje ... pero ahora hay que recorrer a partir de un nodo :-)
ha, haces sort( )
bueno, en realidad solo se va a ordenar 1 vez
@abulafia Haaa... shap, pensé que estaba mal. Muchas gracias :)
Bueno, no es complicado, pues en self.L tienes la lista de claves ordenadas. Con un index() en ella sacas dónde está el nodo, y sigues de esa posición en adelante
@Trauma Sí, hasta que vuelvas a insertar alguna cosa
@abulafia igual me vale ... solo tiene que estar ordenado al recorrer
Pero ya que las ordenaciones son O(N logN) lo veo bastante eficiente
depende de tu patrón de trabajo con la estructura esa. Si se trata de insertar mucho, acceder mucho por clave, y al final del todo recorrer ordenado muchas veces (pero sin insertar ya nada), es perfecto
si vas a alternar muchas veces entre insertar y recorrer, puede no ser tanto
19:12
@abulafia Pues igual me sirve ... el caso de uso va a ser mas o menos ese, insertar muchas cosas y luego recorrer
pero aún así creo que bastante mejor que una implementación en "pure python" del arbol que pretendías
Y si te mola lo de << también se le puede añadir :-)
@abulafia Es que es mas corto que llamar a insert( ) xDD
Voy a hacer algunas pruebas, ahora te cuento :-)
ya. En esta otra implementación no sería insert, sino crazy[clave] = valor
Me sirve :-)
@JuanPerez Yo pondría print(x["spans"][0]["text"]) ya puestos...
19:16
. MAWAR
11.111.111-K
MUJER PARINAS 2 3948 506-A CONDOMINIO PARINAS 2
ALTO HOSPICIO
84
Así lo esta imprimiendo
for block in data:
    blocks = block['lines']

    for x in blocks:
        print(x["spans"][0]["text"])
@abulafia Ah, pero al iterar no puedo acceder a la key
Iteras por las keys
for k in crazy: te va dando las keys en orden
¿qué necesitas?
Edición ampliada y mejorada:
class Crazy(object):
  def __init__(self):
    self.d = {}
    self.L = []
    self.sorted = True
  def __getitem__(self, k):
    return self.d[k]
  def __setitem__(self, k, v):
    if k not in self.d:
      self.L.append(k)
      self.sorted = False
    self.d[k] = v
  def __delitem__(self, k):
    del self.d[k]
    self.L.remove(k)
  def __iter__(self):
    if not self.sorted:
      self.L.sort()
      self.sorted = True
    return iter(self.L)
  def __lshift__(self, par):
    clave, valor = par
Resultado: {1: 'Foo', 3: 'Adios', 5: 'Hola'}
@bemoba1437 al de la esquina y despues en salir...
pregunta rápida, ¿Cuál era el comando para instalar Pandas en Python?
@Christian La respuesta corta es pip install pandas, pero la respuesta larga es ¿quieres instalarlo dentro de un entorno virtual (recomendado) o a nivel de sistema (no recomendado)? ¿No te sería más útil quizás instalar Anaconda que además de pandas te trae jupyter y muchas cosas más?
19:32
@abulafia simplemente lo quería para añadirlo en m respuesta a una pregunta :/
Por cierto, @Trauma, que es trivial modificar esta clase para que la ordenación siga el criterio que tú quieras.
pero recorde que tenia una duda respecto a eso
@Christian Hmm.. entonces decirle que use pip install pandas me parece un poco arriesgado
@abulafia ¿Es mas conveniente instalar los paquetes de python en un entorno virtual?
19:33
dependiendo del nivel del OP yo suelo recomendar más instalar Anaconda a quien vaya a necesitar entornos de procesmiento científico de datos
@Christian Te evitas líos de versiones en conflicto. Instalar cosas a nivel de sistema no suele ser buena idea. Si algo sale mal puedes dejar inútil el python de sistema
@abulafia Me di cuenta que igual en algunas respuesta esta colocando el campo "sexo", "circunscripcion" y "mesa" juntos en un solo blocks es por ello el error que me muestra
a mi me pasó en OSX, no sé cómo me arreglé, pero cuando intentaba lanzar cualquier script que hiciera import tkinter, ¡se reiniciaba instantáneamente el ordenador!
ni mensajes de error ni leches, pantalla negra y manzana blanca. Nunca vi cosa igual
@JuanPerez Este tipo de procesamiento de pdfs es así. No es estructurado. No sale bien a la primera. Hay que andar programando mil casos especiales a medida que se van descubriendo
Cual es la diferencia de esto:
dic = page.getText("dict")
dic = page.getText()
@abulafia :O ya me dio miedo, desde ahora voy a instalar todo en un entorno virtual
le falta la declaracion let
19:36
@JuanPerez ni idea, pues no sé qué es page ni qué hace el método getText()
NaN significa "Not at Number" o "Not a Number"?
@Christian Lo segundo
Gracias, siempre me confundo :)
@Christian Qué significado tendría la primera?
@FranAcuna Lo siento no se mucho de ingles, solo se que NaN significa "No es un numero"
19:52
Oh, disculpa. Creí que tal vez existía algún motivo
@FranAcuna naaa, tranquilo, solo me confundo por el "at" y el "a"
aunque ahora que lo pienso "a" significa "un"
20:05
@abulafia Una pregunta para este tipo de casos se puede aplicar algo para poder separar los datos
'text': 'VARON SANTA ROSA DE ALTO MOLLE 2948 TORRE B DEPTO 1011 CONDOMINIO ALTO HOSPICIO'
Para el sexo solo hay = VARON o MUJER
Debería quedar así
sexo = VARON
domicilio = SANTA ROSA DE ALTO MOLLE 2948 TORRE B DEPTO 1011 CONDOMINIO
circunscripcion = ALTO HOSPICIO
@JuanPerez puedes recorrer el texto y si encuentra el valor de VARON o MUJER toma ese valor
Si, donde veo que es mas difícil es poder sacar las demás variables ya que los textos varían
si tienes una lista donde almacenas los datos de domicilio y circunscripcion (al igual que lo haces con el sexo) no seria tan difícil
Son muchos domicilio
pero si los tienes en una lista?
20:14
eso que necesita Juan es una de las cosas mas dificiles de hacer en sistemas
No están en el pdf
@JuanPerez No está claro el heurístico a aplicar. La primera palabra sí es fácil de separar, pues simplemente divides por el primer espacio
el problema es ¿cómo sabemos cuántas palabras ocupa la circunscripción?
Si, eso es lo que veo
¿hay algún patrón regular en el domicilio que permita saber dónde acaba?
si es un texto de ingreso libre.. es imposible
20:16
No nada es libre el texto
entonces es imposible.. puede haber cosas mal escritas, cosas escritas de diferentes formas...
para eso se usa una heuristica de tener un listado nomenclado de circunscripciones.. y luego, anotar los casos que no entran en ningun lado.. y alguien tiene que revisarlos a mano...
e igual, puede que no quede al 100% bien....
@JuanPerez sip, es imposible, al menos que apliques NLP
Lo único que puedes hacer es señalar esas líneas de algún modo en el resultado. Emitir algún mensaje de Warning o algo, para que esos resultados sean revisados por un humano y corrija la base de datos
por ejemplo: La línea 326 no ha podido ser parseada correctamente. Este es el texto de esa lína: "XXXXXXXX"
Me di cuenta que aparte de:
y eso que vaya a algún tipo de log que alguien revise al final
20:19
if len(blocks) == 5:, if len(blocks) == 6:
para saber entonces qué registros debe corregir
También existe el: if len(blocks) == 4:
@abulafia pero si lo ultimo son palabras compuestas, y palabras simples, es imposible....
@gbianchi Puede detectar que la línea tiene menos campos de los esperados
ya que ese caso se le producen cuando por error dos campos se parsean como uno solo
(está analizando un pdf con una tabla, en el que a veces los datos de diferentes columnas se juntan)
@abulafia si esta en una tabla, es otra cosa...
20:21
@JuanPerez Yo me limitaría a eso, if len(blocks)==4: guardar esa linea en un fichero de errores para que alguien la revise
O lo otro es dejar de lado esos datos? y capturar solo los que cumplan con el patron de que vengan todos los datos bien?
@gbianchi La tabla solo es tabla a nivel visual. A nivel de la información que recibe su script, ya que se pasa un OCR o algo así, solo tiene el texto final
Una pregunta y no se puede convertir todo el pdf a texto ?
Y darle una mejor interpretación?
Ese texto se va separando correctamente en columnas si hay suficiente espacio entre ellas, pero si el texto de una columna es demasiado largo y queda muy cerca de la siguiente, el motor que extrae el texto lo saca en una sola unidad
Si, eso es lo que esta pasando
20:23
@JuanPerez Sería peor aún... si conviertes la hoja completa a texto tendrías ya no solo una línea conflictiva, sino todas. Tal como lo estás haciendo ahora, al menos las que tienen textos claramente separados se parsean bien
pero si lo convirtieras todo a texto tendrías que parsear "a mano" todas, buscando secuencias de espacios más largas como separadores
y en todo caso en las lineas que te daban problemas estarás igual, pues no habrá esas secuencias largas de espacios separando
Como puedo obtener entonces solo las que cumplan con el patron donde vengan todas?
lo único... si se pudiera convertir la página completa a una secuencia de líneas, todas de la misma longitud en cuanto a número de caracteres, tal vez podrías usar la posición de los caracteres dentro de la línea para poder separar por celdas. No obstante eso solo funcionaría (en caso de que fuera posible) si el pdf está escrito con un tipo de letra de ancho fijo (tipo Courier)
@JuanPerez Las que tengan len menor de 5 son las problemáticas
if len(blocks) < 5:
  continue
for page in doc:
    print("Procesando páginas número: ", registro)
    dic = page.getText("dict")

    data = dic['blocks'][157:]

    for block in data:
        blocks = block['lines']

        if len(blocks) != 4:
            if len(blocks) == 6:
                pueblo = saca_texto(blocks[5])
            else:
                pueblo = ""

            nombre = saca_texto(blocks[0])
            ci = saca_texto(blocks[1])

            genero_direccion = blocks[2]['spans'][0]['text']
            gd_index = genero_direccion.find(' ')
Puede ser algo así
Eso ignoraría las líneas problemáticas, aunque yo creo que deberías al menos sacar algún mensaje de error
más bien pondría yo if len(blocks) > 4
porque qué pasa si una línea tiene 3, o 2...?
Omitirlas
20:30
ya, pero tal como lo tienes no funcionará, porque 3 no es 4, y por tanto entraría en tu if
for page in doc:
    print("Procesando páginas número: ", registro)
    dic = page.getText("dict")
    data = dic['blocks'][157:]

    for n, block in enumerate(data):
        blocks = block['lines']

        if len(blocks)<5:
            print("El bloque", n, "tiene problemas y se ignora")
            continue

        if len(blocks) == 6:
            pueblo = saca_texto(blocks[5])
        else:
            pueblo = ""

        nombre = saca_texto(blocks[0])
        ci = saca_texto(blocks[1])
Vale, entiendo
O mejor aún
if len(blocks)<5:
    print("El bloque", n, "tiene problemas y se ignora")
    txt = " | ".join(b['spans'][0]['text'] for b in blocks)
    print("El texto problemático es:")
    print("--->", txt)
    continue
ahi estoy extrayendo el texto de todos los bloques que vengan en esta línea (sean 4, 3, 2, ó 1) para mostrarlo
asi un operador humano podrá ver la información que fue omitida, y si es necesario meterla a mano a posteriori
Vale, gracias @abulafia lo voy a probar..
20:50
@abulafia Igual esos datos se podrían volcar en una tabla(mysql) verdad?
21:21
Alguno de ustedes, que esté en el móvil o celular
Podría confirmarme por favor si la firma es touch?
1
A: Firma por canvas

jachguateUtiliza las propiedades offsetX y offsetY del evento e. La documentación de offsetX dice: The offsetX read-only property of the MouseEvent interface provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node Mi traducción libre: La p...

Me parece interesante esa respuesta para un proyecto que estoy armando, es que ahora mismo no tengo móvil a la mano
Buenas gente !!! Si alguien me lee y sepa de servidores. Les dejo colgada una pregunta...
¿Que tan bueno es un servidor con procesadores Xeon?? Son muy caros como para invertir en ellos?? Hay otras opciones para construir un servidor rentable para una empresa pequeña-mediana ??
 
1 hour later…
22:48
Creo que tener una buena conecion a internet y un OS Linux, Tambien una buena cantidad de RAM, pues se va a estar ejecutando Siempre.Tener Apache(PHP, SQL) o simplemente WAMP o python.
@Excorpion
¿Quieres ver un dibujo?.
<!doctype html>
<html lang='es'>
<head>
<title>Nuevo_Proyecto</title>
<meta charset='utf-8'/>
<meta name='viewport' content='width=device-width, initial-scale=1'/>
<style>

</style>
<script>//ERROR.
var limite_entero=100000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000100000000000000;
La verdad no se como llegue a ese resultado pero se ve calidad.
@Excorpion Si es una empresa pequeña, dependiendo de lo que necesites hacer. Puede ser mejor adquirir un servicio web. Mantener y adquirir un servidor propio de la manera correcta es laborioso y al final de cuentas costoso

« first day (2040 days earlier)      last day (1366 days later) »