# Path: mass_flask_core/models/sample.py
# class Sample(db.Document, CommentsMixin):
#     delivery_date = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     first_seen = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     tags = ListField(StringField(regex=r'^[\w:\-\_\/\+\.]+$'))
#     dispatched_to = ListField(ReferenceField(AnalysisSystem))
#     created_by = GenericReferenceField()
#     tlp_level = TLPLevelField(verbose_name='TLP Level', help_text='Privacy level of this sample', required=True)
# 
#     meta = {
#         'allow_inheritance': True,
#         'ordering': ['-delivery_date'],
#         'indexes': ['delivery_date'],
#         'queryset_class': SampleQuerySet
#     }
# 
#     def __repr__(self):
#         return '[{}] {}'.format(str(self.__class__.__name__), str(self.id))
# 
#     def __str__(self):
#         return self.__repr__()
# 
#     @property
#     def title(self):
#         return self.id
# 
#     def add_tags(self, tags):
#         self.tags = ListFunctions.merge_lists_without_duplicates(self.tags, tags)
#         self.save()
# 
#     def _update(self, **kwargs):
#         if 'first_seen' in kwargs and kwargs['first_seen'] < self.first_seen:
#             self.first_seen = kwargs['first_seen']
#         if 'tlp_level' in kwargs and not self.tlp_level:
#             self.tlp_level = kwargs['tlp_level']
#         if current_authenticated_entity.is_authenticated and not self.created_by:
#             self.created_by = current_authenticated_entity._get_current_object()
#         if 'tags' in kwargs:
#             tags = kwargs['tags']
#         else:
#             tags = []
#         self.tags = ListFunctions.merge_lists_without_duplicates(self.tags, tags)
# 
#     @classmethod
#     def create_or_update(cls, **kwargs):
#         raise ValidationError('Samples of the base type Sample should not be instantiated directly.')
# 
# Path: mass_flask_core/models/analysis_system.py
# class AnalysisSystem(db.Document):
#     identifier_name = StringField(min_length=3, max_length=50, unique=True, required=True)
#     verbose_name = StringField(max_length=200, required=True)
#     information_text = StringField()
#     tag_filter_expression = StringField(max_length=400, required=True, default='')
# 
#     meta = {
#         'ordering': ['identifier_name'],
#         'indexes': ['identifier_name']
#     }
# 
#     def __repr__(self):
#         return '[AnalysisSystem] {}'.format(self.identifier_name)
# 
#     def __str__(self):
#         return self.__repr__()
# 
# Path: mass_flask_core/models/analysis_request.py
# class AnalysisRequest(db.Document):
#     analysis_system = ReferenceField(AnalysisSystem, required=True)
#     sample = ReferenceField(Sample, required=True)
#     analysis_requested = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     priority = IntField(default=0, required=True)
# 
#     meta = {
#         'ordering': ['-analysis_requested'],
#         'indexes': ['analysis_requested']
#     }
# 
#     def __repr__(self):
#         return '[AnalysisRequest] {} on {}'.format(self.sample.id, self.analysis_system.identifier_name)
# 
#     def __str__(self):
#         return self.__repr__()
# 
# Path: mass_flask_core/utils/tag_parser.py
# class TagParser:
# 
#     MAX_TAG_LENGTH = 160
# 
#     def __init__(self, tag_list):
#         self.tag_list = tag_list
#         self._init_parser()
# 
#     def _bool_op(self, t):
#         tag = t[0]
#         if tag in self.tag_list:
#             return True
#         else:
#             return False
# 
#     def _bool_and(self, t):
#         arg1 = t[0][0]
#         arg2 = t[0][2]
#         return arg1 & arg2
# 
#     def _bool_or(self, t):
#         arg1 = t[0][0]
#         arg2 = t[0][2]
#         return arg1 | arg2
# 
#     def _bool_not(self, t):
#         arg = t[0][1]
#         return not arg
# 
#     def _init_parser(self):
#         TRUE = Keyword('True')
#         FALSE = Keyword('False')
#         boolOperand = TRUE | FALSE | Regex(r'[a-zA-Z0-9:\-\/]+')
#         boolOperand.setParseAction(self._bool_op)
# 
#         self._parse_expr = infixNotation(
#             boolOperand,
#             [
#                 ('not', 1, opAssoc.RIGHT, self._bool_not),
#                 ('and', 2, opAssoc.LEFT,  self._bool_and),
#                 ('or',  2, opAssoc.LEFT,  self._bool_or),
#             ])
# 
#     def parse_string(self, string):
#         if string == '':
#             return True
#         try:
#             return self._parse_expr.parseString(string)[0]
#         except ParseException:
#             return False


from mass_flask_core.models import Sample, AnalysisSystem, AnalysisRequest
from mass_flask_core.utils.tag_parser import TagParser


def _filter_matches_tags(tags, tag_filter):
    return TagParser(tags).parse_string(tag_filter)


def _match_sample_and_system(sample, system):
    if system in sample.dispatched_to:
        return
    if not _filter_matches_tags(sample.tags, system.tag_filter_expression):
        return
    analysis_request = AnalysisRequest(sample=sample, analysis_system=system)
    analysis_request.save()
    sample.dispatched_to.append(system)
    sample.save()


def update_dispatch_request_for_new_sample(sender, document, **kwargs):
    if not issubclass(sender, Sample):
        return
    if kwargs.get('created') is True:

# Path: mass_flask_core/models/sample.py
# class Sample(db.Document, CommentsMixin):
#     delivery_date = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     first_seen = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     tags = ListField(StringField(regex=r'^[\w:\-\_\/\+\.]+$'))
#     dispatched_to = ListField(ReferenceField(AnalysisSystem))
#     created_by = GenericReferenceField()
#     tlp_level = TLPLevelField(verbose_name='TLP Level', help_text='Privacy level of this sample', required=True)
# 
#     meta = {
#         'allow_inheritance': True,
#         'ordering': ['-delivery_date'],
#         'indexes': ['delivery_date'],
#         'queryset_class': SampleQuerySet
#     }
# 
#     def __repr__(self):
#         return '[{}] {}'.format(str(self.__class__.__name__), str(self.id))
# 
#     def __str__(self):
#         return self.__repr__()
# 
#     @property
#     def title(self):
#         return self.id
# 
#     def add_tags(self, tags):
#         self.tags = ListFunctions.merge_lists_without_duplicates(self.tags, tags)
#         self.save()
# 
#     def _update(self, **kwargs):
#         if 'first_seen' in kwargs and kwargs['first_seen'] < self.first_seen:
#             self.first_seen = kwargs['first_seen']
#         if 'tlp_level' in kwargs and not self.tlp_level:
#             self.tlp_level = kwargs['tlp_level']
#         if current_authenticated_entity.is_authenticated and not self.created_by:
#             self.created_by = current_authenticated_entity._get_current_object()
#         if 'tags' in kwargs:
#             tags = kwargs['tags']
#         else:
#             tags = []
#         self.tags = ListFunctions.merge_lists_without_duplicates(self.tags, tags)
# 
#     @classmethod
#     def create_or_update(cls, **kwargs):
#         raise ValidationError('Samples of the base type Sample should not be instantiated directly.')
# 
# Path: mass_flask_core/models/analysis_system.py
# class AnalysisSystem(db.Document):
#     identifier_name = StringField(min_length=3, max_length=50, unique=True, required=True)
#     verbose_name = StringField(max_length=200, required=True)
#     information_text = StringField()
#     tag_filter_expression = StringField(max_length=400, required=True, default='')
# 
#     meta = {
#         'ordering': ['identifier_name'],
#         'indexes': ['identifier_name']
#     }
# 
#     def __repr__(self):
#         return '[AnalysisSystem] {}'.format(self.identifier_name)
# 
#     def __str__(self):
#         return self.__repr__()
# 
# Path: mass_flask_core/models/analysis_request.py
# class AnalysisRequest(db.Document):
#     analysis_system = ReferenceField(AnalysisSystem, required=True)
#     sample = ReferenceField(Sample, required=True)
#     analysis_requested = DateTimeField(default=TimeFunctions.get_timestamp, required=True)
#     priority = IntField(default=0, required=True)
# 
#     meta = {
#         'ordering': ['-analysis_requested'],
#         'indexes': ['analysis_requested']
#     }
# 
#     def __repr__(self):
#         return '[AnalysisRequest] {} on {}'.format(self.sample.id, self.analysis_system.identifier_name)
# 
#     def __str__(self):
#         return self.__repr__()
# 
# Path: mass_flask_core/utils/tag_parser.py
# class TagParser:
# 
#     MAX_TAG_LENGTH = 160
# 
#     def __init__(self, tag_list):
#         self.tag_list = tag_list
#         self._init_parser()
# 
#     def _bool_op(self, t):
#         tag = t[0]
#         if tag in self.tag_list:
#             return True
#         else:
#             return False
# 
#     def _bool_and(self, t):
#         arg1 = t[0][0]
#         arg2 = t[0][2]
#         return arg1 & arg2
# 
#     def _bool_or(self, t):
#         arg1 = t[0][0]
#         arg2 = t[0][2]
#         return arg1 | arg2
# 
#     def _bool_not(self, t):
#         arg = t[0][1]
#         return not arg
# 
#     def _init_parser(self):
#         TRUE = Keyword('True')
#         FALSE = Keyword('False')
#         boolOperand = TRUE | FALSE | Regex(r'[a-zA-Z0-9:\-\/]+')
#         boolOperand.setParseAction(self._bool_op)
# 
#         self._parse_expr = infixNotation(
#             boolOperand,
#             [
#                 ('not', 1, opAssoc.RIGHT, self._bool_not),
#                 ('and', 2, opAssoc.LEFT,  self._bool_and),
#                 ('or',  2, opAssoc.LEFT,  self._bool_or),
#             ])
# 
#     def parse_string(self, string):
#         if string == '':
#             return True
#         try:
#             return self._parse_expr.parseString(string)[0]
#         except ParseException:
#             return False


# Path: mass_flask_core/signals/dispatch_request.py
from mass_flask_core.models import Sample, AnalysisSystem, AnalysisRequest
from mass_flask_core.utils.tag_parser import TagParser



def _filter_matches_tags(tags, tag_filter):
    return TagParser(tags).parse_string(tag_filter)


def _match_sample_and_system(sample, system):
    if system in sample.dispatched_to:
        return
    if not _filter_matches_tags(sample.tags, system.tag_filter_expression):
        return
    analysis_request = AnalysisRequest(sample=sample, analysis_system=system)
    analysis_request.save()
    sample.dispatched_to.append(system)
    sample.save()


def update_dispatch_request_for_new_sample(sender, document, **kwargs):
    if not issubclass(sender, Sample):
        return
    if kwargs.get('created') is True:

        for system in AnalysisSystem.objects():