Repository: LibraryOfCongress/concordia

Issue:
Progress percentage to show level of completion on an item
As a user, I want to know how complete an item is so that I can decide if I want to click it and transcribe

Acceptance criteria:
- [x] A function that does the math to generates a percentage based on how many items were completed and items that are open for editing
- [x] UI of percentage based on wireframe

https://github.com/LibraryOfCongress/concordia/issues/41


Discussion:

Resolving patch:
diff --git a/concordia/forms.py b/concordia/forms.py
--- a/concordia/forms.py
+++ b/concordia/forms.py
@@ -79,19 +79,9 @@ class AssetFilteringForm(forms.Form):
         widget=forms.Select(attrs={"class": "form-control"}),
     )
 
-    def __init__(self, asset_qs, *args, **kwargs):
+    def __init__(self, status_counts, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        # We want to get a list of all of the available asset states in this
-        # item's assets and will return that with the preferred display labels
-        # including the asset count to be displayed in the filter UI
-        asset_state_qs = asset_qs.values_list("transcription_status")
-        asset_state_qs = asset_state_qs.annotate(
-            Count("transcription_status")
-        ).order_by()
-
-        status_counts = dict(asset_state_qs)
-
         asset_statuses = {
             status: "%s (%d)" % (TranscriptionStatus.CHOICE_MAP[status], count)
             for status, count in status_counts.items()
diff --git a/concordia/views.py b/concordia/views.py
--- a/concordia/views.py
+++ b/concordia/views.py
@@ -233,7 +233,17 @@ def get_queryset(self):
     def apply_asset_filters(self, asset_qs):
         """Use optional GET parameters to filter the asset list"""
 
-        self.filter_form = form = self.form_class(asset_qs, self.request.GET)
+        # We want to get a list of all of the available asset states in this
+        # item's assets and will return that with the preferred display labels
+        # including the asset count to be displayed in the filter UI
+        asset_state_qs = asset_qs.values_list("transcription_status")
+        asset_state_qs = asset_state_qs.annotate(
+            Count("transcription_status")
+        ).order_by()
+
+        self.transcription_status_counts = status_counts = dict(asset_state_qs)
+
+        self.filter_form = form = self.form_class(status_counts, self.request.GET)
         if form.is_valid():
             asset_qs = asset_qs.filter(
                 **{k: v for k, v in form.cleaned_data.items() if v}
@@ -244,12 +254,33 @@ def apply_asset_filters(self, asset_qs):
     def get_context_data(self, **kwargs):
         res = super().get_context_data(**kwargs)
 
+        # We'll collect some extra stats for the progress bar. We can reuse the values
+        # which are calculated for the transcription status filters but that displays
+        # items as open for edit whether or not anyone has started transcribing them.
+        # For the progress bar, we'll only count the records which have at least one
+        # transcription, no matter how far along it is:
+
+        contributors = Transcription.objects.filter(asset__item=self.item).aggregate(
+            Count("user", distinct=True), Count("asset", distinct=True)
+        )
+
+        asset_count = len(self.object_list)
+        if asset_count:
+            in_progress_percent = round(
+                100 * (contributors["asset__count"] / asset_count)
+            )
+        else:
+            in_progress_percent = 0
+
         res.update(
             {
                 "campaign": self.item.project.campaign,
                 "project": self.item.project,
                 "item": self.item,
                 "filter_form": self.filter_form,
+                "transcription_status_counts": self.transcription_status_counts,
+                "contributor_count": contributors["user__count"],
+                "in_progress_percent": in_progress_percent,
             }
         )
         return res


Test patch: