Initialize fork and rebrand app to event_manager
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2025, BWH Studios and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Event Attendance Summary"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "event",
|
||||
label: __("Event"),
|
||||
fieldtype: "Link",
|
||||
options: "Pohodex Event Manager Event",
|
||||
reqd: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"add_translate_data": 0,
|
||||
"columns": [],
|
||||
"creation": "2025-12-12 15:56:38.533142",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"letter_head": null,
|
||||
"modified": "2025-12-12 15:56:50.729859",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Events",
|
||||
"name": "Event Attendance Summary",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Event Check In",
|
||||
"report_name": "Event Attendance Summary",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Event Manager"
|
||||
},
|
||||
{
|
||||
"role": "System Manager"
|
||||
}
|
||||
],
|
||||
"timeout": 0
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
# Copyright (c) 2025, BWH Studios and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import formatdate
|
||||
|
||||
|
||||
def execute(filters: dict | None = None):
|
||||
"""Return columns and data for the report.
|
||||
|
||||
This is the main entry point for the report. It accepts the filters as a
|
||||
dictionary and should return columns and data. It is called by the framework
|
||||
every time the report is refreshed or a filter is updated.
|
||||
"""
|
||||
event = filters.get("event")
|
||||
if not event:
|
||||
return [], []
|
||||
|
||||
# Get all unique check-in dates for this event (sorted)
|
||||
check_in_dates = get_check_in_dates(event)
|
||||
|
||||
columns = get_columns(check_in_dates)
|
||||
data = get_data(event, check_in_dates)
|
||||
chart = get_chart(data, check_in_dates)
|
||||
report_summary = get_report_summary(data, check_in_dates)
|
||||
|
||||
return columns, data, None, chart, report_summary
|
||||
|
||||
|
||||
def get_check_in_dates(event: str) -> list:
|
||||
"""Get all unique check-in dates for the event, sorted chronologically."""
|
||||
dates = frappe.db.sql(
|
||||
"""
|
||||
SELECT DISTINCT date
|
||||
FROM `tabEvent Check In`
|
||||
WHERE event = %s AND docstatus = 1 AND date IS NOT NULL
|
||||
ORDER BY date ASC
|
||||
""",
|
||||
(event,),
|
||||
as_dict=True,
|
||||
)
|
||||
return [d.date for d in dates]
|
||||
|
||||
|
||||
def get_columns(check_in_dates: list) -> list[dict]:
|
||||
"""Return columns for the report.
|
||||
|
||||
One field definition per column, just like a DocType field definition.
|
||||
Dynamic columns are added for each check-in date.
|
||||
"""
|
||||
columns = [
|
||||
{
|
||||
"label": _("Ticket"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Event Ticket",
|
||||
"fieldname": "ticket",
|
||||
"width": 120,
|
||||
},
|
||||
{
|
||||
"label": _("Attendee Name"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "attendee_name",
|
||||
"width": 180,
|
||||
},
|
||||
{
|
||||
"label": _("Attendee Email"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "attendee_email",
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"label": _("Ticket Type"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Event Ticket Type",
|
||||
"fieldname": "ticket_type",
|
||||
"width": 150,
|
||||
},
|
||||
]
|
||||
|
||||
# Add dynamic columns for each check-in date
|
||||
for i, date in enumerate(check_in_dates):
|
||||
columns.append(
|
||||
{
|
||||
"label": formatdate(date, "d MMM"), # e.g., "12 Dec"
|
||||
"fieldtype": "Check",
|
||||
"fieldname": f"day_{i}",
|
||||
"width": 80,
|
||||
}
|
||||
)
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def get_data(event: str, check_in_dates: list) -> list[dict]:
|
||||
"""Return data for the report.
|
||||
|
||||
Shows each ticket with check-in status for each day.
|
||||
"""
|
||||
# Get all check-ins for this event
|
||||
check_ins = frappe.get_all(
|
||||
"Event Check In",
|
||||
filters={"event": event, "docstatus": 1},
|
||||
fields=["ticket", "date"],
|
||||
)
|
||||
|
||||
if not check_ins:
|
||||
return []
|
||||
|
||||
# Build a map of ticket -> set of dates checked in
|
||||
ticket_dates = {}
|
||||
for ci in check_ins:
|
||||
if ci.ticket not in ticket_dates:
|
||||
ticket_dates[ci.ticket] = set()
|
||||
if ci.date:
|
||||
ticket_dates[ci.ticket].add(ci.date)
|
||||
|
||||
# Get ticket details
|
||||
tickets = frappe.get_all(
|
||||
"Event Ticket",
|
||||
filters={"name": ["in", list(ticket_dates.keys())]},
|
||||
fields=["name", "attendee_name", "attendee_email", "ticket_type"],
|
||||
)
|
||||
|
||||
# Build the data rows
|
||||
data = []
|
||||
for ticket in tickets:
|
||||
row = {
|
||||
"ticket": ticket.name,
|
||||
"attendee_name": ticket.attendee_name,
|
||||
"attendee_email": ticket.attendee_email,
|
||||
"ticket_type": ticket.ticket_type,
|
||||
}
|
||||
|
||||
# Add check-in status for each date (1 or 0 for Check fieldtype)
|
||||
checked_in_dates = ticket_dates.get(ticket.name, set())
|
||||
for i, date in enumerate(check_in_dates):
|
||||
row[f"day_{i}"] = 1 if date in checked_in_dates else 0
|
||||
|
||||
data.append(row)
|
||||
|
||||
# Sort by attendee name
|
||||
data.sort(key=lambda x: (x.get("attendee_name") or "").lower())
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_chart(data: list[dict], check_in_dates: list) -> dict:
|
||||
"""Return chart data showing attendance per day."""
|
||||
if not data or not check_in_dates:
|
||||
return {}
|
||||
|
||||
labels = []
|
||||
values = []
|
||||
|
||||
for i, date in enumerate(check_in_dates):
|
||||
labels.append(formatdate(date, "d MMM"))
|
||||
day_count = sum(1 for row in data if row.get(f"day_{i}") == 1)
|
||||
values.append(day_count)
|
||||
|
||||
return {
|
||||
"data": {
|
||||
"labels": labels,
|
||||
"datasets": [{"name": _("Attendees"), "values": values}],
|
||||
},
|
||||
"type": "bar",
|
||||
"colors": ["#4F46E5"],
|
||||
}
|
||||
|
||||
|
||||
def get_report_summary(data: list[dict], check_in_dates: list) -> list[dict]:
|
||||
"""Return report summary with attendance counts per day and total unique attendees."""
|
||||
if not data:
|
||||
return []
|
||||
|
||||
summary = []
|
||||
|
||||
# Count attendance for each day
|
||||
for i, date in enumerate(check_in_dates):
|
||||
day_count = sum(1 for row in data if row.get(f"day_{i}") == 1)
|
||||
summary.append(
|
||||
{
|
||||
"value": day_count,
|
||||
"label": formatdate(date, "d MMM YYYY"),
|
||||
"datatype": "Int",
|
||||
"indicator": "blue",
|
||||
}
|
||||
)
|
||||
|
||||
# Total unique attendees (anyone who attended at least one day)
|
||||
total_unique = len(data)
|
||||
summary.append(
|
||||
{
|
||||
"value": total_unique,
|
||||
"label": _("Total Unique Attendees"),
|
||||
"datatype": "Int",
|
||||
"indicator": "green",
|
||||
}
|
||||
)
|
||||
|
||||
return summary
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2025, BWH Studios and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Event Overview"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "event",
|
||||
label: __("Event"),
|
||||
fieldtype: "Link",
|
||||
options: "Pohodex Event Manager Event",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"add_translate_data": 0,
|
||||
"columns": [],
|
||||
"creation": "2025-07-30 20:02:36.464426",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letterhead": null,
|
||||
"modified": "2025-07-30 20:02:40.968996",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Events",
|
||||
"name": "Event Overview",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Pohodex Event Manager Event",
|
||||
"report_name": "Event Overview",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Event Manager"
|
||||
}
|
||||
],
|
||||
"timeout": 0
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright (c) 2025, BWH Studios and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute(filters: dict | None = None):
|
||||
"""Return columns and data for the report.
|
||||
|
||||
This is the main entry point for the report. It accepts the filters as a
|
||||
dictionary and should return columns and data. It is called by the framework
|
||||
every time the report is refreshed or a filter is updated.
|
||||
"""
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns() -> list[dict]:
|
||||
"""Return columns for the report.
|
||||
|
||||
One field definition per column, just like a DocType field definition.
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"label": _("Event"),
|
||||
"fieldname": "event",
|
||||
"fieldtype": "Link",
|
||||
"options": "Pohodex Event Manager Event",
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"label": _("Number of Tickets Sold"),
|
||||
"fieldname": "num_tickets_sold",
|
||||
"fieldtype": "Int",
|
||||
},
|
||||
{"label": _("Number of Add-ons Sold"), "fieldname": "num_add_ons_sold", "fieldtype": "Int"},
|
||||
{"label": _("Ticket Sales"), "fieldname": "sales", "fieldtype": "Currency"},
|
||||
]
|
||||
|
||||
|
||||
def get_data(filters: dict) -> list[dict]:
|
||||
"""Return data for the report.
|
||||
|
||||
The report data is a list of rows, with each row being a list of cell values.
|
||||
"""
|
||||
event = filters.get("event")
|
||||
|
||||
if event:
|
||||
return [get_summary_for_event(event)]
|
||||
|
||||
data = []
|
||||
events = frappe.db.get_all("Pohodex Event Manager Event", pluck="name")
|
||||
for event in events:
|
||||
summary = get_summary_for_event(event)
|
||||
summary["event"] = event
|
||||
data.append(summary)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_summary_for_event(event: str) -> dict:
|
||||
"""Return summary data for the event.
|
||||
|
||||
This function is used to get a summary of the event, such as total tickets sold,
|
||||
total add-ons sold, and total sales.
|
||||
"""
|
||||
event_tickets = frappe.db.get_all("Event Ticket", filters={"event": event, "docstatus": 1}, pluck="name")
|
||||
num_tickets_sold = len(event_tickets)
|
||||
sales = frappe.db.get_all(
|
||||
"Event Booking",
|
||||
filters={"docstatus": 1, "event": event},
|
||||
fields=[{"SUM": "total_amount", "as": "sales"}],
|
||||
pluck="sales",
|
||||
)[0]
|
||||
|
||||
num_add_ons_sold = frappe.db.get_all(
|
||||
"Ticket Add-on Value",
|
||||
filters={"parenttype": "Event Ticket", "parentfield": "add_ons", "parent": ["in", event_tickets]},
|
||||
fields=[{"COUNT": "*", "as": "num_add_ons_sold"}],
|
||||
pluck="num_add_ons_sold",
|
||||
)[0]
|
||||
|
||||
return {
|
||||
"event": event,
|
||||
"num_tickets_sold": num_tickets_sold,
|
||||
"num_add_ons_sold": num_add_ons_sold,
|
||||
"sales": sales,
|
||||
}
|
||||
Reference in New Issue
Block a user