@@ -87,7 +87,8 @@ define([
8787{ id :'add_row' , label :'Add row' , selection :FRAME_SELECT_TYPE . NONE , menuType :FRAME_EDIT_TYPE . ADD_ROW } ,
8888{ id :'delete' , label :'Delete' , selection :FRAME_SELECT_TYPE . MULTI , menuType :FRAME_EDIT_TYPE . DROP } ,
8989{ id :'rename' , label :'Rename' , selection :FRAME_SELECT_TYPE . NONE , menuType :FRAME_EDIT_TYPE . RENAME } ,
90- { id :'asType' , label :'As type' , selection :FRAME_SELECT_TYPE . NONE , axis :FRAME_AXIS . COLUMN , menuType :FRAME_EDIT_TYPE . AS_TYPE } ,
90+ { id :'as_type' , label :'As type' , selection :FRAME_SELECT_TYPE . NONE , axis :FRAME_AXIS . COLUMN , menuType :FRAME_EDIT_TYPE . AS_TYPE } ,
91+ { id :'to_datetime' , label :'To datetime' , selection :FRAME_SELECT_TYPE . SINGLE , axis :FRAME_AXIS . COLUMN , menuType :FRAME_EDIT_TYPE . TO_DATETIME } ,
9192{ id :'replace' , label :'Replace' , selection :FRAME_SELECT_TYPE . SINGLE , axis :FRAME_AXIS . COLUMN , menuType :FRAME_EDIT_TYPE . REPLACE } ,
9293{ id :'discretize' , label :'Discretize' , selection :FRAME_SELECT_TYPE . SINGLE , axis :FRAME_AXIS . COLUMN , numeric_only :true , menuType :FRAME_EDIT_TYPE . DISCRETIZE }
9394]
@@ -618,6 +619,7 @@ define([
618619case FRAME_EDIT_TYPE . RENAME :
619620case FRAME_EDIT_TYPE . REPLACE :
620621case FRAME_EDIT_TYPE . AS_TYPE :
622+ case FRAME_EDIT_TYPE . TO_DATETIME :
621623case FRAME_EDIT_TYPE . DISCRETIZE :
622624case FRAME_EDIT_TYPE . DATA_SHIFT :
623625case FRAME_EDIT_TYPE . SORT_INDEX :
@@ -1106,6 +1108,73 @@ define([
11061108$ ( that . wrapSelector ( '.vp-inner-popup-fill-row' ) ) . hide ( ) ;
11071109}
11081110} ) ;
1111+ } else if ( menuType === FRAME_EDIT_TYPE . TO_DATETIME ) {
1112+ // bind event for selecting format
1113+ $ ( this . wrapSelector ( '.vp-inner-popup-todt-format' ) ) . on ( 'change' , function ( ) {
1114+ let format = $ ( this ) . val ( ) ;
1115+ if ( format === 'auto' ) {
1116+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-dayfirst' ) ) . prop ( 'disabled' , false ) ;
1117+ } else {
1118+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-dayfirst' ) ) . prop ( 'disabled' , true ) ;
1119+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-dayfirst' ) ) . val ( '' ) ;
1120+ }
1121+
1122+ if ( format === 'typing' ) {
1123+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-format-typing' ) ) . prop ( 'disabled' , false ) ;
1124+ } else {
1125+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-format-typing' ) ) . prop ( 'disabled' , true ) ;
1126+ }
1127+ } ) ;
1128+
1129+ // bind event for checking add column
1130+ $ ( this . wrapSelector ( '.vp-inner-popup-todt-use-addcol' ) ) . on ( 'change' , function ( ) {
1131+ let checked = $ ( this ) . prop ( 'checked' ) ;
1132+ if ( checked === true ) {
1133+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-addcol-box' ) ) . show ( ) ;
1134+ } else {
1135+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-addcol-box' ) ) . hide ( ) ;
1136+ }
1137+ } ) ;
1138+
1139+ // Add column set event
1140+ $ ( this . wrapSelector ( '.vp-inner-popup-todt-addcol' ) ) . on ( 'click' , function ( ) {
1141+ let dateTypeList = [ // df[col].dt[{dateType}]
1142+ { label :'Year' , value :'year' } ,
1143+ { label :'Month' , value :'month' } ,
1144+ { label :'Day' , value :'day' } ,
1145+ { label :'Date' , value :'date' } ,
1146+ { label :'DayOfWeek' , value :'dayofweek' } ,
1147+ { label :'DayOfYear' , value :'dayofyear' } ,
1148+ { label :'DaysInMonth' , value :'daysinmonth' } ,
1149+ { label :'Quarter' , value :'quarter' } ,
1150+ { label :'Time' , value :'time' } ,
1151+ { label :'Hour' , value :'hour' } ,
1152+ { label :'Minute' , value :'minute' } ,
1153+ { label :'Second' , value :'second' } ,
1154+ { label :'Nanosecond' , value :'nanosecond' } ,
1155+ ] ;
1156+ let dateTypeOptionTag = new com_String ( ) ;
1157+ dateTypeList . forEach ( opt => {
1158+ dateTypeOptionTag . appendFormat ( '<option value="{0}">{1}</option>' , opt . value , opt . label ) ;
1159+ } ) ;
1160+
1161+ let addColItemTag = $ ( `<div class="vp-inner-popup-todt-addcol-item">
1162+ <input type="text" class="vp-input vp-inner-popup-todt-addcol-colname" placeholder="Type column name" />
1163+ <select class="vp-select vp-inner-popup-todt-addcol-type">
1164+ ${ dateTypeOptionTag . toString ( ) }
1165+ </select>
1166+ <span class="vp-icon-close-small vp-inner-popup-todt-addcol-del mt5 vp-cursor"></span>
1167+ </div>` ) ;
1168+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-addcol-content' ) ) . append ( addColItemTag ) ;
1169+ $ ( addColItemTag ) [ 0 ] . scrollIntoView ( ) ;
1170+
1171+ // bind event for deleting
1172+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-addcol-del' ) ) . off ( 'click' ) ;
1173+ $ ( that . wrapSelector ( '.vp-inner-popup-todt-addcol-del' ) ) . on ( 'click' , function ( ) {
1174+ // delete item
1175+ $ ( this ) . closest ( '.vp-inner-popup-todt-addcol-item' ) . remove ( ) ;
1176+ } ) ;
1177+ } ) ;
11091178}
11101179
11111180}
@@ -2113,6 +2182,75 @@ define([
21132182return content . toString ( ) ;
21142183}
21152184
2185+ renderToDatetime ( ) {
2186+ var content = new com_String ( ) ;
2187+ let formatList = [
2188+ { label :'Auto' , value :'auto' } ,
2189+ { label :'Year' , value :'%Y' } ,
2190+ { label :'Month' , value :'%m' } ,
2191+ { label :'Day' , value :'%d' } ,
2192+ { label :'Day Of Week' , value :'%w' } ,
2193+ { label :'%Y/%m/%d' , value :'%Y/%m/%d' } ,
2194+ { label :'%Y-%m-%d' , value :'%Y-%m-%d' } ,
2195+ { label :'%d/%m/%Y' , value :'%d/%m/%Y' } ,
2196+ { label :'%d-%m-%Y' , value :'%d-%m-%Y' } ,
2197+ { label :'Typing' , value :'typing' } ,
2198+ ] ;
2199+ let formatOptionTag = new com_String ( ) ;
2200+ formatList . forEach ( opt => {
2201+ formatOptionTag . appendFormat ( '<option value="{0}">{1}</option>' , opt . value , opt . label ) ;
2202+ } ) ;
2203+
2204+ content . appendFormat ( `<div class="vp-inner-popup-todt vp-grid-box">
2205+ <div class="vp-grid-col-110">
2206+ <label>Target column</label>
2207+ <input type="text" class="vp-input" value="{0}" readonly />
2208+ </div>
2209+ <div class="vp-grid-col-110">
2210+ <label>Format</label>
2211+ <div>
2212+ <select class="vp-inner-popup-todt-format">
2213+ {1}
2214+ </select>
2215+ <input type="text" class="vp-input vp-inner-popup-todt-format-typing" value="" placeholder="Type format" disabled/>
2216+ </div>
2217+ </div>
2218+ <div class="vp-grid-col-110">
2219+ <label>Day first</label>
2220+ <select class="vp-inner-popup-todt-dayfirst">
2221+ <option value="">Select option...</option>
2222+ <option value="True">True</option>
2223+ <option value="False">False</option>
2224+ </select>
2225+ </div>
2226+ <hr style="margin: 5px 0;"/>
2227+ <div>
2228+ <label>
2229+ <input type="checkbox" class="vp-inner-popup-todt-use-addcol" />
2230+ <span>
2231+ Add column with date type
2232+ </span>
2233+ </label>
2234+ </div>
2235+ <div class="vp-inner-popup-todt-addcol-box vp-grid-border-box" style="display: none;">
2236+ <div class="vp-inner-popup-todt-addcol-head">
2237+ <label>New column name</label>
2238+ <label>Date type</label>
2239+ <label></label>
2240+ </div>
2241+ <div class="vp-inner-popup-todt-addcol-content vp-scrollbar">
2242+
2243+ </div>
2244+ <button class="vp-button vp-inner-popup-todt-addcol">+ Add column</button>
2245+ </div>
2246+ </div>
2247+ ` , this . state . selected [ 0 ] . label , formatOptionTag . toString ( ) , ) ;
2248+
2249+ // set content
2250+ $ ( this . wrapSelector ( '.vp-inner-popup-body' ) ) . html ( content . toString ( ) ) ;
2251+ return content . toString ( ) ;
2252+ }
2253+
21162254renderFillNAPage ( ) {
21172255var content = new com_String ( ) ;
21182256content . appendFormatLine ( '<div class="{0}">' , 'vp-inner-popup-fillna-page' ) ;
@@ -2454,6 +2592,11 @@ define([
24542592title = 'Convert type' ;
24552593content = this . renderAsType ( ) ;
24562594break ;
2595+ case FRAME_EDIT_TYPE . TO_DATETIME :
2596+ title = 'Convert to datetime' ;
2597+ size = { width :500 , height :450 } ;
2598+ content = this . renderToDatetime ( ) ;
2599+ break ;
24572600case FRAME_EDIT_TYPE . FILL_NA :
24582601title = 'Fill NA' ;
24592602content = this . renderFillNAPage ( ) ;
@@ -2823,6 +2966,22 @@ define([
28232966}
28242967} ) ;
28252968break ;
2969+ case FRAME_EDIT_TYPE . TO_DATETIME :
2970+ content [ 'format' ] = $ ( this . wrapSelector ( '.vp-inner-popup-todt-format' ) ) . val ( ) ;
2971+ content [ 'format_typing' ] = $ ( this . wrapSelector ( '.vp-inner-popup-todt-format-typing' ) ) . val ( ) ;
2972+ content [ 'dayfirst' ] = $ ( this . wrapSelector ( '.vp-inner-popup-todt-dayfirst' ) ) . val ( ) ;
2973+ content [ 'use_addcol' ] = $ ( this . wrapSelector ( '.vp-inner-popup-todt-use-addcol' ) ) . prop ( 'checked' ) ;
2974+ var colList = [ ] ;
2975+ var addcolItemTags = $ ( this . wrapSelector ( '.vp-inner-popup-todt-addcol-item' ) ) ;
2976+ addcolItemTags && addcolItemTags . each ( ( idx , tag ) => {
2977+ let colName = $ ( tag ) . find ( '.vp-inner-popup-todt-addcol-colname' ) . val ( ) ;
2978+ let dateType = $ ( tag ) . find ( '.vp-inner-popup-todt-addcol-type' ) . val ( ) ;
2979+ if ( colName !== '' && dateType !== '' ) {
2980+ colList . push ( { colName :colName , dateType :dateType } ) ;
2981+ }
2982+ } ) ;
2983+ content [ 'collist' ] = colList ;
2984+ break ;
28262985case FRAME_EDIT_TYPE . DISCRETIZE :
28272986content [ 'input' ] = $ ( this . wrapSelector ( '.vp-inner-popup-input' ) ) . val ( ) ;
28282987content [ 'inputastext' ] = $ ( this . wrapSelector ( '.vp-inner-popup-inputastext' ) ) . prop ( 'checked' ) ;
@@ -3343,6 +3502,31 @@ define([
33433502} ) ;
33443503code . appendFormat ( "{0} = {1}.astype({{2}})" , tempObj , tempObj , astypeStr . toString ( ) ) ;
33453504break ;
3505+ case FRAME_EDIT_TYPE . TO_DATETIME :
3506+ code . appendFormat ( "{0}[{1}] = pd.to_datetime({2}[{3}]" , tempObj , selectedName , tempObj , selectedName ) ;
3507+ let optionList = [ ] ;
3508+ if ( content [ 'format' ] === 'auto' ) {
3509+ if ( content [ 'dayfirst' ] !== '' ) {
3510+ optionList . push ( `dayfirst=${ content [ 'dayfirst' ] } ` ) ;
3511+ }
3512+ } else if ( content [ 'format' ] === 'typing' ) {
3513+ if ( content [ 'format_typing' ] !== '' ) {
3514+ optionList . push ( `format='${ content [ 'format_typing' ] } '` ) ;
3515+ }
3516+ } else {
3517+ optionList . push ( `format='${ content [ 'format' ] } '` ) ;
3518+ }
3519+ if ( optionList . length > 0 ) {
3520+ code . appendFormat ( ', {0}' , optionList . join ( ', ' ) ) ;
3521+ }
3522+ code . append ( ')' ) ;
3523+ if ( content [ 'use_addcol' ] === true && content [ 'collist' ] . length > 0 ) {
3524+ content [ 'collist' ] . forEach ( obj => {
3525+ code . appendLine ( ) ;
3526+ code . appendFormat ( "{0}['{1}'] = {2}[{3}].dt.{4}" , tempObj , obj . colName , tempObj , selectedName , obj . dateType ) ;
3527+ } ) ;
3528+ }
3529+ break ;
33463530case FRAME_EDIT_TYPE . DISCRETIZE :
33473531let newColumn = com_util . convertToStr ( content [ 'input' ] , content [ 'inputastext' ] ) ;
33483532let method = content [ 'type' ] ;
@@ -3437,7 +3621,7 @@ define([
34373621var indexList = data . index ;
34383622var dataList = data . data ;
34393623
3440- columnList = columnList . map ( col => { return { label :col . label , type :col . dtype , code :col . value } } ) ;
3624+ columnList = columnList . map ( col => { return { label :col . label , type :col . dtype , code :col . value , isNumeric : col . is_numeric } } ) ;
34413625indexList = indexList . map ( idx => { return { label :idx , code :idx } } ) ;
34423626
34433627if ( ! more ) {
@@ -3453,6 +3637,12 @@ define([
34533637while ( colIdx < columnList . length ) {
34543638let col = columnList [ colIdx ] ;
34553639let colCode = col . code . slice ( 0 , colLevIdx + 1 ) . join ( ',' ) ;
3640+ var colIcon = '' ;
3641+ if ( col . isNumeric === true ) {
3642+ colIcon = '<span class="vp-fe-table-column-isnumeric vp-icon-numeric" title="Numeric column"></span>' ;
3643+ } else {
3644+ colIcon = '<span class="vp-fe-table-column-isnumeric vp-icon-non-numeric" title="Non-numeric column"></span>' ;
3645+ }
34563646let nextCol = columnList [ colIdx + 1 ] ;
34573647if ( nextCol && nextCol . code . slice ( 0 , colLevIdx + 1 ) . join ( ',' ) === colCode ) {
34583648colSpan ++ ;
@@ -3467,8 +3657,8 @@ define([
34673657} else {
34683658colClass = VP_FE_TABLE_COLUMN_GROUP ;
34693659}
3470- table . appendFormatLine ( '<th data-code="({0})" data-axis="{1}" data-type="{2}" data-parent="{3}" data-label="{4}" colspan="{7}">{8}</th>'
3471- , colCode , FRAME_AXIS . COLUMN , col . type , col . label [ colLevIdx - 1 ] , col . label [ colLevIdx ] , colClass , selected , colSpan , col . label [ colLevIdx ] ) ;
3660+ table . appendFormatLine ( '<th data-code="({0})" data-axis="{1}" data-type="{2}" data-parent="{3}" data-label="{4}" colspan="{7}">{8}{9} </th>'
3661+ , colCode , FRAME_AXIS . COLUMN , col . type , col . label [ colLevIdx - 1 ] , col . label [ colLevIdx ] , colClass , selected , colSpan , colIcon , col . label [ colLevIdx ] ) ;
34723662colSpan = 1 ;
34733663}
34743664colIdx ++ ;
@@ -3487,12 +3677,18 @@ define([
34873677table . appendLine ( '<tr><th></th>' ) ;
34883678columnList && columnList . forEach ( col => {
34893679var colCode = col . code ;
3680+ var colIcon = '' ;
3681+ if ( col . isNumeric === true ) {
3682+ colIcon = '<span class="vp-fe-table-column-isnumeric vp-icon-numeric" title="Numeric column"></span>' ;
3683+ } else {
3684+ colIcon = '<span class="vp-fe-table-column-isnumeric vp-icon-non-numeric" title="Non-numeric column"></span>' ;
3685+ }
34903686var colClass = '' ;
34913687if ( that . state . axis == FRAME_AXIS . COLUMN && that . state . selected . map ( col => col . code ) . includes ( colCode ) ) {
34923688colClass = 'selected' ;
34933689}
3494- table . appendFormatLine ( '<th data-code="{0}" data-axis="{1}" data-type="{2}" data-label="{3}">{6}</th>'
3495- , colCode , FRAME_AXIS . COLUMN , col . type , col . label , VP_FE_TABLE_COLUMN , colClass , col . label ) ;
3690+ table . appendFormatLine ( '<th data-code="{0}" data-axis="{1}" data-type="{2}" data-label="{3}">{6}{7} </th>'
3691+ , colCode , FRAME_AXIS . COLUMN , col . type , col . label , VP_FE_TABLE_COLUMN , colClass , colIcon , col . label ) ;
34963692} ) ;
34973693// // add column
34983694table . appendFormatLine ( '<th class="{0}"><div class="{1}"></div></th>' , VP_FE_ADD_COLUMN , 'vp-icon-plus' ) ;
@@ -3713,6 +3909,7 @@ define([
37133909DROP :3 ,
37143910RENAME :2 ,
37153911AS_TYPE :10 ,
3912+ TO_DATETIME :19 ,
37163913REPLACE :9 ,
37173914DISCRETIZE :15 ,
37183915