diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index 2f650b18fe..6b488af904 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -20,6 +20,7 @@ from frappe.utils import ( date_diff, format_datetime, get_datetime_str, + get_fullname, getdate, month_diff, now_datetime, @@ -38,6 +39,11 @@ communication_mapping = { "Other": "Other", } +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from frappe.core.doctype.communication.communication import Communication + class Event(Document): # begin: auto-generated types @@ -107,42 +113,49 @@ class Event(Document): def on_trash(self): communications = frappe.get_all( - "Communication", dict(reference_doctype=self.doctype, reference_name=self.name) + "Communication", + filters={"reference_doctype": self.doctype, "reference_name": self.name}, + pluck="name", ) - if communications: - for communication in communications: - frappe.delete_doc_if_exists("Communication", communication.name, force=True) + for communication in communications: + frappe.delete_doc("Communication", communication, force=True) def sync_communication(self): - if self.event_participants: - for participant in self.event_participants: - filters = [ + if not self.event_participants: + return + + for participant in self.event_participants: + if communications := frappe.get_all( + "Communication", + filters=[ ["Communication", "reference_doctype", "=", self.doctype], ["Communication", "reference_name", "=", self.name], ["Communication Link", "link_doctype", "=", participant.reference_doctype], ["Communication Link", "link_name", "=", participant.reference_docname], - ] - if comms := frappe.get_all("Communication", filters=filters, fields=["name"], distinct=True): - for comm in comms: - communication = frappe.get_doc("Communication", comm.name) - self.update_communication(participant, communication) - else: - meta = frappe.get_meta(participant.reference_doctype) - if hasattr(meta, "allow_events_in_timeline") and meta.allow_events_in_timeline == 1: - self.create_communication(participant) + ], + pluck="name", + distinct=True, + ): + for comm in communications: + communication = frappe.get_doc("Communication", comm) + self.update_communication(participant, communication) + else: + meta = frappe.get_meta(participant.reference_doctype) + if hasattr(meta, "allow_events_in_timeline") and meta.allow_events_in_timeline == 1: + self.create_communication(participant) - def create_communication(self, participant): + def create_communication(self, participant: "EventParticipants"): communication = frappe.new_doc("Communication") self.update_communication(participant, communication) self.communication = communication.name - def update_communication(self, participant, communication): + def update_communication(self, participant: "EventParticipants", communication: "Communication"): communication.communication_medium = "Event" communication.subject = self.subject communication.content = self.description if self.description else self.subject communication.communication_date = self.starts_on communication.sender = self.owner - communication.sender_full_name = frappe.utils.get_fullname(self.owner) + communication.sender_full_name = get_fullname(self.owner) communication.reference_doctype = self.doctype communication.reference_name = self.name communication.communication_medium = ( @@ -195,28 +208,24 @@ class Event(Document): @frappe.whitelist() def delete_communication(event, reference_doctype, reference_docname): - deleted_participant = frappe.get_doc(reference_doctype, reference_docname) if isinstance(event, str): event = json.loads(event) - filters = [ - ["Communication", "reference_doctype", "=", event.get("doctype")], - ["Communication", "reference_name", "=", event.get("name")], - ["Communication Link", "link_doctype", "=", deleted_participant.reference_doctype], - ["Communication Link", "link_name", "=", deleted_participant.reference_docname], - ] + deleted_participant = frappe.get_doc(reference_doctype, reference_docname) - comms = frappe.get_list("Communication", filters=filters, fields=["name"]) + comms = frappe.get_list( + "Communication", + filters=[ + ["Communication", "reference_doctype", "=", event.get("doctype")], + ["Communication", "reference_name", "=", event.get("name")], + ["Communication Link", "link_doctype", "=", deleted_participant.reference_doctype], + ["Communication Link", "link_name", "=", deleted_participant.reference_docname], + ], + pluck="name", + ) - if comms: - deletion = [] - for comm in comms: - delete = frappe.get_doc("Communication", comm.name).delete() - deletion.append(delete) - - return deletion - - return {} + for comm in comms: + frappe.delete_doc("Communication", comm) def get_permission_query_conditions(user): diff --git a/frappe/hooks.py b/frappe/hooks.py index 9dafec5a2c..fdbdeda475 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -231,13 +231,13 @@ scheduler_events = { "all": [ "frappe.email.queue.flush", "frappe.monitor.flush", + "frappe.integrations.doctype.google_calendar.google_calendar.sync", ], "hourly": [ "frappe.model.utils.link_count.update_link_count", "frappe.model.utils.user_settings.sync_user_settings", "frappe.desk.page.backups.backups.delete_downloadable_backups", "frappe.desk.form.document_follow.send_hourly_updates", - "frappe.integrations.doctype.google_calendar.google_calendar.sync", "frappe.email.doctype.newsletter.newsletter.send_scheduled_email", "frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request.process_data_deletion_request", ], diff --git a/frappe/integrations/doctype/google_calendar/google_calendar.py b/frappe/integrations/doctype/google_calendar/google_calendar.py index 99a6303ce7..18a56d464a 100644 --- a/frappe/integrations/doctype/google_calendar/google_calendar.py +++ b/frappe/integrations/doctype/google_calendar/google_calendar.py @@ -2,8 +2,10 @@ # License: MIT. See LICENSE -from datetime import datetime, timedelta -from urllib.parse import quote +from contextlib import suppress +from datetime import date, datetime, timedelta +from math import ceil +from typing import TYPE_CHECKING, TypedDict from zoneinfo import ZoneInfo import google.oauth2.credentials @@ -13,7 +15,7 @@ from googleapiclient.discovery import build from googleapiclient.errors import HttpError import frappe -from frappe import _ +from frappe import _, _lt from frappe.integrations.google_oauth import GoogleOAuth from frappe.model.document import Document from frappe.utils import ( @@ -27,6 +29,16 @@ from frappe.utils import ( ) from frappe.utils.password import set_encrypted_password +if TYPE_CHECKING: + from frappe.desk.doctype.event.event import Event + + +class RecurrenceParameters(TypedDict): + frequency: str | None + until: datetime | None + byday: list[str] + + SCOPES = "https://www.googleapis.com/auth/calendar" google_calendar_frequencies = { @@ -64,6 +76,9 @@ framework_days = { } +allow_google_calendar_label = _lt("Allow Google Calendar Access") + + class GoogleCalendar(Document): # begin: auto-generated types # This code is auto-generated. Do not modify anything in this block. @@ -86,7 +101,7 @@ class GoogleCalendar(Document): # end: auto-generated types def validate(self): - google_settings = frappe.get_single("Google Settings") + google_settings = frappe.get_cached_doc("Google Settings") if not google_settings.enable: frappe.throw(_("Enable Google API in Google Settings.")) @@ -99,8 +114,9 @@ class GoogleCalendar(Document): google_settings = self.validate() if not self.refresh_token: - button_label = frappe.bold(_("Allow Google Calendar Access")) - raise frappe.ValidationError(_("Click on {0} to generate Refresh Token.").format(button_label)) + raise frappe.ValidationError( + _("Click on {0} to generate Refresh Token.").format(frappe.bold(allow_google_calendar_label)) + ) data = { "client_id": google_settings.client_id, @@ -113,67 +129,64 @@ class GoogleCalendar(Document): try: r = requests.post(GoogleOAuth.OAUTH_URL, data=data).json() except requests.exceptions.HTTPError: - button_label = frappe.bold(_("Allow Google Calendar Access")) frappe.throw( _( "Something went wrong during the token generation. Click on {0} to generate a new one." - ).format(button_label) + ).format(frappe.bold(allow_google_calendar_label)) ) return r.get("access_token") @frappe.whitelist() -def authorize_access(g_calendar, reauthorize=None): +def authorize_access(g_calendar: str, reauthorize: bool = False): """ If no Authorization code get it from Google and then request for Refresh Token. Google Calendar Name is set to flags to set_value after Authorization Code is obtained. """ - google_settings = frappe.get_doc("Google Settings") google_calendar = frappe.get_doc("Google Calendar", g_calendar) google_calendar.check_permission("write") + google_settings = frappe.get_cached_doc("Google Settings") + redirect_uri = ( - get_request_site_address(True) - + "?cmd=frappe.integrations.doctype.google_calendar.google_calendar.google_callback" + f"{get_request_site_address(full_address=True)}" + f"?cmd={google_callback.__module__}.{google_callback.__qualname__}" ) if not google_calendar.authorization_code or reauthorize: frappe.cache.hset("google_calendar", "google_calendar", google_calendar.name) return get_authentication_url(client_id=google_settings.client_id, redirect_uri=redirect_uri) - else: - try: - data = { - "code": google_calendar.get_password(fieldname="authorization_code", raise_exception=False), - "client_id": google_settings.client_id, - "client_secret": google_settings.get_password( - fieldname="client_secret", raise_exception=False - ), - "redirect_uri": redirect_uri, - "grant_type": "authorization_code", - } - r = requests.post(GoogleOAuth.OAUTH_URL, data=data).json() - if "refresh_token" in r: - frappe.db.set_value( - "Google Calendar", google_calendar.name, "refresh_token", r.get("refresh_token") - ) - frappe.db.commit() + data = { + "code": google_calendar.get_password(fieldname="authorization_code", raise_exception=False), + "client_id": google_settings.client_id, + "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), + "redirect_uri": redirect_uri, + "grant_type": "authorization_code", + } - frappe.local.response["type"] = "redirect" - frappe.local.response["location"] = "/app/Form/{}/{}".format( - quote("Google Calendar"), quote(google_calendar.name) - ) + try: + r = requests.post(GoogleOAuth.OAUTH_URL, data=data).json() + except Exception as e: + frappe.throw(e) - frappe.msgprint(_("Google Calendar has been configured.")) - except Exception as e: - frappe.throw(e) + if "refresh_token" in r: + frappe.db.set_value("Google Calendar", google_calendar.name, "refresh_token", r["refresh_token"]) + frappe.db.commit() + + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = google_calendar.get_url() + + frappe.msgprint(_("Google Calendar has been configured."), indicator="green") def get_authentication_url(client_id=None, redirect_uri=None): return { - "url": "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&prompt=consent&client_id={}&include_granted_scopes=true&scope={}&redirect_uri={}".format( - client_id, SCOPES, redirect_uri + "url": ( + "https://accounts.google.com/o/oauth2/v2/auth?" + f"access_type=offline&response_type=code&prompt=consent&client_id={client_id}" + f"&include_granted_scopes=true&scope={SCOPES}&redirect_uri={redirect_uri}" ) } @@ -191,69 +204,71 @@ def google_callback(code=None): @frappe.whitelist() -def sync(g_calendar=None): - filters = {"enable": 1} +def sync(g_calendar: str | None = None): + filters = {"enable": 1, "pull_from_google_calendar": 1} + user_messages = [] if g_calendar: filters.update({"name": g_calendar}) - google_calendars = frappe.get_list("Google Calendar", filters=filters) + for g in frappe.get_list("Google Calendar", filters=filters, pluck="name"): + user_messages.append(sync_events_from_google_calendar(g)) - for g in google_calendars: - return sync_events_from_google_calendar(g.name) + return user_messages def get_google_calendar_object(g_calendar): """Return an object of Google Calendar along with Google Calendar doc.""" - google_settings = frappe.get_doc("Google Settings") - account = frappe.get_doc("Google Calendar", g_calendar) + google_settings = frappe.get_cached_doc("Google Settings") + account: GoogleCalendar = frappe.get_doc("Google Calendar", g_calendar) - credentials_dict = { - "token": account.get_access_token(), - "refresh_token": account.get_password(fieldname="refresh_token", raise_exception=False), - "token_uri": GoogleOAuth.OAUTH_URL, - "client_id": google_settings.client_id, - "client_secret": google_settings.get_password(fieldname="client_secret", raise_exception=False), - "scopes": [SCOPES], - } - - credentials = google.oauth2.credentials.Credentials(**credentials_dict) + credentials = google.oauth2.credentials.Credentials( + token=account.get_access_token(), + refresh_token=account.get_password(fieldname="refresh_token", raise_exception=False), + token_uri=GoogleOAuth.OAUTH_URL, + client_id=google_settings.client_id, + client_secret=google_settings.get_password(fieldname="client_secret", raise_exception=False), + scopes=[SCOPES], + ) google_calendar = build( serviceName="calendar", version="v3", credentials=credentials, static_discovery=False ) - check_google_calendar(account, google_calendar) + check_google_calendar(account.reload(), google_calendar) - account.load_from_db() - return google_calendar, account + return google_calendar, account.reload() -def check_google_calendar(account, google_calendar): +def check_google_calendar(account: GoogleCalendar, google_calendar): """ Checks if Google Calendar is present with the specified name. If not, creates one. """ - account.load_from_db() - try: - if account.google_calendar_id: - google_calendar.calendars().get(calendarId=account.google_calendar_id).execute() - else: - # If no Calendar ID create a new Calendar - calendar = { - "summary": account.calendar_name, - "timeZone": frappe.get_system_settings("time_zone"), - } - created_calendar = google_calendar.calendars().insert(body=calendar).execute() - frappe.db.set_value( - "Google Calendar", account.name, "google_calendar_id", created_calendar.get("id") + if account.google_calendar_id: + try: + return google_calendar.calendars().get(calendarId=account.google_calendar_id).execute() + except HttpError as err: + frappe.throw( + _("Google Calendar - Could not find Calendar for {0}, error code {1}.").format( + account.name, err.resp.status + ) ) - frappe.db.commit() + + # If no Calendar ID create a new Calendar + calendar = { + "summary": account.calendar_name, + "timeZone": frappe.get_system_settings("time_zone"), + } + try: + created_calendar = google_calendar.calendars().insert(body=calendar).execute() except HttpError as err: frappe.throw( _("Google Calendar - Could not create Calendar for {0}, error code {1}.").format( account.name, err.resp.status ) ) + account.db_set("google_calendar_id", created_calendar.get("id")) + frappe.db.commit() def sync_events_from_google_calendar(g_calendar, method=None): @@ -308,30 +323,33 @@ def sync_events_from_google_calendar(g_calendar, method=None): for idx, event in enumerate(results): frappe.publish_realtime( - "import_google_calendar", dict(progress=idx + 1, total=len(results)), user=frappe.session.user + "import_google_calendar", {"progress": idx + 1, "total": len(results)}, user=frappe.session.user ) # If Google Calendar Event if confirmed, then create an Event if event.get("status") == "confirmed": recurrence = None if event.get("recurrence"): - try: + with suppress(IndexError): recurrence = event.get("recurrence")[0] - except IndexError: - pass if not frappe.db.exists("Event", {"google_calendar_event_id": event.get("id")}): insert_event_to_calendar(account, event, recurrence) else: update_event_in_calendar(account, event, recurrence) + + # If any synced Google Calendar Event is cancelled, then close the Event elif event.get("status") == "cancelled": - # If any synced Google Calendar Event is cancelled, then close the Event - frappe.db.set_value( + event_name = frappe.db.get_value( "Event", { "google_calendar_id": account.google_calendar_id, "google_calendar_event_id": event.get("id"), }, + ) + frappe.db.set_value( + "Event", + event_name, "status", "Closed", ) @@ -340,19 +358,10 @@ def sync_events_from_google_calendar(g_calendar, method=None): "doctype": "Comment", "comment_type": "Info", "reference_doctype": "Event", - "reference_name": frappe.db.get_value( - "Event", - { - "google_calendar_id": account.google_calendar_id, - "google_calendar_event_id": event.get("id"), - }, - "name", - ), + "reference_name": event_name, "content": " - Event deleted from Google Calendar.", } ).insert(ignore_permissions=True) - else: - pass if not results: return _("No Google Calendar Event to sync.") @@ -368,7 +377,7 @@ def insert_event_to_calendar(account, event, recurrence=None): """ calendar_event = { "doctype": "Event", - "subject": event.get("summary"), + "subject": event.get("summary") or "No Title", "description": event.get("description"), "google_calendar_event": 1, "google_calendar": account.name, @@ -376,13 +385,36 @@ def insert_event_to_calendar(account, event, recurrence=None): "google_calendar_event_id": event.get("id"), "google_meet_link": event.get("hangoutLink"), "pulled_from_google_calendar": 1, - "owner": account.owner, "event_type": "Public" if account.sync_as_public else "Private", - } - calendar_event.update( - google_calendar_to_repeat_on(recurrence=recurrence, start=event.get("start"), end=event.get("end")) + } | google_calendar_to_repeat_on(recurrence=recurrence, start=event.get("start"), end=event.get("end")) + + e: Event = frappe.get_doc(calendar_event) + update_participants_in_event(calendar_event=e, google_event=event) + e.insert(ignore_permissions=True) + e.db_set("owner", account.user, update_modified=False) + + +def update_participants_in_event(calendar_event: "Event", google_event: dict): + google_event_participants = [ + attendee["email"] for attendee in google_event.get("attendees", []) if not attendee.get("self") + ] + in_system_participants = frappe.get_all( + "User", filters={"email": ("in", google_event_participants)}, pluck="email" ) - frappe.get_doc(calendar_event).insert(ignore_permissions=True) + + existing_calendar_participants = [ + participant.reference_docname + for participant in calendar_event.event_participants + if participant.reference_doctype == "User" + ] + + for participant in in_system_participants: + if participant not in existing_calendar_participants: + calendar_event.add_participant("User", participant) + + # Add a Guest user to indicate participants not in the system + if len(in_system_participants) < len(google_event_participants): + calendar_event.add_participant("User", "Guest") def update_event_in_calendar(account, event, recurrence=None): @@ -396,6 +428,7 @@ def update_event_in_calendar(account, event, recurrence=None): calendar_event.update( google_calendar_to_repeat_on(recurrence=recurrence, start=event.get("start"), end=event.get("end")) ) + update_participants_in_event(calendar_event, event) calendar_event.save(ignore_permissions=True) @@ -550,13 +583,10 @@ def delete_event_from_google_calendar(doc, method=None): Delete Events from Google Calendar if Frappe Event is deleted. """ - if not frappe.db.exists("Google Calendar", {"name": doc.google_calendar}): + if not frappe.db.exists("Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1}): return - google_calendar, account = get_google_calendar_object(doc.google_calendar) - - if not account.push_to_google_calendar: - return + google_calendar, _ = get_google_calendar_object(doc.google_calendar) try: event = ( @@ -578,28 +608,23 @@ def delete_event_from_google_calendar(doc, method=None): ) -def google_calendar_to_repeat_on(start, end, recurrence=None): +def parse_google_calendar_date(dt): + if dt.get("date"): + return get_datetime(dt.get("date")) + return parser.parse(dt.get("dateTime")).astimezone(ZoneInfo(get_system_timezone())).replace(tzinfo=None) + + +def google_calendar_to_repeat_on(*, start, end, recurrence=None): """ recurrence is in the form ['RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH'] has the frequency and then the days on which the event recurs Both have been mapped in a dict for easier mapping. """ + repeat_on = { - "starts_on": ( - get_datetime(start.get("date")) - if start.get("date") - else parser.parse(start.get("dateTime")) - .astimezone(ZoneInfo(get_system_timezone())) - .replace(tzinfo=None) - ), - "ends_on": ( - get_datetime(end.get("date")) - if end.get("date") - else parser.parse(end.get("dateTime")) - .astimezone(ZoneInfo(get_system_timezone())) - .replace(tzinfo=None) - ), + "starts_on": parse_google_calendar_date(start), + "ends_on": parse_google_calendar_date(end), "all_day": 1 if start.get("date") else 0, "repeat_this_event": 1 if recurrence else 0, "repeat_on": None, @@ -614,44 +639,45 @@ def google_calendar_to_repeat_on(start, end, recurrence=None): } # recurrence rule "RRULE:FREQ=WEEKLY;BYDAY=MO,TU,TH" - if recurrence: - # google_calendar_frequency = RRULE:FREQ=WEEKLY, byday = BYDAY=MO,TU,TH, until = 20191028 - google_calendar_frequency, until, byday = get_recurrence_parameters(recurrence) - repeat_on["repeat_on"] = google_calendar_frequencies.get(google_calendar_frequency) + if not recurrence: + return repeat_on - if repeat_on["repeat_on"] == "Daily": - repeat_on["ends_on"] = None - repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + # google_calendar_frequency = RRULE:FREQ=WEEKLY, byday = BYDAY=MO,TU,TH, until = 20191028 + google_calendar_frequency, until, byday = get_recurrence_parameters(recurrence) + repeat_on["repeat_on"] = google_calendar_frequencies.get(google_calendar_frequency) - if byday and repeat_on["repeat_on"] == "Weekly": - repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None - byday = byday.split("=")[1].split(",") - for repeat_day in byday: - repeat_on[google_calendar_days[repeat_day]] = 1 + if repeat_on["repeat_on"] == "Daily": + repeat_on["ends_on"] = None + repeat_on["repeat_till"] = until - if byday and repeat_on["repeat_on"] == "Monthly": - byday = byday.split("=")[1] - repeat_day_week_number, repeat_day_name = None, None + if byday and repeat_on["repeat_on"] == "Weekly": + repeat_on["repeat_till"] = until + for repeat_day in byday: + repeat_on[google_calendar_days[repeat_day]] = 1 - for num in ["-2", "-1", "1", "2", "3", "4", "5"]: - if num in byday: - repeat_day_week_number = num - break + if byday and repeat_on["repeat_on"] == "Monthly": + byday = byday.split("=")[1] + repeat_day_week_number, repeat_day_name = None, None - for day in ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]: - if day in byday: - repeat_day_name = google_calendar_days.get(day) - break + for num in ["-2", "-1", "1", "2", "3", "4", "5"]: + if num in byday: + repeat_day_week_number = num + break - # Only Set starts_on for the event to repeat monthly - start_date = parse_google_calendar_recurrence_rule(int(repeat_day_week_number), repeat_day_name) - repeat_on["starts_on"] = start_date - repeat_on["ends_on"] = add_to_date(start_date, minutes=5) - repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + for day in ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]: + if day in byday: + repeat_day_name = google_calendar_days.get(day) + break - if repeat_on["repeat_till"] == "Yearly": - repeat_on["ends_on"] = None - repeat_on["repeat_till"] = datetime.strptime(until, "%Y%m%d") if until else None + # Only Set starts_on for the event to repeat monthly + start_date = parse_google_calendar_recurrence_rule(int(repeat_day_week_number), repeat_day_name) + repeat_on["starts_on"] = start_date + repeat_on["ends_on"] = add_to_date(start_date, minutes=5) + repeat_on["repeat_till"] = until + + if repeat_on["repeat_till"] == "Yearly": + repeat_on["ends_on"] = None + repeat_on["repeat_till"] = until return repeat_on @@ -725,13 +751,11 @@ def repeat_on_to_google_calendar_recurrence_rule(doc): return [recurrence] -def get_week_number(dt): +def get_week_number(dt: date): """Return the week number of the month for the specified date. https://stackoverflow.com/questions/3806473/python-week-number-of-the-month/16804556 """ - from math import ceil - first_day = dt.replace(day=1) dom = dt.day @@ -740,19 +764,21 @@ def get_week_number(dt): return int(ceil(adjusted_dom / 7.0)) -def get_recurrence_parameters(recurrence): +def get_recurrence_parameters(recurrence: str) -> RecurrenceParameters: recurrence = recurrence.split(";") - frequency, until, byday = None, None, None + frequency, until, byday = None, None, [] - for r in recurrence: - if "RRULE:FREQ" in r: - frequency = r - elif "UNTIL" in r: - until = r - elif "BYDAY" in r: - byday = r - else: - pass + for token in recurrence: + if "RRULE:FREQ" in token: + frequency = token + + elif "UNTIL" in token: + _until = token.replace("UNTIL=", "").rstrip("Z") + fmt = "%Y%m%dT%H%M%S" if "T" in _until else "%Y%m%d" + until = datetime.strptime(_until, fmt) + + elif "BYDAY" in token: + byday = token.split("=", 1)[1].split(",") return frequency, until, byday diff --git a/frappe/integrations/doctype/google_settings/google_settings.py b/frappe/integrations/doctype/google_settings/google_settings.py index d69ae79572..fb6b9198a1 100644 --- a/frappe/integrations/doctype/google_settings/google_settings.py +++ b/frappe/integrations/doctype/google_settings/google_settings.py @@ -28,7 +28,8 @@ class GoogleSettings(Document): @frappe.whitelist() def get_file_picker_settings(): """Return all the data FileUploader needs to start the Google Drive Picker.""" - google_settings = frappe.get_single("Google Settings") + google_settings = frappe.get_cached_doc("Google Settings") + if not (google_settings.enable and google_settings.google_drive_picker_enabled): return {} diff --git a/frappe/website/doctype/website_settings/website_settings.py b/frappe/website/doctype/website_settings/website_settings.py index 5902c02efb..55df633316 100644 --- a/frappe/website/doctype/website_settings/website_settings.py +++ b/frappe/website/doctype/website_settings/website_settings.py @@ -125,7 +125,7 @@ class WebsiteSettings(Document): ) def validate_google_settings(self): - if self.enable_google_indexing and not frappe.db.get_single_value("Google Settings", "enable"): + if self.enable_google_indexing and not frappe.get_cached_value("Google Settings", None, "enable"): frappe.throw(_("Enable Google API in Google Settings.")) def validate_redirects(self):