Merge branch 'refs/heads/cli-dev'

# Conflicts:
#	.idea/workspace.xml
#	code/iottb/logger.py
#	code/iottb/models/capture_metadata_model.py
#	code/iottb/models/device_metadata_model.py
#	code/iottb/subcommands/add_device.py
This commit is contained in:
Sebastian Lenzlinger 2024-05-08 03:07:16 +02:00
commit 901133c84c
5 changed files with 339 additions and 48 deletions

308
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,308 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="7a3ac8e1-7fbf-4aa7-9cf9-a51d7ade8503" name="Changes" comment="UNTESTED REFACTORING:&#10;Move more functionality into Metadata Model classes to ensure data is available and better passable between functions.">
<change afterPath="$PROJECT_DIR$/code/iottb/logger.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/iottb/models/capture_metadata_model.py" beforeDir="false" afterPath="$PROJECT_DIR$/code/iottb/models/capture_metadata_model.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/iottb/models/device_metadata_model.py" beforeDir="false" afterPath="$PROJECT_DIR$/code/iottb/models/device_metadata_model.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/iottb/subcommands/add_device.py" beforeDir="false" afterPath="$PROJECT_DIR$/code/iottb/subcommands/add_device.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/code/iottb/subcommands/capture.py" beforeDir="false" afterPath="$PROJECT_DIR$/code/iottb/subcommands/capture.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="PUSH_AUTO_UPDATE" value="true" />
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="sync" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="CurrentFile" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3
}</component>
<component name="ProjectId" id="2fYAAba0AnH9jx9D0JkB8Xbuv0r" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_ADD_EXTERNAL_FILES": "true",
"ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true",
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
"Python.__init__.executor": "Run",
"Python.__main__.executor": "Debug",
"Python.iotdb.executor": "Debug",
"Python.main.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"git-widget-placeholder": "cli-dev",
"last_opened_file_path": "/home/slnopriv/projects/2024-bsc-sebastian-lenzlinger/code/iottb/logger.py",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/archive" />
<recent name="$PROJECT_DIR$" />
<recent name="$PROJECT_DIR$/code/misc/archive" />
<recent name="$PROJECT_DIR$/code/misc" />
<recent name="$PROJECT_DIR$/code/kydcap/utils" />
</key>
</component>
<component name="RunManager" selected="Python.__main__">
<configuration name="__init__" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="2024-bsc-sebastian-lenzlinger" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/code/kydcap" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/code/kydcap/__init__.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="__main__" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="2024-bsc-sebastian-lenzlinger" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/code/iottb" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/code/iottb/__main__.py" />
<option name="PARAMETERS" value="add --root /tmp/test --guided" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<list>
<item itemvalue="Python.__main__" />
<item itemvalue="Python.__init__" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.__main__" />
<item itemvalue="Python.__init__" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-1d06a55b98c1-74d2a5396914-JavaScript-PY-241.14494.241" />
<option value="bundled-python-sdk-0509580d9d50-28c9f5db9ffe-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.14494.241" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="7a3ac8e1-7fbf-4aa7-9cf9-a51d7ade8503" name="Changes" comment="" />
<created>1713967494544</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1713967494544</updated>
<workItem from="1713967495566" duration="6927000" />
<workItem from="1714554228183" duration="34000" />
<workItem from="1714554269789" duration="56478000" />
<workItem from="1714616237168" duration="6135000" />
<workItem from="1714850899817" duration="2659000" />
<workItem from="1714917763516" duration="9796000" />
<workItem from="1715078660090" duration="25014000" />
</task>
<task id="LOCAL-00001" summary="Add code for capture testbed. This is a huge commit. End of day sync...">
<option name="closed" value="true" />
<created>1714615532115</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1714615532115</updated>
</task>
<task id="LOCAL-00002" summary="Add some notes.">
<option name="closed" value="true" />
<created>1714615608142</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1714615608142</updated>
</task>
<task id="LOCAL-00003" summary="Update gitignore">
<option name="closed" value="true" />
<created>1714616343905</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1714616343905</updated>
</task>
<task id="LOCAL-00004" summary="Add test module.">
<option name="closed" value="true" />
<created>1714617162903</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1714617162903</updated>
</task>
<task id="LOCAL-00005" summary="Update gitignore again.">
<option name="closed" value="true" />
<created>1714617231842</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1714617231842</updated>
</task>
<task id="LOCAL-00006" summary="Start tracking development config files.">
<option name="closed" value="true" />
<created>1714617266799</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1714617266799</updated>
</task>
<task id="LOCAL-00007" summary="SYNC">
<option name="closed" value="true" />
<created>1714823516954</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1714823516954</updated>
</task>
<task id="LOCAL-00008" summary="Refactor various names.">
<option name="closed" value="true" />
<created>1714919098392</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1714919098392</updated>
</task>
<task id="LOCAL-00009" summary="Refactor subcommands, config etc.">
<option name="closed" value="true" />
<created>1714924463148</created>
<option name="number" value="00009" />
<option name="presentableId" value="LOCAL-00009" />
<option name="project" value="LOCAL" />
<updated>1714924463148</updated>
</task>
<task id="LOCAL-00010" summary="UNTESTED REFACTORING:&#10;Move more functionality into Metadata Model classes to ensure data is available and better passable between functions.">
<option name="closed" value="true" />
<created>1715101138312</created>
<option name="number" value="00010" />
<option name="presentableId" value="LOCAL-00010" />
<option name="project" value="LOCAL" />
<updated>1715101138312</updated>
</task>
<option name="localTasksCounter" value="11" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="RECENT_FILTERS">
<map>
<entry key="Branch">
<value>
<list>
<RecentGroup>
<option name="FILTER_VALUES">
<option value="HEAD" />
</option>
</RecentGroup>
<RecentGroup>
<option name="FILTER_VALUES">
<option value="devel" />
</option>
</RecentGroup>
</list>
</value>
</entry>
</map>
</option>
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="HEAD" />
</list>
</value>
</entry>
</map>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Add code for capture testbed. This is a huge commit. End of day sync..." />
<MESSAGE value="Add some notes." />
<MESSAGE value="Update gitignore" />
<MESSAGE value="Add test module." />
<MESSAGE value="Update gitignore again." />
<MESSAGE value="Start tracking development config files." />
<MESSAGE value="SYNC" />
<MESSAGE value="Refactor various names." />
<MESSAGE value="Refactor subcommands, config etc." />
<MESSAGE value="UNTESTED REFACTORING:&#10;Move more functionality into Metadata Model classes to ensure data is available and better passable between functions." />
<option name="LAST_COMMIT_MESSAGE" value="UNTESTED REFACTORING:&#10;Move more functionality into Metadata Model classes to ensure data is available and better passable between functions." />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/2024_bsc_sebastian_lenzlinger$__main__.coverage" NAME="__main__ Coverage Results" MODIFIED="1715103831289" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/code/iottb" />
<SUITE FILE_PATH="coverage/2024_bsc_sebastian_lenzlinger$iotdb.coverage" NAME="iotdb Coverage Results" MODIFIED="1715103593519" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/code/iottb" />
<SUITE FILE_PATH="coverage/2024_bsc_sebastian_lenzlinger$__init__.coverage" NAME="__init__ Coverage Results" MODIFIED="1714619300966" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/code/kydcap" />
<SUITE FILE_PATH="coverage/2024_bsc_sebastian_lenzlinger$main.coverage" NAME="__main__ Coverage Results" MODIFIED="1714619560177" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/code/kydcap" />
</component>
</project>

View File

@ -5,16 +5,16 @@ from logging.handlers import RotatingFileHandler
def setup_logging(): def setup_logging():
logger_obj = logging.getLogger('iottbLogger') logger_obj = logging.getLogger('iottbLogger')
logger_obj.setLevel(logging.DEBUG) logger_obj.setLevel(logging.INFO)
file_handler = RotatingFileHandler('iottb.log') file_handler = RotatingFileHandler('iottb.log')
console_handler = logging.StreamHandler(sys.stdout) console_handler = logging.StreamHandler(sys.stdout)
file_handler.setLevel(logging.INFO) file_handler.setLevel(logging.DEBUG)
console_handler.setLevel(logging.DEBUG) console_handler.setLevel(logging.INFO)
file_fmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') file_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_fmt = logging.Formatter('%(name)s - %(name)s - %(levelname)s - %(message)s') console_fmt = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_fmt) file_handler.setFormatter(file_fmt)
console_handler.setFormatter(console_fmt) console_handler.setFormatter(console_fmt)

View File

@ -5,19 +5,19 @@ from pathlib import Path
from typing import Optional, Any from typing import Optional, Any
from uuid import UUID from uuid import UUID
from pydantic import BaseModel, Field
from iottb.definitions import ReturnCodes, CAPTURE_METADATA_FILE from iottb.definitions import ReturnCodes, CAPTURE_METADATA_FILE
from iottb.models.device_metadata_model import DeviceMetadata from iottb.models.device_metadata_model import DeviceMetadata
from iottb.logger import logger
class CaptureMetadata: class CaptureMetadata(BaseModel):
# Required Fields # Required Fields
device_metadata: DeviceMetadata device_metadata: DeviceMetadata = Field(exclude=True)
capture_id: uuid.UUID = lambda: str(uuid.uuid4()) capture_id: uuid.UUID = Field(default_factory=lambda: str(uuid.uuid4()))
capture_dir: Path capture_dir: Path
capture_file: str capture_file: str
capture_date: str = lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower() capture_date: str = Field(default_factory=lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower())
# Statistics # Statistics
start_time: str start_time: str
@ -38,15 +38,14 @@ class CaptureMetadata:
firmware_version: Optional[str] = None firmware_version: Optional[str] = None
def __init__(self, device_metadata: DeviceMetadata, capture_dir: Path, /, **data: Any): def __init__(self, device_metadata: DeviceMetadata, capture_dir: Path, /, **data: Any):
logger.info(f"Creating CaptureMetadata model from DeviceMetadata: {device_metadata}")
super().__init__(**data) # Pycharms orders super().__init__(**data) # Pycharms orders
self.device_metadata = device_metadata self.device_metadata = device_metadata
self.capture_dir = capture_dir self.capture_dir = capture_dir
assert capture_dir.is_dir(), f"Capture directory {capture_dir} does not exist" assert capture_dir.is_dir()
# Getters # Getters
def get_device_id(self) -> str: def get_device_id(self) -> str:
return self.device_metadata.get_device_id() return self.device_id
def get_start_time(self) -> str: def get_start_time(self) -> str:
return self.start_time return self.start_time
@ -137,18 +136,14 @@ class CaptureMetadata:
# Other # Other
def build_capture_file_name(self): def build_capture_file_name(self):
logger.info(f"Building capture file name")
prefix = "" prefix = ""
if self.app is None: if self.app is None:
logger.debug(f"No app specified")
prefix = self.device_metadata.get_device_short_name() prefix = self.device_metadata.get_device_short_name()
else: else:
logger.debug(f"App specified: {self.app}")
assert str(self.app).strip() not in {"", " "}, f"app is not a valid name: {self.app}" assert str(self.app).strip() not in {"", " "}, f"app is not a valid name: {self.app}"
prefix = self.get_app() prefix = self.get_app()
# assert self.capture_dir is not None, f"{self.capture_dir} does not exist" # assert self.capture_dir is not None, f"{self.capture_dir} does not exist"
filename = f"{prefix}_{str(self.capture_id)}.pcap" filename = f"{prefix}_{str(self.capture_id)}.pcap"
logger.debug(f"Capture file name: {filename}")
self.set_capture_file(filename) self.set_capture_file(filename)
def save_capture_metadata_to_json(self, file_path: Path = Path(CAPTURE_METADATA_FILE)): def save_capture_metadata_to_json(self, file_path: Path = Path(CAPTURE_METADATA_FILE)):
@ -160,6 +155,3 @@ class CaptureMetadata:
with file_path.open('w') as file: with file_path.open('w') as file:
json.dump(metadata, file) json.dump(metadata, file)
return ReturnCodes.SUCCESS return ReturnCodes.SUCCESS
def model_dump_json(self, indent, exclude_unset, exclude_none):
pass

View File

@ -6,22 +6,22 @@ from typing import Optional, List, Any
# iottb modules # iottb modules
from iottb.definitions import ReturnCodes, DEVICE_METADATA_FILE from iottb.definitions import ReturnCodes, DEVICE_METADATA_FILE
from iottb.logger import logger
# 3rd party libs # 3rd party libs
from pydantic import BaseModel, Field
IMMUTABLE_FIELDS = {"device_name", "device_short_name", "device_id", "date_created"} IMMUTABLE_FIELDS = {"device_name", "device_short_name", "device_id", "date_created"}
class DeviceMetadata: class DeviceMetadata(BaseModel):
# Required fields # Required fields
device_name: str device_name: str
device_short_name: str device_short_name: str
device_id: str = lambda: str(uuid.uuid4()) device_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
date_created: str = lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower() date_created: str = Field(default_factory=lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower())
device_root_path: Path device_root_path: Path
# Optional Fields # Optional Fields
aliases: Optional[List[str]] = None aliases: List[str] = Field(default_factory=lambda: [])
device_type: Optional[str] = None device_type: Optional[str] = None
device_serial_number: Optional[str] = None device_serial_number: Optional[str] = None
device_firmware_version: Optional[str] = None device_firmware_version: Optional[str] = None
@ -29,16 +29,13 @@ class DeviceMetadata:
capture_files: Optional[List[str]] = [] capture_files: Optional[List[str]] = []
def __init__(self, device_name: str, device_root_dir: Path): def __init__(self, device_name: str, device_root_dir: Path, /, **data: Any):
super().__init__(**data)
self.device_name = device_name self.device_name = device_name
self.device_short_name = device_name.lower().replace(" ", "_") self.device_short_name = device_name.lower().replace(" ", "_")
# assert dir_contains_device_metadata(device_root_dir), \ # assert dir_contains_device_metadata(device_root_dir), \
# f"Directory {device_root_dir} is missing a {DEVICE_METADATA_FILE} file" # f"Directory {device_root_dir} is missing a {DEVICE_METADATA_FILE} file"
self.device_root_dir = device_root_dir self.device_root_dir = device_root_dir
logger.debug(f"Device name: {device_name}")
logger.debug(f"Device short_name: {self.device_short_name}")
logger.debug(f"Device root dir: {device_root_dir}")
logger.info(f"Initialized DeviceMetadata model: {device_name}")
def get_device_id(self) -> str: def get_device_id(self) -> str:
return self.device_id return self.device_id
@ -86,18 +83,15 @@ class DeviceMetadata:
@classmethod @classmethod
def load_from_json(cls, device_file_path: Path): def load_from_json(cls, device_file_path: Path):
logger.info(f"Loading DeviceMetadata from JSON file: {device_file_path}")
assert device_file_path.is_file(), f"{device_file_path} is not a file" assert device_file_path.is_file(), f"{device_file_path} is not a file"
assert device_file_path.name == DEVICE_METADATA_FILE, f"{device_file_path} is not a {DEVICE_METADATA_FILE}" assert device_file_path.name == DEVICE_METADATA_FILE, f"{device_file_path} is not a {DEVICE_METADATA_FILE}"
device_meta_filename = device_file_path device_meta_filename = device_file_path
with device_meta_filename.open('r') as file: with device_meta_filename.open('r') as file:
metadata_json = json.load(file) metadata_json = json.load(file)
metadata_model_obj = cls.model_validate_json(metadata_json) metadata_model_obj = cls.model_validate_json(metadata_json)
return metadata_model_obj return metadata_model_obj
def save_to_json(self, file_path: Path): def save_to_json(self, file_path: Path):
logger.info(f"Saving DeviceMetadata to JSON file: {file_path}")
if file_path.is_file(): if file_path.is_file():
print(f"File {file_path} already exists, update instead.") print(f"File {file_path} already exists, update instead.")
return ReturnCodes.FILE_ALREADY_EXISTS return ReturnCodes.FILE_ALREADY_EXISTS
@ -106,12 +100,19 @@ class DeviceMetadata:
json.dump(metadata, file) json.dump(metadata, file)
return ReturnCodes.SUCCESS return ReturnCodes.SUCCESS
@classmethod @classmethod
def model_validate_json(cls, metadata_json): def update_metadata_in_json(cls, file_path: Path, **kwargs):
pass # TODO Maybe not needed at all.
assert file_path.is_file()
def model_dump_json(self, indent): for field in IMMUTABLE_FIELDS:
if field in kwargs:
print(f"Field {field} is immutable")
return ReturnCodes.IMMUTABLE
metadata = cls.load_from_json(file_path)
for field, value in kwargs.items():
if field in metadata.model_fields_set:
setattr(metadata, field, value)
metadata.date_updated = datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower()
pass pass

View File

@ -2,7 +2,6 @@ import pathlib
from iottb import definitions from iottb import definitions
from iottb.definitions import DEVICE_METADATA_FILE, ReturnCodes from iottb.definitions import DEVICE_METADATA_FILE, ReturnCodes
from iottb.logger import logger
from iottb.models.device_metadata_model import DeviceMetadata from iottb.models.device_metadata_model import DeviceMetadata
from iottb.utils.device_metadata_utils import * from iottb.utils.device_metadata_utils import *
@ -17,13 +16,10 @@ def setup_init_device_root_parser(subparsers):
def handle_add(args): def handle_add(args):
logger.info(f"Add device handler called with args {args}")
if args.guided: if args.guided:
logger.debug("Guided setup")
metadata = guided_setup(args.root_dir) metadata = guided_setup(args.root_dir)
else: else:
logger.debug("Setup through passed args: setup")
device_name = args.name device_name = args.name
args.root_dir.mkdir(parents=True, exist_ok=True) args.root_dir.mkdir(parents=True, exist_ok=True)
metadata = DeviceMetadata(device_name, args.root_dir) metadata = DeviceMetadata(device_name, args.root_dir)
@ -33,7 +29,6 @@ def handle_add(args):
if response.lower() not in definitions.AFFIRMATIVE_USER_RESPONSE.add(""): if response.lower() not in definitions.AFFIRMATIVE_USER_RESPONSE.add(""):
configure_metadata() configure_metadata()
assert False, "TODO implement dynamic setup" assert False, "TODO implement dynamic setup"
if metadata.save_to_json(file_path) == ReturnCodes.FILE_ALREADY_EXISTS: if metadata.save_to_json(file_path) == ReturnCodes.FILE_ALREADY_EXISTS:
print("Directory already contains a device metadata file. Aborting operation.") print("Directory already contains a device metadata file. Aborting operation.")
return ReturnCodes.ABORTED return ReturnCodes.ABORTED
@ -46,17 +41,12 @@ def configure_metadata():
def guided_setup(device_root) -> DeviceMetadata: def guided_setup(device_root) -> DeviceMetadata:
logger.info("Guided setup")
response = "N" response = "N"
device_name = "" device_name = ""
while response.upper() == "N": while response.upper() == "N":
device_name = input("Please enter name of device: ") device_name = input("Please enter name of device: ")
response = input(f"Confirm device name: {device_name} [y/N] ")
if device_name == "" or device_name is None: if device_name == "" or device_name is None:
print("Name cannot be empty") print("Name cannot be empty")
logger.warning("Name cannot be empty")
logger.debug(f"Response is {response}")
logger.debug(f"Device name is {device_name}")
return DeviceMetadata(device_name, device_root) return DeviceMetadata(device_name, device_root)