mirror of
https://github.com/ivuorinen/aeonview.git
synced 2026-02-15 17:47:59 +00:00
chore(lint): fix linting
This commit is contained in:
@@ -17,3 +17,10 @@ indent_size = 4
|
|||||||
|
|
||||||
[Makefile]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
max_line_length = 100
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
max_line_length = 100
|
||||||
|
|||||||
6
.github/CODE_OF_CONDUCT.md
vendored
6
.github/CODE_OF_CONDUCT.md
vendored
@@ -15,12 +15,14 @@ welcoming, diverse, inclusive, and healthy community.
|
|||||||
## Our Standards
|
## Our Standards
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment:
|
Examples of behavior that contributes to a positive environment:
|
||||||
|
|
||||||
- Demonstrating empathy and kindness
|
- Demonstrating empathy and kindness
|
||||||
- Being respectful of differing opinions
|
- Being respectful of differing opinions
|
||||||
- Gracefully accepting constructive feedback
|
- Gracefully accepting constructive feedback
|
||||||
- Focusing on what is best for the community
|
- Focusing on what is best for the community
|
||||||
|
|
||||||
Examples of unacceptable behavior:
|
Examples of unacceptable behavior:
|
||||||
|
|
||||||
- The use of sexualized language or imagery
|
- The use of sexualized language or imagery
|
||||||
- Trolling, insulting or derogatory comments
|
- Trolling, insulting or derogatory comments
|
||||||
- Harassment of any kind
|
- Harassment of any kind
|
||||||
@@ -52,6 +54,4 @@ version 2.1.
|
|||||||
|
|
||||||
[cc]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/
|
[cc]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/
|
||||||
|
|
||||||
<!--
|
<!-- vim: ft=md sw=2 ts=2 tw=72 fo=cqt wm=0 et : -->
|
||||||
vim: ft=md sw=2 ts=2 tw=72 fo=cqt wm=0 et
|
|
||||||
-->
|
|
||||||
|
|||||||
9
.github/CONTRIBUTING.md
vendored
9
.github/CONTRIBUTING.md
vendored
@@ -1,6 +1,7 @@
|
|||||||
# Contributing to aeonview
|
# Contributing to aeonview
|
||||||
|
|
||||||
Thanks for your interest in contributing to **aeonview**! This guide will help you get started.
|
Thanks for your interest in contributing to **aeonview**!
|
||||||
|
This guide will help you get started.
|
||||||
|
|
||||||
## 🛠 Project Setup
|
## 🛠 Project Setup
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ make lint # check for lint errors
|
|||||||
Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
|
Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/):
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- `feat: add monthly video generation`
|
- `feat: add monthly video generation`
|
||||||
- `fix: handle invalid date error`
|
- `fix: handle invalid date error`
|
||||||
- `docs: update usage instructions`
|
- `docs: update usage instructions`
|
||||||
@@ -76,10 +78,9 @@ Examples:
|
|||||||
We expect contributors to follow our [Code of Conduct](CODE_OF_CONDUCT.md).
|
We expect contributors to follow our [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
## Questions?
|
## Questions?
|
||||||
|
|
||||||
Feel free to open an issue or start a discussion!
|
Feel free to open an issue or start a discussion!
|
||||||
|
|
||||||
Thanks for helping make Aeonview better 💜
|
Thanks for helping make Aeonview better 💜
|
||||||
|
|
||||||
<!--
|
<!-- vim: ft=md sw=2 ts=2 tw=72 fo=cqt wm=0 et :-->
|
||||||
vim: ft=md sw=2 ts=2 tw=72 fo=cqt wm=0 et
|
|
||||||
-->
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
```
|
```markdown
|
||||||
_)
|
_)
|
||||||
_` | -_) _ \ \ \ \ / | -_) \ \ \ /
|
_` | -_) _ \ \ \ \ / | -_) \ \ \ /
|
||||||
\__,_| \___| \___/ _| _| \_/ _| \___| \_/\_/
|
\__,_| \___| \___/ _| _| \_/ _| \___| \_/\_/
|
||||||
aeonview - a simple timelapse tool
|
aeonview - a simple timelapse tool
|
||||||
```
|
```
|
||||||
|
|
||||||
**aeonview** is a Python-based tool for generating timelapse videos
|
**aeonview** is a Python-based tool for generating timelapse videos
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ def test_mkdir_p_creates_directory():
|
|||||||
|
|
||||||
@pytest.mark.parametrize("ext", ["jpg", "png", "gif", "webp"])
|
@pytest.mark.parametrize("ext", ["jpg", "png", "gif", "webp"])
|
||||||
def test_get_extension_valid(ext):
|
def test_get_extension_valid(ext):
|
||||||
assert AeonViewHelpers.get_extension(f"{default_image_domain}.{ext}") == f".{ext}"
|
assert AeonViewHelpers.get_extension(
|
||||||
|
f"{default_image_domain}.{ext}") == f".{ext}"
|
||||||
|
|
||||||
|
|
||||||
def test_get_extension_invalid():
|
def test_get_extension_invalid():
|
||||||
@@ -93,20 +94,25 @@ def test_get_image_paths_valid():
|
|||||||
paths = aeon_view_images.get_image_paths(url, destination_base, date)
|
paths = aeon_view_images.get_image_paths(url, destination_base, date)
|
||||||
assert paths["url"] == url
|
assert paths["url"] == url
|
||||||
assert paths["file"] == "12-30-45.jpg"
|
assert paths["file"] == "12-30-45.jpg"
|
||||||
assert paths["destinations"]["file"] == destination_base / "2025-04" / "10" / "12-30-45.jpg"
|
assert paths["destinations"][
|
||||||
|
"file"] == destination_base / "2025-04" / "10" / "12-30-45.jpg"
|
||||||
|
|
||||||
|
|
||||||
def test_get_image_paths_invalid_url():
|
def test_get_image_paths_invalid_url():
|
||||||
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
|
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
|
||||||
aeon_view_images = AeonViewImages(default_test_path, "invalid-url")
|
aeon_view_images = AeonViewImages(default_test_path, "invalid-url")
|
||||||
aeon_view_images.get_image_paths("invalid-url", default_test_path, datetime(2025, 4, 10))
|
aeon_view_images.get_image_paths("invalid-url", default_test_path,
|
||||||
|
datetime(2025, 4, 10))
|
||||||
assert AeonViewMessages.INVALID_URL in log.call_args[0][0]
|
assert AeonViewMessages.INVALID_URL in log.call_args[0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_get_image_paths_invalid_date():
|
def test_get_image_paths_invalid_date():
|
||||||
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
|
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
|
||||||
aeon_view_images = AeonViewImages(default_test_path, f"{default_image_domain}.jpg")
|
aeon_view_images = AeonViewImages(default_test_path,
|
||||||
aeon_view_images.get_image_paths(f"{default_image_domain}.jpg", default_test_path, "invalid-date") # pyright: ignore [reportArgumentType]
|
f"{default_image_domain}.jpg")
|
||||||
|
aeon_view_images.get_image_paths(f"{default_image_domain}.jpg",
|
||||||
|
default_test_path,
|
||||||
|
"invalid-date") # pyright: ignore [reportArgumentType]
|
||||||
assert AeonViewMessages.INVALID_DATE in log.call_args[0][0]
|
assert AeonViewMessages.INVALID_DATE in log.call_args[0][0]
|
||||||
|
|
||||||
|
|
||||||
@@ -132,7 +138,8 @@ def test_download_image_success(mock_get):
|
|||||||
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
|
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
|
||||||
destination = Path(temp_file.name)
|
destination = Path(temp_file.name)
|
||||||
avi.download_image(destination)
|
avi.download_image(destination)
|
||||||
mock_get.assert_called_once_with(f"{default_image_domain}.jpg", stream=True, timeout=10)
|
mock_get.assert_called_once_with(f"{default_image_domain}.jpg",
|
||||||
|
stream=True, timeout=10)
|
||||||
assert destination.exists()
|
assert destination.exists()
|
||||||
|
|
||||||
|
|
||||||
@@ -154,7 +161,8 @@ def test_download_image_failure(mock_get):
|
|||||||
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
||||||
@mock.patch("subprocess.run")
|
@mock.patch("subprocess.run")
|
||||||
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
||||||
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
|
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04",
|
||||||
|
year="2025")
|
||||||
avv = AeonViewVideos(default_test_path, args)
|
avv = AeonViewVideos(default_test_path, args)
|
||||||
with mock.patch("aeonview.logging.info") as log:
|
with mock.patch("aeonview.logging.info") as log:
|
||||||
avv.generate_daily_video()
|
avv.generate_daily_video()
|
||||||
@@ -169,27 +177,31 @@ def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
|||||||
|
|
||||||
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
||||||
def test_generate_daily_video_simulate(mock_mkdir_p):
|
def test_generate_daily_video_simulate(mock_mkdir_p):
|
||||||
args = argparse.Namespace(simulate=True, fps=10, day="01", month="04", year="2025")
|
args = argparse.Namespace(simulate=True, fps=10, day="01", month="04",
|
||||||
|
year="2025")
|
||||||
avv = AeonViewVideos(default_test_path, args)
|
avv = AeonViewVideos(default_test_path, args)
|
||||||
avv.generate_daily_video()
|
avv.generate_daily_video()
|
||||||
mock_mkdir_p.assert_not_called()
|
mock_mkdir_p.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_generate_monthly_video_not_implemented():
|
def test_generate_monthly_video_not_implemented():
|
||||||
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
|
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04",
|
||||||
|
year="2025")
|
||||||
avv = AeonViewVideos(default_test_path, args)
|
avv = AeonViewVideos(default_test_path, args)
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
avv.generate_monthly_video(Path("/tmp"))
|
avv.generate_monthly_video(Path("/tmp"))
|
||||||
|
|
||||||
|
|
||||||
def test_generate_yearly_video_not_implemented():
|
def test_generate_yearly_video_not_implemented():
|
||||||
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", year="2025")
|
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04",
|
||||||
|
year="2025")
|
||||||
avv = AeonViewVideos(default_test_path, args)
|
avv = AeonViewVideos(default_test_path, args)
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
avv.generate_yearly_video(Path("/tmp"))
|
avv.generate_yearly_video(Path("/tmp"))
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--url", f"{default_image_domain}.jpg"])
|
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--url",
|
||||||
|
f"{default_image_domain}.jpg"])
|
||||||
def test_parse_arguments_image_mode():
|
def test_parse_arguments_image_mode():
|
||||||
args, _ = AeonViewHelpers.parse_arguments()
|
args, _ = AeonViewHelpers.parse_arguments()
|
||||||
assert args.mode == "image"
|
assert args.mode == "image"
|
||||||
@@ -197,7 +209,8 @@ def test_parse_arguments_image_mode():
|
|||||||
assert args.dest == default_dest
|
assert args.dest == default_dest
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--project", f"{default_project}"])
|
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--project",
|
||||||
|
f"{default_project}"])
|
||||||
def test_parse_arguments_video_mode():
|
def test_parse_arguments_video_mode():
|
||||||
args, _ = AeonViewHelpers.parse_arguments()
|
args, _ = AeonViewHelpers.parse_arguments()
|
||||||
assert args.mode == "video"
|
assert args.mode == "video"
|
||||||
@@ -221,7 +234,8 @@ def test_parse_arguments_fps():
|
|||||||
assert args.fps == 30
|
assert args.fps == 30
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--generate", "2023-10-01"])
|
@mock.patch("sys.argv",
|
||||||
|
["aeonview.py", "--mode", "video", "--generate", "2023-10-01"])
|
||||||
def test_parse_arguments_generate_date():
|
def test_parse_arguments_generate_date():
|
||||||
args, _ = AeonViewHelpers.parse_arguments()
|
args, _ = AeonViewHelpers.parse_arguments()
|
||||||
assert args.mode == "video"
|
assert args.mode == "video"
|
||||||
@@ -253,12 +267,15 @@ def test_image_simulation(mock_download_image, mock_mkdir_p):
|
|||||||
args = mock.MagicMock()
|
args = mock.MagicMock()
|
||||||
args.simulate = True
|
args.simulate = True
|
||||||
args.date = "2025-04-10 12:30:45"
|
args.date = "2025-04-10 12:30:45"
|
||||||
avi = AeonViewImages(default_test_path, f"{default_image_domain}.jpg", args)
|
avi = AeonViewImages(
|
||||||
|
default_test_path, f"{default_image_domain}.jpg", args)
|
||||||
with mock.patch("aeonview.logging.info") as log:
|
with mock.patch("aeonview.logging.info") as log:
|
||||||
avi.get_current_image()
|
avi.get_current_image()
|
||||||
mock_mkdir_p.assert_not_called()
|
mock_mkdir_p.assert_not_called()
|
||||||
mock_download_image.assert_not_called()
|
mock_download_image.assert_not_called()
|
||||||
assert any("Saving image to" in str(call) for call in log.call_args_list)
|
assert any(
|
||||||
|
"Saving image to" in str(call) for call in log.call_args_list
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
||||||
@@ -275,16 +292,22 @@ def test_video_simulation(mock_subprocess_run, mock_mkdir_p):
|
|||||||
avv.generate_daily_video()
|
avv.generate_daily_video()
|
||||||
mock_mkdir_p.assert_not_called()
|
mock_mkdir_p.assert_not_called()
|
||||||
mock_subprocess_run.assert_not_called()
|
mock_subprocess_run.assert_not_called()
|
||||||
assert any("Generating video from" in str(call) for call in log.call_args_list)
|
assert any(
|
||||||
|
"Generating video from" in str(call) for call in log.call_args_list
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("logging.basicConfig")
|
@mock.patch("logging.basicConfig")
|
||||||
def test_setup_logger_verbose(mock_basic_config):
|
def test_setup_logger_verbose(mock_basic_config):
|
||||||
AeonViewHelpers.setup_logger(verbose=True)
|
AeonViewHelpers.setup_logger(verbose=True)
|
||||||
mock_basic_config.assert_called_once_with(level=logging.DEBUG, format="[%(levelname)s] %(message)s")
|
mock_basic_config.assert_called_once_with(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format="[%(levelname)s] %(message)s")
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("logging.basicConfig")
|
@mock.patch("logging.basicConfig")
|
||||||
def test_setup_logger_non_verbose(mock_basic_config):
|
def test_setup_logger_non_verbose(mock_basic_config):
|
||||||
AeonViewHelpers.setup_logger(verbose=False)
|
AeonViewHelpers.setup_logger(verbose=False)
|
||||||
mock_basic_config.assert_called_once_with(level=logging.INFO, format="[%(levelname)s] %(message)s")
|
mock_basic_config.assert_called_once_with(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="[%(levelname)s] %(message)s")
|
||||||
|
|||||||
Reference in New Issue
Block a user