Source code for memote.suite.results.history_manager

# -*- coding: utf-8 -*-

# Copyright 2017 Novo Nordisk Foundation Center for Biosustainability,
# Technical University of Denmark.
#
# 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.

"""Provide a history manager that integrates with a git repository."""

from __future__ import absolute_import

import json
import logging

from six import iteritems, iterkeys
from sqlalchemy.orm.exc import NoResultFound

from memote.suite.results import MemoteResult
from memote.utils import is_modified


__all__ = ("HistoryManager",)

LOGGER = logging.getLogger(__name__)


[docs]class HistoryManager(object): """ Manage access to results in the commit history of a git repository. Attributes ---------- manager : memote.RepoResultManager The manager for accessing individual results. current : str The name of the currently active branch. """ def __init__(self, repository, manager, **kwargs): """ Initialize a manager to access results in the git history. Parameters ---------- location : str The directory for storing JSON files. """ super(HistoryManager, self).__init__(**kwargs) self._repo = repository self.manager = manager self._history = None self._results = None
[docs] def build_branch_structure(self, model, skip): """Inspect and record the repo's branches and their history.""" self._history = dict() self._history["commits"] = commits = dict() self._history["branches"] = branches = dict() for branch in self._repo.refs: LOGGER.debug(branch.name) if branch.name in skip: continue branches[branch.name] = branch_history = list() latest = branch.commit history = [latest] + list(latest.iter_parents()) for commit in history: # Find model in committed files. if not is_modified(model, commit): LOGGER.info( "The model was not modified in commit '{}'. " "Skipping.".format(commit) ) continue branch_history.append(commit.hexsha) if commit.hexsha not in commits: commits[commit.hexsha] = sub = dict() sub["timestamp"] = commit.authored_datetime.isoformat(" ") sub["author"] = commit.author.name sub["email"] = commit.author.email LOGGER.debug("%s", json.dumps(self._history, indent=2))
[docs] def iter_branches(self): """Iterate over branch names and their commit histories.""" return iteritems(self._history["branches"])
[docs] def iter_commits(self): """Iterate over all commit hashes in the repository.""" return iterkeys(self._history["commits"])
[docs] def load_history(self, model, skip=("gh-pages",)): """ Load the entire results history into memory. Could be a bad idea in a far future. """ skip = set(skip) if self._history is None: self.build_branch_structure(model, skip) self._results = dict() all_commits = list(self._history["commits"]) for commit in all_commits: try: self._results[commit] = self.manager.load(commit) except (IOError, NoResultFound) as err: LOGGER.error("Could not load result '%s'.", commit) LOGGER.debug("%s", str(err))
[docs] def get_result(self, commit, default=None): """Return an individual result from the history if it exists.""" if default is None: default = MemoteResult() assert self._results is not None, "Please call the method `load_history` first." return self._results.get(commit, default)
[docs] def __contains__(self, commit): """Test for the existence of a result for a commit.""" assert self._results is not None, "Please call the method `load_history` first." return commit in self._results