def change_password(self, request): """ Change the password from an old password Request: POST user/change_password/ { "old_password": str, "new_password": str } Response: Same as GET user/{id}/ """ if request.method != 'PUT': raise BinderMethodNotAllowed() self._require_model_perm('change_own_password', request) decoded = request.body.decode() try: body = json.loads(decoded) except ValueError: raise BinderRequestError( _('Invalid request body: not a JSON document.')) user = request.user errors = {} for item in ['old_password', 'new_password']: if body.get(item) is None: errors[item] = ['missing'] if not user.check_password(body.get('old_password')): errors['old_password'] = ['incorrect'] if len(errors) != 0: raise BinderValidationError(errors) password = body.get('new_password') try: password_validation.validate_password(password, user) except ValidationError as ve: validation_errors = {'new_password': ve.messages} raise BinderValidationError(validation_errors) user.set_password(password) user.save() logger.info('password changed for {}/{}'.format(user.id, user)) if user == request.user: """ No need to change the password of an user that is not our own """ update_session_auth_hash(request, user) return self.respond_with_user(request, user.id)
def _binder_unset_relation_caretaker(self, request): raise BinderValidationError({ 'animal': { self.pk: { 'caretaker': [{ 'code': 'cant_unset', 'message': 'You can\'t unset zoo.', }] } } })
def activate(self, request, pk=None): """ Adds an endpoint to activate an user. Also logs in the user Request: PUT user/{id}/activate/ { "activation_code": string } Response: Same as GET user/{id}/ """ if request.method != 'PUT': raise BinderMethodNotAllowed() self._require_model_perm('activate', request) decoded = request.body.decode() try: body = json.loads(decoded) except ValueError: raise BinderRequestError( _('Invalid request body: not a JSON document.')) errors = {} for item in ['activation_code']: if body.get(item) is None: errors[item] = ['missing'] if len(errors) != 0: raise BinderValidationError(errors) try: user = self.model._default_manager.get(pk=pk) except (TypeError, ValueError, OverflowError, self.model.DoesNotExist): user = None if user is None or not self.token_generator.check_token( user, body.get('activation_code')): raise BinderNotFound() logger.info('login for {}/{} via successful activation'.format( user.id, user)) user.is_active = True user.save() self.auth_login(request, user) return self.respond_with_user(request, user.id)
def chart(self, request): tx_qs = TransactionView().get_queryset(request=request) start_date = request.GET.get('start_date', None) end_date = request.GET.get('end_date', None) if not start_date or not end_date: raise BinderValidationError('start_date and end_date are required') txs = tx_qs.filter(date__gte=start_date, date__lte=end_date).order_by('date') # Find the initial balance starting_balance = Balance.get_at_date(start_date, request.user) working_balance = 0 if starting_balance is not None: working_balance = starting_balance start_date = datetime.strptime(start_date, '%Y-%m-%d') end_date = datetime.strptime(end_date, '%Y-%m-%d') date_pointer = start_date days_per_bin = 1 bins = [] while date_pointer < end_date: bins.append( (date_pointer.strftime('%Y-%m-%d'), working_balance)) # A chart bin is a (date, balance) tuple # increment the working balance for every transaction in that bin next_date = date_pointer + timedelta(days_per_bin) upper_bound = min(end_date, next_date) # Don't overstep the end_date bound balance_change = 0 txs = tx_qs.filter(date__gte=date_pointer, date__lt=upper_bound).order_by('date') if len(txs): balance_change = txs.aggregate(result=Sum('amount'))['result'] working_balance += balance_change date_pointer = next_date # Add the final bin, this probably isn't necessary if the iteration is better bins.append((date_pointer.strftime('%Y-%m-%d'), working_balance)) return JsonResponse({'data': bins})
def rotate(self, request): body = jsonloads(request.body) form = RotateForm(body) if not form.is_valid(): raise BinderValidationError(form.errors) angle = body['angle'] for s in self._get_images(body, request): file = self._get_file(s) src_im = Image.open(file) rotated_img = src_im.rotate(angle, expand=1) rotated_img.save(file.file.name, overwrite=True) file.close() return JsonResponse([])
def scrape(self, request): # With this new import, the pending transaction should be removed Transaction.objects.filter(type="PENDING").delete() last_import = DataImport.objects.order_by( '-last_transaction_date').first() if last_import: start_date = last_import.last_transaction_date else: start_date = datetime.date.today() - datetime.timedelta(90) end_date = datetime.date.today() params = { "startDate": start_date.strftime('%Y-%m-%d'), "endDate": end_date.strftime('%Y-%m-%d') } r = requests.post(os.environ.get('SCRAPER_URL'), json=params) if r.status_code == 400: raise BinderValidationError( 'Error encountered during scraping: {}'.format(r.text)) parsed_balance = int(r.headers["X-Account-Budget"].replace('.', '')) i = DataImport(file_path="", user=request.user) i.save() import_range = self.get_import_range(request.user) csv_contents = r.text.replace('\r', '') i.parse(r.text.split('\n'), import_range, request.user) i.calculate_metrics() # Rerun the queries on the newly imported data # so the new transactions get the correct category labels Query.run_all(request.user) # Set balance to the new balance b = Balance(user=request.user, after_import=i, amount=parsed_balance) b.save() return self.get(request, pk=i.id)
def _get_images(self, body, request): ''' Get all the scans defined in the body's ids paramater, or retruns a validation error if one of the objects does not exist ''' ids = body['ids'] scans = [] for i in ids: try: scans.append(self.model.objects.get(pk=i)) except ObjectDoesNotExist: raise BinderValidationError( {'ids': ['ImageObject with id {} not found'.format(i)]}) if isinstance(self, PermissionView): self.scope_change_list(request, scans, {}) return scans
def crop(self, request): body = jsonloads(request.body) form = CropForm(body) if not form.is_valid(): raise BinderValidationError(form.errors) x_1 = body['x_1'] x_2 = body['x_2'] y_1 = body['y_1'] y_2 = body['y_2'] for s in self._get_images(body, request): file = self._get_file(s) src_im = Image.open(file) rotated_img = src_im.crop((x_1, y_1, x_2, y_2)) rotated_img.save(file.file.name, overwrite=True) file.close() return JsonResponse([])
def _reset_pass_for_user(self, request, user_id, token, password): """ Helper function that actually resets the password for an user """ try: user = self.model._default_manager.get(pk=user_id) except (TypeError, ValueError, OverflowError, self.model.DoesNotExist): user = None if user is None or not self.token_generator.check_token(user, token): raise BinderNotFound() logger.info('login for {}/{} via successful password reset'.format( user.id, user)) try: password_validation.validate_password(password, user) except ValidationError as ve: raise BinderValidationError({'password': ve.messages}) user.set_password(password) user.save() self.auth_login(request, user) return self.respond_with_user(request, user.id)
def reset_password(self, request, pk=None): """ Resets the password from an reset code Request: POST user/reset_password/ { "reset_code": str, "password": str } Response: Same as GET user/{id}/ """ self._require_model_perm('reset_password', request) decoded = request.body.decode() try: body = json.loads(decoded) except ValueError: raise BinderRequestError( _('Invalid request body: not a JSON document.')) errors = { item: 'missing' for item in ['reset_code', 'password'] if item not in body } if errors: raise BinderValidationError(errors) return self._reset_pass_for_user(request, int(pk), body['reset_code'], body['password'])
def chart(self, request): tx_qs = TransactionView().get_queryset(request=request) c_saving = Category.objects.filter(name='Saving', user=request.user).first() c_work = Category.objects.filter(name='Work', user=request.user).first() print(c_work.id) BUCKET_INCOME = 'Income' BUCKET_SAVING = 'Saving' BUCKET_SPENT = 'Total_spent', # find income from last month # find saving from this month budgets = Budget.objects.filter(user=request.user).all() cat_to_budget_mapping = {} spent_per_category = {} output = { None: { 'name': 'Uncategorised', 'total': 0, 'current': 0, 'count': 0 }, BUCKET_SAVING: { 'name': 'Saving', 'total': -1, 'current': 0, 'count': 0 }, BUCKET_INCOME: { 'name': 'Income', 'total': -1, 'current': 0, 'count': 0 }, BUCKET_SPENT: { 'name': 'Total spent', 'total': -1, 'current': 0, 'count': 0 }, } for budget in budgets: output[budget.id] = { 'name': budget.name, 'total': budget.amount, 'categories': {}, 'current': 0, 'count': 0 } for cat in budget.categories.all(): cat_to_budget_mapping[cat.id] = budget output[budget.id]['categories'][cat.id] = { 'id': cat.id, 'name': cat.name, 'color': cat.color, 'icon': cat.icon, 'total': 1000, 'current': 0, 'count': 0 } start_date = request.GET.get('start_date', None) end_date = request.GET.get('end_date', None) if not start_date or not end_date: raise BinderValidationError('start_date and end_date are required') txs = tx_qs.filter(date__gte=start_date, date__lte=end_date).order_by('date').all() spent_per_category = {} for transaction in txs: cat_id = transaction.category_id budget_id = None if cat_id is not None: if cat_id not in spent_per_category: spent_per_category[cat_id] = { 'name': budget.name, 'total': budget.amount, 'current': 0, 'count': 0 } if cat_id == c_saving.id: budget_id = BUCKET_SAVING elif cat_id == c_work.id: budget_id = BUCKET_INCOME else: budget = cat_to_budget_mapping.get(cat_id) output[BUCKET_SPENT]['current'] -= transaction.amount output[BUCKET_SPENT]['count'] += 1 if budget: budget_id = budget.id # The saving/income/spent buckets don't need this if 'categories' in output[budget_id]: output[budget_id]['categories'][cat_id][ 'current'] -= transaction.amount output[budget_id]['categories'][cat_id]['count'] += 1 output[budget_id]['current'] -= transaction.amount output[budget_id]['count'] += 1 return JsonResponse({ 'data': list(output.values()), 'meta': { 'total_records': len(budgets) } })
def send_activation_email(self, request): """ Endpoint that can be used to send an activation mail for an user. Calls the _send_activation_email callback if the user is succesfully activated Request: POST { "email": "email" } Response: { "code": code } Possible codes: sent Mail is send sucessfully already active User is already active, no mail was send blacklisted User was not activated """ if request.method != 'PUT': raise BinderMethodNotAllowed() # For lack of a better check self._require_model_perm('reset_password', request) decoded = request.body.decode() try: body = json.loads(decoded) except ValueError: raise BinderRequestError( _('Invalid request body: not a JSON document.')) logger.info('activation email attempt for {}'.format( body.get('email', ''))) if body.get('email') is None: raise BinderValidationError({'email': ['missing']}) try: user = self.model._default_manager.get(email=body.get('email')) except self.model.DoesNotExist: raise BinderNotFound() if user.is_active: if user.last_login is None: # TODO: Figure out a way to make this customisable without # allowing injection of arbitrary URLs (phishing!) self._send_activation_email(request, user) response = JsonResponse({'code': 'sent'}) response.status_code = 201 else: response = JsonResponse({'code': 'already active'}) else: response = JsonResponse({'code': 'blacklisted'}) response.status_code = 400 return response