docs: def aggregated_records(all_records, key_fields=KEY_FIELDS):
      """
      Yield dicts that correspond to aggregates of the flow records given by
      the sequence of FlowRecords in `all_records`. Skips incomplete records.
      This will consume the `all_records` iterator, and requires enough memory to
      be able to read it entirely.
      `key_fields` optionally contains the fields over which to aggregate. By
      default it's the typical flow 5-tuple.
      """
      flow_table = defaultdict(_FlowStats)
      for flow_record in all_records:
          key = tuple(getattr(flow_record, attr) for attr in key_fields)
          if any(x is None for x in key):
              continue
          flow_table[key].update(flow_record)
  
      for key in flow_table:
          item = {k: v for k, v in zip(key_fields, key)}
          item.update(flow_table[key].to_dict())
          yield item