def send_emails(self, recipients): """Send the emails using the templates and contexts requested.""" email_subject = self.options['subject'] email_sender = self.options['sender'] txt_template = loader.get_template(self.options['template']) context = ast.literal_eval(self.options['context']) email_txt = txt_template.render(context) emails_sent_count = 0 messages = [] for recipient in recipients: messages.append(( email_subject, email_txt, email_sender, [recipient], )) emails_sent_count += 1 if not self.options['simulate']: send_mass_mail(messages, fail_silently=False) tally_stat('pr.emails.sent', inc=emails_sent_count) logger.info("Sent %s pr emails." % emails_sent_count) else: sys.stdout.write('Simulation mode. Imagine that we just sent %s ' 'emails!\n' % emails_sent_count)
def send_docket_alert(d_pk: int, since: datetime) -> None: """Send an alert for a given docket :param d_pk: The docket PK that was modified :param since: If we run alerts, notify users about items *since* this time. :return: None """ email_addresses = (User.objects.filter( docket_alerts__docket_id=d_pk).distinct().values_list("email", flat=True)) if not email_addresses: # Nobody subscribed to the docket. delete_redis_semaphore("ALERTS", make_alert_key(d_pk)) return d = Docket.objects.get(pk=d_pk) new_des = DocketEntry.objects.filter(date_created__gte=since, docket=d) if new_des.count() == 0: # No new docket entries. delete_redis_semaphore("ALERTS", make_alert_key(d_pk)) return # Notify every user that's subscribed to this alert. messages = make_alert_messages(d, new_des, email_addresses) connection = get_connection() connection.send_messages(messages) # Work completed. Tally, log, and clean up tally_stat("alerts.docket.alerts.sent", inc=len(email_addresses)) DocketAlert.objects.filter(docket=d).update(date_last_hit=now()) delete_redis_semaphore("ALERTS", make_alert_key(d_pk))
def send_docket_alert(d_pk, since): """Send an alert for a given docket :param d_pk: The docket PK that was modified :param since: If we run alerts, notify users about items *since* this time. :return: The dict that was passed in as data is simply passed through. The next task in the chain needs the same information. """ email_addresses = User.objects.filter( docket_alerts__docket_id=d_pk, ).distinct().values_list('email', flat=True) if email_addresses: # We have an alert for this docket. Proceed. docket = Docket.objects.get(pk=d_pk) new_des = DocketEntry.objects.filter(date_created__gte=since, docket=docket) if new_des.count() > 0: # Notify every user that's subscribed to this alert. case_name = trunc(best_case_name(docket), 100, ellipsis='...') subject_template = loader.get_template('docket_alert_subject.txt') subject = subject_template.render({ 'docket': docket, 'count': new_des.count(), 'case_name': case_name, }).strip() # Remove newlines that editors can insist on adding. email_context = {'new_des': new_des, 'docket': docket} txt_template = loader.get_template('docket_alert_email.txt') html_template = loader.get_template('docket_alert_email.html') messages = [] for email_address in email_addresses: msg = EmailMultiAlternatives( subject=subject, body=txt_template.render(email_context), from_email=settings.DEFAULT_ALERTS_EMAIL, to=[email_address], headers={'X-Entity-Ref-ID': 'docket.alert:%s' % d_pk}) html = html_template.render(email_context) msg.attach_alternative(html, "text/html") messages.append(msg) # Add a bcc to the first message in the list so that we get a copy. messages[0].bcc = ['*****@*****.**'] connection = get_connection() connection.send_messages(messages) tally_stat('alerts.docket.alerts.sent', inc=len(email_addresses)) DocketAlert.objects.filter(docket=docket).update(date_last_hit=now()) # Work completed, clear the semaphor r = redis.StrictRedis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=settings.REDIS_DATABASES['ALERTS']) r.delete(make_alert_key(d_pk))
def send_docket_alert(d_pk, since): """Send an alert for a given docket :param d_pk: The docket PK that was modified :param since: If we run alerts, notify users about items *since* this time. :return: None """ email_addresses = (User.objects.filter( docket_alerts__docket_id=d_pk).distinct().values_list("email", flat=True)) if email_addresses: # We have an alert for this docket. Proceed. docket = Docket.objects.get(pk=d_pk) new_des = DocketEntry.objects.filter(date_created__gte=since, docket=docket) if new_des.count() > 0: # Notify every user that's subscribed to this alert. case_name = trunc(best_case_name(docket), 100, ellipsis="...") subject_template = loader.get_template("docket_alert_subject.txt") subject = subject_template.render({ "docket": docket, "count": new_des.count(), "case_name": case_name, }).strip() # Remove newlines that editors can insist on adding. email_context = {"new_des": new_des, "docket": docket} txt_template = loader.get_template("docket_alert_email.txt") html_template = loader.get_template("docket_alert_email.html") messages = [] for email_address in email_addresses: msg = EmailMultiAlternatives( subject=subject, body=txt_template.render(email_context), from_email=settings.DEFAULT_ALERTS_EMAIL, to=[email_address], headers={"X-Entity-Ref-ID": "docket.alert:%s" % d_pk}, ) html = html_template.render(email_context) msg.attach_alternative(html, "text/html") messages.append(msg) # Add a bcc to the first message in the list so that we get a copy. messages[0].bcc = ["*****@*****.**"] connection = get_connection() connection.send_messages(messages) tally_stat("alerts.docket.alerts.sent", inc=len(email_addresses)) DocketAlert.objects.filter(docket=docket).update(date_last_hit=now()) # Work completed, clear the semaphore r = make_redis_interface("ALERTS") r.delete(make_alert_key(d_pk))
def send_emails(self, rate): """Send out an email to every user whose alert has a new hit for a rate. """ users = User.objects.filter(alerts__rate=rate).distinct() alerts_sent_count = 0 for user in users: alerts = user.alerts.filter(rate=rate) logger.info("Running alerts for user '%s': %s" % (user, alerts)) not_donated_enough = ( user.profile.total_donated_last_year < settings.MIN_DONATION["rt_alerts"] ) if not_donated_enough and rate == Alert.REAL_TIME: logger.info( "User: %s has not donated enough for their %s " "RT alerts to be sent.\n" % (user, alerts.count()) ) continue hits = [] for alert in alerts: try: qd, results = self.run_query(alert, rate) except: traceback.print_exc() logger.info( "Search for this alert failed: %s\n" % alert.query ) continue # hits is a multi-dimensional array. It consists of alerts, # paired with a list of document dicts, of the form: # [[alert1, [{hit1}, {hit2}, {hit3}]], [alert2, ...]] if len(results) > 0: hits.append( [alert, qd.get("type", SEARCH_TYPES.OPINION), results] ) alert.query_run = qd.urlencode() alert.date_last_hit = now() alert.save() if len(hits) > 0: alerts_sent_count += 1 send_alert(user.profile, hits) tally_stat("alerts.sent.%s" % rate, inc=alerts_sent_count) logger.info("Sent %s %s email alerts." % (alerts_sent_count, rate))
def send_emails(self, rate): """Send out an email to every user whose alert has a new hit for a rate. """ users = User.objects.filter( alerts__rate=rate, ).distinct() alerts_sent_count = 0 for user in users: alerts = user.alerts.filter(rate=rate) logger.info("\n\nAlerts for user '%s': %s\n" "%s\n" % (user, alerts, '*' * 40)) not_donated_enough = user.profile.total_donated_last_year < \ settings.MIN_DONATION['rt_alerts'] if not_donated_enough and rate == 'rt': logger.info('\n\nUser: %s has not donated enough for their %s ' 'RT alerts to be sent.\n' % (user, len(alerts))) continue hits = [] for alert in alerts: error, alert_type, results = self.run_query(alert, rate) if error: continue # hits is a multi-dimensional array. It consists of alerts, # paired with a list of document dicts, of the form: # [[alert1, [{hit1}, {hit2}, {hit3}]], [alert2, ...]] try: if len(results) > 0: hits.append([alert, alert_type, results]) alert.date_last_hit = now() alert.save() except Exception as e: traceback.print_exc() logger.info(" Search failed on this alert: %s\n%s\n" % (alert.query, e)) if len(hits) > 0: alerts_sent_count += 1 send_alert(user.profile, hits, self.options['simulate']) elif self.options['verbosity'] >= 1: logger.info(" No hits. Not sending mail for this cl.\n") if not self.options['simulate']: tally_stat('alerts.sent.%s' % rate, inc=alerts_sent_count) logger.info("Sent %s %s email alerts." % (alerts_sent_count, rate))
def serve_pagerank_file(request): """Serves the bulk pagerank file from the bulk data directory.""" file_loc = settings.BULK_DATA_DIR + "external_pagerank" file_name = file_loc.split('/')[-1] try: mimetype = magic.from_file(file_loc, mime=True) except IOError: raise Http404('Unable to locate external_pagerank file in %s' % settings.BULK_DATA_DIR) response = HttpResponse() response['X-Sendfile'] = os.path.join(file_loc) response['Content-Disposition'] = 'attachment; filename="%s"' % file_name.encode('utf-8') response['Content-Type'] = mimetype tally_stat('bulk_data.pagerank.served') return response
def financial_disclosures_fileserver(request, pk, slug, filepath): """Serve up the financial disclosure files.""" response = HttpResponse() file_loc = os.path.join(settings.MEDIA_ROOT, filepath.encode('utf-8')) if settings.DEVELOPMENT: # X-Sendfile will only confuse you in a dev env. response.content = open(file_loc, 'r').read() else: response['X-Sendfile'] = file_loc filename = filepath.split('/')[-1] response['Content-Disposition'] = 'inline; filename="%s"' % \ filename.encode('utf-8') response['Content-Type'] = magic.from_file(file_loc, mime=True) if not is_bot(request): tally_stat('financial_reports.static_file.served') return response
def serve_pagerank_file(request): """Serves the bulk pagerank file from the bulk data directory.""" file_loc = settings.BULK_DATA_DIR + "external_pagerank" file_name = file_loc.split('/')[-1] try: mimetype = magic.from_file(file_loc, mime=True) except IOError: raise Http404('Unable to locate external_pagerank file in %s' % settings.BULK_DATA_DIR) response = HttpResponse() response['X-Sendfile'] = os.path.join(file_loc) response[ 'Content-Disposition'] = 'attachment; filename="%s"' % file_name.encode( 'utf-8') response['Content-Type'] = mimetype tally_stat('bulk_data.pagerank.served') return response
def serve_static_file(request, file_path=''): """Sends a static file to a user. This serves up the static case files such as the PDFs in a way that can be blocked from search engines if necessary. We do four things: - Look up the document or audio file associated with the filepath - Check if it's blocked - If blocked, we set the x-robots-tag HTTP header - Serve up the file using Apache2's xsendfile """ response = HttpResponse() file_loc = os.path.join(settings.MEDIA_ROOT, file_path.encode('utf-8')) if file_path.startswith('mp3'): item = get_object_or_404(Audio, local_path_mp3=file_path) mimetype = 'audio/mpeg' elif file_path.startswith('recap'): # Create an empty object, and set it to blocked. No need to hit the DB # since all RECAP documents are blocked. item = RECAPDocument() item.blocked = True mimetype = 'application/pdf' else: item = get_object_or_404(Opinion, local_path=file_path) item.blocked = item.cluster.blocked try: mimetype = magic.from_file(file_loc, mime=True) except IOError: raise Http404 if item.blocked: response['X-Robots-Tag'] = 'noindex, noodp, noarchive, noimageindex' if settings.DEVELOPMENT: # X-Sendfile will only confuse you in a dev env. response.content = open(file_loc, 'r').read() else: response['X-Sendfile'] = file_loc file_name = file_path.split('/')[-1] response['Content-Disposition'] = 'inline; filename="%s"' % \ file_name.encode('utf-8') response['Content-Type'] = mimetype if not is_bot(request): tally_stat('case_page.static_file.served') return response
def mapper_homepage(request): if not is_bot(request): tally_stat('visualization.scotus_homepage_loaded') visualizations = SCOTUSMap.objects.filter( published=True, deleted=False, ).annotate(Count('clusters'), ).filter( # Ensures that we only show good stuff on homepage clusters__count__gt=10, ).order_by( '-date_published', '-date_modified', '-date_created', )[:2] return render(request, 'visualization_home.html', { 'visualizations': visualizations, 'private': False, })
def send_emails(self, rate): """Send out an email to every user whose alert has a new hit for a rate. """ users = User.objects.filter(alerts__rate=rate).distinct() alerts_sent_count = 0 for user in users: alerts = user.alerts.filter(rate=rate) logger.info("Running alerts for user '%s': %s" % (user, alerts)) not_donated_enough = user.profile.total_donated_last_year < \ settings.MIN_DONATION['rt_alerts'] if not_donated_enough and rate == Alert.REAL_TIME: logger.info('User: %s has not donated enough for their %s ' 'RT alerts to be sent.\n' % (user, alerts.count())) continue hits = [] for alert in alerts: try: qd, results = self.run_query(alert, rate) except: traceback.print_exc() logger.info("Search for this alert failed: %s\n" % alert.query) continue # hits is a multi-dimensional array. It consists of alerts, # paired with a list of document dicts, of the form: # [[alert1, [{hit1}, {hit2}, {hit3}]], [alert2, ...]] if len(results) > 0: hits.append([alert, qd.get('type', 'o'), results]) alert.query_run = qd.urlencode() alert.date_last_hit = now() alert.save() if len(hits) > 0: alerts_sent_count += 1 send_alert(user.profile, hits) tally_stat('alerts.sent.%s' % rate, inc=alerts_sent_count) logger.info("Sent %s %s email alerts." % (alerts_sent_count, rate))
def mapper_homepage(request: HttpRequest) -> HttpResponse: if not is_bot(request): tally_stat("visualization.scotus_homepage_loaded") visualizations = ( SCOTUSMap.objects.filter(published=True, deleted=False) .annotate(Count("clusters")) .filter( # Ensures that we only show good stuff on homepage clusters__count__gt=10, ) .order_by("-date_published", "-date_modified", "-date_created")[:2] ) return render( request, "visualization_home.html", {"visualizations": visualizations, "private": False}, )
def mapper_homepage(request): if not is_bot(request): tally_stat('visualization.scotus_homepage_loaded') visualizations = SCOTUSMap.objects.filter( published=True, deleted=False, ).annotate( Count('clusters'), ).filter( # Ensures that we only show good stuff on homepage clusters__count__gt=10, ).order_by( '-date_published', '-date_modified', '-date_created', )[:2] return render(request, 'visualization_home.html', { 'visualizations': visualizations, 'private': False, })
def build_visualization(viz): """Use the start and end points to generate a visualization :param viz: A Visualization object to work on :return: A tuple of (status<str>, viz) """ build_kwargs = { "parent_authority": viz.cluster_end, "visited_nodes": {}, "good_nodes": {}, "max_hops": 3, } t1 = time.time() try: g = viz.build_nx_digraph(**build_kwargs) except TooManyNodes: try: # Try with fewer hops. build_kwargs["max_hops"] = 2 g = viz.build_nx_digraph(**build_kwargs) except TooManyNodes: # Still too many hops. Abort. tally_stat("visualization.too_many_nodes_failure") return "too_many_nodes", viz if len(g.edges()) == 0: tally_stat("visualization.too_few_nodes_failure") return "too_few_nodes", viz t2 = time.time() viz.generation_time = t2 - t1 viz.save() viz.add_clusters(g) j = viz.to_json(g) jv = JSONVersion(map=viz, json_data=j) jv.save() return "success", viz
def test_tally_a_stat(self): count = tally_stat("test") self.assertEqual(count, 1)
def new_visualization(request): demo_viz = SCOTUSMap.objects.filter( published=True, deleted=False, ).annotate( Count('clusters'), ).filter( # Ensures that we only show good stuff on homepage clusters__count__gt=5, clusters__count__lt=15, ).order_by( '-date_published', '-date_modified', '-date_created', )[:1] context = { 'SCDB_LATEST_CASE': settings.SCDB_LATEST_CASE.isoformat(), 'demo_viz': demo_viz, 'private': True, } if request.method == 'POST': form = VizForm(request.POST) context['form'] = form if form.is_valid(): # Process the data in form.cleaned_data cd = form.cleaned_data start, end = reverse_endpoints_if_needed(cd['cluster_start'], cd['cluster_end']) viz = SCOTUSMap( user=request.user, cluster_start=start, cluster_end=end, title=cd['title'], notes=cd['notes'], ) build_kwargs = { 'parent_authority': end, 'visited_nodes': {}, 'good_nodes': {}, 'max_hops': 3, } t1 = time.time() try: g = viz.build_nx_digraph(**build_kwargs) except TooManyNodes: try: # Try with fewer hops. build_kwargs['max_hops'] = 2 g = viz.build_nx_digraph(**build_kwargs) msg = message_dict['fewer_hops_delivered'] messages.add_message(request, msg['level'], msg['message']) except TooManyNodes: # Still too many hops. Abort. tally_stat('visualization.too_many_nodes_failure') msg = message_dict['too_many_nodes'] messages.add_message(request, msg['level'], msg['message']) return render(request, 'new_visualization.html', context) if len(g.edges()) == 0: tally_stat('visualization.too_few_nodes_failure') msg = message_dict['too_few_nodes'] messages.add_message(request, msg['level'], msg['message']) return render(request, 'new_visualization.html', { 'form': form, 'private': True }) t2 = time.time() viz.generation_time = t2 - t1 viz.save() viz.add_clusters(g) j = viz.to_json(g) jv = JSONVersion(map=viz, json_data=j) jv.save() return HttpResponseRedirect(reverse( 'view_visualization', kwargs={'pk': viz.pk, 'slug': viz.slug} )) else: context['form'] = VizForm() return render(request, 'new_visualization.html', context)
def show_results(request): """ This view can vary significantly, depending on how it is called: - In its most simple form, it is called via GET and without any parameters. --> This loads the homepage. - It might also be called with GET *with* parameters. --> This loads search results. - It might be called with a POST. --> This attempts to save an alert. It also has a few failure modes it needs to support: - It must react properly to an invalid alert form. - It must react properly to an invalid or failing search form. All of these paths have tests. """ # Create a search string that does not contain the page numbers get_string = make_get_string(request) get_string_sans_alert = make_get_string(request, ['page', 'edit_alert']) render_dict = { 'private': True, 'get_string': get_string, 'get_string_sans_alert': get_string_sans_alert, } if request.method == 'POST': # The user is trying to save an alert. alert_form = CreateAlertForm(request.POST, user=request.user) if alert_form.is_valid(): cd = alert_form.cleaned_data # save the alert if request.POST.get('edit_alert'): # check if the user can edit this, or if they are url hacking alert = get_object_or_404( Alert, pk=request.POST.get('edit_alert'), user=request.user, ) alert_form = CreateAlertForm(cd, instance=alert, user=request.user) alert_form.save() action = "edited" else: alert_form = CreateAlertForm(cd, user=request.user) alert = alert_form.save(commit=False) alert.user = request.user alert.save() action = "created" messages.add_message(request, messages.SUCCESS, 'Your alert was %s successfully.' % action) # and redirect to the alerts page return HttpResponseRedirect(reverse("profile_alerts")) else: # Invalid form. Do the search again and show them the alert form # with the errors render_dict.update(do_search(request)) render_dict.update({'alert_form': alert_form}) return render(request, 'search.html', render_dict) else: # Either a search or the homepage if len(request.GET) == 0: # No parameters --> Homepage. if not is_bot(request): tally_stat('search.homepage_loaded') # Ensure we get nothing from the future. request.GET = request.GET.copy() # Makes it mutable request.GET['filed_before'] = date.today() # Load the render_dict with good results that can be shown in the # "Latest Cases" section render_dict.update(do_search(request, rows=5, order_by='dateFiled desc', facet=False)) # Get the results from the oral arguments as well oa_dict = do_search(request, rows=5, order_by='dateArgued desc', type='oa', facet=False) render_dict.update({'results_oa': oa_dict['results']}) # But give it a fresh form for the advanced search section render_dict.update({'search_form': SearchForm(request.GET)}) # Get a bunch of stats. render_dict.update(get_homepage_stats()) return render(request, 'homepage.html', render_dict) else: # User placed a search or is trying to edit an alert if request.GET.get('edit_alert'): # They're editing an alert if request.user.is_anonymous(): return HttpResponseRedirect( "{path}?next={next}{encoded_params}".format( path=reverse('sign-in'), next=request.path, encoded_params=quote("?" + request.GET.urlencode()) )) else: alert = get_object_or_404( Alert, pk=request.GET.get('edit_alert'), user=request.user ) alert_form = CreateAlertForm( instance=alert, initial={'query': get_string_sans_alert}, user=request.user, ) else: # Just a regular search if not is_bot(request): tally_stat('search.results') # Create bare-bones alert form. alert_form = CreateAlertForm( initial={'query': get_string, 'rate': "dly"}, user=request.user ) render_dict.update(do_search(request)) render_dict.update({'alert_form': alert_form}) return render(request, 'search.html', render_dict)
def register(request): """allow only an anonymous user to register""" redirect_to = request.GET.get('next', '') if 'sign-in' in redirect_to: # thus, we don't redirect people back to the sign-in form redirect_to = '' # security checks: # Light security check -- make sure redirect_to isn't garbage. if not redirect_to or ' ' in redirect_to: redirect_to = settings.LOGIN_REDIRECT_URL # Heavier security check -- redirects to http://example.com should # not be allowed, but things like /view/?param=http://example.com # should be allowed. This regex checks if there is a '//' *before* a # question mark. elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to): redirect_to = settings.LOGIN_REDIRECT_URL if request.user.is_anonymous: if request.method == 'POST': try: stub_account = User.objects.filter( profile__stub_account=True, ).get( email__iexact=request.POST.get('email'), ) except User.DoesNotExist: stub_account = False if stub_account: form = UserCreationFormExtended( request.POST, instance=stub_account ) else: form = UserCreationFormExtended(request.POST) consent_form = OptInConsentForm(request.POST) if form.is_valid() and consent_form.is_valid(): cd = form.cleaned_data if not stub_account: # make a new user that is active, but has not confirmed # their email address user = User.objects.create_user( cd['username'], cd['email'], cd['password1'] ) up = UserProfile(user=user) else: # Upgrade the stub account to make it a regular account. user = stub_account user.set_password(cd['password1']) user.username = cd['username'] up = stub_account.profile up.stub_account = False if cd['first_name']: user.first_name = cd['first_name'] if cd['last_name']: user.last_name = cd['last_name'] user.save() # Build and assign the activation key salt = hashlib.sha1(str(random.random())).hexdigest()[:5] up.activation_key = hashlib.sha1( salt + user.username).hexdigest() up.key_expires = now() + timedelta(days=5) up.save() email = emails['confirm_your_new_account'] send_mail( email['subject'], email['body'] % (user.username, up.activation_key), email['from'], [user.email] ) email = emails['new_account_created'] send_mail( email['subject'] % up.user.username, email['body'] % ( up.user.get_full_name() or "Not provided", up.user.email ), email['from'], email['to'], ) tally_stat('user.created') get_str = '?next=%s&email=%s' % (urlencode(redirect_to), urlencode(user.email)) return HttpResponseRedirect(reverse('register_success') + get_str) else: form = UserCreationFormExtended() consent_form = OptInConsentForm() return render(request, "register/register.html", { 'form': form, 'consent_form': consent_form, 'private': False }) else: # The user is already logged in. Direct them to their settings page as # a logical fallback return HttpResponseRedirect(reverse('view_settings'))
def register(request): """allow only an anonymous user to register""" redirect_to = sanitize_redirection(request) if request.user.is_anonymous: if request.method == 'POST': try: stub_account = User.objects.filter( profile__stub_account=True, ).get( email__iexact=request.POST.get('email'), ) except User.DoesNotExist: stub_account = False if stub_account: form = UserCreationFormExtended( request.POST, instance=stub_account ) else: form = UserCreationFormExtended(request.POST) consent_form = OptInConsentForm(request.POST) if form.is_valid() and consent_form.is_valid(): cd = form.cleaned_data if not stub_account: # make a new user that is active, but has not confirmed # their email address user = User.objects.create_user( cd['username'], cd['email'], cd['password1'] ) up = UserProfile(user=user) else: # Upgrade the stub account to make it a regular account. user = stub_account user.set_password(cd['password1']) user.username = cd['username'] up = stub_account.profile up.stub_account = False if cd['first_name']: user.first_name = cd['first_name'] if cd['last_name']: user.last_name = cd['last_name'] user.save() # Build and assign the activation key up.activation_key = sha1_activation_key(user.username) up.key_expires = now() + timedelta(days=5) up.save() email = emails['confirm_your_new_account'] send_mail( email['subject'], email['body'] % (user.username, up.activation_key), email['from'], [user.email] ) email = emails['new_account_created'] send_mail( email['subject'] % up.user.username, email['body'] % ( up.user.get_full_name() or "Not provided", up.user.email ), email['from'], email['to'], ) tally_stat('user.created') get_str = '?next=%s&email=%s' % (urlencode(redirect_to), urlencode(user.email)) return HttpResponseRedirect(reverse('register_success') + get_str) else: form = UserCreationFormExtended() consent_form = OptInConsentForm() return render(request, "register/register.html", { 'form': form, 'consent_form': consent_form, 'private': False }) else: # The user is already logged in. Direct them to their settings page as # a logical fallback return HttpResponseRedirect(reverse('view_settings'))
def show_results(request): """ This view can vary significantly, depending on how it is called: - In its most simple form, it is called via GET and without any parameters. --> This loads the homepage. - It might also be called with GET *with* parameters. --> This loads search results. - It might be called with a POST. --> This attempts to save an alert. It also has a few failure modes it needs to support: - It must react properly to an invalid alert form. - It must react properly to an invalid or failing search form. All of these paths have tests. """ # Create a search string that does not contain the page numbers get_string = make_get_string(request) get_string_sans_alert = make_get_string( request, ["page", "edit_alert", "show_alert_modal"]) render_dict = { "private": True, "get_string": get_string, "get_string_sans_alert": get_string_sans_alert, } if request.method == "POST": # The user is trying to save an alert. alert_form = CreateAlertForm(request.POST, user=request.user) if alert_form.is_valid(): cd = alert_form.cleaned_data # save the alert if request.POST.get("edit_alert"): # check if the user can edit this, or if they are url hacking alert = get_object_or_404( Alert, pk=request.POST.get("edit_alert"), user=request.user, ) alert_form = CreateAlertForm(cd, instance=alert, user=request.user) alert_form.save() action = "edited" else: alert_form = CreateAlertForm(cd, user=request.user) alert = alert_form.save(commit=False) alert.user = request.user alert.save() action = "created" messages.add_message( request, messages.SUCCESS, "Your alert was %s successfully." % action, ) # and redirect to the alerts page return HttpResponseRedirect(reverse("profile_alerts")) else: # Invalid form. Do the search again and show them the alert form # with the errors render_dict.update(do_search(request.GET.copy())) render_dict.update({"alert_form": alert_form}) return render(request, "search.html", render_dict) else: # Either a search or the homepage if len(request.GET) == 0: # No parameters --> Homepage. if not is_bot(request): tally_stat("search.homepage_loaded") # Ensure we get nothing from the future. mutable_GET = request.GET.copy() # Makes it mutable mutable_GET["filed_before"] = date.today() # Load the render_dict with good results that can be shown in the # "Latest Cases" section render_dict.update( do_search( mutable_GET, rows=5, override_params={"order_by": "dateFiled desc"}, facet=False, cache_key="homepage-data-o", )) # Get the results from the oral arguments as well render_dict.update({ "results_oa": do_search( mutable_GET, rows=5, override_params={ "order_by": "dateArgued desc", "type": SEARCH_TYPES.ORAL_ARGUMENT, }, facet=False, cache_key="homepage-data-oa", )["results"] }) # But give it a fresh form for the advanced search section render_dict.update({"search_form": SearchForm(request.GET)}) # Get a bunch of stats. stats = get_homepage_stats() render_dict.update(stats) return render(request, "homepage.html", render_dict) else: # User placed a search or is trying to edit an alert if request.GET.get("edit_alert"): # They're editing an alert if request.user.is_anonymous: return HttpResponseRedirect( "{path}?next={next}{encoded_params}".format( path=reverse("sign-in"), next=request.path, encoded_params=quote("?" + request.GET.urlencode()), )) else: alert = get_object_or_404( Alert, pk=request.GET.get("edit_alert"), user=request.user, ) alert_form = CreateAlertForm( instance=alert, initial={"query": get_string_sans_alert}, user=request.user, ) else: # Just a regular search if not is_bot(request): tally_stat("search.results") # Create bare-bones alert form. alert_form = CreateAlertForm( initial={ "query": get_string, "rate": "dly" }, user=request.user, ) render_dict.update(do_search(request.GET.copy())) # Set the value to the query as a convenience alert_form.fields["name"].widget.attrs["value"] = render_dict[ "search_summary_str"] render_dict.update({"alert_form": alert_form}) return render(request, "search.html", render_dict)
def show_results(request): """ This view can vary significantly, depending on how it is called: - In its most simple form, it is called via GET and without any parameters. --> This loads the homepage. - It might also be called with GET *with* parameters. --> This loads search results. - It might be called with a POST. --> This attempts to save an alert. It also has a few failure modes it needs to support: - It must react properly to an invalid alert form. - It must react properly to an invalid or failing search form. All of these paths have tests. """ # Create a search string that does not contain the page numbers get_string = make_get_string(request) get_string_sans_alert = make_get_string(request, ['page', 'edit_alert']) render_dict = { 'private': True, 'get_string': get_string, 'get_string_sans_alert': get_string_sans_alert, } if request.method == 'POST': # The user is trying to save an alert. alert_form = CreateAlertForm(request.POST, user=request.user) if alert_form.is_valid(): cd = alert_form.cleaned_data # save the alert if request.POST.get('edit_alert'): # check if the user can edit this, or if they are url hacking alert = get_object_or_404( Alert, pk=request.POST.get('edit_alert'), user=request.user, ) alert_form = CreateAlertForm(cd, instance=alert, user=request.user) alert_form.save() action = "edited" else: alert_form = CreateAlertForm(cd, user=request.user) alert = alert_form.save(commit=False) alert.user = request.user alert.save() action = "created" messages.add_message(request, messages.SUCCESS, 'Your alert was %s successfully.' % action) # and redirect to the alerts page return HttpResponseRedirect(reverse("profile_alerts")) else: # Invalid form. Do the search again and show them the alert form # with the errors render_dict.update(do_search(request)) render_dict.update({'alert_form': alert_form}) return render(request, 'search.html', render_dict) else: # Either a search or the homepage if len(request.GET) == 0: # No parameters --> Homepage. if not is_bot(request): tally_stat('search.homepage_loaded') # Ensure we get nothing from the future. request.GET = request.GET.copy() # Makes it mutable request.GET['filed_before'] = date.today() homepage_cache_key = 'homepage-data' homepage_dict = cache.get(homepage_cache_key) if homepage_dict is not None: return render(request, 'homepage.html', homepage_dict) # Load the render_dict with good results that can be shown in the # "Latest Cases" section render_dict.update( do_search(request, rows=5, order_by='dateFiled desc', facet=False)) # Get the results from the oral arguments as well oa_dict = do_search(request, rows=5, order_by='dateArgued desc', type='oa', facet=False) render_dict.update({'results_oa': oa_dict['results']}) # But give it a fresh form for the advanced search section render_dict.update({'search_form': SearchForm(request.GET)}) # Get a bunch of stats. render_dict.update(get_homepage_stats()) six_hours = 60 * 60 * 6 cache.set(homepage_cache_key, render_dict, six_hours) return render(request, 'homepage.html', render_dict) else: # User placed a search or is trying to edit an alert if request.GET.get('edit_alert'): # They're editing an alert if request.user.is_anonymous(): return HttpResponseRedirect( "{path}?next={next}{encoded_params}".format( path=reverse('sign-in'), next=request.path, encoded_params=quote("?" + request.GET.urlencode()))) else: alert = get_object_or_404(Alert, pk=request.GET.get('edit_alert'), user=request.user) alert_form = CreateAlertForm( instance=alert, initial={'query': get_string_sans_alert}, user=request.user, ) else: # Just a regular search if not is_bot(request): tally_stat('search.results') # Create bare-bones alert form. alert_form = CreateAlertForm(initial={ 'query': get_string, 'rate': "dly" }, user=request.user) render_dict.update(do_search(request)) # Set the value to the query as a convenience alert_form.fields['name'].widget.attrs['value'] = \ render_dict['search_summary_str'] render_dict.update({'alert_form': alert_form}) return render(request, 'search.html', render_dict)
def test_increment_by_two(self): count = tally_stat('test3', inc=2) self.assertEqual(count, 2) count = tally_stat('test3', inc=2) self.assertEqual(count, 4)
def register(request): """allow only an anonymous user to register""" redirect_to = request.GET.get('next', '') if 'sign-in' in redirect_to: # thus, we don't redirect people back to the sign-in form redirect_to = '' # security checks: # Light security check -- make sure redirect_to isn't garbage. if not redirect_to or ' ' in redirect_to: redirect_to = settings.LOGIN_REDIRECT_URL # Heavier security check -- redirects to http://example.com should # not be allowed, but things like /view/?param=http://example.com # should be allowed. This regex checks if there is a '//' *before* a # question mark. elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to): redirect_to = settings.LOGIN_REDIRECT_URL if request.user.is_anonymous(): if request.method == 'POST': try: stub_account = User.objects.filter( profile__stub_account=True, ).get( email__iexact=request.POST.get('email'), ) except User.DoesNotExist: stub_account = False if stub_account: form = UserCreationFormExtended(request.POST, instance=stub_account) else: form = UserCreationFormExtended(request.POST) if form.is_valid(): cd = form.cleaned_data if not stub_account: # make a new user that is active, but has not confirmed # their email address user = User.objects.create_user(cd['username'], cd['email'], cd['password1']) up = UserProfile(user=user) else: # Upgrade the stub account to make it a regular account. user = stub_account user.set_password(cd['password1']) user.username = cd['username'] up = stub_account.profile up.stub_account = False if cd['first_name']: user.first_name = cd['first_name'] if cd['last_name']: user.last_name = cd['last_name'] user.save() # Build and assign the activation key salt = hashlib.sha1(str(random.random())).hexdigest()[:5] up.activation_key = hashlib.sha1(salt + user.username).hexdigest() up.key_expires = now() + timedelta(days=5) up.save() email = emails['confirm_your_new_account'] send_mail(email['subject'], email['body'] % (user.username, up.activation_key), email['from'], [user.email]) email = emails['new_account_created'] send_mail( email['subject'] % up.user.username, email['body'] % (up.user.get_full_name() or "Not provided", up.user.email), email['from'], email['to'], ) tally_stat('user.created') return HttpResponseRedirect( reverse('register_success') + '?next=%s' % redirect_to) else: form = UserCreationFormExtended() return render(request, "register/register.html", { 'form': form, 'private': False }) else: # The user is already logged in. Direct them to their settings page as # a logical fallback return HttpResponseRedirect(reverse('view_settings'))
def test_increment_a_stat(self): count = tally_stat('test2') self.assertEqual(count, 1) count = tally_stat('test2') self.assertEqual(count, 2)
def test_tally_a_stat(self): count = tally_stat('test') self.assertEqual(count, 1)
def register(request: HttpRequest) -> HttpResponse: """allow only an anonymous user to register""" redirect_to = get_redirect_or_login_url(request, "next") if request.user.is_anonymous: if request.method == "POST": try: stub_account = User.objects.filter( profile__stub_account=True, ).get( email__iexact=request.POST.get("email")) except User.DoesNotExist: stub_account = False if stub_account: form = UserCreationFormExtended(request.POST, instance=stub_account) else: form = UserCreationFormExtended(request.POST) consent_form = OptInConsentForm(request.POST) if form.is_valid() and consent_form.is_valid(): cd = form.cleaned_data if not stub_account: # make a new user that is active, but has not confirmed # their email address user = User.objects.create_user(cd["username"], cd["email"], cd["password1"]) up = UserProfile(user=user) else: # Upgrade the stub account to make it a regular account. user = stub_account user.set_password(cd["password1"]) user.username = cd["username"] up = stub_account.profile up.stub_account = False if cd["first_name"]: user.first_name = cd["first_name"] if cd["last_name"]: user.last_name = cd["last_name"] user.save() # Build and assign the activation key up.activation_key = sha1_activation_key(user.username) up.key_expires = now() + timedelta(days=5) up.save() email: EmailType = emails["confirm_your_new_account"] send_mail( email["subject"], email["body"] % (user.username, up.activation_key), email["from_email"], [user.email], ) email: EmailType = emails["new_account_created"] send_mail( email["subject"] % up.user.username, email["body"] % ( up.user.get_full_name() or "Not provided", up.user.email, ), email["from_email"], email["to"], ) tally_stat("user.created") get_str = "?next=%s&email=%s" % ( urlencode(redirect_to), urlencode(user.email), ) return HttpResponseRedirect( reverse("register_success") + get_str) else: form = UserCreationFormExtended() consent_form = OptInConsentForm() return render( request, "register/register.html", { "form": form, "consent_form": consent_form, "private": False }, ) else: # The user is already logged in. Direct them to their settings page as # a logical fallback return HttpResponseRedirect(reverse("view_settings"))
def test_increment_a_stat(self): count = tally_stat("test2") self.assertEqual(count, 1) count = tally_stat("test2") self.assertEqual(count, 2)
def new_visualization(request): demo_viz = ( SCOTUSMap.objects.filter( published=True, deleted=False, ).annotate(Count("clusters"), ).filter( # Ensures that we only show good stuff on homepage clusters__count__gt=5, clusters__count__lt=15, ).order_by( "-date_published", "-date_modified", "-date_created", )[:1]) context = { "SCDB_LATEST_CASE": settings.SCDB_LATEST_CASE.isoformat(), "demo_viz": demo_viz, "private": True, } if request.method == "POST": form = VizForm(request.POST) context["form"] = form if form.is_valid(): # Process the data in form.cleaned_data cd = form.cleaned_data start, end = reverse_endpoints_if_needed(cd["cluster_start"], cd["cluster_end"]) viz = SCOTUSMap( user=request.user, cluster_start=start, cluster_end=end, title=cd["title"], notes=cd["notes"], ) build_kwargs = { "parent_authority": end, "visited_nodes": {}, "good_nodes": {}, "max_hops": 3, } t1 = time.time() try: g = viz.build_nx_digraph(**build_kwargs) except TooManyNodes: try: # Try with fewer hops. build_kwargs["max_hops"] = 2 g = viz.build_nx_digraph(**build_kwargs) msg = message_dict["fewer_hops_delivered"] messages.add_message(request, msg["level"], msg["message"]) except TooManyNodes: # Still too many hops. Abort. tally_stat("visualization.too_many_nodes_failure") msg = message_dict["too_many_nodes"] messages.add_message(request, msg["level"], msg["message"]) return render(request, "new_visualization.html", context) if len(g.edges()) == 0: tally_stat("visualization.too_few_nodes_failure") msg = message_dict["too_few_nodes"] messages.add_message(request, msg["level"], msg["message"]) return render( request, "new_visualization.html", { "form": form, "private": True }, ) t2 = time.time() viz.generation_time = t2 - t1 viz.save() viz.add_clusters(g) j = viz.to_json(g) jv = JSONVersion(map=viz, json_data=j) jv.save() return HttpResponseRedirect( reverse( "view_visualization", kwargs={ "pk": viz.pk, "slug": viz.slug }, )) else: context["form"] = VizForm() return render(request, "new_visualization.html", context)
def test_increment_by_two(self): count = tally_stat("test3", inc=2) self.assertEqual(count, 2) count = tally_stat("test3", inc=2) self.assertEqual(count, 4)
class Command(BaseCommand): help = 'Sends the alert emails on a real time, daily, weekly or monthly ' \ 'basis.' def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) self.connections = { 'o': ExtraSolrInterface(settings.SOLR_OPINION_URL, mode='r'), 'oa': ExtraSolrInterface(settings.SOLR_AUDIO_URL, mode='r'), } self.options = {} self.valid_ids = {} def add_arguments(self, parser): parser.add_argument( '--rate', required=True, choices=dict(FREQUENCY).keys(), help="The rate to send emails (%s)" % ', '.join(dict(FREQUENCY).keys()), ) parser.add_argument( '--simulate', action='store_true', default=False, help='Simulate the emails that would be sent using the console ' 'backend.', ) def run_query(self, alert, rate): results = [] error = False cd = {} try: logger.info("Now running the query: %s\n" % alert.query) # Set up the data data = search_utils.get_string_to_dict(alert.query) try: del data['filed_before'] except KeyError: pass data['order_by'] = 'score desc' logger.info(" Data sent to SearchForm is: %s\n" % data) search_form = SearchForm(data) if search_form.is_valid(): cd = search_form.cleaned_data if rate == 'rt' and len(self.valid_ids[cd['type']]) == 0: # Bail out. No results will be found if no valid_ids. return error, cd['type'], results cut_off_date = get_cut_off_date(rate) if cd['type'] == 'o': cd['filed_after'] = cut_off_date elif cd['type'] == 'oa': cd['argued_after'] = cut_off_date main_params = search_utils.build_main_query(cd, facet=False) main_params.update({ 'rows': '20', 'start': '0', 'hl.tag.pre': '<em><strong>', 'hl.tag.post': '</strong></em>', 'caller': 'cl_send_alerts', }) if rate == 'rt': main_params['fq'].append('id:(%s)' % ' OR '.join( [str(i) for i in self.valid_ids[cd['type']]])) results = self.connections[cd['type']].query().add_extra( **main_params).execute() regroup_snippets(results) else: logger.info(" Query for alert %s was invalid\n" " Errors from the SearchForm: %s\n" % (alert.query, search_form.errors)) error = True except: traceback.print_exc() logger.info(" Search for this alert failed: %s\n" % alert.query) error = True logger.info(" There were %s results\n" % len(results)) return error, cd.get('type'), results def send_emails(self, rate): """Send out an email to every user whose alert has a new hit for a rate. """ users = User.objects.filter(alerts__rate=rate, ).distinct() alerts_sent_count = 0 for user in users: alerts = user.alerts.filter(rate=rate) logger.info("\n\nAlerts for user '%s': %s\n" "%s\n" % (user, alerts, '*' * 40)) not_donated_enough = user.profile.total_donated_last_year < \ settings.MIN_DONATION['rt_alerts'] if not_donated_enough and rate == 'rt': logger.info('\n\nUser: %s has not donated enough for their %s ' 'RT alerts to be sent.\n' % (user, len(alerts))) continue hits = [] for alert in alerts: error, alert_type, results = self.run_query(alert, rate) if error: continue # hits is a multi-dimensional array. It consists of alerts, # paired with a list of document dicts, of the form: # [[alert1, [{hit1}, {hit2}, {hit3}]], [alert2, ...]] try: if len(results) > 0: hits.append([alert, alert_type, results]) alert.date_last_hit = now() alert.save() except Exception, e: traceback.print_exc() logger.info(" Search failed on this alert: %s\n%s\n" % (alert.query, e)) if len(hits) > 0: alerts_sent_count += 1 send_alert(user.profile, hits, self.options['simulate']) elif self.options['verbosity'] >= 1: logger.info(" No hits. Not sending mail for this cl.\n") if not self.options['simulate']: tally_stat('alerts.sent.%s' % rate, inc=alerts_sent_count) logger.info("Sent %s %s email alerts." % (alerts_sent_count, rate))