Nacim Betrouni

48 papers B 1C 3Misc 1Journal 23Unranked 20
YearRankTypeTitle / Venue / Authors
2025 conf
DeLTA
Moad Hani, Nacim Betrouni, Fatima Zahra Ouardirhi, Saïd Mahmoudi, Mohammed Benjelloun
2025 J jnl
NeuroImage
Mathieu Dhoisne, Nacim Betrouni, Sophie Hennion, Lucie Plomhause, Arnaud Delval, Philippe Derambure
2025 C conf
DATA
Moad Hani, Nacim Betrouni, Saïd Mahmoudi, Mohammed Benjelloun
2022 J jnl
Sensors
Saeed Zahran, Mahdi Mahmoudzadeh, Fabrice Wallois, Nacim Betrouni, Philippe Derambure, Matthieu Le Prado, Augustin Palacios Laloy, Etienne Labyt
2022 conf
EUSIPCO
Isabel Casso, José Rouillard, Hakim Si-Mohammed, Nacim Betrouni, François Cabestaing, Anahita Basirat
2020 conf
ICMI Companion
Claire Dussard, Anahita Basirat, Nacim Betrouni, Caroline Moreau, David Devos, François Cabestaing, José Rouillard
2019 J jnl
Multim. Tools Appl.
Wael Brahim, Nacim Betrouni, Makram Mestiri, Kamel Hamrouni
2018 conf
IWBBIO (2)
Abderraouf Hadj Henni, David Pasquier, Nacim Betrouni
2018 J jnl
Comput. Medical Imaging Graph.
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2017 J jnl
CoRR
Jose Dolz, Nicolas Reyns, Nacim Betrouni, Dris Kharroubi, Mathilde Quidet, Laurent Massoptier, Maximilien Vermandel
2017 Misc conf
ATSIP
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2016 conf
ICFSP
P. Chuzel, A. Mansour, Julien Ognard, Jean-Christophe Gentric, Luc Bressollette, Denis Hamad, Nacim Betrouni
2016 J jnl
J. Digit. Imaging
Ouardia Chilali, Philippe A. Puech, S. Lakroum, Moussa Diaf, Serge Mordon, Nacim Betrouni
2016 C conf
IPAS
Wael Brahim, Makram Mestiri, Nacim Betrouni, Kamel Hamrouni
2016 J jnl
Comput. Medical Imaging Graph.
Jose Dolz, Nacim Betrouni, Mathilde Quidet, Dris Kharroubi, Henri-Arthur Leroy, Nicolas Reyns, Laurent Massoptier, Maximilien Vermandel
2015 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Nacim Betrouni, Nasr Makni, S. Lakroum, Serge Mordon, Arnauld Villers, Philippe A. Puech
2015 conf
ISBI
Maximilien Vermandel, Grégory Baert, Nicolas Reyns, Nacim Betrouni
2014 J jnl
Comput. Biol. Medicine
Ouardia Chilali, Adil Ouzzane, Moussa Diaf, Nacim Betrouni
2014 J jnl
Inf. Fusion
Nasr Makni, Nacim Betrouni, Olivier Colot
2014 J jnl
Comput. Medical Imaging Graph.
Guillaume Tartare, Denis Hamad, Mustapha Azahaf, Philippe A. Puech, Nacim Betrouni
2013 conf
EMBC
Nacim Betrouni, Pierre Colin, Philippe A. Puech, Arnauld Villers, Serge Mordon
2013 J jnl
Int. J. Comput. Assist. Radiol. Surg.
L. Verscheure, Laurent Peyrodie, Anne-Sophie Dewalle, Nicolas Reyns, Nacim Betrouni, Serge Mordon, Maximilien Vermandel
2012 J jnl
Comput. Methods Programs Biomed.
Nasr Makni, Philippe A. Puech, Pierre Colin, Abdelrahmene Azzouzi, Serge Mordon, Nacim Betrouni
2011 conf
Prostate Cancer Imaging
Nasr Makni, A. Iancu, Philippe A. Puech, Serge Mordon, Nacim Betrouni
2011 J jnl
IEEE Trans. Medical Imaging
Anne-Sophie Dewalle-Vignion, Nacim Betrouni, Renaud Lopes, Damien Huglo, Simon Stute, Maximilien Vermandel
2011 J jnl
Pattern Recognit.
Renaud Lopes, P. Dubois, Imen Bhouri, Mohamed Hedi Bedoui, Salah Maouche, Nacim Betrouni
2010 J jnl
Comput. Biol. Medicine
Renaud Lopes, Marc Steinling, William Szurhaj, Salah Maouche, P. Dubois, Nacim Betrouni
2009 conf
ISBI
Renaud Lopes, Maximilien Vermandel, Anne-Sophie Dewalle-Vignion, Salah Maouche, Nacim Betrouni
2009 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Nasr Makni, Philippe A. Puech, Renaud Lopes, Anne-Sophie Dewalle, Olivier Colot, Nacim Betrouni
2009 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Philippe A. Puech, Nacim Betrouni, Nasr Makni, Anne-Sophie Dewalle, Arnauld Villers, Laurent Lemaitre
2009 C conf
CIARP
Foued Derraz, Abdelmalik Taleb-Ahmed, Antonio Pinti, Laurent Peyrodie, Nacim Betrouni, Azeddine Chikh, Fethi Bereksi-Reguig
2009 J jnl
Medical Image Anal.
Renaud Lopes, Nacim Betrouni
2009 B conf
ICIP
Foued Derraz, Abdelmalik Taleb-Ahmed, Nacim Betrouni, Azeddine Chikh, Antonio Pinti, Fethi Bereksi-Reguig
2008 conf
ISBI
Renaud Lopes, Nasr Makni, Romain Viard, Marc Steinling, Salah Maouche, Nacim Betrouni
2008 conf
ICIAR
Sami Bourouis, Kamel Hamrouni, Nacim Betrouni
2008 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Renaud Lopes, P. Dubois, Nasr Makni, William Szurhaj, Salah Maouche, Nacim Betrouni
2008 J jnl
Int. J. Comput. Assist. Radiol. Surg.
Romain Viard, Serge Mordon, Nacim Betrouni, Maximilien Vermandel, Matthieu Vanhoutte, Jean Rousseau
2008 conf
ISBI
Nasr Makni, Philippe A. Puech, Renaud Lopes, Anne-Sophie Dewalle, Olivier Colot, Nacim Betrouni
2007 J jnl
Comput. Medical Imaging Graph.
Maximilien Vermandel, Nacim Betrouni, Christian A. Taschner, Christian Vasseur, Jean Rousseau
2007 J jnl
Pattern Recognit. Lett.
Nacim Betrouni, Maximilien Vermandel, David Pasquier, Jean Rousseau
2006 conf
ISBI
Nacim Betrouni, Maximilien Vermandel, Matthieu Coulanges, David Pasquier, Salah Maouche, Jean Rousseau
2006 conf
EMBC
David Pasquier, Nacim Betrouni, Maximilien Vermandel, Thomas Lacornerie, Eric Lartigau, Jean Rousseau
2006 conf
EMBC
Anne-Sophie Dewalle, Nacim Betrouni, Maximilien Vermandel, Polina Ivanova, Marc Steinling, Jean Rousseau, Christian Vasseur
2006 conf
EMBC
Nacim Betrouni, David Pasquier, Anne-Sophie Dewalle, Reza Jounwaz, P. Dubois, Renaud Lopes, Eric Lartigau
2004 conf
ISBI
Nacim Betrouni, Jean Rousseau, Maximilien Vermandel, Salah Maouche
2004 conf
CARS
Georges Palos, Nacim Betrouni, Maximilien Vermandel, David Pasquier, Vincent Devlaminck, Jean Rousseau
2004 conf
CARS
Nacim Betrouni, Georges Palos, Maximilien Vermandel, David Pasquier, Salah Maouche, Jean Rousseau
2003 conf
MICCAI (1)
Maximilien Vermandel, Nacim Betrouni, Georges Palos, Jean-Yves Gauvrit, Christian Vasseur, Jean Rousseau
tests/unit/test_database_exporters.py
← Index tests/unit/test_database_exporters.py python
"""
Unit tests for database exporters.
"""
import pytest
import json
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.unit, pytest.mark.exporters]


# ============================================================================
# Sample Dataclass for Testing
# ============================================================================

@dataclass
class SampleData:
    """Sample dataclass for export testing."""
    sha256: str = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    name: str = "test_sample"
    value: int = 42
    _id: str = "test_id"


# ============================================================================
# PrintExporter Tests
# ============================================================================

class TestPrintExporter:
    """Tests for PrintExporter class."""

    def test_initialization(self, mock_logger):
        """Test PrintExporter initialization."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        assert exporter.index_prefix == "test_index"
        assert exporter.log is mock_logger

    def test_export_dataclass(self, mock_logger, capsys):
        """Test exporting a dataclass."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        sample = SampleData()

        result = exporter.export(sample)

        assert result is True
        captured = capsys.readouterr()
        assert "test_sample" in captured.out

    def test_export_list_of_dataclasses(self, mock_logger, capsys):
        """Test exporting a list of dataclasses."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        samples = [SampleData(name="sample1"), SampleData(name="sample2")]

        result = exporter.export(samples)

        assert result is True
        captured = capsys.readouterr()
        assert "sample1" in captured.out
        assert "sample2" in captured.out

    def test_export_dict(self, mock_logger, capsys):
        """Test exporting a dictionary."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        data = {"key": "value", "number": 42}

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "key" in captured.out
        assert "value" in captured.out

    def test_export_none(self, mock_logger):
        """Test exporting None."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        result = exporter.export(None)

        assert result is True

    def test_export_empty_list(self, mock_logger):
        """Test exporting empty list."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")

        result = exporter.export([])

        assert result is True


# ============================================================================
# ElasticsearchExporter Tests
# ============================================================================

class TestElasticsearchExporter:
    """Tests for ElasticsearchExporter class."""

    def test_initialization(self, mock_logger):
        """Test ElasticsearchExporter initialization."""
        with patch('redb.settings.get_elasticsearch_client') as mock_client:
            mock_client.return_value = Mock()
            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")

            assert exporter.index_prefix == "test_index"
            assert exporter.log is mock_logger
            assert exporter.client is not None

    def test_export_dataclass(self, mock_logger):
        """Test exporting a dataclass to Elasticsearch."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData()

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is True
            mock_client.index.assert_called()

    def test_export_list(self, mock_logger):
        """Test exporting a list to Elasticsearch."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            samples = [SampleData(name="sample1"), SampleData(name="sample2")]

            result = exporter.export(
                samples,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": "test_hash"}
            )

            assert result is True
            # Should be called twice (once per sample)
            assert mock_client.index.call_count == 2

    def test_export_failure(self, mock_logger):
        """Test handling export failure."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_client.index.side_effect = Exception("Connection failed")
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData()

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is False
            mock_logger.error.assert_called()

    def test_export_with_custom_id(self, mock_logger):
        """Test exporting with custom _id field."""
        with patch('redb.settings.get_elasticsearch_client') as mock_get_client:
            mock_client = Mock()
            mock_get_client.return_value = mock_client

            from redb.extractors.database_exporters import ElasticsearchExporter

            exporter = ElasticsearchExporter(mock_logger, "test_index")
            sample = SampleData(_id="custom_id")

            result = exporter.export(
                sample,
                index="test_index",
                tag="test_tag",
                hashes={"sha256": sample.sha256}
            )

            assert result is True


# ============================================================================
# ClickHouseExporter Tests
# ============================================================================

class TestClickHouseExporter:
    """Tests for ClickHouseExporter class."""

    def test_initialization(self, mock_logger):
        """Test ClickHouseExporter initialization."""
        from redb.extractors.database_exporters import ClickHouseExporter

        mock_client = Mock()
        exporter = ClickHouseExporter(mock_logger, "test_index", client=mock_client)

        assert exporter.index_prefix == "test_index"
        assert exporter.log is mock_logger
        assert exporter.client is mock_client

    def test_export_tuple_data(self, mock_logger):
        """Test exporting tuple data to ClickHouse."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names),
                table="test_table"
            )

            assert result is True

    def test_export_multi_table(self, mock_logger):
        """Test exporting multi-table data to ClickHouse."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table1',
                    'data': [["hash123", "sample.exe"]],
                    'column_names': ["sha256", "name"],
                    'column_type_names': ["String", "String"]
                }
            }

            result = exporter.export(data)

            assert result is True

    def test_export_failure(self, mock_logger):
        """Test handling export failure."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.side_effect = Exception("Connection failed")
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names),
                table="test_table"
            )

            assert result is False

    def test_export_missing_table(self, mock_logger):
        """Test export without table name raises error."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = [["hash123", "sample.exe", 1024]]
            column_names = ["sha256", "name", "size"]
            column_type_names = ["String", "String", "UInt64"]

            result = exporter.export(
                (data, column_names, column_type_names)
                # Missing table parameter
            )

            assert result is False

    def test_get_client(self, mock_logger):
        """Test get_client method."""
        from redb.extractors.database_exporters import ClickHouseExporter

        mock_client = Mock()
        exporter = ClickHouseExporter(mock_logger, "test_index", client=mock_client)

        result = exporter.get_client()

        assert result is mock_client

    def test_get_client_creates_new(self, mock_logger):
        """Test get_client creates new client if none exists."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_new_client = Mock()
            mock_create.return_value = mock_new_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            result = exporter.get_client()

            assert result is mock_new_client


# ============================================================================
# DatabaseExporter Abstract Base Class Tests
# ============================================================================

class TestDatabaseExporter:
    """Tests for DatabaseExporter abstract base class."""

    def test_cannot_instantiate_abstract(self):
        """Test that DatabaseExporter cannot be instantiated directly."""
        from redb.extractors.database_exporters import DatabaseExporter

        with pytest.raises(TypeError):
            DatabaseExporter()

    def test_subclass_must_implement_export(self):
        """Test that subclasses must implement export method."""
        from redb.extractors.database_exporters import DatabaseExporter

        # Define incomplete subclass
        class IncompleteExporter(DatabaseExporter):
            pass

        with pytest.raises(TypeError):
            IncompleteExporter()


# ============================================================================
# Null/Edge Case Tests
# ============================================================================

class TestExporterEdgeCases:
    """Tests for edge cases in exporters."""

    def test_print_exporter_with_complex_nested_data(self, mock_logger, capsys):
        """Test PrintExporter with complex nested data."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        data = {
            "level1": {
                "level2": {
                    "level3": [1, 2, 3]
                }
            },
            "array": [{"a": 1}, {"b": 2}]
        }

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "level1" in captured.out

    def test_clickhouse_null_handling(self, mock_logger):
        """Test ClickHouse exporter handles null values."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            # Multi-table data with null values
            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [[None, "sample.exe", None]],
                    'column_names': ["sha256", "name", "size"],
                    'column_type_names': ["Nullable(String)", "String", "Nullable(UInt64)"]
                }
            }

            result = exporter.export(data)

            # Should handle nulls appropriately
            assert result is True

    def test_clickhouse_array_handling(self, mock_logger):
        """Test ClickHouse exporter handles array types."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [["hash", ["tag1", "tag2"]]],
                    'column_names': ["sha256", "tags"],
                    'column_type_names': ["String", "Array(String)"]
                }
            }

            result = exporter.export(data)

            assert result is True


# ============================================================================
# Type Conversion Tests
# ============================================================================

class TestExporterTypeConversions:
    """Tests for type conversions in exporters."""

    def test_clickhouse_datetime_handling(self, mock_logger):
        """Test ClickHouse exporter handles datetime values."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_client.insert.return_value = True
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            now = datetime.now(timezone.utc)
            data = {
                'multi_table': True,
                'table1': {
                    'table': 'test_table',
                    'data': [["hash", now]],
                    'column_names': ["sha256", "timestamp"],
                    'column_type_names': ["String", "DateTime64(3, 'UTC')"]
                }
            }

            result = exporter.export(data)

            assert result is True

    def test_print_exporter_datetime_serialization(self, mock_logger, capsys):
        """Test PrintExporter handles datetime serialization."""
        from redb.extractors.database_exporters import PrintExporter

        exporter = PrintExporter(mock_logger, "test_index")
        now = datetime.now(timezone.utc)
        data = {"timestamp": now, "name": "test"}

        result = exporter.export(data)

        assert result is True
        captured = capsys.readouterr()
        assert "timestamp" in captured.out


# ============================================================================
# Connection Context Manager Tests
# ============================================================================

class TestClickHouseConnectionManager:
    """Tests for ClickHouse connection context manager."""

    def test_clickhouse_connection_context(self, mock_logger):
        """Test clickhouse_connection context manager."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            with exporter.clickhouse_connection() as client:
                assert client is mock_client

            # Client should be closed after context exits
            mock_client.close.assert_called()

    def test_clickhouse_connection_close_on_error(self, mock_logger):
        """Test client is closed even if operation fails."""
        with patch('redb.settings.create_clickhouse_client') as mock_create:
            mock_client = Mock()
            mock_create.return_value = mock_client

            from redb.extractors.database_exporters import ClickHouseExporter

            exporter = ClickHouseExporter(mock_logger, "test_index")

            try:
                with exporter.clickhouse_connection() as client:
                    raise ValueError("Test error")
            except ValueError:
                pass

            # Client should still be closed
            mock_client.close.assert_called()