예제 #1
0
def test_detalle_mesa_categoria(db, fiscal_client):
    opcs = OpcionFactory.create_batch(3)
    e1 = CategoriaFactory(opciones=opcs)
    e2 = CategoriaFactory(opciones=opcs)
    mesa = MesaFactory(categorias=[e1, e2])
    c1 = CargaFactory(mesa_categoria__mesa=mesa,
                      mesa_categoria__categoria=e1,
                      tipo=Carga.TIPOS.parcial,
                      origen=Carga.SOURCES.csv)
    mc = c1.mesa_categoria
    votos1 = VotoMesaReportadoFactory(
        opcion=opcs[0],
        votos=1,
        carga=c1,
    )
    votos2 = VotoMesaReportadoFactory(
        opcion=opcs[1],
        votos=2,
        carga=c1,
    )
    votos3 = VotoMesaReportadoFactory(
        opcion=opcs[2],
        votos=1,
        carga=c1,
    )

    # a otra carga
    VotoMesaReportadoFactory(opcion=opcs[2], votos=1)
    c1.actualizar_firma()
    consumir_novedades_y_actualizar_objetos([mc])
    assert mc.carga_testigo == c1
    url = reverse('detalle-mesa-categoria', args=[e1.id, mesa.numero])
    response = fiscal_client.get(url)

    assert list(response.context['reportados']) == [votos1, votos2, votos3]
예제 #2
0
def test_siguiente_prioriza_categoria(db, settings):
    f = FiscalFactory()
    c = CategoriaFactory(prioridad=2)
    c2 = CategoriaFactory(prioridad=1)
    m1 = MesaFactory()
    AttachmentFactory(mesa=m1)
    mc1 = MesaCategoriaFactory(
        status=MesaCategoria.STATUS.parcial_sin_consolidar,
        categoria=c,
        mesa=m1,
    )
    mc1.actualizar_coeficiente_para_orden_de_carga()
    m2 = MesaFactory()
    AttachmentFactory(mesa=m2)
    mc2 = MesaCategoriaFactory(
        categoria=c2,
        status=MesaCategoria.STATUS.parcial_sin_consolidar,
        mesa=m2,
    )
    mc2.actualizar_coeficiente_para_orden_de_carga()
    # Se recibe la mc con categoria más prioritaria.
    assert MesaCategoria.objects.siguiente() == mc2
    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc2.asignar_a_fiscal()
    # Luego la de la categoría menos prioritaria.
    assert MesaCategoria.objects.siguiente() == mc1
예제 #3
0
def test_scheduler(db, settings):
    """
    Ejecutar dos veces el scheduler sin nuevas cosas no cambia el estado.
    """

    # Creamos 5 attachments sin identificar
    attachments = AttachmentFactory.create_batch(
        5, status=Attachment.STATUS.sin_identificar)

    c1 = CategoriaFactory()
    c2 = CategoriaFactory()
    m1 = MesaFactory(categorias=[c1])
    IdentificacionFactory(
        mesa=m1,
        status=Identificacion.STATUS.identificada,
        source=Identificacion.SOURCES.web,
    )
    m2 = MesaFactory(categorias=[c1, c2])

    IdentificacionFactory(
        mesa=m2,
        status=Identificacion.STATUS.identificada,
        source=Identificacion.SOURCES.csv,
    )

    # Los cinco del principio y los dos de la identificación.
    assert Attachment.objects.count() == 7
    assert MesaCategoria.objects.count() == 3

    # Empezamos con la cola vacía.
    assert ColaCargasPendientes.largo_cola() == 0

    # Ejecutar el scheduler antes de consolidar sólo encola identificaciones:
    # 2 por cada una de las fotos no identificadas y 1 para las creadas con
    # IdentificationFactory.

    scheduler()
    assert ColaCargasPendientes.largo_cola(
    ) == 5 * settings.MIN_COINCIDENCIAS_IDENTIFICACION + 2 * (
        settings.MIN_COINCIDENCIAS_IDENTIFICACION - 1)

    # Al consumir las novedades de identificación, se consolidan las
    # categorías de la segunda mesa, así que agregamos 4 tareas.
    consumir_novedades_identificacion()
    scheduler()
    assert ColaCargasPendientes.largo_cola() == 16
    cola_primera = list(ColaCargasPendientes.objects.all())

    consumir_novedades_identificacion()
    scheduler()
    # Al volver ejecutar el scheduler sin que haya novedades se mantiene la
    # misma cantidad de tareas.
    assert ColaCargasPendientes.largo_cola() == 16
    cola_segunda = list(ColaCargasPendientes.objects.all())

    # Testeamos la igualdad de las colas con la inclusión mutua.
    for i in cola_primera:
        assert i in cola_segunda
    for i in cola_segunda:
        assert i in cola_primera
예제 #4
0
def definir_prioridades_seccion_categoria(settings):
    asignar_prioridades_standard(settings)
    seccion_cuatro_prioridades = SeccionFactory(nombre="Cuatro prioridades")
    seccion_dos_cantidades = SeccionFactory(nombre="Dos cantidades")
    seccion_prioritaria = SeccionFactory(nombre="Prioritaria")
    seccion_standard = SeccionFactory(nombre="Standard")
    PrioridadSchedulingFactory(seccion=seccion_cuatro_prioridades, desde_proporcion=50, hasta_proporcion=100, prioridad=250)
    PrioridadSchedulingFactory(seccion=seccion_cuatro_prioridades, desde_proporcion=30, hasta_proporcion=50, prioridad=120)
    PrioridadSchedulingFactory(seccion=seccion_cuatro_prioridades, desde_proporcion=10, hasta_proporcion=30, prioridad=80)
    PrioridadSchedulingFactory(seccion=seccion_cuatro_prioridades, desde_proporcion=0,
                               hasta_proporcion=10, hasta_cantidad=12, prioridad=20)
    PrioridadSchedulingFactory(seccion=seccion_dos_cantidades, desde_proporcion=0,
                               hasta_proporcion=2, hasta_cantidad=7, prioridad=5)
    PrioridadSchedulingFactory(seccion=seccion_dos_cantidades, desde_proporcion=2,
                               hasta_proporcion=10, hasta_cantidad=20, prioridad=25)
    PrioridadSchedulingFactory(seccion=seccion_dos_cantidades, desde_proporcion=10, hasta_proporcion=100, prioridad=110)
    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=0,
                               hasta_proporcion=2, hasta_cantidad=7, prioridad=10)
    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=2, hasta_proporcion=10, prioridad=50)
    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=10, hasta_proporcion=100, prioridad=80)

    pv = CategoriaFactory(nombre="PV")
    gv = CategoriaFactory(nombre="GV")
    categoria_standard = CategoriaFactory(nombre="Standard")
    PrioridadSchedulingFactory(categoria=pv, desde_proporcion=0, hasta_proporcion=100, prioridad=5)
    PrioridadSchedulingFactory(categoria=gv, desde_proporcion=0, hasta_proporcion=100, prioridad=30)

    # tambien hay que definir una mesa en cada seccion, para lo cual tengo que definir
    # circuitos y lugares de votacion
    crear_mesa(seccion_cuatro_prioridades)
    crear_mesa(seccion_dos_cantidades)
    crear_mesa(seccion_prioritaria)
    crear_mesa(seccion_standard)
예제 #5
0
def test_secuencia_carga_escenario_complejo_2(db, settings):
    """
    Se verifica la secuencia con la que se asignan mesas considerando 
    - dos secciones, una standard y una muy prioritaria, de 50 mesas cada una
    - dos categorias, una standard y una moderadamente prioritaria
    """
    asignar_prioridades_standard(settings)
    settings.MIN_COINCIDENCIAS_IDENTIFICACION = 1
    fiscal = nuevo_fiscal()

    seccion_prioritaria, circuito_prioritario, lugar_votacion_prioritario = crear_seccion("Barrio marítimo")
    seccion_standard, circuito_standard, lugar_votacion_standard = crear_seccion("Bera centro")
    pv = CategoriaFactory(nombre="PV")
    dip = CategoriaFactory(nombre="Diputados")
    categorias = [pv, dip]

    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=0,
                               hasta_proporcion=2, prioridad=2)
    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=2,
                               hasta_proporcion=10, prioridad=5)
    PrioridadSchedulingFactory(seccion=seccion_prioritaria, desde_proporcion=10,
                               hasta_proporcion=100, prioridad=25)
    PrioridadSchedulingFactory(categoria=pv, desde_proporcion=0, hasta_proporcion=100, prioridad=30)

    mesas_seccion_standard, mesas_seccion_prioritaria = \
        crear_mesas([lugar_votacion_standard, lugar_votacion_prioritario], categorias, 50)

    # se identifican las mesas en orden
    for nro in range(50):
        identificar_mesa(mesas_seccion_standard[nro], fiscal)
        identificar_mesa(mesas_seccion_prioritaria[nro], fiscal)

    verificar_siguiente_mesacat(mesas_seccion_standard[0], pv)        # mesacat 1 - orden de carga 60
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[0], pv)     # mesacat 2 - orden de carga 24
    verificar_siguiente_mesacat(mesas_seccion_standard[0], dip)       # mesacat 3 - orden de carga 200
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[0], dip)    # mesacat 4 - orden de carga 200
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[1], pv)     # mesacat 5 - orden de carga 450
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[2], pv)     # mesacat 6 - orden de carga 750
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[3], pv)     # mesacat 7 - orden de carga 1050
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[4], pv)     # mesacat 8 - orden de carga 1350
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[1], dip)    # mesacat 9 - orden de carga 1500
    verificar_siguiente_mesacat(mesas_seccion_standard[1], pv)        # mesacat 10 - orden de carga 1800
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[2], dip)    # mesacat 11 - orden de carga 2500
    verificar_siguiente_mesacat(mesas_seccion_standard[2], pv)        # mesacat 12 - orden de carga 3000
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[3], dip)    # mesacat 13 - orden de carga 3500
    verificar_siguiente_mesacat(mesas_seccion_standard[3], pv)        # mesacat 14 - orden de carga 4200
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[4], dip)    # mesacat 15 - orden de carga 4500
    verificar_siguiente_mesacat(mesas_seccion_standard[4], pv)        # mesacat 16 - orden de carga 5400
    verificar_siguiente_mesacat(mesas_seccion_standard[1], dip)       # mesacat 17 - orden de carga 6000
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[11], pv, 8)       # mesacat 26 - orden de carga 17250
    verificar_siguiente_mesacat(mesas_seccion_standard[4], dip)             # mesacat 27 - orden de carga 18000
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[5], dip, 6)       # mesacat 34 - orden de carga 27500
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[6], dip, 4)       # mesacat 39 - orden de carga 32500
    verificar_siguiente_mesacat(mesas_seccion_standard[5], pv)              # mesacat 40 - orden de carga 33000
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[22], pv)          # mesacat 41 - orden de carga 33750
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[14], dip, 38)     # mesacat 80 - orden de carga 72500
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[48], pv)          # mesacat 81 - orden de carga 72750
    verificar_siguiente_mesacat(mesas_seccion_prioritaria[49], pv)          # mesacat 82 - orden de carga 74250
    verificar_siguiente_mesacat(mesas_seccion_standard[12], pv)             # mesacat 83 - orden de carga 75000
예제 #6
0
def test_scheduler_orden_distinto(db, settings):
    # Creamos 5 attachments sin identificar
    attachments = AttachmentFactory.create_batch(
        5, status=Attachment.STATUS.sin_identificar)

    c1 = CategoriaFactory(sensible=True)
    c2 = CategoriaFactory(sensible=True)
    m1 = MesaFactory(categorias=[c1])
    IdentificacionFactory(
        mesa=m1,
        status='identificada',
        source=Identificacion.SOURCES.csv,
    )
    m2 = MesaFactory(categorias=[c1, c2])
    assert MesaCategoria.objects.count() == 3
    IdentificacionFactory(
        mesa=m2,
        status='identificada',
        source=Identificacion.SOURCES.csv,
    )
    # Ambas consolidadas vía csv.

    consumir_novedades_identificacion()
    # Si hay más fotos que attachments primero se ponen las fotos,
    # hasta que haya la misma cantidad de cargas pendients.
    with override_config(COEFICIENTE_IDENTIFICACION_VS_CARGA=1):
        scheduler()
    assert ColaCargasPendientes.largo_cola() == 16

    # items = ColaCargasPendientes.objects.all().order_by('orden')
    # for i in range(16):
    #     it = items[i]
    #     print(f'{i}: ({it.orden},{it.attachment},{it.mesa_categoria})')

    # Las primeras seis tareas son de identificaciones.
    for i in range(6):
        consumir()

    assert ColaCargasPendientes.largo_cola() == 10

    # Luego vienen dos cargas...
    for i in range(2):
        consumir(False)

    assert ColaCargasPendientes.largo_cola() == 8

    # luego dos identificaciones y dos cargas, dos veces:
    for j in range(2):
        for i in range(2):
            consumir()

        for i in range(2):
            consumir(False)

    # Ya no queda nada en la cola.
    assert ColaCargasPendientes.largo_cola() == 0
    (mc, attachment) = ColaCargasPendientes.siguiente_tarea(fiscal=None)
    assert mc is None and attachment is None
예제 #7
0
def nueva_categoria(slug, nombres_opciones_prioritarias, nombres_opciones_no_prioritarias, distrito=None):
    categoria = CategoriaFactory(
        opciones=[], slug=slug, distrito=distrito) if distrito else CategoriaFactory(opciones=[], slug=slug)
    # sin opciones para crearlas ad hoc
    for nombre in nombres_opciones_prioritarias:
        CategoriaOpcionFactory(categoria=categoria, opcion__nombre=nombre, prioritaria=True)
    for nombre in nombres_opciones_no_prioritarias:
        CategoriaOpcionFactory(categoria=categoria, opcion__nombre=nombre, prioritaria=False)
    return categoria
예제 #8
0
def test_carga_mesa_redirige_a_siguiente(db, fiscal_client):
    o = OpcionFactory(es_contable=True)
    o2 = OpcionFactory(es_contable=False)
    e1 = CategoriaFactory(opciones=[o, o2])
    e2 = CategoriaFactory(opciones=[o])
    m1 = AttachmentFactory(mesa__categoria=[e1, e2]).mesa

    response = fiscal_client.get(reverse('elegir-acta-a-cargar'))
    assert response.url == reverse('mesa-cargar-resultados', args=[e1.id, m1.numero])

    # formset para categoria e1 arranca en blanco
    url = response.url
    response = fiscal_client.get(response.url)
    formset = response.context['formset']
    assert len(formset) == 2
    assert formset[0].initial == {'opcion': o}
    assert formset[1].initial == {'opcion': o2}

    # response = fiscal_client.get(url)
    response = fiscal_client.post(url, {
        'form-0-opcion': str(o.id),
        'form-0-votos': str(m1.electores // 2),
        'form-1-opcion': str(o2.id),
        'form-1-votos': str(m1.electores // 2),
        'form-TOTAL_FORMS': '2',
        'form-INITIAL_FORMS': '0',
        'form-MIN_NUM_FORMS': '2',
        'form-MAX_NUM_FORMS': '1000',
    })
    assert response.status_code == 302
    assert response.url == reverse('mesa-cargar-resultados', args=[e2.id, m1.numero])

    # el form de la nueva categoria e2 está en blanco
    url = response.url
    response = fiscal_client.get(response.url)
    formset = response.context['formset']
    assert len(formset) == 1
    assert formset[0].initial == {'opcion': o}

    # si completamos y es valido, no quedan
    # categorias por cargar y pide otra acta
    response = fiscal_client.post(url, {
        'form-0-opcion': str(o.id),
        'form-0-votos': str(m1.electores),
        'form-TOTAL_FORMS': '1',
        'form-INITIAL_FORMS': '0',
        'form-MIN_NUM_FORMS': '1',
        'form-MAX_NUM_FORMS': '1000',
    })
    assert response.status_code == 302
    assert response.url == reverse('elegir-acta-a-cargar')
예제 #9
0
def test_chequear_resultado_categoria_desactivada(db, fiscal_client):
    opcs = OpcionFactory.create_batch(3, es_contable=True)
    e1 = CategoriaFactory(opciones=opcs)
    assert e1.activa is True
    mesa = MesaFactory(categoria=[e1])
    # existe una carga para esa categoria / mesa
    CargaFactory(mesa=mesa, categoria=e1)
    url = reverse('chequear-resultado-mesa', args=[e1.id, mesa.numero])
    response = fiscal_client.get(url)
    assert response.status_code == 200
    e1.activa = False
    e1.save()
    response = fiscal_client.get(url)
    assert response.status_code == 404
예제 #10
0
def test_troll_total_consolidada_dc_a_parcial_sin_consolidar(db, settings):
    fiscal_1 = nuevo_fiscal()
    fiscal_2 = nuevo_fiscal()
    presi = CategoriaFactory()
    mesa_1 = MesaFactory(categorias=[presi])
    mesa_categoria_1 = MesaCategoria.objects.filter(mesa=mesa_1,
                                                    categoria=presi).first()
    attach_1 = AttachmentFactory()
    identificar(attach_1, mesa_1, fiscal_1)
    identificar(attach_1, mesa_1, fiscal_2)

    refrescar_data(
        [presi, fiscal_1, fiscal_2, mesa_1, mesa_categoria_1, attach_1])

    nueva_carga(mesa_categoria_1, fiscal_1, [20, 35], Carga.TIPOS.parcial)
    nueva_carga(mesa_categoria_1, fiscal_2, [20, 35], Carga.TIPOS.parcial)

    consumir_novedades_carga()
    refrescar_data([mesa_categoria_1])

    nueva_carga(mesa_categoria_1, fiscal_2, [20, 35], Carga.TIPOS.total)
    nueva_carga(mesa_categoria_1, fiscal_1, [20, 35], Carga.TIPOS.total)

    consumir_novedades_carga()
    refrescar_data([mesa_categoria_1])

    assert mesa_categoria_1.status == MesaCategoria.STATUS.total_consolidada_dc
    assert Carga.objects.filter(invalidada=True).count() == 0

    aplicar_marca_troll(fiscal_2)
    consumir_novedades_carga()
    refrescar_data([mesa_categoria_1, fiscal_2])

    assert mesa_categoria_1.status == MesaCategoria.STATUS.parcial_sin_consolidar
    assert Carga.objects.filter(invalidada=True).count() == 3
예제 #11
0
def test_troll_parcial_dc_a_sin_consolidar(db, settings):
    with override_config(
            SCORING_MINIMO_PARA_CONSIDERAR_QUE_FISCAL_ES_TROLL=20):
        fiscal_1 = nuevo_fiscal()
        fiscal_2 = nuevo_fiscal()
        presi = CategoriaFactory()
        mesa_1 = MesaFactory(categorias=[presi])
        mesa_categoria_1 = MesaCategoria.objects.filter(
            mesa=mesa_1, categoria=presi).first()
        attach_1 = AttachmentFactory()
        identificar(attach_1, mesa_1, fiscal_1)
        identificar(attach_1, mesa_1, fiscal_2)

        refrescar_data(
            [presi, fiscal_1, fiscal_2, mesa_1, mesa_categoria_1, attach_1])

        assert not fiscal_1.troll
        assert not fiscal_2.troll

        nueva_carga(mesa_categoria_1, fiscal_1, [20, 35], Carga.TIPOS.parcial)
        nueva_carga(mesa_categoria_1, fiscal_2, [20, 35], Carga.TIPOS.parcial)

        consumir_novedades_carga()
        refrescar_data([mesa_categoria_1])

        assert mesa_categoria_1.status == MesaCategoria.STATUS.parcial_consolidada_dc
        assert Carga.objects.filter(invalidada=True).count() == 0

        aplicar_marca_troll(fiscal_2)
        consumir_novedades_carga()
        refrescar_data([mesa_categoria_1, fiscal_2])

        assert mesa_categoria_1.status == MesaCategoria.STATUS.parcial_sin_consolidar
        assert Carga.objects.filter(invalidada=True).count() == 1
예제 #12
0
def nueva_categoria(nombres_opciones, prioritaria=False):
    categoria = CategoriaFactory(
        opciones=[])  # sin opciones para crearlas ad hoc
    for nombre in nombres_opciones:
        CategoriaOpcionFactory(categoria=categoria,
                               opcion__nombre=nombre,
                               prioritaria=prioritaria)
    return categoria
예제 #13
0
def test_elegir_acta_mesas_redirige(db, fiscal_client):

    assert Mesa.objects.count() == 0
    assert VotoMesaReportado.objects.count() == 0
    c = CircuitoFactory()
    e1 = CategoriaFactory()
    e2 = CategoriaFactory()

    m1 = AttachmentFactory(mesa__categoria=[e1], mesa__lugar_votacion__circuito=c).mesa
    e2 = CategoriaFactory()
    m2 = AttachmentFactory(mesa__categoria=[e1, e2], mesa__lugar_votacion__circuito=c).mesa

    assert m1.orden_de_carga == 1
    assert m2.orden_de_carga == 2

    response = fiscal_client.get(reverse('elegir-acta-a-cargar'))
    assert response.status_code == 302
    assert response.url == reverse('mesa-cargar-resultados', args=[e1.id, m1.numero])

    # como m1 queda en periodo de "taken" (aunque no se haya ocupado aun)
    # se pasa a la siguiente mesa
    response = fiscal_client.get(reverse('elegir-acta-a-cargar'))
    assert response.status_code == 302
    assert response.url == reverse('mesa-cargar-resultados', args=[e1.id, m2.numero])

    # se carga esa categoria
    VotoMesaReportadoFactory(
        carga__mesa=m2,
        carga__categoria=e1,
        opcion=e1.opciones.first(),
        votos=1
    )

    # FIX ME . El periodo de taken deberia ser *por categoria*.
    # en este escenario donde esta lockeado la mesa para la categoria 1, pero no se está
    # cargando la mesa 2, un dataentry queda idle
    response = fiscal_client.get(reverse('elegir-acta-a-cargar'))
    assert response.status_code == 200   # no hay actas

    m2.taken = None
    m2.save()
    response = fiscal_client.get(reverse('elegir-acta-a-cargar'))
    assert response.status_code == 302
    assert response.url == reverse('mesa-cargar-resultados', args=[e2.id, m2.numero])
예제 #14
0
def test_excluir_fiscal_que_ya_cargo(db, settings):
    """
    Se verifica que no se propone una MesaCategoria para cargar, a un fiscal que ya hizo una carga
    sobre esa MesaCategoria.
    """
    settings.MIN_COINCIDENCIAS_IDENTIFICACION = 1
    settings.MIN_COINCIDENCIAS_CARGAS = 2
    fiscal_1 = nuevo_fiscal()
    fiscal_2 = nuevo_fiscal()
    fiscal_3 = nuevo_fiscal()
    pv = CategoriaFactory(nombre="PV")
    gv = CategoriaFactory(nombre="GV")
    # hago que la categoria pv sea prioritaria
    PrioridadSchedulingFactory(categoria=pv, desde_proporcion=0, hasta_proporcion=100, prioridad=30)

    mesa = MesaFactory(numero="51", categorias=[gv, pv])
    identificar_mesa(mesa, fiscal_3)
    mesacat_pv = MesaCategoria.objects.filter(mesa=mesa, categoria=pv).first()
    mesacat_gv = MesaCategoria.objects.filter(mesa=mesa, categoria=gv).first()

    # antes de cargar, la siguiente mesacat para ambos fiscales es pv, que es más prioritaria
    mesacat_fiscal_2 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_2).mas_prioritaria()
    mesacat_fiscal_3 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_3).mas_prioritaria()
    assert mesacat_fiscal_2 == mesacat_pv
    assert mesacat_fiscal_3 == mesacat_pv

    # agrego una carga 
    carga = CargaFactory(mesa_categoria=mesacat_pv, fiscal=fiscal_2, tipo=Carga.TIPOS.parcial)

    # la siguiente mesacat para el fiscal_2 es gv, porque de pv ya hizo una carga
    # la del fiscal 3 es pv, que es más prioritaria
    mesacat_fiscal_2 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_2).mas_prioritaria()
    mesacat_fiscal_3 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_3).mas_prioritaria()
    assert mesacat_fiscal_2 == mesacat_gv
    assert mesacat_fiscal_3 == mesacat_pv

    # el fiscal 2 carga la mesacat que le queda
    carga = CargaFactory(mesa_categoria=mesacat_gv, fiscal=fiscal_2, tipo=Carga.TIPOS.parcial)

    # ahora el fiscal 2 no tiene mesacat para cargar, el fiscal 3 sigue con pv como prioritaria
    mesacat_fiscal_2 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_2).mas_prioritaria()
    mesacat_fiscal_3 = MesaCategoria.objects.con_carga_pendiente().sin_cargas_del_fiscal(fiscal_3).mas_prioritaria()
    assert mesacat_fiscal_2 == None
    assert mesacat_fiscal_3 == mesacat_pv
예제 #15
0
def test_data_acciones_para_monitoreo_antitrolling(db):
    # 40 mesas con su mesacat
    # 30 cargas, 22 procesadas, de esas 1 inválidas
    # de las 8 no procesadas, 2 inválidas

    presi = CategoriaFactory()
    fiscal = nuevo_fiscal()
    for ix in range(40):
        nueva_mesa = MesaFactory(categorias=[presi])
        mesacat = MesaCategoria.objects.filter(mesa=nueva_mesa,
                                               categoria=presi).first()
        if (ix < 30):
            carga = nueva_carga(mesacat, fiscal, [20, 15], Carga.TIPOS.parcial)
            if (ix < 1):
                carga.invalidada = True
                carga.procesada = True
            elif (ix < 22):
                carga.invalidada = False
                carga.procesada = True
            elif (ix < 24):
                carga.invalidada = True
                carga.procesada = False
            else:
                carga.invalidada = False
                carga.procesada = False
            carga.save(update_fields=['invalidada', 'procesada'])

    ParametrosAntitrolling.reset()
    rangos = GeneradorInfoAcciones(Carga.objects).rangos()
    rango_total = next(rango for rango in rangos if rango.texto == 'Total')
    assert rango_total.cantidad == 30
    assert rango_total.porcentaje == 100
    rango_validas = next(rango for rango in rangos
                         if rango.texto == 'Válidas procesadas')
    assert rango_validas.cantidad == 21
    assert rango_validas.porcentaje == 70
    rango_pendientes = next(rango for rango in rangos
                            if rango.texto == 'Pendientes de proceso')
    assert rango_pendientes.cantidad == 6
    assert rango_pendientes.porcentaje == 20
    rango_invalidas = next(rango for rango in rangos
                           if rango.texto == 'Invalidadas')
    assert rango_invalidas.cantidad == 3
    assert rango_invalidas.porcentaje == 10
    assert rango_invalidas.indicador_peligro == IndicadorDePeligro.indicador_rojo

    # con otros valores de peligro, corresponde otro indicador para el mismo porcentaje
    rango_invalidas.set_umbrales_de_peligro(8, 15, 30)
    assert rango_invalidas.cantidad == 3
    assert rango_invalidas.porcentaje == 10
    assert rango_invalidas.indicador_peligro == IndicadorDePeligro.indicador_amarillo
    rango_invalidas.set_umbrales_de_peligro(28, 35, 50)
    assert rango_invalidas.cantidad == 3
    assert rango_invalidas.porcentaje == 10
    assert rango_invalidas.indicador_peligro == IndicadorDePeligro.indicador_verde
예제 #16
0
def test_scheduler_orden_estandar(db, settings):
    # Creamos 5 attachments sin identificar
    attachments = AttachmentFactory.create_batch(
        5, status=Attachment.STATUS.sin_identificar)

    c1 = CategoriaFactory(sensible=True)
    c2 = CategoriaFactory(sensible=True)
    m1 = MesaFactory(categorias=[c1])
    IdentificacionFactory(
        mesa=m1,
        status='identificada',
        source=Identificacion.SOURCES.csv,
    )
    m2 = MesaFactory(categorias=[c1, c2])
    assert MesaCategoria.objects.count() == 3
    IdentificacionFactory(
        mesa=m2,
        status='identificada',
        source=Identificacion.SOURCES.csv,
    )
    # Ambas consolidadas vía csv.

    consumir_novedades_identificacion()
    with override_config(COEFICIENTE_IDENTIFICACION_VS_CARGA=10):
        scheduler()
    assert ColaCargasPendientes.largo_cola() == 16

    # Las primeras seis tareas son de carga de votos.
    for i in range(6):
        consumir(False)

    assert ColaCargasPendientes.largo_cola() == 10

    # las siguientes diez son identificaciones.
    for i in range(10):
        consumir()

    assert ColaCargasPendientes.largo_cola() == 0

    # Ya no queda nada en la cola.
    (mc, attachment) = ColaCargasPendientes.siguiente_tarea(fiscal=None)
    assert mc is None and attachment is None
예제 #17
0
def test_cargar_resultados_redirige_a_identificar(db, fiscal_client, status,
                                                  parcial):
    mesa = MesaFactory()
    a = AttachmentFactory(mesa=mesa)
    c1 = CategoriaFactory(requiere_cargas_parciales=True)
    m1c1 = MesaCategoriaFactory(categoria=c1,
                                coeficiente_para_orden_de_carga=0.1,
                                status=status,
                                mesa=mesa)
    response = fiscal_client.get(reverse('siguiente-accion'))
    assert response.status_code == HTTPStatus.FOUND
    assert response.url.startswith('/clasificar-actas/')
예제 #18
0
def test_setup_opciones(db):
    assert not Opcion.objects.exists()
    c = CategoriaFactory(opciones=[])
    call_command('setup_opciones_basicas')
    assert Opcion.objects.get(**settings.OPCION_BLANCOS)
    assert Opcion.objects.get(**settings.OPCION_TOTAL_VOTOS)
    assert Opcion.objects.get(**settings.OPCION_TOTAL_SOBRES)
    assert Opcion.objects.get(**settings.OPCION_NULOS)
    assert Opcion.objects.get(**settings.OPCION_RECURRIDOS)
    assert Opcion.objects.get(**settings.OPCION_ID_IMPUGNADA)
    assert Opcion.objects.get(**settings.OPCION_COMANDO_ELECTORAL)
    assert c.opciones.count() == 7
예제 #19
0
def test_configuracion_categoria(db):
    categoria = CategoriaFactory(prioridad=15)
    prioridades = PrioridadScheduling.objects.filter(categoria=categoria)
    assert prioridades.count() == 1
    assert prioridades.first().prioridad == 15
    categoria.prioridad = 21
    categoria.save(update_fields=['prioridad'])
    prioridades = PrioridadScheduling.objects.filter(categoria=categoria)
    assert prioridades.count() == 1
    assert prioridades.first().prioridad == 21
    categoria.prioridad = None
    categoria.save(update_fields=['prioridad'])
    prioridades = PrioridadScheduling.objects.filter(categoria=categoria)
    assert prioridades.count() == 0

    categoria_2 = CategoriaFactory()
    prioridades = PrioridadScheduling.objects.filter(categoria=categoria)
    assert prioridades.count() == 0
예제 #20
0
def test_formset_en_carga_total_reusa_parcial_confirmada(
        db, fiscal_client, admin_user, settings):
    # Solo una carga, para simplificar el setup
    settings.MIN_COINCIDENCIAS_CARGAS = 1

    c = CategoriaFactory(id=25000, opciones=[])

    # Notar que el orden no coincide con el id
    o1 = CategoriaOpcionFactory(categoria=c, orden=3, prioritaria=True).opcion
    o2 = CategoriaOpcionFactory(categoria=c, orden=1, prioritaria=False).opcion
    o3 = CategoriaOpcionFactory(categoria=c, orden=2, prioritaria=False).opcion
    o4 = CategoriaOpcionFactory(categoria=c, orden=4, prioritaria=True).opcion

    mc = MesaCategoriaFactory(categoria=c)
    mc.asignar_a_fiscal()
    admin_user.fiscal.asignar_mesa_categoria(mc)

    # Se carga parcialente, la opcion prioritaira "o"
    carga = CargaFactory(mesa_categoria=mc, tipo='parcial')
    VotoMesaReportadoFactory(carga=carga, opcion=o1, votos=10)
    VotoMesaReportadoFactory(carga=carga, opcion=o4, votos=3)

    # Consolidamos.
    consumir_novedades_carga()
    mc.refresh_from_db()
    assert mc.status == MesaCategoria.STATUS.parcial_consolidada_dc
    assert mc.carga_testigo == carga
    assert set(carga.opcion_votos()) == {(o1.id, 10), (o4.id, 3)}

    # Ahora pedimos la carga total
    totales = reverse('carga-total', args=[mc.id])
    response = fiscal_client.get(totales)

    # Tenemos las tres opciones en orden
    assert len(response.context['formset']) == 4 + len(
        Opcion.opciones_no_partidarias_obligatorias())
    assert response.context['formset'][0].initial['opcion'] == o2
    assert response.context['formset'][1].initial['opcion'] == o3
    assert response.context['formset'][2].initial['opcion'] == o1
    assert response.context['formset'][3].initial['opcion'] == o4

    # y los valores de los votos
    assert response.context['formset'][0].initial['votos'] is None
    assert response.context['formset'][1].initial['votos'] is None
    assert response.context['formset'][2].initial['votos'] == 10
    assert response.context['formset'][3].initial['votos'] == 3

    # el valor previo es readonly
    assert response.context['formset'][2].fields['votos'].widget.attrs[
        'readonly'] is True
    assert response.context['formset'][3].fields['votos'].widget.attrs[
        'readonly'] is True
예제 #21
0
def test_cargar_resultados_redirige_a_parcial_si_es_necesario(
        db, fiscal_client, status, parcial):
    mesa = MesaFactory()
    a = AttachmentFactory(mesa=mesa)
    c1 = CategoriaFactory(requiere_cargas_parciales=True, sensible=True)
    m1c1 = MesaCategoriaFactory(categoria=c1,
                                coeficiente_para_orden_de_carga=0.1,
                                status=status,
                                mesa=mesa)
    response = fiscal_client.get(reverse('siguiente-accion'))
    assert response.status_code == HTTPStatus.FOUND
    assert response.url == reverse(
        'carga-parcial' if parcial else 'carga-total', args=[m1c1.id])
예제 #22
0
def carga_inicial(db):
    d1 = DistritoFactory(numero=1)
    s1 = SeccionFactory(numero=50, distrito=d1)
    circ = CircuitoFactory(numero='2', seccion=s1)

    # Creamos los partidos.
    fdt = OpcionFactory(codigo='FdT', nombre='FdT', partido__nombre='FpT')
    jpc = OpcionFactory(codigo='JpC', nombre='JpC', partido__nombre='JpC')
    c2019 = OpcionFactory(codigo='C2019',
                          nombre='C2019',
                          partido__nombre='C2019')

    categorias = []
    for categoria, prioritaria in CATEGORIAS:
        categoria_general = CategoriaGeneralFactory(nombre=categoria)
        # La categoría en sí tiene un nombre arbitrario para testear que
        # el matching sea en base a la categoría general.
        categoria_bd = CategoriaFactory(nombre=f'Categoría {categoria}',
                                        categoria_general=categoria_general)

        # La factory las crea con unas opciones que hacen ruido en estos tests.
        for nombre in ['opc1', 'opc2', 'opc3', 'opc4']:
            opcion = Opcion.objects.get(nombre=nombre)
            opcion.delete()

        categorias.append(categoria_bd)
        CategoriaOpcionFactory(categoria=categoria_bd,
                               prioritaria=prioritaria,
                               opcion=fdt)
        CategoriaOpcionFactory(categoria=categoria_bd,
                               prioritaria=prioritaria,
                               opcion=jpc)
        if categoria == 'Presidente y vice':
            CategoriaOpcionFactory(categoria=categoria_bd,
                                   prioritaria=False,
                                   opcion=c2019)
        if prioritaria:
            # Adecuamos las opciones prioritarias.
            hacer_prioritaria_en_cat(categoria_bd, Opcion.total_votos())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.blancos())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.nulos())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.sobres())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.recurridos())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.id_impugnada())
            hacer_prioritaria_en_cat(categoria_bd, Opcion.comando_electoral())

    MesaFactory(numero='4012',
                lugar_votacion__circuito=circ,
                electores=100,
                circuito=circ,
                categorias=categorias)
예제 #23
0
def test_orden_de_carga_escenario_base(db, settings):
    """
    Se verifica la asignacion de ordenes de carga para una seccion standard, con dos categorias standard
    con una categoria prioritaria y una standard
    """
    asignar_prioridades_standard(settings)
    settings.MIN_COINCIDENCIAS_IDENTIFICACION = 1
    fiscal = nuevo_fiscal()

    # una seccion standard, un circuito, 25 mesas en el mismo lugar de votacion, dos categorias standard
    la_seccion, circuito, lugar_votacion = crear_seccion("Bera centro")
    pv = CategoriaFactory(nombre="PV")
    gv = CategoriaFactory(nombre="GV")
    categorias = [pv, gv]

    [mesas] = crear_mesas([lugar_votacion], categorias, 25)

    # primera mesa
    identificar_mesa(mesas[0], fiscal)
    verificar_valores_scheduling_mesacat(mesas[0], pv, 1, 1, 200)
    verificar_valores_scheduling_mesacat(mesas[0], gv, 1, 1, 200)

    # segunda mesa
    identificar_mesa(mesas[1], fiscal)
    verificar_valores_scheduling_mesacat(mesas[1], pv, 5, 2, 10000)
    verificar_valores_scheduling_mesacat(mesas[1], gv, 5, 2, 10000)

    # quinta mesa
    for nro in range(2, 5):
        identificar_mesa(mesas[nro], fiscal)
    verificar_valores_scheduling_mesacat(mesas[4], pv, 17, 5, 170000)
    verificar_valores_scheduling_mesacat(mesas[4], gv, 17, 5, 170000)

    # mesa 25
    for nro in range(5, 25):
        identificar_mesa(mesas[nro], fiscal)
    verificar_valores_scheduling_mesacat(mesas[24], pv, 97, 25, 970000)
    verificar_valores_scheduling_mesacat(mesas[24], gv, 97, 25, 970000)
예제 #24
0
def test_mesa_de_circuito__url_mesa_sin_resultados(fiscal_client):
    categoria = CategoriaFactory(nombre='default')
    mesa1 = IdentificacionFactory(status='identificada', source=Identificacion.SOURCES.csv).mesa
    consumir_novedades_identificacion()
    query_string_mesa_1 = (
        f'?mesa={mesa1.id}&circuito={mesa1.circuito.id}'
        '&tipoDeAgregacion=todas_las_cargas&opcionaConsiderar=todas'
        )
    url_mesa_1 = reverse('mesas-circuito', args=[categoria.id]) + query_string_mesa_1
    response = fiscal_client.get(url_mesa_1)

    assert response.status_code == 200
    assert response.context['mensaje_no_hay_info'] in response.content.decode('utf-8')
    assert not response.context['resultados'].exists()
예제 #25
0
def test_datos_previos_desde_metadata(db):
    o1 = OpcionFactory(tipo=Opcion.TIPOS.metadata)
    o2 = OpcionFactory()
    cat1 = CategoriaFactory(opciones=[o1, o2])
    cat1o1 = CategoriaOpcionFactory(categoria=cat1, opcion=o1, orden=1)
    cat1o2 = CategoriaOpcionFactory(categoria=cat1, opcion=o2, orden=2)

    mc = MesaCategoriaFactory(categoria=cat1,
                              status=MesaCategoria.STATUS.total_consolidada_dc)
    carga = CargaFactory(mesa_categoria=mc, tipo='total')
    VotoMesaReportadoFactory(carga=carga, opcion=o1, votos=10)
    VotoMesaReportadoFactory(carga=carga, opcion=o2, votos=12)

    # otra categoria incluye la misma metadata.
    o2 = OpcionFactory()
    cat2 = CategoriaFactory(opciones=[o1, o2])
    cat1o1 = CategoriaOpcionFactory(categoria=cat2, opcion=o1, orden=1)
    cat2o2 = CategoriaOpcionFactory(categoria=cat2, opcion=o2, orden=2)
    mc2 = MesaCategoriaFactory(categoria=cat2, mesa=mc.mesa)

    # esa mesa categoria incluye la metadata ya cargada en mc1
    assert mc2.datos_previos('parcial') == {o1.id: 10}
    assert mc2.datos_previos('total') == {o1.id: 10}
예제 #26
0
def test_mesa_de_circuito__navegacion_categorias(fiscal_client):
    categoria1 = CategoriaFactory(nombre='cat1')
    categoria2 = CategoriaFactory(nombre='cat2')

    mesa1 = MesaFactory(categorias=[categoria1, categoria2])

    query_string_mesa_1 = (
        f'?mesa={mesa1.id}&circuito={mesa1.circuito.id}'
        '&tipoDeAgregacion=todas_las_cargas&opcionaConsiderar=todas'
        )

    url_mesa_1 = reverse('mesas-circuito', args=[categoria1.id]) + query_string_mesa_1
    response = fiscal_client.get(url_mesa_1)

    assert response.status_code == 200
    content = html.unescape(response.content.decode('utf-8'))
    assert response.context['mensaje_no_hay_info'] in content

    link_categoria_1 = f"   href=\"/elecciones/resultados/mesas_circuito/{categoria1.id}{query_string_mesa_1}\""
    link_categoria_2 = f"   href=\"/elecciones/resultados/mesas_circuito/{categoria2.id}{query_string_mesa_1}\""

    assert link_categoria_1 in content
    assert link_categoria_2 in content
예제 #27
0
def test_formset_reusa_metadata(db, fiscal_client, admin_user):
    # Hay una categoria con una opcion metadata ya consolidada.
    o1 = OpcionFactory(tipo=Opcion.TIPOS.metadata)
    cat1 = CategoriaFactory(opciones=[o1])
    from elecciones.models import CategoriaOpcion
    # Me aseguro de que no estuviese asociada con otro orden.
    CategoriaOpcion.objects.filter(categoria=cat1, opcion=o1).delete()
    cat1op1 = CategoriaOpcionFactory(categoria=cat1, opcion=o1, orden=1)
    mc = MesaCategoriaFactory(categoria=cat1,
                              status=MesaCategoria.STATUS.total_consolidada_dc)
    carga = CargaFactory(mesa_categoria=mc, tipo='total')
    VotoMesaReportadoFactory(carga=carga, opcion=o1, votos=10)

    # Otra categoría incluye la misma metadata.
    o2 = OpcionFactory()
    cat2 = CategoriaFactory(opciones=[o1, o2])
    # Me aseguro de que no estuviesen asociadas con otro orden.
    CategoriaOpcion.objects.filter(categoria=cat2, opcion=o1).delete()
    CategoriaOpcion.objects.filter(categoria=cat2, opcion=o2).delete()
    cat2op1 = CategoriaOpcionFactory(categoria=cat2, opcion=o1, orden=1)
    cat2op1 = CategoriaOpcionFactory(categoria=cat2, opcion=o2, orden=2)
    mc2 = MesaCategoriaFactory(categoria=cat2, mesa=mc.mesa)

    mc2.asignar_a_fiscal()
    admin_user.fiscal.asignar_mesa_categoria(mc2)
    response = fiscal_client.get(reverse('carga-total', args=[mc2.id]))
    assert len(response.context['formset']) == 2 + len(
        Opcion.opciones_no_partidarias_obligatorias())
    assert response.context['formset'][0].initial['opcion'] == o1
    assert response.context['formset'][1].initial['opcion'] == o2

    # y los valores de los votos
    assert response.context['formset'][0].initial['votos'] == 10
    assert response.context['formset'][0].fields['votos'].widget.attrs[
        'readonly'] is True

    assert response.context['formset'][1].initial['votos'] is None
예제 #28
0
def test_cargar_resultados_redirige_a_parcial_si_es_necesario_con_scheduler(
        db, fiscal_client, status, parcial):
    mesa = MesaFactory()
    a = AttachmentFactory(mesa=mesa)
    c1 = CategoriaFactory(requiere_cargas_parciales=True, sensible=True)
    with override_config(ASIGNAR_MESA_EN_EL_MOMENTO_SI_NO_HAY_COLA=False):
        m1c1 = MesaCategoriaFactory(categoria=c1,
                                    coeficiente_para_orden_de_carga=0.1,
                                    status=status,
                                    mesa=mesa)
        scheduler()
        response = fiscal_client.get(reverse('siguiente-accion'))
        assert response.status_code == HTTPStatus.FOUND
        assert response.url == reverse(
            'carga-parcial' if parcial else 'carga-total', args=[m1c1.id])
예제 #29
0
def test_chequear_resultado_mesa(db, fiscal_client):
    opcs = OpcionFactory.create_batch(3, es_contable=True)
    e1 = CategoriaFactory(opciones=opcs)
    e2 = CategoriaFactory(opciones=opcs)
    mesa = MesaFactory(categoria=[e1, e2])
    me = MesaCategoria.objects.get(categoria=e1, mesa=mesa)
    assert me.confirmada is False
    votos1 = VotoMesaReportadoFactory(opcion=opcs[0], carga__mesa=mesa, carga__categoria=e1, votos=1)
    votos2 = VotoMesaReportadoFactory(opcion=opcs[1], carga__mesa=mesa, carga__categoria=e1, votos=2)
    votos3 = VotoMesaReportadoFactory(opcion=opcs[2], carga__mesa=mesa, carga__categoria=e1, votos=1)

    # a otra categoria
    VotoMesaReportadoFactory(opcion=opcs[2], carga__mesa=mesa, carga__categoria=e2, votos=1)

    url = reverse('chequear-resultado-mesa', args=[e1.id, mesa.numero])
    response = fiscal_client.get(url)

    assert list(response.context['reportados']) == [votos1, votos2, votos3]

    response = fiscal_client.post(url, {'confirmar': 'confirmar'})
    assert response.status_code == 302
    assert response.url == reverse('chequear-resultado')
    me.refresh_from_db()
    assert me.confirmada is True
예제 #30
0
def test_siguiente_prioriza_estado_y_luego_coeficiente(db, settings, setup_constance, django_assert_num_queries):

    f = FiscalFactory()
    c = CategoriaFactory(prioridad=1)
    m1 = MesaFactory()
    AttachmentFactory(mesa=m1)
    mc1 = MesaCategoriaFactory(
        status=MesaCategoria.STATUS.parcial_sin_consolidar,
        categoria=c,
        coeficiente_para_orden_de_carga=1.0,
        mesa=m1
    )
    m2 = MesaFactory()
    AttachmentFactory(mesa=m2)
    mc2 = MesaCategoriaFactory(
        categoria=c,
        status=MesaCategoria.STATUS.total_en_conflicto,
        coeficiente_para_orden_de_carga=99.0,
        mesa=m2
    )
    m3 = MesaFactory()
    AttachmentFactory(mesa=m3)
    mc3 = MesaCategoriaFactory(
        categoria=c,
        status=MesaCategoria.STATUS.total_en_conflicto,
        coeficiente_para_orden_de_carga=2.0,
        mesa=m3
    )
    with django_assert_num_queries(17):
        assert MesaCategoria.objects.siguiente() == mc1

    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc1.asignar_a_fiscal()
    assert MesaCategoria.objects.siguiente() == mc3
    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc3.asignar_a_fiscal()
    assert MesaCategoria.objects.siguiente() == mc2
    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc2.asignar_a_fiscal()

    # A igualdad de asignaciones, se vuelven a repetir.
    assert MesaCategoria.objects.siguiente() == mc1
    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc1.asignar_a_fiscal()
    assert MesaCategoria.objects.siguiente() == mc3
    for i in range(settings.MIN_COINCIDENCIAS_CARGAS):
        mc3.asignar_a_fiscal()
    assert MesaCategoria.objects.siguiente() == mc2