delay pledge notifications until payment becomes possible

This commit is contained in:
Changaco 2019-02-05 17:54:52 +01:00
parent 1ce6f0bb75
commit ae098df9b8
6 changed files with 91 additions and 35 deletions

View File

@ -118,19 +118,21 @@ elif env.clean_assets:
conf = website.app_conf
if conf:
intervals = conf.cron_intervals
cron = Cron(website)
cron(conf.check_db_every, website.db.self_check, True)
cron(conf.dequeue_emails_every, Participant.dequeue_emails, True)
cron(conf.send_newsletters_every, Participant.send_newsletters, True)
cron(conf.refetch_elsewhere_data_every, refetch_elsewhere_data, True)
cron(conf.refetch_repos_every, refetch_repos, True)
cron(intervals['check_db'], website.db.self_check, True)
cron(intervals['dequeue_emails'], Participant.dequeue_emails, True)
cron(intervals['send_newsletters'], Participant.send_newsletters, True)
cron(intervals['refetch_elsewhere_data'], refetch_elsewhere_data, True)
cron(intervals['refetch_repos'], refetch_repos, True)
cron(Weekly(weekday=3, hour=2), create_payday_issue, True)
cron(conf.clean_up_counters_every, website.db.clean_up_counters, True)
cron(intervals['clean_up_counters'], website.db.clean_up_counters, True)
cron(Daily(hour=16), lambda: fetch_currency_exchange_rates(website.db), True)
cron(Daily(hour=17), Payday.update_cached_amounts, True)
cron(Daily(hour=8), clean_up_closed_accounts, True)
cron(intervals['notify_patrons'], Participant.notify_patrons, True)
if conf.ses_feedback_queue_url:
cron(conf.fetch_email_bounces_every, handle_email_bounces, True)
cron(intervals['fetch_email_bounces'], handle_email_bounces, True)
cron('once', website.cryptograph.rotate_stored_data, True)

View File

@ -12,6 +12,7 @@ import uuid
import aspen_jinja2_renderer
from cached_property import cached_property
from dateutil.parser import parse as parse_date
from html2text import html2text
import mangopay
from markupsafe import escape as htmlescape
@ -1144,7 +1145,7 @@ class Participant(Model, MixinTeam):
# Notifications
# =============
def notify(self, event, force_email=False, email=True, web=True, **context):
def notify(self, event, force_email=False, email=True, web=True, idem_key=None, **context):
if email and not force_email:
bit = EVENTS.get(event.split('~', 1)[0]).bit
email = self.email_notif_bits & bit > 0
@ -1152,8 +1153,8 @@ class Participant(Model, MixinTeam):
context = serialize(context)
n_id = self.db.one("""
INSERT INTO notifications
(participant, event, context, web, email)
VALUES (%(p_id)s, %(event)s, %(context)s, %(web)s, %(email)s)
(participant, event, context, web, email, idem_key)
VALUES (%(p_id)s, %(event)s, %(context)s, %(web)s, %(email)s, %(idem_key)s)
RETURNING id;
""", locals())
if not web:
@ -1280,14 +1281,50 @@ class Participant(Model, MixinTeam):
website.tell_sentry(e, state)
return r
def notify_patrons(self, elsewhere, tips):
@classmethod
def notify_patrons(cls):
grouped_tips = cls.db.all("""
SELECT event.payload AS takeover, json_agg(tip) AS tips
FROM current_tips tip
JOIN participants tippee ON tippee.id = tip.tippee
JOIN events event ON event.participant = tip.tippee
AND event.type = 'take-over'
WHERE tip.renewal_mode > 0
AND tip.paid_in_advance IS NULL
AND tippee.payment_providers > 0
AND EXISTS (
SELECT 1
FROM tips old_tip
WHERE old_tip.tipper = tip.tipper
AND old_tip.tippee = (event.payload->>'owner')::int
)
AND NOT EXISTS (
SELECT 1
FROM notifications n
WHERE n.participant = tip.tipper
AND n.event = 'pledgee_joined~v2'
AND n.idem_key = event.payload->>'owner'
)
GROUP BY event.payload
""")
for takeover, tips in grouped_tips:
elsewhere = AccountElsewhere._from_thing(
'user_id',
takeover['platform'], takeover['user_id'], takeover['domain']
)
cls._notify_patrons(elsewhere, tips)
@classmethod
def _notify_patrons(cls, elsewhere, tips):
assert elsewhere.participant.payment_providers > 0
for t in tips:
Participant.from_id(t.tipper).notify(
Participant.from_id(t['tipper']).notify(
'pledgee_joined~v2',
idem_key=str(elsewhere.participant.id),
user_name=elsewhere.user_name,
platform=elsewhere.platform_data.display_name,
pledge_date=t.ctime.date(),
periodic_amount=t.periodic_amount,
pledge_date=parse_date(t['ctime']).date(),
periodic_amount=Money(**t['periodic_amount']),
elsewhere_profile_url=elsewhere.html_url,
join_time=elsewhere.participant.join_time,
liberapay_profile_url=elsewhere.participant.url(),
@ -2487,9 +2524,6 @@ class Participant(Model, MixinTeam):
platform=platform, domain=domain, user_id=user_id, owner=other.id
))
if old_tips:
self.notify_patrons(elsewhere, tips=old_tips)
self.update_avatar()
# Note: the order matters here, receiving needs to be updated before giving

View File

@ -207,14 +207,11 @@ class AppConf(object):
bountysource_callback=str,
bountysource_id=None.__class__,
bountysource_secret=str,
check_db_every=int,
check_email_domains=bool,
clean_up_counters_every=int,
dequeue_emails_every=int,
cron_intervals=dict,
facebook_callback=str,
facebook_id=str,
facebook_secret=str,
fetch_email_bounces_every=int,
github_callback=str,
github_id=str,
github_secret=str,
@ -243,8 +240,6 @@ class AppConf(object):
paypal_domain=str,
paypal_id=str,
paypal_secret=str,
refetch_elsewhere_data_every=int,
refetch_repos_every=int,
s3_endpoint=str,
s3_public_access_key=str,
s3_secret_key=str,
@ -252,7 +247,6 @@ class AppConf(object):
s3_payday_logs_bucket=str,
ses_feedback_queue_url=str,
ses_region=str,
send_newsletters_every=int,
sepa_creditor_identifier=str,
show_sandbox_warning=bool,
socket_timeout=float,

View File

@ -22,6 +22,11 @@ echo "Applying sql/recreate-schema.sql ... "
echo
psql "$DATABASE_URL" < sql/recreate-schema.sql
echo "=============================================================================="
echo "Applying sql/app-conf-defaults.sql ... "
echo
psql "$DATABASE_URL" < sql/app-conf-defaults.sql
echo "=============================================================================="
echo "Looking for sql/branch.sql ..."
echo
@ -33,11 +38,6 @@ else
echo "include schema changes with your pull request."
fi
echo "=============================================================================="
echo "Applying sql/app-conf-defaults.sql ... "
echo
psql "$DATABASE_URL" < sql/app-conf-defaults.sql
if [ "${1-}" = "test" ]; then
echo "=============================================================================="
echo "Applying sql/app-conf-tests.sql ... "

View File

@ -10,14 +10,21 @@ INSERT INTO app_conf (key, value) VALUES
('bountysource_callback', '"http://127.0.0.1:8339/on/bountysource/associate"'::jsonb),
('bountysource_id', 'null'::jsonb),
('bountysource_secret', '""'::jsonb),
('check_db_every', '600'::jsonb),
('check_email_domains', 'true'::jsonb),
('clean_up_counters_every', '3600'::jsonb),
('dequeue_emails_every', '60'::jsonb),
('cron_intervals', jsonb_build_object(
'check_db', 600,
'clean_up_counters', 3600,
'dequeue_emails', 60,
'fetch_email_bounces', 60,
'notify_patrons', 120,
'refetch_elsewhere_data', 120,
'refetch_repos', 60,
'send_newsletters', 60
)),
('facebook_callback', '"http://localhost:8339/on/facebook/associate"'::jsonb),
('facebook_id', '"1418954898427187"'::jsonb),
('facebook_secret', '"3bcb5dc6ce821e5202870c1e6ef5bbc4"'::jsonb),
('fetch_email_bounces_every', '60'::jsonb),
('github_callback', '"http://127.0.0.1:8339/on/github/associate"'::jsonb),
('github_id', '"18891d01e40e5aef93b8"'::jsonb),
('github_secret', '"46f75669895e96029d57b64832d6f2c8e6291a0e"'::jsonb),
@ -46,14 +53,11 @@ INSERT INTO app_conf (key, value) VALUES
('paypal_domain', '"sandbox.paypal.com"'::jsonb),
('paypal_id', '"ASTH9rn8IosjJcEwNYqV2KeHadB6O8MKVP7fL7kXeSuOml0ei77FRYU5E1thEF-1cT3Wp3Ibo0jXIbul"'::jsonb),
('paypal_secret', '"EAStyBaGBZk9MVBGrI_eb4O4iEVFPZcRoIsbKDwv28wxLzroLDKYwCnjZfr_jDoZyDB5epQVrjZraoFY"'::jsonb),
('refetch_elsewhere_data_every', '120'::jsonb),
('refetch_repos_every', '60'::jsonb),
('s3_endpoint', '""'::jsonb),
('s3_payday_logs_bucket', '""'::jsonb),
('s3_public_access_key', '""'::jsonb),
('s3_secret_key', '""'::jsonb),
('s3_region', '"eu-west-1"'::jsonb),
('send_newsletters_every', '60'::jsonb),
('sepa_creditor_identifier', '"FR65ZZZ858BF1"'::jsonb),
('ses_feedback_queue_url', '""'::jsonb),
('show_sandbox_warning', 'true'::jsonb),

22
sql/branch.sql Normal file
View File

@ -0,0 +1,22 @@
ALTER TABLE notifications ADD COLUMN idem_key text;
INSERT INTO app_conf (key, value) VALUES
('cron_intervals', jsonb_build_object(
'check_db', (SELECT value::text::int FROM app_conf WHERE key = 'check_db_every'),
'clean_up_counters', (SELECT value::text::int FROM app_conf WHERE key = 'clean_up_counters_every'),
'dequeue_emails', (SELECT value::text::int FROM app_conf WHERE key = 'dequeue_emails_every'),
'fetch_email_bounces', (SELECT value::text::int FROM app_conf WHERE key = 'fetch_email_bounces_every'),
'notify_patrons', 120,
'refetch_elsewhere_data', (SELECT value::text::int FROM app_conf WHERE key = 'refetch_elsewhere_data_every'),
'refetch_repos', (SELECT value::text::int FROM app_conf WHERE key = 'refetch_repos_every'),
'send_newsletters', (SELECT value::text::int FROM app_conf WHERE key = 'send_newsletters_every')
))
ON CONFLICT (key) DO UPDATE SET value = excluded.value;
SELECT 'after deployment';
DELETE FROM app_conf WHERE key IN (
'check_db_every', 'clean_up_counters_every', 'dequeue_emails_every',
'fetch_email_bounces_every', 'refetch_elsewhere_data_every',
'refetch_repos_every', 'send_newsletters_every'
);