Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ureport/backend/tests/test_rapidpro.py
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,7 @@ def release_boundary(boundary):
]
)

with self.assertNumQueries(7):
with self.assertNumQueries(9):
boundaries_results = self.backend.pull_boundaries(self.nigeria)

self.assertEqual(
Expand Down Expand Up @@ -1276,7 +1276,7 @@ def release_boundary(boundary):
]
)

with self.assertNumQueries(13):
with self.assertNumQueries(17):
boundaries_results = self.backend.pull_boundaries(self.nigeria)

self.assertEqual(
Expand Down
61 changes: 61 additions & 0 deletions ureport/sql/stats_0028.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
-----------------------------------------------------------------------------
-- Insert missing PollContactResult's ContactEngagementActivity rows
-----------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION
ureport_insert_missing_contact_engagement_activities(_poll_contact_result stats_pollcontactresult)
RETURNS VOID AS $$
BEGIN
INSERT INTO stats_contactengagementactivity(contact, date, org_id) WITH month_days(missing_month) AS (
SELECT generate_series(date_trunc('month', _poll_contact_result.date)::timestamp,(date_trunc('month', _poll_contact_result.date)::timestamp+ interval '11 months')::date,interval '1 month')::date
), curr_activity AS (
SELECT * FROM stats_contactengagementactivity WHERE org_id = _poll_contact_result.org_id and contact = _poll_contact_result.contact
) SELECT _poll_contact_result.contact, missing_month::date, _poll_contact_result.org_id FROM month_days LEFT JOIN stats_contactengagementactivity ON stats_contactengagementactivity.date = month_days.missing_month AND stats_contactengagementactivity.contact = _poll_contact_result.contact AND org_id = _poll_contact_result.org_id
WHERE stats_contactengagementactivity.date IS NULL;
UPDATE stats_contactengagementactivity SET age_segment_id = _poll_contact_result.age_segment_id, gender_segment_id = _poll_contact_result.gender_segment_id, location_id = _poll_contact_result.location_id, scheme_segment_id = _poll_contact_result.scheme_segment_id, used = TRUE WHERE org_id = _poll_contact_result.org_id and contact = _poll_contact_result.contact and date > date_trunc('month', CURRENT_DATE) - INTERVAL '1 year';
END;
$$ LANGUAGE plpgsql;

-----------------------------------------------------------------------------
-- Generate ContactEngagementActivity rows for latest PollContactResult
-----------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION generate_contact_engagement_activities_for_latest_poll_contact_result(_poll_contact_result stats_pollcontactresult)
RETURNS VOID AS $$
BEGIN
-- Count only if we have an org and a flow and a flow_result
IF _poll_contact_result.org_id IS NOT NULL AND _poll_contact_result.flow IS NOT NULL AND _poll_contact_result.flow_result_id IS NOT NULL AND _poll_contact_result.flow_result_category_id IS NOT NULL THEN
PERFORM ureport_insert_missing_contact_engagement_activities(_poll_contact_result);
END IF;
END;
$$ LANGUAGE plpgsql;

-----------------------------------------------------------------------------
-- Updates our results counters
-----------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION ureport_update_contact_engagement_activities() RETURNS TRIGGER AS $$
BEGIN
-- PollContactResult row being created, increment counters for PollContactResult NEW
IF TG_OP = 'INSERT' THEN
PERFORM generate_contact_engagement_activities_for_latest_poll_contact_result(NEW);
ELSIF TG_OP = 'UPDATE' THEN
PERFORM generate_contact_engagement_activities_for_latest_poll_contact_result(NEW);
-- PollContactResult row is being deleted
ELSIF TG_OP = 'TRUNCATE' THEN
-- Clear all ContactEngagementActivity rows
TRUNCATE stats_contactengagementactivity;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;


-- Install trigger for INSERT, UPDATE, AND DELETE on stats_pollcontactresult
DROP TRIGGER IF EXISTS ureport_when_poll_contact_result_contact_engagement_activities on stats_pollcontactresult;
CREATE TRIGGER ureport_when_poll_contact_result_contact_engagement_activities
AFTER INSERT OR DELETE OR UPDATE ON stats_pollcontactresult
FOR EACH ROW EXECUTE PROCEDURE ureport_update_contact_engagement_activities();

-- Install trigger for TRUNCATE on stats_pollcontactresult
DROP TRIGGER IF EXISTS ureport_when_poll_contact_results_truncate_then_update_contact_engagement_activities ON stats_pollcontactresult;
CREATE TRIGGER ureport_when_poll_contact_results_truncate_then_update_contact_engagement_activities
AFTER TRUNCATE ON stats_pollcontactresult
EXECUTE PROCEDURE ureport_update_contact_engagement_activities();
173 changes: 173 additions & 0 deletions ureport/stats/migrations/0027_auto_20211202_1422.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# Generated by Django 3.2.8 on 2021-12-02 14:22

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("orgs", "0029_auto_20211025_1504"),
("flows", "0001_initial"),
("locations", "0006_boundary_backend"),
("stats", "0026_populate_flow_result_word_clouds"),
]

operations = [
migrations.CreateModel(
name="PollContactResult",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("contact", models.CharField(max_length=36)),
("flow", models.CharField(max_length=36)),
("text", models.TextField(null=True)),
("date", models.DateTimeField(null=True)),
(
"age_segment",
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.agesegment"),
),
(
"flow_result",
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="flows.flowresult"),
),
(
"flow_result_category",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="flows.flowresultcategory"
),
),
(
"gender_segment",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.gendersegment"
),
),
(
"location",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="locations.boundary"
),
),
(
"org",
models.ForeignKey(
db_index=False,
on_delete=django.db.models.deletion.PROTECT,
related_name="poll_contact_results",
to="orgs.org",
),
),
(
"scheme_segment",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.schemesegment"
),
),
],
),
migrations.CreateModel(
name="ContactEngagementActivity",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("contact", models.CharField(max_length=36)),
("date", models.DateField(help_text="The starting date for for the month")),
("used", models.BooleanField(null=True)),
(
"age_segment",
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.agesegment"),
),
(
"gender_segment",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.gendersegment"
),
),
(
"location",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="locations.boundary"
),
),
(
"org",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="contact_engagement_activities",
to="orgs.org",
),
),
(
"scheme_segment",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="stats.schemesegment"
),
),
],
),
migrations.AddIndex(
model_name="pollcontactresult",
index=models.Index(fields=["contact"], name="stats_pcr_contact"),
),
migrations.AddIndex(
model_name="pollcontactresult",
index=models.Index(fields=["org", "flow", "contact"], name="stats_pcr_org_flow_contact"),
),
migrations.AddIndex(
model_name="pollcontactresult",
index=models.Index(fields=["org", "flow"], name="stats_pcr_org_flow"),
),
migrations.AddIndex(
model_name="pollcontactresult",
index=models.Index(fields=["org", "flow", "flow_result", "text"], name="stats_pcr_org_flow_result_text"),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(fields=["org", "contact"], name="stats_cea_org_contact"),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(fields=["org", "date"], name="stats_cea_org_date"),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(
condition=models.Q(("used", True)), fields=["org", "date", "used"], name="stats_cea_org_date_used"
),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(
condition=models.Q(("location__isnull", False), ("used", True)),
fields=["org", "date", "location", "used"],
name="stats_cea_org_date_state_used",
),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(
condition=models.Q(("age_segment__isnull", False), ("used", True)),
fields=["org", "date", "age_segment", "used"],
name="stats_cea_org_date_age_used",
),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(
condition=models.Q(("scheme_segment__isnull", False), ("used", True)),
fields=["org", "date", "scheme_segment", "used"],
name="stats_cea_org_date_scheme_used",
),
),
migrations.AddIndex(
model_name="contactengagementactivity",
index=models.Index(
condition=models.Q(("gender_segment__isnull", False), ("used", True)),
fields=["org", "date", "gender_segment", "used"],
name="stats_cea_org_date_gender_used",
),
),
migrations.AlterUniqueTogether(
name="contactengagementactivity",
unique_together={("org", "contact", "date")},
),
]
14 changes: 14 additions & 0 deletions ureport/stats/migrations/0028_install_cea_triggers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 3.2.8 on 2021-12-02 14:23

from django.db import migrations

from ureport.sql import InstallSQL


class Migration(migrations.Migration):

dependencies = [
("stats", "0027_auto_20211202_1422"),
]

operations = [InstallSQL("stats_0028")]
80 changes: 80 additions & 0 deletions ureport/stats/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,86 @@ def calculate_average_response_rate(cls, org):
return percentage


class PollContactResult(models.Model):
org = models.ForeignKey(Org, on_delete=models.PROTECT, related_name="poll_contact_results", db_index=False)

contact = models.CharField(max_length=36)

flow = models.CharField(max_length=36)

flow_result = models.ForeignKey(FlowResult, null=True, on_delete=models.SET_NULL)

flow_result_category = models.ForeignKey(FlowResultCategory, null=True, on_delete=models.SET_NULL)

text = models.TextField(null=True)

age_segment = models.ForeignKey(AgeSegment, null=True, on_delete=models.SET_NULL)

gender_segment = models.ForeignKey(GenderSegment, null=True, on_delete=models.SET_NULL)

scheme_segment = models.ForeignKey(SchemeSegment, null=True, on_delete=models.SET_NULL)

location = models.ForeignKey(Boundary, null=True, on_delete=models.SET_NULL)

date = models.DateTimeField(null=True)

class Meta:
indexes = [
models.Index(name="%(app_label)s_pcr_contact", fields=["contact"]),
models.Index(name="%(app_label)s_pcr_org_flow_contact", fields=["org", "flow", "contact"]),
models.Index(name="%(app_label)s_pcr_org_flow", fields=["org", "flow"]),
models.Index(name="%(app_label)s_pcr_org_flow_result_text", fields=["org", "flow", "flow_result", "text"]),
]


class ContactEngagementActivity(models.Model):
org = models.ForeignKey(Org, on_delete=models.PROTECT, related_name="contact_engagement_activities")

contact = models.CharField(max_length=36)

age_segment = models.ForeignKey(AgeSegment, null=True, on_delete=models.SET_NULL)

gender_segment = models.ForeignKey(GenderSegment, null=True, on_delete=models.SET_NULL)

scheme_segment = models.ForeignKey(SchemeSegment, null=True, on_delete=models.SET_NULL)

location = models.ForeignKey(Boundary, null=True, on_delete=models.SET_NULL)

date = models.DateField(help_text="The starting date for for the month")

used = models.BooleanField(null=True)

class Meta:
indexes = [
models.Index(name="%(app_label)s_cea_org_contact", fields=["org", "contact"]),
models.Index(name="%(app_label)s_cea_org_date", fields=["org", "date"]),
models.Index(
name="%(app_label)s_cea_org_date_used", fields=["org", "date", "used"], condition=Q(used=True)
),
models.Index(
name="%(app_label)s_cea_org_date_state_used",
fields=["org", "date", "location", "used"],
condition=Q(location__isnull=False) & Q(used=True),
),
models.Index(
name="%(app_label)s_cea_org_date_age_used",
fields=["org", "date", "age_segment", "used"],
condition=Q(age_segment__isnull=False) & Q(used=True),
),
models.Index(
name="%(app_label)s_cea_org_date_scheme_used",
fields=["org", "date", "scheme_segment", "used"],
condition=Q(scheme_segment__isnull=False) & Q(used=True),
),
models.Index(
name="%(app_label)s_cea_org_date_gender_used",
fields=["org", "date", "gender_segment", "used"],
condition=Q(gender_segment__isnull=False) & Q(used=True),
),
]
unique_together = ("org", "contact", "date")


class ContactActivity(models.Model):
org = models.ForeignKey(Org, on_delete=models.PROTECT, related_name="contact_activities")

Expand Down
Loading