[Sqlalchemy-commits] [1328] sqlalchemy/branches/schema/test: starting to convert mapper.select() to
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-04-23 23:55:17
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1328] sqlalchemy/branches/schema/test: starting to convert mapper.select() to session.query(class).select(), etc.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1328</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-23 18:55:03 -0500 (Sun, 23 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>starting to convert mapper.select() to session.query(class).select(), etc.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormmapperpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormpropertiespy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormquerypy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemybranchesschemalibsqlalchemyormsessionpy">sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemybranchesschematestmapperpy">sqlalchemy/branches/schema/test/mapper.py</a></li> <li><a href="#sqlalchemybranchesschematestobjectstorepy">sqlalchemy/branches/schema/test/objectstore.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemalibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/mapper.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -12,7 +12,7 @@ </span><span class="cx"> import sqlalchemy.sql_util as sqlutil </span><span class="cx"> import sync </span><span class="cx"> from sqlalchemy.exceptions import * </span><del>-import query </del><ins>+import query as querylib </ins><span class="cx"> import session as sessionlib </span><span class="cx"> import sys </span><span class="cx"> import weakref </span><span class="lines">@@ -215,73 +215,75 @@ </span><span class="cx"> #for key, value in self.columntoproperty.iteritems(): </span><span class="cx"> # print key.table.name, key.key, [(v.key, v) for v in value] </span><span class="cx"> </span><del>- def _get_query(self): </del><ins>+ def using(self, session): + return querylib.Query(self, session=session) + def query(self, session=None): + """returns an instance of sqlalchemy.orm.query.Query, which implements all the query-constructing + methods such as get(), select(), select_by(), etc.""" + if session is not None: + return querylib.Query(self, session=session) + </ins><span class="cx"> try: </span><span class="cx"> if self._query.mapper is not self: </span><del>- self._query = query.Query(self) </del><ins>+ self._query = querylib.Query(self) </ins><span class="cx"> return self._query </span><span class="cx"> except AttributeError: </span><del>- self._query = query.Query(self) </del><ins>+ self._query = querylib.Query(self) </ins><span class="cx"> return self._query </span><del>- query = property(_get_query, doc=\ - """returns an instance of sqlalchemy.orm.query.Query, which implements all the query-constructing - methods such as get(), select(), select_by(), etc. The default Query object uses the global thread-local - Session from the session package. To get a Query object for a specific Session, call the - using(session) method.""") </del><span class="cx"> </span><span class="cx"> def get(self, *ident, **kwargs): </span><span class="cx"> """calls get() on this mapper's default Query object.""" </span><del>- return self.query.get(*ident, **kwargs) </del><ins>+ return self.query().get(*ident, **kwargs) </ins><span class="cx"> </span><span class="cx"> def _get(self, key, ident=None, reload=False): </span><del>- return self.query._get(key, ident=ident, reload=reload) </del><ins>+ return self.query()._get(key, ident=ident, reload=reload) </ins><span class="cx"> </span><span class="cx"> def get_by(self, *args, **params): </span><span class="cx"> """calls get_by() on this mapper's default Query object.""" </span><del>- return self.query.get_by(*args, **params) </del><ins>+ return self.query().get_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def select_by(self, *args, **params): </span><span class="cx"> """calls select_by() on this mapper's default Query object.""" </span><del>- return self.query.select_by(*args, **params) </del><ins>+ return self.query().select_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectfirst_by(self, *args, **params): </span><span class="cx"> """calls selectfirst_by() on this mapper's default Query object.""" </span><del>- return self.query.selectfirst_by(*args, **params) </del><ins>+ return self.query().selectfirst_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectone_by(self, *args, **params): </span><span class="cx"> """calls selectone_by() on this mapper's default Query object.""" </span><del>- return self.query.selectone_by(*args, **params) </del><ins>+ return self.query().selectone_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def count_by(self, *args, **params): </span><span class="cx"> """calls count_by() on this mapper's default Query object.""" </span><del>- return self.query.count_by(*args, **params) </del><ins>+ return self.query().count_by(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectfirst(self, *args, **params): </span><span class="cx"> """calls selectfirst() on this mapper's default Query object.""" </span><del>- return self.query.selectfirst(*args, **params) </del><ins>+ return self.query().selectfirst(*args, **params) </ins><span class="cx"> </span><span class="cx"> def selectone(self, *args, **params): </span><span class="cx"> """calls selectone() on this mapper's default Query object.""" </span><del>- return self.query.selectone(*args, **params) </del><ins>+ return self.query().selectone(*args, **params) </ins><span class="cx"> </span><span class="cx"> def select(self, arg=None, **kwargs): </span><span class="cx"> """calls select() on this mapper's default Query object.""" </span><del>- return self.query.select(arg=arg, **kwargs) </del><ins>+ return self.query().select(arg=arg, **kwargs) </ins><span class="cx"> </span><span class="cx"> def select_whereclause(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> """calls select_whereclause() on this mapper's default Query object.""" </span><del>- return self.query.select_whereclause(whereclause=whereclause, params=params, **kwargs) </del><ins>+ return self.query().select_whereclause(whereclause=whereclause, params=params, **kwargs) </ins><span class="cx"> </span><span class="cx"> def count(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> """calls count() on this mapper's default Query object.""" </span><del>- return self.query.count(whereclause=whereclause, params=params, **kwargs) </del><ins>+ return self.query().count(whereclause=whereclause, params=params, **kwargs) </ins><span class="cx"> </span><span class="cx"> def select_statement(self, statement, **params): </span><span class="cx"> """calls select_statement() on this mapper's default Query object.""" </span><del>- return self.query.select_statement(statement, **params) </del><ins>+ return self.query().select_statement(statement, **params) </ins><span class="cx"> </span><span class="cx"> def select_text(self, text, **params): </span><del>- return self.query.select_text(text, **params) </del><ins>+ return self.query().select_text(text, **params) </ins><span class="cx"> </span><span class="cx"> def add_properties(self, dict_of_properties): </span><span class="cx"> """adds the given dictionary of properties to this mapper, using add_property.""" </span><span class="lines">@@ -430,7 +432,7 @@ </span><span class="cx"> def compile(self, whereclause = None, **options): </span><span class="cx"> """works like select, except returns the SQL statement object without </span><span class="cx"> compiling or executing it""" </span><del>- return self.query._compile(whereclause, **options) </del><ins>+ return self.query()._compile(whereclause, **options) </ins><span class="cx"> </span><span class="cx"> def copy(self, **kwargs): </span><span class="cx"> mapper = Mapper.__new__(Mapper) </span><span class="lines">@@ -439,9 +441,7 @@ </span><span class="cx"> mapper.props = self.props.copy() </span><span class="cx"> return mapper </span><span class="cx"> </span><del>- def using(self, session): - return query.Query(self, session=session) - </del><ins>+ </ins><span class="cx"> def options(self, *options, **kwargs): </span><span class="cx"> """uses this mapper as a prototype for a new mapper with different behavior. </span><span class="cx"> *options is a list of options directives, which include eagerload(), lazyload(), and noload()""" </span><span class="lines">@@ -458,7 +458,7 @@ </span><span class="cx"> </span><span class="cx"> def __getattr__(self, key): </span><span class="cx"> if (key.startswith('select_by_') or key.startswith('get_by_')): </span><del>- return getattr(self.query, key) </del><ins>+ return getattr(self.query(), key) </ins><span class="cx"> else: </span><span class="cx"> raise AttributeError(key) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/properties.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -362,7 +362,7 @@ </span><span class="cx"> (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </span><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><del>- self.use_get = not self.uselist and self.mapper.query._get_clause.compare(self.lazywhere) </del><ins>+ self.use_get = not self.uselist and self.mapper.query()._get_clause.compare(self.lazywhere) </ins><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/query.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -137,6 +137,7 @@ </span><span class="cx"> statement = self._compile(whereclause, **kwargs) </span><span class="cx"> return self._select_statement(statement, params=params) </span><span class="cx"> </span><ins>+ </ins><span class="cx"> def count(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> s = self.table.count(whereclause) </span><span class="cx"> if params is not None: </span><span class="lines">@@ -151,6 +152,10 @@ </span><span class="cx"> t = sql.text(text) </span><span class="cx"> return self.instances(t, params=params) </span><span class="cx"> </span><ins>+ def options(self, *args, **kwargs): + """returns a new Query object using the given MapperOptions.""" + return self.mapper.options(*args, **kwargs).using(session=self._session) + </ins><span class="cx"> def __getattr__(self, key): </span><span class="cx"> if (key.startswith('select_by_')): </span><span class="cx"> key = key[10:] </span></span></pre></div> <a id="sqlalchemybranchesschemalibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/lib/sqlalchemy/orm/session.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -11,7 +11,6 @@ </span><span class="cx"> import sqlalchemy </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> </span><del>- </del><span class="cx"> class SessionTransaction(object): </span><span class="cx"> def __init__(self, session, parent=None, autoflush=True): </span><span class="cx"> self.session = session </span><span class="lines">@@ -56,6 +55,7 @@ </span><span class="cx"> self.session.transaction = None </span><span class="cx"> </span><span class="cx"> class Session(object): </span><ins>+ """encapsulates a set of objects being operated upon within an object-relational operation.""" </ins><span class="cx"> def __init__(self, bind_to=None, hash_key=None, new_imap=True, import_session=None): </span><span class="cx"> if import_session is not None: </span><span class="cx"> self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map) </span><span class="lines">@@ -113,6 +113,9 @@ </span><span class="cx"> """ </span><span class="cx"> if self.transaction is not None: </span><span class="cx"> self.transaction.close() </span><ins>+ def mapper(self, class_, entity_name=None): + """given an Class, returns the primary Mapper responsible for persisting it""" + return class_mapper(class_, entity_name = entity_name) </ins><span class="cx"> def bind_mapper(self, mapper, bindto): </span><span class="cx"> """binds the given Mapper to the given Engine or Connection. All subsequent operations involving this </span><span class="cx"> Mapper will use the given bindto.""" </span></span></pre></div> <a id="sqlalchemybranchesschematestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/mapper.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/mapper.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/test/mapper.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -75,28 +75,30 @@ </span><span class="cx"> </span><span class="cx"> class MapperTest(MapperSuperTest): </span><span class="cx"> def testget(self): </span><del>- m = mapper(User, users) - self.assert_(m.get(19) is None) - u = m.get(7) - u2 = m.get(7) </del><ins>+ s = create_session() + mapper(User, users) + self.assert_(s.get(User, 19) is None) + u = s.get(User, 7) + u2 = s.get(User, 7) </ins><span class="cx"> self.assert_(u is u2) </span><del>- objectstore.clear() - u2 = m.get(7) </del><ins>+ s.clear() + u2 = s.get(User, 7) </ins><span class="cx"> self.assert_(u is not u2) </span><span class="cx"> </span><span class="cx"> def testrefresh(self): </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) - u = m.get(7) </del><ins>+ mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + s = create_session() + u = s.get(User, 7) </ins><span class="cx"> u.user_name = 'foo' </span><span class="cx"> a = Address() </span><span class="cx"> u.addresses.append(a) </span><span class="cx"> </span><span class="cx"> self.assert_(a in u.addresses) </span><span class="cx"> </span><del>- objectstore.refresh(u) </del><ins>+ s.refresh(u) </ins><span class="cx"> </span><span class="cx"> # its refreshed, so not dirty </span><del>- self.assert_(u not in objectstore.get_session().uow.dirty) </del><ins>+ self.assert_(u not in s.dirty) </ins><span class="cx"> </span><span class="cx"> # username is back to the DB </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="lines">@@ -106,10 +108,10 @@ </span><span class="cx"> u.user_name = 'foo' </span><span class="cx"> u.addresses.append(a) </span><span class="cx"> # now its dirty </span><del>- self.assert_(u in objectstore.get_session().uow.dirty) </del><ins>+ self.assert_(u in s.dirty) </ins><span class="cx"> self.assert_(u.user_name == 'foo') </span><span class="cx"> self.assert_(a in u.addresses) </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> </span><span class="cx"> # get the attribute, it refreshes </span><span class="cx"> self.assert_(u.user_name == 'jack') </span><span class="lines">@@ -117,27 +119,29 @@ </span><span class="cx"> </span><span class="cx"> def testrefresh_lazy(self): </span><span class="cx"> """tests that when a lazy loader is set as a trigger on an object's attribute (at the attribute level, not the class level), a refresh() operation doesnt fire the lazy loader or create any problems""" </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) - m2 = m.options(lazyload('addresses')) - u = m2.selectfirst(users.c.user_id==8) </del><ins>+ s = create_session() + mapper(User, users, properties={'addresses':relation(mapper(Address, addresses))}) + q2 = s.query(User).options(lazyload('addresses')) + u = q2.selectfirst(users.c.user_id==8) </ins><span class="cx"> def go(): </span><del>- objectstore.refresh(u) </del><ins>+ s.refresh(u) </ins><span class="cx"> self.assert_sql_count(db, go, 1) </span><span class="cx"> </span><span class="cx"> def testsessionpropigation(self): </span><span class="cx"> sess = create_session() </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) - u = m.using(sess).get(7) - assert objectstore.get_session(u) is sess - assert objectstore.get_session(u.addresses[0]) is sess </del><ins>+ mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) + u = sess.get(User, 7) + assert get_session(u) is sess + assert get_session(u.addresses[0]) is sess </ins><span class="cx"> </span><span class="cx"> def testexpire(self): </span><del>- m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) - u = m.get(7) </del><ins>+ s = create_session() + mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) + u = s.get(User, 7) </ins><span class="cx"> assert(len(u.addresses) == 1) </span><span class="cx"> u.user_name = 'foo' </span><span class="cx"> del u.addresses[0] </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> # test plain expire </span><span class="cx"> self.assert_(u.user_name =='jack') </span><span class="cx"> self.assert_(len(u.addresses) == 1) </span><span class="lines">@@ -145,14 +149,14 @@ </span><span class="cx"> # we're changing the database here, so if this test fails in the middle, </span><span class="cx"> # it'll screw up the other tests which are hardcoded to 7/'jack' </span><span class="cx"> u.user_name = 'foo' </span><del>- objectstore.flush() </del><ins>+ s.flush() </ins><span class="cx"> # change the value in the DB </span><span class="cx"> users.update(users.c.user_id==7, values=dict(user_name='jack')).execute() </span><del>- objectstore.expire(u) </del><ins>+ s.expire(u) </ins><span class="cx"> # object isnt refreshed yet, using dict to bypass trigger </span><span class="cx"> self.assert_(u.__dict__['user_name'] != 'jack') </span><span class="cx"> # do a select </span><del>- m.select() </del><ins>+ s.query(User).select() </ins><span class="cx"> # test that it refreshed </span><span class="cx"> self.assert_(u.__dict__['user_name'] == 'jack') </span><span class="cx"> </span><span class="lines">@@ -161,25 +165,26 @@ </span><span class="cx"> self.assert_(u.user_name =='jack') </span><span class="cx"> </span><span class="cx"> def testrefresh2(self): </span><del>- assign_mapper(Address, addresses) </del><ins>+ s = create_session() + mapper(Address, addresses) </ins><span class="cx"> </span><del>- assign_mapper(User, users, properties = dict(addresses=relation(Address.mapper,private=True,lazy=False)) ) </del><ins>+ mapper(User, users, properties = dict(addresses=relation(Address,private=True,lazy=False)) ) </ins><span class="cx"> </span><span class="cx"> u=User() </span><span class="cx"> u.user_name='Justin' </span><span class="cx"> a = Address() </span><span class="cx"> a.address_id=17 # to work around the hardcoded IDs in this test suite.... </span><span class="cx"> u.addresses.append(a) </span><del>- objectstore.flush() - objectstore.clear() - u = User.mapper.selectfirst() </del><ins>+ s.flush() + s.clear() + u = s.query(User).selectfirst() </ins><span class="cx"> print u.user_name </span><span class="cx"> </span><span class="cx"> #ok so far </span><del>- u.expire() #hangs when </del><ins>+ s.expire(u) #hangs when </ins><span class="cx"> print u.user_name #this line runs </span><span class="cx"> </span><del>- u.refresh() #hangs </del><ins>+ s.refresh(u) #hangs </ins><span class="cx"> </span><span class="cx"> def testmagic(self): </span><span class="cx"> m = mapper(User, users, properties = { </span></span></pre></div> <a id="sqlalchemybranchesschematestobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/branches/schema/test/objectstore.py (1327 => 1328)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 23:17:53 UTC (rev 1327) +++ sqlalchemy/branches/schema/test/objectstore.py 2006-04-23 23:55:03 UTC (rev 1328) </span><span class="lines">@@ -147,8 +147,10 @@ </span><span class="cx"> </span><span class="cx"> def testbasic(self): </span><span class="cx"> class Test(object): </span><del>- pass - assign_mapper(Test, uni_table) </del><ins>+ def __init__(self, id, txt): + self.id = id + self.txt = txt + mapper(Test, uni_table) </ins><span class="cx"> </span><span class="cx"> txt = u"\u0160\u0110\u0106\u010c\u017d" </span><span class="cx"> t1 = Test(id=1, txt = txt) </span></span></pre> </div> </div> </body> </html> |