class IEntityAdapter(BaseIDownloadAdapter): """ Action to download entity attributes. Add items in the global list 'RQL_DOWNLOAD_EXPORT_ENTITIES' to activate such an action. The rset will be exported in csv format. """ __select__ = BaseIDownloadAdapter.__select__ & is_instance( *RQL_DOWNLOAD_EXPORT_ENTITIES) __rset_type__ = u"ecsvexport" def rql(self, rql, parameter_name, identifier=None): """ Method that simply return the input rql. """ return rql, 1
class EmailAddressOneLineView(baseviews.OneLineView): __select__ = is_instance('EmailAddress') def entity_call(self, entity, **kwargs): if entity.reverse_primary_email: self.w(u'<b>') if entity.alias: self.w(u'%s <' % xml_escape(entity.alias)) self.w('<a href="%s">%s</a>' % (xml_escape( entity.absolute_url()), xml_escape(entity.display_address()))) if entity.alias: self.w(u'>\n') if entity.reverse_primary_email: self.w(u'</b>')
class PeopleBox(component.EntityCtxComponent): __regid__ = 'drh.123people_box' __select__ = component.EntityCtxComponent.__select__ & is_instance( 'Person') title = _('The url\'s Person on 123people ') order = 25 def render_body(self, w): firstname = self.entity.firstname surname = self.entity.surname url = 'http://www.123people.com/s/%s+%s/world' % (firstname, surname) label = '%s %s' % (firstname, surname) self.append(self.link(label, url)) self.render_items(w)
class CWSearchExpirationDateHook(hook.Hook): """ On startup, register a task to add an expiration date to each CWSearch. """ __regid__ = "rsetftp.search_add_expiration_hook" __select__ = hook.Hook.__select__ & is_instance("CWSearch") events = ("before_add_entity", ) def __call__(self): """ Method to execute the 'CWSearchExpirationDateHook' hook. """ if "expiration_date" not in self.entity.cw_edited: delay = self._cw.vreg.config["default_expiration_delay"] self.entity.cw_edited["expiration_date"] = ( datetime.date.today() + datetime.timedelta(delay))
class IBreadCrumbsAdapter(EntityAdapter): """adapters for entities which can be"located" on some path to display in the web ui """ __regid__ = 'IBreadCrumbs' __select__ = is_instance('Any', accept_none=False) def parent_entity(self): itree = self.entity.cw_adapt_to('ITree') if itree is not None: return itree.parent() return None def breadcrumbs(self, view=None, recurs=None): """return a list containing some: * tuple (url, label) * entity * simple label string defining path from a root to the current view the main view is given as argument so breadcrumbs may vary according to displayed view (may be None). When recursing on a parent entity, the `recurs` argument should be a set of already traversed nodes (infinite loop safety belt). """ parent = self.parent_entity() if parent is not None: if recurs: _recurs = recurs else: _recurs = set() if _recurs and parent.eid in _recurs: self.error('cycle in breadcrumbs for entity %s' % self.entity) return [] _recurs.add(parent.eid) adapter = parent.cw_adapt_to('IBreadCrumbs') path = adapter.breadcrumbs(view, _recurs) + [self.entity] else: path = [self.entity] if not recurs: if view is None: if 'vtitle' in self._cw.form: # embeding for instance path.append(self._cw.form['vtitle']) elif view.__regid__ != 'primary' and hasattr(view, 'title'): path.append(self._cw._(view.title)) return path
class TSImportAdapter(EntityAdapter): """ import from various sources, delegates actual job to various other adapters entry point typically triggered from creation/udpate hook """ __regid__ = 'TimeSeriesImporter' __select__ = is_instance('TimeSeries') def grok_data(self): """ self.data is something such as an excel file or CSV data or a pickled numpy array or an already processed binary. Ensure it's a pickle numpy array before storing object in db. If data seems to be already processed, return True, else return False. """ entity = self.entity try: filename = entity.data.filename.lower() except AttributeError: data = entity.data if isinstance(data, Binary): return True # if not isinstance(data, numpy.ndarray): # raise TypeError('data is neither a Binary nor a numpy array (%s)' % type(data)) numpy_array = data else: adapter = self._cw.vreg['adapters'].select_or_none( 'source_to_numpy_array', self._cw, entity=entity, filename=filename) if adapter is None: msg = self._cw._( 'Unsupported file type %s') % entity.data.filename raise ValidationError(entity.eid, {'data': msg}) numpy_array = adapter.to_numpy_array(entity.data, filename) if numpy_array.ndim != 1: raise ValidationError( entity.eid, {'data': _('data must be a 1-dimensional array')}) if numpy_array.size == 0: raise ValidationError( entity.eid, {'data': _('data must have at least one value')}) data = Binary() compressed_data = zlib.compress(pickle.dumps(numpy_array, protocol=2)) data.write(compressed_data) entity.cw_edited['data'] = data entity.array = numpy_array return False
class CWGroupsTable(tableview.EntityTableView): __regid__ = 'cw.groups-table' __select__ = is_instance('CWGroup') columns = ['group', 'nb_users'] layout_args = {'display_filter': 'top'} column_renderers = { 'group': tableview.MainEntityColRenderer(), 'nb_users': tableview.EntityTableColRenderer( header=_('num. users'), renderfunc=lambda w, x: w(str(x.num_users())), sortfunc=lambda x: x.num_users()), }
class SetupExcelPreferences(Hook): __regid__ = 'pylos.setup_excel_prefs' events = ('after_add_entity',) __select__ = Hook.__select__ & is_instance('CWUser') def __call__(self): self.debug('hook %s', self.__class__.__name__) try: self.entity.cw_set(format_preferences=self._cw.create_entity('ExcelPreferences')) except Exception as e: # while migrating from a pre 0.15 version, it can happen # that some users are fetched from LDAP and created # but at this moment the schema has not yet been updated # and this will fail self.warning('ExcelPreferences creation failed (%s)', e)
class PIWSSubjectStatistics(component.CtxComponent): """ Display a box containing links to statistics on the cw entities. """ __regid__ = "subject_statistics" context = "left" title = _("Statistics") order = 1 __select__ = is_instance("Subject") def render_body(self, w, **kwargs): """ Method to create the statistic box content. """ # Create a view to see the subject gender repartition in the db href = self._cw.build_url("view", vid="highcharts-basic-pie", rql="Any G WHERE S is Subject, S gender G", title="Subject genders") w(u'<div class="btn-toolbar">') w(u'<div class="btn-group-vertical btn-block">') w(u'<a class="btn btn-primary" href="{0}">'.format(href)) w(u'Subject gender repartition</a>') w(u'</div></div><br/>') # Create a view to see the subject handedness repartition in the db href = self._cw.build_url( "view", vid="highcharts-basic-pie", rql="Any H WHERE S is Subject, S handedness H", title="Subject handednesses") w(u'<div class="btn-toolbar">') w(u'<div class="btn-group-vertical btn-block">') w(u'<a class="btn btn-primary" href="{0}">'.format(href)) w(u'Subject handedness repartition</a>') w(u'</div></div><br/>') # Create a view to see the db subject status href = self._cw.build_url("view", vid="highcharts-relation-summary-view", rql="Any A WHERE A is Assessment", title="Insertion status", relations="subjects", subject_attr="timepoint", object_attr="identifier") w(u'<div class="btn-toolbar">') w(u'<div class="btn-group-vertical btn-block">') w(u'<a class="btn btn-primary" href="{0}">'.format(href)) w(u'Insertion status</a>') w(u'</div></div><br/>')
class FiredTransitionHook(WorkflowHook): """change related entity state and handle exit of subworkflow""" __regid__ = 'wffiretransition' __select__ = WorkflowHook.__select__ & is_instance('TrInfo') events = ('after_add_entity', ) def __call__(self): trinfo = self.entity rcache = trinfo.cw_attr_cache _change_state(self._cw, rcache['wf_info_for'], rcache['from_state'], rcache['to_state']) forentity = self._cw.entity_from_eid(rcache['wf_info_for']) iworkflowable = forentity.cw_adapt_to('IWorkflowable') assert iworkflowable.current_state.eid == rcache['to_state'] if iworkflowable.main_workflow.eid != iworkflowable.current_workflow.eid: _SubWorkflowExitOp(self._cw, foreid=forentity.eid, trinfo=trinfo)
class SCHIIRDORGroupsTable(EntityTableView): """ Display a table with the groups information to be managed. """ __regid__ = "shiirdor.groups-table" __select__ = is_instance("CWGroup") & match_user_groups("managers") columns = ["group", "nb_users"] column_renderers = { "group": MainEntityColRenderer(), "nb_users": EntityTableColRenderer( header=_('num. users'), renderfunc=lambda w, x: w(unicode(x.num_users())), sortfunc=lambda x: x.num_users()), }
class RefundSummaryComponent(ExpenseSummaryComponent): __regid__ = 'expense.summary' __select__ = component.EntityCtxComponent.__select__ & is_instance( 'Refund') title = _('Refund summary') def render_body(self, w): _ = self._cw._ entity = self.entity accounts = [act.view('oneline') for act in entity.paid_by_accounts()] self.field(_('account to refund'), ','.join(accounts), w) self.field(_('total'), entity.total, w) rset = self._cw.execute( 'Any E,ET,EC,EA WHERE X has_lines E, X eid %(x)s, ' 'E title ET, E currency EC, E amount EA', {'x': entity.eid}) self._cw.view('table', rset, w=w)
class BookmarkPrimaryView(primary.PrimaryView): __select__ = is_instance('Bookmark') def cell_call(self, row, col): """the primary view for bookmark entity""" entity = self.cw_rset.complete_entity(row, col) self.w(u' ') self.w(u"<span class='title'><b>") self.w(u"%s : %s" % (self._cw._('Bookmark'), xml_escape(entity.title))) self.w(u"</b></span>") self.w(u'<br/><br/><div class="content"><a href="%s">' % (xml_escape(entity.actual_url()))) self.w(u'</a>') self.w(u'<p>%s%s</p>' % (self._cw._('Used by:'), ', '.join( xml_escape(u.name()) for u in entity.bookmarked_by))) self.w(u'</div>')
class ExpenseLineFilesComponent(component.EntityCtxComponent): """display the list of files attached to the expenselines""" __regid__ = 'expenseline.attachments' __select__ = (component.EntityCtxComponent.__select__ & authenticated_user() & is_instance('Expense') & rql_condition('EXISTS(X has_lines Y, Y has_attachment F) ' 'OR EXISTS(X has_attachment F)')) title = _('has_attachment') def render_body(self, w): rset = self._cw.execute( '(Any F WHERE X has_attachment F, X eid %(x)s)' ' UNION ' '(Any F WHERE X has_lines Y, Y has_attachment F, X eid %(x)s)', dict(x=self.entity.eid)) self._cw.view('incontext', rset, 'null', w=w)
class DontRemoveOwnersGroupHook(IntegrityHook): """delete the composed of a composite relation when this relation is deleted """ __regid__ = 'checkownersgroup' __select__ = IntegrityHook.__select__ & is_instance('CWGroup') events = ('before_delete_entity', 'before_update_entity') def __call__(self): entity = self.entity if self.event == 'before_delete_entity' and entity.name == 'owners': raise validation_error(entity, {None: _("can't be deleted")}) elif self.event == 'before_update_entity' \ and 'name' in entity.cw_edited: oldname, newname = entity.cw_edited.oldnewvalue('name') if oldname == 'owners' and newname != oldname: raise validation_error( entity, {('name', 'subject'): _("can't be changed")})
class ExcelPreferencesCoherency(Hook): __regid__ = 'pylos.excel_prefs_coherency' events = ('after_add_entity', 'after_update_entity') __select__ = Hook.__select__ & is_instance('ExcelPreferences') def __call__(self): self.debug('hook %s', self.__class__.__name__) entity = self.entity errors = {} if entity.thousands_separator == entity.decimal_separator: msg = self._cw._('thousands separator must not be the same as decimal separator') errors['thousands_separator'] =msg if entity.csv_separator == entity.decimal_separator: msg = self._cw._('column separator must not be the same as decimal separator') errors['csv_separator'] =msg if errors: raise ValidationError(entity.eid, errors)
class SourceUpdatedHook(SourceHook): __regid__ = 'cw.sources.configupdate' __select__ = SourceHook.__select__ & is_instance('CWSource') events = ('before_update_entity',) def __call__(self): if 'name' in self.entity.cw_edited: oldname, newname = self.entity.cw_edited.oldnewvalue('name') if oldname == 'system': msg = _("You cannot rename the system source") raise validation_error(self.entity, {('name', 'subject'): msg}) source = self.get_source(self.entity) if 'url' in self.entity.cw_edited: source.check_urls(self.entity) if 'config' in self.entity.cw_edited: source.check_config(self.entity)
class CWSourceSyncView(EntityView): __regid__ = 'cw.source-sync' __select__ = (match_user_groups('managers') & one_line_rset() & is_instance('CWSource') & score_entity(lambda x: x.name != 'system')) title = _('synchronize') def entity_call(self, entity): import_log_eid = self._cw.call_service('source-sync', source_eid=entity.eid) msg = self._cw._( 'Synchronization has been requested, refresh this page in a few ' 'minutes.') import_log = self._cw.entity_from_eid(import_log_eid) url = import_log.absolute_url(__message=msg) raise Redirect(url)
class NDTSCSVToNumpyArray(CSVImportMixin, EntityAdapter): __regid__ = 'source_to_numpy_array' __select__ = is_instance('NonPeriodicTimeSeries') & (filename_ext('.csv') | filename_ext('.txt')) def to_numpy_array(self, file, filename, dialect=None, has_header=False): if dialect is None: dialect, has_header = self.snif_csv_dialect(file) else: assert dialect in csv.list_dialects() reader = csv.reader(file, dialect) if has_header: next(reader) series = [] tstamps = [] # TODO: check granularity if we have a date column prefs = self._cw.user.format_preferences[0] dec_sep = prefs.decimal_separator th_sep = prefs.thousands_separator or '' cal = self.entity.calendar for line, values in enumerate(reader): if len(values) != 2: raise ValueError( 'Expecting exactly 2 columns (timestamp, value), found %s in %s' % (len(values), filename)) try: strval = values[1].replace(th_sep, '').replace(dec_sep, '.') val = float(strval) except ValueError: if line == 0 and not has_header: self.debug('error while parsing first line of %s', filename) continue # assume there was a header else: raise ValueError( 'Invalid data type for value %s on line %s of %s' % (values[-1], reader.line_num, filename)) try: tstamp_datetime = self._cw.parse_datetime(values[0]) tstamp = cal.datetime_to_timestamp(tstamp_datetime) except ValueError: raise series.append(val) tstamps.append(tstamp) self.entity.cw_attr_cache['timestamps'] = numpy.array(tstamps) return numpy.array(series, dtype=self.entity.dtype)
class INotifiableAdapter(view.EntityAdapter): __regid__ = 'INotifiable' __select__ = is_instance('Any') def notification_references(self, view): """used to control References field of email send on notification for this entity. `view` is the notification view. Should return a list of eids which can be used to generate message identifiers of previously sent email(s) """ itree = self.entity.cw_adapt_to('ITree') if itree is not None: return itree.path()[:-1] if view.msgid_timestamp: return (self.entity.eid, ) return ()
class CompteMainTab(PrimaryTab): __select__ = primary.PrimaryView.__select__ & is_instance('Compte') title = _('Compte') def render_entity_relations(self, entity): super(CompteMainTab, self).render_entity_relations(entity) self.w(h2(_('Commanditaires'))) rset = self._cw.execute('Any P, COUNT(I1) GROUPBY P ORDERBY 2 DESC ' 'WHERE C eid %(eid)s, ' 'T compte C, ' 'T intervenants I1, ' 'I1 intervenant P, P identite A,' 'I1 commandement %(true)s', {'eid': entity.eid, 'true': True}) self.wview('table', rset, 'null', headers=('Personne', 'nb interventions')) self.w(h2(_('Destinataires'))) rset = self._cw.execute('Any P, COUNT(I1) GROUPBY P ORDERBY 2 DESC ' 'WHERE C eid %(eid)s, ' 'T compte C, ' 'T destinataires I1?, ' 'I1 destinataire P, P identite A', {'eid': entity.eid}) self.wview('table', rset, 'null', headers=('Personne', 'nb attributions')) self.w(h2(_('Artisans'))) rset = self._cw.execute('Any P, COUNT(I1) GROUPBY P ORDERBY 2 DESC ' 'WHERE C eid %(eid)s, ' 'T compte C, ' 'T travaux I1?, ' 'I1 artisan P, P identite A', {'eid': entity.eid}) self.wview('table', rset, 'null', headers=('Personne', 'nb travaux')) self.w(h2(_('Vendeurs'))) rset = self._cw.execute('Any P, COUNT(I1) GROUPBY P ORDERBY 2 DESC ' 'WHERE C eid %(eid)s, ' 'T compte C, ' 'T vendeurs I1?, ' 'I1 vendeur P, P identite A', {'eid': entity.eid}) self.wview('table', rset, 'null', headers=('Personne', 'nb ventes')) self.w(h2(_('Coursier'))) rset = self._cw.execute('Any P, COUNT(I1) GROUPBY P ORDERBY 2 DESC ' 'WHERE C eid %(eid)s, ' 'T compte C, ' 'T intervenants I1, ' 'I1 intervenant P, P identite A, ' 'I1 pris %(true)s OR I1 payeur %(true)s OR I1 delivre_a %(true)s OR I1 par_la_main %(true)s OR I1 relation_de %(true)s', {'eid': entity.eid, 'true': True}) self.wview('table', rset, 'null', headers=('Personne', 'nb interventions'))
class UploadFileHook(hook.Hook): """ An upload file entity is created/updated, store a fingerprint of binary data fields. """ __regid__ = "rql_upload.upload" __select__ = hook.Hook.__select__ & is_instance("UploadFile") events = ("before_add_entity", "before_update_entity") order = -1 # should be run before other hooks def __call__(self): """ If a 'data' field is uploaded, compute the associated fingerprint. """ if "data" in self.entity.cw_edited: self.entity.set_format_and_encoding() data = self.entity.cw_edited["data"] if data is not None: data = self.entity.compute_sha1hex(data.getvalue()) self.entity.cw_edited["data_sha1hex"] = data
class IDownloadableUser(EntityAdapter): __regid__ = 'IDownloadable' __select__ = is_instance('CWUser') def download_content_type(self): """return MIME type of the downloadable content""" return 'text/plain' def download_encoding(self): """return encoding of the downloadable content""" return 'ascii' def download_file_name(self): """return file name of the downloadable content""" return self.entity.name() + '.txt' def download_data(self): return b'Babar is not dead!'
class PersonneRelationsView(dotgraphview.DotGraphView): __regid__ = 'personne_relations' __select__ = is_instance('Personne') title = 'Personne : relations' backend_kwargs = { 'ratio': 'auto', 'additionnal_param': { #'overlap': 'scale', 'rankdir': 'LR' }, 'renderer': 'dot', } def build_visitor(self, entity): return PersonneRelationVisitor(self._cw, [entity]) def build_dotpropshandler(self): return PersonnePropsHandler(self._cw)
class CWSearchFuseMount(hook.Hook): """ Class that start/update a process specific to a user that mount his CWSearch entities. """ __regid__ = "rqldownload.fuse_mount_hook" __select__ = hook.Hook.__select__ & is_instance("CWSearch") events = ("after_add_entity", ) def __call__(self): """ Method that start/update the user specific process. """ # Check if fuse virtual directory have to be mounted use_fuse = self._cw.vreg.config["start_user_fuse"] if use_fuse: # Update/Create action PostCommitFuseOperation( self._cw, _cw=self._cw, entity=self.entity)
class OutOfContextAssessmentView(BaseOutOfContextView): """ Assessment secondary rendering. """ __select__ = EntityView.__select__ & is_instance("Assessment") def entity_description(self, entity): """ Generate a dictionary with the Assessment description. """ center = entity.center[0] subjects = entity.subjects desc = {} desc["Acquisition center"] = center.name if len(subjects) == 1: subject = subjects[0] desc["Gender"] = subject.gender desc["Handedness"] = subject.handedness desc["Age"] = entity.age_of_subject return desc
class PIWSFilePrimaryView(PIWSPrimaryView): """ Specific view for File entities where binary content has to be displayed. """ __select__ = PIWSPrimaryView.__select__ & is_instance( "File", "RestrictedFile") allowed_relations = ["subject", "object"] def render_entity_attributes(self, entity, separator=";"): """ Renders all attributes and relations in the 'attributes' section. Unwrap binary field. Parameters ---------- separator: str (optional, default ';') the CSV cell separator. """ super(PIWSPrimaryView, self).render_entity_attributes(entity) if "json" in entity.data_format: data = json.loads(entity.data.getvalue()) data = unicode(json.dumps(data, indent=4)) elif "comma-separated-values" in entity.data_format: rset = entity.data.getvalue().split("\n") labels = rset[0].split(separator) records = [] for index, line in enumerate(rset[1:]): elements = line.split(separator) if len(elements) == len(labels): elements.insert(0, str(index)) records.append(elements) self.wview("jtable-hugetable-clientside", None, "null", labels=labels, records=records, csv_export=True, title="", elts_to_sort="ID") return else: data = unicode(entity.data.getvalue()) self.w(data.replace("\n", "<br/>").replace(" ", " "))
class TabPersonneRattachement(tabs.EntityRelationView): __regid__ = 'tab_personne_rattachement' __select__ = one_line_rset( ) & tabs.EntityRelationView.__select__ & is_instance('Personne') title = None rtype = 'rattache_a' role = 'object' def cell_call(self, row, col): entity = self.cw_rset.get_entity(row, col) subst = {'eid': entity.eid} rql = 'Any O, P ORDERBY N WHERE O personne P, O rattache_a X, X eid %(eid)s, P identite N' rset = self._cw.execute(rql, subst) self.wview( 'table', rset, 'null', title=_('Rattachements'), )
class OneLineSelectableView(EntityView): """custom oneline view used by company / division treeview""" __regid__ = 'oneline-selectable' __select__ = is_instance('Company') & match_kwargs('onscreen') def cell_call(self, row, col, onscreen): entity = self.cw_rset.get_entity(row, col) if entity.eid == onscreen: self.w(u'<span class="currentCompany">%s</span>' % xml_escape(entity.view('textincontext'))) else: if entity.headquarters: tooltip = xml_escape(entity.headquarters[0].dc_long_title()) else: tooltip = u'' self.w(u'<a href="%s" title="%s">%s</a>' % (xml_escape(entity.absolute_url()), xml_escape(tooltip), xml_escape(entity.dc_title())))
class CWETypePermTab(SecurityViewMixIn, EntityView): __regid__ = 'cwetype-permissions' __select__ = is_instance('CWEType') & authenticated_user() def cell_call(self, row, col): entity = self.cw_rset.get_entity(row, col) eschema = self._cw.vreg.schema.eschema(entity.name) self.w(u'<h4>%s</h4>' % self._cw._('This entity type permissions:')) self.permissions_table(eschema) self.w(u'<div style="margin: 0px 1.5em">') self.w(u'<h4>%s</h4>' % self._cw._('Attributes permissions:')) for attr, etype in eschema.attribute_definitions(): if attr not in META_RTYPES: rdef = eschema.rdef(attr) attrtype = str(rdef.rtype) self.w(u'<h4 class="schema">%s (%s)</h4> ' % (attrtype, self._cw._(attrtype))) self.permissions_table(rdef) self.w(u'</div>')