def update(self): """Called before the listings renders """ super(PatientsView, self).update() # Render the Add button. We need to do this here because patients live # inside site.patients folder self.context_actions = {} patients = api.get_portal().patients if security.check_permission(AddPatient, patients): self.context_actions = { _("Add"): { "url": "createObject?type_name=Patient", "icon": "++resource++bika.lims.images/add.png"} } # If the current user is a client contact, display those patients that # belong to same client or that do not belong to any client client = api.get_current_client() if client: query = dict(client_uid=[api.get_uid(client), "-1"]) # We add UID "-1" to also include Patients w/o Client assigned self.contentFilter.update(query) for rv in self.review_states: rv["contentFilter"].update(query) # If the current context is a Client, remove the title column if IClient.providedBy(self.context): self.remove_column('getPrimaryReferrerTitle')
def Vocabulary_SampleType(self): vocabulary = CatalogVocabulary(self) vocabulary.catalog = 'bika_setup_catalog' folders = [self.bika_setup.bika_sampletypes] if IClient.providedBy(self.aq_parent): folders.append(self.aq_parent) return vocabulary(allow_blank=True, portal_type='SampleType')
def folderitems(self, **kwargs): items = super(ARImportsView, self).folderitems() for x in range(len(items)): if 'obj' not in items[x]: continue obj = items[x]['obj'] items[x]['Title'] = obj.title_or_id() if items[x]['review_state'] == 'invalid': items[x]['replace']['Title'] = "<a href='%s/edit'>%s</a>" % ( obj.absolute_url(), items[x]['Title']) else: items[x]['replace']['Title'] = "<a href='%s/view'>%s</a>" % ( obj.absolute_url(), items[x]['Title']) items[x]['Creator'] = obj.Creator() items[x]['Filename'] = obj.getFilename() parent = obj.aq_parent items[x]['Client'] = parent if IClient.providedBy(parent) else '' items[x]['replace']['Client'] = "<a href='%s'>%s</a>" % ( parent.absolute_url(), parent.Title()) items[x]['DateCreated'] = ulocalized_time( obj.created(), long_format=True, time_only=False, context=obj) date = getTransitionDate(obj, 'validate') items[x]['DateValidated'] = date if date else '' date = getTransitionDate(obj, 'import') items[x]['DateImported'] = date if date else '' return items
def get_client_uid(self): instance = self.context.aq_parent while not IPloneSiteRoot.providedBy(instance): if IClient.providedBy(instance): return instance.UID() if IBatch.providedBy(instance): client = instance.getClient() if client: return client.UID()
def getClient(self): """Retrieves the Client for which the current Batch is attached to Tries to retrieve the Client from the Schema property, but if not found, searches for linked ARs and retrieve the Client from the first one. If the Batch has no client, returns None. """ client = self.Schema().getField('Client').get(self) if client: return client client = self.aq_parent if IClient.providedBy(client): return client
def getClient(self): """Returns the client the Doctor is assigned to, if any """ # The schema's field PrimaryReferrer is only used to allow the user to # assign the doctor to a client in edit form. The entered value is used # in ObjectModifiedEventHandler to move the doctor to the Client's # folder, so the value stored in the Schema's is not used anymore # See https://github.com/senaite/senaite.core/pull/152 client = self.aq_parent if IClient.providedBy(client): return client return None
def getClient(self): """ Retrieves the Client for which the current Batch is attached to Tries to retrieve the Client from the Schema property, but if not found, searches for linked ARs and retrieve the Client from the first one. If the Batch has no client, returns None. """ client = self.Schema().getField('Client').get(self) if client: return client client = self.aq_parent if IClient.providedBy(client): return client
def __call__(self): plone.protect.CheckAuthenticator(self.request) curr_user = api.get_current_user() contact = api.get_user_contact(curr_user, contact_types=['Contact']) parent = contact and contact.getParent() or None if parent and not IClient.providedBy(parent): parent = None ret = {'ClientTitle': parent and parent.Title() or '', 'ClientID': parent and parent.getClientID() or '', 'ClientSysID': parent and parent.id or '', 'ClientUID': parent and parent.UID() or '',} return json.dumps(ret)
def _linkUser(self, user): """Set the UID of the current Contact in the User properties and update all relevant own properties. """ KEY = "linked_contact_uid" username = user.getId() contact = self.getContactByUsername(username) # User is linked to another contact (fix in UI) if contact and contact.UID() != self.UID(): raise ValueError( "User '{}' is already linked to Contact '{}'".format( username, contact.Title())) # User is linked to multiple other contacts (fix in Data) if isinstance(contact, list): raise ValueError( "User '{}' is linked to multiple Contacts: '{}'".format( username, ",".join(map(lambda x: x.Title(), contact)))) # XXX: Does it make sense to "remember" the UID as a User property? tool = user.getTool() try: user.getProperty(KEY) except ValueError: logger.info("Adding User property {}".format(KEY)) tool.manage_addProperty(KEY, "", "string") # Set the UID as a User Property uid = self.UID() user.setMemberProperties({KEY: uid}) logger.info("Linked Contact UID {} to User {}".format( user.getProperty(KEY), username)) # Set the Username self.setUsername(user.getId()) # Update the Email address from the user self.setEmailAddress(user.getProperty("email")) # somehow the `getUsername` index gets out of sync self.reindexObject() # N.B. Local owner role and client group applies only to client # contacts, but not lab contacts. if IClient.providedBy(self.aq_parent): # Grant local Owner role self._addLocalOwnerRole(username) # Add user to "Clients" group self._addUserToGroup(username, group="Clients") return True
def getClient(self): """Retrieves the Client the current Batch is assigned to """ # We override here getClient from ClientAwareMixin because te schema's # field Client is only used to allow the user to assign the batch to a # client in edit form. The entered value is used in # ObjectModifiedEventHandler to move the batch to the Client's folder, # so the value stored in the Schema's is not used anymore # See https://github.com/senaite/senaite.core/pull/1450 client = self.aq_parent if IClient.providedBy(client): return client return None
def __call__(self, context, mode, field, default): state = default if default else 'hidden' fieldName = field.getName() if fieldName != 'Client': return state parent = self.context.aq_parent if IBatch.providedBy(parent): if parent.getClient(): return 'hidden' if IClient.providedBy(parent): return 'hidden' return state
def get_client(obj): """Returns the client the object passed-in belongs to, if any This walks the acquisition chain up until we find something which provides either IClient or IClientAwareMixin """ for obj in chain(obj): if IClient.providedBy(obj): return obj elif IClientAwareMixin.providedBy(obj): # ClientAwareMixin can return a Client, even if there is no client # in the acquisition chain return obj.getClient() return None
def get_client_from_query(self, query, purge=False): """Resolves the client from the query passed-in """ keys = ["getPrimaryReferrerUID", "getClientUID", "getParentUID", "UID"] for key in keys: uid = query.get(key) if not api.is_uid(uid) or uid == "0": continue client = api.get_object_by_uid(uid) if IClient.providedBy(client): if purge: # Remove the key:value from the query del (query[key]) return client return None
def try_share_unshare(obj): """Tries to share or unshare the object based on the type of its client """ client = resolve_client(obj) if not IClient.providedBy(client): # This object does not (yet) have a client assigned, do nothing return if is_internal_client(client): # Try to share the obj. Note this transition will only take place if # the guard for "share" evaluates to True. doActionFor(obj, "share") else: # Try to unshare the obj. Note this transition will only take place # if the guard for "share" evaluates to True. doActionFor(obj, "unshare")
def get_current_client(self, default=None): """Returns the client the current user belongs to """ user = api.get_current_user() roles = user.getRoles() if 'Client' not in roles: return default contact = api.get_user_contact(user) if not contact or ILabContact.providedBy(contact): return default client = api.get_parent(contact) if not client or not IClient.providedBy(client): return default return client
def get_obj_title(self, obj): """Returns the title of the object to be displayed as breadcrumb """ if not api.is_object(obj): # Some objects (e.g. portal_registry) are not supported return obj.Title() title = api.get_title(obj) if IClient.providedBy(obj.aq_parent): # Objects from inside Client folder are always stored directly, w/o # subfolders, making it difficult for user to know if what is # looking at is a Sample, a Batch or a Contact. Append the name of # the portal type pt_title = self.get_portal_type_title(obj) if pt_title: title = "{} ({})".format(title, _(pt_title)) return title
def get_client(self, schema, fieldnames): """All Client Contact fields must be restricted to show only relevant Contacts. During creation the batch can be in some weird states and is located inside some odd contexs (TempFolder, PortalFactory), so some wiggling required. We also can't use schema field accessors directly, as it causes recursion. """ client = None # If heirarchy does not exist we're in early creation; skip body. if hasattr(self, 'context') and hasattr(self.context, 'aq_parent'): parent = self.context.aq_parent while not IPloneSiteRoot.providedBy(parent): if IClient.providedBy(parent): client = parent break parent = parent.aq_parent return client
def isVisible(self, field, mode="view", default="visible"): if mode == "add": parent = self.context.aq_parent if IClient.providedBy(parent): # Note we return "hidden" here instead of "invisible": we want # the field to be auto-filled and processed on submit return "hidden" if IBatch.providedBy(parent) and parent.getClient(): # The Batch has a Client assigned already! # Note we can have Batches without a client assigned return "hidden" elif mode == "edit": # This is already managed by wf permission, but is **never** a good # idea to allow the user to change the Client from an AR (basically # because otherwise, we'd need to move the object from one client # folder to another!). return "invisible" return default
def getClient(self): """Returns the Client from the Batch passed-in, if any """ parent = self.aq_parent if IClient.providedBy(parent): # The Batch belongs to an External Client return parent elif IPatient.providedBy(parent): # The Batch belongs to a Patient return parent.getClient() parent = self.getField("Client").get(self) if parent: # The Batch belongs to an Internal Client return parent # The Batch belongs to the laboratory (no Client assigned) return None
def _unlinkUser(self): """Remove the UID of the current Contact in the User properties and update all relevant own properties. """ logger.warn("MONKEY PATCHING __unlinkUser") KEY = "linked_contact_uid" # Nothing to do if no user is linked if not self.hasUser(): return False user = self.getUser() username = user.getId() # Unset the UID from the User Property user.setMemberProperties({KEY: ""}) logger.info("Unlinked Contact UID from User {}".format( user.getProperty(KEY, ""))) # Unset the Username self.setUsername(None) # Unset the Email self.setEmailAddress(None) # somehow the `getUsername` index gets out of sync self.reindexObject() # N.B. Local owner role and client group applies only to client # contacts, but not lab contacts. if IClient.providedBy(self.aq_parent): # Revoke local Owner role self._delLocalOwnerRole(username) # Remove user from "Clients" group self._delUserFromGroup(username, group="Clients") # SENAITE.HEALTH-specific! # Remove user from "InternalClients" group if is_internal_client(self.aq_parent): self._delUserFromGroup(username, group="InternalClients") return True
def resolve_client(obj, field_name=None): """Tries to resolve the client for the given obj """ if not field_name: field_name = "Client" if IPatient.providedBy(obj) or IDoctor.providedBy(obj): field_name = "PrimaryReferrer" # Try to get the client directly from the field client = obj.getField(field_name).get(obj) if client and IClient.providedBy(client): return client # Maybe the object is being created if obj.isTemporary(): parent = obj.getFolderWhenPortalFactory() else: parent = api.get_parent(obj) # Get the Client from the acquisition chain, if any return get_client_from_chain(parent)
def isVisible(self, field, mode="view", default="visible"): """Renders the Client field as hidden if the current mode is "edit" and the container is either a Patient or a Client """ if mode == "edit": container = self.context.aq_parent # If the Batch is created or edited inside a Client, Patient or # Doctor make Client field to be rendered, but hidden to prevent # the error message "Patient is required, please correct". if IPatient.providedBy(container): return "readonly" elif IClient.providedBy(container): return "readonly" elif IDoctor.providedBy(container): # Doctor can be assigned to a Client or not! if container.getClient(): return "readonly" return default
def __call__(self): mtool = getToolByName(self.context, 'portal_membership') can_add_doctors = mtool.checkPermission(AddDoctor, self.context) if can_add_doctors: add_doctors_url = '{}/doctors/createObject?type_name=Doctor' \ .format(self.portal_url) self.context_actions[_('Add')] = { 'url': add_doctors_url, 'icon': '++resource++bika.lims.images/add.png' } if mtool.checkPermission(ManageDoctors, self.context): self.review_states[0]['transitions'].append({'id':'deactivate'}) self.review_states.append( {'id':'inactive', 'title': _('Dormant'), 'contentFilter': {'is_active': False}, 'transitions': [{'id':'activate'}, ], 'columns': self.columns.keys()}) self.review_states.append( {'id':'all', 'title': _('All'), 'contentFilter':{}, 'transitions':[{'id':'empty'}], 'columns': self.columns.keys()}) stat = self.request.get("%s_review_state"%self.form_id, 'default') self.show_select_column = stat != 'all' # If the current context is a Client, filter Doctors by Client UID if IClient.providedBy(self.context): client_uid = api.get_uid(self.context) self.contentFilter['getPrimaryReferrerUID'] = client_uid # If the current user is a client contact, do not display the doctors # assigned to other clients elif self.get_user_client_uid(): client_uid = self.get_user_client_uid() self.contentFilter['getPrimaryReferrerUID'] = [client_uid, None] return super(DoctorsView, self).__call__()
def __call__(self): mtool = getToolByName(self.context, 'portal_membership') can_add_doctors = mtool.checkPermission(AddDoctor, self.context) if can_add_doctors: add_doctors_url = '{}/doctors/createObject?type_name=Doctor' \ .format(self.portal_url) self.context_actions[_('Add')] = { 'url': add_doctors_url, 'icon': '++resource++bika.lims.images/add.png' } if mtool.checkPermission(ManageDoctors, self.context): self.review_states[0]['transitions'].append({'id':'deactivate'}) self.review_states.append( {'id':'inactive', 'title': _('Dormant'), 'contentFilter': {'inactive_state': 'inactive'}, 'transitions': [{'id':'activate'}, ], 'columns': self.columns.keys()}) self.review_states.append( {'id':'all', 'title': _('All'), 'contentFilter':{}, 'transitions':[{'id':'empty'}], 'columns': self.columns.keys()}) stat = self.request.get("%s_review_state"%self.form_id, 'default') self.show_select_column = stat != 'all' # If the current context is a Client, filter Doctors by Client UID if IClient.providedBy(self.context): client_uid = api.get_uid(self.context) self.contentFilter['getPrimaryReferrerUID'] = client_uid # If the current user is a client contact, do not display the doctors # assigned to other clients elif self.get_user_client_uid(): client_uid = self.get_user_client_uid() self.contentFilter['getPrimaryReferrerUID'] = [client_uid, None] return super(DoctorsView, self).__call__()
def __call__(self, context): if IClient.providedBy(context): return context # Try with client object explicitly defined in request client = self.get_object_from_request_field("Client") if client: return client # Try to get the client from selected Patient patient = PatientDefaultFieldValue(self.request)(context) client = patient and patient.getPrimaryReferrer() or None if client: return client # Try to get the client from selected Doctor doctor = DoctorDefaultFieldValue(self.request)(context) client = doctor and doctor.getPrimaryReferrer() or None if client: return client return None
def get_user_client(user_or_contact): """Returns the client of the contact of a Plone user If the user passed in has no contact or does not belong to any client, returns None. :param: Plone user or contact :returns: Client the contact of the Plone user belongs to """ if not user_or_contact or ILabContact.providedBy(user_or_contact): # Lab contacts cannot belong to a client return None if not IContact.providedBy(user_or_contact): contact = get_user_contact(user_or_contact, contact_types=['Contact']) if IContact.providedBy(contact): return get_user_client(contact) return None client = get_parent(user_or_contact) if client and IClient.providedBy(client): return client return None
def is_client_context(self): """Returns whether the batch listing is displayed in IClient context or if the current user is a client contact """ return api.get_current_client() or IClient.providedBy(self.context)
def filter_client_lookups(self, schema, client): if IClient.providedBy(client): ids = [c.getId() for c in client.objectValues('Contact')] for fn in ["Contact", "CCContact", "InvoiceContact"]: if fn in schema: schema[fn].widget.base_query['id'] = ids
def __init__(self, context, request): super(ARImportsView, self).__init__(context, request) request.set('disable_plone.rightcolumn', 1) alsoProvides(request, IContentListing) self.catalog = "portal_catalog" self.contentFilter = { 'portal_type': 'ARImport', 'cancellation_state': 'active', 'sort_on': 'sortable_title', } self.context_actions = {} if IClient.providedBy(self.context): self.context_actions = { _('AR Import'): { 'url': 'arimport_add', 'icon': '++resource++bika.lims.images/add.png' } } self.show_sort_column = False self.show_select_row = False self.show_select_column = False self.pagesize = 50 self.form_id = "arimports" self.icon = \ self.portal_url + "/++resource++bika.lims.images/arimport_big.png" self.title = self.context.translate(_("Analysis Request Imports")) self.description = "" self.columns = { 'Title': { 'title': _('Title') }, 'Client': { 'title': _('Client') }, 'Filename': { 'title': _('Filename') }, 'Creator': { 'title': _('Date Created') }, 'DateCreated': { 'title': _('Date Created') }, 'DateValidated': { 'title': _('Date Validated') }, 'DateImported': { 'title': _('Date Imported') }, 'state_title': { 'title': _('State') }, } self.review_states = [ { 'id': 'default', 'title': _('Pending'), 'contentFilter': { 'review_state': ['invalid', 'valid'] }, 'columns': [ 'Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title' ] }, { 'id': 'imported', 'title': _('Imported'), 'contentFilter': { 'review_state': 'imported' }, 'columns': [ 'Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title' ] }, { 'id': 'cancelled', 'title': _('Cancelled'), 'contentFilter': { 'review_state': ['initial', 'invalid', 'valid', 'imported'], 'cancellation_state': 'cancelled' }, 'columns': [ 'Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title' ] }, ]
def __init__(self, context, request): super(ARImportsView, self).__init__(context, request) request.set('disable_plone.rightcolumn', 1) alsoProvides(request, IContentListing) self.catalog = "portal_catalog" self.contentFilter = { 'portal_type': 'ARImport', 'cancellation_state': 'active', 'sort_on': 'sortable_title', } self.context_actions = {} if IClient.providedBy(self.context): self.context_actions = { _('AR Import'): { 'url': 'arimport_add', 'icon': '++resource++bika.lims.images/add.png'}} self.show_sort_column = False self.show_select_row = False self.show_select_column = False self.pagesize = 50 self.form_id = "arimports" self.icon = \ self.portal_url + "/++resource++bika.lims.images/arimport_big.png" self.title = self.context.translate(_("Analysis Request Imports")) self.description = "" self.columns = { 'Title': {'title': _('Title')}, 'Client': {'title': _('Client')}, 'Filename': {'title': _('Filename')}, 'Creator': {'title': _('Date Created')}, 'DateCreated': {'title': _('Date Created')}, 'DateValidated': {'title': _('Date Validated')}, 'DateImported': {'title': _('Date Imported')}, 'state_title': {'title': _('State')}, } self.review_states = [ {'id': 'default', 'title': _('Pending'), 'contentFilter': {'review_state': ['invalid', 'valid']}, 'columns': ['Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title']}, {'id': 'imported', 'title': _('Imported'), 'contentFilter': {'review_state': 'imported'}, 'columns': ['Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title']}, {'id': 'cancelled', 'title': _('Cancelled'), 'contentFilter': { 'review_state': ['initial', 'invalid', 'valid', 'imported'], 'cancellation_state': 'cancelled' }, 'columns': ['Title', 'Creator', 'Filename', 'Client', 'DateCreated', 'DateValidated', 'DateImported', 'state_title']}, ]
def __init__(self, context, request): super(BaseARImportsView, self).__init__(context, request) request.set('disable_plone.rightcolumn', 1) self.catalog = "portal_catalog" self.contentFilter = { 'portal_type': 'ARImport', 'sort_on': 'sortable_title', } self.context_actions = {} if IClient.providedBy(self.context): self.context_actions = \ {_('AR Import'): {'url': 'arimport_add', 'icon': '++resource++bika.lims.images/add.png'}} self.show_sort_column = False self.show_select_row = False self.show_select_column = False self.pagesize = 50 self.form_id = "arimports" self.icon = \ self.portal_url + "/++resource++bika.lims.images/arimport_big.png" self.title = _("Analysis Request Imports") self.description = "" self.columns = { 'title': { 'title': _('Import') }, 'getClientTitle': { 'title': _('Client') }, 'getDateImported': { 'title': _('Date Imported') }, 'getStatus': { 'title': _('Validity') }, 'getDateApplied': { 'title': _('Date Submitted') }, 'state_title': { 'title': _('State') }, } self.review_states = [ { 'id': 'default', 'title': _('All'), 'contentFilter': {}, 'columns': [ 'title', 'getClientTitle', 'getDateImported', 'getStatus', 'getDateApplied', 'state_title' ] }, { 'id': 'imported', 'title': _('Imported'), 'contentFilter': { 'review_state': 'imported' }, 'columns': ['title', 'getClientTitle', 'getDateImported', 'getStatus'] }, { 'id': 'submitted', 'title': _('Applied'), 'contentFilter': { 'review_state': 'submitted' }, 'columns': [ 'title', 'getClientTitle', 'getDateImported', 'getStatus', 'getDateApplied' ] }, ]
def __init__(self, context, request): super(BaseARImportsView, self).__init__(context, request) request.set('disable_plone.rightcolumn', 1) self.catalog = "portal_catalog" self.contentFilter = { 'portal_type': 'ARImport', 'sort_on':'sortable_title', } self.context_actions = {} if IClient.providedBy(self.context): self.context_actions = \ {_('AR Import'): {'url': 'arimport_add', 'icon': '++resource++bika.lims.images/add.png'}} self.show_sort_column = False self.show_select_row = False self.show_select_column = False self.pagesize = 50 self.form_id = "arimports" self.icon = \ self.portal_url + "/++resource++bika.lims.images/arimport_big.png" self.title = self.context.translate(_("Analysis Request Imports")) self.description = "" self.columns = { 'title': {'title': _('Import')}, 'getClientTitle': {'title': _('Client')}, 'getDateImported': {'title': _('Date Imported')}, 'getStatus': {'title': _('Validity')}, 'getDateApplied': {'title': _('Date Submitted')}, 'state_title': {'title': _('State')}, } self.review_states = [ {'id':'default', 'title': _('All'), 'contentFilter':{}, 'columns': ['title', 'getClientTitle', 'getDateImported', 'getStatus', 'getDateApplied', 'state_title']}, {'id':'imported', 'title': _('Imported'), 'contentFilter':{'review_state':'imported'}, 'columns': ['title', 'getClientTitle', 'getDateImported', 'getStatus']}, {'id':'submitted', 'title': _('Applied'), 'contentFilter':{'review_state':'submitted'}, 'columns': ['title', 'getClientTitle', 'getDateImported', 'getStatus', 'getDateApplied']}, ]