55from time import sleep , time
66from typing import Any , Dict , Iterator , List , Literal , Optional , Tuple , Union
77
8+ from biocframe import BiocFrame
89from sqlalchemy import create_engine , func , text
910from sqlalchemy .orm import Session , sessionmaker
1011from sqlalchemy .pool import QueuePool
1415from .models import Base , Metadata , Resource
1516from .utils import (
1617 calculate_file_hash ,
18+ convert_to_columnar ,
1719 copy_or_move ,
1820 create_tmp_dir ,
1921 download_web_file ,
@@ -104,15 +106,19 @@ def _setup_database(self) -> None:
104106
105107 return SCHEMA_VERSION
106108
107- def _get_detached_resource (
108- self , session : Session , obj : Union [Resource , Metadata ]
109- ) -> Optional [Union [Resource , Metadata ]]:
109+ def _get_detached_resource (self , session : Session , obj : Union [Resource , Metadata ]) -> Optional [dict ]:
110110 """Get a detached copy of a resource."""
111111 if obj is None :
112112 return None
113113 session .refresh (obj )
114114 session .expunge (obj )
115- return obj
115+ obj_dict = obj .to_dict ()
116+
117+ if isinstance (obj , Resource ):
118+ if obj_dict ["rtype" ] == "relative" :
119+ obj_dict ["rpath" ] = f'{ self .config .cache_dir } /{ obj_dict ["rpath" ]} '
120+
121+ return obj_dict
116122
117123 def __enter__ (self ) -> "BiocFileCache" :
118124 return self
@@ -137,6 +143,10 @@ def get_session(self) -> Iterator[Session]:
137143 finally :
138144 session .close ()
139145
146+ #########################
147+ ######>> cleanup <<######
148+ #########################
149+
140150 # def _validate_rname(self, rname: str) -> None:
141151 # """Validate resource name format."""
142152 # if not validate_rname(rname, self.config.rname_pattern):
@@ -191,7 +201,11 @@ def cleanup(self) -> int:
191201 self ._last_cleanup = datetime .now ()
192202 return removed
193203
194- def get (self , rname : str = None , rid : str = None ) -> Optional [Resource ]:
204+ ###############################
205+ ######>> get resources <<######
206+ ###############################
207+
208+ def get (self , rname : str = None , rid : str = None ) -> Optional [dict ]:
195209 """Get resource by name from cache.
196210
197211 Args:
@@ -215,7 +229,7 @@ def get(self, rname: str = None, rid: str = None) -> Optional[Resource]:
215229 # Check if path exists with timeout
216230 start = time ()
217231 timeout = 30
218- while not Path (str (resource .rpath )).exists ():
232+ while not Path (str (self . config . cache_dir / resource .rpath )).exists ():
219233 if time () - start >= timeout :
220234 raise TimeoutError (f"For resource: '{ rname } ' the rpath does not exist after { timeout } seconds." )
221235 sleep (0.1 )
@@ -236,7 +250,7 @@ def add(
236250 expires : Optional [datetime ] = None ,
237251 download : bool = True ,
238252 ext : bool = True ,
239- ) -> Resource :
253+ ) -> dict :
240254 """Add a resource to the cache.
241255
242256 Args:
@@ -268,7 +282,7 @@ def add(
268282 Defaults to `True`.
269283
270284 Returns:
271- The `Resource` object added to the cache.
285+ The `Resource` object added to the cache as dictionary .
272286 """
273287 # self._validate_rname(rname)
274288 fpath = Path (fpath ) if rtype != "web" else fpath
@@ -289,7 +303,7 @@ def add(
289303 # Generate paths and check size
290304 rid = generate_id (size = len (self ))
291305 uuid = generate_uuid ()
292- rpath = self . config . cache_dir / f"{ uuid } _{ outpath .name if ext else outpath .stem } " if action != "asis" else fpath
306+ rpath = f"{ uuid } _{ outpath .name if ext else outpath .stem } " if action != "asis" else fpath
293307
294308 # Create resource record
295309 resource = Resource (
@@ -307,10 +321,10 @@ def add(
307321 session .commit ()
308322
309323 try :
310- copy_or_move (outpath , rpath , rname , action , False )
324+ copy_or_move (outpath , self . config . cache_dir / rpath , rname , action , False )
311325
312326 # Calculate and store checksum
313- resource .etag = calculate_file_hash (rpath , self .config .hash_algorithm )
327+ resource .etag = calculate_file_hash (self . config . cache_dir / rpath , self .config .hash_algorithm )
314328 session .commit ()
315329 result = self ._get_detached_resource (session , resource )
316330 return result
@@ -320,7 +334,7 @@ def add(
320334 session .commit ()
321335 raise Exception ("Failed to add resource" ) from e
322336
323- def add_batch (self , resources : List [Dict [str , Any ]]) -> List [ Resource ] :
337+ def add_batch (self , resources : List [Dict [str , Any ]]) -> BiocFrame :
324338 """Add multiple resources in a single transaction.
325339
326340 Args:
@@ -344,7 +358,7 @@ def update(
344358 rname : str ,
345359 fpath : Union [str , Path ],
346360 action : Literal ["copy" , "move" , "asis" ] = "copy" ,
347- ) -> Resource :
361+ ) -> dict :
348362 """Update an existing resource.
349363
350364 Args:
@@ -359,7 +373,7 @@ def update(
359373 Defaults to ``copy``.
360374
361375 Returns:
362- Updated `Resource` object.
376+ Updated `Resource` object as dictionary .
363377
364378 """
365379 fpath = Path (fpath )
@@ -416,7 +430,7 @@ def remove(self, rname: str) -> None:
416430 session .rollback ()
417431 raise Exception (f"Failed to remove resource '{ rname } '" ) from e
418432
419- def list_resources (self , rtype : Optional [str ] = None , expired : Optional [bool ] = None ) -> List [ Resource ] :
433+ def list_resources (self , rtype : Optional [str ] = None , expired : Optional [bool ] = None ) -> BiocFrame :
420434 """List resources in the cache with optional filtering.
421435
422436 Args:
@@ -432,7 +446,7 @@ def list_resources(self, rtype: Optional[str] = None, expired: Optional[bool] =
432446 Note: Resources with no expiration are always considered non-expired.
433447
434448 Returns:
435- List of Resource objects matching the filters
449+ List of Resource objects matching the filters.
436450 """
437451 with self .get_session () as session :
438452 query = session .query (Resource )
@@ -452,7 +466,7 @@ def list_resources(self, rtype: Optional[str] = None, expired: Optional[bool] =
452466 )
453467
454468 resources = query .all ()
455- return [self ._get_detached_resource (session , r ) for r in resources ]
469+ return BiocFrame ( convert_to_columnar ( [self ._get_detached_resource (session , r ) for r in resources ]))
456470
457471 def validate_resource (self , resource : Resource ) -> bool :
458472 """Validate resource integrity.
@@ -521,7 +535,7 @@ def verify_cache(self) -> Tuple[int, int]:
521535 invalid += 1
522536 return valid , invalid
523537
524- def search (self , query : str , field : str = "rname" , exact : bool = False ) -> List [ Resource ] :
538+ def search (self , query : str , field : str = "rname" , exact : bool = False ) -> BiocFrame :
525539 """Search for resources by field value.
526540
527541 Args:
@@ -543,7 +557,7 @@ def search(self, query: str, field: str = "rname", exact: bool = False) -> List[
543557 else :
544558 resources = session .query (Resource ).filter (Resource [field ].ilike (f"%{ query } %" )).all ()
545559
546- return [self ._get_detached_resource (session , r ) for r in resources ]
560+ return BiocFrame ( convert_to_columnar ( [self ._get_detached_resource (session , r ) for r in resources ]))
547561
548562 def get_stats (self ) -> Dict [str , Any ]:
549563 """Get statistics about the cache."""
@@ -669,7 +683,7 @@ def add_metadata(self, key: str, value: str):
669683 except Exception as e :
670684 session .delete (meta )
671685 session .commit ()
672- raise Exception ("Failed to add metadata" ) from e
686+ raise Exception ("Failed to add metadata" , str ( e ) ) from e
673687 else :
674688 raise Exception (f"'key'={ key } already exists in metadata." )
675689
0 commit comments