diff --git a/powerapi/__init__.py b/powerapi/__init__.py
index 69983dd..a99b28c 100644
--- a/powerapi/__init__.py
+++ b/powerapi/__init__.py
@@ -29,4 +29,4 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
-__version__ = "0.1.1"
+__version__ = "0.1.2"
diff --git a/powerapi/database/mongodb.py b/powerapi/database/mongodb.py
index bf029a4..5b6b224 100644
--- a/powerapi/database/mongodb.py
+++ b/powerapi/database/mongodb.py
@@ -133,9 +133,6 @@ class MongoDB(BaseDB):
         except StopIteration:
             raise StopIteration()
 
-        # Re arrange the json before return it by removing '_id' field
-        json.pop('_id', None)
-
         return self.report_model.from_mongodb(json)
 
     def save(self, json):
diff --git a/powerapi/formula/__init__.py b/powerapi/formula/__init__.py
index 4e306eb..1fe268a 100644
--- a/powerapi/formula/__init__.py
+++ b/powerapi/formula/__init__.py
@@ -30,4 +30,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
 from powerapi.formula.formula_actor import FormulaActor, FormulaState
+from powerapi.formula.dummy.dummy_model import DummyModel
 from powerapi.formula.dummy.dummy_formula_actor import DummyFormulaActor
diff --git a/powerapi/formula/dummy/dummy_formula_actor.py b/powerapi/formula/dummy/dummy_formula_actor.py
index a1462d6..06b6a64 100644
--- a/powerapi/formula/dummy/dummy_formula_actor.py
+++ b/powerapi/formula/dummy/dummy_formula_actor.py
@@ -30,53 +30,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
 import logging
-import time
-from powerapi.formula.formula_actor import FormulaActor
-from powerapi.message import UnknowMessageTypeException, PoisonPillMessage
-from powerapi.report import Report, PowerReport
-from powerapi.handler import Handler, PoisonPillMessageHandler
+import re
+from functools import reduce
 
-
-class DummyHWPCReportHandler(Handler):
-    """
-    A fake handler that simulate data processing
-    """
-
-    def __init__(self, actor_pusher):
-        self.actor_pusher = actor_pusher
-
-    def _process_report(self, report):
-        """
-        Wait 1 second and return a power report containing 42
-
-        :param powerapi.Report report: Received report
-
-        :return: A power report containing consumption estimation
-        :rtype:  powerapi.PowerReport
-        """
-
-        result_msg = PowerReport(report.timestamp, report.sensor,
-                                 report.target, {}, 42)
-        return result_msg
-
-    def handle(self, msg, state):
-        """
-        Process a report and send the result to the pusher actor
-
-        :param powerapi.Report msg:       Received message
-        :param powerapi.State state: Actor state
-
-        :return: New Actor state
-        :rtype:  powerapi.State
-
-        :raises UnknowMessageTypeException: If the msg is not a Report
-        """
-        if not isinstance(msg, Report):
-            raise UnknowMessageTypeException(type(msg))
-
-        result = self._process_report(msg)
-        self.actor_pusher.send_data(result)
-        return state
+from powerapi.formula import FormulaActor, FormulaState, DummyModel
+from powerapi.message import PoisonPillMessage
+from powerapi.report import Report
+from powerapi.handler import PoisonPillMessageHandler, FormulaHandler
+from powerapi.actor import Actor, SocketInterface
 
 
 class DummyFormulaActor(FormulaActor):
@@ -84,14 +45,23 @@ class DummyFormulaActor(FormulaActor):
     A fake Formula that simulate data processing by waiting 1s and send a
     power report containing 42
     """
-
-    def __init__(self, name, actor_pusher, level_logger=logging.WARNING,
-                 timeout=None):
+    def __init__(self, name, pusher_actors,
+                 level_logger=logging.WARNING, timeout=None):
         """
-        :param str name:                            Actor name
-        :param powerapi.PusherActor actor_pusher: Pusher to send results.
+        :param str name: Actor name
+        :param powerapi.PusherActor pusher_actors: Pusher actors whom send results
+        :param int level_logger: Define logger level
+        :param bool timeout: Time in millisecond to wait for a message before called timeout_handler
         """
-        FormulaActor.__init__(self, name, actor_pusher, level_logger, timeout)
+        FormulaActor.__init__(self, name, pusher_actors, level_logger, timeout)
+
+        formula_id = reduce(lambda acc, x: acc + (re.search(r'^\(? ?\'(.*)\'\)?', x).group(1),), name.split(','), ())
+
+        #: (powerapi.State): Basic state of the Formula.
+        self.state = FormulaState(Actor._initial_behaviour,
+                                  SocketInterface(name, timeout),
+                                  self.logger, formula_id, pusher_actors,
+                                  DummyModel())
 
     def setup(self):
         """
@@ -99,5 +69,4 @@ class DummyFormulaActor(FormulaActor):
         """
         FormulaActor.setup(self)
         self.add_handler(PoisonPillMessage, PoisonPillMessageHandler())
-        handler = DummyHWPCReportHandler(self.actor_pusher)
-        self.add_handler(Report, handler)
+        self.add_handler(Report, FormulaHandler())
diff --git a/powerapi/formula/dummy/dummy_model.py b/powerapi/formula/dummy/dummy_model.py
new file mode 100644
index 0000000..34175d0
--- /dev/null
+++ b/powerapi/formula/dummy/dummy_model.py
@@ -0,0 +1,49 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+from powerapi.report import PowerReport
+from powerapi.model import Model
+
+
+class DummyModel(Model):
+    """
+    DummyModel for testing
+    """
+
+    def estimate(self, report):
+        """
+        Method that estimate the power consumption from an input report
+        :param report: Input Report
+        :return: List of PowerReport
+        """
+        result_msg = PowerReport(report.timestamp, report.sensor,
+                                 report.target, {}, 42)
+        return [result_msg]
diff --git a/powerapi/formula/formula_actor.py b/powerapi/formula/formula_actor.py
index e0bea04..45b0c3c 100644
--- a/powerapi/formula/formula_actor.py
+++ b/powerapi/formula/formula_actor.py
@@ -30,16 +30,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
 import logging
-import re
-from functools import reduce
 
 from powerapi.actor import Actor, State, SocketInterface
 
 
 class FormulaState(State):
-    def __init__(self, behaviour, socket_interface, logger, formula_id):
+    """
+    Formula Actor State
+    """
+
+    def __init__(self, behaviour, socket_interface, logger, formula_id, pusher_actors, model):
         State.__init__(self, behaviour, socket_interface, logger)
         self.formula_id = formula_id
+        self.pusher_actors = pusher_actors
+        self.model = model
 
 
 class FormulaActor(Actor):
@@ -51,11 +55,11 @@ class FormulaActor(Actor):
     result to a Pusher.
     """
 
-    def __init__(self, name, actor_pusher,
+    def __init__(self, name, pusher_actors,
                  level_logger=logging.WARNING, timeout=None):
         """
         :param str name:                            Actor name
-        :param powerapi.PusherActor actor_pusher: Pusher actor whom send
+        :param powerapi.PusherActor pusher_actors:  Pusher actors whom send
                                                     results
         :param int level_logger:                    Define logger level
         :param bool timeout:                        Time in millisecond to wait
@@ -64,24 +68,22 @@ class FormulaActor(Actor):
         """
         Actor.__init__(self, name, level_logger, timeout)
 
-        #: (powerapi.PusherActor): Pusher actor whom send results.
-        self.actor_pusher = actor_pusher
-
-        formula_id = reduce(lambda acc, x: acc + (re.search(r'^\(? ?\'(.*)\'\)?', x).group(1),), name.split(','), ())
-
         #: (powerapi.State): Basic state of the Formula.
         self.state = FormulaState(Actor._initial_behaviour,
                                   SocketInterface(name, timeout),
-                                  self.logger, formula_id)
+                                  self.logger, None, pusher_actors,
+                                  None)
 
     def setup(self):
         """
         Formula basic setup, Connect the formula to the pusher
         """
-        self.actor_pusher.connect_data()
+        for actor_pusher in self.state.pusher_actors:
+            actor_pusher.connect_data()
 
     def terminated_behaviour(self):
         """
         Allow to close actor_pusher socket
         """
-        self.actor_pusher.state.socket_interface.close()
+        for actor_pusher in self.state.pusher_actors:
+            actor_pusher.state.socket_interface.close()
diff --git a/powerapi/handler/__init__.py b/powerapi/handler/__init__.py
index ffb4e7d..3e9dc98 100644
--- a/powerapi/handler/__init__.py
+++ b/powerapi/handler/__init__.py
@@ -32,3 +32,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 from powerapi.handler.handler import Handler, InitHandler, HandlerException
 from powerapi.handler.poison_pill_message_handler import PoisonPillMessageHandler
 from powerapi.handler.start_handler import StartHandler
+from powerapi.handler.formula_handler import FormulaHandler
\ No newline at end of file
diff --git a/powerapi/handler/formula_handler.py b/powerapi/handler/formula_handler.py
new file mode 100644
index 0000000..6462fb6
--- /dev/null
+++ b/powerapi/handler/formula_handler.py
@@ -0,0 +1,63 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import logging
+from powerapi.handler import Handler
+from powerapi.message import UnknowMessageTypeException
+from powerapi.report import Report
+
+
+class FormulaHandler(Handler):
+    """
+    Basic handler behaviour for a kind of Report
+    """
+
+    def handle(self, msg, state):
+        """
+        Process a report and send the result to the pusher actor
+
+        :param powerapi.Report msg:  Received message
+        :param powerapi.State state: Actor state
+
+        :return: New Actor state
+        :rtype:  powerapi.State
+
+        :raises UnknowMessageTypeException: If the msg is not a Report
+        """
+        if not isinstance(msg, Report):
+            raise UnknowMessageTypeException(type(msg))
+
+        results = state.model.estimate(msg)
+        for actor_pusher in state.pusher_actors:
+            for result in results:
+                actor_pusher.send_data(result)
+        return state
+
diff --git a/powerapi/model/__init__.py b/powerapi/model/__init__.py
new file mode 100644
index 0000000..ecb7079
--- /dev/null
+++ b/powerapi/model/__init__.py
@@ -0,0 +1,32 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+from powerapi.model.model import Model
diff --git a/powerapi/model/model.py b/powerapi/model/model.py
new file mode 100644
index 0000000..9de81ef
--- /dev/null
+++ b/powerapi/model/model.py
@@ -0,0 +1,44 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+
+class Model:
+    """
+    Model abstract class that allow to estimate a power consumption
+    """
+
+    def estimate(self, report):
+        """
+        Method that estimate the power consumption from an input report
+        :param report: Input Report
+        :return: List of PowerReport
+        """
+        raise NotImplementedError()
diff --git a/powerapi/puller/handlers.py b/powerapi/puller/handlers.py
index 86d53ca..5e624ff 100644
--- a/powerapi/puller/handlers.py
+++ b/powerapi/puller/handlers.py
@@ -33,7 +33,8 @@ import logging
 from powerapi.handler import InitHandler, StartHandler
 from powerapi.database import DBError
 from powerapi.message import ErrorMessage, OKMessage, StartMessage
-from powerapi.message import PoisonPillMessage
+from powerapi.report.report import DeserializationFail
+from powerapi.report_model.report_model import BadInputData
 
 
 class NoReportExtractedException(Exception):
@@ -95,12 +96,11 @@ class TimeoutHandler(InitHandler):
         # report in the database, just pass
         try:
             data = next(state.database_it)
-        except StopIteration:
+            # Deserialization
+            report = state.database.report_model.get_type().deserialize(data)
+        except (StopIteration, BadInputData, DeserializationFail):
             raise NoReportExtractedException()
 
-        # Deserialization
-        report = state.database.report_model.get_type().deserialize(data)
-
         # Filter the report
         dispatchers = state.report_filter.route(report)
         return (report, dispatchers)
diff --git a/powerapi/report/__init__.py b/powerapi/report/__init__.py
index f23164c..8db5a72 100644
--- a/powerapi/report/__init__.py
+++ b/powerapi/report/__init__.py
@@ -29,6 +29,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
-from powerapi.report.report import Report
+from powerapi.report.report import Report, DeserializationFail
 from powerapi.report.power_report import PowerReport
 from powerapi.report.hwpc_report import *
diff --git a/powerapi/report/hwpc_report.py b/powerapi/report/hwpc_report.py
index a98285b..c861116 100644
--- a/powerapi/report/hwpc_report.py
+++ b/powerapi/report/hwpc_report.py
@@ -34,7 +34,7 @@ from __future__ import annotations
 from datetime import datetime
 from typing import Dict
 
-from powerapi.report.report import Report
+from powerapi.report.report import Report, DeserializationFail
 
 
 class HWPCReport(Report):
@@ -88,7 +88,12 @@ class HWPCReport(Report):
         :param data: Dictionary containing the report attributes
         :return: The HWPC report initialized with the given data
         """
-        return HWPCReport(data['timestamp'], data['sensor'], data['target'], data['groups'])
+        try:
+            report = HWPCReport(data['timestamp'], data['sensor'], data['target'], data['groups'])
+        except KeyError:
+            raise DeserializationFail()
+
+        return report
 
 
 #############################
diff --git a/powerapi/report/report.py b/powerapi/report/report.py
index b6f64a1..92e7947 100644
--- a/powerapi/report/report.py
+++ b/powerapi/report/report.py
@@ -36,6 +36,13 @@ from typing import Dict
 from powerapi.message import Message
 
 
+class DeserializationFail(Exception):
+    """
+    Exception raised when the
+    in the good format
+    """
+
+
 class Report(Message):
     """
     Report abtract class.
diff --git a/powerapi/report_model/__init__.py b/powerapi/report_model/__init__.py
index af55326..d74e06a 100644
--- a/powerapi/report_model/__init__.py
+++ b/powerapi/report_model/__init__.py
@@ -29,6 +29,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
-from powerapi.report_model.report_model import ReportModel
+from powerapi.report_model.report_model import ReportModel, BadInputData
 from powerapi.report_model.report_model import KEYS_COMMON, KEYS_CSV_COMMON
 from powerapi.report_model.hwpc_model import HWPCModel
diff --git a/powerapi/report_model/hwpc_model.py b/powerapi/report_model/hwpc_model.py
index a6f9257..95f1ff2 100644
--- a/powerapi/report_model/hwpc_model.py
+++ b/powerapi/report_model/hwpc_model.py
@@ -29,7 +29,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 """
 
-from powerapi.report_model import ReportModel, KEYS_CSV_COMMON, KEYS_COMMON
+from powerapi.report_model import ReportModel, KEYS_CSV_COMMON, KEYS_COMMON, BadInputData
 from powerapi.report import HWPCReport
 
 
@@ -51,31 +51,40 @@ class HWPCModel(ReportModel):
         """
         Get HWPCReport from a MongoDB database.
         """
+        # Re arrange the json before return it by removing '_id' field
+        json.pop('_id', None)
+
         return json
 
     def from_csvdb(self, file_name, row):
         """
         Get HWPCReport from a few csv files.
         """
-        final_dict = {key: row[key] for key in KEYS_COMMON}
-        final_dict['groups'] = {}
-
-        # If group doesn't exist, create it
-        if file_name not in final_dict:
-            final_dict['groups'][file_name] = {}
-
-        # If socket doesn't exist, create it
-        if row['socket'] not in final_dict['groups'][file_name]:
-            final_dict['groups'][file_name][row['socket']] = {}
-
-        # If cpu doesn't exist, create it
-        if row['cpu'] not in final_dict['groups'][file_name][row['socket']]:
-            final_dict['groups'][file_name][row['socket']][row['cpu']] = {}
-
-        # Add events
-        for key, value in row.items():
-            if key not in KEYS_CSV_COMMON:
-                final_dict['groups'][file_name][
-                    row['socket']][row['cpu']][key] = int(value)
+        final_dict = {}
+
+        try:
+            final_dict = {key: row[key] for key in KEYS_COMMON}
+            final_dict['groups'] = {}
+
+            # If group doesn't exist, create it
+            if file_name not in final_dict:
+                final_dict['groups'][file_name] = {}
+
+            # If socket doesn't exist, create it
+            if row['socket'] not in final_dict['groups'][file_name]:
+                final_dict['groups'][file_name][row['socket']] = {}
+
+            # If cpu doesn't exist, create it
+            if row['cpu'] not in final_dict['groups'][file_name][row['socket']]:
+                final_dict['groups'][file_name][row['socket']][row['cpu']] = {}
+
+            # Add events
+            for key, value in row.items():
+                if key not in KEYS_CSV_COMMON:
+                    final_dict['groups'][file_name][
+                        row['socket']][row['cpu']][key] = int(value)
+
+        except KeyError:
+            raise BadInputData()
 
         return final_dict
diff --git a/powerapi/report_model/report_model.py b/powerapi/report_model/report_model.py
index 28bb32c..7613fd8 100644
--- a/powerapi/report_model/report_model.py
+++ b/powerapi/report_model/report_model.py
@@ -33,12 +33,19 @@ KEYS_COMMON = ['timestamp', 'sensor', 'target']
 KEYS_CSV_COMMON = KEYS_COMMON + ['socket', 'cpu']
 
 
+class BadInputData(Exception):
+    """
+    Exception raised when the data read in input are not
+    in the good format
+    """
+
+
 class ReportModel:
     """
     ReportModel class.
 
     It define all the function that need to be override if we want
-    to get a report from every kind of db.
+    to format the raw data read in different kind of database.
     """
 
     def get_type(self):

Better Formula API without actor
Currently, formula couldn't be used without a `PusherActor` and managing some internal `State`

Create some methods that encapsulate formula Handlers and State/Pusher management.

For example a method `compute` that take a report and return the power estimation computed from the given report.
diff --git a/tests/acceptation/test_simple_architecture.py b/tests/acceptation/test_simple_architecture.py
index 5845896..615b5cb 100644
--- a/tests/acceptation/test_simple_architecture.py
+++ b/tests/acceptation/test_simple_architecture.py
@@ -50,7 +50,6 @@ Test if:
     socket in the output database
 """
 import logging
-import math
 import pytest
 import pymongo
 
@@ -68,7 +67,6 @@ from powerapi.dispatcher import DispatcherActor, RouteTable
 
 from tests.mongo_utils import gen_base_db_test
 from tests.mongo_utils import clean_base_db_test
-from tests.utils import is_actor_alive
 
 DB_URI = "mongodb://localhost:27017/"
 LOG_LEVEL = logging.NOTSET
@@ -110,7 +108,7 @@ def test_run(database, supervisor):
 
     # Formula
     formula_factory = (lambda name, verbose:
-                       DummyFormulaActor(name, pusher, level_logger=verbose))
+                       DummyFormulaActor(name, [pusher], level_logger=verbose))
 
     # Dispatcher
     route_table = RouteTable()
diff --git a/tests/unit/report/test_hwpc_report.py b/tests/unit/report/test_hwpc_report.py
new file mode 100644
index 0000000..b686862
--- /dev/null
+++ b/tests/unit/report/test_hwpc_report.py
@@ -0,0 +1,83 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import pytest
+import datetime
+from powerapi.report.report import DeserializationFail
+from powerapi.report.hwpc_report import HWPCReport
+
+
+##############################################################################
+#                                Tests                                       #
+##############################################################################
+
+
+@pytest.mark.parametrize("json_input", [
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target",
+      "groups": {}})
+]
+)
+def test_hwpc_report_deserialize_work(json_input):
+    """
+    Test working input for HWPCReport deserialize
+    :param json_input: Data in input
+    """
+    HWPCReport.deserialize(json_input)
+    assert True
+
+
+@pytest.mark.parametrize("json_input", [
+    ({"sensor": "fake-sensor",
+      "target": "fake-target",
+      "groups": {}}),
+    ({"timestamp": datetime.datetime.now(),
+      "target": "fake-target",
+      "groups": {}}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "groups": {}}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target"})
+]
+)
+def test_hwpc_report_deserialize_fail(json_input):
+    """
+    Test failure input for HWPCReport deserialize
+    :param json_input: Data in input
+    """
+    try:
+        HWPCReport.deserialize(json_input)
+        assert False
+    except DeserializationFail:
+        assert True
diff --git a/tests/unit/report_model/test_hwpc_report_model.py b/tests/unit/report_model/test_hwpc_report_model.py
new file mode 100644
index 0000000..0af54ed
--- /dev/null
+++ b/tests/unit/report_model/test_hwpc_report_model.py
@@ -0,0 +1,137 @@
+"""
+Copyright (c) 2018, INRIA
+Copyright (c) 2018, University of Lille
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import pytest
+import datetime
+from powerapi.report_model.hwpc_model import HWPCModel, BadInputData
+
+##############################################################################
+#                                Fixtures                                    #
+##############################################################################
+
+
+@pytest.fixture()
+def hwpc_model():
+    """
+    :return: HWPCModel
+    """
+    return HWPCModel()
+
+
+##############################################################################
+#                                Tests                                       #
+##############################################################################
+
+####################
+# MongoDB
+####################
+
+
+@pytest.mark.parametrize("json_input", [
+    ({"_id": "fake-id",
+      "timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target",
+      "groups": {}})
+]
+)
+def test_hwpc_report_from_mongodb_work(hwpc_model, json_input):
+    """
+    Test working input for HWPCReport from_mongodb
+    :param json_input: Data in input
+    """
+    try:
+        hwpc_model.from_mongodb(json_input)
+        assert True
+    except BadInputData:
+        assert False
+
+
+####################
+# CsvDB
+####################
+
+
+@pytest.mark.parametrize("json_input", [
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target",
+      "socket": 0,
+      "cpu": 0,
+      "event1": 1000,
+      "event2": 2000}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target",
+      "socket": 0,
+      "cpu": 0}),
+]
+)
+def test_hwpc_report_from_csvdb_work(hwpc_model, json_input):
+    """
+    :param json_input: Data in input
+    """
+    try:
+        hwpc_model.from_mongodb(json_input)
+        assert True
+    except BadInputData:
+        assert False
+
+
+@pytest.mark.parametrize("json_input", [
+    ({"sensor": "fake-sensor",
+      "target": "fake-target"}),
+    ({"timestamp": datetime.datetime.now(),
+      "target": "fake-target"}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor"}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target"}),
+    ({"timestamp": datetime.datetime.now(),
+      "sensor": "fake-sensor",
+      "target": "fake-target",
+      "socket": 0}),
+     ({"timestamp": datetime.datetime.now(),
+       "sensor": "fake-sensor",
+       "target": "fake-target",
+       "cpu": 0}),
+]
+)
+def test_hwpc_report_from_csvdb_fail(hwpc_model, json_input):
+    """
+    :param json_input: Data in input
+    """
+    try:
+        hwpc_model.from_csvdb("fake-name", json_input)
+        assert False
+    except BadInputData:
+        assert True
\ No newline at end of file

pip install -e .[mongodb]
parse_log_pytest
pytest
pytest mock
apt-get update apt-get install -y gcc
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
dnspython==2.3.0
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1648562407465/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
mock==5.2.0
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
-e git+https://github.com/powerapi-ng/powerapi.git@100cc30f92e26f673555b54646486dd1d014ed50#egg=powerapi
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pymongo==4.7.3
pytest==7.1.2
pyzmq==26.2.1
setproctitle==1.3.3
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: powerapi
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib-metadata=4.11.3=py37h06a4308_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - dnspython==2.3.0
      - mock==5.2.0
      - pymongo==4.7.3
      - pyzmq==26.2.1
      - setproctitle==1.3.3
prefix: /opt/conda/envs/powerapi

tests/unit/report/test_hwpc_report.py::test_hwpc_report_deserialize_work[json_input0] tests/unit/report/test_hwpc_report.py::test_hwpc_report_deserialize_fail[json_input0] tests/unit/report/test_hwpc_report.py::test_hwpc_report_deserialize_fail[json_input1] tests/unit/report/test_hwpc_report.py::test_hwpc_report_deserialize_fail[json_input2] tests/unit/report/test_hwpc_report.py::test_hwpc_report_deserialize_fail[json_input3] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_mongodb_work[json_input0] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_work[json_input0] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_work[json_input1] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input0] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input1] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input2] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input3] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input4] tests/unit/report_model/test_hwpc_report_model.py::test_hwpc_report_from_csvdb_fail[json_input5]
powerapi/report_model/hwpc_model.py powerapi/report/__init__.py powerapi/report/report.py powerapi/__init__.py powerapi/formula/dummy/dummy_formula_actor.py powerapi/formula/formula_actor.py powerapi/formula/dummy/dummy_model.py powerapi/model/model.py powerapi/puller/handlers.py powerapi/handler/__init__.py powerapi/report_model/report_model.py powerapi/report/hwpc_report.py powerapi/formula/__init__.py powerapi/model/__init__.py powerapi/handler/formula_handler.py powerapi/report_model/__init__.py powerapi/database/mongodb.py
powerapi/report_model/hwpc_model.py powerapi/report/__init__.py powerapi/report/report.py powerapi/__init__.py powerapi/formula/dummy/dummy_formula_actor.py powerapi/formula/formula_actor.py powerapi/formula/dummy/dummy_model.py powerapi/model/model.py powerapi/puller/handlers.py powerapi/handler/__init__.py powerapi/report_model/report_model.py powerapi/report/hwpc_report.py powerapi/formula/__init__.py powerapi/model/__init__.py powerapi/handler/formula_handler.py powerapi/report_model/__init__.py powerapi/database/mongodb.py