From 45f2de62ea2688a9e8b674e0e61b5373f9f0b6f7 Mon Sep 17 00:00:00 2001 From: Jasper Berghoef Date: Wed, 31 May 2017 11:52:37 +0200 Subject: [PATCH] Splits segment dashboard view and set default table view --- frontend/js/dashboard.js | 1 + frontend/scss/dashboard.scss | 183 ++++++++++++++++++ frontend/scss/index.scss | 183 ------------------ src/personalisation/static/css/form.css | 2 + src/personalisation/static/css/form.css.map | 1 + src/personalisation/static/css/index.css | 2 + src/personalisation/static/css/index.css.map | 1 + .../static/img/calendar_icon.png | Bin 0 -> 794 bytes src/personalisation/static/img/key_icon.png | Bin 0 -> 1230 bytes .../static/img/persistent_icon.png | Bin 0 -> 845 bytes .../static/img/referral_icon.png | Bin 0 -> 1301 bytes .../static/img/rocket_icon.png | Bin 0 -> 1525 bytes src/personalisation/static/img/ruler_icon.png | Bin 0 -> 1085 bytes src/personalisation/static/img/time_icon.png | Bin 0 -> 1104 bytes .../static/img/visit_count_icon.png | Bin 0 -> 1473 bytes src/personalisation/static/js/commons.js | 155 +++++++++++++++ src/personalisation/static/js/commons.js.map | 1 + src/personalisation/static/js/form.js | 19 ++ src/personalisation/static/js/form.js.map | 1 + src/personalisation/static/js/index.js | 20 ++ src/personalisation/static/js/index.js.map | 1 + src/wagtail_personalisation/models.py | 5 + .../wagtail_personalisation/segment/base.html | 35 ++++ .../segment/dashboard.html | 111 +++++++++++ .../segment/index.html | 136 ++++--------- .../wagtail_personalisation_filters.py | 25 +-- src/wagtail_personalisation/utils.py | 26 +++ src/wagtail_personalisation/views.py | 34 +++- 28 files changed, 641 insertions(+), 301 deletions(-) create mode 100644 frontend/js/dashboard.js create mode 100644 frontend/scss/dashboard.scss create mode 100644 src/personalisation/static/css/form.css create mode 100644 src/personalisation/static/css/form.css.map create mode 100644 src/personalisation/static/css/index.css create mode 100644 src/personalisation/static/css/index.css.map create mode 100644 src/personalisation/static/img/calendar_icon.png create mode 100644 src/personalisation/static/img/key_icon.png create mode 100644 src/personalisation/static/img/persistent_icon.png create mode 100644 src/personalisation/static/img/referral_icon.png create mode 100644 src/personalisation/static/img/rocket_icon.png create mode 100644 src/personalisation/static/img/ruler_icon.png create mode 100644 src/personalisation/static/img/time_icon.png create mode 100644 src/personalisation/static/img/visit_count_icon.png create mode 100644 src/personalisation/static/js/commons.js create mode 100644 src/personalisation/static/js/commons.js.map create mode 100644 src/personalisation/static/js/form.js create mode 100644 src/personalisation/static/js/form.js.map create mode 100644 src/personalisation/static/js/index.js create mode 100644 src/personalisation/static/js/index.js.map create mode 100644 src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html create mode 100644 src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html diff --git a/frontend/js/dashboard.js b/frontend/js/dashboard.js new file mode 100644 index 0000000..657f371 --- /dev/null +++ b/frontend/js/dashboard.js @@ -0,0 +1 @@ +import '../scss/dashboard.scss'; diff --git a/frontend/scss/dashboard.scss b/frontend/scss/dashboard.scss new file mode 100644 index 0000000..4f9c688 --- /dev/null +++ b/frontend/scss/dashboard.scss @@ -0,0 +1,183 @@ +.nice-padding { + padding-left: 50px; + padding-right: 50px; +} + +.block_container { + display: block; + margin-top: 30px; +} + +.block_container .block { + display: block; + float: left; + box-sizing: border-box; + position: relative; + width: calc(50% - 10px); + min-height: 216px; + padding: 10px 20px; + margin-bottom: 20px; + border: 1px solid #d9d9d9; + border-radius: 3px; + background-color: #fff; + box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00); + transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1); + cursor: pointer; +} + +.block_container .block--disabled h2, +.block_container .block--disabled .inspect_container { + opacity: 0.5; +} + + .block_container .block h2 { + display: inline-block; + width: auto; + } + +.block_container .block:nth-child(odd) { + margin-right: 20px; +} + + .block_container .block .block_actions { + list-style: none; + margin: 0; + padding: 0; + } + + .block_container .block .block_actions li { + float: left; + margin-right: 10px; + } + + .block_container .block .block_actions li:last-child { + margin-right: 0; + } + +.block_container .block.suggestion { + border: 1px dashed #d9d9d9; +} + +.block_container .block:hover { + border: 1px solid #fff; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} + +@media (max-width: 699px) { + .block_container .block { + width: 100%; + margin-right: 0; + } +} + +.block_container .block .inspect_container { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: space-between; + align-items: stretch; + margin-bottom: 10px; +} + +.block_container .block .inspect_container .inspect { + display: block; + float: left; + width: calc(50% - 10px); + padding: 0; + margin: 0; + list-style: none; +} + + .block_container .block .inspect_container .inspect li { + display: inline-block; + margin-bottom: 5px; + } + + .block_container .block .inspect_container .inspect li span { + display: block; + font-size: 20px; + font-weight: bold; + margin: 5px 0; + overflow-wrap: break-word; + } + + .block_container .block .inspect_container .inspect li span::before { + display: inline-block; + content: ""; + width: 16px; + height: 16px; + margin-right: 5px; + background-size: contain; + background-image: url("../img/ruler_icon.png"); + } + + .block_container .block .inspect_container .segment_stats .visit_stat span::before { + background-image: url("../img/rocket_icon.png"); + } + .block_container .block .inspect_container .segment_stats .days_stat span::before { + background-image: url("../img/calendar_icon.png"); + } + + .block_container .block .inspect_container .segment_rules .persistent_state span::before { + background-image: url("../img/persistent_icon.png"); + } + .block_container .block .inspect_container .segment_rules .persistent_state.fleeting span::before { + transform: rotate(45deg) translateY(-2px); + } + .block_container .block .inspect_container .segment_rules .time-rule span::before { + background-image: url("../img/time_icon.png"); + } + .block_container .block .inspect_container .segment_rules .visit-count-rule span::before { + background-image: url("../img/visit_count_icon.png"); + } + .block_container .block .inspect_container .segment_rules .logged-in-rule span::before { + background-image: url("../img/key_icon.png"); + } + .block_container .block .inspect_container .segment_rules .day-rule span::before { + background-image: url("../img/calendar_icon.png"); + } + + .block_container .block .inspect_container .inspect li pre { + position: relative; + box-sizing: border-box; + width: auto; + background-color: #eee; + border: 1px solid #ccc; + margin: 5px 0 5px 21px; + padding: 2px 5px; + word-wrap: break-word; + word-break: break-all; + border-radius: 3px; + } + + .block_container .block .inspect_container .inspect li pre::before { + display: inline-block; + position: absolute; + content: ""; + left: -21px; + top: 6px; + width: 16px; + height: 16px; + margin-right: 5px; + background-size: contain; + background-image: url("../img/ruler_icon.png"); + } + + .block_container .block .inspect_container .segment_rules .referral-rule pre::before { + background-image: url("../img/referral_icon.png"); + } + .block_container .block .inspect_container .segment_rules .query-rule pre::before { + background-image: url("../img/referral_icon.png"); + } + +.block_container .block.suggestion .suggestive_text { + display: block; + position: absolute; + width: calc(100% - 40px); + text-align: center; + top: 50%; + transform: translateY(-50%); + color: #d9d9d9; + font-size: 20px; + font-weight: 100; +} diff --git a/frontend/scss/index.scss b/frontend/scss/index.scss index 4f9c688..e69de29 100644 --- a/frontend/scss/index.scss +++ b/frontend/scss/index.scss @@ -1,183 +0,0 @@ -.nice-padding { - padding-left: 50px; - padding-right: 50px; -} - -.block_container { - display: block; - margin-top: 30px; -} - -.block_container .block { - display: block; - float: left; - box-sizing: border-box; - position: relative; - width: calc(50% - 10px); - min-height: 216px; - padding: 10px 20px; - margin-bottom: 20px; - border: 1px solid #d9d9d9; - border-radius: 3px; - background-color: #fff; - box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00); - transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1); - cursor: pointer; -} - -.block_container .block--disabled h2, -.block_container .block--disabled .inspect_container { - opacity: 0.5; -} - - .block_container .block h2 { - display: inline-block; - width: auto; - } - -.block_container .block:nth-child(odd) { - margin-right: 20px; -} - - .block_container .block .block_actions { - list-style: none; - margin: 0; - padding: 0; - } - - .block_container .block .block_actions li { - float: left; - margin-right: 10px; - } - - .block_container .block .block_actions li:last-child { - margin-right: 0; - } - -.block_container .block.suggestion { - border: 1px dashed #d9d9d9; -} - -.block_container .block:hover { - border: 1px solid #fff; - box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); -} - -@media (max-width: 699px) { - .block_container .block { - width: 100%; - margin-right: 0; - } -} - -.block_container .block .inspect_container { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; - align-items: stretch; - margin-bottom: 10px; -} - -.block_container .block .inspect_container .inspect { - display: block; - float: left; - width: calc(50% - 10px); - padding: 0; - margin: 0; - list-style: none; -} - - .block_container .block .inspect_container .inspect li { - display: inline-block; - margin-bottom: 5px; - } - - .block_container .block .inspect_container .inspect li span { - display: block; - font-size: 20px; - font-weight: bold; - margin: 5px 0; - overflow-wrap: break-word; - } - - .block_container .block .inspect_container .inspect li span::before { - display: inline-block; - content: ""; - width: 16px; - height: 16px; - margin-right: 5px; - background-size: contain; - background-image: url("../img/ruler_icon.png"); - } - - .block_container .block .inspect_container .segment_stats .visit_stat span::before { - background-image: url("../img/rocket_icon.png"); - } - .block_container .block .inspect_container .segment_stats .days_stat span::before { - background-image: url("../img/calendar_icon.png"); - } - - .block_container .block .inspect_container .segment_rules .persistent_state span::before { - background-image: url("../img/persistent_icon.png"); - } - .block_container .block .inspect_container .segment_rules .persistent_state.fleeting span::before { - transform: rotate(45deg) translateY(-2px); - } - .block_container .block .inspect_container .segment_rules .time-rule span::before { - background-image: url("../img/time_icon.png"); - } - .block_container .block .inspect_container .segment_rules .visit-count-rule span::before { - background-image: url("../img/visit_count_icon.png"); - } - .block_container .block .inspect_container .segment_rules .logged-in-rule span::before { - background-image: url("../img/key_icon.png"); - } - .block_container .block .inspect_container .segment_rules .day-rule span::before { - background-image: url("../img/calendar_icon.png"); - } - - .block_container .block .inspect_container .inspect li pre { - position: relative; - box-sizing: border-box; - width: auto; - background-color: #eee; - border: 1px solid #ccc; - margin: 5px 0 5px 21px; - padding: 2px 5px; - word-wrap: break-word; - word-break: break-all; - border-radius: 3px; - } - - .block_container .block .inspect_container .inspect li pre::before { - display: inline-block; - position: absolute; - content: ""; - left: -21px; - top: 6px; - width: 16px; - height: 16px; - margin-right: 5px; - background-size: contain; - background-image: url("../img/ruler_icon.png"); - } - - .block_container .block .inspect_container .segment_rules .referral-rule pre::before { - background-image: url("../img/referral_icon.png"); - } - .block_container .block .inspect_container .segment_rules .query-rule pre::before { - background-image: url("../img/referral_icon.png"); - } - -.block_container .block.suggestion .suggestive_text { - display: block; - position: absolute; - width: calc(100% - 40px); - text-align: center; - top: 50%; - transform: translateY(-50%); - color: #d9d9d9; - font-size: 20px; - font-weight: 100; -} diff --git a/src/personalisation/static/css/form.css b/src/personalisation/static/css/form.css new file mode 100644 index 0000000..bf759f9 --- /dev/null +++ b/src/personalisation/static/css/form.css @@ -0,0 +1,2 @@ +.block_container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap}.block_container .block{display:block;-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;width:calc(100% / 3 - 13.33px);padding:10px 20px;margin-bottom:20px;border:1px solid #d9d9d9;border-radius:3px;background-color:#fff;cursor:pointer;-webkit-box-shadow:0 1px 3px transparent,0 1px 2px transparent;box-shadow:0 1px 3px transparent,0 1px 2px transparent;-webkit-transition:border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1);transition:border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1);transition:box-shadow .3s cubic-bezier(.25,.8,.25,1),border .3s cubic-bezier(.25,.8,.25,1);transition:box-shadow .3s cubic-bezier(.25,.8,.25,1),border .3s cubic-bezier(.25,.8,.25,1),-webkit-box-shadow .3s cubic-bezier(.25,.8,.25,1)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:20px}.block_container .block:hover{border:1px solid #fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}.block_container .block.disabled{background-color:#eee;cursor:default}.block_container .block.disabled:hover{border:1px solid #d9d9d9;-webkit-box-shadow:0 1px 3px transparent,0 1px 2px transparent;box-shadow:0 1px 3px transparent,0 1px 2px transparent}@media (min-width:800px) and (max-width:999px){.block_container .block{width:calc(100% / 2 - 10px)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:0}.block_container .block:nth-child(odd){margin-right:20px}}@media (max-width:599px){.block_container .block{width:calc(100% / 2 - 10px)}.block_container .block:nth-child(3n+1),.block_container .block:nth-child(3n+2){margin-right:0}.block_container .block:nth-child(odd){margin-right:20px}} +/*# sourceMappingURL=form.css.map*/ \ No newline at end of file diff --git a/src/personalisation/static/css/form.css.map b/src/personalisation/static/css/form.css.map new file mode 100644 index 0000000..31aeaf7 --- /dev/null +++ b/src/personalisation/static/css/form.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./scss/form.scss"],"names":[],"mappings":"AAAA,iBACI,oBAAa,iCACb,8BAAmB,uEACnB,mBAAe,eAGf,wBACI,cACA,8BAAsB,sBACtB,kBACA,+BACA,kBACA,mBACA,yBACA,kBACA,sBACA,eACA,+DAAkE,uDAClE,2GAA8F,2UAGlG,gFAEI,iBAAkB,CAGtB,8BACI,sBACA,uEAAkE,+DAGtE,iCACI,sBACA,cAAe,CAGnB,uCACI,yBACA,+DAAkE,uDAG1E,+CACI,wBACI,2BAA4B,CAGhC,gFAEI,cAAe,CAGnB,uCACI,iBAAkB,CACrB,CAGL,yBACI,wBACI,2BAA4B,CAGhC,gFAEI,cAAe,CAGnB,uCACI,iBAAkB,CACrB","file":"../css/form.css","sourcesContent":[".block_container {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n}\n\n .block_container .block {\n display: block;\n box-sizing: border-box;\n position: relative;\n width: calc(100% / 3 - 13.33px);\n padding: 10px 20px;\n margin-bottom: 20px;\n border: 1px solid #d9d9d9;\n border-radius: 3px;\n background-color: #fff;\n cursor: pointer;\n box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);\n transition: box-shadow 0.3s cubic-bezier(.25,.8,.25,1), border 0.3s cubic-bezier(.25,.8,.25,1);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 20px;\n }\n\n .block_container .block:hover {\n border: 1px solid #fff;\n box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);\n }\n\n .block_container .block.disabled {\n background-color: #eee;\n cursor: default;\n }\n\n .block_container .block.disabled:hover {\n border: 1px solid #d9d9d9;\n box-shadow: 0 1px 3px rgba(0,0,0,0.00), 0 1px 2px rgba(0,0,0,0.00);\n }\n\n@media (min-width: 800px) and (max-width: 999px) {\n .block_container .block {\n width: calc(100% / 2 - 10px);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 0;\n }\n\n .block_container .block:nth-child(2n+1) {\n margin-right: 20px;\n }\n}\n\n@media (max-width: 599px) {\n .block_container .block {\n width: calc(100% / 2 - 10px);\n }\n\n .block_container .block:nth-child(3n+1),\n .block_container .block:nth-child(3n+2) {\n margin-right: 0;\n }\n\n .block_container .block:nth-child(2n+1) {\n margin-right: 20px;\n }\n}\n\n\n// WEBPACK FOOTER //\n// ./scss/form.scss"],"sourceRoot":""} \ No newline at end of file diff --git a/src/personalisation/static/css/index.css b/src/personalisation/static/css/index.css new file mode 100644 index 0000000..80c6e0e --- /dev/null +++ b/src/personalisation/static/css/index.css @@ -0,0 +1,2 @@ + +/*# sourceMappingURL=index.css.map*/ \ No newline at end of file diff --git a/src/personalisation/static/css/index.css.map b/src/personalisation/static/css/index.css.map new file mode 100644 index 0000000..9a1c86d --- /dev/null +++ b/src/personalisation/static/css/index.css.map @@ -0,0 +1 @@ +{"version":3,"sources":[],"names":[],"mappings":"","file":"../css/index.css","sourceRoot":""} \ No newline at end of file diff --git a/src/personalisation/static/img/calendar_icon.png b/src/personalisation/static/img/calendar_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a655cd39452fa2b0e0c30bfb3cd3e1ac1e7c87a2 GIT binary patch literal 794 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*Fy#djUQnuBl*9Utgb)kdOugKAD9n zDJc*hL;+AakPBo3$;`~m(9lqbEKnna0g;6$hKRt`0M$b@rh@@QHY+O&$bd@$xj;#v z2!w>F0g@1n5OIi>jEoGRn!>`u?Ck8EoE)GOP#kDZZf-6_s;H<4D4U<3pO=^Saz-96 z(CKz1L4Lu1e>2Jc`xC_w7RtKvAES4~N?Fe3zgV_@2-%89k zejd(~bI<=(U*aLt!zKyl8!a|+?waR!mw|zCt*47)NJZS!DcAEJJMg%sr}V9O7I(S& z{mpwPBzLO({4XDslq6B~WX;y!yPofhabOovX$sL`sW3V5-EhTYp|1?zKOdZEBNn3Y z=Fy9zb7d3kBNp|ur@u>W+1znfIPsJZoeS>~-z zg1f5Y>uh~q$m#R#&h$3Cy!mCnhP2wno*9*IlrQLbwRO4Ie)h2mi^{)wd3L_d?!(Eq zuCSWDlz5-^=kc>Ed6{e1usX+o{PtXx&*L`JEVhcDf5q%4FlD=L$uhEWJd!)zF|g%K zuJWUk1&>r;pMClKTHofGu}YismVYhOy0`j8shlt_6m`Rp+_Jt>L22U1cw4w0_DZywB1(cI2w=4lcj zqIm641V{GjXqQEi`fdIyHm$2?cZ3~MI<}Fm?v`gt!8^K=A+n|AvN!(9lo^ z2L~V{EiDbm&dtqDNJs!O3JVKMN=mY_vVbB51qGRznLrVs8Xz|#BO^aQAE*o{o0F3R zWCI!LV33`i4Wxlwpc)_ms)ula+JWrU)YO!e6rc!D3Mc|rk^$BSB7j^V2^5F3(L^9z zAPF%Rqz49of0EmVtfJh;8fwsVv!3}{6 z0!4tf1N{XI7GU@QLkt*(d3kw7MMaS@xl+KGs4NNc3ua(sWMXDvVP#|I;N;}y;S&%P z5)l;>mynQ>Q&H8@);BOTGq<#|b@TM{@(BnI3y+M6jf+n#EGjOks;h5k?CGB{WzM|$ z^A{{$x@_f|wHr3?IdJgM(PPI?oj!B++=WY5uHC$O>(0|>&tJZL{o&j9?>~P0{Q2wm z?>~S4{o7$-s>Hy+RPE{F7-Dg{_DW{_Wd{NF2lctLinz2E6;(HgPv_8RQeqWZ%dVvL z$WxT{$h4H3CYwFWg6((aXY9^o{`~9b*^MW80>srW*lpXfQJpJVe|dF72-C*IM_=A- z<5{Zk>9E24x6G3abrKT{#DCpMb1mALJ6Y=tgPX01f##C4u}Vrxe)F7{E;qFF?O{2z zZ1ICjx3{Wl+cIYT@>zKQ(<;3P`ByK_Rje^-4%y=PVC^QYbLY1*Ik*KaT)*wZ3a#z# zN3PhKs7qhWwbqKBT@V_|wL7LyxmU%i%=XlG7xj0#9ZI3bdJ7cq_9*thDTr#%^zn=< ze9~d>%n)^5b^BrQi!+})yUqIYP5jvV=S+Tg?q5n2()Mb9!l63#d}qur>DHSXD(#j% z!h$=Mtj+}S>bM8Kah+fIPU6H9w~|*64-`ILAgkwA{pQZcITFu{ESK$G()jd3U*nzo zdDq<=I~kSK*L}*lYP#S(W6+5ye6Qx7nR|_G=NIQ#HYxMaeb+b*IW|AC+QTsE0>GUV2UZ#&;QXS_VW?fvQK zLh~h0_fG4Nu@brW)}p=qSN;Qwo~D(vHW!HeTlh_xJ*e%nK@$I>ee(}Ko?X64q0R*2k}?TQ~XP ePvKWg{~2fAPI1|%=BNxRmONekT-G@yGywqP{2)F6 literal 0 HcmV?d00001 diff --git a/src/personalisation/static/img/persistent_icon.png b/src/personalisation/static/img/persistent_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3805284745724d76c94c82c150a74b566581395a GIT binary patch literal 845 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 literal 0 HcmV?d00001 diff --git a/src/personalisation/static/img/ruler_icon.png b/src/personalisation/static/img/ruler_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bf9e20943dca272684c9437db182d30b198d7da2 GIT binary patch literal 1085 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU3?z3ec*HX>Fg67Egt!8^K=A+n|AvN!(9lo^ z2L~V{EiDbm1~PJUb0Ok|g@q+0C0SWnWo2bRL7;+yf`ZJ<%;Mr=AU7i;BPS;ZNCMeF zHQCwOKyG?^dTMGakP8$CsssX{2#^aTfg*4c&H$PT6o*Iw1t9<;2w}rz@u&f+1Zsh5 z22*HUh!ju|0+7`MMc}d!ajYUhPeCT&m#$ji3eEItA`;VW$KCRRf1*U?3o-U3d7N=XU zWR_oY5Mg^Dz9L}7+R&_|Lg#|*rSSnVE1E89UDJ(n70j3@(35t|WJ%DQ`0spQW}ivf z_vFc$>Un4O7Uwbi^ioKEV%WQ3YQw&LcTZe2)>%F?Zua&~ik_#g+RXl1;ho!O({3K? z4y3wmJ;nd5J0(*o_P+&)vd_xvU+__f3*66?bX#MU)9c@Q)zFZ{Lc4jH9RI9% z+E@AZqu7ZpmxTYG%njIN5dTf|p2f_&5BhE|u{7U0pf3oMCoSb1Ic1XnDWM^jwd3kw3Zf5iJ|QB_Du|g588Z{1;2kl7b0i z7hDtU5>fuGR%9}!@7@zjj^)DzC){D09$lF`Mb+AcChb70;EcSRC%^v^7X@N8_6)gj zzy&AIXhfL|Ge_Hh5whEbg@p++S$`Nk%R2Nq^6z`{rcImnlLuqG^ReVAN1|vn&_dHZ zgc|hgfP{jA0ut+mjNfXe&KJT0_c=U!Lw=lCFANu)%3ZHow|!2h25GrgRWIv?-&aC3 z#A4_U&mP{GJ%@o@(}5Fi)u=}k?NEbMXdA-Th*tMXs_m^^`-R`m%t_oN$?5L}LgBdJ z)&?ZjI3B#G#GJG3*;%X&pi?j;%A7Z&n_!J6k7E)Iw481wUTi5P401NH{jGbf6HgS? zJ6I%WG%(*g4%p5d6uk6(?Z+b z?Qif#_omz3N*6fwW3D3zf7Thu)3fZO80{0iJR%F7hX=d6@y6h0ZK>U@|C&G&l^?ac zn&T$2!05I7FCN@AxS7FAv=wl<$DJNxfD+%a4+tUO;JX_aj_VpbTK!yN;F0?j+GU+d zqUxUo&oQT;S34P(j{Zs)fn9?!XJeQ9hJ3_8g#d$)XZoELa|id3{4Y$4Rc$eG?q%RL z@u1BD6&P%HTYGh~2Bp#w!qK6GpiyvFa7y@cSm=V@agQ53xFfGURfC>zq2AkVion*8 zeOBo#Jri7)zYj}MODQm}!BnQ$6VA~iCIGA*U5^G^Pj!M95`ROG$5h+qh??S*H;EA= z#Lm<8*P#VJWlXg83io<3JgT_3xLYcl1ji{IGav{8i-UNAaE+DUo`^*m0dbF+Zx8Bs z$sE*CaR_R6f3^=*#*#j$Q@-j#{$KL7Iy1lk5D22^0XxZ@Ip_!-p$4CT*LoW*zF^e| z_Wd`>V;FzcF)a8fSTMNd!?>k@mE(a`AwS0>zb;K2S(>PJ=CB)5)J|92IZ)hpF7B*8 z`q!VNK9X7L$j@f-s-nLEA*QZn8!?kxsn3*5Fut1?@e`2PtVs4Xw%1-#x{eN4UQW(?vM!+Ce+MW zyP*@%rjN1z&EQc-9n~XhW(LoiHLFI&6!bBsu_f5wod2}Jx!m9;h7+w?V+;cTZTc9K z*diAOxwdXF5w{xCr%#``OaC`*%&IeQz463mqiTNq`0@K$%-x2CvdM;xX)>5LZQ2qx zrRy8{9)>=EHhqlc>cElA?PKvjO`krUHT%G*QKOcozgDZwS+~Kw&?ilrw4523XoU9| zK4x3tskQzd(58#7={J$35TrA_PvBmD*=6DLk& zE8)($_CoX#Lo7Mu!jY%Z`Y-ef9-~im*7-3P5l3};Y&c9zPJfu z$?@E;;mTVKQ=Qgk*}&|^Zhv#a_nEp}X}$WBJ+*2q00&%f!i@%6s;#Ir zXX|G@fBDB+`@*iPM->b&o4_)$R%Oe0-tcNvBi1po;N|!XrY;XfU4?kC5f0jBg8Z@RSCi6P2^X@ zABL53bu}kmlW=Px(9TuN+X~gY$nOYzrBfLK0qr;P?F}`@jv-Xafg269&_r9ufiCuV zuYO>tY4s4Q<6Jn_Uv=b-)3FTYj4GmuHXhWIim-ThiiOLSs{EZnnc}nDAo*wOSVpeA zYT`jX{q^$p>vXRG37=Z>k}(cpL8K3xFCxI(eKaQLg(ezkp@}vg)Kjj)8Gafe@ae)P zt|#hc4L|z6)&XeK$C$<{hXSp5uj4^IWwEQ}1%I1t34Nm?2*|XvEDk0IV^YBm`WVyc zid?wh41To2ARg3HHh8_PqZ`HGtFZxxkQm(4M2)ZgIMgohWf|dsE}SuTS+M=IMML6*NrwFG`|c$OefOgUa=&Yk;57E`GS{)9h9J)vhETE(LD9clTpR3{geSwH-v^M zS_uSouVfm5?s0lB0C2zsXPjWrQtgt#eEb?6TY#a{H`1$F^f}%uzXGTdBZpi#;F7Iu zw8qzCFrRS7_y!8`Z+`Oq&Tzbqtwl@;#1Kmkxp2VcoTF8%A%pURA|m_C&mQe@ME7&w z(>8u82Wr0B0B!mhBgTz^b~xaIQ-8c{*r0B{@{87W{xe1hvMw=O3C1Fh9CG1+E1zRNPNStP}abn5gcwfcYvO)cX6r-!KhU|I?AMAGJSFJ8Un?A;fA-3vM bwI2Kl_iI`CRKdHN00000NkvXXu0mjf5cuIL literal 0 HcmV?d00001 diff --git a/src/personalisation/static/js/commons.js b/src/personalisation/static/js/commons.js new file mode 100644 index 0000000..1d07d4c --- /dev/null +++ b/src/personalisation/static/js/commons.js @@ -0,0 +1,155 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // install a JSONP callback for chunk loading +/******/ var parentJsonpFunction = window["webpackJsonp"]; +/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0, resolves = [], result; +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(installedChunks[chunkId]) { +/******/ resolves.push(installedChunks[chunkId][0]); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ for(moduleId in moreModules) { +/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { +/******/ modules[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); +/******/ while(resolves.length) { +/******/ resolves.shift()(); +/******/ } +/******/ if(executeModules) { +/******/ for(i=0; i < executeModules.length; i++) { +/******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]); +/******/ } +/******/ } +/******/ return result; +/******/ }; +/******/ +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // objects to store loaded and loading chunks +/******/ var installedChunks = { +/******/ 2: 0 +/******/ }; +/******/ +/******/ var resolvedPromise = new Promise(function(resolve) { resolve(); }); +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __webpack_require__.e = function requireEnsure(chunkId) { +/******/ if(installedChunks[chunkId] === 0) { +/******/ return resolvedPromise; +/******/ } +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunks[chunkId]) { +/******/ return installedChunks[chunkId][2]; +/******/ } +/******/ +/******/ // setup Promise in chunk cache +/******/ var promise = new Promise(function(resolve, reject) { +/******/ installedChunks[chunkId] = [resolve, reject]; +/******/ }); +/******/ installedChunks[chunkId][2] = promise; +/******/ +/******/ // start chunk loading +/******/ var head = document.getElementsByTagName('head')[0]; +/******/ var script = document.createElement('script'); +/******/ script.type = 'text/javascript'; +/******/ script.charset = 'utf-8'; +/******/ script.async = true; +/******/ script.timeout = 120000; +/******/ +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ script.src = __webpack_require__.p + "" + chunkId + ".js"; +/******/ var timeout = setTimeout(onScriptComplete, 120000); +/******/ script.onerror = script.onload = onScriptComplete; +/******/ function onScriptComplete() { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var chunk = installedChunks[chunkId]; +/******/ if(chunk !== 0) { +/******/ if(chunk) { +/******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.')); +/******/ } +/******/ installedChunks[chunkId] = undefined; +/******/ } +/******/ }; +/******/ head.appendChild(script); +/******/ +/******/ return promise; +/******/ }; +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // on error function for async loading +/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; +/******/ }) +/************************************************************************/ +/******/ ([]); +//# sourceMappingURL=commons.js.map \ No newline at end of file diff --git a/src/personalisation/static/js/commons.js.map b/src/personalisation/static/js/commons.js.map new file mode 100644 index 0000000..c0c19ee --- /dev/null +++ b/src/personalisation/static/js/commons.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap e0eb5a8f691b9fda3313"],"names":[],"mappings":";AAAA;AACA;AACA;AACA;AACA;AACA;AACA,gBAAQ,oBAAoB;AAC5B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAY,2BAA2B;AACvC;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA,+DAAuD,WAAW,EAAE;;AAEpE;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA,YAAI;AACJ;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA,kDAA0C,oBAAoB,WAAW","file":"commons.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \tvar resolvedPromise = new Promise(function(resolve) { resolve(); });\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tif(installedChunks[chunkId] === 0) {\n \t\t\treturn resolvedPromise;\n \t\t}\n\n \t\t// a Promise means \"currently loading\".\n \t\tif(installedChunks[chunkId]) {\n \t\t\treturn installedChunks[chunkId][2];\n \t\t}\n\n \t\t// setup Promise in chunk cache\n \t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\tinstalledChunks[chunkId] = [resolve, reject];\n \t\t});\n \t\tinstalledChunks[chunkId][2] = promise;\n\n \t\t// start chunk loading\n \t\tvar head = document.getElementsByTagName('head')[0];\n \t\tvar script = document.createElement('script');\n \t\tscript.type = 'text/javascript';\n \t\tscript.charset = 'utf-8';\n \t\tscript.async = true;\n \t\tscript.timeout = 120000;\n\n \t\tif (__webpack_require__.nc) {\n \t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t}\n \t\tscript.src = __webpack_require__.p + \"\" + chunkId + \".js\";\n \t\tvar timeout = setTimeout(onScriptComplete, 120000);\n \t\tscript.onerror = script.onload = onScriptComplete;\n \t\tfunction onScriptComplete() {\n \t\t\t// avoid mem leaks in IE.\n \t\t\tscript.onerror = script.onload = null;\n \t\t\tclearTimeout(timeout);\n \t\t\tvar chunk = installedChunks[chunkId];\n \t\t\tif(chunk !== 0) {\n \t\t\t\tif(chunk) {\n \t\t\t\t\tchunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));\n \t\t\t\t}\n \t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t}\n \t\t};\n \t\thead.appendChild(script);\n\n \t\treturn promise;\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap e0eb5a8f691b9fda3313"],"sourceRoot":""} \ No newline at end of file diff --git a/src/personalisation/static/js/form.js b/src/personalisation/static/js/form.js new file mode 100644 index 0000000..1d1c80e --- /dev/null +++ b/src/personalisation/static/js/form.js @@ -0,0 +1,19 @@ +webpackJsonp([1],[ +/* 0 */ +/***/ (function(module, exports) { + +// removed by extract-text-webpack-plugin + +/***/ }), +/* 1 */, +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +__webpack_require__(0); + +/***/ }) +],[2]); +//# sourceMappingURL=form.js.map \ No newline at end of file diff --git a/src/personalisation/static/js/form.js.map b/src/personalisation/static/js/form.js.map new file mode 100644 index 0000000..750b737 --- /dev/null +++ b/src/personalisation/static/js/form.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./scss/form.scss?4a48","webpack:///./js/form.js"],"names":[],"mappings":";;;;AAAA,yC;;;;;;;;;;ACAA,uB","file":"form.js","sourcesContent":["// removed by extract-text-webpack-plugin\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./scss/form.scss\n// module id = 0\n// module chunks = 1","import '../scss/form.scss';\n\n\n\n// WEBPACK FOOTER //\n// ./js/form.js"],"sourceRoot":""} \ No newline at end of file diff --git a/src/personalisation/static/js/index.js b/src/personalisation/static/js/index.js new file mode 100644 index 0000000..b60b616 --- /dev/null +++ b/src/personalisation/static/js/index.js @@ -0,0 +1,20 @@ +webpackJsonp([0],[ +/* 0 */, +/* 1 */ +/***/ (function(module, exports) { + +// removed by extract-text-webpack-plugin + +/***/ }), +/* 2 */, +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +__webpack_require__(1); + +/***/ }) +],[3]); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/src/personalisation/static/js/index.js.map b/src/personalisation/static/js/index.js.map new file mode 100644 index 0000000..ace065f --- /dev/null +++ b/src/personalisation/static/js/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./scss/index.scss","webpack:///./js/index.js"],"names":[],"mappings":";;;;;AAAA,yC;;;;;;;;;;ACAA,uB","file":"index.js","sourcesContent":["// removed by extract-text-webpack-plugin\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./scss/index.scss\n// module id = 1\n// module chunks = 0","import '../scss/index.scss';\n\n\n\n// WEBPACK FOOTER //\n// ./js/index.js"],"sourceRoot":""} \ No newline at end of file diff --git a/src/wagtail_personalisation/models.py b/src/wagtail_personalisation/models.py index ea04e3f..34996a2 100644 --- a/src/wagtail_personalisation/models.py +++ b/src/wagtail_personalisation/models.py @@ -14,6 +14,7 @@ from wagtail.wagtailcore.models import Page from wagtail_personalisation.forms import AdminPersonalisablePageForm from wagtail_personalisation.rules import AbstractBaseRule +from wagtail_personalisation.utils import count_active_days @python_2_unicode_compatible @@ -65,6 +66,10 @@ class Segment(ClusterableModel): """Return a string with a slug for the segment.""" return slugify(self.name.lower()) + def get_active_days(self): + """Return the amount of days the segment has been active.""" + return count_active_days(self.enable_date, self.disable_date) + def get_rules(self): """Retrieve all rules in the segment.""" rules = AbstractBaseRule.__subclasses__() diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html new file mode 100644 index 0000000..020fa7f --- /dev/null +++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/base.html @@ -0,0 +1,35 @@ +{% extends "modeladmin/index.html" %} +{% load i18n l10n staticfiles modeladmin_tags %} + +{% block titletag %}{{ view.get_meta_title }}{% endblock %} + +{% block css %} + {{ block.super }} + {{ view.media.css }} +{% endblock %} + +{% block extra_js %} + {{ view.media.js }} +{% endblock %} + +{% block header %} +
+
+
+
+ {% block h1 %}

{{ view.get_page_title }}

{% endblock %} +
+ {% block search %}{% search_form %}{% endblock %} +
+ {% block header_extra %} + {% if user_can_create %} +
+
+ {% include 'modeladmin/includes/button.html' with button=view.button_helper.add_button %} +
+
+ {% endif %} + {% endblock %} +
+
+{% endblock %} diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html new file mode 100644 index 0000000..0e61334 --- /dev/null +++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/dashboard.html @@ -0,0 +1,111 @@ +{% extends "modeladmin/wagtail_personalisation/segment/base.html" %} +{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %} + +{% block content_main %} +
+
+ {% block content_cols %} + + {% block filters %} + {% if view.has_filters and all_count %} +
+

{% trans 'Filter' %}

+ {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} +
+ {% endif %} + {% endblock %} + +
+ {% block result_list %} +
+ {% if all_count %} + {% for segment in object_list %} +
+

{{ segment }}

+
+
    +
  • + {% trans "This segmented has been visited" %} + {{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }} +
  • +
  • + {% trans "This segment has been active for" %} + {{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }} +
  • +
+ +
+ +
    +
  • + {% trans "The visitor must match" %} + {% if segment.match_any %} + {% trans "Any rule" %} + {% else %} + {% trans "All rules" %} + {% endif %} +
  • + +
  • + {% trans "The persistence of this segment is" %} + {% if segment.persistent %} + {% trans "Persistent" %} + {% else %} + {% trans "Fleeting" %} + {% endif %} +
  • + + {% for rule in segment.get_rules %} +
  • + {{ rule.description.title }} + {% if rule.description.code %} +
    {{ rule.description.value }}
    + {% else %} + {{ rule.description.value }} + {% endif %} +
  • + {% endfor %} +
+
+ + {% if user_can_create %} + + {% endif %} +
+ {% endfor %} + {% endif %} + + {% if user_can_create %} + {% blocktrans with url=view.create_url name=view.verbose_name %} + + Add a new {{name}} + + {% endblocktrans %} + {% endif %} +
+ {% endblock %} +
+ + {% block pagination %} + {% if paginator.num_pages > 1 %} + + {% endif %} + {% endblock %} + + {% endblock %} +
+
+{% endblock %} \ No newline at end of file diff --git a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html index 6813d94..b1c0d9e 100644 --- a/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html +++ b/src/wagtail_personalisation/templates/modeladmin/wagtail_personalisation/segment/index.html @@ -1,111 +1,57 @@ -{% extends "modeladmin/index.html" %} +{% extends "modeladmin/wagtail_personalisation/segment/base.html" %} {% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %} {% block content_main %} -
-
- {% block content_cols %} +
+
+ {% block content_cols %} - {% block filters %} - {% if view.has_filters and all_count %} -
-

{% trans 'Filter' %}

- {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} -
- {% endif %} - {% endblock %} + {% block filters %} + {% if view.has_filters and all_count %} +
+

{% trans 'Filter' %}

+ {% for spec in view.filter_specs %}{% admin_list_filter view spec %}{% endfor %} +
+ {% endif %} + {% endblock %} -
- {% block result_list %} -
- {% if all_count %} - {% for segment in object_list %} -
-

{{ segment }}

-
-
    -
  • - {% trans "This segmented has been visited" %} - {{ segment.visit_count|localize }} {% trans "time" %}{{ segment.visit_count|pluralize }} -
  • -
  • - {% trans "This segment has been active for" %} - {{ segment.enable_date|days_since:segment.disable_date }} {% trans "day" %}{{ segment.enable_date|days_since:segment.disable_date|pluralize }} -
  • -
- -
- -
    -
  • - {% trans "The visitor must match" %} - {% if segment.match_any %} - {% trans "Any rule" %} - {% else %} - {% trans "All rules" %} - {% endif %} -
  • - -
  • - {% trans "The persistence of this segment is" %} - {% if segment.persistent %} - {% trans "Persistent" %} - {% else %} - {% trans "Fleeting" %} - {% endif %} -
  • - - {% for rule in segment.get_rules %} -
  • - {{ rule.description.title }} - {% if rule.description.code %} -
    {{ rule.description.value }}
    - {% else %} - {{ rule.description.value }} - {% endif %} -
  • - {% endfor %} -
-
- - {% if user_can_create %} - - {% endif %} -
- {% endfor %} - {% endif %} - - {% if user_can_create %} - {% blocktrans with url=view.create_url name=view.verbose_name %} - - Add a new {{name}} - - {% endblocktrans %} +
+ {% block result_list %} + {% if not all_count %} +
+ {% if no_valid_parents %} +

{% blocktrans with view.verbose_name_plural as name %}No {{ name }} have been created yet. One of the following must be created before you can add any {{ name }}:{% endblocktrans %}

+
    + {% for type in required_parent_types %}
  • {{ type|title }}
  • {% endfor %} +
+ {% else %} +

{% blocktrans with view.verbose_name_plural as name %}No {{ name }} have been created yet.{% endblocktrans %} + {% if user_can_create %} + {% blocktrans with view.create_url as url %} + Why not add one? + {% endblocktrans %} + {% endif %}

{% endif %}
- {% endblock %} -
+ {% else %} + {% result_list %} + {% endif %} + {% endblock %} +
- {% block pagination %} + {% block pagination %} + {% endblock %} -
+ + {% endblock %}
- {% endblock %} +
+{% endblock %} diff --git a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py index 1cc8fdf..d408aea 100644 --- a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py +++ b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py @@ -1,29 +1,10 @@ from django.template import Library -from django.utils import timezone + +from wagtail_personalisation.utils import count_active_days register = Library() @register.filter(name='days_since') def active_days(enable_date, disable_date): - """Return the number of days the segment has been active. - - :param enable_date: The date the segment was enabled - :type enable_date: timezone.datetime - :param disable_date: The date the segment was disabled - :type disable_date: timezone.datetime - :returns: The amount of days a segment is/has been active - :rtype: int - - """ - if enable_date is not None: - if disable_date is None or disable_date <= enable_date: - # There is no disable date, or it is not relevant. - delta = timezone.now() - enable_date - return delta.days - if disable_date > enable_date: - # There is a disable date and it is relevant. - delta = disable_date - enable_date - return delta.days - - return 0 + return count_active_days(enable_date, disable_date) diff --git a/src/wagtail_personalisation/utils.py b/src/wagtail_personalisation/utils.py index 5d420bb..d42876f 100644 --- a/src/wagtail_personalisation/utils.py +++ b/src/wagtail_personalisation/utils.py @@ -1,5 +1,7 @@ import time +from django.utils import timezone + def impersonate_other_page(page, other_page): """Function to change the page metadata so the user gets to see the @@ -32,3 +34,27 @@ def create_segment_dictionary(segment): "timestamp": int(time.time()), "persistent": segment.persistent } + + +def count_active_days(enable_date, disable_date): + """Return the number of days the segment has been active. + + :param enable_date: The date the segment was enabled + :type enable_date: timezone.datetime + :param disable_date: The date the segment was disabled + :type disable_date: timezone.datetime + :returns: The amount of days a segment is/has been active + :rtype: int + + """ + if enable_date is not None: + if disable_date is None or disable_date <= enable_date: + # There is no disable date, or it is not relevant. + delta = timezone.now() - enable_date + return delta.days + if disable_date > enable_date: + # There is a disable date and it is relevant. + delta = disable_date - enable_date + return delta.days + + return 0 diff --git a/src/wagtail_personalisation/views.py b/src/wagtail_personalisation/views.py index a31e884..f5134c2 100644 --- a/src/wagtail_personalisation/views.py +++ b/src/wagtail_personalisation/views.py @@ -1,7 +1,10 @@ from __future__ import absolute_import, unicode_literals +from django import forms + from django.http import HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import get_object_or_404, reverse +from django.utils.translation import ugettext_lazy as _ from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register from wagtail.contrib.modeladmin.views import IndexView @@ -13,19 +16,48 @@ class SegmentModelIndexView(IndexView): pass +class SegmentModelDashboardView(IndexView): + """Placeholder for additional dashboard functionality.""" + def media(self): + return forms.Media( + css={'all': ['css/dashboard.css']}, + js=['js/commons.js', 'js/dashboard.js'] + ) + + def get_template_names(self): + return [ + 'modeladmin/wagtail_personalisation/segment/dashboard.html', + 'modeladmin/index.html' + ] + + @modeladmin_register class SegmentModelAdmin(ModelAdmin): """The model admin for the Segments administration interface.""" model = Segment index_view_class = SegmentModelIndexView + dashboard_view_class = SegmentModelDashboardView menu_icon = 'group' add_to_settings_menu = False - list_display = ('status', 'name', 'create_date', 'edit_date') + list_display = ('name', 'visits', 'active_days', 'status') index_view_extra_js = ['js/commons.js', 'js/index.js'] index_view_extra_css = ['css/index.css'] form_view_extra_js = ['js/commons.js', 'js/form.js'] form_view_extra_css = ['css/form.css'] + def index_view(self, request): + kwargs = {'model_admin': self} + view_class = self.index_view_class + return view_class.as_view(**kwargs)(request) + + def visits(self, obj): + return _("{visits} visits").format( + visits=obj.visit_count) + + def active_days(self, obj): + return _("{days} days").format( + days=obj.get_active_days()) + def toggle(request, segment_id): """Toggle the status of the selected segment.