def verify_default_claims(HOST): today = datetime.date.today() claims = Claim.objects.filter( status=Claim.STAT_CURRENT, claimed_task__scheduled_date__range=[ today + THREEDAYS, today + FOURDAYS ], # REVIEW: Is the following 'claiming_member' restriction actually required? claiming_member=F( 'claimed_task__recurring_task_template__default_claimant'), date_verified__isnull=True, claiming_member__worker__should_nag=True, ) if len(claims) == 0: # No default claims to process. return text_content_template = get_template('tasks/email-verify-claim.txt') html_content_template = get_template('tasks/email-verify-claim.html') for claim in claims: if not claim.claiming_member.worker.should_nag: continue b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count( ) == 0) # uniqueness test nag = Nag.objects.create(who=claim.claiming_member, auth_token_md5=md5) nag.claims.add(claim) nag.tasks.add(claim.claimed_task) dow = claim.claimed_task.scheduled_weekday() d = { 'claimant': claim.claiming_member, 'claim': claim, 'task': claim.claimed_task, 'dow': dow, 'auth_token': b64, 'host': HOST, } # Send email messages: subject = 'Please verify your availability for this {}'.format(dow) from_email = EMAIL_VOLUNTEER bcc_email = EMAIL_ARCHIVE to = claim.claiming_member.email text_content = text_content_template.render(d) html_content = html_content_template.render(d) msg = EmailMultiAlternatives(subject, text_content, from_email, [to], [bcc_email]) msg.attach_alternative(html_content, "text/html") msg.send()
def verify_default_claims(HOST): today = datetime.date.today() claims = Claim.objects.filter( status=Claim.STAT_CURRENT, claimed_task__scheduled_date__range=[today+THREEDAYS, today+FOURDAYS], # REVIEW: Is the following 'claiming_member' restriction actually required? claiming_member=F('claimed_task__recurring_task_template__default_claimant'), date_verified__isnull=True) if len(claims) == 0: # No default claims to process. return text_content_template = get_template('tasks/email-verify-claim.txt') html_content_template = get_template('tasks/email-verify-claim.html') for claim in claims: b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count() == 0) # uniqueness test nag = Nag.objects.create(who=claim.claiming_member, auth_token_md5=md5) nag.claims.add(claim) nag.tasks.add(claim.claimed_task) dow = claim.claimed_task.scheduled_weekday() d = { 'claimant': claim.claiming_member, 'claim': claim, 'task': claim.claimed_task, 'dow': dow, 'auth_token': b64, 'host': HOST, } # Send email messages: subject = 'Please verify your availability for this {}'.format(dow) from_email = EMAIL_VOLUNTEER bcc_email = EMAIL_ARCHIVE to = claim.claiming_member.email text_content = text_content_template.render(d) html_content = html_content_template.render(d) msg = EmailMultiAlternatives(subject, text_content, from_email, [to], [bcc_email]) msg.attach_alternative(html_content, "text/html") msg.send()
def verify_default_claims(HOST): text_content_template = get_template('tasks/email-verify-claim.txt') html_content_template = get_template('tasks/email-verify-claim.html') today = datetime.date.today() for claim in Claim.objects.filter( status = Claim.STAT_CURRENT, claimed_task__scheduled_date__range=[today+THREEDAYS, today+FOURDAYS], claiming_member=F('claimed_task__recurring_task_template__default_claimant'), date_verified__isnull=True): b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count() == 0) # uniqueness test nag = Nag.objects.create(who=claim.claiming_member, auth_token_md5=md5) nag.claims.add(claim) nag.tasks.add(claim.claimed_task) dow = claim.claimed_task.scheduled_weekday() d = Context({ 'claimant': claim.claiming_member, 'claim': claim, 'task': claim.claimed_task, 'dow': dow, 'auth_token': b64, 'host': HOST, }) # Send email messages: subject = 'Please verify your availability for this {}'.format(dow) from_email = VC_EMAIL bcc_email = XIS_EMAIL to = claim.claiming_member.email text_content = text_content_template.render(d) html_content = html_content_template.render(d) msg = EmailMultiAlternatives(subject, text_content, from_email, [to], [bcc_email]) msg.attach_alternative(html_content, "text/html") msg.send()
def nag_for_workers(HOST): today = datetime.date.today() # Find out who's doing what over the next 2 weeks. Who's already scheduled to work and who's heavily scheduled? ppl_already_scheduled = Claim.sum_in_period(today, today+TWOWEEKS) ppl_heavily_scheduled = set([member for member, dur in ppl_already_scheduled.items() if dur >= datetime.timedelta(hours=6.0)]) # Rule out the following sets: ppl_excluded = set() ppl_excluded |= set([worker.member for worker in Worker.objects.filter(should_nag=False)]) ppl_excluded |= set(Member.objects.filter(auth_user__email="")) ppl_excluded |= set(Member.objects.filter(auth_user__is_active=False)) # Cycle through future days' NAGGING tasks to see which need workers and who should be nagged. nag_lists = {} emergency_tasks = [] for task in Task.objects.filter(scheduled_date__gte=today, scheduled_date__lt=today+THREEDAYS, should_nag=True): # No need to nag if task is fully claimed or not workable. if (not task.is_active()) or task.is_fully_claimed(): continue potentials = task.all_eligible_claimants() potentials -= task.claimant_set(Claim.STAT_CURRENT) potentials -= task.claimant_set(Claim.STAT_UNINTERESTED) # potentials -= task.claimant_set(Claim.STAT_EXPIRED) Their claim expired but they're still a possibility. potentials -= task.claimant_set(Claim.STAT_ABANDONED) potentials -= ppl_excluded panic_situation = task.scheduled_date == today and task.priority == Task.PRIO_HIGH if panic_situation: emergency_tasks.append(task) else: # Don't bother heavily scheduled people if it's not time to panic potentials -= ppl_heavily_scheduled for member in potentials: if member not in nag_lists: nag_lists[member] = [] nag_lists[member] += [task] # Send staffing emergency message to staff list: if len(emergency_tasks) > 0: Command.send_staffing_emergency_message(emergency_tasks, HOST) # Send email nag messages to potential workers: text_content_template = get_template('tasks/email_nag_template.txt') html_content_template = get_template('tasks/email_nag_template.html') for member, tasks in nag_lists.items(): b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count() == 0 # uniqueness test ) nag = Nag.objects.create(who=member, auth_token_md5=md5) nag.tasks.add(*tasks) d = { 'token': b64, 'member': member, 'tasks': tasks, 'host': HOST, } subject = 'Call for Volunteers, ' + datetime.date.today().strftime('%a %b %d') from_email = EMAIL_VOLUNTEER bcc_email = EMAIL_ARCHIVE to = member.email text_content = text_content_template.render(d) html_content = html_content_template.render(d) msg = EmailMultiAlternatives(subject, text_content, from_email, [to], [bcc_email]) msg.attach_alternative(html_content, "text/html") msg.send()
def nag_for_workers(HOST): today = datetime.date.today() # Find out who's doing what over the next 2 weeks. Who's already scheduled to work and who's heavily scheduled? ppl_already_scheduled = Claim.sum_in_period(today, today+TWOWEEKS) ppl_heavily_scheduled = set([member for member, dur in ppl_already_scheduled.items() if dur >= datetime.timedelta(hours=6.0)]) # Rule out the following sets: ppl_excluded = set() ppl_excluded |= set([worker.member for worker in Worker.objects.filter(should_nag=False)]) ppl_excluded |= set(Member.objects.filter(auth_user__email="")) ppl_excluded |= set(Member.objects.filter(auth_user__is_active=False)) # Cycle through future days' NAGGING tasks to see which need workers and who should be nagged. nag_lists = {} emergency_tasks = [] for task in Task.objects.filter(scheduled_date__gte=today, scheduled_date__lt=today+THREEDAYS, should_nag=True): # No need to nag if task is fully claimed or not workable. if (not task.is_active()) or task.is_fully_claimed: continue # Skip tasks that repeat at intervals and can slide. Nags for these will be # notifications pushed when an eligible worker walks into the facility. rtt = task.recurring_task_template if rtt.repeat_interval is not None and rtt.missed_date_action == rtt.MDA_SLIDE_SELF_AND_LATER: continue potentials = task.all_eligible_claimants() potentials -= task.claimant_set(Claim.STAT_CURRENT) potentials -= task.claimant_set(Claim.STAT_UNINTERESTED) # potentials -= task.claimant_set(Claim.STAT_EXPIRED) Their claim expired but they're still a possibility. potentials -= task.claimant_set(Claim.STAT_ABANDONED) potentials -= ppl_excluded panic_situation = task.scheduled_date == today and task.priority == Task.PRIO_HIGH if panic_situation: emergency_tasks.append(task) else: # Don't bother heavily scheduled people if it's not time to panic potentials -= ppl_heavily_scheduled for member in potentials: if member not in nag_lists: nag_lists[member] = [] nag_lists[member] += [task] # Send staffing emergency message to staff list: if len(emergency_tasks) > 0: Command.send_staffing_emergency_message(emergency_tasks, HOST) # Send email nag messages to potential workers: text_content_template = get_template('tasks/email_nag_template.txt') html_content_template = get_template('tasks/email_nag_template.html') for member, tasks in nag_lists.items(): b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count() == 0 # uniqueness test ) nag = Nag.objects.create(who=member, auth_token_md5=md5) nag.tasks.add(*tasks) d = { 'token': b64, 'member': member, 'tasks': tasks, 'host': HOST, } subject = 'Call for Volunteers, ' + datetime.date.today().strftime('%a %b %d') from_email = EMAIL_VOLUNTEER bcc_email = EMAIL_ARCHIVE to = member.email text_content = text_content_template.render(d) html_content = html_content_template.render(d) msg = EmailMultiAlternatives(subject, text_content, from_email, [to], [bcc_email]) msg.attach_alternative(html_content, "text/html") msg.send()
def maintenance_nag(sender, **kwargs): unused(sender) try: visit = kwargs.get('instance') # type: VisitEvent # We're only interested in arrivals if visit.event_type != VisitEvent.EVT_ARRIVAL: return # Only act on a member's first visit of the day. start_of_today = localtime(timezone.now()).replace( hour= 4, # For the purpose of this nag, I'm going to say that day begins at 4am. minute=0, second=0, microsecond=0, ) num_visits_today = VisitEvent.objects.filter( who=visit.who, event_type=VisitEvent.EVT_ARRIVAL, when__gte=start_of_today, ).count() if num_visits_today > 1: return # This gets tasks that are scheduled like maintenance tasks. # I.e. those that need to be done every X days, but can slide. tasks = Task.objects.filter( eligibleclaimant2__member=visit.who, eligibleclaimant2__should_nag=True, scheduled_date=date.today(), status=Task.STAT_ACTIVE, should_nag=True, recurring_task_template__repeat_interval__isnull=False, recurring_task_template__missed_date_action=RecurringTaskTemplate. MDA_SLIDE_SELF_AND_LATER, ) # We're going to want to send msgs to a manager to let them know that people were asked to do the work. # TODO: Shouldn't have a hard-coded userid here. Make configurable, perhaps with tags. try: mgr = Member.objects.get(auth_user__username=USER_VOLUNTEER) except Member.DoesNotExist: mgr = None # Nag the visitor by sending a notification for each task they could work. for task in tasks: # type: Task # Create the nag b64, md5 = Member.generate_auth_token_str( lambda token: Nag.objects.filter(auth_token_md5=token).count( ) == 0 # uniqueness test ) nag = Nag.objects.create(who=visit.who, auth_token_md5=md5) nag.tasks.add(task) # Generate an informative message try: last_done = Task.objects.filter( scheduled_date__lt=date.today(), status=Task.STAT_DONE, recurring_task_template=task.recurring_task_template, ).latest('scheduled_date') delta = date.today( ) - last_done.scheduled_date # type: timedelta message = "This task was last completed {} days ago!".format( delta.days) except Task.DoesNotExist: message = "" message += " If you can complete this task today, please click the link AFTER the work is done." relative = reverse('task:note-task-done', kwargs={ 'task_pk': task.id, 'auth_token': b64 }) # Send the notification was_sent = notifications.notify( visit.who, task.short_desc, message, url=HOST + relative, url_title="I Did It!", ) if was_sent: # Let manager know: if visit.who != mgr: notifications.notify( mgr, task.short_desc, visit.who.friendly_name + " was asked to work this task.", ) else: # If the notification wasn't sent, then the user wasn't actually nagged. nag.delete() except Exception as e: # Makes sure that problems here do not prevent subsequent processing. logger.error("Problem in maintenance_nag: %s", str(e))