carconnectivity-plugin-database 0.1a3__py3-none-any.whl

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.
Files changed (34) hide show
  1. carconnectivity_database/__init__.py +0 -0
  2. carconnectivity_database/carconnectivity_database_base.py +26 -0
  3. carconnectivity_plugin_database-0.1a3.dist-info/METADATA +74 -0
  4. carconnectivity_plugin_database-0.1a3.dist-info/RECORD +34 -0
  5. carconnectivity_plugin_database-0.1a3.dist-info/WHEEL +5 -0
  6. carconnectivity_plugin_database-0.1a3.dist-info/entry_points.txt +2 -0
  7. carconnectivity_plugin_database-0.1a3.dist-info/licenses/LICENSE +21 -0
  8. carconnectivity_plugin_database-0.1a3.dist-info/top_level.txt +2 -0
  9. carconnectivity_plugins/database/__init__.py +0 -0
  10. carconnectivity_plugins/database/_version.py +34 -0
  11. carconnectivity_plugins/database/agents/base_agent.py +2 -0
  12. carconnectivity_plugins/database/agents/drive_state_agent.py +71 -0
  13. carconnectivity_plugins/database/agents/state_agent.py +94 -0
  14. carconnectivity_plugins/database/model/__init__.py +10 -0
  15. carconnectivity_plugins/database/model/alembic.ini +100 -0
  16. carconnectivity_plugins/database/model/base.py +4 -0
  17. carconnectivity_plugins/database/model/carconnectivity_schema/README +1 -0
  18. carconnectivity_plugins/database/model/carconnectivity_schema/__init__.py +0 -0
  19. carconnectivity_plugins/database/model/carconnectivity_schema/env.py +78 -0
  20. carconnectivity_plugins/database/model/carconnectivity_schema/script.py.mako +24 -0
  21. carconnectivity_plugins/database/model/carconnectivity_schema/versions/__init__.py +0 -0
  22. carconnectivity_plugins/database/model/connection_state.py +53 -0
  23. carconnectivity_plugins/database/model/datetime_decorator.py +44 -0
  24. carconnectivity_plugins/database/model/drive.py +87 -0
  25. carconnectivity_plugins/database/model/drive_level.py +46 -0
  26. carconnectivity_plugins/database/model/drive_range.py +49 -0
  27. carconnectivity_plugins/database/model/migrations.py +24 -0
  28. carconnectivity_plugins/database/model/outside_temperature.py +48 -0
  29. carconnectivity_plugins/database/model/state.py +52 -0
  30. carconnectivity_plugins/database/model/timedelta_decorator.py +33 -0
  31. carconnectivity_plugins/database/model/vehicle.py +148 -0
  32. carconnectivity_plugins/database/plugin.py +150 -0
  33. carconnectivity_plugins/database/ui/plugin_ui.py +48 -0
  34. carconnectivity_plugins/database/ui/templates/database/status.html +8 -0
File without changes
@@ -0,0 +1,26 @@
1
+ """Module containing the commandline interface for the carconnectivity package."""
2
+ from __future__ import annotations
3
+ from typing import TYPE_CHECKING
4
+
5
+ import logging
6
+
7
+ from carconnectivity.carconnectivity_base import CLI
8
+
9
+ from carconnectivity_plugins.database._version import __version__
10
+
11
+ if TYPE_CHECKING:
12
+ pass
13
+
14
+ LOG = logging.getLogger("carconnectivity-database")
15
+
16
+
17
+ def main() -> None:
18
+ """
19
+ Entry point for the car connectivity database application.
20
+
21
+ This function initializes and starts the command-line interface (CLI) for the
22
+ car connectivity application using the specified logger and application name.
23
+ """
24
+ cli: CLI = CLI(logger=LOG, name='carconnectivity-database', description='Commandline Interface to interact with Car Services of various brands',
25
+ subversion=__version__)
26
+ cli.main()
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: carconnectivity-plugin-database
3
+ Version: 0.1a3
4
+ Summary: CarConnectivity plugin for storing data to Databases
5
+ Author: Till Steinbach
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: End Users/Desktop
9
+ Classifier: Intended Audience :: System Administrators
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Utilities
17
+ Classifier: Topic :: System :: Monitoring
18
+ Classifier: Topic :: Home Automation
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: carconnectivity>=0.9.2
23
+ Requires-Dist: sqlalchemy~=2.0.45
24
+ Requires-Dist: psycopg2-binary~=2.9.11
25
+ Requires-Dist: alembic~=1.17.2
26
+ Requires-Dist: SQLAlchemy-Utc~=0.14.0
27
+ Dynamic: license-file
28
+
29
+
30
+
31
+ # CarConnectivity Plugin for Database storage
32
+ [![GitHub sourcecode](https://img.shields.io/badge/Source-GitHub-green)](https://github.com/tillsteinbach/CarConnectivity-plugin-database/)
33
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/tillsteinbach/CarConnectivity-plugin-database)](https://github.com/tillsteinbach/CarConnectivity-plugin-database/releases/latest)
34
+ [![GitHub](https://img.shields.io/github/license/tillsteinbach/CarConnectivity-plugin-database)](https://github.com/tillsteinbach/CarConnectivity-plugin-database/blob/master/LICENSE)
35
+ [![GitHub issues](https://img.shields.io/github/issues/tillsteinbach/CarConnectivity-plugin-database)](https://github.com/tillsteinbach/CarConnectivity-plugin-database/issues)
36
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/carconnectivity-plugin-database?label=PyPI%20Downloads)](https://pypi.org/project/carconnectivity-plugin-database/)
37
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/carconnectivity-plugin-database)](https://pypi.org/project/carconnectivity-plugin-database/)
38
+ [![Donate at PayPal](https://img.shields.io/badge/Donate-PayPal-2997d8)](https://www.paypal.com/donate?hosted_button_id=2BVFF5GJ9SXAJ)
39
+ [![Sponsor at Github](https://img.shields.io/badge/Sponsor-GitHub-28a745)](https://github.com/sponsors/tillsteinbach)
40
+
41
+ [CarConnectivity](https://github.com/tillsteinbach/CarConnectivity) is a python API to connect to various car services. If you want to store the data collected from your vehicle to a relational database (e.g. MySQL, PostgreSQL, or SQLite) this plugin will help you.
42
+
43
+ ### Install using PIP
44
+ If you want to use the CarConnectivity Plugin for Databases, the easiest way is to obtain it from [PyPI](https://pypi.org/project/carconnectivity-plugin-database/). Just install it using:
45
+ ```bash
46
+ pip3 install carconnectivity-plugin-database
47
+ ```
48
+ after you installed CarConnectivity
49
+
50
+ ## Configuration
51
+ In your carconnectivity.json configuration add a section for the database plugin like this. A documentation of all possible config options can be found [here](https://github.com/tillsteinbach/CarConnectivity-plugin-database/tree/main/doc/Config.md).
52
+ ```
53
+ {
54
+ "carConnectivity": {
55
+ "connectors": [
56
+ ...
57
+ ]
58
+ "plugins": [
59
+ {
60
+ "type": "database",
61
+ "config": {
62
+ "db_url": "sqlite:///carconnectivity.db"
63
+ }
64
+ }
65
+ ]
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Updates
71
+ If you want to update, the easiest way is:
72
+ ```bash
73
+ pip3 install carconnectivity-plugin-database --upgrade
74
+ ```
@@ -0,0 +1,34 @@
1
+ carconnectivity_database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ carconnectivity_database/carconnectivity_database_base.py,sha256=tsCWJJlBody2IDSw55WPkl-q4sAGNWHgid1iK7nQ5Jk,842
3
+ carconnectivity_plugin_database-0.1a3.dist-info/licenses/LICENSE,sha256=PIwI1alwDyOfvEQHdGCm2u9uf_mGE8030xZDfun0xTo,1071
4
+ carconnectivity_plugins/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ carconnectivity_plugins/database/_version.py,sha256=_w2cIB58_wzKZ1MSbddFmQnkPRdfZ2IDpMt6GPDBSXg,707
6
+ carconnectivity_plugins/database/plugin.py,sha256=GoZDei--vp0oT5GIij1E3Pa0XA8OvRXugSjKcUOgoiw,7524
7
+ carconnectivity_plugins/database/agents/base_agent.py,sha256=WsEoB-dIZ6le3vuObwPDOBQ5WJUyfzqrE8SQIJD5X4I,28
8
+ carconnectivity_plugins/database/agents/drive_state_agent.py,sha256=k494_Zq8jgULjICnjiX1v5oZvgG9wApc1n5G5AQstdE,3877
9
+ carconnectivity_plugins/database/agents/state_agent.py,sha256=Cqu4SWrQdct6jYUSLz620uGwv_iEc-JYjIXLEaGABT4,6146
10
+ carconnectivity_plugins/database/model/__init__.py,sha256=dT11R2M29wQLbMLWtrBpgYfQCoYIvOKWSjO9DqrwSjk,426
11
+ carconnectivity_plugins/database/model/alembic.ini,sha256=_cT30ZkhLipi-fFq1NOPzBfpd_FTqj55co53TMuLGSM,2702
12
+ carconnectivity_plugins/database/model/base.py,sha256=bYej-MB6Xp4On2hQA3k5BRvwxz5frKjFLKX7Arq61HQ,147
13
+ carconnectivity_plugins/database/model/connection_state.py,sha256=QI6rPffEhsaYwU-Xsp6W1SDfJ1civWASbZzzxlebtiA,2547
14
+ carconnectivity_plugins/database/model/datetime_decorator.py,sha256=284bx5oQ5ACErm26qnif171QtKtF8efI2IBDXlyffzw,1444
15
+ carconnectivity_plugins/database/model/drive.py,sha256=f6Lu_SL9MdyczDRBHAgyae5FwQq4EAghcRlR0MGI0oA,4290
16
+ carconnectivity_plugins/database/model/drive_level.py,sha256=GbU8Tf2eMN6Y9ixKFkJX-vOHBf3FP-MXqaQA36_bySA,2035
17
+ carconnectivity_plugins/database/model/drive_range.py,sha256=6Rz-j6oFL0UAHHwNGJ0E3ST23sUa1fEgY9qSzoj40KY,2202
18
+ carconnectivity_plugins/database/model/migrations.py,sha256=x6XMnzTFfhIDRvEZ4B1jR73dFdrNUcpMCWeyAi324QI,897
19
+ carconnectivity_plugins/database/model/outside_temperature.py,sha256=kxqZk86rSa2eDkySmyUMRrHBdLMU5n1lQ2iePfUwS6k,2391
20
+ carconnectivity_plugins/database/model/state.py,sha256=rJQV-C4cGxbDZwyVaPo3RjbmeilGv5mPszdCX9YZmiM,2426
21
+ carconnectivity_plugins/database/model/timedelta_decorator.py,sha256=mdB9ZibJV-4v5H4EY25yxg407O2r6b5D960wMmCqLuE,976
22
+ carconnectivity_plugins/database/model/vehicle.py,sha256=NTlQhxzlt0UK2EgeNUZqg71G2qNoKNnGFWjedHAJGv0,8174
23
+ carconnectivity_plugins/database/model/carconnectivity_schema/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
24
+ carconnectivity_plugins/database/model/carconnectivity_schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ carconnectivity_plugins/database/model/carconnectivity_schema/env.py,sha256=trMAoTgw_EWjjTPnw1fWbKyDGEpAtbvd1exqznTozaE,2093
26
+ carconnectivity_plugins/database/model/carconnectivity_schema/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494
27
+ carconnectivity_plugins/database/model/carconnectivity_schema/versions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ carconnectivity_plugins/database/ui/plugin_ui.py,sha256=LQOuXIeBOLH4Gg0Qqjmzu9BOHI0Rir9KLq3gZF5Rlr4,1815
29
+ carconnectivity_plugins/database/ui/templates/database/status.html,sha256=OypmDrRib6tBM1fC7ijyTmF9HI53XlpF4tq_diXiLKc,162
30
+ carconnectivity_plugin_database-0.1a3.dist-info/METADATA,sha256=favJKw69RkxAnQQOwygUaTMJXIzX0wB-zimOHcruOxU,3625
31
+ carconnectivity_plugin_database-0.1a3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
+ carconnectivity_plugin_database-0.1a3.dist-info/entry_points.txt,sha256=YB5RERxEA_pxYQesyAyzV8hmPM2o3_F9FZbnSQaSuLQ,105
33
+ carconnectivity_plugin_database-0.1a3.dist-info/top_level.txt,sha256=836FhYp_OWsQq5Vyjb95dilEMQPHUYCTZa2GBgNFEVE,49
34
+ carconnectivity_plugin_database-0.1a3.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ carconnectivity-database = carconnectivity_database.carconnectivity_database_base:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Till Steinbach
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ carconnectivity_database
2
+ carconnectivity_plugins
File without changes
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.1a3'
32
+ __version_tuple__ = version_tuple = (0, 1, 'a3')
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,2 @@
1
+ class BaseAgent():
2
+ pass
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+
4
+ from carconnectivity.observable import Observable
5
+
6
+ from carconnectivity_plugins.database.agents.base_agent import BaseAgent
7
+ from carconnectivity_plugins.database.model.drive_level import DriveLevel
8
+ from carconnectivity_plugins.database.model.drive_range import DriveRange
9
+
10
+ if TYPE_CHECKING:
11
+ from typing import Optional
12
+ from sqlalchemy.orm.session import Session
13
+
14
+ from carconnectivity.attributes import LevelAttribute, RangeAttribute
15
+
16
+ from carconnectivity_plugins.database.model.drive import Drive
17
+
18
+
19
+ class DriveStateAgent(BaseAgent):
20
+ def __init__(self, session: Session, drive: Drive) -> None:
21
+ if drive is None or drive.carconnectivity_drive is None:
22
+ raise ValueError("Drive or its carconnectivity_drive attribute is None")
23
+ self.session: Session = session
24
+ self.drive: Drive = drive
25
+
26
+ self.last_level: Optional[DriveLevel] = session.query(DriveLevel).filter(DriveLevel.drive_id == drive.id).order_by(DriveLevel.first_date.desc()).first()
27
+ self.last_range: Optional[DriveRange] = session.query(DriveRange).filter(DriveRange.drive_id == drive.id).order_by(DriveRange.first_date.desc()).first()
28
+
29
+ drive.carconnectivity_drive.level.add_observer(self.__on_level_change, Observable.ObserverEvent.UPDATED)
30
+ self.__on_level_change(drive.carconnectivity_drive.level, Observable.ObserverEvent.UPDATED)
31
+
32
+ drive.carconnectivity_drive.range.add_observer(self.__on_range_change, Observable.ObserverEvent.UPDATED)
33
+ self.__on_range_change(drive.carconnectivity_drive.range, Observable.ObserverEvent.UPDATED)
34
+
35
+ def __on_level_change(self, element: LevelAttribute, flags: Observable.ObserverEvent) -> None:
36
+ del flags
37
+ if self.last_level is not None:
38
+ self.session.refresh(self.last_level)
39
+ if (self.last_level is None or self.last_level.level != element.value) \
40
+ and element.last_updated is not None:
41
+ new_level: DriveLevel = DriveLevel(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
42
+ level=element.value)
43
+ with self.session.begin_nested():
44
+ self.session.add(new_level)
45
+ self.session.commit()
46
+ self.last_level = new_level
47
+ elif self.last_level is not None and self.last_level.level == element.value \
48
+ and element.last_updated is not None:
49
+ if self.last_level.last_date is None or element.last_updated > self.last_level.last_date:
50
+ with self.session.begin_nested():
51
+ self.last_level.last_date = element.last_updated
52
+ self.session.commit()
53
+
54
+ def __on_range_change(self, element: RangeAttribute, flags: Observable.ObserverEvent) -> None:
55
+ del flags
56
+ if self.last_range is not None:
57
+ self.session.refresh(self.last_range)
58
+ if (self.last_range is None or self.last_range.range != element.value) \
59
+ and element.last_updated is not None:
60
+ new_range: DriveRange = DriveRange(drive_id=self.drive.id, first_date=element.last_updated, last_date=element.last_updated,
61
+ range=element.value)
62
+ with self.session.begin_nested():
63
+ self.session.add(new_range)
64
+ self.session.commit()
65
+ self.last_range = new_range
66
+ elif self.last_range is not None and self.last_range.range == element.value \
67
+ and element.last_updated is not None:
68
+ if self.last_range.last_date is None or element.last_updated > self.last_range.last_date:
69
+ with self.session.begin_nested():
70
+ self.last_range.last_date = element.last_updated
71
+ self.session.commit()
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+
4
+ from carconnectivity.observable import Observable
5
+
6
+ from carconnectivity_plugins.database.agents.base_agent import BaseAgent
7
+ from carconnectivity_plugins.database.model.state import State
8
+ from carconnectivity_plugins.database.model.connection_state import ConnectionState
9
+ from carconnectivity_plugins.database.model.outside_temperature import OutsideTemperature
10
+
11
+ if TYPE_CHECKING:
12
+ from typing import Optional
13
+ from sqlalchemy.orm.session import Session
14
+
15
+ from carconnectivity.attributes import EnumAttribute, TemperatureAttribute
16
+
17
+ from carconnectivity.vehicle import GenericVehicle
18
+
19
+ from carconnectivity_plugins.database.model.vehicle import Vehicle
20
+
21
+
22
+ class StateAgent(BaseAgent):
23
+ def __init__(self, session: Session, vehicle: Vehicle) -> None:
24
+ if vehicle is None or vehicle.carconnectivity_vehicle is None:
25
+ raise ValueError("Vehicle or its carconnectivity_vehicle attribute is None")
26
+ self.session: Session = session
27
+ self.vehicle: Vehicle = vehicle
28
+ self.last_state: Optional[State] = session.query(State).filter(State.vehicle == vehicle).order_by(State.first_date.desc()).first()
29
+ self.last_connection_state: Optional[ConnectionState] = session.query(ConnectionState).filter(ConnectionState.vehicle == vehicle) \
30
+ .order_by(ConnectionState.first_date.desc()).first()
31
+ self.last_outside_temperature: Optional[OutsideTemperature] = session.query(OutsideTemperature).filter(OutsideTemperature.vehicle == vehicle) \
32
+ .order_by(OutsideTemperature.first_date.desc()).first()
33
+
34
+ vehicle.carconnectivity_vehicle.state.add_observer(self.__on_state_change, Observable.ObserverEvent.UPDATED)
35
+ self.__on_state_change(vehicle.carconnectivity_vehicle.state, Observable.ObserverEvent.UPDATED)
36
+
37
+ vehicle.carconnectivity_vehicle.connection_state.add_observer(self.__on_connection_state_change, Observable.ObserverEvent.UPDATED)
38
+ self.__on_connection_state_change(vehicle.carconnectivity_vehicle.connection_state, Observable.ObserverEvent.UPDATED)
39
+
40
+ vehicle.carconnectivity_vehicle.outside_temperature.add_observer(self.__on_outside_temperature_change, Observable.ObserverEvent.UPDATED)
41
+ self.__on_outside_temperature_change(vehicle.carconnectivity_vehicle.outside_temperature, Observable.ObserverEvent.UPDATED)
42
+
43
+ def __on_state_change(self, element: EnumAttribute[GenericVehicle.State], flags: Observable.ObserverEvent) -> None:
44
+ del flags
45
+ if self.last_state is not None:
46
+ self.session.refresh(self.last_state)
47
+ if (self.last_state is None or self.last_state.state != element.value) \
48
+ and element.last_updated is not None:
49
+ new_state: State = State(vin=self.vehicle.vin, first_date=element.last_updated, last_date=element.last_updated, state=element.value)
50
+ with self.session.begin_nested():
51
+ self.session.add(new_state)
52
+ self.session.commit()
53
+ self.last_state = new_state
54
+ elif self.last_state is not None and self.last_state.state == element.value and element.last_updated is not None:
55
+ if self.last_state.last_date is None or element.last_updated > self.last_state.last_date:
56
+ with self.session.begin_nested():
57
+ self.last_state.last_date = element.last_updated
58
+ self.session.commit()
59
+
60
+ def __on_connection_state_change(self, element: EnumAttribute[GenericVehicle.ConnectionState], flags: Observable.ObserverEvent) -> None:
61
+ del flags
62
+ if self.last_connection_state is not None:
63
+ self.session.refresh(self.last_connection_state)
64
+ if (self.last_connection_state is None or self.last_connection_state.connection_state != element.value) \
65
+ and element.last_updated is not None:
66
+ new_connection_state: ConnectionState = ConnectionState(vin=self.vehicle.vin, first_date=element.last_updated,
67
+ last_date=element.last_updated, connection_state=element.value)
68
+ with self.session.begin_nested():
69
+ self.session.add(new_connection_state)
70
+ self.session.commit()
71
+ self.last_connection_state = new_connection_state
72
+ elif self.last_connection_state is not None and self.last_connection_state.connection_state == element.value and element.last_updated is not None:
73
+ if self.last_connection_state.last_date is None or element.last_updated > self.last_connection_state.last_date:
74
+ with self.session.begin_nested():
75
+ self.last_connection_state.last_date = element.last_updated
76
+ self.session.commit()
77
+
78
+ def __on_outside_temperature_change(self, element: TemperatureAttribute, flags: Observable.ObserverEvent) -> None:
79
+ del flags
80
+ if self.last_outside_temperature is not None:
81
+ self.session.refresh(self.last_outside_temperature)
82
+ if (self.last_outside_temperature is None or self.last_outside_temperature.outside_temperature != element.value) \
83
+ and element.last_updated is not None:
84
+ new_outside_temperature: OutsideTemperature = OutsideTemperature(vin=self.vehicle.vin, first_date=element.last_updated,
85
+ last_date=element.last_updated, outside_temperature=element.value)
86
+ self.session.add(new_outside_temperature)
87
+ self.session.commit()
88
+ self.last_outside_temperature = new_outside_temperature
89
+ elif self.last_outside_temperature is not None and self.last_outside_temperature.outside_temperature == element.value \
90
+ and element.last_updated is not None:
91
+ if self.last_outside_temperature.last_date is None or element.last_updated > self.last_outside_temperature.last_date:
92
+ with self.session.begin_nested():
93
+ self.last_outside_temperature.last_date = element.last_updated
94
+ self.session.commit()
@@ -0,0 +1,10 @@
1
+ """ This module contains the database model for car connectivity plugins."""
2
+
3
+ from .drive import Drive # noqa: F401
4
+ from .drive_level import DriveLevel # noqa: F401
5
+ from .drive_range import DriveRange # noqa: F401
6
+ from .connection_state import ConnectionState # noqa: F401
7
+ from .outside_temperature import OutsideTemperature # noqa: F401
8
+ from .state import State # noqa: F401
9
+ from .vehicle import Vehicle # noqa: F401
10
+
@@ -0,0 +1,100 @@
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts
5
+ script_location = carconnectivity_schema
6
+
7
+ # template used to generate migration files
8
+ # file_template = %%(rev)s_%%(slug)s
9
+
10
+ # sys.path path, will be prepended to sys.path if present.
11
+ # defaults to the current working directory.
12
+ prepend_sys_path = .
13
+
14
+ # timezone to use when rendering the date within the migration file
15
+ # as well as the filename.
16
+ # If specified, requires the python-dateutil library that can be
17
+ # installed by adding `alembic[tz]` to the pip requirements
18
+ # string value is passed to dateutil.tz.gettz()
19
+ # leave blank for localtime
20
+ # timezone =
21
+
22
+ # max length of characters to apply to the
23
+ # "slug" field
24
+ # truncate_slug_length = 40
25
+
26
+ # set to 'true' to run the environment during
27
+ # the 'revision' command, regardless of autogenerate
28
+ # revision_environment = false
29
+
30
+ # set to 'true' to allow .pyc and .pyo files without
31
+ # a source .py file to be detected as revisions in the
32
+ # versions/ directory
33
+ # sourceless = false
34
+
35
+ # version location specification; This defaults
36
+ # to vwsfriend-schema/versions. When using multiple version
37
+ # directories, initial revisions must be specified with --version-path.
38
+ # The path separator used here should be the separator specified by "version_path_separator"
39
+ # version_locations = %(here)s/bar:%(here)s/bat:vwsfriend-schema/versions
40
+
41
+ # version path separator; As mentioned above, this is the character used to split
42
+ # version_locations. Valid values are:
43
+ #
44
+ # version_path_separator = :
45
+ # version_path_separator = ;
46
+ # version_path_separator = space
47
+ version_path_separator = os # default: use os.pathsep
48
+
49
+ # the output encoding used when revision files
50
+ # are written from script.py.mako
51
+ # output_encoding = utf-8
52
+
53
+ sqlalchemy.url = sqlite:///carconnectivity-schema/reference.db
54
+
55
+
56
+ [post_write_hooks]
57
+ # post_write_hooks defines scripts or Python functions that are run
58
+ # on newly generated revision scripts. See the documentation for further
59
+ # detail and examples
60
+
61
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
62
+ # hooks = black
63
+ # black.type = console_scripts
64
+ # black.entrypoint = black
65
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
66
+
67
+ # Logging configuration
68
+ [loggers]
69
+ keys = root,sqlalchemy,alembic
70
+
71
+ [handlers]
72
+ keys = console
73
+
74
+ [formatters]
75
+ keys = generic
76
+
77
+ [logger_root]
78
+ level = WARN
79
+ handlers = console
80
+ qualname =
81
+
82
+ [logger_sqlalchemy]
83
+ level = WARN
84
+ handlers =
85
+ qualname = sqlalchemy.engine
86
+
87
+ [logger_alembic]
88
+ level = INFO
89
+ handlers =
90
+ qualname = alembic
91
+
92
+ [handler_console]
93
+ class = StreamHandler
94
+ args = (sys.stderr,)
95
+ level = NOTSET
96
+ formatter = generic
97
+
98
+ [formatter_generic]
99
+ format = %(levelname)-5.5s [%(name)s] %(message)s
100
+ datefmt = %H:%M:%S
@@ -0,0 +1,4 @@
1
+ """Module just storing the Base class for SQLAlchemy models."""
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+
4
+ Base = declarative_base()
@@ -0,0 +1 @@
1
+ Generic single-database configuration.
@@ -0,0 +1,78 @@
1
+ # pylint: skip-file
2
+ from logging.config import fileConfig
3
+
4
+ from sqlalchemy import engine_from_config
5
+ from sqlalchemy import pool
6
+
7
+ from alembic import context
8
+
9
+ from carconnectivity_plugins.database.model.base import Base
10
+
11
+ # this is the Alembic Config object, which provides
12
+ # access to the values within the .ini file in use.
13
+ config = context.config
14
+
15
+ # Interpret the config file for Python logging.
16
+ # This line sets up loggers basically.
17
+ if config.attributes.get('configure_logger', True):
18
+ fileConfig(config.config_file_name)
19
+
20
+ target_metadata = Base.metadata
21
+
22
+ # other values from the config, defined by the needs of env.py,
23
+ # can be acquired:
24
+ # my_important_option = config.get_main_option("my_important_option")
25
+ # ... etc.
26
+
27
+
28
+ def run_migrations_offline():
29
+ """Run migrations in 'offline' mode.
30
+
31
+ This configures the context with just a URL
32
+ and not an Engine, though an Engine is acceptable
33
+ here as well. By skipping the Engine creation
34
+ we don't even need a DBAPI to be available.
35
+
36
+ Calls to context.execute() here emit the given string to the
37
+ script output.
38
+
39
+ """
40
+ url = config.get_main_option("sqlalchemy.url")
41
+ context.configure(
42
+ url=url,
43
+ target_metadata=target_metadata,
44
+ literal_binds=True,
45
+ dialect_opts={"paramstyle": "named"},
46
+ compare_type=True,
47
+ )
48
+
49
+ with context.begin_transaction():
50
+ context.run_migrations()
51
+
52
+
53
+ def run_migrations_online():
54
+ """Run migrations in 'online' mode.
55
+
56
+ In this scenario we need to create an Engine
57
+ and associate a connection with the context.
58
+
59
+ """
60
+ connectable = engine_from_config(
61
+ config.get_section(config.config_ini_section),
62
+ prefix="sqlalchemy.",
63
+ poolclass=pool.NullPool,
64
+ )
65
+
66
+ with connectable.connect() as connection:
67
+ context.configure(
68
+ connection=connection, target_metadata=target_metadata, compare_type=True
69
+ )
70
+
71
+ with context.begin_transaction():
72
+ context.run_migrations()
73
+
74
+
75
+ if context.is_offline_mode():
76
+ run_migrations_offline()
77
+ else:
78
+ run_migrations_online()
@@ -0,0 +1,24 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+ ${imports if imports else ""}
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = ${repr(up_revision)}
14
+ down_revision = ${repr(down_revision)}
15
+ branch_labels = ${repr(branch_labels)}
16
+ depends_on = ${repr(depends_on)}
17
+
18
+
19
+ def upgrade():
20
+ ${upgrades if upgrades else "pass"}
21
+
22
+
23
+ def downgrade():
24
+ ${downgrades if downgrades else "pass"}