You now need to handle a very large batch of independent image-processing jobs. Each image is still represented only by metadata `(width, height, mode)`, where `mode` is `'RGB'` or `'GRAY'`.
Unlike Part 1, every job must read from the original `files` snapshot only. No job may depend on the output of another job.
Implement a process-based batch processor that:
- uses process parallelism for throughput
- returns one result record per input job
- preserves the original job order in the returned results
- isolates failures so one bad job does not stop the rest
- returns a deterministic final file map
Transformations are the same:
- `('grayscale',)`
- `('scale', f)` -> `width = int(width * f)`, `height = int(height * f)`
- `('resize', w, h)`
Failure rules are also the same:
- `'missing_input'`
- `'invalid_operation'`
- `'invalid_dimensions'`
To keep the final file map deterministic, successful outputs are committed in input order after processing completes. If multiple successful jobs write the same `output_path`, the later job in the input list wins.
Examples
Input: ({'a': (10, 20, 'RGB')}, [{'input_path': 'a', 'output_path': 'b', 'operations': [('grayscale',)]}, {'input_path': 'b', 'output_path': 'c', 'operations': [('resize', 4, 4)]}], 2)
Expected Output: ([('ok', 'b', (10, 20, 'GRAY')), ('error', 'missing_input')], {'a': (10, 20, 'RGB'), 'b': (10, 20, 'GRAY')})
Explanation: Jobs are independent, so the second job cannot read `b` from the first job's output.
Input: ({'x': (4, 4, 'RGB'), 'y': (8, 2, 'RGB')}, [{'input_path': 'x', 'output_path': 'z', 'operations': [('scale', 2)]}, {'input_path': 'y', 'output_path': 'z', 'operations': [('grayscale',)]}], 2)
Expected Output: ([('ok', 'z', (8, 8, 'RGB')), ('ok', 'z', (8, 2, 'GRAY'))], {'x': (4, 4, 'RGB'), 'y': (8, 2, 'RGB'), 'z': (8, 2, 'GRAY')})
Explanation: Both jobs succeed, but the later one wins for `z` when outputs are committed in input order.
Input: ({'p': (1, 1, 'RGB'), 'q': (2, 3, 'RGB')}, [{'input_path': 'p', 'output_path': 'bad', 'operations': [('scale', 0.4)]}, {'input_path': 'q', 'output_path': 'good', 'operations': [('resize', 5, 1), ('grayscale',)]}], 3)
Expected Output: ([('error', 'invalid_dimensions'), ('ok', 'good', (5, 1, 'GRAY'))], {'p': (1, 1, 'RGB'), 'q': (2, 3, 'RGB'), 'good': (5, 1, 'GRAY')})
Explanation: One job fails, but the other still succeeds.
Input: ({'img': (2, 2, 'GRAY')}, [], 4)
Expected Output: ([], {'img': (2, 2, 'GRAY')})
Explanation: Empty batch edge case.