├── README
├── DEONE
├── __local
├── admin.ico
├── deone.ico
├── README.txt
├── demo.py
├── auth.py
├── stock.py
├── market.py
├── client.py
├── trading.py
├── querytable.py
├── sector.py
├── ohlc.py
├── deone.py
├── main.py
└── admin.py
├── .gitignore
└── appspot
├── restlet
├── __init__.py
├── auth.py
├── jsonutil.py
├── parsutil.py
├── restutil.py
└── intgutil.py
├── suas
├── __init__.py
├── users.py
├── signedcookie.py
├── auth_handlers.py
└── session.py
├── app.yaml
├── models.py
├── index.yaml
├── simplejson
├── tool.py
├── scanner.py
├── decoder.py
├── encoder.py
└── __init__.py
└── main.py
/README:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/DEONE/__local:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/appspot/restlet/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/appspot/suas/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/DEONE/admin.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/deone/master/DEONE/admin.ico
--------------------------------------------------------------------------------
/DEONE/deone.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lite/deone/master/DEONE/deone.ico
--------------------------------------------------------------------------------
/appspot/app.yaml:
--------------------------------------------------------------------------------
1 | application: de-one
2 | version: 2
3 | runtime: python
4 | api_version: 1
5 |
6 | handlers:
7 | - url: .*
8 | script: main.py
9 |
--------------------------------------------------------------------------------
/appspot/models.py:
--------------------------------------------------------------------------------
1 | """A toy-level example of a data model in Google Appengine DB terms.
2 | """
3 | import logging
4 | from google.appengine.ext import db
5 | from restlet import restutil
6 |
7 | class Deone(db.Model):
8 | data = db.TextProperty()
9 |
10 | restutil.decorateModuleNamed(__name__)
11 | logging.info('Models in %r decorated', __name__)
12 |
--------------------------------------------------------------------------------
/DEONE/README.txt:
--------------------------------------------------------------------------------
1 | #C:\vpy\DEONE>python c:\vpy\pyinstaller\Configure.py --upx-dir=c:\vpy\upx305w
2 |
3 | #c:\vpy\DEONE>python C:\vpy\pyinstaller\Makespec.py main.py -X -F -w -o c:\vpy\DEONE\build --icon=deone.ico
4 | wrote c:\vpy\DEONE\main.spec
5 | now run Build.py to build the executable
6 |
7 | #C:\vpy\DEONE>python c:\vpy\pyinstaller\Build.py c:\vpy\DEONE\build\main.spec -o c:\vpy\DEONE\build
8 |
--------------------------------------------------------------------------------
/appspot/index.yaml:
--------------------------------------------------------------------------------
1 | indexes:
2 |
3 | # AUTOGENERATED
4 |
5 | # This index.yaml is automatically updated whenever the dev_appserver
6 | # detects that a new type of query is run. If you want to manage the
7 | # index.yaml file manually, remove the above marker line (the line
8 | # saying "# AUTOGENERATED"). If you want to manage some indexes
9 | # manually, move them above the marker line. The index.yaml file is
10 | # automatically uploaded to the admin console when you next deploy
11 | # your application using appcfg.py.
12 |
--------------------------------------------------------------------------------
/DEONE/demo.py:
--------------------------------------------------------------------------------
1 | import sys,random
2 | from client import App3Client
3 | import simplejson
4 |
5 | c = App3Client('de-one.appspot.com', 'test', 'test')
6 | #c = App3Client('localhost:8080', 'test', 'test')
7 | #c = App3Client('localhost:8080', 'test', 'wrong_password')
8 |
9 | #Deone
10 | s = c.list('Deone')
11 | print(s)
12 | if s is not None:
13 | items = simplejson.loads(s)
14 | for item in items:
15 | id = item['id']
16 | print(id)
17 | #print(c.get('Deone', id))
18 | c.delete('Deone', id)
19 | #c.post('Deone', {"data": "1234567890123"})
20 |
--------------------------------------------------------------------------------
/appspot/simplejson/tool.py:
--------------------------------------------------------------------------------
1 | r"""
2 | Using simplejson from the shell to validate and
3 | pretty-print::
4 |
5 | $ echo '{"json":"obj"}' | python -msimplejson
6 | {
7 | "json": "obj"
8 | }
9 | $ echo '{ 1.2:3.4}' | python -msimplejson
10 | Expecting property name: line 1 column 2 (char 2)
11 |
12 | Note that the JSON produced by this module's default settings
13 | is a subset of YAML, so it may be used as a serializer for that as well.
14 | """
15 | import simplejson
16 |
17 | #
18 | # Pretty printer:
19 | # curl http://mochikit.com/examples/ajax_tables/domains.json | python -msimplejson.tool
20 | #
21 |
22 | def main():
23 | import sys
24 | if len(sys.argv) == 1:
25 | infile = sys.stdin
26 | outfile = sys.stdout
27 | elif len(sys.argv) == 2:
28 | infile = open(sys.argv[1], 'rb')
29 | outfile = sys.stdout
30 | elif len(sys.argv) == 3:
31 | infile = open(sys.argv[1], 'rb')
32 | outfile = open(sys.argv[2], 'wb')
33 | else:
34 | raise SystemExit("%s [infile [outfile]]" % (sys.argv[0],))
35 | try:
36 | obj = simplejson.load(infile)
37 | except ValueError, e:
38 | raise SystemExit(e)
39 | simplejson.dump(obj, outfile, sort_keys=True, indent=4)
40 | outfile.write('\n')
41 |
42 |
43 | if __name__ == '__main__':
44 | main()
45 |
--------------------------------------------------------------------------------
/appspot/main.py:
--------------------------------------------------------------------------------
1 | import wsgiref.handlers
2 | from google.appengine.ext import webapp
3 |
4 | from google.appengine.ext.webapp import util
5 |
6 | from google.appengine.ext.webapp import template
7 |
8 | from suas import session, auth_handlers
9 |
10 | # Set up our REST resources
11 | import models
12 |
13 | HOME_VIEW = template.Template("""
14 |
Home
15 |
16 | Demo app for DEONE
17 | {% if session.flash_msg %}
18 | {{ session.flash_msg }}
19 | {% endif %}
20 | {% if session.user %}
21 | Logged in as {{ session.user }}.
22 | Log out
23 | {% else %}
24 | Log in
25 | Sign up
26 | {% endif %}
27 |
28 | """)
29 |
30 | class HomeHandler(session.RequestHandler):
31 | def get(self):
32 | ctx = template.Context({"session": self.session})
33 | self.response.out.write(HOME_VIEW.render(ctx))
34 |
35 | from restlet.intgutil import JsonRestHelper
36 |
37 | helper = JsonRestHelper()
38 |
39 | class CrudRestHandler(webapp.RequestHandler):
40 | def __init__(self, *a, **k):
41 | webapp.RequestHandler.__init__(self, *a, **k)
42 | helper.hookup(self)
43 | ROUTES = [('/', HomeHandler), ('/(rest)/.*', CrudRestHandler)] + auth_handlers.ROUTES
44 |
45 | #from restlet.handler import CrudRestHandler
46 | #ROUTES = [('/home', HomeHandler), ('/', CrudRestHandler)] + auth_handlers.ROUTES
47 |
48 | def main():
49 | import logging
50 | logging.basicConfig(level=logging.DEBUG,)
51 |
52 | # Create the AppEngine WSGI Application
53 | application = webapp.WSGIApplication(ROUTES, debug=True)
54 |
55 | wsgiref.handlers.CGIHandler().run(application)
56 |
57 | if __name__ == '__main__':
58 | main()
59 |
--------------------------------------------------------------------------------
/appspot/suas/users.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/env python2.5
2 | #----------------------------
3 | # Datastore models for user & signup
4 | #----------------------------
5 |
6 | from base64 import b64encode as b64
7 | from hashlib import md5, sha256
8 | from random import randint
9 | from time import time
10 |
11 | from google.appengine.ext import db
12 |
13 | import logging
14 |
15 | N_SALT = 8 # length of the password salt
16 |
17 |
18 | def salt_n_hash(password, salt=None):
19 | """
20 | Generate a salt and return in base64 encoding the hash of the
21 | password with the salt and the character '$' prepended to it.
22 | """
23 | salt = salt or b64( ''.join(chr(randint(0, 0xff)) for _ in range(N_SALT)) )
24 | return salt + '$' + b64( sha256(salt+password.encode("ascii")).digest() )
25 |
26 |
27 | class User(db.Model):
28 | nickname = db.StringProperty(required=True)
29 | email = db.EmailProperty(required=True)
30 | pwd = db.StringProperty(required=True)
31 | suspended = db.BooleanProperty(default=True)
32 |
33 | @classmethod
34 | def authenticate(klass, nickname, password):
35 | """Return an User() entity instance if password is correct"""
36 | logging.info("authenticate-- user:" + nickname + " password:" + password)
37 |
38 | user = klass.get_by_key_name(nickname)
39 | if user:
40 | n_salt = user.pwd.index('$')
41 | logging.info("user: %s password:%s suspended:%d, n_salt:%d" %(user.nickname,user.pwd,user.suspended,n_salt))
42 | if user.pwd == salt_n_hash(password, salt=user.pwd[:n_salt]):
43 | logging.info("authenticated user")
44 | return user
45 |
46 | def __eq__(self, other):
47 | return self.nickname == other.nickname
48 |
49 |
50 | def signup_id(nickname):
51 | return md5( nickname + repr(time()) ).hexdigest()
52 |
53 |
54 | class UserSignup(db.Model):
55 | user = db.ReferenceProperty(User, required=True)
56 | date = db.DateProperty(auto_now_add=True)
57 |
--------------------------------------------------------------------------------
/DEONE/auth.py:
--------------------------------------------------------------------------------
1 | import hmac, sha, base64
2 | from datetime import datetime
3 | import logging
4 |
5 | TIMEFORMAT = "%a, %d %b %Y %H:%M:%S +0000"
6 | SECRETKEY = 'deone_appspot'
7 |
8 | def generate_auth(app3_user, app3_timestamp):
9 | """
10 | The timestamp format should be as specified in RFC 2822 and in UTC:
11 | "%a, %d %b %Y %H:%M:%S +0000"
12 |
13 | - See http://www.faqs.org/rfcs/rfc2822.html
14 | """
15 | message = "%s\n%s" % (app3_user, app3_timestamp)
16 |
17 | auth = hmac.new(
18 | key = SECRETKEY,
19 | msg = message,
20 | digestmod = sha,
21 | ).digest()
22 |
23 | return base64.encodestring(auth).strip()
24 |
25 | def generate_timestamp():
26 | """
27 | Generates a timestamp in the standard format.
28 | """
29 | return datetime.utcnow().strftime(TIMEFORMAT)
30 |
31 | def is_within_n_minutes(sent_time, n=15):
32 | """
33 | Check whether one of our timestamps is within n minutes of
34 | now. (All times are in UTC)
35 | """
36 | sent_time = datetime.strptime(sent_time, TIMEFORMAT)
37 |
38 | return not (datetime.utcnow() - sent_time).seconds >= n * 60
39 |
40 | def is_authorized(request):
41 | #return True
42 | if ('App3-Timestamp' not in request.headers) or ('App3-Auth' not in request.headers) or ('App3-User' not in request.headers) or ('App3-Pass' not in request.headers):
43 | logging.info("Need all of the headers to have been passed for authentication.")
44 | return False
45 | app3_timestamp = request.headers['App3-Timestamp']
46 | app3_auth = request.headers['App3-Auth']
47 | app3_user = request.headers['App3-User']
48 | app3_pass = request.headers['App3-Pass']
49 |
50 | """
51 | Returns whether a user is authorized based on the request.
52 | """
53 | user = User.authenticate(app3_user, app3_pass)
54 | if user and not user.suspended:
55 | logging.info("authenticated user.")
56 | else:
57 | return False
58 |
59 | # Time skew... Could be replay attack?
60 | if not is_within_n_minutes(app3_timestamp, 15):
61 | return False
62 |
63 | # Check whether we generate the same auth header as they did
64 | return app3_auth == generate_auth(app3_user, app3_timestamp)
--------------------------------------------------------------------------------
/appspot/restlet/auth.py:
--------------------------------------------------------------------------------
1 | import hmac, sha, base64
2 | from datetime import datetime
3 | from suas.users import User,salt_n_hash
4 | import logging
5 |
6 | TIMEFORMAT = "%a, %d %b %Y %H:%M:%S +0000"
7 | SECRETKEY = 'deone_appspot'
8 |
9 | def generate_auth(app3_user, app3_timestamp):
10 | """
11 | The timestamp format should be as specified in RFC 2822 and in UTC:
12 | "%a, %d %b %Y %H:%M:%S +0000"
13 |
14 | - See http://www.faqs.org/rfcs/rfc2822.html
15 | """
16 | message = "%s\n%s" % (app3_user, app3_timestamp)
17 |
18 | auth = hmac.new(
19 | key = SECRETKEY,
20 | msg = message,
21 | digestmod = sha,
22 | ).digest()
23 |
24 | return base64.encodestring(auth).strip()
25 |
26 | def generate_timestamp():
27 | """
28 | Generates a timestamp in the standard format.
29 | """
30 | return datetime.utcnow().strftime(TIMEFORMAT)
31 |
32 | def is_within_n_minutes(sent_time, n=15):
33 | """
34 | Check whether one of our timestamps is within n minutes of
35 | now. (All times are in UTC)
36 | """
37 | sent_time = datetime.strptime(sent_time, TIMEFORMAT)
38 |
39 | return not (datetime.utcnow() - sent_time).seconds >= n * 60
40 |
41 | def is_authorized(request):
42 | if ('App3-Timestamp' not in request.headers) or ('App3-Auth' not in request.headers) or ('App3-User' not in request.headers) or ('App3-Pass' not in request.headers):
43 | logging.info("Need all of the headers to have been passed for authentication.")
44 | return False
45 | app3_timestamp = request.headers['App3-Timestamp']
46 | app3_auth = request.headers['App3-Auth']
47 | app3_user = request.headers['App3-User']
48 | app3_pass = request.headers['App3-Pass']
49 |
50 | """
51 | Returns whether a user is authorized based on the request.
52 | """
53 | user = User.authenticate(app3_user, app3_pass)
54 | if user and not user.suspended:
55 | logging.info("authenticated user.")
56 | else:
57 | return False
58 |
59 | # Time skew... Could be replay attack?
60 | if not is_within_n_minutes(app3_timestamp, 15):
61 | return False
62 |
63 | # Check whether we generate the same auth header as they did
64 | return app3_auth == generate_auth(app3_user, app3_timestamp)
--------------------------------------------------------------------------------
/appspot/simplejson/scanner.py:
--------------------------------------------------------------------------------
1 | """
2 | Iterator based sre token scanner
3 | """
4 | import re
5 | from re import VERBOSE, MULTILINE, DOTALL
6 | import sre_parse
7 | import sre_compile
8 | import sre_constants
9 | from sre_constants import BRANCH, SUBPATTERN
10 |
11 | __all__ = ['Scanner', 'pattern']
12 |
13 | FLAGS = (VERBOSE | MULTILINE | DOTALL)
14 |
15 | class Scanner(object):
16 | def __init__(self, lexicon, flags=FLAGS):
17 | self.actions = [None]
18 | # Combine phrases into a compound pattern
19 | s = sre_parse.Pattern()
20 | s.flags = flags
21 | p = []
22 | for idx, token in enumerate(lexicon):
23 | phrase = token.pattern
24 | try:
25 | subpattern = sre_parse.SubPattern(s,
26 | [(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))])
27 | except sre_constants.error:
28 | raise
29 | p.append(subpattern)
30 | self.actions.append(token)
31 |
32 | s.groups = len(p) + 1 # NOTE(guido): Added to make SRE validation work
33 | p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
34 | self.scanner = sre_compile.compile(p)
35 |
36 | def iterscan(self, string, idx=0, context=None):
37 | """
38 | Yield match, end_idx for each match
39 | """
40 | match = self.scanner.scanner(string, idx).match
41 | actions = self.actions
42 | lastend = idx
43 | end = len(string)
44 | while True:
45 | m = match()
46 | if m is None:
47 | break
48 | matchbegin, matchend = m.span()
49 | if lastend == matchend:
50 | break
51 | action = actions[m.lastindex]
52 | if action is not None:
53 | rval, next_pos = action(m, context)
54 | if next_pos is not None and next_pos != matchend:
55 | # "fast forward" the scanner
56 | matchend = next_pos
57 | match = self.scanner.scanner(string, matchend).match
58 | yield rval, matchend
59 | lastend = matchend
60 |
61 |
62 | def pattern(pattern, flags=FLAGS):
63 | def decorator(fn):
64 | fn.pattern = pattern
65 | fn.regex = re.compile(pattern, flags)
66 | return fn
67 | return decorator
--------------------------------------------------------------------------------
/DEONE/stock.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 | from ohlc import OHLCWidget
5 | from querytable import QueryTableWidget
6 |
7 | class StockWidget(QtGui.QWidget):
8 | def __init__(self, parent):
9 | super(QtGui.QWidget, self).__init__(parent)
10 | self.setObjectName("tabStock")
11 |
12 | mainLayout = QtGui.QVBoxLayout()
13 |
14 | hLayoutQuery = QtGui.QHBoxLayout()
15 | self.label = QtGui.QLabel(self)
16 | self.label.setGeometry(QtCore.QRect(20, 10, 46, 14))
17 | self.label.setObjectName("label")
18 | hLayoutQuery.addWidget(self.label)
19 |
20 | self.lineEdit = QtGui.QLineEdit(self)
21 | self.lineEdit.setGeometry(QtCore.QRect(80, 0, 113, 20))
22 | self.lineEdit.setObjectName("lineEdit")
23 | hLayoutQuery.addWidget(self.lineEdit)
24 |
25 | self.pushButtonQuery = QtGui.QPushButton(self)
26 | self.pushButtonQuery.setGeometry(QtCore.QRect(210, 0, 75, 23))
27 | self.pushButtonQuery.setObjectName("pushButtonQuery")
28 | hLayoutQuery.addWidget(self.pushButtonQuery)
29 |
30 | mainLayout.addLayout(hLayoutQuery)
31 |
32 | hLayoutInfo = QtGui.QHBoxLayout()
33 |
34 | self.ohlc = OHLCWidget(self)
35 | self.ohlc.setObjectName("OHLCWidget")
36 | hLayoutInfo.addWidget(self.ohlc)
37 |
38 | self.tbl = QueryTableWidget(self)
39 | self.tbl.setObjectName("QueryTableWidget")
40 | hLayoutInfo.addWidget(self.tbl)
41 |
42 | mainLayout.addLayout(hLayoutInfo)
43 | self.setLayout(mainLayout)
44 |
45 | self.retranslateUi()
46 |
47 | QtCore.QObject.connect(self.pushButtonQuery, QtCore.SIGNAL("pressed()"), self.doQuery)
48 |
49 | self.lineEdit.setText('1')
50 | self.doQuery()
51 |
52 | def retranslateUi(self):
53 | self.label.setText(QtGui.QApplication.translate("MainWindow", "股票代码", None, QtGui.QApplication.UnicodeUTF8))
54 | self.pushButtonQuery.setText(QtGui.QApplication.translate("MainWindow", "查询", None, QtGui.QApplication.UnicodeUTF8))
55 |
56 | def doQuery(self):
57 | print("called doQuery")
58 | code = str(self.lineEdit.text())
59 | #日期 股票代码 公司名称 开盘价格 最高价格 最低价格 收盘价格 成交量
60 | self.ohlc.query(u'select 日期,开盘价格,最高价格,最低价格,收盘价格,成交量 from StockHist where 股票代码=? order by 日期 asc'.encode('utf-8'), (code,))
61 | self.tbl.query(u'select * from StockHist where 股票代码=? order by 日期 desc'.encode('utf-8'), (code,))
62 | pass
63 |
64 | #===============================================================================
65 | # Example
66 | #===============================================================================
67 | if __name__ == '__main__':
68 | import sys
69 | from PyQt4.QtGui import QMainWindow, QApplication
70 |
71 | class ApplicationWindow(QMainWindow):
72 | def __init__(self):
73 | QMainWindow.__init__(self)
74 | self.tblwidget = StockWidget(self)
75 | self.tblwidget.setFocus()
76 | self.setCentralWidget(self.tblwidget)
77 |
78 | app = QApplication(sys.argv)
79 | win = ApplicationWindow()
80 | win.show()
81 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/DEONE/market.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 |
5 | from ohlc import OHLCWidget
6 | from querytable import QueryTableWidget
7 |
8 | class MarketWidget(QtGui.QWidget):
9 | def __init__(self, parent):
10 | super(QtGui.QWidget, self).__init__(parent)
11 |
12 | self.setObjectName("tabMarket")
13 |
14 | mainLayout = QtGui.QVBoxLayout()
15 |
16 | hLayoutQuery = QtGui.QHBoxLayout()
17 | self.pushButtonMarketSH = QtGui.QPushButton(self)
18 | #self.pushButtonMarketSH.setGeometry(QtCore.QRect(50, 5, 75, 23))
19 | self.pushButtonMarketSH.setObjectName("pushButtonMarketSH")
20 | hLayoutQuery.addWidget(self.pushButtonMarketSH)
21 |
22 | self.pushButtonMarketSZ = QtGui.QPushButton(self)
23 | #self.pushButtonMarketSZ.setGeometry(QtCore.QRect(200, 5, 75, 23))
24 | self.pushButtonMarketSZ.setObjectName("pushButtonMarketSZ")
25 | hLayoutQuery.addWidget(self.pushButtonMarketSZ)
26 |
27 | mainLayout.addLayout(hLayoutQuery)
28 |
29 | hLayoutInfo = QtGui.QHBoxLayout()
30 | self.ohlc = OHLCWidget(self)
31 | self.ohlc.setObjectName("OHLCWidget")
32 | hLayoutInfo.addWidget(self.ohlc)
33 |
34 | self.tbl = QueryTableWidget(self)
35 | self.tbl.setObjectName("QueryTableWidget")
36 | hLayoutInfo.addWidget(self.tbl)
37 |
38 | mainLayout.addLayout(hLayoutInfo)
39 | self.setLayout(mainLayout)
40 |
41 | self.retranslateUi()
42 |
43 | QtCore.QObject.connect(self.pushButtonMarketSH, QtCore.SIGNAL("pressed()"), self.doQueryMarketSH)
44 | QtCore.QObject.connect(self.pushButtonMarketSZ, QtCore.SIGNAL("pressed()"), self.doQueryMarketSZ)
45 |
46 | self.doQueryMarketSH()
47 |
48 | def retranslateUi(self):
49 | self.pushButtonMarketSZ.setText(QtGui.QApplication.translate("MainWindow", "深证成指", None, QtGui.QApplication.UnicodeUTF8))
50 | self.pushButtonMarketSH.setText(QtGui.QApplication.translate("MainWindow", "上证指数", None, QtGui.QApplication.UnicodeUTF8))
51 |
52 | def doQueryMarketSH(self):
53 | print("called doQueryMarketSH")
54 | self.doQueryMarket('999999')
55 |
56 | def doQueryMarketSZ(self):
57 | print("called doQueryMarketSH")
58 | self.doQueryMarket('399001')
59 |
60 | def doQueryMarket(self, code):
61 | #日期 市场代码 开盘 最高 最低 收盘 成交量 上涨个股 下跌个股 平盘个股 交易类型I 交易类型II 交易类型III 交易类型IV 风险指标 动能指标
62 | self.ohlc.query(u'select 日期,开盘,最高,最低,收盘,成交量 from Market where 市场代码=? order by 日期 asc'.encode('utf-8'), (code,))
63 | self.tbl.query(u'select * from Market where 市场代码=? order by 日期 desc'.encode('utf-8'), (code,))
64 |
65 | #===============================================================================
66 | # Example
67 | #===============================================================================
68 | if __name__ == '__main__':
69 | import sys
70 | from PyQt4.QtGui import QMainWindow, QApplication
71 |
72 | class ApplicationWindow(QMainWindow):
73 | def __init__(self):
74 | QMainWindow.__init__(self)
75 | self.widget = MarketWidget(self)
76 | self.widget.setFocus()
77 | self.setCentralWidget(self.widget)
78 |
79 | app = QApplication(sys.argv)
80 | win = ApplicationWindow()
81 | win.show()
82 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/appspot/suas/signedcookie.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/env python2.5
2 |
3 | """
4 | Cookie signed with a secret server's key.
5 |
6 | While cookies are a convenient way to store data in clients' browsers,
7 | they can be susceptible to forgery. By attaching a signature to each
8 | (key=value) pair, we can verify that the values the cookies carry are
9 | really those issued by the server.
10 |
11 | This implementation ensure that in a key=value cookie, the value is
12 | really intended for the particular key.
13 |
14 | Each cookie is signed with HMAC-SHA256.
15 |
16 | SignedCookie inherits from SimpleCookie and share the same API.
17 | A SignedCookie is constructed with a secret key as the argument.
18 |
19 | >>> c = SignedCookie('Open, sesame!')
20 | >>> c['user'] = 'username'
21 | >>> c['user'].value
22 | 'username'
23 | >>> c['user'].coded_value
24 | '"usernameCu/vp7hQJ8QsnLoMvFyM6jwyqAyZMIdJcpUZRBE6JYU="'
25 |
26 | Loading also works.
27 |
28 | >>> c.load('Cookie: itemid="12345mwKEC0M1343j/uvi3DIwLsiQWw7UP0Ue/84NxBUwljI="; Max-Age=100')
29 | >>> c['itemid'].value
30 | '12345'
31 | >>> int(c['itemid']['max-age'])
32 | 100
33 | """
34 |
35 | import re
36 | import hmac
37 | from hashlib import sha256
38 | from base64 import b64encode, b64decode
39 | import Cookie
40 |
41 |
42 | SIG_LEN = 44 # the length of a sha256 hash string is 32,
43 | # thus a base64 encoding of that block
44 | # will always be of length 44
45 |
46 | SIG_PATTERN = re.compile(r'[0-9a-zA-Z+/=]{44}')
47 | # This is the regex pattern for the signature generated by SignedCookie
48 | # which is base64 blob of length 44.
49 |
50 | class BadSignatureError(Exception):
51 | pass
52 |
53 | class SignedCookie(Cookie.SimpleCookie):
54 | def __init__(self, key, input=None):
55 | """
56 | Initialize the Cookie with a secret key.
57 | """
58 | self.key = key.encode('ascii')
59 | if input:
60 | self.load(input)
61 |
62 | def __setitem__(self, key, value):
63 | strval = str(value)
64 | sig = b64encode(
65 | hmac.new(self.key + str(key), strval, sha256).digest()
66 | )
67 | cval = Cookie._quote( strval + sig )
68 | self._BaseCookie__set(key, strval, cval)
69 |
70 | ## Copied verbatim from Cookie.py
71 | ## This has to be here to be able to use our implementation of __ParseString
72 | def load(self, rawdata):
73 | """Load cookies from a string (presumably HTTP_COOKIE) or
74 | from a dictionary. Loading cookies from a dictionary 'd'
75 | is equivalent to calling:
76 | map(Cookie.__setitem__, d.keys(), d.values())
77 | """
78 | if type(rawdata) == type(""):
79 | self.__ParseString(rawdata)
80 | else:
81 | self.update(rawdata)
82 | return
83 |
84 | ## Copied from Cookie.py
85 | ## Changed the last block to use decode & verify signature
86 | def __ParseString(self, str, patt=Cookie._CookiePattern):
87 | i = 0 # Our starting point
88 | n = len(str) # Length of string
89 | M = None # current morsel
90 |
91 | while 0 <= i < n:
92 | # Start looking for a cookie
93 | match = patt.search(str, i)
94 | if not match: break # No more cookies
95 |
96 | K,V = match.group("key"), match.group("val")
97 | i = match.end(0)
98 |
99 | # Parse the key, value in case it's metainfo
100 | if K[0] == "$":
101 | # We ignore attributes which pertain to the cookie
102 | # mechanism as a whole. See RFC 2109.
103 | # (Does anyone care?)
104 | if M:
105 | M[ K[1:] ] = V
106 | elif K.lower() in Cookie.Morsel._reserved:
107 | if M:
108 | M[ K ] = Cookie._unquote(V)
109 | else:
110 | if not SIG_PATTERN.search(V):
111 | pass
112 | uval = Cookie._unquote(V)
113 | real_val = uval[:-SIG_LEN]
114 | try:
115 | sig = b64decode( uval[-SIG_LEN:] )
116 | except TypeError:
117 | # Incorrect padding
118 | raise BadSignatureError("Bad signature for cookie '%s'" % K)
119 | # TODO: use constant time string comparison
120 | if sig != hmac.new(self.key + K, real_val, sha256).digest():
121 | raise BadSignatureError("Bad signature for cookie '%s'" % K)
122 | self._BaseCookie__set(K, real_val, V)
123 | M = self[K]
124 |
--------------------------------------------------------------------------------
/DEONE/client.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import httplib2
4 | #import urllib2
5 | #import httplib
6 | from auth import generate_auth, generate_timestamp
7 | import random, time
8 | import simplejson
9 |
10 | class App3Client(object):
11 | """
12 | Simple REST Client for App3.
13 | """
14 | def __init__(self, host, user_name='test', user_pass='test'):
15 | self.__host = host
16 | self.__user_name = user_name
17 | self.__user_pass = user_pass
18 | self.h = httplib2.Http()
19 |
20 | def post(self, resource, params=None):
21 | """
22 | Executes a POST request on the specified resource.
23 | """
24 | resp, body = self.__request(
25 | method = 'POST',
26 | url = "/rest/%s/" % (resource),
27 | params = params,
28 | )
29 | #print(resp, body)
30 | if (resp is not None) and (resp['status'] == '201'):
31 | return body
32 | else:
33 | return None
34 |
35 | def list(self, resource, params=None):
36 | """
37 | Lists the all of the resources of type resource.
38 | """
39 | resp, body = self.__request(
40 | method = 'GET',
41 | url = "/rest/%s/" % resource,
42 | params = params,
43 | )
44 |
45 | if (resp is not None) and (resp['status'] == '200'):
46 | return body
47 | else:
48 | return None
49 |
50 | def get(self, resource, id, params=None):
51 | """
52 | Retrieves the resource specified.
53 | """
54 | resp, body = self.__request(
55 | method = 'GET',
56 | url = "/rest/%s/%s/" % (resource, id),
57 | params = params,
58 | )
59 |
60 | if (resp is not None) and (resp['status'] == '200'):
61 | return body
62 | else:
63 | return None
64 |
65 | def exists(self, resource, id):
66 | """
67 | Returns whether the resource specified exists in the datastore.
68 | """
69 | resp = self.__request(
70 | method = 'GET',
71 | url = "/rest/%s/%s/" % (resource, id),
72 | )
73 | return (resp is not None) and (resp['status'] == '200')
74 |
75 | def delete(self, resource, id):
76 | """
77 | Deletes the specified resource from the datastore.
78 | """
79 | resp, body = self.__request(
80 | method = 'DELETE',
81 | url = "/rest/%s/%s/" % (resource, id),
82 | )
83 | #print(resp, body)
84 | if (resp is not None) and (resp['status'] == '200'):
85 | return body
86 | else:
87 | return None
88 |
89 | def test(self):
90 | resp, body = self.__request(
91 | method = 'GET',
92 | url = "/",
93 | )
94 |
95 | if (resp is not None) and (resp['status'] == '200'):
96 | return body
97 | else:
98 | return None
99 |
100 | def __request(self, method, url, params=None):
101 | """
102 | Internal method for executing all of the REST requests.
103 | """
104 | if params is not None:
105 | params = simplejson.dumps(params)
106 |
107 | self.app3_timestamp = generate_timestamp()
108 |
109 | headers = {
110 | #"Content-type": "application/json",
111 | 'App3-User': self.__user_name,
112 | 'App3-Pass': self.__user_pass,
113 | 'App3-Timestamp': self.app3_timestamp,
114 | 'App3-Auth': generate_auth(self.__user_name, self.app3_timestamp),
115 | }
116 |
117 | #h = httplib2.Http(".cache")
118 | #h.add_credentials('name', 'password')
119 | resp, body = self.h.request("http://"+self.__host + url, method, body=params, headers=headers )
120 | #print(resp, body)
121 | return resp, body
122 |
123 | #===============================================================================
124 | # Example
125 | #===============================================================================
126 | if __name__ == '__main__':
127 | from client import App3Client
128 | c = App3Client('de-one.appspot.com', 'test', 'test')
129 | print(c.test())
130 | print(c.list('Deone'))
131 |
--------------------------------------------------------------------------------
/appspot/restlet/jsonutil.py:
--------------------------------------------------------------------------------
1 | """ Utilities for JSON REST CRUD support for GAE db models.
2 |
3 | Terminology: a subclass of db.Model is known as "a Model"; an instance of
4 | such a subclass is known as "an entity".
5 |
6 | Data is said to be in JSONed or JSONable form if it contains only dicts, lists
7 | and scalars (strings, numbers) in a form that is correctly serializable into a
8 | JSON-format string.
9 |
10 | In particular, a "jobj" is a JSONed dict with a key 'id' mapping the string
11 | format of the numeric value of an entity; each other key must be the name of
12 | a property of that entity's Model, and the corresponding value must be a string
13 | that can be deserialized into a value of that property's type.
14 | """
15 | import re
16 |
17 | import restutil
18 | from django.utils import simplejson
19 |
20 |
21 | def id_of(entity):
22 | """ Make a {'id': } dict for an entity.
23 |
24 | Args:
25 | entity: an entity
26 | Returns:
27 | a jobj corresponding to the entity
28 | """
29 | return dict(id=restutil.id_of(entity))
30 |
31 |
32 | # RE to match: optional /, classname, optional /, ID of 0+ numeric digits
33 | CLASSNAME_ID_RE = re.compile(r'^/?(\w+)/?(\d*)$')
34 |
35 | def path_to_classname_and_id(path):
36 | """ Get a (classname, id) pair from a path.
37 |
38 | Args:
39 | path: a path string to anaylyze
40 | Returns:
41 | a 2-item tuple:
42 | (None, '') if the path does not match CLASSNAME_ID_RE
43 | (classname, idstring) if the path does match
44 | [idstring may be '', or else a string of digits]
45 | """
46 | mo = CLASSNAME_ID_RE.match(path)
47 | if mo: return mo.groups()
48 | else: return (None, '')
49 |
50 |
51 | def send_json(response_obj, jdata):
52 | """ Send data in JSON form to an HTTP-response object.
53 |
54 | Args:
55 | response_obj: an HTTP response object
56 | jdata: a dict or list in correct 'JSONable' form
57 | Side effects:
58 | sends the JSON form of jdata on response.out
59 | """
60 | response_obj.content_type = 'application/json'
61 | simplejson.dump(jdata, response_obj.out)
62 |
63 |
64 | def receive_json(request_obj):
65 | """ Receive data in JSON form from an HTTP-request object.
66 |
67 | Args:
68 | request_obj: an HTTP request object (with body in JSONed form)
69 | Returns:
70 | the JSONable-form result of loading the request's body
71 | """
72 | return simplejson.loads(request_obj.body)
73 |
74 |
75 | def make_jobj(entity):
76 | """ Make a JSONable dict (a jobj) given an entity.
77 |
78 | Args:
79 | entity: an entity
80 | Returns:
81 | the JSONable-form dict (jobj) for the entity
82 | """
83 | model = type(entity)
84 | jobj = id_of(entity)
85 | props = restutil.allProperties(model)
86 | for property_name, property_value in props:
87 | value_in_entity = getattr(entity, property_name, None)
88 | if value_in_entity is not None:
89 | to_string = getattr(model, property_name + '_to_string')
90 | jobj[property_name] = to_string(value_in_entity)
91 | return jobj
92 |
93 |
94 | def parse_jobj(model, jobj):
95 | """ Make dict suitable for instantiating model, given a jobj.
96 |
97 | Args:
98 | model: a Model
99 | jobj: a jobj
100 | Returns:
101 | a dict d such that calling model(**d) properly makes an entity
102 | """
103 | result = dict()
104 | for property_name, property_value in jobj.iteritems():
105 | # ensure we have an ASCII string, not a Unicode one
106 | property_name = str(property_name)
107 | from_string = getattr(model, property_name + '_from_string')
108 | property_value = from_string(property_value)
109 | if property_value is not None:
110 | result[property_name] = property_value
111 | return result
112 |
113 |
114 | def make_entity(model, jobj):
115 | """ Makes an entity whose type is model with the state given by jobj.
116 |
117 | Args:
118 | model: a Model
119 | jobj: a jobj
120 | Side effects:
121 | creates and puts an entity of type model, w/state per jobj
122 | Returns:
123 | a jobj representing the newly created entity
124 | """
125 | entity_dict = parse_jobj(model, jobj)
126 | entity = model(**entity_dict)
127 | entity.put()
128 | jobj = make_jobj(entity)
129 | jobj.update(id_of(entity))
130 | return jobj
131 |
132 |
133 | def update_entity(entity, jobj):
134 | """ Updates an entity's state as per properties given in jobj.
135 |
136 | Args:
137 | entity: an entity
138 | jobj: a jobj
139 | Side effects:
140 | updates the entity with properties as given by jobj
141 | Returns:
142 | a jobj representing the whole new state of the entity
143 | """
144 | new_entity_data = parse_jobj(type(entity), jobj)
145 | for property_name, property_value in new_entity_data.iteritems():
146 | setattr(entity, property_name, property_value)
147 | entity.put()
148 | return make_jobj(entity)
149 |
--------------------------------------------------------------------------------
/DEONE/trading.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 |
5 | from ohlc import OHLCWidget
6 | from querytable import QueryTableWidget
7 |
8 | class TradingWidget(QtGui.QWidget):
9 | def __init__(self, parent):
10 | super(QtGui.QWidget, self).__init__(parent)
11 |
12 | self.setObjectName("tabTrading")
13 | mainLayout = QtGui.QHBoxLayout()
14 |
15 | vLayoutOHLC = QtGui.QVBoxLayout()
16 | self.ohlc = OHLCWidget(self)
17 | self.ohlc.setObjectName("OHLCWidget")
18 | vLayoutOHLC.addWidget(self.ohlc)
19 |
20 | self.tblWidgetMarket = QueryTableWidget(self)
21 | self.tblWidgetMarket.setObjectName("tblWidgetMarket")
22 | vLayoutOHLC.addWidget(self.tblWidgetMarket)
23 |
24 | mainLayout.addLayout(vLayoutOHLC)
25 |
26 | vLayoutInfo = QtGui.QVBoxLayout()
27 |
28 | self.label_Title = QtGui.QLabel(self)
29 | vLayoutInfo.addWidget(self.label_Title)
30 | self.label_FX = QtGui.QLabel(self)
31 | vLayoutInfo.addWidget(self.label_FX)
32 | self.tblWidgetFX = QueryTableWidget(self)
33 | self.tblWidgetFX.setObjectName("tblWidgetFX")
34 | vLayoutInfo.addWidget(self.tblWidgetFX)
35 |
36 | self.label_JY = QtGui.QLabel(self)
37 | vLayoutInfo.addWidget(self.label_JY)
38 | self.tblWidgetJY = QueryTableWidget(self)
39 | self.tblWidgetJY.setObjectName("tblWidgetJY")
40 | vLayoutInfo.addWidget(self.tblWidgetJY)
41 |
42 | self.label_XX = QtGui.QLabel(self)
43 | vLayoutInfo.addWidget(self.label_XX)
44 | self.tblWidgetXX = QueryTableWidget(self)
45 | self.tblWidgetXX.setObjectName("tblWidgetXX")
46 | vLayoutInfo.addWidget(self.tblWidgetXX)
47 |
48 | self.label_ZS = QtGui.QLabel(self)
49 | vLayoutInfo.addWidget(self.label_ZS)
50 | self.tblWidgetZS = QueryTableWidget(self)
51 | self.tblWidgetZS.setObjectName("tblWidgetZS")
52 | vLayoutInfo.addWidget(self.tblWidgetZS)
53 |
54 | mainLayout.addLayout(vLayoutInfo)
55 | self.setLayout(mainLayout)
56 |
57 | self.retranslateUi()
58 |
59 | QtCore.QObject.connect(self.tblWidgetXX, QtCore.SIGNAL("selectionChanged()"), self.tblWidgetXX_selectionChanged)
60 | self.init()
61 |
62 | def retranslateUi(self):
63 | self.label_Title.setText(QtGui.QApplication.translate("MainWindow", "DeOne 量化策略交易模型", None, QtGui.QApplication.UnicodeUTF8))
64 | self.label_FX.setText(QtGui.QApplication.translate("MainWindow", "市场风险分析", None, QtGui.QApplication.UnicodeUTF8))
65 | self.label_JY.setText(QtGui.QApplication.translate("MainWindow", "交易数据", None, QtGui.QApplication.UnicodeUTF8))
66 | self.label_XX.setText(QtGui.QApplication.translate("MainWindow", "详细消息", None, QtGui.QApplication.UnicodeUTF8))
67 | self.label_ZS.setText(QtGui.QApplication.translate("MainWindow", "操作指示", None, QtGui.QApplication.UnicodeUTF8))
68 |
69 | def tblWidgetXX_selectionChanged(self):
70 | print("call tblWidgetXX_selectionChanged")
71 | row=self.tblWidgetXX.currentRow()
72 | code = str(self.tblWidgetXX.item(row,0).text())
73 | print(code)
74 | #日期 股票代码 公司名称 开盘价格 最高价格 最低价格 收盘价格 成交量
75 | self.ohlc.query(u'select 日期,开盘价格,最高价格,最低价格,收盘价格,成交量 from StockHist where 股票代码=? order by 日期 asc'.encode('utf-8'), (code,))
76 | self.tblWidgetZS.query(u'select 日期,初始买入,最低买入,建仓价位,目标价位,最高目标,止损价位 from Suggestion where 股票代码=? and 日期=?'.encode('utf-8'), (code, self.date,))
77 |
78 | def init(self):
79 | self.date = '2009-07-10'
80 | #日期 市场代码 开盘 最高 最低 收盘 成交量 上涨个股 下跌个股 平盘个股 交易类型I 交易类型II 交易类型III 交易类型IV 风险指标 动能指标
81 | self.tblWidgetMarket.query(u'select 日期,市场代码,开盘,最高,最低,收盘,成交量,上涨个股,下跌个股,平盘个股 from Market where 日期=?'.encode('utf-8'), (self.date,))
82 | self.tblWidgetFX.query(u'select 日期,市场代码,风险指标,动能指标 from Market where 日期=?'.encode('utf-8'), (self.date,))
83 | self.tblWidgetJY.query(u'select 日期,市场代码,交易类型I,交易类型II,交易类型III,交易类型IV from Market where 日期=?'.encode('utf-8'), (self.date,))
84 | #日期 股票代码 公司名称 交易类型 初始买入 最低买入 建仓价位 目标价位 最高目标 止损价位
85 | self.tblWidgetXX.query(u'select 股票代码,公司名称,交易类型 from Suggestion where 日期=?'.encode('utf-8'), (self.date,))
86 |
87 | #===============================================================================
88 | # Example
89 | #===============================================================================
90 | if __name__ == '__main__':
91 | import sys
92 | from PyQt4.QtGui import QMainWindow, QApplication
93 |
94 | class ApplicationWindow(QMainWindow):
95 | def __init__(self):
96 | QMainWindow.__init__(self)
97 | self.widget = TradingWidget(self)
98 | self.widget.setFocus()
99 | self.setCentralWidget(self.widget)
100 |
101 | app = QApplication(sys.argv)
102 | win = ApplicationWindow()
103 | win.show()
104 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/DEONE/querytable.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui, Qt
4 | from matplotlibwidget import MatplotlibWidget
5 |
6 | import sqlite3
7 | import datetime
8 |
9 | class QueryTableWidget(QtGui.QTableWidget):
10 | def __init__(self, parent):
11 | super(QtGui.QTableWidget, self).__init__(parent)
12 | self.setObjectName("DBTableWidget")
13 |
14 | self.horizontalHeader().setStretchLastSection(True)
15 | self.setSortingEnabled(True)
16 | self.sortByColumn(0, QtCore.Qt.AscendingOrder)
17 |
18 | self.setShowGrid(True)
19 | self.verticalHeader().hide()
20 | self.verticalHeader().setDefaultSectionSize(20)
21 | self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
22 | self.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
23 | self.setAlternatingRowColors(True)
24 | self.verticalHeader().setResizeMode(Qt.QHeaderView.Fixed)
25 | #self.setHorizontalScrollBarPolicy(Qt.Qt.ScrollBarAlwaysOff)
26 | self.setMouseTracking(True)
27 | #self.setEnabled(False)
28 |
29 | def keyPressEvent(self, event) :
30 | print("keyPressEvent")
31 | #print(event.modifiers(), event.key())
32 | if event.modifiers() == Qt.Qt.AltModifier :
33 | if event.key() == Qt.Qt.Key_Up :
34 | print("alt+up")
35 | elif event.key() == Qt.Qt.Key_Down :
36 | print("alt+down")
37 | else :
38 | Qt.QTableWidget.keyPressEvent(self, event)
39 |
40 | def mousePressEvent(self, event):
41 | print("mousePressEvent")
42 | if event.button() == Qt.Qt.LeftButton and self.indexAt(event.pos()).row() > -1 :
43 | print(event.button, self.indexAt(event.pos()).row())
44 | Qt.QTableWidget.mousePressEvent(self, event)
45 |
46 | def selectionChanged(self, new, old):
47 | print("selectionChanged", new, old)
48 | #row=self.currentRow()
49 | #col=self.currentColumn()
50 | #print(self.item(row,0).text())
51 | #print(self.item(row,2).text())
52 | self.emit(QtCore.SIGNAL("selectionChanged()"))
53 | Qt.QTableWidget.selectionChanged(self, new, old)
54 |
55 | def query(self, sql, parameters=None):
56 | self.clear()
57 |
58 | # select data from sqlite3 db
59 | deone_db = r'data/DEONE.db'
60 | con = sqlite3.connect(deone_db)
61 | con.text_factory=str
62 | con.row_factory = sqlite3.Row
63 | cur = con.cursor()
64 | #t = u'深发展A'.encode('utf-8')
65 | #cur.execute(u'select * from StockHist where 股票名称=?'.encode('utf-8'), (t,))
66 | #cur.execute('select * from StockHist')
67 | cur.execute(sql, parameters)
68 |
69 | irow = 0
70 | ncols = 0
71 | for irow in xrange(100):
72 | row = cur.fetchone()
73 | if row == None:
74 | break
75 | #print(row)
76 | ncols = len(row)
77 | if irow == 0:
78 | self.setColumnCount(ncols)
79 | #self.setStretchLastSection(True)
80 |
81 | for ind in xrange(ncols):
82 | s = row.keys()[ind].decode('utf-8')
83 | item = QtGui.QTableWidgetItem(s)
84 | item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
85 | self.setHorizontalHeaderItem(ind, item)
86 | self.insertRow(irow)
87 | for icol in xrange(ncols):
88 | s = row[icol].decode('utf-8')
89 | item = QtGui.QTableWidgetItem(s)
90 | #item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable)
91 | item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
92 | item.setWhatsThis("You can change this task's comment, start time and end time.");
93 | self.setItem(irow,icol,item);
94 | con.close()
95 | self.setRowCount(irow)
96 |
97 | # Sorting columns
98 | for col in range(ncols):
99 | self.resizeColumnToContents(col)
100 | self.setCurrentItem(self.item(0,0))
101 |
102 | #===============================================================================
103 | # Example
104 | #===============================================================================
105 | if __name__ == '__main__':
106 | import sys
107 | from PyQt4.QtGui import QMainWindow, QApplication
108 |
109 | class ApplicationWindow(QMainWindow):
110 | def __init__(self):
111 | QMainWindow.__init__(self)
112 | self.tblwidget = QueryTableWidget(self)
113 | self.tblwidget.setFocus()
114 | self.setCentralWidget(self.tblwidget)
115 |
116 | app = QApplication(sys.argv)
117 | win = ApplicationWindow()
118 | win.show()
119 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/DEONE/sector.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 |
5 | from querytable import QueryTableWidget
6 |
7 | class SectorWidget(QtGui.QWidget):
8 | def __init__(self, parent):
9 | super(QtGui.QWidget, self).__init__(parent)
10 |
11 | self.setObjectName("tabSector")
12 |
13 | mainLayout = QtGui.QHBoxLayout()
14 |
15 | vLayoutLeft = QtGui.QVBoxLayout()
16 | self.label_DX = QtGui.QLabel(self)
17 | vLayoutLeft.addWidget(self.label_DX)
18 |
19 | hLayoutQuery = QtGui.QHBoxLayout()
20 | self.label_RQ = QtGui.QLabel(self)
21 | hLayoutQuery.addWidget(self.label_RQ)
22 |
23 | self.lineEdit = QtGui.QLineEdit(self)
24 | self.lineEdit.setGeometry(QtCore.QRect(80, 0, 113, 20))
25 | self.lineEdit.setObjectName("lineEdit")
26 | hLayoutQuery.addWidget(self.lineEdit)
27 |
28 | self.pushButtonQuery = QtGui.QPushButton(self)
29 | self.pushButtonQuery.setGeometry(QtCore.QRect(210, 0, 75, 23))
30 | self.pushButtonQuery.setObjectName("pushButtonQuery")
31 | hLayoutQuery.addWidget(self.pushButtonQuery)
32 |
33 | vLayoutLeft.addLayout(hLayoutQuery)
34 |
35 | self.tblWidgetBK = QueryTableWidget(self)
36 | self.tblWidgetBK.setObjectName("tblWidgetBK")
37 | vLayoutLeft.addWidget(self.tblWidgetBK)
38 |
39 | mainLayout.addLayout(vLayoutLeft)
40 |
41 | vLayoutRight = QtGui.QVBoxLayout()
42 | self.label_GG = QtGui.QLabel(self)
43 | vLayoutRight.addWidget(self.label_GG)
44 | self.tblWidgetGG = QueryTableWidget(self)
45 | self.tblWidgetGG.setObjectName("tblWidgetGG")
46 | vLayoutRight.addWidget(self.tblWidgetGG)
47 |
48 | self.label_ZS = QtGui.QLabel(self)
49 | vLayoutRight.addWidget(self.label_ZS)
50 | self.tblWidgetZS = QueryTableWidget(self)
51 | self.tblWidgetZS.setObjectName("tblWidgetZS")
52 | vLayoutRight.addWidget(self.tblWidgetZS)
53 |
54 | mainLayout.addLayout(vLayoutRight)
55 |
56 | self.setLayout(mainLayout)
57 |
58 | self.retranslateUi()
59 |
60 | QtCore.QObject.connect(self.pushButtonQuery, QtCore.SIGNAL("pressed()"), self.doQuery)
61 | #self.emit(QtCore.SIGNAL("selectionChanged()"))
62 | QtCore.QObject.connect(self.tblWidgetBK, QtCore.SIGNAL("selectionChanged()"), self.tblWidgetBK_selectionChanged)
63 | QtCore.QObject.connect(self.tblWidgetGG, QtCore.SIGNAL("selectionChanged()"), self.tblWidgetGG_selectionChanged)
64 |
65 |
66 | self.lineEdit.setText('2009-07-10')
67 | self.doQuery()
68 |
69 | def retranslateUi(self):
70 | self.label_DX.setText(QtGui.QApplication.translate("MainWindow", "板块动向", None, QtGui.QApplication.UnicodeUTF8))
71 | self.label_RQ.setText(QtGui.QApplication.translate("MainWindow", "交易日期", None, QtGui.QApplication.UnicodeUTF8))
72 | self.pushButtonQuery.setText(QtGui.QApplication.translate("MainWindow", "查询", None, QtGui.QApplication.UnicodeUTF8))
73 | self.label_GG.setText(QtGui.QApplication.translate("MainWindow", "板块个股", None, QtGui.QApplication.UnicodeUTF8))
74 | self.label_ZS.setText(QtGui.QApplication.translate("MainWindow", "操作指示", None, QtGui.QApplication.UnicodeUTF8))
75 |
76 | def doQuery(self):
77 | print("called doQuery")
78 | date = str(self.lineEdit.text())
79 | #self.tblWidgetBK.query(u'select * from CategoryHist where 股票代码=?'.encode('utf-8'), (date,))
80 | self.tblWidgetBK.query(u'select * from CategoryHist'.encode('utf-8'), ())
81 | pass
82 |
83 | def tblWidgetBK_selectionChanged(self):
84 | print("called tblWidgetBK_selectionChanged")
85 | row=self.tblWidgetBK.currentRow()
86 | code = str(self.tblWidgetBK.item(row,0).text())
87 | print(code)
88 | #select * from StockHist where 股票代码 in (select 股票代码 from StockCategory where 板块代码='YH')
89 | self.tblWidgetGG.query(u'select * from StockHist where 股票代码 in (select 股票代码 from StockCategory where 板块代码=?) order by 日期 desc'.encode('utf-8'), (code,))
90 |
91 | def tblWidgetGG_selectionChanged(self):
92 | print("called tblWidgetGG_selectionChanged")
93 | row=self.tblWidgetGG.currentRow()
94 | code = str(self.tblWidgetGG.item(row,2).text())
95 | print(code)
96 | self.tblWidgetZS.query(u'select * from Suggestion where 股票代码=? order by 日期 desc'.encode('utf-8'), (code,))
97 |
98 | #===============================================================================
99 | # Example
100 | #===============================================================================
101 | if __name__ == '__main__':
102 | import sys
103 | from PyQt4.QtGui import QMainWindow, QApplication
104 |
105 | class ApplicationWindow(QMainWindow):
106 | def __init__(self):
107 | QMainWindow.__init__(self)
108 | self.widget = SectorWidget(self)
109 | self.widget.setFocus()
110 | self.setCentralWidget(self.widget)
111 |
112 | app = QApplication(sys.argv)
113 | win = ApplicationWindow()
114 | win.show()
115 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/appspot/suas/auth_handlers.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/env python2.5
2 | #-----------------------
3 |
4 | """
5 | Provide user authentication request handlers. Their names should be
6 | self-explanatory. You might need to adapt to suit your app's need.
7 |
8 | Utility method: login_required -- for use as a decorator.
9 | """
10 |
11 | from google.appengine.ext.webapp import template
12 |
13 | from session import RequestHandler
14 | from users import User, UserSignup, salt_n_hash, signup_id
15 |
16 | import logging
17 |
18 | # Adapt to use your app's view.
19 | SIGNUP_VIEW = template.Template("""
20 | Sign up
21 |
22 | {% if session.flash_msg %}
23 | {{ session.flash_msg }}
24 | {% endif %}
25 |
32 | Login
33 |
34 | """)
35 |
36 | LOGIN_VIEW = template.Template("""
37 | Login
38 |
39 | {% if session.flash_msg %}
40 | {{ session.flash_msg }}
41 | {% endif %}
42 |
47 | Sign up
48 |
49 | """)
50 |
51 | class Signup(RequestHandler):
52 | def get(self):
53 | ctx = template.Context({"session": self.session})
54 | self.response.out.write(SIGNUP_VIEW.render(ctx))
55 |
56 | def post(self):
57 | nickname = self.request.get('nickname')
58 | email = self.request.get('email')
59 | password = self.request.get('password')
60 | password2 = self.request.get('password2')
61 | if password2 != password:
62 | self.session.start(None)
63 | self.session[ 'flash_msg' ] = 'Password fields did not match.
'
64 | self.redirect('/signup')
65 | return
66 | user = User.get_or_insert(nickname=nickname,
67 | email=email,
68 | pwd=salt_n_hash(password),
69 | key_name=nickname)
70 | self.session.start(None)
71 | if not User.authenticate(nickname, password):
72 | self.session[ 'flash_msg' ] = 'Sorry, the nickname you chose is already taken.
'
73 | self.redirect(self.request.url)
74 | return
75 | id = signup_id(nickname)
76 | signup = UserSignup(user=user, key_name=id)
77 | signup.put()
78 | confirm_url = self.request.relative_url('confirmsignup?id='+id)
79 | logging.info("user:" + nickname + " password:" + password + " confirm_url:" +confirm_url)
80 | from google.appengine.api import mail
81 | #sender = 'Registrar '
82 | sender = 'deone.appspot@gmail.com'
83 | subject = 'Confirm your registration'
84 | body = \
85 | 'Hello %s,\n\n' % nickname + \
86 | 'To confirm your registration, please visit the link below:\n\n' + \
87 | '<%s>\n' % confirm_url
88 | mail.send_mail( sender, email, subject, body )
89 | self.session['flash_msg'] = \
90 | 'Thank you for signing up, %s! A confirmation ' % nickname + \
91 | 'message is on its way to your email inbox. It will contain a link ' + \
92 | 'which you will need to visit in order to complete your registration.
' + \
93 | 'See you soon!
'
94 | self.redirect('/')
95 |
96 |
97 | class ConfirmSignup(RequestHandler):
98 | def get(self):
99 | id = self.request.get('id')
100 | signup = UserSignup.get_by_key_name(id)
101 | if not signup:
102 | self.error(401)
103 | return
104 | user = signup.user
105 | user.suspended = False
106 | user.put()
107 | signup.delete()
108 | self.session.start(user)
109 | self.session['flash_msg'] = 'Your account has been confirmed.
'
110 | #self.redirect('/user/' + user.nickname)
111 | self.redirect('/')
112 |
113 |
114 | class Login(RequestHandler):
115 | """Handle /login?redirect=%s request. You will need to define your GET method handler."""
116 | def get(self):
117 | ctx = template.Context({"session": self.session})
118 | self.response.out.write(LOGIN_VIEW.render(ctx))
119 |
120 | def post(self):
121 | user = User.authenticate(self.request.get('nickname'), self.request.get('password'))
122 | if user and not user.suspended:
123 | self.session.start(user)
124 | self.session['flash_msg'] = 'Welcome, %s!
' % user.nickname
125 | #redirect = self.request.get('redirect')
126 | #self.redirect(redirect)
127 | self.redirect('/')
128 | else:
129 | self.session.start( None )
130 | self.session['flash_msg'] = 'Incorrect nickname/password combination. Sorry!
'
131 | self.redirect(self.request.url)
132 |
133 |
134 | def login_required(handler_method):
135 | """
136 | A decorator to require that a user be logged in to access a handler method.
137 |
138 | >>> @login_required
139 | ... def get(self):
140 | ... self.response.out.write('Hello, ' + self.session.user.nickname)
141 |
142 | We will redirect to a login page if the user is not logged in.
143 | """
144 | def check_login(self, *args):
145 | if not self.session.user:
146 | self.redirect('/login?' + 'redirect=' + self.request.url)
147 | else:
148 | handler_method(self, *args)
149 | return check_login
150 |
151 |
152 | class Logout(RequestHandler):
153 | def get(self):
154 | if not self.session.user:
155 | self.error(404)
156 | return
157 | nickname = self.session.user.nickname
158 | self.session.start(None)
159 | self.session['flash_msg'] = 'Goodbye, %s!
' % nickname
160 | self.redirect('/login')
161 |
162 | # Add this to your app's routes.
163 | ROUTES = [
164 | ('/signup', Signup),
165 | ('/confirmsignup', ConfirmSignup),
166 | ('/login', Login),
167 | ('/logout', Logout)
168 | ]
169 |
170 |
--------------------------------------------------------------------------------
/appspot/suas/session.py:
--------------------------------------------------------------------------------
1 | #/usr/bin/env python2.5
2 | #-------------------
3 |
4 | """
5 | Session based solely on signed cookie (no Datastore/memcache)
6 |
7 | A CookieSession persists between HTTP requests & responses by using
8 | signed cookies referring to a particular session ID. All session-related
9 | cookies are signed using the SID and the site's secret key, ensuring that:
10 | * In a key=value cookie, the value is really intended for the
11 | particular key.
12 | * Cookies are really pertinent to a particular SID that signed it.
13 | * Cookies were really issued by the server for a particular session.
14 |
15 | The RequestHandler class extends webapp.RequestHandler to provide
16 | access to the current session as an instance variable.
17 |
18 | Limitations:
19 | * currently unsigned cookies are simply ignored
20 | """
21 |
22 | from time import time, gmtime
23 | from calendar import timegm
24 | from hashlib import md5
25 |
26 | from google.appengine.ext import webapp
27 |
28 | from signedcookie import SignedCookie, BadSignatureError, SIG_LEN
29 | from users import User
30 |
31 | SECRET_KEY = 'Open, sesame!'
32 | # A secret key unique to your application.
33 |
34 | SESSION_TTL = 604800 # 604800s = 7d
35 | # the life time of a "persistent" session authenticator, in seconds.
36 | # As long as the user comes back within that time frame, we will nenew
37 | # his session for a full period. In case he doesn't, he will have to
38 | # login again.
39 |
40 | SID_TTL = 900 # 900s = 15m
41 | # the minimum interval between requests after which
42 | # we expire the old and issue a new ID.
43 |
44 |
45 | class NoSIDError(Exception):
46 | pass
47 |
48 | class CookieSession:
49 | """
50 | Provides dictionary-like storage/access to signed cookies.
51 | The keys 'SID', 'user', and 'atime' are internally used by the
52 | session object and the request handler, do not change them.
53 |
54 | Attributes:
55 | user the current user
56 |
57 | flash_msg a possible flash message set by the previous page,
58 | automatically popped from the 'flash_msg' cookie
59 |
60 | cookies the underlying cookies
61 |
62 | Class method:
63 | load(request, response) returns a session instance loaded from cookies
64 |
65 | Instance methods:
66 | start(user, persist=False)
67 | end()
68 |
69 | Do not set/del the same key multiple times, as each __setitem__
70 | call adds a 'Set-Cookie' header in the response. This is due
71 | to webapp's inability to deals directly with cookies or delete
72 | multi-valued headers. You can modify the cookies attribute
73 | directly which do not modify the response, then call set_cookie
74 | to add a header manually.
75 |
76 | Also, since cookie values are strings, you will need to do
77 | serialization/deserialization yourself, if necessary.
78 | """
79 | def __init__(self, user, response):
80 | self.user = user
81 | self.response = response
82 | self.persist = False
83 | id = self.__gen_id()
84 | self.cookies = SignedCookie(SECRET_KEY + id)
85 | self.cookies['SID'] = id
86 | self.cookies['atime'] = repr( timegm(gmtime()) )
87 |
88 | def __gen_id(self):
89 | # This is predictable, but that's ok.
90 | # We just need it to be unique, *especially* for different users
91 | if self.user is None:
92 | name = 'Anonymous'
93 | else:
94 | name = self.user.nickname
95 | return md5( name + repr(time()) ).hexdigest()
96 |
97 | def __getitem__(self, key):
98 | return self.cookies[key].value
99 |
100 | def get(self, key, default=None):
101 | v = self.cookies.get(key)
102 | if v is not None:
103 | return v.value
104 | else:
105 | return default
106 |
107 | def pop(self, key, default=None):
108 | try:
109 | v = self[key]
110 | except KeyError:
111 | if default is None:
112 | raise
113 | else:
114 | return default
115 | else:
116 | self.expire_cookie(key)
117 | return v
118 |
119 | def __setitem__(self, key, value):
120 | self.cookies[key] = value
121 | if self.persist:
122 | self.cookies[key]['max-age'] = SESSION_TTL
123 | self.set_cookie( key )
124 |
125 | def set_cookie(self, key):
126 | self.response.headers.add_header(
127 | 'Set-Cookie', self.cookies[key].output()[12:]
128 | )
129 |
130 | def __delitem__(self, key):
131 | self.expire_cookie(key)
132 |
133 | def expire_cookie(self, key):
134 | self.cookies[key]['max-age'] = 0
135 | self.set_cookie( key )
136 |
137 | @classmethod
138 | def load(klass, request, response):
139 | """
140 | Load the session cookies from the request,
141 | returning a new instance with the response.
142 | """
143 | try:
144 | id = request.cookies['SID'][1:-SIG_LEN-1]
145 | except KeyError:
146 | raise NoSIDError
147 | else:
148 | c = SignedCookie(SECRET_KEY + id)
149 | c.load(request.environ['HTTP_COOKIE'])
150 | try:
151 | user = User.get_by_key_name(c['user'].value)
152 | except KeyError:
153 | user = None
154 | session = klass(user, response)
155 | session.cookies = c
156 | return session
157 |
158 | def start(self, user, persist=False):
159 | """
160 | Log the user in, where user is an object with a nickname
161 | attribute.
162 |
163 | If persist=False, the browser will by default log the
164 | user out when it closes; otherwise we will keep him logged
165 | in as long as he comes back within SESSION_TTL.
166 | """
167 | self.user = user
168 | self.persist = persist
169 | if user is None:
170 | self.set_cookie( 'SID' )
171 | self.set_cookie( 'atime' )
172 | if self.cookies.has_key ( 'user' ):
173 | self.expire_cookie ( 'user' )
174 | else:
175 | self.cookies['user'] = user.nickname
176 | self.regen()
177 | ## TODO: Cache-control
178 |
179 | def regen(self):
180 | """Regenerate a new SID"""
181 | id = self.__gen_id()
182 | c = SignedCookie(SECRET_KEY + id)
183 | for key, morsel in self.cookies.items():
184 | c[key] = morsel.value
185 | c[key].update( morsel.items() ) ## preserves Max-Age
186 | c['SID'] = id
187 | c['atime'] = repr( timegm(gmtime()) )
188 | self.cookies = c
189 | for k in self.cookies.keys():
190 | self.set_cookie( k )
191 |
192 | def end(self):
193 | """Expire all cookies"""
194 | self.user = None
195 | for key in self.cookies.keys():
196 | self.expire_cookie( key )
197 |
198 |
199 | class RequestHandler(webapp.RequestHandler):
200 | """
201 | A session-capable request handler.
202 |
203 | Attribute:
204 | session
205 | """
206 | def initialize(self, request, response):
207 | super(RequestHandler, self).initialize(request, response)
208 | try:
209 | self.session = CookieSession.load(request, response)
210 | except NoSIDError:
211 | self.session = CookieSession(None, self.response)
212 | except BadSignatureError:
213 | self.session = CookieSession(None, self.response)
214 | else:
215 | self.session.flash_msg = self.session.pop('flash_msg', '')
216 | now = timegm( gmtime() )
217 | try:
218 | atime = int( self.session['atime'] )
219 | except ValueError:
220 | self.session.end()
221 | return
222 | if now - atime > SESSION_TTL:
223 | self.session.end()
224 | return
225 | if now - atime > SID_TTL:
226 | self.session.regen()
227 |
228 |
--------------------------------------------------------------------------------
/DEONE/ohlc.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 |
5 | from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
6 | from matplotlib.figure import Figure
7 | from matplotlib.pyplot import setp
8 | from matplotlib.ticker import Formatter
9 | from matplotlib.dates import YearLocator, MonthLocator, WeekdayLocator, DayLocator, DateFormatter, drange, MONDAY, num2date, date2num
10 |
11 | import sqlite3
12 | import datetime
13 |
14 | class OHLCWidget(Canvas):
15 | def __init__(self, parent=None):
16 | from matplotlib import rcParams
17 | rcParams['font.size'] = 6
18 |
19 | self.figure = Figure(facecolor='w',edgecolor='w')
20 | self.figure.subplots_adjust(left=0.1, right=0.9, wspace=0.6)
21 |
22 | Canvas.__init__(self, self.figure)
23 | self.setParent(parent)
24 | self.figure.canvas.mpl_connect('motion_notify_event', self.onmove)
25 |
26 | Canvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
27 | Canvas.updateGeometry(self)
28 |
29 | def sizeHint(self):
30 | w, h = self.get_width_height()
31 | return QtCore.QSize(w, h)
32 |
33 | def minimumSizeHint(self):
34 | return QtCore.QSize(10, 10)
35 |
36 | def onmove(self, event):
37 | if event.xdata == None or event.inaxes == self.info_axes:
38 | return
39 | #print(event.x, event.xdata, event.inaxes)
40 | ind = int(round(event.xdata))
41 | self.info(ind)
42 |
43 | def info(self, ind):
44 | print("call info", ind)
45 | if ind>=len(self.rows) or ind<0: return ''
46 | row = self.rows[ind]
47 | if row == None:
48 | return
49 | print(row)
50 | #today = datetime.datetime(1899,12,30)+datetime.timedelta(days=float(row[0]))
51 | y,m,d = row[0].split('-')
52 | today = datetime.datetime(int(y), int(m), int(d))
53 | open = float(row[1])
54 | high = float(row[2])
55 | low = float(row[3])
56 | close = float(row[4])
57 | volume= int(float(row[5]))
58 | print(today, open, high, low, close, volume)
59 |
60 | self.info_axes.clear()
61 | self.info_axes.text(0.1, 0.95, 'Date')
62 | self.info_axes.text(0.15, 0.93, today.strftime("%Y%m%d"), color='b')
63 | self.info_axes.text(0.1, 0.90, 'Open')
64 | self.info_axes.text(0.15, 0.88, open, color='b')
65 | self.info_axes.text(0.1, 0.85, 'High')
66 | self.info_axes.text(0.15, 0.83, high, color='b')
67 | self.info_axes.text(0.1, 0.80, 'Low')
68 | self.info_axes.text(0.15, 0.78, low, color='b')
69 | self.info_axes.text(0.1, 0.75, 'Close')
70 | self.info_axes.text(0.15, 0.73, close, color='b')
71 | self.info_axes.text(0.1, 0.70, 'Volume')
72 | self.info_axes.text(0.15, 0.68, volume, color='b')
73 | self.info_axes.set_xticklabels([])
74 | self.info_axes.set_yticklabels([])
75 |
76 | self.figure.canvas.draw()
77 |
78 | def query(self, sql, parameters=None):
79 | self.figure.clear()
80 |
81 | self.vol_axes = self.figure.add_axes([0.06, 0.01, 0.87, 0.24], axisbg='#cccccc', autoscale_on=True)
82 | self.ohlc_axes = self.figure.add_axes([0.06, 0.25, 0.87, 0.74], axisbg='#cccccc', autoscale_on=True)
83 | self.info_axes = self.figure.add_axes([0.93, 0.01, 0.06, 0.98], axisbg='#cccccc', autoscale_on=True)
84 |
85 | # select data from sqlite3 db
86 | deone_db = r'data/DEONE.db'
87 | con = sqlite3.connect(deone_db)
88 | con.text_factory=str
89 | cur = con.cursor()
90 | #t = u'深发展A'.encode('utf-8')
91 | #cur.execute(u'select * from StockHist where 股票名称=?'.encode('utf-8'), (t,))
92 | #cur.execute('select * from StockHist')
93 | cur.execute(sql, parameters)
94 |
95 | dates = []
96 | ind = 0
97 | self.rows = []
98 | for ind in xrange(100):
99 | row = cur.fetchone()
100 | if row == None:
101 | break
102 | #print(row)
103 | self.rows += [row]
104 |
105 | #today = datetime.datetime(1899,12,30)+datetime.timedelta(days=float(row[0]))
106 | y,m,d = row[0].split('-')
107 | today = datetime.datetime(int(y), int(m), int(d))
108 | dates += [today]
109 | open = float(row[1])
110 | high = float(row[2])
111 | low = float(row[3])
112 | close = float(row[4])
113 | volume= int(float(row[5]))
114 | #print(ind, today, open, high, low, close, volume)
115 |
116 | if close>open:
117 | col = "r"
118 | elif close=len(self.dates) or ind<0: return ''
156 | s = self.dates[ind].strftime(self.fmt)
157 | #print(x, ind, s)
158 | return s
159 |
160 | #===============================================================================
161 | # Example
162 | #===============================================================================
163 | if __name__ == '__main__':
164 | import sys
165 | from PyQt4.QtGui import QMainWindow, QApplication
166 |
167 | class ApplicationWindow(QMainWindow):
168 | def __init__(self):
169 | QMainWindow.__init__(self)
170 | self.ohlc = OHLCWidget(self)
171 | self.ohlc.setFocus()
172 | self.setCentralWidget(self.ohlc)
173 | code ='1.0'
174 | self.ohlc.query(u'select 日期,开盘,最高,最低,收盘,成交量 from StockHist where 股票代码=?'.encode('utf-8'), (code,))
175 |
176 |
177 | app = QApplication(sys.argv)
178 | win = ApplicationWindow()
179 | win.show()
180 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/DEONE/deone.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'DEONE.ui'
4 | #
5 | # Created: Mon Aug 02 09:11:58 2010
6 | # by: PyQt4 UI code generator 4.5.4
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | from PyQt4 import QtCore, QtGui
11 | import deone_rc
12 |
13 | from trading import TradingWidget
14 | from sector import SectorWidget
15 | from stock import StockWidget
16 | from market import MarketWidget
17 |
18 | class Ui_MainWindow(QtGui.QMainWindow):
19 | def __init__(self, parent=None):
20 | QtGui.QMainWindow.__init__(self)
21 |
22 | self.setObjectName("MainWindow")
23 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Maximum, QtGui.QSizePolicy.Maximum)
24 | sizePolicy.setHorizontalStretch(0)
25 | sizePolicy.setVerticalStretch(0)
26 | self.setSizePolicy(sizePolicy)
27 | self.setDocumentMode(False)
28 |
29 | self.menubar = QtGui.QMenuBar(self)
30 | self.menubar.setObjectName("menubar")
31 | self.menuView = QtGui.QMenu(self.menubar)
32 | self.menuView.setObjectName("menuView")
33 | self.setMenuBar(self.menubar)
34 | self.statusbar = QtGui.QStatusBar(self)
35 | self.statusbar.setSizeGripEnabled(True)
36 | self.statusbar.setObjectName("statusbar")
37 | self.setStatusBar(self.statusbar)
38 | self.toolBar = QtGui.QToolBar(self)
39 | self.toolBar.setObjectName("toolBar")
40 | self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)
41 | self.actionStock = QtGui.QAction(self)
42 | icon = QtGui.QIcon()
43 | icon.addPixmap(QtGui.QPixmap(":/icons/image/icons/log.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
44 | self.actionStock.setIcon(icon)
45 | self.actionStock.setObjectName("actionStock")
46 | self.actionSector = QtGui.QAction(self)
47 | icon1 = QtGui.QIcon()
48 | icon1.addPixmap(QtGui.QPixmap(":/icons/image/icons/misc.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
49 | self.actionSector.setIcon(icon1)
50 | self.actionSector.setObjectName("actionSector")
51 | self.actionTrading = QtGui.QAction(self)
52 | icon2 = QtGui.QIcon()
53 | icon2.addPixmap(QtGui.QPixmap(":/icons/image/icons/kchart.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
54 | self.actionTrading.setIcon(icon2)
55 | self.actionTrading.setObjectName("actionTrading")
56 | self.actionMarket = QtGui.QAction(self)
57 | icon3 = QtGui.QIcon()
58 | icon3.addPixmap(QtGui.QPixmap(":/icons/image/icons/terminal.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
59 | self.actionMarket.setIcon(icon3)
60 | self.actionMarket.setObjectName("actionMarket")
61 | self.actionQuit = QtGui.QAction(self)
62 | icon5 = QtGui.QIcon()
63 | icon5.addPixmap(QtGui.QPixmap(":/icons/image/icons/exit.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
64 | self.actionQuit.setIcon(icon5)
65 | self.actionQuit.setObjectName("actionQuit")
66 | self.menuView.addAction(self.actionTrading)
67 | self.menuView.addAction(self.actionStock)
68 | self.menuView.addAction(self.actionSector)
69 | self.menuView.addAction(self.actionMarket)
70 | self.menuView.addSeparator()
71 | self.menuView.addAction(self.actionQuit)
72 | self.menubar.addAction(self.menuView.menuAction())
73 | self.toolBar.addAction(self.actionTrading)
74 | self.toolBar.addAction(self.actionStock)
75 | self.toolBar.addAction(self.actionSector)
76 | self.toolBar.addAction(self.actionMarket)
77 | self.toolBar.addSeparator()
78 | self.toolBar.addAction(self.actionQuit)
79 |
80 | self.connect(self.actionTrading, QtCore.SIGNAL("triggered()"), self.doTabTrading)
81 | self.connect(self.actionStock, QtCore.SIGNAL("triggered()"), self.doTabStock)
82 | self.connect(self.actionSector, QtCore.SIGNAL("triggered()"), self.doTabSector)
83 | self.connect(self.actionMarket, QtCore.SIGNAL("triggered()"), self.doTabMarket)
84 | self.connect(self.actionQuit, QtCore.SIGNAL("triggered()"), self.doQuit)
85 |
86 | self.tabWidget = QtGui.QTabWidget(self)
87 | self.tabWidget.setTabsClosable(False)
88 | self.tabWidget.setObjectName("tabWidget")
89 |
90 | self.tabTrading = TradingWidget(self.tabWidget)
91 | self.tabWidget.addTab(self.tabTrading, "")
92 |
93 | self.tabSector = SectorWidget(self.tabWidget)
94 | self.tabWidget.addTab(self.tabSector, "")
95 |
96 | self.tabStock = StockWidget(self.tabWidget)
97 | self.tabWidget.addTab(self.tabStock, "")
98 |
99 | self.tabMarket = MarketWidget(self.tabWidget)
100 | self.tabWidget.addTab(self.tabMarket, "")
101 | self.tabWidget.setCurrentIndex(0)
102 |
103 | self.setCentralWidget(self.tabWidget)
104 |
105 | self.retranslateUi()
106 | QtCore.QMetaObject.connectSlotsByName(self)
107 |
108 | def retranslateUi(self):
109 | self.setWindowTitle(QtGui.QApplication.translate("MainWindow", "DEONE", None, QtGui.QApplication.UnicodeUTF8))
110 | self.menuView.setTitle(QtGui.QApplication.translate("MainWindow", "功能", None, QtGui.QApplication.UnicodeUTF8))
111 | self.toolBar.setWindowTitle(QtGui.QApplication.translate("MainWindow", "工具条", None, QtGui.QApplication.UnicodeUTF8))
112 | self.actionStock.setText(QtGui.QApplication.translate("MainWindow", "股票", None, QtGui.QApplication.UnicodeUTF8))
113 | self.actionSector.setText(QtGui.QApplication.translate("MainWindow", "板块", None, QtGui.QApplication.UnicodeUTF8))
114 | self.actionTrading.setText(QtGui.QApplication.translate("MainWindow", "交易", None, QtGui.QApplication.UnicodeUTF8))
115 | self.actionMarket.setText(QtGui.QApplication.translate("MainWindow", "大盘", None, QtGui.QApplication.UnicodeUTF8))
116 |
117 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabTrading), QtGui.QApplication.translate("MainWindow", "交易", None, QtGui.QApplication.UnicodeUTF8))
118 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabSector), QtGui.QApplication.translate("MainWindow", "板块", None, QtGui.QApplication.UnicodeUTF8))
119 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabStock), QtGui.QApplication.translate("MainWindow", "股票", None, QtGui.QApplication.UnicodeUTF8))
120 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tabMarket), QtGui.QApplication.translate("MainWindow", "大盘", None, QtGui.QApplication.UnicodeUTF8))
121 | self.actionQuit.setText(QtGui.QApplication.translate("MainWindow", "退出", None, QtGui.QApplication.UnicodeUTF8))
122 |
123 | def doTabTrading(self):
124 | print("called doTabTrading")
125 | self.tabWidget.setCurrentIndex(1)
126 |
127 | def doTabStock(self):
128 | print("called doTabStock")
129 | self.tabWidget.setCurrentIndex(2)
130 |
131 | def doTabSector(self):
132 | print("called doTabSector")
133 | self.tabWidget.setCurrentIndex(3)
134 |
135 | def doTabMarket(self):
136 | print("called doTabMarket")
137 | self.tabWidget.setCurrentIndex(4)
138 |
139 | def doQuit(self):
140 | print("called doQuit")
141 | self.close()
142 |
143 | #===============================================================================
144 | # Example
145 | #===============================================================================
146 | if __name__ == "__main__":
147 | import sys
148 |
149 | app = QtGui.QApplication(sys.argv)
150 | window = Ui_MainWindow()
151 | window.showFullScreen()
152 | window.show()
153 | sys.exit(app.exec_())
154 |
--------------------------------------------------------------------------------
/DEONE/main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 | from deone import Ui_MainWindow
5 | import os
6 | import simplejson
7 | import sqlite3
8 |
9 | host = 'de-one.appspot.com'
10 | #host = 'localhost:8080'
11 |
12 | deone_db = 'data/DEONE.db'
13 |
14 | tables = ["Market", "Suggestion", "StockHist", "StockCategory", "CategoryHist",]
15 |
16 | class LoginWindow(QtGui.QMainWindow):
17 | def __init__(self):
18 | QtGui.QMainWindow.__init__(self)
19 |
20 | self.setGeometry(QtCore.QRect(0, 0, 200, 120))
21 |
22 | mainLayout = QtGui.QVBoxLayout()
23 |
24 | hLayoutUser = QtGui.QHBoxLayout()
25 | self.label_user = QtGui.QLabel(self)
26 | hLayoutUser.addWidget(self.label_user)
27 | self.lineEdit_user = QtGui.QLineEdit(self)
28 | self.lineEdit_user.setText("test")
29 | hLayoutUser.addWidget(self.lineEdit_user)
30 | mainLayout.addLayout(hLayoutUser)
31 |
32 | hLayoutPass = QtGui.QHBoxLayout()
33 | self.label_pass = QtGui.QLabel(self)
34 | hLayoutPass.addWidget(self.label_pass)
35 | self.lineEdit_pass = QtGui.QLineEdit(self)
36 | self.lineEdit_pass.setEchoMode(QtGui.QLineEdit.Password)
37 | self.lineEdit_pass.setText("test")
38 | hLayoutPass.addWidget(self.lineEdit_pass)
39 | mainLayout.addLayout(hLayoutPass)
40 |
41 | hLayoutCmd = QtGui.QHBoxLayout()
42 | self.pushButtonLogin = QtGui.QPushButton(self)
43 | hLayoutCmd.addWidget(self.pushButtonLogin)
44 | self.pushButtonExit = QtGui.QPushButton(self)
45 | hLayoutCmd.addWidget(self.pushButtonExit)
46 |
47 | self.label_msg = QtGui.QLabel(self)
48 | mainLayout.addWidget(self.label_msg)
49 |
50 | self.progbar = QtGui.QProgressBar(self)
51 | self.progbar.setProperty("value",QtCore.QVariant(0))
52 | mainLayout.addWidget(self.progbar)
53 |
54 | mainLayout.addLayout(hLayoutCmd)
55 |
56 | widget = QtGui.QWidget()
57 | widget.setLayout(mainLayout)
58 | self.setCentralWidget(widget)
59 |
60 | self.retranslateUi()
61 | QtCore.QObject.connect(self.pushButtonExit, QtCore.SIGNAL("pressed()"), self.doExit)
62 | QtCore.QObject.connect(self.pushButtonLogin, QtCore.SIGNAL("pressed()"), self.doLogin)
63 |
64 | self.progbar.hide()
65 | self.center()
66 |
67 | def retranslateUi(self):
68 | self.label_user.setText(QtGui.QApplication.translate("MainWindow", "用户:", None, QtGui.QApplication.UnicodeUTF8))
69 | self.label_pass.setText(QtGui.QApplication.translate("MainWindow", "密码:", None, QtGui.QApplication.UnicodeUTF8))
70 | self.pushButtonLogin.setText(QtGui.QApplication.translate("MainWindow", "登入", None, QtGui.QApplication.UnicodeUTF8))
71 | self.pushButtonExit.setText(QtGui.QApplication.translate("MainWindow", "退出", None, QtGui.QApplication.UnicodeUTF8))
72 |
73 | def doExit(self):
74 | print("called doExit")
75 | self.close()
76 |
77 | def doLogin(self):
78 | print("called doLogin")
79 | usr=str(self.lineEdit_user.text())
80 | pwd=str(self.lineEdit_pass.text())
81 | if self.update(usr, pwd):
82 | self.showDeone()
83 | else:
84 | self.label_msg.setText("user can not login.")
85 |
86 | def showDeone(self):
87 | self.main = Ui_MainWindow()
88 | self.main.showFullScreen()
89 | self.main.show()
90 | self.close()
91 |
92 | def center(self):
93 | screen = QtGui.QDesktopWidget().screenGeometry()
94 | size = self.geometry()
95 | print(screen, size)
96 | self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) /2)
97 |
98 | def update(self, usr, pwd):
99 | print(usr, pwd)
100 | #1. get the version
101 | from client import App3Client
102 | from datetime import datetime
103 |
104 | tm_begin = datetime.now()
105 |
106 | fn_rev = 'data/rev.json'
107 | self.progbar.reset()
108 | self.progbar.show()
109 | self.progbar.setRange(0, 100)
110 | self.label_msg.setText("checking the latest version...")
111 | c = App3Client(host, usr, pwd)
112 | # 1. list all packages
113 | s = c.list('Deone')
114 | if s is None:
115 | ret = False
116 | else:
117 | # get local packages
118 | local_items = []
119 | if os.path.exists(fn_rev):
120 | local_v = simplejson.load(open(fn_rev))
121 | for local_item in local_v:
122 | local_items += [local_item['id']]
123 |
124 | #3. get deone package
125 | self.label_msg.setText("updating package...")
126 | prog_val = 1
127 | v = simplejson.loads(s)
128 | for item in v:
129 | id = item['id']
130 | if id not in local_items:
131 | self.label_msg.setText("updating package %s...(%d/%d)" %(id, prog_val, len(v)))
132 | self.progbar.setValue(prog_val*100/len(v))
133 | s = c.get('Deone', id)
134 | prog_val += 1
135 | if s is not None:
136 | root = simplejson.loads(s)
137 | #admin.json2sqlite3(root["data"] )
138 | self.json2sqlite3(root["data"] )
139 | #4. update local version
140 | simplejson.dump(v, open(fn_rev, 'w'))
141 | ret = True
142 | tm_end = datetime.now()
143 | self.label_msg.setText("package update completed. %d"%(tm_end - tm_begin).seconds)
144 | self.progbar.hide()
145 | return ret
146 |
147 | def json2sqlite3(self, str):
148 | con = sqlite3.connect(deone_db)
149 | obj = simplejson.loads(str)
150 | for tbl in tables:
151 | if obj.has_key(tbl):
152 | self.obj2table(con, tbl, obj[tbl])
153 | con.close()
154 |
155 | def obj2table(self, con, tbl, obj):
156 | cur = con.cursor()
157 | ncols = obj["ncols"]
158 | nrows = obj["nrows"]
159 | cells = obj["cells"]
160 | #print(ncols, nrows, cells)
161 | for rownum in range(nrows):
162 | t = []
163 | for colnum in range(ncols):
164 | #print(rownum, colnum)
165 | idx = rownum*ncols + colnum
166 | t += [cells[idx]]
167 | #print(idx, cells[idx])
168 | if rownum == 0:
169 | s_cols = ""
170 | for s_col in t:
171 | s_cols += s_col + " text,"
172 | s = '''create table %s(%s)'''%(tbl, s_cols.rstrip(','))
173 | try:
174 | cur.execute(s)
175 | con.commit()
176 | except sqlite3.OperationalError, e:
177 | print(e)
178 | else:
179 | s_cols = ("?,"*ncols)
180 | s = '''insert into %s values (%s)'''%(tbl, s_cols.rstrip(','))
181 | #print(s)
182 | cur.execute(s, t)
183 | con.commit()
184 | cur.close()
185 |
186 | #===============================================================================
187 | # Example
188 | #===============================================================================
189 | if __name__ == '__main__':
190 | import os, sys
191 | if not os.path.exists('data'): os.mkdir('data')
192 | if os.path.exists('__local'):
193 | host = 'localhost:8080'
194 | app = QtGui.QApplication(sys.argv)
195 | window = LoginWindow()
196 | window.show()
197 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/appspot/restlet/parsutil.py:
--------------------------------------------------------------------------------
1 | import cgi
2 | import doctest
3 | import logging
4 | import os
5 | import re
6 |
7 | logger = logging.getLogger()
8 | logger.setLevel(getattr(logging, os.environ.get('LOGLEVEL', 'WARNING')))
9 |
10 | class UrlParser(object):
11 | """ Parse a URL path and perform appropriate an callback on regex-matching.
12 |
13 | Instantiate h with a prefix (to be matched, but ignored if it matches),
14 | followed by as many (regex, callback) pairs as needed.
15 | Then, call h.process(path): if the path matches the prefix, then
16 | each regex is tried *IN ORDER* on the rest of the path, and,
17 | upon the first match if any, the corresponding callback gets called
18 | (and its results returned).
19 | If the prefix does not match, or none of the regexes does, then
20 | method call h.process(path) returns None.
21 | The callback is passed *NAMED* arguments (only!) corresponding to
22 | the positional groups matched in the prefix, augmented or overridden
23 | by those matched in the specific regex that matched after that.
24 | So for example:
25 | >>> def show(**k): print sorted(k.items())
26 | >>> h = UrlParser(r'/(?P\w+)/',
27 | ... (r'(?P\d+)', show),
28 | ... (r'(?P[^/]*)', show),
29 | ... )
30 | >>> h.process('/zipzop/23/whatever')
31 | [('bar', '23'), ('foo', 'zipzop')]
32 | >>> h.process('/zipzop/whoo/whatever')
33 | [('foo', 'whoo')]
34 |
35 | You can also change the prefix by passing a prefix to .process(...) [the
36 | new prefix-to-ignore is then remembered in lieu of the previous one].
37 |
38 | >>> h.prefix.pattern
39 | '/(?P\\\\w+)/'
40 | >>> h.process('/zipzop/whoo/whatever', prefix='/')
41 | [('foo', 'zipzop')]
42 | >>> h.process('/zipzop/whoo/whatever')
43 | [('foo', 'zipzop')]
44 | >>> h.prefix.pattern
45 | '/'
46 |
47 | The h.prefix attribute is exposed, and it's a RE object.
48 | """
49 |
50 | def __init__(self, prefix, *args):
51 | """ Takes a prefix to be ignored and 0+ (regex, callback) pair args.
52 |
53 | Args:
54 | prefix: a string regex pattern
55 | args: 0+ pairs (regex_pattern, callback) [each a string + a callable]
56 | """
57 | self.prefix = re.compile(prefix or '')
58 | logging.debug('prefix: %r', prefix)
59 | self.callbacks = []
60 | for pattern, callback in args:
61 | logging.debug('%r -> %r', pattern, callback)
62 | self.callbacks.append((re.compile(pattern), callback))
63 |
64 | def process(self, path, prefix=None):
65 | """ Match the path to one of the regexs and call the appropriate callback.
66 |
67 | Args:
68 | path: a string URL (complete path) to parse
69 | prefix: if not None, a RE pattern string to change self.prefix from now on
70 | Returns:
71 | the result of the appropriate callback, or None if no match
72 | """
73 | if prefix is not None and prefix != self.prefix.pattern:
74 | self.prefix = re.compile(prefix)
75 | prefix_mo = self.prefix.match(path)
76 | if prefix_mo is None:
77 | logging.debug('No prefix match for %r (%r)', path, self.prefix)
78 | return None
79 | pathrest = path[prefix_mo.end():]
80 | logging.debug('Matching %r...', pathrest)
81 | for regex, callback in self.callbacks:
82 | mo = regex.match(pathrest)
83 | if mo:
84 | logging.debug('Matched %r, calling %r', regex, callback)
85 | named_args = prefix_mo.groupdict()
86 | named_args.update(mo.groupdict())
87 | return callback(**named_args)
88 | logging.debug('No match for %r', pathrest)
89 | return None
90 |
91 |
92 | class RestUrlParser(UrlParser):
93 | """ Specifically dispatches on the REs associated with REST-shaped URLs.
94 |
95 | Note that h.process only takes an URL *path*, NOT the rest of the URL (no
96 | protocol, no host, no query).
97 |
98 | >>> h = RestUrlParser('')
99 | >>> h.process('/$foobar')
100 | ('special', '$foobar')
101 | >>> h.process('/foobar')
102 | ('model', 'foobar')
103 | >>> h.process('/$foobar/zak/')
104 | ('special_method', '$foobar', 'zak')
105 | >>> h.process('/foobar/zak/')
106 | ('model_method', 'foobar', 'zak')
107 | >>> h.process('/foobar/23/')
108 | ('model_strid', 'foobar', '23')
109 | >>> h.process('/foobar/23/blop')
110 | ('model_strid_method', 'foobar', '23', 'blop')
111 | >>> h.process('')
112 | >>> h.process('////////')
113 | >>>
114 | """
115 |
116 | @staticmethod
117 | def _doprefix(prefix):
118 | if prefix is None: return None
119 | prefix = prefix.strip('/')
120 | if prefix: return '/%s/' % prefix
121 | else: return '/'
122 |
123 | def process(self, path, prefix=None):
124 | return UrlParser.process(self, path, self._doprefix(prefix))
125 |
126 | def __init__(self, prefix=None, **overrides):
127 | """ Set the prefix-to-ignore, optionally override methods.
128 |
129 | Args:
130 | prefix: a string regex pattern (or None, default)
131 | overrides: 0+ named arguments; values are callables to override the
132 | methods RestUrlParser provides (which just return tuples of strings),
133 | and each such callable must be signature-compatible with the
134 | corresponding named method. The methods & signaturs are:
135 | do_special(special)
136 | do_model(model)
137 | do_special_method(special, method)
138 | do_model_method(model, method)
139 | do_model_strid(model, strid)
140 | do_model_strid_method(model, strid, method)
141 |
142 | The *names* (not necessarily the *order*) of the arguments matter.
143 |
144 | The values of all arguments are strings (the substrings of the
145 | incoming path that match the respective items of the REST URL):
146 | strid is always 1+ digits; special is '$' + a valid identifier;
147 | model and method are identifiers.
148 | """
149 | # let each method be overridden (in the instance) by caller at ctor-time
150 | self.__dict__.update(overrides)
151 |
152 | # prefix must always absorb leading and trailing /
153 | prefix = self._doprefix(prefix)
154 |
155 | # build URL regexes with corresponding names
156 | urls = []
157 | def addurl(name, regex): urls.append((regex, getattr(self, 'do_'+name)))
158 |
159 | sr_method = r'/(?P\w+)'
160 | sr_strid = r'/(?P\d+)'
161 |
162 | # special_method must be before special (ie. special_method > special)
163 | re_special = r'(?P\$\w+)/?'
164 | re_special_method = re_special + sr_method
165 | addurl('special_method', re_special_method)
166 | addurl('special', re_special)
167 |
168 | # model_strid_method > model_strid > model_method > model
169 | re_model = r'(?P\w+)/?'
170 | re_model_method = re_model + sr_method
171 | re_model_strid = re_model + sr_strid
172 | re_model_strid_method = re_model_strid + sr_method
173 | addurl('model_strid_method', re_model_strid_method)
174 | addurl('model_strid', re_model_strid)
175 | addurl('model_method', re_model_method)
176 | addurl('model', re_model)
177 |
178 | UrlParser.__init__(self, prefix, *urls)
179 |
180 | def do_special(self, special):
181 | return 'special', special
182 | def do_model(self, model):
183 | return 'model', model
184 | def do_special_method(self, special, method):
185 | return 'special_method', special, method
186 | def do_model_method(self, model, method):
187 | return 'model_method', model, method
188 | def do_model_strid(self, model, strid):
189 | return 'model_strid', model, strid
190 | def do_model_strid_method(self, model, strid, method):
191 | return 'model_strid_method', model, strid, method
192 |
193 |
194 | def _test():
195 | import doctest
196 | numfailures, numtests = doctest.testmod()
197 | if numfailures == 0:
198 | print '%d tests passed successfully' % numtests
199 | # if there are any failures, doctest does its own reporting!-)
200 |
201 | if __name__ == "__main__":
202 | _test()
203 |
--------------------------------------------------------------------------------
/DEONE/admin.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PyQt4 import QtCore, QtGui
4 | from deone import Ui_MainWindow
5 | import os
6 | import simplejson
7 | import sqlite3
8 | import sys
9 | import string
10 | import xlrd
11 |
12 | host = 'de-one.appspot.com'
13 | #host = 'localhost:8080'
14 |
15 |
16 | fn_uprev = 'data/uprev.json'
17 |
18 | tables = {
19 | u"深成指399001.xls":"Market",
20 | u"BJS交易类型2.xls":"Suggestion",
21 | u"DBS交易类型1.xls":"Suggestion",
22 | u"NRA交易类型4.xls":"Suggestion",
23 | u"SDB交易类型3.xls":"Suggestion",
24 | u"个股历史数据.xls":"StockHist",
25 | u"大盘数据.xls":"Market",
26 | u"StockCategory.xls":"StockCategory",
27 | u"CategoryHist.xls":"CategoryHist",
28 | }
29 |
30 | from client import App3Client
31 | c = App3Client('de-one.appspot.com', 'test', 'test')
32 |
33 | class AdminWindow(QtGui.QMainWindow):
34 | def __init__(self):
35 | QtGui.QMainWindow.__init__(self)
36 |
37 | self.setGeometry(QtCore.QRect(0, 0, 320, 160))
38 |
39 | mainLayout = QtGui.QVBoxLayout()
40 |
41 | hLayoutUser = QtGui.QHBoxLayout()
42 | self.label_user = QtGui.QLabel(self)
43 | hLayoutUser.addWidget(self.label_user)
44 | self.lineEdit_user = QtGui.QLineEdit(self)
45 | self.lineEdit_user.setText("test")
46 | hLayoutUser.addWidget(self.lineEdit_user)
47 | mainLayout.addLayout(hLayoutUser)
48 |
49 | hLayoutPass = QtGui.QHBoxLayout()
50 | self.label_pass = QtGui.QLabel(self)
51 | hLayoutPass.addWidget(self.label_pass)
52 | self.lineEdit_pass = QtGui.QLineEdit(self)
53 | self.lineEdit_pass.setEchoMode(QtGui.QLineEdit.Password)
54 | self.lineEdit_pass.setText("test")
55 | hLayoutPass.addWidget(self.lineEdit_pass)
56 | mainLayout.addLayout(hLayoutPass)
57 |
58 | hLayoutTag = QtGui.QHBoxLayout()
59 | self.label_tag = QtGui.QLabel(self)
60 | hLayoutTag.addWidget(self.label_tag)
61 | self.lineEdit_tag = QtGui.QLineEdit(self)
62 | self.lineEdit_pass.setEchoMode(QtGui.QLineEdit.Password)
63 | hLayoutTag.addWidget(self.lineEdit_tag)
64 | mainLayout.addLayout(hLayoutTag)
65 |
66 | hLayoutCmd = QtGui.QHBoxLayout()
67 | self.pushButtonUpload = QtGui.QPushButton(self)
68 | hLayoutCmd.addWidget(self.pushButtonUpload)
69 | self.pushButtonReset = QtGui.QPushButton(self)
70 | hLayoutCmd.addWidget(self.pushButtonReset)
71 | self.pushButtonExit = QtGui.QPushButton(self)
72 | hLayoutCmd.addWidget(self.pushButtonExit)
73 |
74 | self.label_msg = QtGui.QLabel(self)
75 | self.label_msg.setGeometry(QtCore.QRect(0, 0, 320, 20))
76 | mainLayout.addWidget(self.label_msg)
77 |
78 | self.progbar = QtGui.QProgressBar(self)
79 | self.progbar.setProperty("value",QtCore.QVariant(0))
80 | mainLayout.addWidget(self.progbar)
81 |
82 | mainLayout.addLayout(hLayoutCmd)
83 |
84 | widget = QtGui.QWidget()
85 | widget.setLayout(mainLayout)
86 | self.setCentralWidget(widget)
87 |
88 | self.retranslateUi()
89 | QtCore.QObject.connect(self.pushButtonExit, QtCore.SIGNAL("pressed()"), self.doExit)
90 | QtCore.QObject.connect(self.pushButtonUpload, QtCore.SIGNAL("pressed()"), self.doUpload)
91 | QtCore.QObject.connect(self.pushButtonReset, QtCore.SIGNAL("pressed()"), self.doReset)
92 |
93 | self.progbar.hide()
94 | self.center()
95 |
96 | def retranslateUi(self):
97 | self.label_user.setText(QtGui.QApplication.translate("MainWindow", "用户:", None, QtGui.QApplication.UnicodeUTF8))
98 | self.label_pass.setText(QtGui.QApplication.translate("MainWindow", "密码:", None, QtGui.QApplication.UnicodeUTF8))
99 | self.label_tag.setText(QtGui.QApplication.translate("MainWindow", "标记:", None, QtGui.QApplication.UnicodeUTF8))
100 | self.pushButtonUpload.setText(QtGui.QApplication.translate("MainWindow", "上传", None, QtGui.QApplication.UnicodeUTF8))
101 | self.pushButtonReset.setText(QtGui.QApplication.translate("MainWindow", "恢复", None, QtGui.QApplication.UnicodeUTF8))
102 | self.pushButtonExit.setText(QtGui.QApplication.translate("MainWindow", "退出", None, QtGui.QApplication.UnicodeUTF8))
103 |
104 | def doExit(self):
105 | print("called doExit")
106 | self.close()
107 |
108 | def doReset(self):
109 | print("called doReset")
110 | usr=str(self.lineEdit_user.text())
111 | pwd=str(self.lineEdit_pass.text())
112 | tag=str(self.lineEdit_tag.text())
113 | if not self.reset(usr, pwd, tag):
114 | self.label_msg.setText("user can not reset.")
115 |
116 | def doUpload(self):
117 | print("called doUpload")
118 | usr=str(self.lineEdit_user.text())
119 | pwd=str(self.lineEdit_pass.text())
120 | tag=str(self.lineEdit_tag.text())
121 | if not self.upload(usr, pwd, tag):
122 | self.label_msg.setText("user can not upload.")
123 |
124 | def center(self):
125 | screen = QtGui.QDesktopWidget().screenGeometry()
126 | size = self.geometry()
127 | print(screen, size)
128 | self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) /2)
129 |
130 | def reset(self, usr, pwd, tag):
131 | from client import App3Client
132 | from datetime import datetime
133 |
134 | tm_begin = datetime.now()
135 | self.progbar.reset()
136 | self.progbar.show()
137 | self.progbar.setRange(0, 100)
138 | self.label_msg.setText("resetting package...")
139 | c = App3Client(host, usr, pwd)
140 |
141 | items = {}
142 | if tag is None or len(tag) == 0:
143 | s = c.list('Deone')
144 | #print(s)
145 | if s is not None:
146 | items = simplejson.loads(s)
147 | else:
148 | root = simplejson.load(open(fn_uprev))
149 | if root is not None:
150 | if root.has_key(tag):
151 | items = root[tag]
152 | prog_val = 1
153 | for item in items:
154 | id = item['id']
155 | self.label_msg.setText("reseting package %s...(%d/%d)" %(id, prog_val, len(items)))
156 | self.progbar.setValue(prog_val*100/len(items))
157 | s = c.delete('Deone', id)
158 | if s is None:
159 | ret = False
160 | break
161 | prog_val += 1
162 |
163 | ret = True
164 |
165 | tm_end = datetime.now()
166 | self.label_msg.setText("package reset completed. %d"%(tm_end - tm_begin).seconds)
167 | self.progbar.hide()
168 | return ret
169 |
170 | def upload(self, usr, pwd, tag):
171 |
172 | from client import App3Client
173 | from datetime import datetime
174 |
175 | tm_begin = datetime.now()
176 |
177 | self.progbar.reset()
178 | self.progbar.show()
179 | self.progbar.setRange(0, 100)
180 | c = App3Client(host, usr, pwd)
181 |
182 | self.label_msg.setText("uploading package...")
183 |
184 | ret = True
185 | self.uprev_ids = []
186 |
187 | for fn,tbl in tables.items():
188 | if not self.xls2obj(c, fn, tbl):
189 | ret = False
190 | break
191 |
192 | #print(self.uprev_ids)
193 | local_root = {}
194 | if os.path.exists(fn_uprev):
195 | local_root = simplejson.load(open(fn_uprev))
196 | if local_root is not None:
197 | #print(local_root)
198 | if tag is None or len(tag) == 0:
199 | tag = datetime.now().strftime("%Y-%m-%d")
200 | if local_root.has_key(tag):
201 | local_root[tag] += self.uprev_ids
202 | else:
203 | local_root[tag] = self.uprev_ids
204 | simplejson.dump(local_root, open(fn_uprev, 'w'))
205 |
206 | tm_end = datetime.now()
207 | self.label_msg.setText("package upload completed. %d"%(tm_end - tm_begin).seconds)
208 | self.progbar.hide()
209 | return ret
210 |
211 | def xls2obj(self, c, fn, tbl):
212 | wb = xlrd.open_workbook(os.path.join(os.path.abspath("data"), fn))
213 | sh = wb.sheet_by_index(0)
214 |
215 | #first line is columns
216 | cols = []
217 | for colnum in range(sh.ncols):
218 | t = sh.cell_type(0, colnum)
219 | val = sh.cell_value(0, colnum)
220 | cols +=[val]
221 | cells = []
222 | i_row = 0
223 | #sh.nrows = 5
224 | for rownum in xrange(1, sh.nrows):
225 | for colnum in range(sh.ncols):
226 | t = sh.cell_type(rownum, colnum)
227 | val = sh.cell_value(rownum, colnum)
228 | #print(rownum, colnum, t, val)
229 | if t == 3:
230 | dt = xlrd.xldate_as_tuple(val, 0)
231 | s_dt = "%04d-%02d-%02d"%(dt[0:3])
232 | #print(s_dt)
233 | cells += [s_dt]
234 | else:
235 | try:
236 | f = float(val)
237 | i = int(f)
238 | if f == float(i):
239 | cells += ["%d" %(i)]
240 | else:
241 | cells += ["%.2f" %(f)]
242 | except ValueError:
243 | cells += [val]
244 | i_row += 1
245 |
246 | #print(sh.nrows, rownum, i_row)
247 | self.label_msg.setText("uploading package %s...(%d/%d)" %(fn, rownum, sh.nrows))
248 | self.progbar.setValue(rownum*100/sh.nrows)
249 |
250 | if (i_row%5000==0) or (rownum == sh.nrows-1):
251 | obj = {"ncols":sh.ncols, "nrows":i_row+1, "cells":cols+cells}
252 | s_json = simplejson.dumps({tbl:obj})
253 | #print(s_json)
254 | s = c.post('Deone', {"data": s_json})
255 | #print(s)
256 | if s is None:
257 | return False
258 | else:
259 | obj = simplejson.loads(s)
260 | if obj is not None:
261 | self.uprev_ids += [{"id":obj["id"]}]
262 | cells = []
263 | i_row = 0
264 |
265 | return True
266 |
267 | #===============================================================================
268 | # Example
269 | #===============================================================================
270 | if __name__ == '__main__':
271 | import os, sys
272 | if not os.path.exists('data'): os.mkdir('data')
273 | if os.path.exists('__local'):
274 | host = 'localhost:8080'
275 | app = QtGui.QApplication(sys.argv)
276 | window = AdminWindow()
277 | window.show()
278 | sys.exit(app.exec_())
--------------------------------------------------------------------------------
/appspot/restlet/restutil.py:
--------------------------------------------------------------------------------
1 | """ Utilities for REST CRUD support for GAE db models.
2 |
3 | Specifically, this module facilitates introspection about a data model built
4 | on GAE db -- a registry of what db.Model subclasses are made available for
5 | introspection and by what names, utilities to register and query about such
6 | classes 'in bulk', mapping of property values of instances of those classes
7 | from and to strings. Reference properties, in particular, are mapped to
8 | strings of the form Classname/ where id is a unique-within-class id
9 | usable for the get_by_id method of the corresponding class;
10 | "reverse-reference" properties are *not* supported for conversion to/from
11 | string.
12 |
13 | The conversion of property values to/from string is made by static methods
14 | named foo_to_string and foo_from_string (for a property class attribute
15 | named foo); this module offers facilities to make and install on the class
16 | object all such needed methods, but if the class itself explicitly chooses
17 | to define some methods with these names, those facilities will not override
18 | them (so each db.Model subclass gets a chance to special-case some or all
19 | of its instance's property attributes). The_from_string method is not
20 |
21 | The module also offers the ability to register and retrieve (by string
22 | names):
23 | -- 'special objects' (model-like, but with no entities)
24 | such a registration just creates a namespace (for registering methods on)
25 | which is represented as a dict
26 | -- 'methods' which can be registered as (any one of; for >1 register again)
27 | -- callable on a special object,
28 | -- callable on a model,
29 | -- callable on any entity of a model
30 | all such registrations require a callable taking named args which are
31 | lists coming from the cgi.parse_qs parsing of a query string;
32 | the callable object registered for a method that's registered as callable
33 | on any entity of a model also takes a first argument 'self' that is
34 | the specific entity on which it is being called.
35 | -- entry points to query all the methods callable on a special object,
36 | model, or, any entity of a given model
37 |
38 | """
39 | import datetime
40 | import inspect
41 | import logging
42 | import sys
43 |
44 | from google.appengine.ext import db
45 | from google.appengine.api import users
46 |
47 |
48 | def id_of(x):
49 | """ Get the numeric ID given an instance x of a db.Model subclass. """
50 | return x.key().id()
51 |
52 | def identity(x): return x
53 | identity = staticmethod(identity)
54 |
55 | def isProperty(x):
56 | """ Is class attribute x a 'real' property (not a reverse reference)?
57 |
58 | Args:
59 | x: a class attribute (from some db.Model subclass)
60 | Returns:
61 | True iff x's type is that of a "real" property (not a rev.ref.)
62 | """
63 | return isinstance(x, db.Property
64 | ) and not isinstance(x, db._ReverseReferenceProperty)
65 |
66 |
67 | specials_registry = dict()
68 | def registerSpecialByName(name):
69 | if name in specials_registry:
70 | raise KeyError, 'Duplicate name %r for specials registry' % name
71 | specials_registry[name] = dict(_n=name)
72 |
73 | def specialFromName(name):
74 | """ Get special object with the given name (None if none).
75 |
76 | Args:
77 | name: a string that should be registered as name for a special object
78 | Returns:
79 | dict that's the special object thus named, None if none
80 | """
81 | return specials_registry.get(name)
82 |
83 | def allSpecialNames():
84 | """ Return a list of strings, all special object names in registry. """
85 | return sorted(specials_registry)
86 |
87 | def registerSpecialMethod(special, name, method):
88 | if isinstance(special, str):
89 | spc = specialFromName(special)
90 | if spc is None:
91 | raise KeyError, 'No special %r' % special
92 | special = spc
93 | if name in special:
94 | raise KeyError, 'Duplicated method name %r for special %r' % (
95 | name, special['_n'])
96 | special[name] = method
97 |
98 | def specialMethodFromName(special, name):
99 | if isinstance(special, str):
100 | special = specialFromName(special)
101 | if special is None:
102 | return None
103 | return special.get(name)
104 |
105 |
106 | model_class_registry = dict()
107 |
108 | def registerClassByName(cls, name=None):
109 | """ Register a db.Model subclass with the given name (def. its own name). """
110 | if name is None: name = cls.__name__
111 | if name in model_class_registry:
112 | raise KeyError, 'Duplicate name %r for model class registry' % name
113 | model_class_registry[name] = cls
114 | setattr(cls, '_n', name)
115 |
116 | def isModelClass(x):
117 | """ Is object x a subclass of db.Model?
118 |
119 | Args:
120 | x: any
121 | Returns:
122 | true iff x is a subclass of db.Model
123 | """
124 | try: return issubclass(x, db.Model)
125 | except TypeError: return False
126 |
127 | def registerAllModelClasses(module_obj):
128 | """ Register non-private db.Model subclasses from the given module object. """
129 | for name, cls in inspect.getmembers(module_obj, isModelClass):
130 | if name[0] != '_':
131 | registerClassByName(cls, name)
132 |
133 | def registerAllModelClassesFromModuleNamed(module_name):
134 | """ Register all db.Model subclasses from module w/given name. """
135 | registerAllModelClasses(__import__(module_name))
136 |
137 | def modelClassFromName(classname):
138 | """ Get the db.Model subclass with the given name (None if none).
139 |
140 | Only handles db.Model subclasses enregistered into model_class_registry.
141 |
142 | Args:
143 | classname: a string that should name a db.Model subclass
144 | Returns:
145 | class object with that name, or None if there's no such class
146 | """
147 | return model_class_registry.get(classname)
148 |
149 | def nameFromModelClass(cls):
150 | """ Get the name a db.Model subclass is registered under (or None). """
151 | return getattr(cls, '_n', None)
152 |
153 | def allModelClassNames():
154 | """ Return a list of strings, all model class names in registry. """
155 | return sorted(model_class_registry)
156 |
157 | def _getter(model, an):
158 | if isinstance(model, str):
159 | mdl = modelClassFromName(model)
160 | if mdl is None:
161 | raise KeyError, 'No model named %r' % model
162 | model = mdl
163 | mm = getattr(model, an, None)
164 | if mm is None:
165 | mm = dict()
166 | setattr(model, an, mm)
167 |
168 | def _registerMethod(model, name, method, _getter_an):
169 | model, mm = _getter(model, _getter_an)
170 | if name in mm:
171 | raise KeyError, 'Duplicate name %r for method in model %r' % (name,
172 | nameFromModelClass(model))
173 | mm[name] = method
174 |
175 | def _methodByName(model, name, _getter_an):
176 | model, mm = _getter(model, _getter_an)
177 | return mm.get(name)
178 |
179 | def _allMethods(model, _getter_an):
180 | model, mm = _getter(model, _getter_an)
181 | return sorted(mm)
182 |
183 | def registerModelMethod(model, name, method):
184 | return _registerMethod(model, name, method, '_mm')
185 |
186 | def modelMethodByName(model, name):
187 | return _methodByName(model, name, '_mm')
188 |
189 | def allModelMethods(model):
190 | return _allMethods(model, '_mm')
191 |
192 | def registerInstanceMethod(model, name, method):
193 | return _registerMethod(model, name, method, '_im')
194 |
195 | def instanceMethodByName(model, name):
196 | return _methodByName(model, name, '_im')
197 |
198 | def allInstanceMethods(model):
199 | return _allMethods(model, '_im')
200 |
201 |
202 | def modelInstanceByClassAndId(s):
203 | """ Get a model instance given its class name and numeric ID, or None.
204 |
205 | Args:
206 | s: str of the form 'Classname/1234'
207 | Returns:
208 | model instance from the class of that name, with that ID (or None)
209 | """
210 | classname, theid = s.split('/')
211 | theclass = modelClassFromName(classname)
212 | if theclass is None: return None
213 | return theclass.get_by_id(int(theid))
214 |
215 | def classAndIdFromModelInstance(x, classname=None):
216 | """ Get a string with class name and numeric ID given a model instance.
217 |
218 | Args:
219 | x: a model instance or None
220 | Returns:
221 | str of the form 'Classname/1234' (or None if x is None)
222 | """
223 | if x is None: return None
224 | if classname is None: classname = type(x).__name__
225 | theclass = modelClassFromName(classname)
226 | if theclass is not type(x): return None
227 | return '%s/%s' % (classname, id_of(x))
228 |
229 |
230 | DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
231 |
232 | def datetimeFromString(s):
233 | """ Get a datetime object given a str ('right now' for empty str).
234 |
235 | As per appengine convention, all datetime objs must be UTC.
236 |
237 | Args:
238 | s: str in DATETIME_FORMAT or ''
239 | Returns:
240 | appropriate datetime object
241 | """
242 | if s:
243 | return datetime.datetime.strptime(s, DATETIME_FORMAT)
244 | else:
245 | return datetime.datetime.now()
246 |
247 |
248 | def stringFromDatetime(dt):
249 | """ Get an appropriately formatted str given a datetime object.
250 |
251 | Args:
252 | dt: datetime instance
253 | Returns:
254 | str formatted as per DATETIME_FORMAT
255 | """
256 | return dt.strftime(DATETIME_FORMAT)
257 |
258 |
259 | # mapping from property types to appropriate str->value function if any
260 | # property types not in the mapping must accept a properly formatted str
261 | setter_registry = {
262 | db.BooleanProperty: 'False'.__ne__,
263 | db.DateTimeProperty: staticmethod(datetimeFromString),
264 | db.IntegerProperty: int,
265 | db.FloatProperty: float,
266 | db.ReferenceProperty: staticmethod(modelInstanceByClassAndId),
267 | db.StringListProperty: str.split,
268 | db.UserProperty: users.User,
269 | }
270 |
271 | # mapping from property types to appropriate value->str function if any
272 | # str(value) is used for property types that are not in the mapping
273 | getter_registry = {
274 | db.DateTimeProperty: staticmethod(stringFromDatetime),
275 | db.ReferenceProperty: staticmethod(classAndIdFromModelInstance),
276 | db.StringListProperty: ' '.join,
277 | }
278 |
279 | def allProperties(cls):
280 | """ Get all (name, value) pairs of properties given a db.Model subclass.
281 |
282 | Args:
283 | cls: a class object (a db.Model subclass)
284 | Returns:
285 | list of (name, value) pairs of properties of that class
286 | """
287 | return inspect.getmembers(cls, isProperty)
288 |
289 |
290 | def addHelperMethods(cls):
291 | """ Add _from_string and _to_string methods to a db.Model subclass.
292 |
293 | Args:
294 | cls: a class object (db.Model subclass), adds methods to it.
295 | """
296 | logging.info('decorating model %r', cls)
297 | props = allProperties(cls)
298 | for name, value in props:
299 | fs_name = name + '_from_string'
300 | if not hasattr(cls, fs_name):
301 | setter = setter_registry.get(type(value), identity)
302 | setattr(cls, fs_name, setter)
303 | # logging.info('added %r: %r', fs_name, setter)
304 | ts_name = name + '_to_string'
305 | if not hasattr(cls, ts_name):
306 | getter = getter_registry.get(type(value), str)
307 | setattr(cls, ts_name, getter)
308 | # logging.info('added %r: %r', ts_name, getter)
309 |
310 | def decorateModuleNamed(module_name):
311 | """ Do all needed work for non-private model classes in module thus named. """
312 | module_obj = __import__(module_name)
313 | for name, cls in inspect.getmembers(module_obj, isModelClass):
314 | if name[0] != '_':
315 | registerClassByName(cls, name)
316 | addHelperMethods(cls)
317 |
--------------------------------------------------------------------------------
/appspot/simplejson/decoder.py:
--------------------------------------------------------------------------------
1 | """
2 | Implementation of JSONDecoder
3 | """
4 | import re
5 | import sys
6 |
7 | from simplejson.scanner import Scanner, pattern
8 | try:
9 | from simplejson._speedups import scanstring as c_scanstring
10 | except ImportError:
11 | pass
12 |
13 | FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
14 |
15 | def _floatconstants():
16 | import struct
17 | import sys
18 | _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
19 | if sys.byteorder != 'big':
20 | _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
21 | nan, inf = struct.unpack('dd', _BYTES)
22 | return nan, inf, -inf
23 |
24 | NaN, PosInf, NegInf = _floatconstants()
25 |
26 |
27 | def linecol(doc, pos):
28 | lineno = doc.count('\n', 0, pos) + 1
29 | if lineno == 1:
30 | colno = pos
31 | else:
32 | colno = pos - doc.rindex('\n', 0, pos)
33 | return lineno, colno
34 |
35 |
36 | def errmsg(msg, doc, pos, end=None):
37 | lineno, colno = linecol(doc, pos)
38 | if end is None:
39 | return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos)
40 | endlineno, endcolno = linecol(doc, end)
41 | return '%s: line %d column %d - line %d column %d (char %d - %d)' % (
42 | msg, lineno, colno, endlineno, endcolno, pos, end)
43 |
44 |
45 | _CONSTANTS = {
46 | '-Infinity': NegInf,
47 | 'Infinity': PosInf,
48 | 'NaN': NaN,
49 | 'true': True,
50 | 'false': False,
51 | 'null': None,
52 | }
53 |
54 | def JSONConstant(match, context, c=_CONSTANTS):
55 | s = match.group(0)
56 | fn = getattr(context, 'parse_constant', None)
57 | if fn is None:
58 | rval = c[s]
59 | else:
60 | rval = fn(s)
61 | return rval, None
62 | pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant)
63 |
64 |
65 | def JSONNumber(match, context):
66 | match = JSONNumber.regex.match(match.string, *match.span())
67 | integer, frac, exp = match.groups()
68 | if frac or exp:
69 | fn = getattr(context, 'parse_float', None) or float
70 | res = fn(integer + (frac or '') + (exp or ''))
71 | else:
72 | fn = getattr(context, 'parse_int', None) or int
73 | res = fn(integer)
74 | return res, None
75 | pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber)
76 |
77 |
78 | STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
79 | BACKSLASH = {
80 | '"': u'"', '\\': u'\\', '/': u'/',
81 | 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
82 | }
83 |
84 | DEFAULT_ENCODING = "utf-8"
85 |
86 | def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match):
87 | if encoding is None:
88 | encoding = DEFAULT_ENCODING
89 | chunks = []
90 | _append = chunks.append
91 | begin = end - 1
92 | while 1:
93 | chunk = _m(s, end)
94 | if chunk is None:
95 | raise ValueError(
96 | errmsg("Unterminated string starting at", s, begin))
97 | end = chunk.end()
98 | content, terminator = chunk.groups()
99 | if content:
100 | if not isinstance(content, unicode):
101 | content = unicode(content, encoding)
102 | _append(content)
103 | if terminator == '"':
104 | break
105 | elif terminator != '\\':
106 | if strict:
107 | raise ValueError(errmsg("Invalid control character %r at", s, end))
108 | else:
109 | _append(terminator)
110 | continue
111 | try:
112 | esc = s[end]
113 | except IndexError:
114 | raise ValueError(
115 | errmsg("Unterminated string starting at", s, begin))
116 | if esc != 'u':
117 | try:
118 | m = _b[esc]
119 | except KeyError:
120 | raise ValueError(
121 | errmsg("Invalid \\escape: %r" % (esc,), s, end))
122 | end += 1
123 | else:
124 | esc = s[end + 1:end + 5]
125 | next_end = end + 5
126 | msg = "Invalid \\uXXXX escape"
127 | try:
128 | if len(esc) != 4:
129 | raise ValueError
130 | uni = int(esc, 16)
131 | if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
132 | msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
133 | if not s[end + 5:end + 7] == '\\u':
134 | raise ValueError
135 | esc2 = s[end + 7:end + 11]
136 | if len(esc2) != 4:
137 | raise ValueError
138 | uni2 = int(esc2, 16)
139 | uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
140 | next_end += 6
141 | m = unichr(uni)
142 | except ValueError:
143 | raise ValueError(errmsg(msg, s, end))
144 | end = next_end
145 | _append(m)
146 | return u''.join(chunks), end
147 |
148 |
149 | # Use speedup
150 | try:
151 | scanstring = c_scanstring
152 | except NameError:
153 | scanstring = py_scanstring
154 |
155 | def JSONString(match, context):
156 | encoding = getattr(context, 'encoding', None)
157 | strict = getattr(context, 'strict', True)
158 | return scanstring(match.string, match.end(), encoding, strict)
159 | pattern(r'"')(JSONString)
160 |
161 |
162 | WHITESPACE = re.compile(r'\s*', FLAGS)
163 |
164 | def JSONObject(match, context, _w=WHITESPACE.match):
165 | pairs = {}
166 | s = match.string
167 | end = _w(s, match.end()).end()
168 | nextchar = s[end:end + 1]
169 | # Trivial empty object
170 | if nextchar == '}':
171 | return pairs, end + 1
172 | if nextchar != '"':
173 | raise ValueError(errmsg("Expecting property name", s, end))
174 | end += 1
175 | encoding = getattr(context, 'encoding', None)
176 | strict = getattr(context, 'strict', True)
177 | iterscan = JSONScanner.iterscan
178 | while True:
179 | key, end = scanstring(s, end, encoding, strict)
180 | end = _w(s, end).end()
181 | if s[end:end + 1] != ':':
182 | raise ValueError(errmsg("Expecting : delimiter", s, end))
183 | end = _w(s, end + 1).end()
184 | try:
185 | value, end = iterscan(s, idx=end, context=context).next()
186 | except StopIteration:
187 | raise ValueError(errmsg("Expecting object", s, end))
188 | pairs[key] = value
189 | end = _w(s, end).end()
190 | nextchar = s[end:end + 1]
191 | end += 1
192 | if nextchar == '}':
193 | break
194 | if nextchar != ',':
195 | raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
196 | end = _w(s, end).end()
197 | nextchar = s[end:end + 1]
198 | end += 1
199 | if nextchar != '"':
200 | raise ValueError(errmsg("Expecting property name", s, end - 1))
201 | object_hook = getattr(context, 'object_hook', None)
202 | if object_hook is not None:
203 | pairs = object_hook(pairs)
204 | return pairs, end
205 | pattern(r'{')(JSONObject)
206 |
207 |
208 | def JSONArray(match, context, _w=WHITESPACE.match):
209 | values = []
210 | s = match.string
211 | end = _w(s, match.end()).end()
212 | # Look-ahead for trivial empty array
213 | nextchar = s[end:end + 1]
214 | if nextchar == ']':
215 | return values, end + 1
216 | iterscan = JSONScanner.iterscan
217 | while True:
218 | try:
219 | value, end = iterscan(s, idx=end, context=context).next()
220 | except StopIteration:
221 | raise ValueError(errmsg("Expecting object", s, end))
222 | values.append(value)
223 | end = _w(s, end).end()
224 | nextchar = s[end:end + 1]
225 | end += 1
226 | if nextchar == ']':
227 | break
228 | if nextchar != ',':
229 | raise ValueError(errmsg("Expecting , delimiter", s, end))
230 | end = _w(s, end).end()
231 | return values, end
232 | pattern(r'\[')(JSONArray)
233 |
234 |
235 | ANYTHING = [
236 | JSONObject,
237 | JSONArray,
238 | JSONString,
239 | JSONConstant,
240 | JSONNumber,
241 | ]
242 |
243 | JSONScanner = Scanner(ANYTHING)
244 |
245 |
246 | class JSONDecoder(object):
247 | """
248 | Simple JSON decoder
249 |
250 | Performs the following translations in decoding by default:
251 |
252 | +---------------+-------------------+
253 | | JSON | Python |
254 | +===============+===================+
255 | | object | dict |
256 | +---------------+-------------------+
257 | | array | list |
258 | +---------------+-------------------+
259 | | string | unicode |
260 | +---------------+-------------------+
261 | | number (int) | int, long |
262 | +---------------+-------------------+
263 | | number (real) | float |
264 | +---------------+-------------------+
265 | | true | True |
266 | +---------------+-------------------+
267 | | false | False |
268 | +---------------+-------------------+
269 | | null | None |
270 | +---------------+-------------------+
271 |
272 | It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
273 | their corresponding ``float`` values, which is outside the JSON spec.
274 | """
275 |
276 | _scanner = Scanner(ANYTHING)
277 | __all__ = ['__init__', 'decode', 'raw_decode']
278 |
279 | def __init__(self, encoding=None, object_hook=None, parse_float=None,
280 | parse_int=None, parse_constant=None, strict=True):
281 | """
282 | ``encoding`` determines the encoding used to interpret any ``str``
283 | objects decoded by this instance (utf-8 by default). It has no
284 | effect when decoding ``unicode`` objects.
285 |
286 | Note that currently only encodings that are a superset of ASCII work,
287 | strings of other encodings should be passed in as ``unicode``.
288 |
289 | ``object_hook``, if specified, will be called with the result
290 | of every JSON object decoded and its return value will be used in
291 | place of the given ``dict``. This can be used to provide custom
292 | deserializations (e.g. to support JSON-RPC class hinting).
293 |
294 | ``parse_float``, if specified, will be called with the string
295 | of every JSON float to be decoded. By default this is equivalent to
296 | float(num_str). This can be used to use another datatype or parser
297 | for JSON floats (e.g. decimal.Decimal).
298 |
299 | ``parse_int``, if specified, will be called with the string
300 | of every JSON int to be decoded. By default this is equivalent to
301 | int(num_str). This can be used to use another datatype or parser
302 | for JSON integers (e.g. float).
303 |
304 | ``parse_constant``, if specified, will be called with one of the
305 | following strings: -Infinity, Infinity, NaN, null, true, false.
306 | This can be used to raise an exception if invalid JSON numbers
307 | are encountered.
308 | """
309 | self.encoding = encoding
310 | self.object_hook = object_hook
311 | self.parse_float = parse_float
312 | self.parse_int = parse_int
313 | self.parse_constant = parse_constant
314 | self.strict = strict
315 |
316 | def decode(self, s, _w=WHITESPACE.match):
317 | """
318 | Return the Python representation of ``s`` (a ``str`` or ``unicode``
319 | instance containing a JSON document)
320 | """
321 | obj, end = self.raw_decode(s, idx=_w(s, 0).end())
322 | end = _w(s, end).end()
323 | if end != len(s):
324 | raise ValueError(errmsg("Extra data", s, end, len(s)))
325 | return obj
326 |
327 | def raw_decode(self, s, **kw):
328 | """
329 | Decode a JSON document from ``s`` (a ``str`` or ``unicode`` beginning
330 | with a JSON document) and return a 2-tuple of the Python
331 | representation and the index in ``s`` where the document ended.
332 |
333 | This can be used to decode a JSON document from a string that may
334 | have extraneous data at the end.
335 | """
336 | kw.setdefault('context', self)
337 | try:
338 | obj, end = self._scanner.iterscan(s, **kw).next()
339 | except StopIteration:
340 | raise ValueError("No JSON object could be decoded")
341 | return obj, end
342 |
343 | __all__ = ['JSONDecoder']
344 |
--------------------------------------------------------------------------------
/appspot/simplejson/encoder.py:
--------------------------------------------------------------------------------
1 | """
2 | Implementation of JSONEncoder
3 | """
4 | import re
5 |
6 | try:
7 | from simplejson._speedups import encode_basestring_ascii as c_encode_basestring_ascii
8 | except ImportError:
9 | pass
10 |
11 | ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]')
12 | ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
13 | HAS_UTF8 = re.compile(r'[\x80-\xff]')
14 | ESCAPE_DCT = {
15 | '\\': '\\\\',
16 | '"': '\\"',
17 | '\b': '\\b',
18 | '\f': '\\f',
19 | '\n': '\\n',
20 | '\r': '\\r',
21 | '\t': '\\t',
22 | }
23 | for i in range(0x20):
24 | ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
25 |
26 | # Assume this produces an infinity on all machines (probably not guaranteed)
27 | INFINITY = float('1e66666')
28 | FLOAT_REPR = repr
29 |
30 | def floatstr(o, allow_nan=True):
31 | # Check for specials. Note that this type of test is processor- and/or
32 | # platform-specific, so do tests which don't depend on the internals.
33 |
34 | if o != o:
35 | text = 'NaN'
36 | elif o == INFINITY:
37 | text = 'Infinity'
38 | elif o == -INFINITY:
39 | text = '-Infinity'
40 | else:
41 | return FLOAT_REPR(o)
42 |
43 | if not allow_nan:
44 | raise ValueError("Out of range float values are not JSON compliant: %r"
45 | % (o,))
46 |
47 | return text
48 |
49 |
50 | def encode_basestring(s):
51 | """
52 | Return a JSON representation of a Python string
53 | """
54 | def replace(match):
55 | return ESCAPE_DCT[match.group(0)]
56 | return '"' + ESCAPE.sub(replace, s) + '"'
57 |
58 |
59 | def py_encode_basestring_ascii(s):
60 | if isinstance(s, str) and HAS_UTF8.search(s) is not None:
61 | s = s.decode('utf-8')
62 | def replace(match):
63 | s = match.group(0)
64 | try:
65 | return ESCAPE_DCT[s]
66 | except KeyError:
67 | n = ord(s)
68 | if n < 0x10000:
69 | return '\\u%04x' % (n,)
70 | else:
71 | # surrogate pair
72 | n -= 0x10000
73 | s1 = 0xd800 | ((n >> 10) & 0x3ff)
74 | s2 = 0xdc00 | (n & 0x3ff)
75 | return '\\u%04x\\u%04x' % (s1, s2)
76 | return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
77 |
78 |
79 | try:
80 | encode_basestring_ascii = c_encode_basestring_ascii
81 | except NameError:
82 | encode_basestring_ascii = py_encode_basestring_ascii
83 |
84 |
85 | class JSONEncoder(object):
86 | """
87 | Extensible JSON encoder for Python data structures.
88 |
89 | Supports the following objects and types by default:
90 |
91 | +-------------------+---------------+
92 | | Python | JSON |
93 | +===================+===============+
94 | | dict | object |
95 | +-------------------+---------------+
96 | | list, tuple | array |
97 | +-------------------+---------------+
98 | | str, unicode | string |
99 | +-------------------+---------------+
100 | | int, long, float | number |
101 | +-------------------+---------------+
102 | | True | true |
103 | +-------------------+---------------+
104 | | False | false |
105 | +-------------------+---------------+
106 | | None | null |
107 | +-------------------+---------------+
108 |
109 | To extend this to recognize other objects, subclass and implement a
110 | ``.default()`` method with another method that returns a serializable
111 | object for ``o`` if possible, otherwise it should call the superclass
112 | implementation (to raise ``TypeError``).
113 | """
114 | __all__ = ['__init__', 'default', 'encode', 'iterencode']
115 | item_separator = ', '
116 | key_separator = ': '
117 | def __init__(self, skipkeys=False, ensure_ascii=True,
118 | check_circular=True, allow_nan=True, sort_keys=False,
119 | indent=None, separators=None, encoding='utf-8', default=None):
120 | """
121 | Constructor for JSONEncoder, with sensible defaults.
122 |
123 | If skipkeys is False, then it is a TypeError to attempt
124 | encoding of keys that are not str, int, long, float or None. If
125 | skipkeys is True, such items are simply skipped.
126 |
127 | If ensure_ascii is True, the output is guaranteed to be str
128 | objects with all incoming unicode characters escaped. If
129 | ensure_ascii is false, the output will be unicode object.
130 |
131 | If check_circular is True, then lists, dicts, and custom encoded
132 | objects will be checked for circular references during encoding to
133 | prevent an infinite recursion (which would cause an OverflowError).
134 | Otherwise, no such check takes place.
135 |
136 | If allow_nan is True, then NaN, Infinity, and -Infinity will be
137 | encoded as such. This behavior is not JSON specification compliant,
138 | but is consistent with most JavaScript based encoders and decoders.
139 | Otherwise, it will be a ValueError to encode such floats.
140 |
141 | If sort_keys is True, then the output of dictionaries will be
142 | sorted by key; this is useful for regression tests to ensure
143 | that JSON serializations can be compared on a day-to-day basis.
144 |
145 | If indent is a non-negative integer, then JSON array
146 | elements and object members will be pretty-printed with that
147 | indent level. An indent level of 0 will only insert newlines.
148 | None is the most compact representation.
149 |
150 | If specified, separators should be a (item_separator, key_separator)
151 | tuple. The default is (', ', ': '). To get the most compact JSON
152 | representation you should specify (',', ':') to eliminate whitespace.
153 |
154 | If specified, default is a function that gets called for objects
155 | that can't otherwise be serialized. It should return a JSON encodable
156 | version of the object or raise a ``TypeError``.
157 |
158 | If encoding is not None, then all input strings will be
159 | transformed into unicode using that encoding prior to JSON-encoding.
160 | The default is UTF-8.
161 | """
162 |
163 | self.skipkeys = skipkeys
164 | self.ensure_ascii = ensure_ascii
165 | self.check_circular = check_circular
166 | self.allow_nan = allow_nan
167 | self.sort_keys = sort_keys
168 | self.indent = indent
169 | self.current_indent_level = 0
170 | if separators is not None:
171 | self.item_separator, self.key_separator = separators
172 | if default is not None:
173 | self.default = default
174 | self.encoding = encoding
175 |
176 | def _newline_indent(self):
177 | return '\n' + (' ' * (self.indent * self.current_indent_level))
178 |
179 | def _iterencode_list(self, lst, markers=None):
180 | if not lst:
181 | yield '[]'
182 | return
183 | if markers is not None:
184 | markerid = id(lst)
185 | if markerid in markers:
186 | raise ValueError("Circular reference detected")
187 | markers[markerid] = lst
188 | yield '['
189 | if self.indent is not None:
190 | self.current_indent_level += 1
191 | newline_indent = self._newline_indent()
192 | separator = self.item_separator + newline_indent
193 | yield newline_indent
194 | else:
195 | newline_indent = None
196 | separator = self.item_separator
197 | first = True
198 | for value in lst:
199 | if first:
200 | first = False
201 | else:
202 | yield separator
203 | for chunk in self._iterencode(value, markers):
204 | yield chunk
205 | if newline_indent is not None:
206 | self.current_indent_level -= 1
207 | yield self._newline_indent()
208 | yield ']'
209 | if markers is not None:
210 | del markers[markerid]
211 |
212 | def _iterencode_dict(self, dct, markers=None):
213 | if not dct:
214 | yield '{}'
215 | return
216 | if markers is not None:
217 | markerid = id(dct)
218 | if markerid in markers:
219 | raise ValueError("Circular reference detected")
220 | markers[markerid] = dct
221 | yield '{'
222 | key_separator = self.key_separator
223 | if self.indent is not None:
224 | self.current_indent_level += 1
225 | newline_indent = self._newline_indent()
226 | item_separator = self.item_separator + newline_indent
227 | yield newline_indent
228 | else:
229 | newline_indent = None
230 | item_separator = self.item_separator
231 | first = True
232 | if self.ensure_ascii:
233 | encoder = encode_basestring_ascii
234 | else:
235 | encoder = encode_basestring
236 | allow_nan = self.allow_nan
237 | if self.sort_keys:
238 | keys = dct.keys()
239 | keys.sort()
240 | items = [(k, dct[k]) for k in keys]
241 | else:
242 | items = dct.iteritems()
243 | _encoding = self.encoding
244 | _do_decode = (_encoding is not None
245 | and not (_encoding == 'utf-8'))
246 | for key, value in items:
247 | if isinstance(key, str):
248 | if _do_decode:
249 | key = key.decode(_encoding)
250 | elif isinstance(key, basestring):
251 | pass
252 | # JavaScript is weakly typed for these, so it makes sense to
253 | # also allow them. Many encoders seem to do something like this.
254 | elif isinstance(key, float):
255 | key = floatstr(key, allow_nan)
256 | elif isinstance(key, (int, long)):
257 | key = str(key)
258 | elif key is True:
259 | key = 'true'
260 | elif key is False:
261 | key = 'false'
262 | elif key is None:
263 | key = 'null'
264 | elif self.skipkeys:
265 | continue
266 | else:
267 | raise TypeError("key %r is not a string" % (key,))
268 | if first:
269 | first = False
270 | else:
271 | yield item_separator
272 | yield encoder(key)
273 | yield key_separator
274 | for chunk in self._iterencode(value, markers):
275 | yield chunk
276 | if newline_indent is not None:
277 | self.current_indent_level -= 1
278 | yield self._newline_indent()
279 | yield '}'
280 | if markers is not None:
281 | del markers[markerid]
282 |
283 | def _iterencode(self, o, markers=None):
284 | if isinstance(o, basestring):
285 | if self.ensure_ascii:
286 | encoder = encode_basestring_ascii
287 | else:
288 | encoder = encode_basestring
289 | _encoding = self.encoding
290 | if (_encoding is not None and isinstance(o, str)
291 | and not (_encoding == 'utf-8')):
292 | o = o.decode(_encoding)
293 | yield encoder(o)
294 | elif o is None:
295 | yield 'null'
296 | elif o is True:
297 | yield 'true'
298 | elif o is False:
299 | yield 'false'
300 | elif isinstance(o, (int, long)):
301 | yield str(o)
302 | elif isinstance(o, float):
303 | yield floatstr(o, self.allow_nan)
304 | elif isinstance(o, (list, tuple)):
305 | for chunk in self._iterencode_list(o, markers):
306 | yield chunk
307 | elif isinstance(o, dict):
308 | for chunk in self._iterencode_dict(o, markers):
309 | yield chunk
310 | else:
311 | if markers is not None:
312 | markerid = id(o)
313 | if markerid in markers:
314 | raise ValueError("Circular reference detected")
315 | markers[markerid] = o
316 | for chunk in self._iterencode_default(o, markers):
317 | yield chunk
318 | if markers is not None:
319 | del markers[markerid]
320 |
321 | def _iterencode_default(self, o, markers=None):
322 | newobj = self.default(o)
323 | return self._iterencode(newobj, markers)
324 |
325 | def default(self, o):
326 | """
327 | Implement this method in a subclass such that it returns
328 | a serializable object for ``o``, or calls the base implementation
329 | (to raise a ``TypeError``).
330 |
331 | For example, to support arbitrary iterators, you could
332 | implement default like this::
333 |
334 | def default(self, o):
335 | try:
336 | iterable = iter(o)
337 | except TypeError:
338 | pass
339 | else:
340 | return list(iterable)
341 | return JSONEncoder.default(self, o)
342 | """
343 | raise TypeError("%r is not JSON serializable" % (o,))
344 |
345 | def encode(self, o):
346 | """
347 | Return a JSON string representation of a Python data structure.
348 |
349 | >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
350 | '{"foo": ["bar", "baz"]}'
351 | """
352 | # This is for extremely simple cases and benchmarks.
353 | if isinstance(o, basestring):
354 | if isinstance(o, str):
355 | _encoding = self.encoding
356 | if (_encoding is not None
357 | and not (_encoding == 'utf-8')):
358 | o = o.decode(_encoding)
359 | if self.ensure_ascii:
360 | return encode_basestring_ascii(o)
361 | else:
362 | return encode_basestring(o)
363 | # This doesn't pass the iterator directly to ''.join() because the
364 | # exceptions aren't as detailed. The list call should be roughly
365 | # equivalent to the PySequence_Fast that ''.join() would do.
366 | chunks = list(self.iterencode(o))
367 | return ''.join(chunks)
368 |
369 | def iterencode(self, o):
370 | """
371 | Encode the given object and yield each string
372 | representation as available.
373 |
374 | For example::
375 |
376 | for chunk in JSONEncoder().iterencode(bigobject):
377 | mysocket.write(chunk)
378 | """
379 | if self.check_circular:
380 | markers = {}
381 | else:
382 | markers = None
383 | return self._iterencode(o, markers)
384 |
385 | __all__ = ['JSONEncoder']
386 |
--------------------------------------------------------------------------------
/appspot/simplejson/__init__.py:
--------------------------------------------------------------------------------
1 | r"""
2 | A simple, fast, extensible JSON encoder and decoder
3 |
4 | JSON (JavaScript Object Notation) is a subset of
5 | JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
6 | interchange format.
7 |
8 | simplejson exposes an API familiar to uses of the standard library
9 | marshal and pickle modules.
10 |
11 | Encoding basic Python object hierarchies::
12 |
13 | >>> import simplejson
14 | >>> simplejson.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
15 | '["foo", {"bar": ["baz", null, 1.0, 2]}]'
16 | >>> print simplejson.dumps("\"foo\bar")
17 | "\"foo\bar"
18 | >>> print simplejson.dumps(u'\u1234')
19 | "\u1234"
20 | >>> print simplejson.dumps('\\')
21 | "\\"
22 | >>> print simplejson.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
23 | {"a": 0, "b": 0, "c": 0}
24 | >>> from StringIO import StringIO
25 | >>> io = StringIO()
26 | >>> simplejson.dump(['streaming API'], io)
27 | >>> io.getvalue()
28 | '["streaming API"]'
29 |
30 | Compact encoding::
31 |
32 | >>> import simplejson
33 | >>> simplejson.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
34 | '[1,2,3,{"4":5,"6":7}]'
35 |
36 | Pretty printing::
37 |
38 | >>> import simplejson
39 | >>> print simplejson.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
40 | {
41 | "4": 5,
42 | "6": 7
43 | }
44 |
45 | Decoding JSON::
46 |
47 | >>> import simplejson
48 | >>> simplejson.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
49 | [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
50 | >>> simplejson.loads('"\\"foo\\bar"')
51 | u'"foo\x08ar'
52 | >>> from StringIO import StringIO
53 | >>> io = StringIO('["streaming API"]')
54 | >>> simplejson.load(io)
55 | [u'streaming API']
56 |
57 | Specializing JSON object decoding::
58 |
59 | >>> import simplejson
60 | >>> def as_complex(dct):
61 | ... if '__complex__' in dct:
62 | ... return complex(dct['real'], dct['imag'])
63 | ... return dct
64 | ...
65 | >>> simplejson.loads('{"__complex__": true, "real": 1, "imag": 2}',
66 | ... object_hook=as_complex)
67 | (1+2j)
68 | >>> import decimal
69 | >>> simplejson.loads('1.1', parse_float=decimal.Decimal)
70 | Decimal("1.1")
71 |
72 | Extending JSONEncoder::
73 |
74 | >>> import simplejson
75 | >>> class ComplexEncoder(simplejson.JSONEncoder):
76 | ... def default(self, obj):
77 | ... if isinstance(obj, complex):
78 | ... return [obj.real, obj.imag]
79 | ... return simplejson.JSONEncoder.default(self, obj)
80 | ...
81 | >>> dumps(2 + 1j, cls=ComplexEncoder)
82 | '[2.0, 1.0]'
83 | >>> ComplexEncoder().encode(2 + 1j)
84 | '[2.0, 1.0]'
85 | >>> list(ComplexEncoder().iterencode(2 + 1j))
86 | ['[', '2.0', ', ', '1.0', ']']
87 |
88 |
89 | Using simplejson from the shell to validate and
90 | pretty-print::
91 |
92 | $ echo '{"json":"obj"}' | python -msimplejson.tool
93 | {
94 | "json": "obj"
95 | }
96 | $ echo '{ 1.2:3.4}' | python -msimplejson.tool
97 | Expecting property name: line 1 column 2 (char 2)
98 |
99 | Note that the JSON produced by this module's default settings
100 | is a subset of YAML, so it may be used as a serializer for that as well.
101 | """
102 | __version__ = '1.9.2'
103 | __all__ = [
104 | 'dump', 'dumps', 'load', 'loads',
105 | 'JSONDecoder', 'JSONEncoder',
106 | ]
107 |
108 | if __name__ == '__main__':
109 | import warnings
110 | warnings.warn('python -msimplejson is deprecated, use python -msiplejson.tool', DeprecationWarning)
111 | from simplejson.decoder import JSONDecoder
112 | from simplejson.encoder import JSONEncoder
113 | else:
114 | from decoder import JSONDecoder
115 | from encoder import JSONEncoder
116 |
117 | _default_encoder = JSONEncoder(
118 | skipkeys=False,
119 | ensure_ascii=True,
120 | check_circular=True,
121 | allow_nan=True,
122 | indent=None,
123 | separators=None,
124 | encoding='utf-8',
125 | default=None,
126 | )
127 |
128 | def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
129 | allow_nan=True, cls=None, indent=None, separators=None,
130 | encoding='utf-8', default=None, **kw):
131 | """
132 | Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
133 | ``.write()``-supporting file-like object).
134 |
135 | If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
136 | (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
137 | will be skipped instead of raising a ``TypeError``.
138 |
139 | If ``ensure_ascii`` is ``False``, then the some chunks written to ``fp``
140 | may be ``unicode`` instances, subject to normal Python ``str`` to
141 | ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
142 | understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
143 | to cause an error.
144 |
145 | If ``check_circular`` is ``False``, then the circular reference check
146 | for container types will be skipped and a circular reference will
147 | result in an ``OverflowError`` (or worse).
148 |
149 | If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
150 | serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
151 | in strict compliance of the JSON specification, instead of using the
152 | JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
153 |
154 | If ``indent`` is a non-negative integer, then JSON array elements and object
155 | members will be pretty-printed with that indent level. An indent level
156 | of 0 will only insert newlines. ``None`` is the most compact representation.
157 |
158 | If ``separators`` is an ``(item_separator, dict_separator)`` tuple
159 | then it will be used instead of the default ``(', ', ': ')`` separators.
160 | ``(',', ':')`` is the most compact JSON representation.
161 |
162 | ``encoding`` is the character encoding for str instances, default is UTF-8.
163 |
164 | ``default(obj)`` is a function that should return a serializable version
165 | of obj or raise TypeError. The default simply raises TypeError.
166 |
167 | To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
168 | ``.default()`` method to serialize additional types), specify it with
169 | the ``cls`` kwarg.
170 | """
171 | # cached encoder
172 | if (skipkeys is False and ensure_ascii is True and
173 | check_circular is True and allow_nan is True and
174 | cls is None and indent is None and separators is None and
175 | encoding == 'utf-8' and default is None and not kw):
176 | iterable = _default_encoder.iterencode(obj)
177 | else:
178 | if cls is None:
179 | cls = JSONEncoder
180 | iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
181 | check_circular=check_circular, allow_nan=allow_nan, indent=indent,
182 | separators=separators, encoding=encoding,
183 | default=default, **kw).iterencode(obj)
184 | # could accelerate with writelines in some versions of Python, at
185 | # a debuggability cost
186 | for chunk in iterable:
187 | fp.write(chunk)
188 |
189 |
190 | def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
191 | allow_nan=True, cls=None, indent=None, separators=None,
192 | encoding='utf-8', default=None, **kw):
193 | """
194 | Serialize ``obj`` to a JSON formatted ``str``.
195 |
196 | If ``skipkeys`` is ``True`` then ``dict`` keys that are not basic types
197 | (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
198 | will be skipped instead of raising a ``TypeError``.
199 |
200 | If ``ensure_ascii`` is ``False``, then the return value will be a
201 | ``unicode`` instance subject to normal Python ``str`` to ``unicode``
202 | coercion rules instead of being escaped to an ASCII ``str``.
203 |
204 | If ``check_circular`` is ``False``, then the circular reference check
205 | for container types will be skipped and a circular reference will
206 | result in an ``OverflowError`` (or worse).
207 |
208 | If ``allow_nan`` is ``False``, then it will be a ``ValueError`` to
209 | serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
210 | strict compliance of the JSON specification, instead of using the
211 | JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
212 |
213 | If ``indent`` is a non-negative integer, then JSON array elements and
214 | object members will be pretty-printed with that indent level. An indent
215 | level of 0 will only insert newlines. ``None`` is the most compact
216 | representation.
217 |
218 | If ``separators`` is an ``(item_separator, dict_separator)`` tuple
219 | then it will be used instead of the default ``(', ', ': ')`` separators.
220 | ``(',', ':')`` is the most compact JSON representation.
221 |
222 | ``encoding`` is the character encoding for str instances, default is UTF-8.
223 |
224 | ``default(obj)`` is a function that should return a serializable version
225 | of obj or raise TypeError. The default simply raises TypeError.
226 |
227 | To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
228 | ``.default()`` method to serialize additional types), specify it with
229 | the ``cls`` kwarg.
230 | """
231 | # cached encoder
232 | if (skipkeys is False and ensure_ascii is True and
233 | check_circular is True and allow_nan is True and
234 | cls is None and indent is None and separators is None and
235 | encoding == 'utf-8' and default is None and not kw):
236 | return _default_encoder.encode(obj)
237 | if cls is None:
238 | cls = JSONEncoder
239 | return cls(
240 | skipkeys=skipkeys, ensure_ascii=ensure_ascii,
241 | check_circular=check_circular, allow_nan=allow_nan, indent=indent,
242 | separators=separators, encoding=encoding, default=default,
243 | **kw).encode(obj)
244 |
245 |
246 | _default_decoder = JSONDecoder(encoding=None, object_hook=None)
247 |
248 |
249 | def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
250 | parse_int=None, parse_constant=None, **kw):
251 | """
252 | Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
253 | a JSON document) to a Python object.
254 |
255 | If the contents of ``fp`` is encoded with an ASCII based encoding other
256 | than utf-8 (e.g. latin-1), then an appropriate ``encoding`` name must
257 | be specified. Encodings that are not ASCII based (such as UCS-2) are
258 | not allowed, and should be wrapped with
259 | ``codecs.getreader(fp)(encoding)``, or simply decoded to a ``unicode``
260 | object and passed to ``loads()``
261 |
262 | ``object_hook`` is an optional function that will be called with the
263 | result of any object literal decode (a ``dict``). The return value of
264 | ``object_hook`` will be used instead of the ``dict``. This feature
265 | can be used to implement custom decoders (e.g. JSON-RPC class hinting).
266 |
267 | To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
268 | kwarg.
269 | """
270 | return loads(fp.read(),
271 | encoding=encoding, cls=cls, object_hook=object_hook,
272 | parse_float=parse_float, parse_int=parse_int,
273 | parse_constant=parse_constant, **kw)
274 |
275 |
276 | def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
277 | parse_int=None, parse_constant=None, **kw):
278 | """
279 | Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
280 | document) to a Python object.
281 |
282 | If ``s`` is a ``str`` instance and is encoded with an ASCII based encoding
283 | other than utf-8 (e.g. latin-1) then an appropriate ``encoding`` name
284 | must be specified. Encodings that are not ASCII based (such as UCS-2)
285 | are not allowed and should be decoded to ``unicode`` first.
286 |
287 | ``object_hook`` is an optional function that will be called with the
288 | result of any object literal decode (a ``dict``). The return value of
289 | ``object_hook`` will be used instead of the ``dict``. This feature
290 | can be used to implement custom decoders (e.g. JSON-RPC class hinting).
291 |
292 | ``parse_float``, if specified, will be called with the string
293 | of every JSON float to be decoded. By default this is equivalent to
294 | float(num_str). This can be used to use another datatype or parser
295 | for JSON floats (e.g. decimal.Decimal).
296 |
297 | ``parse_int``, if specified, will be called with the string
298 | of every JSON int to be decoded. By default this is equivalent to
299 | int(num_str). This can be used to use another datatype or parser
300 | for JSON integers (e.g. float).
301 |
302 | ``parse_constant``, if specified, will be called with one of the
303 | following strings: -Infinity, Infinity, NaN, null, true, false.
304 | This can be used to raise an exception if invalid JSON numbers
305 | are encountered.
306 |
307 | To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
308 | kwarg.
309 | """
310 | if (cls is None and encoding is None and object_hook is None and
311 | parse_int is None and parse_float is None and
312 | parse_constant is None and not kw):
313 | return _default_decoder.decode(s)
314 | if cls is None:
315 | cls = JSONDecoder
316 | if object_hook is not None:
317 | kw['object_hook'] = object_hook
318 | if parse_float is not None:
319 | kw['parse_float'] = parse_float
320 | if parse_int is not None:
321 | kw['parse_int'] = parse_int
322 | if parse_constant is not None:
323 | kw['parse_constant'] = parse_constant
324 | return cls(encoding=encoding, **kw).decode(s)
325 |
326 |
327 | #
328 | # Compatibility cruft from other libraries
329 | #
330 |
331 |
332 | def decode(s):
333 | """
334 | demjson, python-cjson API compatibility hook. Use loads(s) instead.
335 | """
336 | import warnings
337 | warnings.warn("simplejson.loads(s) should be used instead of decode(s)",
338 | DeprecationWarning)
339 | return loads(s)
340 |
341 |
342 | def encode(obj):
343 | """
344 | demjson, python-cjson compatibility hook. Use dumps(s) instead.
345 | """
346 | import warnings
347 | warnings.warn("simplejson.dumps(s) should be used instead of encode(s)",
348 | DeprecationWarning)
349 | return dumps(obj)
350 |
351 |
352 | def read(s):
353 | """
354 | jsonlib, JsonUtils, python-json, json-py API compatibility hook.
355 | Use loads(s) instead.
356 | """
357 | import warnings
358 | warnings.warn("simplejson.loads(s) should be used instead of read(s)",
359 | DeprecationWarning)
360 | return loads(s)
361 |
362 |
363 | def write(obj):
364 | """
365 | jsonlib, JsonUtils, python-json, json-py API compatibility hook.
366 | Use dumps(s) instead.
367 | """
368 | import warnings
369 | warnings.warn("simplejson.dumps(s) should be used instead of write(s)",
370 | DeprecationWarning)
371 | return dumps(obj)
372 |
373 |
374 | if __name__ == '__main__':
375 | import simplejson.tool
376 | simplejson.tool.main()
377 |
--------------------------------------------------------------------------------
/appspot/restlet/intgutil.py:
--------------------------------------------------------------------------------
1 | ''' Json-Rest-handlin' integration helper.
2 |
3 | This module offers a JSON+REST-handling integration class meant to be used with
4 | Google App Engine (hooked into a webapp.RequestHandler subclass); it can be
5 | hooked up by simply passing an object h with attributes h.request and
6 | h.response that are duck-like those of webapp.RequestHandler.
7 |
8 | On hookup, the integration-helper class overrides the get/set/put/delete
9 | methods of the object hooking up to it so that they respond appropriately to
10 | REST requests (as documented in json_rest.txt) based on registrations performed
11 | in restutil, parsing and formatting JSON payloads based on jsonutil.
12 |
13 | IOW, this helper integrates functionality found in other modules of the
14 | gae-json-rest package:
15 | parsutil
16 | restutil
17 | jsonutil
18 | "putting it all together" into a highly-reusable (but still modestly
19 | customizable) REST-style, JSON-transport server web-app for GAE.
20 |
21 | TODO: decide what arguments/parameters are passed to various kinds of
22 | methods being called, and implement that decision; add MANY tests!!!
23 | '''
24 | import logging
25 |
26 | import jsonutil
27 | import parsutil
28 | import restutil
29 | import auth
30 |
31 | class JsonRestHelper(object):
32 |
33 | prefix_to_ignore = '/'
34 | #prefix_to_ignore = '/rest'
35 | __delete_parser = __put_parser = __post_parser = __get_parser = None
36 |
37 | def hookup(self, handler):
38 | """ "Hooks up" this helper instance to a handler object.
39 |
40 | Args:
41 | handler: an instance of a webapp.RequestHandler subclass
42 | Side effects:
43 | - sets self.handler to handler
44 | - sets the handler's get, put, post and delete methods from self
45 | - sets the handler's jrh attribute to self
46 | Note this creates reference loops and MUST be undone in hookdown!
47 | """
48 | logging.info('hookup %r/%r', self, handler)
49 | self.handler = handler
50 | handler.get = self.get
51 | handler.put = self.put
52 | handler.post = self.post
53 | handler.delete = self.delete
54 | handler.jrh = self
55 |
56 | def is_authorized(self):
57 | return auth.is_authorized(self.handler.request)
58 |
59 | def hookdown(self):
60 | """ Undoes the effects of self.hookup """
61 | logging.info('hookdn %r/%r', self, self.handler)
62 | h = self.handler
63 | h.jrh = self.handler = None
64 | del h.get, h.put, h.post, h.delete
65 |
66 | def _serve(self, data):
67 | """ Serves a result in JSON, and hooks-down from the handler """
68 | try: return jsonutil.send_json(self.handler.response, data)
69 | finally: self.hookdown()
70 |
71 | def get_model(self, modelname):
72 | """ Gets a model (or None) given a model name.
73 |
74 | Args:
75 | modelname: a string that should name a model
76 | Returns:
77 | a model class, or None (if no model's registered with that name)
78 | Side effects:
79 | sets response status to 400 if no model's registered with that name
80 | """
81 | model = restutil.modelClassFromName(modelname)
82 | if model is None:
83 | self.handler.response.set_status(400, 'Model %r not found' % modelname)
84 | return model
85 |
86 | def get_special(self, specialname):
87 | """ Gets a special (or None) given a special object's name.
88 |
89 | Args:
90 | specialname: a string that should name a special object
91 | Returns:
92 | a special object, or None (if no special's registered with that name)
93 | Side effects:
94 | sets response status to 400 if no special's registered with that name
95 | """
96 | special = restutil.specialFromName(specialname)
97 | if special is None:
98 | self.handler.response.set_status(400, 'Special object %r not found' %
99 | specialname)
100 | return special
101 |
102 | def get_entity(self, modelname, strid):
103 | """ Gets an entity (or None) given a model name and entity ID as string.
104 |
105 | Args:
106 | modelname: a string that should name a model
107 | strid: the str(id) for the numeric id of an entity of that model
108 | Returns:
109 | an entity, or None (if something went wrong)
110 | Side effects:
111 | sets response status to 400 or 404 if various things went wrong
112 | """
113 | model = self.get_model(modelname)
114 | if model is None:
115 | return None
116 | entity = model.get_by_id(int(strid))
117 | if entity is None:
118 | self.handler.response.set_status(404, "Entity %s/%s not found" %
119 | (modelname, strid))
120 | return entity
121 |
122 | def get_special_method(self, specialname, methodname):
123 | """ Gets a special object method (or None) given special & method names.
124 |
125 | Args:
126 | specialname: a string that should name a special object
127 | methodname: a string that should name a method of that special object
128 | Returns:
129 | the method with that name in the special object of that name
130 | Side effects:
131 | sets response status to 400 if special or method not found
132 | """
133 | special = self.get_special(specialname)
134 | if special is None: return ''
135 | method = special.get(methodname)
136 | if method is None:
137 | self.handler.response.set_status(400, 'Method %r not found in special %r'
138 | % (methodname, specialname))
139 | return method
140 |
141 | def _methodhelper(self, modelname, methodname, _getter):
142 | """ Gets a model or instance method given model and method names & getter.
143 |
144 | Args:
145 | modelname: a string that should name a model
146 | methodname: a string that should name a method of that model
147 | (model-method or instance-method, dep. on _getter)
148 | Returns:
149 | a method object, or None if either model or method were not found
150 | Side effects:
151 | sets response status to 400 if either model or method were not found
152 | """
153 | model = self.get_model(modelname)
154 | if model is None: return ''
155 | method = _getter(model, methodname)
156 | if method is None:
157 | self.handler.response.set_status(400, 'Method %r not found in model' %
158 | (methodname, modelname))
159 | return method
160 |
161 | def get_model_method(self, modelname, methodname):
162 | """ Gets a model's method given model and method names.
163 |
164 | Args:
165 | modelname: a string that should name a model
166 | methodname: a sring that should name a method of that model
167 | Returns:
168 | a method object, or None if either model or method were not found
169 | Side effects:
170 | sets response status to 400 if either model or method were not found
171 | """
172 | return self._methodhelper(modelname, methodname, restutil.modelMethodByName)
173 |
174 | def get_instance_method(self, modelname, methodname):
175 | """ Gets an instance method given model and method names.
176 |
177 | Args:
178 | modelname: a string that should name a model
179 | methodname: a sring that should name an instance method of that model
180 | Returns:
181 | a method object, or None if either model or method were not found
182 | Side effects:
183 | sets response status to 400 if either model or method were not found
184 | """
185 | return self._methodhelper(modelname, methodname, restutil.instanceMethodByName)
186 |
187 |
188 | def do_delete(self, model, strid):
189 | """ Hook method to delete an entity given modelname and strid.
190 | """
191 | entity = self.get_entity(model, strid)
192 | if entity is not None:
193 | entity.delete()
194 | return {}
195 |
196 | def delete(self, prefix=None):
197 | """ Delete an entity given by path modelname/strid
198 | Response is JSON for an empty jobj.
199 | """
200 | if not self.is_authorized():
201 | self.handler.response.set_status(403, "You don't have permission to access on this server.")
202 | return self._serve({})
203 | if self.__delete_parser is None:
204 | self.__delete_parser = parsutil.RestUrlParser(self.prefix_to_ignore,
205 | do_model_strid=self.do_delete)
206 | path = self.handler.request.path
207 | result = self.__delete_parser.process(path, prefix)
208 | if result is None or isinstance(result, tuple):
209 | self.handler.response.set_status(400, 'Invalid URL for DELETE: %r' % path)
210 | return self._serve(result)
211 |
212 | def do_put(self, model, strid):
213 | """ Hook method to update an entity given modelname and strid.
214 | """
215 | entity = self.get_entity(model, strid)
216 | if entity is None:
217 | return {}
218 | jobj = jsonutil.receive_json(self.handler.request)
219 | jobj = jsonutil.update_entity(entity, jobj)
220 | updated_entity_path = "/%s/%s" % (model, jobj['id'])
221 | self.handler.response.set_status(200, 'Updated entity %s' %
222 | updated_entity_path)
223 | return jobj
224 |
225 | def put(self, prefix=None):
226 | """ Update an entity given by path modelname/strid
227 | Request body is JSON for the needed changes
228 | Response is JSON for the updated entity.
229 | """
230 | if not self.is_authorized():
231 | self.handler.response.set_status(403, "You don't have permission to access on this server.")
232 | return self._serve({})
233 | if self.__put_parser is None:
234 | self.__put_parser = parsutil.RestUrlParser(self.prefix_to_ignore,
235 | do_model_strid=self.do_put)
236 | path = self.handler.request.path
237 | result = self.__put_parser.process(path, prefix)
238 | if result is None or isinstance(result, tuple):
239 | self.handler.response.set_status(400, 'Invalid URL for POST: %r' % path)
240 | return self._serve({})
241 | return self._serve(result)
242 |
243 | def do_post_special_method(self, special, method):
244 | """ Hook method to call a method on a special object given names.
245 | """
246 | themethod = self.get_special_method(special, method)
247 | if special is None: return ''
248 | try: return themethod()
249 | except Exception, e:
250 | self.handler.response.set_status(400, "Can't call %r/%r: %s" % (
251 | special, method, e))
252 | return ''
253 |
254 | def do_post_model(self, model):
255 | """ Hook method to "call a model" (to create an entity)
256 | """
257 | themodel = self.get_model(model)
258 | if themodel is None: return ''
259 | jobj = jsonutil.receive_json(self.handler.request)
260 | jobj = jsonutil.make_entity(themodel, jobj)
261 | self._classname = model
262 | return jobj
263 |
264 | def do_post_model_method(self, model, method):
265 | """ Hook method to call a method on a model given s.
266 | """
267 | themethod = self.get_model_method(model, method)
268 | if themethod is None: return ''
269 | try: return themethod()
270 | except Exception, e:
271 | self.handler.response.set_status(400, "Can't call %r/%r: %s" % (
272 | model, method, e))
273 | return ''
274 |
275 | def do_post_entity_method(self, model, strid, method):
276 | """ Hook method to call a method on an entity given s and strid.
277 | """
278 | themethod = self.get_instance_method(model, method)
279 | if themethod is None: return ''
280 | entity = self.get_entity(model, strid)
281 | if entity is None: return ''
282 | try: return themethod(entity)
283 | except Exception, e:
284 | self.handler.response.set_status(400, "Can't call %r/%r/%r: %s" % (
285 | model, strid, method, e))
286 | return ''
287 |
288 | def post(self, prefix=None):
289 | """ Create an entity ("call a model") or perform other non-R/O "call".
290 |
291 | Request body is JSON for the needed entity or other call "args".
292 | Response is JSON for the updated entity (or "call result").
293 | """
294 | if not self.is_authorized():
295 | self.handler.response.set_status(403, "You don't have permission to access on this server.")
296 | return self._serve({})
297 | if self.__post_parser is None:
298 | self.__post_parser = parsutil.RestUrlParser(self.prefix_to_ignore,
299 | do_special_method=self.do_post_special_method,
300 | do_model=self.do_post_model,
301 | do_model_method=self.do_post_model_method,
302 | do_model_strid_method=self.do_post_entity_method,
303 | )
304 | path = self.handler.request.path
305 | result = self.__post_parser.process(path, prefix)
306 | if result is None or isinstance(result, tuple):
307 | self.handler.response.set_status(400, 'Invalid URL for POST: %r' % path)
308 | return self._serve({})
309 | try:
310 | strid = result['id']
311 | except (KeyError, AttributeError, TypeError):
312 | pass
313 | else:
314 | new_entity_path = "/%s/%s" % (self._classname, strid)
315 | logging.info('Post (%r) created %r', path, new_entity_path)
316 | self.handler.response.headers['Location'] = new_entity_path
317 | self.handler.response.set_status(201, 'Created entity %s' %
318 | new_entity_path)
319 | return self._serve(result)
320 |
321 | def do_get_special_method(self, special, method):
322 | """ Hook method to R/O call a method on a special object given names.
323 | """
324 | themethod = self.get_special_method(special, method)
325 | if themethod is None: return ''
326 | try: return themethod()
327 | except Exception, e:
328 | self.handler.response.set_status(400, "Can't call %r/%r: %s" % (
329 | special, method, e))
330 | return ''
331 |
332 | def do_get_model(self, model):
333 | """ Hook method to R/O "call a model" ("get list of all its IDs"...?)
334 | """
335 | themodel = self.get_model(model)
336 | if themodel is None: return ''
337 | return [jsonutil.id_of(x) for x in themodel.all()]
338 |
339 | def do_get_entity(self, model, strid):
340 | """ Hook method to get data about an entity given model name and strid
341 | """
342 | entity = self.get_entity(model, strid)
343 | if entity is None:
344 | return {}
345 | return jsonutil.make_jobj(entity)
346 |
347 | def do_get_model_method(self, model, method):
348 | """ Hook method to R/O call a method on a model given s.
349 | """
350 | themethod = self.get_model_method(model, method)
351 | if themethod is None: return ''
352 | try: return themethod()
353 | except Exception, e:
354 | self.handler.response.set_status(400, "Can't call %r/%r: %s" % (
355 | model, method, e))
356 | return ''
357 |
358 | def do_get_entity_method(self, model, strid, method):
359 | """ Hook method to R/O call a method on an entity given s and strid.
360 | """
361 | themethod = self.get_instance_method(model, method)
362 | if themethod is None: return ''
363 | entity = self.get_entity(model, strid)
364 | if entity is None: return ''
365 | try: return themethod(entity)
366 | except Exception, e:
367 | self.handler.response.set_status(400, "Can't call %r/%r/%r: %s" % (
368 | model, strid, method, e))
369 | return ''
370 |
371 | def get(self, prefix=None):
372 | """ Get JSON data for entity IDs of a model, or all about an entity.
373 |
374 | Depending on the request path, serve as JSON to the response object:
375 | - for a path of /classname/id, a jobj for that entity
376 | - for a path of /classname, a list of id-only jobjs for that model
377 | - or, the results of the method being called (should be R/O!)
378 | """
379 | if not self.is_authorized():
380 | self.handler.response.set_status(403, "You don't have permission to access on this server.")
381 | return self._serve({})
382 | logging.info('GET path=%r, prefix=%r', self.handler.request.path, prefix)
383 | if self.__get_parser is None:
384 | self.__get_parser = parsutil.RestUrlParser(self.prefix_to_ignore,
385 | do_special_method=self.do_get_special_method,
386 | do_model=self.do_get_model,
387 | do_model_strid=self.do_get_entity,
388 | do_model_method=self.do_get_model_method,
389 | do_model_strid_method=self.do_get_entity_method,
390 | )
391 | path = self.handler.request.path
392 |
393 | # hacky/kludgy special-case: serve all model names (TODO: remove this!)
394 | # (need to have proper %meta special w/methods to get such info!)
395 | if prefix is not None and path.strip('/') == prefix.strip('/'):
396 | result = restutil.allModelClassNames()
397 | logging.info('Hacky case (%r): %r', path, result)
398 | return self._serve(result)
399 |
400 | result = self.__get_parser.process(path, prefix)
401 | if result is None or isinstance(result, tuple):
402 | self.handler.response.set_status(400, 'Invalid URL for GET: %r' % path)
403 | return self._serve({})
404 | return self._serve(result)
405 |
406 | # expose a single helper object, shd be reusable
407 |
408 | helper = JsonRestHelper()
409 |
410 | # just for testing...:
411 | import wsgiref.handlers
412 | from google.appengine.ext import webapp
413 | import models
414 |
415 | class _TestCrudRestHandler(webapp.RequestHandler):
416 | def __init__(self, *a, **k):
417 | webapp.RequestHandler.__init__(self, *a, **k)
418 | helper.hookup(self)
419 |
420 | def main():
421 | logging.info('intgutil test main()')
422 | application = webapp.WSGIApplication([('/(rest)/.*', _TestCrudRestHandler)],
423 | debug=True)
424 | wsgiref.handlers.CGIHandler().run(application)
425 |
426 | if __name__ == '__main__':
427 | main()
428 |
--------------------------------------------------------------------------------