Пример #1
0
 def update(self, accountid):
     account = self.__own_account(accountid)
     if not account:
         self.notfound(
            'Nonexistent account cannot be modified (or you do not own it)')
     if 'name' in self.args:
         account.name = self.args['name']
     if 'currency' in self.args:
         # Do not update currency if account has transactions
         if not models.TransactionAccount.query.filter(
                     models.TransactionAccount.account == account
                ).count():
             currency = core.Currency.query.filter(
                 db.and_(
                     core.Currency.isocode == self.args['currency'],
                     db.or_(
                         core.Currency.owner_username == self.username,
                         core.Currency.owner_username == None
                     )
                 )
             ).first()
             if currency:
                 account.currency = currency
     if 'start_balance' in self.args:
         account.start_balance = Decimal(self.args['start_balance'])
         self.add_to_response('totalbalance')
     db.session.commit()
     return account.as_dict(self.username)
Пример #2
0
 def __own_preference(self, preferencename):
     return models.UserPreference.query.filter(
                 db.and_(
                     models.UserPreference.user_username == self.username,
                     models.UserPreference.name == preferencename
                 )
            ).first()
Пример #3
0
 def list(self):
     categories = models.Category.query.order_by(
         models.Category.name).options(
             db.joinedload(models.Category.currency)).filter(
                 db.and_(models.Category.owner_username == self.username,
                         models.Category.parent_id == None)).all()
     return [c.as_dict(self.username) for c in categories]
Пример #4
0
    def __search(self, substring):
        """Search on parts of the users name or on exact email address"""
        if len(substring) < 3:
            self.badrequest('Please give at least 3 characters')
        if '@' in substring:
            corresponding_rows = models.User.query.join(
                                    models.UserEmail
            ).filter(
                models.UserEmail.email_address == substring,
                models.UserEmail.confirmation == 'OK'
            )
        else:
            substring=u'%{0}%'.format(substring)
            corresponding_rows = models.User.query.filter(
                db.and_(
                    models.User.username != self.username,
                    db.or_(
                        models.User.username.like(substring),
                        models.User.first_name.like(substring),
                        models.User.last_name.like(substring),
                    )
                )
            )

        return [u.as_dict() for u in corresponding_rows.all()]
Пример #5
0
    def create(self):
        if not (
            'currency' in self.args and
            'name' in self.args and
            'start_balance' in self.args
        ):
            self.badrequest(
                 "Please provide the account name, currency and start balance")
        currency = core.Currency.query.filter(
            db.and_(
                core.Currency.isocode == self.args['currency'],
                db.or_(
                    core.Currency.owner_username == self.username,
                    core.Currency.owner_username == None
                )
            )
        ).first()
        if not currency:
            self.badrequest("This currency does not exist")

        name = self.args['name']
        start_balance = self.args['start_balance']

        a = models.Account(
                name=name,
                currency=currency,
                start_balance=start_balance
        )
        ao = models.AccountOwner(account=a, owner_username=self.username)
        db.session.add_all((a, ao))
        db.session.commit()
        self.add_to_response('totalbalance')
        return a.as_dict(self.username)
Пример #6
0
 def create(self):
     if not 'username' in self.args:
         self.badrequest("Please provide the contact username")
     if self.username in config.DEMO_ACCOUNTS:
         self.badrequest("Cannot add contacts to demo accounts")
     # Verify the contact exists
     contactuser = models.User.query.filter(
                     models.User.username == self.args['username']
                   ).first()
     if not contactuser:
         self.notfound('This user does not exist')
     # Verify the user doesn't already have this contact
     testcontact = models.UserContact.query.filter(
                     db.and_(
                         models.UserContact.user_username == self.username,
                models.UserContact.contact_username == self.args['username']
                     )
                   ).first()
     if testcontact:
         self.badrequest("This contact already exists")
     contact = models.UserContact(
                 user_username=self.username,
                 contact=contactuser,
                 comment=self.args.get('comment', '')
               )
     db.session.add(contact)
     db.session.commit()
     return contact.as_dict()
Пример #7
0
 def create(self):
     if not ('currency' in self.args and 'name' in self.args):
         self.badrequest("Please provide category name and currency")
     if 'parent' in self.args:
         parent = self.__own_category(self.args['parent'])
         if not parent:
             self.badrequest("This parent category does not exist")
     else:
         parent = None
     currency = core.Currency.query.filter(
         db.and_(
             core.Currency.isocode == self.args['currency'],
             db.or_(
                 core.Currency.owner_username == self.username,
                 core.Currency.owner_username == None
             )
         )
     ).first()
     if not currency:
         self.badrequest("This currency does not exist")
     category = models.Category(
                     owner_username=self.username,
                     parent=parent,
                     currency=currency,
                     name=self.args['name']
                )
     db.session.add(category)
     db.session.commit()
     return category.as_dict(self.username)
Пример #8
0
 def __own_currency(self, isocode):
     return models.Currency.query.filter(
         db.and_(
             models.Currency.isocode == isocode,
             db.or_(
                 models.Currency.owner_username == self.username,
                 models.Currency.owner_username == None,
             )))
Пример #9
0
 def __own_account(self, accountid):
     return models.Account.query.options(
                     db.joinedload(models.Account.currency)
             ).join(models.AccountOwner).filter(
                 db.and_(
                     models.AccountOwner.owner_username == self.username,
                     models.Account.id == accountid
                 )
             ).first()
Пример #10
0
 def __own_category(self, categoryid):
     return models.Category.query.options(
                     db.joinedload(models.Category.currency)
            ).filter(
                 db.and_(
                     models.Category.owner_username == self.username,
                     models.Category.id == categoryid
                 )
            ).first()
Пример #11
0
 def read(self, username):
     contact = models.UserContact.query.filter(
         db.and_(
             models.UserContact.user_username == self.username,
             models.UserContact.contact_username == self.args['username']
         )
     ).first()
     if not contact:
         self.notfound('This contact does not exist')
     return contact.as_dict()
Пример #12
0
 def __own_currency(self, isocode):
     return models.Currency.query.filter(
         db.and_(
             models.Currency.isocode == isocode,
             db.or_(
                 models.Currency.owner_username == self.username,
                 models.Currency.owner_username == None,
             )
         )
     )
Пример #13
0
def categoriesbalance(username, categoryid):
    category = models.Category.query.filter(
        db.and_(models.Category.owner_username == username,
                models.Category.id == categoryid)).first()
    balances = category.balance(username)
    balances['id'] = categoryid
    result = [balances]
    # Also return parent category/ies balance(s)
    if category.parent_id:
        result.extend(categoriesbalance(username, category.parent_id))
    return result
Пример #14
0
def accountbalance(username, accountid):
    account = models.Account.query.join(models.AccountOwner).filter(
        db.and_(models.AccountOwner.owner_username == username,
                models.Account.id == accountid)).first()
    balances = account.balance(username)
    return {
        'id': accountid,
        'balance': balances[0],
        'balance_preferred': balances[1],
        'transactions_count': account.transactions_count()
    }
Пример #15
0
 def delete(self, username):
     contact = models.UserContact.query.filter(
                 db.and_(
                     models.UserContact.user_username == self.username,
                     models.UserContact.contact_username == username
                 )
     ).first()
     if not contact:
         self.notfound('Nonexistent contact cannot be deleted')
     db.session.delete(contact)
     db.session.commit()
Пример #16
0
 def __own_transaction(self, transactionid):
     return models.Transaction.query.options(
                       db.joinedload(models.Transaction.currency),
                       db.joinedload(models.Transaction.transaction_accounts),
                       db.joinedload(models.Transaction.transaction_categories)
            ).filter(
                 db.and_(
                     models.Transaction.owner_username == self.username,
                     models.Transaction.id == transactionid
                 )
            ).first()
Пример #17
0
 def list(self):
     categories = models.Category.query.order_by(
                     models.Category.name
                  ).options(
                     db.joinedload(models.Category.currency)
                  ).filter(
                     db.and_(
                         models.Category.owner_username == self.username,
                         models.Category.parent_id == None
                     )
                  ).all()
     return [c.as_dict(self.username) for c in categories]
Пример #18
0
def rate(username, fromisocode, toisocode):
    if fromisocode == toisocode:
        return 1
    # Request the currencies
    fromcurrency = core.Currency.query.filter(
        db.and_(
            core.Currency.isocode == fromisocode,
            db.or_(core.Currency.owner_username == username, core.Currency.owner_username == None),
        )
    ).first()
    tocurrency = core.Currency.query.filter(
        db.and_(
            core.Currency.isocode == toisocode,
            db.or_(core.Currency.owner_username == username, core.Currency.owner_username == None),
        )
    ).first()
    if not fromcurrency or not tocurrency:
        return None
    # Both currencies are globally defined
    if (fromcurrency.rate is None) and (tocurrency.rate is None):
        return exchangerate.getrate(fromcurrency.isocode, tocurrency.isocode)
    # Both currencies are user-defined
    elif (fromcurrency.rate is not None) and (tocurrency.rate is not None):
        return tocurrency.rate / fromcurrency.rate
    # Mixed user-defined / globally defined rates
    else:
        preferred_isocode = core.User.query.filter(core.User.username == username).one().preferred_currency.isocode
        # From a user-defined currency to a globally defined currency
        if (fromcurrency.rate is not None) and (tocurrency.rate is None):
            target_rate = exchangerate.getrate(preferred_isocode, tocurrency.isocode)
            if fromcurrency.rate == 0:
                return 0
            return target_rate / fromcurrency.rate
        if (fromcurrency.rate is None) and (tocurrency.rate is not None):
            source_rate = exchangerate.getrate(preferred_isocode, fromcurrency.isocode)
            if tocurrency.rate == 0:
                return 0
            return tocurrency.rate / source_rate
Пример #19
0
def categoriesbalance(username, categoryid):
    category = models.Category.query.filter(
                    db.and_(
                        models.Category.owner_username == username,
                        models.Category.id == categoryid
                    )
              ).first()
    balances = category.balance(username)
    balances['id'] = categoryid
    result = [ balances ]
    # Also return parent category/ies balance(s)
    if category.parent_id:
        result.extend(categoriesbalance(username, category.parent_id))
    return result
Пример #20
0
 def update(self, username):
     contact = models.UserContact.query.filter(
         db.and_(
             models.UserContact.user_username == self.username,
             models.UserContact.contact_username == self.args['username']
         )
     ).first()
     if not contact:
         self.notfound('Nonexistent contact cannot be modified')
     # Only the comment can be updated
     if 'comment' in self.args:
         contact.comment = self.args['comment']
         db.session.commit()
     return contact.as_dict()
Пример #21
0
def accountbalance(username, accountid):
    account = models.Account.query.join(models.AccountOwner).filter(
                    db.and_(
                        models.AccountOwner.owner_username == username,
                        models.Account.id == accountid
                    )
              ).first()
    balances = account.balance(username)
    return {
        'id': accountid,
        'balance': balances[0],
        'balance_preferred': balances[1],
        'transactions_count': account.transactions_count()
    }
Пример #22
0
 def update(self, categoryid):
     category = self.__own_category(categoryid)
     if not category:
         self.notfound(
           'Nonexistent category cannot be modified (or you do not own it)')
     if 'name' in self.args:
         category.name = self.args['name']
     if 'currency' in self.args:
         currency = core.Currency.query.filter(
             db.and_(
                 core.Currency.isocode == self.args['currency'],
                 db.or_(
                     core.Currency.owner_username == self.username,
                     core.Currency.owner_username == None
                 )
             )
         ).first()
         if currency:
             rate = helpers.rate(
                         self.username,
                         category.currency.isocode,
                         currency.isocode
                    )
             category.currency = currency
             for tc in models.TransactionCategory.query.filter(
                         models.TransactionCategory.category == category
                       ).all():
                 tc.category_amount = tc.category_amount * rate
     if 'parent' in self.args:
         if self.args['parent'] == 'NONE':
             category.parent_id = None
         else:
             parent = self.__own_category(self.args['parent'])
             if not parent:
                 self.badrequest("This parent category does not exist")
             if category.contains_category(parent.id):
                 self.badrequest(
                           "The parent is already a child of this category")
             if parent.id != category.parent_id:
                 allparents = set([parent.id, category.parent_id] + \
                                  parent.all_parents_ids())
                 if category.parent_id:
                     allparents.update(category.parent.all_parents_ids())
                 for parentid in allparents:
                     if parentid:
                         self.add_to_response('categoriesbalance', parentid)
                 category.parent = parent
     db.session.commit()
     return category.as_dict(self.username)
Пример #23
0
 def update(self, categoryid):
     category = self.__own_category(categoryid)
     if not category:
         self.notfound(
             'Nonexistent category cannot be modified (or you do not own it)'
         )
     if 'name' in self.args:
         category.name = self.args['name']
     if 'currency' in self.args:
         currency = core.Currency.query.filter(
             db.and_(
                 core.Currency.isocode == self.args['currency'],
                 db.or_(core.Currency.owner_username == self.username,
                        core.Currency.owner_username == None))).first()
         if currency:
             rate = helpers.rate(self.username, category.currency.isocode,
                                 currency.isocode)
             category.currency = currency
             for tc in models.TransactionCategory.query.filter(
                     models.TransactionCategory.category == category).all():
                 tc.category_amount = tc.category_amount * rate
     if 'parent' in self.args:
         if self.args['parent'] == 'NONE':
             category.parent_id = None
         else:
             parent = self.__own_category(self.args['parent'])
             if not parent:
                 self.badrequest("This parent category does not exist")
             if category.contains_category(parent.id):
                 self.badrequest(
                     "The parent is already a child of this category")
             if parent.id != category.parent_id:
                 allparents = set([parent.id, category.parent_id] + \
                                  parent.all_parents_ids())
                 if category.parent_id:
                     allparents.update(category.parent.all_parents_ids())
                 for parentid in allparents:
                     if parentid:
                         self.add_to_response('categoriesbalance', parentid)
                 category.parent = parent
     db.session.commit()
     return category.as_dict(self.username)
Пример #24
0
 def create(self):
     if not ('currency' in self.args and 'name' in self.args):
         self.badrequest("Please provide category name and currency")
     if 'parent' in self.args:
         parent = self.__own_category(self.args['parent'])
         if not parent:
             self.badrequest("This parent category does not exist")
     else:
         parent = None
     currency = core.Currency.query.filter(
         db.and_(
             core.Currency.isocode == self.args['currency'],
             db.or_(core.Currency.owner_username == self.username,
                    core.Currency.owner_username == None))).first()
     if not currency:
         self.badrequest("This currency does not exist")
     category = models.Category(owner_username=self.username,
                                parent=parent,
                                currency=currency,
                                name=self.args['name'])
     db.session.add(category)
     db.session.commit()
     return category.as_dict(self.username)
Пример #25
0
    def balance(self, username):
        today = datetime.date.today()

        balance = cache.get('categorybalance-{0}'.format(self.id))

        if not balance:
            balance = {'currency': self.currency.isocode}
            balance['year'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)
            ).filter(
                db.and_(
                    TransactionCategory.category_id == self.id,
                    TransactionCategory.transaction_id == Transaction.id,
                    Transaction.date.between(
                        datetime.date(today.year, 1, 1),
                        datetime.date(today.year, 12, 31)
                    )
                )
            ).one()[0] or 0

            if today.month == 12:
                lastdayofmonth = datetime.date(today.year, 12, 31)
            else:
                lastdayofmonth = datetime.date(today.year, today.month+1, 1) -\
                                                          datetime.timedelta(1)
            balance['month'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)
            ).filter(
                db.and_(
                    TransactionCategory.category_id == self.id,
                    TransactionCategory.transaction_id == Transaction.id,
                    Transaction.date.between(
                        datetime.date(today.year, today.month, 1),
                        lastdayofmonth
                    )
                )
            ).one()[0] or 0

            firstdayofweek = today - datetime.timedelta(today.weekday())
            lastdayofweek = today + datetime.timedelta(6-today.weekday())
            balance['week'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)
            ).filter(
                db.and_(
                    TransactionCategory.category_id == self.id,
                    TransactionCategory.transaction_id == Transaction.id,
                    Transaction.date.between(
                        firstdayofweek,
                        lastdayofweek
                    )
                )
            ).one()[0] or 0

            balance['7days'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)
            ).filter(
                db.and_(
                    TransactionCategory.category_id == self.id,
                    TransactionCategory.transaction_id == Transaction.id,
                    Transaction.date.between(
                        today - datetime.timedelta(6),
                        today
                    )
                )
            ).one()[0] or 0

            balance['30days'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)
            ).filter(
                db.and_(
                    TransactionCategory.category_id == self.id,
                    TransactionCategory.transaction_id == Transaction.id,
                    Transaction.date.between(
                        today - datetime.timedelta(29),
                        today
                    )
                )
            ).one()[0] or 0

            # Cache the balance only for 5 seconds : it helps when listing
            # multiple categories by reducing sql requests
            cache.set('categorybalance-{0}'.format(self.id), balance, 5)

        for child in self.children:
            child_balance = child.balance(username)
            rate = helpers.rate(username,
                              child_balance['currency'], self.currency.isocode)
            balance['year'] = balance['year'] + child_balance['year'] * rate
            balance['month'] = balance['month'] + child_balance['month'] * rate
            balance['week'] = balance['week'] + child_balance['week'] * rate
            balance['7days'] = balance['7days'] + child_balance['7days'] * rate
            balance['30days'] = balance['30days'] +child_balance['30days']*rate

        return balance
Пример #26
0
    def update(self, transactionid):
        transaction = self.__own_transaction(transactionid)
        if not transaction:
            self.notfound(
           'Nonexistent transaction cannot be modified (or you do not own it)')

        # First, modifications on the Transaction object itself
        if 'description' in self.args:
            desc = self.args['description']
            if desc.strip() == '':
                transaction.description = transaction.original_description
            else:
                transaction.description = desc
        if 'amount' in self.args:
            transaction.amount = self.args['amount']
        if 'currency' in self.args:
            currency = core.Currency.query.filter(
                db.and_(
                    core.Currency.isocode == self.args['currency'],
                    db.or_(
                        core.Currency.owner_username == self.username,
                        core.Currency.owner_username == None
                    )
                )
            ).first()
            if currency:
                transaction.currency = currency
        if 'date' in self.args:
            date = helpers.date_from_string(self.args['date'])
            if date:
                transaction.date = date

        # Next, update accounts
        if 'accounts' in self.args:
            existing_accounts = dict([ta.as_tuple() for ta in
                                      transaction.transaction_accounts])
            new_accounts_data = json.loads(self.args['accounts'])
            for account_data in new_accounts_data:
                if 'amount' in account_data and 'account' in account_data:
                    amount = account_data['amount']
                    accountid = account_data['account']
                    if accountid in existing_accounts.keys():
                        # Account already linked...
                        if existing_accounts[accountid] != amount:
                            # ...but the amount is different
                            ta = models.TransactionAccount.query.filter(db.and_(
                          models.TransactionAccount.transaction == transaction,
                          models.TransactionAccount.account_id == accountid
                            )).one()
                            ta.amount = amount
                            ta.verified = False
                            self.add_to_response('accountbalance', accountid)
                        existing_accounts.pop(accountid)
                    else:
                        # Account is not already linked
                        # Verify the account is owned by the user
                        accountobject = models.Account.query.options(
                            db.joinedload(models.Account.account_owners)
                        ).filter(
                          db.and_(
                            models.Account.id == accountid,
                            models.AccountOwner.owner_username == self.username
                         )
                        ).first()
                        if accountobject:
                            ta = models.TransactionAccount(
                                    transaction = transaction,
                                    account = accountobject,
                                    amount = amount,
                                    verified = False
                            )
                            self.add_to_response('accountbalance', accountid)
                            db.session.add(ta)
            # All accounts to keep have been poped out from "existing_accounts"
            # Delete all links remaining from this transaction to accounts
            for accountid in existing_accounts.keys():
                ta = models.TransactionAccount.query.filter(db.and_(
                    models.TransactionAccount.transaction == transaction,
                    models.TransactionAccount.account_id == accountid
                )).one()
                self.add_to_response('accountbalance', accountid)
                db.session.delete(ta)

            self.add_to_response('totalbalance')

        # Then, update categories
        if 'categories' in self.args:
            existing_categories = dict([tc.as_tuple() for tc in
                                        transaction.transaction_categories])
            new_categories_data = json.loads(self.args['categories'])
            for category_data in new_categories_data:
                if 'transaction_amount' in category_data and \
                   'category_amount' in category_data and \
                   'category' in category_data:
                    transaction_amount = category_data['transaction_amount']
                    category_amount = category_data['category_amount']
                    categoryid = category_data['category']
                    if categoryid in existing_categories.keys():
                        # Category already linked...
                        if existing_categories[categoryid]['category_amount'] \
                           != category_amount:
                            # ...but the amount is different
                            tc = models.TransactionCategory.query.filter(db.and_(
                         models.TransactionCategory.transaction == transaction,
                         models.TransactionCategory.category_id == categoryid
                            )).one()
                            tc.transaction_amount = transaction_amount
                            tc.category_amount = category_amount
                            tc.verified = False
                            self.add_to_response('categoriesbalance',
                                                 categoryid)
                        existing_categories.pop(categoryid)
                    else:
                        # Category is not already linked
                        # Verify the category is owned by the user
                        categoryobject = models.Category.query.filter(
                          db.and_(
                           models.Category.id == categoryid,
                           models.Category.owner_username == self.username
                         )
                        ).first()
                        if categoryobject:
                            tc = models.TransactionCategory(
                                    transaction = transaction,
                                    category = categoryobject,
                                    transaction_amount = transaction_amount,
                                    category_amount = category_amount
                            )
                            self.add_to_response('categoriesbalance',
                                                 categoryid)
                            db.session.add(tc)
            # All categories to keep have been poped out from
            # "existing_categories"
            # Delete all links remaining from this transaction to categories
            for categoryid in existing_categories.keys():
                tc = models.TransactionCategory.query.filter(db.and_(
                    models.TransactionCategory.transaction == transaction,
                    models.TransactionCategory.category_id == categoryid
                )).one()
                self.add_to_response('categoriesbalance', categoryid)
                db.session.delete(tc)

        db.session.commit()
        return transaction.as_dict(self.username)
Пример #27
0
 def __filter(self, filter):
     filters = [
         models.Transaction.owner_username == self.username,
     ]
     limit = 100
     after = False
     for part in filter.items():
         if part[0] in filter_functions:
             filters.extend(
                 filter_functions[part[0]](part[1])
             )
         elif part[0] == 'limit':
             try:
                 limit = min(int(part[1]), 100)
             except:
                 pass
         elif part[0] == 'after':
             try:
                 after = int(part[1])
             except:
                 pass
     if after:
         # Get offset of the "from" transaction
         # XXX: Is there a more efficient way to do so ?
         all_transactions = db.session.query(models.Transaction.id).order_by(
                             db.desc(models.Transaction.date)
                        ).filter(
                             db.and_(
                                 *filters
                             )
                        )
         all_ids = [t.id for t in all_transactions]
         try:
             offset = all_ids.index(after) + 1
         except:
             offset = 0
         transactions = models.Transaction.query.options(
                     db.joinedload(models.Transaction.currency),
                     db.joinedload(models.Transaction.transaction_accounts),
                     db.joinedload(models.Transaction.transaction_categories)
                 ).order_by(
                     db.desc(models.Transaction.date)
                 ).filter(
                     db.and_(
                         *filters
                     )
                 ).offset(offset).limit(limit)
         return [t.as_dict(self.username) for t in transactions]
     else:
         transactions = models.Transaction.query.options(
                     db.joinedload(models.Transaction.currency),
                     db.joinedload(models.Transaction.transaction_accounts),
                     db.joinedload(models.Transaction.transaction_categories)
                 ).order_by(
                     db.desc(models.Transaction.date)
                 ).filter(
                     db.and_(
                         *filters
                     )
                 ).limit(limit)
         return [t.as_dict(self.username) for t in transactions]
Пример #28
0
    def update(self, username):
        if username == 'me' or username == self.username:
            # A user can only modify his own information
            user = models.User.query.options(
                db.joinedload(models.User.emails)
            ).filter(
                models.User.username == self.username
            ).one()
            if 'first_name' in self.args:
                user.first_name = self.args['first_name']
            if 'last_name' in self.args:
                user.last_name = self.args['last_name']
            if 'password' in self.args and \
               username not in config.DEMO_ACCOUNTS:
                if 'currentpassword' in self.args and \
                           authentication.authenticate(
                                username,
                                self.args['currentpassword'],
                                False
                           ):
                    if len(self.args['password']) < 8:
                        self.badrequest(
                               'Password should be at least 8 characters long')
                    user.passhash = sha512_crypt.encrypt(
                                        self.args['password'],
                                        rounds=config.PASSWORD_SALT_COMPLEXITY
                                    )
                else:
                    self.badrequest(
                                 "Please provide the correct current password")
            if 'preferred_currency' in self.args:
                currency = models.Currency.query.filter(
                  db.and_(
                     models.Currency.isocode == self.args['preferred_currency'],
                     models.Currency.owner_username == None,
                  )
                ).first()
                if currency:
                    # When preferred currency is changed, all owner's
                    # currencies rates must be changed
                    # XXX Debts amounts should also be changed... when debts will be implemented
                    multiplier = user.preferred_currency
                    multiplier = exchangerate.getrate(
                        user.preferred_currency.isocode,
                        currency.isocode
                    )
                    for c in models.Currency.query.filter(
                        models.Currency.owner_username == self.username
                    ):
                        c.rate = c.rate * multiplier
                    user.preferred_currency = currency
                    self.add_to_response('totalbalance')
            if 'emails' in self.args:
                emails = json.loads(self.args['emails'])
                previous_emails = []
                previous_notifications = []
                for address in models.UserEmail.query.filter(
                              models.UserEmail.user_username == self.username):
                    previous_emails.append(address.email_address)
                    if address.notification:
                        previous_notifications.append(address.email_address)
                if type(emails) == type({}):
                    if 'add' in emails and \
                       type(emails['add']) == type([]) and \
                       username not in config.DEMO_ACCOUNTS:
                        for address in emails['add']:
                            # TODO Verify there is a "@" in the email address
                            if address not in previous_emails:
                                # Use random hash for email confirmation
                                # Email confirmation is done outside of OSPFM
                                # Another process must read the database and
                                # send confirmation emails
                                randomhash = os.urandom(8).encode('hex')
                                db.session.add(
                                    models.UserEmail(
                                        user_username = self.username,
                                        email_address = address,
                                        confirmation = randomhash
                                    )
                                )
                    if 'remove' in emails and type(emails['remove'])==type([]):
                        for address in emails['remove']:
                            if address in previous_emails:
                                db.session.delete(
                                    models.UserEmail.query.filter(
                                        db.and_(
                               models.UserEmail.user_username == self.username,
                               models.UserEmail.email_address == address
                                        )
                                    ).first()
                                )
                    if 'enablenotifications' in emails and \
                       type(emails['enablenotifications']) == type([]):
                        for address in emails['enablenotifications']:
                            if address not in previous_notifications:
                                models.UserEmail.query.filter(
                                    db.and_(
                               models.UserEmail.user_username == self.username,
                               models.UserEmail.email_address == address
                                    )
                                ).first().notification = True
                    if 'disablenotifications' in emails and \
                       type(emails['disablenotifications']) == type([]):
                        for address in emails['disablenotifications']:
                            if address in previous_notifications:
                                models.UserEmail.query.filter(
                                    db.and_(
                               models.UserEmail.user_username == self.username,
                               models.UserEmail.email_address == address
                                    )
                                ).first().notification = False


            db.session.commit()
            return self.read(username)
        else:
            self.forbidden('The only user you can modify is yourself')
Пример #29
0
    def create(self):
        if not (
            'currency' in self.args and \
            'date' in self.args and \
            'description' in self.args and \
            'amount' in self.args
        ):
            self.badrequest(
           "Please provide transaction description, currency, amount and date")
        # First, create the transaction object
        currency = core.Currency.query.filter(
            db.and_(
                core.Currency.isocode == self.args['currency'],
                db.or_(
                    core.Currency.owner_username == self.username,
                    core.Currency.owner_username == None
                )
            )
        ).first()
        if not currency:
            self.badrequest("This currency does not exist")
        date = helpers.date_from_string(self.args['date'])
        if not date:
            self.badrequest("This date cannot be understood")
        description = self.args['description']
        if 'original_description' in self.args:
            original_description = self.args['original_description']
        else:
            original_description = description
        transaction = models.Transaction(
            owner_username = self.username,
            description = description,
            original_description = original_description,
            amount = self.args['amount'],
            currency = currency,
            date = date
        )
        db.session.add(transaction)

        # Next, create the links from the transaction to its accounts
        if 'accounts' in self.args:
            accounts = json.loads(self.args['accounts'])
            for accountdata in accounts:
                transaction_accounts = []
                if 'amount' in accountdata:
                    # If no amount is specified, do not associate the account
                    accountobject = models.Account.query.options(
                        db.joinedload(models.Account.account_owners)
                    ).filter(
                        db.and_(
                           models.Account.id == accountdata['account'],
                           models.AccountOwner.owner_username == self.username,
                        )
                    ).first()
                    if accountobject:
                        ta = models.TransactionAccount(
                                transaction = transaction,
                                account = accountobject,
                                amount = accountdata['amount'],
                                verified = False
                        )
                        db.session.add(ta)
                    self.add_to_response('accountbalance',
                                         accountdata['account'])
            self.add_to_response('totalbalance')

        # Next, create the links from the transaction to its categories
        if 'categories' in self.args:
            categories = json.loads(self.args['categories'])
            for categorydata in categories:
                transaction_categories = []
                if 'transaction_amount' in categorydata and \
                   'category_amount' in categorydata:
                    # If no amount is specified, do not associate the category
                    categoryobject = models.Category.query.options(
                        db.joinedload(models.Category.currency)
                    ).filter(
                        db.and_(
                            models.Category.id == categorydata['category'],
                            models.Category.owner_username == self.username,
                        )
                    ).first()
                    if categoryobject:
                        tc = models.TransactionCategory(
                                transaction = transaction,
                                category = categoryobject,
                       transaction_amount = categorydata['transaction_amount'],
                              category_amount = categorydata['category_amount']
                        )
                        db.session.add(tc)
                    self.add_to_response('categoriesbalance',
                                         categorydata['category'])

        # Commit everything...
        db.session.commit()
        return transaction.as_dict(self.username)
Пример #30
0
def create(username, wizard, locale, prefcurrency):
    """Create entries from wizard files"""

    # /!\ Does not work in Python < 2.7
    #
    # (sections order is important)

    ########## Initialization
    data = ConfigParser.RawConfigParser()
    if wizard in ('basic', 'demo'):
        try:
            datafile = codecs.open(
                            os.path.join(config.WIZARD_DATA,'%s.basic'%locale),
                            'r', 'utf8'
                        )
            data.readfp(datafile)
            datafile.close()
        except:
            abort(400)
    if wizard == 'demo':
        try:
            datafile = codecs.open(
                            os.path.join(config.WIZARD_DATA, '%s.demo'%locale),
                            'r', 'utf8'
                        )
            data.readfp(datafile)
            datafile.close()
        except:
            abort(400)
    sections = data.sections()
    if not sections:
        return 200, 'OK'
    try:
        preferred_currency = core.Currency.query.filter(
                                    db.and_(
                                        core.Currency.isocode == prefcurrency,
                                        core.Currency.owner_username == None
                                    )
                                ).one()
    except:
        abort(400)

    today = datetime.date.today()

    ########## Helper functions
    def subsections(name):
        return [ i for i in sections if i.startswith(name) ]

    ########## Currency
    currencies = { prefcurrency: preferred_currency }
    for cur in subsections('currency-'):
        symbol = data.get(cur, 'symbol')
        currency = core.Currency(
                        owner_username = username,
                        isocode = symbol,
                        symbol = symbol,
                        name = data.get(cur, 'name'),
                        rate = data.get(cur, 'rate')
                    )
        db.session.add(currency)
        currencies[symbol] = currency
    db.session.commit()

    ########## Account
    accounts = {}
    for acc in subsections('account-'):
        if data.has_option(acc, 'currency'):
            curname = data.get(acc, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(
                        core.Currency.owner_username == None,
                        core.Currency.isocode == curname
                    )
                ).one()
        else:
            curname = prefcurrency
        account = transaction.Account(
            name = data.get(acc, 'name'),
            currency = currencies[curname],
            start_balance = data.get(acc, 'balance')
        )
        db.session.add(account)
        accounts[acc.split('-')[1]] = account
        db.session.add(
            transaction.AccountOwner(
                account = account,
                owner_username = username
            )
        )
    db.session.commit()

    ########## Category
    categories = {}
    for cat in subsections('category-'):
        if data.has_option(cat, 'currency'):
            curname = data.get(cat, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(
                        core.Currency.owner_username == None,
                        core.Currency.isocode == curname
                    )
                ).one()
        else:
            curname = prefcurrency
        category = transaction.Category(
            owner_username = username,
            currency = currencies[curname],
            name = data.get(cat, 'name')
        )
        try:
            category.parent = categories[data.get(cat, 'parent')]
        except:
            pass
        db.session.add(category)
        categories[cat.split('-')[1]] = category
    db.session.commit()

    ########## Transaction
    for tra in subsections('transaction-'):
        if data.has_option(tra, 'currency'):
            curname = data.get(tra, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(
                        core.Currency.owner_username == None,
                        core.Currency.isocode == curname
                    )
                ).one()
        else:
            curname = prefcurrency
        currency = currencies[curname]
        # Calculate date
        year, month, day = data.get(tra, 'date').split('/')
        # Month
        if month:
            if month[0] in ('-', '+'):
                month = today.month + int(month)
            else:
                month = int(month)
        else:
            month = today.month
        # Year
        if year:
            if year[0] in ('-', '+'):
                year = today.year + int(year)
            elif year == '?':
                # If year = "?", point to the last passed year containing the
                # defined month (either current year or previous year)
                if month < today.month:
                    year = today.year
                else:
                    year = today.year - 1
            else:
                year = int(year)
        else:
            year = today.year
        # Check the month is not outside bounds, or correct the year
        def month_outside_bounds(month, year):
            if month < 1:
                month = month + 12
                year = year - 1
                month, year = month_outside_bounds(month, year)
            elif month > 12:
                month = month - 12
                year = year + 1
                month, year = month_outside_bounds(month, year)
            return month, year
        month, year = month_outside_bounds(month, year)
        # Day
        if day:
            if day[0] in ('-', '+'):
                daydeltafromfirst = today.day + int(day) - 1
                day = 1
            else:
                daydeltafromfirst = int(day) - 1
                day = 1
        else:
            day = today.day
            daydeltafromfirst = 0
        transactiondate = datetime.date(year,month,day)
        if daydeltafromfirst:
            transactiondate = transactiondate + \
                              datetime.timedelta(days=daydeltafromfirst)
        # Create transaction
        trans = transaction.Transaction(
            owner_username = username,
            description = data.get(tra, 'description'),
            amount = data.get(tra, 'amount'),
            currency = currency,
            date = transactiondate
        )
        try:
            trans.original_description = data.get(tra, 'original_description')
        except:
            trans.original_description = data.get(tra, 'description')
        db.session.add(trans)
        # Links to accounts
        for accountdata in data.get(tra, 'accounts').split():
            accountdatatb = accountdata.split(':')
            accountnum = accountdatatb[0]
            if len(accountdatatb) > 1:
                amount = accountdatatb[1]
            else:
                amount = trans.amount
            account = accounts[accountnum]
            accountamount = amount * helpers.rate(
                                            username,
                                            currency.isocode,
                                            account.currency.isocode
                                        )
            db.session.add(
                transaction.TransactionAccount(
                    transaction = trans,
                    account = account,
                    amount = accountamount
                )
            )
        # Links to categories
        for categorydata in data.get(tra, 'categories').split():
            categorydatatb = categorydata.split(':')
            categorynum = categorydatatb[0]
            if len(categorydatatb) > 1:
                amount = categorydatatb[1]
            else:
                amount = trans.amount
            category = categories[categorynum]
            categoryamount = amount * helpers.rate(
                                            username,
                                            currency.isocode,
                                            category.currency.isocode
                                        )
            db.session.add(
                transaction.TransactionCategory(
                    transaction = trans,
                    category = category,
                    transaction_amount = amount,
                    category_amount = categoryamount
                )
            )
    db.session.commit()

    ########## OK, finished
    return 200, 'OK'
Пример #31
0
 def __own_category(self, categoryid):
     return models.Category.query.options(
         db.joinedload(models.Category.currency)).filter(
             db.and_(models.Category.owner_username == self.username,
                     models.Category.id == categoryid)).first()
Пример #32
0
def create(username, wizard, locale, prefcurrency):
    """Create entries from wizard files"""

    # /!\ Does not work in Python < 2.7
    #
    # (sections order is important)

    ########## Initialization
    data = ConfigParser.RawConfigParser()
    if wizard in ('basic', 'demo'):
        try:
            datafile = codecs.open(
                os.path.join(config.WIZARD_DATA, '%s.basic' % locale), 'r',
                'utf8')
            data.readfp(datafile)
            datafile.close()
        except:
            abort(400)
    if wizard == 'demo':
        try:
            datafile = codecs.open(
                os.path.join(config.WIZARD_DATA, '%s.demo' % locale), 'r',
                'utf8')
            data.readfp(datafile)
            datafile.close()
        except:
            abort(400)
    sections = data.sections()
    if not sections:
        return 200, 'OK'
    try:
        preferred_currency = core.Currency.query.filter(
            db.and_(core.Currency.isocode == prefcurrency,
                    core.Currency.owner_username == None)).one()
    except:
        abort(400)

    today = datetime.date.today()

    ########## Helper functions
    def subsections(name):
        return [i for i in sections if i.startswith(name)]

    ########## Currency
    currencies = {prefcurrency: preferred_currency}
    for cur in subsections('currency-'):
        symbol = data.get(cur, 'symbol')
        currency = core.Currency(owner_username=username,
                                 isocode=symbol,
                                 symbol=symbol,
                                 name=data.get(cur, 'name'),
                                 rate=data.get(cur, 'rate'))
        db.session.add(currency)
        currencies[symbol] = currency
    db.session.commit()

    ########## Account
    accounts = {}
    for acc in subsections('account-'):
        if data.has_option(acc, 'currency'):
            curname = data.get(acc, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(core.Currency.owner_username == None,
                            core.Currency.isocode == curname)).one()
        else:
            curname = prefcurrency
        account = transaction.Account(name=data.get(acc, 'name'),
                                      currency=currencies[curname],
                                      start_balance=data.get(acc, 'balance'))
        db.session.add(account)
        accounts[acc.split('-')[1]] = account
        db.session.add(
            transaction.AccountOwner(account=account, owner_username=username))
    db.session.commit()

    ########## Category
    categories = {}
    for cat in subsections('category-'):
        if data.has_option(cat, 'currency'):
            curname = data.get(cat, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(core.Currency.owner_username == None,
                            core.Currency.isocode == curname)).one()
        else:
            curname = prefcurrency
        category = transaction.Category(owner_username=username,
                                        currency=currencies[curname],
                                        name=data.get(cat, 'name'))
        try:
            category.parent = categories[data.get(cat, 'parent')]
        except:
            pass
        db.session.add(category)
        categories[cat.split('-')[1]] = category
    db.session.commit()

    ########## Transaction
    for tra in subsections('transaction-'):
        if data.has_option(tra, 'currency'):
            curname = data.get(tra, 'currency')
            if curname not in currencies:
                currencies[curname] = core.Currency.query.filter(
                    db.and_(core.Currency.owner_username == None,
                            core.Currency.isocode == curname)).one()
        else:
            curname = prefcurrency
        currency = currencies[curname]
        # Calculate date
        year, month, day = data.get(tra, 'date').split('/')
        # Month
        if month:
            if month[0] in ('-', '+'):
                month = today.month + int(month)
            else:
                month = int(month)
        else:
            month = today.month
        # Year
        if year:
            if year[0] in ('-', '+'):
                year = today.year + int(year)
            elif year == '?':
                # If year = "?", point to the last passed year containing the
                # defined month (either current year or previous year)
                if month < today.month:
                    year = today.year
                else:
                    year = today.year - 1
            else:
                year = int(year)
        else:
            year = today.year
        # Check the month is not outside bounds, or correct the year
        def month_outside_bounds(month, year):
            if month < 1:
                month = month + 12
                year = year - 1
                month, year = month_outside_bounds(month, year)
            elif month > 12:
                month = month - 12
                year = year + 1
                month, year = month_outside_bounds(month, year)
            return month, year

        month, year = month_outside_bounds(month, year)
        # Day
        if day:
            if day[0] in ('-', '+'):
                daydeltafromfirst = today.day + int(day) - 1
                day = 1
            else:
                daydeltafromfirst = int(day) - 1
                day = 1
        else:
            day = today.day
            daydeltafromfirst = 0
        transactiondate = datetime.date(year, month, day)
        if daydeltafromfirst:
            transactiondate = transactiondate + \
                              datetime.timedelta(days=daydeltafromfirst)
        # Create transaction
        trans = transaction.Transaction(owner_username=username,
                                        description=data.get(
                                            tra, 'description'),
                                        amount=data.get(tra, 'amount'),
                                        currency=currency,
                                        date=transactiondate)
        try:
            trans.original_description = data.get(tra, 'original_description')
        except:
            trans.original_description = data.get(tra, 'description')
        db.session.add(trans)
        # Links to accounts
        for accountdata in data.get(tra, 'accounts').split():
            accountdatatb = accountdata.split(':')
            accountnum = accountdatatb[0]
            if len(accountdatatb) > 1:
                amount = accountdatatb[1]
            else:
                amount = trans.amount
            account = accounts[accountnum]
            accountamount = amount * helpers.rate(username, currency.isocode,
                                                  account.currency.isocode)
            db.session.add(
                transaction.TransactionAccount(transaction=trans,
                                               account=account,
                                               amount=accountamount))
        # Links to categories
        for categorydata in data.get(tra, 'categories').split():
            categorydatatb = categorydata.split(':')
            categorynum = categorydatatb[0]
            if len(categorydatatb) > 1:
                amount = categorydatatb[1]
            else:
                amount = trans.amount
            category = categories[categorynum]
            categoryamount = amount * helpers.rate(username, currency.isocode,
                                                   category.currency.isocode)
            db.session.add(
                transaction.TransactionCategory(
                    transaction=trans,
                    category=category,
                    transaction_amount=amount,
                    category_amount=categoryamount))
    db.session.commit()

    ########## OK, finished
    return 200, 'OK'
Пример #33
0
    def balance(self, username):
        today = datetime.date.today()

        balance = cache.get('categorybalance-{0}'.format(self.id))

        if not balance:
            balance = {'currency': self.currency.isocode}
            balance['year'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)).filter(
                    db.and_(
                        TransactionCategory.category_id == self.id,
                        TransactionCategory.transaction_id == Transaction.id,
                        Transaction.date.between(
                            datetime.date(today.year, 1, 1),
                            datetime.date(today.year, 12, 31)))).one()[0] or 0

            if today.month == 12:
                lastdayofmonth = datetime.date(today.year, 12, 31)
            else:
                lastdayofmonth = datetime.date(today.year, today.month+1, 1) -\
                                                          datetime.timedelta(1)
            balance['month'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)).filter(
                    db.and_(
                        TransactionCategory.category_id == self.id,
                        TransactionCategory.transaction_id == Transaction.id,
                        Transaction.date.between(
                            datetime.date(today.year, today.month, 1),
                            lastdayofmonth))).one()[0] or 0

            firstdayofweek = today - datetime.timedelta(today.weekday())
            lastdayofweek = today + datetime.timedelta(6 - today.weekday())
            balance['week'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)).filter(
                    db.and_(
                        TransactionCategory.category_id == self.id,
                        TransactionCategory.transaction_id == Transaction.id,
                        Transaction.date.between(firstdayofweek,
                                                 lastdayofweek))).one()[0] or 0

            balance['7days'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)).filter(
                    db.and_(
                        TransactionCategory.category_id == self.id,
                        TransactionCategory.transaction_id == Transaction.id,
                        Transaction.date.between(today - datetime.timedelta(6),
                                                 today))).one()[0] or 0

            balance['30days'] = db.session.query(
                db.func.sum(TransactionCategory.category_amount)).filter(
                    db.and_(
                        TransactionCategory.category_id == self.id,
                        TransactionCategory.transaction_id == Transaction.id,
                        Transaction.date.between(
                            today - datetime.timedelta(29),
                            today))).one()[0] or 0

            # Cache the balance only for 5 seconds : it helps when listing
            # multiple categories by reducing sql requests
            cache.set('categorybalance-{0}'.format(self.id), balance, 5)

        for child in self.children:
            child_balance = child.balance(username)
            rate = helpers.rate(username, child_balance['currency'],
                                self.currency.isocode)
            balance['year'] = balance['year'] + child_balance['year'] * rate
            balance['month'] = balance['month'] + child_balance['month'] * rate
            balance['week'] = balance['week'] + child_balance['week'] * rate
            balance['7days'] = balance['7days'] + child_balance['7days'] * rate
            balance[
                '30days'] = balance['30days'] + child_balance['30days'] * rate

        return balance