Playwright selectors and utilities for rpachallenge.com automation
Project description
cpmf-rpachallenge
Playwright selectors and utilities for automating rpachallenge.com.
Installation
pip install cpmf-rpachallenge
Usage
from cpmf_rpachallenge import Downloads, FormFields, Buttons, Results
# Fetch and parse Excel data in one call
records = Downloads.get_challenge_data()
# Use with Playwright
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("https://rpachallenge.com")
# Click START
await page.click(Buttons.START)
# Fill and submit for each record
for record in records:
for field_name, value in record.as_form_data().items():
await page.fill(f'input[ng-reflect-name="{field_name}"]', value)
await page.click(Buttons.SUBMIT)
# Get results
message = await page.inner_text(Results.MESSAGE_DETAILS)
result = Results.parse_results(message)
print(f"Score: {result.success_rate}% in {result.time_seconds}s")
await browser.close()
API Reference
FormFields
Stable selectors using ng-reflect-name attributes:
FormFields.FIRST_NAMEFormFields.LAST_NAMEFormFields.PHONEFormFields.EMAILFormFields.ADDRESSFormFields.COMPANY_NAMEFormFields.ROLE
Buttons
Buttons.START- Start buttonButtons.SUBMIT- Submit buttonButtons.RESET- Reset button (same element as START)
Downloads
Downloads.fetch_excel(target_dir=None)- Download Excel to temp/custom dirDownloads.get_challenge_data()- Fetch Excel and return list ofChallengeRecordDownloads.EXCEL_URL- Direct URL to Excel file
ChallengeRecord
Dataclass with fields: first_name, last_name, company_name, role, address, email, phone
record.as_form_data()- Returns dict withng-reflect-namekeys
Results
Results.MESSAGE_DETAILS- Selector for results messageResults.parse_results(message)- Parse message intoResultData
ResultData
Dataclass with: success_rate, fields_correct, total_fields, time_ms, raw_message
result.time_seconds- Time in seconds (float)
ReadinessCheck
Verifies that the rpachallenge.com page is ready for automation before proceeding.
from cpmf_rpachallenge import ReadinessCheck
# Async usage
result = await ReadinessCheck.run_async(page)
if result.is_automatable:
# Proceed with automation
pass
else:
print(f"Page not ready: {result.summary}")
for name in result.failed_checks:
print(f" - {result.checks[name].message}")
# Sync usage
result = ReadinessCheck.run(page)
if not result.is_automatable:
raise RuntimeError(result.summary)
# Check only specific elements
result = await ReadinessCheck.run_async(page, checks=["excel_link", "start_button"])
ReadinessResult
Dataclass returned by ReadinessCheck.run() / ReadinessCheck.run_async():
is_automatable-Trueif all checks passedchecks- Dict of check name toCheckStatusfailed_checks- List of check names that failedpassed_checks- List of check names that passedsummary- Human-readable summary string
CheckStatus
Dataclass for individual check results:
name- Check name (e.g., "excel_link", "first_name")passed-Trueif element was foundselector- CSS selector usedmessage- Error message if failed,Noneif passed
ScreenshotCapture
Capture screenshots during automation with support for PNG, JPEG, and PDF formats.
from cpmf_rpachallenge import ScreenshotCapture, ScreenshotFormat
# Initialize capture
capture = ScreenshotCapture()
# Take screenshots during automation
for i, record in enumerate(records):
# Fill form...
await capture.take_async(page, label=f"form_{i}_filled")
await page.click(Buttons.SUBMIT)
# Capture result page
await capture.take_async(page, label="result", full_page=True)
# Take PDF (Chromium only)
await capture.take_pdf_async(page, label="result_pdf")
# Save all screenshots
paths = capture.collection.save_all("./screenshots")
# Create montage (requires pillow)
montage = capture.collection.create_montage(columns=5, padding=10)
Path("montage.png").write_bytes(montage)
# Create vertical strip of just form screenshots
strip = capture.collection.create_vertical_strip(
labels=["form_0_filled", "form_1_filled", "form_2_filled"]
)
Screenshot
Individual screenshot object:
data- Raw bytes of the screenshotformat-ScreenshotFormat.PNG,JPEG, orPDFlabel- User-provided labelindex- Sequential indexsave(path)- Save to fileto_pil_image()- Convert to PIL Image (PNG/JPEG only)
ScreenshotCollection
Collection with montage capabilities:
save_all(directory, prefix)- Save all screenshots to directorycreate_montage(columns, padding, background_color, labels)- Create grid imagecreate_vertical_strip(...)- Single column montagecreate_horizontal_strip(...)- Single row montageget_by_label(label)- Filter screenshots by label
ScreenshotFormat
Enum for screenshot formats:
ScreenshotFormat.PNG- Lossless, larger filesScreenshotFormat.JPEG- Lossy, smaller filesScreenshotFormat.PDF- PDF document (Chromium only)
DataValidator
Validate Excel data before automation:
from cpmf_rpachallenge import Downloads, DataValidator
records = Downloads.get_challenge_data()
result = DataValidator.validate(records)
if not result.is_valid:
print(f"Data issues: {result.summary}")
for record in result.invalid_records:
print(f" {record.summary}")
for error in record.errors:
print(f" - {error.field}: {error.message}")
# Decide: abort, skip invalid records, or proceed anyway
# Quick check
if DataValidator.is_valid(records):
# Proceed with automation
pass
DataValidationResult
Result from DataValidator.validate():
is_valid-Trueif all records valid and count matches expectedrecord_count- Number of records foundexpected_count- Expected number (default: 10)valid_count/invalid_count- Count by validityinvalid_records- List ofRecordValidationResultwith errorstotal_errors- Total error count across all recordssummary- Human-readable summary
RecordValidationResult
Validation result for a single record:
index- Record index (0-based)is_valid-Trueif record has no errorserrors- List ofFieldErrorobjectssummary- Human-readable summary
FieldError
Individual field validation error:
field- Field name (e.g., "email", "phone")value- The invalid valuemessage- Error description
Validation Rules
| Field | Rule |
|---|---|
| All fields | Required (non-empty) |
email |
Must match ^[^@\s]+@[^@\s]+\.[^@\s]+$ |
phone |
Must contain at least 7 digits |
| Record count | Must equal expected (default: 10) |
Configuration
Configuration uses a hierarchy (highest to lowest priority):
- Explicit parameters passed to functions
- Environment variables (
RPACHALLENGE_*) - Default values
Note: This library does NOT auto-load .env files. Load them in your application using python-dotenv or similar before importing this library.
Environment Variables
| Variable | Default | Description |
|---|---|---|
RPACHALLENGE_BASE_URL |
https://rpachallenge.com |
Base URL |
RPACHALLENGE_EXCEL_URL |
https://rpachallenge.com/assets/downloadFiles/challenge.xlsx |
Excel download URL |
RPACHALLENGE_HEADLESS |
true |
Run browser headless |
RPACHALLENGE_TIMEOUT_MS |
30000 |
Timeout in milliseconds |
RPACHALLENGE_DOWNLOAD_DIR |
(temp dir) | Download directory |
RPACHALLENGE_SLOW_MO |
0 |
Slow motion delay (ms) |
Using Config in Code
from cpmf_rpachallenge import get_config, RpaChallengeConfig, Downloads
# Get global config (reads from environment variables)
config = get_config()
print(f"Headless: {config.headless}")
# Create custom config directly
custom_config = RpaChallengeConfig(
headless=False,
slow_mo=100, # Slow down for debugging
)
# Use config with downloads
records = Downloads.get_challenge_data(config=custom_config)
# Override specific values from existing config
debug_config = config.with_overrides(headless=False, slow_mo=50)
Using with .env files
If you want .env support, load it in your application:
# In your application (not the library)
from dotenv import load_dotenv
load_dotenv() # Load before importing cpmf_rpachallenge
from cpmf_rpachallenge import get_config
config = get_config() # Now reads from .env via os.environ
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cpmf_rpachallenge-0.1.1.tar.gz.
File metadata
- Download URL: cpmf_rpachallenge-0.1.1.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36ec8d2ff31ee34ac45004c80d6e25c91cd4ce0ad449d079c960bbad5150600b
|
|
| MD5 |
48b05e4098ec554c4dfc8d6bfaaf66c4
|
|
| BLAKE2b-256 |
889c4eb1ad8e71498ba2d7f570b288f71e2adb1957715687aeefb32cff81994b
|
File details
Details for the file cpmf_rpachallenge-0.1.1-py3-none-any.whl.
File metadata
- Download URL: cpmf_rpachallenge-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc63d5b039ba0fa78fda61791070dea99dde4a6465f53dae2aa57195803c0c94
|
|
| MD5 |
b119aecec7d9fbafc542efa4464603dc
|
|
| BLAKE2b-256 |
020254805071e3dcfbda21e3b42904aa59111c448f496086616f382975658dcc
|