| import logging |
| from typing import List, Dict, Any, Optional |
| import pandas as pd |
| import numpy as np |
|
|
| |
| logging.basicConfig( |
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", |
| level=logging.INFO |
| ) |
| logger = logging.getLogger("csv_metadata_service") |
|
|
| def extract_csv_metadata_logic(csv_url: str) -> List[Dict[str, Any]]: |
| """ |
| Analyzes a CSV file from a URL to infer column metadata. |
| |
| Args: |
| csv_url (str): The public URL of the CSV file. |
| |
| Returns: |
| List[Dict[str, Any]]: A list of dictionaries containing field metadata. |
| """ |
| try: |
| logger.info(f"Extracting metadata from: {csv_url}") |
| |
| |
| |
| |
| df = pd.read_csv(csv_url, nrows=5, on_bad_lines='skip') |
| |
| fields = [] |
| |
| for col in df.columns: |
| dtype = df[col].dtype |
| |
| |
| generic_type = 'string' |
| |
| |
| if pd.api.types.is_integer_dtype(dtype): |
| generic_type = 'integer' |
| elif pd.api.types.is_float_dtype(dtype): |
| generic_type = 'decimal' |
| elif pd.api.types.is_bool_dtype(dtype): |
| generic_type = 'boolean' |
| elif pd.api.types.is_datetime64_any_dtype(dtype): |
| generic_type = 'date' |
| else: |
| |
| |
| if len(df) > 0: |
| try: |
| first_valid = df[col].dropna().iloc[0] if not df[col].dropna().empty else "" |
| val_str = str(first_valid).strip() |
| |
| |
| if val_str and not val_str.isdigit(): |
| pd.to_datetime(val_str) |
| generic_type = 'date' |
| except (ValueError, TypeError): |
| |
| pass |
|
|
| |
| is_nullable = bool(df[col].isnull().any()) |
| |
| |
| lower_name = col.lower() |
| potential_names = ['id', 'uuid', '_id', 'pk'] |
| |
| |
| is_pk_name = lower_name in potential_names or lower_name.endswith('_id') |
| |
| |
| is_unique = df[col].is_unique if not df.empty else False |
| |
| is_primary_key = is_pk_name and is_unique |
|
|
| fields.append({ |
| "name": col, |
| "type": generic_type, |
| "nullable": is_nullable, |
| "isPrimaryKey": is_primary_key |
| }) |
| |
| logger.info(f"Successfully extracted {len(fields)} fields.") |
| return fields |
|
|
| except Exception as e: |
| logger.error(f"CSV Analysis Error for {csv_url}: {str(e)}") |
| |
| raise e |