# Right Triangle CMD tool

I was most interested in feedback on the overall approach to the problem, naming conventions, efficiency, etc.

• I would like to avoid using argparse/docopt or something similar, unless it would have a major impact on complexity of the program.
• Any tips on how should I go about adding help-dialogues for ease of use?

from collections import OrderedDict as oDct
from trianglesolver import solve, degree
from termcolor import colored, cprint
import numpy as np
import cmd

def prntDct(dct):
for k, v in dct.items(): print(k + ' =', v)

def noNones(*args):
return False if any(arg is None for arg in args) else True

def argify(string_in, ref_dict):
fin = ref_dict.copy()
string = string_in.replace(' ', ', ')
temp = eval('dict({})'.format(string))
matched = {k: temp[k] for k in temp if k in fin}
fin.update(matched)
return fin

def solvify(items):
a,b,c,A,B,C = items

if A is not None: A = A * degree
if B is not None: B = B * degree
C = C * degree

try:
if a is None: outs = solve(c=c, b=b, C=C)
elif b is None: outs = solve(a=a, c=c, C=C)
elif c is None: outs = solve(a=a, b=b, C=C)

except:
if noNones(a, A): outs = solve(a=a, A=A,C=C)
elif noNones(a, B): outs = solve(a=a, B=B, C=C)
elif noNones(b, A): outs = solve(b=b, A=A, C=C)
elif noNones(b, B): outs = solve(b=b, B=B, C=C)
elif noNones(c, A): outs = solve(c=c, A=A, C=C)
elif noNones(c, B): outs = solve(c=c, B=B, C=C)

finally:
a,b,c,A,B,C = outs
return ([a, b, c, A / degree, B / degree, C / degree])

def triangify(items, output_format, prec, scale=None, side='c'):
a,b,c,A,B,C = solvify(items)

if side is 'c': ordr = [a, b, c] + ['a', 'b', 'c']; set = [0,1]
if side is 'b': ordr = [c, a, b] + ['c', 'a', 'b']; set = [1,2]
if side is 'a': ordr = [c, b, a] + ['c', 'b', 'a']; set = [2,1]

if scale is not None:
sides_arrange = np.array(ordr[:3])
sides_scaling = sides_arrange / ordr[2]
sVals  = (sides_scaling * scale).tolist()
else: sVals = ordr[:3]

sKeys = ordr[3:]

P = np.sum(sVals)
S = (sVals[set[0]] * sVals[set[1]]) / 2

fKeys = sKeys + ['A','B','C','P','S']
toVals = sVals + [A,  B,  C,  P,  S]

if type(prec) is int: fVals = np.around(np.array(toVals),prec)
else: fVals = (np.around(np.array(toVals), 0)).astype(int)

triangle = oDct(zip(fKeys, fVals))

if output_format is 'all': output_format = 'abcABCPS'

fin = {k: triangle[k] for k in list(output_format) if k in triangle}

return fin

def main():
scalar = ScalarCmd()
print('\n' * 4)
cprint('Triangle Scalar Tool', 'cyan')
cprint('====================', 'cyan')
print()
cprint('type "help" for commands', 'cyan')
print()
scalar.ruler = colored('-', 'cyan')
scalar.cmdloop()

defaults = oDct([
('P', None),
('S', None),
('a', None),
('b', None),
('c', None),
('A', None),
('B', None),
('C', 90),
('prec', 2),
('scale', 1),
('side', 'c'),
('output', 'all')])

class ScalarCmd(cmd.Cmd):
prompt = (colored('\n>', 'green') + colored(' ', 'white'))

def default(self, arg):
print('invalid command, try typing "help"')

def do_quit(self, arg):
'''exits the program'''
cprint('\nexiting...\n', 'red')
exit()

def do_solve(self, arg):
'''type at least 2 sides or angles\n'''

error = ['',
'invalid command:',  # most of these docstrings are temps
'type at least 2 sides named a/b/c or angles named A/B/C',
'usage: TBD','']

try:
if len(arg) < 2:
error[0] = '\nempty command:'
raise

else:
args = argify(arg, defaults)

sides_angles = list(args.values())[2:8]

outs = triangify(
sides_angles,
args['output'],
args['prec'])

print()
prntDct(outs)
print()  # <- easy blank lines

except Exception:
print(*error, sep='\n')
return

def do_scale(self, arg):
'''type at least 2 sides or angles\n'''

error = ['',
'invalid command:',
'type at least 2 sides named a/b/c or angles named A/B/C',
'usage: TBD','']

try:
if len(arg) < 2:
error[0] = '\nempty command:'
raise

else:
args = argify(arg, defaults)

sides_angles = list(args.values())[2:8]

outs = triangify(
sides_angles,
args['output'],
args['prec'],
args['scale'],
args['side'])

print()
prntDct(outs)
print()  # <- easy blank lines

except Exception:
print(*error, sep='\n')
return

if __name__ == '__main__': main()


I'm still quite an amateur with python programming, so I thank you in advance for your feedback!

## General

• Personally I would prefer putting the clauses if if and else statements on separate lines even if they are only one line long.
• You should avoid using a generic except without specifying an exception or using a generic Exception. Instead, you should catch the specific exception you expect the code to throw. Otherwise, you may end up catching TypeError or some other exception that indicates a bug and that should be propagated instead of caught. From PEP 8:

A good rule of thumb is to limit use of bare 'except' clauses to two cases:

1. If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.
2. If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise. try...finally can be a better way to handle this case.
• The main method should be at the end, just before the if __name__=="__main__" statement.

## Triangle Solver

• solvify should return a tuple instead of a list since each index has a specific meaning.
• argify should not use eval; instead parse the input string directly.
• Use the equality operator (side=='c') instead of the is keyword for comparing strings.
• Use isinstance for type checks, as in if isinstance(prec,int) instead of if type(prec) is int

## ScalarCmd

• This should be refactored into a separate file to separate the UI and the backend.
• try...raise inside do_solve has no exception to raise.
• I am not sure if this is possible using termcolor but the cmd.Cmd.cmdloop() has an intro parameter that can be used for printing the intro banner.

• You should write some code to parse the string passed in via string_in instead of using eval. Apr 8, 2018 at 23:25
Your choice of variable names is strange. There is no reason to leave out the i in print and dict, but keep it in argify and solvify. Be consistent and keep them all, or, ltrntvly, rmv ll th vwls.