Skip to content

first proposal of the project structure #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
145 changes: 145 additions & 0 deletions src/endureio/datacomponents/base_data_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from abc import ABC, abstractmethod


# --- Component ---
class DataComponent(ABC):
"""
DataComponent is an abstract base class that serves as the
foundation for all metadata components.
It enforces the implementation of a `get_data` method in
derived classes and provides default
implementations for `add` and `remove` methods,
which raise `NotImplementedError` to indicate
that the component does not support child components.

Methods:
get_data() -> dict:
Abstract method that must be implemented by subclasses to
return the data associated with the component as a dictionary.

add(component: "DataComponent"):
Raises NotImplementedError. Intended to be overridden
by subclasses that support adding child components.

remove(component: "DataComponent"):
Raises NotImplementedError. Intended to be overridden by subclasses that
support removing child components.
"""

@abstractmethod
def get_data(self) -> dict:
pass

def add(self, component: "DataComponent"):
raise NotImplementedError("This component doesn't support children.")

def remove(self, component: "DataComponent"):
raise NotImplementedError("This component doesn't support children.")


# --- Composite ---
class CompositeData(DataComponent):
"""
CompositeData is a class that represents a composite metadata component,
which can hold and manage other DataComponent instances as its children.

Attributes:
name (str): The name of the composite data component.
Defaults to an empty string. children (list[DataComponent]):
A list of child DataComponent instances contained within this composite.

Methods:
add(component: DataComponent):
Adds a child DataComponent to the composite.

remove(component: DataComponent):
Removes a child DataComponent from the composite.

get_data() -> dict:
Aggregates and returns the data from all child components as a dictionary.
If the composite has a name,
the result is nested under the composite's name.
"""

def __init__(self, name: str = ""):
self.name = name
self.children: list[DataComponent] = []

def add(self, component: DataComponent):
"""
Adds a DataComponent to the list of children.

Args:
component (DataComponent):
The data component to be added to the children list.
"""
self.children.append(component)

def remove(self, component: DataComponent):
"""
Removes a specified DataComponent from the list of children.

Args:
component (DataComponent):
The data component to be removed from the children list.

Raises:
ValueError: If the specified component is not found in the children list.
"""
self.children.remove(component)

def get_data(self) -> dict:
"""
Retrieves the data from the current object and
its children in a hierarchical structure.

Returns:
dict: A dictionary containing the data from
the current object and its children.
If the current object has a name,
the data is nested under the name as the key.
Otherwise, the data is returned as a flat dictionary.
"""
result = {}
for child in self.children:
result.update(child.get_data())
return {self.name: result} if self.name else result


# --- Example Usage for a FIT file ---
if __name__ == "__main__":
# Example Data Component
class ExampleData(DataComponent):
"""
Represents a leaf metadata component (key-value pair or similar).
The leaf methods should be the low level fields of the different
files.
"""

def __init__(self, key: str, value: str):
self.key = key
self.value = value

def get_metadata(self) -> dict:
return {self.key: self.value}

# Example usage
# Build metadata structure
fit_metadata = CompositeData("fit_file")

# Header Section
header = CompositeData("header")
header.add(ExampleData("file_type", "FIT"))
header.add(ExampleData("version", "2.0"))

# Body Section
body = CompositeData("body")
# add the training data
body.add(ExampleData("duration", "45 min"))
body.add(ExampleData("distance", "10 km"))

# Add sections to root
fit_metadata.add(header)
fit_metadata.add(body)

print(fit_metadata.get_metadata())
Empty file.
Empty file.
116 changes: 116 additions & 0 deletions src/endureio/filehandlers/base_file_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from os import PathLike
from typing import IO, Self
from abc import ABC, abstractmethod

import pandas as pd

from endureio.datacomponents.base_data_components import CompositeData


class BaseFileHandler(ABC):
"""
BaseFileHandler is an abstract base class that
provides a blueprint for file handling operations.
It defines the structure and methods that subclasses
must implement to handle specific file formats
or data sources. The class includes methods for reading,
writing, validating, and converting data.

Attributes:
file_path_or_buffer (str | bytes | PathLike | IO[bytes]):
The file path or buffer to be handled.
data (CompositeData): A composite data structure
that stores metadata components extracted
from the file or buffer.

Methods:
read() -> Self:
Reads the file or buffer and processes its contents.
This method must be implemented
by subclasses to populate the `data` attribute
with metadata components.

write() -> Self:
Writes data to a file. Subclasses must implement
this method to handle the specific
logic for writing data to a file.

validate() -> Self:
Validates the file or buffer. Subclasses must
implement this method to ensure the
file or buffer meets the required criteria.

to_df() -> pd.DataFrame:
Converts the `data` attribute to a pandas DataFrame.

to_dict() -> dict:
Converts the `data` attribute to a dictionary.
"""

def __init__(self, file_path_or_buffer: str | bytes | PathLike | IO[bytes]):
self.file_path_or_buffer = file_path_or_buffer
self.data: CompositeData
pass

@abstractmethod
def read(self) -> Self:
""" "
Reads the file or buffer and processes its contents.

This method creates a `Data` component for each section of the file
(e.g., header, etc.) and adds them to the `data` attribute. The `data`
attribute is a `CompositeData` object that contains all the metadata
components.

Returns:
Self: The base file handler object with the `data` attribute populated.
"""
pass

@abstractmethod
def write(self) -> Self:
"""
Writes data to a file.

This method should be implemented by subclasses to handle the
specific logic for writing data to a file. The implementation
should ensure that the file is written correctly and any necessary
resources are properly managed.

Returns:
Self: The instance of the class, to allow for method chaining.
"""
pass

@abstractmethod
def validate(self) -> Self:
"""
Validates the file or buffer.

This method is intended to ensure that the file or buffer meets
the required criteria or format. Override this method in a subclass
to implement specific validation logic.

Returns:
Self: The instance of the class, allowing for method chaining.
"""
pass

def to_df(self) -> pd.DataFrame:
""" "
Converts the `data` attribute of the object into a pandas DataFrame.

Returns:
pd.DataFrame: A DataFrame representation of the `data` attribute.
"""

return pd.DataFrame(self.data.get_data())

def to_dict(self) -> dict:
"""
convert the `data` attribute of the object into a python dict

Returns:
dict: A Dictonary representation of the `data` attribute.
"""
return self.data.get_data()
Empty file.
7 changes: 7 additions & 0 deletions src/endureio/filehandlers/fit/fitfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from endureio.filehandlers.base_file_handlers import BaseFileHandler


class FitFile(BaseFileHandler):
def __init__(self):

pass
8 changes: 0 additions & 8 deletions src/endureio/fit.py

This file was deleted.

58 changes: 57 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.