Clean up rack elevation rendering

This commit is contained in:
jeremystretch 2022-06-19 14:54:18 -04:00
parent 84f0561712
commit 0c915f7de9

View File

@ -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.rect(start, size, class_="slot blocked"))
link.add(drawing.text(get_device_name(device), insert=text)) 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): def draw_border(self):
link_url = '{}{}?{}'.format( """
self.base_url, Draw a border around the collection of rack units.
reverse('dcim:device_add'), """
urlencode({ border_width = RACK_ELEVATION_BORDER_WIDTH
'site': rack.site.pk, border_offset = RACK_ELEVATION_BORDER_WIDTH / 2
'location': rack.location.pk if rack.location else '', frame = Rect(
'rack': rack.pk, insert=(self.legend_width + border_offset, border_offset),
'face': face_id, size=(self.unit_width + border_width, self.rack.u_height * self.unit_height + border_width),
'position': unit class_='rack'
})
) )
link = drawing.add( self.drawing.add(frame)
drawing.a(href=link_url, target='_top')
)
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'))
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 start_cordinates = self._get_device_coords(unit['id'], height)
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
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 empty rack & legend
# Draw the unit 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 # Draw the rack border last
border_width = RACK_ELEVATION_BORDER_WIDTH self.draw_border()
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)
return self.drawing return self.drawing