examples/csv_to_kafka/main.py
Rama main · réplica de lectura del repositorio original.
"""Watch local CSV files and publish each row as a JSON message to a Kafka topic."""
import csv
import io
import json
import os
from collections.abc import AsyncIterator
from confluent_kafka.aio import AIOProducer
import cocoindex as coco
from cocoindex.connectors import kafka, localfs
from cocoindex.resources.file import FileLike, PatternFilePathMatcher
KAFKA_PRODUCER = coco.ContextKey[AIOProducer]("kafka_producer")
KAFKA_TOPIC = os.environ.get("KAFKA_TOPIC", "cocoindex-csv-rows")
KAFKA_BOOTSTRAP_SERVERS = os.environ.get("KAFKA_BOOTSTRAP_SERVERS", "localhost:9092")
KAFKA_SASL_USERNAME = os.environ.get("KAFKA_SASL_USERNAME", "")
KAFKA_SASL_PASSWORD = os.environ.get("KAFKA_SASL_PASSWORD", "")
@coco.lifespan
async def coco_lifespan(builder: coco.EnvironmentBuilder) -> AsyncIterator[None]:
config: dict[str, str] = {"bootstrap.servers": KAFKA_BOOTSTRAP_SERVERS}
if KAFKA_SASL_USERNAME:
config.update(
{
"sasl.mechanism": "PLAIN",
"security.protocol": "SASL_SSL",
"sasl.username": KAFKA_SASL_USERNAME,
"sasl.password": KAFKA_SASL_PASSWORD,
}
)
producer = AIOProducer(config)
builder.provide(KAFKA_PRODUCER, producer)
yield
@coco.fn(memo=True)
async def process_csv(file: FileLike, topic_target: kafka.KafkaTopicTarget) -> None:
text = await file.read_text()
reader = csv.DictReader(io.StringIO(text))
headers = reader.fieldnames
if not headers:
return
first_col = headers[0]
for row in reader:
key_value = row.get(first_col, None)
if key_value is not None:
value = json.dumps(row)
topic_target.declare_target_state(key=key_value, value=value)
@coco.fn
async def app_main() -> None:
topic_target = await kafka.mount_kafka_topic_target(KAFKA_PRODUCER, KAFKA_TOPIC)
files = localfs.walk_dir(
localfs.FilePath(path="./data"),
path_matcher=PatternFilePathMatcher(included_patterns=["**/*.csv"]),
live=True,
)
await coco.mount_each(process_csv, files.items(), topic_target)
app = coco.App(
coco.AppConfig(name="CsvToKafka"),
app_main,
)