Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_22812535
incubator-superset
提交
8f5977f4
I
incubator-superset
项目概览
qq_22812535
/
incubator-superset
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
I
incubator-superset
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
8f5977f4
编写于
11月 03, 2016
作者:
M
Maxime Beauchemin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Metric field
上级
14ed10bd
变更
10
隐藏空白更改
内联
并排
Showing
10 changed file
with
488 addition
and
66 deletion
+488
-66
superset/assets/javascripts/explorev2/components/FieldSet.jsx
...rset/assets/javascripts/explorev2/components/FieldSet.jsx
+6
-2
superset/assets/javascripts/explorev2/components/Filters.jsx
superset/assets/javascripts/explorev2/components/Filters.jsx
+0
-1
superset/assets/javascripts/explorev2/components/MetricField.jsx
...t/assets/javascripts/explorev2/components/MetricField.jsx
+303
-0
superset/assets/javascripts/explorev2/components/MetricList.jsx
...et/assets/javascripts/explorev2/components/MetricList.jsx
+84
-0
superset/assets/javascripts/explorev2/components/TextField.jsx
...set/assets/javascripts/explorev2/components/TextField.jsx
+2
-1
superset/assets/javascripts/explorev2/main.css
superset/assets/javascripts/explorev2/main.css
+6
-0
superset/assets/javascripts/explorev2/stores/fields.js
superset/assets/javascripts/explorev2/stores/fields.js
+13
-13
superset/assets/javascripts/explorev2/stores/visTypes.js
superset/assets/javascripts/explorev2/stores/visTypes.js
+3
-1
superset/assets/stylesheets/superset.css
superset/assets/stylesheets/superset.css
+3
-0
superset/models.py
superset/models.py
+68
-48
未找到文件。
superset/assets/javascripts/explorev2/components/FieldSet.jsx
浏览文件 @
8f5977f4
...
...
@@ -3,14 +3,18 @@ import TextField from './TextField';
import
CheckboxField
from
'
./CheckboxField
'
;
import
TextAreaField
from
'
./TextAreaField
'
;
import
SelectField
from
'
./SelectField
'
;
import
MetricList
from
'
./MetricList
'
;
import
MetricField
from
'
./MetricField
'
;
import
ControlLabelWithTooltip
from
'
./ControlLabelWithTooltip
'
;
const
fieldMap
=
{
TextField
,
CheckboxField
,
TextAreaField
,
MetricField
,
MetricList
,
SelectField
,
TextAreaField
,
TextField
,
};
const
fieldTypes
=
Object
.
keys
(
fieldMap
);
...
...
superset/assets/javascripts/explorev2/components/Filters.jsx
浏览文件 @
8f5977f4
import
React
from
'
react
'
;
// import { Tab, Row, Col, Nav, NavItem } from 'react-bootstrap';
import
Filter
from
'
./Filter
'
;
import
{
Button
}
from
'
react-bootstrap
'
;
import
{
connect
}
from
'
react-redux
'
;
...
...
superset/assets/javascripts/explorev2/components/MetricField.jsx
0 → 100644
浏览文件 @
8f5977f4
import
React
,
{
PropTypes
}
from
'
react
'
;
import
Select
from
'
react-select
'
;
import
{
Col
,
FormControl
,
FormGroup
,
InputGroup
,
Label
,
OverlayTrigger
,
Popover
,
Radio
,
Row
,
}
from
'
react-bootstrap
'
;
const
numericAggFunctions
=
{
SUM
:
'
SUM({})
'
,
MIN
:
'
MIN({})
'
,
MAX
:
'
MAX({})
'
,
AVG
:
'
AVG({})
'
,
COUNT_DISTINCT
:
'
COUNT(DISTINCT {})
'
,
COUNT
:
'
COUNT({})
'
,
};
const
NUMERIC_TYPES
=
[
'
INT
'
,
'
INTEGER
'
,
'
BIGINT
'
,
'
DOUBLE
'
,
'
FLOAT
'
,
'
NUMERIC
'
];
const
nonNumericAggFunctions
=
{
COUNT_DISTINCT
:
'
COUNT(DISTINCT {})
'
,
COUNT
:
'
COUNT({})
'
,
};
const
propTypes
=
{
datasource
:
PropTypes
.
object
,
column
:
PropTypes
.
string
,
metricType
:
PropTypes
.
string
,
onChange
:
PropTypes
.
func
,
initialMetricType
:
PropTypes
.
string
,
initialLabel
:
PropTypes
.
string
,
initialSql
:
PropTypes
.
string
,
onDelete
:
PropTypes
.
func
,
};
const
defaultProps
=
{
initialMetricType
:
'
free
'
,
initialLabel
:
'
row_count
'
,
initialSql
:
'
COUNT(*)
'
,
};
export
default
class
MetricField
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
aggregate
:
null
,
label
:
props
.
initialLabel
,
metricType
:
props
.
initialMetricType
,
metricName
:
null
,
sql
:
props
.
initialSql
,
};
this
.
getColumnOptions
=
this
.
getColumnOptions
.
bind
(
this
);
}
onChange
()
{
console
.
log
(
this
.
state
);
this
.
props
.
onChange
(
this
.
state
);
}
onDelete
()
{
this
.
props
.
onDelete
();
}
getColumnOptions
()
{
return
this
.
props
.
datasource
.
columns
.
map
(
col
=>
({
value
:
col
.
column_name
,
label
:
col
.
column_name
,
column
:
col
,
}));
}
setMetricType
(
v
)
{
this
.
setState
({
metricType
:
v
});
}
getMetricOptions
()
{
return
this
.
props
.
datasource
.
metrics
.
map
(
metric
=>
({
value
:
metric
.
metric_name
,
label
:
metric
.
metric_name
,
metric
,
type
:
'
metric
'
,
}));
}
changeLabel
(
e
)
{
const
label
=
e
.
target
.
value
;
this
.
setState
({
label
},
this
.
onChange
);
}
changeExpression
(
e
)
{
const
sql
=
e
.
target
.
value
;
this
.
setState
({
sql
,
columnName
:
null
,
aggregate
:
null
},
this
.
onChange
);
}
optionify
(
arr
)
{
return
arr
.
map
(
s
=>
({
value
:
s
,
label
:
s
}));
}
changeColumnSection
()
{
let
label
;
if
(
this
.
state
.
aggregate
&&
this
.
state
.
column
)
{
label
=
this
.
state
.
aggregate
+
'
__
'
+
this
.
state
.
column
.
column_name
;
}
else
{
label
=
''
;
}
this
.
setState
({
label
},
this
.
onChange
);
}
changeAggregate
(
opt
)
{
const
aggregate
=
opt
?
opt
.
value
:
null
;
this
.
setState
({
aggregate
},
this
.
changeColumnSection
);
}
changeRadio
(
e
)
{
this
.
setState
({
metricType
:
e
.
target
.
value
});
}
changeMetric
(
opt
)
{
let
metricName
;
let
label
;
if
(
opt
)
{
metricName
=
opt
.
metric
.
metric_name
;
label
=
metricName
;
}
this
.
setState
({
label
,
metricName
},
this
.
onChange
);
}
changeColumn
(
opt
)
{
let
column
;
let
aggregate
=
this
.
state
.
aggregate
;
if
(
opt
)
{
column
=
opt
.
column
;
if
(
!
aggregate
)
{
if
(
NUMERIC_TYPES
.
includes
(
column
.
type
))
{
aggregate
=
'
SUM
'
;
}
else
{
aggregate
=
'
COUNT_DISTINCT
'
;
}
}
}
else
{
aggregate
=
null
;
}
this
.
setState
({
column
,
aggregate
},
this
.
changeColumnSection
);
}
renderOverlay
()
{
let
aggregateOptions
=
[];
const
column
=
this
.
state
.
column
;
if
(
column
)
{
if
(
NUMERIC_TYPES
.
includes
(
column
.
type
))
{
aggregateOptions
=
Object
.
keys
(
numericAggFunctions
);
}
else
{
aggregateOptions
=
Object
.
keys
(
nonNumericAggFunctions
);
}
}
const
metricType
=
this
.
state
.
metricType
;
return
(
<
Popover
id
=
"popover-positioned-right"
title
=
"Metric Definition"
>
<
FormGroup
bsSize
=
"sm"
controlId
=
"label"
>
<
InputGroup
bsSize
=
"sm"
>
<
InputGroup
.
Addon
>
Label
</
InputGroup
.
Addon
>
<
FormControl
type
=
"text"
value
=
{
this
.
state
.
label
}
placeholder
=
"Label"
onChange
=
{
this
.
changeLabel
.
bind
(
this
)
}
/>
</
InputGroup
>
</
FormGroup
>
<
hr
/>
<
div
className
=
{
metricType
!==
'
column
'
?
'
dimmed
'
:
''
}
>
<
Row
>
<
Col
md
=
{
1
}
>
<
Radio
name
=
"metricType"
inline
value
=
"column"
onChange
=
{
this
.
changeRadio
.
bind
(
this
)
}
checked
=
{
this
.
state
.
metricType
===
'
column
'
}
/>
</
Col
>
<
Col
md
=
{
11
}
>
<
div
className
=
"m-b-5"
>
<
Select
name
=
"select-schema"
placeholder
=
"Column"
onFocus
=
{
this
.
setMetricType
.
bind
(
this
,
'
column
'
)
}
options
=
{
this
.
getColumnOptions
()
}
onChange
=
{
this
.
changeColumn
.
bind
(
this
)
}
value
=
{
this
.
state
.
column
?
this
.
state
.
column
.
column_name
:
null
}
autosize
=
{
false
}
valueRenderer
=
{
(
o
)
=>
(
<
div
>
<
span
className
=
"text-muted"
>
Column:
</
span
>
{
o
.
label
}
</
div
>
)
}
/>
</
div
>
<
div
>
<
Select
name
=
"select-schema"
placeholder
=
"Aggregate function"
onFocus
=
{
this
.
setMetricType
.
bind
(
this
,
'
column
'
)
}
disabled
=
{
aggregateOptions
.
length
===
0
}
options
=
{
this
.
optionify
(
aggregateOptions
)
}
value
=
{
this
.
state
.
aggregate
}
autosize
=
{
false
}
onChange
=
{
this
.
changeAggregate
.
bind
(
this
)
}
valueRenderer
=
{
(
o
)
=>
(
<
div
>
<
span
className
=
"text-muted"
>
Aggregate:
</
span
>
{
o
.
label
}
</
div
>
)
}
/>
</
div
>
</
Col
>
</
Row
>
</
div
>
<
hr
/>
<
div
className
=
{
metricType
!==
'
metric
'
?
'
dimmed
'
:
''
}
>
<
Row
>
<
Col
md
=
{
1
}
>
<
Radio
inline
name
=
"metricType"
value
=
"metric"
onChange
=
{
this
.
changeRadio
.
bind
(
this
)
}
checked
=
{
this
.
state
.
metricType
===
'
metric
'
}
/>
</
Col
>
<
Col
md
=
{
11
}
>
<
Select
name
=
"select-schema"
placeholder
=
"Predefined metric"
options
=
{
this
.
getMetricOptions
()
}
onFocus
=
{
this
.
setMetricType
.
bind
(
this
,
'
metric
'
)
}
value
=
{
this
.
state
.
metricName
}
autosize
=
{
false
}
onChange
=
{
this
.
changeMetric
.
bind
(
this
)
}
valueRenderer
=
{
(
o
)
=>
(
<
div
>
<
span
className
=
"text-muted"
>
Metric:
</
span
>
{
o
.
label
}
</
div
>
)
}
/>
</
Col
>
</
Row
>
</
div
>
<
hr
/>
<
div
className
=
{
metricType
!==
'
free
'
?
'
dimmed
'
:
''
}
>
<
Row
>
<
Col
md
=
{
1
}
>
<
Radio
inline
name
=
"metricType"
value
=
"free"
onChange
=
{
this
.
changeRadio
.
bind
(
this
)
}
checked
=
{
this
.
state
.
metricType
===
'
free
'
}
/>
</
Col
>
<
Col
md
=
{
11
}
>
<
FormGroup
bsSize
=
"sm"
controlId
=
"sql"
>
<
InputGroup
bsSize
=
"sm"
>
<
InputGroup
.
Addon
>
SQL
</
InputGroup
.
Addon
>
<
FormControl
type
=
"text"
value
=
{
this
.
state
.
sql
}
onFocus
=
{
this
.
setMetricType
.
bind
(
this
,
'
free
'
)
}
placeholder
=
"Free form SQL"
onChange
=
{
this
.
changeExpression
.
bind
(
this
)
}
/>
</
InputGroup
>
</
FormGroup
>
</
Col
>
</
Row
>
</
div
>
</
Popover
>
);
}
render
()
{
if
(
!
this
.
props
.
datasource
)
{
return
null
;
}
let
deleteButton
;
if
(
this
.
props
.
onDelete
)
{
deleteButton
=
<
i
className
=
"fa fa-times"
onClick
=
{
this
.
onDelete
.
bind
(
this
)
}
role
=
"button"
/>;
}
const
trigger
=
(
<
OverlayTrigger
trigger
=
"click"
placement
=
"right"
rootClose
overlay
=
{
this
.
renderOverlay
()
}
>
<
i
className
=
"fa fa-edit"
role
=
"button"
/>
</
OverlayTrigger
>
);
return
(
<
Label
className
=
"MetricField lead"
style
=
{
{
fontSize
:
'
14px
'
,
display
:
'
inline-block
'
,
margin
:
'
0 3 3 0
'
,
padding
:
'
5px
'
}
}
>
<
span
>
{
this
.
state
.
label
}
{
trigger
}
{
deleteButton
}
</
span
>
</
Label
>
);
}
}
MetricField
.
propTypes
=
propTypes
;
MetricField
.
defaultProps
=
defaultProps
;
superset/assets/javascripts/explorev2/components/MetricList.jsx
0 → 100644
浏览文件 @
8f5977f4
import
React
,
{
PropTypes
}
from
'
react
'
;
import
MetricField
from
'
./MetricField
'
;
import
ControlLabelWithTooltip
from
'
./ControlLabelWithTooltip
'
;
const
propTypes
=
{
initialMetrics
:
PropTypes
.
array
.
isRequired
,
datasource
:
PropTypes
.
object
,
onChange
:
PropTypes
.
func
,
};
const
defaultProps
=
{
initialMetrics
:
[],
onChange
:
()
=>
{},
};
export
default
class
MetricList
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
const
metrics
=
this
.
props
.
initialMetrics
.
slice
();
if
(
props
.
initialMetrics
.
length
===
0
)
{
metrics
.
push
({
metricType
:
'
free
'
,
metricLabel
:
'
row_count
'
,
sql
:
'
COUNT(*)
'
,
});
}
this
.
state
=
{
metrics
,
};
}
changeMetric
(
i
,
metric
)
{
const
metrics
=
this
.
state
.
metrics
.
slice
();
metrics
[
i
]
=
metric
;
this
.
setState
({
metrics
},
this
.
onChange
);
}
addMetric
()
{
const
metrics
=
this
.
state
.
metrics
.
slice
();
const
name
=
'
unlabeled metric
'
+
this
.
state
.
metrics
.
length
;
metrics
.
push
({
initialMetricType
:
'
free
'
,
initialLabel
:
name
,
initialSql
:
''
,
});
this
.
setState
({
metrics
},
this
.
onChange
);
}
onChange
()
{
console
.
log
(
this
.
state
.
metrics
);
this
.
props
.
onChange
(
this
.
state
.
metrics
);
}
deleteMetric
(
metric
)
{
this
.
setState
({
metrics
:
this
.
state
.
metrics
.
filter
(
m
=>
m
!==
metric
)
});
}
render
()
{
if
(
!
this
.
props
.
datasource
)
{
return
null
;
}
const
metrics
=
this
.
state
.
metrics
||
[];
return
(
<
div
className
=
"MetricList"
>
<
ControlLabelWithTooltip
label
=
{
this
.
props
.
label
}
description
=
{
this
.
props
.
description
}
/>
<
div
className
=
"MetricList"
>
{
metrics
.
map
((
metric
,
i
)
=>
(
<
MetricField
key
=
{
i
}
datasource
=
{
this
.
props
.
datasource
}
onChange
=
{
this
.
changeMetric
.
bind
(
this
,
i
)
}
onDelete
=
{
this
.
deleteMetric
.
bind
(
this
,
metric
)
}
/>
))
}
<
a
onClick
=
{
this
.
addMetric
.
bind
(
this
)
}
role
=
"button"
>
<
i
className
=
"fa fa-plus-circle"
/>
</
a
>
</
div
>
</
div
>
);
}
}
MetricList
.
propTypes
=
propTypes
;
MetricList
.
defaultProps
=
defaultProps
;
superset/assets/javascripts/explorev2/components/TextField.jsx
浏览文件 @
8f5977f4
...
...
@@ -21,13 +21,14 @@ export default class TextField extends React.Component {
this
.
props
.
onChange
(
this
.
props
.
name
,
event
.
target
.
value
);
}
render
()
{
const
value
=
this
.
props
.
value
||
''
;
return
(
<
FormGroup
controlId
=
"formInlineName"
bsSize
=
"small"
>
<
FormControl
type
=
"text"
placeholder
=
""
onChange
=
{
this
.
onChange
.
bind
(
this
)
}
value
=
{
this
.
props
.
value
}
value
=
{
value
}
/>
</
FormGroup
>
);
...
...
superset/assets/javascripts/explorev2/main.css
浏览文件 @
8f5977f4
...
...
@@ -25,3 +25,9 @@
margin-top
:
0px
;
margin-right
:
3px
;
}
#popover-positioned-right
{
width
:
400px
;
}
.popover
{
max-width
:
100%
;
}
superset/assets/javascripts/explorev2/stores/fields.js
浏览文件 @
8f5977f4
...
...
@@ -46,17 +46,6 @@ export const fields = {
description
:
'
The type of visualization to display
'
,
},
metrics
:
{
type
:
'
SelectField
'
,
multi
:
true
,
label
:
'
Metrics
'
,
mapStateToProps
:
(
state
)
=>
({
choices
:
(
state
.
datasource
)
?
state
.
datasource
.
metrics_combo
:
[],
}),
default
:
[],
description
:
'
One or many metrics to display
'
,
},
order_by_cols
:
{
type
:
'
SelectField
'
,
multi
:
true
,
...
...
@@ -69,15 +58,26 @@ export const fields = {
},
metric
:
{
type
:
'
Select
Field
'
,
type
:
'
Metric
Field
'
,
label
:
'
Metric
'
,
default
:
null
,
description
:
'
Choose the metric
'
,
mapStateToProps
:
(
state
)
=>
({
choices
:
(
state
.
datasource
)
?
state
.
datasource
.
metrics_combo
:
[]
,
datasource
:
state
.
datasource
,
}),
},
metrics
:
{
type
:
'
MetricList
'
,
multi
:
true
,
label
:
'
Metrics
'
,
mapStateToProps
:
(
state
)
=>
({
datasource
:
state
.
datasource
,
}),
default
:
[],
description
:
'
One or many metrics to display
'
,
},
metric_2
:
{
type
:
'
SelectField
'
,
label
:
'
Right Axis Metric
'
,
...
...
superset/assets/javascripts/explorev2/stores/visTypes.js
浏览文件 @
8f5977f4
...
...
@@ -307,7 +307,9 @@ const visTypes = {
{
label
:
null
,
fieldSetRows
:
[
[
'
series
'
,
'
metric
'
,
'
limit
'
],
[
'
series
'
],
[
'
metric
'
],
[
'
limit
'
],
[
'
size_from
'
,
'
size_to
'
],
[
'
rotation
'
],
],
...
...
superset/assets/stylesheets/superset.css
浏览文件 @
8f5977f4
...
...
@@ -193,3 +193,6 @@ div.widget .slice_container {
.table-condensed
{
font-size
:
12px
;
}
.dimmed
{
opacity
:
0.5
;
}
superset/models.py
浏览文件 @
8f5977f4
...
...
@@ -684,6 +684,8 @@ class Queryable(object):
d
=
{
'id'
:
self
.
id
,
'columns'
:
[
col
.
data
for
col
in
self
.
columns
],
'metrics'
:
[
m
.
data
for
m
in
self
.
metrics
],
'type'
:
self
.
type
,
'name'
:
self
.
name
,
'metrics_combo'
:
self
.
metrics_combo
,
...
...
@@ -1426,24 +1428,70 @@ class SqlaTable(Model, Queryable, AuditMixinNullable, ImportMixin):
sqla
.
event
.
listen
(
SqlaTable
,
'after_insert'
,
set_perm
)
sqla
.
event
.
listen
(
SqlaTable
,
'after_update'
,
set_perm
)
class
MetricAbstract
(
object
):
metric_name
=
Column
(
String
(
512
))
verbose_name
=
Column
(
String
(
1024
))
metric_type
=
Column
(
String
(
32
))
expression
=
Column
(
Text
)
description
=
Column
(
Text
)
is_restricted
=
Column
(
Boolean
,
default
=
False
,
nullable
=
True
)
class
SqlMetric
(
Model
,
AuditMixinNullable
,
ImportMixin
):
@
property
def
data
(
self
):
attrs
=
(
'metric_name'
,
'metric_type'
,
'description'
,
)
d
=
{
s
:
getattr
(
self
,
s
)
for
s
in
attrs
}
return
d
class
DruidMetric
(
Model
,
AuditMixinNullable
,
MetricAbstract
):
"""ORM object referencing Druid metrics for a datasource"""
__tablename__
=
'metrics'
id
=
Column
(
Integer
,
primary_key
=
True
)
datasource_name
=
Column
(
String
(
255
),
ForeignKey
(
'datasources.datasource_name'
))
# Setting enable_typechecks=False disables polymorphic inheritance.
datasource
=
relationship
(
'DruidDatasource'
,
backref
=
backref
(
'metrics'
,
cascade
=
'all, delete-orphan'
),
enable_typechecks
=
False
)
json
=
Column
(
Text
)
d3format
=
Column
(
String
(
128
))
@
property
def
json_obj
(
self
):
try
:
obj
=
json
.
loads
(
self
.
json
)
except
Exception
:
obj
=
{}
return
obj
@
property
def
perm
(
self
):
return
(
"{parent_name}.[{obj.metric_name}](id:{obj.id})"
).
format
(
obj
=
self
,
parent_name
=
self
.
datasource
.
full_name
)
if
self
.
datasource
else
None
class
SqlMetric
(
Model
,
AuditMixinNullable
,
ImportMixin
,
MetricAbstract
):
"""ORM object for metrics, each table can have multiple metrics"""
__tablename__
=
'sql_metrics'
id
=
Column
(
Integer
,
primary_key
=
True
)
metric_name
=
Column
(
String
(
512
))
verbose_name
=
Column
(
String
(
1024
))
metric_type
=
Column
(
String
(
32
))
table_id
=
Column
(
Integer
,
ForeignKey
(
'tables.id'
))
table
=
relationship
(
'SqlaTable'
,
backref
=
backref
(
'metrics'
,
cascade
=
'all, delete-orphan'
),
foreign_keys
=
[
table_id
])
expression
=
Column
(
Text
)
description
=
Column
(
Text
)
is_restricted
=
Column
(
Boolean
,
default
=
False
,
nullable
=
True
)
d3format
=
Column
(
String
(
128
))
export_fields
=
(
...
...
@@ -1483,7 +1531,18 @@ class SqlMetric(Model, AuditMixinNullable, ImportMixin):
return
metric_to_import
class
TableColumn
(
Model
,
AuditMixinNullable
,
ImportMixin
):
class
ColumnAbstract
(
object
):
"""Incomplete placeholder for things common to Column fields"""
@
property
def
data
(
self
):
attrs
=
(
'column_name'
,
'verbose_name'
,
'is_dttm'
,
'type'
,
)
d
=
{
s
:
getattr
(
self
,
s
)
for
s
in
attrs
}
return
d
class
TableColumn
(
Model
,
AuditMixinNullable
,
ImportMixin
,
ColumnAbstract
):
"""ORM object for table columns, each table can have multiple columns"""
...
...
@@ -2415,46 +2474,7 @@ class Log(Model):
return
wrapper
class
DruidMetric
(
Model
,
AuditMixinNullable
):
"""ORM object referencing Druid metrics for a datasource"""
__tablename__
=
'metrics'
id
=
Column
(
Integer
,
primary_key
=
True
)
metric_name
=
Column
(
String
(
512
))
verbose_name
=
Column
(
String
(
1024
))
metric_type
=
Column
(
String
(
32
))
datasource_name
=
Column
(
String
(
255
),
ForeignKey
(
'datasources.datasource_name'
))
# Setting enable_typechecks=False disables polymorphic inheritance.
datasource
=
relationship
(
'DruidDatasource'
,
backref
=
backref
(
'metrics'
,
cascade
=
'all, delete-orphan'
),
enable_typechecks
=
False
)
json
=
Column
(
Text
)
description
=
Column
(
Text
)
is_restricted
=
Column
(
Boolean
,
default
=
False
,
nullable
=
True
)
d3format
=
Column
(
String
(
128
))
@
property
def
json_obj
(
self
):
try
:
obj
=
json
.
loads
(
self
.
json
)
except
Exception
:
obj
=
{}
return
obj
@
property
def
perm
(
self
):
return
(
"{parent_name}.[{obj.metric_name}](id:{obj.id})"
).
format
(
obj
=
self
,
parent_name
=
self
.
datasource
.
full_name
)
if
self
.
datasource
else
None
class
DruidColumn
(
Model
,
AuditMixinNullable
):
class
DruidColumn
(
Model
,
AuditMixinNullable
,
ColumnAbstract
):
"""ORM model for storing Druid datasource column metadata"""
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录