Writing your own assignment module

Module customizing

For the assignment module base class to work, there are some requirements. There have to be at least three areas:

  • parent area: in this area, the parent record(s) should be filtered. In the PortfolioWatchlistAssignmentModule, e.g., the portfolio is the parent.
  • source area: the area where all the child records are displayed. In the case of the PortfolioWatchlistAssignmentModule, all projects that are assigned to the current portfolio are displayed
  • assignment area: in this area, new assignment records are created, the value of each data field is filled by looking for a matching data field with identical python id in the parent or in the source area. If no matching python id is found, the field will be ignored. The values can also be filled using a default value in the customizing of the data field.

In this example, the assignment area looks like this:

  • The project id and portfolio id are filled by looking up the values in the source and parent areas
    • In the source area there is a datafield with python id pr_id
    • The portfolio id, on the other hand, is not in the source area and will thus be filled from the parent area, which contains a datafield with the python id portfolio_id
  • Since the records are inserted on mts level we can also use default values, in this example the watchlist that is edited is always that of the currently logged in user, so we can use @1 as default value for the user_id

Necessary methods

To create your own assignment module there are some methods that need to be implemented:

  • open_module_for_parents: This method opens the assignment module and should also do some checks:
    • Check that assignment data is available using check_for_assignment_data
    • Make sure that all other criteria are met to make an assignment, e.g. that a stakeholder record exists
    • If a check fails None should be returned and if the show_messages parameter is set, there should also be a proper error message
  • is_assigned: This method is necessary to check if an assignment already exists and needs to be implemented for each assignment individually
  • id_field_py_id: This property should return the data field python id of the ID field by which a source record can be identified unambiguously, in the example below this is the project id
  • make sure that the other properties match your customizing names and requirements

Example implementation from the PortfolioWatchlistAssignmentModule :

class PortfolioWatchlistAssignmentModule(AssignmentModuleBase):
    @property
    def no_data_available_message_id(self):
        return '1195'

    @classmethod
    def open_module_for_parents(cls, parents=None, show_messages=True, **kwargs):
        if parents and not isinstance(parents, list):
            parents = [parents]

        mod_obj = ppms.get_target_module()
        target = get_global_setting_value('edit_portfolio_watchlist', 'alpha120')
        with ppms.echo_disabled():
            target_mod_obj = mod_obj.open_module(target)
            target_mod_obj.set_current_L_var(16, parents)
            target_mod_obj.menu(MENU_FILTER)
            if not target_mod_obj.check_for_assignment_data(show_messages=show_messages):
                target_mod_obj.menu(MENU_CLOSE)
                return None
        return target_mod_obj

    @property
    def id_field_py_id(self):
        return 'pr_id'

    @staticmethod
    def is_assigned(parent_id, child_id):
        user = ppms.uvar_get('@1')
        assignment_record = ppms.search_record(534, [child_id, parent_id, user], ['uuid'])
        return bool(assignment_record)
PY

Implementation for using marked records and working with radio buttons

Radio Buttons

There is no native radio button type in planta, so we need to implement a value range which ensures that only one record is selected at a time. To create a radio button in your table the following steps are required:

  • Create a normal yes/no type virtual data item
  • Implement a value range which unchecks all other mts records in the processInput

Here is an example value range from the standard implementation:

def di065272_chosen_for_assignment_radio_checkInput(di, oldvalue):
    """This value range simulates a radio button."""
    records = di.get_dfs()[0].get_record().get_da().get_records()
    for record in records:
        if record.chosen_for_assignment_radio.get_raw_value():
            record.chosen_for_assignment_radio.set_raw_value(0)
    return di.get_value()

di065272_chosen_for_assignment_radio_checkInput.deps = ("",)
PY

Marked records

In the standard implementation there are no assignment modules left which use marked records for selection. If you're still required to implement such a module you have to overwrite the get_source_records method. The method should return an iterable of all mts records which are marked in the source area.
The method has an optional parameter ids. If you want to use the static method for assigning records, you have to make sure that all source records with ids within the ids list are also returned. The key field for the comparison is defined in the property id_field_py_id.

Classes

AssignmentModuleBase

Methods

FunctionParametersReturn ValueDescription
AssignmentModuleBase.after_insert(self)

Is called after all inserts are done. Used to close the module and filter in the invoker module to show the new assignment data
AssignmentModuleBase.assign_marked_records(self, applied_dfc, clicked_df)applied_dfc: Applied data field customizing
clicked_df: The clicked DF

The method which is called when pressing the assignment button. Invokes the creation of new assignments
AssignmentModuleBase.check_for_assignment_data(self, show_messages=True)show_messages: Whether a message box is shown which informs that there is no assignment data should be shownTrue when assignment data is available, False otherwiseThis method should be called in the open_module_for_parents method
AssignmentModuleBase.click_assignment_button(self)

Programatic way of clicking on the assignment button. Used in regression tests
AssignmentModuleBase.get_parent_records(self)
All parent records in the module
AssignmentModuleBase.get_source_records(self, ids=None)ids: Can be used to retrieve all source records with the given IDsList of all marked source records as mts_recordUsed to retrieve all relevant source records which need to be assigned
AssignmentModuleBase.get_value_from_source_or_parent(self, parent_mts_record, source_mts_record, df_python_id)parent_mts_record: The parent mts record of the assignment
source_mts_record: The source (child) mts record
df_python_id: The data field id of the new assignment record, there needs to be a DF with the same ID in the parent or source area
The value for the given df python id
AssignmentModuleBase.insert_into_assignment_table(self, parent_mts_record, source_mts_record)parent_mts_record: Parent mts record of the new assignment
source_mts_record: The child mts record of the new assignment
The new assignment mts recordCreates and fills the new assignment record, also saves the record if possible
AssignmentModuleBase.on_load(self)

Filters and resets all checkboxes so that nothing is checked when the module is opened
AssignmentModuleBase.reset_dummy_record_checkbox(self)

Resets all checkboxes

Classmethods

FunctionParametersReturn ValueDescription
AssignmentModuleBase.assign_children_to_parent(cls, children_ids, parent_id, kwargs)children_ids: The ID of the child record to assign
parent_id: The ID of the parent record
kwargs: Special properties needed for some assingments, defined in the child classes

A static method to assign children records to a parent
AssignmentModuleBase.open_module_for_parents(cls, parents=None, show_messages=False, kwargs)parents: The parent ids for which the module is opened

show_messages: Whether error messages should be shown
kwargs: Special parameters used in some assignment modules
The module or None if there is no assignment data availableNeeded in every assignment module, is e.g. also used for the static assignment method

Staticmethods

FunctionParametersReturn ValueDescription
AssignmentModuleBase.get_all_dfs_from_area(mts_record)mts_record: mts recordReturns a list of all df python ids in the record
AssignmentModuleBase.is_assigned(parent_id, child_id)parent_id: ID of the parent record
child_id: ID of the child record
True or False

Properties

PropertyGetterSetterDescription
AssignmentModuleBase.assignment_button_da_idThe data area python ID where the assignment button is located
AssignmentModuleBase.assignment_data_areaThe data area python ID of the data area where the new assignment record is inserted
AssignmentModuleBase.button_df_idThe button data field ID of the assignment button
AssignmentModuleBase.checkbox_python_idThe python ID of the checkbox to mark source records
AssignmentModuleBase.id_field_py_idThe data field python ID with which source records can be identified
AssignmentModuleBase.no_data_available_message_idThe message ID which is displayed when
AssignmentModuleBase.parent_data_areasList of data areas where the data area python id starts with "parent"
AssignmentModuleBase.source_data_areasList of data areas where the data area python id starts with "source"

RadiobuttonAssignmentModuleBase

Properties

PropertyGetterSetterDescription
RadiobuttonAssignmentModuleBase.checkbox_python_idData field python id of the radio button

StakeholderAssignmentModuleBase

Staticmethods

FunctionParametersReturn ValueDescription
StakeholderAssignmentModuleBase.stakeholder_exists(member_id=None)member_id: Member ID of the stakeholderTrue or FalseCheck if a stakeholder record exists, needed for some assignments