Merge branch 'develop' into enable-further-translations

This commit is contained in:
Shariq Ansari 2022-06-28 17:43:06 +05:30 committed by GitHub
commit 6a3e43789b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 111 additions and 37 deletions

View file

@ -77,13 +77,13 @@ if __name__ == "__main__":
updated_py_file_count = len(list(filter(is_py, files_list)))
only_py_changed = updated_py_file_count == len(files_list)
if ci_files_changed:
print("CI related files were updated, running all build processes.")
elif has_skip_ci_label(pr_number, repo):
if has_skip_ci_label(pr_number, repo):
print("Found `Skip CI` label on pr, stopping build process.")
sys.exit(0)
elif ci_files_changed:
print("CI related files were updated, running all build processes.")
elif only_docs_changed:
print("Only docs were updated, stopping build process.")
sys.exit(0)

View file

@ -11,10 +11,10 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.8
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: '3.10'
- name: Install and Run Pre-commit
uses: pre-commit/action@v3.0.0
@ -22,10 +22,8 @@ jobs:
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
- uses: returntocorp/semgrep-action@v1
env:
SEMGREP_TIMEOUT: 120
with:
config: >-
r/python.lang.correctness
./frappe-semgrep-rules/rules
- name: Download semgrep
run: pip install semgrep==0.97.0
- name: Run Semgrep rules
run: semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness

View file

@ -171,7 +171,7 @@ class DocType(Document):
if docfield.fieldname in method_set:
conflict_type = "controller method"
if docfield.fieldname in property_set:
if docfield.fieldname in property_set and not docfield.is_virtual:
conflict_type = "class property"
if conflict_type:

View file

@ -16,7 +16,7 @@ from requests.exceptions import HTTPError, SSLError
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import call_hook_method, cint, get_files_path, get_hook_method
from frappe.utils import call_hook_method, cint, get_files_path, get_hook_method, get_url
from frappe.utils.file_manager import is_safe_path
from frappe.utils.image import optimize_image, strip_exif_data
@ -61,7 +61,12 @@ class File(Document):
self.set_file_name()
self.validate_attachment_limit()
if not self.is_folder and not self.is_remote_file:
if self.is_folder:
return
if self.is_remote_file:
self.validate_remote_file()
else:
self.save_file(content=self.get_content())
self.flags.new_file = True
frappe.local.rollback_observers.append(self)
@ -255,6 +260,12 @@ class File(Document):
title=_("Attachment Limit Reached"),
)
def validate_remote_file(self):
"""Validates if file uploaded using URL already exist"""
site_url = get_url()
if "/files/" in self.file_url and self.file_url.startswith(site_url):
self.file_url = self.file_url.split(site_url, 1)[1]
def set_folder_name(self):
"""Make parent folders if not exists based on reference doctype and name"""
if self.folder:
@ -445,6 +456,10 @@ class File(Document):
file_path = self.file_url or self.file_name
site_url = get_url()
if "/files/" in file_path and file_path.startswith(site_url):
file_path = file_path.split(site_url, 1)[1]
if "/" not in file_path:
if self.is_private:
file_path = f"/private/files/{file_path}"

View file

@ -16,8 +16,11 @@
"server_script",
"frequency",
"cron_format",
"create_log",
"status_section",
"last_execution",
"create_log"
"column_break_9",
"next_execution"
],
"fields": [
{
@ -72,6 +75,22 @@
"options": "Server Script",
"read_only": 1,
"search_index": 1
},
{
"fieldname": "next_execution",
"fieldtype": "Datetime",
"is_virtual": 1,
"label": "Next Execution",
"read_only": 1
},
{
"fieldname": "status_section",
"fieldtype": "Section Break",
"label": "Status"
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
}
],
"in_create": 1,
@ -81,7 +100,7 @@
"link_fieldname": "scheduled_job_type"
}
],
"modified": "2020-10-07 10:39:24.519460",
"modified": "2022-06-28 02:55:12.470915",
"modified_by": "Administrator",
"module": "Core",
"name": "Scheduled Job Type",
@ -103,5 +122,7 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "method",
"track_changes": 1
}

View file

@ -50,6 +50,10 @@ class ScheduledJobType(Document):
queued_jobs = get_jobs(site=frappe.local.site, key="job_type")[frappe.local.site]
return self.method in queued_jobs
@property
def next_execution(self):
return self.get_next_execution()
def get_next_execution(self):
CRON_MAP = {
"Yearly": "0 0 1 1 *",

View file

@ -63,6 +63,7 @@
"otp_issuer_name",
"email",
"email_footer_address",
"email_retry_limit",
"column_break_18",
"disable_standard_email_footer",
"hide_footer_in_auto_email_reports",
@ -495,8 +496,8 @@
"fieldname": "allow_older_web_view_links",
"fieldtype": "Check",
"label": "Allow Older Web View Links (Insecure)"
},
{
},
{
"fieldname": "column_break_64",
"fieldtype": "Column Break"
},
@ -518,12 +519,18 @@
"fieldtype": "Duration",
"label": "Reset Password Link Expiry Duration",
"non_negative": 1
},
{
"default": "3",
"fieldname": "email_retry_limit",
"fieldtype": "Int",
"label": "Email Retry Limit"
}
],
"icon": "fa fa-cog",
"issingle": 1,
"links": [],
"modified": "2022-05-19 00:00:18.095269",
"modified": "2022-06-21 13:55:04.796152",
"modified_by": "Administrator",
"module": "Core",
"name": "System Settings",

View file

@ -156,8 +156,6 @@ def setup_group_by(data):
**data
)
)
if data.aggregate_on_field:
data.fields.append(f"`tab{data.aggregate_on_doctype}`.`{data.aggregate_on_field}`")
else:
raise_invalid_field(data.aggregate_on_field)
@ -435,11 +433,20 @@ def append_totals_row(data):
def get_labels(fields, doctype):
"""get column labels based on column names"""
labels = []
doctype = doctype.lower()
for key in fields:
key = key.split(" as ")[0]
aggregate_function = ""
key = key.casefold().split(" as ", maxsplit=1)[0]
if key.startswith(("count(", "sum(", "avg(")):
continue
# Get aggregate function and _aggregate_column
# key = 'sum(`tabDocType`.`fieldname`)'
if not key.rstrip().endswith(")"):
continue
_agg_fn, _key = key.split("(", maxsplit=1)
aggregate_function = _agg_fn.lower() # aggregate_function = 'sum'
key = _key[:-1] # key = `tabDocType`.`fieldname`
if "." in key:
parenttype, fieldname = key.split(".")[0][4:-1], key.split(".")[1].strip("`")
@ -455,7 +462,10 @@ def get_labels(fields, doctype):
if parenttype != doctype:
# If the column is from a child table, append the child doctype.
# For example, "Item Code (Sales Invoice Item)".
label += f" ({ _(parenttype) })"
label += f" ({ _(parenttype.title()) })"
if aggregate_function:
label = _("{0} of {1}").format(aggregate_function.capitalize(), label)
labels.append(label)
@ -464,7 +474,7 @@ def get_labels(fields, doctype):
def handle_duration_fieldtype_values(doctype, data, fields):
for field in fields:
key = field.split(" as ")[0]
key = field.casefold().split(" as ", maxsplit=1)[0]
if key.startswith(("count(", "sum(", "avg(")):
continue

View file

@ -30,8 +30,6 @@ from frappe.utils import (
split_emails,
)
MAX_RETRY_COUNT = 3
class EmailQueue(Document):
DOCTYPE = "Email Queue"
@ -210,7 +208,7 @@ class SendMailContext:
email_status = (self.sent_to and "Partially Sent") or "Not Sent"
self.queue_doc.update_status(status=email_status, commit=True)
elif exc_type:
if self.queue_doc.retry < MAX_RETRY_COUNT:
if self.queue_doc.retry < get_email_retry_limit():
update_fields = {"status": "Not Sent", "retry": self.queue_doc.retry + 1}
else:
update_fields = {"status": (self.sent_to and "Partially Errored") or "Error"}
@ -372,6 +370,10 @@ def on_doctype_update():
)
def get_email_retry_limit():
return cint(frappe.db.get_system_setting("email_retry_limit")) or 3
class QueueBuilder:
"""Builds Email Queue from the given data"""

View file

@ -417,6 +417,8 @@ class DatabaseQuery(object):
"extract(",
"locate(",
"strpos(",
]
aggregate_functions = [
"count(",
"sum(",
"avg(",
@ -427,6 +429,9 @@ class DatabaseQuery(object):
if not ("tab" in field and "." in field) or any(x for x in sql_functions if x in field):
continue
if any(x for x in aggregate_functions if x in field):
field = field.split("(", 1)[1][:-1]
table_name = field.split(".")[0]
if table_name.lower().startswith("group_concat("):

View file

@ -613,8 +613,8 @@
<symbol viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" id="icon-expenses">
<path d="M17 16a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V2.37a.2.2 0 0 1 .31-.168l1.75 1.143a.5.5 0 0 0 .547 0L7.393 2.18a.5.5 0 0 1 .547 0l1.787 1.166a.5.5 0 0 0 .546 0L12.06 2.18a.5.5 0 0 1 .547 0l1.786 1.166a.5.5 0 0 0 .547 0l1.75-1.143a.2.2 0 0 1 .31.167V16z"
stroke="var(--icon-stroke)" stroke-miterlimit="10" stroke-linecap="square"></path>
<path d="M12.925 9.097l-.298 1.052h-.904c-.174 1.15-.997 1.986-2.748 2.093l2.787 3.413v.072h-1.556l-3.05-3.703-.01-.83H8.66c.938 0 1.53-.372 1.705-1.045H7.04l.298-1.052h2.992c-.196-.601-.737-.963-1.67-.963H7.04L7.351 7h5.578l-.302 1.061-1.343-.008c.222.298.367.652.43 1.044h1.211z"
fill="#192734" stroke="none"></path>
<path d="m13.13,8.1l-0.3,1.05l-0.91,0c-0.17,1.15 -0.99,1.98 -2.74,2.09l2.78,3.41l0,0.08l-1.55,0l-3.05,-3.71l-0.01,-0.83l1.51,0c0.94,0 1.53,-0.37 1.71,-1.04l-3.33,0l0.3,-1.05l2.99,0c-0.2,-0.6 -0.74,-0.97 -1.67,-0.97l-1.62,0l0.31,-1.13l5.58,0l-0.3,1.06l-1.35,-0.01c0.23,0.3 0.37,0.66 0.43,1.05l1.22,0z"
fill="var(--icon-stroke)" stroke="none"></path>
</symbol>
<symbol viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" id="icon-income">

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View file

@ -643,9 +643,7 @@ class TestReportview(unittest.TestCase):
)
response = execute_cmd("frappe.desk.reportview.get")
self.assertListEqual(
response["keys"], ["field_label", "field_name", "_aggregate_column", "columns"]
)
self.assertListEqual(response["keys"], ["field_label", "field_name", "_aggregate_column"])
def test_cast_name(self):
from frappe.core.doctype.doctype.test_doctype import new_doctype

View file

@ -1993,7 +1993,7 @@ QR Code,QR-Code,
QR Code for Login Verification,QR Code für Login-Bestätigung,
QZ Tray Connection Active!,QZ-Tray-Verbindung aktiv!,
QZ Tray Failed: ,QZ-Fach fehlgeschlagen:,
Quarter Day,Quartalstag,
Quarter Day,Viertel-Tag,
Query,Abfrage,
Query Report,Abfragebericht,
Query must be a SELECT,Abfrage muss ein SELECT sein,
@ -4793,3 +4793,17 @@ Reset to default,Auf Standard zurücksetzen,
Column Width,Spaltenbreite,
Choose Kanban Board,Kanban-Tafel auswählen,
Create New Board,Neue Tafel erstellen,
Only If Creator,Nur wenn Ersteller,
Rebuild Tree,Baum neu aufbauen,
Customize Dashboard,Dashboard anpassen,
Reset Dashboard Customizations,Dashboard-Anpassungen zurücksetzen,
Add {0},{0} hinzufügen,
descending,absteigend,
ascending,aufsteigend,
Next Document,Nächstes Dokument,
Previous Document,Vorheriges Dokument,
Mark all as read,Alle als gelesen markieren,
See all Activity,Alle Aktivitäten anzeigen,
Go to Notification Settings List,Gehe zur Listenansicht Benachrichtigungseinstellungen,
Load more,Mehr laden,
Edit Full Form,Vollständiges Formular bearbeiten,

1 A4 A4
1993 QR Code for Login Verification QR Code für Login-Bestätigung
1994 QZ Tray Connection Active! QZ-Tray-Verbindung aktiv!
1995 QZ Tray Failed: QZ-Fach fehlgeschlagen:
1996 Quarter Day Quartalstag Viertel-Tag
1997 Query Abfrage
1998 Query Report Abfragebericht
1999 Query must be a SELECT Abfrage muss ein SELECT sein
4793 Column Width Spaltenbreite
4794 Choose Kanban Board Kanban-Tafel auswählen
4795 Create New Board Neue Tafel erstellen
4796 Only If Creator Nur wenn Ersteller
4797 Rebuild Tree Baum neu aufbauen
4798 Customize Dashboard Dashboard anpassen
4799 Reset Dashboard Customizations Dashboard-Anpassungen zurücksetzen
4800 Add {0} {0} hinzufügen
4801 descending absteigend
4802 ascending aufsteigend
4803 Next Document Nächstes Dokument
4804 Previous Document Vorheriges Dokument
4805 Mark all as read Alle als gelesen markieren
4806 See all Activity Alle Aktivitäten anzeigen
4807 Go to Notification Settings List Gehe zur Listenansicht Benachrichtigungseinstellungen
4808 Load more Mehr laden
4809 Edit Full Form Vollständiges Formular bearbeiten

View file

@ -199,7 +199,7 @@ class RedisWrapper(redis.Redis):
frappe.local.cache[_name][key] = value
elif generator:
value = generator()
self.hset(name, key, value)
self.hset(name, key, value, shared=shared)
return value
def hdel(self, name, key, shared=False):