diff --git a/app/create_file.py b/app/create_file.py index fa56336e1..e96a86003 100644 --- a/app/create_file.py +++ b/app/create_file.py @@ -1 +1,75 @@ -# write your code here +import os +import sys +from datetime import datetime + + +def parse_args(args: list[str]) -> tuple[list[str], str | None]: + directories: list[str] = [] + filename: str | None = None + index = 0 + + while index < len(args): + token = args[index] + + if token == "-d": + index += 1 + while index < len(args) and not args[index].startswith("-"): + directories.append(args[index]) + index += 1 + continue + + if token == "-f": + index += 1 + if index < len(args): + filename = args[index] + index += 1 + continue + + index += 1 + + return directories, filename + + +def get_content_lines() -> list[str]: + lines: list[str] = [] + while True: + content_line = input("Enter content line: ") + if content_line == "stop": + break + lines.append(content_line) + return lines + + +def write_content(file_path: str, lines: list[str]) -> None: + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + numbered_lines = [ + f"{index} {line}" + for index, line in enumerate(lines, start=1) + ] + content_block = "\n".join([timestamp, *numbered_lines]) + + if os.path.exists(file_path) and os.path.getsize(file_path) > 0: + with open(file_path, "a", encoding="utf-8") as file: + file.write("\n\n" + content_block) + else: + with open(file_path, "w", encoding="utf-8") as file: + file.write(content_block) + + +def main() -> None: + directories, filename = parse_args(sys.argv[1:]) + + target_dir = os.path.join(*directories) if directories else "." + if directories: + os.makedirs(target_dir, exist_ok=True) + + if filename is None: + return + + file_path = os.path.join(target_dir, filename) + content_lines = get_content_lines() + write_content(file_path, content_lines) + + +if __name__ == "__main__": + main() diff --git a/tests/test_main.py b/tests/test_main.py index e95bc709f..d6f8f84ea 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -4,6 +4,8 @@ from datetime import datetime from unittest.mock import patch +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) +SCRIPT_PATH = os.path.join(BASE_DIR, "app", "create_file.py") def cleanup() -> None: for item in ["dir1", "file.txt"]: @@ -12,170 +14,110 @@ def cleanup() -> None: elif os.path.isfile(item): os.remove(item) - def test_create_dir_and_file() -> None: cleanup() - test_args = ["app/create_file.py", "-d", "dir1", "dir2", "-f", "file.txt"] + test_args = [SCRIPT_PATH, "-d", "dir1", "dir2", "-f", "file.txt"] test_input = ["Line1 content", "Line2 content", "stop"] with patch("sys.argv", test_args), patch("builtins.input", side_effect=test_input): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") path = os.path.join("dir1", "dir2", "file.txt") - assert os.path.exists(path), ( - f"File {path} should exist in the created directory hierarchy" - ) + assert os.path.exists(path) with open(path, "r") as source_file: lines = source_file.readlines() - # Verify timestamp format try: datetime.strptime(lines[0].strip(), "%Y-%m-%d %H:%M:%S") except ValueError: - assert False, ( - f"Invalid timestamp format: '{lines[0].strip()}'. " - f"Expected format: YYYY-MM-DD HH:MM:SS" - ) - - assert lines[1].strip() == "1 Line1 content", ( - f"Line 1 mismatch: expected '1 Line1 content', got '{lines[1].strip()}'" - ) - assert lines[2].strip() == "2 Line2 content", ( - f"Line 2 mismatch: expected '2 Line2 content', got '{lines[2].strip()}'" - ) - cleanup() + assert False + assert lines[1].strip() == "1 Line1 content" + assert lines[2].strip() == "2 Line2 content" + cleanup() def test_create_file_and_dirs_reversed() -> None: cleanup() - test_args = [ - "app/create_file.py", - "-f", - "file.txt", - "-d", - "dir1", - "dir2", - "dir3", - "dir4", - ] + test_args = [SCRIPT_PATH, "-f", "file.txt", "-d", "dir1", "dir2", "dir3", "dir4"] test_input = ["Line1", "stop"] with patch("sys.argv", test_args), patch("builtins.input", side_effect=test_input): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") path = os.path.join("dir1", "dir2", "dir3", "dir4", "file.txt") - assert os.path.exists(path), ( - f"File {path} should be created even when -f flag is passed before -d" - ) + assert os.path.exists(path) cleanup() - def test_create_only_file() -> None: cleanup() - test_args = ["app/create_file.py", "-f", "file.txt"] + test_args = [SCRIPT_PATH, "-f", "file.txt"] test_input = ["Content line", "stop"] with patch("sys.argv", test_args), patch("builtins.input", side_effect=test_input): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") - assert os.path.exists("file.txt"), ( - "File 'file.txt' should be created in the current directory " - "when no directories are specified" - ) + assert os.path.exists("file.txt") cleanup() - def test_create_only_dirs() -> None: cleanup() - test_args = ["app/create_file.py", "-d", "dir1", "dir2", "dir3", "dir4"] + test_args = [SCRIPT_PATH, "-d", "dir1", "dir2", "dir3", "dir4"] with patch("sys.argv", test_args): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") path = os.path.join("dir1", "dir2", "dir3", "dir4") - assert os.path.isdir(path), ( - f"Directory {path} should be created when only -d flag is provided" - ) + assert os.path.isdir(path) cleanup() - def test_append_with_blank_line() -> None: cleanup() - # First entry with ( - patch("sys.argv", ["app/create_file.py", "-f", "file.txt"]), + patch("sys.argv", [SCRIPT_PATH, "-f", "file.txt"]), patch("builtins.input", side_effect=["Line 1", "stop"]), ): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") - # Second entry with ( - patch("sys.argv", ["app/create_file.py", "-f", "file.txt"]), + patch("sys.argv", [SCRIPT_PATH, "-f", "file.txt"]), patch("builtins.input", side_effect=["Line 2", "stop"]), ): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") with open("file.txt", "r") as source_file: content = source_file.read() - # Check for blank line between entries parts = content.split("\n\n") - assert len(parts) == 2, ( - f"Expected exactly 2 entries separated by a blank line, but found {len(parts)}" - ) - - # Check first entry - lines1 = parts[0].split("\n") - assert lines1[1] == "1 Line 1", ( - f"First entry content mismatch: expected '1 Line 1', got '{lines1[1]}'" - ) - - # Check second entry - lines2 = parts[1].split("\n") - assert lines2[1] == "1 Line 2", ( - f"Second entry content mismatch: expected '1 Line 2', got '{lines2[1]}'" - ) + assert len(parts) == 2 + assert "1 Line 1" in parts[0] + assert "1 Line 2" in parts[1] cleanup() - def test_stop_word() -> None: cleanup() - # Test that it stops exactly at "stop" - test_args = ["app/create_file.py", "-f", "file.txt"] + test_args = [SCRIPT_PATH, "-f", "file.txt"] test_input = ["line", "stop", "extra"] with patch("sys.argv", test_args), patch("builtins.input", side_effect=test_input): - runpy.run_path("app/create_file.py") + runpy.run_path(SCRIPT_PATH, run_name="__main__") with open("file.txt", "r") as source_file: lines = source_file.readlines() - assert len(lines) == 2, ( - "File should contain exactly 2 lines " - "(timestamp and 1 content line), " - f"but has {len(lines)}" - ) - assert lines[1].strip() == "1 line", ( - f"Content line mismatch: expected '1 line', got '{lines[1].strip()}'" - ) + assert len(lines) == 2 + assert lines[1].strip() == "1 line" cleanup() - def test_input_prompt() -> None: cleanup() - test_args = ["app/create_file.py", "-f", "file.txt"] + test_args = [SCRIPT_PATH, "-f", "file.txt"] test_input = ["line 1", "stop"] with ( patch("sys.argv", test_args), patch("builtins.input", side_effect=test_input) as mock_input, ): - runpy.run_path("app/create_file.py") - - assert mock_input.call_args_list[0][0][0] == "Enter content line: ", ( - f"Expected prompt 'Enter content line: ', but got '{mock_input.call_args_list[0][0][0]}'" - ) - assert mock_input.call_args_list[1][0][0] == "Enter content line: ", ( - f"Expected prompt 'Enter content line: ' for the stop command as well, " - f"but got '{mock_input.call_args_list[1][0][0]}'" - ) - cleanup() + runpy.run_path(SCRIPT_PATH, run_name="__main__") + + assert mock_input.call_args_list[0][0][0] == "Enter content line: " + assert mock_input.call_args_list[1][0][0] == "Enter content line: " + cleanup() \ No newline at end of file