Преглед изворни кода

Merge branch 'develop' of frappe/frappe into FrappeOAuth2Client

version-14
Revant Nandgaonkar пре 7 година
родитељ
комит
f60a04c4ed
30 измењених фајлова са 980 додато и 4073 уклоњено
  1. +0
    -1
      .eslintrc
  2. +46
    -0
      CODE_OF_CONDUCT.md
  3. +1
    -1
      frappe/__init__.py
  4. +5
    -2
      frappe/core/doctype/doctype/doctype.js
  5. +6
    -24
      frappe/desk/page/activity/activity.js
  6. +2
    -2
      frappe/desk/reportview.py
  7. +8
    -8
      frappe/model/delete_doc.py
  8. +1
    -0
      frappe/patches.txt
  9. +0
    -0
      frappe/patches/v8_10/__init__.py
  10. +5
    -0
      frappe/patches/v8_10/delete_static_web_page_from_global_search.py
  11. +0
    -2
      frappe/public/build.json
  12. +0
    -140
      frappe/public/css/cal-heatmap.css
  13. +4
    -0
      frappe/public/css/form.css
  14. +69
    -71
      frappe/public/css/graphs.css
  15. +2
    -1
      frappe/public/js/frappe/form/controls/text_editor.js
  16. +7
    -39
      frappe/public/js/frappe/form/dashboard.js
  17. +1
    -1
      frappe/public/js/frappe/form/templates/form_dashboard.html
  18. +595
    -91
      frappe/public/js/frappe/ui/graphs.js
  19. +2
    -0
      frappe/public/js/frappe/ui/toolbar/search_utils.js
  20. +1
    -1
      frappe/public/js/frappe/views/reports/print_grid.html
  21. +1
    -0
      frappe/public/js/frappe/views/reports/query_report.js
  22. +0
    -3471
      frappe/public/js/lib/cal-heatmap.js
  23. +5
    -0
      frappe/public/less/form.less
  24. +204
    -210
      frappe/public/less/graphs.less
  25. +1
    -1
      frappe/tests/test_twofactor.py
  26. +4
    -2
      frappe/tests/ui/data/test_lib.js
  27. +1
    -1
      frappe/tests/ui/test_test_runner.py
  28. +3
    -0
      frappe/twofactor.py
  29. +5
    -3
      frappe/utils/global_search.py
  30. +1
    -1
      requirements.txt

+ 0
- 1
.eslintrc Прегледај датотеку

@@ -49,7 +49,6 @@
"moment": true, "moment": true,
"hljs": true, "hljs": true,
"Awesomplete": true, "Awesomplete": true,
"CalHeatMap": true,
"Sortable": true, "Sortable": true,
"Showdown": true, "Showdown": true,
"Taggle": true, "Taggle": true,


+ 46
- 0
CODE_OF_CONDUCT.md Прегледај датотеку

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hello@frappe.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

+ 1
- 1
frappe/__init__.py Прегледај датотеку

@@ -14,7 +14,7 @@ import os, sys, importlib, inspect, json
from .exceptions import * from .exceptions import *
from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template from .utils.jinja import get_jenv, get_template, render_template, get_email_from_template


__version__ = '8.10.4'
__version__ = '8.10.5'
__title__ = "Frappe Framework" __title__ = "Frappe Framework"


local = Local() local = Local()


+ 5
- 2
frappe/core/doctype/doctype/doctype.js Прегледај датотеку

@@ -13,9 +13,12 @@


frappe.ui.form.on('DocType', { frappe.ui.form.on('DocType', {
refresh: function(frm) { refresh: function(frm) {
if(frm.is_new() && (frappe.session.user !== "Administrator" || !frappe.boot.developer_mode)) {
frm.set_value("custom", 1);
if(frappe.session.user !== "Administrator" || !frappe.boot.developer_mode) {
if(frm.is_new()) {
frm.set_value("custom", 1);
}
frm.toggle_enable("custom", 0); frm.toggle_enable("custom", 0);
frm.toggle_enable("beta", 0);
} }


if(!frappe.boot.developer_mode && !frm.doc.custom) { if(!frappe.boot.developer_mode && !frm.doc.custom) {


+ 6
- 24
frappe/desk/page/activity/activity.js Прегледај датотеку

@@ -180,30 +180,12 @@ frappe.activity.render_heatmap = function(page) {
method: "frappe.desk.page.activity.activity.get_heatmap_data", method: "frappe.desk.page.activity.activity.get_heatmap_data",
callback: function(r) { callback: function(r) {
if(r.message) { if(r.message) {
var legend = [];
var max = Math.max.apply(this, $.map(r.message, function(v) { return v }));
var legend = [cint(max/5), cint(max*2/5), cint(max*3/5), cint(max*4/5)];
var heatmap = new CalHeatMap();
heatmap.init({
itemSelector: ".heatmap",
domain: "month",
subDomain: "day",
start: moment().subtract(1, 'year').add(1, 'month').toDate(),
cellSize: 9,
cellPadding: 2,
domainGutter: 2,
range: 12,
domainLabelFormat: function(date) {
return moment(date).format("MMM").toUpperCase();
},
displayLegend: false,
legend: legend,
tooltip: true,
subDomainTitleFormat: {
empty: "{date}",
filled: "{count} actions on {date}"
},
subDomainDateFormat: "%d-%b"
var heatmap = new frappe.ui.HeatMap({
parent: $(".heatmap"),
height: 100,
start: new Date(moment().subtract(1, 'year').toDate()),
count_label: "actions",
discrete_domains: 0
}); });


heatmap.update(r.message); heatmap.update(r.message);


+ 2
- 2
frappe/desk/reportview.py Прегледај датотеку

@@ -152,8 +152,8 @@ def export_query():
writer = csv.writer(f) writer = csv.writer(f)
for r in data: for r in data:
# encode only unicode type strings and not int, floats etc. # encode only unicode type strings and not int, floats etc.
writer.writerow(map(lambda v: isinstance(v, string_types) and
handle_html(frappe.as_unicode(v)) or v, r))
writer.writerow([handle_html(frappe.as_unicode(v)).encode('utf-8') \
if isinstance(v, string_types) else v for v in r])


f.seek(0) f.seek(0)
frappe.response['result'] = text_type(f.read(), 'utf-8') frappe.response['result'] = text_type(f.read(), 'utf-8')


+ 8
- 8
frappe/model/delete_doc.py Прегледај датотеку

@@ -244,6 +244,14 @@ def delete_dynamic_links(doctype, name):
frappe.db.sql('''delete from `tabEmail Unsubscribe` frappe.db.sql('''delete from `tabEmail Unsubscribe`
where reference_doctype=%s and reference_name=%s''', (doctype, name)) where reference_doctype=%s and reference_name=%s''', (doctype, name))


# delete shares
delete_doc("DocShare", frappe.db.sql_list("""select name from `tabDocShare`
where share_doctype=%s and share_name=%s""", (doctype, name)),
ignore_on_trash=True, force=True)

# delete versions
frappe.db.sql('delete from tabVersion where ref_doctype=%s and docname=%s', (doctype, name))

# delete comments # delete comments
frappe.db.sql("""delete from `tabCommunication` frappe.db.sql("""delete from `tabCommunication`
where where
@@ -268,14 +276,6 @@ def delete_dynamic_links(doctype, name):
set timeline_doctype=null, timeline_name=null set timeline_doctype=null, timeline_name=null
where timeline_doctype=%s and timeline_name=%s""", (doctype, name)) where timeline_doctype=%s and timeline_name=%s""", (doctype, name))


# delete shares
delete_doc("DocShare", frappe.db.sql_list("""select name from `tabDocShare`
where share_doctype=%s and share_name=%s""", (doctype, name)),
ignore_on_trash=True, force=True)

# delete versions
frappe.db.sql('delete from tabVersion where ref_doctype=%s and docname=%s', (doctype, name))

def insert_feed(doc): def insert_feed(doc):
from frappe.utils import get_fullname from frappe.utils import get_fullname




+ 1
- 0
frappe/patches.txt Прегледај датотеку

@@ -192,3 +192,4 @@ frappe.patches.v8_5.delete_email_group_member_with_invalid_emails
frappe.patches.v8_x.update_user_permission frappe.patches.v8_x.update_user_permission
frappe.patches.v8_5.patch_event_colors frappe.patches.v8_5.patch_event_colors
frappe.patches.v8_7.update_email_queue_status frappe.patches.v8_7.update_email_queue_status
frappe.patches.v8_10.delete_static_web_page_from_global_search

+ 0
- 0
frappe/patches/v8_10/__init__.py Прегледај датотеку


+ 5
- 0
frappe/patches/v8_10/delete_static_web_page_from_global_search.py Прегледај датотеку

@@ -0,0 +1,5 @@
from __future__ import unicode_literals
import frappe

def execute():
frappe.db.sql("""delete from `__global_search` where doctype='Static Web Page'""");

+ 0
- 2
frappe/public/build.json Прегледај датотеку

@@ -97,7 +97,6 @@
"public/css/bootstrap.css", "public/css/bootstrap.css",
"public/css/font-awesome.css", "public/css/font-awesome.css",
"public/css/octicons/octicons.css", "public/css/octicons/octicons.css",
"public/css/cal-heatmap.css",
"public/css/c3.min.css", "public/css/c3.min.css",
"public/css/desk.css", "public/css/desk.css",
"public/css/indicator.css", "public/css/indicator.css",
@@ -231,7 +230,6 @@
], ],
"js/d3.min.js": [ "js/d3.min.js": [
"public/js/lib/d3.min.js", "public/js/lib/d3.min.js",
"public/js/lib/cal-heatmap.js",
"public/js/lib/c3.min.js" "public/js/lib/c3.min.js"
], ],
"css/module.min.css": [ "css/module.min.css": [


+ 0
- 140
frappe/public/css/cal-heatmap.css Прегледај датотеку

@@ -1,140 +0,0 @@
/* Cal-HeatMap CSS */

.cal-heatmap-container {
display: block;
}

.cal-heatmap-container .graph-label
{
fill: #999;
font-size: 10px
}

.cal-heatmap-container .graph, .cal-heatmap-container .graph-legend rect {
shape-rendering: crispedges
}

.cal-heatmap-container .graph-rect
{
fill: #ededed
}

.cal-heatmap-container .graph-subdomain-group rect:hover
{
stroke: #000;
stroke-width: 1px
}

.cal-heatmap-container .subdomain-text {
font-size: 8px;
fill: #999;
pointer-events: none
}

.cal-heatmap-container .hover_cursor:hover {
cursor: pointer
}

.cal-heatmap-container .qi {
background-color: #999;
fill: #999
}

/*
Remove comment to apply this style to date with value equal to 0
.q0
{
background-color: #fff;
fill: #fff;
stroke: #ededed
}
*/

.cal-heatmap-container .q1
{
background-color: #dae289;
fill: #dae289
}

.cal-heatmap-container .q2
{
background-color: #cedb9c;
fill: #9cc069
}

.cal-heatmap-container .q3
{
background-color: #b5cf6b;
fill: #669d45
}

.cal-heatmap-container .q4
{
background-color: #637939;
fill: #637939
}

.cal-heatmap-container .q5
{
background-color: #3b6427;
fill: #3b6427
}

.cal-heatmap-container rect.highlight
{
stroke:#444;
stroke-width:1
}

.cal-heatmap-container text.highlight
{
fill: #444
}

.cal-heatmap-container rect.now
{
stroke: red
}

.cal-heatmap-container text.now
{
fill: red;
font-weight: 800
}

.cal-heatmap-container .domain-background {
fill: none;
shape-rendering: crispedges
}

.ch-tooltip {
padding: 10px;
background: #222;
color: #bbb;
font-size: 12px;
line-height: 1.4;
width: 140px;
position: absolute;
z-index: 99999;
text-align: center;
border-radius: 2px;
box-shadow: 2px 2px 2px rgba(0,0,0,0.2);
display: none;
box-sizing: border-box;
}

.ch-tooltip::after{
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
content: "";
padding: 0;
display: block;
bottom: -6px;
left: 50%;
margin-left: -6px;
border-width: 6px 6px 0;
border-top-color: #222;
}

+ 4
- 0
frappe/public/css/form.css Прегледај датотеку

@@ -98,6 +98,10 @@
.form-dashboard-section:last-child { .form-dashboard-section:last-child {
border-bottom: none; border-bottom: none;
} }
.form-heatmap .heatmap {
display: flex;
justify-content: center;
}
.form-heatmap .heatmap-message { .form-heatmap .heatmap-message {
margin-top: 10px; margin-top: 10px;
} }


+ 69
- 71
frappe/public/css/graphs.css Прегледај датотеку

@@ -2,9 +2,10 @@
.graph-container .graph-focus-margin { .graph-container .graph-focus-margin {
margin: 0px 5%; margin: 0px 5%;
} }
.graph-container .graph-graphics {
.graph-container .graphics {
margin-top: 10px; margin-top: 10px;
padding: 10px 0px;
padding-top: 10px;
padding-bottom: 10px;
position: relative; position: relative;
} }
.graph-container .graph-stats-group { .graph-container .graph-stats-group {
@@ -34,31 +35,28 @@
.graph-container .graph-stats-container .graph-data .stats-value { .graph-container .graph-stats-container .graph-data .stats-value {
color: #98d85b; color: #98d85b;
} }
.graph-container .bar-graph .axis,
.graph-container .line-graph .axis {
.graph-container .axis,
.graph-container .chart-label {
font-size: 10px; font-size: 10px;
fill: #6a737d;
fill: #959ba1;
} }
.graph-container .bar-graph .axis line,
.graph-container .line-graph .axis line {
.graph-container .axis line,
.graph-container .chart-label line {
stroke: rgba(27, 31, 35, 0.1); stroke: rgba(27, 31, 35, 0.1);
} }
.graph-container .percentage-graph {
margin-top: 35px;
}
.graph-container .percentage-graph .progress { .graph-container .percentage-graph .progress {
margin-bottom: 0px; margin-bottom: 0px;
} }
.graph-container .graph-data-points circle {
.graph-container .data-points circle {
stroke: #fff; stroke: #fff;
stroke-width: 2; stroke-width: 2;
} }
.graph-container .graph-data-points path {
.graph-container .data-points path {
fill: none; fill: none;
stroke-opacity: 1; stroke-opacity: 1;
stroke-width: 2px; stroke-width: 2px;
} }
.graph-container line.graph-dashed {
.graph-container line.dashed {
stroke-dasharray: 5,3; stroke-dasharray: 5,3;
} }
.graph-container .tick.x-axis-label { .graph-container .tick.x-axis-label {
@@ -73,7 +71,7 @@
.graph-container .tick .x-value-text { .graph-container .tick .x-value-text {
text-anchor: middle; text-anchor: middle;
} }
.graph-container .graph-svg-tip {
.graph-svg-tip {
position: absolute; position: absolute;
z-index: 99999; z-index: 99999;
padding: 10px; padding: 10px;
@@ -83,12 +81,12 @@
background: rgba(0, 0, 0, 0.8); background: rgba(0, 0, 0, 0.8);
border-radius: 3px; border-radius: 3px;
} }
.graph-container .graph-svg-tip.comparison {
.graph-svg-tip.comparison {
padding: 0; padding: 0;
text-align: left; text-align: left;
pointer-events: none; pointer-events: none;
} }
.graph-container .graph-svg-tip.comparison .title {
.graph-svg-tip.comparison .title {
display: block; display: block;
padding: 10px; padding: 10px;
margin: 0; margin: 0;
@@ -96,28 +94,28 @@
line-height: 1; line-height: 1;
pointer-events: none; pointer-events: none;
} }
.graph-container .graph-svg-tip.comparison ul {
.graph-svg-tip.comparison ul {
margin: 0; margin: 0;
white-space: nowrap; white-space: nowrap;
list-style: none; list-style: none;
} }
.graph-container .graph-svg-tip.comparison li {
.graph-svg-tip.comparison li {
display: inline-block; display: inline-block;
padding: 5px 10px; padding: 5px 10px;
} }
.graph-container .graph-svg-tip ul,
.graph-container .graph-svg-tip ol {
.graph-svg-tip ul,
.graph-svg-tip ol {
padding-left: 0; padding-left: 0;
display: flex; display: flex;
} }
.graph-container .graph-svg-tip ul.data-point-list li {
.graph-svg-tip ul.data-point-list li {
min-width: 90px; min-width: 90px;
flex: 1; flex: 1;
} }
.graph-container .graph-svg-tip strong {
.graph-svg-tip strong {
color: #dfe2e5; color: #dfe2e5;
} }
.graph-container .graph-svg-tip::after {
.graph-svg-tip .svg-pointer {
position: absolute; position: absolute;
bottom: -10px; bottom: -10px;
left: 50%; left: 50%;
@@ -128,147 +126,147 @@
border: 5px solid transparent; border: 5px solid transparent;
border-top-color: rgba(0, 0, 0, 0.8); border-top-color: rgba(0, 0, 0, 0.8);
} }
.graph-container .stroke.grey {
.stroke.grey {
stroke: #F0F4F7; stroke: #F0F4F7;
} }
.graph-container .stroke.blue {
.stroke.blue {
stroke: #5e64ff; stroke: #5e64ff;
} }
.graph-container .stroke.red {
.stroke.red {
stroke: #ff5858; stroke: #ff5858;
} }
.graph-container .stroke.light-green {
.stroke.light-green {
stroke: #98d85b; stroke: #98d85b;
} }
.graph-container .stroke.green {
.stroke.green {
stroke: #28a745; stroke: #28a745;
} }
.graph-container .stroke.orange {
.stroke.orange {
stroke: #ffa00a; stroke: #ffa00a;
} }
.graph-container .stroke.purple {
.stroke.purple {
stroke: #743ee2; stroke: #743ee2;
} }
.graph-container .stroke.darkgrey {
.stroke.darkgrey {
stroke: #b8c2cc; stroke: #b8c2cc;
} }
.graph-container .stroke.black {
.stroke.black {
stroke: #36414C; stroke: #36414C;
} }
.graph-container .stroke.yellow {
.stroke.yellow {
stroke: #FEEF72; stroke: #FEEF72;
} }
.graph-container .stroke.light-blue {
.stroke.light-blue {
stroke: #7CD6FD; stroke: #7CD6FD;
} }
.graph-container .stroke.lightblue {
.stroke.lightblue {
stroke: #7CD6FD; stroke: #7CD6FD;
} }
.graph-container .fill.grey {
.fill.grey {
fill: #F0F4F7; fill: #F0F4F7;
} }
.graph-container .fill.blue {
.fill.blue {
fill: #5e64ff; fill: #5e64ff;
} }
.graph-container .fill.red {
.fill.red {
fill: #ff5858; fill: #ff5858;
} }
.graph-container .fill.light-green {
.fill.light-green {
fill: #98d85b; fill: #98d85b;
} }
.graph-container .fill.green {
.fill.green {
fill: #28a745; fill: #28a745;
} }
.graph-container .fill.orange {
.fill.orange {
fill: #ffa00a; fill: #ffa00a;
} }
.graph-container .fill.purple {
.fill.purple {
fill: #743ee2; fill: #743ee2;
} }
.graph-container .fill.darkgrey {
.fill.darkgrey {
fill: #b8c2cc; fill: #b8c2cc;
} }
.graph-container .fill.black {
.fill.black {
fill: #36414C; fill: #36414C;
} }
.graph-container .fill.yellow {
.fill.yellow {
fill: #FEEF72; fill: #FEEF72;
} }
.graph-container .fill.light-blue {
.fill.light-blue {
fill: #7CD6FD; fill: #7CD6FD;
} }
.graph-container .fill.lightblue {
.fill.lightblue {
fill: #7CD6FD; fill: #7CD6FD;
} }
.graph-container .background.grey {
.background.grey {
background: #F0F4F7; background: #F0F4F7;
} }
.graph-container .background.blue {
.background.blue {
background: #5e64ff; background: #5e64ff;
} }
.graph-container .background.red {
.background.red {
background: #ff5858; background: #ff5858;
} }
.graph-container .background.light-green {
.background.light-green {
background: #98d85b; background: #98d85b;
} }
.graph-container .background.green {
.background.green {
background: #28a745; background: #28a745;
} }
.graph-container .background.orange {
.background.orange {
background: #ffa00a; background: #ffa00a;
} }
.graph-container .background.purple {
.background.purple {
background: #743ee2; background: #743ee2;
} }
.graph-container .background.darkgrey {
.background.darkgrey {
background: #b8c2cc; background: #b8c2cc;
} }
.graph-container .background.black {
.background.black {
background: #36414C; background: #36414C;
} }
.graph-container .background.yellow {
.background.yellow {
background: #FEEF72; background: #FEEF72;
} }
.graph-container .background.light-blue {
.background.light-blue {
background: #7CD6FD; background: #7CD6FD;
} }
.graph-container .background.lightblue {
.background.lightblue {
background: #7CD6FD; background: #7CD6FD;
} }
.graph-container .border-top.grey {
.border-top.grey {
border-top: 3px solid #F0F4F7; border-top: 3px solid #F0F4F7;
} }
.graph-container .border-top.blue {
.border-top.blue {
border-top: 3px solid #5e64ff; border-top: 3px solid #5e64ff;
} }
.graph-container .border-top.red {
.border-top.red {
border-top: 3px solid #ff5858; border-top: 3px solid #ff5858;
} }
.graph-container .border-top.light-green {
.border-top.light-green {
border-top: 3px solid #98d85b; border-top: 3px solid #98d85b;
} }
.graph-container .border-top.green {
.border-top.green {
border-top: 3px solid #28a745; border-top: 3px solid #28a745;
} }
.graph-container .border-top.orange {
.border-top.orange {
border-top: 3px solid #ffa00a; border-top: 3px solid #ffa00a;
} }
.graph-container .border-top.purple {
.border-top.purple {
border-top: 3px solid #743ee2; border-top: 3px solid #743ee2;
} }
.graph-container .border-top.darkgrey {
.border-top.darkgrey {
border-top: 3px solid #b8c2cc; border-top: 3px solid #b8c2cc;
} }
.graph-container .border-top.black {
.border-top.black {
border-top: 3px solid #36414C; border-top: 3px solid #36414C;
} }
.graph-container .border-top.yellow {
.border-top.yellow {
border-top: 3px solid #FEEF72; border-top: 3px solid #FEEF72;
} }
.graph-container .border-top.light-blue {
.border-top.light-blue {
border-top: 3px solid #7CD6FD; border-top: 3px solid #7CD6FD;
} }
.graph-container .border-top.lightblue {
.border-top.lightblue {
border-top: 3px solid #7CD6FD; border-top: 3px solid #7CD6FD;
} }

+ 2
- 1
frappe/public/js/frappe/form/controls/text_editor.js Прегледај датотеку

@@ -46,7 +46,8 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
// this function is executed only once // this function is executed only once
$(".note-editable[contenteditable='true']").one('focus', function() { $(".note-editable[contenteditable='true']").one('focus', function() {
var $this = $(this); var $this = $(this);
$this.html($this.html() + '<br>');
if(!$this.html())
$this.html($this.html() + '<br>');
}); });
}, },
onChange: function(value) { onChange: function(value) {


+ 7
- 39
frappe/public/js/frappe/form/dashboard.js Прегледај датотеку

@@ -334,22 +334,12 @@ frappe.ui.form.Dashboard = Class.extend({
// heatmap // heatmap
render_heatmap: function() { render_heatmap: function() {
if(!this.heatmap) { if(!this.heatmap) {
this.heatmap = new CalHeatMap();
this.heatmap.init({
itemSelector: "#heatmap-" + frappe.model.scrub(this.frm.doctype),
domain: "month",
subDomain: "day",
start: moment().subtract(1, 'year').add(1, 'month').toDate(),
cellSize: 9,
cellPadding: 2,
domainGutter: 2,
range: 12,
domainLabelFormat: function(date) {
return moment(date).format("MMM").toUpperCase();
},
displayLegend: false,
legend: [5, 10, 15, 20]
// subDomainTextFormat: "%d",
this.heatmap = new frappe.ui.HeatMap({
parent: this.heatmap_area.find("#heatmap-" + frappe.model.scrub(this.frm.doctype)),
height: 100,
start: new Date(moment().subtract(1, 'year').toDate()),
count_label: "items",
discrete_domains: 0
}); });


// center the heatmap // center the heatmap
@@ -388,16 +378,14 @@ frappe.ui.form.Dashboard = Class.extend({
return indicator; return indicator;
}, },


//graphs
// graphs
setup_graph: function() { setup_graph: function() {
var me = this; var me = this;

var method = this.data.graph_method; var method = this.data.graph_method;
var args = { var args = {
doctype: this.frm.doctype, doctype: this.frm.doctype,
docname: this.frm.doc.name, docname: this.frm.doc.name,
}; };

$.extend(args, this.data.graph_method_args); $.extend(args, this.data.graph_method_args);


frappe.call({ frappe.call({
@@ -421,29 +409,9 @@ frappe.ui.form.Dashboard = Class.extend({
mode: 'line', mode: 'line',
height: 140 height: 140
}); });

new frappe.ui.Graph(args); new frappe.ui.Graph(args);
}, },


setup_chart: function(opts) {
var me = this;

this.graph_area.removeClass('hidden');

$.extend(opts, {
wrapper: me.graph_area,
padding: {
right: 30,
bottom: 30
}
});

this.chart = new frappe.ui.Chart(opts);
if(this.chart) {
this.show();
this.chart.set_chart_size(me.wrapper.width() - 60);
}
},
show: function() { show: function() {
this.section.removeClass('hidden'); this.section.removeClass('hidden');
} }


+ 1
- 1
frappe/public/js/frappe/form/templates/form_dashboard.html Прегледај датотеку

@@ -2,7 +2,7 @@
<div class="progress-area hidden form-dashboard-section"> <div class="progress-area hidden form-dashboard-section">
</div> </div>
<div class="form-heatmap hidden form-dashboard-section"> <div class="form-heatmap hidden form-dashboard-section">
<div id="heatmap-{{ frappe.model.scrub(frm.doctype) }}"></div>
<div id="heatmap-{{ frappe.model.scrub(frm.doctype) }}" class="heatmap"></div>
<div class="text-muted small heatmap-message hidden"></div> <div class="text-muted small heatmap-message hidden"></div>
</div> </div>
<div class="form-graph form-dashboard-section hidden"></div> <div class="form-graph form-dashboard-section hidden"></div>


+ 595
- 91
frappe/public/js/frappe/ui/graphs.js Прегледај датотеку

@@ -28,7 +28,6 @@ frappe.ui.Graph = class Graph {
specific_values = [], specific_values = [],
summary = [], summary = [],


color = 'blue',
mode = '', mode = '',
}) { }) {


@@ -43,27 +42,28 @@ frappe.ui.Graph = class Graph {
} }


this.parent = parent; this.parent = parent;
this.base_height = height;
this.height = height - 40;


this.translate_x = 60;
this.translate_y = 10;
this.set_margins(height);


this.title = title; this.title = title;
this.subtitle = subtitle; this.subtitle = subtitle;


// Begin axis graph-related args

this.y = y; this.y = y;
this.x = x; this.x = x;


this.specific_values = specific_values; this.specific_values = specific_values;
this.summary = summary; this.summary = summary;


this.color = color;
this.mode = mode; this.mode = mode;


// this.current_hover_index = 0;
// this.current_selected_index = 0;

this.$graph = null; this.$graph = null;


// Validate all arguments
// Validate all arguments, check passed data format, set defaults


frappe.require("assets/frappe/js/lib/snap.svg-min.js", this.setup.bind(this)); frappe.require("assets/frappe/js/lib/snap.svg-min.js", this.setup.bind(this));
} }
@@ -81,18 +81,16 @@ frappe.ui.Graph = class Graph {


refresh() { refresh() {


this.base_width = this.parent.width() - 20;
this.width = this.base_width - 100;
this.setup_base_values();
this.set_width();
this.width = this.base_width - this.translate_x * 2;


this.setup_container(); this.setup_container();
this.setup_components();
this.setup_values(); this.setup_values();

this.setup_utils(); this.setup_utils();


this.setup_components();
this.make_graph_components(); this.make_graph_components();

this.make_tooltip(); this.make_tooltip();


if(this.summary.length > 0) { if(this.summary.length > 0) {
@@ -102,6 +100,20 @@ frappe.ui.Graph = class Graph {
} }
} }


set_margins(height) {
this.base_height = height;
this.height = height - 40;

this.translate_x = 60;
this.translate_y = 10;
}

set_width() {
this.base_width = this.parent.width();
}

setup_base_values() {}

setup_container() { setup_container() {
// Graph needs a dedicated parent element // Graph needs a dedicated parent element
this.parent.empty(); this.parent.empty();
@@ -110,11 +122,11 @@ frappe.ui.Graph = class Graph {
.addClass('graph-container') .addClass('graph-container')
.append($(`<h6 class="title" style="margin-top: 15px;">${this.title}</h6>`)) .append($(`<h6 class="title" style="margin-top: 15px;">${this.title}</h6>`))
.append($(`<h6 class="sub-title uppercase">${this.subtitle}</h6>`)) .append($(`<h6 class="sub-title uppercase">${this.subtitle}</h6>`))
.append($(`<div class="graph-graphics"></div>`))
.append($(`<div class="graphics"></div>`))
.append($(`<div class="graph-stats-container"></div>`)) .append($(`<div class="graph-stats-container"></div>`))
.appendTo(this.parent); .appendTo(this.parent);


this.$graphics = this.container.find('.graph-graphics');
this.$graphics = this.container.find('.graphics');
this.$stats_container = this.container.find('.graph-stats-container'); this.$stats_container = this.container.find('.graph-stats-container');


this.$graph = $('<div>') this.$graph = $('<div>')
@@ -130,6 +142,13 @@ frappe.ui.Graph = class Graph {
return this.$svg; return this.$svg;
} }


setup_components() {
this.y_axis_group = this.snap.g().attr({ class: "y axis" });
this.x_axis_group = this.snap.g().attr({ class: "x axis" });
this.data_units = this.snap.g().attr({ class: "data-points" });
this.specific_y_lines = this.snap.g().attr({ class: "specific axis" });
}

setup_values() { setup_values() {
// Multiplier // Multiplier
let all_values = this.specific_values.map(d => d.value); let all_values = this.specific_values.map(d => d.value);
@@ -170,20 +189,15 @@ frappe.ui.Graph = class Graph {
}); });
} }


setup_components() {
this.y_axis_group = this.snap.g().attr({ class: "y axis" });
this.x_axis_group = this.snap.g().attr({ class: "x axis" });
this.data_units = this.snap.g().attr({ class: "graph-data-points" });
this.specific_y_lines = this.snap.g().attr({ class: "specific axis" });
}

make_graph_components() { make_graph_components() {
this.make_y_axis(); this.make_y_axis();
this.make_x_axis(); this.make_x_axis();
this.y_colors = ['lightblue', 'purple', 'blue', 'green', 'lightgreen',
'yellow', 'orange', 'red']


this.y.map((d, i) => { this.y.map((d, i) => {
this.make_units(d.y_tops, d.color, i);
this.make_path(d);
this.make_units(d.y_tops, d.color || this.y_colors[i], i);
this.make_path(d, d.color || this.y_colors[i]);
}); });


if(this.specific_values.length > 0) { if(this.specific_values.length > 0) {
@@ -246,6 +260,11 @@ frappe.ui.Graph = class Graph {
transform: `translate(0,${start_at})` transform: `translate(0,${start_at})`
}); });
this.x.values.map((point, i) => { this.x.values.map((point, i) => {
let allowed_space = this.avg_unit_width * 1.5;
if(this.get_strwidth(point) > allowed_space) {
let allowed_letters = allowed_space / 8;
point = point.slice(0, allowed_letters-3) + " ...";
}
this.x_axis_group.add(this.snap.g( this.x_axis_group.add(this.snap.g(
this.snap.line(0, 0, 0, height), this.snap.line(0, 0, 0, height),
this.snap.text(0, text_start_at, point).attr({ this.snap.text(0, text_start_at, point).attr({
@@ -262,8 +281,13 @@ frappe.ui.Graph = class Graph {
make_units(y_values, color, dataset_index) { make_units(y_values, color, dataset_index) {
let d = this.unit_args; let d = this.unit_args;
y_values.map((y, i) => { y_values.map((y, i) => {
let data_unit = this.draw[d.type](this.x_axis_values[i],
y, d.args, color, dataset_index);
let data_unit = this.draw[d.type](
this.x_axis_values[i],
y,
d.args,
color,
dataset_index
);
this.data_units.add(data_unit); this.data_units.add(data_unit);
this.y[dataset_index].data_units.push(data_unit); this.y[dataset_index].data_units.push(data_unit);
}); });
@@ -272,75 +296,58 @@ frappe.ui.Graph = class Graph {
make_path() { } make_path() { }


make_tooltip() { make_tooltip() {
this.tip = $(`<div class="graph-svg-tip comparison">
<span class="title"></span>
<ul class="data-point-list">
</ul>
</div>`).attr({
style: `top: 0px; left: 0px; opacity: 0; pointer-events: none;`
}).appendTo(this.$graphics);

this.tip_title = this.tip.find('.title');
this.tip_data_point_list = this.tip.find('.data-point-list');

// should be w.r.t. this.parent
this.tip = new frappe.ui.SvgTip({
parent: this.$graphics,
});
this.bind_tooltip(); this.bind_tooltip();
} }


bind_tooltip() { bind_tooltip() {
// should be w.r.t. this.parent, but will have to take care of
// all the elements and padding, margins on top
this.$graphics.on('mousemove', (e) => { this.$graphics.on('mousemove', (e) => {
let offset = $(this.$graphics).offset();
let offset = this.$graphics.offset();
var relX = e.pageX - offset.left - this.translate_x; var relX = e.pageX - offset.left - this.translate_x;
var relY = e.pageY - offset.top - this.translate_y; var relY = e.pageY - offset.top - this.translate_y;


if(relY < this.height) {
for(var i=this.x_axis_values.length - 1; i >= 0 ; i--) {
let x_val = this.x_axis_values[i];
if(relX > x_val - this.avg_unit_width/2) {
let x = x_val - this.tip.width()/2 + this.translate_x;
let y = this.y_min_tops[i] - this.tip.height() + this.translate_y;

this.fill_tooltip(i);

this.tip.attr({
style: `top: ${y}px; left: ${x-0.5}px; opacity: 1; pointer-events: none;`
});
break;
}
}
if(relY < this.height + this.translate_y * 2) {
this.map_tooltip_x_position_and_show(relX);
} else { } else {
this.tip.attr({
style: `top: 0px; left: 0px; opacity: 0; pointer-events: none;`
});
this.tip.hide_tip()
} }
}); });

this.$graphics.on('mouseleave', () => {
this.tip.attr({
style: `top: 0px; left: 0px; opacity: 0; pointer-events: none;`
});
});
} }


fill_tooltip(i) {
this.tip_title.html(this.x.formatted && this.x.formatted.length>0
? this.x.formatted[i] : this.x.values[i]);
this.tip_data_point_list.empty();
this.y.map(y_set => {
let $li = $(`<li>
<strong style="display: block;">
${y_set.formatted ? y_set.formatted[i] : y_set.values[i]}
</strong>
${y_set.title ? y_set.title : '' }
</li>`).addClass(`border-top ${y_set.color}`);
this.tip_data_point_list.append($li);
});
map_tooltip_x_position_and_show(relX) {
for(var i=this.x_axis_values.length - 1; i >= 0 ; i--) {
let x_val = this.x_axis_values[i];
// let delta = i === 0 ? this.avg_unit_width : x_val - this.x_axis_values[i-1];
if(relX > x_val - this.avg_unit_width/2) {
let x = x_val + this.translate_x - 0.5;
let y = this.y_min_tops[i] + this.translate_y;
let title = this.x.formatted && this.x.formatted.length>0
? this.x.formatted[i] : this.x.values[i];
let values = this.y.map((set, j) => {
return {
title: set.title,
value: set.formatted ? set.formatted[i] : set.values[i],
color: set.color || this.y_colors[j],
}
});

this.tip.set_values(x, y, title, '', values);
this.tip.show_tip();
break;
}
}
} }


show_specific_values() { show_specific_values() {
this.specific_values.map(d => { this.specific_values.map(d => {
this.specific_y_lines.add(this.snap.g( this.specific_y_lines.add(this.snap.g(
this.snap.line(0, 0, this.width, 0).attr({ this.snap.line(0, 0, this.width, 0).attr({
class: d.line_type === "dashed" ? "graph-dashed": ""
class: d.line_type === "dashed" ? "dashed": ""
}), }),
this.snap.text(this.width + 5, 0, d.name.toUpperCase()).attr({ this.snap.text(this.width + 5, 0, d.name.toUpperCase()).attr({
dy: ".32em", dy: ".32em",
@@ -434,6 +441,9 @@ frappe.ui.Graph = class Graph {


let width = total_width / args.no_of_datasets; let width = total_width / args.no_of_datasets;
let current_x = start_x + width * index; let current_x = start_x + width * index;
if(y == this.height) {
y = this.height * 0.98;
}
return this.snap.rect(current_x, y, width, this.height - y).attr({ return this.snap.rect(current_x, y, width, this.height - y).attr({
class: `bar mini fill ${color}` class: `bar mini fill ${color}`
}); });
@@ -442,6 +452,11 @@ frappe.ui.Graph = class Graph {
return this.snap.circle(x, y, args.radius).attr({ return this.snap.circle(x, y, args.radius).attr({
class: `fill ${color}` class: `fill ${color}`
}); });
},
'rect': (x, y, args, color) => {
return this.snap.rect(x, y, args.width, args.height).attr({
class: `fill ${color}`
});
} }
}; };


@@ -470,6 +485,7 @@ frappe.ui.BarGraph = class BarGraph extends frappe.ui.Graph {
this.unit_args = { this.unit_args = {
type: 'bar', type: 'bar',
args: { args: {
// More intelligent width setting
space_width: this.y.length > 1 ? space_width: this.y.length > 1 ?
me.avg_unit_width/2 : me.avg_unit_width/8, me.avg_unit_width/2 : me.avg_unit_width/8,
no_of_datasets: this.y.length no_of_datasets: this.y.length
@@ -498,10 +514,10 @@ frappe.ui.LineGraph = class LineGraph extends frappe.ui.Graph {
}; };
} }


make_path(d) {
make_path(d, color) {
let points_list = d.y_tops.map((y, i) => (this.x_axis_values[i] + ',' + y)); let points_list = d.y_tops.map((y, i) => (this.x_axis_values[i] + ',' + y));
let path_str = "M"+points_list.join("L"); let path_str = "M"+points_list.join("L");
d.path = this.snap.path(path_str).attr({class: `stroke ${d.color}`});
d.path = this.snap.path(path_str).attr({class: `stroke ${color}`});
this.data_units.prepend(d.path); this.data_units.prepend(d.path);
} }
}; };
@@ -512,7 +528,9 @@ frappe.ui.PercentageGraph = class PercentageGraph extends frappe.ui.Graph {
} }


make_graph_area() { make_graph_area() {
this.$graphics.addClass('graph-focus-margin');
this.$graphics.addClass('graph-focus-margin').attr({
style: `margin-top: 45px;`
});
this.$stats_container.addClass('graph-focus-margin').attr({ this.$stats_container.addClass('graph-focus-margin').attr({
style: `padding-top: 0px; margin-bottom: 30px;` style: `padding-top: 0px; margin-bottom: 30px;`
}); });
@@ -533,37 +551,523 @@ frappe.ui.PercentageGraph = class PercentageGraph extends frappe.ui.Graph {
return total; return total;
}); });


// Calculate x unit distances for tooltips
if(!this.x.colors) {
this.x.colors = ['green', 'blue', 'purple', 'red', 'orange',
'yellow', 'lightblue', 'lightgreen'];
}
} }


setup_utils() { } setup_utils() { }
setup_components() { setup_components() {
this.$percentage_bar = $(`<div class="progress"> this.$percentage_bar = $(`<div class="progress">
</div>`).appendTo(this.$chart);
</div>`).appendTo(this.$chart); // get this.height, width and avg from this if needed
} }


make_graph_components() { make_graph_components() {
let grand_total = this.x.totals.reduce((a, b) => a + b, 0);
this.grand_total = this.x.totals.reduce((a, b) => a + b, 0);
this.x.units = []; this.x.units = [];
this.x.totals.map((total, i) => { this.x.totals.map((total, i) => {
let $part = $(`<div class="progress-bar background ${this.x.colors[i]}" let $part = $(`<div class="progress-bar background ${this.x.colors[i]}"
style="width: ${total*100/grand_total}%"></div>`);
style="width: ${total*100/this.grand_total}%"></div>`);
this.x.units.push($part); this.x.units.push($part);
this.$percentage_bar.append($part); this.$percentage_bar.append($part);
}); });
} }


make_tooltip() { }
bind_tooltip() {
this.x.units.map(($part, i) => {
$part.on('mouseenter', () => {
let g_off = this.$graphics.offset(), p_off = $part.offset();

let x = p_off.left - g_off.left + $part.width()/2;
let y = p_off.top - g_off.top - 6;
let title = (this.x.formatted && this.x.formatted.length>0
? this.x.formatted[i] : this.x.values[i]) + ': ';
let percent = (this.x.totals[i]*100/this.grand_total).toFixed(1);

this.tip.set_values(x, y, title, percent);
this.tip.show_tip();
});
});
}


show_summary() { show_summary() {
let values = this.x.formatted.length > 0 ? this.x.formatted : this.x.values;
let x_values = this.x.formatted && this.x.formatted.length > 0
? this.x.formatted : this.x.values;
this.x.totals.map((d, i) => { this.x.totals.map((d, i) => {
this.$stats_container.append($(`<div class="stats">
<span class="indicator ${this.x.colors[i]}">
<span class="text-muted">${values[i]}:</span>
${d}
</span>
</div>`));
if(d) {
this.$stats_container.append($(`<div class="stats">
<span class="indicator ${this.x.colors[i]}">
<span class="text-muted">${x_values[i]}:</span>
${d}
</span>
</div>`));
}
});
}
};

frappe.ui.HeatMap = class HeatMap extends frappe.ui.Graph {
constructor({
parent = null,
height = 240,
title = '', subtitle = '',

start = new Date(moment().subtract(1, 'year').toDate()),
domain = '',
subdomain = '',
data = {},
discrete_domains = 0,
count_label = '',

// remove these graph related args
y = [],
x = [],
specific_values = [],
summary = [],
mode = 'heatmap',
} = {}) {
super(arguments[0]);
this.start = start || new Date(moment().subtract(1, 'year').toDate());
this.data = data;
this.discrete_domains = discrete_domains;

this.count_label = count_label;

this.legend_colors = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
}

setup_base_values() {
this.today = new Date();

this.first_week_start = new Date(this.start.toDateString());
this.last_week_start = new Date(this.today.toDateString());
if(this.first_week_start.getDay() !== 7) {
this.add_days(this.first_week_start, (-1) * this.first_week_start.getDay());
}
if(this.last_week_start.getDay() !== 7) {
this.add_days(this.last_week_start, (-1) * this.last_week_start.getDay());
}
this.no_of_cols = this.get_weeks_between(this.first_week_start + '', this.last_week_start + '') + 1;
}

set_width() {
this.base_width = (this.no_of_cols) * 12;
}

setup_components() {
this.domain_label_group = this.snap.g().attr({ class: "domain-label-group chart-label" });
this.data_groups = this.snap.g().attr({ class: "data-groups", transform: `translate(0, 20)` });
}

setup_values() {
this.distribution = this.get_distribution(this.data, this.legend_colors);
this.month_names = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];

this.render_all_weeks_and_store_x_values(this.no_of_cols);
}

render_all_weeks_and_store_x_values(no_of_weeks) {
let current_week_sunday = new Date(this.first_week_start);
this.week_col = 0;
this.current_month = current_week_sunday.getMonth();

this.months = [this.current_month + ''];
this.month_weeks = {}, this.month_start_points = [];
this.month_weeks[this.current_month] = 0;
this.month_start_points.push(13);

for(var i = 0; i < no_of_weeks; i++) {
let data_group, month_change = 0;
let day = new Date(current_week_sunday);

[data_group, month_change] = this.get_week_squares_group(day, this.week_col);
this.data_groups.add(data_group);
this.week_col += 1 + parseInt(this.discrete_domains && month_change);
this.month_weeks[this.current_month]++;
if(month_change) {
this.current_month = (this.current_month + 1) % 12;
this.months.push(this.current_month + '');
this.month_weeks[this.current_month] = 1;
}
this.add_days(current_week_sunday, 7);
}
this.render_month_labels();
}

get_week_squares_group(current_date, index) {
const no_of_weekdays = 7;
const square_side = 10;
const cell_padding = 2;
const step = 1;

let month_change = 0;
let week_col_change = 0;

let data_group = this.snap.g().attr({ class: "data-group" });

for(var y = 0, i = 0; i < no_of_weekdays; i += step, y += (square_side + cell_padding)) {
let data_value = 0;
let color_index = 0;

// TODO: More foolproof for any data
let timestamp = Math.floor(current_date.getTime()/1000).toFixed(1);

if(this.data[timestamp]) {
data_value = this.data[timestamp];
color_index = this.get_max_checkpoint(data_value, this.distribution);
}

if(this.data[Math.round(timestamp)]) {
data_value = this.data[Math.round(timestamp)];
color_index = this.get_max_checkpoint(data_value, this.distribution);
}

let x = 13 + (index + week_col_change) * 12;

data_group.add(this.snap.rect(x, y, square_side, square_side).attr({
'class': `day`,
'fill': this.legend_colors[color_index],
'data-date': this.get_dd_mm_yyyy(current_date),
'data-value': data_value,
'data-day': current_date.getDay()
}));

let next_date = new Date(current_date);
this.add_days(next_date, 1);
if(next_date.getMonth() - current_date.getMonth()) {
month_change = 1;
if(this.discrete_domains) {
week_col_change = 1;
}

this.month_start_points.push(13 + (index + week_col_change) * 12);
}
current_date = next_date;
}

return [data_group, month_change];
}

render_month_labels() {
this.first_month_label = 1;
// if (this.first_week_start.getDate() > 8) {
// this.first_month_label = 0;
// }
this.last_month_label = 1;

let first_month = this.months.shift();
let first_month_start = this.month_start_points.shift();
// render first month if

let last_month = this.months.pop();
let last_month_start = this.month_start_points.pop();
// render last month if

this.month_start_points.map((start, i) => {
let month_name = this.month_names[this.months[i]].substring(0, 3);
this.domain_label_group.add(this.snap.text(start + 12, 10, month_name).attr({
dy: ".32em",
class: "y-value-text"
}));
});
}

make_graph_components() {
this.container.find('.graph-stats-container, .sub-title, .title').hide();
this.container.find('.graphics').css({'margin-top': '0px', 'padding-top': '0px'});
}

bind_tooltip() {
this.container.on('mouseenter', '.day', (e) => {
let subdomain = $(e.target);
let count = subdomain.attr('data-value');
let date_parts = subdomain.attr('data-date').split('-');

let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3);

let g_off = this.$graphics.offset(), p_off = subdomain.offset();

let width = parseInt(subdomain.attr('width'));
let x = p_off.left - g_off.left + (width+2)/2;
let y = p_off.top - g_off.top - (width+2)/2;
let value = count + ' ' + this.count_label;
let name = ' on ' + month + ' ' + date_parts[0] + ', ' + date_parts[2];

this.tip.set_values(x, y, name, value, [], 1);
this.tip.show_tip();
});
}

update(data) {
this.data = data;
this.setup_values();
}

get_distribution(data, mapper_array) {
let data_values = Object.keys(data).map(key => data[key]);
let data_max_value = Math.max(...data_values);

let distribution_step = 1 / (mapper_array.length - 1);
let distribution = [];

mapper_array.map((color, i) => {
let checkpoint = data_max_value * (distribution_step * i);
distribution.push(checkpoint);
});

return distribution;
}

get_max_checkpoint(value, distribution) {
return distribution.filter(d => {
return d <= value;
}).length - 1;
}

// TODO: date utils, move these out

// https://stackoverflow.com/a/11252167/6495043
treat_as_utc(date_str) {
let result = new Date(date_str);
result.setMinutes(result.getMinutes() - result.getTimezoneOffset());
return result;
}

get_dd_mm_yyyy(date) {
let dd = date.getDate();
let mm = date.getMonth() + 1; // getMonth() is zero-based
return [
(dd>9 ? '' : '0') + dd,
(mm>9 ? '' : '0') + mm,
date.getFullYear()
].join('-');
}

get_weeks_between(start_date_str, end_date_str) {
return Math.ceil(this.get_days_between(start_date_str, end_date_str) / 7);
}

get_days_between(start_date_str, end_date_str) {
let milliseconds_per_day = 24 * 60 * 60 * 1000;
return (this.treat_as_utc(end_date_str) - this.treat_as_utc(start_date_str)) / milliseconds_per_day;
}

// mutates
add_days(date, number_of_days) {
date.setDate(date.getDate() + number_of_days);
}

get_month_name() {}
}

frappe.ui.SvgTip = class {
constructor({
parent = null
}) {
this.parent = parent;
this.title_name = '';
this.title_value = '';
this.list_values = [];
this.title_value_first = 0;

this.x = 0;
this.y = 0;

this.top = 0;
this.left = 0;

this.setup();
}

setup() {
this.make_tooltip();
}

refresh() {
this.fill();
this.calc_position();
// this.show_tip();
}

make_tooltip() {
this.container = $(`<div class="graph-svg-tip comparison">
<span class="title"></span>
<ul class="data-point-list"></ul>
<div class="svg-pointer"></div>
</div>`).appendTo(this.parent);
this.hide_tip();

this.title = this.container.find('.title');
this.data_point_list = this.container.find('.data-point-list');

this.parent.on('mouseleave', () => {
this.hide_tip();
});
}

fill() {
let title;
if(this.title_value_first) {
title = `<strong>${this.title_value}</strong>${this.title_name}`;
} else {
title = `${this.title_name}<strong>${this.title_value}</strong>`;
}
this.title.html(title);
this.data_point_list.empty();
this.list_values.map((set, i) => {
let $li = $(`<li>
<strong style="display: block;">${set.value ? set.value : '' }</strong>
${set.title ? set.title : '' }
</li>`).addClass(`border-top ${set.color || 'black'}`);

this.data_point_list.append($li);
});
}

calc_position() {
this.top = this.y - this.container.height();
this.left = this.x - this.container.width()/2;
let max_left = this.parent.width() - this.container.width();

let $pointer = this.container.find('.svg-pointer');

if(this.left < 0) {
$pointer.css({ 'left': `calc(50% - ${-1 * this.left}px)` });
this.left = 0;
} else if(this.left > max_left) {
let delta = this.left - max_left;
$pointer.css({ 'left': `calc(50% + ${delta}px)` });
this.left = max_left;
} else {
$pointer.css({ 'left': `50%` });
}
}

set_values(x, y, title_name = '', title_value = '', list_values = [], title_value_first = 0) {
this.title_name = title_name;
this.title_value = title_value;
this.list_values = list_values;
this.x = x;
this.y = y;
this.title_value_first = title_value_first;
this.refresh();
}

hide_tip() {
this.container.css({
'top': '0px',
'left': '0px',
'opacity': '0'
});
}

show_tip() {
this.container.css({
'top': this.top + 'px',
'left': this.left + 'px',
'opacity': '1'
}); });
} }
}; };


frappe.provide("frappe.ui.graphs");

frappe.ui.graphs.get_timeseries = function(start, frequency, length) {

}

frappe.ui.graphs.map_c3 = function(chart) {
if (chart.data) {
let data = chart.data;
let mode = chart.chart_type || 'line';
if(mode === 'pie') {
mode = 'percentage';
}

let x = {}, y = [];

if(data.columns) {
let columns = data.columns;

x.values = columns.filter(col => {
return col[0] === data.x;
})[0];

if(x.values && x.values.length) {
let dataset_length = x.values.length;
let dirty = false;
columns.map(col => {
if(col[0] !== data.x) {
if(col.length === dataset_length) {
let title = col[0];
col.splice(0, 1);
y.push({
title: title,
values: col,
});
} else {
dirty = true;
}
}
})

if(dirty) {
return;
}

x.values.splice(0, 1);

return {
mode: mode,
y: y,
x: x
}

}
} else if(data.rows) {
let rows = data.rows;
x.values = rows[0];

rows.map((row, i) => {
if(i === 0) {
x.values = row;
} else {
y.push({
title: 'data' + i,
values: row,
})
}
});

return {
mode: mode,
y: y,
x: x
}
}
}
}


// frappe.ui.CompositeGraph = class {
// constructor({
// parent = null
// }) {
// this.parent = parent;
// this.title_name = '';
// this.title_value = '';
// this.list_values = [];

// this.x = 0;
// this.y = 0;

// this.top = 0;
// this.left = 0;

// this.setup();
// }
// }

+ 2
- 0
frappe/public/js/frappe/ui/toolbar/search_utils.js Прегледај датотеку

@@ -490,6 +490,8 @@ frappe.search.utils = {
// 0 - 6 for fuzzy contain // 0 - 6 for fuzzy contain


// **Specific use-case step** // **Specific use-case step**
keywords = keywords || '';

var item = __(_item || '').replace(/-/g, " "); var item = __(_item || '').replace(/-/g, " ");


var ilen = item.length; var ilen = item.length;


+ 1
- 1
frappe/public/js/frappe/views/reports/print_grid.html Прегледај датотеку

@@ -24,7 +24,7 @@
{% for col in columns %} {% for col in columns %}
{% if col.name && col._id !== "_check" %} {% if col.name && col._id !== "_check" %}


{% var value = col.fieldname ? row[col.fieldname] : row[col.id]; %}
{% var value = col.fieldname ? row[col.fieldname] : row[col.field]; %}


<td>{{ col.formatter <td>{{ col.formatter
? col.formatter(row._index, col._index, value, col, row, true) ? col.formatter(row._index, col._index, value, col, row, true)


+ 1
- 0
frappe/public/js/frappe/views/reports/query_report.js Прегледај датотеку

@@ -3,6 +3,7 @@


frappe.provide("frappe.views"); frappe.provide("frappe.views");
frappe.provide("frappe.query_reports"); frappe.provide("frappe.query_reports");
frappe.provide("frappe.ui.graphs");


frappe.standard_pages["query-report"] = function() { frappe.standard_pages["query-report"] = function() {
var wrapper = frappe.container.add_page('query-report'); var wrapper = frappe.container.add_page('query-report');


+ 0
- 3471
frappe/public/js/lib/cal-heatmap.js
Разлика између датотеке није приказан због своје велике величине
Прегледај датотеку


+ 5
- 0
frappe/public/less/form.less Прегледај датотеку

@@ -136,6 +136,11 @@


.form-heatmap { .form-heatmap {


.heatmap {
display: flex;
justify-content: center;
}

.heatmap-message { .heatmap-message {
margin-top: 10px; margin-top: 10px;
} }


+ 204
- 210
frappe/public/less/graphs.less Прегледај датотеку

@@ -5,9 +5,10 @@
margin: 0px 5%; margin: 0px 5%;
} }


.graph-graphics {
.graphics {
margin-top: 10px; margin-top: 10px;
padding: 10px 0px;
padding-top: 10px;
padding-bottom: 10px;
position: relative; position: relative;
} }


@@ -43,28 +44,22 @@
} }
} }


.bar-graph, .line-graph {

// baselines
.axis {
font-size: 10px;
fill: #6a737d;
.axis, .chart-label {
font-size: 10px;
fill: #959ba1;


line {
stroke: rgba(27,31,35,0.1);
}
line {
stroke: rgba(27,31,35,0.1);
} }
} }


.percentage-graph { .percentage-graph {
margin-top: 35px;

.progress { .progress {
margin-bottom: 0px; margin-bottom: 0px;
} }
} }


.graph-data-points {
.data-points {
circle { circle {
// fill: #28a745; // fill: #28a745;
stroke: #fff; stroke: #fff;
@@ -83,7 +78,7 @@
} }
} }


line.graph-dashed {
line.dashed {
stroke-dasharray: 5,3; stroke-dasharray: 5,3;
} }


@@ -104,216 +99,215 @@
text-anchor: middle; text-anchor: middle;
} }
} }
}


.graph-svg-tip {
position: absolute;
z-index: 99999;
padding: 10px;
font-size: 12px;
color: #959da5;
text-align: center;
background: rgba(0,0,0,0.8);
border-radius: 3px;

&.comparison {
padding: 0;
text-align: left;
.graph-svg-tip {
position: absolute;
z-index: 99999;
padding: 10px;
font-size: 12px;
color: #959da5;
text-align: center;
background: rgba(0,0,0,0.8);
border-radius: 3px;

&.comparison {
padding: 0;
text-align: left;
pointer-events: none;

.title {
display: block;
padding: 10px;
margin: 0;
font-weight: 600;
line-height: 1;
pointer-events: none; pointer-events: none;

.title {
display: block;
padding: 10px;
margin: 0;
font-weight: 600;
line-height: 1;
pointer-events: none;
}

ul {
margin: 0;
white-space: nowrap;
list-style: none;
}

li {
display: inline-block;
padding: 5px 10px;
}
}

ul, ol {
padding-left: 0;
display: flex;
}

ul.data-point-list li {
min-width: 90px;
flex: 1;
} }


strong {
color: #dfe2e5;
ul {
margin: 0;
white-space: nowrap;
list-style: none;
} }


&::after {
position: absolute;
bottom: -10px;
left: 50%;
width: 5px;
height: 5px;
margin: 0 0 0 -5px;
content: " ";
border: 5px solid transparent;
border-top-color: rgba(0,0,0,0.8);
li {
display: inline-block;
padding: 5px 10px;
} }
} }


.stroke.grey {
stroke: #F0F4F7;
}
.stroke.blue {
stroke: #5e64ff;
}
.stroke.red {
stroke: #ff5858;
}
.stroke.light-green {
stroke: #98d85b;
}
.stroke.green {
stroke: #28a745;
}
.stroke.orange {
stroke: #ffa00a;
}
.stroke.purple {
stroke: #743ee2;
}
.stroke.darkgrey {
stroke: #b8c2cc;
}
.stroke.black {
stroke: #36414C;
}
.stroke.yellow {
stroke: #FEEF72;
}
.stroke.light-blue {
stroke: #7CD6FD;
}
.stroke.lightblue {
stroke: #7CD6FD;
ul, ol {
padding-left: 0;
display: flex;
} }


.fill.grey {
fill: #F0F4F7;
}
.fill.blue {
fill: #5e64ff;
}
.fill.red {
fill: #ff5858;
}
.fill.light-green {
fill: #98d85b;
}
.fill.green {
fill: #28a745;
}
.fill.orange {
fill: #ffa00a;
}
.fill.purple {
fill: #743ee2;
}
.fill.darkgrey {
fill: #b8c2cc;
}
.fill.black {
fill: #36414C;
}
.fill.yellow {
fill: #FEEF72;
}
.fill.light-blue {
fill: #7CD6FD;
}
.fill.lightblue {
fill: #7CD6FD;
ul.data-point-list li {
min-width: 90px;
flex: 1;
} }


.background.grey {
background: #F0F4F7;
}
.background.blue {
background: #5e64ff;
}
.background.red {
background: #ff5858;
}
.background.light-green {
background: #98d85b;
}
.background.green {
background: #28a745;
}
.background.orange {
background: #ffa00a;
}
.background.purple {
background: #743ee2;
}
.background.darkgrey {
background: #b8c2cc;
}
.background.black {
background: #36414C;
}
.background.yellow {
background: #FEEF72;
}
.background.light-blue {
background: #7CD6FD;
}
.background.lightblue {
background: #7CD6FD;
strong {
color: #dfe2e5;
} }


.border-top.grey {
border-top: 3px solid #F0F4F7;
}
.border-top.blue {
border-top: 3px solid #5e64ff;
}
.border-top.red {
border-top: 3px solid #ff5858;
}
.border-top.light-green {
border-top: 3px solid #98d85b;
}
.border-top.green {
border-top: 3px solid #28a745;
}
.border-top.orange {
border-top: 3px solid #ffa00a;
}
.border-top.purple {
border-top: 3px solid #743ee2;
}
.border-top.darkgrey {
border-top: 3px solid #b8c2cc;
}
.border-top.black {
border-top: 3px solid #36414C;
}
.border-top.yellow {
border-top: 3px solid #FEEF72;
}
.border-top.light-blue {
border-top: 3px solid #7CD6FD;
}
.border-top.lightblue {
border-top: 3px solid #7CD6FD;
.svg-pointer {
position: absolute;
bottom: -10px;
left: 50%;
width: 5px;
height: 5px;
margin: 0 0 0 -5px;
content: " ";
border: 5px solid transparent;
border-top-color: rgba(0,0,0,0.8);
} }
}

.stroke.grey {
stroke: #F0F4F7;
}
.stroke.blue {
stroke: #5e64ff;
}
.stroke.red {
stroke: #ff5858;
}
.stroke.light-green {
stroke: #98d85b;
}
.stroke.green {
stroke: #28a745;
}
.stroke.orange {
stroke: #ffa00a;
}
.stroke.purple {
stroke: #743ee2;
}
.stroke.darkgrey {
stroke: #b8c2cc;
}
.stroke.black {
stroke: #36414C;
}
.stroke.yellow {
stroke: #FEEF72;
}
.stroke.light-blue {
stroke: #7CD6FD;
}
.stroke.lightblue {
stroke: #7CD6FD;
}

.fill.grey {
fill: #F0F4F7;
}
.fill.blue {
fill: #5e64ff;
}
.fill.red {
fill: #ff5858;
}
.fill.light-green {
fill: #98d85b;
}
.fill.green {
fill: #28a745;
}
.fill.orange {
fill: #ffa00a;
}
.fill.purple {
fill: #743ee2;
}
.fill.darkgrey {
fill: #b8c2cc;
}
.fill.black {
fill: #36414C;
}
.fill.yellow {
fill: #FEEF72;
}
.fill.light-blue {
fill: #7CD6FD;
}
.fill.lightblue {
fill: #7CD6FD;
}

.background.grey {
background: #F0F4F7;
}
.background.blue {
background: #5e64ff;
}
.background.red {
background: #ff5858;
}
.background.light-green {
background: #98d85b;
}
.background.green {
background: #28a745;
}
.background.orange {
background: #ffa00a;
}
.background.purple {
background: #743ee2;
}
.background.darkgrey {
background: #b8c2cc;
}
.background.black {
background: #36414C;
}
.background.yellow {
background: #FEEF72;
}
.background.light-blue {
background: #7CD6FD;
}
.background.lightblue {
background: #7CD6FD;
}


.border-top.grey {
border-top: 3px solid #F0F4F7;
}
.border-top.blue {
border-top: 3px solid #5e64ff;
}
.border-top.red {
border-top: 3px solid #ff5858;
}
.border-top.light-green {
border-top: 3px solid #98d85b;
}
.border-top.green {
border-top: 3px solid #28a745;
}
.border-top.orange {
border-top: 3px solid #ffa00a;
}
.border-top.purple {
border-top: 3px solid #743ee2;
}
.border-top.darkgrey {
border-top: 3px solid #b8c2cc;
}
.border-top.black {
border-top: 3px solid #36414C;
}
.border-top.yellow {
border-top: 3px solid #FEEF72;
}
.border-top.light-blue {
border-top: 3px solid #7CD6FD;
}
.border-top.lightblue {
border-top: 3px solid #7CD6FD;
} }

+ 1
- 1
frappe/tests/test_twofactor.py Прегледај датотеку

@@ -51,6 +51,7 @@ class TestTwoFactor(unittest.TestCase):
'''Should return true if enabled for user.''' '''Should return true if enabled for user.'''
toggle_2fa_all_role(state=True) toggle_2fa_all_role(state=True)
self.assertTrue(two_factor_is_enabled_for_(self.user)) self.assertTrue(two_factor_is_enabled_for_(self.user))
self.assertFalse(two_factor_is_enabled_for_("Administrator"))
toggle_2fa_all_role(state=False) toggle_2fa_all_role(state=False)
self.assertFalse(two_factor_is_enabled_for_(self.user)) self.assertFalse(two_factor_is_enabled_for_(self.user))


@@ -87,7 +88,6 @@ class TestTwoFactor(unittest.TestCase):
_str = render_string_template(_str,args) _str = render_string_template(_str,args)
self.assertEqual(_str,'Verification Code from Frappe Technologies') self.assertEqual(_str,'Verification Code from Frappe Technologies')



def set_request(**kwargs): def set_request(**kwargs):
builder = EnvironBuilder(**kwargs) builder = EnvironBuilder(**kwargs)
frappe.local.request = Request(builder.get_environ()) frappe.local.request = Request(builder.get_environ())


+ 4
- 2
frappe/tests/ui/data/test_lib.js Прегледај датотеку

@@ -36,7 +36,8 @@ frappe.tests = {
} }
}; };
tasks.push(task); tasks.push(task);
tasks.push(() => frappe.timeout(0.2));
tasks.push(frappe.after_ajax);
tasks.push(() => frappe.timeout(0.4));
} }
}); });


@@ -68,7 +69,8 @@ frappe.tests = {
return frappe.model.set_value(grid_row.doc.doctype, return frappe.model.set_value(grid_row.doc.doctype,
grid_row.doc.name, child_key, child_value[child_key]); grid_row.doc.name, child_key, child_value[child_key]);
}); });
grid_value_tasks.push(() => frappe.timeout(0.2));
grid_value_tasks.push(frappe.after_ajax);
grid_value_tasks.push(() => frappe.timeout(0.4));
} }
}); });




+ 1
- 1
frappe/tests/ui/test_test_runner.py Прегледај датотеку

@@ -22,7 +22,7 @@ class TestTestRunner(unittest.TestCase):
test, comment = test.split('#') test, comment = test.split('#')
test = test.strip() test = test.strip()
if comment.strip()=='long': if comment.strip()=='long':
timeout = 240
timeout = 300


print('Running {0}...'.format(test)) print('Running {0}...'.format(test))




+ 3
- 0
frappe/twofactor.py Прегледај датотеку

@@ -74,6 +74,9 @@ def cache_2fa_data(user, token, otp_secret, tmp_id):


def two_factor_is_enabled_for_(user): def two_factor_is_enabled_for_(user):
'''Check if 2factor is enabled for user.''' '''Check if 2factor is enabled for user.'''
if user == "Administrator":
return False

if isinstance(user, string_types): if isinstance(user, string_types):
user = frappe.get_doc('User', user) user = frappe.get_doc('User', user)




+ 5
- 3
frappe/utils/global_search.py Прегледај датотеку

@@ -307,9 +307,11 @@ def search(text, start=0, limit=20, doctype=""):
limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True)


for r in results: for r in results:
if frappe.get_meta(r.doctype).image_field:
doc = frappe.get_doc(r.doctype, r.name)
r.image = doc.get(doc.meta.image_field)
try:
if frappe.get_meta(r.doctype).image_field:
r.image = frappe.db.get_value(r.doctype, r.name, frappe.get_meta(r.doctype).image_field)
except Exception:
frappe.clear_messages()


return results return results




+ 1
- 1
requirements.txt Прегледај датотеку

@@ -25,7 +25,7 @@ ipython
html2text html2text
email_reply_parser email_reply_parser
click click
num2words==0.5.4
num2words==0.5.5
watchdog==0.8.0 watchdog==0.8.0
bleach bleach
bleach-whitelist bleach-whitelist


Loading…
Откажи
Сачувај