hive-nectar 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hive-nectar might be problematic. Click here for more details.

Files changed (86) hide show
  1. hive_nectar-0.0.2.dist-info/METADATA +182 -0
  2. hive_nectar-0.0.2.dist-info/RECORD +86 -0
  3. hive_nectar-0.0.2.dist-info/WHEEL +4 -0
  4. hive_nectar-0.0.2.dist-info/entry_points.txt +2 -0
  5. hive_nectar-0.0.2.dist-info/licenses/LICENSE.txt +23 -0
  6. nectar/__init__.py +32 -0
  7. nectar/account.py +4371 -0
  8. nectar/amount.py +475 -0
  9. nectar/asciichart.py +270 -0
  10. nectar/asset.py +82 -0
  11. nectar/block.py +446 -0
  12. nectar/blockchain.py +1178 -0
  13. nectar/blockchaininstance.py +2284 -0
  14. nectar/blockchainobject.py +221 -0
  15. nectar/blurt.py +563 -0
  16. nectar/cli.py +6285 -0
  17. nectar/comment.py +1217 -0
  18. nectar/community.py +513 -0
  19. nectar/constants.py +111 -0
  20. nectar/conveyor.py +309 -0
  21. nectar/discussions.py +1709 -0
  22. nectar/exceptions.py +149 -0
  23. nectar/hive.py +546 -0
  24. nectar/hivesigner.py +420 -0
  25. nectar/imageuploader.py +72 -0
  26. nectar/instance.py +129 -0
  27. nectar/market.py +1013 -0
  28. nectar/memo.py +449 -0
  29. nectar/message.py +357 -0
  30. nectar/nodelist.py +444 -0
  31. nectar/price.py +557 -0
  32. nectar/profile.py +65 -0
  33. nectar/rc.py +308 -0
  34. nectar/snapshot.py +726 -0
  35. nectar/steem.py +582 -0
  36. nectar/storage.py +53 -0
  37. nectar/transactionbuilder.py +622 -0
  38. nectar/utils.py +545 -0
  39. nectar/version.py +2 -0
  40. nectar/vote.py +557 -0
  41. nectar/wallet.py +472 -0
  42. nectar/witness.py +617 -0
  43. nectarapi/__init__.py +11 -0
  44. nectarapi/exceptions.py +123 -0
  45. nectarapi/graphenerpc.py +589 -0
  46. nectarapi/node.py +178 -0
  47. nectarapi/noderpc.py +229 -0
  48. nectarapi/rpcutils.py +97 -0
  49. nectarapi/version.py +2 -0
  50. nectarbase/__init__.py +14 -0
  51. nectarbase/ledgertransactions.py +75 -0
  52. nectarbase/memo.py +243 -0
  53. nectarbase/objects.py +429 -0
  54. nectarbase/objecttypes.py +22 -0
  55. nectarbase/operationids.py +102 -0
  56. nectarbase/operations.py +1297 -0
  57. nectarbase/signedtransactions.py +48 -0
  58. nectarbase/transactions.py +11 -0
  59. nectarbase/version.py +2 -0
  60. nectargrapheneapi/__init__.py +6 -0
  61. nectargraphenebase/__init__.py +27 -0
  62. nectargraphenebase/account.py +846 -0
  63. nectargraphenebase/aes.py +52 -0
  64. nectargraphenebase/base58.py +192 -0
  65. nectargraphenebase/bip32.py +494 -0
  66. nectargraphenebase/bip38.py +134 -0
  67. nectargraphenebase/chains.py +149 -0
  68. nectargraphenebase/dictionary.py +3 -0
  69. nectargraphenebase/ecdsasig.py +326 -0
  70. nectargraphenebase/objects.py +123 -0
  71. nectargraphenebase/objecttypes.py +6 -0
  72. nectargraphenebase/operationids.py +3 -0
  73. nectargraphenebase/operations.py +23 -0
  74. nectargraphenebase/prefix.py +11 -0
  75. nectargraphenebase/py23.py +38 -0
  76. nectargraphenebase/signedtransactions.py +201 -0
  77. nectargraphenebase/types.py +419 -0
  78. nectargraphenebase/unsignedtransactions.py +283 -0
  79. nectargraphenebase/version.py +2 -0
  80. nectarstorage/__init__.py +38 -0
  81. nectarstorage/base.py +306 -0
  82. nectarstorage/exceptions.py +16 -0
  83. nectarstorage/interfaces.py +237 -0
  84. nectarstorage/masterpassword.py +239 -0
  85. nectarstorage/ram.py +30 -0
  86. nectarstorage/sqlite.py +334 -0
nectar/price.py ADDED
@@ -0,0 +1,557 @@
1
+ # -*- coding: utf-8 -*-
2
+ from decimal import Decimal
3
+ from fractions import Fraction
4
+
5
+ from nectar.instance import shared_blockchain_instance
6
+ from nectargraphenebase.py23 import integer_types, string_types
7
+
8
+ from .amount import Amount
9
+ from .asset import Asset
10
+ from .exceptions import InvalidAssetException
11
+ from .utils import assets_from_string, formatTimeString
12
+
13
+
14
+ def check_asset(other, self, stm):
15
+ if isinstance(other, dict) and "asset" in other and isinstance(self, dict) and "asset" in self:
16
+ if not Asset(other["asset"], blockchain_instance=stm) == Asset(
17
+ self["asset"], blockchain_instance=stm
18
+ ):
19
+ raise AssertionError()
20
+ else:
21
+ if not other == self:
22
+ raise AssertionError()
23
+
24
+
25
+ class Price(dict):
26
+ """This class deals with all sorts of prices of any pair of assets to
27
+ simplify dealing with the tuple::
28
+
29
+ (quote, base)
30
+
31
+ each being an instance of :class:`nectar.amount.Amount`. The
32
+ amount themselves define the price.
33
+
34
+ .. note::
35
+
36
+ The price (floating) is derived as ``base/quote``
37
+
38
+ :param list args: Allows to deal with different representations of a price
39
+ :param Asset base: Base asset
40
+ :param Asset quote: Quote asset
41
+ :param Steem blockchain_instance: Steem instance
42
+ :returns: All data required to represent a price
43
+ :rtype: dictionary
44
+
45
+ Way to obtain a proper instance:
46
+
47
+ * ``args`` is a str with a price and two assets
48
+ * ``args`` can be a floating number and ``base`` and ``quote`` being instances of :class:`nectar.asset.Asset`
49
+ * ``args`` can be a floating number and ``base`` and ``quote`` being instances of ``str``
50
+ * ``args`` can be dict with keys ``price``, ``base``, and ``quote`` (*graphene balances*)
51
+ * ``args`` can be dict with keys ``base`` and ``quote``
52
+ * ``args`` can be dict with key ``receives`` (filled orders)
53
+ * ``args`` being a list of ``[quote, base]`` both being instances of :class:`nectar.amount.Amount`
54
+ * ``args`` being a list of ``[quote, base]`` both being instances of ``str`` (``amount symbol``)
55
+ * ``base`` and ``quote`` being instances of :class:`nectar.asset.Amount`
56
+
57
+ This allows instanciations like:
58
+
59
+ * ``Price("0.315 SBD/STEEM")``
60
+ * ``Price(0.315, base="SBD", quote="STEEM")``
61
+ * ``Price(0.315, base=Asset("SBD"), quote=Asset("STEEM"))``
62
+ * ``Price({"base": {"amount": 1, "asset_id": "SBD"}, "quote": {"amount": 10, "asset_id": "SBD"}})``
63
+ * ``Price(quote="10 STEEM", base="1 SBD")``
64
+ * ``Price("10 STEEM", "1 SBD")``
65
+ * ``Price(Amount("10 STEEM"), Amount("1 SBD"))``
66
+ * ``Price(1.0, "SBD/STEEM")``
67
+
68
+ Instances of this class can be used in regular mathematical expressions
69
+ (``+-*/%``) such as:
70
+
71
+ .. code-block:: python
72
+
73
+ >>> from nectar.price import Price
74
+ >>> from nectar import Steem
75
+ >>> stm = Steem("https://api.steemit.com")
76
+ >>> Price("0.3314 SBD/STEEM", blockchain_instance=stm) * 2
77
+ 0.662804 SBD/STEEM
78
+ >>> Price(0.3314, "SBD", "STEEM", blockchain_instance=stm)
79
+ 0.331402 SBD/STEEM
80
+
81
+ """
82
+
83
+ def __init__(
84
+ self,
85
+ price=None,
86
+ base=None,
87
+ quote=None,
88
+ base_asset=None, # to identify sell/buy
89
+ blockchain_instance=None,
90
+ **kwargs,
91
+ ):
92
+ if blockchain_instance is None:
93
+ if kwargs.get("steem_instance"):
94
+ blockchain_instance = kwargs["steem_instance"]
95
+ elif kwargs.get("hive_instance"):
96
+ blockchain_instance = kwargs["hive_instance"]
97
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
98
+ if price == "":
99
+ price = None
100
+ if price is not None and isinstance(price, string_types) and not base and not quote:
101
+ price, assets = price.split(" ")
102
+ base_symbol, quote_symbol = assets_from_string(assets)
103
+ base = Asset(base_symbol, blockchain_instance=self.blockchain)
104
+ quote = Asset(quote_symbol, blockchain_instance=self.blockchain)
105
+ frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
106
+ self["quote"] = Amount(
107
+ amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
108
+ )
109
+ self["base"] = Amount(
110
+ amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
111
+ )
112
+
113
+ elif price is not None and isinstance(price, dict) and "base" in price and "quote" in price:
114
+ if "price" in price:
115
+ raise AssertionError("You cannot provide a 'price' this way")
116
+ # Regular 'price' objects according to steem-core
117
+ # base_id = price["base"]["asset_id"]
118
+ # if price["base"]["asset_id"] == base_id:
119
+ self["base"] = Amount(price["base"], blockchain_instance=self.blockchain)
120
+ self["quote"] = Amount(price["quote"], blockchain_instance=self.blockchain)
121
+ # else:
122
+ # self["quote"] = Amount(price["base"], blockchain_instance=self.blockchain)
123
+ # self["base"] = Amount(price["quote"], blockchain_instance=self.blockchain)
124
+
125
+ elif price is not None and isinstance(base, Asset) and isinstance(quote, Asset):
126
+ frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
127
+ self["quote"] = Amount(
128
+ amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
129
+ )
130
+ self["base"] = Amount(
131
+ amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
132
+ )
133
+
134
+ elif (
135
+ price is not None and isinstance(base, string_types) and isinstance(quote, string_types)
136
+ ):
137
+ base = Asset(base, blockchain_instance=self.blockchain)
138
+ quote = Asset(quote, blockchain_instance=self.blockchain)
139
+ frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
140
+ self["quote"] = Amount(
141
+ amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
142
+ )
143
+ self["base"] = Amount(
144
+ amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
145
+ )
146
+
147
+ elif price is None and isinstance(base, string_types) and isinstance(quote, string_types):
148
+ self["quote"] = Amount(quote, blockchain_instance=self.blockchain)
149
+ self["base"] = Amount(base, blockchain_instance=self.blockchain)
150
+ elif (
151
+ price is not None and isinstance(price, string_types) and isinstance(base, string_types)
152
+ ):
153
+ self["quote"] = Amount(price, blockchain_instance=self.blockchain)
154
+ self["base"] = Amount(base, blockchain_instance=self.blockchain)
155
+ # len(args) > 1
156
+
157
+ elif isinstance(price, Amount) and isinstance(base, Amount):
158
+ self["quote"], self["base"] = price, base
159
+
160
+ # len(args) == 0
161
+ elif price is None and isinstance(base, Amount) and isinstance(quote, Amount):
162
+ self["quote"] = quote
163
+ self["base"] = base
164
+
165
+ elif (
166
+ isinstance(price, float)
167
+ or isinstance(price, integer_types)
168
+ or isinstance(price, Decimal)
169
+ ) and isinstance(base, string_types):
170
+ base_symbol, quote_symbol = assets_from_string(base)
171
+ base = Asset(base_symbol, blockchain_instance=self.blockchain)
172
+ quote = Asset(quote_symbol, blockchain_instance=self.blockchain)
173
+ frac = Fraction(float(price)).limit_denominator(10 ** base["precision"])
174
+ self["quote"] = Amount(
175
+ amount=frac.denominator, asset=quote, blockchain_instance=self.blockchain
176
+ )
177
+ self["base"] = Amount(
178
+ amount=frac.numerator, asset=base, blockchain_instance=self.blockchain
179
+ )
180
+
181
+ else:
182
+ raise ValueError("Couldn't parse 'Price'.")
183
+
184
+ def __setitem__(self, key, value):
185
+ dict.__setitem__(self, key, value)
186
+ if (
187
+ "quote" in self and "base" in self and self["base"] and self["quote"]
188
+ ): # don't derive price for deleted Orders
189
+ dict.__setitem__(
190
+ self, "price", self._safedivide(self["base"]["amount"], self["quote"]["amount"])
191
+ )
192
+
193
+ def copy(self):
194
+ return Price(
195
+ None,
196
+ base=self["base"].copy(),
197
+ quote=self["quote"].copy(),
198
+ blockchain_instance=self.blockchain,
199
+ )
200
+
201
+ def _safedivide(self, a, b):
202
+ if b != 0.0:
203
+ return a / b
204
+ else:
205
+ return float("Inf")
206
+
207
+ def symbols(self):
208
+ return self["base"]["symbol"], self["quote"]["symbol"]
209
+
210
+ def as_base(self, base):
211
+ """Returns the price instance so that the base asset is ``base``.
212
+
213
+ .. note:: This makes a copy of the object!
214
+
215
+ .. code-block:: python
216
+
217
+ >>> from nectar.price import Price
218
+ >>> from nectar import Steem
219
+ >>> stm = Steem("https://api.steemit.com")
220
+ >>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).as_base("STEEM")
221
+ 3.017483 STEEM/SBD
222
+
223
+ """
224
+ if base == self["base"]["symbol"]:
225
+ return self.copy()
226
+ elif base == self["quote"]["symbol"]:
227
+ return self.copy().invert()
228
+ else:
229
+ raise InvalidAssetException
230
+
231
+ def as_quote(self, quote):
232
+ """Returns the price instance so that the quote asset is ``quote``.
233
+
234
+ .. note:: This makes a copy of the object!
235
+
236
+ .. code-block:: python
237
+
238
+ >>> from nectar.price import Price
239
+ >>> from nectar import Steem
240
+ >>> stm = Steem("https://api.steemit.com")
241
+ >>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).as_quote("SBD")
242
+ 3.017483 STEEM/SBD
243
+
244
+ """
245
+ if quote == self["quote"]["symbol"]:
246
+ return self.copy()
247
+ elif quote == self["base"]["symbol"]:
248
+ return self.copy().invert()
249
+ else:
250
+ raise InvalidAssetException
251
+
252
+ def invert(self):
253
+ """Invert the price (e.g. go from ``SBD/STEEM`` into ``STEEM/SBD``)
254
+
255
+ .. code-block:: python
256
+
257
+ >>> from nectar.price import Price
258
+ >>> from nectar import Steem
259
+ >>> stm = Steem("https://api.steemit.com")
260
+ >>> Price("0.3314 SBD/STEEM", blockchain_instance=stm).invert()
261
+ 3.017483 STEEM/SBD
262
+
263
+ """
264
+ tmp = self["quote"]
265
+ self["quote"] = self["base"]
266
+ self["base"] = tmp
267
+ return self
268
+
269
+ def json(self):
270
+ return {"base": self["base"].json(), "quote": self["quote"].json()}
271
+
272
+ def __repr__(self):
273
+ return "{price:.{precision}f} {base}/{quote}".format(
274
+ price=self["price"],
275
+ base=self["base"]["symbol"],
276
+ quote=self["quote"]["symbol"],
277
+ precision=(self["base"]["asset"]["precision"] + self["quote"]["asset"]["precision"]),
278
+ )
279
+
280
+ def __float__(self):
281
+ return float(self["price"])
282
+
283
+ def _check_other(self, other):
284
+ if not other["base"]["symbol"] == self["base"]["symbol"]:
285
+ raise AssertionError()
286
+ if not other["quote"]["symbol"] == self["quote"]["symbol"]:
287
+ raise AssertionError()
288
+
289
+ def __mul__(self, other):
290
+ a = self.copy()
291
+ if isinstance(other, Price):
292
+ # Rotate/invert other
293
+ if (
294
+ self["quote"]["symbol"] not in other.symbols()
295
+ and self["base"]["symbol"] not in other.symbols()
296
+ ):
297
+ raise InvalidAssetException
298
+
299
+ # base/quote = a/b
300
+ # a/b * b/c = a/c
301
+ a = self.copy()
302
+ if self["quote"]["symbol"] == other["base"]["symbol"]:
303
+ a["base"] = Amount(
304
+ float(self["base"]) * float(other["base"]),
305
+ self["base"]["symbol"],
306
+ blockchain_instance=self.blockchain,
307
+ )
308
+ a["quote"] = Amount(
309
+ float(self["quote"]) * float(other["quote"]),
310
+ other["quote"]["symbol"],
311
+ blockchain_instance=self.blockchain,
312
+ )
313
+ # a/b * c/a = c/b
314
+ elif self["base"]["symbol"] == other["quote"]["symbol"]:
315
+ a["base"] = Amount(
316
+ float(self["base"]) * float(other["base"]),
317
+ other["base"]["symbol"],
318
+ blockchain_instance=self.blockchain,
319
+ )
320
+ a["quote"] = Amount(
321
+ float(self["quote"]) * float(other["quote"]),
322
+ self["quote"]["symbol"],
323
+ blockchain_instance=self.blockchain,
324
+ )
325
+ else:
326
+ raise ValueError("Wrong rotation of prices")
327
+ elif isinstance(other, Amount):
328
+ check_asset(other["asset"], self["quote"]["asset"], self.blockchain)
329
+ a = other.copy() * self["price"]
330
+ a["asset"] = self["base"]["asset"].copy()
331
+ a["symbol"] = self["base"]["asset"]["symbol"]
332
+ else:
333
+ a["base"] *= other
334
+ return a
335
+
336
+ def __imul__(self, other):
337
+ if isinstance(other, Price):
338
+ tmp = self * other
339
+ self["base"] = tmp["base"]
340
+ self["quote"] = tmp["quote"]
341
+ else:
342
+ self["base"] *= other
343
+ return self
344
+
345
+ def __div__(self, other):
346
+ a = self.copy()
347
+ if isinstance(other, Price):
348
+ # Rotate/invert other
349
+ if sorted(self.symbols()) == sorted(other.symbols()):
350
+ return float(self.as_base(self["base"]["symbol"])) / float(
351
+ other.as_base(self["base"]["symbol"])
352
+ )
353
+ elif self["quote"]["symbol"] in other.symbols():
354
+ other = other.as_base(self["quote"]["symbol"])
355
+ elif self["base"]["symbol"] in other.symbols():
356
+ other = other.as_base(self["base"]["symbol"])
357
+ else:
358
+ raise InvalidAssetException
359
+ a["base"] = Amount(
360
+ float(self["base"].amount / other["base"].amount),
361
+ other["quote"]["symbol"],
362
+ blockchain_instance=self.blockchain,
363
+ )
364
+ a["quote"] = Amount(
365
+ float(self["quote"].amount / other["quote"].amount),
366
+ self["quote"]["symbol"],
367
+ blockchain_instance=self.blockchain,
368
+ )
369
+ elif isinstance(other, Amount):
370
+ check_asset(other["asset"], self["quote"]["asset"], self.blockchain)
371
+ a = other.copy() / self["price"]
372
+ a["asset"] = self["base"]["asset"].copy()
373
+ a["symbol"] = self["base"]["asset"]["symbol"]
374
+ else:
375
+ a["base"] /= other
376
+ return a
377
+
378
+ def __idiv__(self, other):
379
+ if isinstance(other, Price):
380
+ tmp = self / other
381
+ self["base"] = tmp["base"]
382
+ self["quote"] = tmp["quote"]
383
+ else:
384
+ self["base"] /= other
385
+ return self
386
+
387
+ def __floordiv__(self, other):
388
+ raise NotImplementedError("This is not possible as the price is a ratio")
389
+
390
+ def __ifloordiv__(self, other):
391
+ raise NotImplementedError("This is not possible as the price is a ratio")
392
+
393
+ def __lt__(self, other):
394
+ if isinstance(other, Price):
395
+ self._check_other(other)
396
+ return self["price"] < other["price"]
397
+ else:
398
+ return self["price"] < float(other or 0)
399
+
400
+ def __le__(self, other):
401
+ if isinstance(other, Price):
402
+ self._check_other(other)
403
+ return self["price"] <= other["price"]
404
+ else:
405
+ return self["price"] <= float(other or 0)
406
+
407
+ def __eq__(self, other):
408
+ if isinstance(other, Price):
409
+ self._check_other(other)
410
+ return self["price"] == other["price"]
411
+ else:
412
+ return self["price"] == float(other or 0)
413
+
414
+ def __ne__(self, other):
415
+ if isinstance(other, Price):
416
+ self._check_other(other)
417
+ return self["price"] != other["price"]
418
+ else:
419
+ return self["price"] != float(other or 0)
420
+
421
+ def __ge__(self, other):
422
+ if isinstance(other, Price):
423
+ self._check_other(other)
424
+ return self["price"] >= other["price"]
425
+ else:
426
+ return self["price"] >= float(other or 0)
427
+
428
+ def __gt__(self, other):
429
+ if isinstance(other, Price):
430
+ self._check_other(other)
431
+ return self["price"] > other["price"]
432
+ else:
433
+ return self["price"] > float(other or 0)
434
+
435
+ __truediv__ = __div__
436
+ __truemul__ = __mul__
437
+ __str__ = __repr__
438
+
439
+ @property
440
+ def market(self):
441
+ """Open the corresponding market
442
+
443
+ :returns: Instance of :class:`nectar.market.Market` for the
444
+ corresponding pair of assets.
445
+ """
446
+ from .market import Market
447
+
448
+ return Market(
449
+ base=self["base"]["asset"],
450
+ quote=self["quote"]["asset"],
451
+ blockchain_instance=self.blockchain,
452
+ )
453
+
454
+
455
+ class Order(Price):
456
+ """This class inherits :class:`nectar.price.Price` but has the ``base``
457
+ and ``quote`` Amounts not only be used to represent the price (as a
458
+ ratio of base and quote) but instead has those amounts represent the
459
+ amounts of an actual order!
460
+
461
+ :param Steem blockchain_instance: Steem instance
462
+
463
+ .. note::
464
+
465
+ If an order is marked as deleted, it will carry the
466
+ 'deleted' key which is set to ``True`` and all other
467
+ data be ``None``.
468
+ """
469
+
470
+ def __init__(self, base, quote=None, blockchain_instance=None, **kwargs):
471
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
472
+
473
+ if isinstance(base, dict) and "sell_price" in base:
474
+ super(Order, self).__init__(base["sell_price"], blockchain_instance=self.blockchain)
475
+ self["id"] = base.get("id")
476
+ elif isinstance(base, dict) and "min_to_receive" in base and "amount_to_sell" in base:
477
+ super(Order, self).__init__(
478
+ Amount(base["min_to_receive"], blockchain_instance=self.blockchain),
479
+ Amount(base["amount_to_sell"], blockchain_instance=self.blockchain),
480
+ blockchain_instance=self.blockchain,
481
+ )
482
+ self["id"] = base.get("id")
483
+ elif isinstance(base, Amount) and isinstance(quote, Amount):
484
+ super(Order, self).__init__(
485
+ None, base=base, quote=quote, blockchain_instance=self.blockchain
486
+ )
487
+ else:
488
+ raise ValueError("Unknown format to load Order")
489
+
490
+ def __repr__(self):
491
+ if "deleted" in self and self["deleted"]:
492
+ return "deleted order %s" % self["id"]
493
+ else:
494
+ t = ""
495
+ if "time" in self and self["time"]:
496
+ t += "(%s) " % self["time"]
497
+ if "type" in self and self["type"]:
498
+ t += "%s " % str(self["type"])
499
+ if "quote" in self and self["quote"]:
500
+ t += "%s " % str(self["quote"])
501
+ if "base" in self and self["base"]:
502
+ t += "%s " % str(self["base"])
503
+ return t + "@ " + Price.__repr__(self)
504
+
505
+ __str__ = __repr__
506
+
507
+
508
+ class FilledOrder(Price):
509
+ """This class inherits :class:`nectar.price.Price` but has the ``base``
510
+ and ``quote`` Amounts not only be used to represent the price (as a
511
+ ratio of base and quote) but instead has those amounts represent the
512
+ amounts of an actually filled order!
513
+
514
+ :param Steem blockchain_instance: Steem instance
515
+
516
+ .. note:: Instances of this class come with an additional ``date`` key
517
+ that shows when the order has been filled!
518
+ """
519
+
520
+ def __init__(self, order, blockchain_instance=None, **kwargs):
521
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
522
+ if isinstance(order, dict) and "current_pays" in order and "open_pays" in order:
523
+ # filled orders from account history
524
+ if "op" in order:
525
+ order = order["op"]
526
+
527
+ super(FilledOrder, self).__init__(
528
+ Amount(order["open_pays"], blockchain_instance=self.blockchain),
529
+ Amount(order["current_pays"], blockchain_instance=self.blockchain),
530
+ blockchain_instance=self.blockchain,
531
+ )
532
+ if "date" in order:
533
+ self["date"] = formatTimeString(order["date"])
534
+
535
+ else:
536
+ raise ValueError("Couldn't parse 'Price'.")
537
+
538
+ def json(self):
539
+ return {
540
+ "date": formatTimeString(self["date"]),
541
+ "current_pays": self["base"].json(),
542
+ "open_pays": self["quote"].json(),
543
+ }
544
+
545
+ def __repr__(self):
546
+ t = ""
547
+ if "date" in self and self["date"]:
548
+ t += "(%s) " % self["date"]
549
+ if "type" in self and self["type"]:
550
+ t += "%s " % str(self["type"])
551
+ if "quote" in self and self["quote"]:
552
+ t += "%s " % str(self["quote"])
553
+ if "base" in self and self["base"]:
554
+ t += "%s " % str(self["base"])
555
+ return t + "@ " + Price.__repr__(self)
556
+
557
+ __str__ = __repr__
nectar/profile.py ADDED
@@ -0,0 +1,65 @@
1
+ # -*- coding: utf-8 -*-
2
+ import json
3
+
4
+ try:
5
+ from collections.abc import Mapping # noqa
6
+ except ImportError:
7
+ from collections import Mapping # noqa
8
+
9
+
10
+ class DotDict(dict):
11
+ def __init__(self, *args):
12
+ """This class simplifies the use of "."-separated
13
+ keys when defining a nested dictionary:::
14
+
15
+ >>> from nectar.profile import Profile
16
+ >>> keys = ['profile.url', 'profile.img']
17
+ >>> values = ["http:", "foobar"]
18
+ >>> p = Profile(keys, values)
19
+ >>> print(p["profile"]["url"])
20
+ http:
21
+
22
+ """
23
+ if len(args) == 2:
24
+ for i, item in enumerate(args[0]):
25
+ t = self
26
+ parts = item.split(".")
27
+ for j, part in enumerate(parts):
28
+ if j < len(parts) - 1:
29
+ t = t.setdefault(part, {})
30
+ else:
31
+ t[part] = args[1][i]
32
+ elif len(args) == 1 and isinstance(args[0], dict):
33
+ for k, v in args[0].items():
34
+ self[k] = v
35
+ elif len(args) == 1 and isinstance(args[0], str):
36
+ for k, v in json.loads(args[0]).items():
37
+ self[k] = v
38
+
39
+
40
+ class Profile(DotDict):
41
+ """This class is a template to model a user's on-chain
42
+ profile according to
43
+
44
+ * https://github.com/adcpm/steemscript
45
+ """
46
+
47
+ def __init__(self, *args, **kwargs):
48
+ super(Profile, self).__init__(*args, **kwargs)
49
+
50
+ def __str__(self):
51
+ return json.dumps(self)
52
+
53
+ def update(self, u):
54
+ for k, v in u.items():
55
+ if isinstance(v, Mapping):
56
+ self.setdefault(k, {}).update(v)
57
+ else:
58
+ self[k] = u[k]
59
+
60
+ def remove(self, key):
61
+ parts = key.split(".")
62
+ if len(parts) > 1:
63
+ self[parts[0]].pop(".".join(parts[1:]))
64
+ else:
65
+ super(Profile, self).pop(parts[0], None)