Merged PR 134: PrimAITE app and user dirs are version specific
## Summary - Added _PrimaitePaths class that manages all the primaite locations using PlayformDirs. This class now creates new primaite locations for each version of primaite. - Rolled the _PrimaitePaths class out throughout the code base. - Updated the docs to reference the new version paths. - Updated the author from qinetiq to dstl - Bumped version number to 2.0.0rc2 ## Test process - Manual checks. Tough to test the install paths. ## Checklist - [ ] This PR is linked to a **work item** - [ ] I have performed **self-review** of the code - [ ] I have written **tests** for any new functionality added with this PR - [ ] I have updated the **documentation** if this PR changes or adds functionality - [ ] I have run **pre-commit** checks for code style Related work items: #1647
This commit is contained in:
@@ -19,8 +19,8 @@ sys.path.insert(0, os.path.abspath("../"))
|
|||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
year = datetime.datetime.now().year
|
year = datetime.datetime.now().year
|
||||||
project = "PrimAITE"
|
project = "PrimAITE"
|
||||||
copyright = f"Copyright (C) QinetiQ Training and Simulation Ltd 2021 - {year}"
|
copyright = f"Copyright (C) Defence Science and Technology Laboratory UK 2021 - {year}"
|
||||||
author = "QinetiQ Training and Simulation Ltd"
|
author = "Defence Science and Technology Laboratory UK"
|
||||||
|
|
||||||
# The short Major.Minor.Build version
|
# The short Major.Minor.Build version
|
||||||
with open("../src/primaite/VERSION", "r") as file:
|
with open("../src/primaite/VERSION", "r") as file:
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ Finally, specify your agent in your training config.
|
|||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
# ~/primaite/config/path/to/your/config_main.yaml
|
# ~/primaite/2.0.0rc2/config/path/to/your/config_main.yaml
|
||||||
|
|
||||||
# Training Config File
|
# Training Config File
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ Install PrimAITE
|
|||||||
.. code-tab:: bash
|
.. code-tab:: bash
|
||||||
:caption: Unix
|
:caption: Unix
|
||||||
|
|
||||||
mkdir ~/primaite
|
mkdir ~/primaite/2.0.0rc2
|
||||||
|
|
||||||
.. code-tab:: powershell
|
.. code-tab:: powershell
|
||||||
:caption: Windows (Powershell)
|
:caption: Windows (Powershell)
|
||||||
|
|
||||||
mkdir ~\primaite
|
mkdir ~\primaite\2.0.0rc2
|
||||||
|
|
||||||
2. Navigate to the primaite directory and create a new python virtual environment (venv)
|
2. Navigate to the primaite directory and create a new python virtual environment (venv)
|
||||||
|
|
||||||
@@ -55,13 +55,13 @@ Install PrimAITE
|
|||||||
.. code-tab:: bash
|
.. code-tab:: bash
|
||||||
:caption: Unix
|
:caption: Unix
|
||||||
|
|
||||||
cd ~/primaite
|
cd ~/primaite/2.0.0rc2
|
||||||
python3 -m venv .venv
|
python3 -m venv .venv
|
||||||
|
|
||||||
.. code-tab:: powershell
|
.. code-tab:: powershell
|
||||||
:caption: Windows (Powershell)
|
:caption: Windows (Powershell)
|
||||||
|
|
||||||
cd ~\primaite
|
cd ~\primaite\2.0.0rc2
|
||||||
python3 -m venv .venv
|
python3 -m venv .venv
|
||||||
attrib +h .venv /s /d # Hides the .venv directory
|
attrib +h .venv /s /d # Hides the .venv directory
|
||||||
|
|
||||||
|
|||||||
@@ -77,5 +77,5 @@ Glossary
|
|||||||
Gym
|
Gym
|
||||||
PrimAITE uses the Gym reinforcement learning framework API to create a training environment and interface with RL agents. Gym defines a common way of creating observations, actions, and rewards.
|
PrimAITE uses the Gym reinforcement learning framework API to create a training environment and interface with RL agents. Gym defines a common way of creating observations, actions, and rewards.
|
||||||
|
|
||||||
User data directory
|
User app home
|
||||||
PrimAITE supports upgrading software version while retaining user data. The user data directory is where configs, notebooks, and results are stored, this location is `~/primaite` on linux/darwin and `C:\Users\<username>\primaite` on Windows.
|
PrimAITE supports upgrading software version while retaining user data. The user data directory is where configs, notebooks, and results are stored, this location is `~/primaite<version>` on linux/darwin and `C:\Users\<username>\primaite\<version>` on Windows.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ v1.2 to v2.0 Migration guide
|
|||||||
|
|
||||||
**3. Location of configs**
|
**3. Location of configs**
|
||||||
|
|
||||||
In version 1.2, training configs and laydown configs were all stored in the project repository under ``src/primaite/config``. Version 2.0.0 introduced user data directories, and now when you install and setup PrimAITE, config files are stored in your user data location. On Linux/OSX, this is stored in ``~/primaite/config``. On Windows, this is stored in ``C:\Users\<your username>\primaite\configs``. Upon first setup, the configs folder is populated with some default yaml files. It is recommended that you store all your custom configuration files here.
|
In version 1.2, training configs and laydown configs were all stored in the project repository under ``src/primaite/config``. Version 2.0.0 introduced user data directories, and now when you install and setup PrimAITE, config files are stored in your user data location. On Linux/OSX, this is stored in ``~/primaite/2.0.0rc2/config``. On Windows, this is stored in ``C:\Users\<your username>\primaite\configs``. Upon first setup, the configs folder is populated with some default yaml files. It is recommended that you store all your custom configuration files here.
|
||||||
|
|
||||||
**4. Contents of configs**
|
**4. Contents of configs**
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ Both the ``primaite session`` and :func:`primaite.main.run` take a training conf
|
|||||||
.. code-tab:: bash
|
.. code-tab:: bash
|
||||||
:caption: Unix CLI
|
:caption: Unix CLI
|
||||||
|
|
||||||
cd ~/primaite
|
cd ~/primaite/2.0.0rc2
|
||||||
source ./.venv/bin/activate
|
source ./.venv/bin/activate
|
||||||
primaite session --tc ./config/my_training_config.yaml --ldc ./config/my_lay_down_config.yaml
|
primaite session --tc ./config/my_training_config.yaml --ldc ./config/my_lay_down_config.yaml
|
||||||
|
|
||||||
.. code-tab:: powershell
|
.. code-tab:: powershell
|
||||||
:caption: Powershell CLI
|
:caption: Powershell CLI
|
||||||
|
|
||||||
cd ~\primaite
|
cd ~\primaite\2.0.0rc2
|
||||||
.\.venv\Scripts\activate
|
.\.venv\Scripts\activate
|
||||||
primaite session --tc .\config\my_training_config.yaml --ldc .\config\my_lay_down_config.yaml
|
primaite session --tc .\config\my_training_config.yaml --ldc .\config\my_lay_down_config.yaml
|
||||||
|
|
||||||
@@ -41,11 +41,11 @@ Both the ``primaite session`` and :func:`primaite.main.run` take a training conf
|
|||||||
lay_down_config = <path to lay down config yaml file>
|
lay_down_config = <path to lay down config yaml file>
|
||||||
run(training_config, lay_down_config)
|
run(training_config, lay_down_config)
|
||||||
|
|
||||||
When a session is ran, a session output sub-directory is created in the users app sessions directory (``~/primaite/sessions``).
|
When a session is ran, a session output sub-directory is created in the users app sessions directory (``~/primaite/2.0.0rc2/sessions``).
|
||||||
The sub-directory is formatted as such: ``~/primaite/sessions/<yyyy-mm-dd>/<yyyy-mm-dd>_<hh-mm-dd>/``
|
The sub-directory is formatted as such: ``~/primaite/2.0.0rc2/sessions/<yyyy-mm-dd>/<yyyy-mm-dd>_<hh-mm-dd>/``
|
||||||
|
|
||||||
For example, when running a session at 17:30:00 on 31st January 2023, the session will output to:
|
For example, when running a session at 17:30:00 on 31st January 2023, the session will output to:
|
||||||
``~/primaite/sessions/2023-01-31/2023-01-31_17-30-00/``.
|
``~/primaite/2.0.0rc2/sessions/2023-01-31/2023-01-31_17-30-00/``.
|
||||||
|
|
||||||
``primaite session`` can be ran in the terminal/command prompt without arguments. It will use the default configs in the directory ``primaite/config/example_config``.
|
``primaite session`` can be ran in the terminal/command prompt without arguments. It will use the default configs in the directory ``primaite/config/example_config``.
|
||||||
|
|
||||||
@@ -110,43 +110,44 @@ For each training session, assuming the agent being trained implements the *save
|
|||||||
|
|
||||||
~/
|
~/
|
||||||
└── primaite/
|
└── primaite/
|
||||||
└── sessions/
|
└── 2.0.0rc2/
|
||||||
└── 2023-07-18/
|
└── sessions/
|
||||||
└── 2023-07-18_11-06-04/
|
└── 2023-07-18/
|
||||||
├── evaluation/
|
└── 2023-07-18_11-06-04/
|
||||||
│ ├── all_transactions_2023-07-18_11-06-04.csv
|
├── evaluation/
|
||||||
│ ├── average_reward_per_episode_2023-07-18_11-06-04.csv
|
│ ├── all_transactions_2023-07-18_11-06-04.csv
|
||||||
│ └── average_reward_per_episode_2023-07-18_11-06-04.png
|
│ ├── average_reward_per_episode_2023-07-18_11-06-04.csv
|
||||||
├── learning/
|
│ └── average_reward_per_episode_2023-07-18_11-06-04.png
|
||||||
│ ├── all_transactions_2023-07-18_11-06-04.csv
|
├── learning/
|
||||||
│ ├── average_reward_per_episode_2023-07-18_11-06-04.csv
|
│ ├── all_transactions_2023-07-18_11-06-04.csv
|
||||||
│ ├── average_reward_per_episode_2023-07-18_11-06-04.png
|
│ ├── average_reward_per_episode_2023-07-18_11-06-04.csv
|
||||||
│ ├── checkpoints/
|
│ ├── average_reward_per_episode_2023-07-18_11-06-04.png
|
||||||
│ │ └── sb3ppo_10.zip
|
│ ├── checkpoints/
|
||||||
│ ├── SB3_PPO.zip
|
│ │ └── sb3ppo_10.zip
|
||||||
│ └── tensorboard_logs/
|
│ ├── SB3_PPO.zip
|
||||||
│ ├── PPO_1/
|
│ └── tensorboard_logs/
|
||||||
│ │ └── events.out.tfevents.1689674765.METD-9PMRFB3.42960.0
|
│ ├── PPO_1/
|
||||||
│ ├── PPO_2/
|
│ │ └── events.out.tfevents.1689674765.METD-9PMRFB3.42960.0
|
||||||
│ │ └── events.out.tfevents.1689674766.METD-9PMRFB3.42960.1
|
│ ├── PPO_2/
|
||||||
│ ├── PPO_3/
|
│ │ └── events.out.tfevents.1689674766.METD-9PMRFB3.42960.1
|
||||||
│ │ └── events.out.tfevents.1689674766.METD-9PMRFB3.42960.2
|
│ ├── PPO_3/
|
||||||
│ ├── PPO_4/
|
│ │ └── events.out.tfevents.1689674766.METD-9PMRFB3.42960.2
|
||||||
│ │ └── events.out.tfevents.1689674767.METD-9PMRFB3.42960.3
|
│ ├── PPO_4/
|
||||||
│ ├── PPO_5/
|
│ │ └── events.out.tfevents.1689674767.METD-9PMRFB3.42960.3
|
||||||
│ │ └── events.out.tfevents.1689674767.METD-9PMRFB3.42960.4
|
│ ├── PPO_5/
|
||||||
│ ├── PPO_6/
|
│ │ └── events.out.tfevents.1689674767.METD-9PMRFB3.42960.4
|
||||||
│ │ └── events.out.tfevents.1689674768.METD-9PMRFB3.42960.5
|
│ ├── PPO_6/
|
||||||
│ ├── PPO_7/
|
│ │ └── events.out.tfevents.1689674768.METD-9PMRFB3.42960.5
|
||||||
│ │ └── events.out.tfevents.1689674768.METD-9PMRFB3.42960.6
|
│ ├── PPO_7/
|
||||||
│ ├── PPO_8/
|
│ │ └── events.out.tfevents.1689674768.METD-9PMRFB3.42960.6
|
||||||
│ │ └── events.out.tfevents.1689674769.METD-9PMRFB3.42960.7
|
│ ├── PPO_8/
|
||||||
│ ├── PPO_9/
|
│ │ └── events.out.tfevents.1689674769.METD-9PMRFB3.42960.7
|
||||||
│ │ └── events.out.tfevents.1689674770.METD-9PMRFB3.42960.8
|
│ ├── PPO_9/
|
||||||
│ └── PPO_10/
|
│ │ └── events.out.tfevents.1689674770.METD-9PMRFB3.42960.8
|
||||||
│ └── events.out.tfevents.1689674770.METD-9PMRFB3.42960.9
|
│ └── PPO_10/
|
||||||
├── network_2023-07-18_11-06-04.png
|
│ └── events.out.tfevents.1689674770.METD-9PMRFB3.42960.9
|
||||||
└── session_metadata.json
|
├── network_2023-07-18_11-06-04.png
|
||||||
|
└── session_metadata.json
|
||||||
|
|
||||||
Loading a session
|
Loading a session
|
||||||
-----------------
|
-----------------
|
||||||
@@ -159,14 +160,14 @@ A previous session can be loaded by providing the **directory** of the previous
|
|||||||
.. code-tab:: bash
|
.. code-tab:: bash
|
||||||
:caption: Unix CLI
|
:caption: Unix CLI
|
||||||
|
|
||||||
cd ~/primaite
|
cd ~/primaite/2.0.0rc2
|
||||||
source ./.venv/bin/activate
|
source ./.venv/bin/activate
|
||||||
primaite session --load "path/to/session"
|
primaite session --load "path/to/session"
|
||||||
|
|
||||||
.. code-tab:: bash
|
.. code-tab:: bash
|
||||||
:caption: Powershell CLI
|
:caption: Powershell CLI
|
||||||
|
|
||||||
cd ~\primaite
|
cd ~\primaite\2.0.0rc2
|
||||||
.\.venv\Scripts\activate
|
.\.venv\Scripts\activate
|
||||||
primaite session --load "path\to\session"
|
primaite session --load "path\to\session"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
[project]
|
[project]
|
||||||
name = "primaite"
|
name = "primaite"
|
||||||
description = "PrimAITE (Primary-level AI Training Environment) is a simulation environment for training AI under the ARCD programme."
|
description = "PrimAITE (Primary-level AI Training Environment) is a simulation environment for training AI under the ARCD programme."
|
||||||
authors = [{name="QinetiQ Training and Simulation Ltd"}]
|
authors = [{name="Defence Science and Technology Laboratory UK", email="oss@dstl.gov.uk"}]
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
requires-python = ">=3.8, <3.11"
|
requires-python = ">=3.8, <3.11"
|
||||||
dynamic = ["version", "readme"]
|
dynamic = ["version", "readme"]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.0.0rc1
|
2.0.0rc2
|
||||||
|
|||||||
@@ -1,23 +1,126 @@
|
|||||||
# Crown Owned Copyright (C) Dstl 2023. DEFCON 703. Shared in confidence.
|
# Crown Owned Copyright (C) Dstl 2023. DEFCON 703. Shared in confidence.
|
||||||
import logging
|
import logging
|
||||||
import logging.config
|
import logging.config
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from bisect import bisect
|
from bisect import bisect
|
||||||
from logging import Formatter, Logger, LogRecord, StreamHandler
|
from logging import Formatter, Logger, LogRecord, StreamHandler
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Final
|
from typing import Any, Dict, Final, List
|
||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import yaml
|
import yaml
|
||||||
from platformdirs import PlatformDirs
|
from platformdirs import PlatformDirs
|
||||||
|
|
||||||
_PLATFORM_DIRS: Final[PlatformDirs] = PlatformDirs(appname="primaite")
|
with open(Path(__file__).parent.resolve() / "VERSION", "r") as file:
|
||||||
"""An instance of `PlatformDirs` set with appname='primaite'."""
|
__version__ = file.readline().strip()
|
||||||
|
|
||||||
|
|
||||||
|
class _PrimaitePaths:
|
||||||
|
"""
|
||||||
|
A Primaite paths class that leverages PlatformDirs.
|
||||||
|
|
||||||
|
The PlatformDirs appname is 'primaite' and the version is ``primaite.__version__`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._dirs: Final[PlatformDirs] = PlatformDirs(appname="primaite", version=__version__)
|
||||||
|
|
||||||
|
def _get_dirs_properties(self) -> List[str]:
|
||||||
|
class_items = self.__class__.__dict__.items()
|
||||||
|
return [k for k, v in class_items if isinstance(v, property)]
|
||||||
|
|
||||||
|
def mkdirs(self):
|
||||||
|
"""
|
||||||
|
Creates all Primaite directories.
|
||||||
|
|
||||||
|
Does this by retrieving all properties in the PrimaiteDirs class and calls each one.
|
||||||
|
"""
|
||||||
|
for p in self._get_dirs_properties():
|
||||||
|
getattr(self, p)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_home_path(self) -> Path:
|
||||||
|
"""The PrimAITE user home path."""
|
||||||
|
path = Path.home() / "primaite" / __version__
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_sessions_path(self) -> Path:
|
||||||
|
"""The PrimAITE user sessions path."""
|
||||||
|
path = self.user_home_path / "sessions"
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_config_path(self) -> Path:
|
||||||
|
"""The PrimAITE user config path."""
|
||||||
|
path = self.user_home_path / "config"
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def user_notebooks_path(self) -> Path:
|
||||||
|
"""The PrimAITE user notebooks path."""
|
||||||
|
path = self.user_home_path / "notebooks"
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_home_path(self) -> Path:
|
||||||
|
"""The PrimAITE app home path."""
|
||||||
|
path = self._dirs.user_data_path
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_config_dir_path(self) -> Path:
|
||||||
|
"""The PrimAITE app config directory path."""
|
||||||
|
path = self._dirs.user_config_path
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_config_file_path(self) -> Path:
|
||||||
|
"""The PrimAITE app config file path."""
|
||||||
|
return self.app_config_dir_path / "primaite_config.yaml"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_log_dir_path(self) -> Path:
|
||||||
|
"""The PrimAITE app log directory path."""
|
||||||
|
if sys.platform == "win32":
|
||||||
|
path = self.app_home_path / "logs"
|
||||||
|
else:
|
||||||
|
path = self._dirs.user_log_path
|
||||||
|
path.mkdir(exist_ok=True, parents=True)
|
||||||
|
return path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def app_log_file_path(self) -> Path:
|
||||||
|
"""The PrimAITE app log file path."""
|
||||||
|
return self.app_log_dir_path / "primaite.log"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
properties_str = ", ".join([f"{p}='{getattr(self, p)}'" for p in self._get_dirs_properties()])
|
||||||
|
return f"{self.__class__.__name__}({properties_str})"
|
||||||
|
|
||||||
|
|
||||||
|
PRIMAITE_PATHS: Final[_PrimaitePaths] = _PrimaitePaths()
|
||||||
|
|
||||||
|
|
||||||
|
def _host_primaite_config():
|
||||||
|
if not PRIMAITE_PATHS.app_config_file_path.exists():
|
||||||
|
pkg_config_path = Path(pkg_resources.resource_filename("primaite", "setup/_package_data/primaite_config.yaml"))
|
||||||
|
shutil.copy2(pkg_config_path, PRIMAITE_PATHS.app_config_file_path)
|
||||||
|
|
||||||
|
|
||||||
|
_host_primaite_config()
|
||||||
|
|
||||||
|
|
||||||
def _get_primaite_config() -> Dict:
|
def _get_primaite_config() -> Dict:
|
||||||
config_path = _PLATFORM_DIRS.user_config_path / "primaite_config.yaml"
|
config_path = PRIMAITE_PATHS.app_config_file_path
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
config_path = Path(pkg_resources.resource_filename("primaite", "setup/_package_data/primaite_config.yaml"))
|
config_path = Path(pkg_resources.resource_filename("primaite", "setup/_package_data/primaite_config.yaml"))
|
||||||
with open(config_path, "r") as file:
|
with open(config_path, "r") as file:
|
||||||
@@ -36,35 +139,7 @@ def _get_primaite_config() -> Dict:
|
|||||||
|
|
||||||
_PRIMAITE_CONFIG = _get_primaite_config()
|
_PRIMAITE_CONFIG = _get_primaite_config()
|
||||||
|
|
||||||
_USER_DIRS: Final[Path] = Path.home() / "primaite"
|
|
||||||
"""The users home space for PrimAITE which is located at: ~/primaite."""
|
|
||||||
|
|
||||||
NOTEBOOKS_DIR: Final[Path] = _USER_DIRS / "notebooks"
|
|
||||||
"""
|
|
||||||
The path to the users notebooks directory as an instance of `Path` or
|
|
||||||
`PosixPath`, depending on the OS.
|
|
||||||
|
|
||||||
Users notebooks are stored at: ``~/primaite/notebooks``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
USERS_CONFIG_DIR: Final[Path] = _USER_DIRS / "config"
|
|
||||||
"""
|
|
||||||
The path to the users config directory as an instance of `Path` or
|
|
||||||
`PosixPath`, depending on the OS.
|
|
||||||
|
|
||||||
Users config files are stored at: ``~/primaite/config``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
SESSIONS_DIR: Final[Path] = _USER_DIRS / "sessions"
|
|
||||||
"""
|
|
||||||
The path to the users PrimAITE Sessions directory as an instance of `Path` or
|
|
||||||
`PosixPath`, depending on the OS.
|
|
||||||
|
|
||||||
Users PrimAITE Sessions are stored at: ``~/primaite/sessions``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# region Setup Logging
|
|
||||||
class _LevelFormatter(Formatter):
|
class _LevelFormatter(Formatter):
|
||||||
"""
|
"""
|
||||||
A custom level-specific formatter.
|
A custom level-specific formatter.
|
||||||
@@ -87,14 +162,6 @@ class _LevelFormatter(Formatter):
|
|||||||
return formatter.format(record)
|
return formatter.format(record)
|
||||||
|
|
||||||
|
|
||||||
def _log_dir() -> Path:
|
|
||||||
if sys.platform == "win32":
|
|
||||||
dir_path = _PLATFORM_DIRS.user_data_path / "logs"
|
|
||||||
else:
|
|
||||||
dir_path = _PLATFORM_DIRS.user_log_path
|
|
||||||
return dir_path
|
|
||||||
|
|
||||||
|
|
||||||
_LEVEL_FORMATTER: Final[_LevelFormatter] = _LevelFormatter(
|
_LEVEL_FORMATTER: Final[_LevelFormatter] = _LevelFormatter(
|
||||||
{
|
{
|
||||||
logging.DEBUG: _PRIMAITE_CONFIG["logging"]["logger_format"]["DEBUG"],
|
logging.DEBUG: _PRIMAITE_CONFIG["logging"]["logger_format"]["DEBUG"],
|
||||||
@@ -105,18 +172,10 @@ _LEVEL_FORMATTER: Final[_LevelFormatter] = _LevelFormatter(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
LOG_DIR: Final[Path] = _log_dir()
|
|
||||||
"""The path to the app log directory as an instance of `Path` or `PosixPath`, depending on the OS."""
|
|
||||||
|
|
||||||
LOG_DIR.mkdir(exist_ok=True, parents=True)
|
|
||||||
|
|
||||||
LOG_PATH: Final[Path] = LOG_DIR / "primaite.log"
|
|
||||||
"""The primaite.log file path as an instance of `Path` or `PosixPath`, depending on the OS."""
|
|
||||||
|
|
||||||
_STREAM_HANDLER: Final[StreamHandler] = StreamHandler()
|
_STREAM_HANDLER: Final[StreamHandler] = StreamHandler()
|
||||||
|
|
||||||
_FILE_HANDLER: Final[RotatingFileHandler] = RotatingFileHandler(
|
_FILE_HANDLER: Final[RotatingFileHandler] = RotatingFileHandler(
|
||||||
filename=LOG_PATH,
|
filename=PRIMAITE_PATHS.app_log_file_path,
|
||||||
maxBytes=10485760, # 10MB
|
maxBytes=10485760, # 10MB
|
||||||
backupCount=9, # Max 100MB of logs
|
backupCount=9, # Max 100MB of logs
|
||||||
encoding="utf8",
|
encoding="utf8",
|
||||||
@@ -146,10 +205,3 @@ def getLogger(name: str) -> Logger: # noqa
|
|||||||
logger.setLevel(_PRIMAITE_CONFIG["log_level"])
|
logger.setLevel(_PRIMAITE_CONFIG["log_level"])
|
||||||
|
|
||||||
return logger
|
return logger
|
||||||
|
|
||||||
|
|
||||||
# endregion
|
|
||||||
|
|
||||||
|
|
||||||
with open(Path(__file__).parent.resolve() / "VERSION", "r") as file:
|
|
||||||
__version__ = file.readline().strip()
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from typing import Any, Dict, Optional, Union
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import primaite
|
import primaite
|
||||||
from primaite import getLogger, SESSIONS_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
from primaite.config import lay_down_config, training_config
|
from primaite.config import lay_down_config, training_config
|
||||||
from primaite.config.training_config import TrainingConfig
|
from primaite.config.training_config import TrainingConfig
|
||||||
from primaite.data_viz.session_plots import plot_av_reward_per_episode
|
from primaite.data_viz.session_plots import plot_av_reward_per_episode
|
||||||
@@ -32,7 +32,7 @@ def get_session_path(session_timestamp: datetime) -> Path:
|
|||||||
"""
|
"""
|
||||||
date_dir = session_timestamp.strftime("%Y-%m-%d")
|
date_dir = session_timestamp.strftime("%Y-%m-%d")
|
||||||
session_path = session_timestamp.strftime("%Y-%m-%d_%H-%M-%S")
|
session_path = session_timestamp.strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
session_path = SESSIONS_DIR / date_dir / session_path
|
session_path = PRIMAITE_PATHS.user_sessions_path / date_dir / session_path
|
||||||
session_path.mkdir(exist_ok=True, parents=True)
|
session_path.mkdir(exist_ok=True, parents=True)
|
||||||
|
|
||||||
return session_path
|
return session_path
|
||||||
|
|||||||
@@ -2,17 +2,14 @@
|
|||||||
"""Provides a CLI using Typer as an entry point."""
|
"""Provides a CLI using Typer as an entry point."""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
import typer
|
import typer
|
||||||
import yaml
|
import yaml
|
||||||
from platformdirs import PlatformDirs
|
|
||||||
from typing_extensions import Annotated
|
from typing_extensions import Annotated
|
||||||
|
|
||||||
|
from primaite import PRIMAITE_PATHS
|
||||||
from primaite.data_viz import PlotlyTemplate
|
from primaite.data_viz import PlotlyTemplate
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
@@ -47,10 +44,10 @@ def logs(last_n: Annotated[int, typer.Option("-n")]) -> None:
|
|||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from primaite import LOG_PATH
|
from primaite import PRIMAITE_PATHS
|
||||||
|
|
||||||
if os.path.isfile(LOG_PATH):
|
if os.path.isfile(PRIMAITE_PATHS.app_log_file_path):
|
||||||
with open(LOG_PATH) as file:
|
with open(PRIMAITE_PATHS.app_log_file_path) as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
for line in lines[-last_n:]:
|
for line in lines[-last_n:]:
|
||||||
print(re.sub(r"\n*", "", line))
|
print(re.sub(r"\n*", "", line))
|
||||||
@@ -70,16 +67,13 @@ def log_level(level: Annotated[Optional[_LogLevel], typer.Argument()] = None) ->
|
|||||||
|
|
||||||
For example, to set the to debug, call: primaite log-level DEBUG
|
For example, to set the to debug, call: primaite log-level DEBUG
|
||||||
"""
|
"""
|
||||||
app_dirs = PlatformDirs(appname="primaite")
|
if PRIMAITE_PATHS.app_config_file_path.exists():
|
||||||
app_dirs.user_config_path.mkdir(exist_ok=True, parents=True)
|
with open(PRIMAITE_PATHS.app_config_file_path, "r") as file:
|
||||||
user_config_path = app_dirs.user_config_path / "primaite_config.yaml"
|
|
||||||
if user_config_path.exists():
|
|
||||||
with open(user_config_path, "r") as file:
|
|
||||||
primaite_config = yaml.safe_load(file)
|
primaite_config = yaml.safe_load(file)
|
||||||
|
|
||||||
if level:
|
if level:
|
||||||
primaite_config["logging"]["log_level"] = level.value
|
primaite_config["logging"]["log_level"] = level.value
|
||||||
with open(user_config_path, "w") as file:
|
with open(PRIMAITE_PATHS.app_config_file_path, "w") as file:
|
||||||
yaml.dump(primaite_config, file)
|
yaml.dump(primaite_config, file)
|
||||||
print(f"PrimAITE Log Level: {level}")
|
print(f"PrimAITE Log Level: {level}")
|
||||||
else:
|
else:
|
||||||
@@ -118,16 +112,8 @@ def setup(overwrite_existing: bool = True) -> None:
|
|||||||
|
|
||||||
WARNING: All user-data will be lost.
|
WARNING: All user-data will be lost.
|
||||||
"""
|
"""
|
||||||
# Does this way to avoid using PrimAITE package before config is loaded
|
|
||||||
app_dirs = PlatformDirs(appname="primaite")
|
|
||||||
app_dirs.user_config_path.mkdir(exist_ok=True, parents=True)
|
|
||||||
user_config_path = app_dirs.user_config_path / "primaite_config.yaml"
|
|
||||||
pkg_config_path = Path(pkg_resources.resource_filename("primaite", "setup/_package_data/primaite_config.yaml"))
|
|
||||||
|
|
||||||
shutil.copy2(pkg_config_path, user_config_path)
|
|
||||||
|
|
||||||
from primaite import getLogger
|
from primaite import getLogger
|
||||||
from primaite.setup import old_installation_clean_up, reset_demo_notebooks, reset_example_configs, setup_app_dirs
|
from primaite.setup import old_installation_clean_up, reset_demo_notebooks, reset_example_configs
|
||||||
|
|
||||||
_LOGGER = getLogger(__name__)
|
_LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
@@ -136,7 +122,7 @@ def setup(overwrite_existing: bool = True) -> None:
|
|||||||
_LOGGER.info("Building primaite_config.yaml...")
|
_LOGGER.info("Building primaite_config.yaml...")
|
||||||
|
|
||||||
_LOGGER.info("Building the PrimAITE app directories...")
|
_LOGGER.info("Building the PrimAITE app directories...")
|
||||||
setup_app_dirs.run()
|
PRIMAITE_PATHS.mkdirs()
|
||||||
|
|
||||||
_LOGGER.info("Rebuilding the demo notebooks...")
|
_LOGGER.info("Rebuilding the demo notebooks...")
|
||||||
reset_demo_notebooks.run(overwrite_existing=True)
|
reset_demo_notebooks.run(overwrite_existing=True)
|
||||||
@@ -198,16 +184,13 @@ def plotly_template(template: Annotated[Optional[PlotlyTemplate], typer.Argument
|
|||||||
|
|
||||||
For example, to set as plotly_dark, call: primaite plotly-template PLOTLY_DARK
|
For example, to set as plotly_dark, call: primaite plotly-template PLOTLY_DARK
|
||||||
"""
|
"""
|
||||||
app_dirs = PlatformDirs(appname="primaite")
|
if PRIMAITE_PATHS.app_config_file_path.exists():
|
||||||
app_dirs.user_config_path.mkdir(exist_ok=True, parents=True)
|
with open(PRIMAITE_PATHS.app_config_file_path, "r") as file:
|
||||||
user_config_path = app_dirs.user_config_path / "primaite_config.yaml"
|
|
||||||
if user_config_path.exists():
|
|
||||||
with open(user_config_path, "r") as file:
|
|
||||||
primaite_config = yaml.safe_load(file)
|
primaite_config = yaml.safe_load(file)
|
||||||
|
|
||||||
if template:
|
if template:
|
||||||
primaite_config["session"]["outputs"]["plots"]["template"] = template.value
|
primaite_config["session"]["outputs"]["plots"]["template"] = template.value
|
||||||
with open(user_config_path, "w") as file:
|
with open(PRIMAITE_PATHS.app_config_file_path, "w") as file:
|
||||||
yaml.dump(primaite_config, file)
|
yaml.dump(primaite_config, file)
|
||||||
print(f"PrimAITE plotly template: {template.value}")
|
print(f"PrimAITE plotly template: {template.value}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ from typing import Any, Dict, Final, Union
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from primaite import getLogger, USERS_CONFIG_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
|
|
||||||
_LOGGER: Logger = getLogger(__name__)
|
_LOGGER: Logger = getLogger(__name__)
|
||||||
|
|
||||||
_EXAMPLE_LAY_DOWN: Final[Path] = USERS_CONFIG_DIR / "example_config" / "lay_down"
|
_EXAMPLE_LAY_DOWN: Final[Path] = PRIMAITE_PATHS.user_config_path / "example_config" / "lay_down"
|
||||||
|
|
||||||
|
|
||||||
def convert_legacy_lay_down_config_dict(legacy_config_dict: Dict[str, Any]) -> Dict[str, Any]:
|
def convert_legacy_lay_down_config_dict(legacy_config_dict: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from typing import Any, Dict, Final, Optional, Union
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from primaite import getLogger, USERS_CONFIG_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
from primaite.common.enums import (
|
from primaite.common.enums import (
|
||||||
ActionType,
|
ActionType,
|
||||||
AgentFramework,
|
AgentFramework,
|
||||||
@@ -22,7 +22,7 @@ from primaite.common.enums import (
|
|||||||
|
|
||||||
_LOGGER: Logger = getLogger(__name__)
|
_LOGGER: Logger = getLogger(__name__)
|
||||||
|
|
||||||
_EXAMPLE_TRAINING: Final[Path] = USERS_CONFIG_DIR / "example_config" / "training"
|
_EXAMPLE_TRAINING: Final[Path] = PRIMAITE_PATHS.user_config_path / "example_config" / "training"
|
||||||
|
|
||||||
|
|
||||||
def main_training_config_path() -> Path:
|
def main_training_config_path() -> Path:
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ import polars as pl
|
|||||||
import yaml
|
import yaml
|
||||||
from plotly.graph_objs import Figure
|
from plotly.graph_objs import Figure
|
||||||
|
|
||||||
from primaite import _PLATFORM_DIRS
|
from primaite import PRIMAITE_PATHS
|
||||||
|
|
||||||
|
|
||||||
def get_plotly_config() -> Dict:
|
def get_plotly_config() -> Dict:
|
||||||
"""Get the plotly config from primaite_config.yaml."""
|
"""Get the plotly config from primaite_config.yaml."""
|
||||||
user_config_path = _PLATFORM_DIRS.user_config_path / "primaite_config.yaml"
|
with open(PRIMAITE_PATHS.app_config_file_path, "r") as file:
|
||||||
with open(user_config_path, "r") as file:
|
|
||||||
primaite_config = yaml.safe_load(file)
|
primaite_config = yaml.safe_load(file)
|
||||||
return primaite_config["session"]["outputs"]["plots"]
|
return primaite_config["session"]["outputs"]["plots"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# Crown Owned Copyright (C) Dstl 2023. DEFCON 703. Shared in confidence.
|
||||||
class PrimaiteError(Exception):
|
class PrimaiteError(Exception):
|
||||||
"""The root PrimAITe Error."""
|
"""The root PrimAITe Error."""
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
|
|
||||||
from primaite import getLogger, NOTEBOOKS_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
|
|
||||||
_LOGGER: Logger = getLogger(__name__)
|
_LOGGER: Logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ def start_jupyter_session() -> None:
|
|||||||
jupyter_cmd = "jupyter lab"
|
jupyter_cmd = "jupyter lab"
|
||||||
|
|
||||||
working_dir = os.getcwd()
|
working_dir = os.getcwd()
|
||||||
os.chdir(NOTEBOOKS_DIR)
|
os.chdir(PRIMAITE_PATHS.user_notebooks_path)
|
||||||
subprocess.Popen(jupyter_cmd)
|
subprocess.Popen(jupyter_cmd)
|
||||||
os.chdir(working_dir)
|
os.chdir(working_dir)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from primaite import getLogger, NOTEBOOKS_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
|
|
||||||
_LOGGER: Logger = getLogger(__name__)
|
_LOGGER: Logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ def run(overwrite_existing: bool = True) -> None:
|
|||||||
for file in files:
|
for file in files:
|
||||||
fp = os.path.join(subdir, file)
|
fp = os.path.join(subdir, file)
|
||||||
path_split = os.path.relpath(fp, notebooks_package_data_root).split(os.sep)
|
path_split = os.path.relpath(fp, notebooks_package_data_root).split(os.sep)
|
||||||
target_fp = NOTEBOOKS_DIR / Path(*path_split)
|
target_fp = PRIMAITE_PATHS.user_notebooks_path / Path(*path_split)
|
||||||
target_fp.parent.mkdir(exist_ok=True, parents=True)
|
target_fp.parent.mkdir(exist_ok=True, parents=True)
|
||||||
copy_file = not target_fp.is_file()
|
copy_file = not target_fp.is_file()
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from primaite import getLogger, USERS_CONFIG_DIR
|
from primaite import getLogger, PRIMAITE_PATHS
|
||||||
|
|
||||||
_LOGGER = getLogger(__name__)
|
_LOGGER = getLogger(__name__)
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ def run(overwrite_existing: bool = True) -> None:
|
|||||||
for file in files:
|
for file in files:
|
||||||
fp = os.path.join(subdir, file)
|
fp = os.path.join(subdir, file)
|
||||||
path_split = os.path.relpath(fp, configs_package_data_root).split(os.sep)
|
path_split = os.path.relpath(fp, configs_package_data_root).split(os.sep)
|
||||||
target_fp = USERS_CONFIG_DIR / "example_config" / Path(*path_split)
|
target_fp = PRIMAITE_PATHS.user_config_path / "example_config" / Path(*path_split)
|
||||||
target_fp.parent.mkdir(exist_ok=True, parents=True)
|
target_fp.parent.mkdir(exist_ok=True, parents=True)
|
||||||
copy_file = not target_fp.is_file()
|
copy_file = not target_fp.is_file()
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# Crown Owned Copyright (C) Dstl 2023. DEFCON 703. Shared in confidence.
|
|
||||||
from logging import Logger
|
|
||||||
|
|
||||||
from primaite import _USER_DIRS, getLogger, LOG_DIR, NOTEBOOKS_DIR
|
|
||||||
|
|
||||||
_LOGGER: Logger = getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def run() -> None:
|
|
||||||
"""
|
|
||||||
Handles creation of application directories and user directories.
|
|
||||||
|
|
||||||
Uses `platformdirs.PlatformDirs` and `pathlib.Path` to create the required
|
|
||||||
app directories in the correct locations based on the users OS.
|
|
||||||
"""
|
|
||||||
app_dirs = [
|
|
||||||
_USER_DIRS,
|
|
||||||
NOTEBOOKS_DIR,
|
|
||||||
LOG_DIR,
|
|
||||||
]
|
|
||||||
|
|
||||||
for app_dir in app_dirs:
|
|
||||||
if not app_dir.is_dir():
|
|
||||||
app_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
_LOGGER.info(f"Created directory: {app_dir}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run()
|
|
||||||
Reference in New Issue
Block a user