Skip to content

Commit

Permalink
Add MS project cooper report
Browse files Browse the repository at this point in the history
  • Loading branch information
marcserrat committed Sep 22, 2022
1 parent ccd50a4 commit 17b9c02
Show file tree
Hide file tree
Showing 6 changed files with 330 additions and 0 deletions.
35 changes: 35 additions & 0 deletions reports.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,41 @@
}
]
},
{
"name":"Microsoft Subscriptions report for Cooper",
"readme_file":"reports/ms_cooper/readme.md",
"report_spec":"2",
"renderers":[
{
"id":"xlsx",
"type":"xlsx",
"default":false,
"description":"Export data in Microsoft Excel 2020 format.",
"template":"reports/ms_cooper/template.xlsx",
"args":{
"start_row":2,
"start_col":1
}
},
{
"id":"json",
"type":"json",
"default":false,
"description":"Export data as JSON"
},
{
"id":"csv",
"type":"csv",
"default":true,
"description":"Export data as CSV"
}
],
"entrypoint":"reports.ms_cooper.entrypoint.generate",
"audience":[
"provider"
],
"parameters": []
},
{
"name":"IM all Microsoft Transactions report",
"readme_file":"reports/ms_products/readme.md",
Expand Down
Empty file added reports/ms_cooper/__init__.py
Empty file.
266 changes: 266 additions & 0 deletions reports/ms_cooper/entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
from cnct import R
from reports.utils import (
convert_to_datetime,
get_sub_parameter,
get_sub_ta_parameter,
)
from datetime import date, timedelta

HEADERS = (
'Subscription ID', 'Subscription External ID', 'Subscription Status', 'Subscription Created At',
'Subscription Updated At', 'Subscription Type', 'Contract Type', 'Product ID', 'Product Name',
'Billing Period', 'Anniversary Day', 'Anniversary Month', 'Item ID', 'Item MPN',
'Item Description', 'Item Period', 'Item Quantity', 'Customer ID', 'Customer Name',
'Customer external id', 'Customer Country', 'Tier 1 ID', 'Tier 1 Company name',
'Tier 1 External Id', 'Tier 1 Country location', 'Tier 2 ID', 'Tier 2 Company name',
'Tier 2 External Id', 'Tier 2 Country location', 'Provider ID', 'Provider Name',
'Source MKP', 'MKP Name', 'Vendor ID', 'Vendor Name', 'Microsoft Domain (if any)',
'Microsoft Sub ID (if any)', 'Microsoft Customer ID (if any)', 'Microsoft Order ID (if any)',
'Microsoft Plan Sub ID (if any)', 'Microsoft Tier1 MPN'
)

TC_CACHE = {}

PRODUCTS = []

# Microsoft Vendor ID
VENDOR = "VA-888-104"

def generate(
client=None,
parameters=None,
progress_callback=None,
renderer_type=None,
extra_context_callback=None,
):

populate_products(client)
if len(PRODUCTS) == 0:
return
init_tc_cache(client)
client.default_limit = 1000

last_day_of_prev_month = date.today().replace(day=1) - timedelta(days=1)
start_day_of_prev_month = date.today().replace(day=1) - timedelta(
days=last_day_of_prev_month.day,
)

# Handling Active first
sub_active_suspended = (
client.ns('subscriptions')
.collection('assets')
.filter(R().status.oneof(['active','suspended']) & R().product.id.oneof(PRODUCTS))
.order_by("-events.created.at")
)
# For terminated, termination must be in last month

rql = R().status.eq('terminated') & R().product.id.oneof(PRODUCTS)
rql &= R().events.updated.at.ge(start_day_of_prev_month)
rql &= R().events.updated.at.le(last_day_of_prev_month)
sub_terminated = (
client.ns('subscriptions')
.collection('assets')
.filter(rql)
.order_by("-events.created.at")
)
total_subscriptions = sub_active_suspended.count() + sub_terminated.count()
progress = 0

progress_callback(progress, total_subscriptions)

if renderer_type == 'csv':
yield HEADERS

for subscription in sub_active_suspended:
result = process_subscription(client, subscription)
for res in result:
if renderer_type == 'json':
yield {
HEADERS[idx].replace(' ', '_').lower(): value
for idx, value in enumerate(res)
}
else:
yield res
progress += 1
progress_callback(progress, total_subscriptions)

for subscription in sub_terminated:
result = process_subscription(client, subscription)
for res in result:
if renderer_type == 'json':
yield {
HEADERS[idx].replace(' ', '_').lower(): value
for idx, value in enumerate(res)
}
else:
yield res
progress += 1
progress_callback(progress, total_subscriptions)


def process_subscription(client, subscription):
param_values = get_product_specifics(subscription, client)
output = []
for item in subscription["items"]:
try:
if item["quantity"] == "0":
continue
output.append(
(
subscription["id"],
subscription["external_id"] if subscription["external_id"] else subscription["external_uid"],
subscription["status"].capitalize(),
convert_to_datetime(subscription["events"]["created"]["at"]),
convert_to_datetime(subscription["events"]["updated"]["at"]) if 'updated' in subscription['events'] else '-',
subscription["connection"]["type"].capitalize(),
subscription["contract"].get("type", "distribution").capitalize(),
subscription['product']['id'],
subscription['product']['name'],
str(subscription.get('billing', {}).get('period', {}).get('delta','')) + " " + subscription.get('billing', {}).get('period', {}).get('uom', 'Perpetual').capitalize(),
subscription.get('billing', {}).get('anniversary', {}).get('day', 'Perpetual'),
subscription.get('billing', {}).get('anniversary', {}).get('month', 'Perpetual'),
item['id'],
item['mpn'],
item['display_name'],
item['period'],
(
"unlimited" if item["quantity"] == -1 else item["quantity"]
),
subscription["tiers"]["customer"]["id"],
subscription["tiers"]["customer"]["name"],
(
subscription["tiers"]["customer"]["external_id"]
if "external_id" in subscription["tiers"]["customer"]
else subscription["tiers"]["customer"]["external_uid"]
),
subscription["tiers"]["customer"]["contact_info"]["country"].upper(),
subscription["tiers"]["tier1"]["id"],
subscription["tiers"]["tier1"]["name"],
(
subscription["tiers"]["tier1"]["external_id"]
if "external_id" in subscription["tiers"]["tier1"]
else subscription["tiers"]["tier1"]["external_uid"]
),
subscription["tiers"]["tier1"]["contact_info"]["country"].upper(),
(
subscription["tiers"]["tier2"]["id"]
if "tier2" in subscription["tiers"] and 'id' in subscription["tiers"]['tier2']
else "-"
),
(
subscription["tiers"]["tier2"]["name"]
if "tier2" in subscription["tiers"] and "name" in subscription["tiers"]["tier2"]
else "-"
),
(
subscription["tiers"]["tier2"]["external_id"]
if "tier2" in subscription["tiers"] and "external_id" in subscription["tiers"]["tier2"]
else "-"
),
(
subscription["tiers"]["tier2"]["contact_info"]["country"]
if "tier2" in subscription["tiers"] and "country" in subscription["tiers"]["tier2"]["contact_info"]
else "-"
),
subscription['connection']['provider']['id'] if 'provider' in subscription['connection'] else '-',
subscription['connection']['provider']['name'] if 'provider' in subscription['connection'] else '-',
subscription['marketplace']['id'],
subscription['marketplace']['name'],
subscription['connection']['vendor']['id'],
subscription['connection']['vendor']['name'],
param_values["microsoft_domain"],
param_values["subscription_id"],
param_values["ms_customer_id"],
param_values["microsoft_order_id"],
param_values["microsoft_plan_subscription_id"],
param_values["microsoft_tier1_mpn"],
)
)
except Exception as e:
print(f"Error in {subscription['id']}: {e}")
return output

def get_product_specifics(subscription, client):
values = {
"microsoft_domain": "-",
"subscription_id": "-",
"ms_customer_id": "-",
"microsoft_order_id": "-",
"microsoft_plan_subscription_id": "-",
"microsoft_tier1_mpn": "-",
}
if subscription["connection"]["vendor"]["id"] == "VA-888-104":
values["microsoft_domain"] = get_sub_parameter(subscription, "microsoft_domain")
sub_id = get_sub_parameter(subscription, "subscription_id")
if sub_id == "-":
sub_id = get_sub_parameter(subscription, "microsoft_subscription_id")
values["subscription_id"] = sub_id
cust_id = get_sub_parameter(subscription, "ms_customer_id")
if cust_id == "-":
cust_id = get_sub_parameter(subscription, "customer_id")
values["ms_customer_id"] = cust_id
order_id = get_sub_parameter(subscription, "microsoft_order_id")
if order_id == "-":
order_id = get_sub_parameter(subscription, "csp_order_id")
values["microsoft_plan_subscription_id"] = get_sub_parameter(
subscription,
"microsoft_plan_subscription_id",
)
values["microsoft_order_id"] = order_id
values["microsoft_tier1_mpn"] = get_param_mpn(subscription, client)
return values

def init_tc_cache(client):
for product in PRODUCTS:
TC_CACHE[product] = {}
init_product_tc_cache(product, client)
print(f'Length of cache for product {product} is {len(TC_CACHE[product])}')

def get_param_mpn(subscription, client):
if subscription['tiers']['tier1']['id'] in TC_CACHE[subscription['product']['id']]:
return TC_CACHE[subscription['product']['id']][subscription['tiers']['tier1']['id']]
mpn = get_sub_ta_parameter(subscription, 'tier1', 'tier1_mpn', client)
TC_CACHE[subscription['product']['id']][subscription['tiers']['tier1']['id']] = mpn
return mpn

def populate_ta_cache(parameters, client):
rql = R()
rql &= R().product.id.oneof(PRODUCTS)
if parameters.get('mkp') and parameters['mkp']['all'] is False:
rql &= R().marketplace.id.oneof(parameters['mkp']['choices'])
tcs = client.ns('tier').collection('configs').filter(rql)
for tc in tcs:
if tc['product']['id'] not in TC_CACHE:
TC_CACHE[tc['product']['id']] = {}
TC_CACHE[tc['product']['id']][tc['account']['id']] = get_param_value(tc['params'], 'tier1_mpn')

def get_param_value(params, param_id):
for param in params:
if param_id == param['id']:
return param['value'] if 'value' in param else '-'
return '-'

def init_product_tc_cache(product, client):
rql = R().product.id.eq(product) & R().status.eq('active') & R().tier_level.eq(1)
tcs = client.ns('tier').collection('configs').filter(rql).select(
'-configuration',
'-connection',
'-contract',
'-marketplace'
)
print(f'Obtaining {tcs.count()} for product {product}')
for tc in tcs:
mpn = get_param_value(tc['params'], 'tier1_mpn')
TC_CACHE[product][tc['account']['id']] = mpn


def populate_products(client):
rql = R().owner.id.eq(VENDOR)
rql2 = R().visibility.listing.eq(True) | R().visibility.syndication.eq(True)
rql &= rql2
products = client.products.filter(rql)
for product in products:
PRODUCTS.append(product['id'])
# Empty print due CLI Execution
print("")
print(f"Amount of products from microsoft to include in report {len(PRODUCTS)}")
8 changes: 8 additions & 0 deletions reports/ms_cooper/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Microsoft Products transactions report

This report provides an overview of all requests and billing requests for a given range of dates.
Report includes:
* All syndicated and direct transactions
* Microsoft products specific data

**IMPORTANT**: Select the Microsoft products only
Binary file added reports/ms_cooper/template.xlsx
Binary file not shown.
21 changes: 21 additions & 0 deletions reports/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def get_parameter(request, param_id):
return param["value"]
return "-"

def get_sub_parameter(subscription, param_id):
for param in subscription["params"]:
if param["id"] == param_id:
return param.get('value', '-')
return "-"


def get_asset_parameter(asset, param_id):
for param in asset["params"]:
Expand All @@ -78,6 +84,21 @@ def get_ta_parameter(request, tier, param_id, client):
pass
return "-"

def get_sub_ta_parameter(subscription, tier, param_id, client):
try:
rql = R().configuration.account.id.eq(subscription['tiers'][tier]['id'])
rql &= R().status.eq('approved')
rql &= R().product.id.eq(subscription['product']['id'])
tc = client.ns('tier').collection("config-requests").filter(rql).order_by('-created').first()
if 'params' not in tc:
return "-"
for param in tc['params']:
if param["id"] == param_id:
return param["value"]
except Exception:
pass
return "-"


def today_str():
return datetime.today().strftime('%Y-%m-%d %H:%M:%S')
Expand Down

0 comments on commit 17b9c02

Please sign in to comment.