diff --git a/Trigger/TrigCost/RatesAnalysis/python/Util.py b/Trigger/TrigCost/RatesAnalysis/python/Util.py
index 16cb17d700f231a38dd45f104481e6b62b4a59c9..934582fa845b3feadaf96c37391003d17ec44a80 100644
--- a/Trigger/TrigCost/RatesAnalysis/python/Util.py
+++ b/Trigger/TrigCost/RatesAnalysis/python/Util.py
@@ -8,6 +8,61 @@
 @brief Utility functions used by RatesPostProcessing
 '''
 
+def toCSV(fileName, HLTTriggers):
+  import csv
+
+  with open(fileName, mode='w') as outputCSV_file:
+    rates_csv_writer = csv.writer(outputCSV_file, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL)
+
+    rates_csv_writer.writerow(['Name','Active Time [s]','Group','Weighted PS Rate [Hz]','Weighted PS Rate Err [Hz]','Prescale','ID','Raw Active Events','Raw Pass Events','Active Events','Input Rate [Hz]','Pass Fraction before PS [%]','Pass Fraction after PS [%]','Pass Weighted PS'])
+    rates_csv_writer.writerow(['Trigger name','Integrated length of all lumi blocks which contributed events to this rates prediction.','The group this chain belongs to.','Rate after applying all prescale(s) as weights.','Error on rate after applying all prescale(s) as weights','The prescale of this chain. Only displayed for simple combinations.','The CPTID or HLT Chain ID','Raw underlying statistics on the number events processed for this chain.','Raw underlying statistics on the number events passed by this chain.','Number of events in which the chain - or at least one chain in the combination - was executed.','Input rate to this chain or combination of chains. At L1 this will be the collision frequency for the bunch pattern.','Fraction of events which pass this trigger before prescale.','Fraction of events which pass this trigger after prescale.','Number of events this chain or combination passed after applying prescales as weighting factors.'])
+
+    # Provisional mapping of HLT chains and Group names
+    for trig in HLTTriggers:
+      if trig.name.startswith("HLT_e"):
+        group_name="RATE_SingleElectron"
+      elif trig.name.startswith("HLT_mu"):
+        group_name="RATE_SingleMuon"
+      elif trig.name.startswith("HLT_g"):
+        group_name="RATE_SinglePhoton"
+      elif trig.name.startswith("HLT_j"):
+        group_name="RATE_SingleJet"
+      elif trig.name.startswith("HLT_xe"):
+        group_name="RATE_MET"
+      elif trig.name.startswith("HLT_tau"):
+        group_name="RATE_SingleTau"
+      elif trig.name.startswith("HLT_2j") or trig.name.startswith("HLT_3j") or trig.name.startswith("HLT_4j") or trig.name.startswith("HLT_5j") or trig.name.startswith("HLT_6j"):
+        group_name="RATE_MultiJet"
+      elif trig.name.startswith("HLT_2e") or trig.name.startswith("HLT_3e") or trig.name.startswith("HLT_4e"):
+        group_name="RATE_MultiElectron"
+      elif trig.name.startswith("HLT_2mu_b") or trig.name.startswith("HLT_3mu_b") or trig.name.startswith("HLT_4mu_b"):
+        group_name="RATE_Bphysics"
+      elif trig.name.startswith("HLT_2mu") or trig.name.startswith("HLT_3mu") or trig.name.startswith("HLT_4mu"):
+        group_name="RATE_MultiMuon"
+      elif trig.name.startswith("HLT_2tau"):
+        group_name="RATE_MultiTau"
+      elif trig.name.startswith("HLT_2g"):
+        group_name="RATE_MultiPhoton"
+      elif trig.name.startswith("HLT_noalg"):
+        group_name="RATE_SeededStreamers"
+      else:
+        group_name="UNDEFINED"
+
+      if float(trig.rateDenominator)==0:
+        print("float(trig.rateDenominator) is ZERO! This shouldn't happen")
+      if float(trig.activeRaw)==0:
+        passFrac_beforePS=0
+      else:
+        passFrac_beforePS=100*float(trig.passRaw)/float(trig.activeRaw)
+      if float(trig.activeWeighted)==0:
+        passFrac_afterPS=0
+      else:
+        passFrac_afterPS=100*float(trig.passWeighted)/float(trig.activeWeighted)
+
+      rates_csv_writer.writerow([trig.name,"%.4f" % trig.rateDenominator,group_name,"%.4f" % trig.rate,"%.4f" % trig.rateErr,trig.prescale,'ID',"%.0f" % trig.activeRaw,"%.0f" % trig.passRaw,"%.4f" % trig.activeWeighted,"%.4f" % (float(trig.activeWeighted)/float(trig.rateDenominator)),"%.4f" % passFrac_beforePS,"%.4f" % passFrac_afterPS,"%.4f" % trig.passWeighted])
+      
+    
+
 def toJson(fileName, metadata, L1Triggers, HLTTriggers):
   import json
   l1 = {}
diff --git a/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisPostProcessing.py b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisPostProcessing.py
index 3e578c4db29145f127aea500c660e4933541a68d..dc1ad6cc22c99c3d670d9de516f5b7fb36d5aeac 100755
--- a/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisPostProcessing.py
+++ b/Trigger/TrigCost/RatesAnalysis/share/RatesAnalysisPostProcessing.py
@@ -11,7 +11,7 @@
 '''
 
 import ROOT
-from RatesAnalysis.Util import getMetadata, populateTriggers, getGlobalGroup, toJson
+from RatesAnalysis.Util import getMetadata, populateTriggers, getGlobalGroup, toJson, toCSV
 from AthenaCommon.Logging import logging
 
 def main():
@@ -23,6 +23,8 @@ def main():
                       help='Tag identifying this processing to be used in the output folder name (any underscores will be removed)')
   parser.add_argument('--outputJSONFile', default='rates.json', 
                       help='JSON file of rates for use with the RuleBook')
+  parser.add_argument('--outputCSVFile', default='Table_Rate_ChainHLT_HLT_All.csv', 
+                      help='CSV file of rates for webpage visualization')
   parser.add_argument('--userDetails',
                       help='User supplied metadata string giving any extra details about this run.')                      
   args = parser.parse_args()
@@ -48,6 +50,9 @@ def main():
 
   log.info("Exporting " + args.outputJSONFile)
   toJson(args.outputJSONFile, metadata, L1Triggers, HLTTriggers)
+  log.info("Exporting " + args.outputCSVFile)
+  toCSV(args.outputCSVFile, HLTTriggers)
+  
   
 if __name__== "__main__":
   main()