diff --git a/nodes.py b/nodes.py index 31cf32b3757a001f42ccf12f194e37351f81dce0_bm9kZXMucHk=..dabf37c4ca94dac430cc34cc0551b0c794028b1d_bm9kZXMucHk= 100644 --- a/nodes.py +++ b/nodes.py @@ -84,8 +84,8 @@ except AttributeError: return None - def get_description(self): - return self.get_type() + def get_description(self, mainindex, tr): + return tr(self.get_type()) # rql st edition utilities #################################################### @@ -559,8 +559,8 @@ return 'Float' raise CoercionError(key) - def get_description(self): + def get_description(self, mainindex, tr): """if there is a variable in the math expr used as rhs of a relation, return the name of this relation, else return the type of the math expression """ @@ -563,13 +563,14 @@ """if there is a variable in the math expr used as rhs of a relation, return the name of this relation, else return the type of the math expression """ - schema = self.root.schema - for vref in self.iget_nodes(VariableRef): - rtype = vref.get_description() - if schema.has_relation(rtype): - return rtype - return self.get_type() + try: + return tr(self.get_type()) + except CoercionError: + for vref in self.iget_nodes(VariableRef): + type = vref.get_description(mainindex, tr) + if type is not None : + return type class Function(HSMixin, Node): @@ -613,8 +614,8 @@ pass return rtype or 'Any' - def get_description(self): - return self.descr().st_description(self) + def get_description(self, mainindex, tr): + return self.descr().st_description(self, mainindex, tr) def descr(self): """return the type of object returned by this function if known""" @@ -739,8 +740,8 @@ def get_type(self, solution=None, kwargs=None): return self.variable.get_type(solution, kwargs) - def get_description(self): - return self.variable.get_description() + def get_description(self, mainindex, tr): + return self.variable.get_description(mainindex, tr) class SortTerm(Node): @@ -856,7 +857,7 @@ pass return 'Any' - def get_description(self): + def get_description(self, mainindex, tr, none_allowed=False): """return : * the name of a relation where this variable is used as lhs, * the entity type of this object if specified by a 'is' relation, @@ -864,6 +865,9 @@ give priority to relation name """ - etype = 'Any' - rtype = None + if mainindex is not None: + if mainindex in self.stinfo['selected']: + return ', '.join(sorted( + tr(etype) for etype in self.stinfo['possibletypes'])) + rtype = frtype = None schema = self.schema @@ -869,13 +873,6 @@ schema = self.schema - for rel in chain(self.stinfo['typerels'], self.stinfo['relations']): - if rel.is_types_restriction(): - try: - etype = str(rel.children[1].children[0].value) - except AttributeError: - # "IN" Function node - pass - continue + for rel in self.stinfo['relations']: if schema is not None: rschema = schema.rschema(rel.r_type) if rschema.is_final(): if self.name == rel.children[0].name: @@ -878,10 +875,9 @@ if schema is not None: rschema = schema.rschema(rel.r_type) if rschema.is_final(): if self.name == rel.children[0].name: - continue # ignore - rtype = rel.r_type - break - else: - print 'NO SCHEMA', repr(self), repr(self.stmt.root) + # ignore final relation where this variable is used as subject + continue + # final relation where this variable is used as object + frtype = rel.r_type rtype = rel.r_type @@ -887,9 +883,26 @@ rtype = rel.r_type - # use getattr since variable may have been rewritten - if not self.name != getattr(rel.children[0], 'name', None): - # priority to relation where variable is on the rhs - break - return rtype or etype + lhs, rhs = rel.get_variable_parts() + # use getattr, may not be a variable ref (rewritten, constant...) + lhsvar = getattr(lhs, 'variable', None) + rhsvar = getattr(rhs, 'variable', None) + if mainindex is not None: + # relation to the main variable, stop searching + if mainindex in lhsvar.stinfo['selected']: + return tr(rtype) + if mainindex in rhsvar.stinfo['selected']: + if schema is not None and rschema.symetric: + return tr(rtype) + return tr(rtype + '_object') + if rhsvar is self: + rtype += '_object' + if frtype is not None: + return tr(frtype) + if mainindex is None and rtype is not None: + return tr(rtype) + if none_allowed: + return None + return ', '.join(sorted( + tr(etype) for etype in self.stinfo['possibletypes'])) def selected_index(self): """return the index of this variable in the selection if it's selected, @@ -939,5 +952,5 @@ return vtype return vtype - def get_description(self): + def get_description(self, mainindex, tr): """return entity type of this object, 'Any' if not found""" @@ -943,4 +956,6 @@ """return entity type of this object, 'Any' if not found""" - vtype = super(ColumnAlias, self).get_description() - if vtype == 'Any': + vtype = super(ColumnAlias, self).get_description(mainindex, tr, + none_allowed=True) + if vtype is None: + vtypes = set() for select in self.query.children: @@ -946,7 +961,9 @@ for select in self.query.children: - vtype = select.selection[self.colnum].get_description() - if vtype != 'Any': - return vtype + vtype = select.selection[self.colnum].get_description(mainindex, tr) + if vtype is not None: + vtypes.add(vtype) + if vtypes: + return ', '.join(sorted(tr(vtype) for vtype in vtypes)) return vtype # Variable compatibility diff --git a/stmts.py b/stmts.py index 31cf32b3757a001f42ccf12f194e37351f81dce0_c3RtdHMucHk=..dabf37c4ca94dac430cc34cc0551b0c794028b1d_c3RtdHMucHk= 100644 --- a/stmts.py +++ b/stmts.py @@ -225,8 +225,18 @@ return self return self.parent.root - def get_description(self): - return [c.get_description() for c in self.children] + def get_description(self, mainindex=None, tr=None): + """ + `mainindex`: + selection index to consider as main column, useful to get smarter + results + `tr`: + optional translation function taking a string as argument and + returning a string + """ + if tr is None: + tr = lambda x: x + return [c.get_description(mainindex, tr) for c in self.children] # repr / as_string / copy ################################################# @@ -369,5 +379,5 @@ """return the root node of the tree""" return self.parent - def get_description(self): + def get_description(self, mainindex, tr): """return the list of types or relations (if not found) associated to @@ -373,6 +383,8 @@ """return the list of types or relations (if not found) associated to - selected variables + selected variables. + mainindex is an optional selection index which should be considered has + 'pivot' entity. """ descr = [] for term in self.selection: try: @@ -375,6 +387,6 @@ """ descr = [] for term in self.selection: try: - descr.append(term.get_description()) + descr.append(term.get_description(mainindex, tr) or tr('Any')) except CoercionError: @@ -380,5 +392,5 @@ except CoercionError: - descr.append('Any') + descr.append(tr('Any')) return descr @property diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py index 31cf32b3757a001f42ccf12f194e37351f81dce0_dGVzdC91bml0dGVzdF9ub2Rlcy5weQ==..dabf37c4ca94dac430cc34cc0551b0c794028b1d_dGVzdC91bml0dGVzdF9ub2Rlcy5weQ== 100644 --- a/test/unittest_nodes.py +++ b/test/unittest_nodes.py @@ -8,8 +8,14 @@ schema = DummySchema() from rql.stcheck import RQLSTAnnotator annotator = RQLSTAnnotator(schema, {}) +helper = RQLHelper(schema, None, {'eid': 'uid'}) + +def sparse(rql): + tree = helper.parse(rql) + helper.compute_solutions(tree) + return tree class EtypeFromPyobjTC(TestCase): def test_bool(self): self.assertEquals(nodes.etype_from_pyobj(True), 'Boolean') self.assertEquals(nodes.etype_from_pyobj(False), 'Boolean') @@ -11,9 +17,9 @@ class EtypeFromPyobjTC(TestCase): def test_bool(self): self.assertEquals(nodes.etype_from_pyobj(True), 'Boolean') self.assertEquals(nodes.etype_from_pyobj(False), 'Boolean') - + def test_int(self): self.assertEquals(nodes.etype_from_pyobj(0), 'Int') self.assertEquals(nodes.etype_from_pyobj(1L), 'Int') @@ -452,7 +458,7 @@ # sub-queries tests ####################################################### def test_subq_colalias_compat(self): - tree = parse('Any X ORDERBY N WHERE X creation_date <NOW WITH X,N BEING (' + tree = sparse('Any X ORDERBY N WHERE X creation_date <NOW WITH X,N BEING (' '(Any X,N WHERE X firstname N) UNION (Any X,N WHERE X name N, X is Company))') select = tree.children[0] select.save_state() @@ -463,8 +469,8 @@ self.assertEquals(len(X.references()), 3) self.assertEquals(len(N.references()), 2) tree.schema = schema - annotator.annotate(tree) + #annotator.annotate(tree) # XXX how to choose self.assertEquals(X.get_type(), 'Company') self.assertEquals(X.get_type({'X': 'Personne'}), 'Personne') #self.assertEquals(N.get_type(), 'String') @@ -467,9 +473,9 @@ # XXX how to choose self.assertEquals(X.get_type(), 'Company') self.assertEquals(X.get_type({'X': 'Personne'}), 'Personne') #self.assertEquals(N.get_type(), 'String') - self.assertEquals(X.get_description(), 'Company') - self.assertEquals(N.get_description(), 'firstname') # XXX how to choose + self.assertEquals(X.get_description(0, lambda x:x), 'Company, Person, Student') + self.assertEquals(N.get_description(0, lambda x:x), 'firstname, name') self.assertEquals(X.selected_index(), 0) self.assertEquals(N.selected_index(), None) self.assertEquals(X.main_relation(), None) @@ -477,5 +483,5 @@ # non regression tests #################################################### def test_get_description_and_get_type(self): - tree = parse("Any N,COUNT(X),NOW-D GROUPBY N WHERE X name N, X creation_date D;") + tree = sparse("Any N,COUNT(X),NOW-D GROUPBY N WHERE X name N, X creation_date D;") tree.schema = schema @@ -481,6 +487,5 @@ tree.schema = schema - annotator.annotate(tree) - self.assertEqual(tree.get_description(), [['name', 'COUNT(Any)', 'creation_date']]) + self.assertEqual(tree.get_description(), [['name', 'COUNT(Company, Person, Student)', 'creation_date']]) select = tree.children[0] self.assertEqual(select.selection[0].get_type(), 'Any') self.assertEqual(select.selection[1].get_type(), 'Int') @@ -488,8 +493,6 @@ self.assertEqual(select.selection[2].get_type({'D': 'Datetime'}), 'Interval') def test_get_description_simplified(self): - helper = RQLHelper(DummySchema(), None, {'eid': 'uid'}) - tree = helper.parse('Any X,R,D WHERE X eid 2, X work_for R, R creation_date D') - select = tree.children[0] - self.assertEqual(tree.get_description(), [['work_for', 'work_for', 'creation_date']]) + tree = sparse('Any X,R,D WHERE X eid 2, X work_for R, R creation_date D') + self.assertEqual(tree.get_description(), [['work_for', 'work_for_object', 'creation_date']]) helper.simplify(tree) @@ -495,8 +498,8 @@ helper.simplify(tree) - # None since const.uid_type is used while solutions have not been computed - self.assertEqual(tree.get_description(), [[None, 'work_for', 'creation_date']]) - + # Any since const.uid_type is used while solutions have not been computed + self.assertEqual(tree.get_description(), [['Any', 'work_for_object', 'creation_date']]) + def test_repr_encoding(self): tree = parse(u'Any N where NOT N has_text "bidüle"') repr(tree) @@ -499,7 +502,15 @@ def test_repr_encoding(self): tree = parse(u'Any N where NOT N has_text "bidüle"') repr(tree) + def test_get_description_mainvar_objrel(self): + tree = sparse('Any X,R,D,Y WHERE X work_for R, R creation_date D, Y owned_by X') + self.assertEqual(tree.get_description(0), [['Person', 'work_for', 'creation_date', 'owned_by_object']]) + + def test_get_description_mainvar_symrel(self): + tree = sparse('Any X,R,D,Y WHERE X work_for R, R creation_date D, Y connait X') + self.assertEqual(tree.get_description(0), [['Person, Student', 'work_for', 'creation_date', 'connait']]) + class GetNodesFunctionTest(TestCase): def test_known_values_1(self): diff --git a/utils.py b/utils.py index 31cf32b3757a001f42ccf12f194e37351f81dce0_dXRpbHMucHk=..dabf37c4ca94dac430cc34cc0551b0c794028b1d_dXRpbHMucHk= 100644 --- a/utils.py +++ b/utils.py @@ -55,10 +55,11 @@ from logilab.common.adbh import _GenericAdvFuncHelper, FunctionDescr, \ auto_register_function -def st_description(cls, funcnode): - return '%s(%s)' % (cls.name, - ', '.join(child.get_description() - for child in iter_funcnode_variables(funcnode))) +def st_description(cls, funcnode, mainindex, tr): + return '%s(%s)' % ( + tr(cls.name), + ', '.join(child.get_description(mainindex, tr) + for child in iter_funcnode_variables(funcnode))) FunctionDescr.st_description = classmethod(st_description)