Repository: akvo/akvo-rsr

Issue:
Feature Request: Add narrative column on program overview excel report
### What are you trying to do?

I want to see the narrative text of qualitative indicator updates on the program overview excel  report. 

### Describe the solution you'd like

Add narrative column on program overview excel report.

### Have you consider alternatives?

_No response_

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/akvo/rsr/dataclasses.py b/akvo/rsr/dataclasses.py
--- a/akvo/rsr/dataclasses.py
+++ b/akvo/rsr/dataclasses.py
@@ -403,6 +403,7 @@ class ContributorData(ReportingPeriodMixin):
     indicator_cumulative: bool = False
     target_value: Optional[Decimal] = None
     period_actual_value: Optional[Decimal] = None
+    period_narrative: str = ''
     indicator_baseline_value: Optional[Decimal] = None
     indicator_target_value: Optional[Decimal] = None
     project: Optional[ContributorProjectData] = None
@@ -422,6 +423,7 @@ def make(cls, data, prefix=''):
             indicator_cumulative=data.get(f"{prefix}indicator__cumulative", False),
             target_value=maybe_decimal(data.get(f"{prefix}target_value", None)),
             period_actual_value=ensure_decimal(data.get(f"{prefix}actual_value", None)),
+            period_narrative=data.get(f"{prefix}narrative", ''),
             indicator_baseline_value=data.get(f"{prefix}indicator__baseline_value", None),
             indicator_target_value=data.get(f"{prefix}indicator__target_value", None),
             project=ContributorProjectData.make(data, 'indicator__result__project__')
diff --git a/akvo/rsr/views/py_reports/program_overview_excel_report.py b/akvo/rsr/views/py_reports/program_overview_excel_report.py
--- a/akvo/rsr/views/py_reports/program_overview_excel_report.py
+++ b/akvo/rsr/views/py_reports/program_overview_excel_report.py
@@ -22,7 +22,7 @@
 from . import utils, program_utils
 
 
-AGGREGATED_TARGET_VALUE_COLUMN = 11
+AGGREGATED_TARGET_VALUE_COLUMN = 12
 
 
 def get_dynamic_column_start(aggregate_targets):
@@ -62,7 +62,7 @@ def generate_workbok(program, start_date=None, end_date=None):
     aggregate_targets = is_aggregating_targets(program)
     use_indicator_target = utils.is_using_indicator_target(program)
     disaggregations = get_disaggregations(program)
-    disaggregations_column_start = 16 if aggregate_targets else 15
+    disaggregations_column_start = 17 if aggregate_targets else 16
     disaggregation_types_length = 0
     for types in disaggregations.values():
         disaggregation_types_length += len(types.keys())
@@ -95,6 +95,7 @@ def generate_workbok(program, start_date=None, end_date=None):
         ws.set_col_style(8, Style(size=25))
         ws.set_col_style(9, Style(size=25))
         ws.set_col_style(10, Style(size=25))
+        ws.set_col_style(11, Style(size=30))
         if aggregate_targets:
             ws.set_col_style(AGGREGATED_TARGET_VALUE_COLUMN, Style(size=25))
         col = get_dynamic_column_start(aggregate_targets)
@@ -119,6 +120,7 @@ def generate_workbok(program, start_date=None, end_date=None):
         ws.set_cell_value(1, 8, 'Countries')
         ws.set_cell_value(1, 9, 'Sector')
         ws.set_cell_value(1, 10, 'Baseline value')
+        ws.set_cell_value(1, 11, 'Narrative')
         if aggregate_targets:
             ws.set_cell_value(1, AGGREGATED_TARGET_VALUE_COLUMN, 'Aggregated target value')
         col = get_dynamic_column_start(aggregate_targets)
@@ -129,6 +131,7 @@ def generate_workbok(program, start_date=None, end_date=None):
         ws.set_cell_value(1, col, 'Actual value')
         col += 1
         ws.set_cell_value(1, col, '% of contribution')
+        # col += 1
         if disaggregation_types_length:
             col = disaggregations_column_start
             for category, types in disaggregations.items():
@@ -225,6 +228,9 @@ def render_contributor(ws, row, result, indicator, period, contributor, aggregat
     ws.set_cell_style(row, 9, long_text_style)
     ws.set_cell_value(row, 9, ', '.join(contributor.project.sectors) if contributor.project.sectors else '')
     ws.set_cell_value(row, 10, maybe_decimal(contributor.indicator_baseline_value))
+    if period.is_qualitative:
+        ws.set_cell_style(row, 11, long_text_style)
+        ws.set_cell_value(row, 11, contributor.period_narrative)
     col = get_dynamic_column_start(aggregate_targets)
     ws.set_cell_value(row, col, contributor.indicator_target_value if use_indicator_target else ensure_decimal(contributor.target_value))
     col += 2
diff --git a/akvo/rsr/views/py_reports/program_utils.py b/akvo/rsr/views/py_reports/program_utils.py
--- a/akvo/rsr/views/py_reports/program_utils.py
+++ b/akvo/rsr/views/py_reports/program_utils.py
@@ -143,7 +143,7 @@ def fetch_contributors(root_period_ids):
         )\
         .filter(id__in=contributor_ids)\
         .values(
-            'id', 'period_start', 'parent_period', 'target_value', 'actual_value', 'indicator__id',
+            'id', 'period_start', 'parent_period', 'target_value', 'actual_value', 'narrative', 'indicator__id',
             'indicator__type', 'indicator__measure', 'indicator__cumulative', 'indicator__target_value',
             'indicator__baseline_value', 'indicator__result__project__id',
             'indicator__result__project__title', 'indicator__result__project__subtitle',


Test patch: