Refactor device rendering methods

This commit is contained in:
jeremystretch 2022-06-19 16:00:39 -04:00
parent 0c915f7de9
commit ae12948558

View File

@ -1,6 +1,8 @@
import decimal import decimal
import svgwrite import svgwrite
from svgwrite.container import Group, Hyperlink from svgwrite.container import Group, Hyperlink
from svgwrite.image import Image
from svgwrite.gradients import LinearGradient
from svgwrite.shapes import Line, Rect from svgwrite.shapes import Line, Rect
from svgwrite.text import Text from svgwrite.text import Text
@ -22,11 +24,27 @@ __all__ = (
def get_device_name(device): def get_device_name(device):
if device.virtual_chassis: if device.virtual_chassis:
return f'{device.virtual_chassis.name}:{device.vc_position}' name = f'{device.virtual_chassis.name}:{device.vc_position}'
elif device.name: elif device.name:
return device.name name = device.name
else: else:
return str(device.device_type) name = str(device.device_type)
if device.devicebay_count:
name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count)
return name
def get_device_description(device):
return '{} ({}) — {} {} ({}U) {} {}'.format(
device.name,
device.device_role,
device.device_type.manufacturer.name,
device.device_type.model,
device.device_type.u_height,
device.asset_tag or '',
device.serial or ''
)
class RackElevationSVG: class RackElevationSVG:
@ -56,21 +74,9 @@ class RackElevationSVG:
permitted_devices = permitted_devices.restrict(user, 'view') permitted_devices = permitted_devices.restrict(user, 'view')
self.permitted_device_ids = permitted_devices.values_list('pk', flat=True) self.permitted_device_ids = permitted_devices.values_list('pk', flat=True)
@staticmethod
def _get_device_description(device):
return '{} ({}) — {} {} ({}U) {} {}'.format(
device.name,
device.device_role,
device.device_type.manufacturer.name,
device.device_type.model,
device.device_type.u_height,
device.asset_tag or '',
device.serial or ''
)
@staticmethod @staticmethod
def _add_gradient(drawing, id_, color): def _add_gradient(drawing, id_, color):
gradient = drawing.linearGradient( gradient = LinearGradient(
start=(0, 0), start=(0, 0),
end=(0, 25), end=(0, 25),
spreadMethod='repeat', spreadMethod='repeat',
@ -82,6 +88,7 @@ class RackElevationSVG:
gradient.add_stop_color(offset='50%', color='#f7f7f7') gradient.add_stop_color(offset='50%', color='#f7f7f7')
gradient.add_stop_color(offset='50%', color=color) gradient.add_stop_color(offset='50%', color=color)
gradient.add_stop_color(offset='100%', color=color) gradient.add_stop_color(offset='100%', color=color)
drawing.defs.add(gradient) drawing.defs.add(gradient)
def _setup_drawing(self): def _setup_drawing(self):
@ -90,7 +97,7 @@ class RackElevationSVG:
drawing = svgwrite.Drawing(size=(width, height)) drawing = svgwrite.Drawing(size=(width, height))
# Add the stylesheet # Add the stylesheet
with open('{}/rack_elevation.css'.format(settings.STATIC_ROOT)) as css_file: with open(f'{settings.STATIC_ROOT}/rack_elevation.css') as css_file:
drawing.defs.add(drawing.style(css_file.read())) drawing.defs.add(drawing.style(css_file.read()))
# Add gradients # Add gradients
@ -112,72 +119,60 @@ class RackElevationSVG:
return x, y return x, y
def _draw_device_front(self, drawing, device, start, size): def _draw_device(self, device, coords, size, color=None, image=None):
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: description = get_device_description(device)
name += ' ({}/{})'.format(device.get_children().count(), device.devicebay_count)
color = device.device_role.color
link = drawing.add(
drawing.a(
href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})),
target='_top',
fill='black'
)
)
link.set_desc(self._get_device_description(device))
link.add(drawing.rect(start, size, style='fill: #{}'.format(color), class_='slot'))
hex_color = '#{}'.format(foreground_color(color))
link.add(drawing.text(str(name), insert=text_coords, fill=hex_color))
# Embed front device type image if one exists
if self.include_images and device.device_type.front_image:
image = drawing.image(
href='{}{}'.format(self.base_url, device.device_type.front_image.url),
insert=start,
size=size,
class_='device-image'
)
image.fit(scale='slice')
link.add(image)
link.add(drawing.text(str(name), insert=text_coords, stroke='black',
stroke_width='0.2em', stroke_linejoin='round', 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 = ( text_coords = (
start[0] + size[0] / 2, coords[0] + size[0] / 2,
start[1] + size[1] / 2 coords[1] + size[1] / 2
) )
text_color = f'#{foreground_color(color)}' if color else '#000000'
link = drawing.add( # Create hyperlink element
drawing.a( link = Hyperlink(
href='{}{}'.format(self.base_url, reverse('dcim:device', kwargs={'pk': device.pk})), href='{}{}'.format(
target='_top', self.base_url,
fill='black' reverse('dcim:device', kwargs={'pk': device.pk})
) ),
target='_blank',
) )
link.set_desc(self._get_device_description(device)) link.set_desc(description)
link.add(drawing.rect(start, size, class_="slot blocked")) if color:
link.add(drawing.text(get_device_name(device), insert=text_coords)) link.add(Rect(coords, size, style=f'fill: #{color}', class_='slot'))
else:
link.add(Rect(coords, size, class_='slot blocked'))
link.add(Text(name, insert=text_coords, fill=text_color))
# Embed rear device type image if one exists # Embed device type image if provided
if self.include_images and device.device_type.rear_image: if self.include_images and image:
image = drawing.image( image = Image(
href='{}{}'.format(self.base_url, device.device_type.rear_image.url), href='{}{}'.format(self.base_url, image.url),
insert=start, insert=coords,
size=size, size=size,
class_='device-image' class_='device-image'
) )
image.fit(scale='slice') image.fit(scale='slice')
link.add(image) link.add(image)
link.add(Text(get_device_name(device), insert=text_coords, stroke='black', link.add(Text(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(Text(get_device_name(device), insert=text_coords, fill='white', class_='device-image-label')) link.add(Text(name, insert=text_coords, fill='white', class_='device-image-label'))
self.drawing.add(link)
def draw_device_front(self, device, coords, size):
"""
Draw the front (mounted) face of a device.
"""
color = device.device_role.color
image = device.device_type.front_image
self._draw_device(device, coords, size, color=color, image=image)
def draw_device_rear(self, device, coords, size):
"""
Draw the rear (opposite) face of a device.
"""
image = device.device_type.rear_image
self._draw_device(device, coords, size, image=image)
def draw_border(self): def draw_border(self):
""" """
@ -228,7 +223,7 @@ class RackElevationSVG:
link = Hyperlink(href=url_string.format(ru), target='_blank') 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(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')) link.add(Text('add device', insert=text_coords, class_='add-device'))
self.drawing.add(link) self.drawing.add(link)
@ -242,20 +237,22 @@ class RackElevationSVG:
device = unit['device'] device = unit['device']
height = unit.get('height', decimal.Decimal(1.0)) height = unit.get('height', decimal.Decimal(1.0))
start_cordinates = self._get_device_coords(unit['id'], height) device_coords = self._get_device_coords(unit['id'], height)
end_y = int(self.unit_height * height) device_size = (
size = (self.unit_width, end_y) self.unit_width,
int(self.unit_height * height)
)
# 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:
if device.face == face and not opposite: if device.face == face and not opposite:
self._draw_device_front(self.drawing, device, start_cordinates, size) self.draw_device_front(device, device_coords, device_size)
else: else:
self._draw_device_rear(self.drawing, device, start_cordinates, size) self.draw_device_rear(device, device_coords, device_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(device_coords, device_size, class_='blocked'))
def render(self, face): def render(self, face):
""" """