Jelajahi Sumber

Clean up rack elevation rendering

jeremystretch 3 tahun lalu
induk
melakukan
0c915f7de9
1 mengubah file dengan 83 tambahan dan 84 penghapusan
  1. 83 84
      netbox/dcim/svg.py

+ 83 - 84
netbox/dcim/svg.py

@@ -94,18 +94,34 @@ class RackElevationSVG:
             drawing.defs.add(drawing.style(css_file.read()))
             drawing.defs.add(drawing.style(css_file.read()))
 
 
         # Add gradients
         # Add gradients
-        RackElevationSVG._add_gradient(drawing, 'reserved', '#c7c7ff')
         RackElevationSVG._add_gradient(drawing, 'occupied', '#d7d7d7')
         RackElevationSVG._add_gradient(drawing, 'occupied', '#d7d7d7')
         RackElevationSVG._add_gradient(drawing, 'blocked', '#ffc0c0')
         RackElevationSVG._add_gradient(drawing, 'blocked', '#ffc0c0')
 
 
         return drawing
         return drawing
 
 
-    def _draw_device_front(self, drawing, device, start, end, text):
+    def _get_device_coords(self, position, height):
+        """
+        Return the X, Y coordinates of the top left corner for a device in the specified rack unit.
+        """
+        x = self.legend_width + RACK_ELEVATION_BORDER_WIDTH
+        y = RACK_ELEVATION_BORDER_WIDTH
+        if self.rack.desc_units:
+            y += int((position - 1) * self.unit_height)
+        else:
+            y += int((self.rack.u_height - position + 1) * self.unit_height) - int(height * self.unit_height)
+
+        return x, y
+
+    def _draw_device_front(self, drawing, device, start, size):
+        text_coords = (
+            start[0] + size[0] / 2,
+            start[1] + size[1] / 2
+        )
         name = get_device_name(device)
         name = get_device_name(device)
         if device.devicebay_count:
         if device.devicebay_count:
             name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count)
             name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count)
-
         color = device.device_role.color
         color = device.device_role.color
+
         link = drawing.add(
         link = drawing.add(
             drawing.a(
             drawing.a(
                 href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})),
                 href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})),
@@ -114,25 +130,30 @@ class RackElevationSVG:
             )
             )
         )
         )
         link.set_desc(self._get_device_description(device))
         link.set_desc(self._get_device_description(device))
-        link.add(drawing.rect(start, end, style='fill: #{}'.format(color), class_='slot'))
+        link.add(drawing.rect(start, size, style='fill: #{}'.format(color), class_='slot'))
         hex_color = '#{}'.format(foreground_color(color))
         hex_color = '#{}'.format(foreground_color(color))
-        link.add(drawing.text(str(name), insert=text, fill=hex_color))
+        link.add(drawing.text(str(name), insert=text_coords, fill=hex_color))
 
 
         # Embed front device type image if one exists
         # Embed front device type image if one exists
         if self.include_images and device.device_type.front_image:
         if self.include_images and device.device_type.front_image:
             image = drawing.image(
             image = drawing.image(
                 href='{}{}'.format(self.base_url, device.device_type.front_image.url),
                 href='{}{}'.format(self.base_url, device.device_type.front_image.url),
                 insert=start,
                 insert=start,
-                size=end,
+                size=size,
                 class_='device-image'
                 class_='device-image'
             )
             )
             image.fit(scale='slice')
             image.fit(scale='slice')
             link.add(image)
             link.add(image)
-            link.add(drawing.text(str(name), insert=text, stroke='black',
+            link.add(drawing.text(str(name), insert=text_coords, stroke='black',
                      stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
                      stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
-            link.add(drawing.text(str(name), insert=text, fill='white', class_='device-image-label'))
+            link.add(drawing.text(str(name), insert=text_coords, fill='white', class_='device-image-label'))
+
+    def _draw_device_rear(self, drawing, device, start, size):
+        text_coords = (
+            start[0] + size[0] / 2,
+            start[1] + size[1] / 2
+        )
 
 
-    def _draw_device_rear(self, drawing, device, start, end, text):
         link = drawing.add(
         link = drawing.add(
             drawing.a(
             drawing.a(
                 href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})),
                 href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})),
@@ -141,57 +162,76 @@ class RackElevationSVG:
             )
             )
         )
         )
         link.set_desc(self._get_device_description(device))
         link.set_desc(self._get_device_description(device))
-        link.add(drawing.rect(start, end, class_="slot blocked"))
-        link.add(drawing.text(get_device_name(device), insert=text))
+        link.add(drawing.rect(start, size, class_="slot blocked"))
+        link.add(drawing.text(get_device_name(device), insert=text_coords))
 
 
         # Embed rear device type image if one exists
         # Embed rear device type image if one exists
         if self.include_images and device.device_type.rear_image:
         if self.include_images and device.device_type.rear_image:
             image = drawing.image(
             image = drawing.image(
                 href='{}{}'.format(self.base_url, device.device_type.rear_image.url),
                 href='{}{}'.format(self.base_url, device.device_type.rear_image.url),
                 insert=start,
                 insert=start,
-                size=end,
+                size=size,
                 class_='device-image'
                 class_='device-image'
             )
             )
             image.fit(scale='slice')
             image.fit(scale='slice')
             link.add(image)
             link.add(image)
-            link.add(drawing.text(get_device_name(device), insert=text, stroke='black',
+            link.add(Text(get_device_name(device), insert=text_coords, stroke='black',
                      stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
                      stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
-            link.add(drawing.text(get_device_name(device), insert=text, fill='white', class_='device-image-label'))
+            link.add(Text(get_device_name(device), insert=text_coords, fill='white', class_='device-image-label'))
 
 
-    def _draw_empty(self, drawing, rack, start, end, text, unit, face_id, class_, reservation):
-        link_url = '{}{}?{}'.format(
-            self.base_url,
-            reverse('dcim:device_add'),
-            urlencode({
-                'site': rack.site.pk,
-                'location': rack.location.pk if rack.location else '',
-                'rack': rack.pk,
-                'face': face_id,
-                'position': unit
-            })
-        )
-        link = drawing.add(
-            drawing.a(href=link_url, target='_top')
+    def draw_border(self):
+        """
+        Draw a border around the collection of rack units.
+        """
+        border_width = RACK_ELEVATION_BORDER_WIDTH
+        border_offset = RACK_ELEVATION_BORDER_WIDTH / 2
+        frame = Rect(
+            insert=(self.legend_width + border_offset, border_offset),
+            size=(self.unit_width + border_width, self.rack.u_height * self.unit_height + border_width),
+            class_='rack'
         )
         )
-        if reservation:
-            link.set_desc('{} — {} · {}'.format(
-                reservation.description, reservation.user, reservation.created
-            ))
-        link.add(drawing.rect(start, end, class_=class_))
-        link.add(drawing.text("add device", insert=text, class_='add-device'))
+        self.drawing.add(frame)
 
 
     def draw_legend(self):
     def draw_legend(self):
         """
         """
         Draw the rack unit labels along the lefthand side of the elevation.
         Draw the rack unit labels along the lefthand side of the elevation.
         """
         """
         for ru in range(0, self.rack.u_height):
         for ru in range(0, self.rack.u_height):
-            start_y = ru * self.unit_height
+            start_y = ru * self.unit_height + RACK_ELEVATION_BORDER_WIDTH
             position_coordinates = (self.legend_width / 2, start_y + self.unit_height / 2 + RACK_ELEVATION_BORDER_WIDTH)
             position_coordinates = (self.legend_width / 2, start_y + self.unit_height / 2 + RACK_ELEVATION_BORDER_WIDTH)
             unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru
             unit = ru + 1 if self.rack.desc_units else self.rack.u_height - ru
             self.drawing.add(
             self.drawing.add(
-                Text(str(unit), position_coordinates, class_="unit")
+                Text(str(unit), position_coordinates, class_='unit')
             )
             )
 
 
+    def draw_background(self, face):
+        """
+        Draw the rack unit placeholders which form the "background" of the rack elevation.
+        """
+        x_offset = RACK_ELEVATION_BORDER_WIDTH + self.legend_width
+        url_string = '{}?{}&position={{}}'.format(
+            reverse('dcim:device_add'),
+            urlencode({
+                'site': self.rack.site.pk,
+                'location': self.rack.location.pk if self.rack.location else '',
+                'rack': self.rack.pk,
+                'face': face,
+            })
+        )
+
+        for ru in range(0, self.rack.u_height):
+            y_offset = RACK_ELEVATION_BORDER_WIDTH + ru * self.unit_height
+            text_coords = (
+                x_offset + self.unit_width / 2,
+                y_offset + self.unit_height / 2
+            )
+
+            link = Hyperlink(href=url_string.format(ru), target='_blank')
+            link.add(Rect((x_offset, y_offset), (self.unit_width, self.unit_height), class_='slot'))
+            link.add(self.drawing.text('add device', insert=text_coords, class_='add-device'))
+
+            self.drawing.add(link)
+
     def draw_face(self, face, opposite=False):
     def draw_face(self, face, opposite=False):
         """
         """
         Draw any occupied rack units for the specified rack face.
         Draw any occupied rack units for the specified rack face.
@@ -202,54 +242,21 @@ class RackElevationSVG:
             device = unit['device']
             device = unit['device']
             height = unit.get('height', decimal.Decimal(1.0))
             height = unit.get('height', decimal.Decimal(1.0))
 
 
-            # Setup drawing coordinates
-            x_offset = self.legend_width + RACK_ELEVATION_BORDER_WIDTH
-            if self.rack.desc_units:
-                y_offset = int(unit['id'] * self.unit_height) + RACK_ELEVATION_BORDER_WIDTH
-            else:
-                y_offset = self.drawing['height'] - int(unit['id'] * self.unit_height) - RACK_ELEVATION_BORDER_WIDTH
-
+            start_cordinates = self._get_device_coords(unit['id'], height)
             end_y = int(self.unit_height * height)
             end_y = int(self.unit_height * height)
-            start_cordinates = (x_offset, y_offset)
             size = (self.unit_width, end_y)
             size = (self.unit_width, end_y)
-            text_cordinates = (x_offset + (self.unit_width / 2), y_offset + end_y / 2)
 
 
             # Draw the device
             # Draw the device
             if device and device.pk in self.permitted_device_ids:
             if device and device.pk in self.permitted_device_ids:
-                print(device)
-                print(f'    {start_cordinates}')
-                print(f'    {size}')
-
                 if device.face == face and not opposite:
                 if device.face == face and not opposite:
-                    self._draw_device_front(self.drawing, device, start_cordinates, size, text_cordinates)
+                    self._draw_device_front(self.drawing, device, start_cordinates, size)
                 else:
                 else:
-                    self._draw_device_rear(self.drawing, device, start_cordinates, size, text_cordinates)
+                    self._draw_device_rear(self.drawing, device, start_cordinates, size)
 
 
             elif device:
             elif device:
                 # Devices which the user does not have permission to view are rendered only as unavailable space
                 # Devices which the user does not have permission to view are rendered only as unavailable space
                 self.drawing.add(Rect(start_cordinates, size, class_='blocked'))
                 self.drawing.add(Rect(start_cordinates, size, class_='blocked'))
 
 
-            # else:
-            #     # Draw shallow devices, reservations, or empty units
-            #     class_ = 'slot'
-            #     # reservation = reserved_units.get(unit["id"])
-            #     reservation = None
-            #     if device:
-            #         class_ += ' occupied'
-            #     if reservation:
-            #         class_ += ' reserved'
-            #     self._draw_empty(
-            #         self.drawing,
-            #         self.rack,
-            #         start_cordinates,
-            #         end_cordinates,
-            #         text_cordinates,
-            #         unit["id"],
-            #         face,
-            #         class_,
-            #         reservation
-            #     )
-
     def render(self, face):
     def render(self, face):
         """
         """
         Return an SVG document representing a rack elevation.
         Return an SVG document representing a rack elevation.
@@ -258,10 +265,9 @@ class RackElevationSVG:
         # Initialize the drawing
         # Initialize the drawing
         self.drawing = self._setup_drawing()
         self.drawing = self._setup_drawing()
 
 
-        # reserved_units = self.rack.get_reserved_units()
-
-        # Draw the unit legend
+        # Draw the empty rack & legend
         self.draw_legend()
         self.draw_legend()
+        self.draw_background(face)
 
 
         # Draw the opposite rack face first, then the near face
         # Draw the opposite rack face first, then the near face
         if face == DeviceFaceChoices.FACE_REAR:
         if face == DeviceFaceChoices.FACE_REAR:
@@ -271,15 +277,8 @@ class RackElevationSVG:
         # self.draw_face(opposite_face, opposite=True)
         # self.draw_face(opposite_face, opposite=True)
         self.draw_face(face)
         self.draw_face(face)
 
 
-        # Wrap the drawing with a border
-        border_width = RACK_ELEVATION_BORDER_WIDTH
-        border_offset = RACK_ELEVATION_BORDER_WIDTH / 2
-        frame = Rect(
-            insert=(self.legend_width + border_offset, border_offset),
-            size=(self.unit_width + border_width, self.rack.u_height * self.unit_height + border_width),
-            class_='rack'
-        )
-        self.drawing.add(frame)
+        # Draw the rack border last
+        self.draw_border()
 
 
         return self.drawing
         return self.drawing