def get_student_stats(user): stats = {} if user.has_perm( get_perm_name(Actions.SEE.value, UserGroups.student.value, 'balance')): student_accounts = Account.objects.filter( user__groups__name__contains=UserGroups.student.value) balances = [a.balance for a in student_accounts] stats.update({ 'sum_money': int(sum(balances)), 'mean_money': int(statistics.mean(balances)) }) if user.has_perm( get_perm_name(Actions.PROCESS.value, UserGroups.student.value, 'created_transactions')): stats.update({ 'created_students_len': Transaction.objects.filter( creator__groups__name__in=[UserGroups.student.value] ).filter(state__name=States.created.value).__len__() }) if user.has_perm( get_perm_name(Actions.PROCESS.value, UserGroups.staff.value, 'created_transactions')): stats.update({ 'created_staff_len': Transaction.objects.filter( creator__groups__name__in=[UserGroups.staff.value] ).filter(state__name=States.created.value).__len__() }) return stats
def user_can_decline(request, updated_transaction): if not updated_transaction.can_be_transitioned_to(States.declined.value): log.warning( '%s cant decline transaction because transaction can not be transitioned to declined state', request.user.account.long_name()) return False if updated_transaction.creator.username == request.user.username: if request.user.has_perm( get_perm_name(Actions.DECLINE.value, 'self', updated_transaction.type.name)): return True log.warning( request.user.account.long_name() + ' cant decline transaction because user do not have rights to decline self created ' + updated_transaction.type.name) else: if request.user.has_perm( get_perm_name( Actions.DECLINE.value, updated_transaction.creator.groups.get( name__in=PERMISSION_RESPONSIBLE_GROUPS).name, 'created_transactions')): return True log.warning( '%s is not owner of transaction and do not have rights to decline %s group.' ' but tries to decline it', request.user.account.long_name(), updated_transaction.creator.groups.get( name__in=PERMISSION_RESPONSIBLE_GROUPS).name) return False
def _get_transactions_of_user_who_is(user, target_user, group): created_transactions = [] received_money = [] received_counters = [] if user.has_perm(get_perm_name(Actions.see.value, group, 'created_transactions')): for trans in Transaction.objects.filter(creator=target_user).order_by('-creation_timestamp').all(): trans_info = {'transaction': trans} if group == 'self': target_transaction_identifier = trans.type.name else: target_transaction_identifier = 'created_transactions' trans_info.update( {'update': user.has_perm(get_perm_name(Actions.update.value, group, target_transaction_identifier)) and trans.state.possible_transitions.filter( name=States.substituted.value).exists()}) trans_info.update( {'decline': user.has_perm(get_perm_name(Actions.decline.value, group, target_transaction_identifier)) and trans.state.possible_transitions.filter( name=States.declined.value).exists()}) trans_info.update( {'create': user.has_perm(get_perm_name(Actions.create.value, group, target_transaction_identifier))}) created_transactions.append(trans_info) if user.has_perm(get_perm_name(Actions.see.value, group, 'received_transactions')): received_money = Money.objects.filter(receiver=target_user).filter(counted=True).order_by('-creation_timestamp') received_counters = Attendance.objects.filter(receiver=target_user).filter(counted=True).order_by( '-creation_timestamp') return {'created_transactions': created_transactions, 'received_counters': received_counters, 'received_money': received_money}
def manage(request, user_group, to_decline=None, to_process=None): can_process = request.user.has_perm( get_perm_name(Actions.process.value, user_group, 'created_transactions')) can_decline = request.user.has_perm( get_perm_name(Actions.decline.value, user_group, 'created_transactions')) if not (can_decline or can_process): return HttpResponseForbidden() if to_decline and can_decline: transaction = get_object_or_404(Transaction, id=to_decline) if transaction.creator.groups.filter(name__in=[user_group]).exists(): transaction.decline() else: return HttpResponseForbidden("У вас нет прав отменить эту транзакцию") if to_process and can_process: transaction = get_object_or_404(Transaction, id=to_process) if transaction.creator.groups.filter(name__in=[user_group]).exists(): transaction.process() else: return HttpResponseForbidden("У вас нет прав начислить эту транзакцию") render_dict = {"transactions": Transaction.objects.filter(creator__groups__name__in=[user_group]).filter( state__name=States.created.value)} render_dict.update({'can_process': can_process, 'can_decline': can_decline}) render_dict.update({'user_group': user_group}) return render(request, 'bank/transaction_lists/manage.html', render_dict)
def user(request, username): host = User.objects.get(username=username) host_group = get_used_user_group(host) render_dict = {'host': host} render_dict.update( {'can_see_balance': request.user.has_perm(get_perm_name(Actions.see.value, host_group.name, 'balance')), 'can_see_counters': request.user.has_perm(get_perm_name(Actions.see.value, host_group.name, 'attendance'))}) render_dict.update(_get_transactions_of_user_who_is(request.user, host, host_group.name)) render_dict.update({'counters': get_counters_of_user_who_is(request.user, host, host_group)}) avatar_url = "bank/avatars/{} {}.jpg".format(host.last_name, host.first_name) if USE_PICS else DEFAULT_PIC_PATH render_dict.update({'avatar_url': avatar_url}) return render(request, 'bank/user_page.html', render_dict)
def add_transaction(request, type_name, update_of=None, from_template=None): if not request.user.has_perm(get_perm_name(Actions.create.value, 'self', type_name)): log.warning(request.user.get_username() + ' access denied on add trans ' + type_name) return HttpResponseForbidden() if update_of or from_template: source = update_of if update_of else from_template updated_transaction = get_object_or_404(Transaction, id=source) if update_of and not user_can_update(request, updated_transaction): return HttpResponseForbidden("У вас нет прав на изменение этой транзакции") if from_template and not user_can_use_template(request, updated_transaction): return HttpResponseForbidden("Эту транзакцию нельзя использовать как шаблон") if not updated_transaction.type.name == type_name: return HttpResponseBadRequest("Тип транзакции из шаблона не совпадает с типом указанным в адресной строке") controller = TransactionService.get_controller_for(type_name) TransactionFormset = controller.get_blank_form(creator_username=request.user.username) if update_of or from_template: source = update_of if update_of else from_template initial = json.loads(get_object_or_404(Transaction, id=source).creation_map) else: initial = controller.get_initial_form_data(request.user.username) if request.method == 'POST': formset = TransactionFormset(request.POST, initial=initial) if formset.is_valid(): if update_of: try: get_object_or_404(Transaction, id=update_of).substitute() except AttributeError: raise AttributeError('Попытка изменить транзакцию которую нельзя изменить') created_transaction = controller.get_transaction_from_form_data(formset.cleaned_data, update_of) log.info("Valid add transaction from {}, update={}, transaction={}".format(request.user.account.long_name(), update_of, created_transaction)) if request.user.has_perm(get_perm_name(Actions.process.value, 'self', type_name)): # process transaction if have rights to do so created_transaction.process() # update should be inside. return render(request, 'bank/add/success.html', {'transaction': created_transaction, 'can_use_tmp': user_can_use_template(request, created_transaction), 'can_update': user_can_update(request, created_transaction), 'can_decline': user_can_decline(request, created_transaction)}) else: # if GET # prepare empty form formset = TransactionFormset(initial=initial) # if GET or if form was invalid render_map = {'formset': formset, 'type_name': type_name, 'update_of': update_of, 'from_template': from_template} render_map.update(controller.get_render_map_update()) return render(request, controller.template_url, render_map)
def get_counters_of_user_who_is(user, target_user, group): if not user.has_perm(get_perm_name(Actions.SEE.value, group, 'attendance')): return None all_counters = Attendance.objects.filter(receiver=target_user).filter( counted=True) info = { 'study_needed': OBL_STUDY_NEEDED, 'fac_pass_needed': target_user.account.fac_needed(), 'lab_pass_needed': target_user.account.lab_needed() } counters_val = {} for counter_type in AttendanceType.objects.all(): counter_sum = sum([c.value for c in all_counters.filter(type=counter_type)]) counters_val.update({counter_type.name: int(counter_sum)}) info.update({ 'study': counters_val.get(AttendanceTypeEnum.fac_attend.value) + counters_val.get(AttendanceTypeEnum.seminar_attend.value) }) info.update({ 'next_missed_lec_fine': target_user.account.get_next_missed_lec_penalty(), 'expected_equator_fine': target_user.account.get_equator_study_fine(), 'expected_fine': target_user.account.get_final_study_fine() }) return {'val': counters_val, 'info': info}
def get_students_counters(request): if not request.user.has_perm( get_perm_name(Actions.see.value, UserGroups.staff.value, "created_transactions")): return HttpResponseForbidden() return get_csv_for_model(Attendance, "counters")
def user_can_use_template(request, template_trans): if template_trans.creator.username == request.user.username: return request.user.has_perm( get_perm_name(Actions.CREATE.value, 'self', template_trans.type.name)) else: return False
def get_students_money(request): if not request.user.has_perm( get_perm_name(Actions.SEE.value, UserGroups.staff.value, 'created_transactions')): return HttpResponseForbidden() return get_csv_for_model(Money, 'money')
def get_counters_of_user_who_is(user, target_user, group): if not user.has_perm(get_perm_name(Actions.see.value, group, "attendance")): return None all_counters = Attendance.objects.filter(receiver=target_user).filter( counted=True) info = { "study_needed": OBL_STUDY_NEEDED, "fac_pass_needed": target_user.account.fac_needed(), "lab_pass_needed": target_user.account.lab_needed() } counters_val = {} for counter_type in AttendanceType.objects.all(): counter_sum = sum( [c.value for c in all_counters.filter(type=counter_type)]) counters_val.update({counter_type.name: int(counter_sum)}) info.update({ "study": counters_val.get(AttendanceTypeEnum.fac_attend.value) + counters_val.get(AttendanceTypeEnum.seminar_attend.value) }) info.update({ "next_missed_lec_fine": target_user.account.get_next_missed_lec_penalty(), "expected_fine": target_user.account.get_final_study_fine() }) return {"val": counters_val, "info": info}
def students(request): students_data = User.objects.filter(groups__name__contains=UserGroups.student.value).order_by('account__party', 'last_name') render_dict = get_students_markup(students_data) render_dict.update({'students': students_data}) render_dict.update({'can_see_balance': request.user.has_perm( get_perm_name(Actions.see.value, UserGroups.student.value, 'balance'))}) return render(request, 'bank/user_lists/students.html', render_dict)
def user_can_update(request, updated_transaction): """ can update only self transactions with rights """ if not updated_transaction.can_be_transitioned_to(States.substituted.value): return False if updated_transaction.creator.username == request.user.username: return request.user.has_perm(get_perm_name(Actions.update.value, 'self', updated_transaction.type.name)) else: return False
def get_user_transactions(request): user = request.user log.info('api user info call from {}'.format(user.account.long_name())) if not (user.has_perm(get_perm_name(Actions.see.value, "self", "balance")) and user.has_perm( get_perm_name(Actions.see.value, "self", "attendance"))): return HttpResponseForbidden("user do not have perm to see this info") data = { "balance": user.account.balance, "username": user.username, "first_name": user.first_name, "last_name": user.last_name, "balance_changes": [t.to_python() for t in user.account.get_all_money()], "counters": [t.to_python() for t in user.received_attendance.all()], "counters_value": get_counters_of_user_who_is(user, user, 'self') } return HttpResponse(json.dumps(data), content_type='application/json')
def get_user_transactions(request): user = request.user log.info('api user info call from %s', user.account.long_name()) if not (user.has_perm(get_perm_name(Actions.SEE.value, 'self', 'balance')) and user.has_perm( get_perm_name(Actions.SEE.value, 'self', 'attendance'))): return HttpResponseForbidden('user do not have perm to see this info') data = { 'balance': user.account.balance, 'username': user.username, 'first_name': user.first_name, 'last_name': user.last_name, 'balance_changes': [t.to_python() for t in user.account.get_all_money()], 'counters': [t.to_python() for t in user.received_attendance.all()], 'counters_value': get_counters_of_user_who_is(user, user, 'self') } return HttpResponse(json.dumps(data), content_type='application/json')
def staff(request): staff_data = User.objects.filter( groups__name__contains=UserGroups.staff.value).order_by('last_name') render_dict = {'staff': staff_data} render_dict.update({ 'can_see_balance': request.user.has_perm( get_perm_name(Actions.SEE.value, UserGroups.staff.value, 'balance')) }) return render(request, 'bank/user_lists/staff.html', render_dict)
def index(request): log.info("index page request from {}".format(request.user.account.long_name())) student_stats = get_student_stats(request.user) transaction_types = TransactionType.objects.all() transaction_type_info = [ {"name": t.name, "readable_name": t.readable_name, "can_create": request.user.has_perm(get_perm_name(Actions.create.value, "self", t.name))} for t in transaction_types] counters = get_counters_of_user_who_is(request.user, request.user, 'self') return render(request, 'bank/indexx.html', {'transaction_type_info': transaction_type_info, 'st_stats': student_stats, 'counters': counters})
def get_transaction_HTML(request): transaction_id = request.GET.get('transaction_id', -1) viewer_role = request.GET.get('viewer_role', -1) transaction = Transaction.objects.get(id=transaction_id) # if there is no transaction it fails, but it's ok. group = 'self' if request.user == transaction.creator else get_used_user_group(transaction.creator) if not request.user.has_perm(get_perm_name(Actions.see.value, group, 'created_transactions')): return HttpResponseForbidden("У вас нет прав на просмотр этой транзакции") html = loader.render_to_string('bank/transaction_lists/transaction.html', {'transaction': transaction, 'viewer_role': viewer_role}) output_data = {'transaction_HTML': html} return JsonResponse(output_data)
def make_transaction(request): user = request.user log.info('api add transaction call from {}'.format( user.account.long_name())) trans_data = json.loads(str(request.body, 'utf-8')) # if invalid trans type -- return illegal request if not trans_data_is_valid(trans_data): raise TransactionTypeNotRecognized() # Extract transaction type. type_name = TransactionType.objects.get( name=trans_data.get("transaction_type")).name # check if request owner equals transaction creator if trans_data.get('creator') != request.user.username: raise CreatorDoNotMatchRequestOwner() # check user can create such transactions if not request.user.has_perm( get_perm_name(Actions.create.value, 'self', type_name)): log.warning(request.user.get_username() + ' access denied on add trans ' + type_name + 'through API') raise CantCreateThisType(type_name) # if can -- call transaction controller to create transaction instance from request body controller = TransactionService.get_controller_for(type_name) transaction = controller.build_transaction_from_api_request(trans_data) # check if user can process this transaction if request.user.has_perm( get_perm_name(Actions.process.value, 'self', type_name)): # process transaction if have rights to do so transaction.process() # if can -- process, return success result. return transaction
def index(request): """Home page for a user. Lists avaliable get transactions and add transactios menues Shows user's current balace (student) or general stats (staff) """ log.info('index page request from %s', request.user.account.long_name()) student_stats = get_student_stats(request.user) transaction_types = TransactionType.objects.all() transaction_type_info = [{ 'name': t.name, 'readable_name': t.readable_name, 'can_create': request.user.has_perm( get_perm_name(Actions.CREATE.value, 'self', t.name)) } for t in transaction_types] counters = get_counters_of_user_who_is(request.user, request.user, 'self') return render( request, 'bank/indexx.html', { 'transaction_type_info': transaction_type_info, 'st_stats': student_stats, 'counters': counters })
def get_report_student_stats(user): stats = {} if user.has_perm( get_perm_name(Actions.SEE.value, UserGroups.student.value, 'balance')): student_accounts = Account.objects.filter( user__groups__name__contains=UserGroups.student.value).order_by( 'party', 'user__last_name') balances = [a.balance for a in student_accounts] stats.update({ 'sum_money': int(sum(balances)), 'mean_money': int(statistics.mean(balances)), 'st_dev': round(statistics.stdev(balances), 2), }) accounts_info = [] for acc in student_accounts: money = acc.get_all_money() acc_info = { 'acc': acc, 'name': acc.long_name(), 'str_balance': acc.get_balance(), 'balance_stored': acc.balance, 'party': acc.party, 'balance_calculated': get_balance_change_from_money_list(money, acc.user.username), 'earned_all': get_balance_change_from_money_list( filter( lambda m: m.type.group_general not in ['fine', 'purchase', 'technicals', 'p2p'], money), acc.user.username), 'earned_work': get_balance_change_from_money_list( filter(lambda m: m.type.group_general == 'work', money), acc.user.username), 'earned_fine': get_balance_change_from_money_list( filter(lambda m: m.type.group_general == 'fine', money), acc.user.username), 'earned_activity': get_balance_change_from_money_list( filter(lambda m: m.type.group_general == 'activity', money), acc.user.username), 'earned_sport': get_balance_change_from_money_list( filter(lambda m: m.type.group_general == 'sport', money), acc.user.username), 'earned_studies': get_balance_change_from_money_list( filter(lambda m: m.type.group_general == 'studies', money), acc.user.username), 'counters': get_counters_of_user_who_is(user, acc.user, UserGroups.student.value) } accounts_info.append(acc_info) if math.fabs(acc_info['balance_calculated'] - acc_info['balance_stored']) > 10: log.warning('balances for {} differs more than {}'.format(acc, 10)) best_activity = max( get_list_from_dict_list_by_key(accounts_info, 'earned_activity')) best_work = max( get_list_from_dict_list_by_key(accounts_info, 'earned_work')) best_sport = max( get_list_from_dict_list_by_key(accounts_info, 'earned_sport')) best_studies = max( get_list_from_dict_list_by_key(accounts_info, 'earned_studies')) best_all = max(get_list_from_dict_list_by_key(accounts_info, 'earned_all')) best_balance = max( get_list_from_dict_list_by_key(accounts_info, 'balance_stored')) for acc_info in accounts_info: acc_info['is_best_activity'] = acc_info[ 'earned_activity'] == best_activity acc_info['is_best_work'] = acc_info['earned_work'] == best_work acc_info['is_best_sport'] = acc_info['earned_sport'] == best_sport acc_info['is_best_studies'] = acc_info['earned_studies'] == best_studies acc_info['is_best_all'] = acc_info['earned_all'] == best_all if BALANCE_DANGER < acc_info['balance_stored'] < BALANCE_WARNING: acc_info['row_class'] = 'warning' elif acc_info['balance_stored'] <= BALANCE_DANGER: acc_info['row_class'] = 'danger' elif acc_info['balance_stored'] == best_balance: acc_info['row_class'] = 'my-success' else: acc_info['row_class'] = '' stats.update({'accounts_info': accounts_info}) counters_list = get_list_from_dict_list_by_key(accounts_info, 'counters') stats.update({ 'sum_lab': sum([ t['val'][AttendanceTypeEnum.lab_pass.value] for t in counters_list ]), 'sum_fac': sum([ t['val'][AttendanceTypeEnum.fac_attend.value] for t in counters_list ]), 'sum_sem_pass': sum([ t['val'][AttendanceTypeEnum.seminar_pass.value] for t in counters_list ]), 'sum_sem_attend': sum([ t['val'][AttendanceTypeEnum.seminar_attend.value] for t in counters_list ]) }) return stats
render_dict = { 'transactions': Transaction.objects.filter( creator__groups__name__in=[user_group]).filter( state__name=States.created.value) } render_dict.update({ 'can_process': can_process, 'can_decline': can_decline }) render_dict.update({'user_group': user_group}) return render(request, 'bank/transaction_lists/manage.html', render_dict) @permission_required(get_perm_name(Actions.SEE.value, UserGroups.staff.value, 'created_transactions'), login_url='bank:index') def monitor_table(request): table = TransTable(Transaction.objects.all(), order_by='-date_created') RequestConfig(request).configure(table) table.paginate(per_page=200) return render(request, 'bank/monitor_table.html', {'trans': table}) @permission_required(get_perm_name(Actions.UPLOAD.value, 'self', 'files'), login_url='bank:index') def upload_file(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): f = request.FILES['file']
def test_multiple_args(self): self.assertEqual('bank.a_b', get_perm_name('a', 'b'))
def test_one_arg(self): self.assertEqual('bank.a', get_perm_name('a'))
def add_transaction(request, type_name, update_of=None, from_template=None): """Generic view to serve a form to add/update new transactions of any type Get's transaction type Checks user permissions to create transactions of that type If GET: Returns the form template If POST: Validates the form data and creates a new transaction Uses strategy pattern to insert transaction-type-specific logic from transaction controllers """ if not request.user.has_perm( get_perm_name(Actions.CREATE.value, 'self', type_name)): log.warning('%s access denied on add trans %s', request.user.get_username(), type_name) return HttpResponseForbidden() if update_of or from_template: source = update_of if update_of else from_template updated_transaction = get_object_or_404(Transaction, id=source) if update_of and not user_can_update(request, updated_transaction): return HttpResponseForbidden( 'You dont have permissions to update this transaction') if from_template and not user_can_use_template(request, updated_transaction): return HttpResponseForbidden( 'This transaction can not be used as a template') if not updated_transaction.type.name == type_name: return HttpResponseBadRequest( 'Transaction type from template does not match the type from the URL' ) controller = TransactionService.get_controller_for(type_name) transaction_formset = controller.get_blank_form( creator_username=request.user.username) if update_of or from_template: source = update_of if update_of else from_template initial = json.loads( get_object_or_404(Transaction, id=source).creation_map) else: initial = controller.get_initial_form_data(request.user.username) if request.method == 'POST': # We received some data in the form formset = transaction_formset(request.POST, initial=initial) if formset.is_valid(): if update_of: try: get_object_or_404(Transaction, id=update_of).substitute() except AttributeError as e: raise AttributeError( 'Attempt to update transaction that can not be updated' ) from e created_transaction = controller.get_transaction_from_form_data( formset.cleaned_data, update_of) log.info( 'Valid add transaction from %s, update=%s, transaction=%s', request.user.account.long_name(), update_of, created_transaction) if request.user.has_perm( get_perm_name(Actions.PROCESS.value, 'self', type_name)): # Process transaction if have rights to do so. created_transaction.process() # update should be inside. return render( request, 'bank/add/success.html', { 'transaction': created_transaction, 'can_use_tmp': user_can_use_template(request, created_transaction), 'can_update': user_can_update(request, created_transaction), 'can_decline': user_can_decline(request, created_transaction) }) else: # If GET # Prepare empty form formset = transaction_formset(initial=initial) # if GET or if form was invalid render_map = { 'formset': formset, 'type_name': type_name, 'update_of': update_of, 'from_template': from_template } render_map.update(controller.get_render_map_update()) return render(request, controller.template_url, render_map)
if to_process and can_process: transaction = get_object_or_404(Transaction, id=to_process) if transaction.creator.groups.filter(name__in=[user_group]).exists(): transaction.process() else: return HttpResponseForbidden("У вас нет прав начислить эту транзакцию") render_dict = {"transactions": Transaction.objects.filter(creator__groups__name__in=[user_group]).filter( state__name=States.created.value)} render_dict.update({'can_process': can_process, 'can_decline': can_decline}) render_dict.update({'user_group': user_group}) return render(request, 'bank/transaction_lists/manage.html', render_dict) @permission_required(get_perm_name(Actions.see.value, UserGroups.staff.value, "created_transactions"), login_url='bank:index') def monitor_table(request): table = TransTable(Transaction.objects.all(), order_by='-date_created') RequestConfig(request).configure(table) table.paginate(per_page=200) return render(request, 'bank/monitor_table.html', {'trans': table}) @permission_required(get_perm_name(Actions.upload.value, "self", "files"), login_url='bank:index') def upload_file(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): f = request.FILES['file'] local_path = form.cleaned_data['path'].strip('/')