Skip to content

Commit

Permalink
Add format dropdown to Number.format.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdunkerley committed Feb 13, 2024
1 parent f86daf4 commit c578152
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ type Date_Time_Formatter
Date.parse "1 Nov '95" "d MMM ''yy{2099}" == (Date.new 2095 11 01)
@locale Locale.default_widget
from_simple_pattern : Text -> Locale -> Date_Time_Formatter ! Date_Time_Format_Parse_Error
from_simple_pattern pattern:Text locale:Locale=Locale.default =
from_simple_pattern pattern:Text='' locale:Locale=Locale.default =
used_locale = if locale == Locale.default then Locale.us else locale
FormatterCache.SIMPLE_FORMAT.get_or_set (FormatterCacheKey.new pattern used_locale.java_locale) _->
parsed = Tokenizer.tokenize pattern |> Parser.parse_simple_date_pattern
Expand Down Expand Up @@ -350,7 +350,6 @@ type Date_Time_Formatter
"iso_time" -> "(ISO time) HH:mm[:ss[.f]]"
other -> other


## PRIVATE
Date_Time_Formatter.from (that:Text) (locale:Locale = Locale.default) =
Date_Time_Formatter.from_simple_pattern that locale
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ polyglot java import org.enso.base.Time_Utils
## PRIVATE
interpret : Locale -> Vector (Common_Nodes | Standard_Date_Patterns | ISO_Week_Year_Patterns | Time_Patterns | Time_Zone_Patterns) -> Boolean -> DateTimeFormatter
interpret locale nodes prepare_defaults=True =
builder = DateTimeFormatterBuilder.new
builder = DateTimeFormatterBuilder.new.parseCaseInsensitive
interpret_node node = case node of
Common_Nodes.Literal text ->
builder.appendLiteral text
Expand Down
1 change: 1 addition & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Metadata.enso
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ make_single_choice values display=Display.Always =
make_option value = case value of
_ : Vector -> Choice.Option value.first value.second
_ : Text -> Choice.Option value value.pretty
_ : Choice -> value
Widget.Single_Choice (values.map make_option) Nothing display
34 changes: 30 additions & 4 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Widget_Helpers.enso
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import project.Data.Time.Date_Time_Formatter.Date_Time_Formatter
import project.Data.Time.Time_Of_Day.Time_Of_Day
import project.Meta
import project.Metadata.Widget
from project.Data.Boolean import Boolean, False, True
from project.Metadata import make_single_choice
from project.Metadata.Choice import Option

## PRIVATE
Creates a Regex / Text Widget for search and replace.
Expand Down Expand Up @@ -43,8 +45,10 @@ make_date_format_selector (date:Date=(Date.new 2012 3 14)) =
formats = ['d/M/yyyy', 'dd/MM/yyyy', 'd-MMM-yy', 'd MMMM yyyy', 'M/d/yyyy', 'MM/dd/yyyy', 'MMMM d, yyyy', 'yyyy-MM'].map f->
[f + " (e.g. " + date.format f + ")", f.pretty]
custom_locale_format =
format = Date_Time_Formatter.from 'd MMMM yyyy' locale=Locale.france
['d MMMM yyyy - with custom Locale (e.g. ' + date.format format + ')', "(Date_Time_Formatter.from 'd MMMM yyyy' locale=Locale.france)"]
format = Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy' locale=Locale.france
label = 'd MMMM yyyy - with custom Locale (e.g. ' + date.format format + ')'
code = "(Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy' locale=Locale.france)"
Option label code [["pattern", make_single_choice formats], ["locale", Locale.default_widget]]
week_date_formats = ['YYYY-ww-d', 'YYYY-ww', 'ddd, YYYY-ww'].map f->
format = Date_Time_Formatter.from_iso_week_date_pattern f
["ISO Week-based Date: " + f + " (e.g. " + date.format format + ")", "("+fqn+".from_iso_week_date_pattern " + f.pretty + ")"]
Expand All @@ -68,8 +72,10 @@ make_date_time_format_selector (date_time:Date_Time=(Date_Time.new 2012 3 14 15
formats = ['yyyy-MM-dd HH:mm:ss.f', 'yyyy-MM-dd HH:mm:ss.f TT', 'd/M/yyyy h:mm a', 'dd/MM/yyyy HH:mm:ss', 'd-MMM-yy HH:mm:ss', 'd-MMM-yy h:mm:ss a', 'd MMMM yyyy h:mm a', 'M/d/yyyy h:mm:ss a', 'MM/dd/yyyy HH:mm:ss']
mapped_formats = formats.map f-> [f + " (e.g. " + date_time.format f + ")", f.pretty]
custom_locale_format =
format = Date_Time_Formatter.from 'd MMMM yyyy HH:mm' locale=Locale.france
['d MMMM yyyy HH:mm - with custom Locale (e.g. ' + date_time.format format + ')', "(Date_Time_Formatter.from 'd MMMM yyyy HH:mm' locale=Locale.france)"]
format = Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy HH:mm' locale=Locale.france
label = 'd MMMM yyyy HH:mm - with custom Locale (e.g. ' + date_time.format format + ')'
code = "(Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy HH:mm' locale=Locale.france)"
Option label code [["pattern", make_single_choice mapped_formats], ["locale", Locale.default_widget]]
week_date_formats = ['YYYY-ww-d HH:mm:ss.f'].map f->
format = Date_Time_Formatter.from_iso_week_date_pattern f
["ISO Week-based Date-Time: " + f + " (e.g. " + date_time.format format + ")", "(Date_Time_Formatter.from_iso_week_date_pattern " + f.pretty + ")"]
Expand All @@ -86,3 +92,23 @@ make_time_format_selector (time:Time_Of_Day=(Time_Of_Day.new 13 30 55 123)) =
[f + " (e.g. " + time.format f + ")", f.pretty]

make_single_choice ([iso_format] + formats)

## PRIVATE
Create a Single_Choice Widget for selecting a format value.
make_format_chooser : Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> Widget
make_format_chooser include_number:Boolean=True include_date:Boolean=True include_date_time:Boolean=True include_time:Boolean=True include_boolean:Boolean=True =
numeric = if include_number.not then [] else
['0', '#,##0', '#,##0.00', '#,##0.00;(#,##0.00)'].map f-> [f + " (" + (1234.5678.format f) + ")", f.pretty]
date = if include_date.not then [] else
['d/M/yyyy', 'dd/MM/yyyy', 'M/d/yyyy', 'MM/dd/yyyy', 'd-MMM-yy', 'd MMMM yyyy'].map f-> [f + " (e.g. " + (Date.new 2012 3 14).format f + ")", f.pretty]
date_time = if include_date_time.not then [] else
['yyyy-MM-dd HH:mm:ss.f', 'yyyy-MM-dd HH:mm:ss.f TT', 'd/M/yyyy h:mm a', 'dd/MM/yyyy HH:mm:ss', 'MM/dd/yyyy HH:mm:ss'].map f-> [f + " (e.g. " + (Date_Time.new 2012 3 14 15 9 26 123).format f + ")", f.pretty]
custom_locale_format =
format = Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy HH:mm' locale=Locale.france
label = 'd MMMM yyyy HH:mm - with custom Locale (e.g. ' + date_time.format format + ')'
code = "(Date_Time_Formatter.from_simple_pattern 'd MMMM yyyy HH:mm' locale=Locale.france)"
Option label code [["pattern", make_single_choice (date+date_time)], ["locale", Locale.default_widget]]
time = if include_time.not then [] else
['HH:mm[:ss]', 'h:mm[:ss] a'].map f-> [f + " (e.g. " + (Time_Of_Day.new 13 30 55 123).format f + ")", f.pretty]
boolean = if include_boolean.not then [] else ['Yes|No', '1|0'].map f-> [f + " (Boolean)", f.pretty]
make_single_choice (numeric + date + date_time + time + custom_locale_format + boolean)
13 changes: 9 additions & 4 deletions distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Standard.Base.Errors.Unimplemented.Unimplemented
import Standard.Base.System.File.Generic.Writable_File.Writable_File
from Standard.Base.Metadata import make_single_choice
from Standard.Base.Runtime import assert
from Standard.Base.Widget_Helpers import make_delimiter_selector
from Standard.Base.Widget_Helpers import make_delimiter_selector, make_format_chooser

import Standard.Table.Data.Blank_Selector.Blank_Selector
import Standard.Table.Data.Calculations.Column_Operation.Column_Operation
Expand Down Expand Up @@ -2017,11 +2017,16 @@ type Table
table.parse "birthday" Value_Type.Date
@type (Widget_Helpers.parse_type_selector include_auto=False)
@columns Widget_Helpers.make_column_name_vector_selector
parse : Vector (Text | Integer | Regex) | Text | Integer | Regex -> Value_Type | Auto -> Text | Data_Formatter | Nothing -> Boolean -> Problem_Behavior -> Table
parse self columns=(self.columns . filter (c-> c.value_type.is_text) . map .name) type format=Nothing error_on_missing_columns=True on_problems=Report_Warning =
@format (make_format_chooser include_number=False)
parse : Vector (Text | Integer | Regex) | Text | Integer | Regex -> Value_Type | Auto -> Text | Data_Formatter -> Boolean -> Problem_Behavior -> Table
parse self columns=(self.columns . filter (c-> c.value_type.is_text) . map .name) type format:(Text|Data_Formatter)='' error_on_missing_columns=True on_problems=Report_Warning =
formatter = case format of
_ : Text -> if format.is_empty then Data_Formatter.Value else Data_Formatter.Value.with_format type format
_ : Data_Formatter -> format

selected = self.columns_helper.select_columns columns Case_Sensitivity.Default reorder=False error_on_missing_columns=error_on_missing_columns on_problems=on_problems error_on_empty=False . map self.make_column
selected.fold self table-> column_to_parse->
new_column = column_to_parse.parse type format on_problems
new_column = column_to_parse.parse type formatter on_problems
table.set new_column new_name=column_to_parse.name set_mode=Set_Mode.Update

## GROUP Standard.Base.Conversions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ type Data_Formatter
if formats.length != 2 then Error.throw (Illegal_Argument.Error "The `format` for Booleans must be a string with two values separated by `|`, for example: 'Yes|No'.") else
self.with_boolean_values true_values=[formats.at 0] false_values=[formats.at 1]
Auto ->
Error.throw (Illegal_Argument.Error "Cannot specify a `format` with type `Auto`.")
Error.throw (Illegal_Argument.Error "Specify a `type` to use a custom format.")
_ : Value_Type ->
Error.throw (Illegal_Argument.Error "Cannot specify a `format` for type `"+type.to_text+"`.")

Expand Down
12 changes: 5 additions & 7 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import Standard.Base.Errors.Unimplemented.Unimplemented
import Standard.Base.Runtime.Context
import Standard.Base.System.File.Generic.Writable_File.Writable_File
from Standard.Base.Metadata import make_single_choice
from Standard.Base.Widget_Helpers import make_delimiter_selector
from Standard.Base.Widget_Helpers import make_delimiter_selector, make_format_chooser

import project.Data.Aggregate_Column.Aggregate_Column
import project.Data.Blank_Selector.Blank_Selector
Expand Down Expand Up @@ -965,14 +965,12 @@ type Table
table.parse format=(Data_Formatter.Value.with_number_formatting decimal_point=',')
@columns Widget_Helpers.make_column_name_vector_selector
@type Widget_Helpers.parse_type_selector
parse : Vector (Text | Integer | Regex) | Text | Integer | Regex -> Value_Type | Auto -> Text | Data_Formatter | Nothing -> Boolean -> Problem_Behavior -> Table
parse self columns=(self.columns . filter (c-> c.value_type.is_text) . map .name) type=Auto format=Data_Formatter.Value error_on_missing_columns=True on_problems=Report_Warning =
@format (make_format_chooser include_number=False)
parse : Vector (Text | Integer | Regex) | Text | Integer | Regex -> Value_Type | Auto -> Text | Data_Formatter -> Boolean -> Problem_Behavior -> Table
parse self columns=(self.columns . filter (c-> c.value_type.is_text) . map .name) type=Auto format:(Text|Data_Formatter)='' error_on_missing_columns=True on_problems=Report_Warning =
formatter = case format of
_ : Text ->
Data_Formatter.Value.with_format type format
_ : Text -> if format.is_empty then Data_Formatter.Value else Data_Formatter.Value.with_format type format
_ : Data_Formatter -> format
Nothing -> Data_Formatter.Value
_ -> Error.throw (Illegal_Argument.Error "Invalid format type. Expected Text or Data_Formatter or Nothing.")

parser = formatter.make_value_type_parser type

Expand Down
1 change: 1 addition & 0 deletions test/Base_Tests/src/Data/Text/Parse_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ add_specs suite_builder =
"1999 1 1".parse_date "yyyy M d" . should_equal <| Date.new 1999 1 1
"1999-01-01".parse_date "yyyy M d" . should_fail_with Time_Error
"13 Jan 2023".parse_date "d MMM yyyy" . should_equal <| Date.new 2023 1 13
"13 JAN 2023".parse_date "d MMM yyyy" . should_equal <| Date.new 2023 1 13
"13 January 2023".parse_date "d MMMM yyyy" . should_equal <| Date.new 2023 1 13

group_builder.specify "Date_Time" <|
Expand Down

0 comments on commit c578152

Please sign in to comment.