Refactor: daily refactor

This commit is contained in:
chenjiandongx 2019-03-22 10:43:23 +08:00
parent b638085404
commit 466ac333a3
37 changed files with 223 additions and 126 deletions

View File

@ -3,6 +3,7 @@
# basic Charts
from ..charts.basic_charts.bar import Bar
from ..charts.basic_charts.boxplot import Boxplot
from ..charts.basic_charts.calendar import Calendar
from ..charts.basic_charts.effectscatter import EffectScatter
from ..charts.basic_charts.funnel import Funnel
from ..charts.basic_charts.gauge import Gauge

View File

@ -18,7 +18,7 @@ class Boxplot(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
y_axis: ListTuple,
*,
label_opts: Union[opts.LabelOpts, dict] = opts.LabelOpts(),
@ -32,11 +32,11 @@ class Boxplot(AxisChart):
if isinstance(markline_opts, opts.MarkLineOpts):
markline_opts = markline_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "boxplot",
"name": name,
"name": series_name,
"data": y_axis,
"label": label_opts,
"markPoint": markpoint_opts,

View File

@ -1,7 +1,7 @@
# coding=utf-8
from ... import options as opts
from ...charts.chart import Chart
from ...commons.types import ListTuple, Union
from ...options import InitOpts, LabelOpts, MarkLineOpts, MarkPointOpts
class Calendar(Chart):
@ -12,21 +12,26 @@ class Calendar(Chart):
直角坐标系上必须要使用两个类目轴
"""
def __init__(self, init_opts: Union[InitOpts, dict] = InitOpts()):
def __init__(self, init_opts: Union[opts.InitOpts, dict] = opts.InitOpts()):
super().__init__(init_opts=init_opts)
self.options.update(calendar=opts.CalendarOpts().opts)
def add_yaxis(
def add(
self,
name: str,
series_name: str,
yaxis_data: ListTuple,
*,
label_opts: Union[LabelOpts, dict] = LabelOpts(),
label_opts: Union[opts.LabelOpts, dict] = opts.LabelOpts(),
calendar_opts: Union[opts.CalendarOpts, dict, None] = None,
):
if isinstance(label_opts, LabelOpts):
if isinstance(label_opts, opts.LabelOpts):
label_opts = label_opts.opts
if isinstance(calendar_opts, opts.CalendarOpts):
calendar_opts = calendar_opts.opts
# if _is_calendar:
# name, data = args
if calendar_opts:
self.options.update(calendar=calendar_opts)
self._append_legend(series_name)
# if "yaxis_formatter" not in kwargs:
# kwargs["yaxis_formatter"] = None
@ -35,14 +40,12 @@ class Calendar(Chart):
self.options.get("series").append(
{
"type": "heatmap",
"name": name,
"coordinateSystem": "calendar",
"name": series_name,
"data": yaxis_data,
"label": label_opts,
}
)
# if _is_calendar:
# self.options.get("toolbox").update(left="98%", top="26%")
# self.options.get("series")[0].update(coordinateSystem="calendar")
# self.options.update(calendar=chart["calendar"])
return self

View File

@ -17,7 +17,7 @@ class EffectScatter(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
y_axis: ListTuple,
*,
symbol: Optional[str] = None,
@ -30,11 +30,11 @@ class EffectScatter(AxisChart):
if isinstance(effect_opts, opts.EffectOpts):
effect_opts = effect_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "effectScatter",
"name": name,
"name": series_name,
"showEffectOn": "render",
"rippleEffect": effect_opts,
"symbol": symbol,

View File

@ -14,7 +14,7 @@ class Funnel(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
sort_: str = "descending",
gap: Numeric = 0,
@ -33,7 +33,7 @@ class Funnel(Chart):
self.options.get("series").append(
{
"type": "funnel",
"name": name,
"name": series_name,
"data": data,
"sort": sort_,
"gap": gap,

View File

@ -14,19 +14,19 @@ class Gauge(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
min_: Numeric = 0,
max_: Numeric = 100,
start_angle: Numeric = 225,
end_angle: Numeric = -45,
):
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "gauge",
"detail": {"formatter": "{value}%"},
"name": name,
"name": series_name,
"min": min_,
"max": max_,
"startAngle": start_angle,

View File

@ -57,7 +57,7 @@ class Geo(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
type_: str = "scatter",
maptype: str = "china",
@ -108,13 +108,13 @@ class Geo(Chart):
},
}
)
self._append_legend(name)
self._append_legend(series_name)
if type_ == "scatter":
self.options.get("series").append(
{
"type": type_,
"name": name,
"name": series_name,
"coordinateSystem": "geo",
"symbol": symbol,
"symbolSize": symbol_size,
@ -127,7 +127,7 @@ class Geo(Chart):
self.options.get("series").append(
{
"type": type_,
"name": name,
"name": series_name,
"coordinateSystem": "geo",
"showEffectOn": "render",
"rippleEffect": effect_opts,
@ -140,14 +140,19 @@ class Geo(Chart):
elif type_ == "heatmap":
self.options.get("series").append(
{"type": type_, "name": name, "coordinateSystem": "geo", "data": data}
{
"type": type_,
"name": series_name,
"coordinateSystem": "geo",
"data": data,
}
)
elif type_ == "lines":
self.options.get("series").append(
{
"type": "lines",
"name": name,
"name": series_name,
"zlevel": self._zlevel,
"effect": effect_opts,
"symbol": symbol or ["none", "arrow"],

View File

@ -16,7 +16,7 @@ class Graph(Chart):
def add(
self,
name: str,
series_name: str,
nodes,
links,
categories=None,
@ -48,7 +48,7 @@ class Graph(Chart):
self.options.get("series").append(
{
"type": "graph",
"name": name,
"name": series_name,
"layout": layout,
"symbol": symbol,
"circular": {"rotateLabel": is_rotate_label},

View File

@ -19,7 +19,7 @@ class HeatMap(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
yaxis_data: ListTuple,
value: ListTuple,
*,
@ -38,7 +38,7 @@ class HeatMap(AxisChart):
self.options.get("series").append(
{
"type": "heatmap",
"name": name,
"name": series_name,
"data": value,
"label": label_opts,
"markLine": markline_opts,

View File

@ -22,7 +22,7 @@ class Kline(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
y_axis: ListTuple,
*,
markline_opts: Union[opts.MarkLineOpts, dict, None] = None,
@ -34,11 +34,11 @@ class Kline(AxisChart):
if isinstance(markline_opts, opts.MarkLineOpts):
markline_opts = markline_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": CHART_TYPE.KLINE,
"name": name,
"name": series_name,
"data": y_axis,
"markPoint": markpoint_opts,
"markLine": markline_opts,

View File

@ -17,7 +17,7 @@ class Line(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
y_axis: ListTuple,
*,
is_symbol_show: bool = True,
@ -43,7 +43,7 @@ class Line(AxisChart):
if isinstance(areastyle_opts, opts.AreaStyleOpts):
areastyle_opts = areastyle_opts.opts
self._append_legend(name)
self._append_legend(series_name)
# 合并 x 和 y 轴数据,避免当 X 轴的类型设置为 'value' 的时候,
# X、Y 轴均显示 Y 轴数据
data = [list(z) for z in zip(self._xaxis_data, y_axis)]
@ -51,7 +51,7 @@ class Line(AxisChart):
self.options.get("series").append(
{
"type": "line",
"name": name,
"name": series_name,
"symbol": symbol,
"symbolSize": symbol_size,
"smooth": is_smooth,

View File

@ -17,7 +17,7 @@ class Liquid(Chart):
def add(
self,
name: str,
series_name: str,
data: ListTuple,
shape: str = "circle",
color: Optional[List[str]] = None,
@ -33,7 +33,7 @@ class Liquid(Chart):
self.options.get("series").append(
{
"type": "liquidFill",
"name": name,
"name": series_name,
"data": data,
"waveAnimation": is_animation,
"animationDuration": _animation_dur,

View File

@ -16,7 +16,7 @@ class Map(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
maptype: str = "china",
*,
@ -30,11 +30,11 @@ class Map(Chart):
self.js_dependencies.add(maptype)
data = [{"name": n, "value": v} for (n, v) in data_pair]
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "map",
"name": name,
"name": series_name,
"symbol": symbol,
"label": label_opts,
"mapType": maptype,

View File

@ -20,7 +20,7 @@ class Parallel(Chart):
def add(
self,
name: str,
series_name: str,
data: ListTuple,
linestyle_opts: Union[opts.LineStyleOpts, dict] = opts.LineStyleOpts(),
):
@ -28,13 +28,13 @@ class Parallel(Chart):
linestyle_opts = linestyle_opts.opts
self.options.update(parallel=opts.ParallelOpts())
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "parallel",
"coordinateSystem": "parallel",
"lineStyle": linestyle_opts,
"name": name,
"name": series_name,
"data": data,
}
)

View File

@ -16,7 +16,7 @@ class Pie(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
radius: Optional[ListTuple] = None,
center: Optional[ListTuple] = None,
@ -44,7 +44,7 @@ class Pie(Chart):
self.options.get("series").append(
{
"type": "pie",
"name": name,
"name": series_name,
"data": data,
"radius": radius,
"center": center,

View File

@ -16,7 +16,7 @@ class Polar(Chart):
def add(
self,
name: str,
series_name: str,
data: ListTuple,
angle_data=None,
radius_data=None,
@ -56,7 +56,7 @@ class Polar(Chart):
polar_type = "value" if type == "line" else "category"
is_stack = "stack" if is_stack else ""
self._append_legend(name)
self._append_legend(series_name)
_amin, _amax = None, None
if axis_range:
@ -71,7 +71,7 @@ class Polar(Chart):
"type": "bar",
"coordinateSystem": "polar",
"stack": is_stack,
"name": name,
"name": series_name,
"data": data,
}
@ -103,7 +103,7 @@ class Polar(Chart):
self.options.get("series").append(
{
"type": type_,
"name": name,
"name": series_name,
"coordinateSystem": "polar",
"symbol": symbol,
"symbolSize": symbol_size,
@ -117,7 +117,7 @@ class Polar(Chart):
self.options.get("series").append(
{
"type": type_,
"name": name,
"name": series_name,
"coordinateSystem": "polar",
"showEffectOn": "render",
"rippleEffect": effect_opts,
@ -143,7 +143,7 @@ class Polar(Chart):
self.options.get("series").append(
{
"type": "custom",
"name": name,
"name": series_name,
"coordinateSystem": "polar",
"data": data,
"renderItem": render_item,

View File

@ -22,12 +22,12 @@ class Radar(Chart):
text_color="#333",
text_size=12,
splitline_opt: Union[opts.SplitLineOpts, dict] = opts.SplitLineOpts(),
splitarea_opt: Union[opts.SplitAreaOpt, dict] = opts.SplitAreaOpt(),
splitarea_opt: Union[opts.SplitAreaOpts, dict] = opts.SplitAreaOpts(),
axisline_opt: Union[opts.AxisLineOpts, dict] = opts.AxisLineOpts(),
):
if isinstance(splitline_opt, opts.SplitLineOpts):
splitline_opt = splitline_opt.opts
if isinstance(splitarea_opt, opts.SplitAreaOpt):
if isinstance(splitarea_opt, opts.SplitAreaOpts):
splitarea_opt = splitarea_opt.opts
if isinstance(axisline_opt, opts.AxisLineOpts):
axisline_opt = axisline_opt.opts
@ -53,7 +53,7 @@ class Radar(Chart):
def add(
self,
name: str,
series_name: str,
value: ListTuple,
symbol: Optional[str] = None,
item_color=None,
@ -68,11 +68,11 @@ class Radar(Chart):
if isinstance(areastyle_opts, opts.AreaStyleOpts):
areastyle_opts = areastyle_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "radar",
"name": name,
"name": series_name,
"data": value,
"symbol": symbol,
"label": label_opts,

View File

@ -17,7 +17,7 @@ class Sankey(Chart):
def add(
self,
name: str,
series_name: str,
nodes: ListTuple,
links: ListTuple,
node_width: Numeric = 20,
@ -30,11 +30,11 @@ class Sankey(Chart):
if isinstance(linestyle_opt, opts.LineStyleOpts):
linestyle_opt = linestyle_opt.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "sankey",
"name": name,
"name": series_name,
"layout": None,
"data": nodes,
"links": links,

View File

@ -18,7 +18,7 @@ class Scatter(AxisChart):
def add_yaxis(
self,
name: str,
series_name: str,
y_axis: ListTuple,
*,
symbol=None,
@ -34,12 +34,12 @@ class Scatter(AxisChart):
if isinstance(markpoint_opts, opts.MarkPointOpts):
markpoint_opts = markpoint_opts.opts
self._append_legend(name)
self._append_legend(series_name)
data = [list(z) for z in zip(self._xaxis_data, y_axis)]
self.options.get("series").append(
{
"type": "scatter",
"name": name,
"name": series_name,
"symbol": symbol,
"symbolSize": symbol_size,
"data": data,

View File

@ -16,16 +16,21 @@ class ThemeRiver(Chart):
def add(
self,
name: str,
series_name: str,
data: ListTuple,
label_opts: Union[opts.LabelOpts, dict] = opts.LabelOpts(),
):
if isinstance(label_opts, opts.LabelOpts):
label_opts = label_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{"type": "themeRiver", "name": name, "data": data, "label": label_opts}
{
"type": "themeRiver",
"name": series_name,
"data": data,
"label": label_opts,
}
)
self.options.update(singleAxis={"type": "time"})

View File

@ -34,7 +34,7 @@ class Tree(Chart):
def add(
self,
name: str,
series_name: str,
data: ListTuple,
layout: str = "orthogonal",
symbol: str = "emptyCircle",
@ -57,7 +57,7 @@ class Tree(Chart):
self.options.get("series").append(
{
"type": "tree",
"name": name,
"name": series_name,
"data": _data,
"left": left,
"right": right,

View File

@ -17,7 +17,7 @@ class TreeMap(Chart):
def add(
self,
name: str,
series_name: str,
data,
left_depth=None,
drilldown_icon="",
@ -27,11 +27,11 @@ class TreeMap(Chart):
if isinstance(label_opts, opts.LabelOpts):
label_opts = label_opts.opts
self._append_legend(name)
self._append_legend(series_name)
self.options.get("series").append(
{
"type": "treemap",
"name": name,
"name": series_name,
"data": data,
"label": label_opts,
"leafDepth": left_depth,

View File

@ -30,7 +30,7 @@ class WordCloud(Chart):
def add(
self,
name: str,
series_name: str,
data_pair: ListTuple,
shape: str = "circle",
word_gap: Numeric = 20,
@ -55,7 +55,7 @@ class WordCloud(Chart):
self.options.get("series").append(
{
"type": "wordCloud",
"name": name,
"name": series_name,
"shape": shape,
"rotationRange": [_rmin, _rmax],
"rotationStep": rotate_step,

View File

@ -43,9 +43,7 @@ class Chart(Base):
def __init__(self, init_opts: Union[opts.InitOpts, dict] = opts.InitOpts()):
super().__init__(init_opts=init_opts)
self._colors = COLORS
self.options.update(
series_id=uuid.uuid4().hex, series=[], legend=[{"data": []}]
)
self.options.update(series=[], legend=[{"data": []}])
if self.theme == "white":
self.options.update(color=self._colors)
@ -102,7 +100,7 @@ class Chart(Base):
def set_global_opts(
self,
title_opts: Union[opts.TitleOpts, dict] = opts.TitleOpts(),
toolbox_opts: Union[opts.ToolboxOpst, dict] = opts.ToolboxOpst(),
toolbox_opts: Union[opts.ToolboxOpts, dict] = opts.ToolboxOpts(),
tooltip_opts: Union[opts.TooltipOpts, dict] = opts.TooltipOpts(),
legend_opts: Union[opts.LegendOpts, dict] = opts.LegendOpts(),
xaxis_opt: Union[opts.AxisOpts, dict, None] = None,
@ -112,7 +110,7 @@ class Chart(Base):
):
if isinstance(title_opts, opts.TitleOpts):
title_opts = title_opts.opts
if isinstance(toolbox_opts, opts.ToolboxOpst):
if isinstance(toolbox_opts, opts.ToolboxOpts):
toolbox_opts = toolbox_opts.opts
if isinstance(tooltip_opts, opts.TooltipOpts):
tooltip_opts = tooltip_opts.opts

View File

@ -0,0 +1,21 @@
import datetime
import random
import pyecharts.options as opts
from pyecharts.charts import Calendar
begin = datetime.date(2017, 1, 1)
end = datetime.date(2017, 12, 31)
data = [
[str(begin + datetime.timedelta(days=i)), random.randint(1000, 25000)]
for i in range((end - begin).days + 1)
]
c = Calendar()
c.add("A", data, calendar_opts=opts.CalendarOpts(range_="2017"))
c.set_global_opts(
visualmap_opts=opts.VisualMapOpts(
max_=20000, min_=500, orient="horizontal", is_piecewise=True, pos_top="230px"
)
)
c.render()

View File

@ -7,24 +7,27 @@ from .global_options import (
InitOpts,
TooltipOpts,
TitleOpts,
ToolboxOpst,
ToolboxOpts,
AxisOpts,
Axis3DOpts,
ParallelAxisOpts,
ParallelOpts,
Grid3DOpts,
GridOpts,
CalendarOpts,
)
from .series_options import (
LabelOpts,
MarkLineOpts,
MarkPointOpts,
LineStyleOpts,
SplitAreaOpt,
SplitAreaOpts,
SplitLineOpts,
AreaStyleOpts,
AxisLineOpts,
MarkPointItem,
MarkLineItem,
ItemStyleOpts,
EffectOpts,
TextStyleOpts,
)

View File

@ -8,8 +8,8 @@ from ..options.series_options import *
class InitOpts:
def __init__(
self,
width: str = "800px",
height: str = "400px",
width: str = "900px",
height: str = "500px",
chart_id: str = uuid.uuid4().hex,
renderer: str = RENDER_TYPE.CANVAS,
page_title: str = "Awesome-pyecharts",
@ -27,24 +27,53 @@ class InitOpts:
self.js_host = js_host
class ToolboxOpst:
class ToolBoxFeatureOpts:
def __init__(
self,
save_as_image: Optional[dict] = None,
restore: Optional[dict] = None,
data_view: Optional[dict] = None,
data_zoom: Optional[dict] = None,
):
if not save_as_image:
save_as_image = {"show": True, "title": "save as image"}
if not restore:
restore = {"show": True, "title": "restore"}
if not data_view:
data_view = {"show": True, "title": "data view"}
if not data_zoom:
data_zoom = {"show": True, "title": "data zoom"}
self.opts: dict = {
"saveAsImage": save_as_image,
"restore": restore,
"dataView": data_view,
"dataZoom": data_zoom,
}
class ToolboxOpts:
def __init__(
self,
is_show: bool = True,
orient: Optional[str] = None,
pos_left: str = "95%",
orient: str = "horizontal",
pos_left: str = "80%",
pos_right: Optional[str] = None,
pos_top: Optional[str] = None,
pos_bottom: Optional[str] = None,
feature: Union[ToolBoxFeatureOpts, dict] = ToolBoxFeatureOpts(),
):
if isinstance(feature, ToolBoxFeatureOpts):
feature = feature.opts
self.opts: dict = {
"show": is_show,
"orient": orient,
"left": pos_left,
"right": pos_right,
"top": pos_top,
"feature": {
"saveAsImage": {"show": True, "title": "save as image"},
"restore": {"show": True, "title": "restore"},
"dataView": {"show": True, "title": "data view"},
},
"bottom": pos_bottom,
"feature": feature,
}
@ -214,8 +243,8 @@ class AxisOpts:
boundary_gap: Optional[str] = None,
label_alignment: Optional[str] = None,
inverse: Optional[str] = None,
min_: Union[None, Numeric] = None,
max_: Union[None, Numeric] = None,
min_: Optional[Numeric] = None,
max_: Optional[Numeric] = None,
type_: Optional[str] = None,
name_textstyle_opts: Union[TextStyleOpts, dict, None] = None,
splitline_opts: Union[SplitLineOpts, dict] = SplitLineOpts(),
@ -364,6 +393,34 @@ class ParallelAxisOpts:
}
# TODO
class CalendarOpts:
pass
def __init__(
self,
left: Optional[str] = None,
top: Optional[str] = None,
right: Optional[str] = None,
bottom: Optional[str] = None,
orient: Optional[str] = None,
range_: Union[str, ListTuple, int] = None,
daylabel_opts: Union[LabelOpts, dict, None] = None,
monthlabel_opts: Union[LabelOpts, dict, None] = None,
yearlabel_opts: Union[LabelOpts, dict, None] = None,
):
if isinstance(daylabel_opts, LabelOpts):
daylabel_opts = daylabel_opts.opts
if isinstance(monthlabel_opts, LabelOpts):
monthlabel_opts = monthlabel_opts.opts
if isinstance(yearlabel_opts, LabelOpts):
yearlabel_opts = yearlabel_opts.opts
self.opts: dict = {
"left": left,
"top": top,
"right": right,
"bottom": bottom,
"orient": orient,
"range": range_,
"dayLabel": daylabel_opts,
"monthLabel": monthlabel_opts,
"yearLabel": yearlabel_opts,
}

View File

@ -152,15 +152,34 @@ class MarkLineOpts:
}
# TODO
class MarkLineItem:
pass
def __init__(
self,
name: Optional[str] = None,
type_: Optional[str] = None,
xaxis: Union[str, Numeric, None] = None,
yaxis: Union[str, Numeric, None] = None,
coord: Optional[ListTuple] = None,
symbol: Optional[str] = None,
symbol_size: Optional[Numeric] = None,
):
self.opts: dict = {
"name": name,
"type": type_,
"xAxis": xaxis,
"yAxis": yaxis,
"coord": coord,
"symbol": symbol,
"symbolSize": symbol_size,
}
# TODO
class MarkAreaOpts:
pass
# TODO
class MarkAreaItem:
pass
@ -177,14 +196,23 @@ class AreaStyleOpts:
self.opts: dict = {"opacity": opacity, "color": color}
class SplitAreaOpt:
class SplitAreaOpts:
def __init__(self, is_show=True, area_style: AreaStyleOpts = AreaStyleOpts()):
self.opts: dict = {"show": is_show, "areaStyle": area_style}
# TODO
class ItemStyleOpts:
pass
def __init__(
self,
color: Optional[str] = None,
border_color: Optional[str] = None,
opacity: Optional[Numeric] = None,
):
self.opts: dict = {
"color": color,
"borderColor": border_color,
"opacity": opacity,
}
class TextStyleOpts:
@ -222,30 +250,6 @@ class TextStyleOpts:
# elif symbol not in SYMBOLS:
# symbol = "circle"
# return symbol
# def grid(
# grid_width=None,
# grid_height=None,
# grid_top=None,
# grid_bottom=None,
# grid_left=None,
# grid_right=None,
# ):
# _grid = {}
# if grid_width is not None:
# _grid.update(width=grid_width)
# if grid_height is not None:
# _grid.update(height=grid_height)
# if grid_top is not None:
# _grid.update(top=grid_top)
# if grid_bottom is not None:
# _grid.update(bottom=grid_bottom)
# if grid_left is not None:
# _grid.update(left=grid_left)
# if grid_right is not None:
# _grid.update(right=grid_right)
# return _grid
#
#
# def calendar(calendar_date_range=None, calendar_cell_size=None, **kwargs):