The license.txt file has been replaced with LICENSE for quite a while now. INAL but it didn't seem accurate to say "hey, checkout license.txt although there's no such file". Apart from this, there were inconsistencies in the headers altogether...this change brings consistency.
214 lines
5.1 KiB
Python
214 lines
5.1 KiB
Python
# Copyright (c) 2013, Frappe Technologies and contributors
|
|
# License: MIT. See LICENSE
|
|
|
|
from datetime import datetime
|
|
|
|
import frappe
|
|
from frappe.query_builder.functions import Coalesce, Count
|
|
from frappe.utils import getdate
|
|
from frappe.utils.dateutils import get_dates_from_timegrain
|
|
|
|
|
|
def execute(filters=None):
|
|
return WebsiteAnalytics(filters).run()
|
|
|
|
class WebsiteAnalytics(object):
|
|
def __init__(self, filters=None):
|
|
self.filters = frappe._dict(filters or {})
|
|
|
|
if not self.filters.to_date:
|
|
self.filters.to_date = datetime.now()
|
|
|
|
if not self.filters.from_date:
|
|
self.filters.from_date = frappe.utils.add_days(self.filters.to_date, -7)
|
|
|
|
if not self.filters.range:
|
|
self.filters.range = "Daily"
|
|
|
|
self.filters.to_date = frappe.utils.add_days(self.filters.to_date, 1)
|
|
self.query_filters = {'creation': ['between', [self.filters.from_date, self.filters.to_date]]}
|
|
|
|
def run(self):
|
|
columns = self.get_columns()
|
|
data = self.get_data()
|
|
chart = self.get_chart_data()
|
|
summary = self.get_report_summary()
|
|
|
|
return columns, data[:250], None, chart, summary
|
|
|
|
def get_columns(self):
|
|
return [
|
|
{
|
|
"fieldname": "path",
|
|
"label": "Page",
|
|
"fieldtype": "Data",
|
|
"width": 300
|
|
},
|
|
{
|
|
"fieldname": "count",
|
|
"label": "Page Views",
|
|
"fieldtype": "Int",
|
|
"width": 150
|
|
},
|
|
{
|
|
"fieldname": "unique_count",
|
|
"label": "Unique Visitors",
|
|
"fieldtype": "Int",
|
|
"width": 150
|
|
}
|
|
]
|
|
|
|
def get_data(self):
|
|
WebPageView = frappe.qb.DocType("Web Page View")
|
|
count_all = Count("*").as_("count")
|
|
case = frappe.qb.terms.Case().when(WebPageView.is_unique == "1", "1")
|
|
count_is_unique = Count(case).as_("unique_count")
|
|
|
|
return (
|
|
frappe.qb.from_(WebPageView)
|
|
.select("path", count_all, count_is_unique)
|
|
.where(
|
|
Coalesce(WebPageView.creation, "0001-01-01")[self.filters.from_date:self.filters.to_date]
|
|
)
|
|
.groupby(WebPageView.path)
|
|
.orderby("count", Order=frappe.qb.desc)
|
|
).run()
|
|
|
|
def _get_query_for_mariadb(self):
|
|
filters_range = self.filters.range
|
|
field = 'creation'
|
|
date_format = '%Y-%m-%d'
|
|
|
|
if filters_range == "Weekly":
|
|
field = 'ADDDATE(creation, INTERVAL 1-DAYOFWEEK(creation) DAY)'
|
|
|
|
elif filters_range == "Monthly":
|
|
date_format = '%Y-%m-01'
|
|
|
|
query = """
|
|
SELECT
|
|
DATE_FORMAT({0}, %s) as date,
|
|
COUNT(*) as count,
|
|
COUNT(CASE WHEN is_unique = 1 THEN 1 END) as unique_count
|
|
FROM `tabWeb Page View`
|
|
WHERE creation BETWEEN %s AND %s
|
|
GROUP BY DATE_FORMAT({0}, %s)
|
|
ORDER BY creation
|
|
""".format(field)
|
|
|
|
values = (date_format, self.filters.from_date, self.filters.to_date, date_format)
|
|
|
|
return query, values
|
|
|
|
def _get_query_for_postgres(self):
|
|
filters_range = self.filters.range
|
|
field = 'creation'
|
|
granularity = 'day'
|
|
|
|
if filters_range == "Weekly":
|
|
granularity = 'week'
|
|
|
|
elif filters_range == "Monthly":
|
|
granularity = 'day'
|
|
|
|
query = """
|
|
SELECT
|
|
DATE_TRUNC(%s, {0}) as date,
|
|
COUNT(*) as count,
|
|
COUNT(CASE WHEN CAST(is_unique as Integer) = 1 THEN 1 END) as unique_count
|
|
FROM "tabWeb Page View"
|
|
WHERE coalesce("tabWeb Page View".{0}, '0001-01-01') BETWEEN %s AND %s
|
|
GROUP BY date_trunc(%s, {0})
|
|
ORDER BY date
|
|
""".format(field)
|
|
|
|
values = (granularity, self.filters.from_date, self.filters.to_date, granularity)
|
|
|
|
return query, values
|
|
|
|
def get_chart_data(self):
|
|
current_dialect = frappe.db.db_type or 'mariadb'
|
|
|
|
if current_dialect == 'mariadb':
|
|
query, values = self._get_query_for_mariadb()
|
|
else:
|
|
query, values = self._get_query_for_postgres()
|
|
|
|
self.chart_data = frappe.db.sql(query, values=values, as_dict=1)
|
|
|
|
return self.prepare_chart_data(self.chart_data)
|
|
|
|
def prepare_chart_data(self, data):
|
|
date_range = get_dates_from_timegrain(self.filters.from_date, self.filters.to_date, self.filters.range)
|
|
if self.filters.range == "Monthly":
|
|
date_range = [frappe.utils.add_days(dd, 1) for dd in date_range]
|
|
|
|
labels = []
|
|
total_dataset = []
|
|
unique_dataset = []
|
|
|
|
def get_data_for_date(date):
|
|
for item in data:
|
|
item_date = getdate(item.get("date"))
|
|
if item_date == date:
|
|
return item
|
|
return {'count': 0, 'unique_count': 0}
|
|
|
|
|
|
for date in date_range:
|
|
labels.append(date.strftime("%b %d %Y"))
|
|
match = get_data_for_date(date)
|
|
total_dataset.append(match.get('count', 0))
|
|
unique_dataset.append(match.get('unique_count', 0))
|
|
|
|
chart = {
|
|
"data": {
|
|
'labels': labels,
|
|
'datasets': [
|
|
{
|
|
'name': "Total Views",
|
|
'type': 'line',
|
|
'values': total_dataset
|
|
},
|
|
{
|
|
'name': "Unique Visits",
|
|
'type': 'line',
|
|
'values': unique_dataset
|
|
}
|
|
]
|
|
},
|
|
"type": "axis-mixed",
|
|
'lineOptions': {
|
|
'regionFill': 1,
|
|
},
|
|
'axisOptions': {
|
|
'xIsSeries': 1
|
|
},
|
|
'colors': ['#7cd6fd', '#5e64ff']
|
|
}
|
|
|
|
return chart
|
|
|
|
|
|
def get_report_summary(self):
|
|
total_count = 0
|
|
unique_count = 0
|
|
for data in self.chart_data:
|
|
unique_count += data.get('unique_count')
|
|
total_count += data.get('count')
|
|
|
|
report_summary = [
|
|
{
|
|
"value": total_count,
|
|
"label": "Total Page Views",
|
|
"datatype": "Int",
|
|
},
|
|
{
|
|
"value": unique_count,
|
|
"label": "Unique Page Views",
|
|
"datatype": "Int",
|
|
},
|
|
|
|
]
|
|
return report_summary
|
|
|