# Path: vulyk/app.py
# TASKS_TYPES = bootstrap.init_plugins(app)
# 
# Path: vulyk/models/tasks.py
# class Batch(Document):
#     """
#     Helper category to group tasks.
#     """
#     id = StringField(max_length=50, primary_key=True)
#     task_type = StringField(max_length=50, required=True, db_field='taskType')
#     tasks_count = IntField(default=0, required=True, db_field='tasksCount')
#     tasks_processed = IntField(default=0, db_field='tasksProcessed')
#     closed = BooleanField(default=False, required=False)
#     batch_meta = DictField(db_field='batchMeta')
# 
#     meta = {
#         'collection': 'batches',
#         'allow_inheritance': True,
#         'indexes': [
#             'task_type',
#             'closed'
#         ]
#     }
# 
#     @classmethod
#     def task_done_in(cls, batch_id: str) -> BatchUpdateResult:
#         """
#         Increment needed values upon a task from the batch is done. In case if
#         all tasks are finished – close the batch.
# 
#         :param batch_id: Batch ID
#         :type batch_id: str
# 
#         :return: Aggregate which represents complex effect of the method
#         :rtype: BatchUpdateResult
#         """
#         num_changed = 0
#         batch = cls.objects.get(id=batch_id)  # type: Batch
#         processed = batch.tasks_processed + 1
# 
#         if processed > batch.tasks_count:
#             return BatchUpdateResult(success=False, closed=False)
# 
#         closed = processed == batch.tasks_count
#         update_q = {'inc__tasks_processed': 1}
# 
#         if closed:
#             update_q['set__closed'] = closed
#             num_changed = cls \
#                 .objects(id=batch.id, closed=False) \
#                 .update(**update_q)
# 
#         if num_changed == 0:
#             update_q.pop('set__closed', None)
#             closed = False
# 
#             num_changed = batch.update(**update_q)
#         elif closed:
#             on_batch_done.send(batch)
# 
#         return BatchUpdateResult(success=num_changed > 0, closed=closed)
# 
#     def __str__(self) -> str:
#         return str(self.id)
# 
#     def __repr__(self) -> str:
#         return 'Batch [{id}] ({processed}/{count})'.format(
#             id=self.id,
#             processed=self.tasks_processed,
#             count=self.tasks_count)
# 
# class AbstractTask(Document):
#     """
#     This is AbstractTask model.
#     You need to inherit it in your model
#     """
#     id = StringField(max_length=200, default='', primary_key=True)
#     task_type = StringField(max_length=50, required=True, db_field='taskType')
#     batch = ReferenceField(Batch, reverse_delete_rule=CASCADE)
# 
#     users_count = IntField(default=0, db_field='usersCount')
#     users_processed = ListField(ReferenceField(User),
#                                 db_field='usersProcessed')
#     users_skipped = ListField(ReferenceField(User), db_field='usersSkipped')
# 
#     closed = BooleanField(default=False)
#     task_data = DictField(required=True)
# 
#     meta = {
#         'collection': 'tasks',
#         'allow_inheritance': True,
#         'indexes': [
#             'task_type',
#             'batch'
#         ]
#     }
# 
#     def as_dict(self) -> Dict[str, Any]:
#         """
#         Converts the model-instance into a safe and lightweight dictionary.
# 
#         :rtype: Dict[str, Any]
#         """
#         return {
#             'id': self.id,
#             'closed': self.closed,
#             'data': self.task_data
#         }
# 
#     @classmethod
#     def ids_in_batch(cls, batch: Batch) -> List[str]:
#         """
#         Collects IDs of all tasks that belong to certain batch.
# 
#         :param batch: Batch instance
#         :type batch: Batch
# 
#         :return: List of IDs
#         :rtype: List[str]
#         """
#         return cls.objects(batch=batch).distinct('id')
# 
#     def __str__(self) -> str:
#         return str(self.id)
# 
#     def __repr__(self) -> str:
#         return str(self)


from collections import OrderedDict
from mongoengine import Q
from vulyk.app import TASKS_TYPES
from vulyk.models.tasks import Batch, AbstractTask
            rs_task = TASKS_TYPES[rs(b.id).first().task_type]

            answers = rs(b.id).filter(closed=False).sum('users_count')
            answers_all = rs(b.id).sum('users_count')
            answers_mix = answers + (rs_task.redundancy * b.tasks_processed)
            answers_needed = b.tasks_count * rs_task.redundancy

            batches[b.id] = {
                'total': b.tasks_count,
                'flag': b.tasks_processed,
                'flag_percent': percent(b.tasks_processed, b.tasks_count),
                'answers': answers_all,
                'answers_percent': percent(answers_mix, answers_needed),
                'breakdown': _breakdown_by_processed(b.id)
            }

    return batches


def _breakdown_by_processed(batch: str) -> str:
    """
    Combines stats on responses count ratio for certain batch

    :param batch: Batch ID
    :type batch: str

    :returns: string ready to be displayed in CLI
    :rtype: str
    """
    result = []

# Path: vulyk/app.py
# TASKS_TYPES = bootstrap.init_plugins(app)
# 
# Path: vulyk/models/tasks.py
# class Batch(Document):
#     """
#     Helper category to group tasks.
#     """
#     id = StringField(max_length=50, primary_key=True)
#     task_type = StringField(max_length=50, required=True, db_field='taskType')
#     tasks_count = IntField(default=0, required=True, db_field='tasksCount')
#     tasks_processed = IntField(default=0, db_field='tasksProcessed')
#     closed = BooleanField(default=False, required=False)
#     batch_meta = DictField(db_field='batchMeta')
# 
#     meta = {
#         'collection': 'batches',
#         'allow_inheritance': True,
#         'indexes': [
#             'task_type',
#             'closed'
#         ]
#     }
# 
#     @classmethod
#     def task_done_in(cls, batch_id: str) -> BatchUpdateResult:
#         """
#         Increment needed values upon a task from the batch is done. In case if
#         all tasks are finished – close the batch.
# 
#         :param batch_id: Batch ID
#         :type batch_id: str
# 
#         :return: Aggregate which represents complex effect of the method
#         :rtype: BatchUpdateResult
#         """
#         num_changed = 0
#         batch = cls.objects.get(id=batch_id)  # type: Batch
#         processed = batch.tasks_processed + 1
# 
#         if processed > batch.tasks_count:
#             return BatchUpdateResult(success=False, closed=False)
# 
#         closed = processed == batch.tasks_count
#         update_q = {'inc__tasks_processed': 1}
# 
#         if closed:
#             update_q['set__closed'] = closed
#             num_changed = cls \
#                 .objects(id=batch.id, closed=False) \
#                 .update(**update_q)
# 
#         if num_changed == 0:
#             update_q.pop('set__closed', None)
#             closed = False
# 
#             num_changed = batch.update(**update_q)
#         elif closed:
#             on_batch_done.send(batch)
# 
#         return BatchUpdateResult(success=num_changed > 0, closed=closed)
# 
#     def __str__(self) -> str:
#         return str(self.id)
# 
#     def __repr__(self) -> str:
#         return 'Batch [{id}] ({processed}/{count})'.format(
#             id=self.id,
#             processed=self.tasks_processed,
#             count=self.tasks_count)
# 
# class AbstractTask(Document):
#     """
#     This is AbstractTask model.
#     You need to inherit it in your model
#     """
#     id = StringField(max_length=200, default='', primary_key=True)
#     task_type = StringField(max_length=50, required=True, db_field='taskType')
#     batch = ReferenceField(Batch, reverse_delete_rule=CASCADE)
# 
#     users_count = IntField(default=0, db_field='usersCount')
#     users_processed = ListField(ReferenceField(User),
#                                 db_field='usersProcessed')
#     users_skipped = ListField(ReferenceField(User), db_field='usersSkipped')
# 
#     closed = BooleanField(default=False)
#     task_data = DictField(required=True)
# 
#     meta = {
#         'collection': 'tasks',
#         'allow_inheritance': True,
#         'indexes': [
#             'task_type',
#             'batch'
#         ]
#     }
# 
#     def as_dict(self) -> Dict[str, Any]:
#         """
#         Converts the model-instance into a safe and lightweight dictionary.
# 
#         :rtype: Dict[str, Any]
#         """
#         return {
#             'id': self.id,
#             'closed': self.closed,
#             'data': self.task_data
#         }
# 
#     @classmethod
#     def ids_in_batch(cls, batch: Batch) -> List[str]:
#         """
#         Collects IDs of all tasks that belong to certain batch.
# 
#         :param batch: Batch instance
#         :type batch: Batch
# 
#         :return: List of IDs
#         :rtype: List[str]
#         """
#         return cls.objects(batch=batch).distinct('id')
# 
#     def __str__(self) -> str:
#         return str(self.id)
# 
#     def __repr__(self) -> str:
#         return str(self)


# Path: vulyk/cli/stats.py
from collections import OrderedDict
from mongoengine import Q
from vulyk.app import TASKS_TYPES
from vulyk.models.tasks import Batch, AbstractTask

            rs_task = TASKS_TYPES[rs(b.id).first().task_type]

            answers = rs(b.id).filter(closed=False).sum('users_count')
            answers_all = rs(b.id).sum('users_count')
            answers_mix = answers + (rs_task.redundancy * b.tasks_processed)
            answers_needed = b.tasks_count * rs_task.redundancy

            batches[b.id] = {
                'total': b.tasks_count,
                'flag': b.tasks_processed,
                'flag_percent': percent(b.tasks_processed, b.tasks_count),
                'answers': answers_all,
                'answers_percent': percent(answers_mix, answers_needed),
                'breakdown': _breakdown_by_processed(b.id)
            }

    return batches


def _breakdown_by_processed(batch: str) -> str:
    """
    Combines stats on responses count ratio for certain batch

    :param batch: Batch ID
    :type batch: str

    :returns: string ready to be displayed in CLI
    :rtype: str
    """
    result = []

    rs = AbstractTask.objects(batch=batch)