예제 #1
0
 def suggest_criteria(self):
     # TODO figure out why this stops normal inner function completion. (appears to only stop join)
     if self.argument and self.argument[-1] == '.':
         # Looks like a column/relationship.
         argument = self.argument[:-1].split(',')[-1].lstrip()
         mapped_property = self.get_mapped_property(argument)
         if not mapped_property:
             raise NotQueryException()
         suggestions = self._mapped_property_functions(
             mapped_property, argument)
     elif self.argument and self.get_mapped_property(
             self.argument.split(',')[-1].lstrip()):
         # We want to remind the user how to use criteria.
         return self._get_working_operators()
     elif self._is_boolean_expression():
         #return RedundantCriterionCompleter.suggest()
         raise NotQueryException()
     else:
         from_clause = self.get_query_analyzer().get_from_clause()
         if re.match('.*[a-zA-Z]+ ?= ?$', self.argument):
             raise NotQueryException()
         else:
             suggestions = self._get_normal_suggestions(
                 from_clause, self.argument)
     return suggestions  # + self.get_criterion_suggestion_variants(suggestions)
예제 #2
0
    def get_base(self, string):
        '''
        Receives a string, and returns the query object to be used for extracting information about possible
        suggestions.
        '''
        base = None

        if re.match(r'%s\.[a-zA-Z]+' % self.module_name, string):
            if re.match(r'%s\.[a-zA-Z]+\.%s.*' % (self.module_name, self._get_main_query_func_name()), string):
                # Looks like a full query.
                base = self._get_query_from_query_string(string)
            else:
                # Looks like a class, called through the module.
                class_name = re.sub('%s\.' % self.module_name, '', string)
                if hasattr(self.module, class_name) and isinstance(getattr(self.module, class_name),
                                                                   self._get_base_meta_class()):
                    base = self._get_query_from_class_name(class_name)
                else:
                    raise NotQueryException()

        elif hasattr(self.module, string.split('.')[0]):
            # Looks like a class, separately imported.
            if re.match(r'[a-zA-Z]+\.%s.*' % self._get_main_query_func_name(), string):
                base = self._get_query_from_query_string(string)
            else:
                if not isinstance(getattr(self.module, string), self._get_base_meta_class()):
                    raise NotQueryException()
                else:
                    base = self._get_query_from_class_name(string)

        # TODO implement more cases.
        else:
            raise NotQueryException()

        return base
예제 #3
0
 def get_property_from_property_string(self, property_string):
     try:
         relationship_property = eval(property_string, self.namespace)
     except Exception:
         raise NotQueryException()
     if hasattr(relationship_property, 'property') and isinstance(
             relationship_property.property, RelationshipProperty):
         return relationship_property.property.mapper.entity
     else:
         raise NotQueryException()
예제 #4
0
 def get_last_table(self):
     if not self.open_calls:
         raise NotQueryException()
     last_call = self.open_calls[-1]
     return class_mapper(
         self.get_property_from_property_string(
             self.get_property_string_from_call(last_call)))
예제 #5
0
 def get_base_string(self, line):
     # TODO support parsing of lines not starting with the query.
     regex = self.get_base_regex()
     base_string_parts = re.findall(regex, line)
     if not base_string_parts:
         raise NotQueryException()
     return base_string_parts[0]
예제 #6
0
 def parse_arguments(self, arguments_string, query):
     split_regex = self._get_call_split_regex()
     calls = re.split(split_regex, arguments_string)
     if len(calls) == 1:
         return QuerySimpleCriterionCompleter(argument=arguments_string,
                                              query=query,
                                              module=self.module,
                                              namespace=self.namespace)
     open_calls = self.open_criterion_calls(calls)
     if not open_calls:
         if re.match('.*\) *,.*',
                     calls[-1]):  # Last argument is not the last call
             # ignore any calls, simply pass the last argument.
             return QuerySimpleCriterionCompleter(
                 argument=arguments_string.split(',')[-1].strip(),
                 query=query,
                 module=self.module,
                 namespace=self.namespace)
         else:  # Last argument is the last call.
             raise NotQueryException()
     else:
         # There is an open criterion call,
         # remove all junk and pass everything after the first call to the actual completer.
         first_call = calls[0]
         arguments_to_remove = first_call.count(',')
         return ComplexCriterionCompleter(
             module=self.module,
             namespace=self.namespace,
             argument=arguments_string.split('(')[-1].strip(),
             query=query,
             open_calls=open_calls)
예제 #7
0
    def get_completer(self, arguments, query):

        if not self.validate_argument(arguments):
            raise NotQueryException()

        if ',' in arguments:
            if arguments.count(',') > 1:
                raise NotQueryException()
            parts = arguments.split(',')
            cls_name = parts[0].strip()
            argument = parts[1].strip()
            cls = self._get_cls(cls_name)
            if cls:
                return CriterionJoinCompleter(argument=argument,
                                              query=query,
                                              cls=cls,
                                              module=self.module,
                                              namespace=self.namespace)
            else:
                raise NotQueryException()

        for cls in PuyolLikeQueryAnalyzer(query=query).get_from_clause():
            if arguments.lower() in (
                    '%s.%s' %
                (get_module_name(self.module), cls.__name__)).lower():
                return RelationshipJoinCompleter(argument=arguments,
                                                 query=query,
                                                 cls=cls,
                                                 module=self.module)

        parts = arguments.split('.')
        if len(parts) > 1:
            cls_name = '.'.join(parts[:-1])
            argument = parts[-1].strip()
            cls = self._get_cls(cls_name)
            if cls:
                return RelationshipJoinCompleter(argument=argument,
                                                 query=query,
                                                 cls=cls,
                                                 module=self.module)

        return ClassJoinCompleter(
            argument=arguments,
            query=query,
            base_meta_class=self.module_analyzer.get_base_meta_class(
                self.module),
            module=self.module)
예제 #8
0
    def _suggest_right_side(self, from_clause):
        parts = self.argument.split('==')
        left = parts[0].strip()
        right = parts[1].strip()
        try:
            left_attribute = eval(left, self.namespace)
        except Exception:
            raise NotQueryException()

        if left_attribute.class_ in from_clause:
            left_is_joinee = False
        elif left_attribute.class_ == self.cls:
            left_is_joinee = True
        else:
            raise NotQueryException()

        expression = left_attribute.property.expression
        if expression.primary_key:
            # Either (a, a.id == b.a_id) or (a, b.id == a.b_id)
            if left_is_joinee:
                # (a, a.id == b.a_id)
                return self._suggest_fk(argument=right,
                                        source_classes=from_clause,
                                        target_classes=[self.cls])
            else:
                # (a, b.id == a.b_id)
                return self._suggest_fk(argument=right,
                                        source_classes=[self.cls],
                                        target_classes=from_clause)
        else:
            # Either (a, a.b_id == b.id) or (a, b.a_id == a.id)
            foreign_key = self._get_fk_for_expression(expression)
            if not foreign_key:
                raise NotQueryException()

            if left_is_joinee:
                # (a, a.b_id == b.id)
                cls = self._get_cls_for_fk(foreign_key,
                                           from_clause + [self.cls])
            else:
                # (a, b.a_id == a.id)
                cls = self.cls

            if not cls:
                raise NotQueryException()

            return self._suggest_pk_for_cls(argument=right, cls=cls)
예제 #9
0
 def suggest(self, line):
     parser = self.get_parser()
     query, function, arguments = parser.parse(line)
     completer_factory = self.get_factory_for_function(function)
     if not completer_factory:
         raise NotQueryException()
     completer = completer_factory.get_completer(arguments, query)
     return completer.suggest()
예제 #10
0
 def _get_query_from_query_string(self, string):
     try:
         # TODO should maybe run in different thread? Should check how evaluation is done in IPython
         base = eval(string, self.namespace)
     except SyntaxError:
         # Not a full query. Probably a query inside a query. (And this works with union or minus and such)
         # TODO sometime in the future we may want to support this. Currently this is just annoying.
         raise NotQueryException()
     return base
예제 #11
0
 def _suggest_pk_for_cls(self, argument, cls):
     pk_attr = self.primary_key(cls)
     if pk_attr:
         key = pk_attr.key
     else:
         raise NotQueryException()
     suggestion = '%s.%s.%s' % (get_module_name(
         self.module), cls.__name__, key)
     if argument.lower() in suggestion.lower():
         return [suggestion]
     else:
         return []
예제 #12
0
 def suggest_kwarg(self):
     try:
         attributes = self._get_attributes_for_kwarg(self.query)
     except AttributeError:
         raise NotQueryException()
     suggestions = []
     column_properties = self._get_properties(attributes, ColumnProperty)
     column_suggestions = map(lambda x: x.key + '=', column_properties)
     suggestions += column_suggestions
     relationship_properties = self._get_properties(attributes,
                                                    RelationshipProperty)
     relationship_suggestions = map(
         lambda x: x.key + '=',
         filter(lambda x: not x.uselist, relationship_properties))
     suggestions += relationship_suggestions
     if self.argument:
         argument = self.argument.split(',')[-1]
         if '=' in argument:
             raise NotQueryException()
         suggestions = filter(lambda x: x.startswith(argument.lstrip()),
                              suggestions)
     return suggestions
예제 #13
0
 def _mapped_property_functions(mapped_property, string):
     suggestions = []
     if isinstance(mapped_property, ColumnProperty):
         suggestions = ['in_', 'like', 'ilike'
                        ]  # TODO make a puyol function for getting these.
     elif isinstance(mapped_property, RelationshipProperty):
         if mapped_property.uselist:
             suggestions = ['any']
         else:
             suggestions = ['has']
     else:
         raise NotQueryException()
     return ['%s.%s(' % (string, suggestion) for suggestion in suggestions]
예제 #14
0
 def get_property_string_from_call(self, call):
     if re.match(
             '.*%s\.[a-zA-Z]+\.[a-zA-Z]+$' % get_module_name(self.module),
             call):
         string = re.findall(
             '%s\.[a-zA-Z]+\.[a-zA-Z]+$' % get_module_name(self.module),
             call)[0]
         return string
     elif re.match('.*[a-zA-Z]+\.[a-zA-Z]+$', call):
         string = re.findall('[a-zA-Z]+\.[a-zA-Z]+$', call)[0]
         return string
     else:
         raise NotQueryException()
예제 #15
0
 def suggest(self):
     mapper = class_mapper(self.cls)
     from_clause = self.query_analyzer.get_from_clause()
     if not self.cls in from_clause:
         raise NotQueryException()
         # All relationship properties.
     relationships = [
         attr for attr in mapper.attrs
         if isinstance(attr, RelationshipProperty)
     ]
     # All relationship properties that are not already joined.
     allowed = [
         rel for rel in relationships
         if rel.mapper.entity not in from_clause
     ]
     # And start with the argument.
     suggestions = []
     for rel in allowed:
         suggestion = '%s.%s.%s' % (get_module_name(
             self.module), self.cls.__name__, rel.key)
         if self.argument in suggestion:
             suggestions.append(suggestion)
     return suggestions
예제 #16
0
 def validate_func_and_args(self, func_and_args):
     if len(re.findall(self.get_funcs_regex(), func_and_args)) != 1:
         raise NotQueryException()
     if len(re.findall(self.get_funcs_wit_parenthesis_regex(), func_and_args)) != 1:
         raise NotQueryException()
예제 #17
0
 def get_completer(self, arguments_string, query):
     if not self.validate_argument(arguments_string):
         raise NotQueryException()
     completer = self.parse_arguments(arguments_string, query)
     return completer