Files
event-manager/event_manager/utils.py
T
xadm 786cbc724f
CI / Server (push) Has been cancelled
Linters / Frappe Linter (push) Has been cancelled
Linters / Vulnerable Dependency Check (push) Has been cancelled
UI Tests / Playwright E2E Tests (push) Has been cancelled
Initialize fork and rebrand app to event_manager
2026-05-11 09:56:57 +02:00

191 lines
5.5 KiB
Python

import functools
from collections.abc import Callable
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def is_app_installed(app_name: str) -> bool:
"""Check if a specified app is installed."""
return app_name in frappe.get_installed_apps()
def only_if_app_installed(app_name: str, raise_exception: bool = False) -> Callable:
"""
Decorator to check if a specified app is installed before running the function.
:param app_name: The name of the app to check for installation.
:param raise_exception: If True, raises an exception if the app is not installed.
If False, the function silently returns None.
:return: The decorated function.
"""
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs):
installed_apps = frappe.get_installed_apps()
if app_name not in installed_apps:
if raise_exception:
frappe.throw(
frappe._("This feature requires the <b>{0}</b> app to be installed.").format(app_name)
)
return None
return func(*args, **kwargs)
return wrapper
return decorator
def add_event_manager_user_role(doc, event=None):
doc.add_roles("Pohodex Event Manager User")
# https://github.com/resilient-tech/india-compliance/blob/f259e9d1408a1cbb85c91146df3b5baa72e5fafb/india_compliance/utils/custom_fields.py
def make_custom_fields(custom_fields, module_name, *args, **kwargs):
for _doctypes, fields in custom_fields.items():
if isinstance(fields, dict):
fields = (fields,)
for field in fields:
field["module"] = module_name
return create_custom_fields(custom_fields, *args, **kwargs)
# https://github.com/resilient-tech/india-compliance/blob/f259e9d1408a1cbb85c91146df3b5baa72e5fafb/india_compliance/utils/custom_fields.py
def get_custom_fields_creator(module_name):
return functools.partial(make_custom_fields, module_name=module_name)
# https://github.com/resilient-tech/india-compliance/blob/f259e9d1408a1cbb85c91146df3b5baa72e5fafb/india_compliance/utils/custom_fields.py#L54C1-L77C48
def delete_custom_fields(custom_fields):
"""
:param custom_fields: a dict like `{'Sales Invoice': [{fieldname: 'test', ...}]}`
"""
for doctypes, fields in custom_fields.items():
if isinstance(fields, dict):
# only one field
fields = [fields]
if isinstance(doctypes, str):
# only one doctype
doctypes = (doctypes,)
for doctype in doctypes:
frappe.db.delete(
"Custom Field",
{
"fieldname": ("in", [field["fieldname"] for field in fields]),
"dt": doctype,
},
)
frappe.clear_cache(doctype=doctype)
def make_qr_image(data: str) -> bytes:
"""
Generate QR code image bytes from data string.
:param data: The data to encode in the QR code
:return: PNG image as bytes
"""
import io
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import HorizontalBarsDrawer
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(image_factory=StyledPilImage, module_drawer=HorizontalBarsDrawer())
output = io.BytesIO()
img.save(output, format="PNG")
return output.getvalue()
def generate_qr_code_file(doc, data: str, field_name: str = "qr_code", file_prefix: str = "qr-code") -> str:
"""
Generate QR code image and attach as File to a document.
:param doc: The Frappe document to attach the QR code to
:param data: The data to encode in the QR code
:param field_name: The field name to attach the file to (default: "qr_code")
:param file_prefix: Prefix for the file name (default: "qr-code")
:return: The file URL of the created QR code image
"""
qr_data = make_qr_image(data)
qr_code_file = frappe.get_doc(
{
"doctype": "File",
"content": qr_data,
"attached_to_doctype": doc.doctype,
"attached_to_name": doc.name,
"attached_to_field": field_name,
"file_name": f"{file_prefix}-{doc.name}.png",
}
).save(ignore_permissions=True)
return qr_code_file.file_url
def build_event_datetimes(event_doc):
from datetime import datetime, timedelta
from frappe.utils import get_time, getdate
start_date = getdate(event_doc.start_date)
start_time = get_time(event_doc.start_time)
start_datetime = datetime.combine(start_date, start_time)
end_date = getdate(event_doc.end_date) if event_doc.end_date else start_date
if event_doc.end_time:
end_time = get_time(event_doc.end_time)
end_datetime = datetime.combine(end_date, end_time)
else:
end_datetime = start_datetime + timedelta(hours=1)
return start_datetime, end_datetime
def generate_ics_file(event_doc, attendee_email: str):
from uuid import uuid4
from frappe.utils import now_datetime
start_dt, end_dt = build_event_datetimes(event_doc)
organizer_name = event_doc.host or event_doc.title
organizer_email = frappe.db.get_value(
"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "email_id"
)
venue_address = ""
if event_doc.venue:
venue_address = frappe.db.get_value("Event Venue", event_doc.venue, "address") or ""
context = {
"uid": uuid4(),
"now": now_datetime().strftime("%Y%m%dT%H%M%S"),
"timezone": event_doc.time_zone,
"start": start_dt.strftime("%Y%m%dT%H%M%S"),
"end": end_dt.strftime("%Y%m%dT%H%M%S"),
"title": event_doc.title,
"location": venue_address,
"attendee_email": attendee_email,
"description": f"Your ticket for {event_doc.title}",
"organizer_name": organizer_name,
"organizer_email": organizer_email,
}
return frappe.render_template("templates/ics/ics.jinja2", context, is_path=True)