Skip to content

Commit 4f8426f

Browse files
committed
Refactor classes to use create_folder function from utils.py
1 parent 83c05e8 commit 4f8426f

File tree

4 files changed

+123
-61
lines changed

4 files changed

+123
-61
lines changed

report/helpers/utils.py

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,30 @@
66
from typing import Type
77

88
## CHECKS
9-
def assert_path(filepath: str):
9+
def check_path(filepath: str) -> bool:
1010
"""
11-
Checks that fpath is a string and that it exists
11+
Checks if the given file or folder path exists.
1212
13-
PARAMS
14-
-----
15-
- filepath (str): the filepath or folderpath
13+
PARAMETERS
14+
---------
15+
filepath : str
16+
The file or folder path to check.
1617
17-
OUTPUTS
18-
-----
19-
- raises assertion error if filepath is not a string or doesn't exist
20-
"""
18+
RETURNS
19+
-------
20+
bool
21+
True if the path exists, False otherwise.
2122
23+
RAISES
24+
------
25+
AssertionError
26+
If the filepath is not a valid string.
27+
"""
28+
# Assert that the filepath is a string
2229
assert isinstance(filepath, str), f"Filepath must be a string: {filepath}"
23-
assert os.path.exists(
24-
os.path.abspath(filepath)
25-
), f"Filepath does not exist: {os.path.abspath(filepath)}"
30+
31+
# Check if the path exists
32+
return os.path.exists(os.path.abspath(filepath))
2633

2734

2835
def assert_enum_value(enum_class: Type[StrEnum], value: str, logger: logging.Logger) -> StrEnum:
@@ -55,6 +62,43 @@ def assert_enum_value(enum_class: Type[StrEnum], value: str, logger: logging.Log
5562
logger.error(f"Invalid value for {enum_class.__name__}: '{value}'. Expected values are: {expected_values}")
5663
raise ValueError(f"Invalid {enum_class.__name__}: {value}. Expected values are: {expected_values}")
5764

65+
## FILE_SYSTEM
66+
def create_folder(directory_path: str, is_nested: bool = False) -> bool:
67+
"""
68+
Create a folder. Optionally create nested directories if the specified path includes subdirectories.
69+
70+
Parameters
71+
----------
72+
directory_path : str
73+
The path of the directory to create.
74+
is_nested : bool
75+
A flag indicating whether to create nested directories (True uses os.makedirs, False uses os.mkdir).
76+
77+
Returns
78+
-------
79+
bool
80+
True if the folder was created or False if it already existed.
81+
82+
Raises
83+
------
84+
OSError
85+
If there is an error creating the directory.
86+
"""
87+
try:
88+
if not check_path(directory_path):
89+
if is_nested:
90+
# Create the directory and any necessary parent directories
91+
os.makedirs(directory_path, exist_ok=True)
92+
return True
93+
else:
94+
# Create only the final directory (not nested)
95+
os.mkdir(directory_path)
96+
return True
97+
else:
98+
return False
99+
except OSError as e:
100+
raise OSError(f"Error creating directory '{directory_path}': {e}")
101+
58102
## LOGGING
59103
def get_basename(fname: None | str = None) -> str:
60104
"""
@@ -81,7 +125,8 @@ def get_basename(fname: None | str = None) -> str:
81125
"""
82126
if fname is not None:
83127
# PRECONDITION
84-
assert_path(fname)
128+
if not check_path(fname):
129+
raise FileNotFoundError(f"The specified path does not exist: {fname}")
85130
# MAIN FUNCTIONS
86131
return os.path.splitext(os.path.basename(fname))[0]
87132
else:
@@ -165,7 +210,7 @@ def generate_log_filename(folder: str = "logs", suffix: str = "") -> str:
165210
log_filepath (str): the file path to the log file
166211
"""
167212
# PRECONDITIONS
168-
assert_path(folder)
213+
create_folder(folder)
169214

170215
# MAIN FUNCTION
171216
log_filename = get_time(incl_timezone=False) + "_" + suffix + ".log"

report/main.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010
report, report_metadata = yaml_manager.load_report_metadata('./report_metadata_micw2graph.yaml')
1111

1212
# Create report view
13-
doc_report = doc_reportview.QuartoReportView(report_metadata['report']['id'],
14-
report_metadata['report']['name'],
15-
report=report,
16-
report_type = assert_enum_value(ReportType, report_metadata['report']['report_type'], report.logger),
17-
report_format = assert_enum_value(doc_reportview.ReportFormat, report_metadata['report']['report_format'], report.logger),
18-
columns=None)
19-
doc_report.generate_report()
20-
doc_report.run_report()
13+
# doc_report = doc_reportview.QuartoReportView(report_metadata['report']['id'],
14+
# report_metadata['report']['name'],
15+
# report=report,
16+
# report_type = assert_enum_value(ReportType, report_metadata['report']['report_type'], report.logger),
17+
# report_format = assert_enum_value(doc_reportview.ReportFormat, report_metadata['report']['report_format'], report.logger),
18+
# columns=None)
19+
# doc_report.generate_report()
20+
# doc_report.run_report()
2121

22-
# st_report = st_reportview.StreamlitReportView(report_metadata['report']['id'],
23-
# report_metadata['report']['name'],
24-
# report=report,
25-
# report_type = assert_enum_value(ReportType, report_metadata['report']['report_type'], report.logger),
26-
# columns=None)
27-
# st_report.generate_report()
28-
# st_report.run_report()
22+
st_report = st_reportview.StreamlitReportView(report_metadata['report']['id'],
23+
report_metadata['report']['name'],
24+
report=report,
25+
report_type = assert_enum_value(ReportType, report_metadata['report']['report_type'], report.logger),
26+
columns=None)
27+
st_report.generate_report()
28+
st_report.run_report()
2929

report/quarto_reportview.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import report as r
44
from enum import StrEnum, auto
55
from typing import List, Optional
6+
from helpers.utils import create_folder
67

78
class ReportFormat(StrEnum):
89
HTML = auto()
@@ -26,21 +27,31 @@ def __init__(self, id: int, name: str, report: r.Report, report_type: r.ReportTy
2627
super().__init__(id, name=name, report=report, report_type = report_type, columns=columns)
2728
self.report_format = report_format
2829

29-
def generate_report(self, output_dir: str = BASE_DIR) -> None:
30+
def generate_report(self, output_dir: str = BASE_DIR, static_dir: str = STATIC_FILES_DIR) -> None:
3031
"""
3132
Generates the qmd file of the quarto report. It creates code for rendering each section and its subsections with all components.
3233
3334
Parameters
3435
----------
3536
output_dir : str, optional
3637
The folder where the generated report files will be saved (default is BASE_DIR).
38+
static_dir : str, optional
39+
The folder where the static files will be saved (default is STATIC_FILES_DIR).
3740
"""
3841
self.report.logger.debug(f"Generating '{self.report_type}' report with '{self.report_format}' format in directory: '{output_dir}'")
3942

40-
# Create the output folder if it does not exist
41-
if not os.path.exists(output_dir):
42-
os.mkdir(output_dir)
43-
self.report.logger.debug(f"Created output directory: {output_dir}")
43+
# Create the output folder
44+
if create_folder(output_dir):
45+
self.report.logger.debug(f"Created output directory: '{output_dir}'")
46+
else:
47+
self.report.logger.debug(f"Output directory already existed: '{output_dir}'")
48+
49+
# Create the static folder
50+
if create_folder(static_dir):
51+
self.report.logger.info(f"Created output directory for static content: '{static_dir}'")
52+
else:
53+
self.report.logger.info(f"Output directory for static content already existed: '{static_dir}'")
54+
4455
try:
4556
# Create variable to check if the report is static or revealjs
4657
is_report_static = self.report_format in {ReportFormat.PDF, ReportFormat.DOCX, ReportFormat.ODT, ReportFormat.PPTX}
@@ -230,37 +241,31 @@ def _generate_subsection(self, subsection, is_report_static, is_report_revealjs)
230241
self.report.logger.info(f"Generated content and imports for subsection: '{subsection.name}'")
231242
return subsection_content, subsection_imports
232243

233-
def _generate_plot_content(self, plot, is_report_static, output_dir: str = STATIC_FILES_DIR) -> List[str]:
244+
def _generate_plot_content(self, plot, is_report_static, static_dir: str = STATIC_FILES_DIR) -> List[str]:
234245
"""
235246
Generate content for a plot component based on the report type.
236247
237248
Parameters
238249
----------
239250
plot : Plot
240251
The plot component to generate content for.
241-
is_report_static : bool
242-
A boolean indicating whether the report is static or interactive.
252+
static_dir : str, optional
253+
The folder where the static files will be saved (default is STATIC_FILES_DIR).
243254
244255
Returns
245256
-------
246257
list : List[str]
247258
The list of content lines for the plot.
248-
output_dir : str, optional
249-
The folder where the static files will be saved (default is STATIC_FILES_DIR).
250259
"""
251-
# Create the output folder if it does not exist
252-
if not os.path.exists(output_dir):
253-
os.mkdir(output_dir)
254-
255260
plot_content = []
256261
plot_content.append(f'### {plot.title}')
257262
if plot.plot_type == r.PlotType.INTERACTIVE:
258263
try:
259264
# Define plot path
260265
if is_report_static:
261-
static_plot_path = os.path.join(output_dir, f"{plot.name.replace(' ', '_')}.png")
266+
static_plot_path = os.path.join(static_dir, f"{plot.name.replace(' ', '_')}.png")
262267
else:
263-
html_plot_file = os.path.join(output_dir, f"{plot.name.replace(' ', '_')}.html")
268+
html_plot_file = os.path.join(static_dir, f"{plot.name.replace(' ', '_')}.html")
264269

265270
if plot.int_visualization_tool == r.IntVisualizationTool.PLOTLY:
266271
plot_content.append(self._generate_plot_code(plot))
@@ -447,7 +452,7 @@ def _generate_image_content(self, image_path: str, alt_text: str = "", width: in
447452
return f"""
448453
![{alt_text}]({os.path.join('..', image_path)}){{ width={width}px height={height}px fig-align="center"}}\n"""
449454

450-
def _show_dataframe(self, dataframe, is_report_static, output_dir: str = STATIC_FILES_DIR) -> List[str]:
455+
def _show_dataframe(self, dataframe, is_report_static, static_dir: str = STATIC_FILES_DIR) -> List[str]:
451456
"""
452457
Appends either a static image or an interactive representation of a DataFrame to the content list.
453458
@@ -457,18 +462,18 @@ def _show_dataframe(self, dataframe, is_report_static, output_dir: str = STATIC_
457462
The DataFrame object containing the data to display.
458463
is_report_static : bool
459464
Determines if the report is in a static format (e.g., PDF) or interactive (e.g., HTML).
465+
static_dir : str, optional
466+
The folder where the static files will be saved (default is STATIC_FILES_DIR).
460467
461468
Returns
462469
-------
463470
list : List[str]
464471
The list of content lines for the DataFrame.
465-
output_dir : str, optional
466-
The folder where the static files will be saved (default is STATIC_FILES_DIR).
467472
"""
468473
dataframe_content = []
469474
if is_report_static:
470475
# Generate path for the DataFrame image
471-
df_image = os.path.join(output_dir, f"{dataframe.name.replace(' ', '_')}.png")
476+
df_image = os.path.join(static_dir, f"{dataframe.name.replace(' ', '_')}.png")
472477
dataframe_content.append(f"dfi.export(df, '{os.path.join('..', df_image)}', max_rows=10, max_cols=5)\n```\n")
473478
# Use helper method to add centered image content
474479
dataframe_content.append(self._generate_image_content(df_image, dataframe.name))

report/streamlit_reportview.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import os
33
import subprocess
44
from typing import List, Optional
5+
from helpers.utils import create_folder
56

67
class StreamlitReportView(r.WebAppReportView):
78
"""
@@ -28,9 +29,12 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
2829
self.report.logger.debug(f"Generating '{self.report_type}' report in directory: '{output_dir}'")
2930

3031
# Create the output folder if it does not exist
31-
if not os.path.exists(output_dir):
32-
os.makedirs(output_dir, exist_ok=True)
32+
#if not os.path.exists(output_dir):
33+
# os.makedirs(output_dir, exist_ok=True)
34+
if create_folder(output_dir, is_nested=True):
3335
self.report.logger.debug(f"Created output directory: '{output_dir}'")
36+
else:
37+
self.report.logger.debug(f"Output directory already existed: '{output_dir}'")
3438

3539
try:
3640
self.report.logger.debug("Processing app navigation code.")
@@ -51,13 +55,17 @@ def generate_report(self, output_dir: str = SECTIONS_DIR) -> None:
5155
# Create a folder for each section
5256
subsection_page_vars = []
5357
section_name_var = section.name.replace(" ", "_")
54-
if not os.path.exists(os.path.join(output_dir, section_name_var)):
55-
os.mkdir(os.path.join(output_dir, section_name_var))
56-
self.report.logger.debug(f"Created section directory: {section_name_var}")
58+
section_dir_path = os.path.join(output_dir, section_name_var)
59+
#if not os.path.exists(os.path.join(output_dir, section_name_var)):
60+
# os.mkdir(os.path.join(output_dir, section_name_var))
61+
if create_folder(section_dir_path):
62+
self.report.logger.debug(f"Created section directory: {section_dir_path}")
63+
else:
64+
self.report.logger.debug(f"Section directory already existed: {section_dir_path}")
5765

5866
for subsection in section.subsections:
5967
subsection_name_var = subsection.name.replace(" ", "_")
60-
subsection_file_path = os.path.join(section_name_var, section_name_var + "_" + subsection_name_var + ".py")
68+
subsection_file_path = os.path.join(section_name_var, subsection_name_var + ".py")
6169

6270
# Create a Page object for each subsection and add it to the home page content
6371
report_manag_content.append(f"{subsection_name_var} = st.Page('{subsection_file_path}', title='{subsection.name}')")
@@ -142,11 +150,12 @@ def _generate_home_section(self, output_dir: str, report_manag_content: list) ->
142150
try:
143151
# Create folder for the home page
144152
home_dir_path = os.path.join(output_dir, "Home")
145-
if not os.path.exists(home_dir_path):
146-
os.mkdir(home_dir_path)
153+
#if not os.path.exists(home_dir_path):
154+
#os.mkdir(home_dir_path)
155+
if create_folder(home_dir_path):
147156
self.report.logger.debug(f"Created home directory: {home_dir_path}")
148157
else:
149-
self.report.logger.debug(f"Home directory already exists: {home_dir_path}")
158+
self.report.logger.debug(f"Home directory already existed: {home_dir_path}")
150159

151160
# Create the home page content
152161
home_content = []
@@ -192,7 +201,7 @@ def _generate_sections(self, output_dir: str) -> None:
192201
self.report.logger.debug(f"Processing subsection: '{subsection.name} - {len(subsection.components)} component(s)'")
193202
try:
194203
# Create subsection file
195-
subsection_file_path = os.path.join(output_dir, section_name_var, section_name_var + "_" + subsection.name.replace(" ", "_") + ".py")
204+
subsection_file_path = os.path.join(output_dir, section_name_var, subsection.name.replace(" ", "_") + ".py")
196205

197206
# Generate content and imports for the subsection
198207
subsection_content, subsection_imports = self._generate_subsection(subsection)
@@ -279,9 +288,12 @@ def _generate_plot_content(self, plot, output_dir: str = STATIC_FILES_DIR) -> Li
279288
The folder where the static files will be saved (default is STATIC_FILES_DIR).
280289
"""
281290
# Create the output folder if it does not exist
282-
if not os.path.exists(output_dir):
283-
os.mkdir(output_dir)
291+
#if not os.path.exists(output_dir):
292+
# os.mkdir(output_dir)
293+
if create_folder(output_dir):
284294
self.report.logger.debug(f"Created output directory for static content: {output_dir}")
295+
else:
296+
self.report.logger.debug(f"Output directory for static content already existed: {output_dir}")
285297

286298
plot_content = []
287299
plot_content.append(self._format_text(text=plot.title, type='header', level=4, color='#2b8cbe'))

0 commit comments

Comments
 (0)