def __init__(self, comp_id: int, access_key: Optional[str] = None) -> None:
        # Generate URL and request the rows
        url = self.complib_url + str(comp_id) + "/rows"
        if access_key:
            url += "?accessKey=" + access_key
        request_rows = requests.get(url)
        # Check for the status of the requests
        if request_rows.status_code == 404:
            raise InvalidCompError(comp_id)
        if request_rows.status_code == 403:
            raise PrivateCompError(comp_id)
        request_rows.raise_for_status()
        # Parse the request responses as JSON
        response_rows = json.loads(request_rows.text)
        unparsed_rows = response_rows['rows']
        # Determine the start row of the composition
        num_starting_rounds = 0
        while unparsed_rows[num_starting_rounds][0] == unparsed_rows[0][0]:
            num_starting_rounds += 1
        self._start_stroke = Stroke.from_index(num_starting_rounds)

        # Derive the rows, calls and stage from the JSON response
        self.loaded_rows: List[Tuple[Row, Optional[str]]] = [
            (Row([Bell.from_str(bell)
                  for bell in row]), None if call == '' else call) for row,
            call, property_bitmap in unparsed_rows[num_starting_rounds:]
        ]
        stage = response_rows['stage']

        # Variables from which the summary string is generated
        self.comp_id = comp_id
        self.comp_title = response_rows['title']
        self.is_comp_private = access_key is not None

        super().__init__(stage)
    def __init__(self,
                 comp_id: int,
                 access_key: Optional[str] = None,
                 substituted_method_id: Optional[int] = None) -> None:
        def process_call_string(calls: str) -> List[str]:
            """Parse a sequence of calls, and remove 'Stand'."""
            stripped_calls = [x.strip() for x in calls.split(";")]
            return [c for c in stripped_calls if c != "Stand"]

        # Generate URL from the params
        query_sections: List[str] = []
        if access_key:
            query_sections.append(f"accessKey={access_key}")
        if substituted_method_id:
            query_sections.append(
                f"substitutedmethodid={substituted_method_id}")
        url = self.complib_url + str(comp_id) + "/rows"
        if query_sections:
            url += "?" + "&".join(query_sections)
        # Create an HTTP request for the rows, and deal with potential error codes
        request_rows = requests.get(url)
        if request_rows.status_code == 404:
            raise InvalidCompError(comp_id)
        if request_rows.status_code == 403:
            raise PrivateCompError(comp_id)
        request_rows.raise_for_status()
        # Parse the request responses as JSON
        response_rows = json.loads(request_rows.text)
        unparsed_rows = response_rows["rows"]
        # Determine the start row of the composition
        num_starting_rounds = 0
        while unparsed_rows[num_starting_rounds][0] == unparsed_rows[0][0]:
            num_starting_rounds += 1
        self._start_stroke = Stroke.from_index(num_starting_rounds)
        # Derive the rows, calls and stage from the JSON response
        loaded_rows: List[Tuple[Row, List[str]]] = [
            (Row([Bell.from_str(bell) for bell in row]),
             [] if calls == "" else process_call_string(calls))
            for row, calls, _property_bitmap in unparsed_rows
        ]
        # Convert these parsed rows into a format that we can read more easily when ringing.  I.e.
        # load the non-rounds rows as-is, but keep the calls that should be called in rounds
        self.loaded_rows = loaded_rows[num_starting_rounds:]
        self._early_calls = {
            num_starting_rounds - i: calls
            for i, (_row,
                    calls) in enumerate(loaded_rows[:num_starting_rounds])
            if calls != []
        }
        # Set the variables from which the summary string is generated
        self.comp_id = comp_id
        self.comp_title = response_rows["title"]
        self.is_comp_private = access_key is not None
        # Propogate the initialisation up to the parent class
        super().__init__(response_rows["stage"])
Esempio n. 3
0
    def generate_next_row(self) -> None:
        """ Creates a new row from the row generator and tells the rhythm to expect the new bells. """
        if self._is_ringing_rounds:
            self._row = self._rounds
        else:
            self._row, self._calls = self.row_generator.next_row_and_calls(
                self.stroke)
            # Add cover bells if needed
            if len(self._row) < len(self._rounds):
                self._row = Row(self._row + self._rounds[len(self._row):])

        bells = " ".join([str(bell) for bell in self._row])
        self.logger.info(f"ROW: {bells}")
Esempio n. 4
0
def generate_starting_row(number_of_bells: int, start_row_string: Optional[str] = None) -> Row:
    """Generate the starting row as rounds or a custom input."""
    if start_row_string is None:
        return rounds(number_of_bells)

    start_row = [Bell.from_str(i) for i in start_row_string]
    if len(start_row) > len(set(start_row)):
        raise ValueError(f"starting row '{start_row_string}' contains the same bell multiple times")

    # If there are more bells than given in the starting row
    # add the missing ones in sequential order as cover bells
    for i in range(1, number_of_bells + 1):
        if Bell.from_number(i) not in start_row:
            start_row.append(Bell.from_number(i))

    return Row(start_row)
Esempio n. 5
0
    def permute(self, row: Row, places: Places) -> Row:
        """ Permute a row by a place notation given by `places`. """
        new_row = list(row)

        i = 1
        if places and places[0] % 2 == 0:
            # Skip 1 for implicit lead when lowest pn is even
            i += 1

        while i < self.stage:
            if i in places:
                i += 1
                continue

            # If not in place, must swap, index is 1 less than place
            new_row[i - 1], new_row[i] = new_row[i], new_row[i - 1]
            i += 2

        return Row(new_row)
Esempio n. 6
0
def rounds(number_of_bells: int) -> Row:
    """Generate rounds on the given number of bells."""
    return Row([Bell.from_number(i) for i in range(1, number_of_bells + 1)])
Esempio n. 7
0
 def test_generate_with_custom_row(self):
     self.assertListEqual(Row([Bell(3), Bell(2), Bell(1), Bell(0)]), generate_starting_row(4, "4321"))
     self.assertListEqual(Row([Bell(3), Bell(2), Bell(1), Bell(0)]), generate_starting_row(4, "432"))
     self.assertListEqual(Row([Bell(3), Bell(1), Bell(0), Bell(2)]), generate_starting_row(4, "42"))
     self.assertListEqual(Row([Bell(1), Bell(0), Bell(2), Bell(3)]), generate_starting_row(4, "2"))
Esempio n. 8
0
 def test_generate_without_custom_row(self):
     self.assertListEqual(Row([Bell(0), Bell(1), Bell(2), Bell(3)]), generate_starting_row(4))