ct 0.10.8.114__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cantools/__init__.py +24 -0
- cantools/_db.py +142 -0
- cantools/_memcache.py +76 -0
- cantools/_pay.py +46 -0
- cantools/admin.py +31 -0
- cantools/cfg.py +347 -0
- cantools/config.py +131 -0
- cantools/db/__init__.py +18 -0
- cantools/db/admin.py +27 -0
- cantools/db/gae/__init__.py +0 -0
- cantools/db/gae/model.py +127 -0
- cantools/db/gae/properties.py +35 -0
- cantools/db/wp.py +99 -0
- cantools/geo.py +188 -0
- cantools/hooks.py +13 -0
- cantools/scripts/__init__.py +0 -0
- cantools/scripts/bench.py +167 -0
- cantools/scripts/builder.py +272 -0
- cantools/scripts/deploy.py +154 -0
- cantools/scripts/doc.py +239 -0
- cantools/scripts/index.py +226 -0
- cantools/scripts/init.py +345 -0
- cantools/scripts/migrate.py +593 -0
- cantools/scripts/pubsub/__init__.py +28 -0
- cantools/scripts/pubsub/actor.py +13 -0
- cantools/scripts/pubsub/bots.py +143 -0
- cantools/scripts/pubsub/channel.py +85 -0
- cantools/scripts/pubsub/ps.py +145 -0
- cantools/scripts/pubsub/user.py +51 -0
- cantools/scripts/start.py +53 -0
- cantools/scripts/util.py +24 -0
- cantools/util/__init__.py +78 -0
- cantools/util/admin.py +620 -0
- cantools/util/data.py +109 -0
- cantools/util/media.py +303 -0
- cantools/util/package.py +125 -0
- cantools/util/system.py +73 -0
- cantools/web/__init__.py +9 -0
- cantools/web/dez_server/__init__.py +1 -0
- cantools/web/dez_server/controller.py +129 -0
- cantools/web/dez_server/cron.py +115 -0
- cantools/web/dez_server/daemons.py +64 -0
- cantools/web/dez_server/mail.py +24 -0
- cantools/web/dez_server/response.py +63 -0
- cantools/web/dez_server/routes.py +21 -0
- cantools/web/dez_server/server.py +229 -0
- cantools/web/dez_server/sms.py +12 -0
- cantools/web/gae_server.py +68 -0
- cantools/web/util.py +552 -0
- ct-0.10.8.114.dist-info/LICENSE +9 -0
- ct-0.10.8.114.dist-info/METADATA +25 -0
- ct-0.10.8.114.dist-info/RECORD +55 -0
- ct-0.10.8.114.dist-info/WHEEL +5 -0
- ct-0.10.8.114.dist-info/entry_points.txt +10 -0
- ct-0.10.8.114.dist-info/top_level.txt +1 -0
cantools/scripts/doc.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
### Usage: ctdoc [-w]
|
|
3
|
+
|
|
4
|
+
### Options:
|
|
5
|
+
-h, --help show this help message and exit
|
|
6
|
+
-w, --web build web docs
|
|
7
|
+
-a, --auto use auto mode (even with a plugin)
|
|
8
|
+
-o, --omit omit any files from autodoc?
|
|
9
|
+
|
|
10
|
+
Run from cantools root (contains setup.py, cantools/, README.md, etc), from root
|
|
11
|
+
of a CT plugin, or from within a custom project. In cantools, builds docs for all
|
|
12
|
+
frontend (js) and CLI (py) files. In plugin, docs consist of about file (about.txt),
|
|
13
|
+
initialization config (init.py) and default frontend config (js/config.js). In custom
|
|
14
|
+
(project) mode (when ctdoc is run somewhere other than cantools root or a plugin root,
|
|
15
|
+
and additionally a configuration file, doc.cfg, is present), for each path declared in
|
|
16
|
+
doc.cfg, include the docstring of each file specified, as well as the contents of
|
|
17
|
+
about.txt (if present). Lastly, auto mode doesn't require configuration (doc.cfg) --
|
|
18
|
+
instead, it recurses through the directories of your project, and includes the contents of
|
|
19
|
+
any about.txt files, as well as (the top of) any py/js file that starts with a docstring.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os, json
|
|
23
|
+
from optparse import OptionParser
|
|
24
|
+
from cantools import __version__, config
|
|
25
|
+
from cantools.util import read, write, log, cp, cmd
|
|
26
|
+
from cantools.util.package import here
|
|
27
|
+
|
|
28
|
+
WEB = []
|
|
29
|
+
ALTS = {
|
|
30
|
+
"pubsub": os.path.join("pubsub", "__init__")
|
|
31
|
+
}
|
|
32
|
+
HERE = here()
|
|
33
|
+
CUSTOM = os.path.isfile("doc.cfg") and read("doc.cfg")
|
|
34
|
+
ISPLUGIN = not CUSTOM and HERE.startswith("ct") and HERE
|
|
35
|
+
AUTO = HERE != "cantools" and not CUSTOM and not ISPLUGIN
|
|
36
|
+
|
|
37
|
+
if not CUSTOM and not AUTO:
|
|
38
|
+
if ISPLUGIN:
|
|
39
|
+
JSPATH = os.path.join(HERE, "js")
|
|
40
|
+
BPATH = "."
|
|
41
|
+
ALTS["init"] = os.path.join(ISPLUGIN, "init")
|
|
42
|
+
else:
|
|
43
|
+
JSPATH = os.path.join(HERE, "CT")
|
|
44
|
+
BPATH = os.path.join(HERE, "scripts")
|
|
45
|
+
|
|
46
|
+
def space(data):
|
|
47
|
+
return " " + data.replace("\n", "\n ")
|
|
48
|
+
|
|
49
|
+
def bt2ds(t):
|
|
50
|
+
return t.split('"""')[1].strip()
|
|
51
|
+
|
|
52
|
+
def dsBack(cmd):
|
|
53
|
+
cpath = os.path.join(BPATH, "%s.py"%(ALTS.get(cmd, cmd),))
|
|
54
|
+
log(cpath, 2)
|
|
55
|
+
bdata = read(cpath)
|
|
56
|
+
fdata = ISPLUGIN and space(bdata) or "## ct%s\n%s"%(cmd, bt2ds(bdata))
|
|
57
|
+
WEB[-1]["children"].append({
|
|
58
|
+
"name": cmd,
|
|
59
|
+
"content": fdata
|
|
60
|
+
})
|
|
61
|
+
return fdata
|
|
62
|
+
|
|
63
|
+
def dsFront(mod, modname=None, iline=None):
|
|
64
|
+
modname = modname or "CT.%s"%(mod[:-3],)
|
|
65
|
+
iline = iline or (mod == "ct.js" and '<script src="/js/CT/ct.js"></script>' or 'CT.require("%s");'%(modname,))
|
|
66
|
+
log(modname, 2)
|
|
67
|
+
mdata = read(os.path.join(JSPATH, mod))
|
|
68
|
+
rdata = "\n".join([
|
|
69
|
+
"## %s"%(modname,),
|
|
70
|
+
"### Import line: '%s'"%(iline,),
|
|
71
|
+
(ISPLUGIN and mod == "config.js") and space(mdata) or mdata[3:].split("\n*/")[0]
|
|
72
|
+
])
|
|
73
|
+
WEB[-1]["children"].append({
|
|
74
|
+
"name": mod,
|
|
75
|
+
"content": rdata
|
|
76
|
+
})
|
|
77
|
+
return rdata
|
|
78
|
+
|
|
79
|
+
def back():
|
|
80
|
+
log("back", 1)
|
|
81
|
+
wobj = { "children": [] }
|
|
82
|
+
WEB.append(wobj)
|
|
83
|
+
f = []
|
|
84
|
+
if ISPLUGIN:
|
|
85
|
+
wobj["name"] = "Back (Init Config)"
|
|
86
|
+
fdata = [dsBack("init")]
|
|
87
|
+
else:
|
|
88
|
+
wobj["name"] = "Back (CLI)"
|
|
89
|
+
fdata = list(map(dsBack, ["init", "start", "deploy", "pubsub", "migrate", "index", "doc"]))
|
|
90
|
+
f.append("# %s"%(wobj["name"],))
|
|
91
|
+
f += fdata
|
|
92
|
+
return f
|
|
93
|
+
|
|
94
|
+
def front():
|
|
95
|
+
log("front", 1)
|
|
96
|
+
wobj = { "children": [] }
|
|
97
|
+
WEB.append(wobj)
|
|
98
|
+
f = []
|
|
99
|
+
if not ISPLUGIN:
|
|
100
|
+
wobj["name"] = "Front (JS Library)"
|
|
101
|
+
plist = os.listdir(JSPATH)
|
|
102
|
+
plist.sort()
|
|
103
|
+
fdata = list(map(dsFront, [i for i in plist if i.endswith("js")]))
|
|
104
|
+
elif os.path.isfile(os.path.join(JSPATH, "config.js")):
|
|
105
|
+
wobj["name"] = "Front (JS Config)"
|
|
106
|
+
fdata = [dsFront("config.js", "core.config.%s"%(ISPLUGIN,), 'CT.require("core.config");')]
|
|
107
|
+
if "name" in wobj:
|
|
108
|
+
f.append("# %s"%(wobj["name"],))
|
|
109
|
+
f += fdata
|
|
110
|
+
return f
|
|
111
|
+
|
|
112
|
+
def customChunk(path, fnames):
|
|
113
|
+
log("custom chunk: %s"%(path,), 1)
|
|
114
|
+
kids = []
|
|
115
|
+
wobj = { "name": path, "children": kids }
|
|
116
|
+
WEB.append(wobj)
|
|
117
|
+
f = ["## %s"%(path,)]
|
|
118
|
+
afile = os.path.join(path, "about.txt")
|
|
119
|
+
if os.path.isfile(afile):
|
|
120
|
+
adata = read(afile)
|
|
121
|
+
f.append(adata)
|
|
122
|
+
kids.append({
|
|
123
|
+
"name": "about",
|
|
124
|
+
"content": adata
|
|
125
|
+
})
|
|
126
|
+
for fname in fnames:
|
|
127
|
+
fdata = read(os.path.join(path, fname))
|
|
128
|
+
if fname == "config.js" or fname.startswith("ct.cfg"): # for non-local backend cfgs
|
|
129
|
+
fdata = space(fdata)
|
|
130
|
+
elif fname.endswith(".js"):
|
|
131
|
+
fdata = fdata[3:].split("\n*/")[0]
|
|
132
|
+
elif fname.endswith(".py"):
|
|
133
|
+
fdata = bt2ds(fdata)
|
|
134
|
+
f.append("### %s\n%s"%(fname, fdata))
|
|
135
|
+
kids.append({
|
|
136
|
+
"name": fname,
|
|
137
|
+
"content": fdata
|
|
138
|
+
})
|
|
139
|
+
return f
|
|
140
|
+
|
|
141
|
+
frules = {
|
|
142
|
+
".js": {
|
|
143
|
+
"top": "/*\n",
|
|
144
|
+
"bottom": "\n*/"
|
|
145
|
+
},
|
|
146
|
+
".py": {
|
|
147
|
+
"top": '"""\n',
|
|
148
|
+
"bottom": '\n"""'
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
hashead = set()
|
|
153
|
+
def sethead(curdir, data):
|
|
154
|
+
dirname = curdir.rsplit(os.path.sep, 1)[-1]
|
|
155
|
+
if dirname not in hashead:
|
|
156
|
+
hashead.add(dirname)
|
|
157
|
+
data.append("%s %s"%("#" * len(curdir.split(os.path.sep)), dirname))
|
|
158
|
+
wobj = { "name": dirname, "children": [] }
|
|
159
|
+
WEB.append(wobj)
|
|
160
|
+
return WEB[-1]["children"]
|
|
161
|
+
|
|
162
|
+
OMIT = ""
|
|
163
|
+
def autodoc(data, curdir, contents):
|
|
164
|
+
about = "about.txt"
|
|
165
|
+
if curdir != HERE: # probs revise this...
|
|
166
|
+
about = os.path.join(curdir, about)
|
|
167
|
+
if os.path.isfile(about):
|
|
168
|
+
kids = sethead(curdir, data)
|
|
169
|
+
adata = read(about)
|
|
170
|
+
data.append(adata)
|
|
171
|
+
kids.append({
|
|
172
|
+
"name": "about",
|
|
173
|
+
"content": adata
|
|
174
|
+
})
|
|
175
|
+
for fname in contents:
|
|
176
|
+
if fname in OMIT:
|
|
177
|
+
continue
|
|
178
|
+
for flag, rule in list(frules.items()):
|
|
179
|
+
if fname.endswith(flag):
|
|
180
|
+
fdata = read(os.path.join(curdir, fname))
|
|
181
|
+
if fdata.startswith(rule["top"]):
|
|
182
|
+
kids = sethead(curdir, data)
|
|
183
|
+
fstr = fdata[len(rule["top"]):].split(rule["bottom"])[0]
|
|
184
|
+
data.append("%s# %s"%("#" * len(curdir.split(os.path.sep)), fname))
|
|
185
|
+
data.append(fstr)
|
|
186
|
+
kids.append({
|
|
187
|
+
"name": fname,
|
|
188
|
+
"content": fstr
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
def build():
|
|
192
|
+
global OMIT, HERE
|
|
193
|
+
parser = OptionParser("ctdoc [-w]")
|
|
194
|
+
parser.add_option("-w", "--web", action="store_true",
|
|
195
|
+
dest="web", default=False, help="build web docs")
|
|
196
|
+
parser.add_option("-a", "--auto", action="store_true",
|
|
197
|
+
dest="auto", default=False, help="use auto mode (even with a plugin)")
|
|
198
|
+
parser.add_option("-o", "--omit", dest="omit", default="",
|
|
199
|
+
help="omit any files from autodoc?")
|
|
200
|
+
options, args = parser.parse_args()
|
|
201
|
+
if not os.path.isdir(HERE):
|
|
202
|
+
HERE = input("module name? ")
|
|
203
|
+
log("building docs")
|
|
204
|
+
ds = []
|
|
205
|
+
if AUTO or options.auto:
|
|
206
|
+
OMIT = options.omit
|
|
207
|
+
for dirpath, dirnames, filenames in os.walk(HERE):
|
|
208
|
+
autodoc(ds, dirpath, filenames)
|
|
209
|
+
else:
|
|
210
|
+
abdata = (ISPLUGIN or CUSTOM) and "# %s\n%s"%(HERE, read("about.txt")) or config.about%(__version__,)
|
|
211
|
+
ds.append(abdata)
|
|
212
|
+
WEB.append({
|
|
213
|
+
"name": HERE,
|
|
214
|
+
"children": [{
|
|
215
|
+
"name": "about",
|
|
216
|
+
"content": abdata
|
|
217
|
+
}]
|
|
218
|
+
})
|
|
219
|
+
if CUSTOM:
|
|
220
|
+
for line in CUSTOM.split("\n"):
|
|
221
|
+
path, fnames = line.split(" = ")
|
|
222
|
+
ds.extend(customChunk(path, fnames.split("|")))
|
|
223
|
+
else:
|
|
224
|
+
ds.extend(back())
|
|
225
|
+
ds.extend(front())
|
|
226
|
+
log("writing data", important=True)
|
|
227
|
+
log("README.md", 1)
|
|
228
|
+
write("\n\n".join(ds), "README.md")
|
|
229
|
+
if options.web:
|
|
230
|
+
log("web docs enabled!", 1)
|
|
231
|
+
log("building docs web application", 2)
|
|
232
|
+
if not os.path.isdir("docs"):
|
|
233
|
+
cmd("ctinit docs -p ctdocs")
|
|
234
|
+
log("copying data", 2)
|
|
235
|
+
cp("core.data = %s;"%(json.dumps(WEB, indent=4),), os.path.join("docs", "js", "core", "data.js"))
|
|
236
|
+
log("goodbye")
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
build()
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
### Usage: ctindex [--mode=MODE] [--domain=DOMAIN] [--port=PORT] [--skip=SKIP]
|
|
3
|
+
|
|
4
|
+
### Options:
|
|
5
|
+
-h, --help show this help message and exit
|
|
6
|
+
-m MODE, --mode=MODE may be: 'refcount' (default - count up all foreignkey
|
|
7
|
+
references for sort orders and such); 'index' (assign
|
|
8
|
+
each record a sequential integer index); 'urlsafekeys'
|
|
9
|
+
(update all key/keylist properties to use urlsafe keys
|
|
10
|
+
introduced in ct 0.8); 'cleanup' (delete zero-count
|
|
11
|
+
reference counters). Note regarding 'index' mode: it
|
|
12
|
+
_must_ happen remotely; it's generally unnecessary
|
|
13
|
+
unless you're trying to migrate an unindexed database
|
|
14
|
+
away from gae and need an index/key per record; it
|
|
15
|
+
should be invoked from _outside_ -- that's right,
|
|
16
|
+
outside -- of your project's directory (to avoid
|
|
17
|
+
loading up a bunch of google network tools that may be
|
|
18
|
+
crappy or cause issues outside of their normal
|
|
19
|
+
'dev_appserver' environment)
|
|
20
|
+
-d DOMAIN, --domain=DOMAIN
|
|
21
|
+
('index' mode only) what's the domain of the target
|
|
22
|
+
server? (default: localhost)
|
|
23
|
+
-p PORT, --port=PORT ('index' mode only) what's the port of the target
|
|
24
|
+
server? (default: 8080)
|
|
25
|
+
-s SKIP, --skip=SKIP skip these tables ('index' mode only) - use '|' as
|
|
26
|
+
separator, such as 'table1|table2|table3' (default:
|
|
27
|
+
none)
|
|
28
|
+
-i INDEX, --index=INDEX
|
|
29
|
+
start with this index ('index' mode only) (default: 0)
|
|
30
|
+
|
|
31
|
+
As you can see, this script's behavior changes according to the backend of the target project.
|
|
32
|
+
|
|
33
|
+
### dez
|
|
34
|
+
Run this if your CTRefCount records get messed up for
|
|
35
|
+
some reason. It will go through and recount everything
|
|
36
|
+
(in the default 'refcount' mode -- the other modes,
|
|
37
|
+
'urlsafekeys' and 'cleanup', are for migrating a CT-mediated
|
|
38
|
+
database from an older deployment to CT 0.8 or newer).
|
|
39
|
+
|
|
40
|
+
### gae
|
|
41
|
+
Run this in 'index' mode on a database with lots of missing index values.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from getpass import getpass
|
|
45
|
+
from optparse import OptionParser
|
|
46
|
+
from fyg.util import log, error, batch
|
|
47
|
+
from cantools.db import get_schema, get_model, put_multi, delete_multi, unpad_key
|
|
48
|
+
from cantools.web import fetch
|
|
49
|
+
from cantools import config
|
|
50
|
+
if config.web.server == "dez":
|
|
51
|
+
from cantools.db import session, func, refresh_counter
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
input = raw_input # py2/3 compatibility
|
|
55
|
+
except NameError:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
counts = { "_counters": 0 }
|
|
59
|
+
RETRIES = 5
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# dez
|
|
63
|
+
#
|
|
64
|
+
|
|
65
|
+
def get_keys(kind, reference):
|
|
66
|
+
log("acquiring %s (%s) keys"%(kind, reference), 1)
|
|
67
|
+
mod = get_model(kind)
|
|
68
|
+
q = session.query(getattr(mod, "key"))
|
|
69
|
+
qcount = q.count()
|
|
70
|
+
log("found %s"%(qcount,), 2)
|
|
71
|
+
fname, fkey = reference.split(".")
|
|
72
|
+
fmod = get_model(fname)
|
|
73
|
+
fprop = getattr(fmod, fkey)
|
|
74
|
+
sub = session.query(fprop, func.count("*").label("sub_count")).group_by(fprop).subquery()
|
|
75
|
+
q = q.join(sub, mod.key==getattr(sub.c, fkey))
|
|
76
|
+
newcount = q.count()
|
|
77
|
+
log("filtering out %s untargetted entities"%(qcount - newcount), 2)
|
|
78
|
+
qcount = newcount
|
|
79
|
+
log("returning %s keys"%(qcount,), 2)
|
|
80
|
+
return q.all()
|
|
81
|
+
|
|
82
|
+
def refmap():
|
|
83
|
+
log("compiling back reference map")
|
|
84
|
+
rmap = {}
|
|
85
|
+
for tname, schema in list(get_schema().items()):
|
|
86
|
+
for pname, kinds in list(schema["_kinds"].items()):
|
|
87
|
+
reference = "%s.%s"%(tname, pname)
|
|
88
|
+
counts[reference] = 0
|
|
89
|
+
for kind in [k for k in kinds if k != "*"]: # skip wildcard for now
|
|
90
|
+
if kind not in rmap:
|
|
91
|
+
rmap[kind] = {}
|
|
92
|
+
rmap[kind][reference] = get_keys(kind, reference)
|
|
93
|
+
return rmap
|
|
94
|
+
|
|
95
|
+
def do_batch(chunk, reference):
|
|
96
|
+
log("refreshing %s %s keys"%(len(chunk), reference), 1)
|
|
97
|
+
i = 0
|
|
98
|
+
rc = []
|
|
99
|
+
for item in chunk: # item is single-member tuple
|
|
100
|
+
rc.append(refresh_counter(item[0], reference))
|
|
101
|
+
i += 1
|
|
102
|
+
if not i % 100:
|
|
103
|
+
log("processed %s"%(i,), 3)
|
|
104
|
+
counts[reference] += len(chunk)
|
|
105
|
+
counts["_counters"] += len(rc)
|
|
106
|
+
log("refreshed %s total"%(counts[reference],), 2)
|
|
107
|
+
log("updated %s counters"%(counts["_counters"],), 2)
|
|
108
|
+
put_multi(rc)
|
|
109
|
+
log("saved", 2)
|
|
110
|
+
|
|
111
|
+
def refcount():
|
|
112
|
+
log("indexing foreignkey references throughout database", important=True)
|
|
113
|
+
import model # load schema
|
|
114
|
+
for kind, references in list(refmap().items()):
|
|
115
|
+
log("processing table: %s"%(kind,), important=True)
|
|
116
|
+
for reference, keys in list(references.items()):
|
|
117
|
+
batch(keys, do_batch, reference)
|
|
118
|
+
tcount = sum(counts.values()) - counts["_counters"]
|
|
119
|
+
log("refreshed %s rows and updated %s counters"%(tcount, counts["_counters"]), important=True)
|
|
120
|
+
|
|
121
|
+
#
|
|
122
|
+
# gae
|
|
123
|
+
#
|
|
124
|
+
|
|
125
|
+
def _log_fetch(host, url, port):
|
|
126
|
+
res = fetch(host, url, port)
|
|
127
|
+
log(res)
|
|
128
|
+
return res
|
|
129
|
+
|
|
130
|
+
def _index_kind(kind, host, port, pw, index):
|
|
131
|
+
log("indexing %s"%(kind,), important=True)
|
|
132
|
+
retry = 0
|
|
133
|
+
while "Error" in _log_fetch(host, "/_db?action=index&pw=%s&kind=%s&index=%s"%(pw, kind, index), port):
|
|
134
|
+
log("error indexing %s"%(kind,), important=True)
|
|
135
|
+
if retry == RETRIES:
|
|
136
|
+
error("tried %s times! sorry."%(retry,))
|
|
137
|
+
retry += 1
|
|
138
|
+
log("trying again (retry: %s)"%(retry,))
|
|
139
|
+
|
|
140
|
+
def index(host, port, skips, index):
|
|
141
|
+
pw = getpass("what's the admin password? ")
|
|
142
|
+
log("indexing db at %s:%s"%(host, port), important=True)
|
|
143
|
+
# log(fetch(host, "/_db?action=index&pw=%s"%(pw,), port))
|
|
144
|
+
log("acquiring schema")
|
|
145
|
+
schema = fetch(host, "/_db?action=schema", port, ctjson=True)
|
|
146
|
+
for kind in schema:
|
|
147
|
+
if kind in skips:
|
|
148
|
+
log("skipping %s"%(kind,), important=True)
|
|
149
|
+
else:
|
|
150
|
+
_index_kind(kind, host, port, pw, index)
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# url safety
|
|
154
|
+
#
|
|
155
|
+
def urlsafe():
|
|
156
|
+
log("updating key/keylist properties with urlsafe keys", important=True)
|
|
157
|
+
import model
|
|
158
|
+
schema = get_schema()
|
|
159
|
+
puts = []
|
|
160
|
+
for mod in schema:
|
|
161
|
+
mods = get_model(mod).query().all()
|
|
162
|
+
log("%s (%s)"%(mod, len(mods)), 1)
|
|
163
|
+
for m in mods:
|
|
164
|
+
if m.polytype != mod:
|
|
165
|
+
log("skipping! (%s != %s)"%(m.polytype, mod), 2)
|
|
166
|
+
continue
|
|
167
|
+
m.key = unpad_key(m.key.urlsafe())
|
|
168
|
+
for prop in schema[mod]["_kinds"]:
|
|
169
|
+
if schema[mod][prop] == "key":
|
|
170
|
+
setattr(m, prop, unpad_key(getattr(m, prop).urlsafe()))
|
|
171
|
+
else: # keylist
|
|
172
|
+
setattr(m, prop, [unpad_key(k.urlsafe()) for k in getattr(m, prop)])
|
|
173
|
+
puts.append(m)
|
|
174
|
+
log("saving records")
|
|
175
|
+
put_multi(puts)
|
|
176
|
+
log("updated %s keys"%(len(puts),), important=True)
|
|
177
|
+
if input("want to prune zero-count reference counters? (y/N)").lower().startswith("y"):
|
|
178
|
+
cleanup()
|
|
179
|
+
|
|
180
|
+
def cleanup():
|
|
181
|
+
log("cleaning up zero-count reference counters", important=True)
|
|
182
|
+
from cantools.db import lookup
|
|
183
|
+
ctrz = lookup.CTRefCount.query(lookup.CTRefCount.count == 0).all()
|
|
184
|
+
log("deleting %s zero-count reference counters"%(len(ctrz),))
|
|
185
|
+
delete_multi(ctrz)
|
|
186
|
+
log("all gone!")
|
|
187
|
+
|
|
188
|
+
def go():
|
|
189
|
+
parser = OptionParser("ctindex [--mode=MODE] [--domain=DOMAIN] [--port=PORT] [--skip=SKIP]")
|
|
190
|
+
parser.add_option("-m", "--mode", dest="mode", default="refcount",
|
|
191
|
+
help="may be: 'refcount' (default - count up all foreignkey references for sort "
|
|
192
|
+
"orders and such); 'index' (assign each record a sequential integer index); "
|
|
193
|
+
"'urlsafekeys' (update all key/keylist properties to use urlsafe keys "
|
|
194
|
+
"introduced in ct 0.8); 'cleanup' (delete zero-count reference counters). "
|
|
195
|
+
"Note regarding 'index' mode: it _must_ happen remotely; it's generally "
|
|
196
|
+
"unnecessary unless you're trying to migrate an unindexed database away from "
|
|
197
|
+
"gae and need an index/key per record; it should be invoked from _outside_ "
|
|
198
|
+
"-- that's right, outside -- of your project's directory (to avoid loading "
|
|
199
|
+
"up a bunch of google network tools that may be crappy or cause issues outside "
|
|
200
|
+
"of their normal 'dev_appserver' environment)")
|
|
201
|
+
parser.add_option("-d", "--domain", dest="domain", default="localhost",
|
|
202
|
+
help="('index' mode only) what's the domain of the target server? (default: localhost)")
|
|
203
|
+
parser.add_option("-p", "--port", dest="port", default="8080",
|
|
204
|
+
help="('index' mode only) what's the port of the target server? (default: 8080)")
|
|
205
|
+
parser.add_option("-s", "--skip", dest="skip", default="",
|
|
206
|
+
help="skip these tables ('index' mode only) - use '|' as separator, such as 'table1|table2|table3' (default: none)")
|
|
207
|
+
parser.add_option("-i", "--index", dest="index", default=0,
|
|
208
|
+
help="start with this index ('index' mode only) (default: 0)")
|
|
209
|
+
options, args = parser.parse_args()
|
|
210
|
+
|
|
211
|
+
log("mode: %s"%(options.mode,), important=True)
|
|
212
|
+
if options.mode == "refcount":
|
|
213
|
+
refcount()
|
|
214
|
+
elif options.mode == "index":
|
|
215
|
+
index(options.domain, int(options.port),
|
|
216
|
+
options.skip and options.skip.split("|") or [], options.index)
|
|
217
|
+
elif options.mode == "urlsafekeys":
|
|
218
|
+
urlsafe()
|
|
219
|
+
elif options.mode == "cleanup":
|
|
220
|
+
cleanup()
|
|
221
|
+
else:
|
|
222
|
+
error("unknown mode: %s"%(options.mode,))
|
|
223
|
+
log("goodbye")
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
go()
|