import logging import re # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s - %(message)s", ) logger = logging.getLogger(__name__) INVALID_INPUT_IP_VALUES = {"", "nan", "null", "none", "n/a", "0.0.0.0", "unknown"} BLOCKED_CONNECTION_TYPES = {"mobile wireless", "tx"} BLOCKED_ISP_PATTERN = re.compile( r"cloudflare|fastly|icloud|private relay|google llc|amazon technologies|datacamp|microsoft" ) def _normalize_string(value) -> str: if value is None: return "" return str(value).strip().lower() def _normalize_count(value) -> int: if value is None: return 0 try: return int(float(value)) except (TypeError, ValueError): return 0 def _calculate_s4(data: dict) -> int: customer_type = _normalize_string(data.get("application_customer_type")) input_ip_address = _normalize_string(data.get("input_ip_address")) connection_type = _normalize_string(data.get("input_ip_connection_type")) isp = _normalize_string(data.get("input_ip_isp")) distinct_ssn = _normalize_count(data.get("input_ip_distinct_ssn_24h")) distinct_zip = _normalize_count(data.get("input_ip_distinct_zip_24h")) if customer_type != "direct new": return 0 if input_ip_address in INVALID_INPUT_IP_VALUES: return 0 if connection_type in BLOCKED_CONNECTION_TYPES: return 0 if BLOCKED_ISP_PATTERN.search(isp): return 0 if distinct_ssn < 3 or distinct_zip < 2: return 0 return min(1200, 1191 + (distinct_ssn - 3) + (distinct_zip - 2)) def processing(data: dict) -> dict: try: hd_score_s1 = ( min(1225 + (data["cluster_size_users_v2"] * 5), 1390) if data["cluster_size_users_v2"] >= 3 and data["hd_score_m1"] >= 1140 else 0 ) logger.info(f"score_s1 calculated: {hd_score_s1}") except Exception as e: logger.error(f"Error processing score_s1 calculations: {e}") return {} try: hd_score_s2 = ( min(1215 + (data["cluster_size_users_v2"] * 5), 1380) if data["cluster_size_users_v2"] >= 2 and data["app_dt_day_cnt"] == 1 else 0 ) logger.info(f"score_s2 calculated: {hd_score_s2}") except Exception as e: logger.error(f"Error processing score_s2 calculations: {e}") return {} try: target_connected_30_sum = data.get("target_connected_30_sum", 0) or 0 # Handling None case hd_score_s3 = ( min(1250 + (target_connected_30_sum * 5), 1400) if target_connected_30_sum >= 1 else 0 ) logger.info(f"score_s3 calculated: {hd_score_s3}") except Exception as e: logger.error(f"Error processing score_s3 calculations: {e}") return {} try: hd_score_s4 = _calculate_s4(data) logger.info(f"score_s4 calculated: {hd_score_s4}") except Exception as e: logger.error(f"Error processing score_s4 calculations: {e}") return {} # Return the final results as a dictionary return { "hd_score_m1": data["hd_score_m1"], "hd_score_g1": data["hd_score_g1"], "hd_score_s1": hd_score_s1, "hd_score_s2": hd_score_s2, "hd_score_s3": hd_score_s3, "hd_score_s4": hd_score_s4, "hd_score_iso_m2": data["hd_score_iso_m2"], "hd_score_g2": data["hd_score_g2"] }