コード例 #1
0
 def test_fetch_search_index(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing search indexes.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     search_index = ('function (doc) {\n  index("default", doc._id); '
                     'if (doc._id) {index("name", doc.name, '
                     '{"store": true}); }\n} ')
     ddoc.add_search_index('search001', search_index)
     ddoc.add_search_index('search002', search_index, 'simple')
     ddoc.add_search_index('search003', search_index, 'standard')
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     self.assertNotEqual(ddoc_remote, ddoc)
     ddoc_remote.fetch()
     self.assertEqual(ddoc_remote, ddoc)
     self.assertTrue(ddoc_remote['_rev'].startswith('1-'))
     self.assertEqual(ddoc_remote, {
         '_id': '_design/ddoc001',
         '_rev': ddoc['_rev'],
         'indexes': {
             'search001': {'index': search_index},
             'search002': {'index': search_index, 'analyzer': 'simple'},
             'search003': {'index': search_index, 'analyzer': 'standard'}
         },
         'views': {}
     })
コード例 #2
0
 def test_fetch_query_views(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing query index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         "_id": "_design/ddoc001",
         "language": "query",
         "views": {
             "view001": {
                 "map": {"fields": {"name": "asc", "age": "asc"}},
                 "reduce": "_count",
                 "options": {"def": {"fields": ["name", "age"]}, "w": 2},
             }
         },
     }
     doc = self.db.create_document(data)
     self.assertIsInstance(doc, Document)
     data["_rev"] = doc["_rev"]
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     ddoc.fetch()
     self.assertIsInstance(ddoc, DesignDocument)
     self.assertEqual(ddoc, data)
     self.assertIsInstance(ddoc["views"]["view001"], QueryIndexView)
コード例 #3
0
    def test_cq_view_save_fails_when_lang_is_not_query(self):
        """
        Tests that save fails when language is not query but views are query
        index views.
        """
        # This is not the preferred way of dealing with query index
        # views but it works best for this test.
        data = {
            '_id': '_design/ddoc001',
            'language': 'query',
            'views': {
                'view001': {'map': {'fields': {'name': 'asc', 'age': 'asc'}},
                            'reduce': '_count',
                            'options': {'def': {'fields': ['name', 'age']},
                                        'w': 2}
                            }
                    }
        }
        self.db.create_document(data)
        ddoc = DesignDocument(self.db, '_design/ddoc001')
        ddoc.fetch()
        with self.assertRaises(CloudantException) as cm:
            ddoc['language'] = 'not-query'
            ddoc.save()
        err = cm.exception
        self.assertEqual(str(err), 'View view001 must be of type View.')

        with self.assertRaises(CloudantException) as cm:
            del ddoc['language']
            ddoc.save()
        err = cm.exception
        self.assertEqual(str(err), 'View view001 must be of type View.')
コード例 #4
0
 def test_fetch_query_views(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing query index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         '_id': '_design/ddoc001',
         'language': 'query',
         'views': {
             'view001': {'map': {'fields': {'name': 'asc', 'age': 'asc'}},
                         'reduce': '_count',
                         'options': {'def': {'fields': ['name', 'age']},
                                     'w': 2}
                         }
                 }
     }
     doc = self.db.create_document(data)
     self.assertIsInstance(doc, Document)
     data['_rev'] = doc['_rev']
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.fetch()
     self.assertIsInstance(ddoc, DesignDocument)
     self.assertEqual(ddoc, data)
     self.assertIsInstance(ddoc['views']['view001'], QueryIndexView)
コード例 #5
0
 def test_fetch_text_indexes(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing query index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         '_id': '_design/ddoc001',
         'language': 'query',
         'indexes': {'index001': 
                  {'index': {'index_array_lengths': True,
                             'fields': [{'name': 'name', 'type': 'string'},
                                        {'name': 'age', 'type': 'number'}],
                             'default_field': {'enabled': True,
                                               'analyzer': 'german'},
                             'default_analyzer': 'keyword',
                             'selector': {}},
                   'analyzer': {'name': 'perfield',
                                'default': 'keyword',
                                'fields': {'$default': 'german'}}}}}
     doc = self.db.create_document(data)
     self.assertIsInstance(doc, Document)
     data['_rev'] = doc['_rev']
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.fetch()
     self.assertIsInstance(ddoc, DesignDocument)
     self.assertEqual(ddoc, data)
     self.assertIsInstance(ddoc['indexes']['index001'], dict)
コード例 #6
0
 def test_fetch_map_reduce(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing MapReduce views.
     """
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     view_map = "function (doc) {\n  emit(doc._id, 1);\n}"
     view_reduce = "_count"
     db_copy = "{0}-copy".format(self.db.database_name)
     ddoc.add_view("view001", view_map, view_reduce)
     ddoc.add_view("view002", view_map, view_reduce, dbcopy=db_copy)
     ddoc.add_view("view003", view_map)
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, "_design/ddoc001")
     self.assertNotEqual(ddoc_remote, ddoc)
     ddoc_remote.fetch()
     self.assertEqual(ddoc_remote, ddoc)
     self.assertTrue(ddoc_remote["_rev"].startswith("1-"))
     self.assertEqual(
         ddoc_remote,
         {
             "_id": "_design/ddoc001",
             "_rev": ddoc["_rev"],
             "views": {
                 "view001": {"map": view_map, "reduce": view_reduce},
                 "view002": {"map": view_map, "reduce": view_reduce, "dbcopy": db_copy},
                 "view003": {"map": view_map},
             },
         },
     )
     self.assertIsInstance(ddoc_remote["views"]["view001"], View)
     self.assertIsInstance(ddoc_remote["views"]["view002"], View)
     self.assertIsInstance(ddoc_remote["views"]["view003"], View)
コード例 #7
0
 def test_fetch_map_reduce(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing MapReduce views.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     view_map = 'function (doc) {\n  emit(doc._id, 1);\n}'
     view_reduce = '_count'
     db_copy = '{0}-copy'.format(self.db.database_name)
     ddoc.add_view('view001', view_map, view_reduce)
     ddoc.add_view('view002', view_map, view_reduce, dbcopy=db_copy)
     ddoc.add_view('view003', view_map)
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     self.assertNotEqual(ddoc_remote, ddoc)
     ddoc_remote.fetch()
     self.assertEqual(ddoc_remote, ddoc)
     self.assertTrue(ddoc_remote['_rev'].startswith('1-'))
     self.assertEqual(ddoc_remote, {
         '_id': '_design/ddoc001',
         '_rev': ddoc['_rev'],
         'views': {
             'view001': {'map': view_map, 'reduce': view_reduce},
             'view002': {'map': view_map, 'reduce': view_reduce, 'dbcopy': db_copy},
             'view003': {'map': view_map}
         }
     })
     self.assertIsInstance(ddoc_remote['views']['view001'], View)
     self.assertIsInstance(ddoc_remote['views']['view002'], View)
     self.assertIsInstance(ddoc_remote['views']['view003'], View)
コード例 #8
0
    def test_query_view_save_fails_when_lang_is_not_query(self):
        """
        Tests that save fails when language is not query but views are query
        index views.
        """
        # This is not the preferred way of dealing with query index
        # views but it works best for this test.
        data = {
            "_id": "_design/ddoc001",
            "language": "query",
            "views": {
                "view001": {
                    "map": {"fields": {"name": "asc", "age": "asc"}},
                    "reduce": "_count",
                    "options": {"def": {"fields": ["name", "age"]}, "w": 2},
                }
            },
        }
        self.db.create_document(data)
        ddoc = DesignDocument(self.db, "_design/ddoc001")
        ddoc.fetch()
        with self.assertRaises(CloudantException) as cm:
            ddoc["language"] = "not-query"
            ddoc.save()
        err = cm.exception
        self.assertEqual(str(err), "View view001 must be of type View.")

        with self.assertRaises(CloudantException) as cm:
            del ddoc["language"]
            ddoc.save()
        err = cm.exception
        self.assertEqual(str(err), "View view001 must be of type View.")
コード例 #9
0
 def test_fetch_text_indexes(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing query index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         "_id": "_design/ddoc001",
         "language": "query",
         "indexes": {
             "index001": {
                 "index": {
                     "index_array_lengths": True,
                     "fields": [{"name": "name", "type": "string"}, {"name": "age", "type": "number"}],
                     "default_field": {"enabled": True, "analyzer": "german"},
                     "default_analyzer": "keyword",
                     "selector": {},
                 },
                 "analyzer": {"name": "perfield", "default": "keyword", "fields": {"$default": "german"}},
             }
         },
     }
     doc = self.db.create_document(data)
     self.assertIsInstance(doc, Document)
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     ddoc.fetch()
     self.assertIsInstance(ddoc, DesignDocument)
     data["_rev"] = doc["_rev"]
     data["views"] = dict()
     self.assertEqual(ddoc, data)
     self.assertIsInstance(ddoc["indexes"]["index001"], dict)
コード例 #10
0
ファイル: __init__.py プロジェクト: gonrin/gatco_couchdb
    def get(self, key, default=None, remote=False):
        """
        Overrides dictionary __getitem__ behavior to provide a document
        instance for the specified key from the current database.
        If the document instance does not exist locally, then a remote request
        is made and the document is subsequently added to the local cache and
        returned to the caller.
        If the document instance already exists locally then it is returned and
        a remote request is not performed.
        A KeyError will result if the document does not exist locally or in the
        remote database.
        :param str key: Document id used to retrieve the document from the
            database.
        :returns: A Document or DesignDocument object depending on the
            specified document id (key)
        """
        if remote is False:
            if key in list(self.keys()):
                return super(CouchDatabase, self).__getitem__(key)

        if key.startswith('_design/'):
            doc = DesignDocument(self, key)
        else:
            doc = Document(self, key)

        if doc.exists():
            doc.fetch()
            super(CouchDatabase, self).__setitem__(key, doc)
            return doc

        return default
コード例 #11
0
 def test_view_callable_with_invalid_javascript(self):
     """
     Test error condition when Javascript errors exist.  This test is only
     valid for CouchDB because the map function Javascript is validated on
     the Cloudant server when attempting to save a design document so invalid
     Javascript is not possible there.
     """
     self.populate_db_with_documents()
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.add_view(
         'view001',
         'This is not valid Javascript'
     )
     ddoc.save()
     # Verify that the ddoc and view were saved remotely 
     # along with the invalid Javascript
     del ddoc
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.fetch()
     view = ddoc.get_view('view001')
     self.assertEqual(view.map, 'This is not valid Javascript')
     try:
         for row in view.result:
             self.fail('Above statement should raise an Exception')
     except requests.HTTPError as err:
         self.assertEqual(err.response.status_code, 500)
コード例 #12
0
 def test_fetch(self):
     """
     Ensure that the document fetch from the database returns the
     DesignDocument format as expected.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     view_map = 'function (doc) {\n  emit(doc._id, 1);\n}'
     view_reduce = '_count'
     ddoc.add_view('view001', view_map)
     ddoc.add_view('view002', view_map, view_reduce)
     ddoc.add_view('view003', view_map)
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     self.assertNotEqual(ddoc_remote, ddoc)
     ddoc_remote.fetch()
     self.assertEqual(ddoc_remote, ddoc)
     self.assertEqual(len(ddoc_remote['views']), 3)
     reduce_count = 0
     for x in xrange(1, 4):
         name = 'view{0:03d}'.format(x)
         view = ddoc_remote['views'][name]
         self.assertIsInstance(view, View)
         self.assertEqual(view.map, view_map)
         if name == 'view002':
             reduce_count += 1
             self.assertEqual(view.reduce, view_reduce)
         else:
             self.assertIsNone(view.reduce)
     self.assertEqual(reduce_count, 1)
コード例 #13
0
 def test_text_index_save_fails_with_existing_search_index(self):
     """
     Tests that save fails when language is not query and both a query text
     index and a search index exist in the design document.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     search_index = ('function (doc) {\n  index("default", doc._id); '
                     'if (doc._id) {index("name", doc.name, '
                     '{"store": true}); }\n}')
     ddoc.add_search_index('search001', search_index)
     self.assertIsInstance(
         ddoc['indexes']['search001']['index'], str
     )
     ddoc.save()
     self.assertTrue(ddoc['_rev'].startswith('1-'))
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     ddoc_remote.fetch()
     ddoc_remote['indexes']['index001'] = {
         'index': {'index_array_lengths': True,
                   'fields': [{'name': 'name', 'type': 'string'},
                              {'name': 'age', 'type': 'number'}],
                   'default_field': {'enabled': True, 'analyzer': 'german'},
                   'default_analyzer': 'keyword',
                   'selector': {}},
         'analyzer': {'name': 'perfield','default': 'keyword',
                      'fields': {'$default': 'german'}}}
     self.assertIsInstance(ddoc_remote['indexes']['index001']['index'], dict)
     with self.assertRaises(CloudantException) as cm:
         ddoc_remote.save()
     err = cm.exception
     self.assertEqual(
         str(err),
         'Function for search index index001 must be of type string.'
     )
コード例 #14
0
 def test_delete_a_query_index_view(self):
     """
     Test deleting a query index view fails as expected.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         '_id': '_design/ddoc001',
         'language': 'query',
         'views': {
             'view001': {'map': {'fields': {'name': 'asc', 'age': 'asc'}},
                         'reduce': '_count',
                         'options': {'def': {'fields': ['name', 'age']},
                                     'w': 2}
                         }
                 }
     }
     self.db.create_document(data)
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.fetch()
     with self.assertRaises(CloudantException) as cm:
         ddoc.delete_view('view001')
     err = cm.exception
     self.assertEqual(
         str(err),
         'Cannot delete a query index view using this method.'
     )
コード例 #15
0
def create_summaries(conn):
    """
    Get summary statistics from a view in the sentiments db and update the
    summaries db.
    """

    # query view
    logging.info('Querying database for summary...')

    db = conn['sentiments']
    ddoc = DesignDocument(db, '_design/summary')
    ddoc.fetch()
    view = ddoc.get_view('summary-view')
    view_results = view(group=True)['rows']
    logging.info('Query completed.')

    # switch to summaries db
    db = conn['summaries']

    for view_result in view_results:
        symbol = view_result['key']
        unix_date = int(time.mktime(date.today().timetuple()))

        # query for today's summary record
        query = Query(db,
                      fields=['_id', 'symbol', 'date', 'summary'],
                      selector={
                          'symbol': {
                              '$eq': symbol
                          },
                          'date': {
                              '$eq': unix_date
                          }
                      })
        if query.result[0]:
            # A record for today already exists so overwrite it. This should not normally happen.
            record = db[query.result[0][0]['_id']]
            summary = record['summary']

            logging.info('Updating summary for %s', symbol)
            record['summary'] = view_result['value']
            summary.save()
        else:
            # Creating a new summary
            logging.info('Creating summary for %s...', symbol)
            data = {
                'symbol': symbol,
                'date': unix_date,
                'summary': view_result['value']
            }
            new_summary = db.create_document(data)
            if new_summary.exists():
                logging.info('Finished creating summary.')
            else:
                logging.error('Failed to create summary.')

        # don't exceed rate limit
        time.sleep(1)
コード例 #16
0
 def get_transactions(self, page, pagesize):
     print("Portfolio.get_transactions()")
     skip = page * pagesize
     ddoc = DesignDocument(self.db, 'transactions')
     ddoc.fetch()
     view = View(ddoc, 'history')
     return view(include_docs=False,
                 limit=pagesize,
                 skip=skip,
                 reduce=False)['rows']
コード例 #17
0
 def test_fetch_existing_design_document_with_docid_encoded_url(self):
     """
     Test fetching design document content from an existing document where
     the document id requires an encoded url
     """
     ddoc = DesignDocument(self.db, "_design/http://example.com")
     ddoc.create()
     new_ddoc = DesignDocument(self.db, "_design/http://example.com")
     new_ddoc.fetch()
     self.assertEqual(new_ddoc, ddoc)
コード例 #18
0
ファイル: client.py プロジェクト: dimagi/couchdbkit
 def raw_view(self, view_path, params):
     params = deepcopy(params)
     params.pop('dynamic_properties', None)
     if view_path == '_all_docs':
         return self.cloudant_database.all_docs(**params)
     else:
         view_path = view_path.split('/')
         assert len(view_path) == 4
         ddoc = DesignDocument(self.cloudant_database, view_path[1])
         ddoc.fetch()
         view = ddoc.get_view(view_path[3])
         return view(**params)
コード例 #19
0
 def test_update_design_document_with_encoded_url(self):
     """
     Test that updating a design document where the document id requires that
     the document url be encoded is successful.
     """
     # First create the design document
     ddoc = DesignDocument(self.db, "_design/http://example.com")
     ddoc.save()
     # Now test that the design document gets updated
     ddoc.save()
     self.assertTrue(ddoc["_rev"].startswith("2-"))
     remote_ddoc = DesignDocument(self.db, "_design/http://example.com")
     remote_ddoc.fetch()
     self.assertEqual(remote_ddoc, ddoc)
コード例 #20
0
 def __getitem__(self, key):
     if not self.remote:
         if key in list(self.keys(remote=False)):
             return super(CouchDatabase, self).__getitem__(key)
     if key.startswith("_design/"):
         doc = DesignDocument(self, key)
     else:
         doc = Document(self, key)
     if doc.exists():
         doc.fetch()
         super(CouchDatabase, self).__setitem__(key, doc)
         return doc
     else:
         raise KeyError(key)
コード例 #21
0
 def test_fetch_no_views(self):
     """
     Ensure that the document fetched from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing no views.
     """
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, "_design/ddoc001")
     ddoc_remote.fetch()
     self.assertEqual(set(ddoc_remote.keys()), {"_id", "_rev", "views"})
     self.assertEqual(ddoc_remote["_id"], "_design/ddoc001")
     self.assertTrue(ddoc_remote["_rev"].startswith("1-"))
     self.assertEqual(ddoc_remote["_rev"], ddoc["_rev"])
     self.assertEqual(ddoc_remote.views, {})
コード例 #22
0
 def test_fetch_no_views(self):
     """
     Ensure that the document fetched from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing no views.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     ddoc_remote.fetch()
     self.assertEqual(set(ddoc_remote.keys()),
                      {'_id', '_rev', 'indexes', 'views'})
     self.assertEqual(ddoc_remote['_id'], '_design/ddoc001')
     self.assertTrue(ddoc_remote['_rev'].startswith('1-'))
     self.assertEqual(ddoc_remote['_rev'], ddoc['_rev'])
     self.assertEqual(ddoc_remote.views, {})
コード例 #23
0
    def test_design_doc(self, mock_fetch):
        """test overridden methods work as expected"""
        mock_database = mock.Mock()
        ddoc = DesignDocument(mock_database, '_design/unittest')
        ddoc['views'] = {
            'view1': {'map': "MAP", 'reduce': 'REDUCE'}
        }
        ddoc.fetch()

        self.assertTrue(mock_fetch.called)
        views = [x for x in ddoc.iterviews()]
        self.assertEqual(len(views), 1)
        view = views[0]
        self.assertIn('view1', view)
        funcs = view[1]
        self.assertEqual(funcs['map'], 'MAP')
        self.assertEqual(funcs['reduce'], 'REDUCE')
        self.assertIn('view1', ddoc.views)
コード例 #24
0
def create_latest_recommendations_index():

    ddoc_fn = '''
function(doc) {
  emit([doc.user, doc.timestamp], null);
}
'''    
    db = cloudant_client[CL_RECOMMENDDB]
    index_name = 'latest-recommendation-index'

    ddoc = DesignDocument(db, index_name)
    if ddoc.exists():
        ddoc.fetch()
        ddoc.update_view(index_name, ddoc_fn)
        print('updated', index_name)
    else:
        ddoc.add_view(index_name, ddoc_fn)
        print('created', index_name)
    ddoc.save()
コード例 #25
0
 def test_fetch_no_search_index(self):
     """
     Ensure that the document fetched from the database returns the
     DesignDocument format as expected when retrieving a design document
     containing no search indexes.
     The :func:`~cloudant.design_document.DesignDocument.fetch` function
     adds the ``indexes`` key in the locally cached DesignDocument if
     indexes do not exist in the remote design document.
     """
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.save()
     ddoc_remote = DesignDocument(self.db, '_design/ddoc001')
     ddoc_remote.fetch()
     self.assertEqual(set(ddoc_remote.keys()),
                      {'_id', '_rev', 'indexes', 'views'})
     self.assertEqual(ddoc_remote['_id'], '_design/ddoc001')
     self.assertTrue(ddoc_remote['_rev'].startswith('1-'))
     self.assertEqual(ddoc_remote['_rev'], ddoc['_rev'])
     self.assertEqual(ddoc_remote.indexes, {})
コード例 #26
0
def create_moviedb_indexes():

    ddoc_fn = '''
function(doc){
  index("default", doc._id);
  if (doc.name){
    index("name", doc.name, {"store": true});
  }
}
'''    
    db = cloudant_client[CL_MOVIEDB]
    index_name = 'movie-search-index'

    ddoc = DesignDocument(db, index_name)
    if ddoc.exists():
        ddoc.fetch()
        ddoc.update_search_index(index_name, ddoc_fn, analyzer=None)
        print('updated', index_name)
    else:
        ddoc.add_search_index(index_name, ddoc_fn, analyzer=None)
        print('created', index_name)
    ddoc.save()
コード例 #27
0
def create_authdb_indexes():

    db = cloudant_client[CL_AUTHDB]

    ddoc_fn = '''
function(doc){
  if (doc.email) {
    emit(doc.email);
  }
}
'''    
    view_name = 'authdb-email-index'

    ddoc = DesignDocument(db, view_name)
    if ddoc.exists():
        ddoc.fetch()
        ddoc.update_view(view_name, ddoc_fn)
        print('updated', view_name)
    else:
        ddoc.add_view(view_name, ddoc_fn)
        print('created', view_name)
    ddoc.save()
コード例 #28
0
 def test_view_callable_with_invalid_javascript(self):
     """
     Test error condition when Javascript errors exist.  This test is only
     valid for CouchDB because the map function Javascript is validated on
     the Cloudant server when attempting to save a design document so invalid
     Javascript is not possible there.
     """
     self.populate_db_with_documents()
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.add_view('view001', 'This is not valid Javascript')
     ddoc.save()
     # Verify that the ddoc and view were saved remotely
     # along with the invalid Javascript
     del ddoc
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.fetch()
     view = ddoc.get_view('view001')
     self.assertEqual(view.map, 'This is not valid Javascript')
     try:
         for row in view.result:
             self.fail('Above statement should raise an Exception')
     except requests.HTTPError as err:
         self.assertEqual(err.response.status_code, 500)
コード例 #29
0
 def test_view_callable_with_invalid_javascript(self):
     """
     Test error condition when Javascript errors exist
     """
     self.populate_db_with_documents()
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.add_view(
         'view001',
         'This is not valid Javascript'
     )
     ddoc.save()
     # Verify that the ddoc and view were saved remotely 
     # along with the invalid Javascript
     del ddoc
     ddoc = DesignDocument(self.db, 'ddoc001')
     ddoc.fetch()
     view = ddoc.get_view('view001')
     self.assertEqual(view.map, 'This is not valid Javascript')
     try:
         for row in view.result:
             self.fail('Above statement should raise an Exception')
     except requests.HTTPError, err:
         self.assertEqual(err.response.status_code, 500)
コード例 #30
0
 def test_query_view_save_succeeds(self):
     """
     Tests that save succeeds when language is query and views are query
     index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         "_id": "_design/ddoc001",
         "language": "query",
         "views": {
             "view001": {
                 "map": {"fields": {"name": "asc", "age": "asc"}},
                 "reduce": "_count",
                 "options": {"def": {"fields": ["name", "age"]}, "w": 2},
             }
         },
     }
     self.db.create_document(data)
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     ddoc.fetch()
     self.assertTrue(ddoc["_rev"].startswith("1-"))
     ddoc.save()
     self.assertTrue(ddoc["_rev"].startswith("2-"))
コード例 #31
0
 def test_cq_view_save_succeeds(self):
     """
     Tests that save succeeds when language is query and views are query
     index views.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         '_id': '_design/ddoc001',
         'language': 'query',
         'views': {
             'view001': {'map': {'fields': {'name': 'asc', 'age': 'asc'}},
                         'reduce': '_count',
                         'options': {'def': {'fields': ['name', 'age']},
                                     'w': 2}
                         }
                 }
     }
     self.db.create_document(data)
     ddoc = DesignDocument(self.db, '_design/ddoc001')
     ddoc.fetch()
     self.assertTrue(ddoc['_rev'].startswith('1-'))
     ddoc.save()
     self.assertTrue(ddoc['_rev'].startswith('2-'))
コード例 #32
0
 def test_delete_a_query_index_view(self):
     """
     Test deleting a query index view fails as expected.
     """
     # This is not the preferred way of dealing with query index
     # views but it works best for this test.
     data = {
         "_id": "_design/ddoc001",
         "language": "query",
         "views": {
             "view001": {
                 "map": {"fields": {"name": "asc", "age": "asc"}},
                 "reduce": "_count",
                 "options": {"def": {"fields": ["name", "age"]}, "w": 2},
             }
         },
     }
     self.db.create_document(data)
     ddoc = DesignDocument(self.db, "_design/ddoc001")
     ddoc.fetch()
     with self.assertRaises(CloudantException) as cm:
         ddoc.delete_view("view001")
     err = cm.exception
     self.assertEqual(str(err), "Cannot delete a query index view using this method.")
コード例 #33
0
class Portfolio:
    def __init__(self, stockAPIkey):
        print("Portfolio.init({0})".format(stockAPIkey))
        self.stocks = dict(
        )  # Contains all stocks, both owned and tracked, with all their metadata
        # { symbol: {metadata} }
        self.categories = dict(
        )  # Contains major categories with arrays of (sub)categories and their metadata
        # { category: { target: %, actual: %, type: 'stock/fixed income' }}
        self.prices_last_updated = None  # Might use this to update prices on a schedule
        self.foliodoc = None  # placeholder for Cloudant document that stores portfolio metadata
        self.stockAPIkey = stockAPIkey
        self.barchartAPIkey = getenv('BARCHART')
        self.total_portfolio_value = 0
        self.status = ""

    def load(self, db, portfolioname):
        print("Portfolio.load()")
        # Initialize database and variables
        self.name = portfolioname
        self.db = db

        # Initialize Cloudant database views
        self.stockddoc = DesignDocument(self.db, 'stocks')
        self.stockddoc.fetch()
        self.bycategory_view = self.stockddoc.get_view('bycategory')
        self.owned_view = self.stockddoc.get_view('owned')
        self.allowned_view = self.stockddoc.get_view('allowned')
        self.manualquote_view = self.stockddoc.get_view('manualquotes')
        self.folio_ddoc = DesignDocument(self.db, 'activefolios')
        self.folio_ddoc.fetch()
        self.active_folios_view = self.folio_ddoc.get_view('currentfolio')

        self.populate_categories()

        self.populate_stocks()

        self.refresh_total_value()

    # Load available category metadata into memory from DB
    def populate_categories(self):
        pass
        print("Portfolio.populate_categories()")
        # Load portfolio specification document from DB
        self.foliodoc = Document(self.db, self.name)
        self.foliodoc.fetch()
        for category in self.foliodoc['categories']:
            # print category
            for subcategory in self.foliodoc['categories'][category].keys():
                # print "Subcategory name: {0} Target: {1}".format(subcategory, self.foliodoc['categories'][category][subcategory])
                self.categories[subcategory] = dict(
                    target=self.foliodoc['categories'][category][subcategory],
                    actual=0,
                    type=category)

    # Populate stock metadata in memory from DB. (tracked and owned)
    def populate_stocks(self):
        print("Portfolio.populate_stocks()")

        # get metadata on stocks we're tracking in the portfolio
        with self.bycategory_view.custom_result(include_docs=True,
                                                reduce=False) as rslt:
            for line in rslt:
                doc = line['doc']
                if doc['symbol'] == 'Cash':
                    temp_price = 1
                else:
                    temp_price = -1
                self.stocks[doc['symbol']] = dict(symbol=doc['symbol'],
                                                  name=doc['name'],
                                                  comments=doc['comments'],
                                                  active=doc['active'],
                                                  buybelow=doc['buybelow'],
                                                  lastprice=temp_price,
                                                  category=doc['category'],
                                                  qty=0)

    # Refresh current total value of portfolio for percentage calcuations and update subcategory totals
    def refresh_total_value(self):
        print("Portfolio.refresh_total_value()")
        self.total_portfolio_value = 0

        # Make sure prices are current
        quote_success = self.refresh_all_stock_prices()
        if not quote_success:
            # Alert user that stock prices are potentially out of date
            self.status = 'WARNING: Stock price quote error. Data may be out of date.'

        # Update quantities of stocks we own
        with self.allowned_view.custom_result(
                reduce=True, include_docs=False,
                group_level=1) as resultcollection:
            for stock in resultcollection:
                self.stocks[stock['key']]['qty'] = stock['value']

        # total up the account's value
        for stock in self.stocks.values():
            self.total_portfolio_value = self.total_portfolio_value + (
                stock['qty'] * stock['lastprice'])

        # Update each subcategory's percentage by summing the stocks within it
        # Right now this is a nested for loop, but through data in memory.
        category_value = 0
        for category_name in self.categories.keys():
            for stock in self.stocks.values():
                if stock['category'] == category_name:
                    category_value = category_value + stock[
                        'lastprice'] * stock['qty']
            self.categories[category_name]['actual'] = (
                category_value / self.total_portfolio_value) * 100
            category_value = 0

    def get_subcategory_list(self):
        print("Portfolio.get_subcategory_list()")
        return self.categories.keys()

    def barchart_quote(self, symbols_string):
        # execute stock API call (BarChart)
        start_time = time()
        myurl = "https://marketdata.websol.barchart.com/getQuote.json"
        payload = {
            'apikey': self.barchartAPIkey,
            'only': 'symbol,name,lastPrice',
            'symbols': symbols_string,
            'mode': 'R'
        }
        try:
            r = requests.get(url=myurl, params=payload)
            print r.text
            data = r.json()
            end_time = time()
            print("Barchart API query time: {0} seconds".format(
                float(end_time - start_time)))
            return data
        except Exception as e:
            print "Cannot get quotes from BarChart: {0}".format(e)
            return None

    def alphavantage_quote(self, symbols_string):
        start_time = time()
        myurl = "https://www.alphavantage.co/query"
        payload = {
            'function': 'BATCH_STOCK_QUOTES',
            'symbols': symbols_string,
            'apikey': self.stockAPIkey
        }
        try:
            r = requests.get(url=myurl, params=payload)
            data = r.json()
            end_time = time()
            print("AlphaVantage API query time: {0} seconds".format(
                float(end_time - start_time)))
            return data
        except Exception as e:
            print "Cannot get quotes from AlphaVantage: {0}".format(e)
            return None

    def refresh_all_stock_prices(self):
        print("Portfolio.refresh_all_stock_prices()")

        # Iterate through manual stock quotes in DB first (to cover missing symbols in stock APIs)
        with self.manualquote_view.custom_result(reduce=False) as rslt:
            manual_quotes = rslt[:]
            for row in manual_quotes:
                symbol = row['key'][0]
                date = row['key'][1]
                self.stocks[symbol]['lastprice'] = row['value']

        # construct string for API call
        symbols_string = ''
        for symbol in self.stocks.keys():
            if symbol <> "Cash":
                symbols_string = symbols_string + "{0},".format(symbol)

        # trim last comma
        symbols_string = symbols_string[:-1]

        ## execute stock API call (BarChart)
        barchartdata = self.barchart_quote(symbols_string)
        if barchartdata <> None:
            pass

        # Execute stock API call (AlphaVantage)
        alphavantagedata = self.alphavantage_quote(symbols_string)
        if alphavantagedata <> None:
            for stock_data in alphavantagedata['Stock Quotes']:
                # set the price of the holding in question
                if float(stock_data['2. price']) <> 0.0:
                    self.stocks[stock_data['1. symbol']]['lastprice'] = float(
                        stock_data['2. price'])

        # Update last quoted time
        self.prices_last_updated = int(time())

        # Check for any missing stock prices (any that are not set will be -1)
        for stock in self.stocks.values():
            if stock['lastprice'] == -1.0:
                return False

        return True

    # Create a new doc to track this stock and add it to the dictionary of stock data
    # custom ID is OK, since it prevents duplicate stock trackers
    # We should keep all prices in memory exclusively, or create "quote" docs
    # This DOES NOT store information about how much we own, because that's event sourced by transactions
    def new_stock(self, category, symbol, name, buybelow, comments, active):
        print("Portfolio.new_stock()")
        with Document(self.db, symbol) as stockdoc:
            stockdoc['type'] = 'stock'
            stockdoc['updated'] = strftime("%Y-%m-%d %H:%M:%S")
            stockdoc['category'] = category
            stockdoc['symbol'] = symbol
            stockdoc['active'] = active
            stockdoc['name'] = name
            stockdoc['comments'] = comments
            stockdoc['buybelow'] = buybelow
            self.stocks[symbol] = json.loads(stockdoc.json())
        # Get a quote for the new stock
        self.stocks[symbol]['lastprice'] = -1
        self.stocks[symbol]['qty'] = 0

        # update all holdings and totals
        self.refresh_total_value()

    def new_transaction_doc(self, symbol, quantity, price, fee, action):
        xactiondoc = Document(self.db)
        xactiondoc['type'] = 'transaction'
        xactiondoc['action'] = action
        xactiondoc['quantity'] = quantity
        xactiondoc['date'] = strftime("%Y-%m-%d %H:%M:%S")
        xactiondoc['fee'] = fee
        xactiondoc['price'] = price
        if action == 'deposit' or action == 'withdrawl':
            xactiondoc['symbol'] = 'Cash'
            xactiondoc['price'] = 1
        else:  #otherwise use symbol passed and check to see if updating cash is needed
            xactiondoc['symbol'] = symbol
        xactiondoc.save()

    # Execute a transaction document and update cash balance (if appropriate)
    def trade(self, symbol, quantity, price, fee, action, usebalance):
        print("Portfolio.trade()")
        self.new_transaction_doc(symbol, quantity, price, fee, action)
        if (usebalance == True and symbol <> "Cash"):
            cashqty = (quantity * price) + fee
            if action == 'buy':
                cashaction = 'withdrawl'
            else:
                cashaction = 'deposit'
            self.new_transaction_doc('Cash', cashqty, 1, 0, cashaction)
        self.refresh_total_value()

    # Return currently cached metadata for this stock
    def get_stock(self, symbol):
        return self.stocks[symbol]

    # Get an individual stock symbol's quote via the API
    def get_quote(self, symbol):
        print("Portflio.get_quote({0})".format(symbol))
        myurl = "https://www.alphavantage.co/query?function=BATCH_STOCK_QUOTES&symbols={0}&apikey={1}".format(
            symbol, self.stockAPIkey)
        try:
            r = requests.get(url=myurl)
            data = r.json()
            return float(data['Stock Quotes'][0]['2. price'])
        except Exception as e:
            print_local("Unable to get stock price: {0}".format(e))
            return -1

    # Update a tracked stock's metadata
    def update_stock(self, symbol, name, buybelow, comments, active):
        with Document(self.db, symbol) as doc:
            doc['updated'] = strftime("%Y-%m-%d %H:%M:%S")
            doc['name'] = str(name)
            if active == 'true':
                doc['active'] = True
            else:
                doc['active'] = False
            doc['buybelow'] = float(buybelow)
            doc['comments'] = str(comments)

            for x in ('updated', 'name', 'active', 'buybelow', 'comments'):
                self.stocks[symbol][x] = doc[x]

    # Neutralize content upon logout
    def clear(self):
        print("Portfolio.clear()")
        self.name = None
        self.db = None
        self.categories = dict()
        self.stocks = dict()
        self.prices_last_updated = None
        self.total_portfolio_value = 0
        self.foliodoc = None

    # Return list of historical trasactions from DB
    def get_transactions(self, page, pagesize):
        print("Portfolio.get_transactions()")
        skip = page * pagesize
        ddoc = DesignDocument(self.db, 'transactions')
        ddoc.fetch()
        view = View(ddoc, 'history')
        return view(include_docs=False,
                    limit=pagesize,
                    skip=skip,
                    reduce=False)['rows']

    # Return a full state of the portfolio with metadata formatted for the Jinja template's rendering
    def get_template_data(self):
        print "Portfolio.get_template_data()"
        template_data = dict()

        # Iterate through the sub-categories
        for subcategory in self.categories.keys():
            # print "Processing {0}:\nData: {1}".format(subcategory,self.categories[subcategory])
            # local dictionary for this subcategory's data to go into the array above. Insert what we have so far
            subcategory_data = dict(
                type=subcategory,
                target_percentage=self.categories[subcategory]['target'],
                value=
                0,  # Tracks total value of all invested holdings in this particular subcategory (not used right now)
                actual_percentage="{0:,.1f}".format(
                    self.categories[subcategory]['actual']),
                holdings=[]  # array for all stocks in this subcat
            )
            template_data[subcategory] = subcategory_data

        # print template_data
        # Iterate through all tracked stocks in this subcategory
        for stock in self.stocks.keys():
            # local dictionary for this stock's data
            stock_data = dict(
                symbol=self.stocks[stock]['symbol'],
                name=self.stocks[stock]['name'],
                qty=self.stocks[stock]['qty'],
                price="$ {0:,.2f}".format(self.stocks[stock]['lastprice']),
                buy_below="$ {0:,.2f}".format(self.stocks[stock]['buybelow']),
                comments=self.stocks[stock]['comments'],
                value="$ {0:,.2f}".format(
                    float(self.stocks[stock]['qty'] *
                          self.stocks[stock]['lastprice'])
                )  # value of this security owned
            )
            template_data[self.stocks[stock]['category']]['holdings'].append(
                stock_data)
        return template_data