Repository: nautobot/nautobot

Issue:
Present "Stats" block in Location view
### As ...

Ozzie - Operator

### I want ...

...to see the same "Stats" block that is currently displayed on a Site to be seen on a Location, *if* it has the appropriate content types. 

### So that ...

Currently we organise all our different server rooms as separate sites since we only have one campus. This enables us to assign all Devices to the appropriate room where they are located in. Since the nested Location model either lets me assign a Site (i.e. our campus) or a parent Location, all Devices like Racks etc. must be assigned to the top-level Site if I want to assign them to a specific child Location (e.g. a Room). Since Locations do not have a "Stats" block in their view, this breaks my and my colleagues workflow by navigating to the appropriate room to view all e.g. Racks inside that room.

### I know this is done when...

...the same "Stats" block currently shown in the Site view is also generated when viewing a Location view.

### Optional - Feature groups this request pertains to.

- [ ] Automation
- [ ] Circuits
- [X] DCIM
- [ ] IPAM
- [ ] Misc (including Data Sources)
- [X] Organization
- [ ] Plugins (and other Extensibility)
- [ ] Security (Secrets, etc)
- [ ] Image Management
- [X] UI/UX
- [ ] Documentation
- [ ] Other (not directly a platform feature)

### Database Changes

Likely none, should be doable by modifying the Location view alone.

### External Dependencies

None.


Discussion:

Resolving patch:
diff --git a/nautobot/dcim/views.py b/nautobot/dcim/views.py
--- a/nautobot/dcim/views.py
+++ b/nautobot/dcim/views.py
@@ -219,8 +219,22 @@ def get_extra_context(self, request, instance):
             .restrict(request.user, "view")
             .filter(site=instance)
         )
+        locations = (
+            Location.objects.restrict(request.user, "view")
+            .filter(site=instance)
+            .prefetch_related("parent", "location_type")
+        )
+
+        locations_table = tables.LocationTable(locations)
+
+        paginate = {
+            "paginator_class": EnhancedPaginator,
+            "per_page": get_paginate_count(request),
+        }
+        RequestConfig(request, paginate).configure(locations_table)
 
         return {
+            "locations_table": locations_table,
             "stats": stats,
             "rack_groups": rack_groups,
         }
@@ -334,6 +348,30 @@ class LocationView(generic.ObjectView):
     queryset = Location.objects.all()
 
     def get_extra_context(self, request, instance):
+        related_locations = (
+            instance.descendants(include_self=True).restrict(request.user, "view").values_list("pk", flat=True)
+        )
+        stats = {
+            "rack_count": Rack.objects.restrict(request.user, "view").filter(location__in=related_locations).count(),
+            "device_count": Device.objects.restrict(request.user, "view")
+            .filter(location__in=related_locations)
+            .count(),
+            "prefix_count": Prefix.objects.restrict(request.user, "view")
+            .filter(location__in=related_locations)
+            .count(),
+            "vlan_count": VLAN.objects.restrict(request.user, "view").filter(location__in=related_locations).count(),
+            "circuit_count": Circuit.objects.restrict(request.user, "view")
+            .filter(terminations__location__in=related_locations)
+            .count(),
+            "vm_count": VirtualMachine.objects.restrict(request.user, "view")
+            .filter(cluster__location__in=related_locations)
+            .count(),
+        }
+        rack_groups = (
+            RackGroup.objects.add_related_count(RackGroup.objects.all(), Rack, "group", "rack_count", cumulative=True)
+            .restrict(request.user, "view")
+            .filter(location__in=related_locations)
+        )
         children = (
             Location.objects.restrict(request.user, "view")
             .filter(parent=instance)
@@ -350,6 +388,8 @@ def get_extra_context(self, request, instance):
 
         return {
             "children_table": children_table,
+            "rack_groups": rack_groups,
+            "stats": stats,
         }
 
 


Test patch: