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():
logger_obj = logging.getLogger('iottbLogger')
logger_obj.setLevel(logging.DEBUG)
logger_obj.setLevel(logging.INFO)
file_handler = RotatingFileHandler('iottb.log')
console_handler = logging.StreamHandler(sys.stdout)
file_handler.setLevel(logging.INFO)
console_handler.setLevel(logging.DEBUG)
file_handler.setLevel(logging.DEBUG)
console_handler.setLevel(logging.INFO)
file_fmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_fmt = logging.Formatter('%(name)s - %(name)s - %(levelname)s - %(message)s')
file_fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_fmt = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_fmt)
console_handler.setFormatter(console_fmt)

View File

@ -5,19 +5,19 @@ from pathlib import Path
from typing import Optional, Any
from uuid import UUID
from pydantic import BaseModel, Field
from iottb.definitions import ReturnCodes, CAPTURE_METADATA_FILE
from iottb.models.device_metadata_model import DeviceMetadata
from iottb.logger import logger
class CaptureMetadata:
class CaptureMetadata(BaseModel):
# Required Fields
device_metadata: DeviceMetadata
capture_id: uuid.UUID = lambda: str(uuid.uuid4())
device_metadata: DeviceMetadata = Field(exclude=True)
capture_id: uuid.UUID = Field(default_factory=lambda: str(uuid.uuid4()))
capture_dir: Path
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
start_time: str
@ -38,15 +38,14 @@ class CaptureMetadata:
firmware_version: Optional[str] = None
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
self.device_metadata = device_metadata
self.capture_dir = capture_dir
assert capture_dir.is_dir(), f"Capture directory {capture_dir} does not exist"
assert capture_dir.is_dir()
# Getters
def get_device_id(self) -> str:
return self.device_metadata.get_device_id()
return self.device_id
def get_start_time(self) -> str:
return self.start_time
@ -137,18 +136,14 @@ class CaptureMetadata:
# Other
def build_capture_file_name(self):
logger.info(f"Building capture file name")
prefix = ""
if self.app is None:
logger.debug(f"No app specified")
prefix = self.device_metadata.get_device_short_name()
else:
logger.debug(f"App specified: {self.app}")
assert str(self.app).strip() not in {"", " "}, f"app is not a valid name: {self.app}"
prefix = self.get_app()
# assert self.capture_dir is not None, f"{self.capture_dir} does not exist"
filename = f"{prefix}_{str(self.capture_id)}.pcap"
logger.debug(f"Capture file name: {filename}")
self.set_capture_file(filename)
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:
json.dump(metadata, file)
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
from iottb.definitions import ReturnCodes, DEVICE_METADATA_FILE
from iottb.logger import logger
# 3rd party libs
from pydantic import BaseModel, Field
IMMUTABLE_FIELDS = {"device_name", "device_short_name", "device_id", "date_created"}
class DeviceMetadata:
class DeviceMetadata(BaseModel):
# Required fields
device_name: str
device_short_name: str
device_id: str = lambda: str(uuid.uuid4())
date_created: str = lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower()
device_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
date_created: str = Field(default_factory=lambda: datetime.now().strftime('%d-%m-%YT%H:%M:%S').lower())
device_root_path: Path
# Optional Fields
aliases: Optional[List[str]] = None
aliases: List[str] = Field(default_factory=lambda: [])
device_type: Optional[str] = None
device_serial_number: Optional[str] = None
device_firmware_version: Optional[str] = None
@ -29,16 +29,13 @@ class DeviceMetadata:
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_short_name = device_name.lower().replace(" ", "_")
# assert dir_contains_device_metadata(device_root_dir), \
# f"Directory {device_root_dir} is missing a {DEVICE_METADATA_FILE} file"
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:
return self.device_id
@ -86,18 +83,15 @@ class DeviceMetadata:
@classmethod
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.name == DEVICE_METADATA_FILE, f"{device_file_path} is not a {DEVICE_METADATA_FILE}"
device_meta_filename = device_file_path
with device_meta_filename.open('r') as file:
metadata_json = json.load(file)
metadata_model_obj = cls.model_validate_json(metadata_json)
return metadata_model_obj
def save_to_json(self, file_path: Path):
logger.info(f"Saving DeviceMetadata to JSON file: {file_path}")
if file_path.is_file():
print(f"File {file_path} already exists, update instead.")
return ReturnCodes.FILE_ALREADY_EXISTS
@ -106,12 +100,19 @@ class DeviceMetadata:
json.dump(metadata, file)
return ReturnCodes.SUCCESS
@classmethod
def model_validate_json(cls, metadata_json):
pass
def model_dump_json(self, indent):
def update_metadata_in_json(cls, file_path: Path, **kwargs):
# TODO Maybe not needed at all.
assert file_path.is_file()
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

View File

@ -2,7 +2,6 @@ import pathlib
from iottb import definitions
from iottb.definitions import DEVICE_METADATA_FILE, ReturnCodes
from iottb.logger import logger
from iottb.models.device_metadata_model import DeviceMetadata
from iottb.utils.device_metadata_utils import *
@ -17,13 +16,10 @@ def setup_init_device_root_parser(subparsers):
def handle_add(args):
logger.info(f"Add device handler called with args {args}")
if args.guided:
logger.debug("Guided setup")
metadata = guided_setup(args.root_dir)
else:
logger.debug("Setup through passed args: setup")
device_name = args.name
args.root_dir.mkdir(parents=True, exist_ok=True)
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(""):
configure_metadata()
assert False, "TODO implement dynamic setup"
if metadata.save_to_json(file_path) == ReturnCodes.FILE_ALREADY_EXISTS:
print("Directory already contains a device metadata file. Aborting operation.")
return ReturnCodes.ABORTED
@ -46,17 +41,12 @@ def configure_metadata():
def guided_setup(device_root) -> DeviceMetadata:
logger.info("Guided setup")
response = "N"
device_name = ""
while response.upper() == "N":
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:
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)