def patch(self, species_id):
        """Выполняет принудительное обновление вида листа.

        :param species_id: Идентификатор вида
        :type species_id: str
        """
        _id = ObjectId(species_id)

        species = yield self.application.async_db.species.find_one({"_id": _id})

        if species:
            yield self.application.async_db.species.update(
                {"_id": _id},
                {"$set": {"modified": datetime.utcnow()}}
            )

            species = yield self.application.async_db.species.find_one({"_id": _id})

            yield [send_request(
                branch,
                "branch/species/{}".format(species["_id"]),
                "PATCH",
                species
            ) for branch in self.application.druid.branch]

            cursor = self.application.async_db.leaves.find({"type": species["_id"], "active": True})
            while (yield cursor.fetch_next):
                leaf = full_leaf_info(cursor.next_object(), self.application.druid.air, species)
                branch = next(x for x in self.application.druid.branch if x["name"] == leaf["branch"])
                yield branch_start_leaf(branch, leaf)
        else:
            self.set_status(404)

        self.finish("{}")
    def put(self, branch_name):
        """Выполняет принудительную проверку всех листьев на указанной ветви.

        :param branch_name: Имя ветви
        :type branch_name: str
        """
        try:
            branch = next(x for x in self.application.druid.branch if x["name"] == branch_name)
        except StopIteration:
            self.set_status(404)
            self.finish()
            raise gen.Return()

        cursor = self.application.async_db.leaves.find({"branch": branch_name, "active": True})

        verified_species = set()

        while (yield cursor.fetch_next):
            leaf = cursor.next_object()

            species = yield self.application.async_db.species.find_one({"_id": leaf["type"]})
            leaf = full_leaf_info(leaf, self.application.druid.air, species)

            if leaf["type"] not in verified_species:
                yield branch_prepare_species(branch, species)
                verified_species.add(leaf["type"])

            yield branch_start_leaf(branch, leaf)

        self.finish(dumps({"result": "success"}))
    def put(self, branch_name):
        """Выполняет принудительную проверку всех листьев на указанной ветви.

        :param branch_name: Имя ветви
        :type branch_name: str
        """
        try:
            branch = next(x for x in self.application.druid.branch
                          if x["name"] == branch_name)
        except StopIteration:
            self.set_status(404)
            self.finish()
            raise gen.Return()

        cursor = self.application.async_db.leaves.find({
            "branch": branch_name,
            "active": True
        })

        verified_species = set()

        while (yield cursor.fetch_next):
            leaf = cursor.next_object()

            species = yield self.application.async_db.species.find_one(
                {"_id": leaf["type"]})
            leaf = full_leaf_info(leaf, self.application.druid.air, species)

            if leaf["type"] not in verified_species:
                yield branch_prepare_species(branch, species)
                verified_species.add(leaf["type"])

            yield branch_start_leaf(branch, leaf)

        self.finish(dumps({"result": "success"}))
    def patch(self, leaf_name):
        """Модифицирует информацию о листе.

        :param leaf_name: Имя листа
        :type leaf_name: str
        """
        # Обрабатываем только ключи active, address
        apply_changes = self.get_argument("apply",
                                          default="TRUE").upper() == "TRUE"

        keys = ["active", "address"]
        data = loads(self.request.body)

        for key in data.keys():
            if key not in keys:
                del data[key]

        leaf_data = yield self.application.async_db.leaves.find_one(
            {"name": leaf_name})
        if not leaf_data:
            self.set_status(404)
            self.finish(dumps({
                "result": "failure",
                "message": "Unknown leaf"
            }))
            raise gen.Return()

        yield self.application.async_db.leaves.update({"name": leaf_name},
                                                      {"$set": data})

        leaf = yield self.application.async_db.leaves.find_one(
            {"name": leaf_name})

        if apply_changes:
            if leaf["active"]:
                branch = next(x for x in self.application.druid.branch
                              if x["name"] == leaf["branch"])

                species = yield self.application.async_db.species.find_one(
                    {"_id": leaf["type"]})

                leaf = full_leaf_info(leaf, self.application.druid.air,
                                      species)

                yield branch_prepare_species(branch, species)
                yield branch_start_leaf(branch, leaf)

                yield [
                    air_enable_host(air, address) for air, address in product(
                        self.application.druid.air, leaf["address"])
                ]
            else:
                branch = next(x for x in self.application.druid.branch
                              if x["name"] == leaf["branch"])
                yield branch_stop_leaf(branch, leaf)
        self.finish(dumps({"result": "success", "message": "OK"}))
    def patch(self, leaf_name):
        """Модифицирует информацию о листе.

        :param leaf_name: Имя листа
        :type leaf_name: str
        """
        # Обрабатываем только ключи active, address
        apply_changes = self.get_argument("apply", default="TRUE").upper() == "TRUE"

        keys = ["active", "address"]
        data = loads(self.request.body)

        for key in data.keys():
            if key not in keys:
                del data[key]

        leaf_data = yield self.application.async_db.leaves.find_one({"name": leaf_name})
        if not leaf_data:
            self.set_status(404)
            self.finish(dumps({"result": "failure", "message": "Unknown leaf"}))
            raise gen.Return()

        yield self.application.async_db.leaves.update(
            {"name": leaf_name},
            {"$set": data}
        )

        leaf = yield self.application.async_db.leaves.find_one({"name": leaf_name})

        if apply_changes:
            if leaf["active"]:
                branch = next(x for x in self.application.druid.branch if x["name"] == leaf["branch"])

                species = yield self.application.async_db.species.find_one({"_id": leaf["type"]})

                leaf = full_leaf_info(leaf, self.application.druid.air, species)

                yield branch_prepare_species(branch, species)
                yield branch_start_leaf(branch, leaf)

                yield [
                    air_enable_host(air, address) for air, address in product(
                        self.application.druid.air,
                        leaf["address"]
                    )
                ]
            else:
                branch = next(x for x in self.application.druid.branch if x["name"] == leaf["branch"])
                yield branch_stop_leaf(branch, leaf)
        self.finish(dumps({"result": "success", "message": "OK"}))
    def patch(self, species_id):
        """Выполняет принудительное обновление вида листа.

        :param species_id: Идентификатор вида
        :type species_id: str
        """
        _id = ObjectId(species_id)

        species = yield self.application.async_db.species.find_one(
            {"_id": _id})

        if species:
            yield self.application.async_db.species.update(
                {"_id": _id}, {"$set": {
                    "modified": datetime.utcnow()
                }})

            species = yield self.application.async_db.species.find_one(
                {"_id": _id})

            yield [
                send_request(branch,
                             "branch/species/{}".format(species["_id"]),
                             "PATCH", species)
                for branch in self.application.druid.branch
            ]

            cursor = self.application.async_db.leaves.find({
                "type":
                species["_id"],
                "active":
                True
            })
            while (yield cursor.fetch_next):
                leaf = full_leaf_info(cursor.next_object(),
                                      self.application.druid.air, species)
                branch = next(x for x in self.application.druid.branch
                              if x["name"] == leaf["branch"])
                yield branch_start_leaf(branch, leaf)
        else:
            self.set_status(404)

        self.finish("{}")
    def post(self, **data):
        """Создает новый лист."""
        with (yield self.application.druid.creation_lock.acquire()):
            leaf_address_check = yield self.application.async_db.leaves.find_one({
                "$or": [
                    {"address": data["address"]},
                    {"name": data["name"]}
                ]
            })

            if leaf_address_check:
                self.set_status(400)
                self.finish(dumps({
                    "result": "error",
                    "message": "Duplicate address"
                }))
                raise gen.Return()

            try:
                query = {"_id": ObjectId(data["type"])}
            except (TypeError, InvalidId):
                query = {"name": data["type"]}

            species = yield self.application.async_db.species.find_one(query)

            if not species:
                self.set_status(400)
                self.finish(dumps({
                    "result": "error",
                    "message": "Unknown species"
                }))
                raise gen.Return()

            branch = random.choice(self.application.druid.branch)

            leaf_id = yield self.application.async_db.leaves.insert(
                {
                    "name": data["name"],
                    "desc": data.get("description", ""),
                    "type": species["_id"],
                    "active": data.get("start", True),
                    "address": [data["address"]],
                    "branch": branch["name"],
                    "settings": data.get("settings", {})
                }
            )

            yield [air_enable_host(air, data["address"]) for air in self.application.druid.air]

            if species.get("requires", []):
                roots = self.application.druid.roots[0]
                db_settings, code = yield send_request(
                    roots,
                    "roots/db",
                    "POST",
                    {
                        "name": leaf_id,
                        "db_type": species["requires"]
                    }
                )

                yield self.application.async_db.leaves.update(
                    {"_id": leaf_id},
                    {"$set": {"batteries": db_settings}}
                )
            else:
                pass

            leaf = yield self.application.async_db.leaves.find_one({"_id": leaf_id})

            if leaf.get("active", True):
                leaf = full_leaf_info(leaf, self.application.druid.air, species)

                yield branch_prepare_species(branch, species)
                yield branch_start_leaf(branch, leaf)

            self.finish(dumps({"result": "success", "message": "OK", "branch": branch["name"]}))
    def post(self, **data):
        """Создает новый лист."""
        with (yield self.application.druid.creation_lock.acquire()):
            leaf_address_check = yield self.application.async_db.leaves.find_one(
                {
                    "$or": [{
                        "address": data["address"]
                    }, {
                        "name": data["name"]
                    }]
                })

            if leaf_address_check:
                self.set_status(400)
                self.finish(
                    dumps({
                        "result": "error",
                        "message": "Duplicate address"
                    }))
                raise gen.Return()

            try:
                query = {"_id": ObjectId(data["type"])}
            except (TypeError, InvalidId):
                query = {"name": data["type"]}

            species = yield self.application.async_db.species.find_one(query)

            if not species:
                self.set_status(400)
                self.finish(
                    dumps({
                        "result": "error",
                        "message": "Unknown species"
                    }))
                raise gen.Return()

            branch = random.choice(self.application.druid.branch)

            leaf_id = yield self.application.async_db.leaves.insert({
                "name":
                data["name"],
                "desc":
                data.get("description", ""),
                "type":
                species["_id"],
                "active":
                data.get("start", True),
                "address": [data["address"]],
                "branch":
                branch["name"],
                "settings":
                data.get("settings", {})
            })

            yield [
                air_enable_host(air, data["address"])
                for air in self.application.druid.air
            ]

            if species.get("requires", []):
                roots = self.application.druid.roots[0]
                db_settings, code = yield send_request(
                    roots, "roots/db", "POST", {
                        "name": leaf_id,
                        "db_type": species["requires"]
                    })

                yield self.application.async_db.leaves.update(
                    {"_id": leaf_id}, {"$set": {
                        "batteries": db_settings
                    }})
            else:
                pass

            leaf = yield self.application.async_db.leaves.find_one(
                {"_id": leaf_id})

            if leaf.get("active", True):
                leaf = full_leaf_info(leaf, self.application.druid.air,
                                      species)

                yield branch_prepare_species(branch, species)
                yield branch_start_leaf(branch, leaf)

            self.finish(
                dumps({
                    "result": "success",
                    "message": "OK",
                    "branch": branch["name"]
                }))