def setup_handler(context): """Generic setup handler """ if context.readDataFile("{}.txt".format(PRODUCT_NAME)) is None: return logger.info("{} setup handler [BEGIN]".format(PRODUCT_NAME.upper())) portal = context.getSite() # Setup patient content type add_patient_folder(portal) # Configure visible navigation items setup_navigation_types(portal) # Setup catalogs setup_catalogs(portal) # Apply ID format to content types setup_id_formatting(portal) # Setup workflow (for field permissions mostly) setup_workflow(portal) logger.info("{} setup handler [DONE]".format(PRODUCT_NAME.upper()))
def post_install(portal_setup): """Runs after the last import step of the *default* profile This handler is registered as a *post_handler* in the generic setup profile :param portal_setup: SetupTool """ logger.info("{} install handler [BEGIN]".format(PRODUCT_NAME.upper())) context = portal_setup._getImportContext(PROFILE_ID) # noqa portal = context.getSite() # noqa logger.info("{} install handler [DONE]".format(PRODUCT_NAME.upper()))
def afterUpgradeStepHandler(event): """Event handler that is executed after running an upgrade step of senaite.core """ if not is_installed(): return logger.info("Run senaite.patient.afterUpgradeStepHandler ...") portal = get_portal() setup_navigation_types(portal) setup_id_formatting(portal) setup_workflow(portal) logger.info("Run senaite.patient.afterUpgradeStepHandler [DONE]")
def post_uninstall(portal_setup): """Runs after the last import step of the *uninstall* profile This handler is registered as a *post_handler* in the generic setup profile :param portal_setup: SetupTool """ logger.info("{} uninstall handler [BEGIN]".format(PRODUCT_NAME.upper())) # https://docs.plone.org/develop/addons/components/genericsetup.html#custom-installer-code-setuphandlers-py profile_id = "profile-{}:uninstall".format(PRODUCT_NAME) context = portal_setup._getImportContext(profile_id) # noqa portal = context.getSite() # noqa logger.info("{} uninstall handler [DONE]".format(PRODUCT_NAME.upper()))
def setup_id_formatting(portal, format_definition=None): """Setup default ID formatting """ if not format_definition: logger.info("Setting up ID formatting ...") for formatting in ID_FORMATTING: setup_id_formatting(portal, format_definition=formatting) logger.info("Setting up ID formatting [DONE]") return bs = portal.bika_setup p_type = format_definition.get("portal_type", None) if not p_type: return form = format_definition.get("form", "") if not form: logger.info("Param 'form' for portal type {} not set [SKIP") return logger.info("Applying format '{}' for {}".format(form, p_type)) ids = list() for record in bs.getIDFormatting(): if record.get('portal_type', '') == p_type: continue ids.append(record) ids.append(format_definition) bs.setIDFormatting(ids)
def migrate_to_patient_catalog(portal): """Migrate portal_catalog -> patient_catalog """ logger.info("Migrate patient catalog...") # 1. Setup catalogs setup_catalogs(portal) # 2. Clean up indexes for catalog_id, idx_id in DELETE_INDEXES: catalog = api.get_tool(catalog_id) indexes = get_indexes(catalog) if idx_id in indexes: del_index(catalog, idx_id) logger.info("Deleted index '%s' from catalog '%s'" % (idx_id, catalog_id)) # 3. Clean up columns for catalog_id, column in DELETE_COLUMNS: catalog = api.get_tool(catalog_id) columns = get_columns(catalog) if column in columns: del_column(catalog, column) logger.info("Deleted column '%s' from catalog '%s'" % (column, catalog_id)) # 4. Reindex patient catalog patient_catalog = api.get_tool(PATIENT_CATALOG) patient_catalog.clearFindAndRebuild() logger.info("Migrate patient catalog [DONE]")
def update_patient(instance): if instance.isMedicalRecordTemporary(): return mrn = instance.getMedicalRecordNumberValue() # Allow empty value when patients are not required for samples if mrn is None: return patient = patient_api.get_patient_by_mrn(mrn, include_inactive=True) if patient is None: logger.info("Creating new Patient with MRN #: {}".format(mrn)) patient = patient_api.create_empty_patient() # XXX: Sync the values back from Sample -> Patient? values = get_patient_fields(instance) patient_api.update_patient(patient, **values)
def update_workflow(portal, workflow_id, settings): """Updates the workflow with workflow_id with the settings passed-in """ logger.info("Updating workflow '{}' ...".format(workflow_id)) wf_tool = api.get_tool("portal_workflow") workflow = wf_tool.getWorkflowById(workflow_id) if not workflow: logger.warn("Workflow '{}' not found [SKIP]".format(workflow_id)) states = settings.get("states", {}) for state_id, values in states.items(): update_workflow_state(workflow, state_id, values) transitions = settings.get("transitions", {}) for transition_id, values in transitions.items(): update_workflow_transition(workflow, transition_id, values)
def fix_patients_fullname(portal): """Update the value of attribute 'firstname' with the value of 'fullname' """ logger.info("Fixing patients full names ...") for patient in portal.patients.objectValues(): if patient.get_firstname(): # This one has the value set already continue raw = patient.__dict__ firstname = raw.get("fullname", None) if firstname: patient.set_firstname(firstname) del (patient.__dict__["fullname"]) patient.reindexObject() logger.info("Fixing patients full names ... [DONE]")
def migrate_birthdates(portal): """Migrate all birthdates from patients to be timezone aware """ logger.info("Migrate patient birthdate timezones ...") catalog = api.get_tool(PATIENT_CATALOG) results = catalog({"portal_type": "Patient"}) timezone = dtime.get_os_timezone() for brain in results: patient = api.get_object(brain) birthdate = patient.birthdate if birthdate: # clean existing time and timezone date = birthdate.strftime("%Y-%m-%d") # append current OS timezone if possible if timezone: date = date + " %s" % timezone patient.set_birthdate(date) patient.reindexObject() logger.info("Migrate patient birthdate timezones [DONE]")
def migrate_patient_item_to_container(portal): """Migrate patient objects to be folderish Base class changed from Item -> Container https://community.plone.org/t/changing-dx-content-type-base-class-from-item-to-container http://blog.redturtle.it/2013/02/25/migrating-dexterity-items-to-dexterity-containers """ logger.info("Migrate patients to be folderish ...") patients = portal.patients for patient in patients.objectValues(): pid = patient.getId() patients._delOb(pid) patient.__class__ = Patient patients._setOb(pid, patient) BTreeFolder2Base._initBTrees(patients[pid]) patients[pid].reindexObject() transaction.commit() logger.info("Migrate patients to be folderish [DONE]")
def update_workflow_transition(workflow, transition_id, settings): logger.info("Updating workflow '{}', transition: '{}'".format( workflow.id, transition_id)) if transition_id not in workflow.transitions: workflow.transitions.addTransition(transition_id) transition = workflow.transitions.get(transition_id) transition.setProperties(title=settings.get("title"), new_state_id=settings.get("new_state"), after_script_name=settings.get( "after_script", ""), actbox_name=settings.get("action", settings.get("title"))) guard = transition.guard or Guard() guard_props = { "guard_permissions": "", "guard_roles": "", "guard_expr": "" } guard_props = settings.get("guard", guard_props) guard.changeFromProperties(guard_props) transition.guard = guard
def upgrade(tool): portal = tool.aq_inner.aq_parent setup = portal.portal_setup ut = UpgradeUtils(portal) ver_from = ut.getInstalledVersion(PRODUCT_NAME) if ut.isOlderVersion(PRODUCT_NAME, version): logger.info("Skipping upgrade of {0}: {1} > {2}".format( PRODUCT_NAME, ver_from, version)) return True logger.info("Upgrading {0}: {1} -> {2}".format(PRODUCT_NAME, ver_from, version)) # -------- ADD YOUR STUFF BELOW -------- # Allow/Disallow to verify/publish samples with temporary MRN setup.runImportStepFromProfile(PROFILE_ID, "plone.app.registry") # Update schema interface setup.runImportStepFromProfile(PROFILE_ID, "typeinfo") setup.runImportStepFromProfile(PROFILE_ID, "workflow") # https://github.com/senaite/senaite.patient/pull/24 migrate_patient_item_to_container(portal) # https://github.com/senaite/senaite.patient/pull/14 migrate_to_patient_catalog(portal) # Firstname + lastname instead of fullname fix_patients_fullname(portal) logger.info("{0} upgraded to version {1}".format(PRODUCT_NAME, version)) return True
def update_workflow_state(workflow, status_id, settings): logger.info("Updating workflow '{}', status: '{}' ...".format( workflow.id, status_id)) # Create the status (if does not exist yet) new_status = workflow.states.get(status_id) if not new_status: workflow.states.addState(status_id) new_status = workflow.states.get(status_id) # Set basic info (title, description, etc.) new_status.title = settings.get("title", new_status.title) new_status.description = settings.get("description", new_status.description) # Set transitions trans = settings.get("transitions", ()) if settings.get("preserve_transitions", False): trans = tuple(set(new_status.transitions + trans)) new_status.transitions = trans # Set permissions update_workflow_state_permissions(workflow, new_status, settings)
def upgrade(tool): portal = tool.aq_inner.aq_parent setup = portal.portal_setup # noqa ut = UpgradeUtils(portal) ver_from = ut.getInstalledVersion(PRODUCT_NAME) if ut.isOlderVersion(PRODUCT_NAME, version): logger.info("Skipping upgrade of {0}: {1} > {2}".format( PRODUCT_NAME, ver_from, version)) return True logger.info("Upgrading {0}: {1} -> {2}".format(PRODUCT_NAME, ver_from, version)) # -------- ADD YOUR STUFF BELOW -------- # add dateindex for birthdates setup_catalogs(portal) # migrate birthdates w/o time but with valid timezone migrate_birthdates(portal) logger.info("{0} upgraded to version {1}".format(PRODUCT_NAME, version)) return True
def add_patient_folder(portal): """Adds the initial Patient folder """ if portal.get("patients") is None: logger.info("Adding Patient Folder") portal.invokeFactory("PatientFolder", "patients", title="Patients")
def update_workflow_state_permissions(workflow, status, settings): # Copy permissions from another state? permissions_copy_from = settings.get("permissions_copy_from", None) if permissions_copy_from: logger.info("Copying permissions from '{}' to '{}' ...".format( permissions_copy_from, status.id)) copy_from_state = workflow.states.get(permissions_copy_from) if not copy_from_state: logger.info("State '{}' not found [SKIP]".format(copy_from_state)) else: for perm_id in copy_from_state.permissions: perm_info = copy_from_state.getPermissionInfo(perm_id) acquired = perm_info.get("acquired", 1) roles = perm_info.get("roles", acquired and [] or ()) logger.info( "Setting permission '{}' (acquired={}): '{}'".format( perm_id, repr(acquired), ', '.join(roles))) status.setPermission(perm_id, acquired, roles) # Override permissions logger.info("Overriding permissions for '{}' ...".format(status.id)) state_permissions = settings.get('permissions', {}) if not state_permissions: logger.info("No permissions set for '{}' [SKIP]".format(status.id)) return for permission_id, roles in state_permissions.items(): state_roles = roles and roles or () if isinstance(state_roles, tuple): acq = 0 else: acq = 1 logger.info("Setting permission '{}' (acquired={}): '{}'".format( permission_id, repr(acq), ', '.join(state_roles))) # Check if this permission is defined globally for this workflow if permission_id not in workflow.permissions: workflow.permissions = workflow.permissions + (permission_id, ) status.setPermission(permission_id, acq, state_roles)