FactorsAiAutomation/main.py
2026-07-01 18:00:54 +05:30

137 lines
5.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
main.py — Entry point for the Factors.AI account-export automation.
Usage
─────
python main.py # uses settings in config.py / .env
python main.py --visible # opens a real browser window (debug)
python main.py --dry-run # skip email; just download & compare
Schedule (cron example — runs every day at 8 AM)
─────────────────────────────────────────────────
0 8 * * * /path/to/venv/bin/python /path/to/factors_automation/main.py
"""
import argparse
import logging
import shutil
import sys
from pathlib import Path
# ── Logging (set up before any other import) ──────────────────────────────────
from config import LOG_PATH # noqa: E402 needs to be early
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)-8s] %(name)s%(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler(str(LOG_PATH), encoding="utf-8"),
],
)
logger = logging.getLogger(__name__)
# ── Application imports ───────────────────────────────────────────────────────
from config import ( # noqa: E402
FACTORS_EMAIL,
FACTORS_PASSWORD,
TARGET_PROJECT,
TEMP_DOWNLOAD_DIR,
LAST_CSV_PATH,
SMTP_HOST,
SMTP_PORT,
SENDER_EMAIL,
SENDER_PASSWORD,
RECIPIENT_EMAILS,
FIELDS_TO_SELECT,
HEADLESS,
)
from scraper import FactorsAIScraper
from comparator import find_new_entries
from mailer import send_alert
# ── CLI arguments ─────────────────────────────────────────────────────────────
def _parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description="Factors.AI account-export automation")
p.add_argument(
"--visible",
action="store_true",
help="Open a real browser window instead of running headless",
)
p.add_argument(
"--dry-run",
action="store_true",
help="Download and compare but do NOT send the email",
)
return p.parse_args()
# ── Main flow ─────────────────────────────────────────────────────────────────
def main() -> None:
args = _parse_args()
headless = HEADLESS and not args.visible
logger.info("=" * 65)
logger.info(" Factors.AI Account Export Automation — START")
logger.info(f" headless={headless} dry_run={args.dry_run}")
logger.info("=" * 65)
# ── Step 1 ─ Download CSV via Selenium ────────────────────────────────────
logger.info("STEP 1: Downloading CSV …")
scraper = FactorsAIScraper(
download_dir=str(TEMP_DOWNLOAD_DIR),
headless=headless,
)
downloaded_csv = scraper.run(
email=FACTORS_EMAIL,
password=FACTORS_PASSWORD,
target_project=TARGET_PROJECT,
fields_to_select=FIELDS_TO_SELECT,
)
logger.info(f" Downloaded → {downloaded_csv}")
# ── Step 2 ─ Compare with last export ────────────────────────────────────
logger.info("STEP 2: Comparing with previous export …")
last_csv = str(LAST_CSV_PATH) if LAST_CSV_PATH.exists() else None
new_entries, is_first_run = find_new_entries(downloaded_csv, last_csv)
if new_entries.empty and not is_first_run:
logger.info(" ✓ No new entries found — nothing to mail.")
else:
count = len(new_entries)
tag = "(first run — all entries)" if is_first_run else f"({count} new)"
logger.info(f" ✓ New entries detected {tag}")
# ── Step 3 ─ Send email ───────────────────────────────────────────────
if args.dry_run:
logger.info("STEP 3: [DRY RUN] Email skipped.")
logger.info(new_entries.to_string())
else:
logger.info("STEP 3: Sending alert email …")
send_alert(
smtp_host=SMTP_HOST,
smtp_port=SMTP_PORT,
sender_email=SENDER_EMAIL,
sender_password=SENDER_PASSWORD,
recipients=RECIPIENT_EMAILS,
new_entries=new_entries,
is_first_run=is_first_run,
downloaded_csv=downloaded_csv
)
logger.info(" ✓ Email sent.")
# ── Step 4 ─ Persist the new CSV as last_export.csv ─────────────────────
logger.info(f"STEP 4: Saving export as last_export.csv → {LAST_CSV_PATH}")
shutil.copy2(downloaded_csv, str(LAST_CSV_PATH))
logger.info("=" * 65)
logger.info(" Automation complete ✓")
logger.info("=" * 65)
if __name__ == "__main__":
main()