Skip to content

Commit cd40836

Browse files
committed
Add async task
1 parent ada5c45 commit cd40836

File tree

6 files changed

+129
-6
lines changed

6 files changed

+129
-6
lines changed

apps/storage/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ def find_one_and_update__(coll: collection.Collection, filter: dict, update=dict
3030
def find_one_and_delete__(coll: collection.Collection, filter: dict):
3131
return coll.find_one_and_delete(filter)
3232

33-
def find_one__(coll: collection.Collection, filter: dict, projection):
34-
return coll.find_one(filter=filter, projection=projection)
33+
def find_one__(coll: collection.Collection, filter: dict, projection, **kwargs):
34+
return coll.find_one(filter=filter, projection=projection, **kwargs)
3535

3636
def find_many__(coll: collection.Collection, filter: dict, projection, **kwargs):
3737
return coll.find(filter=filter, projection=projection, **kwargs)
3838

39+
def count_docs__(coll: collection.Collection, filter: dict):
40+
return coll.count_documents(filter=filter)
41+
42+
def estimated_count_docs__(coll: collection.Collection):
43+
return coll.estimated_document_count()
44+
3945
def watch__(coll: collection.Collection, **kwargs):
4046
return coll.watch(**kwargs)
4147

apps/storage/query.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from .storage import *
2+
import pymongo
3+
4+
# find the max value in a collection
5+
def find_max(collection_name, filter: dict, sort_key: str, db_name="tickets"):
6+
sort_seq = [(sort_key, pymongo.DESCENDING)]
7+
return find_one(collection_name, filter, db_name=db_name, sort=sort_seq)
8+
9+
# find the min value in a collection
10+
def find_min(collection_name, filter: dict, sort_key: str, db_name="tickets"):
11+
sort_seq = [(sort_key, pymongo.ASCENDING)]
12+
return find_one(collection_name, filter, db_name=db_name, sort=sort_seq)
13+
14+
def find_many_ascending_order(collection_name, filter: dict, sort_key: str, db_name="tickets"):
15+
sort_seq = [(sort_key, pymongo.ASCENDING)]
16+
return find_many(collection_name, filter, db_name=db_name, sort=sort_seq)

apps/storage/storage.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,29 @@ def find_one_and_delete(collection_name, filter: dict, db_name="tickets"):
5151
return find_one_and_delete__(coll, filter)
5252

5353
# find one
54-
def find_one(collection_name, filter: dict, projection=None, db_name="tickets"):
54+
def find_one(collection_name, filter: dict, projection=None, db_name="tickets", **kwargs):
5555
db = get_db_handle(db_name)
5656
coll = db[collection_name]
57-
return find_one__(coll, filter, projection)
57+
return find_one__(coll, filter, projection, **kwargs)
5858

5959
# find many
6060
def find_many(collection_name, filter: dict, projection=None, db_name="tickets", **kwargs):
6161
db = get_db_handle(db_name)
6262
coll = db[collection_name]
6363
return list(find_many__(coll, filter, projection, **kwargs))
6464

65+
# count with filter
66+
def count_docs(collection_name, filter: dict, db_name="tickets"):
67+
db = get_db_handle(db_name)
68+
coll = db[collection_name]
69+
return count_docs__(coll, filter)
70+
71+
# count all docs in a collection
72+
def estimated_count_docs(collection_name, db_name="tickets"):
73+
db = get_db_handle(db_name)
74+
coll = db[collection_name]
75+
return estimated_count_docs__(coll)
76+
6577
# watch changes
6678
def watch(collection_name, db_name="tickets", **kwargs):
6779
db = get_db_handle(db_name)

apps/ticketscraping/constants.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ def get_top_picks_url(
1717
"BEST_AVAILABLE_SEATS": "best-available-seats",
1818
"BEST_HISTORY_SEATS": "best-history-seats"
1919
}
20+
21+
# metric thresholds
22+
PERCENT_OF_CHANGE = 0.5
23+
PERCENTILE_HISTORY_PRICES = 0.5
24+
2025
def get_top_picks_header(): return {
2126
**BASIC_REQ_HEADER,
2227
"tmps-correlation-id": str(uuid4())
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from ...storage.storage import count_docs
2+
from ...storage.query import find_max, find_min, find_many_ascending_order
3+
from ...ticketscraping import constants
4+
from ..models.pick import Pick
5+
6+
# metric 1
7+
8+
9+
def percent_of_change_metric(pick: Pick, scraping_id: str) -> float:
10+
# Find the % of change
11+
max_seat = find_max(constants.DATABASE['BEST_HISTORY_SEATS'], {
12+
"scraping_id": scraping_id}, 'price')
13+
min_seat = find_min(constants.DATABASE['BEST_HISTORY_SEATS'], {
14+
"scraping_id": scraping_id}, 'price')
15+
max_price = max_seat.get('price', 0)
16+
min_price = min_seat.get('price', 0)
17+
18+
# min and max are identical - abort
19+
if max_price == min_price:
20+
raise Exception('min and max prices are identical')
21+
22+
percent_change = (pick.price - min_price) / (max_price - min_price)
23+
24+
# price of change exceeds the metric value - abort
25+
if percent_change > constants.PERCENT_OF_CHANGE:
26+
raise Exception(
27+
f'price of change ({percent_change}) exceeds the metric value')
28+
29+
return percent_change
30+
31+
# metric 2
32+
33+
34+
def percentile_metric(pick: Pick, scraping_id: str) -> float:
35+
rank = count_docs(constants.DATABASE['BEST_HISTORY_SEATS'],
36+
{"scraping_id": scraping_id, "price": {"$lte": pick.price}})
37+
total_count = count_docs(constants.DATABASE['BEST_HISTORY_SEATS'],
38+
{
39+
"scraping_id": scraping_id})
40+
41+
# no history seats data - abort
42+
if total_count == 0:
43+
raise Exception('no history seats data')
44+
45+
percentile = rank / total_count
46+
47+
# percentile of history prices exceeds the metric value - abort
48+
if percentile > constants.PERCENTILE_HISTORY_PRICES:
49+
raise Exception(
50+
'percentile of history prices ({percentile}) exceeds the metric value')
51+
52+
return percentile
53+
54+
55+
def get_exact_same_seats(pick: Pick, scraping_id: str):
56+
return find_many_ascending_order(constants.DATABASE['BEST_HISTORY_SEATS'],
57+
{"scraping_id": scraping_id, "section": pick.section,
58+
"row": pick.row, "seat_columns": pick.seat_columns},
59+
'last_modified')
60+
61+
62+
def run_async_task(pick: Pick, scraping_id: str):
63+
try:
64+
# Find the % of change
65+
percent_change = percent_of_change_metric(pick, scraping_id)
66+
# Find the percentile of the seat based on some criteria(e.g. rank or price).
67+
percentile = percentile_metric(pick, scraping_id)
68+
# If found the exact same seat based on(sec, row?, seat?), get the history price(s) of the seat.
69+
same_seats = get_exact_same_seats(pick, scraping_id)
70+
71+
print(f"percent change: {percent_change*100}")
72+
print(f"percentile: {percentile*100}")
73+
print(f"same seats in chronological order")
74+
print(f"new seat price: {pick.price}")
75+
print(f"history seat prices:")
76+
print(list(map(lambda seat: seat.get('price', -1), same_seats)))
77+
78+
# TODO
79+
# Alert the user based on alert conditions
80+
81+
except Exception as ex:
82+
print(ex)
83+
pass

apps/ticketscraping/tasks/periodic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from ...storage.storage import find_many, insert_many, delete_many
22
from ...ticketscraping import constants
33
from ..models.pick import Pick
4+
from .asynchronous import run_async_task
45

56
def generate_picks_set_from_picks(picks):
67
def __helper(pick: dict):
@@ -65,7 +66,7 @@ def run_periodic_task(picks: dict, scraping_id: str):
6566
# Save C to best_history_seats.
6667
insert_history_seats(overwritten_seats)
6768

68-
# TODO
6969
# Use D to invoke a handler to analyze them against the best_history_seats asynchronously.
70-
70+
for seat in new_seats:
71+
run_async_task(seat, scraping_id)
7172
pass

0 commit comments

Comments
 (0)