Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## 0.1.0

### Added
- A command for converting from JSON to YAML and a flag for the reverse action: from YAML to JSON.
- The command to convert an image to base64 format.
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ This is the tool's CLI for converting various formats.
## Using

Format Fusion supports two commands for conversion:
- JSON to YAML - `format-fusion yaml`
- Image to Base64 - `format-fusion image`


| Name | Commands |
|-----------------|-----------------------|
| JSON to YAML | `format-fusion yaml` |
| Image to Base64 | `format-fusion image` |

### Usage example
Command to generate from JSON to YAML:

``
```shell
format-fusion yaml D:\response_api.json
``
```

Or you can perform the conversion in reverse order: **from YAML to JSON**

```shell
format-fusion yaml D:\response_api.yaml --reverse
```

The result of executing the command will be the generation of a YAML named `output.yaml`
The result of executing the command will be the creation of a YAML file named `output.yaml` or a JSON file named `output.json`

Optionally, you can specify where to save the converted files:

Expand Down
27 changes: 12 additions & 15 deletions formatfusion/commands/cmd_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,36 @@
--output <output_path> Path to save file [default: output.txt]
"""
import logging
import os
import typing as t
from pathlib import Path

from formatfusion.converter import Converter
from ..core.image import ConverterImage

logger = logging.getLogger(__name__)


def run(opts):
def run(opts: t.Dict[str, t.Any]):
logger.info("Start converting..")
return run_convert(opts)


def get_image_path(opts):
def get_image_path(opts: t.Dict[str, t.Any]) -> Path:
opt_image_path = opts["<path>"]
image_path = os.path.abspath(opt_image_path)
if not os.path.exists(image_path):
image_path = Path(opt_image_path).resolve()
if not image_path.exists():
raise FileNotFoundError(f"File not found: {image_path}.")
return image_path


def get_output_path(opts):
def get_output_path(opts: t.Dict[str, t.Any]) -> Path:
opt_file_path = opts["--output"] if opts["--output"] is not None else "output.txt"
file_path = os.path.abspath(opt_file_path)
file_path = Path(opt_file_path).resolve()
return file_path


def run_convert(opts):
def run_convert(opts: t.Dict[str, t.Any]) -> None:
image_file = get_image_path(opts)
output_path = get_output_path(opts)

convert = Converter(input_file=image_file)
base64_image = convert.convert_image_to_base64()

with open(output_path, "w") as file:
file.write(base64_image)
logger.info(f"The converted image was saved in {output_path}")
convert = ConverterImage(input_file=image_file, output_file=output_path)
convert.convert_image_to_base64()
63 changes: 36 additions & 27 deletions formatfusion/commands/cmd_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,59 @@
format-fusion [g-opts] yaml <path> [options]

Arguments:
<path> Path to JSON file
<path> Path to JSON or YAML file

Options:
--output <output_path> Path to save YAML file [default: output.yaml]
--output <output_path> Path to save YAML or JSON file
--reverse A flag that allows you to convert from YAML to JSON
"""
import logging
import os
import typing as t
from pathlib import Path

from formatfusion.converter import Converter
from formatfusion.helpers import validate_files

from ..core.json_and_yaml import ConverterYAMLandJSON

logger = logging.getLogger(__name__)


def run(opts):
def run(opts: t.Dict[str, t.Any]):
logger.info("Start converting..")
return run_convert(opts)


def get_json_path(opts):
opt_json_path = opts["<path>"]
json_path = os.path.abspath(opt_json_path)
if not os.path.exists(json_path):
raise FileNotFoundError(f"File not found: {json_path}.")
return json_path
def get_input_file_path(opts: t.Dict[str, t.Any]) -> Path:
opt_input_path = opts["<path>"]
input_path = Path(opt_input_path).resolve()
if not input_path.exists():
raise FileNotFoundError(f"File not found: {input_path}.")
return input_path


def get_output_path(opts):
opt_yaml_path = opts["--output"] if opts["--output"] is not None else "output.yaml"
yaml_path = os.path.abspath(opt_yaml_path)
return yaml_path
def get_output_file_path(opts: t.Dict[str, t.Any], input_file: Path) -> Path:
if opts["--output"] is not None:
output_path = Path(opts["--output"]).resolve()
else:
if input_file.suffix == ".json":
default_output = input_file.parent / "output.yaml"
elif input_file.suffix == ".yaml":
default_output = input_file.parent / "output.json"
else:
raise ValueError(f"Unsupported input file extension: {input_file.suffix}.")
output_path = default_output.resolve()

return output_path

def run_convert(opts) -> None:
json_file = get_json_path(opts)
yaml_file = get_output_path(opts)
if not validate_files(json_file, yaml_file):
return

convert = Converter(input_file=json_file, output_file=yaml_file)
yaml_string = convert.convert_json_to_yaml()
def run_convert(opts: t.Dict[str, t.Any]) -> None:
input_file = get_input_file_path(opts)
output_file = get_output_file_path(opts, input_file)
if not validate_files(input_file, output_file):
return

with open(yaml_file, "w", encoding="utf-8") as file:
file.write(yaml_string)
logger.info(
f"The JSON from {json_file} was converted to YAML and saved in {yaml_file}"
)
convert = ConverterYAMLandJSON(input_file=input_file, output_file=output_file)
if opts["--reverse"]:
convert.convert_yaml_to_json()
else:
convert.convert_json_to_yaml()
22 changes: 0 additions & 22 deletions formatfusion/converter.py

This file was deleted.

Empty file added formatfusion/core/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions formatfusion/core/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import logging
import typing as t
from pathlib import Path

logger = logging.getLogger(__name__)


class Base:
def __init__(self, input_file: Path, output_file: Path | None = None):
self.input_file = input_file
self.output_file = output_file

def save_result(self, result: str, success_message: str) -> None:
if not self.output_file:
raise ValueError("Output file is not specified.")

with open(self.output_file, "w", encoding="utf-8") as file:
file.write(result)

logger.info(success_message)
12 changes: 12 additions & 0 deletions formatfusion/core/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import base64

from .base import Base


class ConverterImage(Base):
def convert_image_to_base64(self) -> None:
with open(self.input_file, "rb") as image:
result = base64.b64encode(image.read()).decode("utf-8")
self.save_result(
result, f"The converted image was saved in {self.output_file}"
)
25 changes: 25 additions & 0 deletions formatfusion/core/json_and_yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import json

import yaml

from .base import Base


class ConverterYAMLandJSON(Base):
def convert_json_to_yaml(self) -> None:
with open(self.input_file, "r", encoding="utf-8") as file:
json_dict = json.load(file)
result = yaml.dump(json_dict, sort_keys=False)
self.save_result(
result,
f"The JSON from {self.input_file} was converted to YAML and saved in {self.output_file}",
)

def convert_yaml_to_json(self) -> None:
with open(self.input_file, "r", encoding="utf-8") as file:
yaml_dict = yaml.safe_load(file)
result = json.dumps(yaml_dict, indent=4, ensure_ascii=False)
self.save_result(
result,
f"The YAML from {self.input_file} was converted to JSON and saved in {self.output_file}",
)
7 changes: 6 additions & 1 deletion formatfusion/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

logger = logging.getLogger(__name__)

VALID_EXTENSIONS = {"json", "png", "jpg"}
VALID_EXTENSIONS = {"json", "yaml", "png", "jpg"}


def get_file_extension(filename: str):
Expand All @@ -28,4 +28,9 @@ def validate_files(input_file, output_file) -> bool:
)
return False

if input_extension == "yaml" and get_file_extension(output_file) != "json":
logger.error(
"For a YAML input file, the output file must have the extension '.json'."
)
return False
return True
2 changes: 1 addition & 1 deletion formatfusion/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from formatfusion import commands

logging.basicConfig(
level=logging.DEBUG, # todo: поменять уровень на INFO после всех работ
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler()],
)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "cli-format-fusion"
version = "0.0.1"
version = "0.1.0"
description = "A mini conversion tool"
authors = ["R00kie <driftworks2013@yandex.ru>"]
packages = [
Expand Down
31 changes: 22 additions & 9 deletions tests/test_cmd_image.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from formatfusion.commands.cmd_image import get_image_path, get_output_path, run_convert
Expand All @@ -9,7 +10,7 @@
class TestFormatFusionImage(unittest.TestCase):
@patch("formatfusion.commands.cmd_image.get_image_path")
@patch("formatfusion.commands.cmd_image.get_output_path")
@patch("formatfusion.converter.Converter.convert_image_to_base64")
@patch("formatfusion.core.image.ConverterImage.convert_image_to_base64")
def test_run_convert_success(
self, mock_convert_image_to_base64, mock_get_output_path, mock_get_image_path
):
Expand All @@ -27,9 +28,6 @@ def test_run_convert_success(
run_convert(opts)

mock_convert_image_to_base64.assert_called_once()
with open(output_path, "r") as file:
content = file.read()
self.assertEqual(content, "base64_encoded_data")

os.remove(image_path)
os.remove(output_path)
Expand All @@ -39,22 +37,37 @@ def test_get_image_path_valid(self):
temp_image_path = temp_image.name
opts = {"<path>": temp_image_path}
result = get_image_path(opts)
self.assertEqual(result, os.path.abspath(temp_image_path))
self.assertEqual(result, Path(temp_image_path))
os.remove(temp_image_path)

def test_get_image_path_invalid(self):
opts = {"<path>": "nonexistent.png"}
with self.assertRaises(FileNotFoundError):
get_image_path(opts)

def test_get_output_path(self):
opts = {"--output": "custom_output.txt"}
def test_with_output_specified(self):
opts = {"--output": "/tmp/specified_output.txt"}
result = get_output_path(opts)
self.assertEqual(result, os.path.abspath("custom_output.txt"))
expected = Path("/tmp/specified_output.txt").resolve()
self.assertEqual(result, expected)

def test_with_no_output_specified(self):
opts = {"--output": None}
result = get_output_path(opts)
self.assertEqual(result, os.path.abspath("output.txt"))
expected = Path("output.txt").resolve()
self.assertEqual(result, expected)

def test_relative_path_output(self):
opts = {"--output": "relative_output.txt"}
result = get_output_path(opts)
expected = Path("relative_output.txt").resolve()
self.assertEqual(result, expected)

def test_default_output(self):
opts = {"--output": "output.txt"}
result = get_output_path(opts)
expected = Path("output.txt").resolve()
self.assertEqual(result, expected)

@patch(
"formatfusion.commands.cmd_image.get_image_path",
Expand Down
Loading
Loading