api: new formdef popularity count method (#47889)

This commit is contained in:
Lauréline Guérin 2020-12-07 09:50:04 +01:00
parent cb13232405
commit 18d9fea6ff
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
3 changed files with 94 additions and 9 deletions

View File

@ -420,6 +420,43 @@ def test_formdef_list(pub):
assert resp1.json['err'] == 0
assert len(resp1.json['data']) == 0
formdef.data_class().wipe()
# a draft
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.status = 'draft'
formdata.store()
other_formdef = FormDef()
other_formdef.name = 'test 2'
other_formdef.fields = []
other_formdef.store()
other_formdata = other_formdef.data_class()()
other_formdata.data = {}
other_formdata.just_created()
other_formdata.store()
# formdata created:
# - 1 day ago (=3*4)
# - 7 days ago (=2*2)
# - 29 days ago (=1*1)
# - 31 days ago (=0)
for days in [1, 1, 1, 7, 7, 29, 31]:
formdata = formdef.data_class()()
formdata.data = {}
formdata.just_created()
formdata.receipt_time = (datetime.datetime.now() - datetime.timedelta(days=days)).timetuple()
formdata.store()
resp = get_app(pub).get(sign_uri('/api/formdefs/?include-count=on'))
if not pub.is_using_postgresql():
assert resp.json['data'][0]['count'] == 8
else:
# 3*4 + 2*2 + 1*1
assert resp.json['data'][0]['count'] == 17
def test_limited_formdef_list(pub, local_user):
Role.wipe()

View File

@ -14,12 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import datetime
import json
import re
import time
from quixote import get_request, get_publisher, get_response, get_session
from quixote.errors import MethodNotAllowedError, QueryError
from quixote.errors import MethodNotAllowedError
from quixote.directory import Directory
from django.utils.encoding import force_text
@ -574,7 +575,29 @@ class ApiFormdefsDirectory(Directory):
if include_count:
# we include the count of submitted forms so it's possible to sort
# them by "popularity"
formdict['count'] = formdef.data_class().count()
if get_publisher().is_using_postgresql():
from wcs import sql
# 4 * number of submitted forms of last 2 days
# + 2 * number of submitted forms of last 8 days
# + 1 * number of submitted forms of last 30 days
# exclude drafts
criterias = [Equal('formdef_id', formdef.id), NotEqual('status', 'draft')]
d_now = datetime.datetime.now()
count = 4 * sql.get_period_total(
period_start=d_now - datetime.timedelta(days=2), include_start=True,
criterias=criterias)
count += 2 * sql.get_period_total(
period_start=d_now - datetime.timedelta(days=8), include_start=True,
period_end=d_now - datetime.timedelta(days=2), include_end=False,
criterias=criterias)
count += sql.get_period_total(
period_start=d_now - datetime.timedelta(days=30), include_start=True,
period_end=d_now - datetime.timedelta(days=8), include_end=False,
criterias=criterias)
else:
# naive count
count = formdef.data_class().count()
formdict['count'] = count
formdict['functions'] = {}
formdef_workflow_roles = formdef.workflow_roles or {}

View File

@ -2525,7 +2525,7 @@ class AnyFormData(SqlMixin):
formdatas[0].load_all_evolutions(formdatas)
def get_period_query(period_start=None, period_end=None, criterias=None, parameters=None):
def get_period_query(period_start=None, include_start=True, period_end=None, include_end=True, criterias=None, parameters=None):
clause = [NotNull('receipt_time')]
table_name = 'wcs_all_forms'
if criterias:
@ -2539,9 +2539,15 @@ def get_period_query(period_start=None, period_end=None, criterias=None, paramet
continue
clause.append(criteria)
if period_start:
clause.append(GreaterOrEqual('receipt_time', period_start))
if include_start:
clause.append(GreaterOrEqual('receipt_time', period_start))
else:
clause.append(Greater('receipt_time', period_start))
if period_end:
clause.append(LessOrEqual('receipt_time', period_end))
if include_end:
clause.append(LessOrEqual('receipt_time', period_end))
else:
clause.append(Less('receipt_time', period_end))
where_clauses, params, func_clause = parse_clause(clause)
parameters.update(params)
statement = ' FROM %s ' % table_name
@ -2589,7 +2595,7 @@ def get_weekday_totals(period_start=None, period_end=None, criterias=None):
conn, cur = get_connection_and_cursor()
statement = '''SELECT DATE_PART('dow', receipt_time) AS weekday, COUNT(*)'''
parameters = {}
statement += get_period_query(period_start, period_end, criterias, parameters)
statement += get_period_query(period_start=period_start, period_end=period_end, criterias=criterias, parameters=parameters)
statement += ' GROUP BY weekday ORDER BY weekday'''
cur.execute(statement, parameters)
@ -2612,7 +2618,7 @@ def get_hour_totals(period_start=None, period_end=None, criterias=None):
conn, cur = get_connection_and_cursor()
statement = '''SELECT DATE_PART('hour', receipt_time) AS hour, COUNT(*)'''
parameters = {}
statement += get_period_query(period_start, period_end, criterias, parameters)
statement += get_period_query(period_start=period_start, period_end=period_end, criterias=criterias, parameters=parameters)
statement += ' GROUP BY hour ORDER BY hour'
cur.execute(statement, parameters)
@ -2636,7 +2642,7 @@ def get_monthly_totals(period_start=None, period_end=None, criterias=None):
conn, cur = get_connection_and_cursor()
statement = '''SELECT DATE_TRUNC('month', receipt_time) AS month, COUNT(*) '''
parameters = {}
statement += get_period_query(period_start, period_end, criterias, parameters)
statement += get_period_query(period_start=period_start, period_end=period_end, criterias=criterias, parameters=parameters)
statement += ' GROUP BY month ORDER BY month'''
cur.execute(statement, parameters)
@ -2665,7 +2671,7 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
conn, cur = get_connection_and_cursor()
statement = '''SELECT DATE_TRUNC('year', receipt_time) AS year, COUNT(*)'''
parameters = {}
statement += get_period_query(period_start, period_end, criterias, parameters)
statement += get_period_query(period_start=period_start, period_end=period_end, criterias=criterias, parameters=parameters)
statement += ' GROUP BY year ORDER BY year'
cur.execute(statement, parameters)
@ -2688,6 +2694,25 @@ def get_yearly_totals(period_start=None, period_end=None, criterias=None):
return result
@guard_postgres
def get_period_total(period_start=None, include_start=True, period_end=None, include_end=True, criterias=None):
conn, cur = get_connection_and_cursor()
statement = '''SELECT COUNT(*)'''
parameters = {}
statement += get_period_query(
period_start=period_start, include_start=include_start,
period_end=period_end, include_end=include_end,
criterias=criterias, parameters=parameters)
cur.execute(statement, parameters)
result = int(cur.fetchone()[0])
conn.commit()
cur.close()
return result
# latest migration, number + description (description is not used
# programmaticaly but will make sure git conflicts if two migrations are
# separately added with the same number)