def test_cast_ast_eval_true_int(self):
        # Given
        data = 123

        # When
        result = utility.cast(data, ast_eval=True)
        result_str = utility.cast(str(data), ast_eval=True)

        # Then
        assert result == data
        assert result_str == data
    def test_cast_ast_eval_false_list(self):
        # Given
        data = [1, 2, 3]

        # When
        result = utility.cast(data, ast_eval=False)
        result_str = utility.cast(str(data), ast_eval=False)

        # Then
        assert result == str(data)
        assert result_str == str(data)
    def test_cast_ast_eval_true_str(self):
        # Given
        data = "abc"

        # When
        result = utility.cast(data, ast_eval=True)

        # Then
        assert result == data
Exemplo n.º 4
0
    def _create_compass_dict(form_tree: html.FormElement) -> dict:
        """Create Compass info dict from FormElement."""
        compass_dict = {}
        compass_vars = form_tree.fields["ctl00$_POST_CTRL"]
        for pair in compass_vars.split("~"):
            key, value = pair.split("#", 1)
            compass_dict[key] = cast(value)  # int or str

        return compass_dict
    def test_maybe_int_int(self):
        # Given
        data = 123

        # When
        result = utility.maybe_int(data)
        result_str = utility.cast(str(data))

        # Then
        assert result == data
        assert result_str == data
Exemplo n.º 6
0
    def get_roles_detail(
        self, role_number: int, response: Union[str, requests.Response] = None
    ) -> Union[schema.MemberRolePopup, dict]:
        """Returns detailed data from a given role number.

        Args:
            role_number: Role Number to use
            response: Pre-generated response to use

        Returns:
            A dicts mapping keys to the corresponding data from the
            role detail data.

            E.g.:
            {'hierarchy': {'organisation': 'The Scout Association',
              'country': '...',
              'region': '...',
              'county': '...',
              'district': '...',
              'group': '...',
              'section': '...'},
             'details': {'role_number': ...,
              'organisation_level': '...',
              'birth_date': datetime.datetime(...),
              'membership_number': ...,
              'name': '...',
              'role_title': '...',
              'role_start': datetime.datetime(...),
              'role_status': '...',
              'line_manager_number': ...,
              'line_manager': '...',
              'ce_check': datetime.datetime(...),
              'disclosure_check': '...',
              'references': '...',
              'appointment_panel_approval': '...',
              'commissioner_approval': '...',
              'committee_approval': '...'},
             'getting_started': {...: {'name': '...',
               'validated': datetime.datetime(...),
               'validated_by': '...'},
               ...
              }}

            Keys will always be present.

        Todo:
            Other possible exceptions? i.e. from Requests

        """
        # pylint: disable=too-many-locals,too-many-statements
        renamed_levels = {
            "County / Area / Scottish Region / Overseas Branch": "County",
        }
        renamed_modules = {
            1: "module_01",
            "TRST": "trustee_intro",
            2: "module_02",
            3: "module_03",
            4: "module_04",
            "GDPR": "GDPR",
        }
        unset_vals = {"--- Not Selected ---", "--- No Items Available ---", "--- No Line Manager ---"}

        module_names = {
            "Essential Information": "M01",
            "Trustee Introduction": "TRST",
            "PersonalLearningPlan": "M02",
            "Tools for the Role (Section Leaders)": "M03",
            "Tools for the Role (Managers and Supporters)": "M04",
            "General Data Protection Regulations": "GDPR",
        }

        references_codes = {
            "NC": "Not Complete",
            "NR": "Not Required",
            "RR": "References Requested",
            "S": "References Satisfactory",
            "U": "References Unsatisfactory",
        }

        start_time = time.time()
        if response is None:
            response = self._get(f"{Settings.base_url}/Popups/Profile/AssignNewRole.aspx?VIEW={role_number}")
            logger.debug(f"Getting details for role number: {role_number}. Request in {(time.time() - start_time):.2f}s")

        post_response_time = time.time()
        if isinstance(response, (str, bytes)):
            tree = html.fromstring(response)
        else:
            tree = html.fromstring(response.content)
        form = tree.forms[0]

        if form.action == "./ScoutsPortal.aspx?Invalid=Access":
            raise PermissionError(f"You do not have permission to the details of role {role_number}")

        member_string = form.fields.get("ctl00$workarea$txt_p1_membername")
        ref_code = form.fields.get("ctl00$workarea$cbo_p2_referee_status")

        role_details = dict()
        # Approval and Role details
        role_details["role_number"] = role_number
        role_details["organisation_level"] = form.fields.get("ctl00$workarea$cbo_p1_level")
        role_details["birth_date"] = parse(form.inputs["ctl00$workarea$txt_p1_membername"].get("data-dob"))
        role_details["membership_number"] = int(form.fields.get("ctl00$workarea$txt_p1_memberno"))
        role_details["name"] = member_string.split(" ", maxsplit=1)[1]  # TODO does this make sense - should name be in every role??
        role_details["role_title"] = form.fields.get("ctl00$workarea$txt_p1_alt_title")
        role_details["role_start"] = parse(form.fields.get("ctl00$workarea$txt_p1_startdate"))
        # Role Status
        role_details["role_status"] = form.fields.get("ctl00$workarea$txt_p2_status")
        # Line Manager
        line_manager_el = next((op for op in form.inputs["ctl00$workarea$cbo_p2_linemaneger"] if op.get("selected")), None)
        role_details["line_manager_number"] = maybe_int(line_manager_el.get("value")) if line_manager_el is not None else None
        role_details["line_manager"] = line_manager_el.text.strip() if line_manager_el is not None else None
        # Review Date
        role_details["review_date"] = parse(form.fields.get("ctl00$workarea$txt_p2_review"))
        # CE (Confidential Enquiry) Check  # TODO if CE check date != current date then is valid
        role_details["ce_check"] = parse(form.fields.get("ctl00$workarea$txt_p2_cecheck"))
        # Disclosure Check
        disclosure_with_date = form.fields.get("ctl00$workarea$txt_p2_disclosure")
        if disclosure_with_date.startswith("Disclosure Issued : "):
            disclosure_date = parse(disclosure_with_date.removeprefix("Disclosure Issued : "))
            disclosure_check = "Disclosure Issued"
        else:
            disclosure_date = None
            disclosure_check = disclosure_with_date
        role_details["disclosure_check"] = disclosure_check  # TODO extract date
        role_details["disclosure_date"] = disclosure_date  # TODO extract date
        # References
        role_details["references"] = references_codes.get(ref_code, ref_code)

        approval_values = {}
        for row in tree.xpath("//tr[@class='trProp']"):
            select = row[1][0]
            code = select.get("data-app_code")
            approval_values[code] = select.get("data-db")
            # select.get("title") gives title text, but this is not useful as it does not reflect latest changes,
            # but only who added the role to Compass.

        # Appointment Panel Approval
        role_details["appointment_panel_approval"] = approval_values.get("ROLPRP|AACA")
        # Commissioner Approval
        role_details["commissioner_approval"] = approval_values.get("ROLPRP|CAPR")
        # Committee Approval
        role_details["committee_approval"] = approval_values.get("ROLPRP|CCA")

        if role_details["line_manager_number"] in unset_vals:
            role_details["line_manager_number"] = None

        # Filter null values
        role_details = {k: v for k, v in role_details.items() if v is not None}

        # Getting Started
        modules_output = {}
        getting_started_modules = tree.xpath("//tr[@class='trTrain trTrainData']")
        # Get all training modules and then extract the required modules to a dictionary
        for module in getting_started_modules:
            module_name = module[0][0].text.strip()
            if module_name in module_names:
                info = {
                    # "name": module_names[module_name],  # short_name
                    "validated": parse(module[2][0].value),  # Save module validation date
                    "validated_by": module[1][1].value or None,  # Save who validated the module
                }
                mod_code = cast(module[2][0].get("data-ng_value"))  # int or str
                modules_output[renamed_modules[mod_code]] = info

        # Get all levels of the org hierarchy and select those that will have information:
        # Get all inputs with location data
        org_levels = [v for k, v in sorted(dict(form.inputs).items()) if "ctl00$workarea$cbo_p1_location" in k]
        # TODO
        all_locations = {row.get("title"): row.findtext("./option") for row in org_levels}

        clipped_locations = {
            renamed_levels.get(key, key).lower(): value for key, value in all_locations.items() if value not in unset_vals
        }

        logger.debug(
            f"Processed details for role number: {role_number}. "
            f"Compass: {(post_response_time - start_time):.3f}s; Processing: {(time.time() - post_response_time):.4f}s"
        )
        # TODO data-ng_id?, data-rtrn_id?
        full_details = {
            "hierarchy": clipped_locations,
            "details": role_details,
            "getting_started": modules_output,
        }
        if self.validate:
            return schema.MemberRolePopup.parse_obj(full_details)
        else:
            return full_details