Commit dd8c52df authored by Sebastien Robin's avatar Sebastien Robin

* Add two more columns in the milestone report :

  outcome_description and description
* change milestone report script in order to select
  as much sql column as possible. This allows to
  benefit from optimisations tables
* add erp5_project_mysql_innodb_catalog business template
  in order to keep this report fast even if there is more
  data than before. This extra business template is optional
  to run the report, but it is adviced to use it when
  project reports are use intensively

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@31885 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 98e6e34c
...@@ -93,9 +93,20 @@ if len(query_list):\n ...@@ -93,9 +93,20 @@ if len(query_list):\n
\n \n
select_dict = {}\n select_dict = {}\n
select_dict[\'movement.stop_date\'] = None\n select_dict[\'movement.stop_date\'] = None\n
select_dict[\'catalog.title\'] = None\n select_dict[\'title\'] = None\n
select_dict[\'description\'] = None\n
select_dict[\'parent_title\'] = None\n select_dict[\'parent_title\'] = None\n
milestone_list = []\n milestone_list = []\n
portal_catalog = portal.portal_catalog\n
# Check for some extra properties that are not necessarly\n
# in the catalog. We need by the way to check if\n
# hasColumn exists, it is new and is not installed yet everywhere\n
hasColumn = getattr(portal_catalog, \'hasColumn\', None)\n
if hasColumn is not None:\n
for property in [\'outcome_description\']:\n
if hasColumn(property):\n
select_dict[property] = None\n
\n
if len(project_uid_list):\n if len(project_uid_list):\n
milestone_list = [x for x in portal.portal_catalog(parent_uid=project_uid_list,\n milestone_list = [x for x in portal.portal_catalog(parent_uid=project_uid_list,\n
portal_type=\'Project Milestone\', select_dict=select_dict, **sql_kw)]\n portal_type=\'Project Milestone\', select_dict=select_dict, **sql_kw)]\n
...@@ -106,10 +117,11 @@ for milestone in milestone_list:\n ...@@ -106,10 +117,11 @@ for milestone in milestone_list:\n
# We wish to display the project only for the first milestone\n # We wish to display the project only for the first milestone\n
# of this project\n # of this project\n
line_kw = {}\n line_kw = {}\n
parent_title = milestone.parent_title\n
line_kw[\'project_title\'] = milestone.parent_title\n line_kw[\'project_title\'] = milestone.parent_title\n
line_kw[\'milestone_title\'] = milestone.title\n line_kw[\'milestone_title\'] = milestone.title\n
line_kw[\'stop_date\'] = getattr(milestone, \'stop_date\', None)\n line_kw[\'stop_date\'] = getattr(milestone, \'stop_date\', None)\n
line_kw[\'milestone_description\'] = milestone.getProperty(\'description\')\n
line_kw[\'milestone_outcome_description\'] = milestone.getProperty(\'outcome_description\')\n
listbox.append(line_kw)\n listbox.append(line_kw)\n
\n \n
context.Base_updateDialogForm(listbox=listbox, empty_line_number=0)\n context.Base_updateDialogForm(listbox=listbox, empty_line_number=0)\n
...@@ -186,10 +198,12 @@ return context.ProjectModule_viewMilestoneReport()\n ...@@ -186,10 +198,12 @@ return context.ProjectModule_viewMilestoneReport()\n
<string>query_list</string> <string>query_list</string>
<string>select_dict</string> <string>select_dict</string>
<string>milestone_list</string> <string>milestone_list</string>
<string>portal_catalog</string>
<string>getattr</string>
<string>hasColumn</string>
<string>property</string>
<string>milestone</string> <string>milestone</string>
<string>line_kw</string> <string>line_kw</string>
<string>parent_title</string>
<string>getattr</string>
</tuple> </tuple>
</value> </value>
</item> </item>
......
...@@ -338,6 +338,14 @@ ...@@ -338,6 +338,14 @@
<string>milestone_title</string> <string>milestone_title</string>
<string>Milestone</string> <string>Milestone</string>
</tuple> </tuple>
<tuple>
<string>milestone_description</string>
<string>Description</string>
</tuple>
<tuple>
<string>milestone_outcome_description</string>
<string>Outcome</string>
</tuple>
<tuple> <tuple>
<string>stop_date</string> <string>stop_date</string>
<string>Stop Date</string> <string>Stop Date</string>
......
726 727
\ No newline at end of file
<catalog_method>
<item key="sql_clear_catalog" type="int">
<value>1</value>
</item>
</catalog_method>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<tuple>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_arg</string> </key>
<value>
<object>
<klass>
<global name="Args" module="Shared.DC.ZRDB.Aqueduct"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_data</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_keys</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z0_drop_reporting_outcome</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string>DROP TABLE IF EXISTS reporting_outcome</string> </value>
</item>
<item>
<key> <string>template</string> </key>
<value>
<object>
<klass>
<global name="__newobj__" module="copy_reg"/>
</klass>
<tuple>
<global name="SQL" module="Shared.DC.ZRDB.DA"/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string encoding="cdata"><![CDATA[
<string>
]]></string> </value>
</item>
<item>
<key> <string>_vars</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>globals</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>raw</string> </key>
<value> <string>DROP TABLE IF EXISTS reporting_outcome</string> </value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<catalog_method>
<item key="sql_uncatalog_object" type="int">
<value>1</value>
</item>
<item key="_is_filtered_archive" type="int">
<value>1</value>
</item>
<item key="_filter_expression_archive" type="str">
<value>python: getattr(here, 'getOutcomeDescription', None) is not None</value>
</item>
<item key="_filter_type_archive" type="tuple">
</item>
</catalog_method>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<tuple>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_arg</string> </key>
<value>
<object>
<klass>
<global name="Args" module="Shared.DC.ZRDB.Aqueduct"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_data</string> </key>
<value>
<dictionary>
<item>
<key> <string>uid</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>_keys</string> </key>
<value>
<list>
<string>uid</string>
</list>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>uid</string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z0_uncatalog_reporting_outcome</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
DELETE FROM reporting_outcome WHERE <dtml-sqltest uid op=eq type=int>\n
]]></string> </value>
</item>
<item>
<key> <string>template</string> </key>
<value>
<object>
<klass>
<global name="__newobj__" module="copy_reg"/>
</klass>
<tuple>
<global name="SQL" module="Shared.DC.ZRDB.DA"/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string encoding="cdata"><![CDATA[
<string>
]]></string> </value>
</item>
<item>
<key> <string>_vars</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>globals</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>raw</string> </key>
<value> <string encoding="cdata"><![CDATA[
DELETE FROM reporting_outcome WHERE <dtml-sqltest uid op=eq type=int>\n
]]></string> </value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<catalog_method>
<item key="sql_catalog_object_list" type="int">
<value>1</value>
</item>
<item key="_is_filtered_archive" type="int">
<value>1</value>
</item>
<item key="_filter_expression_archive" type="str">
<value>python: getattr(here, 'getOutcomeDescription', None) is not None</value>
</item>
<item key="_filter_type_archive" type="tuple">
</item>
</catalog_method>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<tuple>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_arg</string> </key>
<value>
<object>
<klass>
<global name="Args" module="Shared.DC.ZRDB.Aqueduct"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_data</string> </key>
<value>
<dictionary>
<item>
<key> <string>getOutcomeDescription</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>uid</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>_keys</string> </key>
<value>
<list>
<string>getOutcomeDescription</string>
<string>uid</string>
</list>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>getOutcomeDescription\r\n
uid</string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_catalog_reporting_outcome_list</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
REPLACE INTO\n
reporting_outcome\n
(`uid`, `outcome_description`)\n
VALUES\n
<dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
(\n
<dtml-sqlvar expr="uid[loop_item]" type="int">, \n
<dtml-sqlvar expr="getOutcomeDescription[loop_item]" type="string" optional>\n
)\n
<dtml-if sequence-end><dtml-else>,</dtml-if>\n
</dtml-in>\n
]]></string> </value>
</item>
<item>
<key> <string>template</string> </key>
<value>
<object>
<klass>
<global name="__newobj__" module="copy_reg"/>
</klass>
<tuple>
<global name="SQL" module="Shared.DC.ZRDB.DA"/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string encoding="cdata"><![CDATA[
<string>
]]></string> </value>
</item>
<item>
<key> <string>_vars</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>globals</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>raw</string> </key>
<value> <string encoding="cdata"><![CDATA[
REPLACE INTO\n
reporting_outcome\n
(`uid`, `outcome_description`)\n
VALUES\n
<dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
(\n
<dtml-sqlvar expr="uid[loop_item]" type="int">, \n
<dtml-sqlvar expr="getOutcomeDescription[loop_item]" type="string" optional>\n
)\n
<dtml-if sequence-end><dtml-else>,</dtml-if>\n
</dtml-in>\n
]]></string> </value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<catalog_method>
<item key="sql_clear_catalog" type="int">
<value>1</value>
</item>
</catalog_method>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<tuple>
<global name="SQL" module="Products.ZSQLMethods.SQL"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_arg</string> </key>
<value>
<object>
<klass>
<global name="Args" module="Shared.DC.ZRDB.Aqueduct"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_data</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_keys</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>allow_simple_one_argument_traversal</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>cache_time_</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>class_file_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>class_name_</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>connection_hook</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>connection_id</string> </key>
<value> <string>erp5_sql_connection</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>z_create_reporting_outcome</string> </value>
</item>
<item>
<key> <string>max_cache_</string> </key>
<value> <int>100</int> </value>
</item>
<item>
<key> <string>max_rows_</string> </key>
<value> <int>1000</int> </value>
</item>
<item>
<key> <string>src</string> </key>
<value> <string>CREATE TABLE reporting_outcome (\n
uid BIGINT UNSIGNED NOT NULL,\n
outcome_description VARCHAR(255),\n
PRIMARY KEY `uid` (`uid`)\n
) TYPE=InnoDB; \n
</string> </value>
</item>
<item>
<key> <string>template</string> </key>
<value>
<object>
<klass>
<global name="__newobj__" module="copy_reg"/>
</klass>
<tuple>
<global name="SQL" module="Shared.DC.ZRDB.DA"/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string encoding="cdata"><![CDATA[
<string>
]]></string> </value>
</item>
<item>
<key> <string>_vars</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>globals</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>raw</string> </key>
<value> <string>CREATE TABLE reporting_outcome (\n
uid BIGINT UNSIGNED NOT NULL,\n
outcome_description VARCHAR(255),\n
PRIMARY KEY `uid` (`uid`)\n
) TYPE=InnoDB; \n
</string> </value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<key_list>
<key>reporting_outcome</key>
</key_list>
\ No newline at end of file
2010-01-21 Seb
* Initial version
\ No newline at end of file
Copyright (c) 2010 Nexedi SA
\ No newline at end of file
This template provides mysql table for optimising project reporting
\ No newline at end of file
erp5_project_catalog
\ No newline at end of file
erp5_mysql_innodb/z0_drop_reporting_outcome
erp5_mysql_innodb/z0_uncatalog_reporting_outcome
erp5_mysql_innodb/z_catalog_reporting_outcome_list
erp5_mysql_innodb/z_create_reporting_outcome
\ No newline at end of file
erp5_project_mysql_innodb_catalog
\ No newline at end of file
...@@ -31,18 +31,9 @@ from Products.ERP5Type.tests.utils import reindex ...@@ -31,18 +31,9 @@ from Products.ERP5Type.tests.utils import reindex
import transaction import transaction
from DateTime import DateTime from DateTime import DateTime
class TestMilestoneReporting(ERP5ReportTestCase): class MilestoneReportingMixin:
"""Test Milestone Reporting
This report is able to display all milestones from many projects, business_template_list = ('erp5_base','erp5_pdm', 'erp5_trade', 'erp5_project',)
it supports start and stop dates parameters
"""
def getTitle(self):
return "Milestone Reporting"
def getBusinessTemplateList(self):
"""Returns list of BT to be installed."""
return ('erp5_base','erp5_pdm', 'erp5_trade', 'erp5_project',)
@reindex @reindex
def _makeOneMilestone(self, project_title, **kw): def _makeOneMilestone(self, project_title, **kw):
...@@ -55,12 +46,17 @@ class TestMilestoneReporting(ERP5ReportTestCase): ...@@ -55,12 +46,17 @@ class TestMilestoneReporting(ERP5ReportTestCase):
else: else:
project = project_module.newContent(portal_type='Project', project = project_module.newContent(portal_type='Project',
title=project_title) title=project_title)
task = project.newContent(portal_type='Project Milestone', **kw) milestone = project.newContent(portal_type='Project Milestone', **kw)
return milestone
def afterSetUp(self): def afterSetUp(self):
"""Setup the fixture. """Setup the fixture.
""" """
self.portal = self.getPortal() self.portal = self.getPortal()
for module in (self.portal.project_module,):
module.manage_delObjects(list(module.objectIds()))
transaction.commit()
self.tic()
def getDataLineLineListByCallingMilestoneReport(self, def getDataLineLineListByCallingMilestoneReport(self,
from_date=None, at_date=None): from_date=None, at_date=None):
...@@ -80,19 +76,20 @@ class TestMilestoneReporting(ERP5ReportTestCase): ...@@ -80,19 +76,20 @@ class TestMilestoneReporting(ERP5ReportTestCase):
data_line_list = [l for l in line_list if l.isDataLine()] data_line_list = [l for l in line_list if l.isDataLine()]
return data_line_list return data_line_list
def testMilestoneReport(self): def checkMilestoneReport(self, optimised=False):
""" """
Check monthly report available on project Check monthly report available on project
""" """
# Create Tasks # Create Tasks
self._makeOneMilestone( milestone = self._makeOneMilestone(
project_title='Foo', project_title='Foo',
title='Foo Milestone A', title='Foo Milestone A',
start_date=DateTime('2009/10/01'), start_date=DateTime('2009/10/01'),
stop_date=DateTime('2009/10/27'), stop_date=DateTime('2009/10/27'),
description='foo',
outcome_description='bar',
) )
# We should have this result # We should have this result
# Project Milestone Stop Date # Project Milestone Stop Date
# Foo Foo Milestone A 2009/10/27 # Foo Foo Milestone A 2009/10/27
...@@ -102,7 +99,24 @@ class TestMilestoneReporting(ERP5ReportTestCase): ...@@ -102,7 +99,24 @@ class TestMilestoneReporting(ERP5ReportTestCase):
self.checkLineProperties(data_line_list[0], self.checkLineProperties(data_line_list[0],
project_title='Foo', project_title='Foo',
milestone_title='Foo Milestone A', milestone_title='Foo Milestone A',
stop_date=DateTime('2009/10/27')) stop_date=DateTime('2009/10/27'),
milestone_description='foo',
milestone_outcome_description='bar')
# Change value in order to check if we get real objects
milestone.setOutcomeDescription('foobar')
data_line_list = self.getDataLineLineListByCallingMilestoneReport(
from_date=DateTime('2009/10/01'), at_date=DateTime('2009/10/31'))
if optimised:
# Check that we do not get real object
self.checkLineProperties(data_line_list[0],
milestone_outcome_description='bar')
else:
# Check that we get real object
self.checkLineProperties(data_line_list[0],
milestone_outcome_description='foobar')
# Put back previous value
milestone.setOutcomeDescription('bar')
# Add other tasks, some of them in another project # Add other tasks, some of them in another project
self._makeOneMilestone( self._makeOneMilestone(
...@@ -156,3 +170,33 @@ class TestMilestoneReporting(ERP5ReportTestCase): ...@@ -156,3 +170,33 @@ class TestMilestoneReporting(ERP5ReportTestCase):
project_title='Foo', project_title='Foo',
milestone_title='Foo Milestone A', milestone_title='Foo Milestone A',
stop_date=DateTime('2009/10/27')) stop_date=DateTime('2009/10/27'))
class TestMilestoneReporting(MilestoneReportingMixin, ERP5ReportTestCase):
"""Milestone Reporting
This report is able to display all milestones from many projects,
it supports start and stop dates parameters
"""
def getTitle(self):
return "Milestone Reporting"
def getBusinessTemplateList(self):
"""Returns list of BT to be installed."""
return self.business_template_list
def testMilestoneReport(self):
self.checkMilestoneReport()
class TestOptimisedMilestoneReporting(MilestoneReportingMixin, ERP5ReportTestCase):
"""Same as above, with additionnal business template adding extra
tables in order to do optimisations
"""
def getTitle(self):
return "Optimised Milestone Reporting"
def getBusinessTemplateList(self):
"""Returns list of BT to be installed."""
return self.business_template_list + ('erp5_project_mysql_innodb_catalog',)
def testMilestoneReport(self):
self.checkMilestoneReport(optimised=True)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment