Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-08 Thread Martijn Moeling
Michael,


I took a look at the recipe you indicated, it looks promising but the check 
should be constructed from database results. Another issue is that this project 
is implemented in my web based desktop/Os which uses SQLAlchemy from the bottem 
up. So modifiing the session object globally is with a PreFilteredQuery is not 
a real option. Creating a session for this program only might be an option 
but I am not sure how that will turn out.

Being it a web based (and so Handle request and die), Persistence is (to me) 
not very usefull and I need to reload everything for every action.

the @reconstructor hook seems too outdated. I moved to 0.6.6 last week, and 
only will upgrade to stable/production versions since in my case there is a lot 
to it.

I need to transparently add being queried functionality to mapped objects. 
This functionality is will be mixed in and should be able to limit the results 
when being queried. Since my class definitions are so complex I would like to 
make a (not functional) example on what I am in search of. and I will not 
bother you with chemistry stuff...


Class ACL(Base):
Id = Column(Integer, primary_key=True)

tablename = Column(Unicode(...
tableId= Column(Integer

RecordId = ForeignKeyContruct( / ForeignKey   (not sure yet)

Record = relation( self.tablename

User_Group = relation to Person, group 

Bool columns..
MayRead
MayWrite
MayCreate


Class Mixinstuff(Object)

Rights = {}  # Rights[MayRead] etc. will be set upon load



Class Person(Base,Mixinstuff)

Id = Column(Integer, primary_key=True)

ACLs = relation('ACL'   All ACL records which have   tablename = 
'person' and tableID = Person.Id, cascade=delete and ACL record for me ) # 
ACL's work on many tables
I might not define the relation here but backref from the acl record 
depending on how to build what I want

addresses = relation( 

Class Address(Base, ACLMixinstuff)

Id = Column(Integer, primary_key=True)

ACLs = relation('ACL'   All ACL records which have   tablename = 
'person' and tableID = Person.Id, cascade=delete) # ACL's work on many tables
I might not define the relation here but backref from the acl record 
depending on how to build what I want

class ME()
userId =  1 (foreignkey to Person)
groups = [1,2,3,4]  (relationship with groups (same polymorhic 
baseclass)


Now consider ME being a member of Everyone not guest

ACLS for Person
ME  | table = person | Id =  1| MayRead = F
Everyone| table = person | Id = 1 | MayRead = T
Guest   | table = person | Id = 1 | MayRead = F


user = ME, GROUPS = [Everyone]

A query for Session.query(Persons).all() should NOT return Person.Id although 
Everyone says True, personal Permissions overrule group permissions , simple 
boolean operations. If no ACLs are found It all defaults to false or true not 
sure yet on how this will work on my real data model, since this will be the 
model on which atoms and molecule connections are Allowed

If However the ACL's turn out that ME.MayRead = T, I will only get related 
addresses I actually may read. This should work automatically for each class 
with Mixedinstuff inherited

This is whilst I do not want the Users of this model to be bothered with 
this, the should add data to their model and query to generate list of possible 
new molecules.

I am some sort of clueless on how to do this properly

the MapperExtention.append_result still seems the best way...   

if calculate_ACLs(Session = object_session(self), tablename = 
instance.__tablename__, TableId = instance.__=TableId__, CheckFor = ME, Right = 
MayRead ):
EXT_CONTINUE
else:
EXT_STOP

Dont you?

One other thing, the CalculateACLs query should be as light as possible It will 
only need to return True or False if possible using database functions and if 
possible be database independant.
Can you help me on that one too?


def calculate-ACLs(...):
BOOLGROUP = Session.query(ACL).filter(and_(tablename= .., tableId 
=...,USER_GROUP in me.literal_colum(..?..?..?))..
BOOLME = the same but now for ME, is easy no boolean calculation needed 
in query  

if BoolME:
return BOOLME
else:
return BOOLGROUP




Martijn











On Feb 7, 2011, at 5:55 PM, Michael Bayer wrote:

 
 On Feb 7, 2011, at 11:42 AM, Martijn Moeling wrote:
 
 I think, I might be helped with the create_instance event
 
 Assuming you're talking about when the ORM establishes an instance from a 
 newly fetched row, you can use the @reconstructor hook for that.   0.7 
 publishes this event additionally as the load event.
 
 
 
 
 I will never ever stop a class from being saved/persistent, 
 
 it is the other way around. I 

Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-08 Thread Michael Bayer

On Feb 8, 2011, at 6:05 AM, Martijn Moeling wrote:

 Michael,
 
 
 I took a look at the recipe you indicated, it looks promising but the check 
 should be constructed from database results. Another issue is that this 
 project is implemented in my web based desktop/Os which uses SQLAlchemy from 
 the bottem up. So modifiing the session object globally is with a 
 PreFilteredQuery is not a real option. Creating a session for this program 
 only might be an option but I am not sure how that will turn out.

Well a MapperExtension is also global to that class.Subclassing Query 
with rules for a specific mapper is fairly easy to isolate to those use cases.

 
 Being it a web based (and so Handle request and die), Persistence is (to me) 
 not very usefull and I need to reload everything for every action.

That is typical for a web application.

 
 the @reconstructor hook seems too outdated. I moved to 0.6.6 last week, and 
 only will upgrade to stable/production versions since in my case there is a 
 lot to it.

@reconstructor is a standard feature since 0.5 and continues to be.

 
 I need to transparently add being queried functionality to mapped objects. 
 This functionality is will be mixed in and should be able to limit the 
 results when being queried. Since my class definitions are so complex I would 
 like to make a (not functional) example on what I am in search of. and I will 
 not bother you with chemistry stuff...
 
 
 user = ME, GROUPS = [Everyone]
 
 A query for Session.query(Persons).all() should NOT return Person.Id although 
 Everyone says True, personal Permissions overrule group permissions , simple 
 boolean operations. If no ACLs are found It all defaults to false or true not 
 sure yet on how this will work on my real data model, since this will be the 
 model on which atoms and molecule connections are Allowed
 
 If However the ACL's turn out that ME.MayRead = T, I will only get related 
 addresses I actually may read. This should work automatically for each 
 class with Mixedinstuff inherited
 
 This is whilst I do not want the Users of this model to be bothered with 
 this, the should add data to their model and query to generate list of 
 possible new molecules.
 
 I am some sort of clueless on how to do this properly
 
 the MapperExtention.append_result still seems the best way... 
 
   if calculate_ACLs(Session = object_session(self), tablename = 
 instance.__tablename__, TableId = instance.__=TableId__, CheckFor = ME, Right 
 = MayRead ):
   EXT_CONTINUE
   else:
   EXT_STOP
 
 Dont you?

I guess what you're expressing is that your ACL rules need to fire off using 
Python code, not SQL expressions.The whole thing seems quite awkward to me 
since there's nothing to stop someone from saying Query(MyACLObject.id, 
MyACLObject.name, ...), etc., they get all the data from the ACL row anyway, or 
similarly if they were to say Query(SomeClass, SomeOtherClass, MyACLObject) 
using a join, again the append_result() hook isn't used.If it were me I'd 
be using some filter function around query() in an explicit sense to do it, but 
this is just a matter of style.  The hook will work fine if its limitations are 
OK with you.


-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-08 Thread Martijn Moeling
Michael,

Thank you,

The final solution has nothing to do with ACL's or addresses and security for 
others getting results by querying is a none issue.
As mentioned before I am building a database and tools to help chemists 
selecting molecule structures. It is all way more complex than you might think 
since the ACL records have ACL records assosiated to them to.
Setting up relations and queries is a total nightmare because almost all 
relations end up to be circular over multiple tables. controlling the eager 
loading where possible for convenience and where impossible has been a huge job 
although SQLAlchemy is a huge help.

I only use this as a understandable data structure since I know how hard it was 
to understand the terminology. I do not want to bring that to this group and 
more importantly since I search the mailinglist myself a lot it can help others 
finding a solution to their needs.  I find that the deeper I dive into SA, the 
less examples are available, the harder it is to test functionality and 
sometimes documentation gets more sparse.

Thank you again...

Martijn

 


On Feb 8, 2011, at 4:21 PM, Michael Bayer wrote:

 
 On Feb 8, 2011, at 6:05 AM, Martijn Moeling wrote:
 
 Michael,
 
 
 I took a look at the recipe you indicated, it looks promising but the check 
 should be constructed from database results. Another issue is that this 
 project is implemented in my web based desktop/Os which uses SQLAlchemy from 
 the bottem up. So modifiing the session object globally is with a 
 PreFilteredQuery is not a real option. Creating a session for this program 
 only might be an option but I am not sure how that will turn out.
 
 Well a MapperExtension is also global to that class.Subclassing Query 
 with rules for a specific mapper is fairly easy to isolate to those use cases.
 
 
 Being it a web based (and so Handle request and die), Persistence is (to me) 
 not very usefull and I need to reload everything for every action.
 
 That is typical for a web application.
 
 
 the @reconstructor hook seems too outdated. I moved to 0.6.6 last week, and 
 only will upgrade to stable/production versions since in my case there is a 
 lot to it.
 
 @reconstructor is a standard feature since 0.5 and continues to be.
 
 
 I need to transparently add being queried functionality to mapped objects. 
 This functionality is will be mixed in and should be able to limit the 
 results when being queried. Since my class definitions are so complex I 
 would like to make a (not functional) example on what I am in search of. and 
 I will not bother you with chemistry stuff...
 
 
 user = ME, GROUPS = [Everyone]
 
 A query for Session.query(Persons).all() should NOT return Person.Id 
 although Everyone says True, personal Permissions overrule group permissions 
 , simple boolean operations. If no ACLs are found It all defaults to false 
 or true not sure yet on how this will work on my real data model, since this 
 will be the model on which atoms and molecule connections are Allowed
 
 If However the ACL's turn out that ME.MayRead = T, I will only get related 
 addresses I actually may read. This should work automatically for each 
 class with Mixedinstuff inherited
 
 This is whilst I do not want the Users of this model to be bothered with 
 this, the should add data to their model and query to generate list of 
 possible new molecules.
 
 I am some sort of clueless on how to do this properly
 
 the MapperExtention.append_result still seems the best way...
 
  if calculate_ACLs(Session = object_session(self), tablename = 
 instance.__tablename__, TableId = instance.__=TableId__, CheckFor = ME, 
 Right = MayRead ):
  EXT_CONTINUE
  else:
  EXT_STOP
 
 Dont you?
 
 I guess what you're expressing is that your ACL rules need to fire off using 
 Python code, not SQL expressions.The whole thing seems quite awkward to 
 me since there's nothing to stop someone from saying Query(MyACLObject.id, 
 MyACLObject.name, ...), etc., they get all the data from the ACL row anyway, 
 or similarly if they were to say Query(SomeClass, SomeOtherClass, 
 MyACLObject) using a join, again the append_result() hook isn't used.If 
 it were me I'd be using some filter function around query() in an explicit 
 sense to do it, but this is just a matter of style.  The hook will work fine 
 if its limitations are OK with you.
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 

[sqlalchemy] MapperExtension.append_result ....

2011-02-07 Thread Martijn Moeling
Hi,

It is me again with an interesting thing, I've searched the net, this group 
etc. Not a lot of people seem interested in append_result, I AM!!

I am looking for a way to implement the following:


I have many tables, a lot with polymorphic inheritance and self and cross 
references.

In order to control available data I have set up a system similar to ACL 
(Access Control Lists)

Depending on Who I am I can get data from the database.

I want to do so within the MapperExtension I already have set up to do some 
before update and before insert


def append_result(self, mapper, selectcontext, row, instance, result, 
**flags):
if instance.__tablename__ == 'he':
return EXT_STOP
else:
return EXT_CONTINUE

would do such a thing, but I want (for the sake of the code behind that) to 
continue with a heavily modified instance.

To avoid making this long code (a lot of different object types pass through 
here, remember the polymorhic bit)

Does anyone have an interesting approach to this? basically I need to do 
something like instance= instance_class_type(new, configuration, based, on, 
the, ACL)



Any help would be wonderfull,

Martijn








-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-07 Thread Michael Bayer

On Feb 7, 2011, at 10:55 AM, Martijn Moeling wrote:

 Hi,
 
 It is me again with an interesting thing, I've searched the net, this group 
 etc. Not a lot of people seem interested in append_result, I AM!!
 
 I am looking for a way to implement the following:
 
 
 I have many tables, a lot with polymorphic inheritance and self and cross 
 references.
 
 In order to control available data I have set up a system similar to ACL 
 (Access Control Lists)
 
 Depending on Who I am I can get data from the database.
 
 I want to do so within the MapperExtension I already have set up to do some 
 before update and before insert

Limitations on inserts, updates and queries are best done outside of the 
Mapper.   By the time the mapper is dealing with instructions to persist or 
load a row, its usually too late, unless you're looking to raise an exception 
upon certain conditions.   For example there's no way to stop the insert 
from happening inside of a before insert operation, short of raising an 
exception (maybe that's what you're doing).

A SessionExtension.before_flush() OTOH allows you to modify everything that's 
going to happen before any flush plans are made.

Regarding append_result(), its a very old hook from 0.1 that's never had any 
real use.   In this case I would instead be ensure that the undesired rows are 
not in the result set to start with.   The recipe at 
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery is a decent 
starting point for such a recipe.


 
 
def append_result(self, mapper, selectcontext, row, instance, result, 
 **flags):
if instance.__tablename__ == 'he':
return EXT_STOP
else:
return EXT_CONTINUE
 
 would do such a thing, but I want (for the sake of the code behind that) to 
 continue with a heavily modified instance.
 
 To avoid making this long code (a lot of different object types pass through 
 here, remember the polymorhic bit)
 
 Does anyone have an interesting approach to this? basically I need to do 
 something like instance= instance_class_type(new, configuration, based, on, 
 the, ACL)
 
 
 
 Any help would be wonderfull,
 
 Martijn
 
 
 
 
 
 
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-07 Thread Martijn Moeling
I think, I might be helped with the create_instance event

I will never ever stop a class from being saved/persistent, 

it is the other way around. I thought I was able to use joins and or relations 
to limit for allowed results from a query.
With all the polymorphic and self references I have got and the fact that I 
need to do so for multiple Polymorphic colums I came up with the ACL idea.

Im not sure it if will perform, but in the create_instance I will look up the 
ACL and set additional properties on the instance or create an empty one.

This whole system is getting very complex now and limiting returned data 
involves modifying relationsships a lot. I'm really glad I got that working.

Many classes are base on top of that and I the only IT guy working on this 
together with programmers with a chemistry degree. Who will need the API I am 
working on to do their stuff without knowing anything about Databases

To have that I Inherit polymorphicly, have many-to-many self references, use 
mixins. 

I'll have a look at the PreFilteredQuery example you gave me, Any thoughts are 
helpful, I'll see If I can make up an example with persons and addresses again 
since the molecule stuff makes it even more confusing..
Will take me some time though 

Martijn




On Feb 7, 2011, at 5:18 PM, Michael Bayer wrote:

 
 On Feb 7, 2011, at 10:55 AM, Martijn Moeling wrote:
 
 Hi,
 
 It is me again with an interesting thing, I've searched the net, this group 
 etc. Not a lot of people seem interested in append_result, I AM!!
 
 I am looking for a way to implement the following:
 
 
 I have many tables, a lot with polymorphic inheritance and self and cross 
 references.
 
 In order to control available data I have set up a system similar to ACL 
 (Access Control Lists)
 
 Depending on Who I am I can get data from the database.
 
 I want to do so within the MapperExtension I already have set up to do some 
 before update and before insert
 
 Limitations on inserts, updates and queries are best done outside of the 
 Mapper.   By the time the mapper is dealing with instructions to persist or 
 load a row, its usually too late, unless you're looking to raise an exception 
 upon certain conditions.   For example there's no way to stop the 
 insert from happening inside of a before insert operation, short of raising 
 an exception (maybe that's what you're doing).
 
 A SessionExtension.before_flush() OTOH allows you to modify everything that's 
 going to happen before any flush plans are made.
 
 Regarding append_result(), its a very old hook from 0.1 that's never had any 
 real use.   In this case I would instead be ensure that the undesired rows 
 are not in the result set to start with.   The recipe at 
 http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery is a decent 
 starting point for such a recipe.
 
 
 
 
   def append_result(self, mapper, selectcontext, row, instance, result, 
 **flags):
   if instance.__tablename__ == 'he':
   return EXT_STOP
   else:
   return EXT_CONTINUE
 
 would do such a thing, but I want (for the sake of the code behind that) to 
 continue with a heavily modified instance.
 
 To avoid making this long code (a lot of different object types pass through 
 here, remember the polymorhic bit)
 
 Does anyone have an interesting approach to this? basically I need to do 
 something like instance= instance_class_type(new, configuration, based, on, 
 the, ACL)
 
 
 
 Any help would be wonderfull,
 
 Martijn
 
 
 
 
 
 
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] MapperExtension.append_result ....

2011-02-07 Thread Michael Bayer

On Feb 7, 2011, at 11:42 AM, Martijn Moeling wrote:

 I think, I might be helped with the create_instance event

Assuming you're talking about when the ORM establishes an instance from a newly 
fetched row, you can use the @reconstructor hook for that.   0.7 publishes this 
event additionally as the load event.



 
 I will never ever stop a class from being saved/persistent, 
 
 it is the other way around. I thought I was able to use joins and or 
 relations to limit for allowed results from a query.
 With all the polymorphic and self references I have got and the fact that I 
 need to do so for multiple Polymorphic colums I came up with the ACL idea.
 
 Im not sure it if will perform, but in the create_instance I will look up the 
 ACL and set additional properties on the instance or create an empty one.
 
 This whole system is getting very complex now and limiting returned data 
 involves modifying relationsships a lot. I'm really glad I got that working.
 
 Many classes are base on top of that and I the only IT guy working on this 
 together with programmers with a chemistry degree. Who will need the API I 
 am working on to do their stuff without knowing anything about 
 Databases
 
 To have that I Inherit polymorphicly, have many-to-many self references, use 
 mixins. 



 
 I'll have a look at the PreFilteredQuery example you gave me, Any thoughts 
 are helpful, I'll see If I can make up an example with persons and addresses 
 again since the molecule stuff makes it even more confusing..
 Will take me some time though 
 
 Martijn
 
 
 
 
 On Feb 7, 2011, at 5:18 PM, Michael Bayer wrote:
 
 
 On Feb 7, 2011, at 10:55 AM, Martijn Moeling wrote:
 
 Hi,
 
 It is me again with an interesting thing, I've searched the net, this group 
 etc. Not a lot of people seem interested in append_result, I AM!!
 
 I am looking for a way to implement the following:
 
 
 I have many tables, a lot with polymorphic inheritance and self and cross 
 references.
 
 In order to control available data I have set up a system similar to ACL 
 (Access Control Lists)
 
 Depending on Who I am I can get data from the database.
 
 I want to do so within the MapperExtension I already have set up to do some 
 before update and before insert
 
 Limitations on inserts, updates and queries are best done outside of the 
 Mapper.   By the time the mapper is dealing with instructions to persist or 
 load a row, its usually too late, unless you're looking to raise an 
 exception upon certain conditions.   For example there's no way to 
 stop the insert from happening inside of a before insert operation, 
 short of raising an exception (maybe that's what you're doing).
 
 A SessionExtension.before_flush() OTOH allows you to modify everything 
 that's going to happen before any flush plans are made.
 
 Regarding append_result(), its a very old hook from 0.1 that's never had any 
 real use.   In this case I would instead be ensure that the undesired rows 
 are not in the result set to start with.   The recipe at 
 http://www.sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery is a 
 decent starting point for such a recipe.
 
 
 
 
  def append_result(self, mapper, selectcontext, row, instance, result, 
 **flags):
  if instance.__tablename__ == 'he':
  return EXT_STOP
  else:
  return EXT_CONTINUE
 
 would do such a thing, but I want (for the sake of the code behind that) to 
 continue with a heavily modified instance.
 
 To avoid making this long code (a lot of different object types pass 
 through here, remember the polymorhic bit)
 
 Does anyone have an interesting approach to this? basically I need to do 
 something like instance= instance_class_type(new, configuration, based, on, 
 the, ACL)
 
 
 
 Any help would be wonderfull,
 
 Martijn
 
 
 
 
 
 
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this