diff --git a/pytabular/culture.py b/pytabular/culture.py
index 3781e23..59fa5cb 100644
--- a/pytabular/culture.py
+++ b/pytabular/culture.py
@@ -17,12 +17,21 @@ def __init__(self, object, model) -> None:
self.ObjectTranslations = self.set_translation()
def set_translation(self) -> List[dict]:
- """Based on the culture, it creates a list of dicts with available translations."""
+ """Based on the culture, it creates a list of dicts with available translations.
+
+ The model object doesn't have a Parent object. So that will stay
+ empty.
+
+ Returns:
+ List[dict]: Translations per object.
+ """
return [
{
"object_translation": translation.Value,
"object_name": translation.Object.Name,
- "object_parent_name": translation.Object.Parent.Name,
+ "object_parent_name": translation.Object.Parent.Name
+ if translation.Object.Parent
+ else "",
"object_type": str(translation.Property),
}
for translation in self._object.ObjectTranslations
@@ -35,9 +44,17 @@ def get_translation(
By default it will search for the "Caption" object type, due to fact that a
Display folder and Description can also have translations.
+
+ Args:
+ object_name (str): Object name that you want to translate.
+ object_parent_name (str): Parent Object name that you want to translate.
+ object_type (str, optional): The Display Folders can also have translations.
+ Defaults to "Caption" > Object translation.
+
+ Returns:
+ dict: With translation of the object.
"""
try:
- # Removed walrus operator so it can be compatible with with python versions before 3.8
translations = [
d
for d in self.ObjectTranslations
diff --git a/pytabular/document.py b/pytabular/document.py
index 1dc4b10..30bd6ed 100644
--- a/pytabular/document.py
+++ b/pytabular/document.py
@@ -6,12 +6,12 @@
from pathlib import Path
-from measure import PyMeasure
from table import PyTable
from column import PyColumn
from culture import PyCulture
-
-from .pytabular import Tabular
+from measure import PyMeasure
+from pytabular import Tabular
+from typing import List, Dict
logger = logging.getLogger("PyTabular")
@@ -67,7 +67,7 @@ def __init__(
# Translation information
self.culture_include: bool = False
self.culture_selected: str = "en-US"
- self.culture_object: PyCulture = None
+ self.culture_object: PyCulture
# Documentation Parts
self.general_page: str = str()
@@ -96,6 +96,14 @@ def create_object_reference(self, object: str, object_parent: str) -> str:
This is based on the technical names in the model,
so not the once in the translations. This makes it
possible to link based on dependencies.
+ (Scope is only Docusaurus)
+
+ Args:
+ object (str): Object Name
+ object_parent (str): Object Parent (e.g. Table)
+
+ Returns:
+ str: String that can be used for custom linking
"""
url_reference = f"{object_parent}-{object}".replace(" ", "")
return f"{{#{url_reference}}}"
@@ -107,19 +115,38 @@ def generate_documentation_pages(self) -> None:
self.column_page = self.generate_markdown_column_page()
self.category_page = self.generate_category_file()
- def get_object_caption(self, object_name: str, object_parent: str):
- """Retrieves the caption of an object, based on the translations in the culture."""
+ def get_object_caption(self, object_name: str, object_parent: str) -> str:
+ """Retrieves the caption of an object, based on the translations in the culture.
+
+ If no culture is present, the object_name is returned.
+
+ Args:
+ object_name (str): Object Name
+ object_parent (str): Object Parent Name
+
+ Returns:
+ str: Translated object.
+ """
if self.culture_include:
- return self.culture_object.get_translation(
- object_name=object_name, object_parent_name=object_parent
- ).get("object_translation")
+ return str(
+ self.culture_object.get_translation(
+ object_name=object_name, object_parent_name=object_parent
+ ).get("object_translation")
+ )
return object_name
def set_translations(
self, enable_translations: bool = False, culture: str = "en-US"
) -> None:
- """Set translations to active or inactive, depending on the needs of the users."""
+ """Set translations to active or inactive, depending on the needs of the users.
+
+ Args:
+ enable_translations (bool, optional): Flag to enable or disable translations.
+ Defaults to False.
+ culture (str, optional): Set culture that needs to be used in the docs.
+ Defaults to "en-US".
+ """
logger.info(f"Using Translations set to > {enable_translations}")
if enable_translations:
@@ -138,12 +165,20 @@ def set_translations(
else:
self.culture_include = enable_translations
- def set_model_friendly_name(self):
- """Replaces the model name to a friendly string, so it can be used in an URL."""
+ def set_model_friendly_name(self) -> str:
+ """Replaces the model name to a friendly string, so it can be used in an URL.
+
+ Returns:
+ str: Friendly model name used in url for docs.
+ """
return (self.model_name).replace(" ", "-").replace("_", "-").lower()
def set_save_path(self) -> Path:
- """Set the location of the documentation."""
+ """Set the location of the documentation.
+
+ Returns:
+ Path: Path where the docs are saved.
+ """
return Path(f"{self.save_location}/{self.friendly_name}")
def save_page(self, content: str, page_name: str, keep_file: bool = False) -> None:
@@ -166,7 +201,7 @@ def save_page(self, content: str, page_name: str, keep_file: bool = False) -> No
target_file = self.save_path / page_name
if keep_file and target_file.exists():
- logger.info(f"{page_name} already exists -> fill will not overwritten.")
+ logger.info(f"{page_name} already exists -> file will not overwritten.")
else:
logger.info(f"Results are written to -> {page_name}.")
@@ -243,6 +278,12 @@ def create_markdown_for_measure(self, object: PyMeasure) -> str:
"""Create Markdown for a specific measure.
That can later on be used for generating the whole measure page.
+
+ Args:
+ object (PyMeasure): The measure to document.
+
+ Returns:
+ str: Markdown section for specific Measure
"""
object_caption = (
self.get_object_caption(
@@ -251,54 +292,47 @@ def create_markdown_for_measure(self, object: PyMeasure) -> str:
or object.Name
)
- object_description = (object.Description or "No Description available").replace(
+ obj_description = (object.Description or "No Description available").replace(
"\\n", ""
)
- return f"""
-### {object_caption}
-**Description**:
-> {object_description}
-
-
-
-- Display Folder
-- {object.DisplayFolder}
-
-- Table Name
-- {object.Parent.Name}
-
-- Format String
-- {object.FormatString}
-
-- Is Hidden
-- {object.IsHidden}
-
-
-
-```dax title="Technical: {object.Name}"
-{
- object.Expression
-}
-```
+ object_properties = [
+ {"Measure Name": object.Name},
+ {"Display Folder": object.DisplayFolder},
+ {"Format String": object.FormatString},
+ {"Is Hidden": "Yes" if object.IsHidden else "No"},
+ ]
----
-"""
+ obj_text = [
+ f"### {object_caption}",
+ "**Description**:",
+ f"> {obj_description}",
+ "" f"{self.generate_object_properties(object_properties)}" "",
+ f'```dax title="Technical: {object.Name}"',
+ f" {object.Expression}",
+ "```",
+ "---",
+ ]
+ return "\n".join(obj_text)
def generate_markdown_measure_page(self) -> str:
- """Based on the measure objects it generates a measure page."""
+ """This function generates the meausure documation page.
+
+ Returns:
+ str: The full markdown text that is needed
+ make it compatible with Docusaurus.
+ """
prev_display_folder = ""
markdown_template = [
- f"""---
-sidebar_position: 3
-title: Measures
-description: This page contains all measures for the {self.model.Name} model, \
-including the description, \
-format string, and other technical details.
----
-
-# Measures for {self.model.Name}
-"""
+ "---",
+ "sidebar_position: 3",
+ "title: Measures",
+ "description: This page contains all measures for "
+ f"the {self.model.Name} model, including the description, "
+ "format string, and other technical details.",
+ "---",
+ "",
+ f"# Measures for {self.model.Name}",
]
measures = sorted(
@@ -316,7 +350,7 @@ def generate_markdown_measure_page(self) -> str:
markdown_template.append(self.create_markdown_for_measure(measure))
- return "".join(markdown_template)
+ return "\n".join(markdown_template)
def create_markdown_for_table(self, object: PyTable) -> str:
"""This functions returns the markdown for a table.
@@ -334,109 +368,87 @@ def create_markdown_for_table(self, object: PyTable) -> str:
or object.Name
)
- object_description = (object.Description or "No Description available").replace(
+ obj_description = (object.Description or "No Description available").replace(
"\\n", ""
)
+ object_properties = [
+ {"Measures (#)": len(object.Measures)},
+ {"Columns (#)": len(object.Columns)},
+ {"Partiton (#)": len(object.Partitions)},
+ {"Data Category": object.DataCategory or "Regular Table"},
+ {"Is Hidden": object.IsHidden},
+ {"Table Type": object.Partitions[0].ObjectType},
+ {"Source Type": object.Partitions[0].SourceType},
+ ]
+
partition_type = ""
partition_source = ""
+ logger.debug(f"{object_caption} => {str(object.Partitions[0].SourceType)}")
+
if str(object.Partitions[0].SourceType) == "Calculated":
partition_type = "dax"
partition_source = object.Partitions[0].Source.Expression
elif str(object.Partitions[0].SourceType) == "M":
partition_type = "powerquery"
partition_source = object.Partitions[0].Source.Expression
+ elif str(object.Partitions[0].SourceType) == "CalculationGroup":
+ partition_type = ""
+ partition_source = ""
else:
partition_type = "sql"
partition_source = object.Partitions[0].Source.Query
- return f"""
-### {object_caption}
-**Description**:
-> {object_description}
-
-
-- Measures (#)
-- {len(object.Measures)}
-
-- Columns (#)
-- {len(object.Columns)}
-
-- Partitions (#)
-- {len(object.Partitions)}
-
-- Data Category
-- {object.DataCategory or "Regular Table"}
-
-- Is Hidden
-- {object.IsHidden}
-
-- Table Type
-- {object.Partitions[0].ObjectType}
-
-- Source Type
-- {object.Partitions[0].SourceType}
-
-
-```{partition_type} title="Table Source: {object.Name}"
-{
- partition_source
-}
-```
-
----
+ obj_text = [
+ f"### {object_caption}",
+ "**Description**: ",
+ f"> {obj_description}",
+ "",
+ f"{self.generate_object_properties(object_properties)}",
+ "",
+ f'```{partition_type} title="Table Source: {object.Name}"',
+ f" {partition_source}",
+ "```",
+ "---",
+ ]
-"""
+ return "\n".join(obj_text)
def generate_markdown_table_page(self) -> str:
- """This function generates the markdown tables documentation for the tables in the Model."""
- markdown_template = f"""---
-sidebar_position: 2
-title: Tables
-description: This page contains all columns with tables for {self.model.Name}, \
-including the description, \
-and technical details.
----
-
-# Tables {self.model.Name}
+ """This function generates the markdown for table documentation.
- """
-
- for table in self.model.Tables:
- markdown_template += self.create_markdown_for_table(table)
-
- return markdown_template
-
- def generate_markdown_column_page(self) -> str:
- """This function generates the markdown for documentation about columns in the Model."""
- markdown_template = f"""---
-sidebar_position: 4
-title: Columns
-description: This page contains all columns with Columns for {self.model.Name}, \
-including the description, format string, and other technical details.
----
-
- """
-
- for table in self.model.Tables:
- markdown_template += f"""
-## Columns for {table.Name}
- """
-
- for column in table.Columns:
- if "RowNumber" in column.Name:
- continue
-
- markdown_template += self.create_markdown_for_column(column)
+ Returns:
+ str: Will be appended to the page text.
+ """
+ markdown_template = [
+ "---",
+ "sidebar_position: 2",
+ "title: Tables",
+ "description: This page contains all columns with "
+ f"tables for {self.model.Name}, including the description, "
+ "and technical details.",
+ "---",
+ "",
+ f"# Tables {self.model.Name}",
+ ]
- return markdown_template
+ markdown_template.extend(
+ self.create_markdown_for_table(table) for table in self.model.Tables
+ )
+ return "\n".join(markdown_template)
def create_markdown_for_column(self, object: PyColumn) -> str:
"""Generates the Markdown for a specifc column.
- If a colums is calculated, then it also shows
- the expression for that column in DAX.
+ If a columns is calculated, then it also shows the expression for
+ that column in DAX.
+
+ Args:
+ object (PyColumn): Needs PyColumn objects input
+
+ Returns:
+ str: Will be appended to the page text.
"""
object_caption = (
self.get_object_caption(
@@ -445,73 +457,140 @@ def create_markdown_for_column(self, object: PyColumn) -> str:
or object.Name
)
- object_description = (object.Description or "No Description available").replace(
- "\\n", ""
+ obj_description = (
+ object.Description.replace("\\n", "") or "No Description available"
)
- basic_info = f"""
-### {object_caption} {self.create_object_reference(
- object=object.Name,
- object_parent=object.Parent.Name
- )}
-**Description**:
-> {object_description}
+ obj_reference = self.create_object_reference(
+ object=object.Name,
+ object_parent=object.Parent.Name
+ )
-
-- Column Name
-- {object.Name}
+ obj_heading = f"""{object_caption} {obj_reference}"""
+
+ object_properties = [
+ {"Column Name": object.Name},
+ {"Object Type": object.ObjectType},
+ {"Type": object.Type},
+ {"Is Available In Excel": object.IsAvailableInMDX},
+ {"Is Hidden": object.IsHidden},
+ {"Data Category": object.DataCategory},
+ {"Data Type": object.DataType},
+ {"Display Folder": object.DisplayFolder},
+ ]
-- Object Type
-- {object.ObjectType}
+ obj_text = [
+ f"### {obj_heading}",
+ "**Description**:",
+ f"> {obj_description}",
+ "",
+ f"{self.generate_object_properties(object_properties)}",
+ ]
-- Type
-- {object.Type}
+ if str(object.Type) == "Calculated":
+ obj_text.extend(
+ (
+ f'```dax title="Technical: {object.Name}"',
+ f" {object.Expression}",
+ "```",
+ )
+ )
+ obj_text.append("---")
-- Is Available In Excel
-- {object.IsAvailableInMDX}
+ return "\n".join(obj_text)
-- Is Hidden
-- {object.IsHidden}
+ def generate_markdown_column_page(self) -> str:
+ """This function generates the markdown for the colums documentation.
-- Data Category
-- {object.DataCategory}
+ Returns:
+ str: Will be appended to the page text.
+ """
+ markdown_template = [
+ "---",
+ "sidebar_position: 4",
+ f"title: Columns description: This page contains all columns with "
+ f"Columns for {self.model.Name} "
+ "including the description, format string, and other technical details.",
+ "---",
+ "",
+ ]
-- Data Type
-- {object.DataType}
+ for table in self.model.Tables:
+ markdown_template.append(f"## Columns for {table.Name}")
-- DisplayFolder
-- {object.DisplayFolder}
+ markdown_template.extend(
+ self.create_markdown_for_column(column)
+ for column in table.Columns
+ if "RowNumber" not in column.Name
+ )
+ return "\n".join(markdown_template)
-
-"""
+ def generate_category_file(self) -> str:
+ """Docusaurs can generate an index based on the files.
- if str(object.Type) == "Calculated":
- basic_info += f"""
-```dax title="Technical: {object.Name}"
-{
- object.Expression
-}
-```
- """
- return (
- basic_info
- + """
----
- """
- )
+ The files that are in the same directory as _category_.ym will
+ be use to create an index and a navigation. For more information
+ see Docusaurus documentation.
+
+ Returns:
+ str: Text that will be the base of _category_.yml.
+ """
+ obj_text = [
+ "position: 2 # float position is supported",
+ f"label: '{self.model_name}'",
+ "collapsible: true # make the category collapsible",
+ "collapsed: true # keep the category open by default",
+ " link:",
+ " type: generated-index",
+ " title: Documentation Overview",
+ "customProps:",
+ " description: To be added in the future.",
+ ]
+
+ return "\n".join(obj_text)
+
+ @staticmethod
+ def generate_object_properties(properties: List[Dict[str, str]]) -> str:
+ """Generate the section for object properties.
- def generate_category_file(self):
- """Docusaurs can generate an index.
+ You can select your own properties to display
+ by providing a the properties in a list of
+ dicts.
- The category yaml will make that happen.
+ Args:
+ Self.
+ Properties (dict): The ones you want to show.
+
+ Returns:
+ str: HTML used in the markdown.
+
+ Example:
+ ```python
+ [
+ { "Display Folder": "Sales Order Information" },
+ { "Is Hidden": "False" },
+ { "Format String": "#.###,## }
+ ]
+ ```
+ Returns:
+ ```
+
+ - Display Folder
+ - Sales Order Information
+
+ - Is Hidden
+ - False
+
+ - Format String
+ - #.###,##
+
+ ```
"""
- return f"""position: 2 # float position is supported
-label: '{self.model_name}'
-collapsible: true # make the category collapsible
-collapsed: true # keep the category open by default
-link:
- type: generated-index
- title: Documentation Overview
-customProps:
- description: To be added in the future.
-"""
+ obj_text = [""]
+
+ for obj_prop in properties:
+ for caption, text in obj_prop.items():
+ obj_text.extend((f" - {caption}
", f" - {text}
", ""))
+ obj_text.append("
")
+
+ return "\n".join(obj_text)