Repository: hydroshare/hydroshare

Issue:
Contribution counts are not always consistent between interfaces
Contribution counts in the author preview do not always match contribution counts in the user profile.  This appears to be a difference of totaling by name (non-unique) vs. totaling by user ID (unique).
Example preview modal (click on the owner link) with 2 contributions shown:
https://beta.hydroshare.org/resource/7b3396eebe0e4efdbcf6c866461aeb89/
Example profile page (click on contributions tab) with 1 contribution shown:
https://beta.hydroshare.org/user/3007/


Discussion:

Resolving patch:
diff --git a/hs_access_control/models/user.py b/hs_access_control/models/user.py
--- a/hs_access_control/models/user.py
+++ b/hs_access_control/models/user.py
@@ -112,7 +112,7 @@ def create_group_membership_request(self, this_group, this_user=None):
             if this_group.gaccess.members.filter(id=self.user.id).exists():
                 raise PermissionDenied("You are already a member of this group")
         elif this_group.gaccess.members.filter(id=this_user.id).exists():
-                raise PermissionDenied("User is already a member of this group")
+            raise PermissionDenied("User is already a member of this group")
 
         # user (self) requesting to join a group
         if this_user is None:
@@ -194,8 +194,8 @@ def act_on_group_membership_request(self, this_request, accept_request=True):
             membership_grantor = this_request.request_from.uaccess
             # owner may cancel his own invitation
         elif self.owns_group(this_request.group_to_join) or self.user.is_superuser:
-                # allow this owner to cancel invitation generated by any group owner
-                pass
+            # allow this owner to cancel invitation generated by any group owner
+            pass
         else:
             raise PermissionDenied(
                 "You don't have permission to act on the group membership request")
@@ -1435,6 +1435,20 @@ def can_view_resource(self, this_resource):
 
         return False
 
+    def can_view_resources_owned_by(self, owner):
+        """
+        Whether user can view the resources owned by another user
+
+        :param owner: The owner whose resources will be checked for viewing
+        :return: Count of owner's resources that can be viewed
+        """
+        these_resources = owner.uaccess.owned_resources
+        viewable_count = 0
+        for resource in these_resources:
+            if self.can_view_resource(resource):
+                viewable_count += 1
+        return viewable_count
+
     def can_delete_resource(self, this_resource):
         """
         Whether user can delete a resource
diff --git a/hs_core/models.py b/hs_core/models.py
--- a/hs_core/models.py
+++ b/hs_core/models.py
@@ -192,6 +192,9 @@ def get_access_object(user, user_type, user_access):
     access_object = None
     picture = None
 
+    if not hasattr(user, 'viewable_contributions'):
+        user.viewable_contributions = 0
+
     if user_type == "user":
         if user.userprofile.picture:
             picture = user.userprofile.picture.url
@@ -209,6 +212,7 @@ def get_access_object(user, user_type, user_access):
             "organization": user.userprofile.organization,
             "title": user.userprofile.title,
             "contributions": len(user.uaccess.owned_resources),
+            "viewable_contributions": user.viewable_contributions,
             "subject_areas": user.userprofile.subject_areas,
             "identifiers": user.userprofile.identifiers,
             "state": user.userprofile.state,
@@ -260,9 +264,12 @@ def page_permissions_page_processor(request, page):
     edit_groups = cm.raccess.edit_groups
     view_groups = cm.raccess.view_groups.exclude(pk__in=edit_groups)
 
+    last_changed_by = cm.last_changed_by
+
     if request.user.is_authenticated():
         for owner in owners:
             owner.can_undo = request.user.uaccess.can_undo_share_resource_with_user(cm, owner)
+            owner.viewable_contributions = request.user.uaccess.can_view_resources_owned_by(owner)
 
         for viewer in viewers:
             viewer.can_undo = request.user.uaccess.can_undo_share_resource_with_user(cm, viewer)
@@ -277,6 +284,9 @@ def page_permissions_page_processor(request, page):
         for edit_grp in edit_groups:
             edit_grp.can_undo = request.user.uaccess.can_undo_share_resource_with_group(cm,
                                                                                         edit_grp)
+
+        last_changed_by.viewable_contributions = request.user.uaccess.can_view_resources_owned_by(last_changed_by)
+
     else:
         for owner in owners:
             owner.can_undo = False
@@ -288,6 +298,7 @@ def page_permissions_page_processor(request, page):
             view_grp.can_undo = False
         for edit_grp in edit_groups:
             edit_grp.can_undo = False
+    last_changed_by.can_undo = False
 
     users_json = []
 
@@ -306,6 +317,17 @@ def page_permissions_page_processor(request, page):
     for usr in view_groups:
         users_json.append(get_access_object(usr, "group", "view"))
 
+    lcb_access_level = cm.raccess.get_effective_user_privilege(last_changed_by)
+    if lcb_access_level == PrivilegeCodes.OWNER:
+        lcb_access_level = 'owner'
+    elif lcb_access_level == PrivilegeCodes.CHANGE:
+        lcb_access_level = 'edit'
+    elif lcb_access_level == PrivilegeCodes.VIEW:
+        lcb_access_level = 'view'
+
+    # last_changed_by.can_undo = False
+    last_changed_by = json.dumps(get_access_object(last_changed_by, "user", lcb_access_level))
+
     users_json = json.dumps(users_json)
 
     is_replaced_by = cm.get_relation_version_res_url(RelationTypes.isReplacedBy)
@@ -331,7 +353,8 @@ def page_permissions_page_processor(request, page):
         "can_change_resource_flags": can_change_resource_flags,
         "is_replaced_by": is_replaced_by,
         "is_version_of": is_version_of,
-        "show_manage_access": show_manage_access
+        "show_manage_access": show_manage_access,
+        "last_changed_by": last_changed_by
     }
 
 
@@ -368,7 +391,7 @@ def update(cls, element_id, **kwargs):
         """Pass through kwargs to update specific metadata object."""
         element = cls.objects.get(id=element_id)
         for key, value in list(kwargs.items()):
-                setattr(element, key, value)
+            setattr(element, key, value)
         element.save()
         return element
 


Test patch: