diff --git a/smdatatools/common/cli_options.py b/smdatatools/common/cli_options.py index 8329681..4f9db95 100644 --- a/smdatatools/common/cli_options.py +++ b/smdatatools/common/cli_options.py @@ -4,24 +4,27 @@ from smdatatools.data_processing.output_processor import OutputProcessor class Options: - + @staticmethod def read_SMtoData(filepath: str) -> DataHandler: data = DataHandler(filepath) istr = read_file(filepath) data.note_data, data.valid = InputProcessor.parse_sm_input(istr) return data + @staticmethod def read_TXTtoData(filepath: str) -> DataHandler: data = DataHandler(filepath) istr = read_file(filepath) data.note_data = InputProcessor.parse_txt_input(istr) return data + @staticmethod def write_DatatoSM(data: DataHandler, filepath: str): filename = strip_filename(filepath) ostr = OutputProcessor.pregenerate_sm_output(filename, data.processed_data) write_file(ostr, filepath) + @staticmethod def write_DatatoTXT(data: DataHandler, filepath: str): ostr = OutputProcessor.pregenerate_txt_output(data.note_data) write_file(ostr, filepath) \ No newline at end of file diff --git a/smdatatools/common/file_utils.py b/smdatatools/common/file_utils.py index 788c43d..0c2b4b6 100644 --- a/smdatatools/common/file_utils.py +++ b/smdatatools/common/file_utils.py @@ -1,18 +1,19 @@ from os import walk from os.path import join, split, splitext from re import sub +from typing import Collection -def read_file(filename): +def read_file(filename: str) -> list[str]: file_data = [] with open(filename, encoding='ascii', errors='ignore') as f: file_data = f.read().splitlines() return file_data -def write_file(output_data, filename): +def write_file(output_data: str, filename: str): with open(filename, 'w') as f: f.write(output_data) -def strip_filename(filename): +def strip_filename(filename: str) -> str: ''' Strips file path Strips file extension @@ -23,7 +24,7 @@ def strip_filename(filename): tail = splitext(tail)[0] return sub(' ', '_', sub('[^a-z0-9-_ ]', '', tail.lower())) -def collect_filenames(input_dir: str, extensions: list): +def collect_filenames(input_dir: str, extensions: Collection[str]) -> list[str]: filenames = [] for root, dirs, files in walk(input_dir): for filename in files: @@ -32,7 +33,7 @@ def collect_filenames(input_dir: str, extensions: list): filenames.append(join(root, filename).replace("\\","/")) return filenames -def getFilePaths(input_dir: list, extensions: list): +def getFilePaths(input_dir: str, extensions: Collection[str]) -> list[str]: filepaths = collect_filenames(input_dir, extensions) if '.sm' in extensions: @@ -40,7 +41,7 @@ def getFilePaths(input_dir: list, extensions: list): return filepaths -def checkFilePaths(sm_filepaths): +def checkFilePaths(sm_filepaths: list[str]): # checks for static bpm in the .sm file # and removes filepath from list if not for sm_file in sm_filepaths: diff --git a/smdatatools/components/measure.py b/smdatatools/components/measure.py index 2e318d6..c82c919 100644 --- a/smdatatools/components/measure.py +++ b/smdatatools/components/measure.py @@ -2,8 +2,8 @@ from math import gcd, ceil class Measure: - - def calculate_timing(measure, measure_index, bpm, offset): + @staticmethod + def calculate_timing(measure: list[str | None], measure_index: int, bpm: float, offset: float) -> list[str]: # calculate time in seconds for each line in the measure: # BPM = beats/minute -> BPS = beats/second = BPM/60 # measure = 4 beats = 4 * 1/4th notes = 1 note @@ -16,7 +16,8 @@ def calculate_timing(measure, measure_index, bpm, offset): # returns the note/timing pair, if the note exists return [measure[i] + ' ' + str(i * note_256 * fraction_256 + measure_timing - offset) for i, is_set in enumerate(measure) if is_set != None] - def find_gcd(note_positions) -> int: + @staticmethod + def find_gcd(note_positions: list[int]) -> int: # attempts to fit the note positions into either a # 256, 128, 64, 32, 16, 8 or 4 note measure based on spacing # found by getting the greatest common denominator @@ -30,7 +31,8 @@ def find_gcd(note_positions) -> int: return int(gcd_gap) - def generate_measure(notes, note_positions) -> list[str]: + @staticmethod + def generate_measure(notes: list[str], note_positions: list[int]) -> list[str]: # we'll want to trim as much output as possible # by reducing the measure size @@ -46,7 +48,8 @@ def generate_measure(notes, note_positions) -> list[str]: return generated_measure - def fit_notes_to_measure(notes, timings, seconds_1_256) -> list[str]: + @staticmethod + def fit_notes_to_measure(notes: list[str], timings: list[float], seconds_1_256: float) -> list[str]: # if no data is passed, generate current measure # as "empty" with the smallest size if not notes or not timings: @@ -78,7 +81,8 @@ def fit_notes_to_measure(notes, timings, seconds_1_256) -> list[str]: return measure - def place_notes(notes_and_timings, bpm) -> list: + @staticmethod + def place_notes(notes_and_timings: list[str], bpm: float) -> list[str]: placed_notes = [] if not notes_and_timings: return placed_notes diff --git a/smdatatools/data_processing/data_handler.py b/smdatatools/data_processing/data_handler.py index 9dd207c..b75d5fc 100644 --- a/smdatatools/data_processing/data_handler.py +++ b/smdatatools/data_processing/data_handler.py @@ -1,4 +1,5 @@ from collections import defaultdict +from typing import Any from smdatatools.common.file_utils import strip_filename from smdatatools.components.measure import Measure @@ -6,12 +7,12 @@ class DataHandler: # each Data Handler will represent the data of one .sm/.txt file - def __init__(self, filepath): + def __init__(self, filepath: str): self.sm_path = filepath self.filename = strip_filename(filepath) - self.note_data = defaultdict(list) - self.processed_data = defaultdict(list) + self.note_data: dict[str, Any] = defaultdict(list) + self.processed_data: dict[str, Any] = defaultdict(list) self.valid = True def process_data_to_sm_format(self): diff --git a/smdatatools/data_processing/input_processor.py b/smdatatools/data_processing/input_processor.py index dbf9625..3e0237d 100644 --- a/smdatatools/data_processing/input_processor.py +++ b/smdatatools/data_processing/input_processor.py @@ -1,16 +1,19 @@ from collections import defaultdict from re import sub, split +from typing import Any from smdatatools.components.measure import Measure -class InputProcessor: - def convert_note(line): +class InputProcessor: + @staticmethod + def convert_note(line: str) -> str: return sub('4', '1', sub('[MKLF]', '0', line)) #replaces extra notes: M, K, L, F; replaces 4 note - def parse_sm_input(sm_file): - note_data = defaultdict(list) - note_data['notes'] = defaultdict(list) # notes are paired with each difficulty + @staticmethod + def parse_sm_input(sm_file: list[str]) -> tuple[dict[str, Any], bool]: + note_data: dict[str, Any] = defaultdict(list) + note_data['notes']: dict[str, Any] = defaultdict(list) # notes are paired with each difficulty current_difficulty = '' measure = [] measure_index = 0 @@ -72,11 +75,12 @@ def parse_sm_input(sm_file): measure.append(note) # adds note if found else: measure.append(None) - + return note_data, valid - def parse_txt_input(txt_file): - note_data = defaultdict(list) + @staticmethod + def parse_txt_input(txt_file: list[str]) -> dict[str, Any]: + note_data: dict[str, Any] = defaultdict(list) note_data['notes'] = defaultdict(list) current_difficulty = '' notes_and_timings = [] @@ -105,5 +109,5 @@ def parse_txt_input(txt_file): else: notes_and_timings.append(line) note_data['notes'][current_difficulty].extend(notes_and_timings) - + return note_data diff --git a/smdatatools/data_processing/output_processor.py b/smdatatools/data_processing/output_processor.py index 51b698e..f8ca142 100644 --- a/smdatatools/data_processing/output_processor.py +++ b/smdatatools/data_processing/output_processor.py @@ -1,8 +1,9 @@ from collections import defaultdict +from typing import Any class OutputProcessor: - - def pregenerate_sm_output(file_name: str, note_data: defaultdict(list)) -> str: + @staticmethod + def pregenerate_sm_output(file_name: str, note_data: dict[str, Any]) -> str: # pre-generate .sm output data title = '#TITLE:%s;\n' % note_data['title'] artist = '#ARTIST:jhaco vs cpuguy96;\n' @@ -26,7 +27,8 @@ def pregenerate_sm_output(file_name: str, note_data: defaultdict(list)) -> str: return ''.join((title, artist, music, select, bpm, notes)) - def pregenerate_txt_output(note_data: defaultdict(list)) -> str: + @staticmethod + def pregenerate_txt_output(note_data: dict[str, Any]) -> str: # pre-generate output data title = 'TITLE %s\n' % note_data['title'] bpm = 'BPM %s\n' % str(note_data['bpm'])