Compare commits

...
14 Commits
Author SHA1 Message Date
Gustavo Henrique Santos Souza de Miranda 77e191f480 test(entry_service): add test for creating an entry without photos
This commit adds a unit test to ensure that the EntryService's
`create` method correctly handles cases where an empty list of photos
is provided.

It verifies that the entry is created successfully and that the
`photos` relationship is an empty list, confirming the feature's
flexibility.
2025-07-20 16:03:45 -03:00
Gustavo Henrique Santos Souza de Miranda 3c7925cb1c test(entry_service): add a failure case test for create method
This commit adds a unit test for an important "unhappy path" in the
EntryService's `create` method.

It specifically verifies that the method gracefully returns `None`
when provided with a `travel_diary_id` that does not exist in the
database, ensuring the initial guard clause works as expected.
2025-07-20 15:21:49 -03:00
Gustavo Henrique Santos Souza de Miranda c4dceda942 test(entry_service): add unit test for creating a new entry
This commit introduces the first unit test for the EntryService,
covering the "happy path" for the `create` method.

It verifies that a new entry is correctly persisted to the database,
including its relationship with associated Photo objects. The test
leverages fixtures to create an isolated, in-memory database
populated with the necessary dependencies.
2025-07-20 03:35:22 -03:00
Gustavo Henrique Miranda 4d87ae1b90 Merge pull request #61 from gmbrax/fix/relative-imports
Fix/relative imports
2025-07-20 03:27:31 -03:00
Gustavo Henrique Mirandaandcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> e1add84537 Update src/pilgrim/database.py
Remove Debug message

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-07-20 03:23:37 -03:00
Gustavo Henrique Santos Souza de Miranda 2ba1cbd0a6 Fix the declarative_base import to the newer way 2025-07-20 03:10:13 -03:00
Gustavo Henrique Santos Souza de Miranda eed16f34c1 Simplify the pyproject.toml 2025-07-20 02:47:05 -03:00
Gustavo Henrique Santos Souza de Miranda 06ee08b201 Fix all imports to be absolute and not relative 2025-07-20 02:46:46 -03:00
Gustavo Henrique Miranda 78e45e26fd Update pylint_sonarqube.yml
Fix the ruff error on the sonarcloud scan
2025-07-19 19:06:53 -03:00
Gustavo Henrique Miranda cc68bbcf2e Update pylint_sonarqube.yml
Add the missing parameters for SonarQube
2025-07-19 18:56:19 -03:00
Gustavo Henrique Miranda 9a698596b1 Update and rename pylint.yml to pylint_sonarqube.yml
Add RUFF and SonarQube scanning
2025-07-19 18:52:53 -03:00
Gustavo Henrique Miranda 0cbcbd3a8d Update CHANGELOG.md
Revert the markdown format missing from the last update
2025-07-19 17:40:01 -03:00
Gustavo Henrique Miranda a1e718c1fc Update CHANGELOG.md
Added info for the next alpha version 0.0.4
2025-07-19 17:34:05 -03:00
Gustavo Henrique Miranda 8919e158bc Merge pull request #60 from gmbrax/fix/Importing-duplicate-images
Fix the bug that collides the hash if identical images
2025-07-19 03:23:54 -03:00
12 changed files with 244 additions and 97 deletions
-24
View File
@@ -1,24 +0,0 @@
name: Pylint
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install pylint
- name: Analysing the code with pylint
run: |
pylint --disable=C0114,C0115,C0116 --exit-zero $(git ls-files '*.py')
+63
View File
@@ -0,0 +1,63 @@
name: Pylint and SonarCloud
on:
push:
branches: [ main, master, staging ]
pull_request:
branches: [ main, master, staging ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Necessário para SonarCloud
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install pylint ruff coverage pytest
- name: Analysing the code with pylint (console output)
run: |
pylint --disable=C0114,C0115,C0116 --exit-zero $(git ls-files '*.py') || true
- name: Generate Pylint report for SonarCloud
run: |
pylint --disable=C0114,C0115,C0116 --output-format=json --exit-zero $(git ls-files '*.py') > pylint-report.json || true
- name: Run Ruff
run: |
ruff check --output-format=json . > ruff-report.json || true
- name: Run tests with coverage (if you have tests)
run: |
if [ -d "tests" ] || [ -f "test_*.py" ] || [ -f "*_test.py" ]; then
coverage run -m pytest || true
coverage xml || true
else
echo "No tests found, skipping coverage"
fi
- name: SonarCloud Scan
uses: SonarSource/sonarqube-scan-action@v5.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.projectKey=gmbrax_Pilgrim
-Dsonar.organization=gmbrax
-Dsonar.python.pylint.reportPaths=pylint-report.json
-Dsonar.python.ruff.reportPaths=ruff-report.json
-Dsonar.python.coverage.reportPaths=coverage.xml
+47 -30
View File
@@ -1,51 +1,68 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [Unreleased]
## Planned
* Installation Method 1 (repository compilation)
* Organization of trips by date, location, or theme
* Enhanced photo management features
* Search functionality
* Export features
* Testing implementation
### Planned
## [0.0.4] - 2025-07-19
- Installation Method 1 (repository compilation)
- Organization of trips by date, location, or theme
- Enhanced photo management features
- Search functionality
- Export features
- Testing implementation
### Added
* Support for creating new diaries asynchronously, with an option to automatically open the newly created diary
* Unified "Enter" key support for saving or creating diaries across relevant modals
* Automatic diary list refresh when returning to the diary screen
* Application configuration management with a new centralized config system
* Database location and initialization now configurable via the new config manager
* Automatic migration of database file to the configuration directory
* Display of database URL on application startup for transparency
* Duplicate photo detection before photo creation to prevent redundant entries
* Photo hash indexing to improve photo lookup performance
### Changed
* Enhanced feedback and validation when editing or creating diary names
* Streamlined and unified save logic for diary modals, reducing duplicated behavior
* About screen now displays the actual installed application version dynamically
* Sidebar and photo-related UI text updated to remove emoji icons for a cleaner appearance
* Sidebar layout and scrolling behavior improved for better usability
* Photo hash generation now relies on existing service-provided hashes instead of local computation
### Improved
* Enhanced feedback and validation when editing or creating diary names
* Streamlined and unified save logic for diary modals, reducing duplicated behavior
* Sidebar layout and scrolling behavior for better usability
## [0.0.3] - 2025-07-07
### Changed
- Removed the dependency on textual-dev from pyproject.toml
* Removed the dependency on textual-dev from pyproject.toml
## [0.0.2] - 2025-07-07
### Changed
- Changed the license in pyproject.toml to BSD
* Changed the license in pyproject.toml to BSD
## [0.0.1] - 2025-07-06
### Added
- Initial alpha release of Pilgrim travel diary application
- Create and edit travel diaries
- Create and edit diary entries
- Photo ingestion system
- Photo addition and reference via sidebar
- Text User Interface (TUI) built with Textual framework
- Pre-compiled binary installation method (Method 2)
- Support for Linux operating systems
- Basic project documentation (README)
* Initial alpha release of Pilgrim travel diary application
* Create and edit travel diaries
* Create and edit diary entries
* Photo ingestion system
* Photo addition and reference via sidebar
* Text User Interface (TUI) built with Textual framework
* Pre-compiled binary installation method (Method 2)
* Support for Linux operating systems
* Basic project documentation (README)
### Known Issues
- Installation Method 1 not yet implemented
- No testing suite implemented yet
- Some features may be unstable in an alpha version
[Unreleased]: https://github.com/username/pilgrim/compare/v0.0.1...HEAD
[0.0.1]: https://github.com/username/pilgrim/releases/tag/v0.0.1
* Installation Method 1 not yet implemented
* No testing suite implemented yet
* Some features may be unstable in an alpha version
+28 -33
View File
@@ -1,36 +1,31 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "pilgrim"
version = "0.0.4"
authors = [
{ name="Gustavo Henrique Santos Souza de Miranda", email="gustavohssmiranda@gmail.com" }
]
description = "Pilgrim's Travel Log"
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
]
dependencies = [
"sqlalchemy",
"textual",
"tomli",
"tomli_w"
]
[project]
name = "Pilgrim"
version = "0.0.4"
authors = [
{ name="Gustavo Henrique Santos Souza de Miranda", email="gustavohssmiranda@gmail.com" }
]
description = "Pilgrim's Travel Log"
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
]
dependencies = [
"sqlalchemy",
"textual",
"tomli",
"tomli_w"
[project.urls]
Homepage = "https://github.com/gmbrax/Pilgrim/"
Issues = "https://github.com/gmbrax/Pilgrim/issues"
]
[template.plugins.default]
src-layout = true
[project.urls]
Homepage = "https://github.com/gmbrax/Pilgrim/"
Issues = "https://github.com/gmbrax/Pilgrim/issues"
[tool.hatch.build.targets.wheel]
packages = ["src/pilgrim"]
[project.scripts]
pilgrim = "pilgrim:main"
[project.scripts]
pilgrim = "pilgrim.command:main"
+1 -1
View File
@@ -1,5 +1,5 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import sessionmaker
from pathlib import Path
import os
+2 -1
View File
@@ -4,7 +4,8 @@ from pilgrim.models.photo_in_entry import photo_entry_association
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
from ..database import Base
from pilgrim.database import Base
class Entry(Base):
+2 -1
View File
@@ -7,7 +7,8 @@ from sqlalchemy.orm import relationship
from sqlalchemy.sql.schema import Index
from pilgrim.models.photo_in_entry import photo_entry_association
from ..database import Base
from pilgrim.database import Base
class Photo(Base):
+1 -1
View File
@@ -1,6 +1,6 @@
from sqlalchemy import Table, Column, Integer, ForeignKey
from ..database import Base
from pilgrim.database import Base
photo_entry_association = Table('photo_entry_association', Base.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
+3 -2
View File
@@ -3,10 +3,11 @@ from typing import Any
from sqlalchemy import Column, Integer, String, UniqueConstraint
from sqlalchemy.orm import relationship
from .. import database
from pilgrim.database import Base
class TravelDiary(database.Base):
class TravelDiary(Base):
__tablename__ = "travel_diaries"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
+3 -3
View File
@@ -1,9 +1,9 @@
from datetime import datetime
from typing import List
from ..models.entry import Entry
from ..models.travel_diary import TravelDiary
from ..models.photo import Photo # ✨ Importe o modelo Photo
from pilgrim.models.entry import Entry
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.models.photo import Photo # ✨ Importe o modelo Photo
class EntryService:
+1 -1
View File
@@ -6,7 +6,7 @@ from pathlib import Path
from pilgrim.utils import DirectoryManager
from sqlalchemy.exc import IntegrityError
from ..models.travel_diary import TravelDiary
from pilgrim.models.travel_diary import TravelDiary
class TravelDiaryService:
+93
View File
@@ -0,0 +1,93 @@
import pytest
from datetime import datetime
from unittest.mock import Mock
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from pilgrim.database import Base
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.models.entry import Entry
from pilgrim.models.photo import Photo
from pilgrim.service.entry_service import EntryService
@pytest.fixture(scope="function")
def db_session():
engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.close()
Base.metadata.drop_all(engine)
@pytest.fixture
def populated_db_session(db_session):
travel_diary = TravelDiary(name="My Travel Diary", directory_name="viagem-teste")
db_session.add(travel_diary)
db_session.commit()
return db_session
def test_create_entry_successfully(populated_db_session):
session = populated_db_session
service = EntryService(session)
diary_id = 1 # Sabemos que o ID é 1 por causa da nossa fixture
title = "Primeiro Dia na Praia"
text = "O dia foi ensolarado e o mar estava ótimo."
date = datetime(2025, 7, 20)
photos = [Photo(filepath="/path/to/photo1.jpg",name="Photo 1",photo_hash="hash_12345678",fk_travel_diary_id=diary_id), Photo(filepath="/path/to/photo2.jpg",name="Photo 1",photo_hash="hash_87654321",fk_travel_diary_id=diary_id)]
created_entry = service.create(
travel_diary_id=diary_id,
title=title,
text=text,
date=date,
photos=photos
)
assert created_entry is not None
assert created_entry.id is not None # Garante que foi salvo no BD e tem um ID
assert created_entry.title == title
assert created_entry.text == text
assert len(created_entry.photos) == 2
assert created_entry.photos[0].filepath == "/path/to/photo1.jpg"
entry_in_db = session.query(Entry).filter_by(id=created_entry.id).one()
assert entry_in_db.title == "Primeiro Dia na Praia"
def test_create_entry_fails_when_diary_id_is_invalid(db_session):
session = db_session
service = EntryService(session)
invalid_id = 666
result = service.create(
travel_diary_id=invalid_id,
title="Título de Teste",
text="Texto de Teste",
date=datetime(2025, 7, 20),
photos=[]
)
assert result is None
def test_create_entry_successfully_without_photo(populated_db_session):
session = populated_db_session
service = EntryService(session)
diary_id = 1 # Sabemos que o ID é 1 por causa da nossa fixture
title = "Primeiro Dia na Praia"
text = "O dia foi ensolarado e o mar estava ótimo."
date = datetime(2025, 7, 20)
photos = []
created_entry = service.create(
travel_diary_id=diary_id,
title=title,
text=text,
date=date,
photos=photos
)
assert created_entry is not None
assert created_entry.id is not None # Garante que foi salvo no BD e tem um ID
assert created_entry.title == title
assert created_entry.text == text
assert len(created_entry.photos) == 0
entry_in_db = session.query(Entry).filter_by(id=created_entry.id).one()
assert entry_in_db.title == "Primeiro Dia na Praia"