Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Beliagal/qa-report-automation/llms.txt

Use this file to discover all available pages before exploring further.

The Valoraclick QA Tool is organized into four distinct layers following a clean separation of concerns. Each file has a single, well-defined responsibility, making it straightforward to extend or replace individual components — such as swapping the PDF engine or adding a new export target — without touching the rest of the codebase.

Layer Overview

The dependency tree flows strictly downward: the entry point boots the GUI, the GUI owns the data model and delegates work to the service layer, and the service layer relies on pure utility functions from the logic layer.
main.py
  └── gui.py (TestingApp)          ← View layer (CustomTkinter)
        ├── models.py (ReportData)   ← Data model
        ├── logic.py                 ← Pure utility functions
        └── services.py              ← Service layer
              ├── PDFService         ← PDF generation (fpdf2)
              └── CSVService         ← Google Drive CSV export (win32com)

models.py — Data Layer

ReportData is a plain Python object with no UI logic and no external dependencies. It exposes two attributes:
  • metadata — a dict keyed by field labels (e.g. "Tester:", "Fecha:", "resumen") that maps to the string values entered in the UI.
  • pruebas — a list of step dictionaries, each containing input, esp (expected), obt (obtained), estado (Pass / Fail), and img (optional screenshot path).
Because ReportData is a pure data container it can be passed freely between layers — from the GUI to either service — without coupling any layer to the UI framework.
models.py
import datetime
from typing import List, Dict

class ReportData:
    def __init__(self):
        self.reset_data()

    def reset_data(self):
        self.metadata = {
            "Aplicación:": "Valoraclick",
            "Tester:": "",
            "Fecha:": datetime.datetime.now().strftime('%d/%m/%Y'),
            "Historia de Usuario:": "",
            "Requisitos:": "",
            "Versión:": "",
            "dep": "",
            "resumen": ""
        }
        self.pruebas: List[Dict] = []

logic.py — Business Logic

logic.py contains pure functions with no side effects. They take arguments and return values — nothing more. This makes them trivially testable and reusable in any context.
FunctionPurpose
validar_fecha(fecha_str)Returns True if the string matches DD/MM/YYYY and is a real calendar date; False otherwise.
obtener_color_estado(estado)Maps "Pass"(46, 204, 113) (green) and any other value → (231, 76, 60) (red) as an RGB tuple.
logic.py
import re
from datetime import datetime

def validar_fecha(fecha_str):
    if not re.match(r"^\d{2}/\d{2}/\d{4}$", fecha_str):
        return False
    try:
        datetime.strptime(fecha_str, '%d/%m/%Y')
        return True
    except ValueError:
        return False

def obtener_color_estado(estado):
    if estado == "Pass":
        return (46, 204, 113)
    return (231, 76, 60)

services.py — Service Layer

The service layer contains two classes that perform I/O operations. Neither class imports from gui.py or touches any UI widget — they receive a ReportData instance and an output path, do their work, and return.

PDFService

PDFService.generate_report(data, output_path) uses fpdf2 to render a complete PDF report:
  1. Writes an INFORMACIÓN GENERAL header block from data.metadata.
  2. Iterates over data.pruebas and renders a table row for each step, colouring pass rows green and fail rows red.
  3. Embeds any screenshot image found at step["img"] directly beneath its row.
  4. Calls pdf.output(output_path) to write the file to disk.

CSVService

CSVService.export(data) writes a timestamped CSV backup to Google Drive:
  1. Uses win32com.client to resolve a Windows .lnk shortcut at G:\Mi unidad\Informes.lnk to its real target directory.
  2. Falls back to G:\Mi unidad if the shortcut cannot be resolved.
  3. Writes a UTF-8 BOM CSV containing all metadata fields and all test step rows.
  4. Returns a (bool, str) tuple — (True, full_path) on success or (False, error_message) on failure. The GUI logs the result but never raises to the user on a Drive failure.

gui.py — View Layer

TestingApp extends ctk.CTk and is the orchestrator of the entire application. On construction it:
  • Instantiates ReportData, PDFService, and CSVService.
  • Builds the UI in three sections via _setup_top_bar(), _setup_metadata_grid(), and _setup_execution_form().
  • Calls _load_session() to restore any previously saved state from sesion_testing.json.
Key methods and their roles:
MethodRole
_sync()Reads all Entry and Textbox widgets and writes their values into report_data.metadata.
_on_export()Validates the date, opens a save-file dialog, calls PDFService then CSVService, and shows a result dialog.
_on_add()Appends a new step dict to report_data.pruebas, refreshes the log viewer, and saves the session.
_on_reset()Asks for confirmation, calls report_data.reset_data(), clears all widgets, and deletes the session file.
_save_session() / _load_session()Serialize / deserialize report_data to sesion_testing.json for crash-safe persistence.
_on_img()Opens a file picker for PNG/JPG screenshots and stores the chosen path in self.current_img.
_update_log()Redraws the live-preview log textbox with colour-coded step entries.

main.py — Entry Point

main.py is intentionally minimal. It imports TestingApp, instantiates it, and hands control to the Tk event loop. No application logic lives here.
main.py
from gui import TestingApp

if __name__ == "__main__":
    app = TestingApp()
    app.mainloop()

Data Flow

Understanding how data moves through the layers is the key to extending the tool:
User types in widgets


  _sync()  ──────────────────►  report_data (ReportData)

                         ┌──────────────┴──────────────┐
                         ▼                             ▼
                   PDFService                    CSVService
               generate_report()               export()
                         │                             │
                         ▼                             ▼
                    .pdf file               backup_*.csv on Drive
_sync() is always called before any export so that the latest widget values are flushed into ReportData before it is handed to either service.
The repository also contains a testing_gui.py file — an alternative monolithic prototype that combines all layers (data, logic, services, and UI) into a single file. It was an early proof of concept. The production codebase uses the modular structure described on this page: models.py, logic.py, services.py, and gui.py as separate, independently testable modules.

Build docs developers (and LLMs) love