Beispiel #1
0
def ensure_valid_configuration():
    """
    Ensures that the DocuSign settings are properly defined. It doesn't actually
    verify that *DocuSign* thinks they're valid, though.
    """

    for setting in REQUIRED_SETTINGS:
        if not getattr(settings, setting):
            raise ImproperlyConfigured(
                f"The {setting} setting is not configured!")

    config = get_config()

    if not config.private_key:
        raise ImproperlyConfigured(
            "DocuSign private key is not configured! Please run the 'manage.py setdocusignkey' "
            "command.")

    if not (config.consent_code and config.base_uri):
        cburl = absolute_reverse("docusign:callback")
        conurl = absolute_reverse("docusign:consent")
        raise ImproperlyConfigured(
            f"DocuSign consent code is not configured!  "
            f"Please make sure you have registered {cburl} as a "
            f"callback URL in DocuSign and then "
            f"obtain consent from a DocuSign user at {conurl}.")
    def handle(self, *args, **options) -> None:
        users = get_old_loc_users()
        if users:
            count = len(users)
            user = users[0]
            if count == 1:
                desc = (
                    f"One user ({slack.hyperlink(text=user.best_first_name, href=user.admin_url)}) "
                    f"has not had their letter of complaint sent"
                )
            else:
                desc = (
                    f"{count} users, including "
                    f"{slack.hyperlink(text=user.best_first_name, href=user.admin_url)}, "
                    f"have not had their letters of "
                    f"complaint sent"
                )
            self.stdout.write(
                f"Posting reminder to admins about sending letters of complaint: '{desc}'\n"
            )

            url = absolute_reverse("admin:loc_locuser_changelist")
            link = slack.hyperlink(url, text="send letters of complaint")
            slack.sendmsg(
                f"<!channel> {desc} in at least {LOC_OLD_AGE.days} days! Please {link}.",
                is_safe=True,
            )
        else:
            self.stdout.write("No reminders need to be posted.\n")
Beispiel #3
0
    def get_upload_url(self) -> str:
        '''
        Returns an absolute path to the upload URL for this token.
        '''

        return absolute_reverse('hpaction:upload',
                                kwargs={'token_str': self.id})
Beispiel #4
0
    def get_upload_url(self) -> str:
        """
        Returns an absolute path to the upload URL for this token.
        """

        return absolute_reverse("hpaction:upload",
                                kwargs={"token_str": self.id})
Beispiel #5
0
def send_admin_notification_for_letter(letter_id: int):
    from django.template.loader import render_to_string
    from django.core.mail import EmailMessage
    from django.conf import settings

    from project.util.site_util import absolute_reverse
    from .models import LetterRequest, LOC_MAILING_CHOICES

    letter = LetterRequest.objects.get(id=letter_id)
    user = letter.user

    assert letter.mail_choice == LOC_MAILING_CHOICES.WE_WILL_MAIL

    body = render_to_string(
        "loc/admin/notification-email.txt",
        {
            "user":
            user,
            "letter":
            letter,
            "send_letter_url":
            absolute_reverse("admin:mail-via-lob",
                             kwargs={"letterid": letter.id}),
            "reject_letter_url":
            absolute_reverse("admin:reject-letter",
                             kwargs={"letterid": letter.id}),
            "archive_letter_url":
            absolute_reverse("admin:archive-letter",
                             kwargs={"letterid": letter.id}),
            "user_loc_url":
            absolutify_url("/loc/issues"),
            "edit_letter_url":
            absolute_reverse("admin:loc_locuser_change", args=(user.pk, )),
        },
    )
    subject = f"Letter of Complaint request for {user.full_legal_name}"
    user_email = user.as_email_recipient()

    msg = EmailMessage(
        subject=subject,
        body=body,
        from_email=settings.DEFAULT_FROM_EMAIL,
        to=[settings.LOC_EMAIL],
        reply_to=[user_email] if user_email else None,
    )
    msg.send()
Beispiel #6
0
    def run_check(self):
        name = Site.objects.get_current().name
        start_time = datetime.datetime.now()

        homepage_url = absolute_reverse('react')
        r = check_url(homepage_url, 'text/html')
        css_url = get_first_regex_match_group(
            r'rel="stylesheet" href="([^"]*)"', r.text)
        css_url = urljoin(homepage_url, css_url)
        check_url(css_url, 'text/css')

        health_url = absolute_reverse('health')
        r = check_url(health_url, 'application/json')
        assert_equal(r.json()['status'], 200)

        total_time = datetime.datetime.now() - start_time
        self.stdout.write(
            f'Health check for {name} successful! Completed in {total_time}.')
Beispiel #7
0
def execute_user_stats_query(cursor, include_pad_bbl: bool = False):
    admin_url_begin, admin_url_end = absolute_reverse(
        'admin:users_justfixuser_change', args=(999, )).split('999')
    cursor.execute(
        USER_STATS_SQLFILE.read_text(), {
            'include_pad_bbl': include_pad_bbl,
            'unusable_password_pattern': UNUSABLE_PASSWORD_PREFIX + '%',
            'admin_url_begin': admin_url_begin,
            'admin_url_end': admin_url_end
        })
Beispiel #8
0
def get_slack_notify_text(rhr: models.RentalHistoryRequest) -> str:
    rh_link = slack.hyperlink(
        text="rent history",
        href=absolute_reverse('admin:rh_rentalhistoryrequest_change', args=[rhr.pk])
    )
    if rhr.user:
        user_text = slack.hyperlink(text=rhr.user.first_name, href=rhr.user.admin_url)
    else:
        user_text = slack.escape(rhr.first_name)
    return f"{user_text} has requested {rh_link}!"
Beispiel #9
0
def consent(request):
    """
    Initiate the DocuSign consent OAuth flow by redirecting the user.
    """

    url = core.create_oauth_consent_url(
        return_url=absolute_reverse("docusign:callback"),
        state=set_random_docusign_state(request),
    )
    return HttpResponseRedirect(url)
Beispiel #10
0
    def admin_pdf_url(self) -> str:
        '''
        A link where an administrative/staff user can view the
        letter of complaint as a PDF.

        If we don't have enough information to generate such a link,
        this will be an empty string.
        '''

        if self.pk is None:
            return ''
        return absolute_reverse('loc_for_user', kwargs={'user_id': self.user.pk})
Beispiel #11
0
def execute_user_stats_query(cursor, include_pad_bbl: bool = False):
    from hpaction.models import HP_DOCUSIGN_STATUS_CHOICES

    admin_url_begin, admin_url_end = absolute_reverse(
        "admin:users_justfixuser_change", args=(999, )).split("999")
    cursor.execute(
        USER_STATS_SQLFILE.read_text(),
        {
            "include_pad_bbl": include_pad_bbl,
            "unusable_password_pattern": UNUSABLE_PASSWORD_PREFIX + "%",
            "admin_url_begin": admin_url_begin,
            "admin_url_end": admin_url_end,
            "docusign_signed_status": HP_DOCUSIGN_STATUS_CHOICES.SIGNED,
        },
    )
 def iter_events(cls, last_synced_at: datetime.datetime) -> Iterator[AmpEvent]:
     # We need to be very careful here that we don't conflict with any of
     # the user properties sent by the front-end code!  See amplitude.ts for
     # more details.
     qs = OnboardingInfo.objects.filter(
         Q(updated_at__gte=last_synced_at) | Q(user__last_login__gte=last_synced_at)
     ).select_related("user")
     for oi in qs:
         user = oi.user
         update_time: datetime.datetime = max(filter(None, [oi.updated_at, user.last_login]))
         yield AmpEvent(
             user_id=user.pk,
             # This was originally an "$identify" event, except the problem
             # with that is that it only changes the user's data on the *next event*
             # they make, which may not be for a long time (or ever, if they don't
             # visit us again). So we'll use a 'fake' event instead.
             event_type="User data updated from server",
             time=update_time,
             user_properties={
                 "canWeSms": oi.can_we_sms,
                 "canRtcSms": oi.can_rtc_sms,
                 "canHj4aSms": oi.can_hj4a_sms,
                 "hasEmail": bool(user.email),
                 "lastLogin": user.last_login,
                 "dateJoined": user.date_joined,
                 "adminUrl": absolute_reverse("admin:users_justfixuser_change", args=(user.pk,)),
                 "agreedToJustfixTerms": oi.agreed_to_justfix_terms,
                 "agreedToNorentTerms": oi.agreed_to_norent_terms,
                 "agreedToEvictionfreeTerms": oi.agreed_to_evictionfree_terms,
                 "canReceiveRttcComms": oi.can_receive_rttc_comms,
                 "canReceiveSajeComms": oi.can_receive_saje_comms,
                 "receivesPublicAssistance": oi.receives_public_assistance,
                 "hasCalled311": oi.has_called_311,
                 "hasAptNumber": bool(oi.apt_number),
                 "zipcode": oi.zipcode,
                 # These are also synced via the front-end, so we need to
                 # be extra certain they're calculated the same way. See amplitude.ts
                 # for more details.
                 "city": oi.city,
                 "state": oi.state,
                 "signupIntent": oi.signup_intent,
                 "leaseType": oi.lease_type,
                 "isEmailVerified": user.is_email_verified,
             },
             insert_id_suffix=str(update_time),
         )
Beispiel #13
0
def create_callback_url(request, querystring_args: Dict[str, str]) -> str:
    """
    Create a DocuSign callback URL with the given querystring arguments.
    This will set state on the given request to ensure that it
    isn't vulnerable to CSRF.

    Note, however, that as such, it should be called sparingly, since a
    subsequent call will obliterate the state set up by the first call.
    """

    state = set_random_docusign_state(request)
    return append_querystring_args(
        absolute_reverse("docusign:callback"),
        {
            "state": state,
            **querystring_args,
        },
    )
Beispiel #14
0
    def run_check(self):
        name = Site.objects.get_current().name
        start_time = datetime.datetime.now()

        homepage_url = absolute_reverse('react')
        r = check_url(homepage_url, 'text/html')
        css_url = get_first_regex_match_group(
            r'rel="stylesheet" href="([^"]*)"', r.text)
        css_url = urljoin(homepage_url, css_url)
        check_url(css_url, 'text/css')

        health_url = (f"{absolute_reverse('health')}?"
                      f"extended={quote(settings.EXTENDED_HEALTHCHECK_KEY)}")
        r = check_url(health_url, 'application/json')
        health = r.json()
        assert_equal(health['status'], 200)
        assert_equal(health['is_extended'], True)

        total_time = datetime.datetime.now() - start_time
        self.stdout.write(
            f'Health check for {name} successful! Completed in {total_time}.')
Beispiel #15
0
    def run_check(self):
        name = get_default_site().name
        start_time = datetime.datetime.now()

        homepage_url = absolute_reverse("react")
        r = check_url(homepage_url, "text/html")
        css_url = get_first_regex_match_group(
            r'rel="stylesheet" href="([^"]*)"', r.text)
        css_url = urljoin(homepage_url, css_url)
        check_url(css_url, "text/css")

        health_url = (f"{absolute_reverse('health')}?"
                      f"extended={quote(settings.EXTENDED_HEALTHCHECK_KEY)}")
        r = check_url(health_url, "application/json")
        health = r.json()
        assert_equal(health["status"], 200)
        assert_equal(health["is_extended"], True)

        total_time = datetime.datetime.now() - start_time
        self.stdout.write(
            f"Health check for {name} successful! Completed in {total_time}.")
Beispiel #16
0
 def admin_url(self):
     return absolute_reverse("admin:users_justfixuser_change",
                             args=[self.pk])
Beispiel #17
0
    def short_link(self, obj):
        if obj is not None and obj.pk:
            return absolute_reverse("shortlinks:redirect", kwargs={"slug": obj.slug})

        return "(This will be set once you save the link.)"