Home | History | Annotate | Download | only in reporting
      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 
     17 import datetime
     18 import logging
     19 import os
     20 import shutil
     21 
     22 
     23 def 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 
     28 class 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