Skip to content

Commit

Permalink
Merge pull request #555 from colts661/ywang_ffq_code_lookup
Browse files Browse the repository at this point in the history
Added admin FFQ code lookup
  • Loading branch information
cassidysymons authored Dec 13, 2023
2 parents 464736e + 4de73f6 commit 8d4c0bd
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 13 deletions.
17 changes: 17 additions & 0 deletions microsetta_private_api/api/_interested_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
from microsetta_private_api.exceptions import RepoException
from microsetta_private_api.repo.campaign_repo import CampaignRepo
from microsetta_private_api.repo.melissa_repo import MelissaRepo
from microsetta_private_api.repo.perk_fulfillment_repo import\
PerkFulfillmentRepo
from microsetta_private_api.tasks import send_email
from microsetta_private_api.admin.admin_impl import validate_admin_access


def create_interested_user(body):
Expand Down Expand Up @@ -285,3 +288,17 @@ def _validate_user_match(interested_user, email):
# someone doesn't stumble upon a valid email and/or id.
# if they don't both match, treat as invalid
return interested_user.email == email


def search_ffq_codes_by_email(email, token_info):
validate_admin_access(token_info)

with Transaction() as t:
pfr = PerkFulfillmentRepo(t)
ffq_diag = pfr.get_ffq_codes_by_email(email)
ffq_codes_obj = {
"ffq_codes": ffq_diag
}
if ffq_diag is None:
return jsonify(code=404, message="Email not found"), 404
return jsonify(ffq_codes_obj), 200
27 changes: 27 additions & 0 deletions microsetta_private_api/api/microsetta_private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,33 @@ paths:
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/search/ffq_codes/{email}':
get:
operationId: microsetta_private_api.api._interested_user.search_ffq_codes_by_email
tags:
- Admin
summary: Retrieve FFQ codes by an email query
description: Retrieve FFQ codes by an email query
parameters:
- in: path
name: email
description: user email address to search for
schema:
type: string
example: "[email protected]"
required: true
responses:
'200':
description: Object containing ffq codes and info related to the email (if any)
content:
application/json:
schema:
type: object
'401':
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/search/interested_users/{email}':
get:
Expand Down
54 changes: 54 additions & 0 deletions microsetta_private_api/repo/perk_fulfillment_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -793,3 +793,57 @@ def check_perk_fulfillment_active(self):
)
row = cur.fetchone()
return row['perk_fulfillment_active']

def get_ffq_codes_by_email(self, email):
email = "%" + email + "%"
with self._transaction.dict_cursor() as cur:
# Note: Use left join to differentiate email not found possibility
cur.execute(
"""
WITH all_codes AS (
SELECT
iu.email,
tr.created AS transaction_created_time,
frc.ffq_registration_code,
frc.registration_code_used
FROM campaign.interested_users AS iu
LEFT JOIN campaign.transaction AS tr
ON iu.interested_user_id = tr.interested_user_id
LEFT JOIN campaign.fundrazr_transaction_perk AS ftp
ON tr.id = ftp.transaction_id
LEFT JOIN campaign.fundrazr_ffq_codes AS ffc
ON ftp.id = ffc.fundrazr_transaction_perk_id
LEFT JOIN campaign.ffq_registration_codes AS frc
ON ffc.ffq_registration_code = frc.ffq_registration_code
WHERE iu.email ILIKE %s
), count_codes AS (
SELECT
ac1.email,
COUNT(ac1.ffq_registration_code) AS num_codes
FROM all_codes AS ac1
GROUP BY ac1.email
)
SELECT DISTINCT
ac.email,
CASE WHEN
cc.num_codes = 0 THEN NULL
ELSE
ac.transaction_created_time
END,
ac.ffq_registration_code,
ac.registration_code_used
FROM all_codes AS ac
LEFT JOIN count_codes AS cc
ON ac.email = cc.email
WHERE
ac.ffq_registration_code IS NOT NULL
OR
cc.num_codes = 0
ORDER BY
ac.email ASC,
ac.registration_code_used DESC
""",
(email,)
)
rows = cur.fetchall()
return [dict(row) for row in rows]
225 changes: 212 additions & 13 deletions microsetta_private_api/repo/tests/test_perk_fulfillment_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from microsetta_private_api.model.campaign import (FundRazrPayment, Item,
Shipping)
from microsetta_private_api.model.address import Address
from microsetta_private_api.model.activation_code import ActivationCode
from microsetta_private_api.model.daklapack_order import DaklapackOrder
from microsetta_private_api.repo.account_repo import AccountRepo
from microsetta_private_api.repo.admin_repo import AdminRepo
Expand Down Expand Up @@ -315,20 +316,102 @@ def setUp(self, verify_address_result):
res = cur.fetchone()
self.sub_ftp_id = res[0]

# dummy information for ffq code search
# interested users
fake_ffq_emails = ["[email protected]"] * 3 + [
"[email protected]", "[email protected]", "[email protected]", "[email protected]"
] + ["[email protected]"] * 3
cur.execute(
"INSERT INTO campaign.interested_users " +
"(campaign_id, first_name, last_name, email) VALUES " +
", ".join([
f"('{self.test_campaign_id1}', 'Fir', 'Last', '{email}')"
for email in fake_ffq_emails
]) +
"RETURNING interested_user_id"
)
self.iu_ids = [tup[0] for tup in cur.fetchall()]

# transactions
self.tx_ids = {
f"MT{idx + 1}": iu_id
for idx, iu_id in enumerate(self.iu_ids) if idx < 6
}
self.tx_ids['MT7'] = self.iu_ids[-6] # extra tx for [email protected]
cur.execute(
"INSERT INTO campaign.transaction VALUES " +
", ".join([
f"('{tx_id}', '{iu_id}', 'fundrazr', '4Tqx5', " +
"'2023-01-01', 100, 100, 'usd', 'Fir', 'Last', " +
"'[email protected]', 'paypal', 'coolcool', TRUE)"
for tx_id, iu_id in self.tx_ids.items()
])
)

# fundrazr transaction perks
cur.execute(
"INSERT INTO campaign.fundrazr_transaction_perk " +
"(transaction_id, perk_id, quantity, processed) VALUES " +
", ".join([
f"('{tx_id}', '3QeVd', 1, TRUE)" for tx_id in self.tx_ids
if tx_id not in ['MT3', 'MT6']
]) +
"RETURNING id"
)
self.dummy_ftp_ids = [tup[0] for tup in cur.fetchall()]
cur.execute(
"INSERT INTO campaign.fundrazr_transaction_perk "
"(transaction_id, perk_id, quantity, processed) VALUES "
"('MT5', '3QeW6', 1, TRUE) "
"RETURNING id"
) # extra ftp for [email protected]
self.dummy_ftp_ids.append(cur.fetchone()[0])

# ffq registration codes
self.new_ffq_codes = [
ActivationCode.generate_code()
for _ in range(6)
]
ffq_reg_codes = [ # odd idx: used
(code, 'NULL') if idx % 2 == 0 else (code, "'2023-02-01'")
for idx, code in enumerate(self.new_ffq_codes)
]
cur.execute(
"INSERT INTO campaign.ffq_registration_codes VALUES " +
", ".join([
f"('{code}', {used})" for code, used in ffq_reg_codes
])
)

# fundrazr ffq codes
fundrazr_ffq_codes = {}
for idx, code in enumerate(self.new_ffq_codes):
if idx <= 3:
fundrazr_ffq_codes[code] = self.dummy_ftp_ids[idx]
else: # 2 codes
fundrazr_ffq_codes[code] = self.dummy_ftp_ids[4]
cur.execute(
"INSERT INTO campaign.fundrazr_ffq_codes VALUES " +
", ".join([
f"('{ftp_id}', '{code}')"
for code, ftp_id in fundrazr_ffq_codes.items()
])
)

t.commit()

def tearDown(self):
with Transaction() as t:
cur = t.cursor()
cur.execute(
"DELETE FROM campaign.campaigns_projects "
"WHERE campaign_id = %s",
(self.test_campaign_id1,)
"DELETE FROM campaign.fundrazr_ffq_codes AS ffc "
"WHERE ffc.ffq_registration_code In %s",
(tuple(self.new_ffq_codes), )
)
cur.execute(
"DELETE FROM campaign.campaigns "
"WHERE campaign_id = %s",
(self.test_campaign_id1, )
"DELETE FROM campaign.ffq_registration_codes AS frc "
"WHERE frc.ffq_registration_code IN %s",
(tuple(self.new_ffq_codes), )
)
cur.execute(
"DELETE FROM campaign.fundrazr_transaction_perk "
Expand All @@ -338,19 +421,38 @@ def tearDown(self):
self.ffq_ftp_id,
self.kit_ftp_id,
self.two_kit_ftp_id,
self.sub_ftp_id
self.sub_ftp_id,
*self.dummy_ftp_ids
),
)
)
cur.execute(
"DELETE FROM campaign.transaction "
"WHERE id IN %s",
((
FFQ_TRANSACTION_ID,
KIT_TRANSACTION_ID,
TWO_KIT_TRANSACTION_ID,
SUB_TRANSACTION_ID
), )
(
(
FFQ_TRANSACTION_ID,
KIT_TRANSACTION_ID,
TWO_KIT_TRANSACTION_ID,
SUB_TRANSACTION_ID,
*self.tx_ids
),
)
)
cur.execute(
"DELETE FROM campaign.interested_users AS iu "
"WHERE iu.interested_user_id IN %s",
(tuple(self.iu_ids), )
)
cur.execute(
"DELETE FROM campaign.campaigns_projects "
"WHERE campaign_id = %s",
(self.test_campaign_id1,)
)
cur.execute(
"DELETE FROM campaign.campaigns "
"WHERE campaign_id = %s",
(self.test_campaign_id1, )
)
t.commit()

Expand Down Expand Up @@ -1174,6 +1276,103 @@ def _count_fundrazr_ffq_codes(self, t):
res = cur.fetchone()
return res[0]

def test_get_ffq_codes_by_email(self):
with Transaction() as t:
pfr = PerkFulfillmentRepo(t)

# Test: email not found
ffq_code = pfr.get_ffq_codes_by_email("[email protected]")
self.assertEqual(len(ffq_code), 0)

# Test: no ffq without transaction
ffq_code = pfr.get_ffq_codes_by_email("[email protected]") # unique
self.assertEqual(len(ffq_code), 1)
self.assertEqual(ffq_code[0]['email'], "[email protected]")
self.assertIsNone(ffq_code[0]['transaction_created_time'])
self.assertIsNone(ffq_code[0]['ffq_registration_code'])

ffq_code = pfr.get_ffq_codes_by_email("[email protected]") # duplicate
self.assertEqual(len(ffq_code), 1)
self.assertEqual(ffq_code[0]['email'], "[email protected]")
self.assertIsNone(ffq_code[0]['ffq_registration_code'])

# Test: no ffq with transaction
ffq_code = pfr.get_ffq_codes_by_email("[email protected]")
self.assertEqual(len(ffq_code), 1)
self.assertEqual(ffq_code[0]['email'], "[email protected]")
self.assertIsNone(ffq_code[0]['transaction_created_time'])
self.assertIsNone(ffq_code[0]['ffq_registration_code'])

# Test: 1 ffq code
ffq_code = pfr.get_ffq_codes_by_email("[email protected]")
self.assertEqual(len(ffq_code), 1)
self.assertEqual(ffq_code[0]['email'], "[email protected]")
self.assertEqual(
ffq_code[0]['transaction_created_time'].strftime('%Y-%m-%d'),
'2023-01-01'
)
self.assertEqual(
ffq_code[0]['ffq_registration_code'], self.new_ffq_codes[2]
)
self.assertIsNone(ffq_code[0]['registration_code_used'])

# Test: 3 duplicate emails, 2 codes
ffq_code = pfr.get_ffq_codes_by_email("[email protected]")
self.assertEqual(len(ffq_code), 2)
self.assertTrue(
ffq_code[0]['email'] == ffq_code[1]['email'] == "[email protected]"
)
self.assertEqual(
ffq_code[0]['transaction_created_time'].strftime('%Y-%m-%d'),
'2023-01-01'
)
got_codes = [
ffq_code[i]['ffq_registration_code']
for i in range(2)
]
self.assertEqual(set(got_codes), set(self.new_ffq_codes[:2]))

# Test: unique email, 3 codes
ffq_code = pfr.get_ffq_codes_by_email("[email protected]")
self.assertEqual(len(ffq_code), 3)
self.assertTrue(
ffq_code[0]['email'] == ffq_code[2]['email'] == "[email protected]"
)
self.assertEqual(
ffq_code[2]['transaction_created_time'].strftime('%Y-%m-%d'),
'2023-01-01'
)
got_codes = [
ffq_code[i]['ffq_registration_code']
for i in range(3)
]
self.assertEqual(set(got_codes), set(self.new_ffq_codes[3:]))

# Test: match multiple emails
ffq_code = pfr.get_ffq_codes_by_email("iu")
self.assertEqual(len(ffq_code), 3)
self.assertTrue(
ffq_code[1]['email'] == ffq_code[2]['email'] == "[email protected]"
)
self.assertEqual(ffq_code[0]['email'], "[email protected]")
self.assertEqual(
ffq_code[1]['transaction_created_time'].strftime('%Y-%m-%d'),
'2023-01-01'
)
got_codes = [
ffq_code[i]['ffq_registration_code']
for i in range(3)
]
self.assertEqual(set(got_codes), set(self.new_ffq_codes[:3]))

ffq_code = pfr.get_ffq_codes_by_email("foo.com")
self.assertEqual(len(ffq_code), 5)
got_codes = [
ffq_code[i]['ffq_registration_code']
for i in range(5)
]
self.assertNotIn(self.new_ffq_codes[2], got_codes)


if __name__ == '__main__':
unittest.main()

0 comments on commit 8d4c0bd

Please sign in to comment.