aboutsummaryrefslogtreecommitdiffstats
path: root/resources/tools/scripts/rename_robot_keywords.py
blob: 9f27b4aaec91b2c4b609ece4ef31ee4e454e6fa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#!/usr/bin/python

# Copyright (c) 2017 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This script renames the given robot keywords in the given directory
recursively.

Example:

  ./rename_robot_keywords.py -i kws.csv -s ";" -d ~/ws/vpp/git/csit/ -vvv

  Input file "kws.csv" is CSV file exported from e.g. MS Excel. Its structure
  must be:

    <Old keyword name><separator><New keyword name>

  One keyword per line.

"""

import argparse
import sys
import re
from os import walk, rename
from os.path import join


def time_interval(func):
    """Decorator function to measure the time spent by the decorated function.

    :param func: Decorated function.
    :type func: Callable object.
    :returns: Wrapper function.
    :rtype: Callable object.
    """

    import time

    def wrapper(*args, **kwargs):
        start = time.clock()
        result = func(*args, **kwargs)
        stop = time.clock()
        print("\nRenaming done in {:.5g} seconds\n".
              format(stop - start))
        return result
    return wrapper


def get_files(path, extension):
    """Generates the list of files to process.

    :param path: Path to files.
    :param extension: Extension of files to process. If it is the empty string,
    all files will be processed.
    :type path: str
    :type extension: str
    :returns: List of files to process.
    :rtype: list
    """

    file_list = list()
    for root, dirs, files in walk(path):
        for filename in files:
            if extension:
                if filename.endswith(extension):
                    file_list.append(join(root, filename))
            else:
                file_list.append(join(root, filename))

    return file_list


def read_keywords(args):
    """This function reads the keywords from the input file and creates:

    - a dictionary where the key is the old name and the value is the new name,
      these keywords will be further processed.
    - a list of keywords which will not be processed, typically keywords with
    argument(s) in its names.
    - a list of duplicates - duplicated keyword names or names which are parts
    of another keyword name, they will not be processed.

    :param args:  Parsed arguments.
    :type args: ArgumentParser
    :returns: keyword names - dictionary where the key is the old name and the
    value is the new name; ignored keyword names - list of keywords which will
    not be processed; duplicates - duplicated keyword names or names which are
    parts of another keyword name, they will not be processed.
    :rtype: tuple(dict, list, list)
    """

    kw_names = dict()
    ignored_kw_names = list()
    duplicates = list()

    for line in args.input:
        old_name, new_name = line.split(args.separator)
        if '$' in old_name:
            ignored_kw_names.append((old_name, new_name[:-1]))
        elif old_name in kw_names.keys():
            duplicates.append((old_name, new_name[:-1]))
        else:
            kw_names[old_name] = new_name[:-1]

    # Remove duplicates:
    for old_name, _ in duplicates:
        new_name = kw_names.pop(old_name, None)
        if new_name:
            duplicates.append((old_name, new_name))

    # Find KW names which are parts of other KW names:
    for old_name in kw_names.keys():
        count = 0
        for key in kw_names.keys():
            if old_name in key:
                count += 1
            if old_name in kw_names[key]:
                if old_name != key:
                    count += 1
        if count > 1:
            duplicates.append((old_name, kw_names[old_name]))
            kw_names.pop(old_name)

    return kw_names, ignored_kw_names, duplicates


def rename_keywords(file_list, kw_names, args):
    """Rename the keywords in specified files.

    :param file_list: List of files to be processed.
    :param kw_names: Dictionary  where the key is the old name and the value is
    the new name
    :type file_list: list
    :type kw_names: dict
    """

    kw_not_found = list()

    for old_name, new_name in kw_names.items():
        kw_found = False
        if args.verbosity > 0:
            print("\nFrom: {}\n  To: {}\n".format(old_name, new_name))
        for file_name in file_list:
            tmp_file_name = file_name + ".new"
            with open(file_name) as file_read:
                file_write = open(tmp_file_name, 'w')
                occurrences = 0
                for line in file_read:
                    new_line = re.sub(old_name, new_name, line)
                    file_write.write(new_line)
                    if new_line != line:
                        occurrences += 1
                if occurrences:
                    kw_found = True
                    if args.verbosity > 1:
                        print(" {:3d}: {}".format(occurrences, file_name))
                file_write.close()
            rename(tmp_file_name, file_name)
        if not kw_found:
            kw_not_found.append(old_name)

    if args.verbosity > 0:
        print("\nKeywords not found:")
        for item in kw_not_found:
            print("  {}".format(item))


def parse_args():
    """Parse arguments from command line.

    :returns: Parsed arguments.
    :rtype: ArgumentParser
    """

    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.
                                     RawDescriptionHelpFormatter)
    parser.add_argument("-i", "--input",
                        required=True,
                        type=argparse.FileType('r'),
                        help="Text file with the old keyword name and the new "
                             "keyword name separated by separator per line.")
    parser.add_argument("-s", "--separator",
                        default=";",
                        type=str,
                        help="Separator which separates the old and the new "
                             "keyword name.")
    parser.add_argument("-d", "--dir",
                        required=True,
                        type=str,
                        help="Directory with robot files where the keywords "
                             "should be recursively searched.")
    parser.add_argument("-v", "--verbosity", action="count",
                        help="Set the output verbosity.")
    return parser.parse_args()


@time_interval
def main():
    """Main function."""

    args = parse_args()

    kw_names, ignored_kw_names, duplicates = read_keywords(args)

    file_list = get_files(args.dir, "robot")

    if args.verbosity > 2:
        print("\nList of files to be processed:")
        for item in file_list:
            print("  {}".format(item))
        print("\n{} files to be processed.\n".format(len(file_list)))

        print("\nList of keywords to be renamed:")
        for item in kw_names:
            print("  {}".format(item))
        print("\n{} keywords to be renamed.\n".format(len(kw_names)))

    rename_keywords(file_list, kw_names, args)

    if args.verbosity >= 0:
        print("\nIgnored keywords: ({})".format(len(ignored_kw_names)))
        for old, new in ignored_kw_names:
            print("  From: {}\n    To: {}\n".format(old, new))

        print("\nIgnored duplicates ({}):".format(len(duplicates)))
        for old, new in duplicates:
            print("  From: {}\n    To: {}\n".format(old, new))


if __name__ == "__main__":
    sys.exit(main())