Skip to content

Instantly share code, notes, and snippets.

@volodymyrsmirnov
Last active August 29, 2015 14:15
Show Gist options
  • Save volodymyrsmirnov/814c2d754743400d9d3e to your computer and use it in GitHub Desktop.
Save volodymyrsmirnov/814c2d754743400d9d3e to your computer and use it in GitHub Desktop.
# Select all users recurring user subscriptions where activation date is bigger than the lowers activation date for unexpired and enabled subscription
recurring_subscriptions = "select user_subscriptions for user where enabled=true and recurring=true and activation_date >= lowest_user_subscription_activation_date_for_user()"
# Select all users non-recurring user subscriptions where activation date is bigger than the lowers activation date for unexpired and enabled subscription (type does not matter)
non_recurring_subscriptions = "select user_subscriptions for user where recurring=false and activation_date >= lowest_user_subscription_activation_date_for_user()"
# In real world recurring_subscriptions and non_recurring_subscriptions can be joined into one DB query, I have 2 different variables here just for the ease of understanding
# Select all sessions starting from the lowers activation date for unexpired and enabled subscription, group them by day, month and year for performance
sessions = "select sessions.date.format('%d-%m-%Y') as date_formatted, SUM(bytes) as bytes_sum for user where start_date >= lowest_user_subscription_activation_date_for_user() GROUP BY date_formatter"
# Make the dictinary with daily summary usage, where key is date and value is sum of bytes for all sessions per day
usage_days = dict([
session["date_formatted"], session["bytes_sum"] for session in sessions
])
#{
# "15-01-2015": 10000000,
# "16-01-2015": 20000000,
# "17-01-2015": 30000000,
#}
# Sum limit of all unexpired subscriptions
traffic_limit = 0
# Sum of all sessions for active (unexpired) subscriptions
traffic_used = 0
# How much traffic user still can use
traffic_left = 0
# Function to process usage dats
# subscription_days - list, i.e. ["01.01.2015", "01.02.2015", ...]
# subscription_limit - int, bytes of subscription or period, i.e. 50000000
# active_billing_period - boolean, if the subscription is still active (unexpired), or todays date is in one of the recurring subscriptions period
def process_usage_days(subscription_days, subscription_limit, active_billing_period):
# If subscription is active - it must go to traffic_limit
if active_billing_period:
traffic_limit += subscription_limit
# Iterate over days of subscription / subscription period
for day in subscription_days:
# If we have usage for subscription day
if day in usage_days:
day_usage = usage_days[day]
# Skip days with no usage
if day_usage == 0:
continue
# If number of bytes used over the selected day is less or equal than limit of subscription
if day_usage <= subscription_limit:
# Set day usage to 0
usage_days[day] = 0
# New subscription limit = subscription limit - number of bytes consumed over the day
subscription_limit -= day_usage
# If subscrption / period is active - add number of bytes to the usage
if active_billing_period
traffic_used += day_usage
# If number of bytes used over the selected day is bigger than a limit of subscription
else:
# New usage for day is usage for day minus the subscription bytes
if active_billing_period
traffic_used += subscription_limit
usage_days[day] -= subscription_limit
subscription_limit = 0
# Recurring subscriptions have higher priority over the non_recurring
# That's why we process them first
for recurring_subscription in recurring_subscriptions:
# recurring_subscription.start_date = 01.01.2015
# recurring_subscription.billing_period = 30
# recurring_subscription.billing_period_number = 3
# Get the list of the list of days per periods
periods = calculate_periods(
recurring_subscription.start_date,
recurring_subscription.billing_period,
recurring_subscription.billing_period_number,
)
# [
# [01.01.2015, 01.01.2015, ..., 31.01.2015],
# [01.02.2015, 01.02.2015, ..., 31.02.2015],
# [01.03.2015, 01.03.2015, ..., 31.01.2015],
# ]
# Process usage for days in every period
for period_days in periods:
process_usage_days(
period_days,
recurring_subscription.bytes_limit,
datetime.datetime.now.format("%d-%m-%Y") in period # Period is active if todays date is in period days
)
# After recurring subscriptions we can process non-recurring (addon) subscriptons
for non_recurring_subscription in non_recurring_subscriptions:
# non_recurring_subscription.activated = 14.01.2015
# non_recurring_subscription.expiration_date = 19.04.2015
# Get the list of days between subscription activation and expiration date
subscription_days = days_between_dates(
non_recurring_subscription.activated,
non_recurring_subscription.expiration_date
)
# ["14.01.2015", "15.01.2015", ... ,"19.01.2015"]
# Process usage for days in every period
process_usage_days(
subscription_days,
non_recurring_subscription.bytes_limit,
datetime.datetime.now() <= non_recurring_subscription.expiration_date
)
# Final calculation, user has traffic left = traffic limit - traffic usage
traffic_left = traffic_limit - traffic_used
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment