def expect_column_values_to_not_be_null(cls, ge_object): return [[ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Missing (n)", "tooltip": { "content": "expect_column_values_to_not_be_null" } } }), ge_object["result"]["unexpected_count"] if "unexpected_count" in ge_object["result"] and ge_object["result"]["unexpected_count"] is not None else "--", ], [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Missing (%)", "tooltip": { "content": "expect_column_values_to_not_be_null" } } }), "%.1f%%" % (ge_object["result"]["unexpected_percent"] * 100.0) if "unexpected_percent" in ge_object["result"] and ge_object["result"]["unexpected_percent"] is not None else "--", ]]
def _get_unexpected_statement(cls, evr): success = evr["success"] result = evr.get("result", {}) if ("expectation_config" in evr and "exception_info" in evr and evr["exception_info"]["raised_exception"] is True): template_str = "\n\n$expectation_type raised an exception:\n$exception_message" return RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": template_str, "params": { "expectation_type": evr["expectation_config"]["expectation_type"], "exception_message": evr["exception_info"]["exception_message"] }, "tag": "strong", "styling": { "classes": ["text-danger"], "params": { "exception_message": { "tag": "code" }, "expectation_type": { "classes": ["badge", "badge-danger", "mb-2"] } } } }, }) if success or not result.get("unexpected_count"): return None else: unexpected_count = result["unexpected_count"] unexpected_percent = "%.2f%%" % (result["unexpected_percent"]) element_count = result["element_count"] template_str = "\n\n$unexpected_count unexpected values found. " \ "$unexpected_percent of $element_count total rows." return RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": template_str, "params": { "unexpected_count": unexpected_count, "unexpected_percent": unexpected_percent, "element_count": element_count }, "tag": "strong", "styling": { "classes": ["text-danger"] } } })
def _render_dataset_info(cls, evrs, content_blocks): expect_table_row_count_to_be_between_evr = cls._find_evr_by_type( evrs['results'], "expect_table_row_count_to_be_between") table_rows = [] table_rows.append([ "Number of variables", len(cls._get_column_list_from_evrs(evrs)), ]) table_rows.append([ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Number of observations", "tooltip": { "content": "expect_table_row_count_to_be_between" }, "params": { "tooltip_text": "Number of observations" } } }), #??? : What is this? # "?" if not expect_table_row_count_to_be_between_evr else expect_table_row_count_to_be_between_evr["result"][ # "observed_value"] ]) table_rows += [ [ "Missing cells", cls._get_percentage_missing_cells_str(evrs), ], # "866 (8.1%)" # ["Duplicate rows", "0 (0.0%)", ], #TODO: bring back when we have an expectation for this ] content_blocks.append( RenderedComponentContent( **{ "content_block_type": "table", "header": "Dataset info", "table": table_rows, "styling": { "classes": ["col-6", "table-responsive"], "styles": { "margin-top": "20px" }, "body": { "classes": ["table", "table-sm"] } }, }))
def _get_status_icon(cls, evr): if evr["exception_info"]["raised_exception"]: return RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": "$icon", "params": {"icon": ""}, "styling": { "params": { "icon": { "classes": ["fas", "fa-exclamation-triangle", "text-warning"], "tag": "i" } } } } }) if evr["success"]: return RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": "$icon", "params": {"icon": ""}, "styling": { "params": { "icon": { "classes": ["fas", "fa-check-circle", "text-success"], "tag": "i" } } } } }) else: return RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": "$icon", "params": {"icon": ""}, "styling": { "params": { "icon": { "tag": "i", "classes": ["fas", "fa-times", "text-danger"] } } } } })
def _missing_content_block_fn(cls, evr, styling=None, include_column_name=True): # Only render EVR objects for which an exception was raised if ("expectation_config" in evr and "exception_info" in evr and evr["exception_info"]["raised_exception"] is True): template_str = "$expectation_type raised an exception: $exception_message" if include_column_name: template_str = "$column: " + template_str try: column = evr["expectation_config"]["kwargs"]["column"] except KeyError: column = None return [RenderedComponentContent(**{ "content_block_type": "string_template", "string_template": { "template": template_str, "params": { "column": column, "expectation_type": evr["expectation_config"]["expectation_type"], "exception_message": evr["exception_info"]["exception_message"] }, "styling": styling, } })]
def _get_table_content_block(cls, header="", subheader="", col=12): return RenderedComponentContent( **{ "content_block_type": "table", "header": header, "subheader": subheader, "table": [ ["", "column_1", "column_2"], [ "row_1", cls._get_bullet_list_content_block( subheader="Nested Bullet List Content Block"), "buffalo" ], ["row_2", "crayon", "derby"], ], "styling": { "classes": ["col-{}".format(col), "table-responsive"], "styles": { "margin-top": "20px" }, "body": { "classes": ["table", "table-sm"] } }, })
def _get_value_list_content_block(cls, header="", subheader="", col=12): return RenderedComponentContent( **{ "content_block_type": "value_list", "header": header, "subheader": subheader, "value_list": [{ "content_block_type": "string_template", "string_template": { "template": "$value", "params": { "value": value }, "styling": { "default": { "classes": ["badge", "badge-info"], }, } } } for value in ["Andrew", "Elijah", "Matthew", "Cindy", "Pam"] ], "styling": { "classes": ["col-{}".format(col)] }, })
def _render_variable_types(cls, evrs, content_blocks): column_types = cls._get_column_types(evrs) #TODO: check if we have the information to make this statement. Do all columns have type expectations? column_type_counter = Counter(column_types.values()) table_rows = [[type, str(column_type_counter[type])] for type in ["int", "float", "string", "unknown"]] content_blocks.append( RenderedComponentContent( **{ "content_block_type": "table", "header": "Variable types", "table": table_rows, "styling": { "classes": [ "col-6", "table-responsive", ], "styles": { "margin-top": "20px" }, "body": { "classes": ["table", "table-sm"] } }, }))
def test_render_header_component(): header_component_content = RenderedComponentContent( **{ # "component_type": "header", "content_block_type": "header", "header": "Overview", }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": header_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == \ """ <div id="section-1-content-block-2" > <div id="section-1-content-block-2-header" > <h3> Overview </h3></div> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def test_render_value_list(): value_list_component_content = RenderedComponentContent(**{ 'content_block_type': 'value_list', 'header': 'Example values', 'value_list': [{ 'content_block_type': 'string_template', 'string_template': { 'template': '$value', 'params': {'value': '0'}, 'styling': {'default': {'classes': ['badge', 'badge-info']}} } }, { 'content_block_type': 'string_template', 'string_template': { 'template': '$value', 'params': {'value': '1'}, 'styling': {'default': {'classes': ['badge', 'badge-info']}} } }], 'styling': { 'classes': ['col-4'], 'styles': {'margin-top': '20px'} } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper(**{ "content_block": value_list_component_content, "section_loop": {"index": 1}, "content_block_loop": {"index": 2}, }) ) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == """ <div id="section-1-content-block-2" class="col-4" style="margin-top:20px;" > <div id="section-1-content-block-2-header" > <h4> Example values </h4></div> <p id="section-1-content-block-2-body" > <span > <span class="badge badge-info" >0</span> </span> <span > <span class="badge badge-info" >1</span> </span> </p> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def _get_unexpected_statement(cls, evr): success = evr["success"] try: result = evr["result"] except KeyError: return RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Expectation failed to execute.", "params": {}, "tag": "strong", "styling": { "classes": ["text-warning"] } } }) if success or not result.get("unexpected_count"): return None else: unexpected_count = result["unexpected_count"] unexpected_percent = "%.2f%%" % (result["unexpected_percent"] * 100.0) element_count = result["element_count"] template_str = "\n\n$unexpected_count unexpected values found. $unexpected_percent of $element_count total rows." return RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": template_str, "params": { "unexpected_count": unexpected_count, "unexpected_percent": unexpected_percent, "element_count": element_count }, "tag": "strong", "styling": { "classes": ["text-danger"] } } })
def test_ExpectationSuiteColumnSectionRenderer_render_header( titanic_profiled_name_column_expectations): remaining_expectations, content_blocks = ExpectationSuiteColumnSectionRenderer._render_header( titanic_profiled_name_column_expectations, #["expectations"], [], ) print(json.dumps(content_blocks, indent=2)) assert content_blocks == [ RenderedComponentContent( **{ "content_block_type": "header", "header": "Name", "styling": { "classes": ["col-12"], "header": { "classes": ["alert", "alert-secondary"] } } }) ] expectation_with_unescaped_dollar_sign = { "expectation_type": "expect_column_values_to_be_in_type_list", "kwargs": { "column": "Car Insurance Premiums ($)", "type_list": [ "DOUBLE_PRECISION", "DoubleType", "FLOAT", "FLOAT4", "FLOAT8", "FloatType", "NUMERIC", "float" ], "result_format": "SUMMARY" }, "meta": { "BasicDatasetProfiler": { "confidence": "very low" } } } remaining_expectations, content_blocks = ExpectationSuiteColumnSectionRenderer._render_header( [expectation_with_unescaped_dollar_sign], []) print(content_blocks) assert content_blocks[0] == { 'content_block_type': 'header', 'header': 'Car Insurance Premiums ($$)', 'styling': { 'classes': ['col-12'], 'header': { 'classes': ['alert', 'alert-secondary'] } } }
def test_render_graph(): graph_component_content = RenderedComponentContent( **{ "content_block_type": "graph", "header": "Histogram", "graph": "{\"$schema\": \"https://vega.github.io/schema/vega-lite/v2.6.0.json\", \"autosize\": \"fit\", \"config\": {\"view\": {\"height\": 300, \"width\": 400}}, \"data\": {\"name\": \"data-a681d02fb484e64eadd9721b37015d5b\"}, \"datasets\": {\"data-a681d02fb484e64eadd9721b37015d5b\": [{\"bins\": 3.7, \"weights\": 5.555555555555555}, {\"bins\": 10.8, \"weights\": 3.439153439153439}, {\"bins\": 17.9, \"weights\": 17.857142857142858}, {\"bins\": 25.0, \"weights\": 24.206349206349206}, {\"bins\": 32.0, \"weights\": 16.137566137566136}, {\"bins\": 39.1, \"weights\": 12.3015873015873}, {\"bins\": 46.2, \"weights\": 9.788359788359788}, {\"bins\": 53.3, \"weights\": 5.423280423280423}, {\"bins\": 60.4, \"weights\": 3.439153439153439}, {\"bins\": 67.5, \"weights\": 1.8518518518518516}]}, \"encoding\": {\"x\": {\"field\": \"bins\", \"type\": \"ordinal\"}, \"y\": {\"field\": \"weights\", \"type\": \"quantitative\"}}, \"height\": 200, \"mark\": \"bar\", \"width\": 200}", "styling": { "classes": ["col-4"] } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": graph_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == """ <div id="section-1-content-block-2" class="col-4" > <div id="section-1-content-block-2-header" > <h4> Histogram </h4> </div> <div class="show-scrollbars"> <div id="section-1-content-block-2-graph" ></div> </div> <script> // Assign the specification to a local variable vlSpec. vlSpec = {"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json", "autosize": "fit", "config": {"view": {"height": 300, "width": 400}}, "data": {"name": "data-a681d02fb484e64eadd9721b37015d5b"}, "datasets": {"data-a681d02fb484e64eadd9721b37015d5b": [{"bins": 3.7, "weights": 5.555555555555555}, {"bins": 10.8, "weights": 3.439153439153439}, {"bins": 17.9, "weights": 17.857142857142858}, {"bins": 25.0, "weights": 24.206349206349206}, {"bins": 32.0, "weights": 16.137566137566136}, {"bins": 39.1, "weights": 12.3015873015873}, {"bins": 46.2, "weights": 9.788359788359788}, {"bins": 53.3, "weights": 5.423280423280423}, {"bins": 60.4, "weights": 3.439153439153439}, {"bins": 67.5, "weights": 1.8518518518518516}]}, "encoding": {"x": {"field": "bins", "type": "ordinal"}, "y": {"field": "weights", "type": "quantitative"}}, "height": 200, "mark": "bar", "width": 200}; // Embed the visualization in the container with id `vis` vegaEmbed('#section-1-content-block-2-graph', vlSpec, { actions: false }).then(result=>console.log(result)).catch(console.warn); </script> </div> """.replace(" ", "").replace("\t", "").replace("\n", "")
def _render_header(cls, evrs, content_blocks): content_blocks.append( RenderedComponentContent( **{ "content_block_type": "header", "header": "Overview", "styling": { "classes": [ "col-12", ], "header": { "classes": ["alert", "alert-secondary"] } } }))
def test_render_table_component(): table_component_content = RenderedComponentContent( **{ # "component_type": "header", "content_block_type": "table", "header": "Overview", "table": [ ["Mean", "446"], ["Minimum", "1"], ], "styling": { "classes": ["col-4"], } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": table_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == \ """ <div id="section-1-content-block-2" class="col-4" > <div id="section-1-content-block-2-header" > <h4> Overview </h4> </div> <table id="section-1-content-block-2-body" > <tr> <td id="section-1-content-block-2-cell-1-1" ><div class="table-cell-frame"><span>Mean</span></div></td><td id="section-1-content-block-2-cell-1-2" ><div class="table-cell-frame"><span>446</span></div></td></tr><tr> <td id="section-1-content-block-2-cell-2-1" ><div class="table-cell-frame"><span>Minimum</span></div></td><td id="section-1-content-block-2-cell-2-2" ><div class="table-cell-frame"><span>1</span></div></td></tr></table> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def expect_column_unique_value_count_to_be_between(cls, ge_object): observed_value = ge_object["result"]["observed_value"] return [[ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Distinct (n)", "tooltip": { "content": "expect_column_unique_value_count_to_be_between" } } }), observed_value, ]]
def _get_unexpected_table(cls, evr): try: result = evr["result"] except KeyError: return None if not result.get("partial_unexpected_list") and not result.get( "partial_unexpected_counts"): return None table_rows = [] if result.get("partial_unexpected_counts"): header_row = ["Unexpected Value", "Count"] for unexpected_count in result.get("partial_unexpected_counts"): if unexpected_count.get("value"): table_rows.append([ unexpected_count.get("value"), unexpected_count.get("count") ]) elif unexpected_count.get("value") == "": table_rows.append(["EMPTY", unexpected_count.get("count")]) else: table_rows.append(["null", unexpected_count.get("count")]) else: header_row = ["Unexpected Value"] for unexpected_value in result.get("partial_unexpected_list"): if unexpected_value: table_rows.append([unexpected_value]) elif unexpected_value == "": table_rows.append(["EMPTY"]) else: table_rows.append(["null"]) unexpected_table_content_block = RenderedComponentContent( **{ "content_block_type": "table", "table": table_rows, "header_row": header_row, "styling": { "body": { "classes": ["table-bordered", "table-sm", "mt-3"] } } }) return unexpected_table_content_block
def _get_string_template_content_block(cls): return RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$icon This is a Font Awesome Icon, using a param-level custom tag\n$red_text\n$bold_serif", "params": { "icon": "", "red_text": "And this is red text!", "bold_serif": "And this is big, bold serif text using style attribute..." }, "styling": { "params": { "icon": { "classes": ["fas", "fa-check-circle", "text-success"], "tag": "i" }, "red_text": { "classes": ["text-danger"] }, "bold_serif": { "styles": { "font-size": "22px", "font-weight": "bold", "font-family": "serif" } } } } }, "styling": { "classes": ["col-12"], "styles": { "margin-top": "20px" }, }, })
def _get_header_content_block(cls, header="", subheader="", highlight=True): return RenderedComponentContent( **{ "content_block_type": "header", "header": { "template": header }, "subheader": subheader, "styling": { "classes": ["col-12"], "header": { "classes": ["alert", "alert-secondary"] if highlight else [] } } })
def _get_tooltip_string_template_content_block(cls): return RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "This is a string template with tooltip, using a top-level custom tag.", "tag": "code", "tooltip": { "content": "This is the tooltip content." } }, "styling": { "classes": ["col-12"], "styles": { "margin-top": "20px" }, }, })
def expect_column_proportion_of_unique_values_to_be_between( cls, ge_object): observed_value = ge_object["result"]["observed_value"] template_string_object = RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "Distinct (%)", "tooltip": { "content": "expect_column_proportion_of_unique_values_to_be_between" } } }) if not observed_value: return [[template_string_object, "--"]] else: return [[ template_string_object, "%.1f%%" % (100 * observed_value) ]]
def _get_bullet_list_content_block(cls, header="", subheader="", col=12): return RenderedComponentContent( **{ "content_block_type": "bullet_list", "header": header, "subheader": subheader, "bullet_list": [ "Aenean porttitor turpis.", "Curabitur ligula urna.", cls._get_header_content_block( header="nested header content block", subheader="subheader", highlight=False) ], "styling": { "classes": ["col-{}".format(col)], "styles": { "margin-top": "20px" }, }, })
def render(cls, ge_object, header_row=[]): """Each expectation method should return a list of rows""" if isinstance(ge_object, list): table_entries = [] for sub_object in ge_object: expectation_type = cls._get_expectation_type(sub_object) extra_rows_fn = getattr(cls, expectation_type, None) if extra_rows_fn is not None: rows = extra_rows_fn(sub_object) table_entries.extend(rows) else: table_entries = [] expectation_type = cls._get_expectation_type(ge_object) extra_rows_fn = getattr(cls, expectation_type, None) if extra_rows_fn is not None: rows = extra_rows_fn(ge_object) table_entries.extend(rows) return RenderedComponentContent( **{ "content_block_type": "table", "header_row": header_row, "table": table_entries })
def _get_graph_content_block(cls, header="", subheader="", col=12): df = pd.DataFrame({ "value": [1, 2, 3, 4, 5, 6], "count": [123, 232, 543, 234, 332, 888] }) bars = alt.Chart(df).mark_bar(size=20).encode( y='count:Q', x="value:O").properties(height=200, width=200, autosize="fit") chart = bars.to_json() return RenderedComponentContent( **{ "content_block_type": "graph", "header": header, "subheader": subheader, "graph": chart, "styling": { "classes": ["col-{}".format(col)], "styles": { "margin-top": "20px" }, }, })
def _render_expectation_types(cls, evrs, content_blocks): type_counts = defaultdict(int) for evr in evrs["results"]: type_counts[evr["expectation_config"]["expectation_type"]] += 1 # table_rows = sorted(type_counts.items(), key=lambda kv: -1*kv[1]) bullet_list = sorted(type_counts.items(), key=lambda kv: -1 * kv[1]) bullet_list = [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$expectation_type $expectation_count", "params": { "expectation_type": tr[0], "expectation_count": tr[1], }, "styling": { "classes": [ "list-group-item", "d-flex", "justify-content-between", "align-items-center" ], "params": { "expectation_count": { "classes": ["badge", "badge-secondary", "badge-pill"], } } } } }) for tr in bullet_list ] content_blocks.append( RenderedComponentContent( **{ "content_block_type": "bullet_list", "header": 'Expectation types <span class="mr-3 triangle"></span>', "bullet_list": bullet_list, "styling": { "classes": ["col-12"], "styles": { "margin-top": "20px" }, "header": { "classes": ["collapsed"], "attributes": { "data-toggle": "collapse", "href": "#{{content_block_id}}-body", "aria-expanded": "true", "aria-controls": "collapseExample", }, "styles": { "cursor": "pointer" } }, "body": { "classes": ["list-group", "collapse"], }, }, }))
def test_render_section_page(): section = RenderedSectionContent( **{ "section_name": None, "content_blocks": [ RenderedComponentContent(**{ "content_block_type": "header", "header": "Overview", }), RenderedComponentContent( **{ "content_block_type": "table", "header": "Dataset info", "table": [ ["Number of variables", "12"], ["Number of observations", "891"], ], "styling": { "classes": ["col-6", "table-responsive"], "styles": { "margin-top": "20px" }, "body": { "classes": ["table", "table-sm"] } } }) ] }) rendered_doc = ge.render.view.view.DefaultJinjaSectionView().render( RenderedComponentContentWrapper(**{ "section": section, "section_loop": { "index": 1 }, })) #.replace(" ", "").replace("\t", "").replace("\n", "") print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == """<div id="section-1" class="ge-section container-fluid"> <div class="row"> <div id="content-block-1" > <div id="content-block-1-header" > <h3> Overview </h3></div> </div> <div id="content-block-2" class="col-6 table-responsive" style="margin-top:20px;" > <div id="content-block-2-header" > <h4> Dataset info </h4> </div> <table id="content-block-2-body" class="table table-sm" > <tr> <td id="content-block-2-cell-1-1" ><div class="table-cell-frame"><span>Number of variables</span></div></td><td id="content-block-2-cell-1-2" ><div class="table-cell-frame"><span>12</span></div></td></tr><tr> <td id="content-block-2-cell-2-1" ><div class="table-cell-frame"><span>Number of observations</span></div></td><td id="content-block-2-cell-2-2" ><div class="table-cell-frame"><span>891</span></div></td></tr></table> </div> </div> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def test_render_text(): text_component_content = RenderedComponentContent( **{ "content_block_type": "text", "header": "Histogram", "content": ["hello"], "styling": { "classes": ["col-4"] } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": text_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == """ <div id="section-1-content-block-2" class="col-4" > <div id="section-1-content-block-2-header" > <h4> Histogram </h4></div> <div id="section-1-content-block-2-body" > <p>hello</p> </div> </div>""".replace(" ", "").replace("\t", "").replace("\n", "") text_component_content = RenderedComponentContent( **{ "content_block_type": "text", "header": "Histogram", "content": ["hello", "goodbye"], "styling": { "classes": ["col-4"] } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": text_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == """ <div id="section-1-content-block-2" class="col-4" > <div id="section-1-content-block-2-header" > <h4> Histogram </h4></div> <div id="section-1-content-block-2-body" > <p>hello</p> <p>goodbye</p> </div> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def test_rendering_components_with_styling(): # Medium-complicated example to verify that all the things are correctly piped to all the places header_component_content = RenderedComponentContent( **{ # "component_type": "table", "content_block_type": "table", "header": { "template": "$var1 $var2 $var3", "params": { "var1": "AAA", "var2": "BBB", "var3": "CCC", }, "styling": { "default": { "classes": ["x"] }, "params": { "var1": { "classes": ["y"] } } } }, "subheader": { "template": "$var1 $var2 $var3", "params": { "var1": "aaa", "var2": "bbb", "var3": "ccc", }, "styling": { "default": { "classes": ["xx"] }, "params": { "var1": { "classes": ["yy"] } } } }, "table": [ ["Mean", "446"], ["Minimum", "1"], ], "styling": { "classes": ["root_foo"], "styles": { "root": "bar" }, "attributes": { "root": "baz" }, "header": { "classes": ["header_foo"], "styles": { "header": "bar" }, "attributes": { "header": "baz" }, }, "subheader": { "classes": ["subheader_foo"], "styles": { "subheader": "bar" }, "attributes": { "subheader": "baz" }, }, "body": { "classes": ["body_foo"], "styles": { "body": "bar" }, "attributes": { "body": "baz" }, } } }) rendered_doc = ge.render.view.view.DefaultJinjaComponentView().render( RenderedComponentContentWrapper( **{ "content_block": header_component_content, "section_loop": { "index": 1 }, "content_block_loop": { "index": 2 }, })) print(rendered_doc) rendered_doc = rendered_doc.replace(" ", "").replace("\t", "").replace("\n", "") assert rendered_doc == \ """ <div id="section-1-content-block-2" class="root_foo" root="baz" style="root:bar;" > <div id="section-1-content-block-2-header" class="header_foo" header="baz" style="header:bar;" > <h4> <span > <span class="y" >AAA</span> <span class="x" >BBB</span> <span class="x" >CCC</span> </span> </h4> <h5 id="section-1-content-block-2-subheader" class="subheader_foo" subheader="baz" style="subheader:bar;" > <span > <span class="yy" >aaa</span> <span class="xx" >bbb</span> <span class="xx" >ccc</span> </span> </h5> </div> <table id="section-1-content-block-2-body" class="body_foo" body="baz" style="body:bar;" > <tr> <td id="section-1-content-block-2-cell-1-1" ><div class="table-cell-frame"><span>Mean</span></div></td><td id="section-1-content-block-2-cell-1-2" ><div class="table-cell-frame"><span>446</span></div></td></tr><tr> <td id="section-1-content-block-2-cell-2-1" ><div class="table-cell-frame"><span>Minimum</span></div></td><td id="section-1-content-block-2-cell-2-2" ><div class="table-cell-frame"><span>1</span></div></td></tr></table> </div>""".replace(" ", "").replace("\t", "").replace("\n", "")
def _generate_data_asset_table_section(cls, data_asset_name, link_lists_dict): section_rows = [] column_count = 1 profiling_links = link_lists_dict["profiling_links"] if profiling_links: column_count += 1 validation_links = link_lists_dict["validation_links"] if validation_links: column_count += 1 expectation_suite_links = link_lists_dict["expectation_suite_links"] if expectation_suite_links: column_count += 1 cell_width_pct = 100.0 / column_count first_row = [] rowspan = str( len(expectation_suite_links)) if expectation_suite_links else "1" data_asset_name = RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$data_asset", "params": { "data_asset": data_asset_name }, "tag": "blockquote", "styling": { "params": { "data_asset": { "classes": ["blockquote"], } } } }, "styling": { "classes": ["col-sm-3", "col-xs-12", "pl-sm-5", "pl-xs-0"], "styles": { "margin-top": "10px", "word-break": "break-all" }, "parent": { "styles": { "width": "{}%".format(cell_width_pct) }, "attributes": { "rowspan": rowspan } } } }) first_row.append(data_asset_name) if profiling_links: profiling_results_bullets = [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": link_dict["expectation_suite_name"] + "-ProfilingResults" }, "tag": "a", "styling": { "attributes": { "href": link_dict["filepath"] } } } }) for link_dict in profiling_links ] profiling_results_bullet_list = RenderedComponentContent( **{ "content_block_type": "bullet_list", "bullet_list": profiling_results_bullets, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct), }, "attributes": { "rowspan": rowspan } } } }) first_row.append(profiling_results_bullet_list) if expectation_suite_links: expectation_suite_link_dict = expectation_suite_links[0] expectation_suite_name = expectation_suite_link_dict[ "expectation_suite_name"] expectation_suite_link = RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": expectation_suite_name }, "tag": "a", "styling": { "attributes": { "href": expectation_suite_link_dict["filepath"] }, } }, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct), } } } }) first_row.append(expectation_suite_link) if validation_links: validation_link_bullets = [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": link_dict["run_id"] }, "tag": "a", "styling": { "attributes": { "href": link_dict["filepath"] } } } }) for link_dict in validation_links if link_dict["expectation_suite_name"] == expectation_suite_name ] validation_link_bullet_list = RenderedComponentContent( **{ "content_block_type": "bullet_list", "bullet_list": validation_link_bullets, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct) } }, "body": { "styles": { "max-height": "15em", "overflow": "scroll" } } } }) first_row.append(validation_link_bullet_list) if not expectation_suite_links and validation_links: validation_link_bullets = [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": link_dict["run_id"] }, "tag": "a", "styling": { "attributes": { "href": link_dict["filepath"] } } } }) for link_dict in validation_links ] validation_link_bullet_list = RenderedComponentContent( **{ "content_block_type": "bullet_list", "bullet_list": validation_link_bullets, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct) } }, "body": { "styles": { "max-height": "15em", "overflow": "scroll" } } } }) first_row.append(validation_link_bullet_list) section_rows.append(first_row) if len(expectation_suite_links) > 1: for expectation_suite_link_dict in expectation_suite_links[1:]: expectation_suite_row = [] expectation_suite_name = expectation_suite_link_dict[ "expectation_suite_name"] expectation_suite_link = RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": expectation_suite_name }, "tag": "a", "styling": { "attributes": { "href": expectation_suite_link_dict["filepath"] }, } }, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct), } } } }) expectation_suite_row.append(expectation_suite_link) if validation_links: validation_link_bullets = [ RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "$link_text", "params": { "link_text": link_dict["run_id"] }, "tag": "a", "styling": { "attributes": { "href": link_dict["filepath"] } } } }) for link_dict in validation_links if link_dict["expectation_suite_name"] == expectation_suite_name ] validation_link_bullet_list = RenderedComponentContent( **{ "content_block_type": "bullet_list", "bullet_list": validation_link_bullets, "styling": { "parent": { "styles": { "width": "{}%".format(cell_width_pct) } }, "body": { "styles": { "max-height": "15em", "overflow": "scroll" } } } }) expectation_suite_row.append(validation_link_bullet_list) section_rows.append(expectation_suite_row) return section_rows
def render(cls, index_links_dict): sections = [] for source, generators in index_links_dict.items(): content_blocks = [] # datasource header source_header_block = RenderedComponentContent( **{ "content_block_type": "header", "header": source, "styling": { "classes": ["col-12"], "header": { "classes": ["alert", "alert-secondary"] } } }) content_blocks.append(source_header_block) # generator header for generator, data_assets in generators.items(): generator_header_block = RenderedComponentContent( **{ "content_block_type": "header", "header": generator, "styling": { "classes": ["col-12", "ml-4"], } }) content_blocks.append(generator_header_block) horizontal_rule = RenderedComponentContent( **{ "content_block_type": "string_template", "string_template": { "template": "", "params": {}, "tag": "hr" }, "styling": { "classes": ["col-12"], } }) content_blocks.append(horizontal_rule) generator_table_rows = [] generator_table_header_row = ["Data Asset"] header_dict = OrderedDict( [["profiling_links", "Profiling Results"], ["expectation_suite_links", "Expectation Suite"], ["validation_links", "Validation Results"]]) for link_lists_key, header in header_dict.items(): for data_asset, link_lists in data_assets.items(): if header in generator_table_header_row: continue if link_lists[link_lists_key]: generator_table_header_row.append(header) generator_table = RenderedComponentContent( **{ "content_block_type": "table", "header_row": generator_table_header_row, "table": generator_table_rows, "styling": { "classes": ["col-12"], "styles": { "margin-top": "10px" }, "body": { "classes": ["table", "table-sm"] } } }) # data_assets for data_asset, link_lists in data_assets.items(): generator_table_rows += cls._generate_data_asset_table_section( data_asset, link_lists) content_blocks.append(generator_table) section = RenderedSectionContent(**{ "section_name": source, "content_blocks": content_blocks }) sections.append(section) return RenderedDocumentContent(**{ "utm_medium": "index-page", "sections": sections })