Tìm và Chạy Báo cáo

Khi bạn đã xác định hoặc tạo báo cáo đáp ứng nhu cầu của mình, đã đến lúc tạo đầu ra. Kết quả báo cáo được lưu trữ trong các tệp báo cáo, có thể được truy xuất và thao tác bằng cách lập trình. Tệp báo cáo được tạo do chạy báo cáo.

Hướng dẫn này trình bày chi tiết cách tạo các tệp báo cáo theo phương thức lập trình thông qua dịch vụ Báo cáo.

Tìm báo cáo

Để chạy báo cáo, bạn cần biết ID của báo cáo. Nếu bạn vừa tạo hoặc cập nhật báo cáo, bạn có thể tìm thấy giá trị này trong trường id của tài nguyên báo cáo được trả về. Người dùng nên lưu trữ các mã nhận dạng được trả về này để tra cứu sau.

Nếu bạn không biết ID báo cáo mà bạn muốn chạy, bạn có thể xem qua danh sách tất cả các báo cáo có sẵn để tìm báo cáo mà bạn muốn. Ví dụ bên dưới minh họa cách tra cứu báo cáo theo một số tiêu chí do người dùng xác định:

C#

Report target = null;
ReportList reports;
String nextPageToken = null;

do {
  // Create and execute the reports list request.
  ReportsResource.ListRequest request = service.Reports.List(profileId);
  request.PageToken = nextPageToken;
  reports = request.Execute();

  foreach (Report report in reports.Items) {
    if (IsTargetReport(report)) {
      target = report;
      break;
    }
  }

  // Update the next page token.
  nextPageToken = reports.NextPageToken;
} while (target == null
    && reports.Items.Any()
    && !String.IsNullOrEmpty(nextPageToken));

Java

Report target = null;
ReportList reports;
String nextPageToken = null;

do {
  // Create and execute the reports list request.
  reports = reporting.reports().list(profileId).setPageToken(nextPageToken).execute();

  for (Report report : reports.getItems()) {
    if (isTargetReport(report)) {
      target = report;
      break;
    }
  }

  // Update the next page token.
  nextPageToken = reports.getNextPageToken();
} while (target == null
    && !reports.getItems().isEmpty()
    && !Strings.isNullOrEmpty(nextPageToken));

1.199

$target = null;
$response = null;
$pageToken = null;

do {
    // Create and execute the report list request.
    $response = $this->service->reports->listReports(
        $userProfileId,
        ['pageToken' => $pageToken]
    );

    foreach ($response->getItems() as $report) {
        if ($this->isTargetReport($report)) {
            $target = $report;
            break;
        }
    }

    $pageToken = $response->getNextPageToken();
} while (empty($target) && !empty($response->getItems()) && !empty($pageToken));

Python

target = None

# Construct the request.
request = service.reports().list(profileId=profile_id)

while True:
  response = request.execute()

  for report in response['items']:
    if is_target_report(report):
      target = report
      break

  if not target and response['items'] and response['nextPageToken']:
    request = service.reports().list_next(request, response)
  else:
    break

Ruby

page_token = nil
target = nil

loop do
  result = service.list_reports(profile_id, page_token: page_token)

  result.items.each do |report|
    if target_report?(report)
      target = report
      break
    end
  end

  page_token = (result.next_page_token if target.nil? && result.items.any?)
  break if page_token.to_s.empty?
end

Hãy tham khảo tài liệu tham khảo để biết các thông số không bắt buộc mà bạn có thể chỉ định để kiểm soát cách sắp xếp và sắp xếp danh sách các báo cáo trả về. Kiểm soát sắp xếp và sắp xếp danh sách này có thể đặc biệt hữu ích để tìm các báo cáo đã được sửa đổi gần đây.

Chạy báo cáo

Khi bạn đã tìm thấy báo cáo phù hợp, bạn có thể sử dụng dịch vụ Báo cáo để chạy báo cáo và tạo tệp báo cáo mới. Báo cáo có thể được chạy đồng bộ hoặc không đồng bộ (mặc định), tuỳ thuộc vào độ phức tạp của báo cáo và thời gian cần để xử lý. Xem hướng dẫn về Báo cáo đồng bộ để biết thêm chi tiết về báo cáo đồng bộ so với không đồng bộ.

Để chạy báo cáo, bạn thực hiện lệnh gọi đến phương thức chạy của dịch vụ Báo cáo, như trong ví dụ dưới đây:

C#

// Run the report.
File file = service.Reports.Run(profileId, reportId).Execute();

Java

// Run the report.
File file = reporting.reports().run(profileId, reportId).execute();

1.199

// Run the report.
$file = $this->service->reports->run($userProfileId, $reportId);

Python

# Run the report.
report_file = service.reports().run(
    profileId=profile_id, reportId=report_id).execute()

Ruby

# Run the report.
report_file = service.run_report(profile_id, report_id)

Phản hồi cho yêu cầu này là Tài nguyên tệp. Nếu đây là một yêu cầu chạy đồng bộ thành công, tất cả các trường của tài nguyên được trả về sẽ được điền vào và tệp sẽ sẵn sàng để tải xuống. Tuy nhiên, vì đây là yêu cầu chạy không đồng bộ, nên một số trường chính nhất định sẽ bị thiếu và status của tệp sẽ được đặt thành PROCESSING, vì báo cáo chưa chạy xong.

Khi nào báo cáo được chạy xong?

Khi bạn chạy báo cáo một cách không đồng bộ, tệp trình giữ chỗ được tạo ngay lập tức và báo cáo được đưa vào hàng đợi để xử lý. Trình giữ chỗ sẽ chứa hai phần thông tin chính để giúp bạn xác định khi nào báo cáo đã chạy xong:

  1. Một trường id có thể dùng để tham chiếu đến tệp này trong các yêu cầu tiếp theo.
  2. Trường status đại diện cho trạng thái hiện tại của lần chạy báo cáo.

Để xác định thời điểm một báo cáo chạy xong, bạn cần kiểm tra định kỳ status của tệp, như trong ví dụ bên dưới:

C#

// Wait for the report file to finish processing.
// An exponential backoff policy is used to limit retries and conserve quota.
int sleep = 0;
int startTime = GetCurrentTimeInSeconds();
do {
  File file = service.Files.Get(reportId, fileId).Execute();

  if ("REPORT_AVAILABLE".Equals(file.Status)) {
    Console.WriteLine("File status is {0}, ready to download.", file.Status);
    return;
  } else if (!"PROCESSING".Equals(file.Status)) {
    Console.WriteLine("File status is {0}, processing failed.", file.Status);
    return;
  } else if (GetCurrentTimeInSeconds() - startTime > MAX_RETRY_ELAPSED_TIME) {
    Console.WriteLine("File processing deadline exceeded.");
    return;
  }

  sleep = GetNextSleepInterval(sleep);
  Console.WriteLine("File status is {0}, sleeping for {1} seconds.", file.Status, sleep);
  Thread.Sleep(sleep * 1000);
} while (true);

Java

BackOff backOff =
    new ExponentialBackOff.Builder()
        .setInitialIntervalMillis(10 * 1000) // 10 second initial retry
        .setMaxIntervalMillis(10 * 60 * 1000) // 10 minute maximum retry
        .setMaxElapsedTimeMillis(60 * 60 * 1000) // 1 hour total retry
        .build();

do {
  File file = reporting.files().get(reportId, fileId).execute();

  if ("REPORT_AVAILABLE".equals(file.getStatus())) {
    // File has finished processing.
    System.out.printf("File status is %s, ready to download.%n", file.getStatus());
    return file;
  } else if (!"PROCESSING".equals(file.getStatus())) {
    // File failed to process.
    System.out.printf("File status is %s, processing failed.", file.getStatus());
    return null;
  }

  // The file hasn't finished processing yet, wait before checking again.
  long retryInterval = backOff.nextBackOffMillis();
  if (retryInterval == BackOff.STOP) {
    System.out.println("File processing deadline exceeded.%n");
    return null;
  }

  System.out.printf("File status is %s, sleeping for %dms.%n", file.getStatus(), retryInterval);
  Thread.sleep(retryInterval);
} while (true);

1.199

// Wait for the report file to finish processing.
// An exponential backoff policy is used to limit retries and conserve
// quota.
$sleep = 0;
$startTime = time();

do {
    $file = $this->service->files->get($reportId, $fileId);

    if ($file->getStatus() === 'REPORT_AVAILABLE') {
        printf('File status is %s, ready to download<br>', $file->getStatus());
        return $file;
    } elseif ($file->getStatus() !== 'PROCESSING') {
        printf('File status is %s, processing failed<br>', $file->getStatus());
        return null;
    } elseif (time() - $startTime > self::MAX_RETRY_ELAPSED_TIME) {
        printf('File processing deadline exceeded<br>');
        return null;
    }

    $sleep = $this->getNextSleepInterval($sleep);
    printf(
        'File status is %s, sleeping for %d seconds<br>',
        $file->getStatus(),
        $sleep
    );
    $this->sleep($sleep);
} while (true);

Python

# Wait for the report file to finish processing.
# An exponential backoff strategy is used to conserve request quota.
sleep = 0
start_time = time.time()
while True:
  report_file = service.files().get(
      reportId=report_id, fileId=file_id).execute()

  status = report_file['status']
  if status == 'REPORT_AVAILABLE':
    print 'File status is %s, ready to download.' % status
    return
  elif status != 'PROCESSING':
    print 'File status is %s, processing failed.' % status
    return
  elif time.time() - start_time > MAX_RETRY_ELAPSED_TIME:
    print 'File processing deadline exceeded.'
    return

  sleep = next_sleep_interval(sleep)
  print 'File status is %s, sleeping for %d seconds.' % (status, sleep)
  time.sleep(sleep)

Ruby

# Wait for the report file to finish processing.
# An exponential backoff strategy is used to conserve request quota.
interval = 0
start_time = Time.now
loop do
  report_file = service.get_file(report_id, file_id)

  status = report_file.status
  if status == 'REPORT_AVAILABLE'
    puts format('File status is %s, ready to download.', status)
    break
  elsif status != 'PROCESSING'
    puts format('File status is %s, processing failed.', status)
    break
  elsif Time.now - start_time > MAX_RETRY_ELAPSED_TIME
    puts 'File processing deadline exceeded.'
    break
  end

  interval = next_sleep_interval(interval)
  puts format('File status is %s, sleeping for %d seconds.', status,
    interval)
  sleep(interval)
end

Khi status thay đổi thành REPORT_AVAILABLE, tệp sẽ sẵn sàng để tải xuống. Như trong ví dụ trên, bạn nên triển khai chiến lược thời gian đợi luỹ thừa khi thăm dò ý kiến như thế này để tối ưu hoá việc sử dụng hạn mức yêu cầu.