def _validate_minimum_dates(self, dates: List[datetime.date]): cfg = AccessDatesValidation(**common_data.load_json("access-dates-validation.json")) today = datetime.date.today() - self.MIN_DAYS_LEEWAY for date in dates: if (date - today).days < cfg.MIN_DAYS: raise ValidationError( f"Please ensure all dates are at least {cfg.MIN_DAYS} days from today." )
from typing import List from graphql import ResolveInfo import graphene from django import forms from django.http import FileResponse from django.core.mail import EmailMessage from users.models import JustfixUser from project import common_data, slack from project.util.django_graphql_forms import DjangoFormMutation MAX_RECIPIENTS = common_data.load_json("email-attachment-validation.json")['maxRecipients'] def email_file_response_as_attachment( subject: str, body: str, recipients: List[str], attachment: FileResponse ) -> None: attachment_bytes = attachment.getvalue() for recipient in recipients: msg = EmailMessage(subject=subject, body=body, to=[recipient]) msg.attach(attachment.filename, attachment_bytes) msg.send() def get_slack_notify_text(user: JustfixUser, attachment_name: str, num_recipients: int) -> str: return ( f"{slack.hyperlink(text=user.first_name, href=user.admin_url)} "
from django import forms from project import common_data from . import models ISSUE_VALIDATION = common_data.load_json("issue-validation.json") CUSTOM_ISSUE_MAX_LENGTH: int = ISSUE_VALIDATION['CUSTOM_ISSUE_MAX_LENGTH'] MAX_CUSTOM_ISSUES_PER_AREA: int = ISSUE_VALIDATION[ 'MAX_CUSTOM_ISSUES_PER_AREA'] class CustomIssueForm(forms.ModelForm): class Meta: model = models.CustomIssue fields = ['description'] description = forms.CharField(max_length=CUSTOM_ISSUE_MAX_LENGTH) class IssueAreaFormV2(forms.Form): area = forms.ChoiceField(choices=models.ISSUE_AREA_CHOICES.choices, help_text="The area for the issues being set.") issues = forms.MultipleChoiceField( required=False, choices=models.ISSUE_CHOICES.choices, help_text= ("The issues to set. Any issues not listed, but in the same area, will be " "removed."))
from typing import Dict, Any from project.util import django_graphql_forms from project import common_data from .graphql import execute_query FORMS_COMMON_DATA = common_data.load_json("forms.json") class LegacyFormSubmissionError(Exception): pass def fix_newlines(d: Dict[str, str]) -> Dict[str, str]: result = dict() result.update(d) for key in d: result[key] = result[key].replace("\r\n", "\n") return result def get_legacy_form_submission_result(request, graphql, input): if request.POST.get(FORMS_COMMON_DATA["LEGACY_FORMSET_ADD_BUTTON_NAME"]): return None return execute_query(request, graphql, variables={"input": input})["output"] def get_legacy_form_submission(request) -> Dict[str, Any]: graphql = request.POST.get("graphql")
from typing import Any, Dict, Optional from django.utils.html import format_html from project import common_data from project.util.admin_util import admin_field USPS_TRACKING_URL_PREFIX = common_data.load_json("loc.json")["USPS_TRACKING_URL_PREFIX"] class SendableViaLobMixin: """ This mixin can be used on any Django model that represents something that may possibly have been mailed via Lob. Note that it doesn't define any model fields itself, but *does* require that subclasses actually define some model fields, which are documented below. This is done in part because this mixin was created after a number of model classes having these fields were already in existence, but which all varied in small ways (e.g. their help text). """ # This should be set to a `JSONField` of the JSON response of the API call that # was made to send the item that was mailed (or None if it wasn't mailed). lob_letter_object: Optional[Dict[str, Any]] = None # This should be set to a `CharField` of the USPS tracking number for the # thing that was mailed (or an empty string if it wasn't mailed). tracking_number: str = "" @property
from django.conf import settings from django.core.mail import send_mail from project import common_data RH_EMAIL_TEXT = common_data.load_json("rh.json") def send_email_to_dhcr(first_name, last_name, address, borough, zipcode, apartment_number): full_name = first_name + ' ' + last_name full_address = address + ', ' + borough new_line = "\n" send_mail( RH_EMAIL_TEXT['DHCR_EMAIL_SUBJECT'], RH_EMAIL_TEXT['DHCR_EMAIL_BODY'].replace( 'FULL_NAME', full_name).replace( 'FULL_ADDRESS', (full_address + ' ' + zipcode).strip()).replace( 'APARTMENT_NUMBER', apartment_number) + new_line + new_line + RH_EMAIL_TEXT['DHCR_EMAIL_SIGNATURE'] + new_line + full_name, settings.DHCR_EMAIL_SENDER_ADDRESS, settings.DHCR_EMAIL_RECIPIENT_ADDRESSES, fail_silently=False, )
import datetime from time import sleep from typing import Any, Dict, List, Optional, Union from dataclasses import dataclass, field from django.utils.timezone import make_aware, utc import requests from project import common_data _CONSTS = common_data.load_json("amplitude.json") USER_ID_PREFIX: str = _CONSTS["USER_ID_PREFIX"] # This is pre-defined by Amplitude for identify events, where we're # not actually reporting a specific event that occurred but rather # just updating user properties. For more information, see: # # https://developers.amplitude.com/docs/batch-event-upload-api IDENTIFY_EVENT = "$identify" AMP_BATCH_URL = "https://api.amplitude.com/batch" AMP_RATE_LIMIT_WAIT_SECS = 15 EPOCH = make_aware(datetime.datetime.utcfromtimestamp(0), timezone=utc) # https://stackoverflow.com/a/11111177 def unix_time_millis(dt: datetime.datetime) -> int: return int((dt - EPOCH).total_seconds() * 1000.0)
(ISSUE_CHOICES.HOME__NO_HEAT, ISSUE_CHOICES.HOME__NO_HOT_WATER)), IssueMerger((ISSUE_CHOICES.HOME__MICE, ISSUE_CHOICES.HOME__RATS, ISSUE_CHOICES.HOME__COCKROACHES)), ] # How many lines the harassment details section of the HP action form has. MAX_HARASSMENT_DETAILS_LINES = 11 # How many characters, on average, fit into a line in the harassment # details section. (The font use is not monospaced.) HARASSMENT_DETAILS_LINE_LENGTH = 60 # The most prior cases to list in EHPAs (so as not to generate an addendum). MAX_EMERGENCY_PRIOR_CASES = 3 NYCHA_ADDRESS = common_data.load_json("nycha-address.json") BOROUGH_COURT_LOCATIONS: Dict[str, hp.CourtLocationMC] = { BOROUGH_CHOICES.MANHATTAN: hp.CourtLocationMC.NEW_YORK_COUNTY, BOROUGH_CHOICES.BRONX: hp.CourtLocationMC.BRONX_COUNTY, BOROUGH_CHOICES.BROOKLYN: hp.CourtLocationMC.KINGS_COUNTY, BOROUGH_CHOICES.QUEENS: hp.CourtLocationMC.QUEENS_COUNTY, BOROUGH_CHOICES.STATEN_ISLAND: hp.CourtLocationMC.RICHMOND_COUNTY, } COURT_COUNTIES: Dict[str, hp.CourtCountyMC] = { BOROUGH_CHOICES.MANHATTAN: hp.CourtCountyMC.NEW_YORK, BOROUGH_CHOICES.BRONX: hp.CourtCountyMC.BRONX, BOROUGH_CHOICES.BROOKLYN: hp.CourtCountyMC.KINGS, BOROUGH_CHOICES.QUEENS: hp.CourtCountyMC.QUEENS, BOROUGH_CHOICES.STATEN_ISLAND: hp.CourtCountyMC.RICHMOND,
from decimal import Decimal from datetime import timedelta, date from typing import Optional, Union, List from enum import Enum from django.db import models from django.utils.crypto import get_random_string from django.utils import timezone from django.core.files.uploadedfile import SimpleUploadedFile from django.core.exceptions import ValidationError from .hpactionvars import HarassmentAllegationsMS from project.util.site_util import absolute_reverse from project import common_data from users.models import JustfixUser COMMON_DATA = common_data.load_json("hp-action.json") # The length, in characters, of an upload token. UPLOAD_TOKEN_LENGTH = 40 # How long an upload token is valid. UPLOAD_TOKEN_LIFETIME = timedelta(minutes=5) CURRENCY_KWARGS = dict(max_digits=10, decimal_places=2) def attr_name_for_harassment_allegation(name: str) -> str: return f"alleg_{name.lower()}" class HarassmentDetails(models.Model):
from django import forms from project import common_data from . import models CUSTOM_ISSUE_MAX_LENGTH: int = common_data.load_json( "issue-validation.json")['CUSTOM_ISSUE_MAX_LENGTH'] class IssueAreaForm(forms.Form): area = forms.ChoiceField(choices=models.ISSUE_AREA_CHOICES.choices, help_text="The area for the issues being set.") issues = forms.MultipleChoiceField( required=False, choices=models.ISSUE_CHOICES.choices, help_text= ("The issues to set. Any issues not listed, but in the same area, will be " "removed.")) other = forms.CharField( required=False, max_length=CUSTOM_ISSUE_MAX_LENGTH, help_text="Any other custom issues the user wants to report.") def clean(self): cleaned_data = super().clean() area = cleaned_data.get('area') issues = cleaned_data.get('issues') if area and (issues is not None):
from collections import defaultdict from django import forms from django.core.validators import RegexValidator from project import common_data from project.forms import ( YesNoRadiosField, ensure_at_least_one_is_true, DynamicallyRequiredFieldsMixin, ) from issues.models import ISSUE_CHOICES, get_issue_area from onboarding.models import OnboardingInfo from loc.landlord_info_mutation import BaseLandlordExtraInfoForm from . import models EMERGENCY_HPA_ISSUE_LIST = common_data.load_json( "emergency-hpa-issue-list.json") EMERGENCY_HPA_CHOICES = [(value, ISSUE_CHOICES.get_label(value)) for value in EMERGENCY_HPA_ISSUE_LIST] EMERGENCY_HPA_ISSUES_BY_AREA: Dict[str, List[str]] = defaultdict(list) for _issue in EMERGENCY_HPA_ISSUE_LIST: EMERGENCY_HPA_ISSUES_BY_AREA[get_issue_area(_issue)].append(_issue) del _issue class FeeWaiverIncomeForm(forms.ModelForm): class Meta: model = models.FeeWaiverDetails fields = [