From 0d641aa214b16c276dd0910d105eb1f55ef55c02 Mon Sep 17 00:00:00 2001
From: Adailson Santos <adailson2@gmail.com>
Date: Oct 01 2017 20:40:49 +0000
Subject: [PATCH 1/6] [REPORTS] Added model to reports

- Create models for reports counter
- Added save method for Reports on monitor

Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges  <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/model.py b/kiskadee/model.py
index c06d3ee..ee860db 100644
--- a/kiskadee/model.py
+++ b/kiskadee/model.py
@@ -3,11 +3,15 @@
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy import Column, Integer, UnicodeText, UniqueConstraint,\
                        Sequence, Unicode, ForeignKey, orm, JSON
-
 import kiskadee
 
 Base = declarative_base()
 
+"""class TypeEnum(enum.Enum):
+    Enum for to use on TypeReport class
+    error = 1
+    style = 2
+    warning = 3"""
 
 class Package(Base):
     """Software packages abstraction.
@@ -27,7 +31,6 @@ class Package(Base):
             UniqueConstraint('name', 'fetcher_id'),
             )
 
-
 class Fetcher(Base):
     """kiskadee fetcher abstraction."""
 
@@ -74,7 +77,16 @@ class Analysis(Base):
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False)
     analyzer_id = Column(Integer, ForeignKey('analyzers.id'), nullable=False)
     raw = Column(JSON)
+    report = orm.relationship('Reports', backref='analysis')
 
+class Reports(Base):
+    """Abstraction of a analysis report"""
+    __tablename__ = 'reports'
+    id = Column(Integer,
+                Sequence('reports_id_seq', optional=True), primary_key=True)
+    report_type = Column(Unicode(100), nullable=False)
+    counter = Column(Integer)
+    analysis_id = Column(Integer, ForeignKey('analysis.id'), nullable=False)
 
 def create_analyzers(_session):
     """Create the analyzers on database.
diff --git a/kiskadee/monitor.py b/kiskadee/monitor.py
index 01f10ca..6b84724 100644
--- a/kiskadee/monitor.py
+++ b/kiskadee/monitor.py
@@ -12,7 +12,8 @@ import json
 import kiskadee.database
 from kiskadee.runner import Runner
 import kiskadee.queue
-from kiskadee.model import Package, Fetcher, Version
+from kiskadee.model import Package, Fetcher, Version, Reports
+import re
 
 RUNNING = True
 
@@ -143,6 +144,28 @@ class Monitor:
         self.session.commit()
         return _package
 
+    def _save_reports(self, analysis):
+        try:
+            reports = analysis.raw
+            report_dictionary = {
+                'warnings': re.subn('warning', '', reports)[1], # get number of matches
+                'styles': re.subn('style', '', reports)[1],
+                'errors':re.subn('error', '', reports)[1]
+            }
+            for key, value in report_dictionary.items():
+                _reports = kiskadee.model.Reports()
+                _reports.report_type = key
+                _reports.counter = value
+                _reports.analysis_id = analysis.id
+                self.session.add(_reports)
+                self.session.commit()
+        except Exception as err:
+            kiskadee.logger.debug(
+                    "MONITOR: Failed to get analysis reports"
+                )
+            kiskadee.logger.debug(err)
+        return
+
     def _save_analysis(self, pkg, analyzer, result, version):
         _analysis = kiskadee.model.Analysis()
         try:
@@ -153,6 +176,7 @@ class Monitor:
             _analysis.raw = json.loads(result)
             self.session.add(_analysis)
             self.session.commit()
+            self._save_reports(_analysis)
             kiskadee.logger.debug(
                     "MONITOR: Saved analysis done by {} for package: {}_{}"
                     .format(analyzer, pkg["name"], pkg["version"])

From f8629639d2ecf0a43100868a728f8df802deb8ef Mon Sep 17 00:00:00 2001
From: Vitor Borges <vitorbertulucci@gmail.com>
Date: Oct 01 2017 20:41:26 +0000
Subject: [PATCH 2/6] [ANALYSIS] Added reports to analysis endpoint


Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/api/app.py b/kiskadee/api/app.py
index 37b3436..fd8b275 100644
--- a/kiskadee/api/app.py
+++ b/kiskadee/api/app.py
@@ -7,6 +7,7 @@ from kiskadee.database import Database
 from kiskadee.model import Package, Fetcher, Version, Analysis
 from kiskadee.api.serializers import PackageSchema, FetcherSchema,\
         AnalysisSchema
+import json
 
 kiskadee = Flask(__name__)
 
@@ -56,6 +57,7 @@ def package_analysis(pkg_name, version):
 
         analysis_schema = AnalysisSchema()
         result = analysis_schema.dump(analysis)
+        result.data['raw'] = json.loads(result.data['raw'])
         return jsonify({'analysis': result.data})
 
 
diff --git a/kiskadee/api/serializers.py b/kiskadee/api/serializers.py
index 401f0cf..db48660 100644
--- a/kiskadee/api/serializers.py
+++ b/kiskadee/api/serializers.py
@@ -1,7 +1,21 @@
 """Provide objects to serialize the kiskadee models."""
 
 from marshmallow import Schema, fields
-from kiskadee.model import Package, Fetcher, Analysis, Version
+from kiskadee.model import Package, Fetcher, Analysis, Version, Reports
+
+
+class ReportsSchema(Schema):
+    """Provide a serializer to the Reports model."""
+
+    id = fields.Int()
+    analysis_id = fields.Int()
+    report_type = fields.Str()
+    counter = fields.Int()
+
+    def make_object(self, data):
+        """Serialize a Reports object."""
+        print('MAKING OBJECT FROM', data)
+        return Reports(**data)
 
 
 class AnalysisSchema(Schema):
@@ -11,6 +25,7 @@ class AnalysisSchema(Schema):
     version_id = fields.Int()
     analyzer_id = fields.Int()
     raw = fields.Dict()
+    reports = fields.Nested(ReportsSchema, many=True)
 
     def make_object(self, data):
         """Serialize a Analysis object."""
diff --git a/kiskadee/model.py b/kiskadee/model.py
index ee860db..81293f3 100644
--- a/kiskadee/model.py
+++ b/kiskadee/model.py
@@ -7,11 +7,6 @@ import kiskadee
 
 Base = declarative_base()
 
-"""class TypeEnum(enum.Enum):
-    Enum for to use on TypeReport class
-    error = 1
-    style = 2
-    warning = 3"""
 
 class Package(Base):
     """Software packages abstraction.
@@ -31,6 +26,7 @@ class Package(Base):
             UniqueConstraint('name', 'fetcher_id'),
             )
 
+
 class Fetcher(Base):
     """kiskadee fetcher abstraction."""
 
@@ -77,16 +73,21 @@ class Analysis(Base):
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False)
     analyzer_id = Column(Integer, ForeignKey('analyzers.id'), nullable=False)
     raw = Column(JSON)
-    report = orm.relationship('Reports', backref='analysis')
+    report = orm.relationship('Reports', uselist=False, back_populates='analysis')
+
 
 class Reports(Base):
-    """Abstraction of a analysis report"""
+    """Abstraction of a analysis report."""
+
     __tablename__ = 'reports'
     id = Column(Integer,
                 Sequence('reports_id_seq', optional=True), primary_key=True)
-    report_type = Column(Unicode(100), nullable=False)
-    counter = Column(Integer)
+    warnings = Column(Integer)
+    styles = Column(Integer)
+    errors = Column(Integer)
     analysis_id = Column(Integer, ForeignKey('analysis.id'), nullable=False)
+    analysis = orm.relationship('Analysis', back_populates='report')
+
 
 def create_analyzers(_session):
     """Create the analyzers on database.
diff --git a/kiskadee/monitor.py b/kiskadee/monitor.py
index 6b84724..25aaa6a 100644
--- a/kiskadee/monitor.py
+++ b/kiskadee/monitor.py
@@ -12,7 +12,7 @@ import json
 import kiskadee.database
 from kiskadee.runner import Runner
 import kiskadee.queue
-from kiskadee.model import Package, Fetcher, Version, Reports
+from kiskadee.model import Package, Fetcher, Version
 import re
 
 RUNNING = True
@@ -144,24 +144,29 @@ class Monitor:
         self.session.commit()
         return _package
 
-    def _save_reports(self, analysis):
+    def _save_reports(self, analysis, pkg):
         try:
             reports = analysis.raw
             report_dictionary = {
-                'warnings': re.subn('warning', '', reports)[1], # get number of matches
+                'warnings': re.subn('warning', '', reports)[1],
                 'styles': re.subn('style', '', reports)[1],
-                'errors':re.subn('error', '', reports)[1]
+                'errors': re.subn('error', '', reports)[1]
             }
-            for key, value in report_dictionary.items():
-                _reports = kiskadee.model.Reports()
-                _reports.report_type = key
-                _reports.counter = value
-                _reports.analysis_id = analysis.id
-                self.session.add(_reports)
-                self.session.commit()
+            _reports = kiskadee.model.Reports()
+            _reports.warnings = report_dictionary['warnings']
+            _reports.styles = report_dictionary['styles']
+            _reports.errors = report_dictionary['errors']
+            _reports.analysis_id = analysis.id
+            self.session.add(_reports)
+            self.session.commit()
+            kiskadee.logger.debug(
+                    "MONITOR: Saved analysis reports for {} package"
+                    .format(pkg["name"])
+                )
         except Exception as err:
             kiskadee.logger.debug(
-                    "MONITOR: Failed to get analysis reports"
+                    "MONITOR: Failed to get analysis reports to {} package"
+                    .format(pkg["name"])
                 )
             kiskadee.logger.debug(err)
         return
@@ -176,7 +181,7 @@ class Monitor:
             _analysis.raw = json.loads(result)
             self.session.add(_analysis)
             self.session.commit()
-            self._save_reports(_analysis)
+            self._save_reports(_analysis, pkg)
             kiskadee.logger.debug(
                     "MONITOR: Saved analysis done by {} for package: {}_{}"
                     .format(analyzer, pkg["name"], pkg["version"])

From 7eee58b1ee375037edb27d2135798f262018bbf8 Mon Sep 17 00:00:00 2001
From: Vitor Borges <vitorbertulucci@gmail.com>
Date: Oct 01 2017 20:41:27 +0000
Subject: [PATCH 3/6] [ENDPOINT] Changed return of `/analysis` endpoint to return array of

analysis

Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/api/app.py b/kiskadee/api/app.py
index fd8b275..3ba1385 100644
--- a/kiskadee/api/app.py
+++ b/kiskadee/api/app.py
@@ -4,7 +4,7 @@ from flask import request
 from flask_cors import CORS
 
 from kiskadee.database import Database
-from kiskadee.model import Package, Fetcher, Version, Analysis
+from kiskadee.model import Package, Fetcher, Version, Analysis, Reports
 from kiskadee.api.serializers import PackageSchema, FetcherSchema,\
         AnalysisSchema
 import json
@@ -52,13 +52,13 @@ def package_analysis(pkg_name, version):
             )
         analysis = (
                 db_session.query(Analysis)
-                .filter(Analysis.version_id == version_id).first()
+                .filter(Analysis.version_id == version_id).all()
             )
-
-        analysis_schema = AnalysisSchema()
-        result = analysis_schema.dump(analysis)
-        result.data['raw'] = json.loads(result.data['raw'])
-        return jsonify({'analysis': result.data})
+        analysis_schema = AnalysisSchema(many=True)
+        results = analysis_schema.dump(analysis)
+        for data in results.data:
+            data['raw'] = json.loads(data['raw'])
+        return jsonify({'analysis': results.data})
 
 
 def kiskadee_db_session():
diff --git a/kiskadee/api/serializers.py b/kiskadee/api/serializers.py
index db48660..f39414a 100644
--- a/kiskadee/api/serializers.py
+++ b/kiskadee/api/serializers.py
@@ -9,8 +9,9 @@ class ReportsSchema(Schema):
 
     id = fields.Int()
     analysis_id = fields.Int()
-    report_type = fields.Str()
-    counter = fields.Int()
+    warnings = fields.Int()
+    styles = fields.Int()
+    errors = fields.Int()
 
     def make_object(self, data):
         """Serialize a Reports object."""
@@ -25,7 +26,7 @@ class AnalysisSchema(Schema):
     version_id = fields.Int()
     analyzer_id = fields.Int()
     raw = fields.Dict()
-    reports = fields.Nested(ReportsSchema, many=True)
+    report = fields.Nested(ReportsSchema)
 
     def make_object(self, data):
         """Serialize a Analysis object."""

From 68b206dc8e036fa506f9d5e3aa614bdb6a6ba135 Mon Sep 17 00:00:00 2001
From: Adailson Santos <adailson2@gmail.com>
Date: Oct 01 2017 20:41:27 +0000
Subject: [PATCH 4/6] [TEST] Created tests cases for reports and /analysis endpoint

- Added monitor test for save_reports method
- Added unit tests for /analysis endpoint

Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/api/app.py b/kiskadee/api/app.py
index 3ba1385..ad1b580 100644
--- a/kiskadee/api/app.py
+++ b/kiskadee/api/app.py
@@ -4,7 +4,7 @@ from flask import request
 from flask_cors import CORS
 
 from kiskadee.database import Database
-from kiskadee.model import Package, Fetcher, Version, Analysis, Reports
+from kiskadee.model import Package, Fetcher, Version, Analysis
 from kiskadee.api.serializers import PackageSchema, FetcherSchema,\
         AnalysisSchema
 import json
diff --git a/kiskadee/model.py b/kiskadee/model.py
index 81293f3..8bb6ed2 100644
--- a/kiskadee/model.py
+++ b/kiskadee/model.py
@@ -73,7 +73,8 @@ class Analysis(Base):
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False)
     analyzer_id = Column(Integer, ForeignKey('analyzers.id'), nullable=False)
     raw = Column(JSON)
-    report = orm.relationship('Reports', uselist=False, back_populates='analysis')
+    report = orm.relationship('Reports',
+                              uselist=False, back_populates='analysis')
 
 
 class Reports(Base):
@@ -82,10 +83,10 @@ class Reports(Base):
     __tablename__ = 'reports'
     id = Column(Integer,
                 Sequence('reports_id_seq', optional=True), primary_key=True)
+    analysis_id = Column(Integer, ForeignKey('analysis.id'), nullable=False)
     warnings = Column(Integer)
     styles = Column(Integer)
     errors = Column(Integer)
-    analysis_id = Column(Integer, ForeignKey('analysis.id'), nullable=False)
     analysis = orm.relationship('Analysis', back_populates='report')
 
 
diff --git a/kiskadee/monitor.py b/kiskadee/monitor.py
index 25aaa6a..ba66e72 100644
--- a/kiskadee/monitor.py
+++ b/kiskadee/monitor.py
@@ -146,7 +146,7 @@ class Monitor:
 
     def _save_reports(self, analysis, pkg):
         try:
-            reports = analysis.raw
+            reports = analysis['raw']
             report_dictionary = {
                 'warnings': re.subn('warning', '', reports)[1],
                 'styles': re.subn('style', '', reports)[1],
@@ -156,7 +156,7 @@ class Monitor:
             _reports.warnings = report_dictionary['warnings']
             _reports.styles = report_dictionary['styles']
             _reports.errors = report_dictionary['errors']
-            _reports.analysis_id = analysis.id
+            _reports.analysis_id = analysis['id']
             self.session.add(_reports)
             self.session.commit()
             kiskadee.logger.debug(
diff --git a/kiskadee/tests/test_api.py b/kiskadee/tests/test_api.py
index 916aebf..075d297 100644
--- a/kiskadee/tests/test_api.py
+++ b/kiskadee/tests/test_api.py
@@ -21,7 +21,29 @@ class ApiTestCase(unittest.TestCase):
         fetcher = kiskadee.model.Fetcher(
                 name='kiskadee-fetcher', target='university'
         )
+        model.Base.metadata.create_all(self.engine)
+        model.create_analyzers(self.session)
+        fetcher = kiskadee.model.Fetcher(
+                name='kiskadee-fetcher', target='university', id=1
+        )
+        pkg = kiskadee.model.Package(
+                name='kiskadee-package', fetcher_id=1, id=1
+        )
+        version = kiskadee.model.Version(
+                number='7.23', package_id=1, id=1
+        )
+        analysis = kiskadee.model.Analysis(
+                version_id=1, analyzer_id=1,
+                raw="{\"metadata\": \"test\"}"
+        )
+        report = model.Reports(
+            analysis_id=1, warnings=1, styles=1, errors=1
+        )
         self.session.add(fetcher)
+        self.session.add(pkg)
+        self.session.add(version)
+        self.session.add(analysis)
+        self.session.add(report)
         self.session.commit()
         self.runner = Runner()
         self.monitor = Monitor(self.session)
@@ -73,6 +95,19 @@ class ApiTestCase(unittest.TestCase):
         self.assertIn('cwe', pkg_first_analysis)
         self.assertIn('message', pkg_first_analysis)
 
+    def test_get_specific_analysis(self):
+
+        def mock_kiskadee_db_session():
+            return self.session
+
+        kiskadee.api.app.kiskadee_db_session = mock_kiskadee_db_session
+        response = self.app.get("/analysis/kiskadee-package/7.23/")
+        response_as_json = json.loads(response.get_data(as_text=True))
+        self.assertIn("analysis", response_as_json)
+        #  On the first analysis of response,
+        #  check if exists some report attribute
+        self.assertIn("report", response_as_json['analysis'][0])
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/kiskadee/tests/test_model.py b/kiskadee/tests/test_model.py
index 0a6b7f0..b1445cc 100644
--- a/kiskadee/tests/test_model.py
+++ b/kiskadee/tests/test_model.py
@@ -15,7 +15,7 @@ class ModelTestCase(unittest.TestCase):
         model.Base.metadata.create_all(self.engine)
         model.create_analyzers(self.session)
         self.fetcher = model.Fetcher(
-                name='kiskadee-fetcher', target='university'
+              name='kiskadee-fetcher', target='university'
             )
         self.package = model.Package(name='python-kiskadee')
         self.version = model.Version(number='1.0-rc1')
@@ -27,9 +27,17 @@ class ModelTestCase(unittest.TestCase):
 
         self.analysis = model.Analysis(
                 analyzer_id=1,
-                version_id=self.version.id,
+                version_id=1,
                 raw=""
                 )
+        self.session.add(self.analysis)
+        self.report = model.Reports(
+                analysis_id=1,
+                warnings=0,
+                styles=0,
+                errors=0
+        )
+        self.session.add(self.report)
         self.session.commit()
 
     def tearDown(self):
@@ -48,6 +56,10 @@ class ModelTestCase(unittest.TestCase):
         versions = self.session.query(model.Version).all()
         self.assertEqual(versions, [self.version])
 
+    def test_query_report(self):
+        reports = self.session.query(model.Reports).all()
+        self.assertEqual(reports, [self.report])
+
     def test_add_fetcher(self):
         fetchers = self.session.query(model.Fetcher).all()
         self.assertEqual(len(fetchers), 1)
@@ -61,6 +73,16 @@ class ModelTestCase(unittest.TestCase):
         with self.assertRaises(exc.IntegrityError):
             self.session.commit()
 
+    def test_add_report_without_analysis(self):
+        report = model.Reports(
+                warnings=0,
+                styles=0,
+                errors=0
+        )
+        self.session.add(report)
+        with self.assertRaises(exc.IntegrityError):
+            self.session.commit()
+
     def test_add_package_without_fetcher(self):
         package = model.Package(name='foo-bar')
         self.session.add(package)
diff --git a/kiskadee/tests/test_monitor.py b/kiskadee/tests/test_monitor.py
index 2d6b70f..ac4a452 100644
--- a/kiskadee/tests/test_monitor.py
+++ b/kiskadee/tests/test_monitor.py
@@ -4,7 +4,7 @@ from sqlalchemy.orm import sessionmaker
 from kiskadee import model
 from kiskadee.monitor import Monitor
 from kiskadee.queue import packages_queue
-from kiskadee.model import Package, Fetcher, create_analyzers
+from kiskadee.model import Package, Fetcher, create_analyzers, Reports
 import kiskadee.queue
 import kiskadee.fetchers.debian
 from kiskadee.database import Database
@@ -46,6 +46,9 @@ class MonitorTestCase(unittest.TestCase):
                             'cppcheck': '<>',
                             'flawfinder': '><'},
                      'fetcher_id': 1}
+        self.analysis = {'analyzer_id': 1,
+                         'id': 1,
+                         'raw': 'warning, style, error'}
 
     def tearDown(self):
         self.session.close()
@@ -86,6 +89,15 @@ class MonitorTestCase(unittest.TestCase):
         self.assertEqual(len(_pkgs), 2)
         self.assertEqual(_pkgs[1].name, _pkg['name'])
 
+    def test_save_reports(self):
+        self.monitor._save_reports(self.analysis, self.pkg1)
+        _report = self.monitor.session.query(Reports).all()
+        self.assertEqual(len(_report), 1)
+        self.assertEqual(_report[0].analysis_id, 1)
+        self.assertEqual(_report[0].warnings, 1)
+        self.assertEqual(_report[0].styles, 1)
+        self.assertEqual(_report[0].errors, 1)
+
     def test_save_version(self):
         self.monitor._save_fetcher(kiskadee.fetchers.debian.Fetcher())
         self.monitor._save_analyzed_pkg(self.pkg1)

From ef3a208dfa05e936b41c410bb14920b771ac0afd Mon Sep 17 00:00:00 2001
From: Vitor Borges <vitorbertulucci@gmail.com>
Date: Oct 01 2017 20:41:48 +0000
Subject: [PATCH 5/6] Changed the Report format on database


Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/api/app.py b/kiskadee/api/app.py
index ad1b580..9860ebb 100644
--- a/kiskadee/api/app.py
+++ b/kiskadee/api/app.py
@@ -56,8 +56,6 @@ def package_analysis(pkg_name, version):
             )
         analysis_schema = AnalysisSchema(many=True)
         results = analysis_schema.dump(analysis)
-        for data in results.data:
-            data['raw'] = json.loads(data['raw'])
         return jsonify({'analysis': results.data})
 
 
diff --git a/kiskadee/api/serializers.py b/kiskadee/api/serializers.py
index f39414a..98e7835 100644
--- a/kiskadee/api/serializers.py
+++ b/kiskadee/api/serializers.py
@@ -1,7 +1,7 @@
 """Provide objects to serialize the kiskadee models."""
 
 from marshmallow import Schema, fields
-from kiskadee.model import Package, Fetcher, Analysis, Version, Reports
+from kiskadee.model import Package, Fetcher, Analysis, Version, Report
 
 
 class ReportsSchema(Schema):
@@ -9,14 +9,12 @@ class ReportsSchema(Schema):
 
     id = fields.Int()
     analysis_id = fields.Int()
-    warnings = fields.Int()
-    styles = fields.Int()
-    errors = fields.Int()
+    results = fields.Dict()
 
     def make_object(self, data):
         """Serialize a Reports object."""
         print('MAKING OBJECT FROM', data)
-        return Reports(**data)
+        return Report(**data)
 
 
 class AnalysisSchema(Schema):
diff --git a/kiskadee/model.py b/kiskadee/model.py
index 8bb6ed2..81fe42c 100644
--- a/kiskadee/model.py
+++ b/kiskadee/model.py
@@ -73,20 +73,18 @@ class Analysis(Base):
     version_id = Column(Integer, ForeignKey('versions.id'), nullable=False)
     analyzer_id = Column(Integer, ForeignKey('analyzers.id'), nullable=False)
     raw = Column(JSON)
-    report = orm.relationship('Reports',
+    report = orm.relationship('Report',
                               uselist=False, back_populates='analysis')
 
 
-class Reports(Base):
+class Report(Base):
     """Abstraction of a analysis report."""
 
     __tablename__ = 'reports'
     id = Column(Integer,
                 Sequence('reports_id_seq', optional=True), primary_key=True)
     analysis_id = Column(Integer, ForeignKey('analysis.id'), nullable=False)
-    warnings = Column(Integer)
-    styles = Column(Integer)
-    errors = Column(Integer)
+    results = Column(JSON)
     analysis = orm.relationship('Analysis', back_populates='report')
 
 
diff --git a/kiskadee/monitor.py b/kiskadee/monitor.py
index ba66e72..1039b74 100644
--- a/kiskadee/monitor.py
+++ b/kiskadee/monitor.py
@@ -10,13 +10,18 @@ import os
 import json
 
 import kiskadee.database
+from kiskadee.report import  CppCheckReport
 from kiskadee.runner import Runner
 import kiskadee.queue
-from kiskadee.model import Package, Fetcher, Version
+from kiskadee.model import Package, Fetcher, Version, Report
 import re
 
 RUNNING = True
 
+REPORTERS = {
+    'cppcheck': CppCheckReport
+}
+
 
 class Monitor:
     """Provide kiskadee monitoring objects."""
@@ -144,19 +149,13 @@ class Monitor:
         self.session.commit()
         return _package
 
-    def _save_reports(self, analysis, pkg):
+    def _save_reports(self, analysis, pkg, analyzer_name):
         try:
-            reports = analysis['raw']
-            report_dictionary = {
-                'warnings': re.subn('warning', '', reports)[1],
-                'styles': re.subn('style', '', reports)[1],
-                'errors': re.subn('error', '', reports)[1]
-            }
-            _reports = kiskadee.model.Reports()
-            _reports.warnings = report_dictionary['warnings']
-            _reports.styles = report_dictionary['styles']
-            _reports.errors = report_dictionary['errors']
-            _reports.analysis_id = analysis['id']
+            results = analysis.raw['results']
+            analyzer_report = REPORTERS[analyzer_name](results)
+            _reports = Report()
+            _reports.results = json.dumps(analyzer_report.compute_reports())
+            _reports.analysis_id = analysis.id
             self.session.add(_reports)
             self.session.commit()
             kiskadee.logger.debug(
@@ -181,7 +180,7 @@ class Monitor:
             _analysis.raw = json.loads(result)
             self.session.add(_analysis)
             self.session.commit()
-            self._save_reports(_analysis, pkg)
+            self._save_reports(_analysis, pkg, _analyzer.name)
             kiskadee.logger.debug(
                     "MONITOR: Saved analysis done by {} for package: {}_{}"
                     .format(analyzer, pkg["name"], pkg["version"])
diff --git a/kiskadee/report.py b/kiskadee/report.py
new file mode 100644
index 0000000..273f339
--- /dev/null
+++ b/kiskadee/report.py
@@ -0,0 +1,29 @@
+from abc import ABCMeta, abstractmethod
+
+class Report:
+    __metaclass__ = ABCMeta
+
+    def __init__(self, results):
+        self.results = results
+
+    @abstractmethod
+    def compute_reports(self, results): pass
+
+
+class CppCheckReport(Report):
+
+    def compute_reports(self):
+        result_dict = {
+                'warning': 0,
+                'error': 0,
+                'style': 0
+        }
+        for result in self.results:
+            if 'severity' in list(result.keys()):
+                result_dict[result['severity']] += 1
+            else:
+                print('deu ruimmmm')
+
+        print(result_dict)
+        return result_dict
+

From 5e2a0d0bf4aec07209a7dfa4b42cb4693b5c3d2b Mon Sep 17 00:00:00 2001
From: Vitor Borges <vitorbertulucci@gmail.com>
Date: Oct 01 2017 20:41:49 +0000
Subject: [PATCH 6/6] [REPORTS] Added another report class

- Added flawfinder concrete reporter
- Added report tests

Signed-off-by: Adailson Santos <adailson2@gmail.com>
Signed-off-by: Vitor Borges <vitorbertulucci@gmail.com>

---

diff --git a/kiskadee/api/app.py b/kiskadee/api/app.py
index 9860ebb..4acdb24 100644
--- a/kiskadee/api/app.py
+++ b/kiskadee/api/app.py
@@ -56,6 +56,11 @@ def package_analysis(pkg_name, version):
             )
         analysis_schema = AnalysisSchema(many=True)
         results = analysis_schema.dump(analysis)
+        for result in results.data:
+            report = result['report']
+            if (report is not None) and ('results' in report.keys()):
+                report['results'] = json\
+                    .loads(report['results'])
         return jsonify({'analysis': results.data})
 
 
diff --git a/kiskadee/monitor.py b/kiskadee/monitor.py
index 1039b74..b8694cb 100644
--- a/kiskadee/monitor.py
+++ b/kiskadee/monitor.py
@@ -10,16 +10,16 @@ import os
 import json
 
 import kiskadee.database
-from kiskadee.report import  CppCheckReport
+from kiskadee.report import CppcheckReport, FlawfinderReport
 from kiskadee.runner import Runner
 import kiskadee.queue
 from kiskadee.model import Package, Fetcher, Version, Report
-import re
 
 RUNNING = True
 
 REPORTERS = {
-    'cppcheck': CppCheckReport
+    'cppcheck': CppcheckReport,
+    'flawfinder': FlawfinderReport
 }
 
 
@@ -151,17 +151,26 @@ class Monitor:
 
     def _save_reports(self, analysis, pkg, analyzer_name):
         try:
-            results = analysis.raw['results']
+            results = analysis['results']
             analyzer_report = REPORTERS[analyzer_name](results)
             _reports = Report()
-            _reports.results = json.dumps(analyzer_report.compute_reports())
-            _reports.analysis_id = analysis.id
+            _reports.results = json.dumps(
+                    analyzer_report
+                    ._compute_reports(analyzer_name)
+                )
+            _reports.analysis_id = analysis['id']
             self.session.add(_reports)
             self.session.commit()
             kiskadee.logger.debug(
                     "MONITOR: Saved analysis reports for {} package"
                     .format(pkg["name"])
                 )
+        except KeyError as key:
+            kiskadee.logger.debug(
+                    "ERROR: There's no reporter " +
+                    "to get reports from {} analyzer. ".format(key) +
+                    "Make shure to import or implement them."
+                )
         except Exception as err:
             kiskadee.logger.debug(
                     "MONITOR: Failed to get analysis reports to {} package"
@@ -180,14 +189,19 @@ class Monitor:
             _analysis.raw = json.loads(result)
             self.session.add(_analysis)
             self.session.commit()
-            self._save_reports(_analysis, pkg, _analyzer.name)
+            dict_analysis = {
+                    'results': _analysis.raw['results'],
+                    'id': _analysis.id
+                }
+            self._save_reports(dict_analysis, pkg, _analyzer.name)
             kiskadee.logger.debug(
                     "MONITOR: Saved analysis done by {} for package: {}_{}"
                     .format(analyzer, pkg["name"], pkg["version"])
                 )
+            return
         except Exception as err:
             kiskadee.logger.debug(
-                    "MONITOR: The required analyzer was" +
+                    "MONITOR: The required analyzer was " +
                     "not registered in kiskadee"
                 )
             kiskadee.logger.debug(err)
diff --git a/kiskadee/report.py b/kiskadee/report.py
index 273f339..10aa7cc 100644
--- a/kiskadee/report.py
+++ b/kiskadee/report.py
@@ -1,29 +1,80 @@
+"""Provide reporter capabilities to kiskadee analyzers.
+
+Every analyzer on kiskadee has differents reports results.
+This module came to compute and deal with this differences.
+"""
+import kiskadee
 from abc import ABCMeta, abstractmethod
 
+
 class Report:
+    """Abstraction of a analyzer reporter."""
+
     __metaclass__ = ABCMeta
 
     def __init__(self, results):
+        """Initialize Report class."""
         self.results = results
 
     @abstractmethod
-    def compute_reports(self, results): pass
+    def _compute_reports(self, results): pass
+
+    @staticmethod
+    def logger_message(errors, analyzer):
+        """Logger of reporter computer methods."""
+        kiskadee.logger.debug(
+                "WARNING: There are " +
+                "{} registers on JSON ".format(errors) +
+                "that was not able to convert to " +
+                "'{}' reports.".format(analyzer)
+            )
+        return
 
 
-class CppCheckReport(Report):
+class CppcheckReport(Report):
+    """Concrete reporter implementation to cppcheck analyzer."""
 
-    def compute_reports(self):
+    def _compute_reports(self, analyzer):
+        """Compute every report type for cppcheck analyzer."""
+        print(analyzer)
         result_dict = {
                 'warning': 0,
                 'error': 0,
                 'style': 0
         }
+        count_error = 0
         for result in self.results:
             if 'severity' in list(result.keys()):
                 result_dict[result['severity']] += 1
             else:
-                print('deu ruimmmm')
+                count_error += 1
+
+        if count_error > 0:
+            self.logger_message(count_error, analyzer)
 
-        print(result_dict)
         return result_dict
 
+
+class FlawfinderReport(Report):
+    """Concrete reporter implementation to flawfinder analyzer."""
+
+    def _compute_reports(self, analyzer):
+        """Compute every report type for flawfinder analyzer."""
+        result_dict = {
+                'severity_1': 0,
+                'severity_2': 0,
+                'severity_3': 0,
+                'severity_4': 0,
+                'severity_5': 0
+        }
+        count_error = 0
+        for result in self.results:
+            if 'severity' in list(result.keys()):
+                result_dict['severity_' + result['severity']] += 1
+            else:
+                count_error += 1
+
+        if count_error > 0:
+            self.logger_message(count_error, analyzer)
+
+        return result_dict
diff --git a/kiskadee/tests/test_api.py b/kiskadee/tests/test_api.py
index 075d297..d3714c1 100644
--- a/kiskadee/tests/test_api.py
+++ b/kiskadee/tests/test_api.py
@@ -18,26 +18,29 @@ class ApiTestCase(unittest.TestCase):
         self.session = Session()
         self.app = kiskadee.api.app.kiskadee.test_client()
         kiskadee.model.create_analyzers(self.session)
+        kiskadee.model.Base.metadata.create_all(self.engine)
+        kiskadee.model.create_analyzers(self.session)
         fetcher = kiskadee.model.Fetcher(
                 name='kiskadee-fetcher', target='university'
         )
-        model.Base.metadata.create_all(self.engine)
-        model.create_analyzers(self.session)
-        fetcher = kiskadee.model.Fetcher(
-                name='kiskadee-fetcher', target='university', id=1
-        )
         pkg = kiskadee.model.Package(
-                name='kiskadee-package', fetcher_id=1, id=1
+                name='kiskadee-package', fetcher_id=1
         )
         version = kiskadee.model.Version(
-                number='7.23', package_id=1, id=1
+                number='7.23', package_id=1
         )
         analysis = kiskadee.model.Analysis(
                 version_id=1, analyzer_id=1,
-                raw="{\"metadata\": \"test\"}"
+                raw={
+                    'results': [
+                        {'severity': 'warning'},
+                        {'severity': 'style'},
+                        {'severity': 'error'}
+                    ]
+                }
         )
-        report = model.Reports(
-            analysis_id=1, warnings=1, styles=1, errors=1
+        report = kiskadee.model.Report(
+            analysis_id=1
         )
         self.session.add(fetcher)
         self.session.add(pkg)
@@ -89,24 +92,12 @@ class ApiTestCase(unittest.TestCase):
         kiskadee.api.app.kiskadee_db_session = mock_kiskadee_db_session
         response = self.app.get("/analysis/test/1.0.0")
         response_data = json.loads(response.data.decode("utf-8"))
-        self.assertIsNotNone(response_data["analysis"]["raw"])
-        pkg_first_analysis = response_data["analysis"]["raw"]["results"][0]
-        self.assertIn('location', pkg_first_analysis)
-        self.assertIn('cwe', pkg_first_analysis)
-        self.assertIn('message', pkg_first_analysis)
-
-    def test_get_specific_analysis(self):
-
-        def mock_kiskadee_db_session():
-            return self.session
-
-        kiskadee.api.app.kiskadee_db_session = mock_kiskadee_db_session
-        response = self.app.get("/analysis/kiskadee-package/7.23/")
-        response_as_json = json.loads(response.get_data(as_text=True))
-        self.assertIn("analysis", response_as_json)
-        #  On the first analysis of response,
-        #  check if exists some report attribute
-        self.assertIn("report", response_as_json['analysis'][0])
+        pkg_first_analysis = response_data["analysis"][0]
+        self.assertIsNotNone(pkg_first_analysis["raw"])
+        self.assertIn('report', pkg_first_analysis)
+        self.assertIn('location', pkg_first_analysis["raw"]["results"][0])
+        self.assertIn('cwe', pkg_first_analysis["raw"]["results"][0])
+        self.assertIn('message', pkg_first_analysis["raw"]["results"][0])
 
 
 if __name__ == '__main__':
diff --git a/kiskadee/tests/test_model.py b/kiskadee/tests/test_model.py
index b1445cc..4a56835 100644
--- a/kiskadee/tests/test_model.py
+++ b/kiskadee/tests/test_model.py
@@ -31,11 +31,8 @@ class ModelTestCase(unittest.TestCase):
                 raw=""
                 )
         self.session.add(self.analysis)
-        self.report = model.Reports(
+        self.report = model.Report(
                 analysis_id=1,
-                warnings=0,
-                styles=0,
-                errors=0
         )
         self.session.add(self.report)
         self.session.commit()
@@ -57,7 +54,7 @@ class ModelTestCase(unittest.TestCase):
         self.assertEqual(versions, [self.version])
 
     def test_query_report(self):
-        reports = self.session.query(model.Reports).all()
+        reports = self.session.query(model.Report).all()
         self.assertEqual(reports, [self.report])
 
     def test_add_fetcher(self):
@@ -74,10 +71,7 @@ class ModelTestCase(unittest.TestCase):
             self.session.commit()
 
     def test_add_report_without_analysis(self):
-        report = model.Reports(
-                warnings=0,
-                styles=0,
-                errors=0
+        report = model.Report(
         )
         self.session.add(report)
         with self.assertRaises(exc.IntegrityError):
diff --git a/kiskadee/tests/test_monitor.py b/kiskadee/tests/test_monitor.py
index ac4a452..57512b4 100644
--- a/kiskadee/tests/test_monitor.py
+++ b/kiskadee/tests/test_monitor.py
@@ -4,7 +4,7 @@ from sqlalchemy.orm import sessionmaker
 from kiskadee import model
 from kiskadee.monitor import Monitor
 from kiskadee.queue import packages_queue
-from kiskadee.model import Package, Fetcher, create_analyzers, Reports
+from kiskadee.model import Package, Fetcher, create_analyzers, Report, Analysis
 import kiskadee.queue
 import kiskadee.fetchers.debian
 from kiskadee.database import Database
@@ -46,9 +46,17 @@ class MonitorTestCase(unittest.TestCase):
                             'cppcheck': '<>',
                             'flawfinder': '><'},
                      'fetcher_id': 1}
-        self.analysis = {'analyzer_id': 1,
-                         'id': 1,
-                         'raw': 'warning, style, error'}
+        self.analysis = {
+                'analyzer_id': 1,
+                'id': 1,
+                'raw': {
+                    'results': [
+                        {'severity': 'warning'},
+                        {'severity': 'style'},
+                        {'severity': 'error'}
+                    ]
+                }
+            }
 
     def tearDown(self):
         self.session.close()
@@ -90,13 +98,40 @@ class MonitorTestCase(unittest.TestCase):
         self.assertEqual(_pkgs[1].name, _pkg['name'])
 
     def test_save_reports(self):
-        self.monitor._save_reports(self.analysis, self.pkg1)
-        _report = self.monitor.session.query(Reports).all()
+        _fetcher = model.Fetcher(
+                name='kiskadee-fetcher', target='university'
+            )
+        _pkg = model.Package(
+                name='kiskadee-package', fetcher_id=1
+            )
+        _version = model.Version(
+                number='1.0-rc1', package_id=1
+                )
+        _raw = {
+            'results': [
+                {'severity': 'warning'},
+                {'severity': 'style'},
+                {'severity': 'error'}
+            ]
+        }
+        _analysis = Analysis(
+                version_id=1,
+                analyzer_id=1,
+                id=1,
+                raw=_raw
+            )
+        self.session.add(_fetcher)
+        self.session.add(_pkg)
+        self.session.add(_version)
+        self.session.add(_analysis)
+        self.session.commit()
+        _dict_analysis = {
+                'results': _raw['results'],
+                'id': 1
+            }
+        self.monitor._save_reports(_dict_analysis, self.pkg1, 'cppcheck')
+        _report = self.monitor.session.query(Report).all()
         self.assertEqual(len(_report), 1)
-        self.assertEqual(_report[0].analysis_id, 1)
-        self.assertEqual(_report[0].warnings, 1)
-        self.assertEqual(_report[0].styles, 1)
-        self.assertEqual(_report[0].errors, 1)
 
     def test_save_version(self):
         self.monitor._save_fetcher(kiskadee.fetchers.debian.Fetcher())
diff --git a/kiskadee/tests/test_report.py b/kiskadee/tests/test_report.py
new file mode 100644
index 0000000..10cb936
--- /dev/null
+++ b/kiskadee/tests/test_report.py
@@ -0,0 +1,50 @@
+import unittest
+
+from sqlalchemy.orm import sessionmaker
+from kiskadee.report import CppcheckReport, FlawfinderReport
+from kiskadee.database import Database
+
+
+class ReportTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.engine = Database('db_test').engine
+        Session = sessionmaker(bind=self.engine)
+        self.session = Session()
+
+    def tearDown(self):
+        self.session.close()
+
+    def test_compute_cppcheck_reports(self):
+        _reports = [
+                {'severity': 'warning'},
+                {'severity': 'error'},
+                {'severity': 'style'},
+                {'some-attribute': None}
+            ]
+        _cpp_reporter = CppcheckReport(_reports)
+        result = _cpp_reporter._compute_reports('cppcheck')
+        self.assertEqual(len(result.keys()), 3)
+        self.assertEqual(result['warning'], 1)
+        self.assertEqual(result['style'], 1)
+        self.assertEqual(result['error'], 1)
+
+    def test_compute_flawfinder_reports(self):
+        _reports = [
+                {'severity': '5'},
+                {'severity': '4'},
+                {'severity': '3'},
+                {'some-attribute': None}
+            ]
+        _flawfinder_reporter = FlawfinderReport(_reports)
+        result = _flawfinder_reporter._compute_reports('flawfinder')
+        self.assertEqual(len(result.keys()), 5)
+        self.assertEqual(result['severity_5'], 1)
+        self.assertEqual(result['severity_4'], 1)
+        self.assertEqual(result['severity_3'], 1)
+        self.assertEqual(result['severity_2'], 0)
+        self.assertEqual(result['severity_1'], 0)
+
+
+if __name__ == '__main__':
+    unittest.main()

