1
1
#!/usr/bin/env python3
2
- import os
3
2
import difflib
4
3
import json
4
+ import os
5
5
import subprocess
6
6
import sys
7
- from typing import Union , List , Tuple , Literal
7
+ from typing import List , Optional
8
+
9
+
10
+ class FileOperation :
11
+ """
12
+ # Define a type hint for the different types of file operations that can be performed.
13
+ # This Union type allows for specifying the operation (as a string literal indicating the type of action),
14
+ # and the required arguments for each operation type:
15
+ # - 'makedirs': Create a new directory; requires the path of the directory.
16
+ # - 'write_file': Write content to a file; requires the file path and the content to write.
17
+ # - 'chmod': Change the file mode; requires the file path and the new mode (as an integer).
8
18
9
- # Define a type hint for the different types of file operations that can be performed.
10
- # This Union type allows for specifying the operation (as a string literal indicating the type of action),
11
- # and the required arguments for each operation type:
12
- # - 'makedirs': Create a new directory; requires the path of the directory.
13
- # - 'write_file': Write content to a file; requires the file path and the content to write.
14
- # - 'chmod': Change the file mode; requires the file path and the new mode (as an integer).
15
- FileOperation = Union [
16
- Tuple [Literal ["makedirs" ], str ],
17
- Tuple [Literal ["write_file" ], str , str ],
18
- Tuple [Literal ["chmod" ], str , int ],
19
- ]
19
+ """
20
+
21
+ def __init__ (
22
+ self ,
23
+ operation : str ,
24
+ path : str ,
25
+ content : Optional [str ] = None ,
26
+ mode : Optional [int ] = None ,
27
+ ):
28
+ self .operation = operation
29
+ self .path = path
30
+ self .content = content
31
+ self .mode = mode
32
+
33
+ def __eq__ (self , other ):
34
+ if not isinstance (other , FileOperation ):
35
+ # don't attempt to compare against unrelated types
36
+ return NotImplemented
37
+
38
+ return (
39
+ self .operation == other .operation
40
+ and self .path == other .path
41
+ and self .content == other .content
42
+ and self .mode == other .mode
43
+ )
20
44
21
45
22
46
# TODO:(piotr1215) fallback if tree is not installed
@@ -133,37 +157,56 @@ def calculate_renaming_operations(renaming_plan):
133
157
Calculate the file operations required to execute the renaming plan.
134
158
135
159
Args:
136
- renaming_plan (list): A list of tuples representing the renaming operations required.
160
+ renaming_plan (list): A list representing the renaming operations required, where each item
161
+ is a tuple of the form (old_name, new_name).
137
162
138
163
Returns:
139
- list: A list of file operations (as tuples) to be performed for renaming.
164
+ list: A list of FileOperation objects to be performed for renaming.
140
165
"""
141
- # Execute the renaming plan
142
166
file_operations = []
143
167
for old_name , new_name in renaming_plan :
144
- # Make the new directory if it doesn't exist
145
- file_operations .append (("makedirs" , new_name ))
146
- # If it's a directory, we need to check for background.sh and foreground.sh
168
+ # Create the new directory if it doesn't exist
169
+ file_operations .append (FileOperation ("makedirs" , new_name ))
170
+
171
+ # If it's a directory, we need to check for and move necessary files
147
172
if os .path .isdir (old_name ):
173
+ # Paths for background and foreground scripts
174
+ old_background = os .path .join (old_name , "background.sh" )
175
+ new_background = os .path .join (new_name , "background.sh" )
176
+ old_foreground = os .path .join (old_name , "foreground.sh" )
177
+ new_foreground = os .path .join (new_name , "foreground.sh" )
178
+
148
179
# Check and move background.sh if it exists
149
- old_background = f"{ old_name } /background.sh"
150
- new_background = f"{ new_name } /background.sh"
151
180
if os .path .isfile (old_background ):
152
- file_operations .append (("rename" , old_background , new_background ))
181
+ file_operations .append (
182
+ FileOperation ("rename" , old_background , content = new_background )
183
+ )
184
+
153
185
# Check and move foreground.sh if it exists
154
- old_foreground = f"{ old_name } /foreground.sh"
155
- new_foreground = f"{ new_name } /foreground.sh"
156
186
if os .path .isfile (old_foreground ):
157
- file_operations .append (("rename" , old_foreground , new_foreground ))
187
+ file_operations .append (
188
+ FileOperation ("rename" , old_foreground , content = new_foreground )
189
+ )
190
+
158
191
# Rename the step markdown file
159
- old_step_md = f"{ old_name } /step{ old_name .replace ('step' , '' )} .md"
160
- new_step_md = f"{ new_name } /step{ new_name .replace ('step' , '' )} .md"
192
+ old_step_md = os .path .join (
193
+ old_name , f"step{ old_name .replace ('step' , '' )} .md"
194
+ )
195
+ new_step_md = os .path .join (
196
+ new_name , f"step{ new_name .replace ('step' , '' )} .md"
197
+ )
161
198
if os .path .isfile (old_step_md ):
162
- file_operations .append (("rename" , old_step_md , new_step_md ))
199
+ file_operations .append (
200
+ FileOperation ("rename" , old_step_md , content = new_step_md )
201
+ )
202
+
163
203
else :
164
- # If it's just a markdown file without a directory
204
+ # If it's just a markdown file without a directory, prepare to rename it
165
205
new_step_md = f"{ new_name } .md"
166
- file_operations .append (("rename" , old_name , new_step_md ))
206
+ file_operations .append (
207
+ FileOperation ("rename" , old_name , content = new_step_md )
208
+ )
209
+
167
210
return file_operations
168
211
169
212
@@ -184,25 +227,27 @@ def calculate_new_step_file_operations(
184
227
List[FileOperation]: A list of file operations that, when executed, will set up
185
228
the new step's directory, markdown file, and script files.
186
229
"""
187
- # Add the new step folder and files
188
230
new_step_folder = f"step{ insert_step_num } "
189
231
new_step_md = f"{ new_step_folder } /step{ insert_step_num } .md"
190
232
new_step_background = f"{ new_step_folder } /background.sh"
191
233
new_step_foreground = f"{ new_step_folder } /foreground.sh"
192
234
193
- file_operations : List [FileOperation ] = [("makedirs" , new_step_folder )]
194
-
195
- # Write the step markdown file
196
- file_operations .append (("write_file" , new_step_md , f"# { step_title } \n " ))
197
-
198
- # Write a simple echo command to the background and foreground scripts
199
- script_content = f'#!/bin/sh\n echo "{ step_title } script"\n '
200
-
201
- file_operations .append (("write_file" , new_step_background , script_content ))
202
- file_operations .append (("write_file" , new_step_foreground , script_content ))
203
-
204
- file_operations .append (("chmod" , new_step_background , 0o755 ))
205
- file_operations .append (("chmod" , new_step_foreground , 0o755 ))
235
+ file_operations = [
236
+ FileOperation ("makedirs" , new_step_folder ),
237
+ FileOperation ("write_file" , new_step_md , content = f"# { step_title } \n " ),
238
+ FileOperation (
239
+ "write_file" ,
240
+ new_step_background ,
241
+ content = f'#!/bin/sh\n echo "{ step_title } script"\n ' ,
242
+ ),
243
+ FileOperation (
244
+ "write_file" ,
245
+ new_step_foreground ,
246
+ content = f'#!/bin/sh\n echo "{ step_title } script"\n ' ,
247
+ ),
248
+ FileOperation ("chmod" , new_step_background , mode = 0o755 ),
249
+ FileOperation ("chmod" , new_step_foreground , mode = 0o755 ),
250
+ ]
206
251
207
252
return file_operations
208
253
@@ -272,6 +317,22 @@ def display_help():
272
317
print (help_text )
273
318
274
319
320
+ def execute_file_operations (file_operations ):
321
+ print ("Debug file operations before execution:" ) # Debugging line
322
+ for op in file_operations :
323
+ print (op ) # Debugging line
324
+ for operation in file_operations :
325
+ if operation .operation == "makedirs" :
326
+ os .makedirs (operation .path , exist_ok = True )
327
+ elif operation .operation == "write_file" :
328
+ with open (operation .path , "w" ) as file :
329
+ file .write (operation .content )
330
+ elif operation .operation == "chmod" :
331
+ os .chmod (operation .path , operation .mode )
332
+ elif operation .operation == "rename" :
333
+ os .rename (operation .path , operation .content )
334
+
335
+
275
336
def main ():
276
337
"""
277
338
This function orchestrates the entire process of adding a new step to the scenario,
@@ -281,90 +342,46 @@ def main():
281
342
and applies those changes to the file system and the index.json file.
282
343
Finally, it outputs the changes to the directory structure for the user to review.
283
344
"""
284
- if len (sys .argv ) > 1 and sys .argv [1 ] in ["-h" , "--help" ]:
285
- display_help ()
286
- sys .exit ()
287
- # Check for the presence of an 'index.json' file
288
- old_tree_structure = get_tree_structure ()
289
- directory_items = os .listdir ("." )
290
- steps_dict = get_current_steps_dict (directory_items )
291
- if not steps_dict :
292
- print (
293
- "No step files or directories found. Please run this command in a directory containing step files or directories."
345
+ try :
346
+ if len (sys .argv ) > 1 and sys .argv [1 ] in ["-h" , "--help" ]:
347
+ display_help ()
348
+ return
349
+ old_tree_structure = get_tree_structure ()
350
+ directory_items = os .listdir ("." )
351
+ steps_dict = get_current_steps_dict (directory_items )
352
+ if "index.json" not in directory_items :
353
+ print (
354
+ "The 'index.json' file is missing. Please ensure it is present in the current directory."
355
+ )
356
+ return
357
+ step_title_input = input ("Enter the title for the new step: " )
358
+ highest_step_num = max (steps_dict .keys (), default = 0 )
359
+ step_number_input = input (
360
+ f"Enter the step number to insert the new step at (1-{ highest_step_num + 1 } ): "
294
361
)
295
- sys .exit (1 )
296
- if "index.json" not in os .listdir ("." ):
297
- print (
298
- "The 'index.json' file is missing. Please ensure it is present in the current directory."
362
+ step_title , insert_step_num = get_user_input (
363
+ steps_dict , step_title_input , step_number_input
299
364
)
300
- sys . exit ( 1 )
301
- if not steps_dict :
302
- print (
303
- "No step files or directories found. Please run this command in a directory containing step files or directories."
365
+ renaming_plan = plan_renaming ( steps_dict , insert_step_num )
366
+ renaming_operations = calculate_renaming_operations ( renaming_plan )
367
+ new_step_operations = calculate_new_step_file_operations (
368
+ insert_step_num , step_title
304
369
)
305
- sys .exit (1 )
306
- step_title_input = input ("Enter the title for the new step: " )
307
- highest_step_num = max (steps_dict .keys (), default = 0 )
308
- while True :
309
- try :
310
- step_number_input = input (
311
- f"Enter the step number to insert the new step at (1-{ highest_step_num + 1 } ): "
312
- )
313
- insert_step_num = int (step_number_input )
314
- if 1 <= insert_step_num <= highest_step_num + 1 :
315
- break
316
- else :
317
- print (
318
- f"Please enter a valid step number between 1 and { highest_step_num + 1 } ."
319
- )
320
- except ValueError :
321
- print ("That's not a valid number. Please try again." )
322
- step_title , insert_step_num = get_user_input (
323
- steps_dict , step_title_input , step_number_input
324
- )
325
- renaming_plan = plan_renaming (steps_dict , insert_step_num )
326
-
327
- # Calculate the file operations for the renaming plan
328
- file_operations = calculate_renaming_operations (renaming_plan )
329
- # Execute the file operations
330
- for operation in file_operations :
331
- if operation [0 ] == "makedirs" :
332
- os .makedirs (operation [1 ], exist_ok = True )
333
- elif operation [0 ] == "rename" :
334
- os .rename (operation [1 ], operation [2 ])
335
-
336
- # Calculate the file operations for the new step
337
- new_step_operations = calculate_new_step_file_operations (
338
- insert_step_num , step_title
339
- )
340
- # Execute the file operations for the new step
341
- for operation in new_step_operations :
342
- if operation [0 ] == "makedirs" :
343
- os .makedirs (operation [1 ], exist_ok = True )
344
- elif operation [0 ] == "write_file" :
345
- with open (operation [1 ], "w" ) as file :
346
- file .write (operation [2 ])
347
- elif operation [0 ] == "chmod" :
348
- os .chmod (operation [1 ], operation [2 ])
349
-
350
- # Read the current index.json data
351
- index_file_path = "index.json"
352
- with open (index_file_path , "r" ) as index_file :
353
- current_index_data = json .load (index_file )
354
-
355
- # Calculate the updates to the index.json data
356
- updated_index_data = calculate_index_json_updates (
357
- insert_step_num , step_title , current_index_data
358
- )
359
-
360
- # Write the updated index.json data back to the file
361
- with open (index_file_path , "w" ) as index_file :
362
- json .dump (updated_index_data , index_file , ensure_ascii = False , indent = 4 )
363
- new_tree_structure = get_tree_structure ()
364
- # Print out the new file structure for confirmation
365
- tree_diff = generate_diff (old_tree_structure , new_tree_structure )
366
- print ("\n File structure changes:" )
367
- print (tree_diff , end = "" )
370
+ index_file_path = "index.json"
371
+ with open (index_file_path , "r" ) as index_file :
372
+ current_index_data = json .load (index_file )
373
+ updated_index_data = calculate_index_json_updates (
374
+ insert_step_num , step_title , current_index_data
375
+ )
376
+ execute_file_operations (renaming_operations + new_step_operations )
377
+ with open (index_file_path , "w" ) as index_file :
378
+ json .dump (updated_index_data , index_file , ensure_ascii = False , indent = 4 )
379
+ new_tree_structure = get_tree_structure ()
380
+ tree_diff = generate_diff (old_tree_structure , new_tree_structure )
381
+ print ("\n File structure changes:" )
382
+ print (tree_diff , end = "" )
383
+ except Exception as e :
384
+ print (f"An error occurred: { e } " )
368
385
369
386
370
387
if __name__ == "__main__" :
0 commit comments