188 lines
6.5 KiB
Python
188 lines
6.5 KiB
Python
from __future__ import annotations
|
|
from copy import deepcopy
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import date, datetime
|
|
from inspect import signature
|
|
from json import dump, load
|
|
from re import match
|
|
from typing import TYPE_CHECKING
|
|
|
|
from src.application import register_db
|
|
from src.market import Market
|
|
from src.rule import get_rule
|
|
from src.util import DictDeserializable, explain_abstract, get_client
|
|
|
|
if TYPE_CHECKING: # pragma: no cover
|
|
from typing import Any, Optional
|
|
|
|
from pymanifold.types import JSONDict
|
|
|
|
from src import Rule
|
|
from src.consts import OutcomeType
|
|
from src.rule import ResolutionValueRule
|
|
|
|
|
|
def main():
|
|
with open('example.json', 'r') as f:
|
|
obj: list = load(f, object_hook=date_deserialization_hook)
|
|
db = register_db()
|
|
try:
|
|
for mkt in obj.copy():
|
|
my_mkt = CreationRequest.from_dict(mkt).create()
|
|
db.execute("INSERT INTO markets VALUES ( (SELECT MAX(id)+1 from markets), ?, 3, NULL);", (my_mkt, ))
|
|
((new_id, ), ) = db.execute("SELECT MAX(id) from markets;")
|
|
print(f"Added as ID {new_id}")
|
|
print(my_mkt.market.url)
|
|
obj.remove(mkt)
|
|
finally:
|
|
db.commit()
|
|
db.close()
|
|
with open('example.json', 'w') as f:
|
|
dump(obj, f, default=date_serialization_hook, indent="\t")
|
|
|
|
|
|
def date_serialization_hook(obj):
|
|
"""JSON serializer for objects not serializable by default json code."""
|
|
if isinstance(obj, (datetime, date)):
|
|
return obj.isoformat()
|
|
raise TypeError(f"Type ${type(obj)} not serializable")
|
|
|
|
|
|
def date_deserialization_hook(json_dict):
|
|
"""JSON deserializer for datetime objects."""
|
|
for key, value in json_dict.items():
|
|
if isinstance(value, str):
|
|
if match(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$', value):
|
|
json_dict[key] = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
|
|
elif match(r'^\d{4}-\d{2}-\d{2}$', value):
|
|
json_dict[key] = datetime.strptime(value, "%Y-%m-%d")
|
|
|
|
return json_dict
|
|
|
|
|
|
@dataclass
|
|
class ManifoldRequest(DictDeserializable):
|
|
outcomeType: OutcomeType
|
|
question: str
|
|
description: str | Any
|
|
closeTime: int
|
|
|
|
initialProb: Optional[float] = None # Note: probability is multiplied by 100, may only allow integers
|
|
|
|
minValue: Optional[float] = None
|
|
maxValue: Optional[float] = None
|
|
isLogScale: Optional[bool] = None
|
|
initialValue: Optional[float] = None
|
|
tags: list[str] = field(default_factory=list)
|
|
answers: Optional[list[str]] = None
|
|
|
|
def __post_init__(self):
|
|
if isinstance(self.closeTime, datetime):
|
|
self.closeTime = round(self.closeTime.timestamp() * 1000)
|
|
|
|
if self.outcomeType == "BINARY":
|
|
self._validate_binary()
|
|
elif self.outcomeType == "PSEUDO_NUMERIC":
|
|
self._validate_pseudo_numeric()
|
|
elif self.outcomeType == "MULTIPLE_CHOICE":
|
|
self._validate_multiple_choice()
|
|
|
|
def _validate_binary(self) -> None:
|
|
if self.initialProb is None:
|
|
raise ValueError("Missing initial probability")
|
|
|
|
def _validate_pseudo_numeric(self) -> None:
|
|
if None in (self.minValue, self.maxValue, self.isLogScale, self.initialValue):
|
|
raise ValueError("Need a minValue, maxValue, isLogScale, and initialValue")
|
|
|
|
def _validate_multiple_choice(self) -> None:
|
|
if self.answers is None or len(self.answers) < 2 or any(len(x) < 1 for x in self.answers):
|
|
raise ValueError("Invalid answers list")
|
|
|
|
def to_dict(self) -> JSONDict:
|
|
state = deepcopy(self.__dict__)
|
|
state['description'].pop('processed', None)
|
|
return {
|
|
k: v for k, v in state.items()
|
|
if k in signature(type(self)).parameters and v is not None and k != "outcomeType"
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class CreationRequest:
|
|
manifold: ManifoldRequest
|
|
time_rules: list[Rule[Optional[bool]]]
|
|
value_rules: list[ResolutionValueRule]
|
|
notes: str = ""
|
|
initial_values: dict[str, int] = field(default_factory=dict)
|
|
|
|
def __post_init__(self):
|
|
if not self.manifold.description.get("processed"):
|
|
explanation = "\n" + explain_abstract(
|
|
time_rules=self.time_rules, value_rules=self.value_rules
|
|
)
|
|
for paragraph in explanation.splitlines():
|
|
if paragraph:
|
|
s_par = paragraph.lstrip()
|
|
self.manifold.description["content"].append({
|
|
"type": "paragraph",
|
|
"content": [{
|
|
"type": "text",
|
|
"text": "-" * (len(paragraph) - len(s_par)) + s_par
|
|
}]
|
|
})
|
|
else:
|
|
self.manifold.description["content"].append({"type": "paragraph"})
|
|
self.manifold.description["processed"] = True
|
|
|
|
@classmethod
|
|
def from_dict(cls, obj: JSONDict) -> 'CreationRequest':
|
|
"""Take a dictionary and return an instance of the associated class."""
|
|
obj = dict(obj)
|
|
manifold = ManifoldRequest.from_dict(obj.pop('manifold'))
|
|
time_rules = [
|
|
get_rule(type_).from_dict(kwargs)
|
|
for type_, kwargs in obj.pop('time_rules')
|
|
]
|
|
value_rules = [
|
|
get_rule(type_).from_dict(kwargs)
|
|
for type_, kwargs in obj.pop('value_rules')
|
|
]
|
|
return cls(
|
|
manifold=manifold,
|
|
time_rules=time_rules,
|
|
value_rules=value_rules,
|
|
**obj
|
|
)
|
|
|
|
def create(self):
|
|
"""Create a market, given its request object."""
|
|
client = get_client()
|
|
if self.manifold.outcomeType == "FREE_RESPONSE": # requires extra actions
|
|
market = client.create_free_response_market(**self.manifold.to_dict())
|
|
for answer, weight in self.initial_values.items():
|
|
client.create_bet(market.id, weight, answer)
|
|
|
|
elif self.manifold.outcomeType in ("BINARY", "PSEUDO_NUMERIC", "MULTIPLE_CHOICE"): # simple markets
|
|
func = {
|
|
"BINARY": client.create_binary_market,
|
|
"PSEUDO_NUMERIC": client.create_numeric_market,
|
|
"MULTIPLE_CHOICE": client.create_multiple_choice_market
|
|
}[self.manifold.outcomeType]
|
|
market = func(**self.manifold.to_dict())
|
|
|
|
else:
|
|
raise ValueError()
|
|
|
|
return Market(
|
|
client.get_market_by_id(market.id),
|
|
do_resolve_rules=self.time_rules,
|
|
resolve_to_rules=self.value_rules,
|
|
notes=self.notes
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|