Skip to content

Commit 847549e

Browse files
committed
Fix state file loading initialization
1 parent 22a040c commit 847549e

File tree

4 files changed

+87
-57
lines changed

4 files changed

+87
-57
lines changed

investing_algorithm_framework/app/app.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -582,20 +582,19 @@ def run(self, number_of_iterations: int = None):
582582
None
583583
"""
584584
self.initialize_config()
585+
586+
# Load the state if a state handler is provided
587+
if self._state_handler is not None:
588+
logger.info("Detected state handler, loading state")
589+
self._state_handler.initialize()
590+
config = self.container.configuration_service().get_config()
591+
self._state_handler.load(config[RESOURCE_DIRECTORY])
592+
585593
self.initialize_storage()
594+
logger.info("App initialization complete")
586595
event_loop_service = None
587596

588597
try:
589-
590-
# Load the state if a state handler is provided
591-
if self._state_handler is not None:
592-
logger.info("Detected state handler, loading state")
593-
self._state_handler.initialize()
594-
config = self.container.configuration_service().get_config()
595-
self._state_handler.load(config[RESOURCE_DIRECTORY])
596-
597-
logger.info("App initialization complete")
598-
599598
# Run all on_initialize hooks
600599
for hook in self._on_initialize_hooks:
601600
hook.on_run(self.context)

investing_algorithm_framework/app/eventloop.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ def initialize(
315315
)
316316

317317
def cleanup(self):
318+
"""
319+
Cleans up the event loop service by saving all snapshots
320+
taken during the event loop run. The snapshots are saved at the
321+
end of the event to prevent too many database writes during the
322+
event loop execution. Saving snapshots in bulk at the end improves
323+
performance and reduces the number of database transactions.
324+
325+
Returns:
326+
None
327+
"""
318328
self._portfolio_snapshot_service.save_all(self._snapshots)
319329

320330
def start(

investing_algorithm_framework/domain/models/data/data_source.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@
1111
@dataclass(frozen=True)
1212
class DataSource:
1313
"""
14-
Base class for data sources.
14+
Data registration model. Defines the data source for a strategy. A simple
15+
data source can be defined as:
16+
DataSource(
17+
symbol="BTC/EUR",
18+
data_type="ohlcv",
19+
window_size=200,
20+
market="BITVAVO",
21+
identifier="BTC/EUR_ohlcv"
22+
)
1523
"""
1624
identifier: str = None
1725
data_provider_identifier: str = None

investing_algorithm_framework/infrastructure/services/aws/state_handler.py

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,37 @@
88
logger = logging.getLogger("investing_algorithm_framework")
99

1010

11+
def _fix_permissions(target_directory: str):
12+
"""
13+
Fix permissions on downloaded files to make them writable.
14+
15+
Args:
16+
target_directory (str): Directory to fix permissions for
17+
"""
18+
try:
19+
# Fix the target directory itself
20+
os.chmod(target_directory, 0o755)
21+
22+
# Recursively fix all subdirectories and files
23+
for root, dirs, files in os.walk(target_directory):
24+
# Fix current directory permissions
25+
os.chmod(root, 0o755)
26+
27+
# Fix all subdirectories
28+
for dir_name in dirs:
29+
dir_path = os.path.join(root, dir_name)
30+
os.chmod(dir_path, 0o755)
31+
32+
# Fix all files - make them readable and writable
33+
for file_name in files:
34+
file_path = os.path.join(root, file_name)
35+
os.chmod(file_path, 0o644)
36+
37+
logger.info(f"Update permissions for {target_directory}")
38+
except Exception as e:
39+
logger.warning(f"Error fixing permissions: {e}")
40+
41+
1142
class AWSS3StorageStateHandler(StateHandler):
1243
"""
1344
A state handler for AWS S3 storage.
@@ -28,40 +59,13 @@ def initialize(self):
2859

2960
if not self.bucket_name:
3061
raise OperationalException(
31-
"AWS S3 state handler requires a bucket name or the "
32-
"AWS_S3_BUCKET_NAME environment variable to be set."
62+
"AWS S3 state handler requires a bucket_name para or the "
63+
"AWS_S3_BUCKET_NAME environment variable needs to be set "
64+
"in the environment."
3365
)
3466

3567
self.s3_client = boto3.client("s3")
3668

37-
def _fix_permissions(self, target_directory: str):
38-
"""
39-
Fix permissions on downloaded files to make them writable.
40-
41-
Args:
42-
target_directory (str): Directory to fix permissions for
43-
"""
44-
try:
45-
# Fix directory permissions
46-
os.chmod(target_directory, 0o755)
47-
48-
# Recursively fix all files and subdirectories
49-
for root, dirs, files in os.walk(target_directory):
50-
# Fix subdirectories
51-
for dir_name in dirs:
52-
dir_path = os.path.join(root, dir_name)
53-
os.chmod(dir_path, 0o755)
54-
55-
# Fix files - make them readable and writable
56-
for file_name in files:
57-
file_path = os.path.join(root, file_name)
58-
# Set to 0o644 (rw-r--r--)
59-
os.chmod(file_path, 0o644)
60-
61-
logger.info(f"Permissions fixed for {target_directory}")
62-
except Exception as e:
63-
logger.warning(f"Error fixing permissions: {e}")
64-
6569
def save(self, source_directory: str):
6670
"""
6771
Save the state to AWS S3.
@@ -85,9 +89,11 @@ def save(self, source_directory: str):
8589
s3_key = os.path.relpath(file_path, source_directory) \
8690
.replace("\\", "/")
8791

88-
# Upload the file
8992
self.s3_client.upload_file(
90-
file_path, self.bucket_name, s3_key
93+
file_path,
94+
self.bucket_name,
95+
s3_key,
96+
ExtraArgs={'ACL': 'private'}
9197
)
9298

9399
except (NoCredentialsError, PartialCredentialsError) as ex:
@@ -102,37 +108,44 @@ def save(self, source_directory: str):
102108
def load(self, target_directory: str):
103109
"""
104110
Load the state from AWS S3.
105-
106-
Args:
107-
target_directory (str): Directory to load the state
108-
109-
Returns:
110-
None
111111
"""
112112
logger.info("Loading state from AWS S3 ...")
113113

114114
try:
115-
# Ensure the local directory exists
116115
if not os.path.exists(target_directory):
117-
os.makedirs(target_directory)
116+
os.makedirs(target_directory, mode=0o755)
117+
118+
os.chmod(target_directory, 0o755)
118119

119-
# List and download objects
120120
response = self.s3_client.list_objects_v2(Bucket=self.bucket_name)
121+
121122
if "Contents" in response:
122123
for obj in response["Contents"]:
123124
s3_key = obj["Key"]
124125
file_path = os.path.join(target_directory, s3_key)
125126

126-
# Create subdirectories locally if needed
127-
os.makedirs(os.path.dirname(file_path), exist_ok=True)
127+
os.makedirs(os.path.dirname(file_path), exist_ok=True,
128+
mode=0o755)
128129

129-
# Download object to file
130130
self.s3_client.download_file(
131131
self.bucket_name, s3_key, file_path
132132
)
133133

134-
# Fix permissions on all downloaded files
135-
self._fix_permissions(target_directory)
134+
if os.path.isfile(file_path):
135+
os.chmod(file_path, 0o644)
136+
else:
137+
os.chmod(file_path, 0o755)
138+
139+
# Final recursive fix
140+
_fix_permissions(target_directory)
141+
142+
# Add write permission to file (0o666 = rw-rw-rw-, then masked by umask)
143+
db_file = os.path.join(target_directory,
144+
'databases/prod-database.sqlite3')
145+
if os.path.exists(db_file):
146+
os.chmod(db_file, 0o666)
147+
logger.info(
148+
f"Database file permissions after fix: {oct(os.stat(db_file).st_mode)}")
136149

137150
except (NoCredentialsError, PartialCredentialsError) as ex:
138151
logger.error(f"Error loading state from AWS S3: {ex}")

0 commit comments

Comments
 (0)