seitime-frappe/frappe/frappeclient.py

234 lines
6.4 KiB
Python

import requests
import json
import frappe
class AuthError(Exception):
pass
class FrappeException(Exception):
pass
class FrappeClient(object):
def __init__(self, url, username, password, verify=True):
self.verify = verify
self.session = requests.session()
self.url = url
self.login(username, password)
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
self.logout()
def login(self, username, password):
r = self.session.post(self.url, data={
'cmd': 'login',
'usr': username,
'pwd': password
}, verify=self.verify)
if r.status_code==200 and r.json().get('message') == "Logged In":
return r.json()
else:
raise AuthError
def logout(self):
self.session.get(self.url, params={
'cmd': 'logout',
}, verify=self.verify)
def get_list(self, doctype, fields='"*"', filters=None, limit_start=0, limit_page_length=0):
"""Returns list of records of a particular type"""
if not isinstance(fields, basestring):
fields = json.dumps(fields)
params = {
"fields": fields,
}
if filters:
params["filters"] = json.dumps(filters)
if limit_page_length:
params["limit_start"] = limit_start
params["limit_page_length"] = limit_page_length
res = self.session.get(self.url + "/api/resource/" + doctype, params=params, verify=self.verify)
return self.post_process(res)
def insert(self, doc):
res = self.session.post(self.url + "/api/resource/" + doc.get("doctype"),
data={"data":frappe.as_json(doc)}, verify=self.verify)
return self.post_process(res)
def update(self, doc):
url = self.url + "/api/resource/" + doc.get("doctype") + "/" + doc.get("name")
res = self.session.put(url, data={"data":frappe.as_json(doc)}, verify=self.verify)
return self.post_process(res)
def bulk_update(self, docs):
return self.post_request({
"cmd": "frappe.client.bulk_update",
"docs": frappe.as_json(docs)
})
def delete(self, doctype, name):
return self.post_request({
"cmd": "frappe.model.delete_doc",
"doctype": doctype,
"name": name
})
def submit(self, doclist):
return self.post_request({
"cmd": "frappe.client.submit",
"doclist": frappe.as_json(doclist)
})
def get_value(self, doctype, fieldname=None, filters=None):
return self.get_request({
"cmd": "frappe.client.get_value",
"doctype": doctype,
"fieldname": fieldname or "name",
"filters": frappe.as_json(filters)
})
def set_value(self, doctype, docname, fieldname, value):
return self.post_request({
"cmd": "frappe.client.set_value",
"doctype": doctype,
"name": docname,
"fieldname": fieldname,
"value": value
})
def cancel(self, doctype, name):
return self.post_request({
"cmd": "frappe.client.cancel",
"doctype": doctype,
"name": name
})
def get_doc(self, doctype, name="", filters=None, fields=None):
params = {}
if filters:
params["filters"] = json.dumps(filters)
if fields:
params["fields"] = json.dumps(fields)
res = self.session.get(self.url + "/api/resource/" + doctype + "/" + name,
params=params, verify=self.verify)
return self.post_process(res)
def rename_doc(self, doctype, old_name, new_name):
params = {
"cmd": "frappe.client.rename_doc",
"doctype": doctype,
"old_name": old_name,
"new_name": new_name
}
return self.post_request(params)
def migrate_doctype(self, doctype, filters=None, update=None, verbose=1, exclude=None, preprocess=None):
"""Migrate records from another doctype"""
meta = frappe.get_meta(doctype)
tables = {}
for df in meta.get_table_fields():
if verbose: print "getting " + df.options
tables[df.fieldname] = self.get_list(df.options, limit_page_length=999999)
# get links
if verbose: print "getting " + doctype
docs = self.get_list(doctype, limit_page_length=999999, filters=filters)
# build - attach children to parents
if tables:
docs = [frappe._dict(doc) for doc in docs]
docs_map = dict((doc.name, doc) for doc in docs)
for fieldname in tables:
for child in tables[fieldname]:
child = frappe._dict(child)
if child.parent in docs_map:
docs_map[child.parent].setdefault(fieldname, []).append(child)
if verbose: print "inserting " + doctype
for doc in docs:
if exclude and doc["name"] in exclude:
continue
if preprocess:
preprocess(doc)
if not doc.get("owner"):
doc["owner"] = "Administrator"
if doctype != "User" and not frappe.db.exists("User", doc.get("owner")):
frappe.get_doc({"doctype": "User", "email": doc.get("owner"),
"first_name": doc.get("owner").split("@")[0] }).insert()
if update:
doc.update(update)
doc["doctype"] = doctype
new_doc = frappe.get_doc(doc)
new_doc.insert()
if not meta.istable:
if doctype != "Communication":
self.migrate_doctype("Communication", {"reference_doctype": doctype, "reference_name": doc["name"]},
update={"reference_name": new_doc.name}, verbose=0)
if doctype != "File":
self.migrate_doctype("File", {"attached_to_doctype": doctype,
"attached_to_name": doc["name"]}, update={"attached_to_name": new_doc.name}, verbose=0)
def migrate_single(self, doctype):
doc = self.get_doc(doctype, doctype)
doc = frappe.get_doc(doc)
# change modified so that there is no error
doc.modified = frappe.db.get_single_value(doctype, "modified")
frappe.get_doc(doc).insert()
def get_api(self, method, params={}):
res = self.session.get(self.url + "/api/method/" + method + "/",
params=params, verify=self.verify)
return self.post_process(res)
def post_api(self, method, params={}):
res = self.session.post(self.url + "/api/method/" + method + "/",
params=params, verify=self.verify)
return self.post_process(res)
def get_request(self, params):
res = self.session.get(self.url, params=self.preprocess(params), verify=self.verify)
res = self.post_process(res)
return res
def post_request(self, data):
res = self.session.post(self.url, data=self.preprocess(data), verify=self.verify)
res = self.post_process(res)
return res
def preprocess(self, params):
"""convert dicts, lists to json"""
for key, value in params.iteritems():
if isinstance(value, (dict, list)):
params[key] = json.dumps(value)
return params
def post_process(self, response):
try:
rjson = response.json()
except ValueError:
print response.text
raise
if rjson and ("exc" in rjson) and rjson["exc"]:
raise FrappeException(rjson["exc"])
if 'message' in rjson:
return rjson['message']
elif 'data' in rjson:
return rjson['data']
else:
return None