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

Revision 498, 13.9 KB checked in by cbalan, 5 years ago (diff)

ResourceTools?: - Moved api/model under property.

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