| Index: appengine/chromium_rietveld/codereview/dependency_utils.py
|
| diff --git a/appengine/chromium_rietveld/codereview/dependency_utils.py b/appengine/chromium_rietveld/codereview/dependency_utils.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..991d3454707c11ec691d17cec35eaea057b3dbdf
|
| --- /dev/null
|
| +++ b/appengine/chromium_rietveld/codereview/dependency_utils.py
|
| @@ -0,0 +1,153 @@
|
| +# Copyright 2015 Google Inc.
|
| +#
|
| +# 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.
|
| +
|
| +"""Contains utilities to help deal with the dependencies of a patchset."""
|
| +
|
| +from codereview import models
|
| +
|
| +
|
| +DEPENDENCY_DELIMITER = ':'
|
| +
|
| +
|
| +def remove_dependencies_from_all_patchsets(issue):
|
| + """Remove all dependencies from all patchsets of this issue.
|
| +
|
| + This method should be called when a CL is closed or deleted.
|
| +
|
| + Args:
|
| + issue: (models.Issue) The issue we to remove dependencies to.
|
| + """
|
| + for patchset in issue.patchsets:
|
| + remove_dependencies(patchset)
|
| +
|
| +
|
| +def remove_all_patchsets_as_dependents(issue):
|
| + """Remove all patchsets of this issue as dependents.
|
| +
|
| + This method should be called when a CL is deleted.
|
| +
|
| + Args:
|
| + issue: (models.Issue) The issue whose dependencies we want to update.
|
| + """
|
| + for patchset in issue.patchsets:
|
| + remove_as_dependent(patchset)
|
| +
|
| +
|
| +def remove_dependencies(patchset):
|
| + """Remove all dependencies on this patchset.
|
| +
|
| + This method should be called when a patchset is deleted.
|
| +
|
| + Args:
|
| + patchset: (models.PatchSet) The patchset we want to remove dependencies to.
|
| + """
|
| + _update_dependents(patchset, remove_dependency=True)
|
| +
|
| +
|
| +def remove_as_dependent(patchset):
|
| + """Remove the specified patchset as a dependent.
|
| +
|
| + This method should be called when a patchset is deleted.
|
| +
|
| + Args:
|
| + patchset: (models.PatchSet) The patchset we want to remove as a dependent.
|
| + """
|
| + dependency_str = patchset.depends_on_patchset
|
| + if not dependency_str:
|
| + return
|
| + depends_on_tokens = get_dependency_tokens(dependency_str)
|
| + depends_on_issue = models.Issue.get_by_id(int(depends_on_tokens[0]))
|
| + if not depends_on_issue:
|
| + return
|
| + depends_on_patchset = models.PatchSet.get_by_id(
|
| + int(depends_on_tokens[1]), parent=depends_on_issue.key)
|
| + if not depends_on_patchset:
|
| + return
|
| + if depends_on_patchset.dependent_patchsets:
|
| + target_str = _get_dependency_str(patchset.issue_key.id(),
|
| + patchset.key.id())
|
| + if target_str in depends_on_patchset.dependent_patchsets:
|
| + depends_on_patchset.dependent_patchsets.remove(target_str)
|
| + depends_on_patchset.put()
|
| +
|
| +
|
| +def get_dependency_tokens(dependency_str):
|
| + return dependency_str.split(DEPENDENCY_DELIMITER)
|
| +
|
| +
|
| +def _get_dependency_str(issue_key, patchset_key):
|
| + return '%s%s%s' % (issue_key, DEPENDENCY_DELIMITER, patchset_key)
|
| +
|
| +
|
| +def _update_dependents(patchset, remove_dependency):
|
| + if not patchset:
|
| + return
|
| + for dependency_str in patchset.dependent_patchsets:
|
| + depends_on_tokens = get_dependency_tokens(dependency_str)
|
| + dependent_issue = models.Issue.get_by_id(int(depends_on_tokens[0]))
|
| + if not dependent_issue:
|
| + continue
|
| + dependent_patchset = models.PatchSet.get_by_id(
|
| + int(depends_on_tokens[1]), parent=dependent_issue.key)
|
| + if not dependent_patchset:
|
| + continue
|
| + dependent_str = _get_dependency_str(patchset.issue_key.id(),
|
| + patchset.key.id())
|
| + if remove_dependency:
|
| + # Sanity check that the dependency_str of this patchset is what we expect
|
| + # before we delete it.
|
| + if dependent_patchset.depends_on_patchset == dependent_str:
|
| + dependent_patchset.depends_on_patchset = ""
|
| + dependent_patchset.put()
|
| + else:
|
| + # Sanity check that this patchset has no existing dependency before we add
|
| + # a new one.
|
| + if dependent_patchset.depends_on_patchset == "":
|
| + dependent_patchset.depends_on_patchset = dependent_str
|
| + dependent_patchset.put()
|
| +
|
| +
|
| +def mark_as_dependent_and_get_dependency_str(
|
| + dependency_str, dependent_issue_key, dependent_patchset_key):
|
| + """Marks the specified patchset as a dependent and returns its dependency str.
|
| +
|
| + Args:
|
| + dependency_str: (str) The issue and patchset the specified patchset depends
|
| + on.
|
| + dependent_issue_key: (str) The issue key of the patchset we want to mark as
|
| + dependent.
|
| + dependent_patchset_key: (str) The key of the patchset we want to mark as
|
| + dependent.
|
| + """
|
| + if not dependency_str:
|
| + return None
|
| + depends_on_tokens = get_dependency_tokens(dependency_str)
|
| + depends_on_issue = models.Issue.get_by_id(int(depends_on_tokens[0]))
|
| + if not depends_on_issue:
|
| + return None
|
| + depends_on_patchset = models.PatchSet.get_by_id(
|
| + int(depends_on_tokens[1]), parent=depends_on_issue.key)
|
| + if not depends_on_patchset:
|
| + return None
|
| +
|
| + if not depends_on_patchset.dependent_patchsets:
|
| + depends_on_patchset.dependent_patchsets = []
|
| + dependent_str = _get_dependency_str(dependent_issue_key,
|
| + dependent_patchset_key)
|
| + if dependent_str not in depends_on_patchset.dependent_patchsets:
|
| + depends_on_patchset.dependent_patchsets.append(dependent_str)
|
| + depends_on_patchset.put()
|
| +
|
| + return dependency_str
|
| +
|
|
|