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 22 23def NotNoneStr(item): 24 '''Convert a veriable to string only if it is not None''' 25 return str(item) if item is not None else None 26 27 28class ReportFileUtil(object): 29 '''Utility class for report file saving. 30 31 Contains methods to save report files or read incremental parts of 32 report files to a destination folder and get URLs. 33 Used by profiling util, systrace util, and host log reporting. 34 35 Attributes: 36 _flatten_source_dir: bool, whether to flatten source directory 37 structure in destination directory. Current 38 implementation assumes no duplicated fine names 39 _use_destination_date_dir: bool, whether to create date directory 40 in destination directory 41 _source_dir: string, source directory that contains report files 42 _destination_dir: string, destination directory for report saving 43 _url_prefix: string, a prefix added to relative destination file paths. 44 If set to None, will use parent directory path. 45 ''' 46 47 def __init__(self, 48 flatten_source_dir=False, 49 use_destination_date_dir=False, 50 source_dir=None, 51 destination_dir=None, 52 url_prefix=None): 53 source_dir = NotNoneStr(source_dir) 54 destination_dir = NotNoneStr(destination_dir) 55 url_prefix = NotNoneStr(url_prefix) 56 57 self._flatten_source_dir = flatten_source_dir 58 self._use_destination_date_dir = use_destination_date_dir 59 self._source_dir = source_dir 60 self._destination_dir = destination_dir 61 self._url_prefix = url_prefix 62 63 def _ConvertReportPath(self, 64 src_path, 65 root_dir=None, 66 new_file_name=None, 67 file_name_prefix=None): 68 '''Convert report source file path to destination path and url. 69 70 Args: 71 src_path: string, source report file path. 72 new_file_name: string, new file name to use on destination. 73 file_name_prefix: string, prefix added to destination file name. 74 if new_file_name is set, prefix will be added 75 to new_file_name as well. 76 77 Returns: 78 tuple(string, string), containing destination path and url 79 ''' 80 root_dir = NotNoneStr(root_dir) 81 new_file_name = NotNoneStr(new_file_name) 82 file_name_prefix = NotNoneStr(file_name_prefix) 83 84 dir_path = os.path.dirname(src_path) 85 86 relative_path = os.path.basename(src_path) 87 if new_file_name: 88 relative_path = new_file_name 89 if file_name_prefix: 90 relative_path = file_name_prefix + relative_path 91 if not self._flatten_source_dir and root_dir: 92 relative_path = os.path.join( 93 os.path.relpath(dir_path, root_dir), relative_path) 94 if self._use_destination_date_dir: 95 now = datetime.datetime.now() 96 date = now.strftime('%Y-%m-%d') 97 relative_path = os.path.join(date, relative_path) 98 99 dest_path = os.path.join(self._destination_dir, relative_path) 100 101 url = dest_path 102 if self._url_prefix is not None: 103 url = self._url_prefix + relative_path 104 105 return dest_path, url 106 107 def _PushReportFile(self, src_path, dest_path): 108 '''Push a report file to destination. 109 110 Args: 111 src_path: string, source path of report file 112 dest_path: string, destination path of report file 113 ''' 114 src_path = NotNoneStr(src_path) 115 dest_path = NotNoneStr(dest_path) 116 117 parent_dir = os.path.dirname(dest_path) 118 if not os.path.exists(parent_dir): 119 try: 120 os.makedirs(parent_dir) 121 except OSError as e: 122 logging.exception(e) 123 shutil.copy(src_path, dest_path) 124 125 def SaveReport(self, src_path, new_file_name=None, file_name_prefix=None): 126 '''Save report file to destination. 127 128 Args: 129 src_path: string, source report file path. 130 new_file_name: string, new file name to use on destination. 131 file_name_prefix: string, prefix added to destination file name. 132 if new_file_name is set, prefix will be added 133 to new_file_name as well. 134 135 Returns: 136 string, destination URL of saved report file. 137 If url_prefix is set to None, will return destination path of 138 report files. If error happens during read or write operation, 139 this method will return None. 140 ''' 141 src_path = NotNoneStr(src_path) 142 new_file_name = NotNoneStr(new_file_name) 143 file_name_prefix = NotNoneStr(file_name_prefix) 144 145 try: 146 dest_path, url = self._ConvertReportPath( 147 src_path, 148 new_file_name=new_file_name, 149 file_name_prefix=file_name_prefix) 150 self._PushReportFile(src_path, dest_path) 151 152 return url 153 except IOError as e: 154 logging.exception(e) 155 156 def SaveReportsFromDirectory(self, 157 source_dir=None, 158 file_name_prefix=None, 159 file_path_filters=None): 160 '''Save report files from source directory to destination. 161 162 Args: 163 source_dir: string, source directory where report files are stored. 164 if None, class attribute source_dir will be used. 165 Default is None. 166 file_name_prefix: string, prefix added to destination file name 167 file_path_filter: function, a functions that return True (pass) or 168 False (reject) given original file path. 169 170 Returns: 171 A list of string, containing destination URLs of saved report files. 172 If url_prefix is set to None, will return destination path of 173 report files. If error happens during read or write operation, 174 this method will return None. 175 ''' 176 source_dir = NotNoneStr(source_dir) 177 file_name_prefix = NotNoneStr(file_name_prefix) 178 if not source_dir: 179 source_dir = self._source_dir 180 181 try: 182 urls = [] 183 184 for (dirpath, dirnames, filenames) in os.walk( 185 source_dir, followlinks=False): 186 for filename in filenames: 187 src_path = os.path.join(dirpath, filename) 188 dest_path, url = self._ConvertReportPath( 189 src_path, 190 root_dir=source_dir, 191 file_name_prefix=file_name_prefix) 192 193 if file_path_filters and not file_path_filters(src_path): 194 continue 195 196 #TODO(yuexima): handle duplicated destination file names 197 self._PushReportFile(src_path, dest_path) 198 urls.append(url) 199 200 return urls 201 except IOError as e: 202 logging.exception(e) 203