Source code for service.database

# coding=utf-8
"""
Pinyto cloud - A secure cloud database for your personal data
Copyright (C) 2105 Johannes Merkert <jonny@pinyto.de>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from pymongo.son_manipulator import ObjectId
from pymongo.errors import InvalidId
from pymongo import ASCENDING, DESCENDING
from datetime import datetime


[docs]class CollectionWrapper(object): """ This wrapper is user to expose the db to the users assemblies. """ def __init__(self, collection, assembly_name, only_own_data=True): self.db = collection self.assembly_name = assembly_name self.only_own_data = only_own_data
[docs] def find(self, query, skip=0, limit=0, sorting=None, sort_direction='asc'): """ Use this function to read from the database. This method encodes all fields beginning with _ for returning a valid json response. :param query: :type query: dict :param skip: Count of documents which should be skipped in the query. This is useful for pagination. :type skip: int :param limit: Number of documents which should be returned. This number is of course the maximum. :type limit: int :param sorting: String identifying the key which is used for sorting. :type sorting: str :param sort_direction: 'asc' or 'desc' :type sort_direction: str :return: The list of found documents. If no document is found the list is empty. :rtype: list """ if self.only_own_data: query['assembly'] = self.assembly_name return encode_underscore_fields_list(self.find_documents( inject_object_id(query), skip=skip, limit=limit, sorting=sorting, sort_direction=sort_direction ))
[docs] def count(self, query): """ Use this function to get a count from the database. :param query: :type query: dict :return: The number of documents matching the query :rtype: int """ if self.only_own_data: query['assembly'] = self.assembly_name try: count = self.db.find(inject_object_id(query)).count() except InvalidId: count = -1 return count
[docs] def find_documents(self, query, skip=0, limit=0, sorting=None, sort_direction='asc'): """ Use this function to read from the database. This method returns complete documents with _id fields. Do not use this to construct json responses! :param query: :type query: dict :param skip: Count of documents which should be skipped in the query. This is useful for pagination. :type skip: int :param limit: Number of documents which should be returned. This number is of course the maximum. :type limit: int :param sorting: String identifying the key which is used for sorting. :type sorting: str :param sort_direction: 'asc' or 'desc' :type sort_direction: str :return: The list of found documents. If no document is found the list is empty. :rtype: list """ if self.only_own_data: query['assembly'] = self.assembly_name if sort_direction == 'desc': sort_direction = DESCENDING else: sort_direction = ASCENDING if sorting: return self.db.find(inject_object_id(query), skip=skip, limit=limit, sort=[(sorting, sort_direction)]) else: return self.db.find(inject_object_id(query), skip=skip, limit=limit)
[docs] def find_document_for_id(self, document_id): """ Find the document with the given ID in the database. On success this returns a single document. :param document_id: :type document_id: string :return: The document with the given _id :rtype: dict """ if self.only_own_data: return self.db.find_one(inject_object_id({'_id': document_id, 'assembly': self.assembly_name})) else: return self.db.find_one(inject_object_id({'_id': document_id}))
[docs] def find_distinct(self, query, attribute): """ Return a list representing the diversity of a given attribute in the documents matched by the query. :param query: json :type query: str :param attribute: String describing the attribute :type attribute: str :return: A list of values the attribute can have in the set of documents described by the query :rtype: list """ if self.only_own_data: query['assembly'] = self.assembly_name return self.db.find(inject_object_id(query)).distinct(attribute)
[docs] def save(self, document): """ Saves the document. The document must have a valid _id :param document: :type document: dict :return: The ObjectId of the insrted document :rtype: str """ document['assembly'] = self.assembly_name if not isinstance(document['_id'], ObjectId): document['_id'] = ObjectId(document['_id']) return str(self.db.save(document))
[docs] def insert(self, document): """ Inserts a document. If the given document has a ID the ID is removed and a new ID will be generated. Time will be set to now. :param document: :type document: dict :return: The ObjectId of the insrted document :rtype: str """ if '_id' in document: del document['_id'] document['time'] = datetime.utcnow() document['assembly'] = self.assembly_name return str(self.db.insert(document))
[docs] def remove(self, document): """ Deletes the document. The document must have a valid _id :param document: :type document: dict """ if self.only_own_data: self.db.remove(spec_or_id=inject_object_id({"_id": document['_id'], 'assembly': self.assembly_name})) else: self.db.remove(spec_or_id=inject_object_id({"_id": document['_id']}))
[docs]def encode_underscore_fields(data): """ Removes _id :param data: :type data: dict :rtype: dict """ converted = {} for key in data: if key[0] != '_': if key == 'time': converted[key] = str(data[key]) else: converted[key] = data[key] else: converted[key] = str(data[key]) return converted
[docs]def encode_underscore_fields_list(data_list): """ Removes _id for every dict in the list :param data_list: :type data_list: list :rtype: list """ converted_list = [] for item in data_list: converted_list.append(encode_underscore_fields(item)) return converted_list
[docs]def inject_object_id(query): """ Traverses all fields of the query dict and converts all '_id' to ObjectId instances. :param query: :type query: dict :rtype: dict """ if isinstance(query, list): for index, value in enumerate(query): if isinstance(value, dict) or isinstance(value, list): query[index] = inject_object_id(value) else: for key in query: if key == '_id' and not isinstance(query[key], ObjectId): query[key] = ObjectId(query[key]) if isinstance(query[key], dict) or isinstance(query[key], list): query[key] = inject_object_id(query[key]) return query