otripy 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
otripy-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.2
2
+ Name: otripy
3
+ Version: 1.0.0
4
+ Summary: A tool to plan your next trip
5
+ Author-email: "Gaël de Chalendar (aka Kleag)" <kleagg@gmail.com>
6
+ License: AGPL
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: folium>=0.19.5
15
+ Requires-Dist: geopy>=2.4.1
16
+ Requires-Dist: lxml>=5.3.1
17
+ Requires-Dist: markdown>=3.7
18
+ Requires-Dist: nc-py-api>=0.19.0
19
+ Requires-Dist: numpy>=2.2.3
20
+ Requires-Dist: pillow>=11.1.0
21
+ Requires-Dist: pyside6>=6.8.2.1
22
+ Requires-Dist: typing-extensions>=4.12.2
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest; extra == "dev"
25
+ Requires-Dist: black; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: mypy; extra == "dev"
28
+ Provides-Extra: build
29
+ Requires-Dist: bumpver; extra == "build"
30
+ Provides-Extra: deploy
31
+ Requires-Dist: uv; extra == "deploy"
32
+
33
+ # Otripy - An open trip planning tool
34
+
35
+
36
+ [![PyPI Version](https://img.shields.io/pypi/v/otripy)](https://pypi.org/project/otripy)
37
+ [![License](https://img.shields.io/pypi/l/otripy)](https://github.com/kleag/otripy/blob/main/AGPL.md)
38
+ [![Downloads](https://static.pepy.tech/badge/otripy/month)](https://pepy.tech/project/otripy)
39
+ [![Supported Versions](https://img.shields.io/pypi/pyversions/otripy)](https://pypi.org/project/otripy)
40
+ [![Contributors](https://img.shields.io/github/contributors/kleag/otripy)](https://github.com/kleag/otripy/graphs/contributors)
41
+
42
+ <!--
43
+ ![Python Versions](https://img.shields.io/pypi/pyversions/otripy)
44
+ ![Build Status](https://img.shields.io/github/actions/workflow/status/kleag/otripy/ci.yml)
45
+ -->
46
+
47
+ ## Description
48
+
49
+ Otripy is a GUI application for trip planning, providing an intuitive user interface for organizing travel visits efficiently. Otripy allows to plan a trip by adding markers on a map (based on OpenStreetMap) associated with a note.
50
+
51
+ Otripy is developed and daily tested on Linux. It should work on any platform. It has already been tested on MacOS.
52
+
53
+ Otripy is already usable but would be better with a lot of other features. Some wanted features are listed [here](https://github.com/kleag/otripy/issues). Don't hesitate to create a new issue if you think at other features. And contribute them if you can!
54
+
55
+ ### Current features:
56
+
57
+ * Display OpenStreetMap map
58
+ * Zoom and pan the map
59
+ * Adding location markers on the map
60
+ * List existing location on the left part of the GUI
61
+ * Associate a note to a location. The first line of the note is used as the title in the list
62
+ * Search location by name, and add a new marker when selecting an entry in the list
63
+ * Delete markers
64
+ * Open and save local files
65
+ * Open and save files on any Nextcloud server you have access to
66
+ * Note text formatting (heading, bold, …)
67
+ * Image insertion (copy paste only currently)
68
+
69
+ ## Screenshots
70
+
71
+ ![Otripy GUI snapshot](https://github.com/kleag/otripy/blob/main/pics/otripy-snapshot.png "Otripy GUI")
72
+
73
+ ## Installation
74
+
75
+ ### For users familiar with Python
76
+
77
+ Otripy is on PyPi. To install it, run:
78
+
79
+ ```sh
80
+ pip install otripy
81
+ ```
82
+
83
+ ### For all others
84
+
85
+ If you are not used to installing Python packages, the simplest method is probably to use [uv](https://docs.astral.sh/uv/). Please [install it](https://docs.astral.sh/uv/getting-started/installation/) first. Then, open a terminal and create an uv virtual environment and activate it:
86
+
87
+
88
+ ```sh
89
+ uv venv otripy
90
+ source otripy/bin/activate
91
+ ```
92
+
93
+ Then install Otripy and run it:
94
+
95
+ ```sh
96
+ uv pip install otripy
97
+ otripy
98
+ ```
99
+
100
+ ## Usage
101
+
102
+ To launch the application:
103
+ ```bash
104
+ otripy
105
+ ```
106
+
107
+ ## Building and publishing
108
+
109
+ Otripy uses [uv](https://docs.astral.sh/uv/), please [install it](https://docs.astral.sh/uv/getting-started/installation/) if not already available.
110
+
111
+ If you just cloned this repository, cd to it and then:
112
+
113
+ ```sh
114
+ uv venv
115
+ uv sync --all-extras
116
+ uv pip install -r pyproject.toml --extra build
117
+ install -d dist
118
+ ```
119
+
120
+ Then, to build and publish:
121
+
122
+ ```sh
123
+ rm dist/otripy-*
124
+ bumpver update --patch # or --minor or --major
125
+ uv build
126
+ uv publish
127
+ uv sync --all-extras
128
+ git add uv.lock
129
+ git commit -m "Update lock to new package version"
130
+ ```
131
+
132
+
133
+ ## Contributing
134
+
135
+ Contributions are welcome! Please follow these steps:
136
+ 1. Fork the repository.
137
+ 2. Create a new branch (`git checkout -b feature-name`).
138
+ 3. Commit changes (`git commit -m "Add feature"`).
139
+ 4. Push to your branch (`git push origin feature-name`).
140
+ 5. Open a pull request.
141
+
142
+ ## License
143
+ This project is Free Software, licensed under the AGPL License. See the [AGPL](https://github.com/kleag/otripy/blob/main/AGPL.md) file for details. In summary: you can use it, share it, change it, redistribute your changes, but any version you offer, with or without changes must be under the same (or a compatible) license.
144
+
145
+ ## Credits
146
+ Otripy is developed and maintained by [Kleag](https://github.com/kleag). Special thanks to all contributors!
147
+
148
+ ---
149
+
150
+ For more information, visit the [GitHub repository](https://github.com/kleag/otripy) or the [PyPI page](https://pypi.org/project/otripy/).
151
+
otripy-1.0.0/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Otripy - An open trip planning tool
2
+
3
+
4
+ [![PyPI Version](https://img.shields.io/pypi/v/otripy)](https://pypi.org/project/otripy)
5
+ [![License](https://img.shields.io/pypi/l/otripy)](https://github.com/kleag/otripy/blob/main/AGPL.md)
6
+ [![Downloads](https://static.pepy.tech/badge/otripy/month)](https://pepy.tech/project/otripy)
7
+ [![Supported Versions](https://img.shields.io/pypi/pyversions/otripy)](https://pypi.org/project/otripy)
8
+ [![Contributors](https://img.shields.io/github/contributors/kleag/otripy)](https://github.com/kleag/otripy/graphs/contributors)
9
+
10
+ <!--
11
+ ![Python Versions](https://img.shields.io/pypi/pyversions/otripy)
12
+ ![Build Status](https://img.shields.io/github/actions/workflow/status/kleag/otripy/ci.yml)
13
+ -->
14
+
15
+ ## Description
16
+
17
+ Otripy is a GUI application for trip planning, providing an intuitive user interface for organizing travel visits efficiently. Otripy allows to plan a trip by adding markers on a map (based on OpenStreetMap) associated with a note.
18
+
19
+ Otripy is developed and daily tested on Linux. It should work on any platform. It has already been tested on MacOS.
20
+
21
+ Otripy is already usable but would be better with a lot of other features. Some wanted features are listed [here](https://github.com/kleag/otripy/issues). Don't hesitate to create a new issue if you think at other features. And contribute them if you can!
22
+
23
+ ### Current features:
24
+
25
+ * Display OpenStreetMap map
26
+ * Zoom and pan the map
27
+ * Adding location markers on the map
28
+ * List existing location on the left part of the GUI
29
+ * Associate a note to a location. The first line of the note is used as the title in the list
30
+ * Search location by name, and add a new marker when selecting an entry in the list
31
+ * Delete markers
32
+ * Open and save local files
33
+ * Open and save files on any Nextcloud server you have access to
34
+ * Note text formatting (heading, bold, …)
35
+ * Image insertion (copy paste only currently)
36
+
37
+ ## Screenshots
38
+
39
+ ![Otripy GUI snapshot](https://github.com/kleag/otripy/blob/main/pics/otripy-snapshot.png "Otripy GUI")
40
+
41
+ ## Installation
42
+
43
+ ### For users familiar with Python
44
+
45
+ Otripy is on PyPi. To install it, run:
46
+
47
+ ```sh
48
+ pip install otripy
49
+ ```
50
+
51
+ ### For all others
52
+
53
+ If you are not used to installing Python packages, the simplest method is probably to use [uv](https://docs.astral.sh/uv/). Please [install it](https://docs.astral.sh/uv/getting-started/installation/) first. Then, open a terminal and create an uv virtual environment and activate it:
54
+
55
+
56
+ ```sh
57
+ uv venv otripy
58
+ source otripy/bin/activate
59
+ ```
60
+
61
+ Then install Otripy and run it:
62
+
63
+ ```sh
64
+ uv pip install otripy
65
+ otripy
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ To launch the application:
71
+ ```bash
72
+ otripy
73
+ ```
74
+
75
+ ## Building and publishing
76
+
77
+ Otripy uses [uv](https://docs.astral.sh/uv/), please [install it](https://docs.astral.sh/uv/getting-started/installation/) if not already available.
78
+
79
+ If you just cloned this repository, cd to it and then:
80
+
81
+ ```sh
82
+ uv venv
83
+ uv sync --all-extras
84
+ uv pip install -r pyproject.toml --extra build
85
+ install -d dist
86
+ ```
87
+
88
+ Then, to build and publish:
89
+
90
+ ```sh
91
+ rm dist/otripy-*
92
+ bumpver update --patch # or --minor or --major
93
+ uv build
94
+ uv publish
95
+ uv sync --all-extras
96
+ git add uv.lock
97
+ git commit -m "Update lock to new package version"
98
+ ```
99
+
100
+
101
+ ## Contributing
102
+
103
+ Contributions are welcome! Please follow these steps:
104
+ 1. Fork the repository.
105
+ 2. Create a new branch (`git checkout -b feature-name`).
106
+ 3. Commit changes (`git commit -m "Add feature"`).
107
+ 4. Push to your branch (`git push origin feature-name`).
108
+ 5. Open a pull request.
109
+
110
+ ## License
111
+ This project is Free Software, licensed under the AGPL License. See the [AGPL](https://github.com/kleag/otripy/blob/main/AGPL.md) file for details. In summary: you can use it, share it, change it, redistribute your changes, but any version you offer, with or without changes must be under the same (or a compatible) license.
112
+
113
+ ## Credits
114
+ Otripy is developed and maintained by [Kleag](https://github.com/kleag). Special thanks to all contributors!
115
+
116
+ ---
117
+
118
+ For more information, visit the [GitHub repository](https://github.com/kleag/otripy) or the [PyPI page](https://pypi.org/project/otripy/).
119
+
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "otripy"
3
+ version = "1.0.0"
4
+ description = "A tool to plan your next trip"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "folium>=0.19.5",
9
+ "geopy>=2.4.1",
10
+ "lxml>=5.3.1",
11
+ "markdown>=3.7",
12
+ "nc-py-api>=0.19.0",
13
+ "numpy>=2.2.3",
14
+ "pillow>=11.1.0",
15
+ "pyside6>=6.8.2.1",
16
+ "typing-extensions>=4.12.2",
17
+ ]
18
+ authors = [{ name = "Gaël de Chalendar (aka Kleag)", email = "kleagg@gmail.com" }]
19
+ license = { text = "AGPL" }
20
+ classifiers = [
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ ]
27
+
28
+ [tool.uv]
29
+ package = true
30
+
31
+ [project.scripts]
32
+ otripy = "otripy.main:main"
33
+
34
+ [project.optional-dependencies]
35
+ dev = ["pytest", "black", "ruff", "mypy"] # Development tools
36
+ build = ["bumpver"] # For packaging and uploading
37
+ deploy = ["uv"] # Add tools needed for deployment
38
+
39
+ [bumpver]
40
+ current_version = "1.0.0"
41
+ version_pattern = "MAJOR.MINOR.PATCH"
42
+ commit = true
43
+ tag = true
44
+ push = true # Set to true if you want to push automatically
45
+
46
+ [bumpver.file_patterns]
47
+ "pyproject.toml" = [
48
+ 'version = "{version}"',
49
+ ]
50
+ "src/otripy/__init__.py" = [
51
+ '__version__ = "{version}"',
52
+ ]
53
+
otripy-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ __version__ = "2.0.0"
@@ -0,0 +1,84 @@
1
+ from PySide6.QtGui import QAction
2
+ from PySide6.QtWidgets import QApplication, QMainWindow, QDialog, QLabel, QLineEdit, QPushButton, QVBoxLayout, QMenuBar
3
+ from PySide6.QtCore import QSettings
4
+
5
+
6
+ class ConfigDialog(QDialog):
7
+ """Dialog for configuring Nextcloud settings"""
8
+ def __init__(self, settings, parent=None):
9
+ super().__init__(parent)
10
+ self.settings = settings
11
+ self.setWindowTitle("Configure Otripy")
12
+
13
+ # Create widgets
14
+ self.url_label = QLabel("Nextcloud URL:")
15
+ self.url_input = QLineEdit()
16
+
17
+ self.username_label = QLabel("Nextcloud Username:")
18
+ self.username_input = QLineEdit()
19
+
20
+ self.password_label = QLabel("Nextcloud Password:")
21
+ self.password_input = QLineEdit()
22
+ self.password_input.setEchoMode(QLineEdit.Password) # Hide password
23
+
24
+ self.save_button = QPushButton("Save")
25
+ self.save_button.clicked.connect(self.save_settings)
26
+
27
+ # Layout
28
+ layout = QVBoxLayout()
29
+ layout.addWidget(self.url_label)
30
+ layout.addWidget(self.url_input)
31
+ layout.addWidget(self.username_label)
32
+ layout.addWidget(self.username_input)
33
+ layout.addWidget(self.password_label)
34
+ layout.addWidget(self.password_input)
35
+ layout.addWidget(self.save_button)
36
+ self.setLayout(layout)
37
+
38
+ # Load existing settings
39
+ self.load_settings()
40
+
41
+ def load_settings(self):
42
+ """Load settings from QSettings"""
43
+ self.url_input.setText(self.settings.value("nextcloud/url", ""))
44
+ self.username_input.setText(self.settings.value("nextcloud/username", ""))
45
+ self.password_input.setText(self.settings.value("nextcloud/password", ""))
46
+
47
+ def save_settings(self):
48
+ """Save settings to QSettings"""
49
+ self.settings.setValue("nextcloud/url", self.url_input.text())
50
+ self.settings.setValue("nextcloud/username", self.username_input.text())
51
+ self.settings.setValue("nextcloud/password", self.password_input.text())
52
+ self.accept() # Close the dialog
53
+
54
+
55
+ class MainWindow(QMainWindow):
56
+ """Main Application Window"""
57
+ def __init__(self):
58
+ super().__init__()
59
+ self.setWindowTitle("MyApp")
60
+ self.setGeometry(100, 100, 800, 600)
61
+
62
+ # QSettings initialization
63
+ self.settings = QSettings("Kleag", "Otripy")
64
+
65
+ # Menu bar
66
+ menubar = self.menuBar()
67
+ config_menu = menubar.addMenu("Configuration")
68
+
69
+ # Configuration action
70
+ config_action = QAction("Configure MyApp", self)
71
+ config_action.triggered.connect(self.open_config_dialog)
72
+ config_menu.addAction(config_action)
73
+
74
+ def open_config_dialog(self):
75
+ """Open the configuration dialog"""
76
+ dialog = ConfigDialog(self.settings, self)
77
+ dialog.exec()
78
+
79
+
80
+ if __name__ == "__main__":
81
+ app = QApplication([])
82
+ window = MainWindow()
83
+ window.show()
84
+ app.exec()
@@ -0,0 +1,58 @@
1
+ from typing import List, Iterator
2
+
3
+ try:
4
+ from .location import Location
5
+ except ImportError:
6
+ from location import Location
7
+
8
+ class Journey:
9
+ def __init__(self, locations: List[Location] = None):
10
+ """Initialize the journey with a list of Location objects."""
11
+ self._locations = locations if locations is not None else []
12
+
13
+ def __getitem__(self, index):
14
+ """Enable indexing and slicing."""
15
+ return self._locations[index]
16
+
17
+ def __setitem__(self, index, value: Location):
18
+ """Enable item assignment."""
19
+ if not isinstance(value, Location):
20
+ raise TypeError("Only Location instances can be added to the journey.")
21
+ self._locations[index] = value
22
+
23
+ def __delitem__(self, index):
24
+ """Enable item deletion."""
25
+ del self._locations[index]
26
+
27
+ def __iter__(self) -> Iterator[Location]:
28
+ """Enable iteration."""
29
+ return iter(self._locations)
30
+
31
+ def __len__(self) -> int:
32
+ """Return the number of locations in the journey."""
33
+ return len(self._locations)
34
+
35
+ def append(self, location: Location):
36
+ """Add a location to the journey."""
37
+ if not isinstance(location, Location):
38
+ raise TypeError("Only Location instances can be added to the journey.")
39
+ self._locations.append(location)
40
+
41
+ def insert(self, index: int, location: Location):
42
+ """Insert a location at a specific index."""
43
+ if not isinstance(location, Location):
44
+ raise TypeError("Only Location instances can be inserted into the journey.")
45
+ self._locations.insert(index, location)
46
+
47
+ def remove(self, location: Location):
48
+ """Remove a location from the journey."""
49
+ self._locations.remove(location)
50
+
51
+ def __repr__(self) -> str:
52
+ return f"Journey({self._locations})"
53
+
54
+ def loc_by_id(self, id: str) -> Location:
55
+ for loc in self:
56
+ if loc.id == id:
57
+ return loc
58
+ return None
@@ -0,0 +1,46 @@
1
+ import logging
2
+ import uuid
3
+
4
+ from typing import Dict
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ class Location:
9
+ def __init__(self,
10
+ lat: float = 0.0,
11
+ lon: float = 0.0,
12
+ note: Dict[str, str] = {"markdown": ""},
13
+ id: str = str(uuid.uuid4())):
14
+ logger.info(f"Location({lat}, {lon}, {note}, {id})")
15
+ self.id = id
16
+ self.lat = lat
17
+ self.lon = lon
18
+ self.note = note
19
+
20
+ @classmethod
21
+ def from_data(cls, data: Dict[str, str | dict] = {}):
22
+ logger.info(f"Location.from_data({data})")
23
+ lat = float(data["lat"]) if "lat" in data else 0.0
24
+ lon = float(data["lon"]) if "lon" in data else 0.0
25
+ note = data["note"] if "note" in data else {"markdown": ""}
26
+ if type(note) is str:
27
+ note = {"markdown": note}
28
+ id = data["id"] if "id" in data else str(uuid.uuid4())
29
+ return cls(lat, lon, note, id)
30
+
31
+ def label(self):
32
+ return self.note["markdown"].split('\n')[0]
33
+
34
+ def to_html(self):
35
+ return self.note["markdown"].split('\n')[0].replace("\n", "<br>")
36
+
37
+ def location(self):
38
+ return [self.lat, self.lon]
39
+
40
+ def to_dict(self):
41
+ return {
42
+ "id": self.id,
43
+ "lat": self.lat,
44
+ "lon": self.lon,
45
+ "note": self.note
46
+ }