mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) Improve suggestions for formula autocomplete
Summary: - Make suggestions less case-sensitive (not entirely case-insensitive, but allow top-level suggestions to match in all-lowercase) - Add function signatures to suggestions for Grist functions. - Excel-like functions that are present but not implemented are no longer offered as suggestions. Test Plan: Added a test case on python side, and a browser test case for how suggestions are rendered and inserted. Reviewers: paulfitz Reviewed By: paulfitz Differential Revision: https://phab.getgrist.com/D2608
This commit is contained in:
parent
2fbd3f1706
commit
003029bf8a
87
sandbox/grist/autocomplete_context.py
Normal file
87
sandbox/grist/autocomplete_context.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
"""
|
||||||
|
Helper class for handling formula autocomplete.
|
||||||
|
|
||||||
|
It's intended to use with rlcompleter.Completer. It allows finding global names using
|
||||||
|
lowercase searches, and adds function usage information to some results.
|
||||||
|
"""
|
||||||
|
import __builtin__
|
||||||
|
import inspect
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
# funcname is the function name, e.g. "MAX"
|
||||||
|
# argspec is the signature, e.g. "(arg, *more_args)"
|
||||||
|
# isgrist is a boolean for whether this function should be in Grist documentation.
|
||||||
|
Completion = namedtuple('Completion', ['funcname', 'argspec', 'isgrist'])
|
||||||
|
|
||||||
|
def is_grist_func(func):
|
||||||
|
try:
|
||||||
|
return inspect.getmodule(func).__name__.startswith('functions.')
|
||||||
|
except Exception, e:
|
||||||
|
return e
|
||||||
|
|
||||||
|
class AutocompleteContext(object):
|
||||||
|
def __init__(self, usercode_context):
|
||||||
|
# rlcompleter is case-sensitive. This is hard to work around while maintaining attribute
|
||||||
|
# lookups. As a middle ground, we only introduce lowercase versions of all global names.
|
||||||
|
self._context = {
|
||||||
|
key: value for key, value in usercode_context.iteritems()
|
||||||
|
# Don't propose unimplemented functions in autocomplete
|
||||||
|
if not (value and callable(value) and getattr(value, 'unimplemented', None))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare detailed Completion objects for functions where we can supply more info.
|
||||||
|
# TODO It would be nice to include builtin functions too, but getargspec doesn't work there.
|
||||||
|
self._functions = {}
|
||||||
|
for key, value in self._context.iteritems():
|
||||||
|
if value and callable(value):
|
||||||
|
argspec = inspect.formatargspec(*inspect.getargspec(value))
|
||||||
|
self._functions[key] = Completion(key, argspec, is_grist_func(value))
|
||||||
|
|
||||||
|
# Add in the important UserTable methods, with custom friendlier descriptions.
|
||||||
|
self._functions['.lookupOne'] = Completion('.lookupOne', '(colName=<value>, ...)', True)
|
||||||
|
self._functions['.lookupRecords'] = Completion('.lookupRecords', '(colName=<value>, ...)', True)
|
||||||
|
|
||||||
|
# Remember the original name for each lowercase one.
|
||||||
|
self._lowercase = {}
|
||||||
|
for key in self._context:
|
||||||
|
lower = key.lower()
|
||||||
|
if lower == key:
|
||||||
|
continue
|
||||||
|
if lower not in self._context and lower not in __builtin__.__dict__:
|
||||||
|
self._lowercase[lower] = key
|
||||||
|
else:
|
||||||
|
# This is still good enough to find a match for, and translate back to the original.
|
||||||
|
# It allows rlcompleter to match e.g. 'max' against 'max', 'Max', and 'MAX' (using keys
|
||||||
|
# 'max', 'max*', and 'max**', respectively).
|
||||||
|
lower += '*'
|
||||||
|
if lower in self._lowercase:
|
||||||
|
lower += '*'
|
||||||
|
self._lowercase[lower] = key
|
||||||
|
|
||||||
|
# Add the lowercase names to the context, and to the detailed completions in _functions.
|
||||||
|
for lower, key in self._lowercase.iteritems():
|
||||||
|
self._context[lower] = self._context[key]
|
||||||
|
if key in self._functions:
|
||||||
|
self._functions[lower] = self._functions[key]
|
||||||
|
|
||||||
|
def get_context(self):
|
||||||
|
return self._context
|
||||||
|
|
||||||
|
def process_result(self, result):
|
||||||
|
# Callables are returned by rlcompleter with a trailing "(".
|
||||||
|
if result.endswith('('):
|
||||||
|
funcname = result[0:-1]
|
||||||
|
dot = funcname.rfind(".")
|
||||||
|
key = funcname[dot:] if dot >= 0 else funcname
|
||||||
|
completion = self._functions.get(key)
|
||||||
|
# Return the detailed completion if we have it, or the result string otherwise.
|
||||||
|
if completion:
|
||||||
|
# For methods (eg ".lookupOne"), use the original result as funcname (eg "Foo.lookupOne").
|
||||||
|
if dot >= 0:
|
||||||
|
completion = completion._replace(funcname=funcname)
|
||||||
|
return tuple(completion)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Return translation from lowercase if there is one, or the result string otherwise.
|
||||||
|
return self._lowercase.get(result, result)
|
@ -1,3 +1,4 @@
|
|||||||
|
# pylint:disable=too-many-lines
|
||||||
"""
|
"""
|
||||||
The data engine ties the code generated from the schema with the document data, and with
|
The data engine ties the code generated from the schema with the document data, and with
|
||||||
dependency tracking.
|
dependency tracking.
|
||||||
@ -5,16 +6,17 @@ dependency tracking.
|
|||||||
import contextlib
|
import contextlib
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
|
import rlcompleter
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from collections import namedtuple, OrderedDict, Hashable
|
from collections import namedtuple, OrderedDict, Hashable
|
||||||
from sortedcontainers import SortedSet
|
from sortedcontainers import SortedSet
|
||||||
|
|
||||||
import time
|
|
||||||
import rlcompleter
|
|
||||||
import acl
|
import acl
|
||||||
import actions
|
import actions
|
||||||
import action_obj
|
import action_obj
|
||||||
|
from autocomplete_context import AutocompleteContext
|
||||||
from codebuilder import DOLLAR_REGEX
|
from codebuilder import DOLLAR_REGEX
|
||||||
import depend
|
import depend
|
||||||
import docactions
|
import docactions
|
||||||
@ -232,6 +234,9 @@ class Engine(object):
|
|||||||
# current cell.
|
# current cell.
|
||||||
self._cell_required_error = None
|
self._cell_required_error = None
|
||||||
|
|
||||||
|
# Initial empty context for autocompletions; we update it when we generate the usercode module.
|
||||||
|
self._autocomplete_context = AutocompleteContext({})
|
||||||
|
|
||||||
def load_empty(self):
|
def load_empty(self):
|
||||||
"""
|
"""
|
||||||
Initialize an empty document, e.g. a newly-created one.
|
Initialize an empty document, e.g. a newly-created one.
|
||||||
@ -1016,6 +1021,9 @@ class Engine(object):
|
|||||||
self._repl.locals.update(self.gencode.usercode.__dict__)
|
self._repl.locals.update(self.gencode.usercode.__dict__)
|
||||||
self.gencode.usercode.__dict__.update(self._repl.locals)
|
self.gencode.usercode.__dict__.update(self._repl.locals)
|
||||||
|
|
||||||
|
# Update the context used for autocompletions.
|
||||||
|
self._autocomplete_context = AutocompleteContext(self.gencode.usercode.__dict__)
|
||||||
|
|
||||||
# TODO: Whenever schema changes, we need to adjust the ACL resources to remove or rename
|
# TODO: Whenever schema changes, we need to adjust the ACL resources to remove or rename
|
||||||
# tableIds and colIds.
|
# tableIds and colIds.
|
||||||
|
|
||||||
@ -1205,8 +1213,9 @@ class Engine(object):
|
|||||||
if txt == '$':
|
if txt == '$':
|
||||||
tweaked_txt = 'rec.'
|
tweaked_txt = 'rec.'
|
||||||
table = self.tables[table_id]
|
table = self.tables[table_id]
|
||||||
context = {'rec': table.sample_record}
|
|
||||||
context.update(self.gencode.usercode.__dict__)
|
context = self._autocomplete_context.get_context()
|
||||||
|
context['rec'] = table.sample_record
|
||||||
|
|
||||||
completer = rlcompleter.Completer(context)
|
completer = rlcompleter.Completer(context)
|
||||||
results = []
|
results = []
|
||||||
@ -1219,11 +1228,12 @@ class Engine(object):
|
|||||||
break
|
break
|
||||||
if skipped_completions.search(result):
|
if skipped_completions.search(result):
|
||||||
continue
|
continue
|
||||||
results.append(result)
|
results.append(self._autocomplete_context.process_result(result))
|
||||||
|
|
||||||
# If we changed the prefix (expanding the $ symbol) we now need to change it back.
|
# If we changed the prefix (expanding the $ symbol) we now need to change it back.
|
||||||
if tweaked_txt != txt:
|
if tweaked_txt != txt:
|
||||||
results = [txt + result[len(tweaked_txt):] for result in results]
|
results = [txt + result[len(tweaked_txt):] for result in results]
|
||||||
results.sort()
|
results.sort(key=lambda r: r[0] if type(r) == tuple else r)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _get_undo_checkpoint(self):
|
def _get_undo_checkpoint(self):
|
||||||
|
@ -9,9 +9,11 @@ import re
|
|||||||
|
|
||||||
import column
|
import column
|
||||||
from functions import date # pylint: disable=import-error
|
from functions import date # pylint: disable=import-error
|
||||||
|
from functions.unimplemented import unimplemented
|
||||||
from usertypes import AltText # pylint: disable=import-error
|
from usertypes import AltText # pylint: disable=import-error
|
||||||
from records import Record, RecordSet
|
from records import Record, RecordSet
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def ISBLANK(value):
|
def ISBLANK(value):
|
||||||
"""
|
"""
|
||||||
Returns whether a value refers to an empty cell. It isn't implemented in Grist. To check for an
|
Returns whether a value refers to an empty cell. It isn't implemented in Grist. To check for an
|
||||||
@ -486,6 +488,7 @@ def NA():
|
|||||||
return float('nan')
|
return float('nan')
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TYPE(value):
|
def TYPE(value):
|
||||||
"""
|
"""
|
||||||
Returns a number associated with the type of data passed into the function. This is not
|
Returns a number associated with the type of data passed into the function. This is not
|
||||||
@ -493,6 +496,7 @@ def TYPE(value):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def CELL(info_type, reference):
|
def CELL(info_type, reference):
|
||||||
"""
|
"""
|
||||||
Returns the requested information about the specified cell. This is not implemented in Grist
|
Returns the requested information about the specified cell. This is not implemented in Grist
|
||||||
|
@ -1,57 +1,72 @@
|
|||||||
# pylint: disable=redefined-builtin, line-too-long
|
# pylint: disable=redefined-builtin, line-too-long
|
||||||
|
from unimplemented import unimplemented
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def ADDRESS(row, column, absolute_relative_mode, use_a1_notation, sheet):
|
def ADDRESS(row, column, absolute_relative_mode, use_a1_notation, sheet):
|
||||||
"""Returns a cell reference as a string."""
|
"""Returns a cell reference as a string."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def CHOOSE(index, choice1, choice2):
|
def CHOOSE(index, choice1, choice2):
|
||||||
"""Returns an element from a list of choices based on index."""
|
"""Returns an element from a list of choices based on index."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def COLUMN(cell_reference=None):
|
def COLUMN(cell_reference=None):
|
||||||
"""Returns the column number of a specified cell, with `A=1`."""
|
"""Returns the column number of a specified cell, with `A=1`."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def COLUMNS(range):
|
def COLUMNS(range):
|
||||||
"""Returns the number of columns in a specified array or range."""
|
"""Returns the number of columns in a specified array or range."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def GETPIVOTDATA(value_name, any_pivot_table_cell, original_column_1, pivot_item_1=None, *args):
|
def GETPIVOTDATA(value_name, any_pivot_table_cell, original_column_1, pivot_item_1=None, *args):
|
||||||
"""Extracts an aggregated value from a pivot table that corresponds to the specified row and column headings."""
|
"""Extracts an aggregated value from a pivot table that corresponds to the specified row and column headings."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def HLOOKUP(search_key, range, index, is_sorted):
|
def HLOOKUP(search_key, range, index, is_sorted):
|
||||||
"""Horizontal lookup. Searches across the first row of a range for a key and returns the value of a specified cell in the column found."""
|
"""Horizontal lookup. Searches across the first row of a range for a key and returns the value of a specified cell in the column found."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def HYPERLINK(url, link_label):
|
def HYPERLINK(url, link_label):
|
||||||
"""Creates a hyperlink inside a cell."""
|
"""Creates a hyperlink inside a cell."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def INDEX(reference, row, column):
|
def INDEX(reference, row, column):
|
||||||
"""Returns the content of a cell, specified by row and column offset."""
|
"""Returns the content of a cell, specified by row and column offset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def INDIRECT(cell_reference_as_string):
|
def INDIRECT(cell_reference_as_string):
|
||||||
"""Returns a cell reference specified by a string."""
|
"""Returns a cell reference specified by a string."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def LOOKUP(search_key, search_range_or_search_result_array, result_range=None):
|
def LOOKUP(search_key, search_range_or_search_result_array, result_range=None):
|
||||||
"""Looks through a row or column for a key and returns the value of the cell in a result range located in the same position as the search row or column."""
|
"""Looks through a row or column for a key and returns the value of the cell in a result range located in the same position as the search row or column."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def MATCH(search_key, range, search_type):
|
def MATCH(search_key, range, search_type):
|
||||||
"""Returns the relative position of an item in a range that matches a specified value."""
|
"""Returns the relative position of an item in a range that matches a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def OFFSET(cell_reference, offset_rows, offset_columns, height, width):
|
def OFFSET(cell_reference, offset_rows, offset_columns, height, width):
|
||||||
"""Returns a range reference shifted a specified number of rows and columns from a starting cell reference."""
|
"""Returns a range reference shifted a specified number of rows and columns from a starting cell reference."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def ROW(cell_reference):
|
def ROW(cell_reference):
|
||||||
"""Returns the row number of a specified cell."""
|
"""Returns the row number of a specified cell."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def ROWS(range):
|
def ROWS(range):
|
||||||
"""Returns the number of rows in a specified array or range."""
|
"""Returns the number of rows in a specified array or range."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -7,6 +7,7 @@ import operator
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from functions.info import ISNUMBER, ISLOGICAL
|
from functions.info import ISNUMBER, ISLOGICAL
|
||||||
|
from functions.unimplemented import unimplemented
|
||||||
import roman
|
import roman
|
||||||
|
|
||||||
# Iterates through elements of iterable arguments, or through individual args when not iterable.
|
# Iterates through elements of iterable arguments, or through individual args when not iterable.
|
||||||
@ -727,6 +728,7 @@ def SQRTPI(value):
|
|||||||
"""
|
"""
|
||||||
return _math.sqrt(_math.pi * value)
|
return _math.sqrt(_math.pi * value)
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SUBTOTAL(function_code, range1, range2):
|
def SUBTOTAL(function_code, range1, range2):
|
||||||
"""
|
"""
|
||||||
Returns a subtotal for a vertical range of cells using a specified aggregation function.
|
Returns a subtotal for a vertical range of cells using a specified aggregation function.
|
||||||
@ -755,12 +757,14 @@ def SUM(value1, *more_values):
|
|||||||
return sum(_chain_numeric_a(value1, *more_values))
|
return sum(_chain_numeric_a(value1, *more_values))
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SUMIF(records, criterion, sum_range):
|
def SUMIF(records, criterion, sum_range):
|
||||||
"""
|
"""
|
||||||
Returns a conditional sum across a range.
|
Returns a conditional sum across a range.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SUMIFS(sum_range, criteria_range1, criterion1, *args):
|
def SUMIFS(sum_range, criteria_range1, criterion1, *args):
|
||||||
"""
|
"""
|
||||||
Returns the sum of a range depending on multiple criteria.
|
Returns the sum of a range depending on multiple criteria.
|
||||||
@ -782,6 +786,7 @@ def SUMPRODUCT(array1, *more_arrays):
|
|||||||
"""
|
"""
|
||||||
return sum(reduce(operator.mul, values) for values in itertools.izip(array1, *more_arrays))
|
return sum(reduce(operator.mul, values) for values in itertools.izip(array1, *more_arrays))
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SUMSQ(value1, value2):
|
def SUMSQ(value1, value2):
|
||||||
"""
|
"""
|
||||||
Returns the sum of the squares of a series of numbers and/or cells.
|
Returns the sum of the squares of a series of numbers and/or cells.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
from math import _chain, _chain_numeric, _chain_numeric_a
|
from math import _chain, _chain_numeric, _chain_numeric_a
|
||||||
from info import ISNUMBER, ISLOGICAL
|
from info import ISNUMBER, ISLOGICAL
|
||||||
from date import DATE # pylint: disable=unused-import
|
from date import DATE # pylint: disable=unused-import
|
||||||
|
from unimplemented import unimplemented
|
||||||
|
|
||||||
def _average(iterable):
|
def _average(iterable):
|
||||||
total, count = 0.0, 0
|
total, count = 0.0, 0
|
||||||
@ -24,6 +24,7 @@ def _default_if_empty(iterable, default):
|
|||||||
yield default
|
yield default
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def AVEDEV(value1, value2):
|
def AVEDEV(value1, value2):
|
||||||
"""Calculates the average of the magnitudes of deviations of data from a dataset's mean."""
|
"""Calculates the average of the magnitudes of deviations of data from a dataset's mean."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -95,14 +96,17 @@ def AVERAGE_WEIGHTED(pairs):
|
|||||||
return sum_value / sum_weight
|
return sum_value / sum_weight
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def AVERAGEIF(criteria_range, criterion, average_range=None):
|
def AVERAGEIF(criteria_range, criterion, average_range=None):
|
||||||
"""Returns the average of a range depending on criteria."""
|
"""Returns the average of a range depending on criteria."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def AVERAGEIFS(average_range, criteria_range1, criterion1, *args):
|
def AVERAGEIFS(average_range, criteria_range1, criterion1, *args):
|
||||||
"""Returns the average of a range depending on multiple criteria."""
|
"""Returns the average of a range depending on multiple criteria."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def BINOMDIST(num_successes, num_trials, prob_success, cumulative):
|
def BINOMDIST(num_successes, num_trials, prob_success, cumulative):
|
||||||
"""
|
"""
|
||||||
Calculates the probability of drawing a certain number of successes (or a maximum number of
|
Calculates the probability of drawing a certain number of successes (or a maximum number of
|
||||||
@ -111,10 +115,12 @@ def BINOMDIST(num_successes, num_trials, prob_success, cumulative):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def CONFIDENCE(alpha, standard_deviation, pop_size):
|
def CONFIDENCE(alpha, standard_deviation, pop_size):
|
||||||
"""Calculates the width of half the confidence interval for a normal distribution."""
|
"""Calculates the width of half the confidence interval for a normal distribution."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def CORREL(data_y, data_x):
|
def CORREL(data_y, data_x):
|
||||||
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
|
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -156,22 +162,27 @@ def COUNTA(value, *more_values):
|
|||||||
return sum(1 for v in _chain(value, *more_values))
|
return sum(1 for v in _chain(value, *more_values))
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def COVAR(data_y, data_x):
|
def COVAR(data_y, data_x):
|
||||||
"""Calculates the covariance of a dataset."""
|
"""Calculates the covariance of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def CRITBINOM(num_trials, prob_success, target_prob):
|
def CRITBINOM(num_trials, prob_success, target_prob):
|
||||||
"""Calculates the smallest value for which the cumulative binomial distribution is greater than or equal to a specified criteria."""
|
"""Calculates the smallest value for which the cumulative binomial distribution is greater than or equal to a specified criteria."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def DEVSQ(value1, value2):
|
def DEVSQ(value1, value2):
|
||||||
"""Calculates the sum of squares of deviations based on a sample."""
|
"""Calculates the sum of squares of deviations based on a sample."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def EXPONDIST(x, lambda_, cumulative):
|
def EXPONDIST(x, lambda_, cumulative):
|
||||||
"""Returns the value of the exponential distribution function with a specified lambda at a specified value."""
|
"""Returns the value of the exponential distribution function with a specified lambda at a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def F_DIST(x, degrees_freedom1, degrees_freedom2, cumulative):
|
def F_DIST(x, degrees_freedom1, degrees_freedom2, cumulative):
|
||||||
"""
|
"""
|
||||||
Calculates the left-tailed F probability distribution (degree of diversity) for two data sets
|
Calculates the left-tailed F probability distribution (degree of diversity) for two data sets
|
||||||
@ -180,6 +191,7 @@ def F_DIST(x, degrees_freedom1, degrees_freedom2, cumulative):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def F_DIST_RT(x, degrees_freedom1, degrees_freedom2):
|
def F_DIST_RT(x, degrees_freedom1, degrees_freedom2):
|
||||||
"""
|
"""
|
||||||
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
|
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
|
||||||
@ -188,6 +200,7 @@ def F_DIST_RT(x, degrees_freedom1, degrees_freedom2):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def FDIST(x, degrees_freedom1, degrees_freedom2):
|
def FDIST(x, degrees_freedom1, degrees_freedom2):
|
||||||
"""
|
"""
|
||||||
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
|
Calculates the right-tailed F probability distribution (degree of diversity) for two data sets
|
||||||
@ -196,46 +209,57 @@ def FDIST(x, degrees_freedom1, degrees_freedom2):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def FISHER(value):
|
def FISHER(value):
|
||||||
"""Returns the Fisher transformation of a specified value."""
|
"""Returns the Fisher transformation of a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def FISHERINV(value):
|
def FISHERINV(value):
|
||||||
"""Returns the inverse Fisher transformation of a specified value."""
|
"""Returns the inverse Fisher transformation of a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def FORECAST(x, data_y, data_x):
|
def FORECAST(x, data_y, data_x):
|
||||||
"""Calculates the expected y-value for a specified x based on a linear regression of a dataset."""
|
"""Calculates the expected y-value for a specified x based on a linear regression of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def GEOMEAN(value1, value2):
|
def GEOMEAN(value1, value2):
|
||||||
"""Calculates the geometric mean of a dataset."""
|
"""Calculates the geometric mean of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def HARMEAN(value1, value2):
|
def HARMEAN(value1, value2):
|
||||||
"""Calculates the harmonic mean of a dataset."""
|
"""Calculates the harmonic mean of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def HYPGEOMDIST(num_successes, num_draws, successes_in_pop, pop_size):
|
def HYPGEOMDIST(num_successes, num_draws, successes_in_pop, pop_size):
|
||||||
"""Calculates the probability of drawing a certain number of successes in a certain number of tries given a population of a certain size containing a certain number of successes, without replacement of draws."""
|
"""Calculates the probability of drawing a certain number of successes in a certain number of tries given a population of a certain size containing a certain number of successes, without replacement of draws."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def INTERCEPT(data_y, data_x):
|
def INTERCEPT(data_y, data_x):
|
||||||
"""Calculates the y-value at which the line resulting from linear regression of a dataset will intersect the y-axis (x=0)."""
|
"""Calculates the y-value at which the line resulting from linear regression of a dataset will intersect the y-axis (x=0)."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def KURT(value1, value2):
|
def KURT(value1, value2):
|
||||||
"""Calculates the kurtosis of a dataset, which describes the shape, and in particular the "peakedness" of that dataset."""
|
"""Calculates the kurtosis of a dataset, which describes the shape, and in particular the "peakedness" of that dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def LARGE(data, n):
|
def LARGE(data, n):
|
||||||
"""Returns the nth largest element from a data set, where n is user-defined."""
|
"""Returns the nth largest element from a data set, where n is user-defined."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def LOGINV(x, mean, standard_deviation):
|
def LOGINV(x, mean, standard_deviation):
|
||||||
"""Returns the value of the inverse log-normal cumulative distribution with given mean and standard deviation at a specified value."""
|
"""Returns the value of the inverse log-normal cumulative distribution with given mean and standard deviation at a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def LOGNORMDIST(x, mean, standard_deviation):
|
def LOGNORMDIST(x, mean, standard_deviation):
|
||||||
"""Returns the value of the log-normal cumulative distribution with given mean and standard deviation at a specified value."""
|
"""Returns the value of the log-normal cumulative distribution with given mean and standard deviation at a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -364,14 +388,17 @@ def MINA(value, *more_values):
|
|||||||
return min(_default_if_empty(_chain_numeric_a(value, *more_values), 0))
|
return min(_default_if_empty(_chain_numeric_a(value, *more_values), 0))
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def MODE(value1, value2):
|
def MODE(value1, value2):
|
||||||
"""Returns the most commonly occurring value in a dataset."""
|
"""Returns the most commonly occurring value in a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def NEGBINOMDIST(num_failures, num_successes, prob_success):
|
def NEGBINOMDIST(num_failures, num_successes, prob_success):
|
||||||
"""Calculates the probability of drawing a certain number of failures before a certain number of successes given a probability of success in independent trials."""
|
"""Calculates the probability of drawing a certain number of failures before a certain number of successes given a probability of success in independent trials."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def NORMDIST(x, mean, standard_deviation, cumulative):
|
def NORMDIST(x, mean, standard_deviation, cumulative):
|
||||||
"""
|
"""
|
||||||
Returns the value of the normal distribution function (or normal cumulative distribution
|
Returns the value of the normal distribution function (or normal cumulative distribution
|
||||||
@ -379,42 +406,52 @@ def NORMDIST(x, mean, standard_deviation, cumulative):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def NORMINV(x, mean, standard_deviation):
|
def NORMINV(x, mean, standard_deviation):
|
||||||
"""Returns the value of the inverse normal distribution function for a specified value, mean, and standard deviation."""
|
"""Returns the value of the inverse normal distribution function for a specified value, mean, and standard deviation."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def NORMSDIST(x):
|
def NORMSDIST(x):
|
||||||
"""Returns the value of the standard normal cumulative distribution function for a specified value."""
|
"""Returns the value of the standard normal cumulative distribution function for a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def NORMSINV(x):
|
def NORMSINV(x):
|
||||||
"""Returns the value of the inverse standard normal distribution function for a specified value."""
|
"""Returns the value of the inverse standard normal distribution function for a specified value."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PEARSON(data_y, data_x):
|
def PEARSON(data_y, data_x):
|
||||||
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
|
"""Calculates r, the Pearson product-moment correlation coefficient of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PERCENTILE(data, percentile):
|
def PERCENTILE(data, percentile):
|
||||||
"""Returns the value at a given percentile of a dataset."""
|
"""Returns the value at a given percentile of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PERCENTRANK(data, value, significant_digits=None):
|
def PERCENTRANK(data, value, significant_digits=None):
|
||||||
"""Returns the percentage rank (percentile) of a specified value in a dataset."""
|
"""Returns the percentage rank (percentile) of a specified value in a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PERCENTRANK_EXC(data, value, significant_digits=None):
|
def PERCENTRANK_EXC(data, value, significant_digits=None):
|
||||||
"""Returns the percentage rank (percentile) from 0 to 1 exclusive of a specified value in a dataset."""
|
"""Returns the percentage rank (percentile) from 0 to 1 exclusive of a specified value in a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PERCENTRANK_INC(data, value, significant_digits=None):
|
def PERCENTRANK_INC(data, value, significant_digits=None):
|
||||||
"""Returns the percentage rank (percentile) from 0 to 1 inclusive of a specified value in a dataset."""
|
"""Returns the percentage rank (percentile) from 0 to 1 inclusive of a specified value in a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PERMUT(n, k):
|
def PERMUT(n, k):
|
||||||
"""Returns the number of ways to choose some number of objects from a pool of a given size of objects, considering order."""
|
"""Returns the number of ways to choose some number of objects from a pool of a given size of objects, considering order."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def POISSON(x, mean, cumulative):
|
def POISSON(x, mean, cumulative):
|
||||||
"""
|
"""
|
||||||
Returns the value of the Poisson distribution function (or Poisson cumulative distribution
|
Returns the value of the Poisson distribution function (or Poisson cumulative distribution
|
||||||
@ -422,42 +459,52 @@ def POISSON(x, mean, cumulative):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def PROB(data, probabilities, low_limit, high_limit=None):
|
def PROB(data, probabilities, low_limit, high_limit=None):
|
||||||
"""Given a set of values and corresponding probabilities, calculates the probability that a value chosen at random falls between two limits."""
|
"""Given a set of values and corresponding probabilities, calculates the probability that a value chosen at random falls between two limits."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def QUARTILE(data, quartile_number):
|
def QUARTILE(data, quartile_number):
|
||||||
"""Returns a value nearest to a specified quartile of a dataset."""
|
"""Returns a value nearest to a specified quartile of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def RANK(value, data, is_ascending=None):
|
def RANK(value, data, is_ascending=None):
|
||||||
"""Returns the rank of a specified value in a dataset."""
|
"""Returns the rank of a specified value in a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def RANK_AVG(value, data, is_ascending=None):
|
def RANK_AVG(value, data, is_ascending=None):
|
||||||
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the average rank of the entries will be returned."""
|
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the average rank of the entries will be returned."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def RANK_EQ(value, data, is_ascending=None):
|
def RANK_EQ(value, data, is_ascending=None):
|
||||||
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the top rank of the entries will be returned."""
|
"""Returns the rank of a specified value in a dataset. If there is more than one entry of the same value in the dataset, the top rank of the entries will be returned."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def RSQ(data_y, data_x):
|
def RSQ(data_y, data_x):
|
||||||
"""Calculates the square of r, the Pearson product-moment correlation coefficient of a dataset."""
|
"""Calculates the square of r, the Pearson product-moment correlation coefficient of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SKEW(value1, value2):
|
def SKEW(value1, value2):
|
||||||
"""Calculates the skewness of a dataset, which describes the symmetry of that dataset about the mean."""
|
"""Calculates the skewness of a dataset, which describes the symmetry of that dataset about the mean."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SLOPE(data_y, data_x):
|
def SLOPE(data_y, data_x):
|
||||||
"""Calculates the slope of the line resulting from linear regression of a dataset."""
|
"""Calculates the slope of the line resulting from linear regression of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def SMALL(data, n):
|
def SMALL(data, n):
|
||||||
"""Returns the nth smallest element from a data set, where n is user-defined."""
|
"""Returns the nth smallest element from a data set, where n is user-defined."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def STANDARDIZE(value, mean, standard_deviation):
|
def STANDARDIZE(value, mean, standard_deviation):
|
||||||
"""Calculates the normalized equivalent of a random variable given mean and standard deviation of the distribution."""
|
"""Calculates the normalized equivalent of a random variable given mean and standard deviation of the distribution."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@ -559,50 +606,62 @@ def STDEVPA(value, *more_values):
|
|||||||
"""
|
"""
|
||||||
return _stddev(list(_chain_numeric_a(value, *more_values)), 0)
|
return _stddev(list(_chain_numeric_a(value, *more_values)), 0)
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def STEYX(data_y, data_x):
|
def STEYX(data_y, data_x):
|
||||||
"""Calculates the standard error of the predicted y-value for each x in the regression of a dataset."""
|
"""Calculates the standard error of the predicted y-value for each x in the regression of a dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def T_INV(probability, degrees_freedom):
|
def T_INV(probability, degrees_freedom):
|
||||||
"""Calculates the negative inverse of the one-tailed TDIST function."""
|
"""Calculates the negative inverse of the one-tailed TDIST function."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def T_INV_2T(probability, degrees_freedom):
|
def T_INV_2T(probability, degrees_freedom):
|
||||||
"""Calculates the inverse of the two-tailed TDIST function."""
|
"""Calculates the inverse of the two-tailed TDIST function."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TDIST(x, degrees_freedom, tails):
|
def TDIST(x, degrees_freedom, tails):
|
||||||
"""Calculates the probability for Student's t-distribution with a given input (x)."""
|
"""Calculates the probability for Student's t-distribution with a given input (x)."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TINV(probability, degrees_freedom):
|
def TINV(probability, degrees_freedom):
|
||||||
"""Calculates the inverse of the two-tailed TDIST function."""
|
"""Calculates the inverse of the two-tailed TDIST function."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TRIMMEAN(data, exclude_proportion):
|
def TRIMMEAN(data, exclude_proportion):
|
||||||
"""Calculates the mean of a dataset excluding some proportion of data from the high and low ends of the dataset."""
|
"""Calculates the mean of a dataset excluding some proportion of data from the high and low ends of the dataset."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TTEST(range1, range2, tails, type):
|
def TTEST(range1, range2, tails, type):
|
||||||
"""Returns the probability associated with t-test. Determines whether two samples are likely to have come from the same two underlying populations that have the same mean."""
|
"""Returns the probability associated with t-test. Determines whether two samples are likely to have come from the same two underlying populations that have the same mean."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def VAR(value1, value2):
|
def VAR(value1, value2):
|
||||||
"""Calculates the variance based on a sample."""
|
"""Calculates the variance based on a sample."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def VARA(value1, value2):
|
def VARA(value1, value2):
|
||||||
"""Calculates an estimate of variance based on a sample, setting text to the value `0`."""
|
"""Calculates an estimate of variance based on a sample, setting text to the value `0`."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def VARP(value1, value2):
|
def VARP(value1, value2):
|
||||||
"""Calculates the variance based on an entire population."""
|
"""Calculates the variance based on an entire population."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def VARPA(value1, value2):
|
def VARPA(value1, value2):
|
||||||
"""Calculates the variance based on an entire population, setting text to the value `0`."""
|
"""Calculates the variance based on an entire population, setting text to the value `0`."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def WEIBULL(x, shape, scale, cumulative):
|
def WEIBULL(x, shape, scale, cumulative):
|
||||||
"""
|
"""
|
||||||
Returns the value of the Weibull distribution function (or Weibull cumulative distribution
|
Returns the value of the Weibull distribution function (or Weibull cumulative distribution
|
||||||
@ -610,6 +669,7 @@ def WEIBULL(x, shape, scale, cumulative):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def ZTEST(data, value, standard_deviation):
|
def ZTEST(data, value, standard_deviation):
|
||||||
"""Returns the two-tailed P-value of a Z-test with standard distribution."""
|
"""Returns the two-tailed P-value of a Z-test with standard distribution."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -5,6 +5,7 @@ import dateutil.parser
|
|||||||
import numbers
|
import numbers
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from unimplemented import unimplemented
|
||||||
from usertypes import AltText # pylint: disable=import-error
|
from usertypes import AltText # pylint: disable=import-error
|
||||||
|
|
||||||
def CHAR(table_number):
|
def CHAR(table_number):
|
||||||
@ -499,6 +500,7 @@ def T(value):
|
|||||||
str(value) if isinstance(value, AltText) else "")
|
str(value) if isinstance(value, AltText) else "")
|
||||||
|
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
def TEXT(number, format_type):
|
def TEXT(number, format_type):
|
||||||
"""
|
"""
|
||||||
Converts a number into text according to a specified format. It is not yet implemented in
|
Converts a number into text according to a specified format. It is not yet implemented in
|
||||||
|
11
sandbox/grist/functions/unimplemented.py
Normal file
11
sandbox/grist/functions/unimplemented.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
Decorator that marks functions as not implemented. It sets func.unimplemented=True.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
@unimplemented
|
||||||
|
def func(...):
|
||||||
|
raise NotImplemented
|
||||||
|
"""
|
||||||
|
def unimplemented(func):
|
||||||
|
func.unimplemented = True
|
||||||
|
return func
|
@ -1,5 +1,4 @@
|
|||||||
import testsamples
|
import testsamples
|
||||||
import testutil
|
|
||||||
import test_engine
|
import test_engine
|
||||||
|
|
||||||
class TestCompletion(test_engine.EngineTestCase):
|
class TestCompletion(test_engine.EngineTestCase):
|
||||||
@ -24,18 +23,63 @@ class TestCompletion(test_engine.EngineTestCase):
|
|||||||
|
|
||||||
def test_function(self):
|
def test_function(self):
|
||||||
self.assertEqual(self.engine.autocomplete("MEDI", "Address"),
|
self.assertEqual(self.engine.autocomplete("MEDI", "Address"),
|
||||||
["MEDIAN("])
|
[('MEDIAN', '(value, *more_values)', True)])
|
||||||
|
self.assertEqual(self.engine.autocomplete("ma", "Address"), [
|
||||||
|
('MAX', '(value, *more_values)', True),
|
||||||
|
('MAXA', '(value, *more_values)', True),
|
||||||
|
'map(',
|
||||||
|
'math',
|
||||||
|
'max(',
|
||||||
|
])
|
||||||
|
|
||||||
def test_member(self):
|
def test_member(self):
|
||||||
self.assertEqual(self.engine.autocomplete("datetime.tz", "Address"),
|
self.assertEqual(self.engine.autocomplete("datetime.tz", "Address"),
|
||||||
["datetime.tzinfo("])
|
["datetime.tzinfo("])
|
||||||
|
|
||||||
|
def test_case_insensitive(self):
|
||||||
|
self.assertEqual(self.engine.autocomplete("medi", "Address"),
|
||||||
|
[('MEDIAN', '(value, *more_values)', True)])
|
||||||
|
self.assertEqual(self.engine.autocomplete("std", "Address"), [
|
||||||
|
('STDEV', '(value, *more_values)', True),
|
||||||
|
('STDEVA', '(value, *more_values)', True),
|
||||||
|
('STDEVP', '(value, *more_values)', True),
|
||||||
|
('STDEVPA', '(value, *more_values)', True)
|
||||||
|
])
|
||||||
|
self.assertEqual(self.engine.autocomplete("stu", "Address"),
|
||||||
|
["Students"])
|
||||||
|
|
||||||
|
# Add a table name whose lowercase version conflicts with a builtin.
|
||||||
|
self.apply_user_action(['AddTable', 'Max', []])
|
||||||
|
self.assertEqual(self.engine.autocomplete("max", "Address"), [
|
||||||
|
('MAX', '(value, *more_values)', True),
|
||||||
|
('MAXA', '(value, *more_values)', True),
|
||||||
|
'Max',
|
||||||
|
'max(',
|
||||||
|
])
|
||||||
|
self.assertEqual(self.engine.autocomplete("MAX", "Address"), [
|
||||||
|
('MAX', '(value, *more_values)', True),
|
||||||
|
('MAXA', '(value, *more_values)', True),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_suggest_globals_and_tables(self):
|
def test_suggest_globals_and_tables(self):
|
||||||
# Should suggest globals and table names.
|
# Should suggest globals and table names.
|
||||||
self.assertEqual(self.engine.autocomplete("ME", "Address"), ['MEDIAN('])
|
self.assertEqual(self.engine.autocomplete("ME", "Address"),
|
||||||
|
[('MEDIAN', '(value, *more_values)', True)])
|
||||||
self.assertEqual(self.engine.autocomplete("Ad", "Address"), ['Address'])
|
self.assertEqual(self.engine.autocomplete("Ad", "Address"), ['Address'])
|
||||||
self.assertGreaterEqual(set(self.engine.autocomplete("S", "Address")),
|
self.assertGreaterEqual(set(self.engine.autocomplete("S", "Address")), {
|
||||||
{'Schools', 'Students', 'SUM(', 'STDEV('})
|
'Schools',
|
||||||
|
'Students',
|
||||||
|
('SUM', '(value1, *more_values)', True),
|
||||||
|
('STDEV', '(value, *more_values)', True),
|
||||||
|
})
|
||||||
|
self.assertGreaterEqual(set(self.engine.autocomplete("s", "Address")), {
|
||||||
|
'Schools',
|
||||||
|
'Students',
|
||||||
|
'sum(',
|
||||||
|
('SUM', '(value1, *more_values)', True),
|
||||||
|
('STDEV', '(value, *more_values)', True),
|
||||||
|
})
|
||||||
self.assertEqual(self.engine.autocomplete("Addr", "Schools"), ['Address'])
|
self.assertEqual(self.engine.autocomplete("Addr", "Schools"), ['Address'])
|
||||||
|
|
||||||
def test_suggest_columns(self):
|
def test_suggest_columns(self):
|
||||||
@ -56,11 +100,16 @@ class TestCompletion(test_engine.EngineTestCase):
|
|||||||
|
|
||||||
def test_suggest_lookup_methods(self):
|
def test_suggest_lookup_methods(self):
|
||||||
# Should suggest lookup formulas for tables.
|
# Should suggest lookup formulas for tables.
|
||||||
self.assertEqual(self.engine.autocomplete("Address.", "Students"),
|
self.assertEqual(self.engine.autocomplete("Address.", "Students"), [
|
||||||
['Address.all', 'Address.lookupOne(', 'Address.lookupRecords('])
|
'Address.all',
|
||||||
|
('Address.lookupOne', '(colName=<value>, ...)', True),
|
||||||
|
('Address.lookupRecords', '(colName=<value>, ...)', True),
|
||||||
|
])
|
||||||
|
|
||||||
self.assertEqual(self.engine.autocomplete("Address.lookup", "Students"),
|
self.assertEqual(self.engine.autocomplete("Address.lookup", "Students"), [
|
||||||
['Address.lookupOne(', 'Address.lookupRecords('])
|
('Address.lookupOne', '(colName=<value>, ...)', True),
|
||||||
|
('Address.lookupRecords', '(colName=<value>, ...)', True),
|
||||||
|
])
|
||||||
|
|
||||||
def test_suggest_column_type_methods(self):
|
def test_suggest_column_type_methods(self):
|
||||||
# Should treat columns as correct types.
|
# Should treat columns as correct types.
|
||||||
|
Loading…
Reference in New Issue
Block a user