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:
Christopher McCarthy
2023-07-21 13:50:56 +00:00
19 changed files with 192 additions and 185 deletions

View File

@@ -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:

View 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

View 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

View File

@@ -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.

View File

@@ -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**

View File

@@ -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"

View File

@@ -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"]

View File

@@ -1 +1 @@
2.0.0rc1 2.0.0rc2

View File

@@ -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()

View File

@@ -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

View File

@@ -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:

View File

@@ -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]:

View File

@@ -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:

View File

@@ -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"]

View File

@@ -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."""

View File

@@ -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:

View File

@@ -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()

View 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()

View 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()