From 316bca9162dfa803b0b07a933a5190bdc8ea7746 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Thu, 17 Nov 2022 21:11:06 -0500 Subject: [PATCH 01/17] feat: image support init --- eva/binder/binder_utils.py | 66 +++++++++- eva/binder/statement_binder.py | 40 ++++-- eva/catalog/catalog_manager.py | 19 ++- eva/catalog/column_type.py | 6 + eva/catalog/models/df_metadata.py | 18 +-- eva/catalog/services/df_service.py | 15 ++- eva/configuration/constants.py | 1 + eva/eva.yml | 2 + eva/executor/load_executor.py | 3 + eva/executor/load_image_executor.py | 68 ++++++++++ eva/executor/storage_executor.py | 17 ++- eva/parser/evaql/evaql_lexer.g4 | 1 + eva/parser/evaql/evaql_parser.g4 | 7 +- eva/parser/parser_visitor/_load_statement.py | 3 +- eva/parser/types.py | 2 +- eva/readers/opencv_image_reader.py | 40 ++++++ eva/storage/image_storage_engine.py | 132 +++++++++++++++++++ eva/storage/storage_engine.py | 7 +- test/integration_tests/test_load_executor.py | 19 ++- test/util.py | 1 + 20 files changed, 421 insertions(+), 46 deletions(-) create mode 100644 eva/executor/load_image_executor.py create mode 100644 eva/readers/opencv_image_reader.py create mode 100644 eva/storage/image_storage_engine.py diff --git a/eva/binder/binder_utils.py b/eva/binder/binder_utils.py index 877ed8576..c65afa7a4 100644 --- a/eva/binder/binder_utils.py +++ b/eva/binder/binder_utils.py @@ -17,11 +17,13 @@ import re from typing import TYPE_CHECKING, List +from eva.parser.types import FileFormatType + if TYPE_CHECKING: from eva.binder.statement_binder_context import StatementBinderContext from eva.catalog.catalog_manager import CatalogManager -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.column_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_metadata import DataFrameMetadata from eva.expression.tuple_value_expression import TupleValueExpression from eva.parser.create_statement import ColConstraintInfo, ColumnDefinition @@ -34,6 +36,15 @@ class BinderError(Exception): pass +def create_multimedia_metadata(name: str, format_type: FileFormatType): + if format_type is FileFormatType.VIDEO: + return create_video_metadata(name) + elif format_type is FileFormatType.IMAGE: + return create_image_table_metadata(name) + else: + raise BinderError(f"Format Type {format_type} is not supported") + + def create_video_metadata(name: str) -> DataFrameMetadata: """Create video metadata object. We have predefined columns for such a object @@ -59,7 +70,44 @@ def create_video_metadata(name: str) -> DataFrameMetadata: col_metadata = create_column_metadata(columns) uri = str(generate_file_path(name)) metadata = catalog.create_metadata( - name, uri, col_metadata, identifier_column="id", is_video=True + name, + uri, + col_metadata, + identifier_column="id", + table_type=TableType.VIDEO_DATA, + ) + return metadata + + +def create_image_table_metadata(name: str) -> DataFrameMetadata: + """Create image table metadata object. + We have predefined columns for such a object + name: image path + data: image data + + Arguments: + name (str): name of the metadata to be added to the catalog + + Returns: + DataFrameMetadata: corresponding metadata for the input table info + """ + catalog = CatalogManager() + columns = [ + ColumnDefinition( + "name", ColumnType.TEXT, None, [], ColConstraintInfo(unique=True) + ), + ColumnDefinition( + "data", ColumnType.NDARRAY, NdArrayType.UINT8, [None, None, None] + ), + ] + col_metadata = create_column_metadata(columns) + uri = str(generate_file_path(name)) + metadata = catalog.create_metadata( + name, + uri, + col_metadata, + identifier_column="id", + table_type=TableType.IMAGE_DATA, ) return metadata @@ -71,7 +119,10 @@ def create_table_metadata( column_metadata_list = create_column_metadata(columns) file_url = str(generate_file_path(table_name)) metadata = CatalogManager().create_metadata( - table_name, file_url, column_metadata_list + table_name, + file_url, + column_metadata_list, + table_type=TableType.STRUCTURAL_DATA, ) return metadata @@ -112,12 +163,15 @@ def bind_table_info(table_info: TableInfo) -> DataFrameMetadata: DataFrameMetadata - corresponding metadata for the input table info """ catalog = CatalogManager() - obj = catalog.get_dataset_metadata(table_info.database_name, table_info.table_name) + obj = catalog.get_dataset_metadata( + table_info.database_name, table_info.table_name + ) if obj: table_info.table_obj = obj else: - error = "{} does not exist. Create the table using" " CREATE TABLE.".format( - table_info.table_name + error = ( + "{} does not exist. Create the table using" + " CREATE TABLE.".format(table_info.table_name) ) logger.error(error) raise BinderError(error) diff --git a/eva/binder/statement_binder.py b/eva/binder/statement_binder.py index 7c95f391e..cfc4158b0 100644 --- a/eva/binder/statement_binder.py +++ b/eva/binder/statement_binder.py @@ -21,6 +21,7 @@ bind_table_info, check_groupby_pattern, check_table_object_is_video, + create_multimedia_metadata, create_video_metadata, extend_star, ) @@ -31,7 +32,9 @@ from eva.expression.function_expression import FunctionExpression from eva.expression.tuple_value_expression import TupleValueExpression from eva.parser.alias import Alias -from eva.parser.create_mat_view_statement import CreateMaterializedViewStatement +from eva.parser.create_mat_view_statement import ( + CreateMaterializedViewStatement, +) from eva.parser.drop_statement import DropTableStatement from eva.parser.explain_statement import ExplainStatement from eva.parser.load_statement import LoadDataStatement @@ -111,7 +114,9 @@ def _bind_select_statement(self, node: SelectStatement): self._binder_context = current_context @bind.register(CreateMaterializedViewStatement) - def _bind_create_mat_statement(self, node: CreateMaterializedViewStatement): + def _bind_create_mat_statement( + self, node: CreateMaterializedViewStatement + ): self.bind(node.query) # Todo Verify if the number projected columns matches table @@ -122,12 +127,15 @@ def _bind_load_and_upload_data_statement( ): table_ref = node.table_ref name = table_ref.table.table_name - if node.file_options["file_format"] == FileFormatType.VIDEO: - # Sanity check to make sure there is no existing video table with same name + if node.file_options["file_format"] in [ + FileFormatType.VIDEO, + FileFormatType.IMAGE, + ]: + # Sanity check to make sure there is no existing table with same name if self._catalog.check_table_exists( table_ref.table.database_name, table_ref.table.table_name ): - msg = f"Adding to an existing video table {name}." + msg = f"Adding to an existing table {name}." logger.info(msg) else: @@ -139,11 +147,13 @@ def _bind_load_and_upload_data_statement( Path(node.path).exists() or Path(Path(upload_dir) / node.path).exists() ): - create_video_metadata(name) + create_multimedia_metadata( + name, node.file_options["file_format"] + ) # else raise error else: - err_msg = f"Video file {node.path} does not exist." + err_msg = f"Path {node.path} does not exist." logger.error(err_msg) raise BinderError(err_msg) @@ -208,7 +218,9 @@ def _bind_tableref(self, node: TableRef): func_expr.alias = node.alias self.bind(func_expr) output_cols = [] - for obj, alias in zip(func_expr.output_objs, func_expr.alias.col_names): + for obj, alias in zip( + func_expr.output_objs, func_expr.alias.col_names + ): alias_obj = self._catalog.udf_io( alias, data_type=obj.type, @@ -263,7 +275,9 @@ def _bind_func_expr(self, node: FunctionExpression): if obj.name.lower() == node.output: node.output_objs = [obj] if not node.output_objs: - err_msg = f"Output {node.output} does not exist for {udf_obj.name}." + err_msg = ( + f"Output {node.output} does not exist for {udf_obj.name}." + ) logger.error(err_msg) raise BinderError(err_msg) node.projection_columns = [node.output] @@ -272,12 +286,16 @@ def _bind_func_expr(self, node: FunctionExpression): node.projection_columns = [obj.name.lower() for obj in output_objs] default_alias_name = node.name.lower() - default_output_col_aliases = [str(obj.name.lower()) for obj in node.output_objs] + default_output_col_aliases = [ + str(obj.name.lower()) for obj in node.output_objs + ] if not node.alias: node.alias = Alias(default_alias_name, default_output_col_aliases) else: if not len(node.alias.col_names): - node.alias = Alias(node.alias.alias_name, default_output_col_aliases) + node.alias = Alias( + node.alias.alias_name, default_output_col_aliases + ) else: output_aliases = [ str(col_name.lower()) for col_name in node.alias.col_names diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index 99d71e398..f78cb6b8c 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -14,7 +14,7 @@ # limitations under the License. from typing import List -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.column_type import ColumnType, NdArrayType, TableType from eva.catalog.models.base_model import drop_db, init_db from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata @@ -79,7 +79,7 @@ def create_metadata( file_url: str, column_list: List[DataFrameColumn], identifier_column="id", - is_video=False, + table_type=TableType.VIDEO_DATA, ) -> DataFrameMetadata: """Creates metadata object @@ -91,13 +91,16 @@ def create_metadata( file_url: #todo column_list: list of columns identifier_column (str): A unique identifier column for each row - is_video (bool): True if the table is a video + table_type (TableType): type of the table, video, images etc Returns: The persisted DataFrameMetadata object with the id field populated. """ metadata = self._dataset_service.create_dataset( - name, file_url, identifier_id=identifier_column, is_video=is_video + name, + file_url, + identifier_id=identifier_column, + table_type=table_type, ) for column in column_list: column.metadata_id = metadata.id @@ -260,7 +263,9 @@ def get_udf_outputs(self, udf_obj: UdfMetadata) -> List[UdfIO]: ) return self._udf_io_service.get_outputs_by_udf_id(udf_obj.id) - def drop_dataset_metadata(self, database_name: str, table_name: str) -> bool: + def drop_dataset_metadata( + self, database_name: str, table_name: str + ) -> bool: """ This method deletes the table along with its columns from df_metadata and df_columns respectively @@ -271,7 +276,9 @@ def drop_dataset_metadata(self, database_name: str, table_name: str) -> bool: Returns: True if successfully deleted else False """ - return self._dataset_service.drop_dataset_by_name(database_name, table_name) + return self._dataset_service.drop_dataset_by_name( + database_name, table_name + ) def drop_udf(self, udf_name: str) -> bool: """ diff --git a/eva/catalog/column_type.py b/eva/catalog/column_type.py index 91a417a6d..150c76dc6 100644 --- a/eva/catalog/column_type.py +++ b/eva/catalog/column_type.py @@ -19,6 +19,12 @@ class Dimension(IntEnum): ANYDIM = -1 +class TableType(IntEnum): + STRUCTURAL_DATA = auto() + VIDEO_DATA = auto() + IMAGE_DATA = auto() + + class ColumnType(Enum): BOOLEAN = 1 INTEGER = 2 diff --git a/eva/catalog/models/df_metadata.py b/eva/catalog/models/df_metadata.py index d7dc4a23a..dc9e6dcbd 100644 --- a/eva/catalog/models/df_metadata.py +++ b/eva/catalog/models/df_metadata.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from sqlalchemy import Boolean, Column, String +from sqlalchemy import Column, Integer, String from sqlalchemy.orm import relationship from eva.catalog.df_schema import DataFrameSchema @@ -25,19 +25,21 @@ class DataFrameMetadata(BaseModel): _name = Column("name", String(100), unique=True) _file_url = Column("file_url", String(100)) _unique_identifier_column = Column("identifier_column", String(100)) - _is_video = Column("is_video", Boolean) + _table_type = Column("table_type", Integer) _columns = relationship( "DataFrameColumn", back_populates="_dataset", cascade="all, delete, delete-orphan", ) - def __init__(self, name: str, file_url: str, identifier_id="id", is_video=False): + def __init__( + self, name: str, file_url: str, table_type: int, identifier_id="id" + ): self._name = name self._file_url = file_url self._schema = None self._unique_identifier_column = identifier_id - self._is_video = is_video + self._table_type = table_type @property def schema(self): @@ -68,8 +70,8 @@ def identifier_column(self): return self._unique_identifier_column @property - def is_video(self): - return self._is_video + def table_type(self): + return self._table_type def __eq__(self, other): return ( @@ -78,7 +80,7 @@ def __eq__(self, other): and self.schema == other.schema and self.identifier_column == other.identifier_column and self.name == other.name - and self.is_video == other.is_video + and self.table_type == other.table_type ) def __hash__(self) -> int: @@ -89,6 +91,6 @@ def __hash__(self) -> int: self.schema, self.identifier_column, self.name, - self.is_video, + self.table_type, ) ) diff --git a/eva/catalog/services/df_service.py b/eva/catalog/services/df_service.py index 66c171d1a..565ef32f2 100644 --- a/eva/catalog/services/df_service.py +++ b/eva/catalog/services/df_service.py @@ -19,6 +19,7 @@ from eva.catalog.models.df_metadata import DataFrameMetadata from eva.catalog.services.base_service import BaseService from eva.utils.logging_manager import logger +from eva.catalog.column_type import TableType class DatasetService(BaseService): @@ -26,14 +27,14 @@ def __init__(self): super().__init__(DataFrameMetadata) def create_dataset( - self, name, file_url, identifier_id="id", is_video=False + self, name: str, file_url: str, identifier_id, table_type: TableType ) -> DataFrameMetadata: """ Create a new dataset entry for given name and file URL. Arguments: name (str): name of the dataset file_url (str): file path of the dataset. - is_video (bool): True if the table is a video + table_type (TableType): type of data in the table Returns: DataFrameMetadata object """ @@ -41,7 +42,7 @@ def create_dataset( name=name, file_url=file_url, identifier_id=identifier_id, - is_video=is_video, + table_type=int(table_type), ) metadata = metadata.save() return metadata @@ -91,7 +92,9 @@ def dataset_object_by_name( Returns: DataFrameMetadata - metadata for given dataset_name """ - return self.model.query.filter(self.model._name == dataset_name).one_or_none() + return self.model.query.filter( + self.model._name == dataset_name + ).one_or_none() def drop_dataset_by_name(self, database_name: str, dataset_name: str): """Delete dataset from the db @@ -116,7 +119,9 @@ def rename_dataset_by_name( self, new_name: str, curr_database_name: str, curr_dataset_name: str ): try: - dataset = self.dataset_object_by_name(curr_database_name, curr_dataset_name) + dataset = self.dataset_object_by_name( + curr_database_name, curr_dataset_name + ) dataset.update(_name=new_name) except Exception as e: diff --git a/eva/configuration/constants.py b/eva/configuration/constants.py index bf2d8e124..a4a16ae30 100644 --- a/eva/configuration/constants.py +++ b/eva/configuration/constants.py @@ -17,6 +17,7 @@ import eva EVA_INSTALLATION_DIR = Path(eva.__file__).parent +EVA_ROOT_DIR = Path(eva.__file__).parent.parent EVA_DEFAULT_DIR = Path.home() / ".eva" EVA_DATASET_DIR = "eva_datasets" EVA_UPLOAD_DIR = "tmp" diff --git a/eva/eva.yml b/eva/eva.yml index b7d72e0b7..8ac75e604 100644 --- a/eva/eva.yml +++ b/eva/eva.yml @@ -20,6 +20,8 @@ storage: engine: "eva.storage.sqlite_storage_engine.SQLStorageEngine" video_engine: "eva.storage.opencv_storage_engine.OpenCVStorageEngine" video_engine_version: 0 + image_engine: "eva.storage.image_storage_engine.OpenCVImageStorageEngine" + image_engine_version: 0 server: host: "0.0.0.0" diff --git a/eva/executor/load_executor.py b/eva/executor/load_executor.py index b16594ca0..38309c05b 100644 --- a/eva/executor/load_executor.py +++ b/eva/executor/load_executor.py @@ -14,6 +14,7 @@ # limitations under the License. from eva.executor.abstract_executor import AbstractExecutor from eva.executor.load_csv_executor import LoadCSVExecutor +from eva.executor.load_image_executor import LoadImageExecutor from eva.executor.load_video_executor import LoadVideoExecutor from eva.parser.types import FileFormatType from eva.planner.load_data_plan import LoadDataPlan @@ -34,6 +35,8 @@ def exec(self): # invoke the appropriate executor if self.node.file_options["file_format"] == FileFormatType.VIDEO: executor = LoadVideoExecutor(self.node) + elif self.node.file_options["file_format"] == FileFormatType.IMAGE: + executor = LoadImageExecutor(self.node) elif self.node.file_options["file_format"] == FileFormatType.CSV: executor = LoadCSVExecutor(self.node) diff --git a/eva/executor/load_image_executor.py b/eva/executor/load_image_executor.py new file mode 100644 index 000000000..89af91d17 --- /dev/null +++ b/eva/executor/load_image_executor.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# Copyright 2018-2020 EVA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import pandas as pd +from pathlib import Path +from eva.planner.load_data_plan import LoadDataPlan +from eva.executor.abstract_executor import AbstractExecutor +from eva.storage.storage_engine import ImageStorageEngine +from eva.models.storage.batch import Batch +from eva.configuration.configuration_manager import ConfigurationManager + + +class LoadImageExecutor(AbstractExecutor): + def __init__(self, node: LoadDataPlan): + super().__init__(node) + self.upload_dir = Path( + ConfigurationManager().get_value("storage", "upload_dir") + ) + + def validate(self): + pass + + def exec(self): + """ """ + image_file_path = None + # Validate file_path + if Path(self.node.file_path).exists(): + image_file_path = self.node.file_path + # check in the upload directory + else: + image_path = Path(self.upload_path / self.node.file_path) + if image_path.exists(): + image_file_path = image_path + if image_file_path is None: + error = "Failed to find the video file {}".format( + self.node.file_path + ) + + raise RuntimeError(error) + + success = ImageStorageEngine.create(self.node.table_metainfo) + + if not success: + raise RuntimeError("ImageStorageEngine create call failed") + + file_count = 0 + for file in image_file_path.iterdir(): + if file.is_file(): + if file.suffix in [".png", ".jpeg", ".jpg"]: + ImageStorageEngine.write(self.node.table_metainfo, file) + file_count += 1 + + yield Batch( + pd.DataFrame( + {"Number of loaded images": str(file_count)}, index=[0] + ) + ) diff --git a/eva/executor/storage_executor.py b/eva/executor/storage_executor.py index e3bd3057a..215db879d 100644 --- a/eva/executor/storage_executor.py +++ b/eva/executor/storage_executor.py @@ -13,11 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Generator, Iterator +from eva.catalog.column_type import TableType from eva.executor.abstract_executor import AbstractExecutor from eva.models.storage.batch import Batch from eva.planner.storage_plan import StoragePlan -from eva.storage.storage_engine import StorageEngine, VideoStorageEngine +from eva.storage.storage_engine import ( + StorageEngine, + VideoStorageEngine, + ImageStorageEngine, +) class StorageExecutor(AbstractExecutor): @@ -28,15 +33,21 @@ def validate(self): pass def exec(self) -> Iterator[Batch]: - if self.node.video.is_video: + if self.node.video.table_type is TableType.VIDEO_DATA: return VideoStorageEngine.read( self.node.video, self.node.batch_mem_size, predicate=self.node.predicate, sampling_rate=self.node.sampling_rate, ) + elif self.node.video.table_type is TableType.STRUCTURAL_DATA: + return StorageEngine.read( + self.node.video, self.node.batch_mem_size + ) else: - return StorageEngine.read(self.node.video, self.node.batch_mem_size) + return ImageStorageEngine.read( + self.node.video, self.node.batch_mem_size, self.node.predicate + ) def __call__(self, **kwargs) -> Generator[Batch, None, None]: yield from self.exec() diff --git a/eva/parser/evaql/evaql_lexer.g4 b/eva/parser/evaql/evaql_lexer.g4 index bb7e3b8ff..e0b736686 100644 --- a/eva/parser/evaql/evaql_lexer.g4 +++ b/eva/parser/evaql/evaql_lexer.g4 @@ -89,6 +89,7 @@ USING: 'USING'; VALUES: 'VALUES'; WHERE: 'WHERE'; XOR: 'XOR'; +IMAGE: 'IMAGE'; // File Formats WITH: 'WITH'; diff --git a/eva/parser/evaql/evaql_parser.g4 b/eva/parser/evaql/evaql_parser.g4 index f46e9b386..eb425e789 100644 --- a/eva/parser/evaql/evaql_parser.g4 +++ b/eva/parser/evaql/evaql_parser.g4 @@ -187,11 +187,12 @@ loadStatement ( ('(' columns=uidList ')') )? - (WITH fileOptions)? + fileOptions? ; + fileOptions - : FORMAT fileFormat=(CSV|VIDEO) + : FORMAT fileFormat=(CSV|VIDEO|IMAGE) ; uploadStatement @@ -202,7 +203,7 @@ uploadStatement ( ('(' columns=uidList ')') )? - (WITH fileOptions)? + (fileOptions)? ; fileName diff --git a/eva/parser/parser_visitor/_load_statement.py b/eva/parser/parser_visitor/_load_statement.py index 00e5c3e5b..d95ee9165 100644 --- a/eva/parser/parser_visitor/_load_statement.py +++ b/eva/parser/parser_visitor/_load_statement.py @@ -45,7 +45,8 @@ def visitFileOptions(self, ctx: evaql_parser.FileOptionsContext): # Check the file format if ctx.CSV() is not None: file_format = FileFormatType.CSV - + elif ctx.IMAGE() is not None: + file_format = FileFormatType.IMAGE # parse and add more file options in future file_options = {} file_options["file_format"] = file_format diff --git a/eva/parser/types.py b/eva/parser/types.py index cf12260ba..bfcd8a4e4 100644 --- a/eva/parser/types.py +++ b/eva/parser/types.py @@ -64,7 +64,7 @@ class FileFormatType(Enum): """ Manages enums for all order by sort types """ - + IMAGE = auto() VIDEO = auto() CSV = auto() diff --git a/eva/readers/opencv_image_reader.py b/eva/readers/opencv_image_reader.py new file mode 100644 index 000000000..2429ed256 --- /dev/null +++ b/eva/readers/opencv_image_reader.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# Copyright 2018-2020 EVA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from PIL import Image +import cv2 +from typing import Iterator, Dict + +from eva.readers.abstract_reader import AbstractReader +# from eva.utils.logging_manager import LoggingLevel +# from eva.utils.logging_manager import LoggingManager +# from eva.expression.expression_utils import parse_predicate + + +class CVImageReader(AbstractReader): + def __init__(self, *args, start_frame_id=0, **kwargs): + self._start_frame_id = start_frame_id + self._predicate = kwargs.pop("predicate", None) + self._resolution = kwargs.pop("resolution", None) + super().__init__(*args, **kwargs) + + def _read(self) -> Iterator[Dict]: + for file in self.file_url: + # frame = Image.open(str(file)).load() + frame = cv2.imread(str(file)) + if frame is None: + print("Failed to read Image {}".format(file)) + else: + yield {"name": str(file), "data": frame} diff --git a/eva/storage/image_storage_engine.py b/eva/storage/image_storage_engine.py new file mode 100644 index 000000000..9d5b8c579 --- /dev/null +++ b/eva/storage/image_storage_engine.py @@ -0,0 +1,132 @@ +# coding=utf-8 +# Copyright 2018-2020 EVA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import Iterator +from pathlib import Path +import struct +import shutil +import cv2, io +import numpy as np + +from eva.catalog.models.df_metadata import DataFrameMetadata +from eva.storage.abstract_storage_engine import AbstractStorageEngine +from eva.readers.opencv_image_reader import CVImageReader +from eva.models.storage.batch import Batch +from eva.configuration.configuration_manager import ConfigurationManager + +# from eva.utils.logging_manager import LoggingManager +# from eva.utils.logging_manager import LoggingLevel + + +class OpenCVImageStorageEngine(AbstractStorageEngine): + def __init__(self): + self.metadata = "metadata" + self.curr_version = ConfigurationManager().get_value( + "storage", "image_engine_version" + ) + + def create(self, table: DataFrameMetadata): + # Create directory to store images + dir_path = Path(table.file_url) + try: + dir_path.mkdir(parents=True) + except FileExistsError: + error = f"Failed to create, directory already exists {dir_path}" + raise FileExistsError(error) + self._create_metadata(dir_path) + return True + + def write(self, table: DataFrameMetadata, image_file: Path): + dir_path = Path(table.file_url) + try: + shutil.copy2(str(image_file), str(dir_path)) + except FileExistsError: + error = "Failed to load the image as directory \ + already exists {}".format( + dir_path + ) + raise FileExistsError(error) + self._update_metadata(dir_path, image_file) + + def read( + self, + table: DataFrameMetadata, + batch_size: int, + predicate=None, + resolution=None, + ) -> Iterator[Batch]: + + files = self._get_file_list(table.file_url) + reader = CVImageReader( + files, + batch_mem_size=batch_size, + predicate=predicate, + resolution=resolution, + ) + for batch in reader.read(): + yield batch + + def _get_file_list(self, dir_path): + metadata_file = Path(dir_path) / self.metadata + if not metadata_file.exists(): + return + with open(metadata_file, "rb") as f: + (version,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) + if version > self.curr_version: + error = "Invalid metadata version {}".format(version) + # LoggingManager().log(error, LoggingLevel.ERROR) + raise RuntimeError(error) + (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) + for i in range(num_files): + (length,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) + path = f.read(length) + yield Path(path.decode()) + + def _get_num_files(self, dir_path): + metadata_file = Path(dir_path) / self.metadata + if not metadata_file.exists(): + return 0 + with open(metadata_file, "rb") as f: + f.seek(struct.calcsize("!H")) + (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) + return num_files + + def _create_metadata(self, dir_path, overwrite=False): + metadata_file = Path(dir_path) / self.metadata + if metadata_file.exists(): + if not overwrite: + print( + "Trying to overwrite metadata. Please set overwrite = True" + ) + return + # + with open(metadata_file, "wb") as f: + # write version number + data = struct.pack("!HH", self.curr_version, 0) + f.write(data) + + def _update_metadata(self, dir_path, image_file): + with open(dir_path / self.metadata, "r+b") as f: + # increment num files + f.seek(struct.calcsize("!H")) + (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) + num_files += 1 + f.seek(struct.calcsize("!H")) + f.write(struct.pack("!H", num_files)) + # write version number + f.seek(0, io.SEEK_END) + file_path_bytes = str(image_file).encode() + length = len(file_path_bytes) + data = struct.pack("!H%ds" % (length,), length, file_path_bytes) + f.write(data) diff --git a/eva/storage/storage_engine.py b/eva/storage/storage_engine.py index daa2cf413..db176e6de 100644 --- a/eva/storage/storage_engine.py +++ b/eva/storage/storage_engine.py @@ -15,7 +15,12 @@ from eva.configuration.configuration_manager import ConfigurationManager from eva.utils.generic_utils import str_to_class -StorageEngine = str_to_class(ConfigurationManager().get_value("storage", "engine"))() +StorageEngine = str_to_class( + ConfigurationManager().get_value("storage", "engine") +)() VideoStorageEngine = str_to_class( ConfigurationManager().get_value("storage", "video_engine") )() +ImageStorageEngine = str_to_class( + ConfigurationManager().get_value("storage", "image_engine") +)() diff --git a/test/integration_tests/test_load_executor.py b/test/integration_tests/test_load_executor.py index 4929502f4..7a66a1342 100644 --- a/test/integration_tests/test_load_executor.py +++ b/test/integration_tests/test_load_executor.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import unittest +from eva.configuration.constants import EVA_INSTALLATION_DIR, EVA_ROOT_DIR from test.util import ( create_dummy_batches, create_dummy_csv_batches, @@ -54,6 +55,20 @@ def test_should_load_video_in_table(self): actual_batch = execute_query_fetch_all(select_query) self.assertEqual(len(actual_batch), 2 * len(expected_batch)) + def test_should_load_images_in_table(self): + path = f"{EVA_ROOT_DIR}/data/mnist/images/*.jpg" + query = f"""LOAD FILE "{path}" INTO MyImages + WITH FORMAT IMAGE;""" + execute_query_fetch_all(query) + + select_query = """SELECT name, data FROM MyImages;""" + + actual_batch = execute_query_fetch_all(select_query) + self.assertEqual(len(actual_batch), 20) + + file_names = actual_batch.project(['myimages.name']).frames + + # integration tests for csv def test_should_load_csv_in_table(self): @@ -124,7 +139,9 @@ def test_should_load_csv_with_columns_in_table(self): # assert the batches are equal select_columns = ["id", "frame_id", "video_id", "dataset_name"] - expected_batch = create_dummy_csv_batches(target_columns=select_columns) + expected_batch = create_dummy_csv_batches( + target_columns=select_columns + ) expected_batch.modify_column_alias("myvideocsv") self.assertEqual(actual_batch, expected_batch) diff --git a/test/util.py b/test/util.py index 49bbc6f23..848de7b00 100644 --- a/test/util.py +++ b/test/util.py @@ -284,6 +284,7 @@ def copy_sample_videos_to_upload_dir(): "data/actions/actions.mp4", os.path.join(upload_dir_from_config, "actions.mp4"), ) + def file_remove(path): From 9faf6a4f58f57bed38635e9f75de5896ce0674c4 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Sun, 20 Nov 2022 05:00:55 -0500 Subject: [PATCH 02/17] feat: LOAD cmd clean up + storage Engine moved to factory pattern --- README.md | 2 +- api-docs/source/overview/installation.rst | 2 +- api-docs/source/tutorials/tutorials.rst | 2 +- eva/binder/binder_utils.py | 13 +++---- eva/binder/statement_binder.py | 29 ++++----------- eva/catalog/catalog_manager.py | 10 ++---- .../{column_type.py => catalog_type.py} | 0 eva/catalog/catalog_utils.py | 20 +++++++++++ eva/catalog/models/df_column.py | 2 +- eva/catalog/models/df_metadata.py | 4 +-- eva/catalog/models/udf_io.py | 2 +- eva/catalog/schema_utils.py | 2 +- eva/catalog/services/df_service.py | 10 ++---- eva/executor/create_executor.py | 3 +- eva/executor/create_mat_view_executor.py | 5 +-- eva/executor/drop_executor.py | 11 +++--- eva/executor/load_csv_executor.py | 3 +- eva/executor/load_image_executor.py | 25 +++++++------ eva/executor/load_video_executor.py | 7 ++-- eva/executor/storage_executor.py | 27 +++++++------- eva/expression/constant_value_expression.py | 2 +- eva/optimizer/rules/rules.py | 6 ++-- eva/parser/create_statement.py | 2 +- eva/parser/evaql/evaql_parser.g4 | 16 ++++----- eva/parser/load_statement.py | 2 +- .../parser_visitor/_create_statements.py | 2 +- eva/parser/parser_visitor/_expressions.py | 2 +- eva/parser/parser_visitor/_load_statement.py | 15 ++++---- eva/parser/types.py | 1 + eva/readers/opencv_image_reader.py | 7 ++-- eva/storage/image_storage_engine.py | 21 +++++------ eva/storage/sqlite_storage_engine.py | 2 +- eva/storage/storage_engine.py | 32 ++++++++++++----- test/binder/test_binder_utils.py | 8 +++-- test/binder/test_statement_binder.py | 36 +++++++++++-------- test/catalog/models/test_models.py | 19 +++++++--- test/catalog/services/test_dataset_service.py | 11 ++++-- test/catalog/test_catalog_manager.py | 4 +-- test/catalog/test_column_type.py | 2 +- test/catalog/test_schema.py | 2 +- test/executor/test_load_executor.py | 35 +++++++++--------- test/executor/test_upload_executor.py | 35 +++++++++--------- test/expression/test_comparison.py | 2 +- test/integration_tests/test_array_count.py | 2 +- test/integration_tests/test_drop_executor.py | 2 +- .../test_explain_executor.py | 2 +- .../integration_tests/test_insert_executor.py | 2 +- test/integration_tests/test_load_executor.py | 23 +++++------- test/integration_tests/test_mat_executor.py | 4 +-- test/integration_tests/test_pytorch.py | 6 ++-- .../integration_tests/test_rename_executor.py | 2 +- .../integration_tests/test_select_executor.py | 4 +-- test/integration_tests/test_udf_executor.py | 2 +- test/optimizer/rules/test_rules.py | 2 +- test/optimizer/test_cascade_optimizer.py | 2 +- test/optimizer/test_optimizer_utils.py | 2 +- test/parser/test_parser.py | 11 +++--- test/parser/test_parser_visitor.py | 2 +- test/planner/test_plan.py | 2 +- test/storage/test_sqlite_storage_engine.py | 6 ++-- test/storage/test_video_storage.py | 12 ++++--- test/util.py | 3 +- test/utils/test_timer.py | 2 +- 63 files changed, 282 insertions(+), 254 deletions(-) rename eva/catalog/{column_type.py => catalog_type.py} (100%) create mode 100644 eva/catalog/catalog_utils.py diff --git a/README.md b/README.md index ac0a5c98c..ccda28de2 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ eva_client # launch client 2. Load a video onto the server using the client (we use [ua_detrac.mp4](data/ua_detrac/ua_detrac.mp4) video as an example): ```mysql -LOAD FILE "data/ua_detrac/ua_detrac.mp4" INTO MyVideo; +LOAD VIDEO "data/ua_detrac/ua_detrac.mp4" INTO MyVideo; ``` 3. That's it! You can now start running queries over the loaded video: diff --git a/api-docs/source/overview/installation.rst b/api-docs/source/overview/installation.rst index 001028498..161dc8383 100644 --- a/api-docs/source/overview/installation.rst +++ b/api-docs/source/overview/installation.rst @@ -45,5 +45,5 @@ Once the connection is established, you can run queries using the cursor:: EVA offers a command line interface (CLI) to query the server for quick testing and debugging:: python eva/eva_client - >>> LOAD FILE "eva/data/mnist/mnist.mp4" INTO MNISTVid; + >>> LOAD VIDEO "eva/data/mnist/mnist.mp4" INTO MNISTVid; >>> SELECT id, data FROM MNISTVid WHERE id < 5 diff --git a/api-docs/source/tutorials/tutorials.rst b/api-docs/source/tutorials/tutorials.rst index 812c9bf4a..829c1809c 100644 --- a/api-docs/source/tutorials/tutorials.rst +++ b/api-docs/source/tutorials/tutorials.rst @@ -74,7 +74,7 @@ Quickstart Tutorial .. code-block:: python - cursor.execute('LOAD FILE "../data/ua_detrac/ua_detrac.mp4" INTO MyVideo') + cursor.execute('LOAD VIDEO "../data/ua_detrac/ua_detrac.mp4" INTO MyVideo') response = cursor.fetch_all() print(response) diff --git a/eva/binder/binder_utils.py b/eva/binder/binder_utils.py index c65afa7a4..f9568bcfb 100644 --- a/eva/binder/binder_utils.py +++ b/eva/binder/binder_utils.py @@ -23,7 +23,7 @@ from eva.binder.statement_binder_context import StatementBinderContext from eva.catalog.catalog_manager import CatalogManager -from eva.catalog.column_type import ColumnType, NdArrayType, TableType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_metadata import DataFrameMetadata from eva.expression.tuple_value_expression import TupleValueExpression from eva.parser.create_statement import ColConstraintInfo, ColumnDefinition @@ -163,15 +163,12 @@ def bind_table_info(table_info: TableInfo) -> DataFrameMetadata: DataFrameMetadata - corresponding metadata for the input table info """ catalog = CatalogManager() - obj = catalog.get_dataset_metadata( - table_info.database_name, table_info.table_name - ) + obj = catalog.get_dataset_metadata(table_info.database_name, table_info.table_name) if obj: table_info.table_obj = obj else: - error = ( - "{} does not exist. Create the table using" - " CREATE TABLE.".format(table_info.table_name) + error = "{} does not exist. Create the table using" " CREATE TABLE.".format( + table_info.table_name ) logger.error(error) raise BinderError(error) @@ -221,6 +218,6 @@ def check_groupby_pattern(groupby_string: str) -> None: def check_table_object_is_video(table_ref: TableRef) -> None: - if not table_ref.table.table_obj.is_video: + if table_ref.table.table_obj.table_type == TableType.VIDEO_DATA: err_msg = "GROUP BY only supported for video tables" raise BinderError(err_msg) diff --git a/eva/binder/statement_binder.py b/eva/binder/statement_binder.py index cfc4158b0..14b4fa60c 100644 --- a/eva/binder/statement_binder.py +++ b/eva/binder/statement_binder.py @@ -22,7 +22,6 @@ check_groupby_pattern, check_table_object_is_video, create_multimedia_metadata, - create_video_metadata, extend_star, ) from eva.binder.statement_binder_context import StatementBinderContext @@ -32,9 +31,7 @@ from eva.expression.function_expression import FunctionExpression from eva.expression.tuple_value_expression import TupleValueExpression from eva.parser.alias import Alias -from eva.parser.create_mat_view_statement import ( - CreateMaterializedViewStatement, -) +from eva.parser.create_mat_view_statement import CreateMaterializedViewStatement from eva.parser.drop_statement import DropTableStatement from eva.parser.explain_statement import ExplainStatement from eva.parser.load_statement import LoadDataStatement @@ -114,9 +111,7 @@ def _bind_select_statement(self, node: SelectStatement): self._binder_context = current_context @bind.register(CreateMaterializedViewStatement) - def _bind_create_mat_statement( - self, node: CreateMaterializedViewStatement - ): + def _bind_create_mat_statement(self, node: CreateMaterializedViewStatement): self.bind(node.query) # Todo Verify if the number projected columns matches table @@ -147,9 +142,7 @@ def _bind_load_and_upload_data_statement( Path(node.path).exists() or Path(Path(upload_dir) / node.path).exists() ): - create_multimedia_metadata( - name, node.file_options["file_format"] - ) + create_multimedia_metadata(name, node.file_options["file_format"]) # else raise error else: @@ -218,9 +211,7 @@ def _bind_tableref(self, node: TableRef): func_expr.alias = node.alias self.bind(func_expr) output_cols = [] - for obj, alias in zip( - func_expr.output_objs, func_expr.alias.col_names - ): + for obj, alias in zip(func_expr.output_objs, func_expr.alias.col_names): alias_obj = self._catalog.udf_io( alias, data_type=obj.type, @@ -275,9 +266,7 @@ def _bind_func_expr(self, node: FunctionExpression): if obj.name.lower() == node.output: node.output_objs = [obj] if not node.output_objs: - err_msg = ( - f"Output {node.output} does not exist for {udf_obj.name}." - ) + err_msg = f"Output {node.output} does not exist for {udf_obj.name}." logger.error(err_msg) raise BinderError(err_msg) node.projection_columns = [node.output] @@ -286,16 +275,12 @@ def _bind_func_expr(self, node: FunctionExpression): node.projection_columns = [obj.name.lower() for obj in output_objs] default_alias_name = node.name.lower() - default_output_col_aliases = [ - str(obj.name.lower()) for obj in node.output_objs - ] + default_output_col_aliases = [str(obj.name.lower()) for obj in node.output_objs] if not node.alias: node.alias = Alias(default_alias_name, default_output_col_aliases) else: if not len(node.alias.col_names): - node.alias = Alias( - node.alias.alias_name, default_output_col_aliases - ) + node.alias = Alias(node.alias.alias_name, default_output_col_aliases) else: output_aliases = [ str(col_name.lower()) for col_name in node.alias.col_names diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index f78cb6b8c..ef069ce68 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -14,7 +14,7 @@ # limitations under the License. from typing import List -from eva.catalog.column_type import ColumnType, NdArrayType, TableType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.base_model import drop_db, init_db from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata @@ -263,9 +263,7 @@ def get_udf_outputs(self, udf_obj: UdfMetadata) -> List[UdfIO]: ) return self._udf_io_service.get_outputs_by_udf_id(udf_obj.id) - def drop_dataset_metadata( - self, database_name: str, table_name: str - ) -> bool: + def drop_dataset_metadata(self, database_name: str, table_name: str) -> bool: """ This method deletes the table along with its columns from df_metadata and df_columns respectively @@ -276,9 +274,7 @@ def drop_dataset_metadata( Returns: True if successfully deleted else False """ - return self._dataset_service.drop_dataset_by_name( - database_name, table_name - ) + return self._dataset_service.drop_dataset_by_name(database_name, table_name) def drop_udf(self, udf_name: str) -> bool: """ diff --git a/eva/catalog/column_type.py b/eva/catalog/catalog_type.py similarity index 100% rename from eva/catalog/column_type.py rename to eva/catalog/catalog_type.py diff --git a/eva/catalog/catalog_utils.py b/eva/catalog/catalog_utils.py new file mode 100644 index 000000000..9473ceb4c --- /dev/null +++ b/eva/catalog/catalog_utils.py @@ -0,0 +1,20 @@ +# coding=utf-8 +# Copyright 2018-2022 EVA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from eva.catalog.catalog_type import TableType +from eva.catalog.models.df_metadata import DataFrameMetadata + + +def is_video_table(table: DataFrameMetadata): + return table.table_type == TableType.VIDEO_DATA diff --git a/eva/catalog/models/df_column.py b/eva/catalog/models/df_column.py index 04164820d..00c179839 100644 --- a/eva/catalog/models/df_column.py +++ b/eva/catalog/models/df_column.py @@ -19,7 +19,7 @@ from sqlalchemy.orm import relationship from sqlalchemy.types import Enum -from eva.catalog.column_type import ColumnType, Dimension, NdArrayType +from eva.catalog.catalog_type import ColumnType, Dimension, NdArrayType from eva.catalog.models.base_model import BaseModel diff --git a/eva/catalog/models/df_metadata.py b/eva/catalog/models/df_metadata.py index dc9e6dcbd..b27b956f3 100644 --- a/eva/catalog/models/df_metadata.py +++ b/eva/catalog/models/df_metadata.py @@ -32,9 +32,7 @@ class DataFrameMetadata(BaseModel): cascade="all, delete, delete-orphan", ) - def __init__( - self, name: str, file_url: str, table_type: int, identifier_id="id" - ): + def __init__(self, name: str, file_url: str, table_type: int, identifier_id="id"): self._name = name self._file_url = file_url self._schema = None diff --git a/eva/catalog/models/udf_io.py b/eva/catalog/models/udf_io.py index f0da3bea4..f8b835cfd 100644 --- a/eva/catalog/models/udf_io.py +++ b/eva/catalog/models/udf_io.py @@ -19,7 +19,7 @@ from sqlalchemy.orm import relationship from sqlalchemy.types import Enum -from eva.catalog.column_type import ColumnType, Dimension, NdArrayType +from eva.catalog.catalog_type import ColumnType, Dimension, NdArrayType from eva.catalog.models.base_model import BaseModel diff --git a/eva/catalog/schema_utils.py b/eva/catalog/schema_utils.py index 08c58cdfb..0c510448c 100644 --- a/eva/catalog/schema_utils.py +++ b/eva/catalog/schema_utils.py @@ -16,7 +16,7 @@ from sqlalchemy import TEXT, Column, Float, Integer, LargeBinary -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.catalog.models.df_column import DataFrameColumn from eva.utils.logging_manager import logger diff --git a/eva/catalog/services/df_service.py b/eva/catalog/services/df_service.py index 565ef32f2..42139d8c9 100644 --- a/eva/catalog/services/df_service.py +++ b/eva/catalog/services/df_service.py @@ -16,10 +16,10 @@ from sqlalchemy.orm.exc import NoResultFound +from eva.catalog.catalog_type import TableType from eva.catalog.models.df_metadata import DataFrameMetadata from eva.catalog.services.base_service import BaseService from eva.utils.logging_manager import logger -from eva.catalog.column_type import TableType class DatasetService(BaseService): @@ -92,9 +92,7 @@ def dataset_object_by_name( Returns: DataFrameMetadata - metadata for given dataset_name """ - return self.model.query.filter( - self.model._name == dataset_name - ).one_or_none() + return self.model.query.filter(self.model._name == dataset_name).one_or_none() def drop_dataset_by_name(self, database_name: str, dataset_name: str): """Delete dataset from the db @@ -119,9 +117,7 @@ def rename_dataset_by_name( self, new_name: str, curr_database_name: str, curr_dataset_name: str ): try: - dataset = self.dataset_object_by_name( - curr_database_name, curr_dataset_name - ) + dataset = self.dataset_object_by_name(curr_database_name, curr_dataset_name) dataset.update(_name=new_name) except Exception as e: diff --git a/eva/executor/create_executor.py b/eva/executor/create_executor.py index 6bb9d1c82..5f774330d 100644 --- a/eva/executor/create_executor.py +++ b/eva/executor/create_executor.py @@ -28,4 +28,5 @@ def validate(self): def exec(self): if not handle_if_not_exists(self.node.table_ref, self.node.if_not_exists): metadata = create_table_metadata(self.node.table_ref, self.node.column_list) - StorageEngine.create(table=metadata) + storage_engine = StorageEngine.factory(metadata) + storage_engine.create(table=metadata) diff --git a/eva/executor/create_mat_view_executor.py b/eva/executor/create_mat_view_executor.py index d736110e5..9c841cad6 100644 --- a/eva/executor/create_mat_view_executor.py +++ b/eva/executor/create_mat_view_executor.py @@ -80,9 +80,10 @@ def exec(self): ) view_metainfo = create_table_metadata(self.node.view, col_defs) - StorageEngine.create(table=view_metainfo) + storage_engine = StorageEngine.factory(view_metainfo) + storage_engine.create(table=view_metainfo) # Populate the view for batch in child.exec(): batch.drop_column_alias() - StorageEngine.write(view_metainfo, batch) + storage_engine.write(view_metainfo, batch) diff --git a/eva/executor/drop_executor.py b/eva/executor/drop_executor.py index a8b85cdcc..0242bf9f8 100644 --- a/eva/executor/drop_executor.py +++ b/eva/executor/drop_executor.py @@ -19,7 +19,7 @@ from eva.executor.executor_utils import ExecutorError from eva.models.storage.batch import Batch from eva.planner.drop_plan import DropPlan -from eva.storage.storage_engine import StorageEngine, VideoStorageEngine +from eva.storage.storage_engine import StorageEngine from eva.utils.logging_manager import logger @@ -46,10 +46,11 @@ def exec(self): else: logger.exception(err_msg) - if table_ref.table.table_obj.is_video: - VideoStorageEngine.drop(table=table_ref.table.table_obj) - else: - StorageEngine.drop(table=table_ref.table.table_obj) + try: + storage_engine = StorageEngine.factory(table_ref.table.table_obj) + except RuntimeError as err: + raise ExecutorError(str(err)) + storage_engine.drop(table=table_ref.table.table_obj) success = catalog_manager.drop_dataset_metadata( table_ref.table.database_name, table_ref.table.table_name diff --git a/eva/executor/load_csv_executor.py b/eva/executor/load_csv_executor.py index 0cc8e9d6e..45bc9ce1b 100644 --- a/eva/executor/load_csv_executor.py +++ b/eva/executor/load_csv_executor.py @@ -48,10 +48,11 @@ def exec(self): batch_mem_size=self.node.batch_mem_size, ) + storage_engine = StorageEngine.factory(self.node.table_metainfo) # write with storage engine in batches num_loaded_frames = 0 for batch in csv_reader.read(): - StorageEngine.write(self.node.table_metainfo, batch) + storage_engine.write(self.node.table_metainfo, batch) num_loaded_frames += len(batch) # yield result diff --git a/eva/executor/load_image_executor.py b/eva/executor/load_image_executor.py index 89af91d17..358ac0583 100644 --- a/eva/executor/load_image_executor.py +++ b/eva/executor/load_image_executor.py @@ -1,5 +1,5 @@ # coding=utf-8 -# Copyright 2018-2020 EVA +# Copyright 2018-2022 EVA # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,13 +12,15 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import pandas as pd from pathlib import Path -from eva.planner.load_data_plan import LoadDataPlan + +import pandas as pd + +from eva.configuration.configuration_manager import ConfigurationManager from eva.executor.abstract_executor import AbstractExecutor -from eva.storage.storage_engine import ImageStorageEngine from eva.models.storage.batch import Batch -from eva.configuration.configuration_manager import ConfigurationManager +from eva.planner.load_data_plan import LoadDataPlan +from eva.storage.storage_engine import StorageEngine class LoadImageExecutor(AbstractExecutor): @@ -43,13 +45,12 @@ def exec(self): if image_path.exists(): image_file_path = image_path if image_file_path is None: - error = "Failed to find the video file {}".format( - self.node.file_path - ) + error = "Failed to find the video file {}".format(self.node.file_path) raise RuntimeError(error) - success = ImageStorageEngine.create(self.node.table_metainfo) + storage_engine = StorageEngine.factory(self.node.table_metainfo) + success = storage_engine.create(self.node.table_metainfo) if not success: raise RuntimeError("ImageStorageEngine create call failed") @@ -58,11 +59,9 @@ def exec(self): for file in image_file_path.iterdir(): if file.is_file(): if file.suffix in [".png", ".jpeg", ".jpg"]: - ImageStorageEngine.write(self.node.table_metainfo, file) + storage_engine.write(self.node.table_metainfo, file) file_count += 1 yield Batch( - pd.DataFrame( - {"Number of loaded images": str(file_count)}, index=[0] - ) + pd.DataFrame({"Number of loaded images": str(file_count)}, index=[0]) ) diff --git a/eva/executor/load_video_executor.py b/eva/executor/load_video_executor.py index de3445a50..6db06cc2e 100644 --- a/eva/executor/load_video_executor.py +++ b/eva/executor/load_video_executor.py @@ -20,7 +20,7 @@ from eva.executor.abstract_executor import AbstractExecutor from eva.models.storage.batch import Batch from eva.planner.load_data_plan import LoadDataPlan -from eva.storage.storage_engine import VideoStorageEngine +from eva.storage.storage_engine import StorageEngine from eva.utils.logging_manager import logger @@ -56,8 +56,9 @@ def exec(self): logger.error(error) raise RuntimeError(error) - VideoStorageEngine.create(self.node.table_metainfo, if_not_exists=True) - success = VideoStorageEngine.write( + storage_engine = StorageEngine.factory(self.node.table_metainfo) + storage_engine.create(self.node.table_metainfo, if_not_exists=True) + success = storage_engine.write( self.node.table_metainfo, Batch(pd.DataFrame([{"video_file_path": str(video_file_path)}])), ) diff --git a/eva/executor/storage_executor.py b/eva/executor/storage_executor.py index 215db879d..91da77e60 100644 --- a/eva/executor/storage_executor.py +++ b/eva/executor/storage_executor.py @@ -13,16 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Generator, Iterator -from eva.catalog.column_type import TableType +from eva.catalog.catalog_type import TableType from eva.executor.abstract_executor import AbstractExecutor +from eva.executor.executor_utils import ExecutorError from eva.models.storage.batch import Batch from eva.planner.storage_plan import StoragePlan -from eva.storage.storage_engine import ( - StorageEngine, - VideoStorageEngine, - ImageStorageEngine, -) +from eva.storage.storage_engine import StorageEngine class StorageExecutor(AbstractExecutor): @@ -33,20 +30,24 @@ def validate(self): pass def exec(self) -> Iterator[Batch]: - if self.node.video.table_type is TableType.VIDEO_DATA: - return VideoStorageEngine.read( + storage_engine = StorageEngine.factory(self.node.video) + + if self.node.video.table_type == TableType.VIDEO_DATA: + return storage_engine.read( self.node.video, self.node.batch_mem_size, predicate=self.node.predicate, sampling_rate=self.node.sampling_rate, ) - elif self.node.video.table_type is TableType.STRUCTURAL_DATA: - return StorageEngine.read( - self.node.video, self.node.batch_mem_size + elif self.node.video.table_type == TableType.STRUCTURAL_DATA: + return storage_engine.read(self.node.video, self.node.batch_mem_size) + elif self.node.video.table_type == TableType.IMAGE_DATA: + return storage_engine.read( + self.node.video, self.node.batch_mem_size, self.node.predicate ) else: - return ImageStorageEngine.read( - self.node.video, self.node.batch_mem_size, self.node.predicate + raise ExecutorError( + f"Unsupported TableType {self.node.video.table_type} encountered" ) def __call__(self, **kwargs) -> Generator[Batch, None, None]: diff --git a/eva/expression/constant_value_expression.py b/eva/expression/constant_value_expression.py index f7cb12f9f..dd0451694 100644 --- a/eva/expression/constant_value_expression.py +++ b/eva/expression/constant_value_expression.py @@ -16,7 +16,7 @@ import pandas as pd -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.expression.abstract_expression import AbstractExpression, ExpressionType from eva.models.storage.batch import Batch diff --git a/eva/optimizer/rules/rules.py b/eva/optimizer/rules/rules.py index e9cb585cb..544dcb298 100644 --- a/eva/optimizer/rules/rules.py +++ b/eva/optimizer/rules/rules.py @@ -16,6 +16,8 @@ from typing import TYPE_CHECKING +from eva.catalog.catalog_type import TableType +from eva.catalog.catalog_utils import is_video_table from eva.expression.expression_utils import conjuction_list_to_expression_tree from eva.optimizer.optimizer_utils import ( extract_equi_join_keys, @@ -99,7 +101,7 @@ def check(self, before: LogicalFilter, context: OptimizerContext): # System supports predicate pushdown only while reading video data predicate = before.predicate lget: LogicalGet = before.children[0] - if predicate and lget.dataset_metadata.is_video: + if predicate and is_video_table(lget.dataset_metadata): # System only supports pushing basic range predicates on id video_alias = lget.video.alias col_alias = f"{video_alias}.id" @@ -148,7 +150,7 @@ def promise(self): def check(self, before: LogicalSample, context: OptimizerContext): # System supports sample pushdown only while reading video data lget: LogicalGet = before.children[0] - if lget.dataset_metadata.is_video: + if lget.dataset_metadata.table_type == TableType.VIDEO_DATA: return True return False diff --git a/eva/parser/create_statement.py b/eva/parser/create_statement.py index b24f6cc6a..08ab7ea60 100644 --- a/eva/parser/create_statement.py +++ b/eva/parser/create_statement.py @@ -14,7 +14,7 @@ # limitations under the License. from typing import List -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType from eva.parser.statement import AbstractStatement from eva.parser.table_ref import TableRef from eva.parser.types import StatementType diff --git a/eva/parser/evaql/evaql_parser.g4 b/eva/parser/evaql/evaql_parser.g4 index eb425e789..ff368df5d 100644 --- a/eva/parser/evaql/evaql_parser.g4 +++ b/eva/parser/evaql/evaql_parser.g4 @@ -182,33 +182,31 @@ updateStatement loadStatement : LOAD - FILE fileName + fileFormat + stringLiteral INTO tableName ( ('(' columns=uidList ')') )? - fileOptions? ; -fileOptions - : FORMAT fileFormat=(CSV|VIDEO|IMAGE) +fileFormat + : (CSV|VIDEO|IMAGE) ; + uploadStatement : UPLOAD - PATH fileName + fileFormat + PATH stringLiteral BLOB videoBlob INTO tableName ( ('(' columns=uidList ')') )? - (fileOptions)? ; -fileName - : stringLiteral - ; videoBlob : stringLiteral diff --git a/eva/parser/load_statement.py b/eva/parser/load_statement.py index 0fbb5cf97..04189b097 100644 --- a/eva/parser/load_statement.py +++ b/eva/parser/load_statement.py @@ -44,7 +44,7 @@ def __init__( self._file_options = file_options def __str__(self) -> str: - print_str = "LOAD FILE {} INTO {}({}) WITH {}".format( + print_str = "LOAD {} INTO {}({}) WITH {}".format( self._path.name, self._table_ref, self._column_list, self._file_options ) return print_str diff --git a/eva/parser/parser_visitor/_create_statements.py b/eva/parser/parser_visitor/_create_statements.py index 187795221..2bc479359 100644 --- a/eva/parser/parser_visitor/_create_statements.py +++ b/eva/parser/parser_visitor/_create_statements.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from eva.catalog.column_type import ColumnType, Dimension, NdArrayType +from eva.catalog.catalog_type import ColumnType, Dimension, NdArrayType from eva.parser.create_mat_view_statement import CreateMaterializedViewStatement from eva.parser.create_statement import ( ColConstraintInfo, diff --git a/eva/parser/parser_visitor/_expressions.py b/eva/parser/parser_visitor/_expressions.py index 283d52d93..0f280357d 100644 --- a/eva/parser/parser_visitor/_expressions.py +++ b/eva/parser/parser_visitor/_expressions.py @@ -16,7 +16,7 @@ import numpy as np -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.expression.abstract_expression import ExpressionType from eva.expression.comparison_expression import ComparisonExpression from eva.expression.constant_value_expression import ConstantValueExpression diff --git a/eva/parser/parser_visitor/_load_statement.py b/eva/parser/parser_visitor/_load_statement.py index d95ee9165..daa98dd39 100644 --- a/eva/parser/parser_visitor/_load_statement.py +++ b/eva/parser/parser_visitor/_load_statement.py @@ -21,17 +21,17 @@ class Load(evaql_parserVisitor): def visitLoadStatement(self, ctx: evaql_parser.LoadStatementContext): - file_path = self.visit(ctx.fileName()).value + file_path = self.visit(ctx.stringLiteral()).value table = TableRef(self.visit(ctx.tableName())) # Set default for file_format as Video file_format = FileFormatType.VIDEO + if ctx.fileFormat(): + file_format = self.visit(ctx.fileFormat()) + file_options = {} file_options["file_format"] = file_format - if ctx.fileOptions(): - file_options = self.visit(ctx.fileOptions()) - # set default for column_list as None column_list = None if ctx.uidList(): @@ -40,15 +40,12 @@ def visitLoadStatement(self, ctx: evaql_parser.LoadStatementContext): stmt = LoadDataStatement(table, file_path, column_list, file_options) return stmt - def visitFileOptions(self, ctx: evaql_parser.FileOptionsContext): + def visitFileFormat(self, ctx: evaql_parser.FileFormatContext): file_format = FileFormatType.VIDEO # Check the file format if ctx.CSV() is not None: file_format = FileFormatType.CSV elif ctx.IMAGE() is not None: file_format = FileFormatType.IMAGE - # parse and add more file options in future - file_options = {} - file_options["file_format"] = file_format - return file_options + return file_format diff --git a/eva/parser/types.py b/eva/parser/types.py index bfcd8a4e4..cd38e18da 100644 --- a/eva/parser/types.py +++ b/eva/parser/types.py @@ -64,6 +64,7 @@ class FileFormatType(Enum): """ Manages enums for all order by sort types """ + IMAGE = auto() VIDEO = auto() CSV = auto() diff --git a/eva/readers/opencv_image_reader.py b/eva/readers/opencv_image_reader.py index 2429ed256..6b261d3a5 100644 --- a/eva/readers/opencv_image_reader.py +++ b/eva/readers/opencv_image_reader.py @@ -1,5 +1,5 @@ # coding=utf-8 -# Copyright 2018-2020 EVA +# Copyright 2018-2022 EVA # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from PIL import Image +from typing import Dict, Iterator + import cv2 -from typing import Iterator, Dict from eva.readers.abstract_reader import AbstractReader + # from eva.utils.logging_manager import LoggingLevel # from eva.utils.logging_manager import LoggingManager # from eva.expression.expression_utils import parse_predicate diff --git a/eva/storage/image_storage_engine.py b/eva/storage/image_storage_engine.py index 9d5b8c579..649ac89a6 100644 --- a/eva/storage/image_storage_engine.py +++ b/eva/storage/image_storage_engine.py @@ -1,5 +1,5 @@ # coding=utf-8 -# Copyright 2018-2020 EVA +# Copyright 2018-2022 EVA # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,18 +12,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Iterator -from pathlib import Path -import struct +import io import shutil -import cv2, io -import numpy as np +import struct +from pathlib import Path +from typing import Iterator from eva.catalog.models.df_metadata import DataFrameMetadata -from eva.storage.abstract_storage_engine import AbstractStorageEngine -from eva.readers.opencv_image_reader import CVImageReader -from eva.models.storage.batch import Batch from eva.configuration.configuration_manager import ConfigurationManager +from eva.models.storage.batch import Batch +from eva.readers.opencv_image_reader import CVImageReader +from eva.storage.abstract_storage_engine import AbstractStorageEngine # from eva.utils.logging_manager import LoggingManager # from eva.utils.logging_manager import LoggingLevel @@ -106,9 +105,7 @@ def _create_metadata(self, dir_path, overwrite=False): metadata_file = Path(dir_path) / self.metadata if metadata_file.exists(): if not overwrite: - print( - "Trying to overwrite metadata. Please set overwrite = True" - ) + print("Trying to overwrite metadata. Please set overwrite = True") return # with open(metadata_file, "wb") as f: diff --git a/eva/storage/sqlite_storage_engine.py b/eva/storage/sqlite_storage_engine.py index 7cb48a943..a35d53f8a 100644 --- a/eva/storage/sqlite_storage_engine.py +++ b/eva/storage/sqlite_storage_engine.py @@ -17,7 +17,7 @@ import numpy as np import pandas as pd -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.catalog.models.base_model import BaseModel from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata diff --git a/eva/storage/storage_engine.py b/eva/storage/storage_engine.py index db176e6de..f5c0f13c8 100644 --- a/eva/storage/storage_engine.py +++ b/eva/storage/storage_engine.py @@ -12,15 +12,29 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from eva.catalog.catalog_type import TableType +from eva.catalog.models.df_metadata import DataFrameMetadata from eva.configuration.configuration_manager import ConfigurationManager +from eva.storage.abstract_storage_engine import AbstractStorageEngine from eva.utils.generic_utils import str_to_class -StorageEngine = str_to_class( - ConfigurationManager().get_value("storage", "engine") -)() -VideoStorageEngine = str_to_class( - ConfigurationManager().get_value("storage", "video_engine") -)() -ImageStorageEngine = str_to_class( - ConfigurationManager().get_value("storage", "image_engine") -)() + +class StorageEngine: + storages = { + TableType.STRUCTURAL_DATA: str_to_class( + ConfigurationManager().get_value("storage", "engine") + )(), + TableType.VIDEO_DATA: str_to_class( + ConfigurationManager().get_value("storage", "video_engine") + )(), + TableType.IMAGE_DATA: str_to_class( + ConfigurationManager().get_value("storage", "image_engine") + )(), + } + + @classmethod + def factory(cls, table_metadata: DataFrameMetadata) -> AbstractStorageEngine: + if table_metadata.table_type in cls.storages: + return cls.storages[table_metadata.table_type] + + raise RuntimeError(f"Invalid table type {table_metadata.table_type}") diff --git a/test/binder/test_binder_utils.py b/test/binder/test_binder_utils.py index 0c1c2c65d..cdb15a8ad 100644 --- a/test/binder/test_binder_utils.py +++ b/test/binder/test_binder_utils.py @@ -22,7 +22,7 @@ create_video_metadata, handle_if_not_exists, ) -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType class BinderUtilsTest(unittest.TestCase): @@ -74,7 +74,11 @@ def test_create_video_metadata(self, m_gfp, m_ccm, m_cci, m_cd, m_cm): m_cci.assert_called_once_with(unique=True) m_cd.assert_has_calls(calls) catalog_ins.create_metadata.assert_called_with( - name, uri, "col_metadata", identifier_column="id", is_video=True + name, + uri, + "col_metadata", + identifier_column="id", + table_type=TableType.VIDEO_DATA, ) self.assertEqual(actual, expected) diff --git a/test/binder/test_statement_binder.py b/test/binder/test_statement_binder.py index dabc71ad7..5b5f2b4e0 100644 --- a/test/binder/test_statement_binder.py +++ b/test/binder/test_statement_binder.py @@ -228,7 +228,7 @@ def test_bind_select_statement_union_starts_new_context(self, mock_ctx): binder._bind_select_statement(select_statement) self.assertEqual(mock_ctx.call_count, 1) - @patch("eva.binder.statement_binder.create_video_metadata") + @patch("eva.binder.statement_binder.create_multimedia_metadata") @patch("eva.binder.statement_binder.TupleValueExpression") def test_bind_load_video_statement(self, mock_tve, mock_create): load_statement = MagicMock() @@ -244,19 +244,25 @@ def test_bind_load_video_statement(self, mock_tve, mock_create): mock_tve.return_value = tve_return_value = MagicMock() with patch.object(StatementBinder, "bind") as mock_binder: - with patch.object(Path, "exists") as mock_exists: - mock_exists.return_value = True - binder = StatementBinder(StatementBinderContext()) - binder._bind_load_and_upload_data_statement(load_statement) - mock_binder.assert_any_call(load_statement.table_ref) - mock_create.assert_any_call("table_name") - mock_tve.assert_called_with( - col_name=column.name, - table_alias="table_alias", - col_object=column, - ) - mock_binder.assert_any_call(tve_return_value) - self.assertEqual(load_statement.column_list, [tve_return_value]) + with patch.object( + CatalogManager, "check_table_exists" + ) as mock_catalog_check: + with patch.object(Path, "exists") as mock_exists: + mock_catalog_check.return_value = False + mock_exists.return_value = True + binder = StatementBinder(StatementBinderContext()) + binder._bind_load_and_upload_data_statement(load_statement) + mock_binder.assert_any_call(load_statement.table_ref) + mock_create.assert_any_call( + "table_name", load_statement.file_options["file_format"] + ) + mock_tve.assert_called_with( + col_name=column.name, + table_alias="table_alias", + col_object=column, + ) + mock_binder.assert_any_call(tve_return_value) + self.assertEqual(load_statement.column_list, [tve_return_value]) @patch("eva.binder.statement_binder.create_video_metadata") @patch("eva.binder.statement_binder.TupleValueExpression") @@ -285,7 +291,7 @@ def test_bind_load_data_raises(self, mock_tve, mock_create): binder = StatementBinder(StatementBinderContext()) binder._bind_load_and_upload_data_statement(load_statement) self.assertEqual( - str(cm.exception), f"Video file {file_path} does not exist." + str(cm.exception), f"Path {file_path} does not exist." ) def test_bind_unknown_object(self): diff --git a/test/catalog/models/test_models.py b/test/catalog/models/test_models.py index 9430a4acc..bd9a0f17a 100644 --- a/test/catalog/models/test_models.py +++ b/test/catalog/models/test_models.py @@ -14,7 +14,7 @@ # limitations under the License. import unittest -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.df_schema import DataFrameSchema from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata @@ -64,7 +64,9 @@ def test_df_equality(self): self.assertNotEqual(df_col, df_col1) def test_df_metadata(self): - df_metadata = DataFrameMetadata("name", "eva_dataset") + df_metadata = DataFrameMetadata( + "name", "eva_dataset", table_type=TableType.VIDEO_DATA + ) column_1 = DataFrameColumn("frame_id", ColumnType.INTEGER, False) column_2 = DataFrameColumn("frame_label", ColumnType.INTEGER, False) col_list = [column_1, column_2] @@ -76,22 +78,29 @@ def test_df_metadata(self): self.assertEqual(df_metadata.id, None) self.assertEqual(df_metadata.identifier_column, "id") self.assertEqual(df_metadata.schema, schema) + self.assertEqual(df_metadata.table_type, TableType.VIDEO_DATA) def test_df_metadata_equality(self): - df_metadata = DataFrameMetadata("name", "eva_dataset") + df_metadata = DataFrameMetadata( + "name", "eva_dataset", table_type=TableType.VIDEO_DATA + ) column_1 = DataFrameColumn("frame_id", ColumnType.INTEGER, False) column_2 = DataFrameColumn("frame_label", ColumnType.INTEGER, False) col_list = [column_1, column_2] df_metadata.schema = col_list self.assertEqual(df_metadata, df_metadata) - df_metadata1 = DataFrameMetadata("name2", "eva_dataset") + df_metadata1 = DataFrameMetadata( + "name2", "eva_dataset", table_type=TableType.VIDEO_DATA + ) column_1 = DataFrameColumn("frame_id", ColumnType.INTEGER, False) column_2 = DataFrameColumn("frame_label", ColumnType.INTEGER, False) col_list = [column_1, column_2] df_metadata1.schema = col_list self.assertNotEqual(df_metadata, df_metadata1) - df_metadata2 = DataFrameMetadata("name2", "eva_dataset") + df_metadata2 = DataFrameMetadata( + "name2", "eva_dataset", table_type=TableType.IMAGE_DATA + ) df_metadata2.schema = col_list[1:] self.assertNotEqual(df_metadata1, df_metadata2) diff --git a/test/catalog/services/test_dataset_service.py b/test/catalog/services/test_dataset_service.py index 356e07dff..d90118bb2 100644 --- a/test/catalog/services/test_dataset_service.py +++ b/test/catalog/services/test_dataset_service.py @@ -16,6 +16,7 @@ from mock import patch +from eva.catalog.catalog_type import TableType from eva.catalog.services.df_service import DatasetService DATASET_ID = 123 @@ -24,18 +25,24 @@ DATABASE_NAME = "test" IDENTIFIER = "data_id" DATASET_NEW_NAME = "new_name" +TABLE_TYPE = TableType.STRUCTURAL_DATA class DatasetServiceTest(unittest.TestCase): @patch("eva.catalog.services.df_service.DataFrameMetadata") def test_create_dataset_should_create_model(self, mocked): service = DatasetService() - service.create_dataset(DATASET_NAME, DATASET_URL, identifier_id=IDENTIFIER) + service.create_dataset( + DATASET_NAME, + DATASET_URL, + table_type=TABLE_TYPE, + identifier_id=IDENTIFIER, + ) mocked.assert_called_with( name=DATASET_NAME, file_url=DATASET_URL, identifier_id=IDENTIFIER, - is_video=False, + table_type=TABLE_TYPE, ) mocked.return_value.save.assert_called_once() diff --git a/test/catalog/test_catalog_manager.py b/test/catalog/test_catalog_manager.py index 931d3c8a5..24dff304c 100644 --- a/test/catalog/test_catalog_manager.py +++ b/test/catalog/test_catalog_manager.py @@ -18,7 +18,7 @@ from mock import MagicMock from eva.catalog.catalog_manager import CatalogManager -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.udf import UdfMetadata @@ -68,7 +68,7 @@ def test_create_metadata_should_create_dataset_and_columns( columns = [(DataFrameColumn("c1", ColumnType.INTEGER))] actual = catalog.create_metadata(dataset_name, file_url, columns) ds_mock.return_value.create_dataset.assert_called_with( - dataset_name, file_url, identifier_id="id", is_video=False + dataset_name, file_url, identifier_id="id", table_type=TableType.VIDEO_DATA ) for column in columns: column.metadata_id = ds_mock.return_value.create_dataset.return_value.id diff --git a/test/catalog/test_column_type.py b/test/catalog/test_column_type.py index 5a606b55a..52dd25e20 100644 --- a/test/catalog/test_column_type.py +++ b/test/catalog/test_column_type.py @@ -17,7 +17,7 @@ import numpy as np -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType class ColumnTypeTests(unittest.TestCase): diff --git a/test/catalog/test_schema.py b/test/catalog/test_schema.py index afad977b6..e8d2c2d03 100644 --- a/test/catalog/test_schema.py +++ b/test/catalog/test_schema.py @@ -14,7 +14,7 @@ # limitations under the License. import unittest -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType from eva.catalog.df_schema import DataFrameSchema from eva.catalog.models.df_column import DataFrameColumn diff --git a/test/executor/test_load_executor.py b/test/executor/test_load_executor.py index 8f8e4ab79..2308abcf1 100644 --- a/test/executor/test_load_executor.py +++ b/test/executor/test_load_executor.py @@ -27,11 +27,8 @@ class LoadExecutorTest(unittest.TestCase): - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_call_opencv_reader_and_storage_engine( - self, write_mock, create_mock - ): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_call_opencv_reader_and_storage_engine(self, factory_mock): file_path = "video" table_metainfo = "info" batch_mem_size = 3000 @@ -52,8 +49,10 @@ def test_should_call_opencv_reader_and_storage_engine( with patch.object(Path, "exists") as mock_exists: mock_exists.return_value = True batch = next(load_executor.exec()) - create_mock.assert_called_once_with(table_metainfo, if_not_exists=True) - write_mock.assert_called_once_with( + factory_mock.return_value.create.assert_called_once_with( + table_metainfo, if_not_exists=True + ) + factory_mock.return_value.write.assert_called_once_with( table_metainfo, Batch(pd.DataFrame([{"video_file_path": file_path}])) ) expected = Batch( @@ -61,9 +60,8 @@ def test_should_call_opencv_reader_and_storage_engine( ) self.assertEqual(batch, expected) - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_search_in_upload_directory(self, write_mock, create_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_search_in_upload_directory(self, factory_mock): self.upload_path = Path( ConfigurationManager().get_value("storage", "upload_dir") ) @@ -88,8 +86,10 @@ def test_should_search_in_upload_directory(self, write_mock, create_mock): mock_exists.side_effect = [False, True] batch = next(load_executor.exec()) location = self.upload_path / file_path - create_mock.assert_called_once_with(table_metainfo, if_not_exists=True) - write_mock.assert_called_once_with( + factory_mock.return_value.create.assert_called_once_with( + table_metainfo, if_not_exists=True + ) + factory_mock.return_value.write.assert_called_once_with( table_metainfo, Batch(pd.DataFrame([{"video_file_path": str(location)}])), ) @@ -98,9 +98,8 @@ def test_should_search_in_upload_directory(self, write_mock, create_mock): ) self.assertEqual(batch, expected) - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_fail_to_find_file(self, write_mock, create_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_fail_to_find_file(self, factory_mock): file_path = "video" table_metainfo = "info" batch_mem_size = 3000 @@ -125,8 +124,8 @@ def test_should_fail_to_find_file(self, write_mock, create_mock): with self.assertRaises(RuntimeError): next(load_executor.exec()) - @patch("eva.storage.storage_engine.StorageEngine.write") - def test_should_call_csv_reader_and_storage_engine(self, write_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_call_csv_reader_and_storage_engine(self, factory_mock): batch_frames = [list(range(5))] * 2 # creates a dummy.csv @@ -156,7 +155,7 @@ def test_should_call_csv_reader_and_storage_engine(self, write_mock): load_executor = LoadDataExecutor(plan) batch = next(load_executor.exec()) - write_mock.has_calls( + factory_mock.return_value.write.has_calls( call(table_metainfo, batch_frames[0]), call(table_metainfo, batch_frames[1]), ) diff --git a/test/executor/test_upload_executor.py b/test/executor/test_upload_executor.py index 136ca6d78..f20758308 100644 --- a/test/executor/test_upload_executor.py +++ b/test/executor/test_upload_executor.py @@ -27,11 +27,8 @@ class UploadExecutorTest(unittest.TestCase): - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_call_opencv_reader_and_storage_engine( - self, write_mock, create_mock - ): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_call_opencv_reader_and_storage_engine(self, factory_mock): file_path = "video" video_blob = "b'AAAA'" table_metainfo = "info" @@ -54,8 +51,10 @@ def test_should_call_opencv_reader_and_storage_engine( with patch.object(Path, "exists") as mock_exists: mock_exists.return_value = True batch = next(upload_executor.exec()) - create_mock.assert_called_once_with(table_metainfo, if_not_exists=True) - write_mock.assert_called_once_with( + factory_mock.return_value.create.assert_called_once_with( + table_metainfo, if_not_exists=True + ) + factory_mock.return_value.write.assert_called_once_with( table_metainfo, Batch(pd.DataFrame([{"video_file_path": file_path}])) ) location = file_path @@ -65,9 +64,8 @@ def test_should_call_opencv_reader_and_storage_engine( self.assertEqual(batch, expected) - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_search_in_upload_directory(self, write_mock, create_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_search_in_upload_directory(self, factory_mock): self.upload_dir = Path( ConfigurationManager().get_value("storage", "upload_dir") ) @@ -95,8 +93,10 @@ def test_should_search_in_upload_directory(self, write_mock, create_mock): batch = next(upload_executor.exec()) location = self.upload_dir / file_path - create_mock.assert_called_once_with(table_metainfo, if_not_exists=True) - write_mock.assert_called_once_with( + factory_mock.return_value.create.assert_called_once_with( + table_metainfo, if_not_exists=True + ) + factory_mock.return_value.write.assert_called_once_with( table_metainfo, Batch(pd.DataFrame([{"video_file_path": str(location)}])), ) @@ -105,9 +105,8 @@ def test_should_search_in_upload_directory(self, write_mock, create_mock): ) self.assertEqual(batch, expected) - @patch("eva.executor.load_video_executor.VideoStorageEngine.create") - @patch("eva.executor.load_video_executor.VideoStorageEngine.write") - def test_should_fail_to_find_file(self, write_mock, create_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_fail_to_find_file(self, factory_mock): file_path = "video" video_blob = "b'AAAA'" table_metainfo = "info" @@ -134,8 +133,8 @@ def test_should_fail_to_find_file(self, write_mock, create_mock): with self.assertRaises(RuntimeError): next(upload_executor.exec()) - @patch("eva.storage.storage_engine.StorageEngine.write") - def test_should_call_csv_reader_and_storage_engine(self, write_mock): + @patch("eva.executor.load_video_executor.StorageEngine.factory") + def test_should_call_csv_reader_and_storage_engine(self, factory_mock): batch_frames = [list(range(5))] * 2 # creates a dummy.csv @@ -166,7 +165,7 @@ def test_should_call_csv_reader_and_storage_engine(self, write_mock): upload_executor = UploadExecutor(plan) batch = next(upload_executor.exec()) - write_mock.has_calls( + factory_mock.return_value.write.has_calls( call(table_metainfo, batch_frames[0]), call(table_metainfo, batch_frames[1]) ) diff --git a/test/expression/test_comparison.py b/test/expression/test_comparison.py index 0ea46f387..db5b55721 100644 --- a/test/expression/test_comparison.py +++ b/test/expression/test_comparison.py @@ -14,7 +14,7 @@ # limitations under the License. import unittest -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.expression.abstract_expression import ExpressionType from eva.expression.comparison_expression import ComparisonExpression from eva.expression.constant_value_expression import ConstantValueExpression diff --git a/test/integration_tests/test_array_count.py b/test/integration_tests/test_array_count.py index e40f322f7..1a3660ddb 100644 --- a/test/integration_tests/test_array_count.py +++ b/test/integration_tests/test_array_count.py @@ -26,7 +26,7 @@ class ArrayCountTests(unittest.TestCase): def setUp(self): CatalogManager().reset() create_sample_video(NUM_FRAMES) - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) load_inbuilt_udfs() diff --git a/test/integration_tests/test_drop_executor.py b/test/integration_tests/test_drop_executor.py index 5fb4058ce..54ff786c0 100644 --- a/test/integration_tests/test_drop_executor.py +++ b/test/integration_tests/test_drop_executor.py @@ -32,7 +32,7 @@ def tearDown(self): # integration test def test_should_drop_table(self): catalog_manager = CatalogManager() - query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(query) metadata_obj = catalog_manager.get_dataset_metadata(None, "MyVideo") diff --git a/test/integration_tests/test_explain_executor.py b/test/integration_tests/test_explain_executor.py index c0f31fac4..dce235ec4 100644 --- a/test/integration_tests/test_explain_executor.py +++ b/test/integration_tests/test_explain_executor.py @@ -26,7 +26,7 @@ class ExplainExecutorTest(unittest.TestCase): def setUpClass(cls): CatalogManager().reset() create_sample_video(NUM_FRAMES) - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) load_inbuilt_udfs() cls.table1 = create_table("table1", 100, 3) diff --git a/test/integration_tests/test_insert_executor.py b/test/integration_tests/test_insert_executor.py index ed5e117a4..fe8724428 100644 --- a/test/integration_tests/test_insert_executor.py +++ b/test/integration_tests/test_insert_executor.py @@ -33,7 +33,7 @@ def tearDown(self): # integration test @unittest.skip("Not supported in current version") def test_should_load_video_in_table(self): - query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(query) insert_query = """ INSERT INTO MyVideo (id, data) VALUES (40, diff --git a/test/integration_tests/test_load_executor.py b/test/integration_tests/test_load_executor.py index 7a66a1342..793f874fc 100644 --- a/test/integration_tests/test_load_executor.py +++ b/test/integration_tests/test_load_executor.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. import unittest -from eva.configuration.constants import EVA_INSTALLATION_DIR, EVA_ROOT_DIR from test.util import ( create_dummy_batches, create_dummy_csv_batches, @@ -23,6 +22,7 @@ ) from eva.catalog.catalog_manager import CatalogManager +from eva.configuration.constants import EVA_ROOT_DIR from eva.server.command_handler import execute_query_fetch_all @@ -39,8 +39,7 @@ def tearDown(self): # integration test for video def test_should_load_video_in_table(self): - query = """LOAD FILE 'dummy.avi' INTO MyVideo - WITH FORMAT VIDEO;""" + query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(query) select_query = """SELECT name, id, data FROM MyVideo;""" @@ -57,17 +56,15 @@ def test_should_load_video_in_table(self): def test_should_load_images_in_table(self): path = f"{EVA_ROOT_DIR}/data/mnist/images/*.jpg" - query = f"""LOAD FILE "{path}" INTO MyImages - WITH FORMAT IMAGE;""" + query = f"""LOAD IMAGE "{path}" INTO MyImages;""" execute_query_fetch_all(query) select_query = """SELECT name, data FROM MyImages;""" actual_batch = execute_query_fetch_all(select_query) self.assertEqual(len(actual_batch), 20) - - file_names = actual_batch.project(['myimages.name']).frames - + + file_names = actual_batch.project(["myimages.name"]).frames # integration tests for csv def test_should_load_csv_in_table(self): @@ -89,8 +86,7 @@ def test_should_load_csv_in_table(self): execute_query_fetch_all(create_table_query) # load the CSV - load_query = """LOAD FILE 'dummy.csv' INTO MyVideoCSV - WITH FORMAT CSV;""" + load_query = """LOAD CSV 'dummy.csv' INTO MyVideoCSV;""" execute_query_fetch_all(load_query) # execute a select query @@ -126,8 +122,7 @@ def test_should_load_csv_with_columns_in_table(self): execute_query_fetch_all(create_table_query) # load the CSV - load_query = """LOAD FILE 'dummy.csv' INTO MyVideoCSV (id, frame_id, video_id, dataset_name) - WITH FORMAT CSV;""" + load_query = """LOAD CSV 'dummy.csv' INTO MyVideoCSV (id, frame_id, video_id, dataset_name);""" execute_query_fetch_all(load_query) # execute a select query @@ -139,9 +134,7 @@ def test_should_load_csv_with_columns_in_table(self): # assert the batches are equal select_columns = ["id", "frame_id", "video_id", "dataset_name"] - expected_batch = create_dummy_csv_batches( - target_columns=select_columns - ) + expected_batch = create_dummy_csv_batches(target_columns=select_columns) expected_batch.modify_column_alias("myvideocsv") self.assertEqual(actual_batch, expected_batch) diff --git a/test/integration_tests/test_mat_executor.py b/test/integration_tests/test_mat_executor.py index 733f52766..61f55b928 100644 --- a/test/integration_tests/test_mat_executor.py +++ b/test/integration_tests/test_mat_executor.py @@ -38,9 +38,9 @@ def setUpClass(cls): CatalogManager().reset() create_sample_video() copy_sample_videos_to_upload_dir() - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) - query = """LOAD FILE 'ua_detrac.mp4' + query = """LOAD VIDEO 'ua_detrac.mp4' INTO UATRAC;""" execute_query_fetch_all(query) load_inbuilt_udfs() diff --git a/test/integration_tests/test_pytorch.py b/test/integration_tests/test_pytorch.py index 1dbc103bb..08bef9d99 100644 --- a/test/integration_tests/test_pytorch.py +++ b/test/integration_tests/test_pytorch.py @@ -28,13 +28,13 @@ class PytorchTest(unittest.TestCase): def setUpClass(cls): CatalogManager().reset() copy_sample_videos_to_upload_dir() - query = """LOAD FILE 'ua_detrac.mp4' + query = """LOAD VIDEO 'ua_detrac.mp4' INTO MyVideo;""" execute_query_fetch_all(query) - query = """LOAD FILE 'mnist.mp4' + query = """LOAD VIDEO 'mnist.mp4' INTO MNIST;""" execute_query_fetch_all(query) - query = """LOAD FILE 'actions.mp4' + query = """LOAD VIDEO 'actions.mp4' INTO Actions;""" execute_query_fetch_all(query) load_inbuilt_udfs() diff --git a/test/integration_tests/test_rename_executor.py b/test/integration_tests/test_rename_executor.py index 8bb8ba24e..dcbdc80bb 100644 --- a/test/integration_tests/test_rename_executor.py +++ b/test/integration_tests/test_rename_executor.py @@ -31,7 +31,7 @@ def tearDown(self): # integration test def test_should_rename_table(self): catalog_manager = CatalogManager() - query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(query) self.assertTrue( diff --git a/test/integration_tests/test_select_executor.py b/test/integration_tests/test_select_executor.py index 04df7f99a..8d6c4445b 100644 --- a/test/integration_tests/test_select_executor.py +++ b/test/integration_tests/test_select_executor.py @@ -40,7 +40,7 @@ class SelectExecutorTest(unittest.TestCase): def setUpClass(cls): CatalogManager().reset() create_sample_video(NUM_FRAMES) - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) load_inbuilt_udfs() cls.table1 = create_table("table1", 100, 3) @@ -140,7 +140,7 @@ def test_select_star_in_lateral_join(self): self.assertEqual(actual_batch.frames.columns, ["myvideo.id"]) def test_should_load_and_select_real_video_in_table(self): - query = """LOAD FILE 'data/mnist/mnist.mp4' + query = """LOAD VIDEO 'data/mnist/mnist.mp4' INTO MNIST;""" execute_query_fetch_all(query) diff --git a/test/integration_tests/test_udf_executor.py b/test/integration_tests/test_udf_executor.py index bfdeabfbe..465d88bc7 100644 --- a/test/integration_tests/test_udf_executor.py +++ b/test/integration_tests/test_udf_executor.py @@ -35,7 +35,7 @@ class UDFExecutorTest(unittest.TestCase): def setUp(self): CatalogManager().reset() create_sample_video(NUM_FRAMES) - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) create_udf_query = """CREATE UDF DummyObjectDetector diff --git a/test/optimizer/rules/test_rules.py b/test/optimizer/rules/test_rules.py index 70971fece..2270471a2 100644 --- a/test/optimizer/rules/test_rules.py +++ b/test/optimizer/rules/test_rules.py @@ -75,7 +75,7 @@ def setUpClass(cls): # reset the catalog manager before running each test CatalogManager().reset() create_sample_video() - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) load_inbuilt_udfs() diff --git a/test/optimizer/test_cascade_optimizer.py b/test/optimizer/test_cascade_optimizer.py index 8d5d3efdf..6851b80a9 100644 --- a/test/optimizer/test_cascade_optimizer.py +++ b/test/optimizer/test_cascade_optimizer.py @@ -31,7 +31,7 @@ def tearDown(self): file_remove("dummy.avi") def test_logical_to_physical_udf(self): - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(load_query) create_udf_query = """CREATE UDF DummyObjectDetector diff --git a/test/optimizer/test_optimizer_utils.py b/test/optimizer/test_optimizer_utils.py index f6caee0c7..27e332669 100644 --- a/test/optimizer/test_optimizer_utils.py +++ b/test/optimizer/test_optimizer_utils.py @@ -14,7 +14,7 @@ # limitations under the License. import unittest -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType from eva.optimizer.optimizer_utils import column_definition_to_udf_io from eva.parser.create_statement import ColumnDefinition diff --git a/test/parser/test_parser.py b/test/parser/test_parser.py index ba6f6a57d..819d15746 100644 --- a/test/parser/test_parser.py +++ b/test/parser/test_parser.py @@ -15,7 +15,7 @@ import unittest from pathlib import Path -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType from eva.expression.abstract_expression import ExpressionType from eva.expression.comparison_expression import ComparisonExpression from eva.expression.constant_value_expression import ConstantValueExpression @@ -484,8 +484,8 @@ def test_create_udf_statement(self): def test_load_video_data_statement(self): parser = Parser() - load_data_query = """LOAD FILE 'data/video.mp4' - INTO MyVideo WITH FORMAT VIDEO;""" + load_data_query = """LOAD VIDEO 'data/video.mp4' + INTO MyVideo""" file_options = {} file_options["file_format"] = FileFormatType.VIDEO column_list = None @@ -505,10 +505,9 @@ def test_load_video_data_statement(self): def test_load_csv_data_statement(self): parser = Parser() - load_data_query = """LOAD FILE 'data/meta.csv' + load_data_query = """LOAD CSV 'data/meta.csv' INTO - MyMeta (id, frame_id, video_id, label) - WITH FORMAT CSV;""" + MyMeta (id, frame_id, video_id, label);""" file_options = {} file_options["file_format"] = FileFormatType.CSV expected_stmt = LoadDataStatement( diff --git a/test/parser/test_parser_visitor.py b/test/parser/test_parser_visitor.py index d0e58ba08..d98c3890d 100644 --- a/test/parser/test_parser_visitor.py +++ b/test/parser/test_parser_visitor.py @@ -20,7 +20,7 @@ import pandas as pd from antlr4 import TerminalNode -from eva.catalog.column_type import NdArrayType +from eva.catalog.catalog_type import NdArrayType from eva.expression.abstract_expression import ExpressionType from eva.models.storage.batch import Batch from eva.parser.evaql.evaql_parser import evaql_parser diff --git a/test/planner/test_plan.py b/test/planner/test_plan.py index 370753222..66b30d5d5 100644 --- a/test/planner/test_plan.py +++ b/test/planner/test_plan.py @@ -15,7 +15,7 @@ import unittest from eva.catalog.catalog_manager import CatalogManager -from eva.catalog.column_type import ColumnType +from eva.catalog.catalog_type import ColumnType from eva.catalog.models.df_column import DataFrameColumn from eva.parser.table_ref import TableInfo, TableRef from eva.parser.types import FileFormatType diff --git a/test/storage/test_sqlite_storage_engine.py b/test/storage/test_sqlite_storage_engine.py index 88dbf9087..79def027d 100644 --- a/test/storage/test_sqlite_storage_engine.py +++ b/test/storage/test_sqlite_storage_engine.py @@ -16,7 +16,7 @@ import unittest from test.util import create_dummy_batches -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata from eva.storage.sqlite_storage_engine import SQLStorageEngine @@ -28,7 +28,9 @@ def __init__(self, *args, **kwargs): self.table = None def create_sample_table(self): - table_info = DataFrameMetadata("dataset", "dataset") + table_info = DataFrameMetadata( + "dataset", "dataset", table_type=TableType.VIDEO_DATA + ) column_0 = DataFrameColumn("name", ColumnType.TEXT, False) column_1 = DataFrameColumn("id", ColumnType.INTEGER, False) column_2 = DataFrameColumn( diff --git a/test/storage/test_video_storage.py b/test/storage/test_video_storage.py index 14cb962dd..018acc403 100644 --- a/test/storage/test_video_storage.py +++ b/test/storage/test_video_storage.py @@ -19,11 +19,11 @@ import mock from mock import mock_open -from eva.catalog.column_type import ColumnType, NdArrayType +from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata from eva.configuration.configuration_manager import ConfigurationManager -from eva.storage.storage_engine import VideoStorageEngine +from eva.storage.storage_engine import StorageEngine class VideoStorageEngineTest(unittest.TestCase): @@ -32,7 +32,9 @@ def __init__(self, *args, **kwargs): self.table = None def create_sample_table(self): - table_info = DataFrameMetadata("dataset", "dataset") + table_info = DataFrameMetadata( + "dataset", "dataset", table_type=TableType.VIDEO_DATA + ) column_1 = DataFrameColumn("id", ColumnType.INTEGER, False) column_2 = DataFrameColumn( "data", ColumnType.NDARRAY, False, NdArrayType.UINT8, [2, 2, 3] @@ -41,7 +43,9 @@ def create_sample_table(self): return table_info def setUp(self): - self.video_engine = VideoStorageEngine + mock = MagicMock() + mock.table_type = TableType.VIDEO_DATA + self.video_engine = StorageEngine.factory(mock) self.table = self.create_sample_table() self.curr_version = ConfigurationManager().get_value( "storage", "video_engine_version" diff --git a/test/util.py b/test/util.py index 848de7b00..ee64e940c 100644 --- a/test/util.py +++ b/test/util.py @@ -219,8 +219,7 @@ def create_table(table_name, num_rows, num_columns): columns = ["a{}".format(i) for i in range(num_columns)] df = create_csv(num_rows, columns) # load the CSV - load_query = """LOAD FILE 'dummy.csv' INTO {} - WITH FORMAT CSV;""".format( + load_query = """LOAD CSV 'dummy.csv' INTO {};""".format( table_name ) execute_query_fetch_all(load_query) diff --git a/test/utils/test_timer.py b/test/utils/test_timer.py index 3676eba60..a1c793555 100644 --- a/test/utils/test_timer.py +++ b/test/utils/test_timer.py @@ -42,7 +42,7 @@ def test_timer(self): def test_timer_with_query(self): CatalogManager().reset() create_sample_video(NUM_FRAMES) - load_query = """LOAD FILE 'dummy.avi' INTO MyVideo;""" + load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" transport = MagicMock() transport.write = MagicMock(return_value="response_message") response = asyncio.run(handle_request(transport, load_query)) From 62f5c83f433a1ffe662a76795c5971f1629116f9 Mon Sep 17 00:00:00 2001 From: Jiashen Cao Date: Wed, 23 Nov 2022 23:03:29 -0500 Subject: [PATCH 03/17] attempt to fix row_id --- eva/catalog/catalog_manager.py | 7 +++++++ eva/storage/sqlite_storage_engine.py | 20 ++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index ef069ce68..a0b7cb881 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -102,9 +102,16 @@ def create_metadata( identifier_id=identifier_column, table_type=table_type, ) + + # Append row_id to table metadata. + column_list = [ + DataFrameColumn("_row_id", ColumnType.INTEGER) + ] + column_list + for column in column_list: column.metadata_id = metadata.id column_list = self._column_service.create_column(column_list) + metadata.schema = column_list return metadata diff --git a/eva/storage/sqlite_storage_engine.py b/eva/storage/sqlite_storage_engine.py index a35d53f8a..8dee31d19 100644 --- a/eva/storage/sqlite_storage_engine.py +++ b/eva/storage/sqlite_storage_engine.py @@ -71,7 +71,12 @@ def create(self, table: DataFrameMetadata, **kwargs): to create the table """ attr_dict = {"__tablename__": table.name} - sqlalchemy_schema = SchemaUtils.get_sqlalchemy_schema(table.columns) + + # During table creation, assume row_id is automatically handled by + # the sqlalchemy engine. + table_columns = [col for col in table.columns if col.name != "_row_id"] + sqlalchemy_schema = SchemaUtils.get_sqlalchemy_schema(table_columns) + attr_dict.update(sqlalchemy_schema) # dynamic schema generation # https://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html @@ -105,10 +110,16 @@ def write(self, table: DataFrameMetadata, rows: Batch): new_table = BaseModel.metadata.tables[table.name] columns = rows.frames.keys() data = [] + + # During table writes, assume row_id is automatically handled by + # the sqlalchemy engine. Another assumption we make here is the + # updated data need not to take care of row_id. + table_columns = [col for col in table.columns if col.name != "_row_id"] + # ToDo: validate the data type before inserting into the table for record in rows.frames.values: row_data = {col: record[idx] for idx, col in enumerate(columns)} - data.append(self._dict_to_sql_row(row_data, table.columns)) + data.append(self._dict_to_sql_row(row_data, table_columns)) self._sql_engine.execute(new_table.insert(), data) self._sql_session.commit() @@ -134,8 +145,9 @@ def read( row_size = None for row in result: # Todo: Verfiy the order of columns in row matches the table.columns - # ignore the first dummy (_row_id) primary column - data_batch.append(self._sql_row_to_dict(row[1:], table.columns)) + # For table read, we provide row_id so that user can also retrieve + # row_id from the table. + data_batch.append(self._sql_row_to_dict(row, table.columns)) if row_size is None: row_size = 0 row_size = get_size(data_batch) From d6cc24427c088b6a090f69c72e96b492abda91c6 Mon Sep 17 00:00:00 2001 From: Jiashen Cao Date: Wed, 23 Nov 2022 23:04:08 -0500 Subject: [PATCH 04/17] reformat --- eva/catalog/catalog_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index a0b7cb881..c19e3a294 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -104,9 +104,7 @@ def create_metadata( ) # Append row_id to table metadata. - column_list = [ - DataFrameColumn("_row_id", ColumnType.INTEGER) - ] + column_list + column_list = [DataFrameColumn("_row_id", ColumnType.INTEGER)] + column_list for column in column_list: column.metadata_id = metadata.id From 1ef34cfbbe8899e25cd3cb585339c6e5e135b432 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Thu, 24 Nov 2022 01:39:01 -0500 Subject: [PATCH 05/17] checkpoint --- eva/storage/opencv_storage_engine.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/eva/storage/opencv_storage_engine.py b/eva/storage/opencv_storage_engine.py index cb31ed842..9eb0a6980 100644 --- a/eva/storage/opencv_storage_engine.py +++ b/eva/storage/opencv_storage_engine.py @@ -16,6 +16,7 @@ import struct from pathlib import Path from typing import Iterator +from eva.catalog.catalog_type import TableType from eva.catalog.models.df_metadata import DataFrameMetadata from eva.catalog.sql_config import SQLConfig @@ -24,6 +25,7 @@ from eva.models.storage.batch import Batch from eva.readers.opencv_reader import OpenCVReader from eva.storage.abstract_storage_engine import AbstractStorageEngine +from eva.storage.storage_engine import StorageEngine from eva.utils.logging_manager import logger @@ -33,8 +35,9 @@ def __init__(self): self.curr_version = ConfigurationManager().get_value( "storage", "video_engine_version" ) - self._sql_session = SQLConfig().session - self._sql_engine = SQLConfig().engine + self._database_handler: AbstractStorageEngine = StorageEngine.storages[ + TableType.STRUCTURED_DATA + ] def create(self, table: DataFrameMetadata, if_not_exists=True): """ @@ -53,7 +56,9 @@ def create(self, table: DataFrameMetadata, if_not_exists=True): ) logger.error(error) raise FileExistsError(error) - + + DataFrameMetadata() + self._database_handler.create(table) return True def drop(self, table: DataFrameMetadata): @@ -65,13 +70,14 @@ def drop(self, table: DataFrameMetadata): def delete(self, table: DataFrameMetadata, rows: Batch): return - + def write(self, table: DataFrameMetadata, rows: Batch): try: dir_path = Path(table.file_url) for video_file_path in rows.video_file_paths(): video_file = Path(video_file_path) shutil.copy2(str(video_file), str(dir_path)) + self._create_video_metadata(dir_path, video_file.name) except Exception: error = "Current video storage engine only supports loading videos on disk." From 1788c713ed476e03d0e405a46755306e7722d962 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Sat, 3 Dec 2022 20:29:06 -0500 Subject: [PATCH 06/17] feat: regex support with rollback --- eva/binder/statement_binder.py | 30 ++++---- eva/catalog/catalog_manager.py | 19 +++++ eva/catalog/schema_utils.py | 7 +- eva/configuration/constants.py | 1 + eva/executor/executor_utils.py | 24 +++++- eva/executor/load_image_executor.py | 33 ++++---- eva/executor/load_video_executor.py | 78 ++++++++++++------- eva/models/storage/batch.py | 4 +- eva/storage/opencv_storage_engine.py | 79 ++++++++------------ eva/storage/sqlite_storage_engine.py | 24 +++++- eva/storage/storage_engine.py | 1 + eva/utils/errors.py | 0 test/integration_tests/test_load_executor.py | 12 +++ 13 files changed, 204 insertions(+), 108 deletions(-) create mode 100644 eva/utils/errors.py diff --git a/eva/binder/statement_binder.py b/eva/binder/statement_binder.py index 14b4fa60c..1f0693a93 100644 --- a/eva/binder/statement_binder.py +++ b/eva/binder/statement_binder.py @@ -133,22 +133,22 @@ def _bind_load_and_upload_data_statement( msg = f"Adding to an existing table {name}." logger.info(msg) else: - + # create catalog entry only if the file path exists - upload_dir = Path( - ConfigurationManager().get_value("storage", "upload_dir") - ) - if ( - Path(node.path).exists() - or Path(Path(upload_dir) / node.path).exists() - ): - create_multimedia_metadata(name, node.file_options["file_format"]) - - # else raise error - else: - err_msg = f"Path {node.path} does not exist." - logger.error(err_msg) - raise BinderError(err_msg) + # upload_dir = Path( + # ConfigurationManager().get_value("storage", "upload_dir") + # ) + # if ( + # Path(node.path).exists() + # or Path(Path(upload_dir) / node.path).exists() + # ): + create_multimedia_metadata(name, node.file_options["file_format"]) + + # # else raise error + # else: + # err_msg = f"Path {node.path} does not exist." + # logger.error(err_msg) + # raise BinderError(err_msg) self.bind(table_ref) diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index c19e3a294..488bb8c12 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -312,3 +312,22 @@ def check_table_exists(self, database_name: str, table_name: str): def get_all_udf_entries(self): return self._udf_service.get_all_udfs() + + def get_video_metadata_table( + self, + input_table: DataFrameMetadata, + ) -> DataFrameMetadata: + """Return an in-memory object of video metadata table. + We use this table to store all the video filenames and corresponding information + Args: + input_table (DataFrameMetadata): input vidoe table + + Returns: + DataFrameMetadata: metadata table maintained by the system + """ + name = f"_metadata_{input_table.name}" + metadata = DataFrameMetadata(name, "", TableType.STRUCTURED_DATA, identifier_id="file_url") + metadata.columns = [ + self.create_column_metadata("file_url", ColumnType.TEXT, None, None) + ] + return metadata diff --git a/eva/catalog/schema_utils.py b/eva/catalog/schema_utils.py index 0c510448c..26242fbb4 100644 --- a/eva/catalog/schema_utils.py +++ b/eva/catalog/schema_utils.py @@ -18,12 +18,13 @@ from eva.catalog.catalog_type import ColumnType from eva.catalog.models.df_column import DataFrameColumn +from eva.catalog.models.df_metadata import DataFrameMetadata from eva.utils.logging_manager import logger class SchemaUtils(object): @staticmethod - def get_sqlalchemy_column(df_column: DataFrameColumn) -> Column: + def xform_to_sqlalchemy_column(df_column: DataFrameColumn) -> Column: column_type = df_column.type sqlalchemy_column = None @@ -41,7 +42,7 @@ def get_sqlalchemy_column(df_column: DataFrameColumn) -> Column: return sqlalchemy_column @staticmethod - def get_sqlalchemy_schema( + def xform_to_sqlalchemy_schema( column_list: List[DataFrameColumn], ) -> Dict[str, Column]: """Converts the list of DataFrameColumns to SQLAlchemyColumns @@ -55,4 +56,4 @@ def get_sqlalchemy_schema( return { column.name: SchemaUtils.get_sqlalchemy_column(column) for column in column_list - } + } \ No newline at end of file diff --git a/eva/configuration/constants.py b/eva/configuration/constants.py index 79fb5e92a..33301235e 100644 --- a/eva/configuration/constants.py +++ b/eva/configuration/constants.py @@ -18,6 +18,7 @@ from eva.version import VERSION EVA_INSTALLATION_DIR = Path(eva.__file__).parent +EVA_ROOT_DIR = Path(eva.__file__).parent.parent # Using eva version to govern the EVA_DEFAULT_DIR # This means we won't support backward compatibility as each version will maintain its own copy of database. # Ideally, if the new release is not breaking backward compatibilty, we can keep using the same copy. diff --git a/eva/executor/executor_utils.py b/eva/executor/executor_utils.py index aff1b38b2..08642fd58 100644 --- a/eva/executor/executor_utils.py +++ b/eva/executor/executor_utils.py @@ -21,6 +21,7 @@ from eva.expression.abstract_expression import AbstractExpression from eva.models.storage.batch import Batch +from eva.utils.logging_manager import logger class ExecutorError(Exception): @@ -47,5 +48,24 @@ def iter_path_regex(path_regex: Path) -> Generator[str, None, None]: def validate_image(image_path: Path) -> bool: - data = cv2.imread(str(image_path)) - return data is not None + try: + data = cv2.imread(str(image_path)) + return data is not None + except Exception as e: + logger.warning( + f"Unexpected Exception {e} occured while reading image file {image_path}" + ) + return False + + +def validate_video(video_path: Path) -> bool: + try: + vid = cv2.VideoCapture(video_path) + if not vid.isOpened(): + return False + return True + except Exception as e: + logger.warning( + f"Unexpected Exception {e} occured while reading video file {video_path}" + ) + return False diff --git a/eva/executor/load_image_executor.py b/eva/executor/load_image_executor.py index 360737d35..7f87b3778 100644 --- a/eva/executor/load_image_executor.py +++ b/eva/executor/load_image_executor.py @@ -48,28 +48,36 @@ def exec(self): if not success: raise RuntimeError(f"StorageEngine {storage_engine} create call failed") - file_count = 0 - corrupt_files = [] + invalid_files = [] + valid_files = [] for file_path in iter_path_regex(self.node.file_path): if file_path.is_file(): # we should validate the file before loading if validate_image(file_path): - storage_engine.write(self.node.table_metainfo, file_path) - file_count += 1 + storage_engine.write( + self.node.table_metainfo, + Batch( + pd.DataFrame( + [{"file_path": str(file_path)}] + ) + ), + ) + valid_files.append(file_path) else: - corrupt_files.append(file_path) + invalid_files.append(file_path) except Exception as e: - self._rollback_load(file_count, corrupt_files) + self._rollback_load(valid_files) err_msg = f"Load command with error {str(e)}" logger.error(err_msg) raise ExecutorError(err_msg) - if corrupt_files: + loaded_file_count = len(valid_files) + if invalid_files: yield Batch( pd.DataFrame( { - "Number of loaded images": str(file_count), - "Failed to load files": str(corrupt_files), + "Number of loaded images": str(loaded_file_count), + "Failed to load files": str(invalid_files), }, index=[0], ) @@ -78,7 +86,7 @@ def exec(self): yield Batch( pd.DataFrame( { - "Number of loaded images": str(file_count), + "Number of loaded images": str(loaded_file_count), }, index=[0], ) @@ -87,8 +95,7 @@ def exec(self): def _rollback_load( self, storage_engine: AbstractStorageEngine, - file_count: int, - corrupt_files: List[Path], + valid_files: List[Path], ): - for file_path in corrupt_files: + for file_path in valid_files: storage_engine.delete(self.node.table_metainfo, file_path) diff --git a/eva/executor/load_video_executor.py b/eva/executor/load_video_executor.py index 6db06cc2e..1306853eb 100644 --- a/eva/executor/load_video_executor.py +++ b/eva/executor/load_video_executor.py @@ -13,13 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. from pathlib import Path +from typing import List import pandas as pd from eva.configuration.configuration_manager import ConfigurationManager from eva.executor.abstract_executor import AbstractExecutor +from eva.executor.executor_utils import ExecutorError, iter_path_regex, validate_video from eva.models.storage.batch import Batch from eva.planner.load_data_plan import LoadDataPlan +from eva.storage.abstract_storage_engine import AbstractStorageEngine from eva.storage.storage_engine import StorageEngine from eva.utils.logging_manager import logger @@ -39,35 +42,60 @@ def exec(self): using storage engine """ - video_file_path = None - # Validate file_path - if Path(self.node.file_path).exists(): - video_file_path = self.node.file_path - # check in the upload directory - else: - video_path = Path(Path(self.upload_dir) / self.node.file_path) - if video_path.exists(): - video_file_path = video_path - - if video_file_path is None: - error = "Failed to find a video file at location: {}".format( - self.node.file_path + try: + storage_engine = StorageEngine.factory(self.node.table_metainfo) + # ToDo: create based on if the metadata object exists in the catalog + success = storage_engine.create( + self.node.table_metainfo, if_not_exists=True ) - logger.error(error) - raise RuntimeError(error) + if not success: + raise RuntimeError(f"StorageEngine {storage_engine} create call failed") - storage_engine = StorageEngine.factory(self.node.table_metainfo) - storage_engine.create(self.node.table_metainfo, if_not_exists=True) - success = storage_engine.write( - self.node.table_metainfo, - Batch(pd.DataFrame([{"video_file_path": str(video_file_path)}])), - ) + valid_files = [] + invalid_files = [] + for file_path in iter_path_regex(self.node.file_path): + if file_path.is_file(): + # ToDo: we should validate the file before loading + if validate_video(file_path): + storage_engine.write( + self.node.table_metainfo, + Batch(pd.DataFrame([{"file_path": str(file_path)}])), + ) + valid_files.append(file_path) + else: + raise ValueError(file_path) - # ToDo: Add logic for indexing the video file - # Create an index of I frames to speed up random video seek - if success: + except RuntimeError as e: + raise ExecutorError(str(e)) + except ValueError as e: + self._rollback_load(storage_engine, valid_files) + err_msg = f"Encountered invalid file while loading video {str(e)}" + logger.error(err_msg) + raise ExecutorError(err_msg) + except Exception as e: + self._rollback_load(storage_engine, valid_files) + err_msg = f"Video Load command with unexpected error {str(e)}" + logger.error(err_msg) + raise ExecutorError(err_msg) + else: yield Batch( pd.DataFrame( - [f"Video successfully added at location: {video_file_path}"] + { + "Number of loaded images": str(len(valid_files)), + }, + index=[0], ) ) + + def _rollback_load( + self, + storage_engine: AbstractStorageEngine, + valid_files: List[Path], + ): + try: + rows = Batch(pd.DataFrame(data={"file_path": list(valid_files)})) + storage_engine.delete(self.node.table_metainfo, rows) + except Exception as e: + logger.exception( + f"Unexpected Exception {e} occured while rolling back. This is bad as the video table can be in a corrupt state. Please verify the video table {self.node.table_metainfo} for correctness." + ) diff --git a/eva/models/storage/batch.py b/eva/models/storage/batch.py index bb361c360..9a7f933bc 100644 --- a/eva/models/storage/batch.py +++ b/eva/models/storage/batch.py @@ -235,8 +235,8 @@ def update_indices(self, indices: List, other: Batch): self._frames.iloc[indices] = other._frames self._frames = pd.DataFrame(self._frames) - def video_file_paths(self) -> Iterable: - yield from self._frames["video_file_path"] + def file_paths(self) -> Iterable: + yield from self._frames["file_path"] def project(self, cols: None) -> Batch: """ diff --git a/eva/storage/opencv_storage_engine.py b/eva/storage/opencv_storage_engine.py index 9eb0a6980..89e689680 100644 --- a/eva/storage/opencv_storage_engine.py +++ b/eva/storage/opencv_storage_engine.py @@ -12,10 +12,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import os import shutil import struct from pathlib import Path from typing import Iterator + +from sqlalchemy import Column +from eva.catalog.catalog_manager import CatalogManager from eva.catalog.catalog_type import TableType from eva.catalog.models.df_metadata import DataFrameMetadata @@ -25,19 +29,17 @@ from eva.models.storage.batch import Batch from eva.readers.opencv_reader import OpenCVReader from eva.storage.abstract_storage_engine import AbstractStorageEngine -from eva.storage.storage_engine import StorageEngine +from eva.storage.sqlite_storage_engine import SQLStorageEngine from eva.utils.logging_manager import logger class OpenCVStorageEngine(AbstractStorageEngine): def __init__(self): - self.metadata = "metadata" - self.curr_version = ConfigurationManager().get_value( - "storage", "video_engine_version" - ) - self._database_handler: AbstractStorageEngine = StorageEngine.storages[ - TableType.STRUCTURED_DATA - ] + self._rdb_handler: SQLStorageEngine = SQLStorageEngine() + + @staticmethod + def _get_metadata_table(table: DataFrameMetadata): + return CatalogManager().get_video_metadata_table(table) def create(self, table: DataFrameMetadata, if_not_exists=True): """ @@ -57,28 +59,42 @@ def create(self, table: DataFrameMetadata, if_not_exists=True): logger.error(error) raise FileExistsError(error) - DataFrameMetadata() - self._database_handler.create(table) + self._rdb_handler.create(self._get_metadata_table(table)) return True def drop(self, table: DataFrameMetadata): dir_path = Path(table.file_url) try: shutil.rmtree(str(dir_path)) + self._rdb_handler.drop(self._get_metadata_table(table)) except Exception as e: logger.exception(f"Failed to drop the video table {e}") def delete(self, table: DataFrameMetadata, rows: Batch): - return + try: + video_metadata_table = self._get_metadata_table(table) + for video_file_path in rows.file_paths(): + video_file = Path(table.file_url) / video_file_path + video_file.unlink() + self._rdb_handler.delete( + video_metadata_table, + where_clause={video_metadata_table.identifier_id: video_file_path}, + ) + except Exception: + error = f"Deleting file path {video_file_path} failed" + logger.exception(error) + raise RuntimeError(error) + return True def write(self, table: DataFrameMetadata, rows: Batch): try: dir_path = Path(table.file_url) - for video_file_path in rows.video_file_paths(): + for video_file_path in rows.file_paths(): video_file = Path(video_file_path) shutil.copy2(str(video_file), str(dir_path)) - - self._create_video_metadata(dir_path, video_file.name) + self._rdb_handler.write( + self._get_metadata_table(table, video_file.name) + ) except Exception: error = "Current video storage engine only supports loading videos on disk." logger.exception(error) @@ -93,8 +109,9 @@ def read( sampling_rate: int = None, ) -> Iterator[Batch]: - metadata_file = Path(table.file_url) / self.metadata - for video_file_name in self._get_video_file_path(metadata_file): + for video_file_name in self._rdb_handler.read( + self._get_metadata_table(table), 1 + ): video_file = Path(table.file_url) / video_file_name reader = OpenCVReader( str(video_file), @@ -107,36 +124,6 @@ def read( batch.frames[column_name] = str(video_file_name) yield batch - def _get_video_file_path(self, metadata_file): - with open(metadata_file, "rb") as f: - while True: - buf = f.read(struct.calcsize("!H")) - if not buf: - break - (version,) = struct.unpack("!H", buf) - if version > self.curr_version: - error = "Invalid metadata version {}".format(version) - logger.error(error) - raise RuntimeError(error) - (length,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - path = f.read(length) - yield Path(path.decode()) - - def _create_video_metadata(self, dir_path, video_file): - # File structure - # - with open(dir_path / self.metadata, "ab") as f: - # write version number - file_path_bytes = str(video_file).encode() - length = len(file_path_bytes) - data = struct.pack( - "!HH%ds" % (length,), - self.curr_version, - length, - file_path_bytes, - ) - f.write(data) - def _open(self, table): pass diff --git a/eva/storage/sqlite_storage_engine.py b/eva/storage/sqlite_storage_engine.py index 8dee31d19..a6f772577 100644 --- a/eva/storage/sqlite_storage_engine.py +++ b/eva/storage/sqlite_storage_engine.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from typing import Iterator, List +from typing import Any, Dict, Iterator, List import numpy as np import pandas as pd @@ -75,7 +75,7 @@ def create(self, table: DataFrameMetadata, **kwargs): # During table creation, assume row_id is automatically handled by # the sqlalchemy engine. table_columns = [col for col in table.columns if col.name != "_row_id"] - sqlalchemy_schema = SchemaUtils.get_sqlalchemy_schema(table_columns) + sqlalchemy_schema = SchemaUtils.xform_to_sqlalchemy_schema(table_columns) attr_dict.update(sqlalchemy_schema) # dynamic schema generation @@ -156,3 +156,23 @@ def read( data_batch = [] if data_batch: yield Batch(pd.DataFrame(data_batch)) + + def delete(self, table: DataFrameMetadata, where_clause: Dict[str, Any]): + """Delete tuples from the table where rows satisfy the where_clause. + The current implementation only handles equality predicates. + + Argument: + table: table metadata object of the table + where_clause (Dict[str, Any]): the where clause use to find the tuples to remove. The key should be the column name and value should be the tuple value. The function assumes an equality condition + """ + sqlite_table = BaseModel.metadata.tables[table.name] + table_columns = [col for col in sqlite_table.columns if col.name != "_row_id"] + # verify where clause + for column in where_clause: + if column not in table_columns: + raise Exception( + f"where_clause contains a column {column} not in the table {sqlite_table}" + ) + d = sqlite_table.delete().where(**where_clause) + self._sql_engine.execute(d) + self._sql_session.commit() diff --git a/eva/storage/storage_engine.py b/eva/storage/storage_engine.py index 533c0b446..8c2e090c9 100644 --- a/eva/storage/storage_engine.py +++ b/eva/storage/storage_engine.py @@ -38,3 +38,4 @@ def factory(cls, table_metadata: DataFrameMetadata) -> AbstractStorageEngine: return cls.storages[table_metadata.table_type] raise RuntimeError(f"Invalid table type {table_metadata.table_type}") + diff --git a/eva/utils/errors.py b/eva/utils/errors.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration_tests/test_load_executor.py b/test/integration_tests/test_load_executor.py index 793f874fc..544874fd5 100644 --- a/test/integration_tests/test_load_executor.py +++ b/test/integration_tests/test_load_executor.py @@ -66,6 +66,18 @@ def test_should_load_images_in_table(self): file_names = actual_batch.project(["myimages.name"]).frames + def test_should_load_videos_in_table(self): + path = f"{EVA_ROOT_DIR}/data/**/*.mp4" + query = f"""LOAD VIDEO "{path}" INTO MyVideos;""" + execute_query_fetch_all(query) + + select_query = """SELECT name, data FROM MyImages;""" + + actual_batch = execute_query_fetch_all(select_query) + self.assertEqual(len(actual_batch), 20) + + file_names = actual_batch.project(["myimages.name"]).frames + # integration tests for csv def test_should_load_csv_in_table(self): From c959c0b71b1645b3daf61bc65b8abb2d838b9b97 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Sun, 4 Dec 2022 04:53:09 -0500 Subject: [PATCH 07/17] fix: undo import image code --- api-docs/source/tutorials/tutorials.rst | 115 ----------------- eva/catalog/catalog_type.py | 1 - eva/eva.yml | 3 - eva/executor/executor_utils.py | 11 -- eva/executor/load_executor.py | 3 - eva/executor/load_image_executor.py | 97 -------------- eva/executor/storage_executor.py | 10 +- eva/parser/evaql/evaql_lexer.g4 | 1 - eva/parser/evaql/evaql_parser.g4 | 10 +- eva/parser/parser_visitor/_load_statement.py | 2 - eva/parser/types.py | 1 - eva/readers/opencv_image_reader.py | 41 ------ eva/storage/image_storage_engine.py | 129 ------------------- eva/storage/storage_engine.py | 3 - test/catalog/models/test_models.py | 2 +- 15 files changed, 10 insertions(+), 419 deletions(-) delete mode 100644 api-docs/source/tutorials/tutorials.rst delete mode 100644 eva/executor/load_image_executor.py delete mode 100644 eva/readers/opencv_image_reader.py delete mode 100644 eva/storage/image_storage_engine.py diff --git a/api-docs/source/tutorials/tutorials.rst b/api-docs/source/tutorials/tutorials.rst deleted file mode 100644 index 829c1809c..000000000 --- a/api-docs/source/tutorials/tutorials.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. _guide-tutorials: - -Setup for local system -====================== - -Configure GPU (Recommended) ----------------------------- - -1. If your workstation has a GPU, you need to first set it up and configure it. You can run the following command first to check your hardware capabilities. - - .. code-block:: bash - - ubuntu-drivers devices - nvidia-smi - - A valid output from the commands indicates that your GPU is configured and ready to use. If not, you need to first install necessary drivers and configure your GPU. `this `_ page provides a step-by-step guide to install and configure the drivers for ubuntu. - - Some pointers: - * When installing NVIDIA drivers, check the correct driver version for your GPU to avoid compatibiility issues. - * When installing cuDNN, you will have to create an account. Make sure you get the correct deb files for your OS and architecture. - -2. You can run the following code in a jupyter instance to verify your GPU is working well along with PyTorch. - - .. code-block:: python - - import torch - device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') - print(device) - - Output of `cuda:0` indicates the presence of a GPU. - (Note: 0 indicates the index of the GPU in system. Incase you have multiple GPUs, the index needs to be accordingly changed) - -3. Now configure the executor section in eva.yml as follows: - - .. code-block:: yaml - - executor: - - gpus: {'127.0.1.1': [0]} - - `127.0.1.1` is the loopback address on which the eva server is started. 0 refers to the GPU index to be used. - -Quickstart Tutorial --------------------- - -1. **Start server**: - - * Open a terminal instance and start the server: - - .. code-block:: bash - - python eva.py - -2. **Start client**: - - * Open another terminal instance. Start a jupyter lab/notebook instance, and navigate to `tutorials/object_detection.ipynb`. - - * You might have to install ipywidgets to visualize the input video and output. Follow steps `here `_ as per your jupyter environment. - -3. **Notebook walkthrough**: - - * Establish connection with the DB: - - .. code-block:: python - - import nest_asyncio - nest_asyncio.apply() - connection = connect(host = '0.0.0.0', port = 5432) - cursor = connection.cursor() - - We use `nest_asyncio` to allow nested asyncio calls. This is required for the client to work with the server. `cursor` is a cursor object that allows us to execute queries on the database. - - * Load the video to be analyzed: - - .. code-block:: python - - cursor.execute('LOAD VIDEO "../data/ua_detrac/ua_detrac.mp4" INTO MyVideo') - response = cursor.fetch_all() - print(response) - - The `UPLOAD` command is used to upload a video to EVA. `INFILE` takes in the path of the video on filesystem and `PATH` is the path where the video is to be stored in the database. - - * Visualize video: - - .. code-block:: python - - from ipywidgets import Video - Video.from_file("../data/ua_detrac/ua_detrac.mp4", embed=True) - - * Registering an user-defined function (UDFs): - - .. code-block:: python - - cursor.execute("""CREATE UDF IF NOT EXISTS FastRCNNObjectDetector - INPUT (frame NDARRAY UINT8(3, ANYDIM, ANYDIM)) - OUTPUT (labels NDARRAY STR(ANYDIM), bboxes NDARRAY FLOAT32(ANYDIM, 4), - scores NDARRAY FLOAT32(ANYDIM)) - TYPE Classification - IMPL 'src/udfs/fastrcnn_object_detector.py'; - """) - response = cursor.fetch_all() - print(response) - - To learn more about UDFs, check out `this page `_. - - * Running the UDF in a query: - - .. code-block:: python - - cursor.execute("""SELECT id, FastRCNNObjectDetector(data) FROM MyVideo""") - response = cursor.fetch_all() - - UDFs are typically used like SQL functions in 'SELECT' queries. The 'UNNEST' function is used to unnest the output of the UDF. - - That's all folks! You can now use EVA for analysing your own videos with your own UDFs. diff --git a/eva/catalog/catalog_type.py b/eva/catalog/catalog_type.py index 67f3af09c..78617bdf6 100644 --- a/eva/catalog/catalog_type.py +++ b/eva/catalog/catalog_type.py @@ -22,7 +22,6 @@ class Dimension(IntEnum): class TableType(IntEnum): STRUCTURED_DATA = auto() VIDEO_DATA = auto() - IMAGE_DATA = auto() class ColumnType(Enum): diff --git a/eva/eva.yml b/eva/eva.yml index c92aec693..35452dd37 100644 --- a/eva/eva.yml +++ b/eva/eva.yml @@ -20,9 +20,6 @@ storage: structured_data_engine: "eva.storage.sqlite_storage_engine.SQLStorageEngine" video_engine: "eva.storage.opencv_storage_engine.OpenCVStorageEngine" video_engine_version: 0 - image_engine: "eva.storage.image_storage_engine.OpenCVImageStorageEngine" - image_engine_version: 0 - server: host: "0.0.0.0" port: 5432 diff --git a/eva/executor/executor_utils.py b/eva/executor/executor_utils.py index b817d8c39..a9e65dc40 100644 --- a/eva/executor/executor_utils.py +++ b/eva/executor/executor_utils.py @@ -64,17 +64,6 @@ def iter_path_regex(path_regex: Path) -> Generator[str, None, None]: return glob.iglob(os.path.expanduser(path_regex), recursive=True) -def validate_image(image_path: Path) -> bool: - try: - data = cv2.imread(str(image_path)) - return data is not None - except Exception as e: - logger.warning( - f"Unexpected Exception {e} occured while reading image file {image_path}" - ) - return False - - def validate_video(video_path: Path) -> bool: try: vid = cv2.VideoCapture(str(video_path)) diff --git a/eva/executor/load_executor.py b/eva/executor/load_executor.py index 38309c05b..b16594ca0 100644 --- a/eva/executor/load_executor.py +++ b/eva/executor/load_executor.py @@ -14,7 +14,6 @@ # limitations under the License. from eva.executor.abstract_executor import AbstractExecutor from eva.executor.load_csv_executor import LoadCSVExecutor -from eva.executor.load_image_executor import LoadImageExecutor from eva.executor.load_video_executor import LoadVideoExecutor from eva.parser.types import FileFormatType from eva.planner.load_data_plan import LoadDataPlan @@ -35,8 +34,6 @@ def exec(self): # invoke the appropriate executor if self.node.file_options["file_format"] == FileFormatType.VIDEO: executor = LoadVideoExecutor(self.node) - elif self.node.file_options["file_format"] == FileFormatType.IMAGE: - executor = LoadImageExecutor(self.node) elif self.node.file_options["file_format"] == FileFormatType.CSV: executor = LoadCSVExecutor(self.node) diff --git a/eva/executor/load_image_executor.py b/eva/executor/load_image_executor.py deleted file mode 100644 index 2d57971d0..000000000 --- a/eva/executor/load_image_executor.py +++ /dev/null @@ -1,97 +0,0 @@ -# coding=utf-8 -# Copyright 2018-2022 EVA -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from pathlib import Path -from typing import List - -import pandas as pd - -from eva.configuration.configuration_manager import ConfigurationManager -from eva.executor.abstract_executor import AbstractExecutor -from eva.executor.executor_utils import ExecutorError, iter_path_regex, validate_image -from eva.models.storage.batch import Batch -from eva.planner.load_data_plan import LoadDataPlan -from eva.storage.abstract_storage_engine import AbstractStorageEngine -from eva.storage.storage_engine import StorageEngine -from eva.utils.logging_manager import logger - - -class LoadImageExecutor(AbstractExecutor): - def __init__(self, node: LoadDataPlan): - super().__init__(node) - self.upload_dir = Path( - ConfigurationManager().get_value("storage", "upload_dir") - ) - - def validate(self): - pass - - def exec(self): - - try: - storage_engine = StorageEngine.factory(self.node.table_metainfo) - # ToDo: create based on if the metadata object exists in the catalog - success = storage_engine.create( - self.node.table_metainfo, if_not_exists=True - ) - if not success: - raise RuntimeError(f"StorageEngine {storage_engine} create call failed") - - invalid_files = [] - valid_files = [] - for file_path in iter_path_regex(self.node.file_path): - if file_path.is_file(): - # we should validate the file before loading - if validate_image(file_path): - storage_engine.write( - self.node.table_metainfo, - Batch(pd.DataFrame([{"file_path": str(file_path)}])), - ) - valid_files.append(file_path) - else: - invalid_files.append(file_path) - except Exception as e: - self._rollback_load(valid_files) - err_msg = f"Load command with error {str(e)}" - logger.error(err_msg) - raise ExecutorError(err_msg) - - loaded_file_count = len(valid_files) - if invalid_files: - yield Batch( - pd.DataFrame( - { - "Number of loaded images": str(loaded_file_count), - "Failed to load files": str(invalid_files), - }, - index=[0], - ) - ) - else: - yield Batch( - pd.DataFrame( - { - "Number of loaded images": str(loaded_file_count), - }, - index=[0], - ) - ) - - def _rollback_load( - self, - storage_engine: AbstractStorageEngine, - valid_files: List[Path], - ): - for file_path in valid_files: - storage_engine.delete(self.node.table_metainfo, file_path) diff --git a/eva/executor/storage_executor.py b/eva/executor/storage_executor.py index 41e1b77f7..ae41e9983 100644 --- a/eva/executor/storage_executor.py +++ b/eva/executor/storage_executor.py @@ -39,15 +39,11 @@ def exec(self) -> Iterator[Batch]: predicate=self.node.predicate, sampling_rate=self.node.sampling_rate, ) - elif self.node.video.table_type == TableType.STRUCTURED_DATA: - return storage_engine.read(self.node.video, self.node.batch_mem_size) - elif self.node.video.table_type == TableType.IMAGE_DATA: - return storage_engine.read( - self.node.video, self.node.batch_mem_size, self.node.predicate - ) + elif self.node.table.table_type == TableType.STRUCTURED_DATA: + return storage_engine.read(self.node.table, self.node.batch_mem_size) else: raise ExecutorError( - f"Unsupported TableType {self.node.video.table_type} encountered" + f"Unsupported TableType {self.node.table.table_type} encountered" ) def __call__(self, **kwargs) -> Generator[Batch, None, None]: diff --git a/eva/parser/evaql/evaql_lexer.g4 b/eva/parser/evaql/evaql_lexer.g4 index e0b736686..bb7e3b8ff 100644 --- a/eva/parser/evaql/evaql_lexer.g4 +++ b/eva/parser/evaql/evaql_lexer.g4 @@ -89,7 +89,6 @@ USING: 'USING'; VALUES: 'VALUES'; WHERE: 'WHERE'; XOR: 'XOR'; -IMAGE: 'IMAGE'; // File Formats WITH: 'WITH'; diff --git a/eva/parser/evaql/evaql_parser.g4 b/eva/parser/evaql/evaql_parser.g4 index c9f26f20d..6279aca6a 100644 --- a/eva/parser/evaql/evaql_parser.g4 +++ b/eva/parser/evaql/evaql_parser.g4 @@ -192,7 +192,7 @@ loadStatement fileFormat - : (CSV|VIDEO|IMAGE) + : (CSV|VIDEO) ; @@ -200,18 +200,20 @@ fileOptions : FORMAT fileFormat ; - uploadStatement : UPLOAD - fileFormat - PATH stringLiteral + PATH fileName BLOB videoBlob INTO tableName ( ('(' columns=uidList ')') )? + (WITH fileOptions)? ; +fileName + : stringLiteral + ; videoBlob diff --git a/eva/parser/parser_visitor/_load_statement.py b/eva/parser/parser_visitor/_load_statement.py index 1cb998caa..ae3986d92 100644 --- a/eva/parser/parser_visitor/_load_statement.py +++ b/eva/parser/parser_visitor/_load_statement.py @@ -44,8 +44,6 @@ def visitFileFormat(self, ctx: evaql_parser.FileFormatContext): # Check the file format if ctx.CSV() is not None: file_format = FileFormatType.CSV - elif ctx.IMAGE() is not None: - file_format = FileFormatType.IMAGE return file_format diff --git a/eva/parser/types.py b/eva/parser/types.py index cd38e18da..cf12260ba 100644 --- a/eva/parser/types.py +++ b/eva/parser/types.py @@ -65,7 +65,6 @@ class FileFormatType(Enum): Manages enums for all order by sort types """ - IMAGE = auto() VIDEO = auto() CSV = auto() diff --git a/eva/readers/opencv_image_reader.py b/eva/readers/opencv_image_reader.py deleted file mode 100644 index 6b261d3a5..000000000 --- a/eva/readers/opencv_image_reader.py +++ /dev/null @@ -1,41 +0,0 @@ -# coding=utf-8 -# Copyright 2018-2022 EVA -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import Dict, Iterator - -import cv2 - -from eva.readers.abstract_reader import AbstractReader - -# from eva.utils.logging_manager import LoggingLevel -# from eva.utils.logging_manager import LoggingManager -# from eva.expression.expression_utils import parse_predicate - - -class CVImageReader(AbstractReader): - def __init__(self, *args, start_frame_id=0, **kwargs): - self._start_frame_id = start_frame_id - self._predicate = kwargs.pop("predicate", None) - self._resolution = kwargs.pop("resolution", None) - super().__init__(*args, **kwargs) - - def _read(self) -> Iterator[Dict]: - for file in self.file_url: - # frame = Image.open(str(file)).load() - frame = cv2.imread(str(file)) - if frame is None: - print("Failed to read Image {}".format(file)) - else: - yield {"name": str(file), "data": frame} diff --git a/eva/storage/image_storage_engine.py b/eva/storage/image_storage_engine.py deleted file mode 100644 index 649ac89a6..000000000 --- a/eva/storage/image_storage_engine.py +++ /dev/null @@ -1,129 +0,0 @@ -# coding=utf-8 -# Copyright 2018-2022 EVA -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import io -import shutil -import struct -from pathlib import Path -from typing import Iterator - -from eva.catalog.models.df_metadata import DataFrameMetadata -from eva.configuration.configuration_manager import ConfigurationManager -from eva.models.storage.batch import Batch -from eva.readers.opencv_image_reader import CVImageReader -from eva.storage.abstract_storage_engine import AbstractStorageEngine - -# from eva.utils.logging_manager import LoggingManager -# from eva.utils.logging_manager import LoggingLevel - - -class OpenCVImageStorageEngine(AbstractStorageEngine): - def __init__(self): - self.metadata = "metadata" - self.curr_version = ConfigurationManager().get_value( - "storage", "image_engine_version" - ) - - def create(self, table: DataFrameMetadata): - # Create directory to store images - dir_path = Path(table.file_url) - try: - dir_path.mkdir(parents=True) - except FileExistsError: - error = f"Failed to create, directory already exists {dir_path}" - raise FileExistsError(error) - self._create_metadata(dir_path) - return True - - def write(self, table: DataFrameMetadata, image_file: Path): - dir_path = Path(table.file_url) - try: - shutil.copy2(str(image_file), str(dir_path)) - except FileExistsError: - error = "Failed to load the image as directory \ - already exists {}".format( - dir_path - ) - raise FileExistsError(error) - self._update_metadata(dir_path, image_file) - - def read( - self, - table: DataFrameMetadata, - batch_size: int, - predicate=None, - resolution=None, - ) -> Iterator[Batch]: - - files = self._get_file_list(table.file_url) - reader = CVImageReader( - files, - batch_mem_size=batch_size, - predicate=predicate, - resolution=resolution, - ) - for batch in reader.read(): - yield batch - - def _get_file_list(self, dir_path): - metadata_file = Path(dir_path) / self.metadata - if not metadata_file.exists(): - return - with open(metadata_file, "rb") as f: - (version,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - if version > self.curr_version: - error = "Invalid metadata version {}".format(version) - # LoggingManager().log(error, LoggingLevel.ERROR) - raise RuntimeError(error) - (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - for i in range(num_files): - (length,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - path = f.read(length) - yield Path(path.decode()) - - def _get_num_files(self, dir_path): - metadata_file = Path(dir_path) / self.metadata - if not metadata_file.exists(): - return 0 - with open(metadata_file, "rb") as f: - f.seek(struct.calcsize("!H")) - (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - return num_files - - def _create_metadata(self, dir_path, overwrite=False): - metadata_file = Path(dir_path) / self.metadata - if metadata_file.exists(): - if not overwrite: - print("Trying to overwrite metadata. Please set overwrite = True") - return - # - with open(metadata_file, "wb") as f: - # write version number - data = struct.pack("!HH", self.curr_version, 0) - f.write(data) - - def _update_metadata(self, dir_path, image_file): - with open(dir_path / self.metadata, "r+b") as f: - # increment num files - f.seek(struct.calcsize("!H")) - (num_files,) = struct.unpack("!H", f.read(struct.calcsize("!H"))) - num_files += 1 - f.seek(struct.calcsize("!H")) - f.write(struct.pack("!H", num_files)) - # write version number - f.seek(0, io.SEEK_END) - file_path_bytes = str(image_file).encode() - length = len(file_path_bytes) - data = struct.pack("!H%ds" % (length,), length, file_path_bytes) - f.write(data) diff --git a/eva/storage/storage_engine.py b/eva/storage/storage_engine.py index 533c0b446..a14a14d74 100644 --- a/eva/storage/storage_engine.py +++ b/eva/storage/storage_engine.py @@ -27,9 +27,6 @@ class StorageEngine: TableType.VIDEO_DATA: str_to_class( ConfigurationManager().get_value("storage", "video_engine") )(), - TableType.IMAGE_DATA: str_to_class( - ConfigurationManager().get_value("storage", "image_engine") - )(), } @classmethod diff --git a/test/catalog/models/test_models.py b/test/catalog/models/test_models.py index bd9a0f17a..cbcb1f9c5 100644 --- a/test/catalog/models/test_models.py +++ b/test/catalog/models/test_models.py @@ -99,7 +99,7 @@ def test_df_metadata_equality(self): df_metadata1.schema = col_list self.assertNotEqual(df_metadata, df_metadata1) df_metadata2 = DataFrameMetadata( - "name2", "eva_dataset", table_type=TableType.IMAGE_DATA + "name2", "eva_dataset", table_type=TableType.VIDEO_DATA ) df_metadata2.schema = col_list[1:] self.assertNotEqual(df_metadata1, df_metadata2) From 03a92ee77dc70f0f502454bb4b74a2238e0b405b Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Sun, 4 Dec 2022 16:57:12 -0500 Subject: [PATCH 08/17] fix: code clean up --- eva/binder/binder_utils.py | 2 + eva/catalog/catalog_manager.py | 46 +++++++++---- eva/catalog/services/df_service.py | 14 ++-- eva/eva.yml | 1 - eva/executor/drop_executor.py | 4 +- eva/executor/load_video_executor.py | 2 +- eva/storage/opencv_storage_engine.py | 17 +++-- eva/storage/sqlite_storage_engine.py | 2 +- test/catalog/test_catalog_manager.py | 5 +- test/executor/test_load_executor.py | 68 ------------------- test/integration_tests/test_array_count.py | 11 +-- test/integration_tests/test_drop_executor.py | 7 ++ .../test_explain_executor.py | 5 +- test/integration_tests/test_load_executor.py | 27 ++++++-- test/integration_tests/test_mat_executor.py | 12 ++-- test/integration_tests/test_pytorch.py | 19 +++--- .../integration_tests/test_select_executor.py | 5 +- test/integration_tests/test_udf_executor.py | 5 +- test/optimizer/rules/test_rules.py | 8 ++- test/optimizer/test_cascade_optimizer.py | 6 +- test/storage/test_video_storage.py | 12 ---- test/util.py | 3 +- test/utils/test_timer.py | 4 +- 23 files changed, 135 insertions(+), 150 deletions(-) diff --git a/eva/binder/binder_utils.py b/eva/binder/binder_utils.py index e13e068f2..d631dca66 100644 --- a/eva/binder/binder_utils.py +++ b/eva/binder/binder_utils.py @@ -18,6 +18,7 @@ from typing import TYPE_CHECKING, List from eva.catalog.catalog_utils import is_video_table +from eva.sql_config import IDENTIFIER_COLUMN if TYPE_CHECKING: from eva.binder.statement_binder_context import StatementBinderContext @@ -64,6 +65,7 @@ def extend_star( [ TupleValueExpression(col_name=col_name, table_alias=alias) for alias, col_name in col_objs + if col_name != IDENTIFIER_COLUMN ] ) return target_list diff --git a/eva/catalog/catalog_manager.py b/eva/catalog/catalog_manager.py index 20e715f12..ce3ce6c35 100644 --- a/eva/catalog/catalog_manager.py +++ b/eva/catalog/catalog_manager.py @@ -27,6 +27,7 @@ from eva.parser.create_statement import ColConstraintInfo, ColumnDefinition from eva.parser.table_ref import TableInfo from eva.sql_config import IDENTIFIER_COLUMN +from eva.utils.errors import CatalogError from eva.utils.generic_utils import generate_file_path from eva.utils.logging_manager import logger @@ -343,18 +344,18 @@ def get_udf_outputs(self, udf_obj: UdfMetadata) -> List[UdfIO]: ) return self._udf_io_service.get_outputs_by_udf_id(udf_obj.id) - def drop_dataset_metadata(self, database_name: str, table_name: str) -> bool: + def drop_dataset_metadata(self, obj: DataFrameMetadata) -> bool: """ This method deletes the table along with its columns from df_metadata and df_columns respectively Arguments: - table_name: table name to be deleted. + obj: dataframe metadata entry to remove Returns: True if successfully deleted else False """ - return self._dataset_service.drop_dataset_by_name(database_name, table_name) + return self._dataset_service.drop_dataset(obj) def drop_udf(self, udf_name: str) -> bool: """ @@ -389,8 +390,27 @@ def get_all_udf_entries(self): return self._udf_service.get_all_udfs() def get_video_metadata_table( - self, - input_table: DataFrameMetadata, + self, input_table: DataFrameMetadata + ) -> DataFrameMetadata: + """Get a video metadata table. + Raise if it does not exists + Args: + input_table (DataFrameMetadata): input video table + + Returns: + DataFrameMetadata: metadata table maintained by the system + """ + name = f"_metadata_{input_table.name}" + obj = self.get_dataset_metadata(None, name) + if not obj: + err = f"Table with name {name} does not exist in catalog" + logger.exception(err) + raise CatalogError(err) + + return obj + + def create_video_metadata_table( + self, input_table: DataFrameMetadata ) -> DataFrameMetadata: """Get a video metadata table. Create one if it does not exists @@ -404,10 +424,12 @@ def get_video_metadata_table( name = f"_metadata_{input_table.name}" obj = self.get_dataset_metadata(None, name) if obj: - return obj - else: - columns = [ColumnDefinition("file_url", ColumnType.TEXT, None, None)] - obj = self.create_table_metadata( - TableInfo(name), columns, identifier_column=columns[0].name - ) - return obj + err_msg = f"Table with name {name} already exists" + logger.exception(err_msg) + raise CatalogError(err_msg) + + columns = [ColumnDefinition("file_url", ColumnType.TEXT, None, None)] + obj = self.create_table_metadata( + TableInfo(name), columns, identifier_column=columns[0].name + ) + return obj diff --git a/eva/catalog/services/df_service.py b/eva/catalog/services/df_service.py index 661dd1d64..3ff29c2cd 100644 --- a/eva/catalog/services/df_service.py +++ b/eva/catalog/services/df_service.py @@ -102,24 +102,22 @@ def dataset_object_by_name( """ return self.model.query.filter(self.model._name == dataset_name).one_or_none() - def drop_dataset_by_name(self, database_name: str, dataset_name: str): + def drop_dataset(self, dataset: DataFrameMetadata): """Delete dataset from the db Arguments: - database_name (str): Database to which dataset belongs - dataset_name (str): name of the dataset + dataset (DataFrameMetadata): dataset to delete Returns: True if successfully removed else false """ try: - dataset = self.dataset_object_by_name(database_name, dataset_name) dataset.delete() return True except Exception as e: - err_msg = "Delete dataset failed for name {} with error {}".format( - dataset_name, str(e) + err_msg = ( + f"Delete dataset failed for name {dataset.name} with error {str(e)}." ) - logger.error(err_msg) - raise RuntimeError(err_msg) + logger.exception(err_msg) + raise CatalogError(err_msg) def rename_dataset_by_name( self, new_name: str, curr_database_name: str, curr_dataset_name: str diff --git a/eva/eva.yml b/eva/eva.yml index 35452dd37..b3c3fa955 100644 --- a/eva/eva.yml +++ b/eva/eva.yml @@ -19,7 +19,6 @@ storage: upload_dir: "" structured_data_engine: "eva.storage.sqlite_storage_engine.SQLStorageEngine" video_engine: "eva.storage.opencv_storage_engine.OpenCVStorageEngine" - video_engine_version: 0 server: host: "0.0.0.0" port: 5432 diff --git a/eva/executor/drop_executor.py b/eva/executor/drop_executor.py index 0242bf9f8..909c98c5f 100644 --- a/eva/executor/drop_executor.py +++ b/eva/executor/drop_executor.py @@ -52,9 +52,7 @@ def exec(self): raise ExecutorError(str(err)) storage_engine.drop(table=table_ref.table.table_obj) - success = catalog_manager.drop_dataset_metadata( - table_ref.table.database_name, table_ref.table.table_name - ) + success = catalog_manager.drop_dataset_metadata(table_ref.table.table_obj) if not success: err_msg = "Failed to drop {}".format(table_ref) diff --git a/eva/executor/load_video_executor.py b/eva/executor/load_video_executor.py index 73206f31d..1d042ba1b 100644 --- a/eva/executor/load_video_executor.py +++ b/eva/executor/load_video_executor.py @@ -42,6 +42,7 @@ def exec(self): using storage engine """ try: + valid_files = [] # Create catalog entry table_info = self.node.table_info database_name = table_info.database_name @@ -65,7 +66,6 @@ def exec(self): f"StorageEngine {storage_engine} create call failed" ) - valid_files = [] for file_path in iter_path_regex(self.node.file_path): file_path = Path(file_path) if validate_video(file_path): diff --git a/eva/storage/opencv_storage_engine.py b/eva/storage/opencv_storage_engine.py index 1520578ee..2f1dee2e4 100644 --- a/eva/storage/opencv_storage_engine.py +++ b/eva/storage/opencv_storage_engine.py @@ -37,6 +37,9 @@ def __init__(self): def _get_metadata_table(self, table: DataFrameMetadata): return CatalogManager().get_video_metadata_table(table) + def _create_metadata_table(self, table: DataFrameMetadata): + return CatalogManager().create_video_metadata_table(table) + def _xform_file_url_to_file_name(self, file_url: Path) -> str: # convert video_path to file name # This is done to support duplicate video_names with different complete paths. Without conversion, we cannot copy files with same name but different paths. Eg., a/b/my.mp4 and a/b/c/my.mp4 @@ -62,14 +65,17 @@ def create(self, table: DataFrameMetadata, if_not_exists=True): logger.error(error) raise FileExistsError(error) - self._rdb_handler.create(self._get_metadata_table(table)) + self._rdb_handler.create(self._create_metadata_table(table)) return True def drop(self, table: DataFrameMetadata): dir_path = Path(table.file_url) try: shutil.rmtree(str(dir_path)) - self._rdb_handler.drop(self._get_metadata_table(table)) + metadata_table = self._get_metadata_table(table) + self._rdb_handler.drop(metadata_table) + # remove the metadata table from the catalog + CatalogManager().drop_dataset_metadata(metadata_table) except Exception as e: logger.exception(f"Failed to drop the video table {e}") @@ -82,7 +88,7 @@ def delete(self, table: DataFrameMetadata, rows: Batch): self._rdb_handler.delete( video_metadata_table, where_clause={ - video_metadata_table.identifier_column: dst_file_name + video_metadata_table.identifier_column: str(video_file_path) }, ) video_file.unlink() @@ -106,7 +112,7 @@ def write(self, table: DataFrameMetadata, rows: Batch): shutil.copy2(str(video_file), str(dst_path)) self._rdb_handler.write( self._get_metadata_table(table), - Batch(pd.DataFrame({"file_url": [dst_file_name]})), + Batch(pd.DataFrame({"file_url": [str(video_file)]})), ) except CatalogError: @@ -130,7 +136,8 @@ def read( for video_files in self._rdb_handler.read(self._get_metadata_table(table), 12): for video_file_name in video_files.frames["file_url"]: - video_file = Path(table.file_url) / video_file_name + system_file_name = self._xform_file_url_to_file_name(video_file_name) + video_file = Path(table.file_url) / system_file_name reader = OpenCVReader( str(video_file), batch_mem_size=batch_mem_size, diff --git a/eva/storage/sqlite_storage_engine.py b/eva/storage/sqlite_storage_engine.py index fd1af272a..463b27232 100644 --- a/eva/storage/sqlite_storage_engine.py +++ b/eva/storage/sqlite_storage_engine.py @@ -91,11 +91,11 @@ def drop(self, table: DataFrameMetadata): try: table_to_remove = BaseModel.metadata.tables[table.name] table_to_remove.drop() - self._sql_session.commit() # In-memory metadata does not automatically sync with the database # therefore manually removing the table from the in-memory metadata # https://github.com/sqlalchemy/sqlalchemy/issues/5112 BaseModel.metadata.remove(table_to_remove) + self._sql_session.commit() except Exception as e: logger.exception( f"Failed to drop the table {table.name} with Exception {str(e)}" diff --git a/test/catalog/test_catalog_manager.py b/test/catalog/test_catalog_manager.py index 51b66ffd8..02491bb43 100644 --- a/test/catalog/test_catalog_manager.py +++ b/test/catalog/test_catalog_manager.py @@ -183,9 +183,10 @@ def test_create_udf(self, udfio_mock, udf_mock): @mock.patch("eva.catalog.catalog_manager.DatasetColumnService") def test_drop_metadata(self, dcs_mock, ds_mock, initdb_mock): catalog = CatalogManager() - catalog.drop_dataset_metadata("database", "table") + obj = MagicMock() + catalog.drop_dataset_metadata(obj) ds_name_mock = ds_mock.return_value.drop_dataset_by_name - ds_name_mock.assert_called_with("database", "table") + ds_name_mock.assert_called_with(obj.table.database_name, obj.table.table_name) @mock.patch("eva.catalog.catalog_manager.UdfService") def test_get_udf_by_name(self, udf_mock): diff --git a/test/executor/test_load_executor.py b/test/executor/test_load_executor.py index 5ff46236f..6b72c9d64 100644 --- a/test/executor/test_load_executor.py +++ b/test/executor/test_load_executor.py @@ -13,13 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. import unittest -from pathlib import Path from test.util import create_sample_csv, file_remove import pandas as pd from mock import MagicMock, call, patch -from eva.configuration.configuration_manager import ConfigurationManager from eva.executor.executor_utils import ExecutorError from eva.executor.load_executor import LoadDataExecutor from eva.models.storage.batch import Batch @@ -45,72 +43,6 @@ def _create_load_video_plan(self): ) return plan - @patch("eva.catalog.catalog_manager.CatalogManager.create_video_metadata") - @patch( - "eva.catalog.catalog_manager.CatalogManager.get_dataset_metadata", - return_value=None, - ) - @patch("eva.executor.load_video_executor.StorageEngine.factory") - def test_should_call_opencv_reader_and_storage_engine( - self, factory_mock, get_mock, create_mock - ): - table_obj = MagicMock() - create_mock.return_value = table_obj - - plan = self._create_load_video_plan() - load_executor = LoadDataExecutor(plan) - with patch.object(Path, "exists") as mock_exists: - mock_exists.return_value = True - batch = next(load_executor.exec()) - factory_mock.return_value.create.assert_called_once_with(table_obj) - factory_mock.return_value.write.assert_called_once_with( - table_obj, Batch(pd.DataFrame([{"video_file_path": plan.file_path}])) - ) - expected = Batch( - pd.DataFrame( - [{f"Video successfully added at location: {plan.file_path}"}] - ) - ) - self.assertEqual(batch, expected) - - @patch("eva.catalog.catalog_manager.CatalogManager.create_video_metadata") - @patch( - "eva.catalog.catalog_manager.CatalogManager.get_dataset_metadata", - return_value=None, - ) - @patch("eva.executor.load_video_executor.StorageEngine.factory") - def test_should_search_in_upload_directory( - self, factory_mock, catalog_mock, create_mock - ): - table_obj = MagicMock() - create_mock.return_value = table_obj - plan = self._create_load_video_plan() - load_executor = LoadDataExecutor(plan) - with patch.object(Path, "exists") as mock_exists: - mock_exists.side_effect = [False, True] - batch = next(load_executor.exec()) - upload_path = Path( - ConfigurationManager().get_value("storage", "upload_dir") - ) - location = upload_path / plan.file_path - factory_mock.return_value.create.assert_called_once_with(table_obj) - factory_mock.return_value.write.assert_called_once_with( - table_obj, - Batch(pd.DataFrame([{"video_file_path": str(location)}])), - ) - expected = Batch( - pd.DataFrame([{f"Video successfully added at location: {location}"}]) - ) - self.assertEqual(batch, expected) - - @patch("eva.executor.load_video_executor.StorageEngine.factory") - def test_should_fail_to_find_file(self, factory_mock): - load_executor = LoadDataExecutor(self._create_load_video_plan()) - with patch.object(Path, "exists") as mock_exists: - mock_exists.side_effect = [False, False] - with self.assertRaises(ExecutorError): - next(load_executor.exec()) - @patch( "eva.catalog.catalog_manager.CatalogManager.get_dataset_metadata", return_value=None, diff --git a/test/integration_tests/test_array_count.py b/test/integration_tests/test_array_count.py index 1a3660ddb..b41f32495 100644 --- a/test/integration_tests/test_array_count.py +++ b/test/integration_tests/test_array_count.py @@ -23,14 +23,17 @@ class ArrayCountTests(unittest.TestCase): - def setUp(self): + @classmethod + def setUpClass(cls): CatalogManager().reset() - create_sample_video(NUM_FRAMES) - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video(NUM_FRAMES) + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) load_inbuilt_udfs() - def tearDown(self): + @classmethod + def tearDownClass(cls): + execute_query_fetch_all("DROP TABLE MyVideo;") file_remove("dummy.avi") # integration test diff --git a/test/integration_tests/test_drop_executor.py b/test/integration_tests/test_drop_executor.py index 2d2e94e00..78c647208 100644 --- a/test/integration_tests/test_drop_executor.py +++ b/test/integration_tests/test_drop_executor.py @@ -35,14 +35,21 @@ def test_should_drop_table(self): query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" execute_query_fetch_all(query) + # catalog should contain vidoe table and the metedata table metadata_obj = catalog_manager.get_dataset_metadata(None, "MyVideo") video_dir = metadata_obj.file_url self.assertFalse(metadata_obj is None) column_objects = catalog_manager.get_all_column_objects(metadata_obj) self.assertEqual(len(column_objects), 4) self.assertTrue(Path(video_dir).exists()) + # metadata table + video_metadata_table = catalog_manager.get_video_metadata_table(metadata_obj) + self.assertTrue(video_metadata_table is not None) + drop_query = """DROP TABLE MyVideo;""" execute_query_fetch_all(drop_query) + with self.assertRaises(Exception): + catalog_manager.get_video_metadata_table(metadata_obj) self.assertTrue(catalog_manager.get_dataset_metadata(None, "MyVideo") is None) column_objects = catalog_manager.get_all_column_objects(metadata_obj) self.assertEqual(len(column_objects), 0) diff --git a/test/integration_tests/test_explain_executor.py b/test/integration_tests/test_explain_executor.py index dce235ec4..e256f6c3b 100644 --- a/test/integration_tests/test_explain_executor.py +++ b/test/integration_tests/test_explain_executor.py @@ -25,8 +25,8 @@ class ExplainExecutorTest(unittest.TestCase): @classmethod def setUpClass(cls): CatalogManager().reset() - create_sample_video(NUM_FRAMES) - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video(NUM_FRAMES) + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) load_inbuilt_udfs() cls.table1 = create_table("table1", 100, 3) @@ -42,6 +42,7 @@ def tearDownClass(cls): execute_query_fetch_all(drop_query) drop_query = """DROP TABLE table3;""" execute_query_fetch_all(drop_query) + execute_query_fetch_all("DROP TABLE MyVideo;") def test_explain_simple_select(self): select_query = "EXPLAIN SELECT id, data FROM MyVideo" diff --git a/test/integration_tests/test_load_executor.py b/test/integration_tests/test_load_executor.py index c22c795c1..cd39d1497 100644 --- a/test/integration_tests/test_load_executor.py +++ b/test/integration_tests/test_load_executor.py @@ -38,7 +38,7 @@ class LoadExecutorTest(unittest.TestCase): def setUp(self): # reset the catalog manager before running each test CatalogManager().reset() - create_sample_video() + self.video_file_path = create_sample_video() create_sample_csv() def tearDown(self): @@ -47,7 +47,7 @@ def tearDown(self): # integration test for video def test_should_load_video_in_table(self): - query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + query = f"LOAD VIDEO '{self.video_file_path}' INTO MyVideo;" execute_query_fetch_all(query) select_query = """SELECT name, id, data FROM MyVideo;""" @@ -57,10 +57,7 @@ def test_should_load_video_in_table(self): expected_batch = list(create_dummy_batches())[0] self.assertEqual(actual_batch, expected_batch) - # Try adding video to an existing table - execute_query_fetch_all(query) - actual_batch = execute_query_fetch_all(select_query) - self.assertEqual(len(actual_batch), 2 * len(expected_batch)) + execute_query_fetch_all("DROP TABLE MyVideo;") def test_should_load_videos_in_table(self): path = f"{EVA_ROOT_DIR}/data/sample_videos/1/*.mp4" @@ -69,12 +66,17 @@ def test_should_load_videos_in_table(self): expected = Batch(pd.DataFrame(["Number of loaded videos: 2"])) self.assertEqual(result, expected) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") + def test_should_load_videos_with_same_name_but_different_path(self): path = f"{EVA_ROOT_DIR}/data/sample_videos/**/*.mp4" query = f"""LOAD VIDEO "{path}" INTO MyVideos;""" result = execute_query_fetch_all(query) expected = Batch(pd.DataFrame(["Number of loaded videos: 3"])) self.assertEqual(result, expected) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") def test_should_fail_to_load_videos_with_same_path(self): path = f"{EVA_ROOT_DIR}/data/sample_videos/1/*.mp4" @@ -87,6 +89,9 @@ def test_should_fail_to_load_videos_with_same_path(self): with self.assertRaises(Exception): execute_query_fetch_all(query) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") + def test_should_fail_to_load_corrupt_video(self): # should fail on an empty file with tempfile.NamedTemporaryFile() as tmp: @@ -97,6 +102,8 @@ def test_should_fail_to_load_corrupt_video(self): str(cm.exception), f"Load video failed: encountered invalid file {tmp.name}", ) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") def test_should_fail_to_load_invalid_files_as_video(self): path = f"{EVA_ROOT_DIR}/data/**" @@ -105,6 +112,8 @@ def test_should_fail_to_load_invalid_files_as_video(self): execute_query_fetch_all(query) result = execute_query_fetch_all("SELECT name FROM MyVideos;") self.assertEqual(len(result), 0) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") def test_should_rollback_if_video_load_fails(self): path_regex = Path(f"{EVA_ROOT_DIR}/data/sample_videos/1/*.mp4") @@ -136,6 +145,9 @@ def test_should_rollback_if_video_load_fails(self): result = execute_query_fetch_all("SELECT name FROM MyVideos") self.assertEqual(len(result), 0) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") + def test_should_rollback_and_preserve_previous_state(self): path_regex = Path(f"{EVA_ROOT_DIR}/data/sample_videos/1/*.mp4") valid_videos = glob.glob(str(path_regex.expanduser()), recursive=True) @@ -158,6 +170,9 @@ def test_should_rollback_and_preserve_previous_state(self): file_names = np.unique(result.frames) self.assertEqual(len(file_names), 1) + # clean up + execute_query_fetch_all("DROP TABLE MyVideos;") + # integration tests for csv def test_should_load_csv_in_table(self): diff --git a/test/integration_tests/test_mat_executor.py b/test/integration_tests/test_mat_executor.py index 61f55b928..a2b80ee42 100644 --- a/test/integration_tests/test_mat_executor.py +++ b/test/integration_tests/test_mat_executor.py @@ -25,6 +25,7 @@ import pytest from eva.catalog.catalog_manager import CatalogManager +from eva.configuration.constants import EVA_ROOT_DIR from eva.models.storage.batch import Batch from eva.server.command_handler import execute_query_fetch_all @@ -36,19 +37,20 @@ class MaterializedViewTest(unittest.TestCase): def setUpClass(cls): # reset the catalog manager before running each test CatalogManager().reset() - create_sample_video() + video_file_path = create_sample_video() copy_sample_videos_to_upload_dir() - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) - query = """LOAD VIDEO 'ua_detrac.mp4' - INTO UATRAC;""" - execute_query_fetch_all(query) + ua_detrac = f"{EVA_ROOT_DIR}/data/ua_detrac/ua_detrac.mp4" + execute_query_fetch_all(f"LOAD VIDEO '{ua_detrac}' INTO UATRAC;") load_inbuilt_udfs() @classmethod def tearDownClass(cls): file_remove("dummy.avi") file_remove("ua_detrac.mp4") + execute_query_fetch_all("DROP TABLE MyVideo;") + execute_query_fetch_all("DROP TABLE UATRAC;") def test_should_mat_view_with_dummy(self): materialized_query = """CREATE MATERIALIZED VIEW dummy_view (id, label) diff --git a/test/integration_tests/test_pytorch.py b/test/integration_tests/test_pytorch.py index 08bef9d99..e94acdac9 100644 --- a/test/integration_tests/test_pytorch.py +++ b/test/integration_tests/test_pytorch.py @@ -20,6 +20,7 @@ import pytest from eva.catalog.catalog_manager import CatalogManager +from eva.configuration.constants import EVA_ROOT_DIR from eva.server.command_handler import execute_query_fetch_all @@ -28,15 +29,12 @@ class PytorchTest(unittest.TestCase): def setUpClass(cls): CatalogManager().reset() copy_sample_videos_to_upload_dir() - query = """LOAD VIDEO 'ua_detrac.mp4' - INTO MyVideo;""" - execute_query_fetch_all(query) - query = """LOAD VIDEO 'mnist.mp4' - INTO MNIST;""" - execute_query_fetch_all(query) - query = """LOAD VIDEO 'actions.mp4' - INTO Actions;""" - execute_query_fetch_all(query) + ua_detrac = f"{EVA_ROOT_DIR}/data/ua_detrac/ua_detrac.mp4" + mnist = f"{EVA_ROOT_DIR}/data/mnist/mnist.mp4" + actions = f"{EVA_ROOT_DIR}/data/actions/actions.mp4" + execute_query_fetch_all(f"LOAD VIDEO '{ua_detrac}' INTO MyVideo;") + execute_query_fetch_all(f"LOAD VIDEO '{mnist}' INTO MNIST;") + execute_query_fetch_all(f"LOAD VIDEO '{actions}' INTO Actions;") load_inbuilt_udfs() @classmethod @@ -44,6 +42,9 @@ def tearDownClass(cls): file_remove("ua_detrac.mp4") file_remove("mnist.mp4") file_remove("actions.mp4") + execute_query_fetch_all("DROP TABLE Actions;") + execute_query_fetch_all("DROP TABLE MNIST;") + execute_query_fetch_all("DROP TABLE MyVideo;") @pytest.mark.torchtest def test_should_run_pytorch_and_fastrcnn(self): diff --git a/test/integration_tests/test_select_executor.py b/test/integration_tests/test_select_executor.py index 8d6c4445b..7d177b8d6 100644 --- a/test/integration_tests/test_select_executor.py +++ b/test/integration_tests/test_select_executor.py @@ -39,8 +39,8 @@ class SelectExecutorTest(unittest.TestCase): @classmethod def setUpClass(cls): CatalogManager().reset() - create_sample_video(NUM_FRAMES) - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video(NUM_FRAMES) + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) load_inbuilt_udfs() cls.table1 = create_table("table1", 100, 3) @@ -56,6 +56,7 @@ def tearDownClass(cls): execute_query_fetch_all(drop_query) drop_query = """DROP TABLE table3;""" execute_query_fetch_all(drop_query) + execute_query_fetch_all("DROP TABLE MyVideo;") def test_sort_on_nonprojected_column(self): """This tests doing an order by on a column diff --git a/test/integration_tests/test_udf_executor.py b/test/integration_tests/test_udf_executor.py index 465d88bc7..1e88025d8 100644 --- a/test/integration_tests/test_udf_executor.py +++ b/test/integration_tests/test_udf_executor.py @@ -34,8 +34,8 @@ class UDFExecutorTest(unittest.TestCase): def setUp(self): CatalogManager().reset() - create_sample_video(NUM_FRAMES) - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video(NUM_FRAMES) + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) create_udf_query = """CREATE UDF DummyObjectDetector @@ -48,6 +48,7 @@ def setUp(self): def tearDown(self): file_remove("dummy.avi") + execute_query_fetch_all("DROP TABLE MyVideo;") # integration test diff --git a/test/optimizer/rules/test_rules.py b/test/optimizer/rules/test_rules.py index 2270471a2..30b347dff 100644 --- a/test/optimizer/rules/test_rules.py +++ b/test/optimizer/rules/test_rules.py @@ -74,11 +74,15 @@ class TestRules(unittest.TestCase): def setUpClass(cls): # reset the catalog manager before running each test CatalogManager().reset() - create_sample_video() - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video() + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) load_inbuilt_udfs() + @classmethod + def tearDownClass(cls): + execute_query_fetch_all("DROP TABLE MyVideo;") + def test_rules_promises_order(self): # Promise of all rewrite should be greater than implementation self.assertTrue( diff --git a/test/optimizer/test_cascade_optimizer.py b/test/optimizer/test_cascade_optimizer.py index 6851b80a9..959bf92d8 100644 --- a/test/optimizer/test_cascade_optimizer.py +++ b/test/optimizer/test_cascade_optimizer.py @@ -25,13 +25,13 @@ class CascadeOptimizer(unittest.TestCase): def setUp(self): CatalogManager().reset() - create_sample_video(NUM_FRAMES) + self.video_file_path = create_sample_video(NUM_FRAMES) def tearDown(self): file_remove("dummy.avi") def test_logical_to_physical_udf(self): - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + load_query = f"LOAD VIDEO '{self.video_file_path}' INTO MyVideo;" execute_query_fetch_all(load_query) create_udf_query = """CREATE UDF DummyObjectDetector @@ -54,3 +54,5 @@ def test_logical_to_physical_udf(self): ] expected_batch = Batch(frames=pd.DataFrame(expected)) self.assertEqual(actual_batch, expected_batch) + + execute_query_fetch_all("DROP TABLE MyVideo;") diff --git a/test/storage/test_video_storage.py b/test/storage/test_video_storage.py index 018acc403..f9b5e30dc 100644 --- a/test/storage/test_video_storage.py +++ b/test/storage/test_video_storage.py @@ -12,17 +12,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import struct import unittest from unittest.mock import MagicMock import mock -from mock import mock_open from eva.catalog.catalog_type import ColumnType, NdArrayType, TableType from eva.catalog.models.df_column import DataFrameColumn from eva.catalog.models.df_metadata import DataFrameMetadata -from eva.configuration.configuration_manager import ConfigurationManager from eva.storage.storage_engine import StorageEngine @@ -47,9 +44,6 @@ def setUp(self): mock.table_type = TableType.VIDEO_DATA self.video_engine = StorageEngine.factory(mock) self.table = self.create_sample_table() - self.curr_version = ConfigurationManager().get_value( - "storage", "video_engine_version" - ) def tearDown(self): pass @@ -60,12 +54,6 @@ def test_should_raise_file_exist_error(self, m): with self.assertRaises(FileExistsError): self.video_engine.create(self.table, if_not_exists=False) - def test_invalid_metadata(self): - corrupt_meta = struct.pack("!H", self.curr_version + 1) - with mock.patch("builtins.open", mock_open(read_data=corrupt_meta)): - with self.assertRaises(RuntimeError): - list(self.video_engine._get_video_file_path("metadata")) - def test_write(self): batch = MagicMock() batch.frames = [] diff --git a/test/util.py b/test/util.py index d81c008b7..d60677389 100644 --- a/test/util.py +++ b/test/util.py @@ -242,6 +242,7 @@ def create_sample_video(num_frames=NUM_FRAMES): out.write(frame) out.release() + return os.path.join(upload_dir_from_config, "dummy.avi") def create_sample_video_as_blob(num_frames=NUM_FRAMES): @@ -294,7 +295,7 @@ def create_dummy_batches(num_frames=NUM_FRAMES, filters=[], batch_size=10, start for i in filters: data.append( { - "myvideo.name": "dummy.avi", + "myvideo.name": os.path.join(upload_dir_from_config, "dummy.avi"), "myvideo.id": i + start_id, "myvideo.data": np.array( np.ones((2, 2, 3)) * float(i + 1) * 25, dtype=np.uint8 diff --git a/test/utils/test_timer.py b/test/utils/test_timer.py index a1c793555..93d4a4368 100644 --- a/test/utils/test_timer.py +++ b/test/utils/test_timer.py @@ -41,8 +41,8 @@ def test_timer(self): def test_timer_with_query(self): CatalogManager().reset() - create_sample_video(NUM_FRAMES) - load_query = """LOAD VIDEO 'dummy.avi' INTO MyVideo;""" + video_file_path = create_sample_video(NUM_FRAMES) + load_query = f"LOAD VIDEO '{video_file_path}' INTO MyVideo;" transport = MagicMock() transport.write = MagicMock(return_value="response_message") response = asyncio.run(handle_request(transport, load_query)) From 436b93b46e7f9219688dc51770391471bea300e2 Mon Sep 17 00:00:00 2001 From: Gaurav Tarlok Kakkar Date: Sun, 4 Dec 2022 17:10:56 -0500 Subject: [PATCH 09/17] bug: add missing data files --- data/sample_videos/1/1.mp4 | Bin 0 -> 622311 bytes data/sample_videos/1/2.mp4 | Bin 0 -> 1661565 bytes data/sample_videos/2/1.mp4 | Bin 0 -> 622311 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/sample_videos/1/1.mp4 create mode 100755 data/sample_videos/1/2.mp4 create mode 100644 data/sample_videos/2/1.mp4 diff --git a/data/sample_videos/1/1.mp4 b/data/sample_videos/1/1.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..d7da324f481f934943b732412b76222bad1f477d GIT binary patch literal 622311 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%0D0s?T4%)hp8Z-fTLUV6_I3PLmmV| zn5N4E@ufD|l8+L)Ere`V7n2`(+~Qu@FyI2G^0I3M@1P=6RJfa_ULmB7GC}UX29kHD z3ko>nyLKg&jb1&YSp7M*e)1r!f$5P<+Y~lyvXD7S*-8t?we`;f7^CaaS3tYbqU54( z#=?UN60?e2E7Oo#Jyx(4MP0`G;9k4R(b4Vyq9q(1M4dpn7*J8-M6cPMm~L9K%n*E9 zBYfq4Zws(<=Okm6qEx0XjU~KdHDYyDiEI0#CJMCcQ*WD;2+HqzOUnXXZ+=0; zWT}3rl<~SY=Y_U6^-;MBX-^VvTGD}ag~qBGo^a#I9h7<~RhoY{NibWj&r9!uGyWUcf%5jKX+i382)6u-I8)#K2(4$AdY4=(Woib7aXc>>o5gS*3EjIf#) z_Z3XD^$5q$ms~TF9qjA(+o6m>$>V0gN59Ug7zH(aux~EMD0>n*LxrFIamvE6XJK$V z-xSN=(6o|I8T-UWRkf5rdTcMoXbf>~=@j>dVBY~iWFp5jNlzUI+n)g~x}zUI$yRE+ zeF{nqVNS6^&}^(d-Or|^w)x{ZxYk+B^zl#Vz7SGh)KgMl?F#sGaAAgWb=&>oR3Wic zE??7N3kYJhHBk({+yLkN^-0+u5Ea%qT%SAk6P5PX?f>bdHaxH&&E?6noa2ELnMnH+ z`g{_LSG<~Ce*ouqG8An21O){~@TR+$A`mHC z#!l(4FE3J@rbv8*L{YTY#uaanF{bznWK_WSmrN9L7P`NCZ+%D zexaeg{LCGeQ9iUc;i%ZB4Aq2PRY}=be?0FM#(XiroD0mi>@H*w0z&kZd;_L@qag3w z2X+A^wjjZ$v8En3f3)D1Eh z4b=-r9D;AEq9BtJTdMr32(uJrOt*8sIMU#hKBY9 zDnmOFU$%!yVWJ>jzus)pF9H0uc>9yfG?7I#*1?-A71hK*4!J+IrFX{yK6E#Ik(|6Y z{lG1~&o;X;oi=ZD(N^Aue3#MnWK!RV*I#M!AD5r}jI`C;8+%ok-^3G#-<^{0D=|py zhQQGXS1Mre#yTcSoxxA~HuCImXsJ}3)On6P{9dTz_jaOI% zkkmx}?0A!&MN`HkU+N)1seTx)sV{$;v=IL3{;;U8qC@K5k0ph6U033fi(x^)56;|} zlz&c-OUJOQA0@i!svT*5^KQ;^djiNyuhv%SXs7xuoj*;s>Sn1LF$`j#0bi?n_D>>t zniewXV+rP0O;2>>1!gd${}~1o&(}%oZD4ix!lbB4)WDc@T3|prcH0z)lf${bf8VmQ zwwN(`-sE_9BkPoXiF}~@s{?13DPDQ6Y$y>2bS4B{{-IJc#27BY<6MAJK~p}scQWEe+EvVbkdTICar~N66=OJ^+xPmmqA$0Xh@6R(8jZfix=Z_6t%(m_RKFSa#>g?DE0FR<4A z+&yG(tx>onzDrHYH1yBT4dxeGfa5*+(fYIjv9#44?k~$p7f3qYE7b77Y`0Pk9hZ*6 zn5va5?%C!b`wrhy)~p~6fHUZDC2W2e79zY~I{bYl=PJpc9O|G2bnQ@_@;>mcsVP=F zOn`o9#wxs+ZUE+eSp2qQ8<&Hh2E#(lFFZ8!oLhu6psya0iuf41X$yx3MENV@Y`;3D z(fx*Lf#CJj%PBaOhIru|-Vpix?|dvj2`XAJuRv=&f}d5dN+x_Am2klhoB)c`l5@Gl zX-h#=jA5?FsA#(M{-L#Kc5gH!*F6i6aNL`4!dxHJ!Ty#k>gLqatHAXFP%b$rCI9|2 zVm~kTloujLmcbx-jS>e6F=%zvSl{=NT9P+MnTPXT#+C^EceYzx_oB^_ zZJ2`B8tZ`9ZZ;pHp+6*;>06!hvTqYE^@GMgwiL#T;x2dihUeEhKSfM_zO#@-&W~A6 zq7z$BeK4?eH@(9?X`a6^ibAgbGP84$7X?r)wz?bXFQ`v;_9Chy7O3o)KSqDe=wnna zXu@mXHUYeUV>I%0Rs3xq zZAH)TTU7N{BK|SsT_$Z(gEZ{bBB8y63J8*Ki-#UofLZG#o31iMhl{sqkb1At+;tHW0H=iFEb7NKL=zKN0&Ciue)T!rhc zW{Faqe?Oh5EA}>fgy^t^HG#)?N{Y}OuIZ!ItlB_DT5h7}mZp54OkiTq1q!1#l70&H z#Ete*|6_B@=a51@DFxL`tDU5%gL9&5pgeTyac2z!hl0v0f7r0DHx?bG`TZ#P-SP_5 z2{8R5&eIU z@5Gb7g@8X(nvp)nIth1lQ~5(gP!1a0;`jFL0!aQ$I|EV8%eE#F1F?mxLlkff=QxV5<5kr!&Z@xE@81RW zJ1wKcj{rJYhA&_uGj7Tsz|#c})0!70a!06Bf-*I%96^|d_=0gPX7-gDD`6hZFkyV1 zcr;%LH$tUoUhZdoh(oj+Fw5q&-OwBFHX>BDjw=kE(4maQ)Y(mYM3`};=2USQ8J*4k zYgP6!%9|IuhT6E5>3c&_epyjm!4DbQ<@&Weh&kEeY?~UN`Vr3)y7Qfr4HdE+Ax7|W zvPu-*y}tu5qFeVatot@tZG=ILxc}MQ5icLE*R>vw%`Ob{d!Rh>Ma4}7OV5}(7K?it zlYcsG1xPhUu@w|M1(GYw!LIfo64Gj9UX8e!b7ADM-xKH_vP)&fwf5(v5Si3B2`T9F zXO}-y>nnTg03BdvkGoM$9S(1HP@)>VwW%slLfgJkSwvCTG6S8(Q&P)zC)AlR&iem+ z2($2iyzj|6=++<*T5kC(xMRNZarLIXJnTjzv{Qlk)r=>DhddcgWcaun{|rG?`|x5N zc5z7jn4x6V#{<`=UP*89I)$y#o-a?NRfW#Vh;KL^pAg{)b&NF|v`n?d2zLJs6nvz4 zD5wf$Z17W7*;5*QS7GY0>65*|Q-S2W8IX5f@&65f>G8{0->Xn>>ip6VzY{yY&PKr1 zN}Qw_TnNvrXv^5~ChcguKUXq6<3d^81;sLbN3;u=M}lrlIfd`vgSG%9k<<<82yqQD zeTpe^$VruU;gk?~9Azz4-NJ;iPgK)AylDYAF^bnL#l0;PhAQZg;J^D7dTJIQa!cIBl#rIW zz$Ah+W1daWQlOO5a(@yOMqb0D?1`?-@;}9#-~D=gy@(o-@4`H;Zu*Q zw2|UTUeq+DDIuGeeQmz+4A@T7k`faS{Z^COX8C6j*+T}sv;ianN9vkb6>8}a=^Q7w z)vUp|qB4_OhPY))bE@SngwmNaJ$#GZxL`LF60r-lGnS*#oQ+dTk1W_DIay{W_cj$z z%Z^xGH{Ubem!;txL~|a61x-j@`-Yh@>nVL zaaNT$fa}xRaQ%5Cj_~Igb&-nAJ@|suDZvXtBhLBEVafgZwj9q62~#!zgG;Yxu^ik} zoS`WEYTM5NY;63m_p;Cghe{(;N-&yq^i(IaqvJE;Bcq1gkYo{om%NdMGV;g9zT*g4 z{xCZ_EXj=43pCH5ZL_*R!||!M@c%({v#pGlrKj`3m-VKg%F0AmYId-p97MA%Vq^RY*{p zzFRah;jCCb80&~4aIqHSyQ8*OMMz0j+!yhgPma8)DWrx_{*Ui>xWNpte0+t;0wbiCh`G}FX($xTshue9#+x--4mB1>-srvA zHr1U>7feIu;&m_e-C{hhhgzTIzC|CoLIC%j88y2LMrR; zGud1!a#n`FyXx)lV6Fl0;x=RQ615c&-TxahRGAIBfew^lAd25}Za`j(O_iT9%~X67 zSLDoz%#aua3F`>0P^S*CQ+-_Ny-O`}8lWU7QmYg|BSznS{#M=7FC4$pE<;_3OoyJD z&ml1hhC}-@wxnXL(p}NH^aBrfYcFk3p=OJ`OqxPs99+ZH5n{F6U&D&5_S7XE==?!W z?sfqoDlVS3p%iA=U-x8UG*#vn<~>K1hnh2v@O5=$OTHg{S~c!BVnjO4g|k6YRsoz% z9T)9Le*==#WPCU5-XXn2!lt!qe;wbuuBT?W?DOUhN1@&VngVM;4_N7ePO*oRpf<^} zzbY+oIKZ0*&hqp4g3GE~o^TpNNfTE%_Y`{hBSDqS38cRDlQ2n2@aAkuOpG%yTL|E; z`NaF6#&U5?gLPv5-7K@V`0~sgO2w#?x@+$Pc#xok4C}u zor`pe@_Oz<11QE2X_rhy`=axqEJX#mBN-E#tB2XNoZ*n>jln=~oj8fr-;K*VGZtpS zJx|1kvgb)A%5>i^6UUdxmQNE`vHZ#!^NYU42>$-ioFH2zhix86q_BAl6p+{Gi#*jL z+1HqJ!a6~oyHHF(Wm%=HpgfUvQJI|LK=nK^?i?=`?_pE5@D@{({rw27EA#Av|AugVCfp3QrEpT*p$bt4dpeeI(ez`Ru%Q>SW(Gn(JMscx-d!Bg2uh$c}lK^O-@*<;l< zF+n=H#-VOMnc~+(U{UI6(iQz8Q`ux+RG1&FGPJii;}Dyp`ju@SGM3!}#2zZ2#wM-i z>+bhp3k?F-wTy73f2F+n2*)#nX+PqKx2?72|D~!_pvQ!Ib~kHlttyrF-Ux;P2yg4^ zUOxz)at{%L|2TEC9ZmlY?#>FZK=bNIUGzv5n4Ga}2a817a);qc8GDw(Mj`_uk6Wf$ zxqc#~Wj*~>-T%gjAjkHE!-V5bVY)iA?_T8@P&0<9-vxkpOP0M=ki-roAgL8IDvE3K zDkCRYZ_Jq)2D%fVi#sM}p`$t~nI+Q7e;TyQHnaMYG+Q;MxZ0 zF(2&Q!Z$ahvQnSu7kc*IEHRak3~9uV(owQ|h4^6U zK3H5pZVeAhlmMvc-z?CwDdJd1bA8>TC|&fxiJohnGssE~E0nD@QmBQ{Ja7$PVu!LB zCZr3OLq%%hpupiy$VliiV;^{t^cUIqV4GULcG#+`#M-#@`P8A$=F+oqGDj1|q5bT?LcxbU(?|0M<0>a_sxArwGEI9hKcO@v$YYIkB z$mLc5n^oZHrG!01({SXoQ#y%>C2L2HQ4s`6@M`h?gu+ha4mHe3cCa|Oq;V8Cqs4~u zf;V7>qMncGs7^jR|CmPtr!PTQ3oVd;Coo|Lp2hgd(Ku>0BHAg#)r$4wVP1gV&E$3Z z7(25`6HO@PkI3%rM@--RJ*1u3Z9e#8-X*6NxjMkpCWd63%lVmTczAmPB~Dc*EUr;F z7uJ8A{iLVZA58U*O+-jSDn=OMQN`w9oGd3GHL6Bxg7nmaA){W{e|GaJly#Q@?C8kLtjZz6= zCH=E&a~)pZz99RIKazFirUCB|l(FEk7fLTVppW3B3bn0JEjW+(RwWY=@C1a@_i_cU z04kHTxFLZo70M4mu>Xo0c=Mk7D+pj)hoVUvzJ*_eC&NAc8K1q|(y@R`Y2d+`B&P42 z)C_q13w?OE#Txm#0CWyHZudMVZ{4$q7gnZ%huL?&-HYJswebNYJHHN+lk4kbluLVL z7m*6eS8MrXPNdF*d0w41n;vFxZ_!wOER3lw#zpXzM^yQ83iLM>GICu~5AKf6^56B_ z3*r~{04p7Dbd*%XEz3;JN}Rz`h5sg_yA+Hm_aRH9)y?u zbA9%6_SnQ}I(S>P_mw>L9n<(*v)Pp88_BQWI>UTBtj!ZOo~Bm+xdW_U_XN|xXrZDh zbrYN8q(gXQxTI`0e{c?M!?jo!fy8i0i{y2FS$Y#0TfbHBY>9>cT}o@rch6k z9_GLJvLqUKdF4E9GM3D+Z&6j^#i}#wZW6)bY{f!)vlsh1;CYEoMn!9{7QqfOySkd= zQ%(`4&e!Qc?77uYMaw$r6sB#3yPj~R?{?*;Px3pqAiy{b?7ILHUpyI>1}3~Rqm+p< zCNo3#0!bJ1^K^F>4*+yCv<4gq>;Bs`lP$y><$_X?sxtDCqs=Jt=9kPb6YDw7!zsZM z(z$vhh1hLt2CDf6Wh(L3!>zK40NVx6-RVrPwIBi`FXAFP+XBlq;BjhM z99z}y_>zA$deE>!Mv$V_LHPf>9th)0Fg;@=<@BdZ;Dex*G=0)nzT&#Yt8YK{&ivFA z&Hr@>KL!9=1KF7$VNcudLoy&zp>gOwq^J;Ny#9E8rm;VQ{%L_ zMd2=-;Vc4+LLH`SK4p53-`MTFh&HXG(5^pKyAHQaI^Spk*Srdsz-MxU6QsCMdU^6F z?E_z;7eyE-3Tk3}jbX{Psx!}KV;#Lg$BxyNmJ3Ci{Vt+alf^o6wa*`%rR+Yl)eb{9 z*o)>)N)S6rp?fb2N)qcL;CT0ZW9*)e$tG82w=6{%jAs6F8q1v6t!xT_NkFHx4HQH# zJ9n`z4u61D9DSpLg%q+0Z}N&B!)ty!=t8ZjS^tV3GxF)#A)H>9H;Q}L)Lk$Vb?DwAvg+_Y+h2e zi(dDM7f4NH{Z(uYlaSgvoT7*OufKyMzMjH{ zlBT9ZwnEBHuwYm6%fQ<3%r;Kvu3C)Qu-%WGeSY0T)>mi1SHe$Q7qkzvw3{=TzQ-H9 zwMfcb;(an8t+`<+#@>JE$l5|c|G7kW0(=GX4_QPuT(tS$vcu4;P9F+szEBZoRvY+M zM(q>;J4}c!grJ#XQ)muDd>v(z)s4MB_9N=rn+f9CHbt&5^|UNv*0@-AH*COB;DAc zx@p20wHbXI!5{GM)m6xEyOInV+SicKPvBz1pZP`@Y5UE+I~SmUC>NtGSJAW12>s!B z_&mGH>FX4H$sIn0D&c)CwvfA zg@#0zBmW&s)>yjiMHC!-cr-rTYN?*oTRL@Sdkky>ThaW)IRi7 zchd}kk20?6I46e-M4eqwZi09mj69IguNPgi);v@d4nk`cyE^sy>IOz|q!AqY*aD1(b zr>d$@Ue6$=0Xvvh&70z`+9ZVIEXYew&3>^4Sm)!p^sIR)yKW^wV-vG^(;JgM^FXzy$Vu&=E>Az)#RGP>@UZT-N$>P|i>OsD-SY{j$at zlFJRhs5UGP`6MT6;?(eZe42Q1+P?RRfgH>aOI#%bEOgHa3-iLL z_|`)s2Ydtr@_|tIkd>8jV{H$N{me6tQULZ+9$d{0)#iE!7u4dJAYx#E6vv*hePVV| z8`O>6DULuKWu0}K6#rUlhApz7hGPbc;J8WuqLzSz z4&6@PQJ>PFIl2}yCoN;!j30$Cy~nO`$SfwV6#{eEpg>L1_~W@N0=W`C`5pt0;yR7B zMba`%>%N+R3o85lBT6!4-^-wqIu?oBK0Wq!Wb%n8Y#Cr9myKTT ze1KJV;?;B+H81=B>8U?aifvX(R;uhY9s2*aDhnN5qvuBO?uk$x2cXUWhd4hm|1QBJ zKH1GKsf@j!`NM*%;L!PmP1Bfyrh$bk?T!?-nGa#;^*~?$DN{hG^|KD~wnr=Wa7gJ~ z{{!XSU3#G$oo}t$6Jj#?40Y!UwOPKbDL>qA3Kc+waFr5^M-~IiH5DqcS<(_gs~_o zq_-U(Ks5yN>U6BBY@RTaD__PNb$@tFUfVpJXl$6&vO;EHLAiqXq*)=hfqWH856|Mh zCL$aaVJ_QT8#`{RV)E=VC^-}Zn9?Dqz#)<{uqYppWZ)pGAi%DvNO%@K$7D_LzkTEy zJ=E3rsil2leLwV>@+b0|MVETz3)!BYn6t^k<3-z;zQ$);6%#@(s-)@yQ zh}>__NO$euqv9c1>9|RtHkeQ z70{~y6`qAUvV)L39%1eUN%^Z&ac|%||A7CAI2f~yu<{~`j1lBXftaqJpA1s=5{0~v z?g$Q+O@omJ?L}EE+%nbP??}-<$_x-b{B$RA;Y-cXL^_Snv4U075q4w!npzV6Q-SUP0h^LKF z@XNsYJ@ZH3Jp~?W$B+&HjgKBmh>KY+uY8}1uhTCMJrNn@J7FKgt3bfKCr*f%W zRf(xUI-Rxqt@>pJ&LCU-Wc|KyJ^N${ zY1AT908;g@R`LJ<1z!LG;UYar*V-f`V1hIAwwo(VM_p;*n3!%n%oOYh<%FF53oyfc z{E^U0q`mV3)RS)!;yC7G*OQ!IprU{jbh#$v#JaaWG!JEIa6P-UCN?Je?_}m0%rBFt zAPAbEZ*rxREj*{z7$It@_P5lvym~8r4RP*>(I!RGXr_yVOG>X+MBOK85CVsf@K{4@ z;Qa^6*?eG}s#U)LlCpwt@nO%0M2gBC`JqzG=mag;4#%68gPRT}JXcjQ-X<1RsL@Yuj;6a^w5K|{o68r^Pn!X64y-PIlZBC1GUO?SU)Tb-wS$t-5!vD4+as{Mmlr z)iR^<$`5Le{T+JKUqHRtm^U(Q!2>a5;>|PMx^vS`uu?4R^bj0y1UtLA03CbPiN&Qf zz_(54g|U!uoey5Tv?0Z2@0bL?Fcr$iVKp1f{Nb{ZhHkSPMQp%o?z=`r#BMpn6vw zVi03JM;^4@GA*nQ0AogkEf&cE_a5U4oP(sXw1Yisog)vD0!5wq1y-MdC#Lb7Ohh<1 zQuWBzBNBjZ>QG}HUTfZw35%*&N|@wEzer<$i@eBRS1ZtTITCsfHwtCQsShIIMKM zCvtg@Ae8Dts?y_&P|+agIIe=I)@@;>F^s;=-104 zeE#V=z9lqzZpJLnX1nc-z0;3}{;hrQd`8u={3C0g1GW-$7AnhoHWaTNn7{ykRePjRclG=iPyhe~y#N8>Vy#r4+#w?b z36BdE!Q4SiZ&w^ZqfqV9y==K}|*`n;Sa$SnT;4d!i4m$&d|2@!?{*v6Nl5j*FG{s!nEoJ3<7%TCm?O2zl0S@d^oo)*(`h?d6hx?fe^}Q{1N|(| z7fSc)qgek|RhGq(p4+KU%Y1{WQ>;OTo34Z|SEPn4ux*VyBnw_$2|zTkb1!RV(# zMy=;TRB6JPs0%@b%s4nR-q&cuTYok0iA=>@}1I+ zSs?yp1BeV^e&_GG9M`L5hViqE&yEG`^iXSX2UWc14GCaE|28|T9AU(wXy1XL*P-oW zmGy*Fu;i5r6;sIOPQ`>}%(r1;W`uY` zbRY2W$wIrM#%A2<#Xss+RD($cn8axYECjS9rG zhbEA@AbC8&ssin$E%y^91)$fCB%LQlcjEy&?Bxt@B7-io$uU~kU7>qL$k`*oNw94)Nm)#5AqzU_ zpyDIXG^mD+5RXJ6>)jHRTsryL(yigLH;?w(V^3 zz&=ctqZ#wYwy@XdV%!>G1G3uKI)6V_NMY#|&Q;oY^f_#6n#-b78_PS2&OUWzw(I}4 zu_AEWf4BNvH#>i_=}#QP#CBUW^iCR=Qj?^>)Sq5`m$u^J0tf*Nub!33*^bLh%F zE&6wj@t!i~E8uReo0vK-B6np{nDqpufeQ*OhIaYCqmKTN*2vAbP$5O{7>9GYTWEB{ zU7~IEeotuUK>EPcgl0&~SG<6wrS(Nfvy_D-bwghsSblmWK!rwK7QH|2Wi%!ZaadiX z`EnTXzuq17%c5^nj}SSv42)uPEt?tp5WMsd|nMy?AE*m$^m~dnx^6ye)$09$q4BF-hFb=#t z$C?|>O6rk##BdVs%-Yp%#qFx4$)_#sA9y@mJQsVRp~9t*ryPrR7SRb^$|*2h72ciQ zA8`(emmseOHFM+eRE>T>BS8w+N)DnZ#GUQbrUfDwt9PuW3WW-Y`&r6 zLweQQxGSXVm9uhSi1nGCu_!9A z0);(FA{RBT!e@7f%GgY%9oK3|Mfv4ioJIqgXOowbAmJEl1$Ero{SK&`=qI7?x=cTj z5(Cvd->brscsTZx2pYJ7>=ym|2pLWI%I=#BW~Ob3m~E+~G06-9z1BQF7c^^kxv^h> z_*_OHUh92`Xxckegj+97aWhuwmx=aZAbjp7m^oa(jG7J8ogTd@~ROZl1u_*pUIZl4>eBUpxrril%I+% ziRPPu(#5@)0M0Ks?Ya$E6<~97Ku0}IHEQ%Gn8~Vt&bfrmUhOo5yvKpmD|CFnrTl+- zZRvcJ_|gw9uRvG!9KqzN2h`o$B|>Y>Qm?k+h`TfS#PY~mVrtj-Yyeq6roTWI5ZR+^NFy-L{^8Lxx^?5{m zXjez$(Gw=j$K!PJ)oIe3D)d5VxA+#n=Qo_>pDLN{ZDkFn?Jy_(+Wb-$$Xh5km}pBK z)a8G18SO2h>pEf+Tt&wC1bNe!4;muLguR_Zz&W*<`Hj>HT8z193i&{;izq<9qU=1_ zm_=`0xKGNR<@8?V2vn%vh$j=&ZqcBH%j_0!V89|P6Fiz;F6K{{z~<{go?T~9Id=a` z{Dus)s9witov2)3)q8-56HF+olo+8WEDz5DG!tUU3lVBtr?LKoD*|*Pb}kQA5J3Zy zk~G$^8P-l-%&Jj0K%(_@4tK29Hl~_#%lMKTQ!6@}ElRko43*>i2q?+;x70K_QaJ+$ z1J-i>EcMmWa$XJL=&~A3@wZ_mkLSSSPQc)BT_ul(z+>=kCVPF@W1rDb-Z!@tX>y$y^|i-4F!j{EI~WXSFxpJ5(YNYI5ZsLL2wA zN1C5+)}bvdTLw*b5tpv!)FKfeTQ;T2hx!imh{X;&u)!6z(If& z-Kva|Y#TCul}wjay0+-nk(TFg7B+Csr#d5Auex%H|CmZtcvVukM zB62y%t&h3zN!pgNgAW_elkuWoZ~K6ayFAi0paoq?F#YyGZ`oQWWE}ewaR?Xy#5z!v z*@Fw6a~FxjuYEV)&$bdX*paroyyTX9cHM zf`Vbi7|^KSk3T7X0Tf^0`a~x1Xk3s8{Cg~MJPWnZ3Xe;kAceyAg;*-ZHqG}Prx5dF zdJl^V1N*T>L+e`NVd#tIrAgEQ3;V^n3>+-VcHO-Zq~0*LvJZHfgnN@LS&O-d8UD>U zqwBuuGJ6%Ug6CAuigB#*$<047lrYpPq+6!)POiB1a3q{aNXjp5)cKq>*{43>gQ5qh zO=Z_UCUFu`5M22j5}py}$VCPv!NX^GrzR?ZJMZ2zT}T1S9Uvi`Rf(AjMeQ;3j-+ zVpz_$Az63~`aTaYwVU>w4oM~Holf17GcCioNiFoB#ODKQ7;$BVzfw(Cuf)B`3)VWy zYkc5|5W&-*12Z|c^g9?tiEw36`u!NautXQm4Q^&9j}!T!0!%;;zdO z|5Z*^@&+3IR%&7m>)DHqSwOvb9l0Z*Nz1KOOb|FOwJ!C0dpuQZA;Vo50o3wWL0(pW z>7nRQqkDc*1yfVr?bDfYMyJr{G=))2Nb->jh%o~ec)|TIge9ZsBM=ltNY32A-<#xj zT*J9|*9IO3UPpQZVS9_w+KZs#g`+<*sALH&F?O;?wBs%s<*@^F<1a?hM%ekF1|bJI z!sAUxkYwGsDa-?iK3|*+gIX7m@6wfhGctd(wKT&9)l-6HDHtpNb zKzHtQVcv%N3ctZcJCx1sGnYax&}n}VS5ddxz|;2L6+*2|T_3X6iwSU~IP;tOE_sK; z&hELeY>?`wzPIG!{z{<3`%GrBA(2(Pv|%bQ+hMQa?81yB!rbTK(-U4st5vA{-I3e; zfFJJnZcG%A%oY^!2F)b#v&#iWzF;u?TGxnf*9{Rf2z>r*!+4uL4TKktcBGTF+j}uH zvP%Fvz3m_q6Kk?lDg0rvfA07}svJp(OM_+5t=ZO?);Rt~HKHkjP%ruyYLL z!xfR{&U&nE65%Z|SFK_n5AsnCflW@#fD)@_Ce=#6PT}1IN5Llk3oAzRoJPoD}YLDOC_eO%^`gft}y_hC3(?aiy~{ z+u#C^PQ5i3O4EPWK?NwKy{>a zAIuvTlcy#=gxkbaK9SEOEl>QqrP3+SK_ofEfXBBE3(h8kwtBp;knPJchsG5n7;UjE z7_O*H7i+IBYp%nj~j>4p!vV#9#-xhHIuth`?r{|nof{Lb()SYDJp~gMj$J) zZbxJXd#wGw4ffV`Pt*-pT^<3s5m|l$^+2j0Nzih@TqnwiI-rHS<99&b(*$+pnudS zo4Oj93H>>liIyc?=yy1c+7U7%9#7SN-NeXfc2iF%%O+3@sK+*Iojy1-ZBHaa7cYf4 z{^vH%_W{J{_T5Wa`UNTB+X~c3h;c;EIq>v8L6%y2Ja|WIaQ0^VO0+7WOad;QVZ+?! zJ`>&fMCr*W9PwHlK)?>#u@FYcfURRH}iHBZD z&+mr!2+bVpZ+iM1&moL^a&fs&;<&;2Q3WuUV;jpT$}O>AHUGWfn@a=lMnD|1_B^{Z%m)l0ZgM1?JJ=3r@7hp z+zRLs3d9)UkS#Jtnejo=|J3bOVZp}ON=7qpkE$?#*MP;~guNs*rdU~!#X0_SaF+v_ zHd`{dKJd|~MLXxa%yePiN(a7h56QxIEtHF44aM^!i?C02Q)Bw^8Idja3z^}4t?Bex zHXJ-16Qg{RmaqPS{h3$&gqBJN>~wmwAB{Noiy}SrM3U)65>~+hR z&T6@MC}^$(s0+s|z!F+5AeCMDD+N6B8-IaDM z(^)VYe=m1i4fJ@bt|d8fxK|{ImSSbq=iG34U7M2tHd^mT4bGfcDkYx&Ay?Fq{$)7%qgaxk5Q}|;_wYP|pj!gTb zBMK;EnPqrB6qCfjcYgh2Ys*dSPM2v?J_1N^_6x8DviGbHOZUyT&;QXugYg&3d~(e~ z|Ez&}dUx8XGlW;M&DdijN+TeV*cz}wFC~*ywDDBq^^xj(Ap$>G-fXcK=j~#CG^eu+ zl9W0B#*+EKNGH0GJ$Ys|>d))G4_Y<=>%5j%pm7voIreG+tN$46Ir!c!BGEhf36*xL z9v2xbXesQe`m8R#*E`C~doC4s#Q*^{j=yFy!%mwmRSA-!>vM!Z28DGZ1Z%H8=7qUu ziUt~C!w9|0Gy)aL;8Na$q@8F4$+vbOM#D2LHDwk)Db*dr2#i)nazSr=D@s}!yu=)3 zN~$~#r8&$Z3$qX@&{3FpasDN#;!)B*O(*_f5Fj z{AL+J+fgTLzD^!-a9drMb@whg(eK+wY`mmGt#g+joxPm6D~50Q;J#gLcg=_ZJZhS}Gb` zzk%$(KomfJ;aed74O7b;eJkr#TZ>W*2NY!q;~`8uA5x7~cS`{a?CN*siV2f5$$DxLB>*PYVk z)6(xmbB@LQG&w)Re76N?fxJOczG^>AHxBn+k1Ke)CHkcS%F`oD^-8JGAepN+Gj@92 zq;LeyBNH^Lb@vrR0i|`-HtbHcpB!{^dy)_>=1a=LtYst+^tfMPaIa5LZY^<#+OLgM z11eIs!~JiUg)6Pw?x^?kYZcKDbfAnLjRRkPq1E{riykk4_4m*+Gn z(b#j|7S7AWwrZ=(Uoz+>817DiK^XcIlT8-+yKZ#_^` z1#q|>61uW6rGfhc^pkmWCz;Om{8o<}Q^@T|t4XYTGSAy)ZWze^V1Bq7Owo8NkdA(v zmxbE`hb^(!p2fdsrMp(r=WgU&Q(Sivx*7v8+QjLCxu%eN3Ox7?s1UKvetVBz@iz^GTO8XB}qHbFR5gGG^`L^ z#V2&!RGRp6iA*_I&+b+dg{`v=zzIG;zK^48^o-O#e82IEM~f=n7*)vBF^#4yqo2cKE)vE zH<%8j=ap@Z!=4WEG>J*`4ms0qev*>1PpwS>+Q*Vx`%PD2lD?l{&Y=90Ng8=jY$J!v z2CCbr>Gp4$CZNWB%s3SQv^UD^<*0qvIxu5&-EAXWVy|c+G-%d4v1;>ocGESk@!9MT zj72^?f8xTzV3=YH@ER-V<@Bn$p5{M#LM&mP*lbt`uWP1wh^-dF9U8Ns$EUR@h!hIZKca3#Q?e-_6+5 ztPBIXaS4wqG%4+4x1XGW&jopR{4~X-jq%U-(iPEV-fGsc0F+)ov0#~79F&JOOp`s{KQoNVpRe%;CTWE&Qf}dybHrf!zn^@{{U;+(f1&l5m<%tx> zafE>Tz9%mOU)~QSE)X?xFH)IJ`>1J_*j>{Rql*%>KO*N?dGRnCrJ8cgcKs^n!87P8 zw(CbId|~FtL6aAhy+?s&w>Sfm(2io_pr?W>V@IGSm1A|1WX@*)M`;+&T>ZdQ{@Umo z@C79h^43rPEo!_l{fpWe>%Smhthd&{)Xy_3?o|={sWfmbOlMv(Q-R*DI611p!}uk} zZvVh?vJM933Ux-=xi`hMsVO7%)yRRQIFur0yEg*>gCQPppJLqS08t#=8J3n5;yjc! zF>k)bP?UCC+@v{4F3E&ICwI&F+5MtAWU+T11_6>sstF9{Uh`#fJSq|Hw_#uzug3V3 zq?S&rc1@IBj<~kQkRnTxuD~`C->O#`b!CQ;#}Xf!3jIM&iMIYyA2k{M-RY$PYA@KW zCn!Q{zzg5$D3X5{8Xe>s(0Cn#)y2@nj*HbB=4=Qppklff=wcUR##34RzGymT$`#Z2&LE0JTdK;SpS zTDC%vS2FLyNOm3d`x>Bk6$-0iSDEpJ-{j%C9^F@Ow}ePGd5w+;zGgB{iNInVxd!cqq{fY)$+;eK!; zWa||~Pv8;m>wFa$)0nFhKb!P{9G7a5d|UK5;;W%@!$%0_M_VmS8qeW3*S2f&SQu0L z;Hoc`PxtQk5;gLN4?;mh--`G@7;cq`>&?eHN zzgp3nB~VtL6!x5O=^((bOGQaEW&Qw{gaLq#0>U4*aQj$9Kanms2DBfY+h9i6O)Ohk zJy_E2@_)x4aTzQ+-H`eY3S1jE%Pns#26^x^n_-Q>W%To*UMYiyR*X5+zXi5g3xl9p zrJVh6s{$hpTxKm8v+5yzsp3@~kK;2;QM|!)Py>J@3UbhN7ql(?U6;I&6DG9UF9k6c z#}@@~S0z~Uc~=+C4I^rshz`&U9iOYW)F#^~sQJWmBj9(!iTQf{`#;f0pSp0GdXs~# zx0n6K*2z{dXxq3BvM0XkgSVpqJkD>tdVgdj=wAeL@bnw;$K>(YUw(5RYv{vTH$sKm*4~^^` zs`}ywz{i}JBnb(ZbmhM-3Nevy!8E>3TQuz#6gbpVWle@Tu^W+}%X4|Ic|4zOm9LRl zpl`=rxwP+E#!7=enmN(?ZZ1sAeJa6WAcxsEXs|i+M;$#;7BU*nsCD;ro0llE%^1+ z1uvPw-%(Y%-LO8$=f(ge4S_>fcFRSlOPV>i2Ca6)dZ=xv2tU@ni#c&=6G*eyj-iZHlgfdao`Sd!V`eP(r?p7+o=I5vOT; z5Br7|2xIKZ5BojpIi9*DF^upOw)wG%lL{>+P$JZp;Z zJC9^3j!#CTn^7SKDZg7B*dBfYE0~A=9@^1;!A>3YgIp(&lxEDaAoROE#M%g zGhqn>{94gCF+(9d!Jc=zb0=_WTaJXX$Ce@@$h`X9&&@fa_9)VCepJKU7pHWl@7!!M zJ4+JbVx+s58%8qD@Fu45WC_N|85K5=vqJn4!H#Z`fxBywb#neH&cMesd(LNH;*Gl0 z<~%tTPF(xHw$H9oz884rlNG&1wL2Og^2x(KEtP^%s=(aXj@9e~w^B~N6EN!otL|Bt zEeKFbRs81W^rc^ezMHZJnJrIeQ^d)t8e7p>!wjlr-<(m(Ptkjj)E?bf-j&bro;m)q zxGXMt!4!oRiPlo}uy(FaQD@9%S|qn;*&+KLMkm>=%#hmoZJR0M3rfmeB@+3DU~-DZ zL)TFjToY<_=!MWXpCR30Ozi_XUrR|WkQWLwU~>hUAaVSj&97FiEz-B`QX5PnaFKpW z?46slv2L0HyMLgj-lL=8N;TLorY9Pgn02Ql#_#<*+>D7m<4gXgj)Ex`sf_WiR1Ux^ zumF-kPi%9G1nhS(TRXjdCrdXRw75Wxrk4%FljQ;w!P&tRK(HapE{eP4h&fiPH>|Qc zVg^l6Yny=cdNRF`5+HaY)d-AU&cka=xgwLTM@-QoG0&zhdOh!a1`uaU37TpvU3#cO z=8ufn&it<77AVNc4_gOvR{5|e$+U-hi@@>ZA`1O$*AU?|&Hg*HxBH-&EBb=?cpNMdzmp%U8Yvkzl)k6}q`? z$0ZxGF{wd*DMCgsIR~u{hx$$+U_RAgh`!npg4?MIUH$8Y@`&bP9Bp0}XFO zHYv=bMM;=|2g8#lGD!~jx@^NC8RP^G%c264Zakw0>S*-!{x$WJr7WA@agD{dBemtk zt-pf-?ISWhL{*CLBV2~;`0P$w+S4%&e=HJS0(Er1QMiKr;AX!^)09k5N+os|zU+n} ze1%sJ{eAoBQPBQ4X|e!C*mmErHXL1eK~j{axV8e$xF-{i&Md<%(pD*vGB*(3x)c9Z zuNj)yYQt2r0k)OBA^5;WpsIHtuA{NC6^`CT7<9_|Y?+y$sa;=RpUI*a4&Hsf<_$ty z6@wjueiTDV#b32S0=!aQnr3QoQ0~km*Sqxre0{_5>Yyh)Uxc}1YIKkwpN3`!k{K+* zdc}*e8l(h>ctUs!xx3}h2psYM&=EDbZ|iv*`mf>37H#}t9-BhErx3j)VYXfG7aytJ z>qMxfen)E;NSSp)%3>0mI6rVZi)T--Am0d`EazN?5% zi%N153#rN>pT}V|ftO>tH4y=3Z#NVU0fL9irO`aMSoj+U9(;pL%M)vTC0l4P6*K83)b*A!bc1LIks z88TcCzE@oi?LERt>J~l45$Tc&*B*r~F0mb-X@KZ5*S55$NF+oqx@amIY7zG^L7Oc?oD&(Uts6Aw=s8ISw{k@=T%qyD$Bo(Q)b2-_S6ic*Z zAF@~R3w%kn+FqV=D4040g_p85=>xIp^>YycWA+H`?reKu
m5eCuux@ZkR$3&o; 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

^XaH@8cdDF& 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%0DHC{X|>>kx>X>=g*QzM+bZg%%j-+92>=N##7 zh)1_0Nih`?>0(+wwF`O?7fV&Gw0Gy=1*Lkh}&~Y~OY@aHSLLhqB9NTOJHL1B>+JG$g zjHZ)xb{!8?8!Diq{`<2GDj%?Wy}xOtG{Op=4tfDromL+Im5zlD{l(_5TA6OJ#az8} zyXjPkl{Dax&m6@(1^o4sA3kH?W?qsz5CS6S_lbEc>b6;V*0gk90)zz{RWVJ*IHiO< zC@i@#(PSW;F;Q^x+ZUNb6LCv@v}&9Bi9@OVT;Iy^7ZV|}sl}0i*l4&PybbUroaLR+ z7&E5Z6D4r53ut9VxnT+bHwQ|ZOpbSnWLk4cUo19zW1RYro~gB z1~RiLOm72upBc)15>K2(H|}8Fve)YxCQJcFv*%P^YFvm7PbeFsH-|n5rJ3h_L}8pb zz7kG2lHQF`v#rmFs|QRgZP;2ZXEzJ@dLsS-60@BhJSrhQ*>MXK2t3X*h2n9u6LH0( zWL?q6Kx#Dny5*4*RgaJ1cerI&`QHY5P#Fyw(y~O&5&r~jtDGc0#|mpziW|+M-Z7sh z3+>{Y2)f&pF=d%}^A+bbgfsL=)1$E@+I|Av{X+T`h1r8Ra@4bubvaaJ4!Wy#7FM>+ z%7Zir-*&IQ&A^QA%xr<@o&ZxhUmrYG-oxvvz0{U(`Qh-;E_vhOGrx>R1l zL-k$J`w{O_s=jd?Ng`s{Hre+Z;Q4Zy%M&2`P`E{BB>CMNM7xH+$sX(@=ij60B0u*O zBiGb@ed*Y?R~1Q{BDtY#PG`d3tpMG7nY5Pa*i6f_RV7l{$=cIqk8t%Rrts;+)d#wV zg16^fJtr<3R}q08Mu_o%AGvCTnnZ0}rQ7r07rJk^@gW$EsUaOb%ft0JlsO18^adzR zO1a^U4TG=#mWy_<{p%Ku&ZEHA+iy>HMCh{VqVZlStschyFU!uq9D#_Y5EL zU3zv(qCqIYstQc;&NPNm?>)@OUeJo2kx@XUrT5lQCcUgrhNFh#ySzG7>!8CKE_3Bi z!JZp^?Xt9)350QKr*2=t7;aSw$Y%y25{Uzh`z%t0aBxHg&PFb0V8k&hR!>;^>%X0Oz&;>0LL&vL9-hp~b!FU$p_>Z@j5j2C4%+=fn3{*wr7+U%N z0z=JXITS?^i$GhAk~;E!$S|otcpL$@K@;k&&_t^*>#~x|{7?5Q9}#h06h`ck-C00I z5#T&w>LhBpe55Qg8zFZ8Q=%+<+uDkW7X-6v3zk|_KeR_dy}R9I``QQ)h2yY!ZC-Iu zan$f>XUQ2TF

NqSSU$C%iM6qD;&Xh0%tF95t3)z1)OWdM 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