Python in Heptora
This manual describes how to develop business automations using Python in the Heptora platform. Unlike the Heptora Language, here you can use the full power of Python along with the specific functions of the heptora library.
Language Philosophy
Section titled “Language Philosophy”The Python approach in Heptora is to offer flexibility and power. You can use:
- ✅ Native Python libraries
- ✅ Control structures (
if,for,while) - ✅ The entire language ecosystem
- ✅ Exception handling (
try/except) - ✅ Object-oriented programming
- ✅ Any package from PyPI
The heptora library provides high-level functions to interact with applications, browsers, and the platform itself.
When to Use Python vs Heptora Language
Section titled “When to Use Python vs Heptora Language”| Aspect | Heptora Language | Python in Heptora |
|---|---|---|
| Complexity | Simple processes | Complex automations |
| Flow control | Not allowed | Fully allowed |
| Libraries | Only Heptora functions | Entire Python ecosystem |
| Learning curve | Low | Medium-High (requires Python) |
| Security | Very high (restricted) | High (more flexible) |
| Ideal for | Business users | Developers |
Installation and Configuration
Section titled “Installation and Configuration”Requirements
Section titled “Requirements”- Python 3.8 or higher installed on the local robot
- The
heptoralibrary is automatically installed with the robot
Import the Library
Section titled “Import the Library”# Always at the beginning of your scriptimport heptora
# Import specific modules if you preferfrom heptora import log, browser, excel, dataAvailable Functions
Section titled “Available Functions”Below is the complete API of the heptora library for Python.
Functions to record information and results.
heptora.log.info()
Section titled “heptora.log.info()”heptora.log.info(message: str) -> NoneDescription: Publishes an informative log message to the platform.
Parameters:
message(str): Informative message to log
Examples:
heptora.log.info("Data loading process started.")heptora.log.info(f"Processing client: {client_name}")
# With variablestotal = len(invoices)heptora.log.info(f"Total invoices to process: {total}")heptora.log.result()
Section titled “heptora.log.result()”heptora.log.result(messageType: str, message: str) -> NoneDescription: Records a final process result.
Parameters:
messageType(str): Result type:"ok","ko", or"error"message(str): Result description
Examples:
# Successheptora.log.result("ok", "50 invoices processed successfully.")
# Controlled errorheptora.log.result("ko", f"Invoice {number} doesn't meet validations.")
# Unexpected errorheptora.log.result("error", "Could not connect to database server.")Usage with conditionals:
if validation_successful: heptora.log.result("ok", f"Invoice {number} processed correctly")else: heptora.log.result("ko", f"Invoice {number}: {error_reason}")heptora.log.force_ko()
Section titled “heptora.log.force_ko()”heptora.log.force_ko(message: str) -> NoneDescription: Records a controlled KO and stops processing the current record.
Parameters:
message(str): Description of the controlled error
Examples:
# Data validationif not invoice_number: heptora.log.force_ko("Invoice number cannot be empty")
# Business rule verificationif status == "Paid": heptora.log.force_ko("Invoice is already paid, no processing required")
# Format validationif not validate_nif(client_nif): heptora.log.force_ko(f"NIF {client_nif} has incorrect format")Report
Section titled “Report”Functions to manage file attachments in results.
heptora.report.add_attachment()
Section titled “heptora.report.add_attachment()”heptora.report.add_attachment(file_path: str) -> NoneDescription: Attaches a file to the current task. It will be sent in the summary email.
Parameters:
file_path(str): Path of the file to attach
Examples:
# Attach a generated reportheptora.report.add_attachment("reports/sales_report.pdf")
# With absolute pathsimport osfull_path = os.path.join(os.getcwd(), "output", "result.xlsx")heptora.report.add_attachment(full_path)
# Verify existence before attachingif os.path.exists("error_report.csv"): heptora.report.add_attachment("error_report.csv") heptora.log.info("Error report attached")Browser
Section titled “Browser”Functions for web automation with Playwright.
heptora.browser.connect()
Section titled “heptora.browser.connect()”heptora.browser.connect() -> PageDescription: Connects to a browser session managed by Heptora and returns a Playwright Page object.
Returns: Playwright Page object to interact with the browser
Prerequisite: The browser must be previously opened from a Heptora code block with heptora.browser.open().
Basic example:
# Connect to browserpage = heptora.browser.connect()
# Navigate to a URLpage.goto("https://www.google.com")
# Interact with elementspage.locator("#APjFqb").fill("Automation with Heptora")page.locator("input[name='btnK']").first.click()
# Wait and get textpage.wait_for_selector(".g")results = page.locator(".g").all_text_contents()heptora.log.info(f"Found {len(results)} results")Advanced example - Login:
page = heptora.browser.connect()page.goto("https://portal.company.com/login")
# Wait for form to loadpage.wait_for_selector("#username")
# Enter credentialsusername = heptora.data.get("portal_username")password = heptora.data.get("portal_password")
page.locator("#username").fill(username)page.locator("#password").fill(password)page.locator("button[type='submit']").click()
# Verify successful logintry: page.wait_for_selector(".dashboard", timeout=5000) heptora.log.info("Login successful")except: heptora.log.force_ko("Could not log in")Example - Data scraping:
page = heptora.browser.connect()page.goto("https://example.com/data-table")
# Wait for tablepage.wait_for_selector("table.data")
# Extract data from tablerows = page.locator("table.data tbody tr").all()extracted_data = []
for row in rows: columns = row.locator("td").all_text_contents() extracted_data.append({ "code": columns[0], "name": columns[1], "price": columns[2] })
heptora.log.info(f"Extracted {len(extracted_data)} records")heptora.data.set("web_data", extracted_data)heptora.browser.go_to()
Section titled “heptora.browser.go_to()”heptora.browser.go_to(url: str) -> NoneDescription: Simplified way to navigate to a URL, which includes a small automatic pause.
Parameters:
url(str): Complete URL to navigate to
Example:
# Simple navigationheptora.browser.go_to("https://portal.heptora.com")
# In a loopurls_to_visit = [ "https://example.com/page1", "https://example.com/page2", "https://example.com/page3"]
for url in urls_to_visit: heptora.browser.go_to(url) # Process the page... heptora.log.info(f"Processed: {url}")heptora.browser.solve_captcha()
Section titled “heptora.browser.solve_captcha()”heptora.browser.solve_captcha() -> NoneDescription: Automatically solves the captcha on the current browser page.
Example:
page = heptora.browser.connect()page.goto("https://site-with-captcha.com/login")
# Try to solve captcha if presenttry: heptora.browser.solve_captcha() heptora.time.pause(3) # Wait for resolution heptora.log.info("Captcha solved")except Exception as e: heptora.log.info("No captcha found or already solved")Functions to work with Excel files.
heptora.excel.load()
Section titled “heptora.excel.load()”heptora.excel.load( file_path: str, sheet_name: str, columns: List[str], has_header: bool) -> List[Dict]Description: Reads data from an Excel file.
Parameters:
file_path(str): Path to the Excel filesheet_name(str): Sheet namecolumns(List[str]): List of column names to extracthas_header(bool): Whether the first row contains headers
Returns: List of dictionaries, each representing a row
Basic example:
# Define columns to readcolumns = ["Product_ID", "Name", "Stock", "Price"]
# Load dataproducts = heptora.excel.load( file_path="inventory.xlsx", sheet_name="Warehouse", columns=columns, has_header=True)
# Process the datafor product in products: heptora.log.info( f"Product {product['Product_ID']}: " f"{product['Name']} - Stock: {product['Stock']}" )Example with processing:
# Load invoicesinvoices = heptora.excel.load( "pending_invoices.xlsx", "January", ["InvoiceNumber", "Client", "Amount", "Status"], True)
# Filter only pending onespending_invoices = [ f for f in invoices if f["Status"] == "Pending"]
heptora.log.info( f"Of {len(invoices)} invoices, " f"{len(pending_invoices)} are pending")
# Save for later processingheptora.data.set("invoices_to_process", pending_invoices)Example with validations:
clients = heptora.excel.load("clients.xlsx", "Sheet1", ["Name", "Email", "NIF"], True)
valid_clients = []errors = []
for i, client in enumerate(clients, start=2): # start=2 for header # Validate required fields if not client.get("Email") or not client.get("NIF"): errors.append(f"Row {i}: Missing required fields") continue
# Validate email format if "@" not in client["Email"]: errors.append(f"Row {i}: Invalid email") continue
valid_clients.append(client)
heptora.log.info(f"Valid clients: {len(valid_clients)}")heptora.log.info(f"Errors found: {len(errors)}")
for error in errors: heptora.log.info(error)heptora.excel.write()
Section titled “heptora.excel.write()”heptora.excel.write( values: List[Dict], file_path: str, sheet_name: str) -> NoneDescription: Writes data to an Excel file.
Parameters:
values(List[Dict]): List of dictionaries with the datafile_path(str): Path where to save the filesheet_name(str): Sheet name
Basic example:
new_data = [ {"Name": "John", "City": "Madrid", "Age": 30}, {"Name": "Laura", "City": "Barcelona", "Age": 28}]
heptora.excel.write( new_data, "new_clients.xlsx", "Clients")
heptora.log.info("Excel file created successfully")Example - Generate processing report:
# Process invoices and generate reportinvoices = heptora.data.get("invoices_to_process")results = []
for invoice in invoices: try: # Process invoice... result = process_invoice(invoice)
results.append({ "InvoiceNumber": invoice["InvoiceNumber"], "Client": invoice["Client"], "Status": "Processed", "Result": "OK", "Observations": "" }) except Exception as e: results.append({ "InvoiceNumber": invoice["InvoiceNumber"], "Client": invoice["Client"], "Status": "Error", "Result": "KO", "Observations": str(e) })
# Save reportfrom datetime import datetimedate = datetime.now().strftime("%Y%m%d_%H%M%S")report_file = f"invoice_report_{date}.xlsx"
heptora.excel.write(results, report_file, "Results")heptora.report.add_attachment(report_file)
heptora.log.info(f"Report generated: {report_file}")Example - Consolidate multiple sources:
# Read from multiple filessales_january = heptora.excel.load("sales_january.xlsx", "Sales", ["Product", "Quantity"], True)sales_february = heptora.excel.load("sales_february.xlsx", "Sales", ["Product", "Quantity"], True)
# Consolidate dataall_sales = sales_january + sales_february
# Group by productfrom collections import defaultdictsales_by_product = defaultdict(int)
for sale in all_sales: product = sale["Product"] quantity = int(sale["Quantity"]) sales_by_product[product] += quantity
# Prepare for writingconsolidated = [ {"Product": product, "Total_Sold": total} for product, total in sales_by_product.items()]
# Write consolidatedheptora.excel.write(consolidated, "consolidated_sales.xlsx", "Summary")Windows
Section titled “Windows”Functions for desktop application automation.
heptora.windows.open_app()
Section titled “heptora.windows.open_app()”heptora.windows.open_app(path: str) -> NoneDescription: Opens a Windows application.
Example:
# Common applicationsheptora.windows.open_app("calc.exe")heptora.windows.open_app("notepad.exe")heptora.windows.open_app("excel.exe")
# With full pathheptora.windows.open_app("C:\\Program Files\\MyApp\\app.exe")
# Wait for it to openheptora.time.pause(2)heptora.windows.type()
Section titled “heptora.windows.type()”heptora.windows.type(text: str) -> NoneDescription: Simulates typing text.
Example:
heptora.windows.type("Order confirmation number 12345.")
# With variablesorder_number = "ORD-2024-001"heptora.windows.type(f"Order: {order_number}")heptora.windows.press()
Section titled “heptora.windows.press()”heptora.windows.press(*keys: str) -> NoneDescription: Simulates pressing keys.
Examples:
# Single keyheptora.windows.press("enter")heptora.windows.press("escape")
# Combinationsheptora.windows.press("control", "s") # Ctrl+Sheptora.windows.press("alt", "f4") # Alt+F4heptora.windows.press("control", "shift", "escape") # Ctrl+Shift+Escheptora.windows.click_by_vision()
Section titled “heptora.windows.click_by_vision()”heptora.windows.click_by_vision(base64Image: str) -> NoneDescription: Searches for an image on screen and clicks on it.
Example:
button_image = "data:image/png;base64,iVBORw0KG..."heptora.windows.click_by_vision(button_image)heptora.time.pause(1)heptora.windows.alert()
Section titled “heptora.windows.alert()”heptora.windows.alert(message: str) -> NoneDescription: Shows a dialog box.
Example:
heptora.windows.alert("Process has finished. Review the results.")heptora.beta.windows.press_macro()
Section titled “heptora.beta.windows.press_macro()”heptora.beta.windows.press_macro( requiredKey1: str, requiredKey2: str, *optionalKeys: str) -> NoneDescription: Simulates complex keyboard shortcuts.
Example:
# Win+R (Run)heptora.beta.windows.press_macro("win", "r")Functions for timing control.
heptora.time.pause()
Section titled “heptora.time.pause()”heptora.time.pause(seconds: float = None) -> NoneDescription: Pauses execution for a specified time.
Parameters:
seconds(float, optional): Seconds to pause
Examples:
# 5-second pauseheptora.time.pause(5)
# Fractional pauseheptora.time.pause(1.5)
# Process default pauseheptora.time.pause()Functions to pass data between blocks.
heptora.data.set()
Section titled “heptora.data.set()”heptora.data.set(var_name: str, value: Any) -> NoneDescription: Saves a variable for later use.
Parameters:
var_name(str): Variable namevalue(Any): Value to save (any serializable type)
Examples:
# Save different data typesheptora.data.set("client_name", "Ana García")heptora.data.set("total_invoices", 42)heptora.data.set("average_price", 125.50)
# Save complex structuresuser_data = { "id": 101, "name": "Carlos", "roles": ["admin", "user"]}heptora.data.set("currentUser", user_data)
# Save listsprocessed_products = ["PROD001", "PROD002", "PROD003"]heptora.data.set("products_ok", processed_products)heptora.data.get()
Section titled “heptora.data.get()”heptora.data.get(var_name: str) -> AnyDescription: Retrieves a previously saved variable.
Parameters:
var_name(str): Variable name
Returns: The saved value, or None if it doesn’t exist
Examples:
# Retrieve simple dataname = heptora.data.get("client_name")total = heptora.data.get("total_invoices")
# Retrieve and use complex structuresuser = heptora.data.get("currentUser")if user and user["id"] == 101: heptora.log.info(f"Processing user: {user['name']}")
# With default valueinvoices = heptora.data.get("invoices") or []for invoice in invoices: # Process... passBest Practices
Section titled “Best Practices”1. Leverage Python to the Maximum
Section titled “1. Leverage Python to the Maximum”Use language features for cleaner and more robust code.
Loops and comprehensions:
# Load and filter datainvoices = heptora.excel.load("invoices.xlsx", "Sheet1", ["Number", "Amount", "Status"], True)
# Filter using list comprehensionpending = [f for f in invoices if f["Status"] == "Pending"]over_1000 = [f for f in pending if float(f["Amount"]) > 1000]
heptora.log.info(f"Pending invoices over 1000€: {len(over_1000)}")
# Process with forfor invoice in over_1000: number = invoice["Number"] amount = invoice["Amount"]
heptora.log.info(f"Processing invoice {number} for {amount}€") # Processing...Complex conditionals:
def validate_invoice(invoice): """Validates an invoice according to business rules."""
# Validate required fields if not invoice.get("Number"): return False, "Missing invoice number"
if not invoice.get("Client"): return False, "Missing client"
# Validate amounts try: amount = float(invoice.get("Amount", 0)) if amount <= 0: return False, "Amount must be greater than 0" except ValueError: return False, "Invalid amount"
# Validate status valid_statuses = ["Pending", "Paid", "Cancelled"] if invoice.get("Status") not in valid_statuses: return False, f"Invalid status. Must be: {', '.join(valid_statuses)}"
return True, "OK"
# Use the functioninvoices = heptora.data.get("invoices")for invoice in invoices: is_valid, message = validate_invoice(invoice)
if not is_valid: heptora.log.force_ko(f"Invoice {invoice['Number']}: {message}") continue
# Process valid invoice...2. Robust Error Handling
Section titled “2. Robust Error Handling”Use try/except to manage errors in a controlled way.
Basic pattern:
invoices = heptora.data.get("invoices")
for invoice in invoices: number = invoice["Number"]
try: # Attempt to process heptora.log.info(f"Processing invoice {number}")
# Code that may fail result = process_in_system(invoice)
heptora.log.result("ok", f"Invoice {number} processed correctly")
except ValueError as e: # Data error (controlled) heptora.log.result("ko", f"Invoice {number}: Data error - {str(e)}")
except ConnectionError as e: # Connectivity error (unexpected) heptora.log.result("error", f"Invoice {number}: Connection error - {str(e)}")
except Exception as e: # Any other error heptora.log.result("error", f"Invoice {number}: Unexpected error - {str(e)}")Advanced pattern with retries:
def process_with_retries(invoice, max_attempts=3): """Processes an invoice with retries in case of error."""
for attempt in range(1, max_attempts + 1): try: heptora.log.info(f"Attempt {attempt}/{max_attempts} for invoice {invoice['Number']}")
# Process result = send_to_portal(invoice)
heptora.log.result("ok", f"Invoice {invoice['Number']} processed") return True
except ConnectionError: if attempt < max_attempts: heptora.log.info(f"Connection error. Retrying in 5 seconds...") heptora.time.pause(5) else: heptora.log.result("error", f"Invoice {invoice['Number']}: Error after {max_attempts} attempts") return False
except Exception as e: # Unrecoverable error heptora.log.result("error", f"Unrecoverable error: {str(e)}") return False
return False
# Useinvoices = heptora.data.get("invoices")for invoice in invoices: process_with_retries(invoice)3. Organize Your Code with Functions
Section titled “3. Organize Your Code with Functions”For long scripts, define functions to separate logic.
def load_excel_data(file_path): """Loads and validates data from Excel file.""" heptora.log.info(f"Loading data from: {file_path}")
columns = ["Number", "Client", "Amount", "Status"] data = heptora.excel.load(file_path, "Invoices", columns, True)
heptora.log.info(f"Loaded {len(data)} invoices") return data
def validate_data(invoices): """Validates that invoices meet requirements.""" valid = [] invalid = []
for invoice in invoices: if invoice.get("Status") == "Pending" and float(invoice.get("Amount", 0)) > 0: valid.append(invoice) else: invalid.append(invoice)
heptora.log.info(f"Valid: {len(valid)}, Invalid: {len(invalid)}") return valid, invalid
def process_invoices(invoices): """Processes invoices in the system.""" successful = 0 failed = 0
for invoice in invoices: try: # Processing logic... successful += 1 heptora.log.result("ok", f"Invoice {invoice['Number']}: OK") except Exception as e: failed += 1 heptora.log.result("error", f"Invoice {invoice['Number']}: {str(e)}")
return successful, failed
def generate_report(successful, failed): """Generates a results report.""" summary = { "Total_Processed": successful, "Total_Failed": failed, "Success_Rate": f"{(successful / (successful + failed) * 100):.2f}%" }
heptora.excel.write([summary], "summary_report.xlsx", "Summary") heptora.report.add_attachment("summary_report.xlsx")
# Main script flowdef main(): # 1. Load data invoices = load_excel_data("pending_invoices.xlsx")
# 2. Validate valid, invalid = validate_data(invoices)
# 3. Process successful, failed = process_invoices(valid)
# 4. Report generate_report(successful, failed)
heptora.log.info("Process completed")
# Executemain()4. Use F-Strings for Dynamic Logs
Section titled “4. Use F-Strings for Dynamic Logs”Python’s f-strings are the cleanest way to create messages.
# ✅ Recommended: f-stringsname = "Carlos"age = 30heptora.log.info(f"Processing user: {name} (age: {age})")
# ✅ With expressionsinvoices = [1, 2, 3, 4, 5]heptora.log.info(f"Total invoices: {len(invoices)}")heptora.log.info(f"Sum of amounts: {sum(amounts):.2f}€")
# ❌ Avoid: concatenation with +heptora.log.info("Processing user: " + name + " (age: " + str(age) + ")")
# ❌ Avoid: old format()heptora.log.info("Processing user: {} (age: {})".format(name, age))5. Security in Handling Sensitive Data
Section titled “5. Security in Handling Sensitive Data”Never include passwords or keys in code.
❌ DON’T do this:
# NEVER do this!user = "admin@company.com"password = "MySecretPassword123"api_key = "sk-1234567890abcdef"✅ DO this:
# Use Heptora's secrets manageruser = heptora.data.get("portal_user")password = heptora.data.get("portal_password")api_key = heptora.data.get("api_key")
# Verify they existif not user or not password: heptora.log.force_ko("Missing credentials. Configure them in Secrets.")
# Use securelypage = heptora.browser.connect()page.goto("https://portal.com/login")page.locator("#username").fill(user)page.locator("#password").fill(password)6. Comments and Documentation
Section titled “6. Comments and Documentation”Document your code to facilitate maintenance.
"""Process: Automatic InvoicingDescription: Processes pending invoices and sends them to FACe portalAuthor: RPA TeamDate: 2024-01-15"""
import heptorafrom datetime import datetime
# ==============================================================================# CONFIGURATION# ==============================================================================INVOICES_FILE = "invoices_january.xlsx"AMOUNT_THRESHOLD = 1000.0 # Only invoices over 1000€
# ==============================================================================# HELPER FUNCTIONS# ==============================================================================
def load_configuration(): """ Loads necessary configuration and secrets.
Returns: dict: Process configuration """ return { "user": heptora.data.get("face_user"), "password": heptora.data.get("face_password"), "portal_url": "https://face.gob.es" }
def process_invoice(invoice, config): """ Processes an individual invoice.
Args: invoice (dict): Invoice data config (dict): Process configuration
Returns: bool: True if processed correctly
Raises: ValueError: If invoice is not valid ConnectionError: If there's a connection error """ # Validate invoice if not invoice.get("Number"): raise ValueError("Missing invoice number")
# Connect and process page = heptora.browser.connect() page.goto(config["portal_url"])
# ... processing logic ...
return True
# ==============================================================================# MAIN FLOW# ==============================================================================
def main(): """Main process function.""" heptora.log.info("=== START: Invoice Processing ===")
# 1. Load configuration config = load_configuration()
# 2. Load invoices invoices = heptora.excel.load( INVOICES_FILE, "Invoices", ["Number", "Client", "Amount"], True )
# 3. Filter by amount filtered_invoices = [ f for f in invoices if float(f["Amount"]) >= AMOUNT_THRESHOLD ]
heptora.log.info(f"Invoices to process: {len(filtered_invoices)}")
# 4. Process each invoice for invoice in filtered_invoices: try: process_invoice(invoice, config) heptora.log.result("ok", f"Invoice {invoice['Number']}: OK") except Exception as e: heptora.log.result("error", f"Invoice {invoice['Number']}: {str(e)}")
heptora.log.info("=== END: Process completed ===")
# Execute processif __name__ == "__main__": main()Complete Example: Advanced Invoice Process
Section titled “Complete Example: Advanced Invoice Process”This example shows a complete process with all best practices.
"""Advanced Process: Invoice Management with FACe Portal"""
import heptorafrom datetime import datetimefrom typing import List, Dict, Tuple
# ==============================================================================# CONFIGURATION# ==============================================================================
CONFIG = { "input_file": "pending_invoices.xlsx", "input_sheet": "Invoices", "columns": ["InvoiceNumber", "CIF", "Amount", "Date"], "portal_url": "https://face.gob.es", "page_timeout": 10000 # 10 seconds}
# ==============================================================================# LOADING AND VALIDATION FUNCTIONS# ==============================================================================
def load_invoices() -> List[Dict]: """Loads invoices from Excel.""" heptora.log.info("Loading invoices...")
invoices = heptora.excel.load( CONFIG["input_file"], CONFIG["input_sheet"], CONFIG["columns"], has_header=True )
heptora.log.info(f"Loaded {len(invoices)} invoices") return invoices
def validate_invoice(invoice: Dict) -> Tuple[bool, str]: """ Validates an invoice.
Returns: Tuple[bool, str]: (is_valid, error_message) """ # Validate number if not invoice.get("InvoiceNumber"): return False, "Missing invoice number"
# Validate CIF cif = invoice.get("CIF", "") if len(cif) != 9: return False, f"Invalid CIF: {cif}"
# Validate amount try: amount = float(invoice.get("Amount", 0)) if amount <= 0: return False, "Amount must be greater than 0" except (ValueError, TypeError): return False, "Invalid amount"
return True, ""
def filter_valid_invoices(invoices: List[Dict]) -> Tuple[List[Dict], List[Dict]]: """Separates valid from invalid invoices.""" valid = [] invalid = []
for invoice in invoices: is_valid, error = validate_invoice(invoice)
if is_valid: valid.append(invoice) else: invoice["_error"] = error invalid.append(invoice) heptora.log.info( f"Invoice {invoice.get('InvoiceNumber', 'NO_NUM')}: {error}" )
heptora.log.info(f"Valid: {len(valid)}, Invalid: {len(invalid)}") return valid, invalid
# ==============================================================================# WEB PROCESSING FUNCTIONS# ==============================================================================
def login_portal(page, user: str, password: str) -> bool: """Performs login to the portal.""" try: heptora.log.info("Logging into portal...")
page.goto(CONFIG["portal_url"] + "/login") page.wait_for_selector("#username", timeout=CONFIG["page_timeout"])
page.locator("#username").fill(user) page.locator("#password").fill(password) page.locator("button[type='submit']").click()
# Verify successful login page.wait_for_selector(".dashboard", timeout=CONFIG["page_timeout"])
heptora.log.info("Login successful") return True
except Exception as e: heptora.log.result("error", f"Login error: {str(e)}") return False
def process_invoice_in_portal(page, invoice: Dict) -> bool: """Processes an invoice in the web portal.""" number = invoice["InvoiceNumber"]
try: heptora.log.info(f"Processing invoice {number}...")
# Navigate to form page.goto(CONFIG["portal_url"] + "/new-invoice") page.wait_for_selector("#invoice-form", timeout=CONFIG["page_timeout"])
# Fill form page.locator("#number").fill(str(invoice["InvoiceNumber"])) page.locator("#cif").fill(str(invoice["CIF"])) page.locator("#amount").fill(str(invoice["Amount"])) page.locator("#date").fill(str(invoice["Date"]))
# Submit page.locator("button#submit").click()
# Wait for confirmation page.wait_for_selector(".success-message", timeout=CONFIG["page_timeout"])
heptora.log.result("ok", f"Invoice {number} processed correctly") return True
except Exception as e: heptora.log.result("error", f"Invoice {number}: {str(e)}") return False
# ==============================================================================# REPORTING FUNCTIONS# ==============================================================================
def generate_invalid_report(invalid: List[Dict]): """Generates report of invalid invoices.""" if not invalid: return
report = [ { "InvoiceNumber": f.get("InvoiceNumber", "NO_NUM"), "CIF": f.get("CIF", ""), "Amount": f.get("Amount", ""), "Error": f.get("_error", "") } for f in invalid ]
file = f"invalid_invoices_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" heptora.excel.write(report, file, "Invalid") heptora.report.add_attachment(file)
heptora.log.info(f"Invalid report generated: {file}")
def generate_summary_report(total: int, successful: int, failed: int, invalid: int): """Generates process summary report.""" success_rate = (successful / total * 100) if total > 0 else 0
summary = [{ "Total_Invoices": total, "Processed_OK": successful, "Processed_Error": failed, "Invalid": invalid, "Success_Rate": f"{success_rate:.2f}%", "Process_Date": datetime.now().strftime("%Y-%m-%d %H:%M:%S") }]
file = f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx" heptora.excel.write(summary, file, "Summary") heptora.report.add_attachment(file)
heptora.log.info(f"Summary generated: {file}")
# ==============================================================================# MAIN FLOW# ==============================================================================
def main(): """Main process function.""" heptora.log.info("=" * 60) heptora.log.info("START: FACe Invoicing Process") heptora.log.info("=" * 60)
# 1. LOAD DATA invoices = load_invoices() total_invoices = len(invoices)
# 2. VALIDATE DATA valid, invalid = filter_valid_invoices(invoices)
if not valid: heptora.log.force_ko("No valid invoices to process") return
# 3. CONNECT TO PORTAL page = heptora.browser.connect()
# Get credentials user = heptora.data.get("face_user") password = heptora.data.get("face_password")
if not user or not password: heptora.log.force_ko("Missing credentials. Configure them in Secrets.") return
# Login if not login_portal(page, user, password): heptora.log.force_ko("Could not log into portal") return
# 4. PROCESS VALID INVOICES successful = 0 failed = 0
for i, invoice in enumerate(valid, 1): heptora.log.info(f"Processing {i}/{len(valid)}...")
if process_invoice_in_portal(page, invoice): successful += 1 else: failed += 1
# Pause between invoices if i < len(valid): heptora.time.pause(2)
# 5. GENERATE REPORTS generate_invalid_report(invalid) generate_summary_report(total_invoices, successful, failed, len(invalid))
# 6. FINAL LOG heptora.log.info("=" * 60) heptora.log.info(f"Total invoices: {total_invoices}") heptora.log.info(f" - Processed OK: {successful}") heptora.log.info(f" - Processed with error: {failed}") heptora.log.info(f" - Invalid: {len(invalid)}") heptora.log.info("=" * 60) heptora.log.info("END: Process completed")
# ==============================================================================# ENTRY POINT# ==============================================================================
if __name__ == "__main__": try: main() except Exception as e: heptora.log.result("error", f"Fatal process error: {str(e)}") raiseComparison: Heptora Language vs Python
Section titled “Comparison: Heptora Language vs Python”Same Process in Both Languages
Section titled “Same Process in Both Languages”Heptora Language (restrictive, secure):
// Cannot use if, for, foreachList<Dictionary<string, object>> invoices = (List<Dictionary<string, object>>)heptora.data.get("invoices");
Dictionary<string, object> invoice = invoices[0];string number = (string)invoice["InvoiceNumber"];
heptora.log.info("Processing: " + number);heptora.log.result("ok", "Invoice processed");Python (flexible, powerful):
# You can use all Pythoninvoices = heptora.data.get("invoices")
for invoice in invoices: number = invoice["InvoiceNumber"]
# Use if/else if invoice["Amount"] > 1000: try: # Complex logic with try/except process_invoice(invoice) heptora.log.result("ok", f"Invoice {number} processed") except Exception as e: heptora.log.result("error", f"Error: {str(e)}") else: heptora.log.info(f"Invoice {number} skipped (low amount)")External Libraries
Section titled “External Libraries”You can use any Python library installed on the robot.
# Date manipulationfrom datetime import datetime, timedelta
current_date = datetime.now()thirty_days_ago = current_date - timedelta(days=30)
heptora.log.info(f"Current date: {current_date.strftime('%Y-%m-%d')}")
# Regular expressionsimport re
def validate_email(email): pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$' return re.match(pattern, email) is not None
# Requests for APIs (if installed)import requests
response = requests.get("https://api.example.com/data")if response.status_code == 200: data = response.json() heptora.data.set("api_data", data)Need more help?
Section titled “Need more help?”If this guide didn’t solve your problem or you found an error in the documentation:
- Technical support: help@heptora.com
- Clearly describe the problem you encountered
- Include screenshots if possible
- Indicate which documentation steps you followed
Our support team will help you resolve any issue.
Related Resources
Section titled “Related Resources”- Heptora Language - Restrictive and secure alternative
- Execution Results - Understand OK, KO, and ERROR
- Secrets Management - Store credentials
- Playwright Python - Official Playwright documentation