Permalink
Browse files

And finally, we support history messages.

  • Loading branch information...
1 parent 3a66765 commit a17544240852c315ea83b7f12da75480db3a887f Greg Taylor committed Apr 12, 2012
View
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+"""
+A fake history upload script, used to manually test the whole stack.
+"""
+import simplejson
+import requests
+
+data = """
+{
+ "resultType" : "history",
+ "version" : "0.1alpha",
+ "uploadKeys" : [
+ { "name" : "emk", "key" : "abc" },
+ { "name" : "ec" , "key" : "def" }
+ ],
+ "generator" : { "name" : "Yapeal", "version" : "11.335.1737" },
+ "currentTime" : "2011-10-22T15:46:00+00:00",
+ "columns" : ["date","orders","quantity","low","high","average"],
+ "rowsets" : [
+ {
+ "generatedAt" : "2011-10-22T15:42:00+00:00",
+ "regionID" : 10000065,
+ "typeID" : 11134,
+ "rows" : [
+ ["2011-12-03T00:00:00+00:00",40,40,1999,499999.99,35223.50],
+ ["2011-12-02T00:00:00+00:00",83,252,9999,11550,11550]
+ ]
+ }
+ ]
+}
+"""
+data = simplejson.loads(data)
+data = simplejson.dumps(data)
+
+r = requests.post(
+ 'http://localhost:8080/api/market-order/upload/unified/',
+ data=data,
+)
+print "RESPONSE"
+print r.text
@@ -1,6 +1,6 @@
#!/usr/bin/env python
"""
-A fake upload script, used to manually test the whole stack.
+A fake order upload script, used to manually test the whole stack.
"""
import simplejson
import requests
View
@@ -180,6 +180,25 @@ def __init__(self, upload_keys=None, history_generator=None,
else:
self.history_generator = history_generator
+ def __repr__(self):
+ """
+ Basic string representation of the history.
+ """
+ template = Template(
+ "<MarketHistory: \n"
+ " upload_keys: $upload_keys\n"
+ " order_generator: $order_generator\n"
+ )
+ list_repr = template.substitute(
+ upload_keys = self.upload_keys,
+ order_generator = self.history_generator,
+ )
+ for history_entry_list in self._history.values():
+ for entry in history_entry_list:
+ list_repr += repr(entry)
+
+ return list_repr
+
def add_entry(self, entry):
"""
Adds a MarketHistoryEntry instance to the list of market history entries
@@ -202,21 +221,25 @@ class MarketHistoryEntry(object):
Represents a single point of market history data.
"""
def __init__(self, type_id, region_id, historical_date, num_orders,
- low_price, high_price, average_price, total_quantity):
+ low_price, high_price, average_price, total_quantity,
+ generated_at):
self.type_id = int(type_id)
if region_id:
self.region_id = int(region_id)
else:
# Client lacked the data for result rows.
self.region_id = None
if not isinstance(historical_date, datetime.datetime):
- raise TypeError('historical_date should be a datetime.')
+ raise TypeError('historical_date should be a datetime, not %s.' % type(historical_date))
self.historical_date = historical_date
self.num_orders = int(num_orders)
self.low_price = float(low_price)
self.high_price = float(high_price)
self.average_price = float(average_price)
self.total_quantity = int(total_quantity)
+ if not isinstance(generated_at, datetime.datetime):
+ raise TypeError('generated_at should be a datetime.')
+ self.generated_at = generated_at
def __repr__(self):
"""
@@ -232,6 +255,7 @@ def __repr__(self):
" high_price: $high_price\n"
" average_price: $average_price\n"
" total_quantity: $total_quantity\n"
+ " generated_at: $generated_at\n"
)
return template.substitute(
type_id = self.type_id,
@@ -242,4 +266,5 @@ def __repr__(self):
high_price = self.high_price,
average_price = self.average_price,
total_quantity = self.total_quantity,
+ generated_at = self.generated_at,
)
@@ -1,7 +1,7 @@
import unittest
import datetime
-from emdr.core.market_data import MarketOrder, MarketOrderList
-from emdr.core.serialization.unified import orders as unified_order
+from emdr.core.market_data import MarketOrder, MarketOrderList, MarketHistory, MarketHistoryEntry
+from emdr.core.serialization import unified
class BaseSerializationCase(unittest.TestCase):
@@ -42,27 +42,71 @@ def setUp(self):
)
self.order_list.add_order(self.order2)
+ self.history = MarketHistory()
+ self.history1 = MarketHistoryEntry(
+ type_id=2413387906,
+ region_id=10000068,
+ historical_date=datetime.datetime.now(),
+ num_orders=5,
+ low_price=5.0,
+ high_price=10.5,
+ average_price=7.0,
+ total_quantity=200,
+ generated_at=datetime.datetime.now(),
+ )
+ self.history.add_entry(self.history1)
+ self.history2 = MarketHistoryEntry(
+ type_id=1413387203,
+ region_id=10000067,
+ historical_date=datetime.datetime.now(),
+ num_orders=50,
+ low_price=50.0,
+ high_price=100.5,
+ average_price=70.0,
+ total_quantity=2000,
+ generated_at=datetime.datetime.now(),
+ )
+ self.history.add_entry(self.history2)
+
class UnifiedSerializationTests(BaseSerializationCase):
"""
Tests for serializing and de-serializing orders in Unified format.
"""
- def test_serialization(self):
+ def test_order_serialization(self):
# Encode the sample order list.
- encoded_orderlist = unified_order.encode_to_json(self.order_list)
+ encoded_orderlist = unified.encode_to_json(self.order_list)
# Should return a string JSON representation.
self.assertIsInstance(encoded_orderlist, basestring)
# De-code the JSON to instantiate a list of MarketOrder instances that
# should be identical to self.orderlist.
- decoded_list = unified_order.parse_from_json(encoded_orderlist)
- re_encoded_list = unified_order.encode_to_json(decoded_list)
+ decoded_list = unified.parse_from_json(encoded_orderlist)
+ self.assertIsInstance(decoded_list, MarketOrderList)
+ re_encoded_list = unified.encode_to_json(decoded_list)
# Re-encode the decoded orderlist. Match the two encoded strings. They
# should still be the same.
self.assertEqual(
encoded_orderlist,
re_encoded_list,
)
+ def test_history_serialization(self):
+ # Encode the sample history instance.
+ encoded_history = unified.encode_to_json(self.history)
+ # Should return a string JSON representation.
+ self.assertIsInstance(encoded_history, basestring)
+ # De-code the JSON to instantiate a MarketHistory instances that
+ # should be identical to self.orderlist.
+ decoded_list = unified.parse_from_json(encoded_history)
+ self.assertIsInstance(decoded_list, MarketHistory)
+ re_encoded_history = unified.encode_to_json(decoded_list)
+ # Re-encode the decoded history. Match the two encoded strings. They
+ # should still be the same.
+ self.assertEqual(
+ encoded_history,
+ re_encoded_history,
+ )
+
class EveMarketeerSerializationTests(BaseSerializationCase):
"""
Tests for serializing and de-serializing orders in EVE Marketeer format.
@@ -0,0 +1,36 @@
+import simplejson
+from emdr.core.market_data import MarketHistory
+from emdr.core.market_data import MarketOrderList
+from emdr.core.serialization.unified import history, orders
+
+def parse_from_json(json_str):
+ """
+ Given a Unified Uploader message, parse the contents and return a
+ MarketOrderList or MarketHistory instance.
+
+ :param str json_str: A Unified Uploader message as a JSON string.
+ :rtype: MarketOrderList or MarketHistory
+ """
+ job_dict = simplejson.loads(json_str)
+
+ if job_dict['resultType'] == 'orders':
+ return orders.parse_from_dict(job_dict)
+ else:
+ return history.parse_from_dict(job_dict)
+
+def encode_to_json(order_or_history):
+ """
+ Given an order or history entry, encode it to JSON and return.
+
+ :type order_or_history: MarketOrderList or MarketHistory
+ :param order_or_history: A MarketOrderList or MarketHistory instance to
+ encode to JSON.
+ :rtype: str
+ :return: The encoded JSON string.
+ """
+ if isinstance(order_or_history, MarketOrderList):
+ return orders.encode_to_json(order_or_history)
+ elif isinstance(order_or_history, MarketHistory):
+ return history.encode_to_json(order_or_history)
+ else:
+ raise Exception("Must be one of MarketOrderList or MarketHistory.")
@@ -0,0 +1,113 @@
+"""
+Parser for the Unified uploader format market history.
+"""
+import logging
+import datetime
+import simplejson
+from emdr.core.market_data import MarketHistory, MarketHistoryEntry
+from emdr.core.serialization.unified.unified_utils import _columns_to_kwargs
+from emdr.core.serialization.unified.unified_utils import parse_iso8601_str
+
+logger = logging.getLogger(__name__)
+
+# This is the standard list of columns to return data in for encoding.
+STANDARD_ENCODED_COLUMNS = [
+ 'date', 'orders', 'quantity', 'low', 'high', 'average',
+]
+
+# This is a dict that acts like a mapping table, with the key being the
+# Unified uploader format field name, and the value being the corresponding
+# kwarg to the MarketOrder class. This lets us instantiate the class directly
+# from the data.
+SPEC_TO_KWARG_CONVERSION = {
+ 'date': 'historical_date',
+ 'orders': 'num_orders',
+ 'low': 'low_price',
+ 'high': 'high_price',
+ 'average': 'average_price',
+ 'quantity': 'total_quantity',
+}
+
+def parse_from_dict(json_dict):
+ """
+ Given a Unified Uploader message, parse the contents and return a
+ MarketHistory instance.
+
+ :param dict json_dict: A Unified Uploader message as a dict.
+ :rtype: MarketOrderList
+ :returns: An instance of MarketOrderList, containing the orders
+ within.
+ """
+ history_columns = json_dict['columns']
+
+ history = MarketHistory(
+ upload_keys=json_dict['uploadKeys'],
+ order_generator=json_dict['generator'],
+ )
+
+ for rowset in json_dict['rowsets']:
+ generated_at = parse_iso8601_str(rowset['generatedAt'])
+ region_id = rowset['regionID']
+ type_id = rowset['typeID']
+
+ for row in rowset['rows']:
+ history_kwargs = _columns_to_kwargs(
+ SPEC_TO_KWARG_CONVERSION, history_columns, row)
+ historical_date = parse_iso8601_str(history_kwargs['historical_date'])
+
+ history_kwargs.update({
+ 'type_id': type_id,
+ 'region_id': region_id,
+ 'historical_date': historical_date,
+ 'generated_at': generated_at,
+ })
+
+ history.add_entry(MarketHistoryEntry(**history_kwargs))
+
+ return history
+
+def encode_to_json(history_list):
+ """
+ Encodes this MarketHistory instance to a JSON string.
+
+ :param MarketHistory history_list: The history instance to serialize.
+ :rtype: str
+ """
+ rowsets = []
+ for key, history_entries in history_list._history.items():
+ rows = []
+ for entry in history_entries:
+ historical_date = entry.historical_date.replace(microsecond=0).isoformat()
+
+ # The order in which these values are added is crucial. It must
+ # match STANDARD_ENCODED_COLUMNS.
+ rows.append([
+ historical_date,
+ entry.num_orders,
+ entry.total_quantity,
+ entry.low_price,
+ entry.high_price,
+ entry.average_price,
+ ])
+
+ #noinspection PyUnresolvedReferences
+ rowsets.append(dict(
+ generatedAt = datetime.datetime.now().replace(microsecond=0).isoformat(),
+ regionID = history_entries[0].region_id,
+ typeID = history_entries[0].type_id,
+ rows = rows,
+ ))
+
+ json_dict = {
+ 'resultType': history_list.result_type,
+ 'version': history_list.version,
+ 'uploadKeys': history_list.upload_keys,
+ 'generator': history_list.history_generator,
+ 'currentTime': datetime.datetime.now().replace(microsecond=0).isoformat(),
+ # This must match the order of the values in the row assembling portion
+ # above this.
+ 'columns': STANDARD_ENCODED_COLUMNS,
+ 'rowsets': rowsets,
+ }
+
+ return simplejson.dumps(json_dict, indent=4 * ' ')
Oops, something went wrong.

0 comments on commit a175442

Please sign in to comment.