Open mfitzp opened 9 years ago
I'm for this.
I think we should add an additional field type_extra that ties into HTML attribute tags.
This would look like: type_extra = {min: '0', max:'20', step:'.25', value:'5'}
Implementing it this way would allow the frontend to just unpack type_extra which will automatically be the widget attributes. I feel this is a very extensible approach.
One wrinkle will be people having multiple arguments where a range slider allows only 1. So we check the choice_limit, and if it's == 1, implement this logic, otherwise do a SelectMany.
I've implemented this capability with the multiInput branch. In the get_field method in factory.py, we can infer this behavior and pass any extra widget attributes.
Just to make it a little more explicit (for me more than anyone): clinto will call the function like range to generate code like this:
{'name': 'test_param4', 'required': False, 'param': '--test-param4', 'choices': [-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18], 'choice_limit': None, 'model': 'CharField', 'type': 'text', 'help': None}
This is to make it more language agnostic. In Wooey, we look at choices, and see if it's all numeric/floating and make a range function. We can do this by a function like check_numeric_choices, which sees if each element is numeric, takes the difference between each element, and if they are all numbers and evenly spaced, we create a range-input.
Back from my holiday... on with the code.
For the range field I was actually thinking to use the range
object type (Python 3.4 only) to make it explicit. That way it's possible to have both a list box of values or a range slider by choice. The idea would be that you could have a slider of 100+ items without storing all 100 possible choices: instead just the start, stop, step.
>>> x = range(0, 10, 2)
>>> x
range(0, 10, 2)
>>> x.start
0
>>> x.stop
10
>>> x.step
2
So we would be storing the 3 values (similar to how I'd done it in flask-wooey with a bit of a hack). But it occurs to me now that range only allows integer step values so that isn't ideal.
Testing for the monotonic numeric-only is good work, but what about (thinking from experience here) a list of NMR MHz 400, 500, 600 ...thats monotonic, numeric, but selecting with a slider doesn't "make sense". It's because of cases like that I initially implemented range as using the range object. Is it always possible to tell from a list of numbers whether a slider is appropriate? Perhaps we should best-guess and add an option to toggle it in the admin.
This isn't so much of an opinion, just rambling thoughts.
With the output from this do we then need to test on the back (template) end for monotonicity and numeric? Is it worth adding additional fields to this for "is_numeric" and "is_monotonic" since we're already doing the testing? Or just "is_range" True/False?
I did a quick test of this to see how argparse handles a lot of choices:
python /home/chris/git/wooey/wooey/tests/scripts/choices.py -h
usage: choices.py [-h] [--one-choice {0,1,2,3}]
[--two-choices {0,1,2,3} {0,1,2,3}]
[--at-least-one-choice {0,1,2,3} [{0,1,2,3} ...]]
[--all-choices [{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299} [{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299} ...]]]
[--multiple-file-choices [MULTIPLE_FILE_CHOICES [MULTIPLE_FILE_CHOICES ...]]]
Something
optional arguments:
-h, --help show this help message and exit
--one-choice {0,1,2,3}
--two-choices {0,1,2,3} {0,1,2,3}
--at-least-one-choice {0,1,2,3} [{0,1,2,3} ...]
--all-choices [{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299} [{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299} ...]]
--multiple-file-choices [MULTIPLE_FILE_CHOICES [MULTIPLE_FILE_CHOICES ...]]
Given this, I don't think we'll have cases where people are passing an absurd number of choices. There are a few reasons I adopted the approach of executing whatever callable is in choices:
1) It may be a custom function. 2) I wanted it to be serializable, a function isn't. 3) I didn't want the schema to be python-specific.
Point 3 is a bit of a weak point I'll admit, considering I'm using Django names for the model.
I'd rather not make a split between what we can do for Python2/3 as well. I think it's easy enough to reverse-engineer the ranges (and if someone is making an iterator with millions of entries in argparse...then I hope they never try to use -h). We can use hasattr(choices, 'start') to see if we can fetch the values, otherwise just do this:
range_start = min(choices)
range_max = max(choices)
range_step = set([choices[i+1]-choices[i] for i in range(len(choices)-1)])
Ok, I'm rambling too.
How about we just use a cutoff in wooey for deciding between a choice list and a range? Clinto shouldn't be making front-end decisions, which is partly why I'm arguing to keep it just as a list of choices -- when we do decide it's a range versus a list?.
In Wooey, we can have a widget admin interface to control all these decisions (so we can easily add and not have a ton of WOOEY_SLIDER_CUTOFF, etc.).
Yeah, think this is the best approach :+1: I missed that the check for the monotonicness was handled in Wooey so there is nothing to pass from clinto.
In Python 3 a
range(0,10,3)
creates a range object rather than a list. We can make use of this to display a range-specific UI element such as slider.This will require additions to
clinto
to detect and pass the object type and parameters somehow.In flask-wooey this was handled and passed as follows:
Since range supports integers only it might make sense to implement this as a
range
type inTYPE_FIELDS
? The parameters for the range can then be passed to the field definition.https://github.com/wooey/django-djangui/issues/9