1# 2# Copyright (C) 2017 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import datetime 18import logging 19import os 20import shutil 21 22def NotNoneStr(item): 23 '''Convert a veriable to string only if it is not None''' 24 return str(item) if item is not None else None 25 26class ReportFileUtil(object): 27 '''Utility class for report file saving. 28 29 Contains methods to save report files or read incremental parts of 30 report files to a destination folder and get URLs. 31 Used by profiling util, systrace util, and host log reporting. 32 33 Attributes: 34 _flatten_source_dir: bool, whether to flatten source directory 35 structure in destination directory. Current 36 implementation assumes no duplicated fine names 37 _use_destination_date_dir: bool, whether to create date directory 38 in destination directory 39 _source_dir: string, source directory that contains report files 40 _destination_dir: string, destination directory for report saving 41 _url_prefix: string, a prefix added to relative destination file paths. 42 If set to None, will use parent directory path. 43 ''' 44 45 def __init__(self, 46 flatten_source_dir=False, 47 use_destination_date_dir=False, 48 source_dir=None, 49 destination_dir=None, 50 url_prefix=None): 51 source_dir = NotNoneStr(source_dir) 52 destination_dir = NotNoneStr(destination_dir) 53 url_prefix = NotNoneStr(url_prefix) 54 55 self._flatten_source_dir = flatten_source_dir 56 self._use_destination_date_dir = use_destination_date_dir 57 self._source_dir = source_dir 58 self._destination_dir = destination_dir 59 self._url_prefix = url_prefix 60 61 def _ConvertReportPath(self, 62 src_path, 63 root_dir=None, 64 new_file_name=None, 65 file_name_prefix=None): 66 '''Convert report source file path to destination path and url. 67 68 Args: 69 src_path: string, source report file path. 70 new_file_name: string, new file name to use on destination. 71 file_name_prefix: string, prefix added to destination file name. 72 if new_file_name is set, prefix will be added 73 to new_file_name as well. 74 75 Returns: 76 tuple(string, string), containing destination path and url 77 ''' 78 root_dir = NotNoneStr(root_dir) 79 new_file_name = NotNoneStr(new_file_name) 80 file_name_prefix = NotNoneStr(file_name_prefix) 81 82 dir_path = os.path.dirname(src_path) 83 84 relative_path = os.path.basename(src_path) 85 if new_file_name: 86 relative_path = new_file_name 87 if file_name_prefix: 88 relative_path = file_name_prefix + relative_path 89 if not self._flatten_source_dir and root_dir: 90 relative_path = os.path.join( 91 os.path.relpath(dir_path, root_dir), relative_path) 92 if self._use_destination_date_dir: 93 now = datetime.datetime.now() 94 date = now.strftime('%Y-%m-%d') 95 relative_path = os.path.join(date, relative_path) 96 97 dest_path = os.path.join(self._destination_dir, relative_path) 98 99 url = dest_path 100 if self._url_prefix is not None: 101 url = self._url_prefix + relative_path 102 103 return dest_path, url 104 105 def _PushReportFile(self, src_path, dest_path): 106 '''Push a report file to destination. 107 108 Args: 109 src_path: string, source path of report file 110 dest_path: string, destination path of report file 111 ''' 112 src_path = NotNoneStr(src_path) 113 dest_path = NotNoneStr(dest_path) 114 115 parent_dir = os.path.dirname(dest_path) 116 if not os.path.exists(parent_dir): 117 try: 118 os.makedirs(parent_dir) 119 except OSError as e: 120 logging.exception(e) 121 shutil.copy(src_path, dest_path) 122 123 def SaveReport(self, src_path, new_file_name=None, file_name_prefix=None): 124 '''Save report file to destination. 125 126 Args: 127 src_path: string, source report file path. 128 new_file_name: string, new file name to use on destination. 129 file_name_prefix: string, prefix added to destination file name. 130 if new_file_name is set, prefix will be added 131 to new_file_name as well. 132 133 Returns: 134 string, destination URL of saved report file. 135 If url_prefix is set to None, will return destination path of 136 report files. If error happens during read or write operation, 137 this method will return None. 138 ''' 139 src_path = NotNoneStr(src_path) 140 new_file_name = NotNoneStr(new_file_name) 141 file_name_prefix = NotNoneStr(file_name_prefix) 142 143 try: 144 dest_path, url = self._ConvertReportPath( 145 src_path, 146 new_file_name=new_file_name, 147 file_name_prefix=file_name_prefix) 148 self._PushReportFile(src_path, dest_path) 149 150 return url 151 except IOError as e: 152 logging.exception(e) 153 154 def SaveReportsFromDirectory(self, source_dir=None, file_name_prefix=None): 155 '''Save report files from source directory to destination. 156 157 Args: 158 source_dir: string, source directory where report files are stored. 159 if None, class attribute source_dir will be used. 160 Default is None. 161 file_name_prefix: string, prefix added to destination file name 162 163 Returns: 164 A list of string, containing destination URLs of saved report files. 165 If url_prefix is set to None, will return destination path of 166 report files. If error happens during read or write operation, 167 this method will return None. 168 ''' 169 source_dir = NotNoneStr(source_dir) 170 file_name_prefix = NotNoneStr(file_name_prefix) 171 if not source_dir: 172 source_dir = self._source_dir 173 174 try: 175 urls = [] 176 177 for (dirpath, dirnames, filenames) in os.walk( 178 source_dir, followlinks=False): 179 for filename in filenames: 180 src_path = os.path.join(dirpath, filename) 181 dest_path, url = self._ConvertReportPath( 182 src_path, 183 root_dir=source_dir, 184 file_name_prefix=file_name_prefix) 185 186 #TODO(yuexima): handle duplicated destination file names 187 self._PushReportFile(src_path, dest_path) 188 urls.append(url) 189 190 return urls 191 except IOError as e: 192 logging.exception(e) 193