Пример #1
0
def register_rand_employee(store_id):
    name = "Employee" + str(randint(0, 9999))
    username = name + "@repunch.com"
    store = Store.objects().get(objectId=store_id, include="Settings")

    return register_employee(name, name, username, username, "repunch7575",
                             store.settings.retailer_pin)
Пример #2
0
def register_rand_employee(store_id):
    name = "Employee" + str(randint(0,9999))
    username = name+"@repunch.com"
    store = Store.objects().get(objectId=store_id, include="Settings")
    
    return register_employee(name, name, username, 
        username, "repunch7575", store.settings.retailer_pin)
Пример #3
0
def activate(request):
    """
    Handles account activation from email form sent at user sign up.
    """
    if request.method == "POST":
        store_id = request.POST['store_id']
        act_id = request.POST['act_id']
        act = StoreActivate.objects.filter(id=act_id,
                store_id=store_id)
        if len(act) > 0:
            act[0].delete()
            store = Store.objects().get(objectId=store_id)
            if store:
                store.active = True
                store.update()
                return HttpResponse(store.get(\
                    "store_name").capitalize() +\
                    " has been activated.")
            else:
                return HttpResponse("Account/store not found.")  
        else:  
            return HttpResponse("This form has already "+\
                "been used.")                
    
    return HttpResponse("Bad request")
Пример #4
0
def store_avatar_to_thumbnail():
    """
    Created column thumbnail_image in Store.
    This just copies the store_avatar in Store to thumbnail_image.
    The store_avatar column is kept for backwards compatibility.
    """
    for i, store in enumerate(Store.objects().filter(limit=999)):
        store.thumbnail_image = store.store_avatar
        store.update()
        
        print "Updated Store #" + str(i) + ": " + store.objectId
Пример #5
0
def supported_chain_stores():
    """
    Created the StoreLocation class, which contains Store location
    information. A Store may have multiple StoreLocations.
    Punches also now have store_location_id.
    
    This assigns a new StoreLocation to the store_locations aray of pointers of each store.
    All existing Punches are also assigned the newly created StoreLocation's id.
    All existing RedeemRewards are also assigned the newly created StoreLocation's id.
    
    WARNING! Assumes this assumes that # Stores < 1000 and that each store
    has less than 1000 punches/redeemRewards in their respective relations.
    """
    for i, store in enumerate(Store.objects().filter(limit=999)):
    
        if store.store_locations: # just in case we re-run this script
            store_location = store.store_locations[0]
            
        else: # empty or None
            store_location = StoreLocation.objects().create(**{
                "street": store.street,
                "city": store.city,
                "state": store.state,
                "zip": store.zip,
                "country": store.country,
                "phone_number": store.phone_number,
                "store_timezone": store.store_timezone,
                "neighborhood": store.neighborhood,
                "coordinates": store.coordinates,
                "hours": store.hours,
                "Store": store.objectId,
            })
            store.store_locations = [store_location]
            store.update()
        
        # update all the punches for this store
        punches = store.get("punches", order="createdAt", limit=1000)
        if punches:
            for punch in punches:
                if not punch.store_location_id:
                    punch.store_location_id = store_location.objectId
                    punch.update()
                
        # update all the redeemRewards for this store
        redeem_rewards = store.get("redeemRewards", order="createdAt", limit=1000)
        if redeem_rewards:
            for rr in redeem_rewards:
                if not rr.store_location_id:
                    rr.store_location_id = store_location.objectId
                    rr.update()
        
        print "Updated Store #" + str(i) + ": " + store.objectId
Пример #6
0
def get_store_locations(session):
    """ limit of 100 store locations for now """
    if "store_locations" not in session:
        store = get_store(session)
        store_tmp = Store.objects().get(objectId=store.objectId,
            include="store_locations")
            
        store_locations = {}
        for sl in store_tmp.store_locations:
            store_locations[sl.objectId] = sl
            session['store_locations'] = store_locations

    return session['store_locations']
Пример #7
0
    def resize_thumbnails(self):
        for i, store in enumerate(Store.objects().filter(limit=999)):
            old_thumbnail = store.thumbnail_image
            self.get_store_location_image(0, store.thumbnail_image_url)
            thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)
            while "error" in thumbnail:
                print "Retrying create_png"
                thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)

            store.thumbnail_image = thumbnail.get("name")
            store.store_avatar = store.thumbnail_image
            store.update()

            # delete thumbnail if exist
            if old_thumbnail:
                delete_file(old_thumbnail, "image/png")

            print "Updated Store #%d: %s" % (i, store.objectId)
Пример #8
0
 def resize_thumbnails(self):
     for i, store in enumerate(Store.objects().filter(limit=999)):
         old_thumbnail = store.thumbnail_image
         self.get_store_location_image(0, store.thumbnail_image_url)
         thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)
         while "error" in thumbnail:
             print "Retrying create_png"
             thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)
         
         store.thumbnail_image = thumbnail.get("name")
         store.store_avatar = store.thumbnail_image
         store.update()
         
         # delete thumbnail if exist
         if old_thumbnail:
             delete_file(old_thumbnail, "image/png")
             
         
         print "Updated Store #%d: %s" % (i, store.objectId)
Пример #9
0
def activate(request):
    """
    Handles account activation from email form sent at user sign up.
    """
    if request.method == "POST":
        store_id = request.POST['store_id']
        act_id = request.POST['act_id']
        act = StoreActivate.objects.filter(id=act_id, store_id=store_id)
        if len(act) > 0:
            act[0].delete()
            store = Store.objects().get(objectId=store_id)
            if store:
                store.active = True
                store.update()
                return HttpResponse(store.get(\
                    "store_name").capitalize() +\
                    " has been activated.")
            else:
                return HttpResponse("Account/store not found.")
        else:
            return HttpResponse("This form has already "+\
                "been used.")

    return HttpResponse("Bad request")
Пример #10
0
def sign_up(request):
    """
    Creates User, store, subscription, and settings objects.
    """
    # renders the signup page on GET and returns a json object on POST.
    data = {'sign_up_nav': True}

    if request.method == 'POST':
        # this conversion to a regular dictionay is important
        postDict = request.POST.dict()

        from_associated_account = False
        # check if this post is from the associated account dialog
        # if it is then skip form validations
        aaf_nonce_id = postDict.get('aaf-nonce')
        aaf_account_id = postDict.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

        # some keys are repeated so must catch this at init
        store_form = StoreSignUpForm(request.POST)
        store_location_form = StoreLocationForm(request.POST)
        account_form = AccountSignUpForm(request.POST)

        cats = postDict.get("categories")
        category_names = None
        if cats and len(cats) > 0:
            category_names = cats.split("|")[:-1]
            # make sure that there are only up to 2 categories
            while len(category_names) > 2:
                category_names.pop()
            data["category_names"] = category_names

        if not from_associated_account:
            all_forms_valid = store_form.is_valid() and\
                store_location_form.is_valid() and account_form.is_valid()
        else:
            all_forms_valid = True

        if all_forms_valid:
            # 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 hasattr(account_form, "associated_account"):
                aa = account_form.associated_account
                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")
            #########################################################

            # create store
            store = Store(**postDict)
            # set defaults for these guys to prevent
            # ParseObjects from making parse calls repeatedly
            store.punches_facebook = 1
            store.set("rewards", [])
            store.set("categories", [])
            if category_names:
                for name in category_names:
                    alias = Category.objects.filter(name__iexact=name)
                    if len(alias) > 0:
                        store.categories.append({
                            "alias": alias[0].alias,
                            "name": name
                        })

            # create settings
            settings = Settings(Store=store.objectId)
            store.set('settings', settings)

            # create account
            if not from_associated_account:
                account = Account(**postDict)
                # username = email
                # we should be doing this in the form but ehh
                account.set("username", postDict['email'].strip().lower())
                account.set("email", postDict['email'].strip().lower())
                account.set_password(postDict.get('password'))
            else:
                account =\
                    Account.objects().get(objectId=aaf_account_id)

            account.set("store", store)

            # create subscription
            subscription = Subscription()
            subscription.subscriptionType = 0
            subscription.date_last_billed = timezone.now()
            subscription.create()

            # create settings
            settings.create()

            # create store
            store.Settings = settings.objectId
            store.Subscription = subscription.objectId
            store.create()

            # add the pointer to the created store
            settings.Store = store.objectId
            settings.update()
            subscription.Store = store.objectId
            subscription.update()

            # create the store location
            store_location = StoreLocation(**postDict)
            # format the phone number
            store_location.store_timezone =\
                rputils.get_timezone(postDict.get("zip")).zone
            store_location.set("hours", [])
            # coordinates and neighborhood
            # the call to get map data is actually also in the clean
            full_address = " ".join(\
                store_location.get_full_address().split(", "))
            map_data = rputils.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")))
            store_location.phone_number =\
                format_phone_number(postDict["phone_number"])
            store_location.Store = store.objectId
            store_location.create()

            # add the StoreLocation to the relation
            store.store_locations = [store_location]
            store.update()

            # create account
            account.Store = store.objectId
            if not from_associated_account:
                account.create()
            else:
                account.update()

            # create the store ACL with the account having r/w access
            store.ACL = {
                "*": {
                    "read": True,
                    "write": True
                },
                account.objectId: {
                    "read": True,
                    "write": True
                },
            }
            store.owner_id = account.objectId
            store.update()

            # note that username has been fed the email
            # this shouldn't change anything though shouldn't matter
            # need to put username and pass in request
            postDict['username'] = account.username
            postDict['password'] = account.password

            # send matt and new user a pretty email.
            send_email_signup(account)

            # auto login
            user_login = login(request, postDict, no_recaptcha=True)
            if user_login != None:
                data = {"code": -1}
                # response to signup.js - not login returns
                # 0 - Associated account already exists
                # 2 - subscription is not active
                # 3 - success (login now)
                if type(user_login) is int:  # subscription not active
                    data['code'] = 2
                else:
                    # required for datetime awareness!
                    rputils.set_timezone(request, tz)
                    data['code'] = 3
                return HttpResponse(json.dumps(data),
                                    content_type="application/json")

    else:
        store_form = StoreSignUpForm()
        store_location_form = StoreLocationForm()
        account_form = AccountSignUpForm()

    data['store_form'] = store_form
    data['store_location_form'] = store_location_form
    data['account_form'] = account_form
    return render(request, 'public/signup.djhtml', data)
Пример #11
0
    def create_random_stores(self, amount):
        for i in range(amount):
            print "Creating store %s" % (str(i), )
            # create the store
            street, city, state, zip, country, phone_number =\
                self.addrs[i].split(", ")
            first_name, last_name = self.owners[i].split(" ")
            neighborhood = self.neighborhoods[i]
            store_name = self.stores[i]
            store_i = STORE.copy()
            store_location_i = STORE_LOCATION.copy()

            # create the thumbnaiil and cover (same image different size)
            self.get_store_location_image(i)

            thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)
            while "error" in image:
                print "Retrying create_png"
                thumbnail = create_png(TMP_IMG_PATH)

            cover = create_png(TMP_IMG_PATH)
            while "error" in cover:
                print "Retrying create_png"
                cover = create_png(TMP_IMG_PATH)

            store_i.update({
                "store_name": store_name,
                "first_name": first_name,
                "last_name": last_name,
                "thumbnail_image": thumbnail.get("name"),
                "cover_image": cover.get("name"),
            })
            store_location_i.update({
                "street":
                street,
                "city":
                city,
                "state":
                state,
                "zip":
                zip,
                "neighborhood":
                neighborhood,
                "country":
                country,
                "phone_number":
                phone_number,
                "coordinates":
                self.get_random_coordinates(),
            })

            # create the store
            store = Store.objects().create(**store_i)

            # create the store location
            store_location = StoreLocation(**store_location_i)
            store_location.Store = store.objectId
            store_location.update()

            # create the settings
            settings = Settings.objects().create(Store=store.objectId)

            # create the subscription
            subscription =\
                Subscription.objects().create(Store=store.objectId,
                    date_last_billed=timezone.now())

            # create the user
            email = first_name + str(randint(0, 99)) + USER_EMAIL_POSTFIX
            email = email.lower()
            acc = Account.objects().create(\
                username=email, email=email,
                password=USER_PASSWORD, Store=store.objectId)
            if not acc.objectId:
                raise Exception("Account creation failed.")

            # link the store
            store.Settings = settings.objectId
            store.Subscription = subscription.objectId
            store.owner_id = acc.objectId
            store.ACL[acc.objectId] = {"read": True, "write": True}
            store.store_locations = [store_location]
            store.update()
Пример #12
0
    def processCometReceivedDict(session, postDict):
        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)
        
        #############################################################
        # FEEDBACKS_UNREAD ##################################
        newFeedback = postDict.get('newFeedback')
        if newFeedback:
            messages_received_ids =\
                [ fb.objectId for fb in messages_received_list ]
            m = Message(**newFeedback)
            if m.objectId not in messages_received_ids:
                messages_received_list.insert(0, m)
                
            session['messages_received_list'] =\
                messages_received_list

        #############################################################
        # FEEDBACK DELETED ##################################
        deletedFeedback = postDict.get("deletedFeedback")
        if deletedFeedback:
            fb = Message(**deletedFeedback)
            for i, mro in enumerate(messages_received_list):
                if fb.objectId == mro.objectId:
                    messages_received_list.pop(i)
                    break
            session['messages_received_list'] =\
                messages_received_list
            
            
        #############################################################
        # MESSAGE SENT ##################################
        # need to check if this new message is an original message 
        # or a reply to a feedback (the message sent by the patron)!
        # also may increment the message count!
        newMessage = postDict.get("newMessage")
        if newMessage:
            messages_received_ids =\
                    [ fb.objectId for fb in messages_received_list ]
            messages_sent_list =\
                SESSION.get_messages_sent_list(session)
            messages_sent_ids =\
                [ msg.objectId for msg in messages_sent_list ]
            m = Message(**newMessage)
            if m.objectId not in messages_sent_ids and\
                m.message_type != FEEDBACK:
                messages_sent_list.insert(0, m)
                if 'message_count' in session:
                    session['message_count'] =\
                        int(session['message_count']) + 1
            # update an existing feedback
            if m.objectId in messages_received_ids and\
                m.message_type == FEEDBACK:
                for i, mrl in enumerate(messages_received_list):
                    if mrl.objectId == m.objectId:
                        messages_received_list.pop(i)
                        messages_received_list.insert(i, m)
                        break
            session['messages_received_list'] =\
                messages_received_list
            session['messages_sent_list'] = messages_sent_list
        
        #############################################################
        # EMPLOYEES_PENDING ##################################
        # must also check if employee is already approved!
        pendingEmployee = postDict.get("pendingEmployee")
        if pendingEmployee:
            employees_approved_ids =\
                [ emp.objectId for emp in employees_approved_list ]
            employees_pending_ids =\
                [ emp.objectId for emp in employees_pending_list ]
            e = Employee(**pendingEmployee)
            if e.objectId not in employees_pending_ids and\
                e.objectId not in employees_approved_ids:
                employees_pending_list.insert(0, e)
                
            session['employees_pending_list'] =\
                employees_pending_list
        
        #############################################################
        # EMPLOYEES APPROVED (pending to approved) #################
        approvedEmployee = postDict.get("approvedEmployee")
        if approvedEmployee:
            emp = Employee(**approvedEmployee)
            # first check if the employee is in the pending list
            # if not then check if it is already approved
            for i, emp_pending in\
                enumerate(employees_pending_list):
                if emp.objectId == emp_pending.objectId:
                    emp = employees_pending_list.pop(i)
                    emp.status = APPROVED
                    employees_approved_list.insert(0, emp)
                    break
                
            session['employees_pending_list'] =\
                employees_pending_list
            session['employees_approved_list'] =\
                employees_approved_list
                
        #############################################################
        # EMPLOYEES NEW (straight to approved) #################
        newEmployee = postDict.get("newEmployee")
        if newEmployee:
            employees_approved_ids =\
                [ emp.objectId for emp in employees_approved_list ]
            emp = Employee(**newEmployee)
            if emp.objectId not in employees_approved_ids:
                employees_approved_list.insert(0, emp)
                session['employees_approved_list'] =\
                    employees_approved_list
            
        #############################################################
        # EMPLOYEES DELETED/DENIED/REJECTED (pending/approved to pop)!
        deletedEmployee = postDict.get("deletedEmployee")
        if deletedEmployee:
            emp = Employee(**deletedEmployee)
            # check in approved emps
            for i, cop in enumerate(employees_approved_list):
                if cop.objectId == emp.objectId:
                    employees_approved_list.pop(i)
                    break
                
            # check in pending emps
            for i, cop in enumerate(employees_pending_list):
                if cop.objectId == emp.objectId:
                    employees_pending_list.pop(i)
                    break
                        
            session['employees_approved_list'] =\
                employees_approved_list
            session['employees_pending_list'] =\
                employees_pending_list
         
        #############################################################           
        # EMPLOYEE UPDATED PUNCHES
        updatedEmployeePunch = postDict.get("updatedEmployeePunch")
        if updatedEmployeePunch:
            u_emp = Employee(**updatedEmployeePunch)
            for emp in employees_approved_list:
                if u_emp.objectId == emp.objectId:
                    emp.set("lifetime_punches",
                        u_emp.lifetime_punches)
                    break
            session['employees_approved_list'] =\
                employees_approved_list
           
        #############################################################
        # REDEMPTIONS PENDING
        ### Only added to cache if it has the store_location_id as
        ### active_store_location_id
        pendingRedemption = postDict.get("pendingRedemption")
        if pendingRedemption:
            rr = RedeemReward(**pendingRedemption)
            
            # store_location_id can be null for backwards compat
            if not rr.store_location_id or rr.store_location_id ==\
                session.get('active_store_location_id'):
                redemptions_pending_ids =\
                    [ red.objectId for red in redemptions_pending ]
                redemptions_past_ids =\
                    [ red.objectId for red in redemptions_past ]
                # need to check here if the redemption is new because 
                # the dashboard that validated it will also receive
                # the validated redemption back.
                if rr.objectId not in redemptions_past_ids and\
                    rr.objectId not in redemptions_pending_ids:
                    redemptions_pending.insert(0, rr)
                    
                session['redemptions_pending'] =\
                    redemptions_pending
            
        #############################################################
        # REDEMPTIONS APPROVED (pending to history)
        # Save cpu by skipping those that do not have the same 
        # store_location_id as active_store_location_id
        approvedRedemption = postDict.get("approvedRedemption") 
        if approvedRedemption:  
            redemp = RedeemReward(**approvedRedemption)
            
            # store_location_id can be null for backwards compat
            if not redemp.store_location_id or redemp.store_location_id ==\
                session.get('active_store_location_id'):
                # check if redemp is still in pending
                for i, redem in enumerate(redemptions_pending):
                    if redem.objectId == redemp.objectId:
                        r = redemptions_pending.pop(i)
                        r.is_redeemed = True
                        r.updatedAt = redemp.updatedAt
                        redemptions_past.insert(0, r)
                        break
                # if not then check if it is in the history already
                # the above shouldn't happen!
                    
                session['redemptions_pending'] =\
                    redemptions_pending
                session['redemptions_past'] =\
                    redemptions_past
            
        #############################################################
        # REDEMPTIONS DELETED ##############################
        # remove from pending (should not be in history!)
        # Save cpu by skipping those that do not have the same 
        # store_location_id as active_store_location_id
        deletedRedemption = postDict.get("deletedRedemption")
        if deletedRedemption:
            redemp = RedeemReward(**deletedRedemption)
            
            # store_location_id can be null for backwards compat
            if not redemp.store_location_id or redemp.store_location_id ==\
                session.get('active_store_location_id'):
                # check if redemp is still in pending
                for i, redem in enumerate(redemptions_pending):
                    if redem.objectId == redemp.objectId:
                        redemptions_pending.pop(i)
                        break
                    
                session['redemptions_pending'] =\
                    redemptions_pending
               
        #############################################################
        # STORE UPDATED ##############################
        updatedStore = postDict.get("updatedStore")
        if updatedStore:
            store = Store(**updatedStore)
            # have to add the image url manually
            store.thumbnail_image_url = updatedStore.get("thumbnail_image_url")
            store.cover_image_url = updatedStore.get("cover_image_url")
            # below here for backwards compat
            store.store_avatar_url = store.thumbnail_image_url
            session['store'] = store
            
        updatedStoreThumbnailName = postDict.get("updatedStoreThumbnailName")
        if updatedStoreThumbnailName:
            store = session['store']
            store.thumbnail_image = updatedStoreThumbnailName
            store.thumbnail_image_url = postDict.get("updatedStoreThumbnailUrl")
            # below here for backwards compat
            store.store_avatar = store.thumbnail_image
            store.store_avatar_url = store.thumbnail_image_url
            session['store'] = store
            
            
        updatedStoreCoverName = postDict.get("updatedStoreCoverName")
        if updatedStoreCoverName:
            store = session['store']
            store.cover_image = updatedStoreCoverName
            store.cover_image_url = postDict.get("updatedStoreCoverUrl")
            session['store'] = store 
        
        # this is in the settings tab in the dashboard but the field
        # is in the Store class
        updatedPunchesFacebook_int =\
            postDict.get("updatedPunchesFacebook_int")
        if updatedPunchesFacebook_int:
            store = session['store']
            store.punches_facebook = int(updatedPunchesFacebook_int)
            session['store'] = store
            
            
            
            
            
        #############################################################
        # STORE LOCATION UPDATED ##############################
        ### Note that this is also being used to insert new StoreLocations
        updatedStoreLocation = postDict.get("updatedStoreLocation")
        if updatedStoreLocation:
            store_location = StoreLocation(**updatedStoreLocation)
            session['store_locations'][store_location.objectId] =\
                store_location
                
            try: # also update the store_timezone
                session['store_timezone'] =\
                    pytz.timezone(store_location.get('store_timezone'))
            except Exception: # assign a default timezone
                session['store_timezone'] =\
                    pytz.timezone(TIME_ZONE)

            
        #############################################################
        # ACCOUNT UPDATED ##############################
        updatedAccount = postDict.get("updatedAccount")
        if updatedAccount:
            updatedAccountObject = Account(**updatedAccount)
            # need to make sure that these are the same accounts!
            if session['account'].objectId ==\
                updatedAccountObject.objectId:
                session['account'] = updatedAccountObject
                    
        #############################################################
        # SUBSCRIPTION UPDATED ##############################
        updatedSubscription =\
            postDict.get("updatedSubscription")
        if updatedSubscription:
            subscription = Subscription(**updatedSubscription)
            store = session["store"]
            store.set('subscription', subscription)
            store.set('Subscription', subscription.objectId)
            session['subscription'] = subscription
            session['store'] = store
            
        #############################################################
        # SETTINGS UPDATED ##############################
        updatedSettings = postDict.get("updatedSettings")
        if updatedSettings:
            settings = Settings(**updatedSettings)
            store = session["store"]
            store.set('settings', settings)
            store.set("Settings", settings.objectId)
            session['settings'] = settings
            session['store'] = store
            
        #############################################################
        # REWARDS NEW ##############################
        newReward = postDict.get("newReward")
        if newReward:
            store = session['store']
            rewards = store.get("rewards")
            rewards_ids = [ r['reward_id'] for r in rewards ]
            if newReward['reward_id'] not in rewards_ids:
                rewards.append(newReward)
            store.rewards = rewards
            session['store'] = store
        
        #############################################################
        # REWARDS UPDATED ##############################
        updatedReward = postDict.get('updatedReward')
        if updatedReward:
            store = session['store']
            mod_rewards = store.get("rewards")
            for i, mreward in enumerate(mod_rewards):
                # [{"reward_name":"Free bottle of wine", 
                # "description":"Must be under $25 in value",
                # "punches":10,"redemption_count":0,reward_id:0},]
                if updatedReward['reward_id']==mreward['reward_id']:
                    if updatedReward.has_key("redemption_count"):
                        mod_rewards[i]['redemption_count'] =\
                            updatedReward['redemption_count']
                    if updatedReward.has_key("reward_name"):
                        mod_rewards[i]['reward_name'] =\
                            updatedReward['reward_name']
                    if updatedReward.has_key("punches"):
                        mod_rewards[i]['punches'] =\
                            updatedReward['punches']
                    if updatedReward.has_key("description"):
                        mod_rewards[i]['description'] =\
                            updatedReward['description']
                    break
                        
            store.rewards = mod_rewards
            session['store'] = store
            
        #############################################################
        # REWARDS DELETED ##############################
        deletedReward = postDict.get("deletedReward")
        if deletedReward:
            store = session['store']
            rewards = store.get("rewards")
            rewards_ids = [ r['reward_id'] for r in rewards ]
            if deletedReward['reward_id'] in rewards_ids:
                for i, r in enumerate(rewards):
                    if r['reward_id'] == deletedReward['reward_id']:
                        rewards.pop(i)
                        break
            store.rewards = rewards
            session['store'] = store
           
        #############################################################
        # PATRONSTORE_COUNT ##################################
        patronStore_int = postDict.get('patronStore_int')
        if patronStore_int:
            patronStore_int = int(patronStore_int)
            session['patronStore_count'] = patronStore_int
Пример #13
0
def sign_up(request):
    """
    Creates User, store, subscription, and settings objects.
    """
    # renders the signup page on GET and returns a json object on POST.
    data = {'sign_up_nav': True}
            
    if request.method == 'POST':
        # this conversion to a regular dictionay is important
        postDict = request.POST.dict()
    
        from_associated_account = False
        # check if this post is from the associated account dialog
        # if it is then skip form validations
        aaf_nonce_id = postDict.get('aaf-nonce')
        aaf_account_id = postDict.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
    
        # some keys are repeated so must catch this at init
        store_form = StoreSignUpForm(request.POST)
        store_location_form = StoreLocationForm(request.POST)
        account_form = AccountSignUpForm(request.POST)
        
        cats = postDict.get("categories")
        category_names = None
        if cats and len(cats) > 0:
            category_names = cats.split("|")[:-1]
            # make sure that there are only up to 2 categories
            while len(category_names) > 2:
                category_names.pop()
            data["category_names"] = category_names
        
        if not from_associated_account:
            all_forms_valid = store_form.is_valid() and\
                store_location_form.is_valid() and account_form.is_valid()
        else:
            all_forms_valid = True
            
        if all_forms_valid:
            # 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 hasattr(account_form, "associated_account"):
                aa = account_form.associated_account
                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")
            #########################################################

            # create store
            store = Store(**postDict)
            # set defaults for these guys to prevent 
            # ParseObjects from making parse calls repeatedly
            store.punches_facebook = 1
            store.set("rewards", [])
            store.set("categories", [])
            if category_names:
                for name in category_names:
                    alias = Category.objects.filter(name__iexact=name)
                    if len(alias) > 0:
                        store.categories.append({
                            "alias":alias[0].alias,
                            "name":name })
                            
            # create settings
            settings = Settings(Store=store.objectId)
            store.set('settings', settings)

            # create account
            if not from_associated_account:
                account = Account(**postDict)
                # username = email
                # we should be doing this in the form but ehh
                account.set("username", 
                    postDict['email'].strip().lower())
                account.set("email", 
                    postDict['email'].strip().lower())
                account.set_password(postDict.get('password'))
            else:
                account =\
                    Account.objects().get(objectId=aaf_account_id)
                
            account.set("store", store)

            # create subscription
            subscription = Subscription() 
            subscription.subscriptionType = 0
            subscription.date_last_billed = timezone.now()
            subscription.create()
            
            # create settings
            settings.create()
            
            # create store
            store.Settings = settings.objectId
            store.Subscription = subscription.objectId
            store.create()
            
            # add the pointer to the created store
            settings.Store = store.objectId
            settings.update()
            subscription.Store = store.objectId
            subscription.update()
            
            # create the store location
            store_location = StoreLocation(**postDict)
            # format the phone number
            store_location.store_timezone =\
                rputils.get_timezone(postDict.get("zip")).zone
            store_location.set("hours", [])
            # coordinates and neighborhood
            # the call to get map data is actually also in the clean 
            full_address = " ".join(\
                store_location.get_full_address().split(", "))
            map_data = rputils.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")))
            store_location.phone_number =\
                format_phone_number(postDict["phone_number"])
            store_location.Store = store.objectId
            store_location.create()
            
            # add the StoreLocation to the relation
            store.store_locations = [store_location] 
            store.update()
            
            # create account
            account.Store = store.objectId
            if not from_associated_account:
                account.create()
            else:
                account.update()
            
            # create the store ACL with the account having r/w access
            store.ACL = {
                "*": {"read": True, "write": True},
                account.objectId: {"read": True, "write": True},
            }
            store.owner_id = account.objectId
            store.update()
            
            # note that username has been fed the email
            # this shouldn't change anything though shouldn't matter
            # need to put username and pass in request
            postDict['username'] = account.username
            postDict['password'] = account.password
            
            # send matt and new user a pretty email.
            send_email_signup(account)

            # auto login
            user_login = login(request, postDict, no_recaptcha=True)
            if user_login != None:
                data = {"code":-1}
                # response to signup.js - not login returns
                # 0 - Associated account already exists
                # 2 - subscription is not active
                # 3 - success (login now)
                if type(user_login) is int: # subscription not active
                    data['code'] = 2
                else:
                    # required for datetime awareness!
                    rputils.set_timezone(request, tz)
                    data['code'] = 3
                return HttpResponse(json.dumps(data), 
                            content_type="application/json")
           
    else:
        store_form = StoreSignUpForm()
        store_location_form = StoreLocationForm()
        account_form = AccountSignUpForm()
        
    data['store_form'] = store_form
    data['store_location_form'] = store_location_form
    data['account_form'] = account_form
    return render(request, 'public/signup.djhtml', data)
Пример #14
0
 def create_random_stores(self, amount):
     for i in range(amount):
         print "Creating store %s" % (str(i),) 
         # create the store
         street, city, state, zip, country, phone_number =\
             self.addrs[i].split(", ")
         first_name, last_name = self.owners[i].split(" ")
         neighborhood = self.neighborhoods[i]
         store_name = self.stores[i]
         store_i = STORE.copy()
         store_location_i = STORE_LOCATION.copy()
         
         # create the thumbnaiil and cover (same image different size)
         self.get_store_location_image(i)
         
         thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE)
         while "error" in image:
             print "Retrying create_png"
             thumbnail = create_png(TMP_IMG_PATH)
             
         cover = create_png(TMP_IMG_PATH)
         while "error" in cover:
             print "Retrying create_png"
             cover = create_png(TMP_IMG_PATH)
         
         store_i.update({
             "store_name": store_name,
             "first_name": first_name,
             "last_name": last_name,
             "thumbnail_image": thumbnail.get("name"),
             "cover_image": cover.get("name"),
         })
         store_location_i.update({
             "street": street,
             "city": city,
             "state": state,
             "zip": zip,
             "neighborhood": neighborhood,
             "country": country,
             "phone_number": phone_number,
             "coordinates": self.get_random_coordinates(),
         })
         
         # create the store
         store = Store.objects().create(**store_i)   
 
         # create the store location
         store_location = StoreLocation(**store_location_i)
         store_location.Store = store.objectId
         store_location.update()
         
         # create the settings
         settings = Settings.objects().create(Store=store.objectId)
         
         # create the subscription
         subscription =\
             Subscription.objects().create(Store=store.objectId,
                 date_last_billed=timezone.now())
         
         # create the user
         email = first_name+str(randint(0, 99))+USER_EMAIL_POSTFIX
         email = email.lower()
         acc = Account.objects().create(\
             username=email, email=email,
             password=USER_PASSWORD, Store=store.objectId)
         if not acc.objectId:
             raise Exception("Account creation failed.")
             
         # link the store
         store.Settings = settings.objectId
         store.Subscription = subscription.objectId
         store.owner_id = acc.objectId
         store.ACL[acc.objectId] = {"read": True,"write": True}
         store.store_locations = [store_location]
         store.update()
Пример #15
0
    def processCometReceivedDict(session, postDict):
        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)

        #############################################################
        # FEEDBACKS_UNREAD ##################################
        newFeedback = postDict.get('newFeedback')
        if newFeedback:
            messages_received_ids =\
                [ fb.objectId for fb in messages_received_list ]
            m = Message(**newFeedback)
            if m.objectId not in messages_received_ids:
                messages_received_list.insert(0, m)

            session['messages_received_list'] =\
                messages_received_list

        #############################################################
        # FEEDBACK DELETED ##################################
        deletedFeedback = postDict.get("deletedFeedback")
        if deletedFeedback:
            fb = Message(**deletedFeedback)
            for i, mro in enumerate(messages_received_list):
                if fb.objectId == mro.objectId:
                    messages_received_list.pop(i)
                    break
            session['messages_received_list'] =\
                messages_received_list

        #############################################################
        # MESSAGE SENT ##################################
        # need to check if this new message is an original message
        # or a reply to a feedback (the message sent by the patron)!
        # also may increment the message count!
        newMessage = postDict.get("newMessage")
        if newMessage:
            messages_received_ids =\
                    [ fb.objectId for fb in messages_received_list ]
            messages_sent_list =\
                SESSION.get_messages_sent_list(session)
            messages_sent_ids =\
                [ msg.objectId for msg in messages_sent_list ]
            m = Message(**newMessage)
            if m.objectId not in messages_sent_ids and\
                m.message_type != FEEDBACK:
                messages_sent_list.insert(0, m)
                if 'message_count' in session:
                    session['message_count'] =\
                        int(session['message_count']) + 1
            # update an existing feedback
            if m.objectId in messages_received_ids and\
                m.message_type == FEEDBACK:
                for i, mrl in enumerate(messages_received_list):
                    if mrl.objectId == m.objectId:
                        messages_received_list.pop(i)
                        messages_received_list.insert(i, m)
                        break
            session['messages_received_list'] =\
                messages_received_list
            session['messages_sent_list'] = messages_sent_list

        #############################################################
        # EMPLOYEES_PENDING ##################################
        # must also check if employee is already approved!
        pendingEmployee = postDict.get("pendingEmployee")
        if pendingEmployee:
            employees_approved_ids =\
                [ emp.objectId for emp in employees_approved_list ]
            employees_pending_ids =\
                [ emp.objectId for emp in employees_pending_list ]
            e = Employee(**pendingEmployee)
            if e.objectId not in employees_pending_ids and\
                e.objectId not in employees_approved_ids:
                employees_pending_list.insert(0, e)

            session['employees_pending_list'] =\
                employees_pending_list

        #############################################################
        # EMPLOYEES APPROVED (pending to approved) #################
        approvedEmployee = postDict.get("approvedEmployee")
        if approvedEmployee:
            emp = Employee(**approvedEmployee)
            # first check if the employee is in the pending list
            # if not then check if it is already approved
            for i, emp_pending in\
                enumerate(employees_pending_list):
                if emp.objectId == emp_pending.objectId:
                    emp = employees_pending_list.pop(i)
                    emp.status = APPROVED
                    employees_approved_list.insert(0, emp)
                    break

            session['employees_pending_list'] =\
                employees_pending_list
            session['employees_approved_list'] =\
                employees_approved_list

        #############################################################
        # EMPLOYEES NEW (straight to approved) #################
        newEmployee = postDict.get("newEmployee")
        if newEmployee:
            employees_approved_ids =\
                [ emp.objectId for emp in employees_approved_list ]
            emp = Employee(**newEmployee)
            if emp.objectId not in employees_approved_ids:
                employees_approved_list.insert(0, emp)
                session['employees_approved_list'] =\
                    employees_approved_list

        #############################################################
        # EMPLOYEES DELETED/DENIED/REJECTED (pending/approved to pop)!
        deletedEmployee = postDict.get("deletedEmployee")
        if deletedEmployee:
            emp = Employee(**deletedEmployee)
            # check in approved emps
            for i, cop in enumerate(employees_approved_list):
                if cop.objectId == emp.objectId:
                    employees_approved_list.pop(i)
                    break

            # check in pending emps
            for i, cop in enumerate(employees_pending_list):
                if cop.objectId == emp.objectId:
                    employees_pending_list.pop(i)
                    break

            session['employees_approved_list'] =\
                employees_approved_list
            session['employees_pending_list'] =\
                employees_pending_list

        #############################################################
        # EMPLOYEE UPDATED PUNCHES
        updatedEmployeePunch = postDict.get("updatedEmployeePunch")
        if updatedEmployeePunch:
            u_emp = Employee(**updatedEmployeePunch)
            for emp in employees_approved_list:
                if u_emp.objectId == emp.objectId:
                    emp.set("lifetime_punches", u_emp.lifetime_punches)
                    break
            session['employees_approved_list'] =\
                employees_approved_list

        #############################################################
        # REDEMPTIONS PENDING
        ### Only added to cache if it has the store_location_id as
        ### active_store_location_id
        pendingRedemption = postDict.get("pendingRedemption")
        if pendingRedemption:
            rr = RedeemReward(**pendingRedemption)

            # store_location_id can be null for backwards compat
            if not rr.store_location_id or rr.store_location_id ==\
                session.get('active_store_location_id'):
                redemptions_pending_ids =\
                    [ red.objectId for red in redemptions_pending ]
                redemptions_past_ids =\
                    [ red.objectId for red in redemptions_past ]
                # need to check here if the redemption is new because
                # the dashboard that validated it will also receive
                # the validated redemption back.
                if rr.objectId not in redemptions_past_ids and\
                    rr.objectId not in redemptions_pending_ids:
                    redemptions_pending.insert(0, rr)

                session['redemptions_pending'] =\
                    redemptions_pending

        #############################################################
        # REDEMPTIONS APPROVED (pending to history)
        # Save cpu by skipping those that do not have the same
        # store_location_id as active_store_location_id
        approvedRedemption = postDict.get("approvedRedemption")
        if approvedRedemption:
            redemp = RedeemReward(**approvedRedemption)

            # store_location_id can be null for backwards compat
            if not redemp.store_location_id or redemp.store_location_id ==\
                session.get('active_store_location_id'):
                # check if redemp is still in pending
                for i, redem in enumerate(redemptions_pending):
                    if redem.objectId == redemp.objectId:
                        r = redemptions_pending.pop(i)
                        r.is_redeemed = True
                        r.updatedAt = redemp.updatedAt
                        redemptions_past.insert(0, r)
                        break
                # if not then check if it is in the history already
                # the above shouldn't happen!

                session['redemptions_pending'] =\
                    redemptions_pending
                session['redemptions_past'] =\
                    redemptions_past

        #############################################################
        # REDEMPTIONS DELETED ##############################
        # remove from pending (should not be in history!)
        # Save cpu by skipping those that do not have the same
        # store_location_id as active_store_location_id
        deletedRedemption = postDict.get("deletedRedemption")
        if deletedRedemption:
            redemp = RedeemReward(**deletedRedemption)

            # store_location_id can be null for backwards compat
            if not redemp.store_location_id or redemp.store_location_id ==\
                session.get('active_store_location_id'):
                # check if redemp is still in pending
                for i, redem in enumerate(redemptions_pending):
                    if redem.objectId == redemp.objectId:
                        redemptions_pending.pop(i)
                        break

                session['redemptions_pending'] =\
                    redemptions_pending

        #############################################################
        # STORE UPDATED ##############################
        updatedStore = postDict.get("updatedStore")
        if updatedStore:
            store = Store(**updatedStore)
            # have to add the image url manually
            store.thumbnail_image_url = updatedStore.get("thumbnail_image_url")
            store.cover_image_url = updatedStore.get("cover_image_url")
            # below here for backwards compat
            store.store_avatar_url = store.thumbnail_image_url
            session['store'] = store

        updatedStoreThumbnailName = postDict.get("updatedStoreThumbnailName")
        if updatedStoreThumbnailName:
            store = session['store']
            store.thumbnail_image = updatedStoreThumbnailName
            store.thumbnail_image_url = postDict.get(
                "updatedStoreThumbnailUrl")
            # below here for backwards compat
            store.store_avatar = store.thumbnail_image
            store.store_avatar_url = store.thumbnail_image_url
            session['store'] = store

        updatedStoreCoverName = postDict.get("updatedStoreCoverName")
        if updatedStoreCoverName:
            store = session['store']
            store.cover_image = updatedStoreCoverName
            store.cover_image_url = postDict.get("updatedStoreCoverUrl")
            session['store'] = store

        # this is in the settings tab in the dashboard but the field
        # is in the Store class
        updatedPunchesFacebook_int =\
            postDict.get("updatedPunchesFacebook_int")
        if updatedPunchesFacebook_int:
            store = session['store']
            store.punches_facebook = int(updatedPunchesFacebook_int)
            session['store'] = store

        #############################################################
        # STORE LOCATION UPDATED ##############################
        ### Note that this is also being used to insert new StoreLocations
        updatedStoreLocation = postDict.get("updatedStoreLocation")
        if updatedStoreLocation:
            store_location = StoreLocation(**updatedStoreLocation)
            session['store_locations'][store_location.objectId] =\
                store_location

            try:  # also update the store_timezone
                session['store_timezone'] =\
                    pytz.timezone(store_location.get('store_timezone'))
            except Exception:  # assign a default timezone
                session['store_timezone'] =\
                    pytz.timezone(TIME_ZONE)

        #############################################################
        # ACCOUNT UPDATED ##############################
        updatedAccount = postDict.get("updatedAccount")
        if updatedAccount:
            updatedAccountObject = Account(**updatedAccount)
            # need to make sure that these are the same accounts!
            if session['account'].objectId ==\
                updatedAccountObject.objectId:
                session['account'] = updatedAccountObject

        #############################################################
        # SUBSCRIPTION UPDATED ##############################
        updatedSubscription =\
            postDict.get("updatedSubscription")
        if updatedSubscription:
            subscription = Subscription(**updatedSubscription)
            store = session["store"]
            store.set('subscription', subscription)
            store.set('Subscription', subscription.objectId)
            session['subscription'] = subscription
            session['store'] = store

        #############################################################
        # SETTINGS UPDATED ##############################
        updatedSettings = postDict.get("updatedSettings")
        if updatedSettings:
            settings = Settings(**updatedSettings)
            store = session["store"]
            store.set('settings', settings)
            store.set("Settings", settings.objectId)
            session['settings'] = settings
            session['store'] = store

        #############################################################
        # REWARDS NEW ##############################
        newReward = postDict.get("newReward")
        if newReward:
            store = session['store']
            rewards = store.get("rewards")
            rewards_ids = [r['reward_id'] for r in rewards]
            if newReward['reward_id'] not in rewards_ids:
                rewards.append(newReward)
            store.rewards = rewards
            session['store'] = store

        #############################################################
        # REWARDS UPDATED ##############################
        updatedReward = postDict.get('updatedReward')
        if updatedReward:
            store = session['store']
            mod_rewards = store.get("rewards")
            for i, mreward in enumerate(mod_rewards):
                # [{"reward_name":"Free bottle of wine",
                # "description":"Must be under $25 in value",
                # "punches":10,"redemption_count":0,reward_id:0},]
                if updatedReward['reward_id'] == mreward['reward_id']:
                    if updatedReward.has_key("redemption_count"):
                        mod_rewards[i]['redemption_count'] =\
                            updatedReward['redemption_count']
                    if updatedReward.has_key("reward_name"):
                        mod_rewards[i]['reward_name'] =\
                            updatedReward['reward_name']
                    if updatedReward.has_key("punches"):
                        mod_rewards[i]['punches'] =\
                            updatedReward['punches']
                    if updatedReward.has_key("description"):
                        mod_rewards[i]['description'] =\
                            updatedReward['description']
                    break

            store.rewards = mod_rewards
            session['store'] = store

        #############################################################
        # REWARDS DELETED ##############################
        deletedReward = postDict.get("deletedReward")
        if deletedReward:
            store = session['store']
            rewards = store.get("rewards")
            rewards_ids = [r['reward_id'] for r in rewards]
            if deletedReward['reward_id'] in rewards_ids:
                for i, r in enumerate(rewards):
                    if r['reward_id'] == deletedReward['reward_id']:
                        rewards.pop(i)
                        break
            store.rewards = rewards
            session['store'] = store

        #############################################################
        # PATRONSTORE_COUNT ##################################
        patronStore_int = postDict.get('patronStore_int')
        if patronStore_int:
            patronStore_int = int(patronStore_int)
            session['patronStore_count'] = patronStore_int
Пример #16
0
    def handle(self, *args, **options):
        # for logging when ran by CRON
        print "Running detect_suspicious_activity: " + str(timezone.now())

        # first count the number of active stores
        store_count = Store.objects().count(active=True)
        # store_count = Store.objects().count(objectId="o72LmDy0YK")

        end = timezone.now()
        start = end + relativedelta(hours=-24)
        conn = mail.get_connection(fail_silently=(not DEBUG))
        conn.open()

        # to send to the admins
        admin_chunks = []

        # get 500 stores at a time
        LIMIT, skip = 500, 0
        while store_count > 0:
            for store in Store.objects().filter(active=True,
                                                include="store_locations",
                                                limit=LIMIT,
                                                skip=skip,
                                                order="createdAt"):
                # for store in Store.objects().filter(\
                #     objectId="o72LmDy0YK", include="store_locations"):

                ### CHUNK1 ####################################
                chunk1, account_patron, patron_punch = {}, {}, {}
                total_punches = []

                # check approved EMPLOYEES
                employees = store.get("employees", status=APPROVED, limit=900)
                employee_punches = []

                def add_to_patron_punch(punch, employee=None):
                    if punch.Patron not in patron_punch:
                        patron_punch[punch.Patron] =\
                            [{"punch":punch, "employee": employee}]
                    else:
                        patron_punch[punch.Patron].append({"punch":\
                            punch, "employee":employee})

                def get_location(location_id):
                    for loc in store.store_locations:
                        if loc.objectId == location_id:
                            return loc

                if employees and len(employees) > 0:
                    # check all the punches of each employee
                    for employee in employees:
                        # get all the punches for today
                        punches = employee.get("punches",
                                               limit=900,
                                               createdAt__lte=end,
                                               createdAt__gte=start)
                        if not punches:
                            continue

                        # for querying the dashboard punches
                        employee_punches.extend([p.objectId for p in\
                            punches])

                        # group the punches by patron
                        for punch in punches:
                            add_to_patron_punch(punch, employee)

                # now check DASHBOARD
                punches = store.get("punches",
                                    limit=900,
                                    createdAt__lte=end,
                                    createdAt__gte=start,
                                    objectId__nin=employee_punches)

                # group the punches by patron
                if punches:
                    for punch in punches:
                        add_to_patron_punch(punch, None)

                # check for a group with a list >= 6
                for key, val in patron_punch.iteritems():
                    suspicious_punches = []
                    if val and len(val) >= 6:
                        for punch in val:
                            suspicious_punches.append({
                                "store_location":\
                                    get_location(punch["punch"].store_location_id),
                                "punch": punch["punch"],
                                "employee": punch["employee"]
                            })

                        # cache the account and patron
                        if key not in account_patron:
                            acc = Account.objects().get(Patron=key,
                                                        include="Patron")
                            account_patron[key] = {
                                "account": acc,
                                "patron": acc.patron,
                            }

                        if key not in chunk1:
                            chunk1[key] = {
                                "account":\
                                   account_patron[key]['account'],
                                "patron":\
                                   account_patron[key]['patron'],
                                "punches": suspicious_punches
                            }
                        else:
                            chunk1[key]['punches'].extend(suspicious_punches)

                ### CHUNK2 ####################################
                # hours per location
                # punches are still grouped per patron
                chunk2 = {}
                for loc in store.store_locations:
                    if loc.hours and len(loc.hours) > 0 and\
                        loc.hours[0]['day'] != 0: # 24/7
                        # check for punches out of hours
                        tz = pytz.timezone(loc.store_timezone)
                        start = timezone.localtime(start, tz)
                        end = timezone.localtime(end, tz)
                        # isoweekday is from 1-7 monday to sunday
                        # convert to 1-7 sunday to saturday
                        day1_weekday = (start.isoweekday()) % 7 + 1
                        day2_weekday = (end.isoweekday()) % 7 + 1

                        # get the hours for day1 and day2
                        def get_hours_range(weekday, d):
                            for hr in loc.hours:
                                if hr["day"] == weekday:
                                    hr_start_hour =\
                                        int(hr["open_time"][:2])
                                    hr_start_minute =\
                                        int(hr["open_time"][2:])
                                    hr_end_hour =\
                                        int(hr["close_time"][:2])
                                    hr_end_minute =\
                                        int(hr["close_time"][2:])
                                    return d.replace(hour=hr_start_hour,
                                        minute=hr_start_minute),\
                                        d.replace(hour=hr_end_hour,
                                        minute=hr_end_minute)
                            return None, None

                        (hours1_start, hours1_end) =\
                            get_hours_range(day1_weekday, start)
                        (hours2_start, hours2_end) =\
                            get_hours_range(day2_weekday, end)

                        # now convert to utc since punch times are in utc
                        if hours1_start:
                            hours1_start =\
                                timezone.localtime(hours1_start, tzutc())
                            hours1_end =\
                                timezone.localtime(hours1_end, tzutc())
                        if hours2_start:
                            hours2_start =\
                                timezone.localtime(hours2_start, tzutc())
                            hours2_end =\
                                timezone.localtime(hours2_end, tzutc())

                        for key, val in patron_punch.iteritems():
                            if not val:
                                continue

                            suspicious_punches = []

                            # process only those punches that are in this location
                            for p in [
                                    x for x in val
                                    if x["punch"].store_location_id ==
                                    loc.objectId
                            ]:
                                punch = p["punch"]
                                # suspicious if not in hours1 and 2
                                if not (hours1_start and\
                                    punch.createdAt>hours1_start and\
                                    punch.createdAt<hours1_end) and\
                                    not (hours2_start and\
                                    punch.createdAt>hours2_start and\
                                    punch.createdAt<hours2_end):
                                    # not in hours1 or 2 so suspicious!
                                    suspicious_punches.append({
                                        "store_location":
                                        loc,
                                        "punch":
                                        punch,
                                        "employee":
                                        p["employee"],
                                    })

                            if len(suspicious_punches) == 0:
                                continue

                            # cache the account and patron
                            if key not in account_patron:
                                acc = Account.objects().get(Patron=key,
                                                            include="Patron")
                                account_patron[key] = {
                                    "account": acc,
                                    "patron": acc.patron,
                                }

                            if key not in chunk2:
                                chunk2[key] = {
                                    "account":\
                                       account_patron[key]['account'],
                                    "patron":\
                                       account_patron[key]['patron'],
                                    "punches": suspicious_punches
                                }
                            else:
                                chunk2[key]['punches'].extend(
                                    suspicious_punches)

                # all tasks are done for this store - send email
                if len(chunk1) > 0 or len(chunk2) > 0:
                    store_acc = Account.objects().get(Store=store.objectId)
                    admin_chunks.append({
                        "store_acc": store_acc,
                        "store": store,
                        "data": (chunk1, chunk2),
                    })

                    try:
                        send_email_suspicious_activity(store_acc, store,
                                                       chunk1, chunk2, conn)
                    except SMTPServerDisconnected:
                        conn = mail.get_connection(fail_silently=(not DEBUG))
                        conn.open()
                        send_email_suspicious_activity(store_acc, store,
                                                       chunk1, chunk2, conn)

            # end of while loop
            store_count -= LIMIT
            skip += LIMIT

        if len(admin_chunks) > 0:
            send_email_suspicious_activity_admin(admin_chunks, start, end,
                                                 conn)

        # everything is done. close the connection
        try:
            conn.close()
        except Exception:
            pass