source: trunk/plugins/resourcetoolsplugin/tracresourcetools/property/model.py @ 685

Revision 685, 15.3 KB checked in by cbalan, 5 years ago (diff)

ResourceTools?: - Added more debug messages for get_resource_properties method.

Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright 2008 Optaros, Inc.
4#
5
6import traceback 
7from StringIO import StringIO
8
9from trac.core import *
10from trac.resource import Resource, ResourceNotFound
11from trac.env import IEnvironmentSetupParticipant
12from trac.db import Table, Column, Index
13from trac.util.translation import _
14from trac.util.compat import groupby
15
16class ResourcePropertyModel(object):
17    def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, **kwargs):
18        self.env = env
19        self.resource = Resource(resource).child('property', name)
20        self.resource_realm = self.resource.parent.realm
21        self.resource_id = self.resource.parent.id
22        self.name = name
23        self.value = default_value
24        self.data = kwargs
25        self.resource_property_system = None
26        self.changed = False
27               
28        if self.resource.id and fetch:
29            try:
30                self._fetch(self.resource.id, db)
31            except ResourceNotFound, e:
32                out = StringIO()
33                traceback.print_exc(file=out)
34                env.log.debug('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue()))
35
36    # internals
37    def _set_name(self, val):
38        self.resource.id = val
39    name = property(lambda self: self.resource.id, _set_name)
40
41    def _fetch(self, name, db=None):
42        raise TracError('Not implemented')
43   
44    def _load(self, **kargs):
45        raise TracError('Not implemented')
46   
47    def _handle_ta(self, db=None):
48        handle_ta = db is None
49        return handle_ta, handle_ta and self.env.get_db_cnx() or db
50   
51    # public methods
52    def insert(self, name=None, value=None, db=None):
53        raise TracError('Not implemented')
54
55    def update(self, db=None):
56        handle_ta, db = self._handle_ta(db)
57        self.delete(db)
58        self.insert(self.name, self.value, db)
59        if handle_ta:
60            db.commit()
61   
62    def delete(self, db=None):
63        handle_ta, db = self._handle_ta(db)
64        cursor = db.cursor()
65        cursor.execute("DELETE FROM resource_custom "
66                       "WHERE resource_realm=%s AND resource_id=%s "
67                       "AND name=%s", (self.resource_realm, self.resource_id, self.name))
68        if handle_ta:
69            db.commit()
70   
71    def get(self):
72        return self.value
73   
74    def set(self, value, db=None, pretend=False):
75        # no need to update if nothing changed
76        if self.value == value and not self.changed:
77            return 
78       
79        # updating value
80        old_value = self.value
81        self.value = value
82       
83        if pretend:
84            self.changed = True
85            return
86       
87        # update our changes
88        self.update(db)
89       
90        # notify change listeners
91        if self.resource_property_system:
92            for listener in self.resource_property_system.change_listeners:
93                listener.resource_property_changed(self.resource.parent, self.name, old_value, value)
94   
95    @classmethod
96    def select(cls, env, *args, **kwargs ):
97        """Returns all properties for a specific resource.
98
99        >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties
100        >>> select(env, type='status-report') # all properties with name='type' and value='status-report'.
101               
102        @param cls: Class
103        @param env: Environment
104        @param resource: Resource
105        @return: Generator
106        """
107        raise TracError("Not implemented.")
108   
109    @classmethod           
110    def delete_all(cls, env, resource, db=None):
111        """Removes all resource_custom records for a specific resource.
112       
113        @param cls: Class
114        @param env: Environment
115        @param resource: Resource
116        @param db: Db
117        @return: None
118        """
119        handle_ta = db is None
120        db = handle_ta and env.get_db_cnx() or db
121       
122        cursor = db.cursor()   
123        cursor.execute("DELETE FROM resource_custom "
124                       "WHERE resource_realm=%s AND resource_id=%s", (resource.realm, resource.id))
125        if handle_ta:
126            db.commit()
127   
128    @classmethod
129    def get_resources_with_properties(cls, env, limit=None, **kwargs):
130        """Returns resources with properties.
131       
132        >>> list(ResourcePropertyModel.get_resources_with_properties(env, prop1_name='prop_value1', prop2_name='prop_value2'))
133        [<Resource u'wiki:Page1'>, <Resource u'wiki:Page2'>]
134       
135        @param cls: Type
136        @param env: Environment
137        @return: Generator
138        """
139        db = env.get_db_cnx()
140        cursor = db.cursor()
141       
142        # dict to list attr_dict = { k1:v1, k2:v2, ... } -> [k1,v1,k2,v2..., len(attr_dict)]
143        kwargs_list = []
144        for k, v in kwargs.items():
145            kwargs_list.append(k.startswith('NOT_') and k[4:] or k)
146            kwargs_list.append(v)
147        kwargs_list.append(len(kwargs))
148       
149        def _get_condition(k,v):
150            return "name=%s AND value" + (k.startswith('NOT_') and 'NOT' or ' ') + " LIKE %s"
151       
152        cursor.execute("SELECT resource_realm, resource_id "
153                       "FROM resource_custom "
154                       "WHERE " + " OR ".join([ _get_condition(k,v) for k,v in kwargs.items()]) + " "
155                       "GROUP BY resource_realm, resource_id "
156                       "HAVING count(*)=%s "
157                       "ORDER BY resource_realm, resource_id DESC"
158                       +(limit and " LIMIT %s" or ""), kwargs_list+(limit and [limit] or []))
159       
160        for reasource_realm, resource_id in cursor:
161            yield Resource(reasource_realm, resource_id)
162       
163        cursor.close()
164           
165    @classmethod
166    def get_resource_properties(cls, env, resource=Resource(), resource_properties={}, **kwargs):
167        """Returns resource property model objects for resource.
168       
169        @param env: Environment
170        @param resource: Resource
171        @param resource_properties: dict
172        @return: Generator
173        """
174        db = env.get_db_cnx() or db
175        cursor = db.cursor()
176       
177        where_cond=(resource.realm and ["resource_realm=%s"] or [])
178        where_cond.extend(resource.id and ["resource_id=%s"] or [])
179        where_cond.extend(len(resource_properties.keys())>0 and 
180                    ["name IN (%s)"%(','.join(['%s' for name in resource_properties.keys()]))] or [] )
181                 
182        cursor.execute("SELECT id, resource_realm, resource_id, name, value, property_index"
183                       " FROM resource_custom"
184                            +(len(where_cond) and " WHERE "+" AND ".join(where_cond) or "")+
185                       " ORDER BY name, property_index",
186                       (resource.realm and [resource.realm] or [])+
187                       (resource.id and [resource.id] or [])+
188                       (len(resource_properties.keys())>0 and resource_properties.keys() or []))       
189       
190        def strip_resource_for_iterable(c):
191            for id, resource_realm, resource_id, name, value, property_index in c:
192                yield id, name, value, property_index
193       
194        kwargs['fetch'] = False
195        env.log.debug('get_resource_properties: start(resource=%r, resource_properties=%s)'%(resource, resource_properties))
196        for (resource_realm, resource_id), data_per_resource in groupby(cursor, lambda row: (row[1], row[2])):
197            for name, data in groupby(strip_resource_for_iterable(data_per_resource), lambda row: row[1]):
198                if name in resource_properties:
199                    rp_resource = (resource.realm and resource.id) and resource or Resource(resource_realm, resource_id)
200                    rp_model = resource_properties[name].get(rp_resource, env=env, **kwargs)
201                    rp_model._load(iterator = data)
202                    env.log.debug("get_resource_properties: Loaded resource property: %s, value=%s"%(rp_model.resource, rp_model.value))
203                    yield rp_model
204        env.log.debug('get_resource_properties: end(resource=%r, resource_properties=%s)'%(resource, resource_properties))           
205        cursor.close()
206       
207class StrResourcePropertyModel(ResourcePropertyModel):
208    def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, uniq_id=None, **kwargs):
209        self.uniq_id = uniq_id
210        super(StrResourcePropertyModel, self).__init__(env, resource, name=name, db=db, default_value=default_value, fetch=fetch, **kwargs)
211 
212    def _fetch(self, name, db=None):
213        handle_ta, db = self._handle_ta(db) 
214        self.name = name
215        cursor = db.cursor()
216        cursor.execute("SELECT id, name, value, property_index "
217                       "FROM resource_custom "
218                       "WHERE resource_realm=%s AND resource_id=%s AND name=%s "
219                       "ORDER BY property_index",
220                       (self.resource_realm, self.resource_id, name))
221        self._load(iterator = cursor)
222        self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value))
223        cursor.close()
224           
225    def _load(self, **kwargs):
226        if 'iterator' in kwargs:
227            for uniq_id, name, value, property_index in kwargs['iterator']:
228                self.uniq_id, self.value, self.index = uniq_id, value, property_index
229                return 
230           
231        self.uniq_id = kwargs.get('uniq_id', self.uniq_id)
232        self.value = kwargs.get('value', self.value)
233        self.index = kwargs.get('property_index') and int(kwargs.get('property_index')) or 0 
234
235    def insert(self, name=None, value=None, db=None):
236        handle_ta, db = self._handle_ta(db)
237        cursor = db.cursor()
238        cursor.execute("INSERT INTO resource_custom "
239                       "(resource_realm, resource_id, name, value) "
240                       "VALUES (%s,%s,%s,%s)",
241                       (self.resource_realm, self.resource_id, name, value))
242        self.uniq_id = db.get_last_id(cursor, 'resource_custom')
243        self.resource.id = self.name = name
244        self.value = value
245        self.env.log.info('New resource property: %s, value=%s', self.resource, self.value)
246        if handle_ta:
247            db.commit()
248                   
249    def update(self, db=None):
250        if self.uniq_id is None:
251            self.delete(db)
252            return self.insert(self.name, self.value, db)
253        handle_ta, db = self._handle_ta(db)
254        cursor = db.cursor()
255        cursor.execute("UPDATE resource_custom SET value=%s WHERE id=%s", (self.value, self.uniq_id))
256        if handle_ta:
257            db.commit()
258
259    def delete(self, db=None):
260        if self.uniq_id:
261            handle_ta, db = self._handle_ta(db)           
262            cursor = db.cursor()
263            cursor.execute("DELETE FROM resource_custom WHERE id=%s", (self.uniq_id,))
264            if handle_ta:
265                db.commit()
266        else:
267            super(StrResourcePropertyModel, self).delete(db=db)
268           
269    @classmethod
270    def select(cls, env, *args, **kwargs ):
271        """Returns all properties for a specific resource.
272        @attention: Not stable, will be changed/dropped soon.
273       
274        >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties
275               
276        @param cls: Class
277        @param env: Environment
278        @param resource: Resource
279        @return: Generator
280        """
281        resource = Resource()
282        if 'resource' in kwargs:
283             resource = kwargs['resource']
284       
285        db = env.get_db_cnx() 
286        cursor = db.cursor()
287        cursor.execute("SELECT id, name, value, property_index "
288                        "FROM resource_custom "
289                        "WHERE resource_realm=%s AND resource_id=%s "
290                        "ORDER BY name, property_index",
291                       (resource.realm, resource.id))
292       
293        for uniq_id, name, value, property_index in cursor:
294            yield StrResourcePropertyModel(env, resource, name=name, default_value=value, fetch=False, uniq_id=uniq_id)
295
296class ListResourcePropertyModel(ResourcePropertyModel):       
297    def _fetch(self, name, db=None):
298        handle_ta, db = self._handle_ta(db) 
299        self.name = name
300        cursor = db.cursor()
301        cursor.execute("SELECT id, name, value, property_index "
302                       "FROM resource_custom "
303                       "WHERE resource_realm=%s AND resource_id=%s AND name=%s "
304                       "ORDER BY property_index",
305                       (self.resource_realm, self.resource_id, name))
306        self._load(iterator=cursor)
307        self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value))
308        cursor.close()
309           
310    def _load(self, **kwargs):
311        if 'iterator' in kwargs:
312            self.value=[]
313            for uniq_id, name, value, property_index in kwargs['iterator']:
314                self.value.append(value)
315            return
316        raise ResourceNotFound(_("Property '%(name)s' does not exist.",
317                                     name=self.resource), _('Invalid property'))
318
319    def set(self, value, db=None, pretend=False):
320        if isinstance(value, basestring):
321            value=value.split(' ')
322        return super(ListResourcePropertyModel, self).set(value, db, pretend)
323
324    def insert(self, name=None, value=None, db=None):
325        handle_ta, db = self._handle_ta(db)
326       
327        cursor = db.cursor()
328        cursor.executemany("INSERT INTO resource_custom "
329                       "(resource_realm, resource_id, name, value, property_index) "
330                       "VALUES (%s,%s,%s,%s,%s)",
331                       [(self.resource_realm, self.resource_id, name, value_item, idx) 
332                                for idx, value_item in enumerate(value)])
333        self.env.log.info('New resource property: %s, value=%s', self.resource, self.value)
334        if handle_ta:
335            db.commit()
336           
337class ResourcePropertyModelProvider(Component):
338    implements(IEnvironmentSetupParticipant)
339
340    SCHEMA = [
341        Table('resource_custom', key='id')[
342              Column('id', auto_increment=True),
343              Column('resource_realm'),
344              Column('resource_id'),
345              Column('name'),
346              Column('value'),
347              Column('property_index', type='int'),
348              Index(['resource_realm', 'resource_id', 'name'])
349              ]
350        ]
351
352    # IEnvironmentSetupParticipant methods
353    def environment_created(self):
354        self._upgrade_db(self.env.get_db_cnx())
355
356    def environment_needs_upgrade(self, db):
357        cursor = db.cursor()
358        try:
359            cursor.execute("select count(*) from resource_custom")
360            cursor.fetchone()
361            return False
362        except:
363            db.rollback()
364            return True
365
366    def upgrade_environment(self, db):
367        self._upgrade_db(db)
368
369    def _upgrade_db(self, db):
370        try:
371            try:
372                from trac.db import DatabaseManager
373                db_backend, _ = DatabaseManager(self.env)._get_connector()
374            except ImportError:
375                db_backend = self.env.get_db_cnx()
376
377            cursor = db.cursor()
378            for table in self.SCHEMA:
379                for stmt in db_backend.to_sql(table):
380                    self.env.log.debug(stmt)
381                    cursor.execute(stmt)
382            db.commit()
383        except:
384            db.rollback()
385            raise
Note: See TracBrowser for help on using the repository browser.