Author: humbedooh
Date: Sun Mar 29 12:38:08 2015
New Revision: 1669902
URL: http://svn.apache.org/r1669902
Log:
Separate each DB backend into its owns script, startign with file-based and ES
Added:
steve/trunk/pysteve/lib/backends/
steve/trunk/pysteve/lib/backends/__init__.py
steve/trunk/pysteve/lib/backends/es.py
steve/trunk/pysteve/lib/backends/files.py
Added: steve/trunk/pysteve/lib/backends/__init__.py
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/backends/__init__.py?rev=1669902&view=auto
==============================================================================
--- steve/trunk/pysteve/lib/backends/__init__.py (added)
+++ steve/trunk/pysteve/lib/backends/__init__.py Sun Mar 29 12:38:08 2015
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Database backends"""
+
+__all__ = ['files','es']
\ No newline at end of file
Added: steve/trunk/pysteve/lib/backends/es.py
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/backends/es.py?rev=1669902&view=auto
==============================================================================
--- steve/trunk/pysteve/lib/backends/es.py (added)
+++ steve/trunk/pysteve/lib/backends/es.py Sun Mar 29 12:38:08 2015
@@ -0,0 +1,232 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import hashlib
+import json
+import os
+import random
+import time
+from lib import constants
+
+es = None
+
+def init(config):
+ global es
+ from elasticsearch import Elasticsearch
+ es = Elasticsearch([
+ {
+ 'host': config.get("elasticsearch", "host"),
+ 'port': int(config.get("elasticsearch", "port")),
+ 'url_prefix': config.get("elasticsearch", "uri"),
+ 'use_ssl': False if config.get("elasticsearch",
"secure") == "false" else True
+ },
+ ])
+ if not es.indices.exists("steve"):
+ es.indices.create(index = "steve", body = {
+ "settings": {
+ "number_of_shards" : 3,
+ "number_of_replicas" : 1
+ }
+ }
+ )
+
+
+
+def exists(election, *issue):
+ doc = "elections"
+ eid = election
+ if issue and issue[0]:
+ doc = "issues"
+ eid = hashlib.sha224(election + "/" + issue[0]).hexdigest()
+ return es.exists(index="steve", doc_type=doc, id=eid)
+
+
+
+def getBasedata(election):
+ "Get base data from an election"
+ res = es.get(index="steve", doc_type="elections", id=election)
+ if res:
+ return res['_source']
+ return None
+
+
+def close(election, reopen = False):
+ "Mark an election as closed"
+ basedata = getBasedata(election)
+ if reopen:
+ basedata['closed'] = False
+ else:
+ basedata['closed'] = True
+ es.index(index="steve", doc_type="elections", id=election, body = basedata
)
+
+
+def getIssue(electionID, issueID):
+ "Get JSON data from an issue"
+ issuedata = None
+ ihash = ""
+ iid = hashlib.sha224(electionID + "/" + issueID).hexdigest()
+ res = es.get(index="steve", doc_type="issues", id=iid)
+ if res:
+ issuedata = res['_source']
+ ihash = hashlib.sha224(json.dumps(issuedata)).hexdigest()
+ return issuedata, ihash
+
+
+def getVotes(electionID, issueID):
+ "Read votes from the vote file"
+ res = es.search(index="steve", doc_type="votes", q = "election:%s AND
issue:%s" % (electionID, issueID), size = 9999999)
+ results = len(res['hits']['hits'])
+ if results > 0:
+ votes = {}
+ for entry in res['hits']['hits']:
+ votes[entry['_source']['key']] = entry['_source']['data']['vote']
+ return votes
+ return {}
+
+
+
+def getVotesRaw(electionID, issueID):
+ res = es.search(index="steve", doc_type="votes", q = "election:%s AND
issue:%s" % (electionID, issueID), size = 9999999)
+ results = len(res['hits']['hits'])
+ if results > 0:
+ votes = {}
+ for entry in res['hits']['hits']:
+ votes[entry['_source']['key']] = entry['_source']['data']
+ return votes
+ return {}
+
+
+def createElection(electionID, basedata):
+ "Create a new election"
+ es.index(index="steve", doc_type="elections", id=electionID, body =
+ basedata
+ );
+
+def updateElection(electionID, basedata):
+ es.index(index = "steve", doc_type = "elections", id=electionID, body =
basedata)
+
+def updateIssue(electionID, issueID, issueData):
+ es.index(index = "steve", doc_type = "issues",
id=hashlib.sha224(electionID + "/" + issueID).hexdigest(), body = issueData)
+
+
+def listIssues(election):
+ "List all issues in an election"
+ issues = []
+ try:
+ res = es.search(index="steve", doc_type="issues", sort = "id", q =
"election:%s" % election, size = 999)
+ results = len(res['hits']['hits'])
+ if results > 0:
+ for entry in res['hits']['hits']:
+ issues.append(entry['_source']['id'])
+ except:
+ pass # THIS IS OKAY! ES WILL FAIL IF THERE ARE NO ISSUES YET
+ return issues
+
+def listElections():
+ "List all elections"
+ elections = []
+ try:
+ res = es.search(index="steve", doc_type="elections", sort = "id", q =
"*", size = 99999)
+ results = len(res['hits']['hits'])
+ if results > 0:
+ for entry in res['hits']['hits']:
+ source = entry['_source']
+ elections.append(source['id'])
+ except Exception as err:
+ pass # THIS IS OKAY! On initial setup, this WILL fail until an
election has been created
+ return elections
+
+def vote(electionID, issueID, uid, vote):
+ "Casts a vote on an issue"
+ eid = hashlib.sha224(electionID + ":" + issueID + ":" + uid).hexdigest()
+ es.index(index="steve", doc_type="votes", id=eid, body =
+ {
+ 'issue': issueID,
+ 'election': electionID,
+ 'key': uid,
+ 'data': {
+ 'timestamp': time.time(),
+ 'vote': vote
+ }
+ }
+ );
+
+
+def deleteIssue(electionID, issueID):
+ "Deletes an issue if it exists"
+ es.delete(index="steve", doc_type="issues", id=hashlib.sha224(electionID +
"/" + issueID).hexdigest());
+
+def createIssue(electionID, issueID, data):
+ es.index(index="steve", doc_type="issues", id=hashlib.sha224(electionID +
"/" + issueID).hexdigest(), body = data);
+
+
+
+def voter_get(electionID, votekey):
+ "Get the UID/email for a voter given the vote key hash"
+ try:
+ res = es.search(index="steve", doc_type="voters", q = "election:%s" %
electionID, size = 999999)
+ results = len(res['hits']['hits'])
+ if results > 0:
+ for entry in res['hits']['hits']:
+ voter = entry['_source']
+ if voter['hash'] == votekey:
+ return voter['uid']
+ except:
+ return False # ES Error, probably not seeded the voters doc yet
+
+def voter_add(election, PID, xhash):
+ eid = hashlib.sha224(election + ":" + PID).hexdigest()
+ es.index(index="steve", doc_type="voters", id=eid, body = {
+ 'election': election,
+ 'hash': xhash,
+ 'uid': PID
+ }
+ )
+
+def voter_remove(election, UID):
+ votehash = hashlib.sha224(election + ":" + UID).hexdigest()
+ es.delete(index="steve", doc_type="voters", id=votehash);
+
+def voter_has_voted(election, issue, uid):
+ eid = hashlib.sha224(election + ":" + issue + ":" + uid).hexdigest()
+ try:
+ return es.exists(index="steve", doc_type="votes", id=eid)
+ except:
+ return False
+
+constants.appendBackend( {
+ 'id': 'elasticsearch',
+ 'init': init,
+ 'document_exists': exists,
+ 'get_basedata': getBasedata,
+ 'election_close': close,
+ 'election_vote': vote,
+ 'election_list': listElections,
+ 'issue_list': listIssues,
+ 'election_create': createElection,
+ 'issue_create': createIssue,
+ 'issue_delete': deleteIssue,
+ 'election_update': updateElection,
+ 'issue_update': updateIssue,
+ 'issue_get': getIssue,
+ 'vote': vote,
+ 'votes_get': getVotes,
+ 'votes_get_raw': getVotesRaw,
+ 'voter_get_uid': voter_get,
+ 'voter_add': voter_add,
+ 'voter_remove': voter_remove,
+ 'voter_has_voted': voter_has_voted
+})
\ No newline at end of file
Added: steve/trunk/pysteve/lib/backends/files.py
URL:
http://svn.apache.org/viewvc/steve/trunk/pysteve/lib/backends/files.py?rev=1669902&view=auto
==============================================================================
--- steve/trunk/pysteve/lib/backends/files.py (added)
+++ steve/trunk/pysteve/lib/backends/files.py Sun Mar 29 12:38:08 2015
@@ -0,0 +1,239 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import hashlib
+import json
+import os
+import random
+import time
+
+from lib import constants
+
+homedir = None
+
+def init(config):
+ global homedir
+ homedir = config.get("general", "homedir")
+
+
+def exists(election, *issue):
+ "Returns True if an election/issue exists, False otherwise"
+ elpath = os.path.join(homedir, "issues", election)
+ if issue:
+ elpath += "/" + issue[0] + ".json"
+ return os.path.isfile(elpath)
+ else:
+ return os.path.isdir(elpath)
+
+
+def getBasedata(election):
+ "Get base data from an election"
+ elpath = os.path.join(homedir, "issues", election)
+ if os.path.isdir(elpath):
+ with open(elpath + "/basedata.json", "r") as f:
+ data = f.read()
+ f.close()
+ basedata = json.loads(data)
+ if hideHash and 'hash' in basedata:
+ del basedata['hash']
+ basedata['id'] = election
+ return basedata
+ return None
+
+def close(election, reopen = False):
+ "Mark an election as closed"
+
+ elpath = os.path.join(homedir, "issues", election)
+ basedata = getBasedata(election)
+ if reopen:
+ basedata['closed'] = False
+ else:
+ basedata['closed'] = True
+ with open(elpath + "/basedata.json", "w") as f:
+ f.write(json.dumps(basedata))
+ f.close()
+
+
+def getIssue(electionID, issueID):
+ "Get JSON data from an issue"
+ issuedata = None
+ ihash = ""
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) + ".json"
+ if os.path.isfile(issuepath):
+ with open(issuepath, "r") as f:
+ data = f.read()
+ ihash = hashlib.sha224(data).hexdigest()
+ f.close()
+ issuedata = json.loads(data)
+
+ return issuedata, ihash
+
+
+def getVotes(electionID, issueID):
+ "Read votes from the vote file"
+ rvotes = getVotesRaw(electionID, issueID)
+ votes = {}
+ for key in rvotes:
+ votes[key] = rvotes[key]['vote']
+ return {}
+
+
+def getVotesRaw(electionID, issueID):
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) +
".json.votes"
+ if os.path.isfile(issuepath):
+ with open(issuepath, "r") as f:
+ votes = json.loads(f.read())
+ f.close()
+ return votes
+ return {}
+
+
+def createElection(eid, basedata):
+ elpath = os.path.join(homedir, "issues", eid)
+ os.mkdir(elpath)
+ with open(elpath + "/basedata.json", "w") as f:
+ f.write(json.dumps(basedata))
+ f.close()
+ with open(elpath + "/voters.json", "w") as f:
+ f.write("{}")
+ f.close()
+
+
+def updateElection(electionID, basedata):
+ elpath = os.path.join(homedir, "issues", electionID)
+ with open(elpath + "/basedata.json", "w") as f:
+ f.write(json.dumps(basedata))
+ f.close()
+
+def updateIssue(electionID, issueID, issueData):
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) + ".json"
+ with open(issuepath, "w") as f:
+ f.write(json.dumps(issueData))
+ f.close()
+
+
+def listIssues(election):
+ "List all issues in an election"
+ issues = []
+ elpath = os.path.join(homedir, "issues", election)
+ if os.path.isdir(elpath):
+ issues = [f.strip(".json") for f in os.listdir(elpath) if
os.path.isfile(os.path.join(elpath, f)) and f != "basedata.json" and f !=
"voters.json" and f.endswith(".json")]
+ return issues
+
+def listElections():
+ "List all elections"
+ elections = []
+ path = os.path.join(homedir, "issues")
+ if os.path.isdir(path):
+ elections = [f for f in os.listdir(path) if
os.path.isdir(os.path.join(path, f))]
+ return elections
+
+def vote(electionID, issueID, uid, vote):
+ "Casts a vote on an issue"
+ votes = {}
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) + ".json"
+ if os.path.isfile(issuepath + ".votes"):
+ with open(issuepath + ".votes", "r") as f:
+ votes = json.loads(f.read())
+ f.close()
+ votes[uid] = {
+ 'vote': vote,
+ 'timestamp': time.time()
+ }
+ with open(issuepath + ".votes", "w") as f:
+ f.write(json.dumps(votes))
+ f.close()
+
+
+def deleteIssue(electionID, issueID):
+ "Deletes an issue if it exists"
+
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) + ".json"
+ if os.path.isfile(issuepath):
+ os.unlink(issuepath)
+ if os.path.isfile(issuepath + ".votes"):
+ os.unlink(issuepath + ".votes")
+
+def createIssue(electionID, issueID, data):
+ issuepath = os.path.join(homedir, "issues", electionID, issueID) + ".json"
+ with open(issuepath, "w") as f:
+ f.write(json.dumps(data))
+ f.close()
+
+def voter_get(electionID, votekey):
+ "Get vote UID/email with a given vote key hash"
+ elpath = os.path.join(homedir, "issues", electionID)
+ with open(elpath + "/voters.json", "r") as f:
+ voters = json.loads(f.read())
+ f.close()
+ for voter in voters:
+ if voters[voter] == xhash:
+ return voter
+ return None
+
+def voter_add(election, PID, xhash):
+ elpath = os.path.join(homedir, "issues", election)
+ with open(elpath + "/voters.json", "r") as f:
+ voters = json.loads(f.read())
+ f.close()
+ voters[PID] = xhash
+ with open(elpath + "/voters.json", "w") as f:
+ f.write(json.dumps(voters))
+ f.close()
+
+def voter_remove(election, UID):
+ elpath = os.path.join(homedir, "issues", election)
+ with open(elpath + "/voters.json", "r") as f:
+ voters = json.loads(f.read())
+ f.close()
+ if UID in voters:
+ del voters[UID]
+ with open(elpath + "/voters.json", "w") as f:
+ f.write(json.dumps(voters))
+ f.close()
+
+def voter_has_voted(election, issue, uid):
+ path = os.path.join(homedir, "issues", election, issue)
+ votes = {}
+ if os.path.isfile(path + ".json.votes"):
+ with open(path + ".json.votes", "r") as f:
+ votes = json.loads(f.read())
+ f.close()
+ return True if uid in votes else False
+
+constants.appendBackend( {
+ 'id': 'file',
+ 'init': init,
+ 'document_exists': exists,
+ 'get_basedata': getBasedata,
+ 'election_close': close,
+ 'election_vote': vote,
+ 'election_list': listElections,
+ 'issue_list': listIssues,
+ 'election_create': createElection,
+ 'issue_create': createIssue,
+ 'issue_delete': deleteIssue,
+ 'election_update': updateElection,
+ 'issue_update': updateIssue,
+ 'issue_get': getIssue,
+ 'vote': vote,
+ 'votes_get': getVotes,
+ 'votes_get_raw': getVotesRaw,
+ 'voter_get_uid': voter_get,
+ 'voter_add': voter_add,
+ 'voter_remove': voter_remove,
+ 'voter_has_voted': voter_has_voted
+})
\ No newline at end of file