+import re\r
+import datetime\r
+\r
+from slaclient import wsag_model\r
+from slaclient.wsag_model import AgreementStatus\r
+from slaclient.wsag_model import Violation\r
+\r
+\r
+VIOLATED = AgreementStatus.StatusEnum.VIOLATED\r
+NON_DETERMINED = AgreementStatus.StatusEnum.NON_DETERMINED\r
+FULFILLED = AgreementStatus.StatusEnum.FULFILLED\r
+\r
+\r
+def get_violations_bydate(violations):\r
+ """Returns a list of violations per date, from a list of violations\r
+\r
+ :param violations list[Violation]:\r
+ :rtype: list\r
+ """\r
+ d = dict()\r
+ for v in violations:\r
+ assert isinstance(v, Violation)\r
+ date = v.datetime.date()\r
+ if not date in d:\r
+ d[date] = []\r
+ d[date].append(v)\r
+\r
+ result = [(key, d[key]) for key in sorted(d.keys(), reverse=True)]\r
+ return result\r
+\r
+\r
+class AgreementAnnotator(object):\r
+ """Annotates an agreement with the following attributes:\r
+\r
+ agreement.guaranteestatus\r
+ agreement.statusclass\r
+ agreement.guaranteeterms[*].status\r
+ agreement.guaranteeterms[*].statusclass\r
+ agreement.guaranteeterms[*].nviolations\r
+ agreement.guaranteeterms[*].servicelevelobjetive.bounds\r
+\r
+ """\r
+ def __init__(self):\r
+ pass\r
+\r
+ @staticmethod\r
+ def _get_statusclass(status):\r
+ if status is None or status == "" or status == NON_DETERMINED:\r
+ return "non-determined"\r
+ return "success" if status == FULFILLED else "error"\r
+\r
+ @staticmethod\r
+ def _parse_bounds(servicelevel):\r
+# pattern = re.compile(".*BETWEEN *[(]?(.*), *([^)]*)[)]?")\r
+ pattern = re.compile(".*GT *([+-]?\\d*\\.\\d+)(?![-+0-9\\.])")\r
+ constraint = eval(servicelevel.strip(' \t\n\r'))\r
+ m = pattern.match(constraint['constraint'])\r
+ return m.groups()\r
+\r
+ def _annotate_guaranteeterm(self, term, violations):\r
+ #\r
+ # Annotate a guarantee term: set bounds and violations\r
+ #\r
+ level = term.servicelevelobjective.customservicelevel\r
+ bounds = AgreementAnnotator._parse_bounds(level)\r
+ term.servicelevelobjective.bounds = bounds\r
+\r
+ #\r
+ # set status attribute if not set before\r
+ #\r
+ if not hasattr(term, 'status'):\r
+ term.status = wsag_model.AgreementStatus.StatusEnum.NON_DETERMINED\r
+ #\r
+ # TODO: efficiency\r
+ #\r
+ n = 0\r
+ for violation in violations:\r
+ if violation.metric_name == term.servicelevelobjective.kpiname:\r
+ n += 1\r
+ term.nviolations = n\r
+\r
+ def _annotate_guaranteeterm_by_status(\r
+ self, agreement, termstatus, violations):\r
+ #\r
+ # Annotate a guarantee term: it is different from the previous\r
+ # one in that this takes the status into account.\r
+ #\r
+ name = termstatus.name\r
+ status = termstatus.status\r
+\r
+ term = agreement.guaranteeterms[name]\r
+ term.status = status\r
+ term.statusclass = AgreementAnnotator._get_statusclass(status)\r
+ self._annotate_guaranteeterm(term, violations)\r
+\r
+ def annotate_agreement(self, agreement, status=None, violations=()):\r
+\r
+ """Annotate an agreement with certain values needed in the templates\r
+\r
+ :param wsag_model.Agreement agreement: agreement to annotate\r
+ :param wsag_model.AgreementStatus status: status of the agreement.\r
+ :param violations: list of agreement's violations\r
+ (wsag_model.Violation[])\r
+ """\r
+ a = agreement\r
+\r
+ if status is not None:\r
+ a.guaranteestatus = status.guaranteestatus\r
+ a.statusclass = self._get_statusclass(status.guaranteestatus)\r
+ for termstatus in status.guaranteeterms:\r
+ self._annotate_guaranteeterm_by_status(\r
+ agreement, termstatus, violations)\r
+ else:\r
+ a.guaranteestatus = NON_DETERMINED\r
+ for termname, term in agreement.guaranteeterms.items():\r
+ self._annotate_guaranteeterm(term, violations)\r