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()
def balance(self, username): """ Return a list : [ <balance in account currency>, <balance in preferred currency> ] """ balance = db.session.query( db.func.sum(TransactionAccount.amount) ).filter( TransactionAccount.account_id == self.id ).one()[0] if balance: balances = [ self.start_balance + balance ] else: balances = [ self.start_balance ] balances.append( helpers.rate( username, self.currency.isocode, coremodels.User.query.options( db.joinedload(coremodels.User.preferred_currency) ).get( username ).preferred_currency.isocode ) * balances[0] ) return balances
def list(self): contacts = models.UserContact.query.options( db.joinedload(models.UserContact.contact) ).filter( models.UserContact.user_username == self.username ) return [c.as_dict() for c in contacts.all()]
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]
def totalbalance(username): accounts = models.Account.query.options( db.joinedload(models.Account.currency)).join( models.AccountOwner).filter( models.AccountOwner.owner_username == username).all() # Calculate the total balance, in the user's preferred currency totalbalance = 0 totalcurrency = core.User.query.options( db.joinedload( core.User.preferred_currency)).get(username).preferred_currency for account in accounts: totalbalance += account.balance(username)[0] * \ helpers.rate(username, account.currency.isocode, totalcurrency.isocode) return {'balance': totalbalance, 'currency': totalcurrency.isocode}
def read(self, username): if username == 'me' or username == self.username: # When requesting his own information, a user gets more details user = models.User.query.options( db.joinedload(models.User.emails), db.joinedload(models.User.preferred_currency) ).filter( models.User.username == self.username ).one() return user.as_dict(own=True) else: user = models.User.query.filter( models.User.username == username ).first() if not user: self.notfound('This user does not exist') return user.as_dict()
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()
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()
def totalbalance(username): accounts = models.Account.query.options( db.joinedload(models.Account.currency) ).join(models.AccountOwner).filter( models.AccountOwner.owner_username == username ).all() # Calculate the total balance, in the user's preferred currency totalbalance = 0 totalcurrency = core.User.query.options( db.joinedload(core.User.preferred_currency) ).get(username).preferred_currency for account in accounts: totalbalance += account.balance(username)[0] * \ helpers.rate(username, account.currency.isocode, totalcurrency.isocode) return { 'balance': totalbalance, 'currency': totalcurrency.isocode }
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]
def balance(self, username): """ Return a list : [ <balance in account currency>, <balance in preferred currency> ] """ balance = db.session.query(db.func.sum( TransactionAccount.amount)).filter( TransactionAccount.account_id == self.id).one()[0] if balance: balances = [self.start_balance + balance] else: balances = [self.start_balance] balances.append( helpers.rate( username, self.currency.isocode, coremodels.User.query.options( db.joinedload(coremodels.User.preferred_currency)).get( username).preferred_currency.isocode) * balances[0]) return balances
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')
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()
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)
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]
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)