Repository: wagtail/wagtail

Issue:
Attribute Error for last_published_by_user on Aging Pages report download

### Issue Summary

We have a wagtail project that is the migration of an existing website from Drupal to wagtail. Due to the volume of information being moved  we use python scripts to recreate the existing pages as model instance. The scripts read assigned csv files with the data and populate the models with the data read (usually one script per Page model). All model instance creations are done with "created_by"= "migrator" (migrator is a specific user for this)

When accessing the Aging Pages report in the admin panel, the "Last Updated By" field is blank and when trying to export the report as xlsx I get the following error: 

![last_published_by_user_Error](https://github.com/wagtail/wagtail/assets/143010000/88c13355-4ec5-4ffe-a048-932014bd07e7)

Looking at the source code in https://github.com/wagtail/wagtail/blob/main/wagtail/admin/views/reports/aging_pages.py I see that the "last_updated_by_user" field is an annotation from the Log model.  I'm not sure how the Log model implementation work, and if model instances created through scripts are logged in or not. 

When I manually do any update to a page through the administrator panel, the "last_published_by_user" is updated on the report and the error is fixed for that page. 

If it is the case that we are importing the pages incorrectly, I would like the correct way to do it so this error does not happen, and how to fix it for the pages already imported. 

### Technical details

-   Python version: 3.11.1.
-   Django version: 4.2.4`.
-   Wagtail version: 5.1
-   Browser version: Chrome 116.0.5845.97.



Discussion:
I can confirm this issue exists. The web view of the report copes with empty user information in the publish record in PageLogEntry but the Excel download errors. 

Resolving patch:
diff --git a/wagtail/admin/views/reports/aging_pages.py b/wagtail/admin/views/reports/aging_pages.py
--- a/wagtail/admin/views/reports/aging_pages.py
+++ b/wagtail/admin/views/reports/aging_pages.py
@@ -82,6 +82,8 @@ def add_last_publisher_name_to_page(self, username_mapping, page):
                 user_id_value, get_deleted_user_display_name(user_id=user_id_value)
             )
             page.last_published_by_user = last_published_by_user
+        else:
+            page.last_published_by_user = ""
 
     def decorate_paginated_queryset(self, queryset):
         user_ids = set(queryset.values_list("last_published_by", flat=True))


Test patch:
diff --git a/wagtail/admin/tests/test_reports_views.py b/wagtail/admin/tests/test_reports_views.py
--- a/wagtail/admin/tests/test_reports_views.py
+++ b/wagtail/admin/tests/test_reports_views.py
@@ -631,6 +631,48 @@ def test_xlsx_export(self):
 
         self.assertEqual(worksheet["C2"].number_format, ExcelDateFormatter().get())
 
+    def test_xlsx_export_without_published_by(self):
+        """
+        Test that the xlsx export works when a page has no 'published_by' set.
+        See https://github.com/wagtail/wagtail/issues/10821
+        """
+
+        self.home.save_revision().publish()
+
+        if settings.USE_TZ:
+            self.home.last_published_at = "2013-01-01T12:00:00.000Z"
+        else:
+            self.home.last_published_at = "2013-01-01T12:00:00"
+
+        # mimic a page that does not have a 'published_by' on creation
+        self.home.last_published_by = None
+        self.home.save()
+
+        response = self.get(params={"export": "xlsx"})
+        self.assertEqual(response.status_code, 200)
+
+        workbook_data = response.getvalue()
+        worksheet = load_workbook(filename=BytesIO(workbook_data))["Sheet1"]
+        cell_array = [[cell.value for cell in row] for row in worksheet.rows]
+
+        self.assertEqual(
+            cell_array[0],
+            ["Title", "Status", "Last published at", "Last published by", "Type"],
+        )
+        self.assertEqual(
+            cell_array[1],
+            [
+                "Welcome to your new Wagtail site!",
+                "live + draft",
+                datetime.datetime(2013, 1, 1, 12, 0),
+                None,
+                "Page",
+            ],
+        )
+        self.assertEqual(len(cell_array), 2)
+
+        self.assertEqual(worksheet["C2"].number_format, ExcelDateFormatter().get())
+
     def test_report_renders_when_page_publisher_deleted(self):
         temp_user = self.create_superuser(
             "temp", email="temp@user.com", password="tempuser"