def refresh(request): """ Returns a json response containing a new retailer_pin for the store. """ if request.session.get('account') and\ request.session.get(SESSION_KEY): data = {'success': False} settings = SESSION.get_settings(request.session) if settings == None: raise Http404 else: settings.set('retailer_pin', Settings.generate_id()) settings.update() # update the session cache request.session['settings'] = settings # notify other dashboards of these changes store = SESSION.get_store(request.session) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSettings": settings.jsonify() } comet_receive(store.objectId, payload) data['success'] = True data['retailer_pin'] = settings.retailer_pin return HttpResponse(json.dumps(data), content_type="application/json") else: return HttpResponse(json.dumps({'success': False}), content_type="application/json")
def refresh(request): """ Returns a json response containing a new retailer_pin for the store. """ if request.session.get('account') and\ request.session.get(SESSION_KEY): data = {'success': False} settings = SESSION.get_settings(request.session) if settings == None: raise Http404 else: settings.set('retailer_pin', Settings.generate_id()) settings.update() # update the session cache request.session['settings'] = settings # notify other dashboards of these changes store = SESSION.get_store(request.session) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSettings":settings.jsonify() } comet_receive(store.objectId, payload) data['success'] = True data['retailer_pin'] = settings.retailer_pin return HttpResponse(json.dumps(data), content_type="application/json") else: return HttpResponse(json.dumps({'success': False}), content_type="application/json")
def delete(request, employee_id): """ This will also remove the employee from the ACL, delete the employee object and also delete the Parse.User object if and only if it has no pointer to a Store or a Patron. """ # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_approved_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been removed.'})) employees_approved_list.pop(i_remove) request.session['employees_approved_list'] =\ employees_approved_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: store = SESSION.get_store(request.session) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY } if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been deleted.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def delete(request, employee_id): """ This will also remove the employee from the ACL, delete the employee object and also delete the Parse.User object if and only if it has no pointer to a Store or a Patron. """ # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_approved_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been removed.'})) employees_approved_list.pop(i_remove) request.session['employees_approved_list'] =\ employees_approved_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: store = SESSION.get_store(request.session) payload = {COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY} if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been deleted.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def deny(request, employee_id): """ same as delete except this uses the pending list """ # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) # update session cache for employees_pending_list employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has already been denied.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY } store = SESSION.get_store(request.session) # no need to check acl here but just in case if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been denied.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def punch(request): if request.method == "POST": try: nump = int(request.POST['num_punches']) except ValueError: return HttpResponse(json.dumps({'error': 'float'}), content_type="application/json") settings = SESSION.get_settings(request.session) if nump > settings.get("punches_employee"): return HttpResponse(json.dumps({ 'error': 'over', 'limit': nump }), content_type="application/json") store = SESSION.get_store(request.session) data = { "store_location_id":\ SESSION.get_active_store_location_id(request.session), "store_id":store.objectId, "store_name":str(store.get('store_name')), "punch_code":str(request.POST['punch_code']), "num_punches":nump, } # Check if the user is an employee employee = request.session.get("employee") if employee: data['employee_id'] = employee.objectId res = cloud_call("punch", data) if 'error' not in res: res['patron_name'] = res['result'] # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(res), content_type="application/json") else: if res['error'] == "PATRON_NOT_FOUND": request.session.clear() request.session.update( SessionStore(request.session.session_key)) return HttpResponse(json.dumps({"error":\ "PATRON_NOT_FOUND"}), content_type="application/json") # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps({'error': 'error'}), content_type="application/json")
def deny(request, employee_id): """ same as delete except this uses the pending list """ # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) # update session cache for employees_pending_list employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has already been denied.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: payload = {COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY} store = SESSION.get_store(request.session) # no need to check acl here but just in case if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been denied.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def settings(request): """ Renders the settings page or handles changes to it. """ data = {'settings_nav': True} store = SESSION.get_store(request.session) settings = SESSION.get_settings(request.session) # user made changes if request.method == 'POST': form = SettingsForm(request.POST) if form.is_valid(): # expect numbers so cast to int dct = request.POST.dict().copy() dct['punches_employee'] = int(dct['punches_employee']) settings.update_locally(dct, False) settings.update() # Shin chose to move punches_facebook to Store... store.set("punches_facebook", int(request.POST["punches_facebook"])) store.Settings = settings.objectId store.settings = settings store.update() # notify other dashboards of this changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSettings":settings.jsonify(), "updatedPunchesFacebook_int":\ store.punches_facebook, } comet_receive(store.objectId, payload) data['success'] = "Settings have been saved." else: data['error'] = 'Error saving settings.' # user navigated to this page so just render the settings template # with a filled settings form else: form = SettingsForm() form.initial = settings.__dict__.copy() # shin chose to move punches_facebook to Store... form.initial['punches_facebook'] =\ store.get('punches_facebook') # update the session cache since we may have made changes above request.session['store'] = store request.session['settings'] = settings data['form'] = form data['settings'] = settings return render(request, 'manage/settings.djhtml', data)
def settings(request): """ Renders the settings page or handles changes to it. """ data = {'settings_nav': True} store = SESSION.get_store(request.session) settings = SESSION.get_settings(request.session) # user made changes if request.method == 'POST': form = SettingsForm(request.POST) if form.is_valid(): # expect numbers so cast to int dct = request.POST.dict().copy() dct['punches_employee'] = int(dct['punches_employee']) settings.update_locally(dct, False) settings.update() # Shin chose to move punches_facebook to Store... store.set("punches_facebook", int(request.POST["punches_facebook"])) store.Settings = settings.objectId store.settings = settings store.update() # notify other dashboards of this changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSettings":settings.jsonify(), "updatedPunchesFacebook_int":\ store.punches_facebook, } comet_receive(store.objectId, payload) data['success'] = "Settings have been saved." else: data['error'] = 'Error saving settings.' # user navigated to this page so just render the settings template # with a filled settings form else: form = SettingsForm() form.initial = settings.__dict__.copy() # shin chose to move punches_facebook to Store... form.initial['punches_facebook'] =\ store.get('punches_facebook') # update the session cache since we may have made changes above request.session['store'] = store request.session['settings'] = settings data['form'] = form data['settings'] = settings return render(request, 'manage/settings.djhtml', data)
def _wrapped_view(request, *args, **kwargs): if SESSION.get_store(request.session).has_access(\ request.session['account']): return view_func(request, *args, **kwargs) else: if http_response: resp = http_response if content_type == "application/json": resp = json.dumps(http_response) return HttpResponse(resp, content_type=content_type) else: return redirect(reverse("manage_logout"))
def index(request): """ Render the analysis page. """ # sort the rewards by redemption count in descending order rewards = SESSION.get_store(request.session).get("rewards") rewards.sort(key=lambda k: k['redemption_count'], reverse=True) return render(request, 'manage/analysis.djhtml', { 'analysis_nav': True, 'rewards': rewards, })
def _wrapped_view(request, *args, **kwargs): if SESSION.get_store(request.session).has_access(\ request.session['account']): return view_func(request, *args, **kwargs) else: if http_response: resp = http_response if content_type == "application/json": resp = json.dumps(http_response) return HttpResponse(resp, content_type=content_type) else: return redirect(reverse("manage_logout"))
def index(request): """ Render the analysis page. """ # sort the rewards by redemption count in descending order rewards = SESSION.get_store(request.session).get("rewards") rewards.sort(key=lambda k: k['redemption_count'], reverse=True) return render(request, 'manage/analysis.djhtml', { 'analysis_nav': True, 'rewards': rewards, })
def punch(request): if request.method == "POST": try: nump = int(request.POST['num_punches']) except ValueError: return HttpResponse(json.dumps({'error': 'float'}), content_type="application/json") settings = SESSION.get_settings(request.session) if nump > settings.get("punches_employee"): return HttpResponse(json.dumps({'error': 'over', 'limit': nump}), content_type="application/json") store = SESSION.get_store(request.session) data = { "store_location_id":\ SESSION.get_active_store_location_id(request.session), "store_id":store.objectId, "store_name":str(store.get('store_name')), "punch_code":str(request.POST['punch_code']), "num_punches":nump, } # Check if the user is an employee employee = request.session.get("employee") if employee: data['employee_id'] = employee.objectId res = cloud_call("punch", data) if 'error' not in res: res['patron_name'] = res['result'] # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(res), content_type="application/json") else: if res['error'] == "PATRON_NOT_FOUND": request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps({"error":\ "PATRON_NOT_FOUND"}), content_type="application/json") # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps({'error': 'error'}), content_type="application/json")
def _wrapped_view(request, *args, **kwargs): if test_func(request): if SESSION.get_store(request.session) and\ SESSION.get_store(request.session).active: # may not want to import parse.session here due # to cyclic imports timezone.activate(SESSION.get_store_timezone(\ request.session)) try: return view_func(request, *args, **kwargs) except KeyError: return logout(request, "manage_login") else: return logout(request, "manage_login") # if http_response is provided and content_type is json # and request.is_ajax then this request if from comet.js if request.is_ajax() and http_response and\ content_type == "application/json": # no need to update session- if it got here then the session is empty #request.session.clear() #request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(http_response), content_type=content_type) path = request.build_absolute_uri() # If the login url is the same scheme and net location then just # use the path as the "next" url. login_scheme, login_netloc = urlparse.urlparse(login_url or settings.LOGIN_URL)[:2] current_scheme, current_netloc = urlparse.urlparse(path)[:2] if ((not login_scheme or login_scheme == current_scheme) and (not login_netloc or login_netloc == current_netloc)): path = request.get_full_path() from django.contrib.auth.views import redirect_to_login # no need to update session- if it got here then the session is empty #request.session.clear() #request.session.update(SessionStore(request.session.session_key)) return redirect_to_login(path, login_url, redirect_field_name)
def image_get(request): """ Returns a json response containing the cover and thumbnail url for the store. """ if request.method == "GET": store = SESSION.get_store(request.session) return HttpResponse(json.dumps({ "src_cover": store.cover_image_url, "src_thumbnail": store.thumbnail_image_url, }), content_type="application/json") raise Http404
def _wrapped_view(request, *args, **kwargs): if test_func(request): if SESSION.get_store(request.session) and\ SESSION.get_store(request.session).active: # may not want to import parse.session here due # to cyclic imports timezone.activate(SESSION.get_store_timezone(\ request.session)) try: return view_func(request, *args, **kwargs) except KeyError: return logout(request, "manage_login") else: return logout(request, "manage_login") # if http_response is provided and content_type is json # and request.is_ajax then this request if from comet.js if request.is_ajax() and http_response and\ content_type == "application/json": # no need to update session- if it got here then the session is empty #request.session.clear() #request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(http_response), content_type=content_type) path = request.build_absolute_uri() # If the login url is the same scheme and net location then just # use the path as the "next" url. login_scheme, login_netloc = urlparse.urlparse( login_url or settings.LOGIN_URL)[:2] current_scheme, current_netloc = urlparse.urlparse(path)[:2] if ((not login_scheme or login_scheme == current_scheme) and (not login_netloc or login_netloc == current_netloc)): path = request.get_full_path() from django.contrib.auth.views import redirect_to_login # no need to update session- if it got here then the session is empty #request.session.clear() #request.session.update(SessionStore(request.session.session_key)) return redirect_to_login(path, login_url, redirect_field_name)
def feedback(request, feedback_id): """ Renders the feedback template with the stores feedbacks. """ data = { 'messages_nav': True, 'feedback_id':feedback_id, "store_name":\ SESSION.get_store(request.session).get("store_name"), } # get from the messages_received_list in session cache messages_received_list = SESSION.get_messages_received_list(\ request.session) i_remove, feedback = 0, None for ind, m in enumerate(messages_received_list): if m.objectId == feedback_id: feedback = m i_remove = ind break if not feedback: # feedack not found - redirect to messages page with error message return redirect(reverse('messages_index')+ "?%s" %\ urllib.urlencode({'error': 'Feedback not found.', "tab_feedback":1})) if not feedback.is_read: # update the feedback's read if not yet read feedback.is_read = True feedback.update() # make sure that the message stored in the list is the updated 1 messages_received_list.pop(i_remove) messages_received_list.insert(i_remove, feedback) request.session['messages_received_list'] = messages_received_list # inserting this success and error message into the template # should be done in a cleaner way - this was done by the # first guy. I just didn't bother changing it. if request.GET.get("success"): data['success'] = request.GET.get("success") if request.GET.get("error"): data['error'] = request.GET.get("error") # there should only be at most 1 reply data['reply'] = feedback.get('reply') data['feedback'] = feedback return render(request, 'manage/feedback.djhtml', data)
def edit(request): account = request.session['account'] store = SESSION.get_store(request.session) email_form, pass_form, data = None, None, {} if request.method == "POST": action, success = request.POST['action'], None if action == "email": email_form = EmailForm(account, request.POST) if email_form.is_valid(): success = "Successfully updated email." # Need to make sure that the account is the latest - # User in dashboard then signs up for a mobile account # and then edits store details = bug! account.fetch_all(clear_first=True, with_cache=False) # update the account - email = username! postEmail = email_form.cleaned_data['email'] if account.email != postEmail: account.email = postEmail account.username = postEmail account.update() elif action == "password": pass_form = PasswordForm(account, request.POST) if pass_form.is_valid(): success = "Successfully updated password." account.set_password(pass_form.cleaned_data['new']) account.update(save_password=True) if success: data['success'] = success # notify other dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedAccount": account.jsonify() } comet_receive(store.objectId, payload) request.session["account"] = account if not email_form: email_form = EmailForm(None) email_form.initial = request.session['account'].__dict__.copy() if not pass_form: pass_form = PasswordForm(None) data["email_form"] = email_form data["password_form"] = pass_form return render(request, 'manage/account_edit.html', data)
def image_get(request): """ Returns a json response containing the cover and thumbnail url for the store. """ if request.method == "GET": store = SESSION.get_store(request.session) return HttpResponse(json.dumps({ "src_cover": store.cover_image_url, "src_thumbnail": store.thumbnail_image_url, }), content_type="application/json") raise Http404
def edit(request, employee_id): data = {'employees_nav': True, 'employee_id': employee_id} # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employee = None for m in employees_approved_list: if m.objectId == employee_id: employee = m break acc = Account.objects().get(Employee=employee.objectId) store = SESSION.get_store(request.session) if not employee or not acc: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee does not exist.'})) if request.method == "POST": store.set_access_level(acc, request.POST["ACL"]) store.update() request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify() } comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been updated.'})) form = EmployeeForm(employee.__dict__.copy()) form.data['email'] = acc.get('email') data.update({ 'ACCESS_ADMIN': ACCESS_ADMIN[0], 'ACCESS_PUNCHREDEEM': ACCESS_PUNCHREDEEM[0], 'ACCESS_NONE': ACCESS_NONE[0], 'form': form, 'employee': employee, 'employee_acl': store.get_access_level(acc)[0], }) return render(request, 'manage/employee_edit.djhtml', data)
def edit(request, employee_id): data = {'employees_nav': True, 'employee_id': employee_id} # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employee = None for m in employees_approved_list: if m.objectId == employee_id: employee = m break acc = Account.objects().get(Employee=employee.objectId) store = SESSION.get_store(request.session) if not employee or not acc: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee does not exist.'})) if request.method == "POST": store.set_access_level(acc, request.POST["ACL"]) store.update() request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":store.jsonify() } comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been updated.'})) form = EmployeeForm(employee.__dict__.copy()) form.data['email'] = acc.get('email') data.update({ 'ACCESS_ADMIN': ACCESS_ADMIN[0], 'ACCESS_PUNCHREDEEM': ACCESS_PUNCHREDEEM[0], 'ACCESS_NONE': ACCESS_NONE[0], 'form': form, 'employee': employee, 'employee_acl': store.get_access_level(acc)[0], }) return render(request, 'manage/employee_edit.djhtml', data)
def index(request): """ Render the rewards template with the store's rewards. """ data = {'rewards_nav': True} store = SESSION.get_store(request.session) lst, rewards = [], store.get('rewards') if rewards: # record the pre-sorted reward presorted = [(reward['reward_id'], int(reward['punches'])) for reward in rewards] # cannot just use punches as the map since it is not unique reward_map = {(reward['reward_id'], int(reward['punches'])): reward for reward in rewards} # get a sorted list of punches in descending order sorted_ = presorted[:] sorted_.sort(key=lambda r: r[1]) sorted_rewards = [reward_map[p] for p in sorted_] store.rewards = sorted_rewards # do not just update needlessly check if order has changed. for i, pre in enumerate(presorted): if pre[0] != sorted_[i][0]: store.update() data['rewards'] = sorted_rewards # store has no rewards =( else: data['rewards'] = [] # inserting this success and error message into the template # should be done in a cleaner way - this was done by the first guy # I just didn't bother changing it. if request.GET.get("success"): data['success'] = request.GET.get("success") if request.GET.get("error"): data['error'] = request.GET.get("error") # update session cache request.session['store'] = store return render(request, 'manage/rewards.djhtml', data)
def delete(request, reward_id): """ Deletes a reward with the given reward_id. """ # check if the reward_id passed in is invalid and raise a 404 if so. # reward ids are integers try: reward_id = int(str(reward_id)) except ValueError: raise Http404 account = request.session['account'] store = SESSION.get_store(request.session) rewards = store.get('rewards') rewards_map = {reward['reward_id']: reward for reward in rewards} # reward cannot be found for deletion. Redirect user to the rewards # page with a success message (maybe this should be an error message instead?). try: reward = rewards_map[reward_id] except KeyError: return redirect(reverse('rewards_index')+\ "?%s" % urllib.urlencode({'success':\ 'Reward has been removed.'})) # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "deletedReward": { "reward_id": reward["reward_id"] } } comet_receive(store.objectId, payload) # update session cache store.array_remove('rewards', [reward]) store.rewards = None store.get('rewards') request.session['store'] = store return redirect(reverse('rewards_index')+\ "?%s" % urllib.urlencode({'success':\ 'Reward has been removed.'}))
def _wrapped_view(request, *args, **kwargs): if SESSION.get_store(request.session).is_admin(\ request.session['account']) or (except_method and\ request.method in except_method): return view_func(request, *args, **kwargs) else: if reverse_url: reversed_url = reverse(reverse_url) + "?%s" %\ urlencode({'error': "Permission denied"}) if reverse_postfix: reversed_url =\ reversed_url + "&" + reverse_postfix return redirect(reversed_url) elif http_response: resp = http_response if content_type == "application/json": resp = json.dumps(http_response) return HttpResponse(resp, content_type=content_type) else: raise Http404
def _wrapped_view(request, *args, **kwargs): if SESSION.get_store(request.session).is_admin(\ request.session['account']) or (except_method and\ request.method in except_method): return view_func(request, *args, **kwargs) else: if reverse_url: reversed_url = reverse(reverse_url) + "?%s" %\ urlencode({'error': "Permission denied"}) if reverse_postfix: reversed_url =\ reversed_url + "&" + reverse_postfix return redirect(reversed_url) elif http_response: resp = http_response if content_type == "application/json": resp = json.dumps(http_response) return HttpResponse(resp, content_type=content_type) else: raise Http404
def edit_store(request): """ Render the edit store template or handle a form submission from it. """ data = {'account_nav': True} store = SESSION.get_store(request.session) # validate form submission and redirect to store details if # success or re-render the store edit template with previous form # input and errors. if request.method == "POST": store_form = StoreForm(request.POST) if store_form.is_valid(): store.update_locally(request.POST.dict(), False) store.update() # notify other dashboards comet_receive( store.objectId, { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify(), }) # make sure that we have the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) return redirect(reverse('store_index')+ "?%s" %\ urllib.urlencode({'success': 'Store details has been updated.'})) # user navigates to this page so just render the store edit template # with a blank form. else: store_form = StoreForm() store_form.initial = store.__dict__.copy() data['store_form'] = store_form return render(request, 'manage/store_edit.html', data)
def feedback_delete(request, feedback_id): """ Handles requests to delete the feedback with the given feedback_id. """ store = SESSION.get_store(request.session) # get the feedback from the messages_received_list in session cache messages_received_list = SESSION.get_messages_received_list(\ request.session) i_remove, feedback = 0, None for ind, m in enumerate(messages_received_list): if m.objectId == feedback_id: feedback = m i_remove = ind break if not feedback: # feedback not found - it may have been deleted return redirect(reverse('messages_index')+ "?%s" %\ urllib.urlencode({'error': 'Feedback not found.'})) # we don't actually delete the feedback object, # we just remove from the store's relation store.remove_relation("ReceivedMessages_", [feedback.objectId]) # remove it from the messages_received_list in session cache messages_received_list.pop(i_remove) request.session['messages_received_list'] =\ messages_received_list # notify other dashboards logged into the same store of this change comet_receive( store.objectId, { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "deletedFeedback": Message(objectId=feedback.objectId).jsonify(), }) return redirect(reverse('messages_index')+ "?%s" %\ urllib.urlencode({'success':'Feedback has been deleted.', 'tab_feedback':1}))
def approve(request, employee_id): # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) employee.set('status', APPROVED) employee.update() employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list # update session cache for employees_approved_list employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employees_approved_list.insert(0, employee) request.session['employees_approved_list'] =\ employees_approved_list # notify other dashboards of this change store_id = SESSION.get_store(request.session).objectId approved_employee = Employee(objectId=employee.objectId) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "approvedEmployee":approved_employee.jsonify() } comet_receive(store_id, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been approved.'}))
def approve(request, employee_id): # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) employee.set('status', APPROVED) employee.update() employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list # update session cache for employees_approved_list employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employees_approved_list.insert(0, employee) request.session['employees_approved_list'] =\ employees_approved_list # notify other dashboards of this change store_id = SESSION.get_store(request.session).objectId approved_employee = Employee(objectId=employee.objectId) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "approvedEmployee": approved_employee.jsonify() } comet_receive(store_id, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been approved.'}))
def edit_store(request): """ Render the edit store template or handle a form submission from it. """ data = {'account_nav': True} store = SESSION.get_store(request.session) # validate form submission and redirect to store details if # success or re-render the store edit template with previous form # input and errors. if request.method == "POST": store_form = StoreForm(request.POST) if store_form.is_valid(): store.update_locally(request.POST.dict(), False) store.update() # notify other dashboards comet_receive(store.objectId, { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify(), }) # make sure that we have the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) return redirect(reverse('store_index')+ "?%s" %\ urllib.urlencode({'success': 'Store details has been updated.'})) # user navigates to this page so just render the store edit template # with a blank form. else: store_form = StoreForm() store_form.initial = store.__dict__.copy() data['store_form'] = store_form return render(request, 'manage/store_edit.html', data)
def account_is_admin(session): return SESSION.get_store(session).is_admin(session['account'])
def trends_graph(request, data_type=None, start=None, end=None): """ Handles requests for the trends graph. """ store = SESSION.get_store(request.session) # We need the store's timezone to convert everything to UTC # because the time we get from start and end are local times # and in order to convert to UTC we must first make start and end # timezone aware. We use parse.utils.make_aware_to_utc to do # this in 1 step. We convert everything to UTC for use in queries. store_timezone = SESSION.get_store_timezone(request.session) start = datetime.strptime(start, "%Y-%m-%d") end = datetime.strptime(end, "%Y-%m-%d") start = start.replace(hour=0, minute=0, second=0, microsecond=0) end = end.replace(hour=23, minute=59, second=59, microsecond=0) start_aware = make_aware_to_utc(start, store_timezone) end_aware = make_aware_to_utc(end, store_timezone) rows, columns = [], [] if data_type == 'punches': # we need to return graph data for punches for all days in # between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with punch count columns = [{ "id": "", "label": "Date", "type": "string" }, { "id": "", "label": 'Punches', "type": "number" }] # get the Punches punches = store.get('punches', createdAt__lte=end_aware, createdAt__gte=start_aware, order='createdAt', limit=900) # have to clear the punches cache attr of store filled # by the above query store.punches = None #create dictionary for easy search punch_map = {} if punches: for punch in punches: # The keys in the punch map is the month/day of the # createdAt of the punch object. We also convert it # to the store's local time for when we send back the # data to the client. key = timezone.localtime(punch.createdAt, store_timezone).strftime("%m/%d") if key in punch_map: # add to the punch count for the existing key punch_map[key] =\ punch_map[key] + punch.get('punches') else: # initialize the key in the punch map punch_map[key] = punch.get('punches') for single_date in rputils.daterange(start, end): # we now populate the rows with the corresponding punch counts # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") try: punch_count = punch_map[str_date] except KeyError: punch_count = 0 # the first item in our row is the date # the second item is the corresponding punch_count row = [{"v": str_date}, {"v": punch_count}] rows.append({'c': row}) elif data_type == 'facebook': # we need to return graph data for facebook posts for # all days in between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with post count columns = [{ "id": "", "label": "Date", "type": "string" }, { "id": "", "label": 'Posts', "type": "number" }] # get the FacebookPosts posts = store.get("facebookPosts", createdAt__lte=end, createdAt__gte=start, limit=900) # have to clear the facebookPosts cache attr of store filled # by the above query store.facebookPosts = None #create dictionary for easy search post_map = {} if posts: for post in posts: # The keys in the post map is the month/day of the # createdAt of the punch object. We also convert it # to the store's local time for when we send back the # data to the client. key = timezone.localtime(post.createdAt, store_timezone).strftime("%m/%d") if key in post_map: # add to the post count for the existing key post_map[key] = post_map[key] + 1 else: # initialize the post count post_map[key] = 1 for single_date in rputils.daterange(start, end): # we now populate the rows with the corresponding post counts # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") try: post_count = post_map[str_date] except KeyError: post_count = 0 # the first item in our row is the date # the second item is the corresponding post_count row = [{"v": str_date}, {"v": post_count}] rows.append({'c': row}) else: # we need to return graph data for unique patrons for # all days in between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with accumulative patron count columns = [{ "id": "", "label": "Date", "type": "string" }, { "id": "", "label": 'Patrons', "type": "number" }] for single_date in rputils.daterange(start, end): # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") # FIXME In order to get the cumulative count for each day, # we make a query of the count for each day. Optimization? d = single_date.replace(hour=23, minute=59, second=59) d_aware = make_aware_to_utc(d, store_timezone) patron_count = store.get('patronStores', count=1, limit=0, createdAt__lte=d_aware) row = [{"v": str_date}, {"v": patron_count}] rows.append({'c': row}) # return the graph data return HttpResponse(json.dumps({ 'cols': columns, 'rows': rows }), content_type="application/json")
def image_crop(request): """ Takes in crop coordinates and creates a new png image. """ if request.method == "POST": data = {} store = SESSION.get_store(request.session) old_cover, old_thumbnail = None, None if store.get("cover_image"): old_cover = store.get("cover_image") if store.get("thumbnail_image"): old_thumbnail = store.get("thumbnail_image") # if there are 2 windows with the same session_key editing the # store image, the image will be deleted by the first window # to crop. The 2nd window will then have no image. image = UploadedImageFile.objects.filter(session_key=\ request.session.session_key) if not image: # flag the execution of image Crop Complete js function data['success'] = True return render(request, 'manage/image_crop.djhtml', data) image = image[0] # the crop coords are used for the thumbnail_image crop_coords = None if len(request.POST["x1"]) > 0: crop_coords = { "x1": int(request.POST["x1"].split(".")[0]), "y1": int(request.POST["y1"].split(".")[0]), "x2": int(request.POST["x2"].split(".")[0]), "y2": int(request.POST["y2"].split(".")[0]), } # save the session before a cloud call! request.session.save() res_cover = create_png(image.image.path) res_thumbnail = create_png(image.image.path, IMAGE_THUMBNAIL_SIZE, crop_coords) # make sure that we have the latest session session = SessionStore(request.session.session_key) store = SESSION.get_store(session) if res_cover and 'error' not in res_cover: setattr(store, "cover_image", res_cover.get('name')) setattr(store, "cover_image_url", res_cover.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) if res_thumbnail and 'error' not in res_thumbnail: setattr(store, "thumbnail_image", res_thumbnail.get('name')) setattr(store, "thumbnail_image_url", res_thumbnail.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) # below here for backwards compat store.store_avatar = getattr(store, "thumbnail_image") store.store_avatar_url = getattr(store, "thumbnail_image_url") store.update() # delete the model and file since it's useless to keep image.delete() # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStoreThumbnailName": store.thumbnail_image, "updatedStoreThumbnailUrl": store.thumbnail_image_url, "updatedStoreCoverName": store.cover_image, "updatedStoreCoverUrl": store.cover_image_url, } comet_receive(store.objectId, payload) # need to remove old files if old_cover: delete_file(old_cover, 'image/png') if old_thumbnail: delete_file(old_thumbnail, 'image/png') # flag the execution of image Crop Complete js function data['success'] = True # make sure we have latest session data request.session.clear() request.session.update(SessionStore(request.session.session_key)) return render(request, 'manage/image_crop.djhtml', data) raise Http404
def breakdown_graph(request, data_type=None, filter=None, range=None): """ handles requests for the breakdown graph. """ store = SESSION.get_store(request.session) store_timezone = SESSION.get_store_timezone(request.session) (start, end) = rputils.calculate_daterange(range) start = start.replace(hour=0, minute=0, second=0, microsecond=0) end = end.replace(hour=23, minute=59, second=59, microsecond=0) # need to make aware and then convert to utc for querying start_aware = make_aware_to_utc(start, store_timezone) end_aware = make_aware_to_utc(end, store_timezone) results = [] if data_type == 'punches': if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]) # WARNING! max punches returned is 1000! unknown, male, female = 0, 0, 0 male_punches = relational_query(store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", {"gender": "male"}, { 'createdAt__lte': end_aware, 'createdAt__gte': start_aware }) female_punches = relational_query(store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", {"gender": "female"}, { 'createdAt__lte': end_aware, 'createdAt__gte': start_aware }) if male_punches: male_punches = male_punches['results'] # aggregate the punches for p in male_punches: male += p.get('punches') if female_punches: female_punches = female_punches['results'] for p in female_punches: female += p.get('punches') rows = [start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), unknown, male, female] results.append(rows) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]) now = datetime.now() rows = [ start.strftime("%m/%d/%Y") + ' - ' + end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0 ] age_ranges = [(1, 0, -20), (2, -20, -30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) punches = relational_query( store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", { 'date_of_birth__lte': end_dob_aware, 'date_of_birth__gte': start_dob_aware }, { 'createdAt__lte': end_aware, 'createdAt__gte': start_aware }) punch_count = 0 if punches: punches = punches['results'] for punch in punches: punch_count += punch['punches'] rows[idx] = punch_count results.append(rows) elif data_type == 'facebook': if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]) results.append([ start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), 0, relational_query(store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", {"gender": "male"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), relational_query(store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", {"gender": "female"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), ]) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]) now = datetime.now() rows = [ start.strftime("%m/%d/%Y") + ' - ' + end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0 ] age_ranges = [(1, 0, -20), (2, -20, -30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) rows[idx] = relational_query( store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", { 'date_of_birth__lte': end_dob_aware, 'date_of_birth__gte': start_dob_aware }, { 'createdAt__lte': end_aware, 'createdAt__gte': start_aware }, count=True) results.append(rows) else: # patrons if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]) results.append([ start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), 0, relational_query(store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", {"gender": "male"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), relational_query(store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", {"gender": "female"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), ]) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]) now = datetime.now() rows = [ start.strftime("%m/%d/%Y") + ' - ' + end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0 ] age_ranges = [(1, 0, -20), (2, -20, -30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) rows[idx] = relational_query( store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", { 'date_of_birth__lte': end_dob_aware, 'date_of_birth__gte': start_dob_aware }, { 'createdAt__lte': end_aware, 'createdAt__gte': start_aware }, count=True) results.append(rows) return HttpResponse(json.dumps(results), content_type="application/json")
def image_crop(request): """ Takes in crop coordinates and creates a new png image. """ if request.method == "POST": data = {} store = SESSION.get_store(request.session) old_cover, old_thumbnail = None, None if store.get("cover_image"): old_cover = store.get("cover_image") if store.get("thumbnail_image"): old_thumbnail = store.get("thumbnail_image") # if there are 2 windows with the same session_key editing the # store image, the image will be deleted by the first window # to crop. The 2nd window will then have no image. image = UploadedImageFile.objects.filter(session_key=\ request.session.session_key) if not image: # flag the execution of image Crop Complete js function data['success'] = True return render(request, 'manage/image_crop.djhtml', data) image = image[0] # the crop coords are used for the thumbnail_image crop_coords = None if len(request.POST["x1"]) > 0: crop_coords = { "x1": int(request.POST["x1"].split(".")[0]), "y1": int(request.POST["y1"].split(".")[0]), "x2": int(request.POST["x2"].split(".")[0]), "y2": int(request.POST["y2"].split(".")[0]), } # save the session before a cloud call! request.session.save() res_cover = create_png(image.image.path) res_thumbnail = create_png(image.image.path, IMAGE_THUMBNAIL_SIZE, crop_coords) # make sure that we have the latest session session = SessionStore(request.session.session_key) store = SESSION.get_store(session) if res_cover and 'error' not in res_cover: setattr(store, "cover_image", res_cover.get('name')) setattr(store, "cover_image_url", res_cover.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) if res_thumbnail and 'error' not in res_thumbnail: setattr(store, "thumbnail_image", res_thumbnail.get('name')) setattr(store, "thumbnail_image_url", res_thumbnail.get('url').replace(\ "http:/", "https://s3.amazonaws.com")) # below here for backwards compat store.store_avatar = getattr(store, "thumbnail_image") store.store_avatar_url = getattr(store, "thumbnail_image_url") store.update() # delete the model and file since it's useless to keep image.delete() # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStoreThumbnailName": store.thumbnail_image, "updatedStoreThumbnailUrl": store.thumbnail_image_url, "updatedStoreCoverName": store.cover_image, "updatedStoreCoverUrl": store.cover_image_url, } comet_receive(store.objectId, payload) # need to remove old files if old_cover: delete_file(old_cover, 'image/png') if old_thumbnail: delete_file(old_thumbnail, 'image/png') # flag the execution of image Crop Complete js function data['success'] = True # make sure we have latest session data request.session.clear() request.session.update(SessionStore(request.session.session_key)) return render(request, 'manage/image_crop.djhtml', data) raise Http404
def update_subscription(request): """ This view is also used for explicit upgrades. """ do_upgrade = request.GET.get("do_upgrade") is not None if do_upgrade: data = {'account_nav': True, 'upgrade': True} else: data = {'account_nav': True, 'update': True} store = SESSION.get_store(request.session) subscription = SESSION.get_subscription(request.session) if request.method == 'POST': form = SubscriptionForm(request.POST) form.subscription = subscription # to validate cc_number all_forms_valid = form.is_valid() if all_forms_valid: # upgrade account if date_passed_user_limit is on # should fetch the most up-to-date subscription first subscription = Subscription.objects().get(objectId=\ subscription.objectId) upgraded = False if subscription.date_passed_user_limit or do_upgrade: level = subscription.get("subscriptionType") if level == 0: subscription.set("subscriptionType", 1) subscription.date_passed_user_limit = None upgraded = True elif level == 1: subscription.date_passed_user_limit = None subscription.set("subscriptionType", 2) upgraded = True # subscription.update() called in store_cc subscription.update_locally(request.POST.dict(), False) d = datetime(int(request.POST['date_cc_expiration_year']), int(request.POST['date_cc_expiration_month']), 1) subscription.set( "date_cc_expiration", make_aware_to_utc(d, SESSION.get_store_timezone(request.session))) def invalid_card(): # add some asterisk to cc_number if form.initial.get("cc_number"): form.initial['cc_number'] = "*" * 12 +\ form.initial.get('cc_number')[-4:] errs = form._errors.setdefault(\ "cc_number", ErrorList()) errs.append("Invalid credit " +\ "card. Please make sure that you provide " +\ "correct credit card information and that you " +\ "have sufficient funds, then try again.") data['form'] = form return render(request, 'manage/subscription_update.djhtml', data) res = True # only store_cc if it is a digit (new) if str(form.data['cc_number']).isdigit(): res = subscription.store_cc(form.data['cc_number'], form.data['cc_cvv'], False) if not res: return invalid_card() # if monthly billing failed if subscription.date_charge_failed: sub_cost = sub_type[subscription.get(\ "subscriptionType")]["monthly_cost"] invoice = subscription.charge_cc(\ sub_cost, EMAIL_MONTHLY_SUBJECT, MONTHLY) if invoice: subscription.date_last_billed =\ subscription.date_last_billed +\ relativedelta(days=30) subscription.date_charge_failed = None subscription.update() send_email_receipt_monthly_success(\ request.session['account'], store, subscription, invoice) else: return invalid_card() ########### if upgraded: max_users = sub_type[\ subscription.subscriptionType]["max_users"] if max_users == UNLIMITED: max_users = "Unlimited" package = { "sub_type": sub_type[\ subscription.subscriptionType-1]["name"], "new_sub_type": sub_type[\ subscription.subscriptionType]["name"], "new_sub_type_cost": sub_type[\ subscription.subscriptionType]["monthly_cost"], "new_max_patronStore_count": max_users, } send_email_account_upgrade(request.session['account'], store, package) # Important that this is last since invalid_card may be # returned! subscription.update() # update the session cache request.session['store'] = store request.session['subscription'] = subscription # notify other dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": subscription.jsonify() } comet_receive(store.objectId, payload) # if coming from the message edit limit reached if do_upgrade: if request.session.get('from_limit_reached') and\ request.session.get('message_b4_upgrade'): # redirect back to message_edit view to process return redirect(reverse('message_edit', args=(0,)) + "?%s" %\ urllib.urlencode({'send_message': '1'})) if do_upgrade: return redirect(reverse('store_index')+ "?%s" %\ urllib.urlencode({'success':\ 'Your subscription has been upgraded.'})) else: return redirect(reverse('store_index')+ "?%s" %\ urllib.urlencode({'success':\ 'Your subscription has been updated.'})) else: form = SubscriptionForm() form.initial = subscription.__dict__.copy() # add some asterisk to cc_number if form.initial.get("cc_number"): form.initial['cc_number'] = "*" * 12 +\ form.initial.get('cc_number')[-4:] if do_upgrade: from_limit_reached =\ request.session.get("from_limit_reached") if from_limit_reached: data['from_limit_reached'] = from_limit_reached # update the session cache request.session['store'] = store request.session['subscription'] = subscription data['form'] = form return render(request, 'manage/subscription_update.djhtml', data)
def edit_location(request, store_location_id): """ Renders the edit location template or handle form submissions for location creation or update. """ data = {'account_nav': True, 'store_location_id': store_location_id} store = SESSION.get_store(request.session) # we are editing a new location if store_location_id is 0 new_location = store_location_id == '0' def common(store_location_form): """ Returns a rendered store location edit template with the given store location form. """ data['store_location_form'] = store_location_form return render(request, 'manage/store_location_edit.djhtml', data) # Handle form submission and return the appropriate json response if request.method == 'POST': store_location_form = StoreLocationForm(request.POST) postDict = request.POST.dict() hours = HoursInterpreter(json.loads(postDict["hours"])) # we are either creating a new store location or updating an # existing one with new values in the postDict if new_location: store_location = StoreLocation(**postDict) else: store_location = SESSION.get_store_location(\ request.session, store_location_id) store_location.update_locally(postDict, False) if store_location_form.is_valid(): # validate and format the hours hours_validation = hours.is_valid() if type(hours_validation) is bool: store_location.set("hours", hours.from_javascript_to_parse()) else: data['hours_data'] = hours._format_javascript_input() data['hours_error'] = hours_validation return HttpResponse(json.dumps({ "result": "error", "html": common(store_location_form).content, }), content_type="application/json") # set the timezone if store_location.get('zip'): store_location.store_timezone =\ get_timezone(store_location.get('zip')).zone # format the phone number store_location.phone_number =\ format_phone_number(postDict['phone_number']) # update the store's coordinates and neighborhood full_address = " ".join(\ store_location.get_full_address().split(", ")) map_data = get_map_data(full_address) store_location.set("coordinates", map_data.get("coordinates")) store_location.set("neighborhood", store_location.get_best_fit_neighborhood(\ map_data.get("neighborhood"))) if new_location: store_location.Store = store.objectId store_location.create() store.array_add_unique("store_locations", [store_location]) else: store_location.update() # if this location is the first location then update # the corresponding store columns for backwards compat sl_list = SESSION.get_store_locations_list(request.session) if sl_list[0].objectId == store_location.objectId: store.inherit_store_location(store_location) store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify(), "updatedStoreLocation": store_location.jsonify(), } comet_receive(store.objectId, payload) # make sure that we have the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) if new_location: success_msg = 'New store location has been added.' else: success_msg = 'Store location has been updated.' # set the active location to this one SESSION.set_active_store_location_id(request.session, store_location.objectId) return HttpResponse(json.dumps({ "result": "success", "url": reverse('store_index')+ "?%s" %\ urllib.urlencode({'success': success_msg}) }), content_type="application/json") else: data['store_location'] = store_location data['hours_data'] = hours._format_javascript_input() return HttpResponse(json.dumps({ "result": "error", "html": common(store_location_form).content, }), content_type="application/json") # user navigated to this page so just render a store location edit # template with a blank form (if new_location) or a form with fields # filled out if user is editing an existing location else: store_location_form = StoreLocationForm(None) # user is editing an existing location so fill up initial # form content if not new_location: store_location = SESSION.get_store_location(\ request.session, store_location_id) store_location_form.initial = store_location.__dict__.copy() # make sure that the phone number is unformatted store_location_form.initial['phone_number'] =\ store_location_form.initial['phone_number'].replace("(", "").replace(")","").replace(" ", "").replace("-","") data['store_location'] = store_location data['hours_data'] = HoursInterpreter(\ store_location.hours)._format_parse_input() return common(store_location_form)
def edit_location(request, store_location_id): """ Renders the edit location template or handle form submissions for location creation or update. """ data = {'account_nav': True, 'store_location_id': store_location_id} store = SESSION.get_store(request.session) # we are editing a new location if store_location_id is 0 new_location = store_location_id == '0' def common(store_location_form): """ Returns a rendered store location edit template with the given store location form. """ data['store_location_form'] = store_location_form return render(request, 'manage/store_location_edit.djhtml', data) # Handle form submission and return the appropriate json response if request.method == 'POST': store_location_form = StoreLocationForm(request.POST) postDict = request.POST.dict() hours = HoursInterpreter(json.loads(postDict["hours"])) # we are either creating a new store location or updating an # existing one with new values in the postDict if new_location: store_location = StoreLocation(**postDict) else: store_location = SESSION.get_store_location(\ request.session, store_location_id) store_location.update_locally(postDict, False) if store_location_form.is_valid(): # validate and format the hours hours_validation = hours.is_valid() if type(hours_validation) is bool: store_location.set("hours", hours.from_javascript_to_parse()) else: data['hours_data'] = hours._format_javascript_input() data['hours_error'] = hours_validation return HttpResponse(json.dumps({ "result": "error", "html": common(store_location_form).content, }), content_type="application/json") # set the timezone if store_location.get('zip'): store_location.store_timezone =\ get_timezone(store_location.get('zip')).zone # format the phone number store_location.phone_number =\ format_phone_number(postDict['phone_number']) # update the store's coordinates and neighborhood full_address = " ".join(\ store_location.get_full_address().split(", ")) map_data = get_map_data(full_address) store_location.set("coordinates", map_data.get("coordinates")) store_location.set("neighborhood", store_location.get_best_fit_neighborhood(\ map_data.get("neighborhood"))) if new_location: store_location.Store = store.objectId store_location.create() store.array_add_unique("store_locations", [store_location]) else: store_location.update() # if this location is the first location then update # the corresponding store columns for backwards compat sl_list = SESSION.get_store_locations_list(request.session) if sl_list[0].objectId == store_location.objectId: store.inherit_store_location(store_location) store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify(), "updatedStoreLocation": store_location.jsonify(), } comet_receive(store.objectId, payload) # make sure that we have the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) if new_location: success_msg = 'New store location has been added.' else: success_msg = 'Store location has been updated.' # set the active location to this one SESSION.set_active_store_location_id(request.session, store_location.objectId) return HttpResponse(json.dumps({ "result": "success", "url": reverse('store_index')+ "?%s" %\ urllib.urlencode({'success': success_msg}) }), content_type="application/json") else: data['store_location'] = store_location data['hours_data'] = hours._format_javascript_input() return HttpResponse(json.dumps({ "result": "error", "html": common(store_location_form).content, }), content_type="application/json") # user navigated to this page so just render a store location edit # template with a blank form (if new_location) or a form with fields # filled out if user is editing an existing location else: store_location_form = StoreLocationForm(None) # user is editing an existing location so fill up initial # form content if not new_location: store_location = SESSION.get_store_location(\ request.session, store_location_id) store_location_form.initial = store_location.__dict__.copy() # make sure that the phone number is unformatted store_location_form.initial['phone_number'] =\ store_location_form.initial['phone_number'].replace("(", "").replace(")","").replace(" ", "").replace("-","") data['store_location'] = store_location data['hours_data'] = HoursInterpreter(\ store_location.hours)._format_parse_input() return common(store_location_form)
def pull(request): """ This is where the comet approach is put into play. This handles ajax requests from clients, holding on to the request while checking Parse for new activity. IMPORTANT! The order in which the session cache is checked is very critical. Take for example and employee that registers. Dashboard A receives the pending employee and immediately approves it. Now Dashboard B will run pull with the pending employee and the approved employee. We must first add the pending then check for the approved! """ def comet(session_copy): # used by more than 1 (note that it is ok to retrieve all of # the lists since they are all pointers - not the actual list! employees_pending_list_copy =\ SESSION.get_employees_pending_list(session_copy) employees_approved_list_copy =\ SESSION.get_employees_approved_list(session_copy) messages_received_list_copy =\ SESSION.get_messages_received_list(session_copy) redemptions_pending_copy =\ SESSION.get_redemptions_pending(session_copy) redemptions_past_copy =\ SESSION.get_redemptions_past(session_copy) # this is the latest session data session = SessionStore(request.session.session_key) employees_pending_list =\ SESSION.get_employees_pending_list(session) employees_approved_list =\ SESSION.get_employees_approved_list(session) messages_received_list =\ SESSION.get_messages_received_list(session) redemptions_pending =\ SESSION.get_redemptions_pending(session) redemptions_past =\ SESSION.get_redemptions_past(session) # put the diffs between session_copy and session here data = {} ############################################################# # FEEDBACKS_UNREAD ################################## fbs_unread_copy = [ fb.objectId for fb in\ messages_received_list_copy if not fb.is_read ] fbs_unread = [ fb.objectId for fb in\ messages_received_list if not fb.is_read ] # get the difference between the two feedbacks_unread =\ tuple(set(fbs_unread) - set(fbs_unread_copy)) if feedbacks_unread: fb_unread = [] messages_received_ids =\ [ fb.objectId for fb in messages_received_list ] for feedback_id in feedbacks_unread: for fb in messages_received_list: if fb.objectId == feedback_id: fb_unread.append(fb.jsonify()) break if len(fb_unread) > 0: fb_count = 0 for fb in messages_received_list: if not fb.get("is_read"): fb_count += 1 data['feedbacks_unread'] = fb_unread data['feedback_unread_count'] = fb_count ############################################################# # EMPLOYEES_PENDING ################################## # must also check if employee is already approved! emps_pending_copy = [ emp.objectId for emp in employees_pending_list_copy ] emps_pending = [emp.objectId for emp in employees_pending_list] employees_pending =\ tuple(set(emps_pending) - set(emps_pending_copy)) if employees_pending: pending = [] for emp_id in employees_pending: for emp in employees_pending_list: if emp.objectId == emp_id: pending.append(emp.jsonify()) break if len(pending) > 0: data['employees_pending_count'] =\ len(employees_pending_list) data['employees_pending'] = pending ############################################################# # EMPLOYEES APPROVED (pending to approved) ################# emps_approved_copy = [ emp.objectId for emp in\ employees_approved_list_copy] emps_approved = [ emp.objectId for emp in\ employees_approved_list] appr_emps =\ tuple(set(emps_approved) - set(emps_approved_copy)) if appr_emps: approved = [] for appr_emp_id in appr_emps: for emp in employees_approved_list: if emp.objectId == appr_emp_id: approved.append(emp.jsonify()) break if len(approved) > 0: data['employees_approved'] = approved data['employees_pending_count'] =\ len(employees_pending_list) ############################################################# # EMPLOYEES DELETED/DENIED/REJECTED (pending/approved to pop)! # need to compare approved and pending! emps_copy = emps_approved_copy[:] emps_copy.extend(emps_pending_copy) emps = emps_approved[:] emps.extend(emps_pending) # emps_copy has the same or more items that emps del_emps = tuple(set(emps_copy) - set(emps)) if del_emps: deleted = [] for demp_id in del_emps: if demp_id in emps_approved_copy: emps_list = employees_approved_list_copy else: emps_list = employees_pending_list_copy for emp in emps_list: if emp.objectId == demp_id: deleted.append(emp.jsonify()) break if len(deleted) > 0: data['employees_pending_count'] =\ len(employees_pending_list) data['employees_deleted'] = deleted ############################################################# # REDEMPTIONS PENDING reds_pending_copy = [ r.objectId for r in\ redemptions_pending_copy ] reds_pending = [r.objectId for r in redemptions_pending] reds = tuple(set(reds_pending) - set(reds_pending_copy)) if reds: redemps = [] for r_id in reds: for redemp in redemptions_pending: if redemp.objectId == r_id: redemps.append(redemp.jsonify()) break if len(redemps) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_pending'] = redemps ############################################################# # REDEMPTIONS APPROVED (pending to history) reds_past_copy = [ r.objectId for r in\ redemptions_past_copy ] reds_past = [r.objectId for r in redemptions_past] appr_redemps =\ tuple(set(reds_past) - set(reds_past_copy)) if appr_redemps: redemp_js = [] for red_id in appr_redemps: for redemp in redemptions_past: if redemp.objectId == red_id: redemp_js.append(redemp.jsonify()) break if len(redemp_js) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_approved'] = redemp_js ############################################################# # REDEMPTIONS DELETED ############################## # remove from pending (should not be in history!) reds_copy = reds_past_copy[:] reds_copy.extend(reds_pending_copy) reds = reds_past[:] reds.extend(reds_pending) # reds_copy has the same or more items that reds del_redemps = tuple(set(reds_copy) - set(reds)) if del_redemps: redemp_js = [] for red_id in del_redemps: reds_list = [] if red_id in reds_past_copy: reds_list = redemptions_past_copy elif red_id in reds_pending_copy: reds_list = redemptions_pending_copy for redemp in reds_list: if redemp.objectId == red_id: redemp_js.append(redemp.jsonify()) break if len(redemp_js) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_deleted'] = redemp_js ############################################################# # SETTINGS UPDATED ############################## settings_copy = session_copy.get("settings") settings = session.get("settings") if settings_copy.get("retailer_pin") !=\ settings.get("retailer_pin"): data['retailer_pin'] = settings.get("retailer_pin") ############################################################# # REWARDS UPDATED ############################## rewards_copy = session_copy.get("store").get("rewards") rewards_copy =\ { reward['reward_id']:reward for reward in rewards_copy } rewards = session.get("store").get("rewards") rewards = {reward['reward_id']: reward for reward in rewards} updated_rewards = [] for reward_id, rew_copy in rewards_copy.iteritems(): # Note that some rewards may have been deleted! rew = rewards.get(reward_id) if rew and rew_copy['redemption_count'] !=\ rew['redemption_count']: # only the redemtpion_count and reward_id are used # in the client side updated_rewards.append({ "reward_id": reward_id, "redemption_count": rew['redemption_count'], }) if updated_rewards: data['rewards'] = updated_rewards ############################################################# # PATRONSTORE_COUNT ################################## patronStore_count_copy = int(session_copy["patronStore_count"]) patronStore_count = int(session["patronStore_count"]) if patronStore_count_copy != patronStore_count: data['patronStore_count'] = patronStore_count ############################################################# # ACTIVE_STORE_LOCATION_ID ############################ if session['active_store_location_id'] !=\ session_copy['active_store_location_id']: data['active_store_location_id'] =\ session['active_store_location_id'] # IMPORTANT! The request.session is the same as the # SessionStore(session_key)! so we must use the # request.session because it is automatically saved at the end # of each request- thereby overriding/undoing any changes made # to the SessionStore(session_key) key! # need to check if we are still logged in session = SessionStore(request.session.session_key) if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) ############################################################ # Respond ########################################### try: return HttpResponse(json.dumps(data), content_type="application/json") except (IOError, socket.error) as e: # broken pipe/socket. thread.exit() # exit silently ################################################################## ##### ENTRY POINT ###################################################### # get the timestamp and uid t = parser.parse(request.GET["timestamp"]) timestamp = str(t.hour).zfill(2) + ":" +\ str(t.minute).zfill(2) + ":" + str(t.second).zfill(2) uid = request.GET['uid'] # update the last_updated field of the CometSessionIndex try: csi = CometSessionIndex.objects.get(session_key=\ request.session.session_key) csi.last_updated = timezone.now() csi.save() except CometSessionIndex.DoesNotExist: # should never go here but just in case. CometSessionIndex.objects.create(session_key=\ request.session.session_key, store_id=SESSION.get_store(request.session).objectId, last_updated=timezone.now()) # register the CometSession CometSession.objects.update() CometSession.objects.create(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) # cache the current session at this state session_copy = dict(request.session) timeout_time = timezone.now() + relativedelta(seconds=REQUEST_TIMEOUT) # keep going until its time to return a response forcibly while timezone.now() < timeout_time: # need to update he objects manager to get the latest objects CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) except CometSession.DoesNotExist: # cometsession was deleted - time to go try: # make sure that the latest session is saved! # need to check if we are still logged in session = SessionStore(request.session.session_key) if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) return HttpResponse(json.dumps({"result": -1}), content_type="application/json") except (IOError, socket.error) as e: thread.exit() if scomet.modified: # delete the registered comet session object CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing try: return comet(session_copy) except KeyError: # if a key error occurs then that probably means that # the session has been flushed- was logged out by user # or forcefully by server =) # now time to flag existing tabs. request.session.clear() try: return HttpResponse(json.dumps({"result": -3}), content_type="application/json") except (IOError, socket.error) as e: # broken pipe/socket. thread.exit() # exit silently else: # nothing new, sleep for a bit sleep(COMET_PULL_RATE) # TIME IS UP - return a response result 0 means no change # try 1 last time if scomet.modified: # delete the registered comet session object CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing return comet(session_copy) # make sure that request.session is the most up to date session = SessionStore(request.session.session_key) # need to check if we are still logged in if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) # attempt to delete registered comet session if not yet deleted try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing try: return HttpResponse(json.dumps({"result": 0}), content_type="application/json") except (IOError, socket.error) as e: thread.exit() # exit silently
def breakdown_graph(request, data_type=None, filter=None, range=None): """ handles requests for the breakdown graph. """ store = SESSION.get_store(request.session) store_timezone = SESSION.get_store_timezone(request.session) (start, end) = rputils.calculate_daterange(range) start = start.replace(hour=0, minute=0, second=0, microsecond=0) end = end.replace(hour=23, minute=59, second=59, microsecond=0) # need to make aware and then convert to utc for querying start_aware = make_aware_to_utc(start, store_timezone) end_aware = make_aware_to_utc(end, store_timezone) results = [] if data_type == 'punches': if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]); # WARNING! max punches returned is 1000! unknown, male, female = 0, 0, 0 male_punches = relational_query(store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", {"gender": "male"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}) female_punches = relational_query(store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", {"gender": "female"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}) if male_punches: male_punches = male_punches['results'] # aggregate the punches for p in male_punches: male += p.get('punches') if female_punches: female_punches = female_punches['results'] for p in female_punches: female += p.get('punches') rows = [start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), unknown, male, female] results.append(rows) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]); now = datetime.now() rows = [start.strftime("%m/%d/%Y")+' - '+end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0] age_ranges = [(1, 0, -20), (2, -20,-30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) punches = relational_query(store.objectId, "Store", "Punches", "Punch", "Patron", "Patron", {'date_of_birth__lte':end_dob_aware, 'date_of_birth__gte':start_dob_aware}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}) punch_count = 0 if punches: punches = punches['results'] for punch in punches: punch_count += punch['punches'] rows[idx] = punch_count results.append(rows) elif data_type == 'facebook': if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]); results.append([ start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), 0, relational_query(store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", {"gender": "male"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), relational_query(store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", {"gender": "female"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), ]) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]); now = datetime.now() rows = [start.strftime("%m/%d/%Y")+' - '+end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0] age_ranges = [(1, 0, -20), (2, -20,-30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) rows[idx] = relational_query(store.objectId, "Store", "FacebookPosts", "FacebookPost", "Patron", "Patron", {'date_of_birth__lte':end_dob_aware, 'date_of_birth__gte':start_dob_aware}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True) results.append(rows) else: # patrons if filter == 'gender': results.append(["Range", "Unknown", "Male", "Female"]); results.append([ start.strftime("%m/%d/%Y")+\ ' - '+end.strftime("%m/%d/%Y"), 0, relational_query(store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", {"gender": "male"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), relational_query(store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", {"gender": "female"}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True), ]) elif filter == 'age': results.append(["Range", "<20", "20-29", "30-39", "40-49", ">50"]); now = datetime.now() rows = [start.strftime("%m/%d/%Y")+' - '+end.strftime("%m/%d/%Y"), 0, 0, 0, 0, 0] age_ranges = [(1, 0, -20), (2, -20,-30), (3, -30, -40), (4, -40, -50), (5, -50, -200)] for (idx, start_age, end_age) in age_ranges: start_dob = now + relativedelta(years=end_age) start_dob = start_dob.replace(hour=0, minute=0, second=0) end_dob = now + relativedelta(years=start_age) end_dob = end_dob + relativedelta(days=-1) end_dob = end_dob.replace(hour=23, minute=59, second=59) # need to make aware and then convert to utc for querying start_dob_aware = make_aware_to_utc(start_dob, store_timezone) end_dob_aware = make_aware_to_utc(end_dob, store_timezone) rows[idx] = relational_query(store.objectId, "Store", "PatronStores", "PatronStore", "Patron", "Patron", {'date_of_birth__lte':end_dob_aware, 'date_of_birth__gte':start_dob_aware}, {'createdAt__lte':end_aware, 'createdAt__gte':start_aware}, count=True) results.append(rows) return HttpResponse(json.dumps(results), content_type="application/json")
def redeem(request): """ returns json object. result is 0 if fail, 1 if success, 2 if insufficient punches, 3 if already validated, 4 if successfully deleted/denied, 5 has been deleted elsewhere and action is deny, 6 PatronStore has been removed, 7 deleted elsewhere and action is approve. """ if request.method == "GET": # approve or deny action = request.GET.get("action") redeemId = request.GET.get('redeemRewardId') # may come in as "None" or "null" or "undefined" rewardId = request.GET.get('rewardId') if str(rewardId).isdigit(): rewardId = int(rewardId) else: rewardId = None store = SESSION.get_store(request.session) if action == "approve": res = cloud_call( "validate_redeem", { "redeem_id": redeemId, "store_id": store.get("objectId"), "reward_id": rewardId, }) elif action == "deny": res = cloud_call("reject_redeem", { "redeem_id": redeemId, "store_id": store.get("objectId"), }) # retrieve latest session since user may click a bunch # of redemptions consecutively session = SessionStore(request.session.session_key) data = {"result": 0} # success result removed means patronstore is null! if 'error' not in res: redemptions_pending =\ SESSION.get_redemptions_pending(session) i_remove = -1 if action == "approve": result = res.get("result").get("code") result_red =\ RedeemReward(**res.get("result").get("result")) else: result = res.get("result") # remove from redemptions_pending for i, red in enumerate(redemptions_pending): if red.objectId == redeemId: i_remove = i break # IMPORTANT! Since comet receive immediately commits # changes to a session, i_remove will probably be -1 if action == "approve": redemptions_past =\ SESSION.get_redemptions_past(session) if result and result in ("insufficient", "PATRONSTORE_REMOVED" ) and i_remove != -1: redemptions_pending.pop(i_remove) elif i_remove != -1: # success redemption = redemptions_pending.pop(i_remove) redemption.is_redeemed = True redemption.updatedAt = result_red.updatedAt redemptions_past.append(redemption) session['redemptions_past'] =\ redemptions_past # session changed only if i_remove was not 1 if i_remove != -1: session['redemptions_pending'] =\ redemptions_pending if result and result == "insufficient": data["result"] = 2 elif result and result == "validated": data["result"] = 3 elif result and result == "PATRONSTORE_REMOVED": data["result"] = 6 else: data["result"] = 1 # request.session will be saved after return request.session.clear() request.session.update(session) return HttpResponse(json.dumps(data), content_type="application/json") elif action == "deny": if i_remove != -1: del_red = redemptions_pending.pop(i_remove) session['redemptions_pending'] =\ redemptions_pending data["result"] = 4 request.session.clear() request.session.update(session) return HttpResponse(json.dumps(data), content_type="application/json") elif 'error' in res: if res['error'] == "REDEEMREWARD_NOT_FOUND": if action == "approve": data["result"] = 7 elif action == "deny": data["result"] = 5 elif res['error'] == "REDEEMREWARD_VALIDATED": data["result"] = 3 elif res['error'] == "PATRONSTORE_REMOVED": data["result"] = 6 # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(data), content_type="application/json")
def redeem(request): """ returns json object. result is 0 if fail, 1 if success, 2 if insufficient punches, 3 if already validated, 4 if successfully deleted/denied, 5 has been deleted elsewhere and action is deny, 6 PatronStore has been removed, 7 deleted elsewhere and action is approve. """ if request.method == "GET": # approve or deny action = request.GET.get("action") redeemId = request.GET.get('redeemRewardId') # may come in as "None" or "null" or "undefined" rewardId = request.GET.get('rewardId') if str(rewardId).isdigit(): rewardId = int(rewardId) else: rewardId = None store = SESSION.get_store(request.session) if action == "approve": res = cloud_call("validate_redeem", { "redeem_id":redeemId, "store_id":store.get("objectId"), "reward_id":rewardId, }) elif action == "deny": res = cloud_call("reject_redeem", { "redeem_id":redeemId, "store_id":store.get("objectId"), }) # retrieve latest session since user may click a bunch # of redemptions consecutively session = SessionStore(request.session.session_key) data = {"result" : 0} # success result removed means patronstore is null! if 'error' not in res: redemptions_pending =\ SESSION.get_redemptions_pending(session) i_remove = -1 if action == "approve": result = res.get("result").get("code") result_red =\ RedeemReward(**res.get("result").get("result")) else: result = res.get("result") # remove from redemptions_pending for i, red in enumerate(redemptions_pending): if red.objectId == redeemId: i_remove = i break # IMPORTANT! Since comet receive immediately commits # changes to a session, i_remove will probably be -1 if action == "approve": redemptions_past =\ SESSION.get_redemptions_past(session) if result and result in ("insufficient", "PATRONSTORE_REMOVED") and i_remove != -1: redemptions_pending.pop(i_remove) elif i_remove != -1: # success redemption = redemptions_pending.pop(i_remove) redemption.is_redeemed = True redemption.updatedAt = result_red.updatedAt redemptions_past.append(redemption) session['redemptions_past'] =\ redemptions_past # session changed only if i_remove was not 1 if i_remove != -1: session['redemptions_pending'] =\ redemptions_pending if result and result == "insufficient": data["result"] = 2 elif result and result == "validated": data["result"] = 3 elif result and result == "PATRONSTORE_REMOVED": data["result"] = 6 else: data["result"] = 1 # request.session will be saved after return request.session.clear() request.session.update(session) return HttpResponse(json.dumps(data), content_type="application/json") elif action == "deny": if i_remove != -1: del_red = redemptions_pending.pop(i_remove) session['redemptions_pending'] =\ redemptions_pending data["result"] = 4 request.session.clear() request.session.update(session) return HttpResponse(json.dumps(data), content_type="application/json") elif 'error' in res: if res['error'] == "REDEEMREWARD_NOT_FOUND": if action == "approve": data["result"] = 7 elif action == "deny": data["result"] = 5 elif res['error'] == "REDEEMREWARD_VALIDATED": data["result"] = 3 elif res['error'] == "PATRONSTORE_REMOVED": data["result"] = 6 # always make sure to get the latest session since the session # will be saved on return!!! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return HttpResponse(json.dumps(data), content_type="application/json")
def account_is_owner(session): return SESSION.get_store(session).is_owner(session['account'])
def register(request): """ Adds a new employee to the currently logged in Store. This automatically sets this employee to approved. """ data = {'employees_nav': True} settings = SESSION.get_settings(request.session) store = SESSION.get_store(request.session) if request.method == "POST": from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = request.POST.get('aaf-nonce') aaf_account_id = request.POST.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True account_form = EmployeeAccountSignUpForm(request.POST) employee_form = EmployeeForm(request.POST) if not from_associated_account: all_forms_valid = account_form.is_valid() and\ employee_form.is_valid() else: all_forms_valid = True if all_forms_valid: postDict = request.POST.dict() # make the cloud call # see cloud param for possible access level values acl = postDict['acl'] if acl == ACCESS_ADMIN[0]: access_level = "admin" elif acl == ACCESS_PUNCHREDEEM[0]: access_level = "punch_redeem" else: access_level = None params = { "retailer_pin": settings.get("retailer_pin"), "username": postDict['email'].strip().lower(), "first_name": postDict['first_name'].capitalize(), "last_name": postDict['last_name'].capitalize(), "email": postDict['email'].strip().lower(), "status": APPROVED, "access_level": access_level, } if from_associated_account: res = cloud_call("link_employee", params) else: params["password"] = postDict['password'] res = cloud_call("register_employee", params) # don't forget to retrieve the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if "error" in res and res['error'] in ("EMAIL_TAKEN_AVAILABLE", "USERNAME_TAKEN_AVAILABLE"): aa = Account.objects().get(email=postDict['email'].strip().lower()) aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") elif "error" in res and res['error'] in ("EMAIL_TAKEN", "USERNAME_TAKEN"): account_form._errors.setdefault("email", ErrorList()).append(u"Email is already being used.") elif "error" not in res: # add the employee to the approved list employees_approved_list =\ SESSION.get_employees_approved_list(request.session) employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] new_employee = Employee(**res["result"]) if new_employee.objectId not in employees_approved_ids: employees_approved_list.insert(0, new_employee) request.session['employees_approved_list'] =\ employees_approved_list # update our local store's acl - don't wait for # the cloud post store = SESSION.get_store(request.session) store.set_access_level(Account.objects().get(\ email=postDict['email'].strip().lower()), acl) request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":store.jsonify() } comet_receive(store.objectId, payload) return HttpResponse(json.dumps({"code": 2}), content_type="application/json") else: return HttpResponse(json.dumps({"code": 3}), content_type="application/json") else: employee_form = EmployeeForm(initial={"acl":\ ACCESS_PUNCHREDEEM[0]}) account_form = EmployeeAccountSignUpForm() data["employee_form"] = employee_form data["account_form"] = account_form return render(request, 'manage/employee_register.djhtml', data)
def register(request): """ Adds a new employee to the currently logged in Store. This automatically sets this employee to approved. """ data = {'employees_nav': True} settings = SESSION.get_settings(request.session) store = SESSION.get_store(request.session) if request.method == "POST": from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = request.POST.get('aaf-nonce') aaf_account_id = request.POST.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True account_form = EmployeeAccountSignUpForm(request.POST) employee_form = EmployeeForm(request.POST) if not from_associated_account: all_forms_valid = account_form.is_valid() and\ employee_form.is_valid() else: all_forms_valid = True if all_forms_valid: postDict = request.POST.dict() # make the cloud call # see cloud param for possible access level values acl = postDict['acl'] if acl == ACCESS_ADMIN[0]: access_level = "admin" elif acl == ACCESS_PUNCHREDEEM[0]: access_level = "punch_redeem" else: access_level = None params = { "retailer_pin": settings.get("retailer_pin"), "username": postDict['email'].strip().lower(), "first_name": postDict['first_name'].capitalize(), "last_name": postDict['last_name'].capitalize(), "email": postDict['email'].strip().lower(), "status": APPROVED, "access_level": access_level, } if from_associated_account: res = cloud_call("link_employee", params) else: params["password"] = postDict['password'] res = cloud_call("register_employee", params) # don't forget to retrieve the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if "error" in res and res['error'] in ("EMAIL_TAKEN_AVAILABLE", "USERNAME_TAKEN_AVAILABLE"): aa = Account.objects().get( email=postDict['email'].strip().lower()) aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") elif "error" in res and res['error'] in ("EMAIL_TAKEN", "USERNAME_TAKEN"): account_form._errors.setdefault( "email", ErrorList()).append(u"Email is already being used.") elif "error" not in res: # add the employee to the approved list employees_approved_list =\ SESSION.get_employees_approved_list(request.session) employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] new_employee = Employee(**res["result"]) if new_employee.objectId not in employees_approved_ids: employees_approved_list.insert(0, new_employee) request.session['employees_approved_list'] =\ employees_approved_list # update our local store's acl - don't wait for # the cloud post store = SESSION.get_store(request.session) store.set_access_level(Account.objects().get(\ email=postDict['email'].strip().lower()), acl) request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify() } comet_receive(store.objectId, payload) return HttpResponse(json.dumps({"code": 2}), content_type="application/json") else: return HttpResponse(json.dumps({"code": 3}), content_type="application/json") else: employee_form = EmployeeForm(initial={"acl":\ ACCESS_PUNCHREDEEM[0]}) account_form = EmployeeAccountSignUpForm() data["employee_form"] = employee_form data["account_form"] = account_form return render(request, 'manage/employee_register.djhtml', data)
def account_is_owner(session): return SESSION.get_store(session).is_owner(session['account'])
def pull(request): """ This is where the comet approach is put into play. This handles ajax requests from clients, holding on to the request while checking Parse for new activity. IMPORTANT! The order in which the session cache is checked is very critical. Take for example and employee that registers. Dashboard A receives the pending employee and immediately approves it. Now Dashboard B will run pull with the pending employee and the approved employee. We must first add the pending then check for the approved! """ def comet(session_copy): # used by more than 1 (note that it is ok to retrieve all of # the lists since they are all pointers - not the actual list! employees_pending_list_copy =\ SESSION.get_employees_pending_list(session_copy) employees_approved_list_copy =\ SESSION.get_employees_approved_list(session_copy) messages_received_list_copy =\ SESSION.get_messages_received_list(session_copy) redemptions_pending_copy =\ SESSION.get_redemptions_pending(session_copy) redemptions_past_copy =\ SESSION.get_redemptions_past(session_copy) # this is the latest session data session = SessionStore(request.session.session_key) employees_pending_list =\ SESSION.get_employees_pending_list(session) employees_approved_list =\ SESSION.get_employees_approved_list(session) messages_received_list =\ SESSION.get_messages_received_list(session) redemptions_pending =\ SESSION.get_redemptions_pending(session) redemptions_past =\ SESSION.get_redemptions_past(session) # put the diffs between session_copy and session here data = {} ############################################################# # FEEDBACKS_UNREAD ################################## fbs_unread_copy = [ fb.objectId for fb in\ messages_received_list_copy if not fb.is_read ] fbs_unread = [ fb.objectId for fb in\ messages_received_list if not fb.is_read ] # get the difference between the two feedbacks_unread =\ tuple(set(fbs_unread) - set(fbs_unread_copy)) if feedbacks_unread: fb_unread = [] messages_received_ids =\ [ fb.objectId for fb in messages_received_list ] for feedback_id in feedbacks_unread: for fb in messages_received_list: if fb.objectId == feedback_id: fb_unread.append(fb.jsonify()) break if len(fb_unread) > 0: fb_count = 0 for fb in messages_received_list: if not fb.get("is_read"): fb_count += 1 data['feedbacks_unread'] = fb_unread data['feedback_unread_count'] = fb_count ############################################################# # EMPLOYEES_PENDING ################################## # must also check if employee is already approved! emps_pending_copy = [ emp.objectId for emp in employees_pending_list_copy ] emps_pending = [ emp.objectId for emp in employees_pending_list ] employees_pending =\ tuple(set(emps_pending) - set(emps_pending_copy)) if employees_pending: pending = [] for emp_id in employees_pending: for emp in employees_pending_list: if emp.objectId == emp_id: pending.append(emp.jsonify()) break if len(pending) > 0: data['employees_pending_count'] =\ len(employees_pending_list) data['employees_pending'] = pending ############################################################# # EMPLOYEES APPROVED (pending to approved) ################# emps_approved_copy = [ emp.objectId for emp in\ employees_approved_list_copy] emps_approved = [ emp.objectId for emp in\ employees_approved_list] appr_emps =\ tuple(set(emps_approved) - set(emps_approved_copy)) if appr_emps: approved = [] for appr_emp_id in appr_emps: for emp in employees_approved_list: if emp.objectId == appr_emp_id: approved.append(emp.jsonify()) break if len(approved) > 0: data['employees_approved'] = approved data['employees_pending_count'] =\ len(employees_pending_list) ############################################################# # EMPLOYEES DELETED/DENIED/REJECTED (pending/approved to pop)! # need to compare approved and pending! emps_copy = emps_approved_copy[:] emps_copy.extend(emps_pending_copy) emps = emps_approved[:] emps.extend(emps_pending) # emps_copy has the same or more items that emps del_emps = tuple(set(emps_copy) - set(emps)) if del_emps: deleted = [] for demp_id in del_emps: if demp_id in emps_approved_copy: emps_list = employees_approved_list_copy else: emps_list = employees_pending_list_copy for emp in emps_list: if emp.objectId == demp_id: deleted.append(emp.jsonify()) break if len(deleted) > 0: data['employees_pending_count'] =\ len(employees_pending_list) data['employees_deleted'] = deleted ############################################################# # REDEMPTIONS PENDING reds_pending_copy = [ r.objectId for r in\ redemptions_pending_copy ] reds_pending = [ r.objectId for r in redemptions_pending ] reds = tuple(set(reds_pending) - set(reds_pending_copy)) if reds: redemps = [] for r_id in reds: for redemp in redemptions_pending: if redemp.objectId == r_id: redemps.append(redemp.jsonify()) break if len(redemps) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_pending'] = redemps ############################################################# # REDEMPTIONS APPROVED (pending to history) reds_past_copy = [ r.objectId for r in\ redemptions_past_copy ] reds_past = [ r.objectId for r in redemptions_past ] appr_redemps =\ tuple(set(reds_past) - set(reds_past_copy)) if appr_redemps: redemp_js = [] for red_id in appr_redemps: for redemp in redemptions_past: if redemp.objectId == red_id: redemp_js.append(redemp.jsonify()) break if len(redemp_js) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_approved'] = redemp_js ############################################################# # REDEMPTIONS DELETED ############################## # remove from pending (should not be in history!) reds_copy = reds_past_copy[:] reds_copy.extend(reds_pending_copy) reds = reds_past[:] reds.extend(reds_pending) # reds_copy has the same or more items that reds del_redemps = tuple(set(reds_copy) - set(reds)) if del_redemps: redemp_js = [] for red_id in del_redemps: reds_list = [] if red_id in reds_past_copy: reds_list = redemptions_past_copy elif red_id in reds_pending_copy: reds_list = redemptions_pending_copy for redemp in reds_list: if redemp.objectId == red_id: redemp_js.append(redemp.jsonify()) break if len(redemp_js) > 0: data['redemption_pending_count'] =\ len(redemptions_pending) data['redemptions_deleted'] = redemp_js ############################################################# # SETTINGS UPDATED ############################## settings_copy = session_copy.get("settings") settings = session.get("settings") if settings_copy.get("retailer_pin") !=\ settings.get("retailer_pin"): data['retailer_pin'] = settings.get("retailer_pin") ############################################################# # REWARDS UPDATED ############################## rewards_copy = session_copy.get("store").get("rewards") rewards_copy =\ { reward['reward_id']:reward for reward in rewards_copy } rewards = session.get("store").get("rewards") rewards = { reward['reward_id']:reward for reward in rewards } updated_rewards = [] for reward_id, rew_copy in rewards_copy.iteritems(): # Note that some rewards may have been deleted! rew = rewards.get(reward_id) if rew and rew_copy['redemption_count'] !=\ rew['redemption_count']: # only the redemtpion_count and reward_id are used # in the client side updated_rewards.append({ "reward_id": reward_id, "redemption_count": rew['redemption_count'], }) if updated_rewards: data['rewards'] = updated_rewards ############################################################# # PATRONSTORE_COUNT ################################## patronStore_count_copy =int(session_copy["patronStore_count"]) patronStore_count = int(session["patronStore_count"]) if patronStore_count_copy != patronStore_count: data['patronStore_count'] = patronStore_count ############################################################# # ACTIVE_STORE_LOCATION_ID ############################ if session['active_store_location_id'] !=\ session_copy['active_store_location_id']: data['active_store_location_id'] =\ session['active_store_location_id'] # IMPORTANT! The request.session is the same as the # SessionStore(session_key)! so we must use the # request.session because it is automatically saved at the end # of each request- thereby overriding/undoing any changes made # to the SessionStore(session_key) key! # need to check if we are still logged in session = SessionStore(request.session.session_key) if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) ############################################################ # Respond ########################################### try: return HttpResponse(json.dumps(data), content_type="application/json") except (IOError, socket.error) as e: # broken pipe/socket. thread.exit() # exit silently ################################################################## ##### ENTRY POINT ###################################################### # get the timestamp and uid t = parser.parse(request.GET["timestamp"]) timestamp = str(t.hour).zfill(2) + ":" +\ str(t.minute).zfill(2) + ":" + str(t.second).zfill(2) uid = request.GET['uid'] # update the last_updated field of the CometSessionIndex try: csi = CometSessionIndex.objects.get(session_key=\ request.session.session_key) csi.last_updated = timezone.now() csi.save() except CometSessionIndex.DoesNotExist: # should never go here but just in case. CometSessionIndex.objects.create(session_key=\ request.session.session_key, store_id=SESSION.get_store(request.session).objectId, last_updated=timezone.now()) # register the CometSession CometSession.objects.update() CometSession.objects.create(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) # cache the current session at this state session_copy = dict(request.session) timeout_time = timezone.now() + relativedelta(seconds=REQUEST_TIMEOUT) # keep going until its time to return a response forcibly while timezone.now() < timeout_time: # need to update he objects manager to get the latest objects CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) except CometSession.DoesNotExist: # cometsession was deleted - time to go try: # make sure that the latest session is saved! # need to check if we are still logged in session = SessionStore(request.session.session_key) if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) return HttpResponse(json.dumps({"result":-1}), content_type="application/json") except (IOError, socket.error) as e: thread.exit() if scomet.modified: # delete the registered comet session object CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing try: return comet(session_copy) except KeyError: # if a key error occurs then that probably means that # the session has been flushed- was logged out by user # or forcefully by server =) # now time to flag existing tabs. request.session.clear() try: return HttpResponse(json.dumps({"result": -3}), content_type="application/json") except (IOError, socket.error) as e: # broken pipe/socket. thread.exit() # exit silently else: # nothing new, sleep for a bit sleep(COMET_PULL_RATE) # TIME IS UP - return a response result 0 means no change # try 1 last time if scomet.modified: # delete the registered comet session object CometSession.objects.update() try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing return comet(session_copy) # make sure that request.session is the most up to date session = SessionStore(request.session.session_key) # need to check if we are still logged in if 'account' in session and SESSION_KEY in session: request.session.clear() request.session.update(session) else: flush(request.session) # attempt to delete registered comet session if not yet deleted try: scomet = CometSession.objects.get(session_key=\ request.session.session_key, timestamp=timestamp, uid=uid) scomet.delete() except CometSession.DoesNotExist: pass # do nothing try: return HttpResponse(json.dumps({"result":0}), content_type="application/json") except (IOError, socket.error) as e: thread.exit() # exit silently
def employee_is_owner(session, employee_id): account = Account.objects().get(Employee=employee_id) return SESSION.get_store(session).is_owner(account)
def account_is_admin(session): return SESSION.get_store(session).is_admin(session['account'])
def trends_graph(request, data_type=None, start=None, end=None ): """ Handles requests for the trends graph. """ store = SESSION.get_store(request.session) # We need the store's timezone to convert everything to UTC # because the time we get from start and end are local times # and in order to convert to UTC we must first make start and end # timezone aware. We use parse.utils.make_aware_to_utc to do # this in 1 step. We convert everything to UTC for use in queries. store_timezone = SESSION.get_store_timezone(request.session) start = datetime.strptime(start, "%Y-%m-%d") end = datetime.strptime(end, "%Y-%m-%d") start = start.replace(hour=0, minute=0, second=0, microsecond=0) end = end.replace(hour=23, minute=59, second=59, microsecond=0) start_aware = make_aware_to_utc(start, store_timezone) end_aware = make_aware_to_utc(end, store_timezone) rows, columns = [], [] if data_type == 'punches': # we need to return graph data for punches for all days in # between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with punch count columns = [ {"id":"", "label":"Date", "type":"string"}, {"id":"", "label":'Punches', "type":"number"} ] # get the Punches punches = store.get('punches', createdAt__lte=end_aware, createdAt__gte=start_aware,order='createdAt', limit=900) # have to clear the punches cache attr of store filled # by the above query store.punches = None #create dictionary for easy search punch_map = {} if punches: for punch in punches: # The keys in the punch map is the month/day of the # createdAt of the punch object. We also convert it # to the store's local time for when we send back the # data to the client. key = timezone.localtime(punch.createdAt, store_timezone).strftime("%m/%d") if key in punch_map: # add to the punch count for the existing key punch_map[key] =\ punch_map[key] + punch.get('punches') else: # initialize the key in the punch map punch_map[key] = punch.get('punches') for single_date in rputils.daterange(start, end): # we now populate the rows with the corresponding punch counts # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") try: punch_count = punch_map[str_date] except KeyError: punch_count = 0 # the first item in our row is the date # the second item is the corresponding punch_count row = [{"v": str_date}, {"v": punch_count}] rows.append({'c': row}) elif data_type == 'facebook': # we need to return graph data for facebook posts for # all days in between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with post count columns = [ {"id":"", "label":"Date", "type":"string"}, {"id":"", "label":'Posts', "type":"number"} ] # get the FacebookPosts posts = store.get("facebookPosts", createdAt__lte=end, createdAt__gte=start, limit=900) # have to clear the facebookPosts cache attr of store filled # by the above query store.facebookPosts = None #create dictionary for easy search post_map = {} if posts: for post in posts: # The keys in the post map is the month/day of the # createdAt of the punch object. We also convert it # to the store's local time for when we send back the # data to the client. key = timezone.localtime(post.createdAt, store_timezone).strftime("%m/%d") if key in post_map: # add to the post count for the existing key post_map[key] = post_map[key] + 1 else: # initialize the post count post_map[key] = 1 for single_date in rputils.daterange(start, end): # we now populate the rows with the corresponding post counts # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") try: post_count = post_map[str_date] except KeyError: post_count = 0 # the first item in our row is the date # the second item is the corresponding post_count row = [{"v": str_date}, {"v": post_count}] rows.append({'c': row}) else: # we need to return graph data for unique patrons for # all days in between start and end. # columns contains the data that any given row needs to have. # rows contains multiple dates paired with accumulative patron count columns = [ {"id":"", "label":"Date", "type":"string"}, {"id":"", "label":'Patrons', "type":"number"} ] for single_date in rputils.daterange(start, end): # str_date is a day in between start and end with the # same format as a key in our punch_map str_date = single_date.strftime("%m/%d") # FIXME In order to get the cumulative count for each day, # we make a query of the count for each day. Optimization? d = single_date.replace(hour=23, minute=59, second=59) d_aware = make_aware_to_utc(d, store_timezone) patron_count = store.get('patronStores', count=1, limit=0, createdAt__lte=d_aware) row = [{"v": str_date}, {"v": patron_count}] rows.append({'c': row}) # return the graph data return HttpResponse(json.dumps({'cols': columns, 'rows': rows}), content_type="application/json")
def employee_is_owner(session, employee_id): account = Account.objects().get(Employee=employee_id) return SESSION.get_store(session).is_owner(account)
def feedback_reply(request, feedback_id): """ Render the feedback reply template. """ account = request.session['account'] store = SESSION.get_store(request.session) # data to be passed in the templace context data = { 'messages_nav': True, 'from_address': store.get("store_name"), } # get from the messages_received_list in session cache messages_received_list = SESSION.get_messages_received_list(\ request.session) i_remove, feedback = 0, None for ind, m in enumerate(messages_received_list): if m.objectId == feedback_id: feedback = m i_remove = ind break if not feedback: # feedack not found - redirect to messages page with error message return redirect(reverse('messages_index')+ "?%s" %\ urllib.urlencode({'error': 'Feedback not found.'})) if request.method == 'POST': # user submitted reply form body = request.POST.get('body') if body is not None: # strip the body so that it doesn't have trailing or # leading whitespaces body = body.strip() else: body = "" data['body'] = body if len(body) == 0: # body cannot be empty data['error'] = 'Please enter a message.' elif len(body) > 750: # body cannot exceed 750 cahracters data['error'] = 'Body must be less than 750 characters.' elif feedback.get('Reply'): # double check if feedback already has a reply # should not go here unless it is a hacker return redirect(reverse('messages_index')+ "?%s" %\ urllib.urlencode({'error':\ 'Feedback has already been replied to.'})) else: # all valid - this method of validation is dirty and not # the way to do it in Django. Use a form instead. # I just got lazy here. # create the Parse Message object msg = Message.objects().create(message_type=\ FEEDBACK, sender_name=store.get('store_name'), store_id=store.objectId, body=body) # add the created reply to the store's sent messages relation store.add_relation("SentMessages_", [msg.objectId]) # set feedback Reply pointer to message and update it feedback.set('Reply', msg.objectId) feedback.update() # store the updated feedback messages_received_list.pop(i_remove) messages_received_list.insert(i_remove, feedback) request.session['messages_received_list'] =\ messages_received_list # save the session now! cloud_call may take a bit! request.session.save() # make the cloud call cloud_call( "retailer_message", { "store_id": store.objectId, "store_name": store.get('store_name'), "message_id": feedback.objectId, "filter": 'one', "patron_id": feedback.get('patron_id'), "feedback_reply_body": body, }) # notify other tabs/browsers logged into the same store # about the newly created message. comet_receive( store.objectId, { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "newMessage": feedback.jsonify() }) # make sure we have the latest session to save! request.session.clear() request.session.update(SessionStore(request.session.session_key)) return redirect(reverse('feedback_details', args=(feedback.objectId,)) + "?%s" %\ urllib.urlencode({'success':'Reply has been sent.'})) else: # user navigated to this page if feedback.get("Reply"): # if the user manually tweaks the url, then s/he might be # able to reply to a feedback that already has a reply. return redirect(reverse('feedback_details', args=(feedback.objectId,)) + "?%s" %\ urllib.urlencode({'error':'Cannot reply more than once.'})) # update store session cache request.session['store'] = store data['feedback'] = feedback # store the updated feedback messages_received_list.pop(i_remove) messages_received_list.insert(i_remove, feedback) request.session['messages_received_list'] =\ messages_received_list return render(request, 'manage/feedback_reply.djhtml', data)