{"action":"created","comment":{"url":"https://api.github.com/repos/metoppv/improver/pulls/comments/233800249","pull_request_review_id":175296820,"id":233800249,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMzgwMDI0OQ==","diff_hunk":"@@ -486,6 +484,369 @@ def __repr__(self):\n                        ' coord_adjust = {2}>')\n         return description.format(self.coord, self.mode, self.coord_adjust)\n \n+    def check_percentile_coord(self, cube):\n+        \"\"\"\n+        Determines if the cube to be blended has a percentile dimension\n+        coordinate.\n+\n+        Args:\n+            cube (iris.cube.Cube):\n+                The cube to be checked for a percentile coordinate.\n+        Returns:\n+            None or perc_coord (iris.coords.DimCoord):\n+                None if no percentile dimension coordinate is found. If\n+                such a coordinate is found it is returned.\n+        Raises:\n+            ValueError : If there is a percentile coord and it is not a\n+                dimension coord in the cube.\n+            ValueError : If there is a percentile dimension with only one\n+                point, we need at least two points in order to do the blending.\n+            ValueError : If there is a percentile dimension on the cube and the\n+                mode for blending is 'weighted_maximum'\n+        \"\"\"\n+        try:\n+            perc_coord = find_percentile_coordinate(cube)\n+            perc_dim = cube.coord_dims(perc_coord.name())\n+            if not perc_dim:\n+                msg = ('The percentile coord must be a dimension '\n+                       'of the cube.')\n+                raise ValueError(msg)\n+            # Check the percentile coordinate has more than one point,\n+            # otherwise raise an error as we won't be able to blend.\n+            if len(perc_coord.points) < 2.0:\n+                msg = ('Percentile coordinate does not have enough points'\n+                       ' in order to blend. Must have at least 2 percentiles.')\n+                raise ValueError(msg)\n+            # If we have a percentile dimension and the mode is 'max' raise an\n+            # exception.\n+            if perc_coord and self.mode == 'weighted_maximum':\n+                msg = ('The \"weighted_maximum\" mode cannot be used with'\n+                       ' percentile data.')\n+                raise ValueError(msg)\n+\n+            return perc_coord\n+        except CoordinateNotFoundError:\n+            return None\n+\n+    def check_compatible_time_points(self, cube):\n+        \"\"\"\n+        Check that the time coordinate only contains a single time if the\n+        coordinate being blended is forecast_reference_time.\n+\n+        Args:\n+            cube (iris.cube.Cube):\n+                The cube upon which the compatibility of the time coords is\n+                being checked.\n+        Raises:\n+            ValueError : If blending over forecast reference time on a cube\n+                         with multiple times.\n+        \"\"\"\n+        if self.coord == \"forecast_reference_time\":\n+            time_points = cube.coord(\"time\").points\n+            if len(time_points) > 1:\n+                msg = (\"For blending using the forecast_reference_time \"\n+                       \"coordinate, the points within the time coordinate need\"\n+                       \" to be the same. The time points within the input cube\"\n+                       \" are {}\".format(time_points))\n+                raise ValueError(msg)\n+\n+    def shape_weights(self, cube, weights):\n+        \"\"\"\n+        The function shapes weights to match the diagnostic cube. A 1D cube of\n+        weights that vary across the blending coordinate will be broadcast to\n+        match the complete multidimensional cube shape. A multidimensional cube\n+        of weights will be checked to ensure that the coordinate names match\n+        between the two cubes. If they match the order will be enforced and\n+        then the shape will be checked. If the shapes match the weights will be\n+        returned as an array.\n+\n+        Args:\n+            cube (iris.cube.Cube):\n+                The data cube on which a coordinate is being blended.\n+            weights (iris.cube.Cube):\n+                Cube of blending weights.\n+        Returns:\n+            weights_array (np.array):\n+                An array of weights that matches the cube data shape.\n+        Raises:\n+            ValueError: If weights cube coordinates do not match the diagnostic\n+                        cube in the case of a multidimensional weights cube.\n+            ValueError: If weights cube shape is not broadcastable to the data\n+                        cube shape.\n+        \"\"\"\n+        # Check that a multidimensional weights cube has coordinates that match\n+        # the diagnostic cube. Checking names only to not to be too exacting.\n+        weight_dims = [crd.name() for crd in weights.coords(dim_coords=True)]\n+        cube_dims = [crd.name() for crd in cube.coords(dim_coords=True)]\n+        if set(weight_dims) == set(cube_dims):\n+            enforce_coordinate_ordering(weights, cube_dims)\n+        elif len(weight_dims) > 1:\n+            msg = (\"Multidimensional weights cube does not contain the same \"\n+                   \"coordinates as the diagnostic cube. Weights: {}, \"\n+                   \"Diagnostic: {}\".format(weight_dims, cube_dims))\n+            raise ValueError(msg)\n+\n+        msg = (\"Weights cube is not a compatible shape with the data cube. \"\n+               \"Weights: {}, Diagnostic: {}\".format(weights.shape, cube.shape))\n+\n+        if weights.shape == cube.shape:\n+            # Multi-dimensional weights array provided that matches cube.\n+            weights_array = weights.data.astype(np.float32)\n+        else:\n+            # 1D array of weights across the blending coordinate.\n+            coord_dim_thres = cube.coord_dims(self.coord)\n+            try:\n+                weights_array = iris.util.broadcast_to_shape(\n+                    np.array(weights.data, dtype=np.float32),\n+                    cube.shape, coord_dim_thres)\n+            except ValueError:\n+                raise ValueError(msg)\n+\n+        return weights_array\n+\n+    @staticmethod\n+    def check_weights(weights, blend_dim):\n+        \"\"\"\n+        Checks that weights across the blending dimension sum up to 1.\n+\n+        Args:\n+            weights (np.array):\n+                Array of weights shaped to match the data cube.\n+            blend_dim (integer):\n+                The dimension in the weights array that is being collapsed.\n+        Raises:\n+            ValueError: Raised if the weights do not sum to 1 over the blending\n+                        dimension.\n+        \"\"\"\n+        sum_of_weights = np.sum(weights, axis=blend_dim)\n+        msg = ('Weights do not sum to 1 over the blending coordinate. Max sum '\n+               'of weights: {}'.format(sum_of_weights.max()))\n+        if not (np.isclose(sum_of_weights, 1)).all():\n+            raise ValueError(msg)\n+\n+    def non_percentile_weights(self, cube, weights, custom_aggregator=False):\n+        \"\"\"\n+        Given a 1 or multidimensional cube of weights, reshape and broadcast\n+        these in such a way as to make them applicable to the data cube. If no\n+        weights are provided, an array of weights is returned that equally\n+        weights all slices across the blending coordinate of the cube.\n+\n+        The output of this function is different depending upon the method\n+        being used to blend the data.\n+\n+        weighted_mean:\n+            reshape and broadcast to match data shape.\n+        weighted_maximum:\n+            reshape and broadcast to match data shape, before reordering in\n+            anticipation of the blending coord being shifted to the -1 position\n+            by the custom aggregator.\n+\n+        Args:\n+            cube (iris.cube.Cube):\n+                The data cube on which a coordinate is being blended.\n+            weights (iris.cube.Cube):\n+                Cube of blending weights.\n+        Returns:\n+            weights_array (np.array):\n+                An array of weights that matches the cube data shape.\n+        \"\"\"\n+        weights_array = None\n+        if weights is None:\n+            number_of_fields, = cube.coord(self.coord).shape\n+            weights_array = (\n+                np.broadcast_to(1./number_of_fields, cube.shape).astype(\n+                    np.float32))\n+\n+        blend_dim, = cube.coord_dims(self.coord)\n+\n+        if weights_array is None:\n+            weights_array = self.shape_weights(cube, weights)\n+\n+        # Our custom aggregator moves the blending coordinate to the -1\n+        # index, so we need to reshape the weights to match.\n+        if custom_aggregator:\n+            coord_dim = cube.coord_dims(self.coord)\n+            weights_array = np.moveaxis(weights_array, coord_dim, -1)\n+            self.check_weights(weights_array, -1)\n+        else:\n+            self.check_weights(weights_array, blend_dim)\n+\n+        return weights_array.astype(np.float32)\n+\n+    def percentile_weights(self, cube, weights, perc_coord):\n+        \"\"\"\n+        Given a 1, or multidimensional cube of weights, reshape and broadcast\n+        these in such a way as to make them applicable to the data cube. If no\n+        weights are provided, an array of weights is returned that equally\n+        weights all slices across the blending coordinate of the cube.\n+\n+        For percentiles the dimensionality of the weights cube is checked\n+        against the cube without the including the percentile coordinate for\n+        which no weights are likely to ever be provided (e.g. we don't want to\n+        weight different percentiles differently across the blending\n+        coordinate). Reshape and broadcast to match the data shape excluding\n+        the percentile dimension before finally broadcasting to match at the\n+        end.\n+\n+        Args:\n+            cube (iris.cube.Cube):\n+                The data cube on which a coordinate is being blended.\n+            weights (iris.cube.Cube):\n+                Cube of blending weights.\n+        Returns:\n+            weights_array (np.array):\n+                An array of weights that matches the cube data shape.\n+        \"\"\"\n+        weights_array = None","path":"lib/improver/blending/weighted_blend.py","position":379,"original_position":379,"commit_id":"ce7ced6a5320fcfb9d41a93f1ecb1bed1c5ec267","original_commit_id":"ce7ced6a5320fcfb9d41a93f1ecb1bed1c5ec267","user":{"login":"bayliffe","id":28042362,"node_id":"MDQ6VXNlcjI4MDQyMzYy","avatar_url":"https://avatars3.githubusercontent.com/u/28042362?v=4","gravatar_id":"","url":"https://api.github.com/users/bayliffe","html_url":"https://github.com/bayliffe","followers_url":"https://api.github.com/users/bayliffe/followers","following_url":"https://api.github.com/users/bayliffe/following{/other_user}","gists_url":"https://api.github.com/users/bayliffe/gists{/gist_id}","starred_url":"https://api.github.com/users/bayliffe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bayliffe/subscriptions","organizations_url":"https://api.github.com/users/bayliffe/orgs","repos_url":"https://api.github.com/users/bayliffe/repos","events_url":"https://api.github.com/users/bayliffe/events{/privacy}","received_events_url":"https://api.github.com/users/bayliffe/received_events","type":"User","site_admin":false},"body":"Why not.","created_at":"2018-11-15T11:06:27Z","updated_at":"2018-11-15T11:06:27Z","html_url":"https://github.com/metoppv/improver/pull/734#discussion_r233800249","pull_request_url":"https://api.github.com/repos/metoppv/improver/pulls/734","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/metoppv/improver/pulls/comments/233800249"},"html":{"href":"https://github.com/metoppv/improver/pull/734#discussion_r233800249"},"pull_request":{"href":"https://api.github.com/repos/metoppv/improver/pulls/734"}},"in_reply_to_id":233415456},"pull_request":{"url":"https://api.github.com/repos/metoppv/improver/pulls/734","id":230425849,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMwNDI1ODQ5","html_url":"https://github.com/metoppv/improver/pull/734","diff_url":"https://github.com/metoppv/improver/pull/734.diff","patch_url":"https://github.com/metoppv/improver/pull/734.patch","issue_url":"https://api.github.com/repos/metoppv/improver/issues/734","number":734,"state":"open","locked":false,"title":"IMPRO-888: Weights as cubes and spatially varying weights","user":{"login":"bayliffe","id":28042362,"node_id":"MDQ6VXNlcjI4MDQyMzYy","avatar_url":"https://avatars3.githubusercontent.com/u/28042362?v=4","gravatar_id":"","url":"https://api.github.com/users/bayliffe","html_url":"https://github.com/bayliffe","followers_url":"https://api.github.com/users/bayliffe/followers","following_url":"https://api.github.com/users/bayliffe/following{/other_user}","gists_url":"https://api.github.com/users/bayliffe/gists{/gist_id}","starred_url":"https://api.github.com/users/bayliffe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bayliffe/subscriptions","organizations_url":"https://api.github.com/users/bayliffe/orgs","repos_url":"https://api.github.com/users/bayliffe/repos","events_url":"https://api.github.com/users/bayliffe/events{/privacy}","received_events_url":"https://api.github.com/users/bayliffe/received_events","type":"User","site_admin":false},"body":"The PR changes our various weights generation functions such that they return cubes of weights. The weighted blending code is also modified to accept a cube of weights that varies spatially.\r\n\r\nThe work also includes substantial refactoring of the WeightedBlendAcrossWholeDimension plugin. The unit tests for the percentile aggregator were also modified to make clearer the impacts of various array rearrangements; the input data has been changed from a shape of 2,2,2,2 to 2,3,2,2, making clearer the impact of reshaping/rearrangement that is occurring.\r\n\r\nTesting:\r\n - [x] Ran tests and they passed OK\r\n - [x] Added new tests for the new feature(s)","created_at":"2018-11-13T10:41:15Z","updated_at":"2018-11-15T11:06:27Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7989d8a7790fd2f75265dbbc4bf27dc0a9110af5","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/metoppv/improver/pulls/734/commits","review_comments_url":"https://api.github.com/repos/metoppv/improver/pulls/734/comments","review_comment_url":"https://api.github.com/repos/metoppv/improver/pulls/comments{/number}","comments_url":"https://api.github.com/repos/metoppv/improver/issues/734/comments","statuses_url":"https://api.github.com/repos/metoppv/improver/statuses/ce7ced6a5320fcfb9d41a93f1ecb1bed1c5ec267","head":{"label":"bayliffe:impro888","ref":"impro888","sha":"ce7ced6a5320fcfb9d41a93f1ecb1bed1c5ec267","user":{"login":"bayliffe","id":28042362,"node_id":"MDQ6VXNlcjI4MDQyMzYy","avatar_url":"https://avatars3.githubusercontent.com/u/28042362?v=4","gravatar_id":"","url":"https://api.github.com/users/bayliffe","html_url":"https://github.com/bayliffe","followers_url":"https://api.github.com/users/bayliffe/followers","following_url":"https://api.github.com/users/bayliffe/following{/other_user}","gists_url":"https://api.github.com/users/bayliffe/gists{/gist_id}","starred_url":"https://api.github.com/users/bayliffe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bayliffe/subscriptions","organizations_url":"https://api.github.com/users/bayliffe/orgs","repos_url":"https://api.github.com/users/bayliffe/repos","events_url":"https://api.github.com/users/bayliffe/events{/privacy}","received_events_url":"https://api.github.com/users/bayliffe/received_events","type":"User","site_admin":false},"repo":{"id":89453063,"node_id":"MDEwOlJlcG9zaXRvcnk4OTQ1MzA2Mw==","name":"improver","full_name":"bayliffe/improver","private":false,"owner":{"login":"bayliffe","id":28042362,"node_id":"MDQ6VXNlcjI4MDQyMzYy","avatar_url":"https://avatars3.githubusercontent.com/u/28042362?v=4","gravatar_id":"","url":"https://api.github.com/users/bayliffe","html_url":"https://github.com/bayliffe","followers_url":"https://api.github.com/users/bayliffe/followers","following_url":"https://api.github.com/users/bayliffe/following{/other_user}","gists_url":"https://api.github.com/users/bayliffe/gists{/gist_id}","starred_url":"https://api.github.com/users/bayliffe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bayliffe/subscriptions","organizations_url":"https://api.github.com/users/bayliffe/orgs","repos_url":"https://api.github.com/users/bayliffe/repos","events_url":"https://api.github.com/users/bayliffe/events{/privacy}","received_events_url":"https://api.github.com/users/bayliffe/received_events","type":"User","site_admin":false},"html_url":"https://github.com/bayliffe/improver","description":"IMPROVER","fork":true,"url":"https://api.github.com/repos/bayliffe/improver","forks_url":"https://api.github.com/repos/bayliffe/improver/forks","keys_url":"https://api.github.com/repos/bayliffe/improver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bayliffe/improver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bayliffe/improver/teams","hooks_url":"https://api.github.com/repos/bayliffe/improver/hooks","issue_events_url":"https://api.github.com/repos/bayliffe/improver/issues/events{/number}","events_url":"https://api.github.com/repos/bayliffe/improver/events","assignees_url":"https://api.github.com/repos/bayliffe/improver/assignees{/user}","branches_url":"https://api.github.com/repos/bayliffe/improver/branches{/branch}","tags_url":"https://api.github.com/repos/bayliffe/improver/tags","blobs_url":"https://api.github.com/repos/bayliffe/improver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bayliffe/improver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bayliffe/improver/git/refs{/sha}","trees_url":"https://api.github.com/repos/bayliffe/improver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bayliffe/improver/statuses/{sha}","languages_url":"https://api.github.com/repos/bayliffe/improver/languages","stargazers_url":"https://api.github.com/repos/bayliffe/improver/stargazers","contributors_url":"https://api.github.com/repos/bayliffe/improver/contributors","subscribers_url":"https://api.github.com/repos/bayliffe/improver/subscribers","subscription_url":"https://api.github.com/repos/bayliffe/improver/subscription","commits_url":"https://api.github.com/repos/bayliffe/improver/commits{/sha}","git_commits_url":"https://api.github.com/repos/bayliffe/improver/git/commits{/sha}","comments_url":"https://api.github.com/repos/bayliffe/improver/comments{/number}","issue_comment_url":"https://api.github.com/repos/bayliffe/improver/issues/comments{/number}","contents_url":"https://api.github.com/repos/bayliffe/improver/contents/{+path}","compare_url":"https://api.github.com/repos/bayliffe/improver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bayliffe/improver/merges","archive_url":"https://api.github.com/repos/bayliffe/improver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bayliffe/improver/downloads","issues_url":"https://api.github.com/repos/bayliffe/improver/issues{/number}","pulls_url":"https://api.github.com/repos/bayliffe/improver/pulls{/number}","milestones_url":"https://api.github.com/repos/bayliffe/improver/milestones{/number}","notifications_url":"https://api.github.com/repos/bayliffe/improver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bayliffe/improver/labels{/name}","releases_url":"https://api.github.com/repos/bayliffe/improver/releases{/id}","deployments_url":"https://api.github.com/repos/bayliffe/improver/deployments","created_at":"2017-04-26T07:45:39Z","updated_at":"2018-10-23T16:01:24Z","pushed_at":"2018-11-14T08:47:38Z","git_url":"git://github.com/bayliffe/improver.git","ssh_url":"git@github.com:bayliffe/improver.git","clone_url":"https://github.com/bayliffe/improver.git","svn_url":"https://github.com/bayliffe/improver","homepage":null,"size":4125,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"metoppv:master","ref":"master","sha":"47221a6104c8da5cf84042b4efcb2403c6fe312e","user":{"login":"metoppv","id":26465485,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2NDY1NDg1","avatar_url":"https://avatars2.githubusercontent.com/u/26465485?v=4","gravatar_id":"","url":"https://api.github.com/users/metoppv","html_url":"https://github.com/metoppv","followers_url":"https://api.github.com/users/metoppv/followers","following_url":"https://api.github.com/users/metoppv/following{/other_user}","gists_url":"https://api.github.com/users/metoppv/gists{/gist_id}","starred_url":"https://api.github.com/users/metoppv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metoppv/subscriptions","organizations_url":"https://api.github.com/users/metoppv/orgs","repos_url":"https://api.github.com/users/metoppv/repos","events_url":"https://api.github.com/users/metoppv/events{/privacy}","received_events_url":"https://api.github.com/users/metoppv/received_events","type":"Organization","site_admin":false},"repo":{"id":85334761,"node_id":"MDEwOlJlcG9zaXRvcnk4NTMzNDc2MQ==","name":"improver","full_name":"metoppv/improver","private":false,"owner":{"login":"metoppv","id":26465485,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI2NDY1NDg1","avatar_url":"https://avatars2.githubusercontent.com/u/26465485?v=4","gravatar_id":"","url":"https://api.github.com/users/metoppv","html_url":"https://github.com/metoppv","followers_url":"https://api.github.com/users/metoppv/followers","following_url":"https://api.github.com/users/metoppv/following{/other_user}","gists_url":"https://api.github.com/users/metoppv/gists{/gist_id}","starred_url":"https://api.github.com/users/metoppv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metoppv/subscriptions","organizations_url":"https://api.github.com/users/metoppv/orgs","repos_url":"https://api.github.com/users/metoppv/repos","events_url":"https://api.github.com/users/metoppv/events{/privacy}","received_events_url":"https://api.github.com/users/metoppv/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/metoppv/improver","description":"IMPROVER is a library of algorithms for meteorological post-processing and verification.","fork":false,"url":"https://api.github.com/repos/metoppv/improver","forks_url":"https://api.github.com/repos/metoppv/improver/forks","keys_url":"https://api.github.com/repos/metoppv/improver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/metoppv/improver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/metoppv/improver/teams","hooks_url":"https://api.github.com/repos/metoppv/improver/hooks","issue_events_url":"https://api.github.com/repos/metoppv/improver/issues/events{/number}","events_url":"https://api.github.com/repos/metoppv/improver/events","assignees_url":"https://api.github.com/repos/metoppv/improver/assignees{/user}","branches_url":"https://api.github.com/repos/metoppv/improver/branches{/branch}","tags_url":"https://api.github.com/repos/metoppv/improver/tags","blobs_url":"https://api.github.com/repos/metoppv/improver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/metoppv/improver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/metoppv/improver/git/refs{/sha}","trees_url":"https://api.github.com/repos/metoppv/improver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/metoppv/improver/statuses/{sha}","languages_url":"https://api.github.com/repos/metoppv/improver/languages","stargazers_url":"https://api.github.com/repos/metoppv/improver/stargazers","contributors_url":"https://api.github.com/repos/metoppv/improver/contributors","subscribers_url":"https://api.github.com/repos/metoppv/improver/subscribers","subscription_url":"https://api.github.com/repos/metoppv/improver/subscription","commits_url":"https://api.github.com/repos/metoppv/improver/commits{/sha}","git_commits_url":"https://api.github.com/repos/metoppv/improver/git/commits{/sha}","comments_url":"https://api.github.com/repos/metoppv/improver/comments{/number}","issue_comment_url":"https://api.github.com/repos/metoppv/improver/issues/comments{/number}","contents_url":"https://api.github.com/repos/metoppv/improver/contents/{+path}","compare_url":"https://api.github.com/repos/metoppv/improver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/metoppv/improver/merges","archive_url":"https://api.github.com/repos/metoppv/improver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/metoppv/improver/downloads","issues_url":"https://api.github.com/repos/metoppv/improver/issues{/number}","pulls_url":"https://api.github.com/repos/metoppv/improver/pulls{/number}","milestones_url":"https://api.github.com/repos/metoppv/improver/milestones{/number}","notifications_url":"https://api.github.com/repos/metoppv/improver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/metoppv/improver/labels{/name}","releases_url":"https://api.github.com/repos/metoppv/improver/releases{/id}","deployments_url":"https://api.github.com/repos/metoppv/improver/deployments","created_at":"2017-03-17T16:51:29Z","updated_at":"2018-11-14T15:55:55Z","pushed_at":"2018-11-15T10:53:24Z","git_url":"git://github.com/metoppv/improver.git","ssh_url":"git@github.com:metoppv/improver.git","clone_url":"https://github.com/metoppv/improver.git","svn_url":"https://github.com/metoppv/improver","homepage":"http://improver.readthedocs.io/en/latest/","size":4084,"stargazers_count":20,"watchers_count":20,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":29,"mirror_url":null,"archived":false,"open_issues_count":90,"license":{"key":"other","name":"Other","spdx_id":"NOASSERTION","url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":29,"open_issues":90,"watchers":20,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/metoppv/improver/pulls/734"},"html":{"href":"https://github.com/metoppv/improver/pull/734"},"issue":{"href":"https://api.github.com/repos/metoppv/improver/issues/734"},"comments":{"href":"https://api.github.com/repos/metoppv/improver/issues/734/comments"},"review_comments":{"href":"https://api.github.com/repos/metoppv/improver/pulls/734/comments"},"review_comment":{"href":"https://api.github.com/repos/metoppv/improver/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/metoppv/improver/pulls/734/commits"},"statuses":{"href":"https://api.github.com/repos/metoppv/improver/statuses/ce7ced6a5320fcfb9d41a93f1ecb1bed1c5ec267"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"bayliffe"}}