untangle rates from exporters

Change-Id: I3aa000ab0d40ba7b7305b85b297c95714434d196
This commit is contained in:
Chris Forbes 2014-03-27 13:14:40 +13:00
parent 38e990663b
commit dd425ef89c
6 changed files with 22 additions and 44 deletions

View File

@ -1,6 +1,7 @@
import flask import flask
from flask import Flask, Blueprint from flask import Flask, Blueprint
from artifice import interface, database from artifice import interface, database
from artifice.sales_order import RatesFile
from artifice.models import UsageEntry, SalesOrder, Tenant, billing from artifice.models import UsageEntry, SalesOrder, Tenant, billing
import sqlalchemy import sqlalchemy
from sqlalchemy import create_engine, func from sqlalchemy import create_engine, func
@ -170,7 +171,7 @@ def run_usage_collection():
return json.dumps(resp) return json.dumps(resp)
def generate_sales_order(tenant, session, end): def generate_sales_order(tenant, session, end, rates):
db = database.Database(session) db = database.Database(session)
session.begin() session.begin()
@ -196,7 +197,7 @@ def generate_sales_order(tenant, session, end):
# and will probably result in the CSV exporter being changed. # and will probably result in the CSV exporter being changed.
billable = billing.build_billable(usage, session) billable = billing.build_billable(usage, session)
session.close() session.close()
exporter = invoicer(start, end, config["export_config"]) exporter = invoicer(start, end, config["export_config"], rates)
exporter.bill(billable) exporter.bill(billable)
exporter.close() exporter.close()
return {"id": tenant.id, return {"id": tenant.id,
@ -262,9 +263,10 @@ def run_sales_order_generation():
# Handled like this for a later move to Celery distributed workers # Handled like this for a later move to Celery distributed workers
resp = {"tenants": []} resp = {"tenants": []}
rates = RatesFile(config['export_config'])
for tenant in tenant_query: for tenant in tenant_query:
resp['tenants'].append(generate_sales_order(tenant, session, end)) resp['tenants'].append(generate_sales_order(tenant, session, end, rates))
return 200, resp return 200, resp

View File

@ -2,8 +2,7 @@ import requests
from decimal import Decimal from decimal import Decimal
class ClerkRatesMixin(object): class ClerkRatesSource(object):
def rate(self, name, loc_name): def rate(self, name, loc_name):
url = "http://10.5.36.32/" url = "http://10.5.36.32/"
url = (url + "regions/" + loc_name + url = (url + "regions/" + loc_name +

View File

@ -5,13 +5,14 @@ from artifice import sales_order
from decimal import * from decimal import *
class Csv(sales_order.RatesFileMixin, sales_order.SalesOrder): class Csv(sales_order.SalesOrder):
def __init__(self, start, end, config): def __init__(self, start, end, config, rates):
super(Csv, self).__init__(start, end, config) super(Csv, self).__init__(start, end, config, rates)
self.lines = {} self.lines = {}
self.total = Decimal(0.0) self.total = Decimal(0.0)
self.tenant = None self.tenant = None
self.rates = rates
def _bill(self, tenant): def _bill(self, tenant):
"""Generates the lines for a sales order for the tenant.""" """Generates the lines for a sales order for the tenant."""
@ -49,7 +50,7 @@ class Csv(sales_order.RatesFileMixin, sales_order.SalesOrder):
# GET REGION FROM CONFIG: # GET REGION FROM CONFIG:
region = 'wellington' region = 'wellington'
rate = self.rate(service.name, region) rate = self.rates.rate(service.name, region)
cost = usage * rate cost = usage * rate
total_cost += cost total_cost += cost
appendee.append(service.name) appendee.append(service.name)

View File

@ -18,7 +18,7 @@ class SalesOrder(object):
# __metaclass__ = requirements # __metaclass__ = requirements
def __init__(self, start, end, config): def __init__(self, start, end, config, rates):
self.start = start self.start = start
self.end = end self.end = end
self.config = config self.config = config
@ -33,10 +33,12 @@ class SalesOrder(object):
raise NotImplementedError("Not implemented in base class") raise NotImplementedError("Not implemented in base class")
class RatesFileMixin(object): class RatesFile(object):
# Mixin # Mixin
# Adds a rates file loader, expecting various things from the # Adds a rates file loader, expecting various things from the
# configuration # configuration
def __init__(self, config):
self.config = config
def rate(self, name, region=None): def rate(self, name, region=None):
try: try:

View File

@ -2,7 +2,6 @@ from artifice import sales_order
class MockExporter(sales_order.SalesOrder): class MockExporter(sales_order.SalesOrder):
def _bill(self, tenant): def _bill(self, tenant):
pass pass

View File

@ -4,6 +4,7 @@ from artifice.plugins import csv_exporter
from decimal import Decimal from decimal import Decimal
import csv import csv
import os import os
import mock
config = { config = {
@ -27,44 +28,18 @@ class TestCSVExporter(test_interface.TestInterface):
tenant = helpers.build_billable(numb_resources, volume) tenant = helpers.build_billable(numb_resources, volume)
self.tenant = tenant self.tenant = tenant
# mock rates provider that just yields 1.0 for everything.
rates = mock.Mock()
rates.rate.return_value = Decimal(1.0)
sales_order = csv_exporter.Csv(self.start, self.end, sales_order = csv_exporter.Csv(self.start, self.end,
csv_config) csv_config, rates)
sales_order.bill(tenant) sales_order.bill(tenant)
self.filename = sales_order.filename self.filename = sales_order.filename
sales_order.close() sales_order.close()
def get_rate(self, service): def get_rate(self, service):
try: return Decimal(1.0);
self.__rates
except AttributeError:
self.__rates = {}
if not self.__rates:
self.__rates = {}
try:
fh = open(config["rates"]["file"])
# Makes no opinions on the file structure
reader = csv.reader(fh, delimiter="|")
for row in reader:
# The default layout is expected to be:
# location | rate name | rate measurement | rate value
self.__rates[row[1].strip()] = {
"cost": Decimal(row[3].strip()),
"region": row[0].strip(),
"measures": row[2].strip()
}
if not self.__rates:
raise IndexError("malformed rates CSV!")
fh.close()
except KeyError:
# couldn't actually find the useful info for rateS?
print "Couldn't find rates info configuration option!"
raise
except IndexError:
raise IndexError("Malformed rates CSV!")
except IOError:
print "Couldn't open the file!"
raise
return self.__rates[service]["cost"] # ignore the regions-ness for now
def test_generate_csv(self): def test_generate_csv(self):
"""Generates a CSV, checks that: """Generates a CSV, checks that: