Skip to content

Commit

Permalink
Next release (#110)
Browse files Browse the repository at this point in the history
  • Loading branch information
johentsch authored May 23, 2024
2 parents 1e6b0e3 + 4689897 commit 12cdddb
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 125 deletions.
87 changes: 75 additions & 12 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ This means that the docs are kept in the same repository as the project code, an
that any documentation update is done in the same way was a code contribution.
The documentation is written in reStructuredText_ and includes the myst-nb_ extension.

Documentation pages are written in reStructuredText_ (as are the docstrings that are automatically compiled to the
API docs).

.. tip::
Please notice that the `GitHub web interface`_ provides a quick way of
propose changes in ``ms3``'s files. While this mechanism can
be tricky for normal code contributions, it works perfectly fine for
contributing to the docs, and can be quite handy.

If you are interested in trying this method out, please navigate to
the ``docs`` folder in the source repository_, find which file you
would like to propose changes and click in the little pencil icon at the
top, to open `GitHub's code editor`_. Once you finish editing the file,
please write a message in the form at the bottom of the page describing
which changes have you made and what are the motivations behind them and
submit your proposal.


When working on documentation changes in your local machine, you can
Expand All @@ -64,11 +80,15 @@ and use Python's built-in web server for a preview in your web browser
Code Contributions
==================

.. todo:: Please include a reference or explanation about the internals of the project.
.. admonition:: TL;DR

* Fork the repository.
* (Create a virtual environment, :ref:`see below <virtenv>`).
* Head into the local clone of your fork and hit ``pip install -e ".[dev]"`` (where ``.`` is the current directory).
* Install the precommit hooks via ``pre-commit install``.
* Implement the changes and create a Pull Request against the ``development`` branch.
* Thank you!

An architecture description, design principles or at least a summary of the
main concepts will make it easy for potential contributors to get started
quickly.

Submit an issue
---------------
Expand Down Expand Up @@ -100,7 +120,7 @@ Clone the repository
page. This creates a copy of the code under your account on |the repository service|.
#. Clone this copy to your local disk::

git clone [email protected]:johentsch/ms3.git
git clone [email protected]:YourLogin/ms3.git

#. You should run::

Expand Down Expand Up @@ -132,6 +152,10 @@ Implement your changes

to record your changes in git_.

Please make sure to see the validation messages from |pre-commit|_ and fix
any eventual issues.
This should automatically use flake8_/black_ to check/fix the code style
in a way that is compatible with the project.

.. important:: Don't forget to add unit tests and documentation in case your
contribution adds an additional feature and is not just a bugfix.
Expand Down Expand Up @@ -162,13 +186,52 @@ Submit your contribution
#. Go to the web page of your fork and click |contribute button|
to send your changes for review.

.. todo:: if you are using GitHub, you can uncomment the following paragraph

Find more detailed information in `creating a PR`_. You might also want to open
the PR as a draft first and mark it as ready for review after the feedbacks
from the continuous integration (CI) system or any required fixes.


Find more detailed information in `creating a PR`_. You might also want to open
the PR as a draft first and mark it as ready for review after the feedbacks
from the continuous integration (CI) system or any required fixes.


Coding Conventions
------------------

Please make sure to run ``pre-commit install`` in your local clone of the repository. This way, many coding
conventions are automatically applied before each commit!

Commit messages
~~~~~~~~~~~~~~~

``ms3`` uses `Conventional Commits <https://www.conventionalcommits.org/>`__ to determine the next SemVer version number. Please make sure to prefix each
message with one of:

+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| Commit Type | Title | Description | SemVer |
+===============+==========================+=============================================================================================================+========+
| ``feat`` | Features | A new feature | MINOR |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``fix`` | Bug Fixes | A bug Fix | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``docs`` | Documentation | Documentation only changes | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``style`` | Styles | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``refactor`` | Code Refactoring | A code change that neither fixes a bug nor adds a feature | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``perf`` | Performance Improvements | A code change that improves performance | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``test`` | Tests | Adding missing tests or correcting existing tests | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``build`` | Builds | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``ci`` | Continuous Integrations | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``chore`` | Chores | Other changes that don't modify src or test files | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+
| ``revert`` | Reverts | Reverts a previous commit | PATCH |
+---------------+--------------------------+-------------------------------------------------------------------------------------------------------------+--------+

In the case of breaking changes, which result in a new major version, please add a ``!`` after the type, e.g., ``refactor!:``.
This type of commit message needs to come with a body, starting with ``BREAKING CHANGE:``, which explains in great detail everything
that will not be working anymore.
Troubleshooting
---------------

Expand Down
133 changes: 25 additions & 108 deletions src/ms3/bs4_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
sort_note_list,
unfold_measures_table,
unfold_repeats,
write_score_to_handler,
)
from .utils.constants import DCML_DOUBLE_REGEX, FORM_DETECTION_REGEX

Expand Down Expand Up @@ -2492,7 +2493,7 @@ def rl(self, recompute: bool = False) -> pd.DataFrame:
self.make_standard_restlist()
return self._rl

def parse_soup(self):
def parse_soup(self) -> None:
"""First step of parsing the MuseScore source. Involves discovering the <staff> tags and storing the
<Measure> tags of each in the :attr:`measure_nodes` dictionary. Also stores the drum_map for each Drumset
staff.
Expand All @@ -2504,12 +2505,31 @@ def parse_soup(self):
f"Use 'ms3 convert' command or pass parameter 'ms' to Score to temporally convert."
)

root_tag = self.soup.find("museScore")
if root_tag is None:
self.logger.error(
"This does not seem to be a MuseScore file because it lacks the <museScore> tag that "
"would normally be the root of the XML tree."
)
return

score_tags = root_tag.find_all("Score")
if len(score_tags) == 0:
score_tag = root_tag
else:
score_tag = score_tags[0]
if len(score_tags) > 1:
self.logger.warning(
"The file seems to include separately encoded parts, encoded with their own "
"<Score> tags. Only the first one will be considered."
)

# Check if any of the <Part> tags contains a pitch -> drumset instrument map
# all_part_tags = self.soup.find_all('Part')
# if len(all_part_tags) == 0:
# self.logger.error(f"Looks like an empty score to me.")
part_tag = None
for part_tag in self.soup.find_all("Part"):
for part_tag in score_tag.find_all("Part", recursive=False):
drum_tags = part_tag.find_all("Drum")
staff_tag = part_tag.find("Staff")
if len(drum_tags) == 0 or staff_tag is None:
Expand All @@ -2527,11 +2547,11 @@ def parse_soup(self):
# Populate measure_nodes with one {mc: <Measure>} dictionary per staff.
# The <Staff> nodes containing the music are siblings of <Part>
if part_tag is None:
iterator = self.soup.find_all("Staff")
staff_iterator = score_tag.find_all("Staff")
else:
iterator = part_tag.find_next_siblings("Staff")
staff_iterator = part_tag.find_next_siblings("Staff")
staff = None
for staff in iterator:
for staff in staff_iterator:
staff_id = int(staff["id"])
self.measure_nodes[staff_id] = {}
for mc, measure in enumerate(
Expand Down Expand Up @@ -4687,109 +4707,6 @@ def decode_harmony_tag(tag):
return label


# region Functions for writing BeautifulSoup to MSCX file


def escape_string(s):
return (
str(s)
.replace("&", "&amp;")
.replace('"', "&quot;")
.replace("<", "&lt;")
.replace(">", "&gt;")
)


def opening_tag(node, closed=False):
result = f"<{node.name}"
attributes = node.attrs
if len(attributes) > 0:
result += " " + " ".join(
f'{attr}="{escape_string(value)}"' for attr, value in attributes.items()
)
closing = "/" if closed else ""
return f"{result}{closing}>"


def closing_tag(node_name):
return f"</{node_name}>"


def make_oneliner(node):
"""Pass a tag of which the layout does not spread over several lines."""
result = opening_tag(node)
for c in node.children:
if isinstance(c, bs4.element.Tag):
result += make_oneliner(c)
else:
result += escape_string(c)
result += closing_tag(node.name)
return result


def format_node(node, indent):
"""Recursively format Beautifulsoup tag as in an MSCX file."""
nxt_indent = indent + 2
space = indent * " "
node_name = node.name
# The following tags are exceptionally not abbreviated when empty,
# so for instance you get <metaTag></metaTag> and not <metaTag/>
if node_name in [
"continueAt",
"continueText",
"endText",
"LayerTag",
"metaTag",
"name",
"programRevision",
"text",
"trackName",
]:
return f"{space}{make_oneliner(node)}\n"
children = node.find_all(recursive=False)
if len(children) > 0:
result = f"{space}{opening_tag(node)}\n"
result += "".join(format_node(child, nxt_indent) for child in children)
result += f"{nxt_indent * ' '}{closing_tag(node_name)}\n"
return result
if node.string == "\n":
return (
f"{space}{opening_tag(node)}\n{nxt_indent * ' '}{closing_tag(node_name)}\n"
)
if node.string is None:
return f"{space}{opening_tag(node, closed=True)}\n"
return f"{space}{make_oneliner(node)}\n"


def bs4_to_mscx(soup: bs4.BeautifulSoup):
"""Turn the BeautifulSoup into a string representing an MSCX file"""
assert soup is not None, "BeautifulSoup XML structure is None"
initial_tag = """<?xml version="1.0" encoding="UTF-8"?>\n"""
first_tag = soup.find()
return initial_tag + format_node(first_tag, indent=0)


def write_score_to_handler(
soup: bs4.BeautifulSoup,
file_handler: IO,
logger=None,
) -> bool:
if logger is None:
logger = module_logger
elif isinstance(logger, str):
logger = get_logger(logger)
try:
mscx_string = bs4_to_mscx(soup)
except Exception as e:
logger.error(f"Couldn't output score because of the following error:\n{e}")
return False
file_handler.write(mscx_string)
return True


# endregion Functions for writing BeautifulSoup to MSCX file


def text_tag2str(tag: bs4.Tag) -> str:
"""Transforms a <text> tag into a string that potentially includes written-out HTML tags."""
components = []
Expand Down
Loading

0 comments on commit 12cdddb

Please sign in to comment.