Repository: adamchainz/django-mysql

Issue:
Smart iteration reporting with reverse says "highest pk so far"
It should read "lowest pk so far" when iterating from high to low.



Discussion:

Resolving patch:
diff --git a/django_mysql/models/query.py b/django_mysql/models/query.py
--- a/django_mysql/models/query.py
+++ b/django_mysql/models/query.py
@@ -264,7 +264,7 @@ def __iter__(self):
         db_alias = self.queryset.db
         status = GlobalStatus(db_alias)
 
-        self.init_progress()
+        self.init_progress(direction)
 
         while comp(current_pk, last_pk):
             status.wait_until_load_low(self.status_thresholds)
@@ -288,7 +288,7 @@ def __iter__(self):
                 # can be read by SmartRangeIterator or other client code
                 chunk._smart_iterator_pks = (start_pk, end_pk)
                 yield chunk
-                self.update_progress(chunk=chunk, end_pk=end_pk)
+                self.update_progress(direction, chunk, end_pk)
 
             self.adjust_chunk_size(chunk, timer.total_time)
 
@@ -387,7 +387,7 @@ def adjust_chunk_size(self, chunk, chunk_time):
 
         self.chunk_size = new_chunk_size
 
-    def init_progress(self):
+    def init_progress(self, direction):
         if not self.report_progress:
             return
 
@@ -402,9 +402,9 @@ def init_progress(self):
             except ValueError:  # Cannot be approximately counted
                 self.total = self.queryset.count()  # Fallback - will be slow
 
-        self.update_progress()
+        self.update_progress(direction)
 
-    def update_progress(self, chunk=None, end_pk=None):
+    def update_progress(self, direction, chunk=None, end_pk=None):
         if not self.report_progress:
             return
 
@@ -440,7 +440,12 @@ def update_progress(self, chunk=None, end_pk=None):
             )
         )
         if end_pk is not None:
-            sys.stdout.write("; highest pk so far {}".format(end_pk))
+            sys.stdout.write(
+                "; {dir} pk so far {end_pk}".format(
+                    dir="highest" if direction == 1 else "lowest",
+                    end_pk=end_pk,
+                )
+            )
         sys.stdout.flush()
 
     def end_progress(self):


Test patch:
diff --git a/tests/testapp/test_models.py b/tests/testapp/test_models.py
--- a/tests/testapp/test_models.py
+++ b/tests/testapp/test_models.py
@@ -592,8 +592,27 @@ def test_reporting(self):
         reports = lines[0].split('\r')
         for report in reports:
             assert re.match(
-                r"AuthorSmartChunkedIterator processed \d+/10 objects "
-                r"\(\d+\.\d+%\) in \d+ chunks(; highest pk so far \d+)?",
+                r"^AuthorSmartChunkedIterator processed \d+/10 objects "
+                r"\(\d+\.\d+%\) in \d+ chunks(; highest pk so far \d+)?$",
+                report
+            )
+
+        assert lines[1] == 'Finished!'
+
+    def test_reporting_reverse(self):
+        with captured_stdout() as output:
+            qs = Author.objects.reverse()
+            for authors in qs.iter_smart_chunks(report_progress=True):
+                list(authors)  # fetch them
+
+        lines = output.getvalue().split('\n')
+
+        reports = lines[0].split('\r')
+        assert len(reports) > 0
+        for report in reports:
+            assert re.match(
+                r"^AuthorSmartChunkedIterator processed \d+/10 objects "
+                r"\(\d+\.\d+%\) in \d+ chunks(; lowest pk so far \d+)?$",
                 report
             )
 
@@ -610,8 +629,8 @@ def test_reporting_with_total(self):
         reports = lines[0].split('\r')
         for report in reports:
             assert re.match(
-                r"AuthorSmartChunkedIterator processed \d+/4 objects "
-                r"\(\d+\.\d+%\) in \d+ chunks(; highest pk so far \d+)?",
+                r"^AuthorSmartChunkedIterator processed \d+/4 objects "
+                r"\(\d+\.\d+%\) in \d+ chunks(; highest pk so far \d+)?$",
                 report
             )