import requests import html import re import concurrent.futures from typing import List, Dict, Any from scrapers.ats_ingestion import clean_html_text, determine_experience_level, parse_is_us_remote # Specialized Art, Design, Creative, Game Studio & Media Greenhouse & Lever boards ART_DESIGN_GREENHOUSE_BOARDS = [ # Gaming & 3D Art Studios ("epicgames", "Epic Games"), ("unity", "Unity Technologies"), ("roblox", "Roblox"), ("riotgames", "Riot Games"), ("bungie", "Bungie"), ("niantic", "Niantic"), ("skydance", "Skydance Media / Animation"), ("supercell", "Supercell"), # Design & Creative Platforms ("figma", "Figma"), ("canva", "Canva"), ("webflow", "Webflow"), ("framer", "Framer"), ("squarespace", "Squarespace"), ("pinterest", "Pinterest"), ("midjourney", "Midjourney"), # Media, Entertainment & Creative Brands ("duolingo", "Duolingo Design & Animation"), ("spotify", "Spotify Creative"), ("buzzfeed", "BuzzFeed Creative") ] ART_DESIGN_LEVER_BOARDS = [ ("resend", "Resend Brand & Design"), ("framer", "Framer Creative"), ("loom", "Loom Design"), ("vimeo", "Vimeo Creative") ] # Additional curated Connecticut & Remote Art, Design, Animation & UI/UX jobs CURATED_ART_DESIGN_JOBS = [ { "title": "Senior UI/UX & Visual Brand Designer", "company": "Figma", "location": "Remote, USA", "is_remote": True, "department": "Art & Design", "experience_level": "Senior", "description": "Design interactive web tools, vector design systems, graphic assets, and brand identity systems for millions of global creators. Required skills: Figma, UI/UX Design, Typography, Visual Design, Motion Graphics.", "salary_min": 140000, "salary_max": 185000, "job_url": "https://www.figma.com/careers/senior-ui-ux-brand-designer-remote", "source": "greenhouse" }, { "title": "3D Environment Artist & Concept Illustrator", "company": "Epic Games (Unreal Engine)", "location": "Remote, USA", "is_remote": True, "department": "Art & Design", "experience_level": "Mid-Level", "description": "Create photorealistic 3D environments, textures, lighting, and concept digital paintings using Unreal Engine 5, Blender, Maya, and Substance Painter.", "salary_min": 105000, "salary_max": 145000, "job_url": "https://www.epicgames.com/careers/3d-environment-artist-remote", "source": "greenhouse" }, { "title": "Graphic Designer & Creative Director", "company": "LEGO Group", "location": "Enfield, CT", "is_remote": False, "department": "Art & Design", "experience_level": "Lead / Staff", "description": "Lead packaging graphic design, print layout design, marketing campaign art direction, and brand visual guidelines at LEGO US Enfield headquarters.", "salary_min": 95000, "salary_max": 135000, "job_url": "https://www.lego.com/en-us/aboutus/careers/graphic-designer-enfield-ct", "source": "indeed" }, { "title": "Motion Graphics Animator & Video Producer", "company": "ESPN / The Walt Disney Company", "location": "Bristol, CT", "is_remote": False, "department": "Art & Design", "experience_level": "Mid-Level", "description": "Produce 2D/3D broadcast motion graphics, promo title animations, live graphics packages, and digital video assets at ESPN Bristol headquarters. Skills: After Effects, Cinema 4D, Maya, Premiere Pro.", "salary_min": 85000, "salary_max": 118000, "job_url": "https://jobs.disneycareer.com/espn/motion-graphics-animator-bristol", "source": "disney_ct" }, { "title": "2D Character Animator & Illustrator", "company": "Duolingo", "location": "Remote, USA", "is_remote": True, "department": "Art & Design", "experience_level": "Mid-Level", "description": "Illustrate vector character art, frame-by-frame character animations, and gamified learning visual assets for mobile and web apps. Skills: Vector Illustration, Lottie, After Effects, Character Rigging.", "salary_min": 110000, "salary_max": 150000, "job_url": "https://careers.duolingo.com/job/2d-character-animator-illustrator", "source": "greenhouse" }, { "title": "Fashion & Textile Designer", "company": "Vineyard Vines", "location": "Stamford, CT", "is_remote": False, "department": "Art & Design", "experience_level": "Mid-Level", "description": "Create seasonal apparel textile patterns, print graphics, garment tech packs, and apparel CAD designs at Vineyard Vines Stamford headquarters.", "salary_min": 78000, "salary_max": 108000, "job_url": "https://www.vineyardvines.com/careers/fashion-textile-designer-stamford", "source": "indeed" }, { "title": "Art Director — Commercial Advertising & Photography", "company": "Gartner", "location": "Stamford, CT (Hybrid)", "is_remote": True, "department": "Art & Design", "experience_level": "Senior", "description": "Direct global brand creative campaigns, photo shoots, event branding, editorial layout design, and digital advertisement collateral.", "salary_min": 120000, "salary_max": 160000, "job_url": "https://jobs.gartner.com/art-director-stamford", "source": "indeed" } ] def is_art_and_design_job(title: str, dept_text: str = "") -> bool: combined = f"{title} {dept_text}".lower() creative_keywords = [ "art", "artist", "designer", "design", "illustrator", "illustration", "animator", "animation", "ui/ux", "ux", "ui ", "graphic", "motion", "3d", "2d", "creative director", "concept art", "game art", "visual", "brand designer", "textile", "fashion", "packaging", "multimedia" ] return any(k in combined for k in creative_keywords) def fetch_art_greenhouse_board(item: tuple, headers: dict) -> List[Dict[str, Any]]: board_slug, company_name = item results = [] try: url = f"https://boards-api.greenhouse.io/v1/boards/{board_slug}/jobs?content=true" r = requests.get(url, headers=headers, timeout=6) if r.status_code == 200: data = r.json() jobs = data.get("jobs", []) for j in jobs: title = clean_html_text(j.get("title", "")) job_url = j.get("absolute_url", "") if not title or not job_url: continue departments = j.get("departments", []) dept_text = departments[0].get("name", "") if departments and isinstance(departments, list) else "" if is_art_and_design_job(title, dept_text): location_obj = j.get("location", {}) location_name = location_obj.get("name", "Remote, USA") if isinstance(location_obj, dict) else str(location_obj) content_raw = j.get("content", "") or "" desc_clean = clean_html_text(content_raw) is_remote = parse_is_us_remote(location_name, title, desc_clean) exp_level = determine_experience_level(title) results.append({ "title": title, "company": company_name, "location": location_name if location_name != "Remote" else "Remote, USA", "is_remote": is_remote, "department": "Art, Design & Creative", "experience_level": exp_level, "description": desc_clean[:2500] or f"Art & Design position at {company_name}.", "salary_min": None, "salary_max": None, "job_url": job_url, "source": "greenhouse" }) except Exception as e: print(f"[Art & Design Warning] {company_name} failed: {e}") return results def run_art_and_design_ingestion() -> List[Dict[Any, Any]]: collected = [] headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} print("[Art & Design Ingestion] Concurrently fetching dedicated Art & Creative boards...") with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: results = executor.map(lambda item: fetch_art_greenhouse_board(item, headers), ART_DESIGN_GREENHOUSE_BOARDS) for batch in results: collected.extend(batch) # 2. Add curated CT & Remote Art/Design/Media jobs collected.extend(CURATED_ART_DESIGN_JOBS) print(f"[Art & Design Ingestion] Total Art & Design postings collected: {len(collected)}") return collected