diff --git a/README.md b/README.md index 1c54d89e..a21bb1c1 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,15 @@ python scripts/generate_survey_models.py ``` This will generate models and documentation for all surveys in the Tamanu database. +## Generate report boilerplate +Execute the following command to generate boilerplate SQL and JSON files: +```bash +python scripts/report_generator.py [OPTIONS] +``` +### Command-Line Arguments +- `--project` / `-p`: Project name (defaults to "generic") +- `--name` / `-n`: Report name (defaults to timestamped name like "report-20241205_143022") + ## Script to list Tamanu reports This script generates a list of all reports in the repository and outputs the result in a Markdown file. diff --git a/scripts/report_generator.py b/scripts/report_generator.py new file mode 100644 index 00000000..bf8c1494 --- /dev/null +++ b/scripts/report_generator.py @@ -0,0 +1,58 @@ +import re +import sys +from datetime import datetime +from pathlib import Path + +from utils.file_utils import ensure_directory_exists, write_file +from utils.report_utils import create_report_config, create_report_sql +from utils.system_utils import get_arg_value + +BASE_DIR = Path(__file__).parent.parent +SQL_DIR = BASE_DIR / "models" / "reports" / "sql" +CONFIG_DIR = BASE_DIR / "models" / "reports" / "config" + + +def get_report_filename(name): + """Convert a report name to a valid filename.""" + filename = name.lower().strip() + filename = re.sub(r"[\s_]+", "-", filename) + filename = re.sub(r"[^a-z0-9-]", "", filename) + filename = re.sub(r"-+", "-", filename) + return filename.strip("-") + + +def main(): + args = sys.argv[1:] + project = get_arg_value(args, "--project", "-p", "generic") + report_name = get_arg_value( + args, "--name", "-n", f"report-{datetime.now().strftime('%Y%m%d_%H%M%S')}" + ) + + print(f"\nGenerating files for '{report_name}' in '{project}'") + + ensure_directory_exists(str(CONFIG_DIR)) + ensure_directory_exists(str(SQL_DIR)) + + report = get_report_filename(report_name) + config_file = CONFIG_DIR / project / f"{report}.json" + sql_file = SQL_DIR / project / f"{report}.sql" + + if config_file.exists() or sql_file.exists(): + print(f"One or more files already exists.") + return + + config_content = create_report_config(report_name) + sql_content = create_report_sql() + + write_file(str(config_file), config_content, file_type="json") + write_file(str(sql_file), sql_content, file_type="text") + + print(f"\nSuccessfully created config and sql files for '{report_name}'!") + + +if __name__ == "__main__": + try: + main() + except Exception as e: + print(f"Error creating files: {e}") + sys.exit(1) diff --git a/scripts/utils/__init__.py b/scripts/utils/__init__.py index 00d9f6ba..810f8819 100644 --- a/scripts/utils/__init__.py +++ b/scripts/utils/__init__.py @@ -11,6 +11,8 @@ write_file, ) from .report_utils import ( + create_report_config, + create_report_sql, generate_import_report_script, generate_project_reports, generate_reporting_schema_script, diff --git a/scripts/utils/report_utils.py b/scripts/utils/report_utils.py index 743d3c75..09cb4460 100644 --- a/scripts/utils/report_utils.py +++ b/scripts/utils/report_utils.py @@ -12,6 +12,69 @@ VIEWS_DIR = os.path.join(BASE_DIR, "compiled", "views") +def create_report_config(name): + """ + Creates a report configuration dictionary. + + Args: + project (str): The project name + name (str): The filename for the report + + Returns: + dict: The report configuration dictionary + """ + config = { + "query": "replace this", + "name": name, + "notes": f"This report generates {name}. Please update this description with specific details about what the report includes and its purpose.", + "status": "published", + "dbSchema": "reporting", + "queryOptions": { + "defaultDateRange": "30days", + "dataSources": ["thisFacility", "allFacilities"], + "parameters": [], + }, + } + return config + + +def create_report_sql(): + """ + Creates a SQL template for a new report. + + Args: + project (str): The project name + name (str): The filename for the report + + Returns: + str: The SQL template content + """ + sql_content = f"""-- TODO: Replace this +select + -- TODO: Add columns here with the following format: + -- 'column_name' as "{{{{ translate_label('columnLabel','Column Display Name') }}}}" +from {{{{ ref('dataset_name') }}}} +-- TODO: Replace 'dataset_name' with the actual dataset reference +where case + -- TODO: Replace 'date_column' with the actual date column name + when {{{{ parameter('fromDate', default_value='2024-01-01', data_type='date') }}}} is null then true + else date_column >= {{{{ parameter('fromDate', default_value='2024-01-01', data_type='date') }}}} + end + and case + -- TODO: Replace 'date_column' with the actual date column name + when {{{{ parameter('toDate', default_value='2024-12-31', data_type='date') }}}} is null then true + else date_column <= {{{{ parameter('toDate', default_value='2024-12-31', data_type='date') }}}} + end + -- TODO: Add other filtering conditions here + -- Example facility filtering: + -- and case + -- when {{{{ parameter('facilityId', default_value='null', data_type='text') }}}} is null then true + -- else facility_id::text = {{{{ parameter('facilityId', default_value='null', data_type='text') }}}} + -- end +""" + return sql_content + + def compile_report(database, sql_file, config_file, output_file): """ Compiles a report by processing the SQL and config files and generating a JSON output.