Update of VPP_STABLE_VER files + fix Centos dep list
[csit.git] / resources / tools / scripts / rename_robot_keywords.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2017 Cisco and/or its affiliates.
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 """This script renames the given robot keywords in the given directory
17 recursively.
18
19 Example:
20
21   ./rename_robot_keywords.py -i kws.csv -s ";" -d ~/ws/vpp/git/csit/ -vvv
22
23   Input file "kws.csv" is CSV file exported from e.g. MS Excel. Its structure
24   must be:
25
26     <Old keyword name><separator><New keyword name>
27
28   One keyword per line.
29
30 """
31
32 import argparse
33 import sys
34 import re
35 from os import walk, rename
36 from os.path import join
37
38
39 def time_interval(func):
40     """Decorator function to measure the time spent by the decorated function.
41
42     :param func: Decorated function.
43     :type func: Callable object.
44     :returns: Wrapper function.
45     :rtype: Callable object.
46     """
47
48     import time
49
50     def wrapper(*args, **kwargs):
51         start = time.clock()
52         result = func(*args, **kwargs)
53         stop = time.clock()
54         print("\nRenaming done in {:.5g} seconds\n".
55               format(stop - start))
56         return result
57     return wrapper
58
59
60 def get_files(path, extension):
61     """Generates the list of files to process.
62
63     :param path: Path to files.
64     :param extension: Extension of files to process. If it is the empty string,
65     all files will be processed.
66     :type path: str
67     :type extension: str
68     :returns: List of files to process.
69     :rtype: list
70     """
71
72     file_list = list()
73     for root, dirs, files in walk(path):
74         for filename in files:
75             if extension:
76                 if filename.endswith(extension):
77                     file_list.append(join(root, filename))
78             else:
79                 file_list.append(join(root, filename))
80
81     return file_list
82
83
84 def read_keywords(args):
85     """This function reads the keywords from the input file and creates:
86
87     - a dictionary where the key is the old name and the value is the new name,
88       these keywords will be further processed.
89     - a list of keywords which will not be processed, typically keywords with
90     argument(s) in its names.
91     - a list of duplicates - duplicated keyword names or names which are parts
92     of another keyword name, they will not be processed.
93
94     :param args:  Parsed arguments.
95     :type args: ArgumentParser
96     :returns: keyword names - dictionary where the key is the old name and the
97     value is the new name; ignored keyword names - list of keywords which will
98     not be processed; duplicates - duplicated keyword names or names which are
99     parts of another keyword name, they will not be processed.
100     :rtype: tuple(dict, list, list)
101     """
102
103     kw_names = dict()
104     ignored_kw_names = list()
105     duplicates = list()
106
107     for line in args.input:
108         old_name, new_name = line.split(args.separator)
109         if '$' in old_name:
110             ignored_kw_names.append((old_name, new_name[:-1]))
111         elif old_name in kw_names.keys():
112             duplicates.append((old_name, new_name[:-1]))
113         else:
114             kw_names[old_name] = new_name[:-1]
115
116     # Remove duplicates:
117     for old_name, _ in duplicates:
118         new_name = kw_names.pop(old_name, None)
119         if new_name:
120             duplicates.append((old_name, new_name))
121
122     # Find KW names which are parts of other KW names:
123     for old_name in kw_names.keys():
124         count = 0
125         for key in kw_names.keys():
126             if old_name in key:
127                 count += 1
128             if old_name in kw_names[key]:
129                 if old_name != key:
130                     count += 1
131         if count > 1:
132             duplicates.append((old_name, kw_names[old_name]))
133             kw_names.pop(old_name)
134
135     return kw_names, ignored_kw_names, duplicates
136
137
138 def rename_keywords(file_list, kw_names, args):
139     """Rename the keywords in specified files.
140
141     :param file_list: List of files to be processed.
142     :param kw_names: Dictionary  where the key is the old name and the value is
143     the new name
144     :type file_list: list
145     :type kw_names: dict
146     """
147
148     kw_not_found = list()
149
150     for old_name, new_name in kw_names.items():
151         kw_found = False
152         if args.verbosity > 0:
153             print("\nFrom: {}\n  To: {}\n".format(old_name, new_name))
154         for file_name in file_list:
155             tmp_file_name = file_name + ".new"
156             with open(file_name) as file_read:
157                 file_write = open(tmp_file_name, 'w')
158                 occurrences = 0
159                 for line in file_read:
160                     new_line = re.sub(old_name, new_name, line)
161                     file_write.write(new_line)
162                     if new_line != line:
163                         occurrences += 1
164                 if occurrences:
165                     kw_found = True
166                     if args.verbosity > 1:
167                         print(" {:3d}: {}".format(occurrences, file_name))
168                 file_write.close()
169             rename(tmp_file_name, file_name)
170         if not kw_found:
171             kw_not_found.append(old_name)
172
173     if args.verbosity > 0:
174         print("\nKeywords not found:")
175         for item in kw_not_found:
176             print("  {}".format(item))
177
178
179 def parse_args():
180     """Parse arguments from command line.
181
182     :returns: Parsed arguments.
183     :rtype: ArgumentParser
184     """
185
186     parser = argparse.ArgumentParser(description=__doc__,
187                                      formatter_class=argparse.
188                                      RawDescriptionHelpFormatter)
189     parser.add_argument("-i", "--input",
190                         required=True,
191                         type=argparse.FileType('r'),
192                         help="Text file with the old keyword name and the new "
193                              "keyword name separated by separator per line.")
194     parser.add_argument("-s", "--separator",
195                         default=";",
196                         type=str,
197                         help="Separator which separates the old and the new "
198                              "keyword name.")
199     parser.add_argument("-d", "--dir",
200                         required=True,
201                         type=str,
202                         help="Directory with robot files where the keywords "
203                              "should be recursively searched.")
204     parser.add_argument("-v", "--verbosity", action="count",
205                         help="Set the output verbosity.")
206     return parser.parse_args()
207
208
209 @time_interval
210 def main():
211     """Main function."""
212
213     args = parse_args()
214
215     kw_names, ignored_kw_names, duplicates = read_keywords(args)
216
217     file_list = get_files(args.dir, "robot")
218
219     if args.verbosity > 2:
220         print("\nList of files to be processed:")
221         for item in file_list:
222             print("  {}".format(item))
223         print("\n{} files to be processed.\n".format(len(file_list)))
224
225         print("\nList of keywords to be renamed:")
226         for item in kw_names:
227             print("  {}".format(item))
228         print("\n{} keywords to be renamed.\n".format(len(kw_names)))
229
230     rename_keywords(file_list, kw_names, args)
231
232     if args.verbosity >= 0:
233         print("\nIgnored keywords: ({})".format(len(ignored_kw_names)))
234         for old, new in ignored_kw_names:
235             print("  From: {}\n    To: {}\n".format(old, new))
236
237         print("\nIgnored duplicates ({}):".format(len(duplicates)))
238         for old, new in duplicates:
239             print("  From: {}\n    To: {}\n".format(old, new))
240
241
242 if __name__ == "__main__":
243     sys.exit(main())