提交 6963af93 编写于 作者: L luopengting

add lineage cache, add update method and put api, modify ut/st

上级 8301a7fb
......@@ -14,9 +14,10 @@
# ============================================================================
"""Trigger data manager load."""
from mindinsight.datavisual.data_transform.data_manager import DATA_MANAGER
from mindinsight.datavisual.common.log import logger
from mindinsight.conf import settings
from mindinsight.datavisual.common.log import logger
from mindinsight.datavisual.data_transform.data_manager import DATA_MANAGER
from mindinsight.lineagemgr.cache_item_updater import LineageCacheItemUpdater
def init_module(app):
......@@ -29,6 +30,6 @@ def init_module(app):
"""
# Just to suppress pylint warning about unused arg.
logger.debug("App: %s", type(app))
# Register cache item updater here, before start load data.
DATA_MANAGER.register_brief_cache_item_updater(LineageCacheItemUpdater())
DATA_MANAGER.start_load_data(reload_interval=int(settings.RELOAD_INTERVAL),
max_threads_count=int(settings.MAX_THREADS_COUNT))
......@@ -20,9 +20,10 @@ from flask import Blueprint, jsonify, request
from mindinsight.conf import settings
from mindinsight.datavisual.utils.tools import get_train_id
from mindinsight.lineagemgr import filter_summary_lineage, get_summary_lineage
from mindinsight.lineagemgr.common.validator.validate import validate_path
from mindinsight.datavisual.data_transform.data_manager import DATA_MANAGER
from mindinsight.lineagemgr.api.model import general_filter_summary_lineage, general_get_summary_lineage
from mindinsight.utils.exceptions import MindInsightException, ParamValueError
from mindinsight.lineagemgr.cache_item_updater import update_lineage_object
BLUEPRINT = Blueprint("lineage", __name__, url_prefix=settings.URL_PREFIX.rstrip("/"))
......@@ -68,8 +69,10 @@ def _get_lineage_info(search_condition):
"""
summary_base_dir = str(settings.SUMMARY_BASE_DIR)
try:
lineage_info = filter_summary_lineage(
summary_base_dir, search_condition)
lineage_info = general_filter_summary_lineage(
data_manager=DATA_MANAGER,
search_condition=search_condition,
added=True)
lineages = lineage_info['object']
......@@ -91,6 +94,30 @@ def _get_lineage_info(search_condition):
return lineage_info
@BLUEPRINT.route("/lineagemgr/lineages", methods=["PUT"])
def update_lineage():
"""
Get lineage.
Returns:
str, update the lineage information about cache and tag.
Raises:
MindInsightException: If method fails to be called.
Examples:
>>> PUT http://xxxx/v1/mindinsight/lineagemgr/lineages?train_id=./run1
"""
train_id = get_train_id(request)
added_info = request.json
if not isinstance(added_info, dict):
raise ParamValueError("The request body should be a dict.")
update_lineage_object(DATA_MANAGER, train_id, added_info)
return jsonify({"status": "success"})
@BLUEPRINT.route("/datasets/dataset_graph", methods=["GET"])
def get_dataset_graph():
"""
......@@ -109,18 +136,9 @@ def get_dataset_graph():
summary_base_dir = str(settings.SUMMARY_BASE_DIR)
summary_dir = get_train_id(request)
if summary_dir.startswith('/'):
validate_path(summary_dir)
elif summary_dir.startswith('./'):
summary_dir = os.path.join(summary_base_dir, summary_dir[2:])
summary_dir = validate_path(summary_dir)
else:
raise ParamValueError(
"Summary dir should be absolute path or "
"relative path that relate to summary base dir."
)
try:
dataset_graph = get_summary_lineage(
dataset_graph = general_get_summary_lineage(
DATA_MANAGER,
summary_dir=summary_dir,
keys=['dataset_graph']
)
......
......@@ -21,10 +21,10 @@ It can read events data through the DataLoader.
This module also acts as a thread pool manager.
"""
import abc
import datetime
import enum
import threading
import time
import datetime
import os
from typing import Iterable, Optional
......@@ -76,11 +76,21 @@ class _BasicTrainJob:
"""Get summary directory path."""
return self._abs_summary_dir
@property
def summary_base_dir(self):
"""Get summary base directory path."""
return self._abs_summary_base_dir
@property
def train_id(self):
"""Get train id."""
return self._train_id
@property
def update_time(self):
"""Get update time."""
return self._update_time
class CachedTrainJob:
"""
......@@ -99,6 +109,7 @@ class CachedTrainJob:
self._content = {}
self._cache_status = _CacheStatus.NOT_IN_CACHE
self._key_locks = {}
@property
def cache_status(self):
......@@ -124,6 +135,11 @@ class CachedTrainJob:
"""Get summary directory path."""
return self._basic_info.summary_dir
@property
def summary_base_dir(self):
"""Get summary base directory path."""
return self._basic_info.summary_base_dir
def set(self, key, value):
"""Set value to cache."""
self._content[key] = value
......@@ -145,6 +161,12 @@ class CachedTrainJob:
"""Set basic train job info."""
self._basic_info = value
def lock_key(self, key):
"""Threading lock with given key."""
if key not in self._key_locks:
self._key_locks[key] = threading.Lock()
return self._key_locks[key]
class TrainJob:
"""
......@@ -325,6 +347,11 @@ class _BriefCacheManager(_BaseCacheManager):
for cache_item in self._cache_items.values():
updater.update_item(cache_item)
@property
def cache_items(self):
"""Get cache items."""
return self._cache_items
# Key for plugin tags.
DATAVISUAL_PLUGIN_KEY = "tag_mapping"
......@@ -740,6 +767,11 @@ class DataManager:
self._detail_cache = _DetailCacheManager(loader_generators)
self._brief_cache = _BriefCacheManager()
@property
def summary_base_dir(self):
"""Get summary base dir."""
return self._summary_base_dir
def start_load_data(self,
reload_interval=settings.RELOAD_INTERVAL,
max_threads_count=MAX_DATA_LOADER_SIZE):
......@@ -955,5 +987,13 @@ class DataManager:
"""Register brief cache item updater for brief cache manager."""
self._brief_cache.register_cache_item_updater(updater)
def get_brief_cache(self):
"""Get brief cache."""
return self._brief_cache
def get_brief_train_job(self, train_id):
"""Get brief train job."""
return self._brief_cache.get_train_job(train_id)
DATA_MANAGER = DataManager(settings.SUMMARY_BASE_DIR)
......@@ -16,15 +16,15 @@
import os
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamValueError, \
LineageFileNotFoundError, LineageQuerySummaryDataError, LineageParamSummaryPathError, \
LineageQuerySummaryDataError, LineageParamSummaryPathError, \
LineageQuerierParamException, LineageDirNotExistError, LineageSearchConditionParamError, \
LineageParamTypeError, LineageSummaryParseException
from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.path_parser import SummaryPathParser
from mindinsight.lineagemgr.common.utils import normalize_summary_dir
from mindinsight.lineagemgr.common.validator.model_parameter import SearchModelConditionParameter
from mindinsight.lineagemgr.common.validator.validate import validate_filter_key
from mindinsight.lineagemgr.common.validator.validate import validate_search_model_condition, \
validate_condition, validate_path
from mindinsight.lineagemgr.common.validator.validate import validate_filter_key, validate_search_model_condition, \
validate_condition, validate_path, validate_train_id
from mindinsight.lineagemgr.lineage_parser import LineageParser, LineageOrganizer
from mindinsight.lineagemgr.querier.querier import Querier
from mindinsight.utils.exceptions import MindInsightException
......@@ -58,33 +58,61 @@ def get_summary_lineage(summary_dir, keys=None):
>>> summary_lineage_info = get_summary_lineage(summary_dir)
>>> hyper_parameters = get_summary_lineage(summary_dir, keys=["hyper_parameters"])
"""
try:
summary_dir = validate_path(summary_dir)
except MindInsightException as error:
log.error(str(error))
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))
return general_get_summary_lineage(summary_dir=summary_dir, keys=keys)
def general_get_summary_lineage(data_manager=None, summary_dir=None, keys=None):
"""
Get summary lineage from data_manager or parsing from summaries.
One of data_manager or summary_dir needs to be specified. Support getting
super_lineage_obj from data_manager or parsing summaries by summary_dir.
Args:
data_manager (DataManager): Data manager defined as
mindinsight.datavisual.data_transform.data_manager.DataManager
summary_dir (str): The summary directory. It contains summary logs for
one training.
keys (list[str]): The filter keys of lineage information. The acceptable
keys are `metric`, `user_defined`, `hyper_parameters`, `algorithm`,
`train_dataset`, `model`, `valid_dataset` and `dataset_graph`.
If it is `None`, all information will be returned. Default: None.
Returns:
dict, the lineage information for one training.
Raises:
LineageParamSummaryPathError: If summary path is invalid.
LineageQuerySummaryDataError: If querying summary data fails.
LineageFileNotFoundError: If the summary log file is not found.
"""
default_result = {}
if data_manager is None and summary_dir is None:
raise LineageParamTypeError("One of data_manager or summary_dir needs to be specified.")
if keys is not None:
validate_filter_key(keys)
summary_path = SummaryPathParser.get_latest_lineage_summary(summary_dir)
if summary_path is None:
log.error('There is no summary log file under summary_dir.')
raise LineageFileNotFoundError(
'There is no summary log file under summary_dir.'
)
if data_manager is None:
normalize_summary_dir(summary_dir)
super_lineage_obj = None
if os.path.isabs(summary_dir):
super_lineage_obj = LineageParser(summary_dir).super_lineage_obj
elif data_manager is not None:
validate_train_id(summary_dir)
super_lineage_obj = LineageOrganizer(data_manager=data_manager).get_super_lineage_obj(summary_dir)
if super_lineage_obj is None:
return default_result
try:
result = Querier(summary_path).get_summary_lineage(
summary_dir, filter_keys=keys)
except LineageSummaryParseException:
return {}
result = Querier({summary_dir: super_lineage_obj}).get_summary_lineage(summary_dir, keys)
except (LineageQuerierParamException, LineageParamTypeError) as error:
log.error(str(error))
log.exception(error)
raise LineageQuerySummaryDataError("Get summary lineage failed.")
return result[0]
......@@ -209,12 +237,30 @@ def filter_summary_lineage(summary_base_dir, search_condition=None):
>>> summary_lineage = filter_summary_lineage(summary_base_dir)
>>> summary_lineage_filter = filter_summary_lineage(summary_base_dir, search_condition)
"""
try:
summary_base_dir = validate_path(summary_base_dir)
except (LineageParamValueError, LineageDirNotExistError) as error:
log.error(str(error))
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))
return general_filter_summary_lineage(summary_base_dir=summary_base_dir, search_condition=search_condition)
def general_filter_summary_lineage(data_manager=None, summary_base_dir=None, search_condition=None, added=False):
"""
Filter summary lineage from data_manager or parsing from summaries.
One of data_manager or summary_base_dir needs to be specified. Support getting
super_lineage_obj from data_manager or parsing summaries by summary_base_dir.
Args:
data_manager (DataManager): Data manager defined as
mindinsight.datavisual.data_transform.data_manager.DataManager
summary_base_dir (str): The summary base directory. It contains summary
directories generated by training.
search_condition (dict): The search condition.
"""
if data_manager is None and summary_base_dir is None:
raise LineageParamTypeError("One of data_manager or summary_base_dir needs to be specified.")
if data_manager is None:
summary_base_dir = normalize_summary_dir(summary_base_dir)
else:
summary_base_dir = data_manager.summary_base_dir
search_condition = {} if search_condition is None else search_condition
......@@ -233,16 +279,11 @@ def filter_summary_lineage(summary_base_dir, search_condition=None):
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))
summary_path = SummaryPathParser.get_latest_lineage_summaries(summary_base_dir)
if not summary_path:
log.error('There is no summary log file under summary_base_dir.')
raise LineageFileNotFoundError(
'There is no summary log file under summary_base_dir.'
)
try:
result = Querier(summary_path).filter_summary_lineage(
condition=search_condition
lineage_objects = LineageOrganizer(data_manager, summary_base_dir).super_lineage_objs
result = Querier(lineage_objects).filter_summary_lineage(
condition=search_condition,
added=added
)
except LineageSummaryParseException:
result = {'object': [], 'count': 0}
......
......@@ -16,8 +16,26 @@
import os
from mindinsight.datavisual.data_transform.data_manager import BaseCacheItemUpdater, CachedTrainJob
from mindinsight.lineagemgr.querier.query_model import LineageObj
from mindinsight.lineagemgr.summary.lineage_summary_analyzer import LineageSummaryAnalyzer
from mindinsight.lineagemgr.common.log import logger
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageFileNotFoundError
from mindinsight.lineagemgr.common.validator.validate import validate_train_id
from mindinsight.lineagemgr.lineage_parser import LineageParser, LINEAGE
from mindinsight.utils.exceptions import ParamValueError
def update_lineage_object(data_manager, train_id, added_info: dict):
"""Update lineage objects about tag and remark."""
validate_train_id(train_id)
cache_item = data_manager.get_brief_train_job(train_id)
cached_added_info = cache_item.get(key=LINEAGE).added_info
new_added_info = dict(cached_added_info)
for key, value in added_info.items():
if key in ["tag", "remark"]:
new_added_info.update({key: value})
with cache_item.lock_key(LINEAGE):
cache_item.get(key=LINEAGE).added_info = new_added_info
class LineageCacheItemUpdater(BaseCacheItemUpdater):
......@@ -25,15 +43,28 @@ class LineageCacheItemUpdater(BaseCacheItemUpdater):
def update_item(self, cache_item: CachedTrainJob):
"""Update cache item in place."""
log_path = cache_item.summary_dir
log_dir = os.path.dirname(log_path)
lineage_info = LineageSummaryAnalyzer.get_summary_infos(log_path)
user_defined_info = LineageSummaryAnalyzer.get_user_defined_info(log_path)
lineage_obj = LineageObj(
log_dir,
train_lineage=lineage_info.train_lineage,
evaluation_lineage=lineage_info.eval_lineage,
dataset_graph=lineage_info.dataset_graph,
user_defined_info=user_defined_info
)
cache_item.set(key="lineage", value=lineage_obj)
summary_base_dir = cache_item.summary_base_dir
summary_dir = cache_item.summary_dir
update_time = cache_item.basic_info.update_time
# The summary_base_dir and summary_dir have been normalized in data_manager.
if summary_base_dir == summary_dir:
relative_path = "./"
else:
relative_path = f'./{os.path.basename(summary_dir)}'
try:
cached_added_info = cache_item.get(key=LINEAGE).added_info
except ParamValueError:
cached_added_info = None
try:
lineage_parser = LineageParser(summary_dir, update_time, cached_added_info)
super_lineage_obj = lineage_parser.super_lineage_obj
except LineageFileNotFoundError:
super_lineage_obj = None
if super_lineage_obj is None:
logger.warning("There is no lineage to update in tran job %s.", relative_path)
return
with cache_item.lock_key(LINEAGE):
cache_item.set(key=LINEAGE, value=super_lineage_obj)
......@@ -15,8 +15,11 @@
"""Lineage utils."""
from functools import wraps
from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamRunContextError, \
LineageGetModelFileError, LineageLogError
LineageGetModelFileError, LineageLogError, LineageParamValueError, LineageDirNotExistError, \
LineageParamSummaryPathError
from mindinsight.lineagemgr.common.validator.validate import validate_path
from mindinsight.utils.exceptions import MindInsightException
......@@ -54,3 +57,14 @@ def try_except(logger):
return wrapper
return try_except_decorate
def normalize_summary_dir(summary_dir):
"""Normalize summary dir."""
try:
summary_dir = validate_path(summary_dir)
except (LineageParamValueError, LineageDirNotExistError) as error:
log.error(str(error))
log.exception(error)
raise LineageParamSummaryPathError(str(error.message))
return summary_dir
......@@ -23,7 +23,7 @@ from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamMiss
from mindinsight.lineagemgr.common.log import logger as log
from mindinsight.lineagemgr.common.validator.validate_path import safe_normalize_path
from mindinsight.lineagemgr.querier.query_model import FIELD_MAPPING
from mindinsight.utils.exceptions import MindInsightException
from mindinsight.utils.exceptions import MindInsightException, ParamValueError
try:
from mindspore.nn import Cell
......@@ -437,3 +437,26 @@ def validate_user_defined_info(user_defined_info):
if len(field_map) + len(user_defined_keys) != len(all_keys):
raise LineageParamValueError("There are some keys have defined in lineage.")
def validate_train_id(relative_path):
"""
Check if train_id is valid.
Args:
relative_path (str): Train ID of a summary directory, e.g. './log1'.
Returns:
bool, if train id is valid, return True.
"""
if not relative_path.startswith('./'):
log.warning("The relative_path does not start with './'.")
raise ParamValueError(
"Summary dir should be relative path starting with './'."
)
if len(relative_path.split("/")) > 2:
log.warning("The relative_path contains multiple '/'.")
raise ParamValueError(
"Summary dir should be relative path starting with './'."
)
# Copyright 2020 Huawei Technologies Co., Ltd
#
# 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.
# ============================================================================
"""This file is used to parse lineage info."""
import os
from mindinsight.datavisual.data_transform.summary_watcher import SummaryWatcher
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageSummaryAnalyzeException, \
LineageEventNotExistException, LineageEventFieldNotExistException, LineageFileNotFoundError, \
MindInsightException
from mindinsight.lineagemgr.common.log import logger
from mindinsight.lineagemgr.common.path_parser import SummaryPathParser
from mindinsight.lineagemgr.summary.lineage_summary_analyzer import LineageSummaryAnalyzer
from mindinsight.lineagemgr.querier.query_model import LineageObj
from mindinsight.utils.exceptions import ParamValueError
LINEAGE = "lineage"
class SuperLineageObj:
"""This is an object for LineageObj and its additional info."""
def __init__(self, lineage_obj, update_time, added_info=None):
self._lineage_obj = lineage_obj
self._update_time = update_time
self._added_info = added_info if added_info is not None else dict()
@property
def lineage_obj(self):
"""Get lineage object."""
return self._lineage_obj
@property
def added_info(self):
"""Get added info."""
return self._added_info
@added_info.setter
def added_info(self, added_info):
"""Set added info."""
self._added_info = added_info
@property
def update_time(self):
"""Get update time."""
return self._update_time
class LineageParser:
"""Lineage parser."""
def __init__(self, summary_dir, update_time=None, added_info=None):
self._super_lineage_obj = None
self._summary_dir = summary_dir
self._update_time = update_time
self._added_info = added_info
self._parse_summary_log()
def _parse_summary_log(self):
"""
Parse the single summary log.
Returns:
bool, `True` if parse summary log success, else `False`.
"""
file_path = SummaryPathParser.get_latest_lineage_summary(self._summary_dir)
if file_path is None:
logger.warning('There is no summary log file under summary_dir %s.', self._summary_dir)
raise LineageFileNotFoundError(
'There is no summary log file under summary_dir.'
)
try:
lineage_info = LineageSummaryAnalyzer.get_summary_infos(file_path)
user_defined_info = LineageSummaryAnalyzer.get_user_defined_info(file_path)
lineage_obj = LineageObj(
self._summary_dir,
train_lineage=lineage_info.train_lineage,
evaluation_lineage=lineage_info.eval_lineage,
dataset_graph=lineage_info.dataset_graph,
user_defined_info=user_defined_info
)
self._super_lineage_obj = SuperLineageObj(lineage_obj, self._update_time, self._added_info)
except (LineageSummaryAnalyzeException,
LineageEventNotExistException,
LineageEventFieldNotExistException):
logger.warning("Parse file failed under summary_dir %s.", self._summary_dir)
except MindInsightException as error:
logger.error(str(error))
logger.exception(error)
logger.warning("Parse file failed under summary_dir %s.", self._summary_dir)
@property
def super_lineage_obj(self):
"""Get super lineage object."""
return self._super_lineage_obj
class LineageOrganizer:
"""Lineage organizer."""
def __init__(self, data_manager=None, summary_base_dir=None):
self._data_manager = data_manager
self._summary_base_dir = summary_base_dir
self._check_params()
self._super_lineage_objs = {}
self._organize_from_cache()
self._organize_from_disk()
def _check_params(self):
"""Check params."""
if self._data_manager is not None and self._summary_base_dir is not None:
self._summary_base_dir = None
def _organize_from_disk(self):
"""Organize lineage objs from disk."""
if self._summary_base_dir is None:
return
summary_watcher = SummaryWatcher()
relative_dirs = summary_watcher.list_summary_directories(
summary_base_dir=self._summary_base_dir
)
no_lineage_count = 0
for item in relative_dirs:
relative_dir = item.get('relative_path')
update_time = item.get('update_time')
abs_summary_dir = os.path.realpath(os.path.join(self._summary_base_dir, relative_dir))
try:
lineage_parser = LineageParser(abs_summary_dir, update_time)
super_lineage_obj = lineage_parser.super_lineage_obj
if super_lineage_obj is not None:
self._super_lineage_objs.update({abs_summary_dir: super_lineage_obj})
except LineageFileNotFoundError:
no_lineage_count += 1
if no_lineage_count == len(relative_dirs):
logger.error('There is no summary log file under summary_base_dir.')
raise LineageFileNotFoundError(
'There is no summary log file under summary_base_dir.'
)
def _organize_from_cache(self):
"""Organize lineage objs from cache."""
if self._data_manager is None:
return
brief_cache = self._data_manager.get_brief_cache()
cache_items = brief_cache.cache_items
for relative_dir, cache_train_job in cache_items.items():
try:
super_lineage_obj = cache_train_job.get("lineage")
self._super_lineage_objs.update({relative_dir: super_lineage_obj})
except ParamValueError:
logger.info("This is no lineage info in train job %s.", relative_dir)
@property
def super_lineage_objs(self):
"""Get super lineage objects."""
return self._super_lineage_objs
def get_super_lineage_obj(self, relative_path):
"""Get super lineage object by given relative path."""
return self._super_lineage_objs.get(relative_path)
......@@ -16,17 +16,11 @@
import enum
import functools
import operator
import os
from mindinsight.lineagemgr.common.exceptions.exceptions import \
LineageParamTypeError, LineageSummaryAnalyzeException, \
LineageEventNotExistException, LineageQuerierParamException, \
LineageSummaryParseException, LineageEventFieldNotExistException
from mindinsight.lineagemgr.common.log import logger
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageQuerierParamException, LineageParamTypeError
from mindinsight.lineagemgr.common.utils import enum_to_list
from mindinsight.lineagemgr.querier.query_model import LineageObj, FIELD_MAPPING
from mindinsight.lineagemgr.summary.lineage_summary_analyzer import \
LineageSummaryAnalyzer
from mindinsight.lineagemgr.lineage_parser import SuperLineageObj
from mindinsight.lineagemgr.querier.query_model import FIELD_MAPPING
@enum.unique
......@@ -173,20 +167,24 @@ class Querier:
See the method `filter_summary_lineage` for supported fields.
Args:
summary_path (Union[str, list[str]]): The single summary log path or
a list of summary log path.
super_lineage_objs (dict): A dict of <summary_dir, SuperLineageObject>.
Raises:
LineageParamTypeError: If the input parameter type is invalid.
LineageQuerierParamException: If the input parameter value is invalid.
LineageSummaryParseException: If all summary logs parsing failed.
"""
def __init__(self, summary_path):
self._lineage_objects = []
self._index_map = {}
self._parse_failed_paths = []
self._parse_summary_logs(summary_path)
self._size = len(self._lineage_objects)
def __init__(self, super_lineage_objs):
self._super_lineage_objs = self._check_objs(super_lineage_objs)
def _check_objs(self, super_lineage_objs):
if super_lineage_objs is None:
raise LineageQuerierParamException(
'querier_init_param', 'The querier init param is empty.'
)
if not isinstance(super_lineage_objs, dict):
raise LineageParamTypeError("Init param should be a dict.")
return super_lineage_objs
def get_summary_lineage(self, summary_dir=None, filter_keys=None):
"""
......@@ -209,7 +207,6 @@ class Querier:
Returns:
list[dict], summary lineage information.
"""
self._parse_fail_summary_logs()
if filter_keys is None:
filter_keys = LineageFilterKey.get_key_list()
......@@ -222,20 +219,20 @@ class Querier:
if summary_dir is None:
result = [
item.get_summary_info(filter_keys) for item in self._lineage_objects
item.lineage_obj.get_summary_info(filter_keys) for item in self._super_lineage_objs.values()
]
else:
index = self._index_map.get(summary_dir)
if index is None:
raise LineageQuerierParamException(
'summary_dir',
'Summary dir {} does not exist.'.format(summary_dir)
)
lineage_obj = self._lineage_objects[index]
elif summary_dir in self._super_lineage_objs:
lineage_obj = self._super_lineage_objs[summary_dir].lineage_obj
result = [lineage_obj.get_summary_info(filter_keys)]
else:
raise LineageQuerierParamException(
'summary_dir',
'Summary dir {} does not exist.'.format(summary_dir)
)
return result
def filter_summary_lineage(self, condition=None):
def filter_summary_lineage(self, condition=None, added=False):
"""
Filter and sort lineage information based on the specified condition.
......@@ -253,7 +250,7 @@ class Querier:
Returns:
dict, filtered and sorted model lineage information.
"""
def _filter(lineage_obj: LineageObj):
def _filter(super_lineage_obj: SuperLineageObj):
for condition_key, condition_value in condition.items():
if ConditionParam.is_condition_type(condition_key):
continue
......@@ -263,7 +260,7 @@ class Querier:
'The field {} not supported'.format(condition_key)
)
value = lineage_obj.get_value_by_key(condition_key)
value = super_lineage_obj.lineage_obj.get_value_by_key(condition_key)
for exp_key, exp_value in condition_value.items():
if not ExpressionType.is_valid_exp(exp_key):
raise LineageQuerierParamException(
......@@ -274,9 +271,9 @@ class Querier:
return False
return True
def _cmp(obj1: LineageObj, obj2: LineageObj):
value1 = obj1.get_value_by_key(sorted_name)
value2 = obj2.get_value_by_key(sorted_name)
def _cmp(obj1: SuperLineageObj, obj2: SuperLineageObj):
value1 = obj1.lineage_obj.get_value_by_key(sorted_name)
value2 = obj2.lineage_obj.get_value_by_key(sorted_name)
if value1 is None and value2 is None:
cmp_result = 0
......@@ -293,11 +290,14 @@ class Querier:
cmp_result = (type1 > type2) - (type1 < type2)
return cmp_result
self._parse_fail_summary_logs()
if condition is None:
condition = {}
results = list(filter(_filter, self._lineage_objects))
self._add_dataset_mark()
super_lineage_objs = list(self._super_lineage_objs.values())
super_lineage_objs.sort(key=lambda x: x.update_time, reverse=True)
results = list(filter(_filter, super_lineage_objs))
if ConditionParam.SORTED_NAME.value in condition:
sorted_name = condition.get(ConditionParam.SORTED_NAME.value)
......@@ -323,9 +323,11 @@ class Querier:
for item in offset_results:
lineage_object = dict()
if LineageType.MODEL.value in lineage_types:
lineage_object.update(item.to_model_lineage_dict())
lineage_object.update(item.lineage_obj.to_model_lineage_dict())
if LineageType.DATASET.value in lineage_types:
lineage_object.update(item.to_dataset_lineage_dict())
lineage_object.update(item.lineage_obj.to_dataset_lineage_dict())
if added:
lineage_object.update({"added_info": item.added_info})
object_items.append(lineage_object)
lineage_info = {
......@@ -341,7 +343,7 @@ class Querier:
customized = dict()
for offset_result in offset_results:
for obj_name in ["metric", "user_defined"]:
self._organize_customized_item(customized, offset_result, obj_name)
self._organize_customized_item(customized, offset_result.lineage_obj, obj_name)
# If types contain numbers and string, it will be "mixed".
# If types contain "int" and "float", it will be "float".
......@@ -410,10 +412,10 @@ class Querier:
Args:
condition (dict): Filter and sort condition.
result (list[LineageObj]): Filtered and sorted result.
result (list[SuperLineageObj]): Filtered and sorted result.
Returns:
list[LineageObj], paginated result.
list[SuperLineageObj], paginated result.
"""
offset = 0
limit = 10
......@@ -428,87 +430,12 @@ class Querier:
offset_result = result[offset * limit: limit * (offset + 1)]
return offset_result
def _parse_summary_logs(self, summary_path):
"""
Parse summary logs.
Args:
summary_path (Union[str, list[str]]): The single summary log path or
a list of summary log path.
"""
if not summary_path:
raise LineageQuerierParamException(
'summary_path', 'The summary path is empty.'
)
if isinstance(summary_path, str):
self._parse_summary_log(summary_path, 0)
elif isinstance(summary_path, list):
index = 0
for path in summary_path:
parse_result = self._parse_summary_log(path, index)
if parse_result:
index += 1
else:
raise LineageParamTypeError('Summary path is not str or list.')
if self._parse_failed_paths:
logger.info('Parse failed paths: %s', str(self._parse_failed_paths))
if not self._lineage_objects:
raise LineageSummaryParseException()
def _parse_summary_log(self, log_path, index: int, is_save_fail_path=True):
"""
Parse the single summary log.
Args:
log_path (str): The single summary log path.
index (int): TrainInfo instance index in the train info list.
is_save_fail_path (bool): Set whether to save the failed summary
path. Default: True.
Returns:
bool, `True` if parse summary log success, else `False`.
"""
log_dir = os.path.dirname(log_path)
try:
lineage_info = LineageSummaryAnalyzer.get_summary_infos(log_path)
user_defined_info = LineageSummaryAnalyzer.get_user_defined_info(log_path)
lineage_obj = LineageObj(
log_dir,
train_lineage=lineage_info.train_lineage,
evaluation_lineage=lineage_info.eval_lineage,
dataset_graph=lineage_info.dataset_graph,
user_defined_info=user_defined_info
)
self._lineage_objects.append(lineage_obj)
self._add_dataset_mark()
self._index_map[log_dir] = index
return True
except (LineageSummaryAnalyzeException,
LineageEventNotExistException,
LineageEventFieldNotExistException):
if is_save_fail_path:
self._parse_failed_paths.append(log_path)
return False
def _parse_fail_summary_logs(self):
"""Parse fail summary logs."""
if self._parse_failed_paths:
failed_paths = []
for path in self._parse_failed_paths:
parse_result = self._parse_summary_log(path, self._size, False)
if parse_result:
self._size += 1
else:
failed_paths.append(path)
self._parse_failed_paths = failed_paths
def _add_dataset_mark(self):
"""Add dataset mark into LineageObj."""
# give a dataset mark for each dataset graph in lineage information
marked_dataset_group = {'1': None}
for lineage in self._lineage_objects:
for super_lineage_obj in self._super_lineage_objs.values():
lineage = super_lineage_obj.lineage_obj
dataset_mark = '0'
for dataset_graph_mark, marked_dataset_graph in marked_dataset_group.items():
if marked_dataset_graph == lineage.dataset_graph:
......
......@@ -298,6 +298,7 @@ class LineageObj:
result[self._name_user_defined] = self.user_defined
# add dataset_graph into filtration result
result[self._name_dataset_graph] = getattr(self, self._name_dataset_graph)
return result
def _parse_train_lineage(self, train_lineage):
......
......@@ -356,6 +356,7 @@ class TestModelApi(TestCase):
assert expect_result == res
expect_result = {
'customized': {},
'object': [],
'count': 0
}
......
......@@ -70,7 +70,7 @@ class TestSearchModel(TestCase):
self.url = '/v1/mindinsight/lineagemgr/lineages'
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.settings')
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.filter_summary_lineage')
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.general_filter_summary_lineage')
def test_search_model_success(self, *args):
"""Test the success of model_success."""
base_dir = '/path/to/test_lineage_summary_dir_base'
......@@ -113,7 +113,7 @@ class TestSearchModel(TestCase):
self.assertDictEqual(expect_result, response.get_json())
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.settings')
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.filter_summary_lineage')
@mock.patch('mindinsight.backend.lineagemgr.lineage_api.general_filter_summary_lineage')
def test_search_model_fail(self, *args):
"""Test the function of model_lineage with exception."""
response = self.app_client.post(self.url, data='xxx')
......
......@@ -29,12 +29,12 @@ class TestModel(TestCase):
"""Test the function of get_summary_lineage and filter_summary_lineage."""
@mock.patch('mindinsight.lineagemgr.api.model.Querier')
@mock.patch('mindinsight.lineagemgr.api.model.SummaryPathParser.get_latest_lineage_summary')
@mock.patch('mindinsight.lineagemgr.api.model.LineageParser')
@mock.patch('os.path.isdir')
def test_get_summary_lineage_success(self, isdir_mock, latest_summary_mock, qurier_mock):
def test_get_summary_lineage_success(self, isdir_mock, parser_mock, qurier_mock):
"""Test the function of get_summary_lineage."""
isdir_mock.return_value = True
latest_summary_mock.return_value = '/path/to/summary_dir/a_MS_lineage'
parser_mock.return_value = MagicMock()
mock_querier = MagicMock()
qurier_mock.return_value = mock_querier
......@@ -53,7 +53,7 @@ class TestModel(TestCase):
invalid_path
)
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch.object(SummaryPathParser, 'get_latest_lineage_summary')
def test_get_summary_lineage_failed2(self, mock_summary, mock_valid):
"""Test get_summary_lineage failed."""
......@@ -66,29 +66,19 @@ class TestModel(TestCase):
'/path/to/summary_dir'
)
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.api.model.Querier')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageParser._parse_summary_log')
@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch.object(SummaryPathParser, 'get_latest_lineage_summary')
def test_get_summary_lineage_failed3(self,
mock_summary,
mock_querier,
mock_valid):
mock_valid,
mock_paser):
"""Test get_summary_lineage failed."""
mock_summary.return_value = '/path/to/summary/file'
mock_querier.return_value.get_summary_lineage.side_effect = \
LineageSummaryParseException()
mock_valid.return_value = '/path/to/summary_dir'
res = get_summary_lineage('/path/to/summary_dir')
assert res == {}
mock_querier.side_effect = LineageQuerierParamException(
['keys'], 'key')
self.assertRaisesRegex(
LineageQuerySummaryDataError,
'Get summary lineage failed',
get_summary_lineage,
'/path/to/summary_dir'
)
mock_paser.return_value = None
result = get_summary_lineage('/path/to/summary_dir')
assert {} == result
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
def test_convert_relative_path_to_abspath(self, validate_path_mock):
......@@ -135,13 +125,13 @@ class TestModel(TestCase):
class TestFilterAPI(TestCase):
"""Test the function of filter_summary_lineage."""
@mock.patch('mindinsight.lineagemgr.api.model.LineageOrganizer')
@mock.patch('mindinsight.lineagemgr.api.model.Querier')
@mock.patch('mindinsight.lineagemgr.api.model.SummaryPathParser.get_latest_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_latest_lineage_summary')
@mock.patch('mindinsight.lineagemgr.api.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.api.model.normalize_summary_dir')
def test_filter_summary_lineage(self, validate_path_mock, convert_path_mock,
latest_summary_mock, qurier_mock):
latest_summary_mock, qurier_mock, organizer_mock):
"""Test the function of filter_summary_lineage."""
convert_path_mock.return_value = {
'summary_dir': {
......@@ -151,6 +141,8 @@ class TestFilterAPI(TestCase):
'gt': 2.0
}
}
organizer_mock = MagicMock()
organizer_mock.super_lineage_objs = None
validate_path_mock.return_value = True
latest_summary_mock.return_value = ['/path/to/summary_base_dir/summary_dir']
mock_querier = MagicMock()
......@@ -172,7 +164,7 @@ class TestFilterAPI(TestCase):
)
@mock.patch('mindinsight.lineagemgr.api.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.api.model.normalize_summary_dir')
def test_invalid_search_condition(self, mock_path, mock_valid):
"""Test filter_summary_lineage with invalid invalid param."""
mock_path.return_value = None
......@@ -188,7 +180,7 @@ class TestFilterAPI(TestCase):
@mock.patch('mindinsight.lineagemgr.api.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.common.utils.validate_path')
@mock.patch('mindinsight.lineagemgr.api.model._convert_relative_path_to_abspath')
def test_failed_to_convert_path(self, mock_convert, *args):
"""Test filter_summary_lineage with invalid invalid param."""
......@@ -205,23 +197,24 @@ class TestFilterAPI(TestCase):
@mock.patch('mindinsight.lineagemgr.api.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.api.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch.object(SummaryPathParser, 'get_latest_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.api.model.normalize_summary_dir')
@mock.patch.object(SummaryPathParser, 'get_latest_lineage_summary')
def test_failed_to_get_summary_filesh(self, mock_parse, *args):
"""Test filter_summary_lineage with invalid invalid param."""
mock_parse.return_value = []
args[0].return_value = None
path = '/path/to/summary/dir'
mock_parse.return_value = None
args[0].return_value = path
self.assertRaisesRegex(
LineageFileNotFoundError,
'There is no summary log file under summary_base_dir.',
filter_summary_lineage,
'/path/to/summary/dir'
path
)
@mock.patch('mindinsight.lineagemgr.api.model._convert_relative_path_to_abspath')
@mock.patch('mindinsight.lineagemgr.api.model.validate_search_model_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_condition')
@mock.patch('mindinsight.lineagemgr.api.model.validate_path')
@mock.patch('mindinsight.lineagemgr.api.model.normalize_summary_dir')
@mock.patch.object(SummaryPathParser, 'get_latest_lineage_summaries')
@mock.patch('mindinsight.lineagemgr.api.model.Querier')
def test_failed_to_querier(self, mock_query, mock_parse, *args):
......
......@@ -13,14 +13,15 @@
# limitations under the License.
# ============================================================================
"""Test the querier module."""
import time
from unittest import TestCase, mock
from google.protobuf.json_format import ParseDict
import mindinsight.datavisual.proto_files.mindinsight_lineage_pb2 as summary_pb2
from mindinsight.lineagemgr.common.exceptions.exceptions import (LineageParamTypeError, LineageQuerierParamException,
LineageSummaryAnalyzeException,
LineageSummaryParseException)
from mindinsight.lineagemgr.common.exceptions.exceptions import LineageParamTypeError, LineageQuerierParamException
from mindinsight.lineagemgr.lineage_parser import LineageOrganizer
from mindinsight.lineagemgr.querier.querier import Querier
from mindinsight.lineagemgr.summary.lineage_summary_analyzer import LineageInfo
......@@ -246,8 +247,11 @@ LINEAGE_FILTRATION_6 = {
class TestQuerier(TestCase):
"""Test the class of `Querier`."""
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_user_defined_info')
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_summary_infos')
@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryPathParser.get_latest_lineage_summary')
@mock.patch('mindinsight.lineagemgr.lineage_parser.SummaryWatcher.list_summary_directories')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_user_defined_info')
@mock.patch('mindinsight.lineagemgr.lineage_parser.LineageSummaryAnalyzer.get_summary_infos')
def setUp(self, *args):
"""Initialization before test case execution."""
args[0].return_value = create_lineage_info(
......@@ -256,22 +260,22 @@ class TestQuerier(TestCase):
event_data.EVENT_DATASET_DICT_0
)
args[1].return_value = []
args[3].return_value = 'path'
single_summary_path = '/path/to/summary0/log0'
self.single_querier = Querier(single_summary_path)
args[2].return_value = [{'relative_path': './', 'update_time': 1}]
single_summary_path = '/path/to/summary0'
lineage_objects = LineageOrganizer(summary_base_dir=single_summary_path).super_lineage_objs
self.single_querier = Querier(lineage_objects)
lineage_infos = get_lineage_infos()
args[0].side_effect = lineage_infos
summary_paths = [
'/path/to/summary0/log0',
'/path/to/summary1/log1',
'/path/to/summary2/log2',
'/path/to/summary3/log3',
'/path/to/summary4/log4',
'/path/to/summary5/log5',
'/path/to/summary6/log6'
]
self.multi_querier = Querier(summary_paths)
summary_base_dir = '/path/to'
relative_dirs = []
for i in range(7):
relative_dirs.append(dict(relative_path=f'./summary{i}', update_time=time.time() - i))
args[2].return_value = relative_dirs
lineage_objects = LineageOrganizer(summary_base_dir=summary_base_dir).super_lineage_objs
self.multi_querier = Querier(lineage_objects)
def test_get_summary_lineage_success_1(self):
"""Test the success of get_summary_lineage."""
......@@ -282,9 +286,7 @@ class TestQuerier(TestCase):
def test_get_summary_lineage_success_2(self):
"""Test the success of get_summary_lineage."""
expected_result = [LINEAGE_INFO_0]
result = self.single_querier.get_summary_lineage(
summary_dir='/path/to/summary0'
)
result = self.single_querier.get_summary_lineage()
self.assertListEqual(expected_result, result)
def test_get_summary_lineage_success_3(self):
......@@ -601,60 +603,12 @@ class TestQuerier(TestCase):
condition=condition
)
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_summary_infos')
def test_init_fail(self, *args):
def test_init_fail(self):
"""Test the function of init with exception."""
summary_path = {'xxx': 1}
obj_dict = 'a'
with self.assertRaises(LineageParamTypeError):
Querier(summary_path)
Querier(obj_dict)
summary_path = None
obj_dict = None
with self.assertRaises(LineageQuerierParamException):
Querier(summary_path)
args[0].side_effect = LineageSummaryAnalyzeException
summary_path = '/path/to/summary0/log0'
with self.assertRaises(LineageSummaryParseException):
Querier(summary_path)
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_user_defined_info')
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_summary_infos')
def test_parse_fail_summary_logs_1(self, *args):
"""Test the function of parsing fail summary logs."""
lineage_infos = get_lineage_infos()
args[0].side_effect = lineage_infos
args[1].return_value = []
summary_path = ['/path/to/summary0/log0']
querier = Querier(summary_path)
querier._parse_failed_paths.append('/path/to/summary1/log1')
expected_result = [
LINEAGE_INFO_0,
LINEAGE_INFO_1
]
result = querier.get_summary_lineage()
self.assertListEqual(expected_result, result)
self.assertListEqual([], querier._parse_failed_paths)
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_user_defined_info')
@mock.patch('mindinsight.lineagemgr.querier.querier.LineageSummaryAnalyzer.get_summary_infos')
def test_parse_fail_summary_logs_2(self, *args):
"""Test the function of parsing fail summary logs."""
args[0].return_value = create_lineage_info(
event_data.EVENT_TRAIN_DICT_0,
event_data.EVENT_EVAL_DICT_0,
event_data.EVENT_DATASET_DICT_0,
)
args[1].return_value = []
summary_path = ['/path/to/summary0/log0']
querier = Querier(summary_path)
querier._parse_failed_paths.append('/path/to/summary1/log1')
args[0].return_value = create_lineage_info(None, None, None)
expected_result = [LINEAGE_INFO_0]
result = querier.get_summary_lineage()
self.assertListEqual(expected_result, result)
self.assertListEqual(
['/path/to/summary1/log1'], querier._parse_failed_paths
)
Querier(obj_dict)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册