Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean code #39

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
236eb88
Initial commit
titus-ong Aug 22, 2020
a237ef6
Initial commit
titus-ong Aug 22, 2020
6052672
Added Note class and as_int method
titus-ong Aug 22, 2020
5dad0ce
Added symbol-int converters
titus-ong Aug 22, 2020
d8fb051
Initial commit
titus-ong Aug 23, 2020
adac790
Made examples naming clearer
titus-ong Aug 23, 2020
e31a5d0
Included transpose methods
titus-ong Aug 23, 2020
4361f6f
Abstracted NotationParser class out into separate file
titus-ong Aug 23, 2020
59c68e4
Included mode patterns
titus-ong Aug 23, 2020
468a248
Initial commit
titus-ong Aug 23, 2020
8a6b46f
Rename group count method and change pattern to property
titus-ong Aug 23, 2020
1e7911b
Update test with method name change
titus-ong Aug 23, 2020
5ce4875
Included docstring for Note
titus-ong Aug 23, 2020
174932c
Included Mode, Key, and their notation parsers
titus-ong Aug 23, 2020
38247d8
Added check for valid symbol
titus-ong Aug 23, 2020
f6a10ea
Added symbol pattern
titus-ong Aug 23, 2020
9481819
Test for valid symbol
titus-ong Aug 23, 2020
e6a51d0
Initial commit
titus-ong Aug 23, 2020
d024330
Create static method for getting number of groups from regex pattern
titus-ong Aug 23, 2020
da4ac02
Minor ordering change
titus-ong Aug 23, 2020
7107fdd
Added from_components class method
titus-ong Aug 23, 2020
b035078
Minor name changes
titus-ong Aug 23, 2020
1fb661e
Added check for set_mode to have at least one argument
titus-ong Aug 29, 2020
3b1729c
Minor formatting changes to error messages
titus-ong Aug 29, 2020
def3675
Fix some references
titus-ong Aug 29, 2020
ead0be2
Updated docs
titus-ong Aug 29, 2020
cf92648
Fix method references
titus-ong Aug 29, 2020
9e22023
Initial commit for scales
titus-ong Aug 29, 2020
cd056ef
Change submode and mode to enums for easier comparison
titus-ong Aug 30, 2020
1b1bb2e
Minor changes to repr to be more in line with Python's standard repr
titus-ong Aug 30, 2020
bb9b2cc
Remove unused auto function
titus-ong Aug 30, 2020
5740c9f
Included docstring for enums
titus-ong Aug 30, 2020
5d732f6
Updated docstrings and included step_pattern property for mode and su…
titus-ong Aug 30, 2020
0a9a5ca
minor name changes
titus-ong Aug 31, 2020
c2f3233
Change letter and symbol to enums, and abstracted symbolful classes i…
titus-ong Aug 31, 2020
618330e
Added more template-specific tests
titus-ong Aug 31, 2020
f0f4b5b
Moved mode and submode enums to separate files for other modules to a…
titus-ong Aug 31, 2020
42d4028
Made abstract classes more formalised (cannot be instantiated without…
titus-ong Aug 31, 2020
010ec94
Changed regex flags not to ignore case by default (only key notation …
titus-ong Sep 5, 2020
80b6916
Remove tests for enums
titus-ong Sep 5, 2020
f8310d9
moved enums into components folders for cleaner folder structure
titus-ong Sep 5, 2020
bf6196a
removed value description for enums - they are explained in the methods
titus-ong Sep 13, 2020
42830e2
minor formatting edit
titus-ong Sep 13, 2020
d2555d5
Initial commit for quality enums
titus-ong Sep 13, 2020
ed55c6f
Included test for creating scale degrees for basequality enum
titus-ong Sep 13, 2020
abf8c8f
Initial commit for matching power chords
titus-ong Sep 13, 2020
51d1793
Added more regex patterns and tests for the more complex ones
titus-ong Sep 13, 2020
613155c
Initial commit for suspended chords
titus-ong Sep 13, 2020
5a3bb8b
Initial commit for triads
titus-ong Sep 13, 2020
4539183
Included power into base quality
titus-ong Sep 13, 2020
0df9c84
Fix power regex result
titus-ong Sep 13, 2020
7d0f29d
Change roman converter to accept the roman notation
titus-ong Sep 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions src/chordparser/music/chordcomponents/basequality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from enum import Enum

from chordparser.music.scaledegree import ScaleDegree


class BaseQuality(Enum):
"""Enum for the base quality of a `Chord`.

The enum members available are MAJOR, MINOR, AUGMENTED, DIMINISHED,
SUS2, SUS4, DOMINANT, HALFDIMINISHED and POWER.

"""

MAJOR = (
str.upper,
True,
("1", "3", "5"),
"",
)
MINOR = (
str.lower,
True,
("1", "b3", "5"),
"m",
)
AUGMENTED = (
str.upper,
False,
("1", "3", "#5"),
"aug",
)
DIMINISHED = (
str.lower,
False,
("1", "b3", "b5"),
"dim",
)
SUS2 = (
str.upper,
False,
("1", "2", "5"),
"sus2",
)
SUS4 = (
str.upper,
False,
("1", "4", "5"),
"sus4",
)
DOMINANT = (
str.upper,
True,
("1", "3", "5"),
"",
)
HALFDIMINISHED = (
str.lower,
False,
("1", "b3", "b5"),
"\u00f8",
)
POWER = (
str.upper,
False,
("1", "5"),
"5",
)

def roman_letter_case_converter(self, roman):
"""Convert the Roman numeral letter case based on quality."""
return self.value[0](roman)

def is_derived_from_scale(self):
"""Return if the quality is derived from a scale mode."""
return self.value[1]

@property
def scale_degrees(self):
return tuple(ScaleDegree(notation) for notation in self.value[2])

def __str__(self):
return self.value[3]

def __repr__(self):
return f"BaseQuality.{self.name}"
38 changes: 38 additions & 0 deletions src/chordparser/music/chordcomponents/extensionquality.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from enum import Enum


class ExtensionQuality(Enum):
"""Enum for the quality of the extension of an extended `Chord`.

The enum members available are DIMINISHED, HALFDIMINISHED, MINOR,
DOMINANT and MAJOR.

"""

DIMINISHED = (-2, "")
HALFDIMINISHED = (-1, "")
MINOR = (-1, "")
DOMINANT = (-1, "")
MAJOR = (0, "maj")

def seventh_shift(self):
"""Return the step shift of the seventh.

Returns
-------
int
The step shift of the seventh.

Examples
--------
>>> ExtensionQuality.DIMINISHED.seventh_shift()
-2

"""
return self.value[0]

def __str__(self):
return self.value[1]

def __repr__(self):
return f"ExtensionQuality.{self.name}"
17 changes: 17 additions & 0 deletions src/chordparser/music/chordtypes/power.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from chordparser.music.chordcomponents.basequality import BaseQuality
from chordparser.music.notationparser import NotationParserTemplate
from chordparser.utils.regex_patterns import power_pattern


power_enum_dict = {
"power": BaseQuality.POWER,
}


class PowerChordNotationParser(NotationParserTemplate):
"""Parse power chords."""

_pattern = f"({power_pattern})"

def _split_into_groups(self, regex):
return "power"
19 changes: 19 additions & 0 deletions src/chordparser/music/chordtypes/suspended.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from chordparser.music.chordcomponents.basequality import BaseQuality
from chordparser.music.notationparser import NotationParserTemplate
from chordparser.utils.regex_patterns import sus2_pattern, sus4_pattern


sus_enum_dict = {
"sus2": BaseQuality.SUS2,
"sus4": BaseQuality.SUS4,
}


class SusNotationParser(NotationParserTemplate):
"""Parse suspended chords."""

# sus2 must come first since sus4 detects for "sus"
_pattern = f"({sus2_pattern})|({sus4_pattern})"

def _split_into_groups(self, regex):
return "sus2" if regex.group(1) else "sus4"
42 changes: 42 additions & 0 deletions src/chordparser/music/chordtypes/triad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from chordparser.music.chordcomponents.basequality import BaseQuality
from chordparser.music.chordtypes.suspended import (SusNotationParser,
sus_enum_dict)
from chordparser.music.notationparser import NotationParserTemplate
from chordparser.utils.regex_patterns import (dim_triad_pattern,
aug_triad_pattern,
minor_pattern, major_pattern)


triad_enum_dict = {
"major": BaseQuality.MAJOR,
"minor": BaseQuality.MINOR,
"diminished": BaseQuality.DIMINISHED,
"augmented": BaseQuality.AUGMENTED,
**sus_enum_dict,
}


class TriadNotationParser(NotationParserTemplate):
"""Parse triads."""

_SNP = SusNotationParser()

_pattern = (
f"({major_pattern})|({minor_pattern})|"
f"({dim_triad_pattern})|({aug_triad_pattern})|"
f"({_SNP.pattern})"
)

def _split_into_groups(self, regex):
triad_dict = {
0: "major",
1: "minor",
2: "diminished",
3: "augmented",
4: not regex.group(5) or self._SNP.parse_notation(regex.group(5)),
}
index = next(
idx for (idx, triad) in enumerate(regex.groups())
if triad is not None
)
return triad_dict[index]
137 changes: 137 additions & 0 deletions src/chordparser/music/hassymbol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from chordparser.music.notecomponents.symbol import Symbol


class HasSymbol:
"""Abstract class that contains a `Symbol`."""

_symbol: Symbol # To be defined in concrete class

@property
def symbol(self):
return self._symbol

def raise_by(self, steps=1):
"""Raise the pitch by changing only its `Symbol`.

The number of steps must be positive. If you wish to change
the pitch without knowing the number of steps, use shift_by()
instead.

Parameters
----------
steps: int, Optional
The number of steps to raise by. Default 1 when optional.

Raises
------
ValueError
If the number of steps is not positive.
IndexError
If the new Symbol is not within the range of DOUBLEFLAT and
DOUBLESHARP.

Examples
--------
The examples are given using `Notes`. They will apply similarly
to other classes with `Symbols`.

>>> note = Note("Cb")
>>> note.raise_by()
>>> note
C Note
>>> note.raise_by(2)
>>> note
C\U0001D12A Note

"""
if steps <= 0:
raise ValueError(
f"Expected positive steps, {steps} given. Use lower_by() or "
"shift_by() instead"
)
self.shift_by(steps)

def lower_by(self, steps=1):
"""Lower the pitch by changing only its `Symbol`.

The number of steps must be positive. If you wish to change
the pitch without knowing the number of steps, use shift_by()
instead.

Parameters
----------
steps: int, Optional
The number of steps to lower by. Default 1 when optional.

Raises
------
ValueError
If the number of steps is not positive.
IndexError
If the new Symbol is not within the range of DOUBLEFLAT and
DOUBLESHARP.

Examples
--------
The examples are given using `Notes`. They will apply similarly
to other classes with `Symbols`.

>>> note = Note("C#")
>>> note.lower_by()
>>> note
C Note
>>> note.lower_by(2)
>>> note
C\U0001D12B Note

"""
if steps <= 0:
raise ValueError(
f"Expected positive steps, {steps} given. Use raise_by() or "
"shift_by() instead"
)
self.shift_by(-steps)

def shift_by(self, steps):
"""Shift the pitch by changing only its `Symbol`.

A positive number of steps will raise the pitch, while a
negative number of steps will lower the pitch.

Parameters
----------
steps: int
The number of steps to shift by.

Raises
------
IndexError
If the new Symbol is not within the range of DOUBLEFLAT and
DOUBLESHARP.

Examples
--------
The examples are given using `Notes`. They will apply similarly
to other classes with `Symbols`.

>>> note = Note("C#")
>>> note.shift_by(-1)
>>> note
C Note
>>> note.shift_by(1)
>>> note
C\u266f Note

"""
old_pitch = self._symbol.as_steps()
new_pitch = old_pitch + steps
try:
self._symbol = next(
symbol for symbol in list(Symbol)
if new_pitch == symbol.as_steps()
)
except StopIteration:
raise IndexError(
"New symbol is not within the range of DOUBLEFLAT and "
"DOUBLESHARP"
)
Loading