cswork / libraryOpen practice workspace

Coding questions · Bloomberg

Implement classes within an abstract Python framework

This question evaluates a candidate's skills in object-oriented design, implementing concrete classes from abstract interfaces, file I/O and streaming data transformations, error handling, and integration with a registry and CLI within a Python codebase.

You are given an existing Python codebase (~200 lines shown) that defines an abstract base class DataProcessor with abstract methods load(self), transform(self, record), and save(self, records); a registry/factory that instantiates processors by name; and a CLI that wires them together. Without editing the abstractions, implement a concrete CsvToJsonProcessor that: (1) reads CSV files in chunks; (2) transforms each row to a normalized JSON object; (3) writes line-delimited JSON to an output path; (4) handles bad rows with a retry/logging policy; and (5) integrates with the registry so the CLI can invoke it by the name 'csv_to_json'. Provide the class implementation outline and explain the control flow through the existing template methods.

Implement a Chunked CsvToJsonProcessor

You are working inside an existing Python framework that already defines an abstract base class DataProcessor with template methods load, transform, and save, plus a registry/factory and CLI wiring. Your task is to implement the core behavior of a concrete processor named csv_to_json.

To keep the problem self-contained and testable, you will write a function that simulates what the concrete CsvToJsonProcessor would do:

  • load: read CSV records in contiguous chunks of size chunk_size
  • transform: normalize one row at a time into a JSON-ready object
  • save: emit line-delimited JSON records in the same order as successful rows
  • bad rows: retry transformation up to max_retries times after the first failure; if the row still fails, log it by recording its 0-based row index and skip it

Normalization rules:

  1. Normalize each header by trimming whitespace, lowercasing, and replacing runs of spaces with a single underscore.
  2. The normalized header is guaranteed to contain id, name, and age, and normalized headers are unique.
  3. A row is bad if its column count does not match the header length.
  4. Required fields: id, name, age.
    • id: convert to integer
    • name: trim and collapse internal whitespace to a single space; it must not be empty
    • age: convert to integer and it must be non-negative
  5. Optional fields:
    • empty string becomes None
    • email, if present and non-empty, is lowercased
    • all other optional fields are trimmed but otherwise unchanged
  6. Output each successful record as a compact JSON string (one JSON object per line conceptually).

Return a tuple (output_lines, failed_rows) where:

  • output_lines is a list of JSON strings for successfully processed rows
  • failed_rows is a list of 0-based row indices that still failed after all retries

This captures the control flow of the framework's template methods without requiring actual file I/O.

Constraints

  • 0 <= len(rows) <= 100000
  • 1 <= len(header) <= 50
  • 1 <= chunk_size <= 10000
  • 0 <= max_retries <= 5
  • The normalized header contains the keys id, name, and age
  • Normalized header names are unique

Examples

Example 1

Input: ([' ID ', ' Name ', 'Age', ' Email '], [['1', ' Alice ', '30', 'ALICE@EXAMPLE.COM'], ['x2', 'Bob', '22', 'bob@example.com'], ['3', '  Carol   Danvers  ', '41', ''], ['4', '   ', '28', 'dave@example.com'], ['5', 'Eve', '0', 'EVE@EXAMPLE.COM']], 2, 1)
Output: (['{"id":1,"name":"Alice","age":30,"email":"alice@example.com"}', '{"id":3,"name":"Carol Danvers","age":41,"email":null}', '{"id":5,"name":"Eve","age":0,"email":"eve@example.com"}'], [1, 3])

Explanation: Rows 1 and 3 fail because id is not an integer and name becomes empty after trimming. The other rows are normalized and emitted as compact JSON.

Example 2

Input: (['id', 'name', 'age'], [], 3, 2)
Output: ([], [])

Explanation: No data rows means no output lines and no failures.

Hints

  1. Precompute the normalized column names once, then use a helper to validate and transform a single row.
  2. Process rows chunk by chunk, but keep the original row index so failed rows can be logged correctly.