asset_library/tests/test_library_cache.py
2026-01-07 16:05:47 +01:00

287 lines
7.4 KiB
Python

import bpy
from pathlib import Path
from asset_library.common.file_utils import read_file, write_file
from copy import deepcopy
import time
from itertools import groupby
class AssetCache:
def __init__(self, file_cache, data):
self.file_cache = file_cache
self._data = data
self.catalog = data["catalog"]
self.author = data.get("author", "")
self.description = data.get("description", "")
self.tags = data.get("tags", [])
self.type = data.get("type")
self.name = data["name"]
self._metadata = data.get("metadata", {})
@property
def filepath(self):
return self.file_cache.filepath
@property
def metadata(self):
metadata = {".library_id": self.library.id, ".filepath": self.filepath}
metadata.update(self.metadata)
return metadata
@property
def norm_name(self):
return self.name.replace(" ", "_").lower()
def to_dict(self):
return dict(
catalog=self.catalog,
author=self.author,
metadata=self.metadata,
description=self.description,
tags=self.tags,
type=self.type,
name=self.name,
)
def __str__(self):
return f"AssetCache(name={self.name}, type={self.type}, catalog={self.catalog})"
class FileCache:
def __init__(self, library_cache, data):
self.library_cache = library_cache
self.filepath = data["filepath"]
self.modified = data.get("modified", time.time_ns())
self._data = []
for asset_cache_data in data.get("assets", []):
self.add(asset_cache_data)
def add(self, asset_cache_data):
asset_cache = AssetCache(self, asset_cache_data)
self._data.append(asset_cache)
def to_dict(self):
return dict(
filepath=self.filepath.as_posix(),
modified=self.modified,
library_id=self.library_cache.library.id,
assets=[asset_cache.to_dict() for asset_cache in self],
)
def __iter__(self):
return self._data.__iter__()
def __getitem__(self, key):
return self._data[key]
def __str__(self):
return f"FileCache(filepath={self.filepath})"
class AssetCacheDiff:
def __init__(self, library_diff, asset_cache, operation):
self.library_cache = library_cache
self.filepath = data["filepath"]
self.operation = operation
class LibraryCacheDiff:
def __init__(self, filepath=None):
self.filepath = filepath
self._data = []
def add(self, asset_diff):
asset_diff = AssetCacheDiff(self, asset_diff)
self._data.append(asset_cache_diff)
def set(self, asset_diffs):
for asset_diff in asset_diffs:
self.add(asset_diff)
def read(self):
print(f"Read cache from {self.filepath}")
for asset_diff_data in read_file(self.filepath):
self.add(asset_diff_data)
return self
def group_by(self, key):
"""Return groups of file cache diff using the key provided"""
data = list(self).sort(key=key)
return groupby(data, key=key)
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
def __getitem__(self, key):
return self._data[key]
class LibraryCache:
def __init__(self, directory, id):
self.directory = directory
self.id = id
self._data = []
@classmethod
def from_library(cls, library):
return cls(library.library_path, library.id)
@property
def filename(self):
return f"blender_assets.{self.id}.json"
@property
def filepath(self):
"""Get the filepath of the library json file relative to the library"""
return self.directory / self.filename
@property
def asset_caches(self):
"""Return an iterator to get all asset caches"""
return (asset_cache for file_cache in self for asset_cache in file_cache)
@property
def tmp_filepath(self):
return Path(bpy.app.tempdir) / self.filename
def read(self):
print(f"Read cache from {self.filepath}")
for file_cache_data in read_file(self.filepath):
self.add(file_cache_data)
return self
def write(self, temp=False):
filepath = self.filepath
if temp:
filepath = self.tmp_filepath
print(f"Write cache file to {filepath}")
write_file(filepath, self._data)
return filepath
def add(self, file_cache_data):
file_cache = FileCache(self, file_cache_data)
self._data.append(file_cache)
def unflatten_cache(self, cache):
"""Return a new unflattten list of asset data
grouped by filepath"""
new_cache = []
cache = deepcopy(cache)
cache.sort(key=lambda x: x["filepath"])
groups = groupby(cache, key=lambda x: x["filepath"])
keys = ["filepath", "modified", "library_id"]
for _, asset_datas in groups:
asset_datas = list(asset_datas)
# print(asset_datas[0])
asset_info = {k: asset_datas[0][k] for k in keys}
asset_info["assets"] = [
{k: v for k, v in a.items() if k not in keys + ["operation"]}
for a in asset_datas
]
new_cache.append(asset_info)
return new_cache
def diff(self, new_cache):
"""Compare the library cache with it current state and return the cache differential"""
cache = self.read()
cache_dict = {f"{a['filepath']}/{a['name']}": a for a in cache.asset_caches}
new_cache_dict = {
f"{a['filepath']}/{a['name']}": a for a in new_cache.asset_caches
}
assets_added = [
AssetCacheDiff(v, "ADD") for k, v in new_cache.items() if k not in cache
]
assets_removed = [
AssetCacheDiff(v, "REMOVED") for k, v in cache.items() if k not in new_cache
]
assets_modified = [
AssetCacheDiff(v, "MODIFIED")
for k, v in cache.items()
if v not in assets_removed and v != new_cache[k]
]
if assets_added:
print(
f"{len(assets_added)} Assets Added \n{tuple(a.name for a in assets_added[:10])}...\n"
)
if assets_removed:
print(
f"{len(assets_removed)} Assets Removed \n{tuple(a.name for a in assets_removed[:10])}...\n"
)
if assets_modified:
print(
f"{len(assets_modified)} Assets Modified \n{tuple(a.name for a in assets_modified[:10])}...\n"
)
cache_diff = LibraryCacheDiff()
cache_diff.set(assets_added + assets_removed + assets_modified)
if not len(LibraryCacheDiff):
print("No change in the library")
return cache_diff
def __len__(self):
return len(self._data)
def __iter__(self):
return iter(self._data)
def __getitem__(self, key):
return self._data[key]
def __str__(self):
return f"LibraryCache(library={self.library.name})"
print()
prefs = bpy.context.preferences.addons["asset_library"].preferences
library = prefs.env_libraries[0]
library_cache = LibraryCache.from_library(library).read()
data = library.library_type.fetch()
print(data)
print(library_cache[0][0])
# library_cache.diff(library.library_type.fetch())
# print(library_cache[0])