fix: [exportpdf] Add suggestions (UX)

pull/358/head
Falconieri 2019-03-01 10:45:44 +01:00
parent ead9cec7d3
commit b2c5477cd9
1 changed files with 36 additions and 19 deletions

View File

@ -27,7 +27,7 @@ try:
from reportlab.lib.utils import ImageReader from reportlab.lib.utils import ImageReader
from reportlab.lib.pagesizes import A4 from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Spacer, Table, TableStyle, Flowable, Image from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Spacer, Table, TableStyle, Flowable, Image, Indenter
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm from reportlab.lib.units import mm
@ -130,6 +130,8 @@ moduleconfig = ["MISP_base_url_for_dynamic_link", "MISP_name_for_metadata", "Act
# == Row colors of the table (alternating) == # == Row colors of the table (alternating) ==
EVEN_COLOR = colors.whitesmoke EVEN_COLOR = colors.whitesmoke
ODD_COLOR = colors.lightgrey ODD_COLOR = colors.lightgrey
EVEN_COLOR_GALAXY = colors.powderblue
ODD_COLOR_GALAXY = colors.lavenderblush
# == Lines parameters of the table == # == Lines parameters of the table ==
LINE_COLOR = colors.lightslategray LINE_COLOR = colors.lightslategray
@ -163,15 +165,17 @@ ROW_HEIGHT_FOR_TAGS = 4 * mm # 4.5 * mm (a bit too short to allow vertical alig
# == Whole document margins and size == # == Whole document margins and size ==
PAGESIZE = A4 # (140 * mm, 216 * mm) # width, height PAGESIZE = A4 # (140 * mm, 216 * mm) # width, height
BASE_MARGIN = 5 * mm # Create a list here to specify each row separately BASE_MARGIN = 5 * mm # Create a list here to specify each row separately
INDENT_SIZE = 6 * mm # The Indentation of attribute from object, etc.
INDENT_SIZE_HEADING = 3 * mm # The Indentation of attribute from object, etc.
# == Parameters for error handling for content too long to fit on a page == # == Parameters for error handling for content too long to fit on a page ==
FRAME_MAX_HEIGHT = 500 # 650 # Ad hoc value for a A4 page FRAME_MAX_HEIGHT = 200*mm # 500 # Ad hoc value for a A4 page
FRAME_MAX_WIDTH = 356 FRAME_MAX_WIDTH = 145*mm - INDENT_SIZE # 356
STR_TOO_LONG_WARNING = "<br/><b><font color=red>[Too long to fit on a single page. Cropped]</font></b>" STR_TOO_LONG_WARNING = "<br/><b><font color=red>[Too long to fit on a single page. Cropped]</font></b>"
# == Parameters for error handling for image too big to fit on a page == # == Parameters for error handling for image too big to fit on a page ==
FRAME_PICTURE_MAX_WIDTH = 88 * mm FRAME_PICTURE_MAX_HEIGHT = 200*mm # 195 * mm
FRAME_PICTURE_MAX_HEIGHT = 195 * mm FRAME_PICTURE_MAX_WIDTH = 145*mm - INDENT_SIZE # 88 * mm
# == Parameters for links management == # == Parameters for links management ==
LINK_TYPE = "link" # Name of the type that define 'good' links LINK_TYPE = "link" # Name of the type that define 'good' links
@ -254,7 +258,7 @@ def uuid_to_url(baseurl, uuid):
return baseurl + "events/view/" + uuid return baseurl + "events/view/" + uuid
def create_flowable_table_from_data(data, col_w=COL_WIDTHS, color_alternation=None, line_alternation=None): def create_flowable_table_from_data(data, col_w=COL_WIDTHS, color_alternation=None, line_alternation=None, galaxy_colors=False):
''' '''
Given a list of flowables items (2D/list of list), creates a Table with styles. Given a list of flowables items (2D/list of list), creates a Table with styles.
:param data: list of list of items (flowables is better) :param data: list of list of items (flowables is better)
@ -268,7 +272,7 @@ def create_flowable_table_from_data(data, col_w=COL_WIDTHS, color_alternation=No
# rowHeights=ROW_HEIGHT if you want a fixed height. /!\ Problems with paragraphs that are spreading everywhere # rowHeights=ROW_HEIGHT if you want a fixed height. /!\ Problems with paragraphs that are spreading everywhere
# Create styles and set parameters # Create styles and set parameters
alternate_colors_style = alternate_colors_style_generator(data,color_alternation) alternate_colors_style = alternate_colors_style_generator(data,color_alternation, galaxy_colors)
lines_style = lines_style_generator(data,line_alternation) lines_style = lines_style_generator(data,line_alternation)
general_style = general_style_generator() general_style = general_style_generator()
@ -278,7 +282,7 @@ def create_flowable_table_from_data(data, col_w=COL_WIDTHS, color_alternation=No
return curr_table return curr_table
def alternate_colors_style_generator(data, color_alternation): def alternate_colors_style_generator(data, color_alternation, galaxy_colors=True):
''' '''
Create a style, applicable on a table that will be built with parameter's data, with alternated Create a style, applicable on a table that will be built with parameter's data, with alternated
background color for each line. background color for each line.
@ -296,20 +300,20 @@ def alternate_colors_style_generator(data, color_alternation):
# For each line, generate a tuple giving to a line a color # For each line, generate a tuple giving to a line a color
for each in range(data_len): for each in range(data_len):
if each % 2 == 0: if each % 2 == 0:
bg_color = EVEN_COLOR bg_color = EVEN_COLOR if not galaxy_colors else EVEN_COLOR_GALAXY
else: else:
bg_color = ODD_COLOR bg_color = ODD_COLOR if not galaxy_colors else ODD_COLOR_GALAXY
color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color)) color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color))
else: else:
if data_len > len(color_alternation) : # if data_len > len(color_alternation) :
logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.") # logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.")
# For each line, generate a tuple giving to a line a color # For each line, generate a tuple giving to a line a color
for each in range(data_len): for each in range(data_len):
if color_alternation[each%len(color_alternation)] % 2 == 0: if color_alternation[each%len(color_alternation)] % 2 == 0:
bg_color = EVEN_COLOR bg_color = EVEN_COLOR if not galaxy_colors else EVEN_COLOR_GALAXY
else: else:
bg_color = ODD_COLOR bg_color = ODD_COLOR if not galaxy_colors else ODD_COLOR_GALAXY
color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color)) color_list.append(('BACKGROUND', (0, each), (-1, each), bg_color))
return color_list return color_list
@ -338,8 +342,8 @@ def lines_style_generator(data, line_alternation):
# Do nothing # Do nothing
return lines_list return lines_list
else: else:
if data_len > len(line_alternation) : # if data_len > len(line_alternation) :
logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.") # logger.warning("Line alternation for PDF display isn't correctly set. Looping on given values only.")
# For each line, generate a tuple giving to a line a color # For each line, generate a tuple giving to a line a color
for each in range(data_len): for each in range(data_len):
@ -784,7 +788,6 @@ class Value_Formatter():
tmp_text = "" tmp_text = ""
if is_safe_dict_attribute(misp_cluster, item[1]): if is_safe_dict_attribute(misp_cluster, item[1]):
print(misp_cluster[item[1]])
tmp_text += safe_string(misp_cluster[item[1]]) tmp_text += safe_string(misp_cluster[item[1]])
#if is_safe_dict_attribute(misp_cluster, item[3]) : #if is_safe_dict_attribute(misp_cluster, item[3]) :
@ -1020,7 +1023,10 @@ class Attributes():
# There is some attributes for this object # There is some attributes for this object
for item in getattr(misp_event, "Attribute"): for item in getattr(misp_event, "Attribute"):
# you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm)) # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
flowable_table.append(Indenter(left=INDENT_SIZE_HEADING))
flowable_table.append(Paragraph("Attribute #" + str(i+OFFSET), self.sample_style_sheet['Heading4'])) flowable_table.append(Paragraph("Attribute #" + str(i+OFFSET), self.sample_style_sheet['Heading4']))
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
flowable_table += self.create_flowable_table_from_one_attribute(item) flowable_table += self.create_flowable_table_from_one_attribute(item)
i += 1 i += 1
else: else:
@ -1092,11 +1098,14 @@ class Attributes():
flowable_table.append(create_flowable_table_from_data(data)) flowable_table.append(create_flowable_table_from_data(data))
# Galaxies # Galaxies
item = ["Related Galaxies", 'Galaxy', "None"] item = ["Related Galaxies", 'Galaxy', "None"]
if is_safe_attribute_table(misp_attribute, item[1]) : if is_safe_attribute_table(misp_attribute, item[1]) :
curr_Galaxy = Galaxy(self.config, self.value_formatter) curr_Galaxy = Galaxy(self.config, self.value_formatter)
flowable_table.append(Indenter(left=INDENT_SIZE))
flowable_table += curr_Galaxy.get_galaxy_value(misp_attribute, item) flowable_table += curr_Galaxy.get_galaxy_value(misp_attribute, item)
flowable_table.append(Indenter(left=-INDENT_SIZE))
return flowable_table return flowable_table
@ -1234,7 +1243,9 @@ class Object():
# There is a list of objects # There is a list of objects
for item in getattr(misp_event, "Object"): for item in getattr(misp_event, "Object"):
# you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm)) # you can use a spacer instead of title to separate paragraph: flowable_table.append(Spacer(1, 5 * mm))
flowable_table.append(Indenter(left=INDENT_SIZE_HEADING))
flowable_table.append(Paragraph("Object #" + str(i+OFFSET), self.sample_style_sheet['Heading3'])) flowable_table.append(Paragraph("Object #" + str(i+OFFSET), self.sample_style_sheet['Heading3']))
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
flowable_table += self.create_flowable_table_from_one_object(item, config) flowable_table += self.create_flowable_table_from_one_object(item, config)
i += 1 i += 1
else: else:
@ -1281,7 +1292,9 @@ class Object():
# Handle all the attributes # Handle all the attributes
if is_safe_attribute(misp_object, "Attribute"): if is_safe_attribute(misp_object, "Attribute"):
curr_attributes = Attributes(self.config, self.value_formatter) curr_attributes = Attributes(self.config, self.value_formatter)
data.append(Indenter(left=INDENT_SIZE))
data += curr_attributes.create_flowable_table_from_attributes(misp_object) data += curr_attributes.create_flowable_table_from_attributes(misp_object)
data.append(Indenter(left=-INDENT_SIZE))
# Add a page break at the end of an object # Add a page break at the end of an object
data.append(PageBreak()) data.append(PageBreak())
@ -1317,7 +1330,9 @@ class Galaxy():
if is_safe_attribute_table(misp_event, item[1]) and is_in_config(self.config, 3): if is_safe_attribute_table(misp_event, item[1]) and is_in_config(self.config, 3):
galaxy_title = Paragraph(item[0], self.sample_style_sheet['Heading5']) galaxy_title = Paragraph(item[0], self.sample_style_sheet['Heading5'])
flowable_table.append(Indenter(left=INDENT_SIZE_HEADING))
flowable_table.append(galaxy_title) flowable_table.append(galaxy_title)
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
flowable_table += self.create_flowable_table_from_galaxies(misp_event) flowable_table += self.create_flowable_table_from_galaxies(misp_event)
else : else :
flowable_table.append(self.value_formatter.get_unoverflowable_paragraph(item[2])) flowable_table.append(self.value_formatter.get_unoverflowable_paragraph(item[2]))
@ -1344,7 +1359,9 @@ class Galaxy():
txt_title = "Galaxy #" + str(i+OFFSET) + " - " + safe_string(curr_galaxy["name"]) txt_title = "Galaxy #" + str(i+OFFSET) + " - " + safe_string(curr_galaxy["name"])
galaxy_title = Paragraph(txt_title, self.sample_style_sheet['Heading6']) galaxy_title = Paragraph(txt_title, self.sample_style_sheet['Heading6'])
flowable_table.append(Indenter(left=INDENT_SIZE_HEADING))
flowable_table.append(galaxy_title) flowable_table.append(galaxy_title)
flowable_table.append(Indenter(left=-INDENT_SIZE_HEADING))
i += 1 i += 1
# Add metadata about the Galaxy # Add metadata about the Galaxy
@ -1430,7 +1447,7 @@ class Galaxy_cluster():
# For each cluster # For each cluster
tmp_data = self.create_flowable_table_from_one_galaxy_cluster(curr_cluster) tmp_data = self.create_flowable_table_from_one_galaxy_cluster(curr_cluster)
tmp_flowable_table = [] tmp_flowable_table = []
tmp_flowable_table.append(create_flowable_table_from_data(tmp_data, col_w=SECOND_LEVEL_GALAXY_WIDTHS, color_alternation = CLUSTER_COLORS, line_alternation=[])) tmp_flowable_table.append(create_flowable_table_from_data(tmp_data, col_w=SECOND_LEVEL_GALAXY_WIDTHS, color_alternation = CLUSTER_COLORS, line_alternation=[], galaxy_colors=True))
data.append([self.value_formatter.get_col1_paragraph(item[0], do_small=DO_SMALL_GALAXIES), tmp_flowable_table]) # Cluster #X - 3 lines data.append([self.value_formatter.get_col1_paragraph(item[0], do_small=DO_SMALL_GALAXIES), tmp_flowable_table]) # Cluster #X - 3 lines
else: else:
@ -1438,7 +1455,7 @@ class Galaxy_cluster():
data = [self.value_formatter.get_unoverflowable_paragraph("No galaxy cluster", do_small=DO_SMALL_GALAXIES)] data = [self.value_formatter.get_unoverflowable_paragraph("No galaxy cluster", do_small=DO_SMALL_GALAXIES)]
flowable_table = [] flowable_table = []
flowable_table.append(create_flowable_table_from_data(data, col_w=FIRST_LEVEL_GALAXY_WIDTHS, color_alternation = CLUSTER_COLORS)) flowable_table.append(create_flowable_table_from_data(data, col_w=FIRST_LEVEL_GALAXY_WIDTHS, color_alternation = CLUSTER_COLORS, galaxy_colors=True))
return flowable_table return flowable_table