diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java
index 22745a3c54f57b211594a8b40bb5400f2884db32..b6647c4dbb60372082b3d4c87b218bf8639f98d5 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java
@@ -50,6 +50,7 @@
 import org.sleuthkit.datamodel.SleuthkitCase;
 import org.sleuthkit.datamodel.TskCoreException;
 import org.sleuthkit.autopsy.modules.interestingitems.FilesSet;
+import org.sleuthkit.autopsy.python.FactoryClassNameNormalizer;
 
 /**
  * Encapsulates a data source and the ingest module pipelines used to process
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java
index 80251599fda8746638bcaf5e4d0f341c4b4586f0..28377be688d4664531432979840bfde8fdeac954 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java
@@ -41,6 +41,7 @@
 import org.sleuthkit.autopsy.coreutils.PlatformUtil;
 import org.sleuthkit.autopsy.modules.interestingitems.FilesSet;
 import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager;
+import org.sleuthkit.autopsy.python.FactoryClassNameNormalizer;
 
 /**
  * The settings for an ingest job.
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java
index dcaaca9c3500ef572d54eecfbe4e37604911c01c..1f3d3ee4fd660ad0201f88ec2a2a203e944b88c7 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java
@@ -18,6 +18,7 @@
  */
 package org.sleuthkit.autopsy.ingest;
 
+import org.sleuthkit.autopsy.python.FactoryClassNameNormalizer;
 import java.awt.Component;
 import java.awt.event.ActionEvent;
 import java.awt.event.WindowAdapter;
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java b/Core/src/org/sleuthkit/autopsy/python/FactoryClassNameNormalizer.java
similarity index 77%
rename from Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java
rename to Core/src/org/sleuthkit/autopsy/python/FactoryClassNameNormalizer.java
index 6ac324e31f86ee8715a5d65eef4f00650a65b62e..0a5d0efaa7325935bd0a371eca84e4520bd2eab8 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/FactoryClassNameNormalizer.java
+++ b/Core/src/org/sleuthkit/autopsy/python/FactoryClassNameNormalizer.java
@@ -16,20 +16,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.sleuthkit.autopsy.ingest;
+package org.sleuthkit.autopsy.python;
 
 import org.sleuthkit.autopsy.coreutils.Logger;
 
 /**
  * Used to strip Python IDs on factory class names.
  */
-class FactoryClassNameNormalizer {
+public class FactoryClassNameNormalizer {
 
     private static final CharSequence pythonModuleSettingsPrefixCS = "org.python.proxies.".subSequence(0, "org.python.proxies.".length() - 1); //NON-NLS
     private static final Logger logger = Logger.getLogger(FactoryClassNameNormalizer.class.getName());
 
-    static String normalize(String canonicalClassName) {
-        if (isPythonModuleSettingsFile(canonicalClassName)) {
+    public static String normalize(String canonicalClassName) {
+        if (isPythonClassName(canonicalClassName)) {
             // Compiled Python modules have variable instance number as a part
             // of their file name. This block of code gets rid of that variable
             // instance number and helps maitains constant module name over
@@ -41,17 +41,17 @@ static String normalize(String canonicalClassName) {
     }
 
     /**
-     * Determines if the moduleSettingsFilePath is that of a serialized Jython
+     * Determines if the classNameToVerify is that of a serialized Jython
      * instance. Serialized Jython instances (settings saved on the disk)
      * contain "org.python.proxies." in their fileName based on the current
      * implementation.
      *
-     * @param moduleSettingsFilePath path to the module settings file.
+     * @param classNameToVerify class name to verify.
      *
      * @return True or false
      */
-    private static boolean isPythonModuleSettingsFile(String moduleSettingsFilePath) {
-        return moduleSettingsFilePath.contains(pythonModuleSettingsPrefixCS);
+    private static boolean isPythonClassName(String classNameToVerify) {
+        return classNameToVerify.contains(pythonModuleSettingsPrefixCS);
     }
 
 }
diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java
index c88d05b94e6006ea11e92e3c61332504956f8818..04c541364151a3e2ebe11c56c2adcf336edcd2b3 100644
--- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java
+++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java
@@ -47,6 +47,7 @@
 import org.sleuthkit.autopsy.casemodule.Case;
 import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
 import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.python.FactoryClassNameNormalizer;
 import org.sleuthkit.autopsy.report.ReportProgressPanel;
 import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
 import org.sleuthkit.datamodel.AbstractFile;
@@ -119,12 +120,47 @@ public ReportGenerator(String configName, ReportProgressIndicator progressIndica
         this.progressIndicator = panel.getProgressPanel();
         this.configName = configName;
     }
+    
+    /**
+     * Generates the reports specified by the reporting configuration passed 
+     * in via the constructor. Does lookup of all existing report modules.
+     */
+    public void generateReports() {
+        // load all report modules 
+        Map<String, ReportModule> modules = new HashMap<>();
+        for (TableReportModule module : ReportModuleLoader.getTableReportModules()) {
+            modules.put(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()), module);
+        }
+
+        for (GeneralReportModule module : ReportModuleLoader.getGeneralReportModules()) {
+            modules.put(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()), module);
+        }
+
+        for (FileReportModule module : ReportModuleLoader.getFileReportModules()) {
+            modules.put(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()), module);
+        }
+
+        // special case for PortableCaseReportModule
+        modules.put(FactoryClassNameNormalizer.normalize(PortableCaseReportModule.class.getCanonicalName()), new PortableCaseReportModule());
+        
+        generateReports(modules);
+    }
 
     /**
      * Generates the reports specified by the reporting configuration passed in
-     * via the constructor.
+     * via the constructor. 
+     * 
+     * @param modules Map of report module objects to use. This is useful when we want to 
+     *                re-use the module instances or limit which reports are generated.
      */
-    public void generateReports() {
+    public void generateReports(Map<String, ReportModule> modules) {
+        
+        if (modules == null || modules.isEmpty()) {
+            logger.log(Level.SEVERE, "No report modules found");
+            progressIndicator.updateStatusLabel("No report modules found. Exiting");
+            return;            
+        }
+        
         ReportingConfig config = null;
         try {
             config = ReportingConfigLoader.loadConfig(configName);
@@ -141,23 +177,6 @@ public void generateReports() {
         }
 
         try {
-            // load all report modules 
-            Map<String, ReportModule> modules = new HashMap<>();
-            for (TableReportModule module : ReportModuleLoader.getTableReportModules()) {
-                modules.put(module.getClass().getCanonicalName(), module);
-            }
-
-            for (GeneralReportModule module : ReportModuleLoader.getGeneralReportModules()) {
-                modules.put(module.getClass().getCanonicalName(), module);
-            }
-
-            for (FileReportModule module : ReportModuleLoader.getFileReportModules()) {
-                modules.put(module.getClass().getCanonicalName(), module);
-            }
-
-            // special case for PortableCaseReportModule
-            modules.put(PortableCaseReportModule.class.getCanonicalName(), new PortableCaseReportModule());
-
             // generate reports for enabled modules
             for (Map.Entry<String, ReportModuleConfig> entry : config.getModuleConfigs().entrySet()) {
                 ReportModuleConfig moduleConfig = entry.getValue();
diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel1.java
index 74a9e6d73827ab2eabbd4222749543c61b99ecdf..7eca143b80fc304b761fe2de7288c3c82b9f99b2 100644
--- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel1.java
+++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportVisualPanel1.java
@@ -42,6 +42,7 @@
 import org.openide.NotifyDescriptor;
 import org.openide.util.NbBundle;
 import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.python.FactoryClassNameNormalizer;
 
 /**
  * Display reports modules.
@@ -128,7 +129,7 @@ private void initModules() {
             ReportModuleSettings settings = null;
             if (moduleConfigs != null) {
                 // get configuration for this module
-                ReportModuleConfig config = moduleConfigs.get(module.getClass().getCanonicalName());                
+                ReportModuleConfig config = moduleConfigs.get(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()));                
                 if (config != null) {
                     // there is an existing configuration for this module
                     settings = config.getModuleSettings();
@@ -239,16 +240,24 @@ Map<String, ReportModuleConfig> getUpdatedModuleConfigs() {
         for (ReportModule module : modules) {
             // get updated module configuration
             ReportModuleSettings settings = module.getConfiguration();
-            moduleConfigs.put(module.getClass().getCanonicalName(), new ReportModuleConfig(module, false, settings));
+            moduleConfigs.put(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()), new ReportModuleConfig(module, false, settings));
         }
         
         // set "enabled" flag for the selected module
         ReportModule mod = getSelectedModule();
-        ReportModuleConfig config = moduleConfigs.get(mod.getClass().getCanonicalName());
+        ReportModuleConfig config = moduleConfigs.get(FactoryClassNameNormalizer.normalize(mod.getClass().getCanonicalName()));
         config.setEnabled(true);
         
         return moduleConfigs;
     }
+    
+    Map<String, ReportModule> getReportModules() {
+        Map<String, ReportModule> modulesMap = new HashMap<>();
+        for (ReportModule module : modules) {
+            modulesMap.put(FactoryClassNameNormalizer.normalize(module.getClass().getCanonicalName()), module);
+        }
+        return modulesMap;
+    } 
 
     /**
      * This method is called from within the constructor to initialize the form.
diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java
index 634f7799887dd07e77fc537e6e1c90c47ec1c150..236263200a53476b8b622e41084559a7ba810733 100644
--- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java
+++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java
@@ -52,6 +52,7 @@
 import org.sleuthkit.autopsy.casemodule.Case;
 import org.sleuthkit.autopsy.core.RuntimeProperties;
 import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.report.ReportModule;
 
 @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.report.infrastructure.ReportWizardAction")
 @ActionRegistration(displayName = "#CTL_ReportWizardAction", lazy = false)
@@ -101,9 +102,10 @@ public static void doReportWizard(String configName, boolean displayCaseSpecific
             if (runReports) {
                 // generate reports in a separate thread
                 panel = new ReportGenerationPanel();
+                Map<String, ReportModule> modules = (Map<String, ReportModule>) wiz.getProperty("modules");
                 ReportGenerator generator = new ReportGenerator(configName, panel); //NON-NLS
                 ReportWorker worker = new ReportWorker(() -> {
-                    generator.generateReports();
+                    generator.generateReports(modules);
                 });
                 worker.execute();
                 generator.displayProgressPanel();
diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardPanel1.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardPanel1.java
index dc1927318ef59f86eb41cab8b1225cc06105936c..768a4acf563d8507928387740301696c693d6d88 100644
--- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardPanel1.java
+++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardPanel1.java
@@ -28,6 +28,7 @@
 import org.openide.util.HelpCtx;
 import org.openide.util.NbBundle;
 import org.openide.util.NbPreferences;
+import org.sleuthkit.autopsy.report.ReportModule;
 
 class ReportWizardPanel1 implements WizardDescriptor.FinishablePanel<WizardDescriptor> {
 
@@ -110,6 +111,7 @@ public void readSettings(WizardDescriptor wiz) {
     @Override
     public void storeSettings(WizardDescriptor wiz) {
         wiz.putProperty("moduleConfigs", getComponent().getUpdatedModuleConfigs()); //NON-NLS
+        wiz.putProperty("modules", getComponent().getReportModules()); //NON-NLS
 
         // Store preferences that WizardIterator will use to determine what 
         // panels need to be shown