Sau khi xác định hoặc tạo một báo cáo đáp ứng nhu cầu của mình, bạn nên tạo kết quả đầ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 theo phương thức lập trình. Tệp báo cáo được tạo sau khi chạy báo cáo.
Hướng dẫn này trình bày chi tiết cách tạo 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 phải biết mã của báo cáo. Nếu vừa tạo hoặc cập nhật một báo cáo, thì 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ề để tra cứu sau này.
Nếu không biết ID của báo cáo 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 bạn muốn. Ví dụ dưới đây minh hoạ 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));
PHP
$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 được trả về. Việc kiểm soát việc sắp xếp và sắp xếp danh sách này có thể đặc biệt hữu ích khi tìm thấy các báo cáo đã được sửa đổi gần đây.
Chạy báo cáo
Sau khi 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ạn có thể chạy báo cáo một cách đồng bộ hoặc không đồng bộ (mặc định), tuỳ thuộc vào mức độ phức tạp của báo cáo và thời gian xử lý. Xem hướng dẫn về Báo cáo đồng bộ để biết thông tin chi tiết về báo cáo đồng bộ và không đồng bộ.
Để chạy báo cáo, bạn cầ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ụ bên dưới:
C#
// Run the report.
File file = service.Reports.Run(profileId, reportId).Execute();
Java
// Run the report.
File file = reporting.reports().run(profileId, reportId).execute();
PHP
// 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à một tài nguyên Files. Nếu đây là một yêu cầu chạy đồng bộ thành công, thì 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 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 chạy xong?
Khi bạn chạy một báo cáo theo cách không đồng bộ, tệp phần giữ chỗ sẽ được tạo ngay lập tức và báo cáo sẽ được đưa vào hàng đợi để được xử lý. Phần giữ chỗ sẽ chứa 2 thông tin chính để giúp bạn xác định thời điểm chạy xong báo cáo:
- Trường
id
có thể dùng để tham chiếu tệp này trong các yêu cầu tiếp theo. - Trường
status
thể hiện trạng thái hiện tại của báo cáo đang chạy.
Để xác định thời điểm 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);
PHP
// 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
, bạn có thể tải tệp xuống. Như minh hoạ 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.