1 # -----------------------------------------------------------------------
2 # Copyright (C) 2007 Georgia Public Library Service
3 # Bill Erickson <billserickson@gmail.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # -----------------------------------------------------------------------
16 from osrf.log import *
17 from osrf.json import *
18 from oils.utils.idl import IDLParser
19 from osrf.ses import ClientSession
20 from oils.const import *
23 ACTIONS = ['create', 'retrieve', 'batch_retrieve', 'update', 'delete', 'search']
25 class CSEditor(object):
27 Contains generated methods for accessing fieldmapper objects using the
30 <ret> = <instance>.<action>_<schema>_<table>(<args>)
32 * <instance> = CSEditor class instance
35 <args> = object to create
36 <ret> = the numeric ID of the newly created object
38 <args> = numeric ID of the object to retrieve
39 <ret> = object, instance of osrf.net_obj.NetworkObject
41 <args> = list of numeric ID's
42 <ret> = list of objects, instances of osrf.net_obj.NetworkObject
44 <args> = object to update
47 <args> = object to delete
50 <args> = a cstore-compatible search dict. e.g. {"id":1}.
51 See cstore docs for the full range of search options.
52 <ret> = a list of search results. For standard searches, this
53 will be a list of objects. idlist searches will return
55 * <schema> = the name of the schema that contains the table
56 * <table> = the name of the table
58 Each generated object has accessor methods corresponding to the fieldmapper
59 name attributes for a given field. The following example demonstrates how to
60 instantiate the CSEditor and a given table object, and how to invoke an
61 accessor method on that table object:
63 >>> import oils.utils.csedit
64 >>> import oils.utils.idl
65 >>> import osrf.system
66 >>> osrf.system.connect('/openils/conf/opensrf_core.xml', 'config.opensrf')
67 >>> oils.utils.idl.oilsParseIDL()
68 >>> oils.utils.csedit.oilsLoadCSEditor()
69 >>> editor = oils.utils.csedit.CSEditor()
70 >>> rec = editor.retrieve_biblio_record_entry(-1)
71 >>> print rec.tcn_value()
74 def __init__(self, **args):
76 Creates a new editor object.
78 Support keyword arguments:
79 authtoken - Authtoken string -- used to determine
80 the requestor if none is provided.
81 requestor - existing user (au) object. The requestor is
82 is the user performing the action. This is important
83 for permission checks, logging, etc.
84 connect - boolean. If true, a connect call is sent to the opensrf
85 service at session create time
86 xact - boolean. If true, a cstore transaction is created at
87 connect time. xact implies connect.
90 self.app = args.get('app', OILS_APP_CSTORE)
91 self.authtoken = args.get('authtoken', args.get('auth'))
92 self.requestor = args.get('requestor')
93 self.connect = args.get('connect')
94 self.xact = args.get('xact')
98 ''' Rolls back the existing transaction, disconnects our session,
99 and returns the last received event.
104 ''' Checks the authtoken against open-ils.auth and uses the
105 retrieved user as the requestor
110 # -------------------------------------------------------------------------
111 # Creates a session if one does not already exist. If necessary, connects
112 # to the remote service and starts a transaction
113 # -------------------------------------------------------------------------
114 def session(self, ses=None):
115 ''' Creates a session if one does not already exist. If necessary, connects
116 to the remote service and starts a transaction
118 if not self.__session:
119 self.__session = ClientSession(self.app)
121 if self.connect or self.xact:
122 self.log(log_debug,'connecting to ' + self.app)
123 self.__session.connect()
126 self.log(log_info, "starting new db transaction")
127 self.request(self.app + '.transaction.begin')
129 return self.__session
132 def log(self, func, string):
133 ''' Logs string with some meta info '''
136 if self.xact: s += "1|"
138 if self.requestor: s += str(self.requestor.id())
141 func("%s %s" % (s, string))
145 ''' Rolls back the existing db transaction '''
147 if self.__session and self.xact:
148 self.log(log_info, "rolling back db transaction")
149 self.request(self.app + '.transaction.rollback')
153 ''' Commits the existing db transaction and disconnects '''
155 if self.__session and self.xact:
156 self.log(log_info, "comitting db transaction")
157 self.request(self.app + '.transaction.commit')
161 def disconnect(self):
162 ''' Disconnects from the remote service '''
164 self.__session.disconnect()
165 self.__session = None
168 # -------------------------------------------------------------------------
170 # -------------------------------------------------------------------------
171 def request(self, method, params=[]):
173 # XXX improve param logging here
175 self.log(log_info, "request %s %s" % (method, unicode(params)))
177 if self.xact and self.session().state != OSRF_APP_SESSION_CONNECTED:
178 self.log(log_error, "csedit lost its connection!")
183 req = self.session().request2(method, params)
188 self.log(log_error, "request error: %s" % unicode(e))
194 # -------------------------------------------------------------------------
195 # Returns true if our requestor is allowed to perform the request action
196 # 'org' defaults to the requestors ws_ou
197 # -------------------------------------------------------------------------
198 def allowed(self, perm, org=None):
202 def runMethod(self, action, type, arg, options={}):
204 method = "%s.direct.%s.%s" % (self.app, type, action)
206 if options.get('idlist'):
207 method = method.replace('search', 'id_list')
208 del options['idlist']
210 if action == 'search':
213 if action == 'batch_retrieve':
214 method = method.replace('batch_retrieve', 'search')
219 if len(options.keys()):
220 params.append(options)
222 val = self.request( method, params )
226 def rawSearch(self, args):
227 method = "%s.json_query.atomic" % self.app
228 self.log(log_debug, "rawSearch args: %s" % unicode(args))
229 return self.request(method, [args])
231 def rawSearch2(self, hint, fields, where, from_=None):
233 from_ = {'%s' % hint : {}}
236 'select' : { '%s' % hint : fields },
238 'where' : { "+%s" % hint : where }
240 return self.rawSearch(args)
243 def fieldSearch(self, hint, fields, where):
244 return self.rawSearch2(hint, fields, where)
248 # -------------------------------------------------------------------------
249 # Creates a class method for each action on each type of fieldmapper object
250 # -------------------------------------------------------------------------
251 __editor_loaded = False
252 def oilsLoadCSEditor():
253 global __editor_loaded
256 __editor_loaded = True
258 obj = IDLParser.get_parser().IDLObject
260 for k, fm in obj.iteritems():
261 for action in ACTIONS:
263 fmname = fm.fieldmapper.replace('::', '_')
264 type = fm.fieldmapper.replace('::', '.')
265 name = "%s_%s" % (action, fmname)
267 s = 'def %s(self, arg, **options):\n' % name
268 s += '\treturn self.runMethod("%s", "%s", arg, dict(options))\n' % (action, type)
269 s += 'setattr(CSEditor, "%s", %s)' % (name, name)