Sau khi xác định hoặc tạo một báo cáo đáp ứng nhu cầu của bạn, bạn có thể tạo đầu ra. Đầu ra của báo cáo được lưu trữ trong các tệp báo cáo. Bạn có thể truy xuất và thao tác các tệp này theo phương thức lập trình. Tệp báo cáo được tạo ra khi bạn chạy một 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 biết mã nhận dạng của báo cáo. Nếu bạn 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ề này để tra cứu sau.
Nếu không biết mã báo cáo mà bạn muốn chạy, bạn có thể xem danh sách tất 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 một 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 tham 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 thứ tự danh sách báo cáo được trả về. Việc kiểm soát cách sắp xếp và thứ tự của danh sách này có thể đặc biệt hữu ích khi bạn muốn tìm 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 một 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 một tệp báo cáo mới. Bạn có thể chạy báo cáo đồ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 xử lý. Hãy 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ộ so với báo cáo không đồng bộ.
Để chạy báo cáo, bạn sẽ gọi 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 tệp. 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à tệp sẽ sẵn sàng để tải xuống. Tuy nhiên, vì đây là một yêu cầu chạy không đồng bộ, nên một số trường khoá 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 thì báo cáo chạy xong?
Khi bạn chạy báo cáo không đồng bộ, một tệp giữ chỗ sẽ được tạo ngay lập tức và báo cáo sẽ được đưa vào hàng đợi để xử lý. Trình giữ chỗ này sẽ chứa 2 thông tin chính giúp bạn xác định thời điểm báo cáo chạy xong:
- Một trường
idmà bạn có thể dùng để tham chiếu tệp này trong các yêu cầu tiếp theo. - Trường
status, đại diện cho trạng thái hiện tại của quá trình chạy báo cáo.
Để xác định thời điểm báo cáo đã chạy xong, bạn cần định kỳ kiểm tra 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 chuyển thành REPORT_AVAILABLE, tức là tệp đã 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 lũy thừa khi thăm dò ý kiến theo cách này để tối ưu hoá mức sử dụng hạn mức yêu cầu.