Skip to content

Commit

Permalink
Support Bool options (#17)
Browse files Browse the repository at this point in the history
* add bool options

* updated tests

* use deepcopy on dicts to avoid changing default options
  • Loading branch information
ravachol-yang authored Mar 31, 2024
1 parent e005270 commit b2d51f0
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 93 deletions.
31 changes: 26 additions & 5 deletions app/middlewares/option_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def pre_process(self, message, data):
if isinstance(message, InlineQuery):
msg_text = message.query
if msg_text == "":
msg_text = "*"
msg_text = "placeholder"
if msg_text[0] == '/':
options = msg_text.split(" ",1)
options.pop(0)
Expand All @@ -26,12 +26,33 @@ def pre_process(self, message, data):
options = msg_text.split(" ",1)
options.pop(0)

opt_data = dict({"bool_options": False,
"options":[]})
if options:
# remove spaces and make options list
options = options[0].replace(" ","")
options = options.split(",")
# takes the option string
opt_str = options[0]
try:
# if user input options are in binary
opt = bin(int(opt_str, 2))[2:]
except ValueError:
try:
# if user input options are in hex
opt = bin(int(opt_str,16))[2:]
except ValueError:
# if it's in plain string
opt = opt_str
# build binary array and not too long
if opt.isdigit():
# the last 16 options
opt = opt[-16:]
# set the flag to true
opt_data["bool_options"] = True
for i in opt.zfill(16):
opt_data["options"].append(bool(int(i)))
else:
opt_data["options"].append(opt)

data['options'] = options
data["options"] = opt_data

def post_process(self, message, data, exception=None):
pass
40 changes: 20 additions & 20 deletions app/models/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ class Audio(Base):
VOLUME_HIGH = 0.6
VOLUME_LOW = 0.3

OPTIONS_AVAILABLE = ['sine', 'noise']
# s: sine; n: noise
OPTIONS_AVAILABLE = ['s', 'n']
OPTIONS_DEFAULT = {
"sine": True,
"noise": False
"s":{"enabled": True},
"n":{"enabled": False}
}

# generate a noise sample
Expand All @@ -51,7 +52,7 @@ def sine_sample(freq, volume, x):
def _mix_up(self, len):
values = []
for i in range(0, len):
if self._options['sine']:
if self._options['s']["enabled"]:
# two channels
freq_channel_1 = random.randint(Audio.FREQ_LOW, Audio.FREQ_HIGH)
freq_channel_2 = random.randint(Audio.FREQ_LOW, Audio.FREQ_HIGH)
Expand All @@ -62,21 +63,21 @@ def _mix_up(self, len):
# duration of every chunk
chunk_duration = random.randint(1, 3)
for x in range(0, chunk_duration * Audio.SAMPLE_RATE):
if self._options['sine']:
if self._options['s']["enabled"]:
sine_channel_1 = Audio.sine_sample(freq_channel_1, volume_channel_1, x)
sine_channel_2 = Audio.sine_sample(freq_channel_2, volume_channel_2, x)
if self._options['noise']:
if self._options['n']["enabled"]:
noise_channel_1 = Audio.noise_sample()
noise_channel_2 = Audio.noise_sample()

# build into values
if self._options['sine'] and self._options['noise']:
if self._options['s']["enabled"] and self._options['n']["enabled"]:
values.append(random.choice([sine_channel_1, noise_channel_1]))
values.append(random.choice([sine_channel_2, noise_channel_2]))
elif self._options['sine'] and not self._options['noise']:
elif self._options['s']["enabled"] and not self._options['n']["enabled"]:
values.append(sine_channel_1)
values.append(sine_channel_2)
elif self._options['noise'] and not self._options['sine']:
elif self._options['n']["enabled"] and not self._options['s']["enabled"]:
values.append(noise_channel_1)
values.append(noise_channel_2)

Expand All @@ -88,21 +89,20 @@ def _mix_up(self, len):
def generate(self, options=None):
name = uuid.uuid4().hex
# add prefix to file name
name_prefix = "/sine-"
name_prefix = "/"
# set options
self._options = dict(Audio.OPTIONS_DEFAULT)
if options and not options == ['']:
name_prefix = "/"
self._options = dict.fromkeys(Audio.OPTIONS_AVAILABLE, False)
for option in options:
if option in Audio.OPTIONS_AVAILABLE:
self._options[option] = True
name_prefix = name_prefix + option + "-"
if options:
self.set_options(options)

# apply options to filename
for i in self._options:
if self._options[i]["enabled"]:
name_prefix = name_prefix + i + "-"

# if options are empty now, return to default
if self._options == dict.fromkeys(Audio.OPTIONS_AVAILABLE, False):
if self._options == dict.fromkeys(Audio.OPTIONS_AVAILABLE, {"enabled":False}):
self._options = dict(Audio.OPTIONS_DEFAULT)
name_prefix += "sine-"
name_prefix = "/s-"

self._filename = name_prefix+name+".wav"
self._filepath = dirs.AUDIO_DIR+self._filename
Expand Down
21 changes: 21 additions & 0 deletions app/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@
base.py
the Base model
"""
import copy

from telebot import TeleBot
from telebot.types import Message

class Base:

OPTIONS_AVAILABLE = []
OPTIONS_DEFAULT = {}

def __init__(self,
bot:TeleBot = None,
message:Message = None):
self.__bot = bot
self.__message = message
self._options = copy.deepcopy(self.OPTIONS_DEFAULT)


# get content of this object
Expand All @@ -23,6 +28,22 @@ def content(self):
def set_content(self, content:str):
self._content = content

# set options
def set_options(self, options):
if options["bool_options"]:
opt = options["options"]
# index for mapping from boolean array to options
index = 0
for i in self._options:
# change options accordingly, opt with be chopped to the last elements
self._options[i]["enabled"] = opt[-len(self.OPTIONS_AVAILABLE):][index]
index += 1
else:
# TODO: support non-bool options
pass

return self

# generate content
def generate(self):
raise NotImplementedError
Expand Down
80 changes: 25 additions & 55 deletions app/models/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,67 +20,37 @@ class Text(Base):
'<', '>', '#', '+',
'-', '=', '|', '{',
'}', '.', '!']

OPTIONS_AVAILABLE = ['en', 'zh', 'punc','d']

# Stands for zh, english, punctuations, digits, must be in order
OPTIONS_AVAILABLE = ['z', 'e', 'p','d']
# Default: 0111
OPTIONS_DEFAULT = {
"en": True,
"zh": False,
"punc": True,
"d": True,
"z":{"enabled": False,
"content": lambda zh:[zh+chr(random.randint(0x4e00,0xa000)) for i in range(0,30)]},
"e":{"enabled": True,
"content": string.ascii_letters},
"p":{"enabled": True,
"content": string.punctuation},
"d":{"enabled": True,
"content": string.digits}
}

# generate random string, returns the object itself
def generate(self, options = None):

# set default options
self._options = dict(Text.OPTIONS_DEFAULT)
# check options
if options:
self.set_options(options)

# if option not nothing
if options and not options == ['']:
option_bodies = []
# check each option in options
for option in options:
# if the option is empty, we see it as setting all others false
if option == '':
for i in Text.OPTIONS_AVAILABLE:
if i not in option_bodies:
self._options[i] = False
# if the option is not empty
else:
# get option body without "+" or "-"
if option[0] == "+" or option[0] == "-":
option_body = option.replace(option[0],"",1)
option_bodies.append(option_body)
else:
# if option has no operator, it is "+"
option_body = option
option_bodies.append(option_body)
if option_body in Text.OPTIONS_AVAILABLE:
if option[0] == '-':
self._options[option_body] = False
else:
self._options[option_body] = True

# make choice string
# build the choices string
choices_string = ""

# add strings for each options
if self._options["en"]:
choices_string += string.ascii_letters

if self._options["zh"]:
zh_string = ""
# generate a Chinese string of 30 chars
for i in range(0,30):
zh_string += chr(random.randint(0x4e00, 0xa000))
choices_string += zh_string

if self._options["punc"]:
choices_string += string.punctuation

if self._options["d"]:
choices_string += string.digits

for i in self._options:
if self._options[i]["enabled"]:
# if the content is a function
if callable(self._options[i]["content"]):
choices_string += "".join(self._options[i]["content"](""))
else:
choices_string += "".join(self._options[i]["content"])

# if the string is empty, return random *s
if not choices_string:
choices_string = "*"
Expand All @@ -94,7 +64,7 @@ def generate(self, options = None):
# convert to mono for markdown parsing
def to_mono(self):
# if there is no puncs, no need to check
if self._options['punc']:
if self._options["p"]["enabled"]:
# add a "\" before every special char
for i in Text.SPECIAL:
self._content = self._content.replace(i,"\\"+i)
Expand Down
10 changes: 3 additions & 7 deletions tests/audio_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@ def test_audio_generated():

def test_audio_noise():
audio = Audio()
assert isinstance(audio.generate(options = ['noise']), Audio)
assert isinstance(audio.generate(options = {"bool_options": True, "options":[False,True]}), Audio)

def test_audio_mix():
audio = Audio()
assert isinstance(audio.generate(options = ['noise', 'sine']), Audio)

def test_audio_wrong_options():
audio = Audio()
assert isinstance(audio.generate(options = ['aa']), Audio)
assert isinstance(audio.generate(options = {"bool_options": True, "options":[True,True]}), Audio)

def test_audio_empty_options():
audio = Audio()
assert isinstance(audio.generate(options = ['']), Audio)
assert isinstance(audio.generate(options = {"bool_options": False, "options": "aa"}), Audio)

def test_audio_to_mpeg():
audio = Audio()
Expand Down
10 changes: 5 additions & 5 deletions tests/text_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ def test_text_generated():

def test_text_options():
text = Text()
text.generate(options = ['zh','+en', '-d'])
text.generate(options = {"bool_options":True, "options":[True, True, True, False]})
assert isinstance(text.content(),str)

def test_text_option_only():
text = Text()
text.generate(options = ['en', ''])
assert isinstance(text.content(),str) and text.content().isascii()
text.generate(options = {"bool_options":True, "options":[False, False, False, True]})
assert isinstance(text.content(),str) and text.content().isdigit()

def test_text_wrong_options():
text = Text()
text.generate(options = ['aaa'])
text.generate(options = {"bool_options": True, "options":[True,False,True,False,False]})
assert isinstance(text.content(),str) and text.content().isascii()

def test_text_empty_options():
text = Text()
text.generate(options = [''])
text.generate(options = {"bool_options": False, "options": "aaa"})
assert isinstance(text.content(),str) and text.content().isascii()


Expand Down
2 changes: 1 addition & 1 deletion tests/voice_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ def test_voice_generated():

def test_voice_noise():
voice = Voice()
assert isinstance(voice.generate(options = ['noise']).to_voice(), Voice)
assert isinstance(voice.generate(options = {"bool_options":True, "options": [False, True]}).to_voice(), Voice)

0 comments on commit b2d51f0

Please sign in to comment.