--- a/.hgtags Tue Jul 26 19:34:43 2011 +0200
+++ b/.hgtags Wed Jul 27 20:17:45 2011 +0200
@@ -216,3 +216,5 @@
cc0578049cbe8b1d40009728e36c17e45da1fc6b cubicweb-debian-version-3.13.1-1
f9227b9d61835f03163b8133a96da35db37a0c8d cubicweb-version-3.13.2
9ad5411199e00b2611366439b82f35d7d3285423 cubicweb-debian-version-3.13.2-1
+0e82e7e5a34f57d7239c7a42e48ba4d5e53abab2 cubicweb-version-3.13.3
+fb48c55cb80234bc0164c9bcc0e2cfc428836e5f cubicweb-debian-version-3.13.3-1
--- a/__pkginfo__.py Tue Jul 26 19:34:43 2011 +0200
+++ b/__pkginfo__.py Wed Jul 27 20:17:45 2011 +0200
@@ -22,7 +22,7 @@
modname = distname = "cubicweb"
-numversion = (3, 13, 2)
+numversion = (3, 13, 3)
version = '.'.join(str(num) for num in numversion)
description = "a repository of entities / relations for knowledge management"
--- a/debian/changelog Tue Jul 26 19:34:43 2011 +0200
+++ b/debian/changelog Wed Jul 27 20:17:45 2011 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.13.3-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Wed, 27 Jul 2011 19:06:16 +0200
+
cubicweb (3.13.2-1) unstable; urgency=low
* new upstream release
--- a/hooks/__init__.py Tue Jul 26 19:34:43 2011 +0200
+++ b/hooks/__init__.py Wed Jul 27 20:17:45 2011 +0200
@@ -57,7 +57,7 @@
or not repo.config.source_enabled(source)
or not source.config['synchronize']):
continue
- session = repo.internal_session()
+ session = repo.internal_session(safe=True)
try:
stats = source.pull_data(session)
if stats.get('created'):
--- a/hooks/metadata.py Tue Jul 26 19:34:43 2011 +0200
+++ b/hooks/metadata.py Wed Jul 27 20:17:45 2011 +0200
@@ -42,8 +42,10 @@
def __call__(self):
timestamp = datetime.now()
edited = self.entity.cw_edited
- edited.setdefault('creation_date', timestamp)
- edited.setdefault('modification_date', timestamp)
+ if not edited.get('creation_date'):
+ edited['creation_date'] = timestamp
+ if not edited.get('modification_date'):
+ edited['modification_date'] = timestamp
if not self._cw.get_shared_data('do-not-insert-cwuri'):
cwuri = u'%s%s' % (self._cw.base_url(), self.entity.eid)
edited.setdefault('cwuri', cwuri)
--- a/i18n/de.po Tue Jul 26 19:34:43 2011 +0200
+++ b/i18n/de.po Wed Jul 27 20:17:45 2011 +0200
@@ -1914,10 +1914,6 @@
msgid "cw_schema_object"
msgstr ""
-msgctxt "CWAttribute"
-msgid "cw_schema_object"
-msgstr ""
-
msgctxt "CWEType"
msgid "cw_schema_object"
msgstr ""
--- a/i18n/en.po Tue Jul 26 19:34:43 2011 +0200
+++ b/i18n/en.po Wed Jul 27 20:17:45 2011 +0200
@@ -1869,10 +1869,6 @@
msgid "cw_schema_object"
msgstr "mapped by"
-msgctxt "CWAttribute"
-msgid "cw_schema_object"
-msgstr "mapped by"
-
msgctxt "CWEType"
msgid "cw_schema_object"
msgstr "mapped by"
@@ -4407,3 +4403,7 @@
#, python-format
msgid "you should un-inline relation %s which is supported and may be crossed "
msgstr ""
+
+#~ msgctxt "CWAttribute"
+#~ msgid "cw_schema_object"
+#~ msgstr "mapped by"
--- a/i18n/es.po Tue Jul 26 19:34:43 2011 +0200
+++ b/i18n/es.po Wed Jul 27 20:17:45 2011 +0200
@@ -1943,10 +1943,6 @@
msgid "cw_schema_object"
msgstr "mapeado por"
-msgctxt "CWAttribute"
-msgid "cw_schema_object"
-msgstr "mapeado por"
-
msgctxt "CWEType"
msgid "cw_schema_object"
msgstr "mapeado por"
@@ -4576,3 +4572,7 @@
msgstr ""
"usted debe quitar la puesta en línea de la relación %s que es aceptada y "
"puede ser cruzada"
+
+#~ msgctxt "CWAttribute"
+#~ msgid "cw_schema_object"
+#~ msgstr "mapeado por"
--- a/i18n/fr.po Tue Jul 26 19:34:43 2011 +0200
+++ b/i18n/fr.po Wed Jul 27 20:17:45 2011 +0200
@@ -1946,10 +1946,6 @@
msgid "cw_schema_object"
msgstr "mappé par"
-msgctxt "CWAttribute"
-msgid "cw_schema_object"
-msgstr "mappé par"
-
msgctxt "CWEType"
msgid "cw_schema_object"
msgstr "mappé par"
@@ -4576,3 +4572,7 @@
msgstr ""
"vous devriez enlevé la mise en ligne de la relation %s qui est supportée et "
"peut-être croisée"
+
+#~ msgctxt "CWAttribute"
+#~ msgid "cw_schema_object"
+#~ msgstr "mappé par"
--- a/server/repository.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/repository.py Wed Jul 27 20:17:45 2011 +0200
@@ -927,14 +927,16 @@
nbclosed += 1
return nbclosed
- def internal_session(self, cnxprops=None):
- """return a dbapi like connection/cursor using internal user which
- have every rights on the repository. You'll *have to* commit/rollback
- or close (rollback implicitly) the session once the job's done, else
- you'll leak connections set up to the time where no one is
- available, causing irremediable freeze...
+ def internal_session(self, cnxprops=None, safe=False):
+ """return a dbapi like connection/cursor using internal user which have
+ every rights on the repository. The `safe` argument is a boolean flag
+ telling if integrity hooks should be activated or not.
+
+ *YOU HAVE TO* commit/rollback or close (rollback implicitly) the
+ session once the job's done, else you'll leak connections set up to the
+ time where no one is available, causing irremediable freeze...
"""
- session = InternalSession(self, cnxprops)
+ session = InternalSession(self, cnxprops, safe)
session.set_cnxset()
return session
@@ -1030,7 +1032,7 @@
return extid
def extid2eid(self, source, extid, etype, session=None, insert=True,
- sourceparams=None):
+ complete=True, commit=True, sourceparams=None):
"""Return eid from a local id. If the eid is a negative integer, that
means the entity is known but has been copied back to the system source
hence should be ignored.
@@ -1089,15 +1091,16 @@
session, extid, etype, eid, sourceparams)
if source.should_call_hooks:
self.hm.call_hooks('before_add_entity', session, entity=entity)
- # XXX call add_info with complete=False ?
- self.add_info(session, entity, source, extid)
+ self.add_info(session, entity, source, extid, complete=complete)
source.after_entity_insertion(session, extid, entity, sourceparams)
if source.should_call_hooks:
self.hm.call_hooks('after_add_entity', session, entity=entity)
- session.commit(free_cnxset)
+ if commit or free_cnxset:
+ session.commit(free_cnxset)
return eid
- except:
- session.rollback(free_cnxset)
+ except Exception:
+ if commit or free_cnxset:
+ session.rollback(free_cnxset)
raise
def add_info(self, session, entity, source, extid=None, complete=True):
--- a/server/rqlannotation.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/rqlannotation.py Wed Jul 27 20:17:45 2011 +0200
@@ -211,16 +211,22 @@
relation for the rhs variable
"""
principal = None
+ others = []
# sort for test predictability
for rel in sorted(relations, key=lambda x: (x.children[0].name, x.r_type)):
# only equality relation with a variable as rhs may be principal
if rel.operator() not in ('=', 'IS') \
or not isinstance(rel.children[1].children[0], VariableRef) or rel.neged(strict=True):
continue
+ if rel.optional:
+ others.append(rel)
+ continue
if rel.scope is rel.stmt:
return rel
principal = rel
if principal is None:
+ if others:
+ return others[0]
raise BadRQLQuery('unable to find principal in %s' % ', '.join(
r.as_string() for r in relations))
return principal
--- a/server/session.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/session.py Wed Jul 27 20:17:45 2011 +0200
@@ -1276,12 +1276,13 @@
is_internal_session = True
running_dbapi_query = False
- def __init__(self, repo, cnxprops=None):
+ def __init__(self, repo, cnxprops=None, safe=False):
super(InternalSession, self).__init__(InternalManager(), repo, cnxprops,
_id='internal')
self.user._cw = self # XXX remove when "vreg = user._cw.vreg" hack in entity.py is gone
self.cnxtype = 'inmemory'
- self.disable_hook_categories('integrity')
+ if not safe:
+ self.disable_hook_categories('integrity')
@property
def cnxset(self):
--- a/server/sources/__init__.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/sources/__init__.py Wed Jul 27 20:17:45 2011 +0200
@@ -25,6 +25,7 @@
from logging import getLogger
from logilab.common import configuration
+from logilab.common.deprecation import deprecated
from yams.schema import role_name
@@ -269,12 +270,6 @@
# external source api ######################################################
- def eid2extid(self, eid, session=None):
- return self.repo.eid2extid(self, eid, session)
-
- def extid2eid(self, value, etype, session=None, **kwargs):
- return self.repo.extid2eid(self, value, etype, session, **kwargs)
-
def support_entity(self, etype, write=False):
"""return true if the given entity's type is handled by this adapter
if write is true, return true only if it's a RW support
@@ -522,6 +517,15 @@
pass
+ @deprecated('[3.13] use repo.eid2extid(source, eid, session)')
+ def eid2extid(self, eid, session=None):
+ return self.repo.eid2extid(self, eid, session)
+
+ @deprecated('[3.13] use extid2eid(source, value, etype, session, **kwargs)')
+ def extid2eid(self, value, etype, session=None, **kwargs):
+ return self.repo.extid2eid(self, value, etype, session, **kwargs)
+
+
class TrFunc(object):
"""lower, upper"""
def __init__(self, trname, index, attrname=None):
--- a/server/sources/datafeed.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/sources/datafeed.py Wed Jul 27 20:17:45 2011 +0200
@@ -147,6 +147,7 @@
return True
def release_synchronization_lock(self, session):
+ session.set_cnxset()
session.execute('SET X synchronizing FALSE WHERE X eid %(x)s',
{'x': self.eid})
session.commit()
@@ -220,9 +221,6 @@
entity.cw_edited['cwuri'] = unicode(lid)
entity.cw_edited.set_defaults()
sourceparams['parser'].before_entity_copy(entity, sourceparams)
- # avoid query to search full-text indexed attributes
- for attr in entity.e_schema.indexable_attributes():
- entity.cw_edited.setdefault(attr, u'')
return entity
def after_entity_insertion(self, session, lid, entity, sourceparams):
@@ -267,15 +265,20 @@
"""return an entity for the given uri. May return None if it should be
skipped
"""
+ session = self._cw
# if cwsource is specified and repository has a source with the same
# name, call extid2eid on that source so entity will be properly seen as
# coming from this source
- source = self._cw.repo.sources_by_uri.get(
- sourceparams.pop('cwsource', None), self.source)
+ source_uri = sourceparams.pop('cwsource', None)
+ if source_uri is not None and source_uri != 'system':
+ source = session.repo.sources_by_uri.get(source_uri, self.source)
+ else:
+ source = self.source
sourceparams['parser'] = self
try:
- eid = source.extid2eid(str(uri), etype, self._cw,
- sourceparams=sourceparams)
+ eid = session.repo.extid2eid(source, str(uri), etype, session,
+ complete=False, commit=False,
+ sourceparams=sourceparams)
except ValidationError, ex:
self.source.error('error while creating %s: %s', etype, ex)
return None
@@ -285,14 +288,14 @@
# Don't give etype to entity_from_eid so we get UnknownEid if the
# entity has been removed
try:
- entity = self._cw.entity_from_eid(-eid)
+ entity = session.entity_from_eid(-eid)
except UnknownEid:
return None
self.notify_updated(entity) # avoid later update from the source's data
return entity
if self.sourceuris is not None:
self.sourceuris.pop(str(uri), None)
- return self._cw.entity_from_eid(eid, etype)
+ return session.entity_from_eid(eid, etype)
def process(self, url, partialcommit=True):
"""main callback: process the url"""
--- a/server/sources/ldapuser.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/sources/ldapuser.py Wed Jul 27 20:17:45 2011 +0200
@@ -310,7 +310,7 @@
except Exception:
self.error('while trying to authenticate %s', user, exc_info=True)
raise AuthenticationError()
- eid = self.extid2eid(user['dn'], 'CWUser', session)
+ eid = self.repo.extid2eid(self, user['dn'], 'CWUser', session)
if eid < 0:
# user has been moved away from this source
raise AuthenticationError()
@@ -423,7 +423,7 @@
filteredres = []
for resdict in res:
# get sure the entity exists in the system table
- eid = self.extid2eid(resdict['dn'], 'CWUser', session)
+ eid = self.repo.extid2eid(self, resdict['dn'], 'CWUser', session)
for eidfilter in eidfilters:
if not eidfilter(eid):
break
@@ -537,7 +537,7 @@
res = cnx.result(all=0)[1]
except ldap.NO_SUCH_OBJECT:
self.info('ldap NO SUCH OBJECT')
- eid = self.extid2eid(base, 'CWUser', session, insert=False)
+ eid = self.repo.extid2eid(self, base, 'CWUser', session, insert=False)
if eid:
self.warning('deleting ldap user with eid %s and dn %s',
eid, base)
@@ -646,6 +646,7 @@
"""generate an LDAP filter for a rql query"""
def __init__(self, source, session, args=None, mainvars=()):
self.source = source
+ self.repo = source.repo
self._ldap_attrs = source.user_rev_attrs
self._base_filters = source.base_filters
self._session = session
@@ -751,7 +752,7 @@
}[rhs.operator]
self._eidfilters.append(filter)
return
- dn = self.source.eid2extid(eid, self._session)
+ dn = self.repo.eid2extid(self.source, eid, self._session)
raise GotDN(dn)
try:
filter = '(%s%s)' % (self._ldap_attrs[relation.r_type],
--- a/server/sources/pyrorql.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/sources/pyrorql.py Wed Jul 27 20:17:45 2011 +0200
@@ -281,8 +281,8 @@
continue
for etype, extid in deleted:
try:
- eid = self.extid2eid(str(extid), etype, session,
- insert=False)
+ eid = self.repo.extid2eid(self, str(extid), etype, session,
+ insert=False)
# entity has been deleted from external repository but is not known here
if eid is not None:
entity = session.entity_from_eid(eid, etype)
@@ -423,7 +423,7 @@
def _entity_relations_and_kwargs(self, session, entity):
relations = []
- kwargs = {'x': self.eid2extid(entity.eid, session)}
+ kwargs = {'x': self.repo.eid2extid(self, entity.eid, session)}
for key, val in entity.cw_attr_cache.iteritems():
relations.append('X %s %%(%s)s' % (key, key))
kwargs[key] = val
@@ -449,15 +449,15 @@
return
cu = session.cnxset[self.uri]
cu.execute('DELETE %s X WHERE X eid %%(x)s' % entity.__regid__,
- {'x': self.eid2extid(entity.eid, session)})
+ {'x': self.repo.eid2extid(self, entity.eid, session)})
self._query_cache.clear()
def add_relation(self, session, subject, rtype, object):
"""add a relation to the source"""
cu = session.cnxset[self.uri]
cu.execute('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
- {'x': self.eid2extid(subject, session),
- 'y': self.eid2extid(object, session)})
+ {'x': self.repo.eid2extid(self, subject, session),
+ 'y': self.repo.eid2extid(self, object, session)})
self._query_cache.clear()
session.entity_from_eid(subject).cw_clear_all_caches()
session.entity_from_eid(object).cw_clear_all_caches()
@@ -470,8 +470,8 @@
return
cu = session.cnxset[self.uri]
cu.execute('DELETE X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
- {'x': self.eid2extid(subject, session),
- 'y': self.eid2extid(object, session)})
+ {'x': self.repo.eid2extid(self, subject, session),
+ 'y': self.repo.eid2extid(self, object, session)})
self._query_cache.clear()
session.entity_from_eid(subject).cw_clear_all_caches()
session.entity_from_eid(object).cw_clear_all_caches()
@@ -481,6 +481,7 @@
"""translate a local rql query to be executed on a distant repository"""
def __init__(self, source):
self.source = source
+ self.repo = source.repo
self.current_operator = None
def _accept_children(self, node):
@@ -676,7 +677,7 @@
def eid2extid(self, eid):
try:
- return self.source.eid2extid(eid, self._session)
+ return self.repo.eid2extid(self.source, eid, self._session)
except UnknownEid:
operator = self.current_operator
if operator is not None and operator != '=':
--- a/server/sources/rql2sql.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/sources/rql2sql.py Wed Jul 27 20:17:45 2011 +0200
@@ -1292,9 +1292,16 @@
relation.r_type)
try:
self._state.ignore_varmap = True
- return '%s%s' % (lhssql, relation.children[1].accept(self))
+ sql = lhssql + relation.children[1].accept(self)
finally:
self._state.ignore_varmap = False
+ if relation.optional == 'right':
+ leftalias = self._var_table(principal.children[0].variable)
+ rightalias = self._var_table(relation.children[0].variable)
+ self._state.replace_tables_by_outer_join(
+ leftalias, rightalias, 'LEFT', sql)
+ return ''
+ return sql
return ''
def _visit_attribute_relation(self, rel):
@@ -1372,12 +1379,15 @@
def visit_comparison(self, cmp):
"""generate SQL for a comparison"""
+ optional = getattr(cmp, 'optional', None) # rql < 0.30
if len(cmp.children) == 2:
- # XXX occurs ?
+ # simplified expression from HAVING clause
lhs, rhs = cmp.children
else:
lhs = None
rhs = cmp.children[0]
+ assert not optional
+ sql = None
operator = cmp.operator
if operator in ('LIKE', 'ILIKE'):
if operator == 'ILIKE' and not self.dbhelper.ilike_support:
@@ -1385,18 +1395,39 @@
else:
operator = ' %s ' % operator
elif operator == 'REGEXP':
- return ' %s' % self.dbhelper.sql_regexp_match_expression(rhs.accept(self))
+ sql = ' %s' % self.dbhelper.sql_regexp_match_expression(rhs.accept(self))
elif (operator == '=' and isinstance(rhs, Constant)
and rhs.eval(self._args) is None):
if lhs is None:
- return ' IS NULL'
- return '%s IS NULL' % lhs.accept(self)
+ sql = ' IS NULL'
+ else:
+ sql = '%s IS NULL' % lhs.accept(self)
elif isinstance(rhs, Function) and rhs.name == 'IN':
assert operator == '='
operator = ' '
- if lhs is None:
- return '%s%s'% (operator, rhs.accept(self))
- return '%s%s%s'% (lhs.accept(self), operator, rhs.accept(self))
+ if sql is None:
+ if lhs is None:
+ sql = '%s%s'% (operator, rhs.accept(self))
+ else:
+ sql = '%s%s%s'% (lhs.accept(self), operator, rhs.accept(self))
+ if optional is None:
+ return sql
+ leftvars = cmp.children[0].get_nodes(VariableRef)
+ assert len(leftvars) == 1
+ leftalias = self._var_table(leftvars[0].variable.stinfo['attrvar'])
+ rightvars = cmp.children[1].get_nodes(VariableRef)
+ assert len(rightvars) == 1
+ rightalias = self._var_table(rightvars[0].variable.stinfo['attrvar'])
+ if optional == 'right':
+ self._state.replace_tables_by_outer_join(
+ leftalias, rightalias, 'LEFT', sql)
+ elif optional == 'left':
+ self._state.replace_tables_by_outer_join(
+ rightalias, leftalias, 'LEFT', sql)
+ else:
+ self._state.replace_tables_by_outer_join(
+ leftalias, rightalias, 'FULL', sql)
+ return ''
def visit_mathexpression(self, mexpr):
"""generate SQL for a mathematic expression"""
@@ -1438,15 +1469,15 @@
value = constant.value
if constant.type == 'etype':
return value
- if constant.type == 'Int' and isinstance(constant.parent, SortTerm):
- return value
+ if constant.type == 'Int': # XXX Float?
+ return str(value)
if constant.type in ('Date', 'Datetime'):
rel = constant.relation()
if rel is not None:
rel._q_needcast = value
return self.keyword_map[value]()
if constant.type == 'Boolean':
- value = self.dbhelper.boolean_value(value)
+ return str(self.dbhelper.boolean_value(value))
if constant.type == 'Substitute':
try:
# we may found constant from simplified var in varmap
--- a/server/test/data/migratedapp/schema.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/data/migratedapp/schema.py Wed Jul 27 20:17:45 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -17,7 +17,7 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""cw.server.migraction test"""
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
- SubjectRelation,
+ SubjectRelation, Bytes,
RichString, String, Int, Boolean, Datetime, Date)
from yams.constraints import SizeConstraint, UniqueConstraint
from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
@@ -36,6 +36,7 @@
sujet = String(fulltextindexed=True,
constraints=[SizeConstraint(256)])
concerne = SubjectRelation('Societe')
+ opt_attr = Bytes()
class concerne(RelationType):
__permissions__ = {
--- a/server/test/data/schema.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/data/schema.py Wed Jul 27 20:17:45 2011 +0200
@@ -24,9 +24,6 @@
RQLConstraint, RQLUniqueConstraint,
ERQLExpression, RRQLExpression)
-class BFSSTestable(EntityType):
- opt_attr = Bytes()
-
class Affaire(WorkflowableEntityType):
__permissions__ = {
'read': ('managers',
@@ -45,6 +42,7 @@
duration = Int()
invoiced = Float()
+ opt_attr = Bytes()
depends_on = SubjectRelation('Affaire')
require_permission = SubjectRelation('CWPermission')
--- a/server/test/unittest_datafeed.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_datafeed.py Wed Jul 27 20:17:45 2011 +0200
@@ -96,9 +96,8 @@
# test_delete_source
req = self.request()
- with self.debugged('DBG_RQL'):
- req.execute('DELETE CWSource S WHERE S name "myfeed"')
- self.commit()
+ req.execute('DELETE CWSource S WHERE S name "myfeed"')
+ self.commit()
self.failIf(self.execute('Card X WHERE X title "cubicweb.org"'))
self.failIf(self.execute('Any X WHERE X has_text "cubicweb.org"'))
--- a/server/test/unittest_ldapuser.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_ldapuser.py Wed Jul 27 20:17:45 2011 +0200
@@ -61,7 +61,7 @@
# no such user
raise AuthenticationError()
# don't check upassword !
- return self.extid2eid(user['dn'], 'CWUser', session)
+ return self.repo.extid2eid(self, user['dn'], 'CWUser', session)
def setUpModule(*args):
create_slapd_configuration(LDAPUserSourceTC.config)
--- a/server/test/unittest_querier.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_querier.py Wed Jul 27 20:17:45 2011 +0200
@@ -800,6 +800,12 @@
'Password', 'String',
'TZDatetime', 'TZTime',
'Time'])
+ req = self.session
+ req.create_entity('Personne', nom=u'louis', test=True)
+ self.assertEqual(len(req.execute('Any X WHERE X test %(val)s', {'val': True})), 1)
+ self.assertEqual(len(req.execute('Any X WHERE X test TRUE')), 1)
+ self.assertEqual(len(req.execute('Any X WHERE X test %(val)s', {'val': False})), 0)
+ self.assertEqual(len(req.execute('Any X WHERE X test FALSE')), 0)
def test_select_constant(self):
rset = self.execute('Any X, "toto" ORDERBY X WHERE X is CWGroup')
--- a/server/test/unittest_repository.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_repository.py Wed Jul 27 20:17:45 2011 +0200
@@ -68,7 +68,7 @@
namecol, table, finalcol))
self.assertEqual(cu.fetchall(), [])
cu = self.session.system_sql('SELECT %s FROM %s WHERE %s=%%(final)s ORDER BY %s'
- % (namecol, table, finalcol, namecol), {'final': 'TRUE'})
+ % (namecol, table, finalcol, namecol), {'final': True})
self.assertEqual(cu.fetchall(), [(u'BigInt',), (u'Boolean',), (u'Bytes',),
(u'Date',), (u'Datetime',),
(u'Decimal',),(u'Float',),
--- a/server/test/unittest_rql2sql.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_rql2sql.py Wed Jul 27 20:17:45 2011 +0200
@@ -807,6 +807,11 @@
OUTER_JOIN = [
+
+ ('Any U,G WHERE U login L, G name L?, G is CWGroup',
+ '''SELECT _U.cw_eid, _G.cw_eid
+FROM cw_CWUser AS _U LEFT OUTER JOIN cw_CWGroup AS _G ON (_G.cw_name=_U.cw_login)'''),
+
('Any X,S WHERE X travaille S?',
'''SELECT _X.cw_eid, rel_travaille0.eid_to
FROM cw_Personne AS _X LEFT OUTER JOIN travaille_relation AS rel_travaille0 ON (rel_travaille0.eid_from=_X.cw_eid)'''
@@ -969,6 +974,18 @@
'''SELECT _CFG.cw_ecrit_par, _CALIBCFG.cw_eid, _CFG.cw_eid
FROM cw_Note AS _CFG LEFT OUTER JOIN cw_Note AS _CALIBCFG ON (_CALIBCFG.cw_ecrit_par=_CFG.cw_ecrit_par)
WHERE _CFG.cw_ecrit_par=1'''),
+
+ ('Any U,G WHERE U login UL, G name GL, G is CWGroup HAVING UPPER(UL)=UPPER(GL)?',
+ '''SELECT _U.cw_eid, _G.cw_eid
+FROM cw_CWUser AS _U LEFT OUTER JOIN cw_CWGroup AS _G ON (UPPER(_U.cw_login)=UPPER(_G.cw_name))'''),
+
+ ('Any U,G WHERE U login UL, G name GL, G is CWGroup HAVING UPPER(UL)?=UPPER(GL)',
+ '''SELECT _U.cw_eid, _G.cw_eid
+FROM cw_CWGroup AS _G LEFT OUTER JOIN cw_CWUser AS _U ON (UPPER(_U.cw_login)=UPPER(_G.cw_name))'''),
+
+ ('Any U,G WHERE U login UL, G name GL, G is CWGroup HAVING UPPER(UL)?=UPPER(GL)?',
+ '''SELECT _U.cw_eid, _G.cw_eid
+FROM cw_CWUser AS _U FULL OUTER JOIN cw_CWGroup AS _G ON (UPPER(_U.cw_login)=UPPER(_G.cw_name))'''),
]
VIRTUAL_VARS = [
@@ -1871,7 +1888,7 @@
backend = 'sqlite'
def _norm_sql(self, sql):
- return sql.strip().replace(' ILIKE ', ' LIKE ')
+ return sql.strip().replace(' ILIKE ', ' LIKE ').replace('TRUE', '1').replace('FALSE', '0')
def test_date_extraction(self):
self._check("Any MONTH(D) WHERE P is Personne, P creation_date D",
--- a/server/test/unittest_storage.py Tue Jul 26 19:34:43 2011 +0200
+++ b/server/test/unittest_storage.py Wed Jul 27 20:17:45 2011 +0200
@@ -259,7 +259,7 @@
@tag('update', 'NULL')
def test_bfss_update_to_None(self):
- f = self.session.create_entity('BFSSTestable', opt_attr=Binary('toto'))
+ f = self.session.create_entity('Affaire', opt_attr=Binary('toto'))
self.session.commit()
self.session.set_pool()
f.set_attributes(opt_attr=None)
--- a/sobjects/parsers.py Tue Jul 26 19:34:43 2011 +0200
+++ b/sobjects/parsers.py Wed Jul 27 20:17:45 2011 +0200
@@ -75,7 +75,10 @@
if rschema == 'eid':
continue
attrtype = eschema.destination(rschema)
- typeddict[rschema.type] = converters[attrtype](stringdict[rschema])
+ value = stringdict[rschema]
+ if value is not None:
+ value = converters[attrtype](value)
+ typeddict[rschema.type] = value
return typeddict
def rtype_role_rql(rtype, role):
@@ -244,7 +247,7 @@
except ValueError:
return url + '?' + self._cw.build_url_params(**params)
try:
- etype = self._cw.vreg.case_insensitive_etypes[etype]
+ etype = self._cw.vreg.case_insensitive_etypes[etype.lower()]
except KeyError:
return url + '?' + self._cw.build_url_params(**params)
if add_relations:
@@ -294,9 +297,12 @@
# relation
related = rels.setdefault(role, {}).setdefault(child.tag, [])
related += [ritem for ritem, _ in self.parser.parse_etree(child)]
- else:
+ elif child.text:
# attribute
item[child.tag] = unicode(child.text)
+ else:
+ # None attribute (empty tag)
+ item[child.tag] = None
return item, rels
--- a/sobjects/test/unittest_parsers.py Tue Jul 26 19:34:43 2011 +0200
+++ b/sobjects/test/unittest_parsers.py Wed Jul 27 20:17:45 2011 +0200
@@ -40,7 +40,7 @@
BASEXML = ''.join(u'''
<rset size="1">
- <CWUser eid="5" cwuri="http://pouet.org/5">
+ <CWUser eid="5" cwuri="http://pouet.org/5" cwsource="system">
<login>sthenault</login>
<upassword>toto</upassword>
<last_login_time>2011-01-25 14:14:06</last_login_time>
@@ -113,20 +113,28 @@
<last_login_time>2011-01-25 14:14:06</last_login_time>
<creation_date>2010-01-22 10:27:59</creation_date>
<modification_date>2011-01-25 14:14:06</modification_date>
+ <in_group role="subject">
+ <CWGroup cwuri="http://pouet.org/7" eid="7"/>
+ </in_group>
</CWUser>
</rset>
'''.splitlines()
)
+
+
class CWEntityXMLParserTC(CubicWebTC):
+ """/!\ this test use a pre-setup database /!\, if you modify above xml,
+ REMOVE THE DATABASE TEMPLATE else it won't be considered
+ """
test_db_id = 'xmlparser'
@classmethod
def pre_setup_database(cls, session, config):
- source = session.create_entity('CWSource', name=u'myfeed', type=u'datafeed',
+ myfeed = session.create_entity('CWSource', name=u'myfeed', type=u'datafeed',
parser=u'cw.entityxml', url=BASEXML)
- session.create_entity('CWSource', name=u'myotherfeed', type=u'datafeed',
- parser=u'cw.entityxml', url=OTHERXML)
+ myotherfeed = session.create_entity('CWSource', name=u'myotherfeed', type=u'datafeed',
+ parser=u'cw.entityxml', url=OTHERXML)
session.commit()
- source.init_mapping([(('CWUser', 'use_email', '*'),
+ myfeed.init_mapping([(('CWUser', 'use_email', '*'),
u'role=subject\naction=copy'),
(('CWUser', 'in_group', '*'),
u'role=subject\naction=link\nlinkattr=name'),
@@ -135,11 +143,18 @@
(('*', 'tags', 'CWUser'),
u'role=object\naction=link-or-create\nlinkattr=name'),
])
+ myotherfeed.init_mapping([(('CWUser', 'in_group', '*'),
+ u'role=subject\naction=link\nlinkattr=name'),
+ (('CWUser', 'in_state', '*'),
+ u'role=subject\naction=link\nlinkattr=name'),
+ ])
session.create_entity('Tag', name=u'hop')
def test_complete_url(self):
dfsource = self.repo.sources_by_uri['myfeed']
parser = dfsource._get_parser(self.session)
+ self.assertEqual(parser.complete_url('http://www.cubicweb.org/CWUser'),
+ 'http://www.cubicweb.org/CWUser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject&vid=xml')
self.assertEqual(parser.complete_url('http://www.cubicweb.org/cwuser'),
'http://www.cubicweb.org/cwuser?relation=tags-object&relation=in_group-subject&relation=in_state-subject&relation=use_email-subject&vid=xml')
self.assertEqual(parser.complete_url('http://www.cubicweb.org/cwuser?vid=rdf&relation=hop'),
@@ -164,7 +179,7 @@
(u'EmailAddress', {})]
}
})
- session = self.repo.internal_session()
+ session = self.repo.internal_session(safe=True)
stats = dfsource.pull_data(session, force=True, raise_on_error=True)
self.assertEqual(sorted(stats.keys()), ['created', 'updated'])
self.assertEqual(len(stats['created']), 2)
@@ -248,7 +263,7 @@
def test_external_entity(self):
dfsource = self.repo.sources_by_uri['myotherfeed']
- session = self.repo.internal_session()
+ session = self.repo.internal_session(safe=True)
stats = dfsource.pull_data(session, force=True, raise_on_error=True)
user = self.execute('CWUser X WHERE X login "sthenault"').get_entity(0, 0)
self.assertEqual(user.creation_date, datetime(2010, 01, 22, 10, 27, 59))
@@ -256,6 +271,33 @@
self.assertEqual(user.cwuri, 'http://pouet.org/5')
self.assertEqual(user.cw_source[0].name, 'myfeed')
+ def test_noerror_missing_fti_attribute(self):
+ dfsource = self.repo.sources_by_uri['myfeed']
+ session = self.repo.internal_session(safe=True)
+ parser = dfsource._get_parser(session)
+ dfsource.process_urls(parser, ['''
+<rset size="1">
+ <Card eid="50" cwuri="http://pouet.org/50" cwsource="system">
+ <title>how-to</title>
+ </Card>
+</rset>
+'''], raise_on_error=True)
+
+ def test_noerror_unspecified_date(self):
+ dfsource = self.repo.sources_by_uri['myfeed']
+ session = self.repo.internal_session(safe=True)
+ parser = dfsource._get_parser(session)
+ dfsource.process_urls(parser, ['''
+<rset size="1">
+ <Card eid="50" cwuri="http://pouet.org/50" cwsource="system">
+ <title>how-to</title>
+ <content>how-to</content>
+ <synopsis>how-to</synopsis>
+ <creation_date/>
+ </Card>
+</rset>
+'''], raise_on_error=True)
+
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
unittest_main()
--- a/web/data/cubicweb.css Tue Jul 26 19:34:43 2011 +0200
+++ b/web/data/cubicweb.css Wed Jul 27 20:17:45 2011 +0200
@@ -309,7 +309,7 @@
}
div#contentmain{
- margin-top: %(pageContentPadding)s
+ margin-top: %(pageContentPadding)s;
}
/*FIXME */
@@ -923,7 +923,7 @@
ul.startup li,
ul.section li {
- margin-left: 0px
+ margin-left: 0px;
}
ul.simple li,