Compare commits

..
Author SHA1 Message Date
Gustavo Henrique Miranda cad6a93dd4 Merge pull request #2 from gmbrax/refactor/refactoring_basics
Refactor/refactoring basics
2025-06-16 14:04:37 -03:00
Matheus Toledo 99eb0a0651 🎨refactor: move modules and refactor import statements 2025-06-16 12:43:39 -03:00
Matheus Toledo dec8a7b445 ♻️ refactor: remove intelliJ files 2025-06-16 12:33:30 -03:00
Matheus Toledo 83f6d15ba9 🙈 feat: update .gitignore to include IDE settings for VS Code and IntelliJ 2025-06-16 12:32:43 -03:00
Matheus Toledo 190cef2627 📝 docs: update README with project overview, features, requirements, installation, and usage instructions 2025-06-16 12:31:05 -03:00
Matheus Toledo c2f95b1cf2 ♻️refactor: remove blank lines on dependencies file 2025-06-16 12:09:02 -03:00
Matheus Toledo 9024d30662 🙈 feat: update gitignore for python projects 2025-06-16 12:08:01 -03:00
Gustavo Henrique Miranda 1cb6cfa1b5 Merge pull request #1 from gmbrax/dependabot/pip/greenlet-3.2.3
Bump greenlet from 3.2.2 to 3.2.3
2025-06-09 19:49:51 -03:00
dependabot[bot] 39017a96b6 Bump greenlet from 3.2.2 to 3.2.3
Bumps [greenlet](https://github.com/python-greenlet/greenlet) from 3.2.2 to 3.2.3.
- [Changelog](https://github.com/python-greenlet/greenlet/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-greenlet/greenlet/compare/3.2.2...3.2.3)

---
updated-dependencies:
- dependency-name: greenlet
  dependency-version: 3.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 14:56:53 +00:00
33 changed files with 217 additions and 610 deletions
+143 -1
View File
@@ -1,2 +1,144 @@
# Database files
database.db
__pycache__
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
build/
temp/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
Pipfile.lock
# poetry
poetry.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.env.*
.venv
venv/
ENV/
env/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# IDE settings
.vscode/
.idea/
-11
View File
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.10 (Pilgrim)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
-6
View File
@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Pilgrim.iml" filepath="$PROJECT_DIR$/.idea/Pilgrim.iml" />
</modules>
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
-200
View File
@@ -1,200 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="0a7f92e2-b44a-4dfe-8e01-136d1c0c18be" name="Changes" comment="Added the travel_diary id as foreign key to the photos and add a check on the creation to avoid leaving it empty or bad referenced">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pilgrim/service/photo_service.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/pilgrim/service/photo_service.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pilgrim/ui/screens/diary_list_screen.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/pilgrim/ui/screens/diary_list_screen.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="proposed_changes" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="UPDATE_TYPE" value="REBASE" />
</component>
<component name="GitHubPullRequestSearchHistory">{
&quot;lastFilter&quot;: {
&quot;state&quot;: &quot;OPEN&quot;,
&quot;assignee&quot;: &quot;gmbrax&quot;
}
}</component>
<component name="GithubPullRequestsUISettings">{
&quot;selectedUrlAndAccountId&quot;: {
&quot;url&quot;: &quot;https://github.com/gmbrax/Pilgrim.git&quot;,
&quot;accountId&quot;: &quot;213d8456-c67d-4cfd-99a6-337d47c35b4a&quot;
}
}</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 0
}</component>
<component name="ProjectId" id="2y0y1J9RlHT6m1qL5RuCBf0Ramp" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"Python.Database.executor": "Run",
"Python.command.executor": "Run",
"Python.main.executor": "Run",
"Python.pilgrim.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "feat-TUI",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager">
<configuration name="pilgrim" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="Pilgrim" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/.venv/bin" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/.venv/bin/pilgrim" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.pilgrim" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-6a121458b545-JavaScript-PY-251.25410.159" />
<option value="bundled-python-sdk-e0ed3721d81e-36ea0e71a18c-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-251.25410.159" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="0a7f92e2-b44a-4dfe-8e01-136d1c0c18be" name="Changes" comment="" />
<created>1748985568579</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1748985568579</updated>
<workItem from="1748985569621" duration="2124000" />
<workItem from="1748992451560" duration="312000" />
<workItem from="1749508687224" duration="371000" />
</task>
<task id="LOCAL-00001" summary="Added a Back Relationship in Entry to list all the photos">
<option name="closed" value="true" />
<created>1749004109515</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1749004109515</updated>
</task>
<task id="LOCAL-00002" summary="Changed the names of the files to conform the python convection and also added photo_in_entry.py to diminish the cyclic import error in pylint">
<option name="closed" value="true" />
<created>1749006784623</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1749006784623</updated>
</task>
<task id="LOCAL-00003" summary="Added the Services layer to the code to do the database operation">
<option name="closed" value="true" />
<created>1749140898576</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1749140898576</updated>
</task>
<task id="LOCAL-00004" summary="Added the photo_service.py and modified photo.py to have all the crud operations">
<option name="closed" value="true" />
<created>1749155713848</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1749155713848</updated>
</task>
<task id="LOCAL-00005" summary="Made some changes to naming in both the classes and the tables of the database to conform the correct naming scheme">
<option name="closed" value="true" />
<created>1749164385581</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1749164385581</updated>
</task>
<task id="LOCAL-00006" summary="Added the travel_diary id as foreign key to the photos and add a check on the creation to avoid leaving it empty or bad referenced">
<option name="closed" value="true" />
<created>1749168650225</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1749168650225</updated>
</task>
<option name="localTasksCounter" value="7" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Added a Back Relationship in Entry to list all the photos" />
<MESSAGE value="Changed the names of the files to conform the python convection and also added photo_in_entry.py to diminish the cyclic import error in pylint" />
<MESSAGE value="Added the Services layer to the code to do the database operation" />
<MESSAGE value="Added the photo_service.py and modified photo.py to have all the crud operations" />
<MESSAGE value="Made some changes to naming in both the classes and the tables of the database to conform the correct naming scheme" />
<MESSAGE value="Added the travel_diary id as foreign key to the photos and add a check on the creation to avoid leaving it empty or bad referenced" />
<option name="LAST_COMMIT_MESSAGE" value="Added the travel_diary id as foreign key to the photos and add a check on the creation to avoid leaving it empty or bad referenced" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/Pilgrim$Database.coverage" NAME="Database Coverage Results" MODIFIED="1748987101492" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/src/Database" />
<SUITE FILE_PATH="coverage/Pilgrim$pilgrim.coverage" NAME="pilgrim Coverage Results" MODIFIED="1749097142827" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/.venv/bin" />
<SUITE FILE_PATH="coverage/Pilgrim$main.coverage" NAME="main Coverage Results" MODIFIED="1748992510527" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/src" />
<SUITE FILE_PATH="coverage/Pilgrim$command.coverage" NAME="command Coverage Results" MODIFIED="1748992876551" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/src/Pilgrim" />
</component>
</project>
+48 -1
View File
@@ -1,3 +1,50 @@
# Python_Pilgrim
Python Based Travel Diary
## Overview
**Python_Pilgrim** is a Python-based travel diary application designed to help users document and manage their travel experiences. The project provides tools for recording trips, organizing travel notes, and storing memories in a structured and accessible format.
## Features
- Create and manage travel diaries
- Add, edit, and delete travel entries
- Organize trips by date, location, or theme
- Store photos, notes, and other media
- Export and share travel logs
## Requirements
- Python 3.8 or higher
- Linux operating system (tested on Ubuntu 20.04+)
- Visual Studio Code (VSCode) for development (optional but strongly recommended)
- pip (Python package installer)
- Optional: virtualenv for isolated environments
## Installation
1. Clone the repository:
```bash
git clone https://github.com/gmbrax/Pilgrim.git
```
2. Navigate to the project directory:
```bash
cd Pilgrim
```
3. Create a virtual environment and, then, activate it:
```bash
python -m venv .venv
source .venv/bin/activate
```
4. Install the required dependencies:
```bash
pip install -r requirements.txt
```
## Usage
To run the main application, execute:
```bash
python <???>.py
```
This will start the Python_Pilgrim application. Follow the on-screen instructions to create and manage your travel diaries.
+1 -3
View File
@@ -18,9 +18,7 @@ classifiers = [
"Operating System :: OS Independent",
]
dependencies = [
"sqlalchemy",
"textual",
"textual-dev"
"sqlalchemy"
]
[template.plugins.default]
src-layout = true
+2 -5
View File
@@ -1,6 +1,3 @@
greenlet==3.2.2
greenlet==3.2.3
SQLAlchemy==2.0.41
typing_extensions==4.14.0
textual~=3.3.0
typing_extensions==4.14.0
@@ -1,17 +1,13 @@
from pilgrim.database import Database
from pilgrim.service.mocks.service_manager_mock import ServiceManagerMock
from pilgrim.service.servicemanager import ServiceManager
from pilgrim.ui.ui import UIApp
from src.database import Database
from src.service.servicemanager import ServiceManager
class Application:
def __init__(self):
self.database = Database()
self.ui = UIApp(ServiceManagerMock())
def run(self):
self.database.create()
self.ui.run()
def get_service_manager(self):
session = self.database.session()
+1 -1
View File
@@ -1,4 +1,4 @@
from pilgrim.application import Application
from src.application import Application
def main():
@@ -4,7 +4,6 @@ from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Database:
def __init__(self):
self.engine = create_engine(
@@ -3,8 +3,8 @@ from typing import Any
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from pilgrim.models.photo_in_entry import photo_entry_association
from ..database import Base
from src.models.photo_in_entry import photo_entry_association
from src.database import Base
class Entry(Base):
@@ -1,10 +1,10 @@
from typing import Any
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, ForeignKey
from pilgrim.models.photo_in_entry import photo_entry_association
from ..database import Base
from src.models.photo_in_entry import photo_entry_association
from src.database import Base
class Photo(Base):
@@ -1,6 +1,6 @@
from sqlalchemy import Table, Column, Integer, ForeignKey
from ..database import Base
from src.database import Base
photo_entry_association = Table('photo_entry_association', Base.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
@@ -1,8 +1,9 @@
from typing import Any
from src.database import Base
from sqlalchemy import Column, String, Integer
from ..database import Base
class TravelDiary(Base):
__tablename__ = "travel_diaries"
-8
View File
@@ -1,8 +0,0 @@
from pilgrim.application import Application
from pilgrim.command import main
from pilgrim.database import Database, Base
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.models.entry import Entry
from pilgrim.models.photo import Photo
__all__ = ["Application", "Database", "TravelDiary", "Entry", "Photo", "main", "Base"]
@@ -1,47 +0,0 @@
from typing import List
from pilgrim.service.entry_service import EntryService
from pilgrim.models.entry import Entry
class EntryServiceMock(EntryService):
def __init__(self):
super().__init__(None)
self.mock_data = {
1: Entry(title="The Adventure Begins", text="I'm hopping in the Plane to finally visit canadian lands",
date="26/07/2025", travel_diary_id=1, id=1,
photos=[]),
2: Entry(title="The Landing", text="Finally on Canadian Soil", date="27/07/2025",
travel_diary_id=1, id=2,photos=[]),
3: Entry(title="The Mount Royal", text="The Mount Royal is fucking awesome", date="28/07/2025",
travel_diary_id=1, id=3, photos=[]),
}
self._next_id = 4
def create(self, travel_diary_id: int, title: str, text: str, date: str) -> Entry:
new_entry = Entry(title, text, date, travel_diary_id, id=self._next_id)
self.mock_data[self._next_id] = new_entry
self._next_id += 1
return new_entry
def read_by_id(self, entry_id: int) -> Entry | None:
return self.mock_data.get(entry_id)
def read_all(self) -> List[Entry]:
return list(self.mock_data.values())
def update(self, entry_id: int, entry_dst: Entry) -> Entry | None:
item_to_update = self.mock_data.get(entry_id)
if item_to_update:
item_to_update.title = entry_dst.title if entry_dst.title is not None else item_to_update.title
item_to_update.text = entry_dst.text if entry_dst.text is not None else item_to_update.text
item_to_update.date = entry_dst.date if entry_dst.date is not None else item_to_update.date
item_to_update.fk_travel_diary_id = entry_dst.fk_travel_diary_id if (entry_dst.fk_travel_diary_id
is not None) else entry_dst.id
item_to_update.photos.extend(entry_dst.photos)
return item_to_update
return None
def delete(self, entry_id: int) -> Entry | None:
return self.mock_data.pop(entry_id, None)
@@ -1,42 +0,0 @@
from pathlib import Path
from typing import List
from pilgrim.models.photo import Photo
from pilgrim.service.photo_service import PhotoService
class PhotoServiceMock(PhotoService):
def __init__(self):
super().__init__(None)
self.mock_data = {}
self._next_id = 1
def create(self, filepath: Path, name: str, travel_diary_id, addition_date=None, caption=None) -> Photo | None:
new_photo = Photo(filepath, name, addition_date=addition_date, caption=caption)
self.mock_data[self._next_id] = new_photo
self._next_id += 1
return new_photo
def read_by_id(self, photo_id: int) -> Photo:
return self.mock_data.get(photo_id)
def read_all(self) -> List[Photo]:
return list(self.mock_data.values())
def update(self, photo_id: Photo, photo_dst: Photo) -> Photo | None:
item_to_update:Photo = self.mock_data.get(photo_id)
if item_to_update:
item_to_update.filepath = photo_dst.filepath if photo_dst.filepath else item_to_update.filepath
item_to_update.name = photo_dst.name if photo_dst.name else item_to_update.name
item_to_update.caption = photo_dst.caption if photo_dst.caption else item_to_update.caption
item_to_update.addition_date = photo_dst.addition_date if photo_dst.addition_date\
else item_to_update.addition_date
item_to_update.fk_travel_diary_id = photo_dst.fk_travel_diary_id if photo_dst.fk_travel_diary_id \
else item_to_update.fk_travel_diary_id
item_to_update.entries.extend(photo_dst.entries)
return item_to_update
return None
def delete(self, photo_id: int) -> Photo | None:
return self.mock_data.pop(photo_id, None)
@@ -1,19 +0,0 @@
from pilgrim.service.mocks.entry_service_mock import EntryServiceMock
from pilgrim.service.mocks.photo_service_mock import PhotoServiceMock
from pilgrim.service.mocks.travel_diary_service_mock import TravelDiaryServiceMock
from pilgrim.service.photo_service import PhotoService
from pilgrim.service.servicemanager import ServiceManager
class ServiceManagerMock(ServiceManager):
def __init__(self):
super().__init__()
def get_entry_service(self):
return EntryServiceMock()
def get_travel_diary_service(self):
return TravelDiaryServiceMock()
def get_photo_service(self):
return PhotoServiceMock()
@@ -1,34 +0,0 @@
from pilgrim.service.travel_diary_service import TravelDiaryService
from pilgrim.models.travel_diary import TravelDiary
class TravelDiaryServiceMock(TravelDiaryService):
def __init__(self):
super().__init__(None)
self.mock_data = {
1:TravelDiary(id=1,name="Montreal"),
2:TravelDiary(id=2,name="Rio de Janeiro"),
}
self._next_id = 3
def create(self, name: str):
new_travel_diary = TravelDiary(id=self._next_id,name=name)
self.mock_data[self._next_id] = new_travel_diary
self._next_id += 1
return new_travel_diary
def read_by_id(self, travel_id: int):
return self.mock_data[travel_id]
def read_all(self):
return list(self.mock_data.values())
def update(self, travel_diary_id: int, travel_diary_dst: TravelDiary):
item_to_update = self.mock_data.get(travel_diary_id)
if item_to_update:
item_to_update.name = travel_diary_dst.name if travel_diary_dst.name is not None else item_to_update.name
return item_to_update
return None
def delete(self, travel_diary_id: int):
return self.mock_data.pop(travel_diary_id, None)
View File
View File
-146
View File
@@ -1,146 +0,0 @@
from textual.app import ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Label, Static, OptionList, Button
from textual.binding import Binding
from textual.containers import Vertical, Container, Horizontal
class DiaryListScreen(Screen):
TITLE = "Pilgrim - Main"
BINDINGS = [
Binding("n", "new_diary", "Novo Diário"),
Binding("^q", "quit", "Sair"),
]
def __init__(self):
super().__init__()
self.selected_diary_index = None # Armazena o índice do diário selecionado
def compose(self) -> ComposeResult:
yield Header()
yield Container(
Static("Pilgrim", classes="app-title"),
Label("Select a diary"),
OptionList(id="option-list", classes="diary-options"),
Horizontal(
Button("New diary", id="new-diary"),
Button("Edit diary", id="edit-diary"),
Button("🔄 Refresh", id="refresh-btn"),
classes="actions-buttons",
),
classes="dialog-container"
)
yield Static(
"Tip: use ↑↓ to navigate • ENTER to Select • "
"TAB to alternate the fields • SHIFT + TAB to alternate back ",
classes="tips"
)
yield Footer()
def on_mount(self) -> None:
self.refresh_diaries()
self.update_buttons_state() # Atualiza estado inicial dos botões
def refresh_diaries(self):
try:
service_manager = self.app.service_manager
option_list = self.query_one(".diary-options")
option_list.clear_options()
travel_diary_service = service_manager.get_travel_diary_service()
diaries = travel_diary_service.read_all()
if not diaries:
# Para OptionList vazio, você pode adicionar uma string simples
option_list.add_option("[dim]Nenhum diário encontrado. Pressione 'N' para criar um novo![/dim]")
else:
for diary in diaries:
# Adiciona cada opção como string com markup rich
option_list.add_option(f"[b]{diary.name}[/b]\n[dim]ID: {diary.id}[/dim]")
except Exception as e:
self.notify("Error: " + str(e))
def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> None:
"""Handle quando uma opção é selecionada"""
diaries = self.app.service_manager.get_travel_diary_service().read_all()
if diaries and event.option_index < len(diaries):
self.selected_diary_index = event.option_index
selected_diary = diaries[event.option_index]
self.notify(f"Diário selecionado: {selected_diary.name}")
else:
# Caso seja a opção "nenhum diário encontrado"
self.selected_diary_index = None
self.update_buttons_state()
def update_buttons_state(self):
"""Atualiza o estado dos botões baseado na seleção"""
edit_button = self.query_one("#edit-diary")
# Só habilita os botões se há um diário selecionado
has_selection = self.selected_diary_index is not None
edit_button.disabled = not has_selection
def on_button_pressed(self, event: Button.Pressed) -> None:
"""Handle cliques nos botões"""
button_id = event.button.id
if button_id == "new-diary":
self.action_new_diary()
elif button_id == "edit-diary":
self.action_edit_diary()
elif button_id == "refresh-btn":
self.refresh_diaries()
self.notify("Lista atualizada manualmente!")
def action_new_diary(self):
"""Ação para criar novo diário"""
self.notify("Criando novo diário...")
# Aqui você pode navegar para uma tela de criação de diário
def action_edit_diary(self):
"""Ação para editar diário selecionado"""
if self.selected_diary_index is not None:
diaries = self.app.service_manager.get_travel_diary_service().read_all()
if self.selected_diary_index < len(diaries):
selected_diary = diaries[self.selected_diary_index]
self.notify(f"Editando diário: {selected_diary.name}")
# Aqui você pode navegar para uma tela de edição
# self.app.push_screen(EditDiaryScreen(diary=selected_diary))
def refresh_diaries(self):
"""Atualiza a lista de diários no OptionList"""
try:
service_manager = self.app.service_manager
option_list = self.query_one("#option-list") # Usando ID em vez de classe
# Debug
current_count = len(option_list.options) if hasattr(option_list, 'options') else 0
self.notify(f"OptionList atual tem {current_count} opções")
option_list.clear_options()
travel_diary_service = service_manager.get_travel_diary_service()
diaries = travel_diary_service.read_all()
self.notify(f"Carregando {len(diaries)} diários do serviço")
if not diaries:
option_list.add_option("[dim]Nenhum diário encontrado. Pressione 'N' para criar um novo![/dim]")
self.selected_diary_index = None
else:
for diary in diaries:
option_list.add_option(f"[b]{diary.name}[/b]\n[dim]ID: {diary.id}[/dim]")
# Valida se a seleção ainda é válida
if (self.selected_diary_index is not None and
self.selected_diary_index >= len(diaries)):
self.selected_diary_index = None
except Exception as e:
self.notify("Error no refresh_diaries: " + str(e))
-25
View File
@@ -1,25 +0,0 @@
.dialog-container{
content-align: center top;
}
.app-title{
dock: top;
height: 3;
content-align: center middle;
text-style: bold;
color: $accent;
}
.actions-buttons{
height: auto;
margin: 1 0;
align: center middle;
}
.tips{
dock: bottom;
height: 4;
content-align: center middle;
color: $text-muted;
}
-21
View File
@@ -1,21 +0,0 @@
from pathlib import Path
from textual.app import App
from pilgrim.service.servicemanager import ServiceManager
from pilgrim.ui.screens.diary_list_screen import DiaryListScreen
CSS_FILE_PATH = Path(__file__).parent / "styles" / "pilgrim.css"
class UIApp(App):
CSS_PATH = CSS_FILE_PATH
def __init__(self,service_manager: ServiceManager):
super().__init__()
self.service_manager = service_manager
def on_mount(self) -> None:
"""Chamado quando a app inicia. Carrega a tela principal."""
self.push_screen(DiaryListScreen())
@@ -1,7 +1,7 @@
from typing import List
from ..models.entry import Entry
from ..models.travel_diary import TravelDiary
from src.models.entry import Entry
from src.models.travel_diary import TravelDiary
class EntryService:
@@ -1,9 +1,9 @@
from pathlib import Path
from typing import List
from src.models.photo import Photo
from src.models.travel_diary import TravelDiary
from pilgrim.models.photo import Photo
from pilgrim.models.travel_diary import TravelDiary
class PhotoService:
def __init__(self, session):
@@ -1,5 +1,5 @@
from pilgrim.service.entry_service import EntryService
from pilgrim.service.travel_diary_service import TravelDiaryService
from src.service.entry_service import EntryService
from src.service.travel_diary_service import TravelDiaryService
class ServiceManager:
@@ -1,4 +1,4 @@
from ..models.travel_diary import TravelDiary
from src.models.travel_diary import TravelDiary
class TravelDiaryService:
@@ -18,18 +18,18 @@ class TravelDiaryService:
def read_all(self):
return self.session.query(TravelDiary).all()
def update(self, travel_diary_id: TravelDiary, travel_diary_dst: TravelDiary):
original = self.read_by_id(travel_diary_id.id)
def update(self, travel_diary_src:TravelDiary,travel_diary_dst:TravelDiary):
original = self.read_by_id(travel_diary_src.id)
if original is not None:
original.name = travel_diary_dst.name
self.session.commit()
self.session.refresh(original)
return original
def delete(self, travel_diary_id: TravelDiary):
excluded = self.read_by_id(travel_diary_id.id)
def delete(self, travel_diary_src:TravelDiary):
excluded = self.read_by_id(travel_diary_src.id)
if excluded is not None:
self.session.delete(travel_diary_id)
self.session.delete(travel_diary_src)
self.session.commit()
return excluded
return None