Compare commits
1 Commits
03-19-2024
...
fixing-the
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
049ad31dcc |
20
.github/ISSUE_TEMPLATE/bug_template.yml
vendored
@@ -27,17 +27,6 @@ body:
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: In which environment did you experience the bug?
|
||||
options:
|
||||
- staging
|
||||
- demo
|
||||
- production
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: page
|
||||
attributes:
|
||||
@@ -45,13 +34,6 @@ body:
|
||||
description: please include URLs of where you saw the bug
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproSteps
|
||||
attributes:
|
||||
label: "Detail the steps for someone to reproduce"
|
||||
description: "Reproduction steps"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: browsers
|
||||
attributes:
|
||||
@@ -68,4 +50,4 @@ body:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
|
||||
required: false
|
||||
|
||||
82
.github/ISSUE_TEMPLATE/manual_qa_template.yml
vendored
@@ -1,82 +0,0 @@
|
||||
name: "QA Checklist"
|
||||
description: "Use this template to manually QA for prod push"
|
||||
title: "QA for prod push: Admin #[insert PR number], API #[insert PR number], Utils #[insert PR number]"
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: Logging_in
|
||||
attributes:
|
||||
label: Log In
|
||||
options:
|
||||
- label: notify-staging.app.cloud.gov resolves
|
||||
- label: Clicking sign in takes you to login page
|
||||
- label: 2FA code is sent promptly
|
||||
- label: 2FA code input screen appears as expected
|
||||
- label: After code is entered, you are brought to the “choose service” screen
|
||||
- label: Choosing a service brings you to the dashboard for that service
|
||||
|
||||
- type: checkboxes
|
||||
id: send_template
|
||||
attributes:
|
||||
label: Create a template and send
|
||||
options:
|
||||
- label: Clicking “Send messages” brings you to templates page
|
||||
- label: When “Create a template” button is clicked, the option to create new or copy existing template appears
|
||||
- label: Uploading a csv is quick and automatically directs you to the preview/send page
|
||||
- label: Formatting looks clean and in place
|
||||
- label: Message dashboard populates and shows “pending” until call is made to AWS for delivery receipt
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
id: send__copied_template
|
||||
attributes:
|
||||
label: Send a message from copied template
|
||||
options:
|
||||
- label: When “Create a template” button is clicked, the option to create new or copy existing template appears
|
||||
- label: Clicking “copy an existing template” allows you to choose from pre-existing template
|
||||
- label: After editing, the new template saves and you prepare to send
|
||||
|
||||
|
||||
- type: checkboxes
|
||||
id: send__existing_template
|
||||
attributes:
|
||||
label: Send a message from existing template
|
||||
options:
|
||||
- label: Clicking on existing template brings you to preview screen with `prepare to send` button
|
||||
- label: All else above persists as true (formatting, send, dashboard, etc)
|
||||
|
||||
- type: checkboxes
|
||||
id: download_report
|
||||
attributes:
|
||||
label: Download the csv delivery report
|
||||
options:
|
||||
- label: Clicking on the csv download directly after a message send downloads a report that contains the data we expect
|
||||
- label: The batch report appears on the main dashboard batch table with accurate information
|
||||
- label: Clicking on the csv download link from the main dashboard downloads a report that contains the data we expect
|
||||
|
||||
- type: checkboxes
|
||||
id: invite_member
|
||||
attributes:
|
||||
label: Invite a team member
|
||||
options:
|
||||
- label: Clicking on “Team” in settings brings you to team member screen
|
||||
- label: Clicking “Invite a team member” brings you to email and permissions screen
|
||||
- label: Clicking invite team member sends an invite and shows confirmation
|
||||
- label: Invite email provides live link that works to register new team member
|
||||
|
||||
- type: checkboxes
|
||||
id: delete_member
|
||||
attributes:
|
||||
label: Delete a team member
|
||||
options:
|
||||
- label: From team member screen, clicking “Change Details” on a specific profile brings you to that person’s profile
|
||||
- label: Clicking on “Remove this team member” prompts a confirmation
|
||||
- label: Clicking the confirmation removes the team member
|
||||
|
||||
- type: checkboxes
|
||||
id: sign_out
|
||||
attributes:
|
||||
label: Sign Out
|
||||
options:
|
||||
- label: Hitting “Sign Out” takes you back to the homepage and logs you out
|
||||
|
||||
79
.github/pull_request_template.md
vendored
@@ -1,79 +0,0 @@
|
||||
<!--
|
||||
Please follow the instructions found in this pull request template so that we
|
||||
have all of the relevant details needed for our work.
|
||||
|
||||
At the minimum, please be sure to fill in all sections found below and also do
|
||||
the following:
|
||||
|
||||
- Provide an appropriate and descriptive title for the pull request
|
||||
- Link the pull request to its corresponding issue (must be done after creating
|
||||
the pull request itself)
|
||||
- Assign yourself as the author
|
||||
- Attach the appropriate labels to it
|
||||
- Set it to be on the Notify.gov project board
|
||||
- Select one or more reviewers from the team or mark the pull request as a draft
|
||||
depending on its current state
|
||||
- If the pull request is a draft, please be sure to add reviewers once it is
|
||||
ready for review and mark it ready for review
|
||||
|
||||
For each section, please delete the instructions/sample text (that includes this
|
||||
text, though it is wrapped in an HTML comment just in case) and put in your own
|
||||
information. Thank you!
|
||||
-->
|
||||
|
||||
*A note to PR reviewers: it may be helpful to review our
|
||||
[code review documentation](https://github.com/GSA/notifications-api/blob/main/docs/all.md#code-reviews)
|
||||
to know what to keep in mind while reviewing pull requests.*
|
||||
|
||||
## Description
|
||||
|
||||
Please enter a clear description about your proposed changes and what the
|
||||
expected outcome(s) is/are from there. If there are complex implementation
|
||||
details within the changes, this is a great place to explain those details using
|
||||
plain language.
|
||||
|
||||
This should include:
|
||||
|
||||
- Links to issues that this PR addresses
|
||||
- Screenshots or screen captures of any visible changes, especially for UI work
|
||||
- Dependency changes
|
||||
|
||||
If there are any caveats, known issues, follow-up items, etc., make a quick note
|
||||
of them here as well, though more details are probably warranted in the issue
|
||||
itself in this case.
|
||||
|
||||
## TODO (optional)
|
||||
|
||||
If you're opening a draft PR, it might be helpful to list any outstanding work,
|
||||
especially if you're asking folks to take a look before it's ready for full
|
||||
review. In this case, create a small checklist with the outstanding items:
|
||||
|
||||
- [ ] TODO item 1
|
||||
- [ ] TODO item 2
|
||||
- [ ] TODO item ...
|
||||
|
||||
## Security Considerations
|
||||
|
||||
Please think about the security compliance aspect of your changes and what the
|
||||
potential impacts might be.
|
||||
|
||||
**NOTE: Please be mindful of sharing sensitive information here! If you're not
|
||||
sure of what to write, please ask the team first before writing anything here.**
|
||||
|
||||
Relevant details could include (and are not limited to) the following:
|
||||
|
||||
- Handling secrets/credential management (or specifically calling out that there
|
||||
is nothing to handle)
|
||||
- Any adjustments to the flow of data in and out the system, or even within it
|
||||
- Connecting or disconnecting any external services to the application
|
||||
- Handling of any sensitive information, such as PII
|
||||
- Handling of information within log statements or other application monitoring
|
||||
services/hooks
|
||||
- The inclusion of a new external dependency or the removal of an existing one
|
||||
- ... (anything else relevant from a security compliance perspective)
|
||||
|
||||
There are some cases where there are no security considerations to be had, e.g.,
|
||||
updating our documentation with publicly available information. In those cases
|
||||
it is fine to simply put something like this:
|
||||
|
||||
- None; this is a documentation update with publicly available information.
|
||||
115
.github/workflows/checks.yml
vendored
@@ -7,12 +7,12 @@ permissions:
|
||||
|
||||
env:
|
||||
NOTIFY_ENVIRONMENT: test
|
||||
NEW_RELIC_CONFIG_FILE: newrelic.ini
|
||||
NEW_RELIC_ENVIRONMENT: test
|
||||
FLASK_APP: application.py
|
||||
WERKZEUG_DEBUG_PIN: off
|
||||
REDIS_ENABLED: 0
|
||||
NODE_VERSION: 16.15.1
|
||||
AWS_US_TOLL_FREE_NUMBER: "+18556438890"
|
||||
ADMIN_BASE_URL: http://localhost:6012
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -44,97 +44,29 @@ jobs:
|
||||
run: npm test
|
||||
- name: Run py tests with coverage
|
||||
run: poetry run coverage run --omit=*/notifications_utils/* -m pytest --maxfail=10 --ignore=tests/end_to_end tests/
|
||||
# - name: Run E2E tests
|
||||
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||
# env:
|
||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
||||
- name: Check coverage threshold
|
||||
run: poetry run coverage report --fail-under=90
|
||||
|
||||
end-to-end-tests:
|
||||
permissions:
|
||||
checks: write
|
||||
pull-requests: write
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: password
|
||||
POSTGRES_DB: test_notification_api
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
redis:
|
||||
image: redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup-project
|
||||
- uses: jwalton/gh-find-current-pr@v1
|
||||
id: findPr
|
||||
- name: Clone API
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: GSA/notifications-api
|
||||
path: 'notifications-api'
|
||||
- name: Install API dependencies
|
||||
working-directory: 'notifications-api'
|
||||
run: make bootstrap
|
||||
env:
|
||||
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
|
||||
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
|
||||
REDIS_URL: redis://localhost:6379
|
||||
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
NOTIFY_ENVIRONMENT: development
|
||||
- name: Run API server
|
||||
working-directory: 'notifications-api'
|
||||
run: make run-procfile &
|
||||
env:
|
||||
DATABASE_URL: postgresql://user:password@localhost:5432/test_notification_api
|
||||
SQLALCHEMY_DATABASE_TEST_URI: postgresql://user:password@localhost:5432/test_notification_api
|
||||
REDIS_URL: redis://localhost:6379
|
||||
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
NOTIFY_ENVIRONMENT: development
|
||||
- name: Run Admin server
|
||||
run: make run-flask &
|
||||
env:
|
||||
# API_HOST_NAME: https://notify-api-staging.app.cloud.gov
|
||||
API_HOST_NAME: http://localhost:6011
|
||||
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
ADMIN_CLIENT_USERNAME: notify-admin
|
||||
NOTIFY_ENVIRONMENT: e2etest
|
||||
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
NOTIFY_E2E_TEST_URI: http://localhost:6012
|
||||
- name: Run E2E tests
|
||||
# Run the E2E tests against the code found in this PR.
|
||||
# run: poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||
# --browser webkit doesn't work at this time.
|
||||
run: make e2e-test
|
||||
# Debugging for now to troubleshoot a connectivity issue to the local servers
|
||||
# run: curl --request GET --url "http://localhost:6012"
|
||||
env:
|
||||
NOTIFY_ENVIRONMENT: e2etest
|
||||
NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
NOTIFY_E2E_TEST_URI: http://localhost:6012
|
||||
# - name: Health check
|
||||
# run: |
|
||||
# response=$(curl -url ${{secrets.NOTIFY_E2E_TEST_URI}}_status)
|
||||
# if grep -q "ok" <<< "$response"; then
|
||||
# echo "Health check passed"
|
||||
# else
|
||||
# echo "Health check failed"
|
||||
# exit 1
|
||||
# fi
|
||||
# env:
|
||||
# NOTIFY_E2E_AUTH_STATE_PATH: ${{ secrets.NOTIFY_E2E_AUTH_STATE_PATH }}
|
||||
# NOTIFY_E2E_TEST_EMAIL: ${{ secrets.NOTIFY_E2E_TEST_EMAIL }}
|
||||
# NOTIFY_E2E_TEST_PASSWORD: ${{ secrets.NOTIFY_E2E_TEST_PASSWORD }}
|
||||
# NOTIFY_E2E_TEST_URI: ${{ secrets.NOTIFY_E2E_TEST_URI }}
|
||||
|
||||
validate-new-relic-config:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -144,7 +76,6 @@ jobs:
|
||||
- uses: ./.github/actions/setup-project
|
||||
- name: Validate NewRelic config
|
||||
env:
|
||||
NEW_RELIC_CONFIG_FILE: newrelic.ini
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
# Need to set a NEW_RELIC_ENVIRONMENT with monitor_mode: true
|
||||
NEW_RELIC_ENVIRONMENT: staging
|
||||
|
||||
16
.github/workflows/deploy-demo.yml
vendored
@@ -54,15 +54,7 @@ jobs:
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
COMMIT_HASH: ${{ github.sha }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
|
||||
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
|
||||
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token"
|
||||
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-demo.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
|
||||
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-demo.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-demo.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
@@ -76,15 +68,7 @@ jobs:
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var COMMIT_HASH="$COMMIT_HASH"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
|
||||
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
|
||||
--var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL"
|
||||
--var LOGIN_DOT_GOV_LOGOUT_URL="$LOGIN_DOT_GOV_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
|
||||
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
|
||||
|
||||
- name: Check for changes to egress config
|
||||
id: changed-egress-config
|
||||
|
||||
17
.github/workflows/deploy-prod.yml
vendored
@@ -54,15 +54,8 @@ jobs:
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
COMMIT_HASH: ${{ github.sha }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
|
||||
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
|
||||
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token"
|
||||
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://beta.notify.gov/sign-out"
|
||||
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
|
||||
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://beta.notify.gov/sign-out"
|
||||
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://beta.notify.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
|
||||
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
@@ -76,15 +69,7 @@ jobs:
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var COMMIT_HASH="$COMMIT_HASH"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
|
||||
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
|
||||
--var LOGIN_DOT_GOV_ACCESS_TOKEN_URL="$LOGIN_DOT_GOV_ACCESS_TOKEN_URL"
|
||||
--var LOGIN_DOT_GOV_LOGOUT_URL="$LOGIN_DOT_GOV_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_BASE_LOGOUT_URL="$LOGIN_DOT_GOV_BASE_LOGOUT_URL"
|
||||
--var LOGIN_DOT_GOV_SIGNOUT_REDIRECT="$LOGIN_DOT_GOV_SIGNOUT_REDIRECT"
|
||||
--var LOGIN_DOT_GOV_INITIAL_SIGNIN_URL="$LOGIN_DOT_GOV_INITIAL_SIGNIN_URL"
|
||||
|
||||
- name: Check for changes to egress config
|
||||
id: changed-egress-config
|
||||
|
||||
12
.github/workflows/deploy.yml
vendored
@@ -60,15 +60,14 @@ jobs:
|
||||
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
|
||||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||
NR_BROWSER_KEY: ${{ secrets.NR_BROWSER_KEY }}
|
||||
COMMIT_HASH: ${{ github.sha }}
|
||||
LOGIN_PEM: ${{ secrets.LOGIN_PEM }}
|
||||
LOGIN_DOT_GOV_CLIENT_ID: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov"
|
||||
LOGIN_DOT_GOV_USER_INFO_URL: "https://secure.login.gov/api/openid_connect/userinfo"
|
||||
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://secure.login.gov/api/openid_connect/token"
|
||||
LOGIN_DOT_GOV_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://secure.login.gov/openid_connect/logout?"
|
||||
LOGIN_DOT_GOV_USER_INFO_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/userinfo"
|
||||
LOGIN_DOT_GOV_ACCESS_TOKEN_URL: "https://idp.int.identitysandbox.gov/api/openid_connect/token"
|
||||
LOGIN_DOT_GOV_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&post_logout_redirect_uri=https://notify-staging.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_BASE_LOGOUT_URL: "https://idp.int.identitysandbox.gov/openid_connect/logout?"
|
||||
LOGIN_DOT_GOV_SIGNOUT_REDIRECT: "https://notify-staging.app.cloud.gov/sign-out"
|
||||
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://secure.login.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
|
||||
LOGIN_DOT_GOV_INITIAL_SIGNIN_URL: "https://idp.int.identitysandbox.gov/openid_connect/authorize?acr_values=http%3A%2F%2Fidmanagement.gov%2Fns%2Fassurance%2Fial%2F1&client_id=urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:notify-gov&nonce=01234567890123456789012345&prompt=select_account&redirect_uri=https://notify-staging.app.cloud.gov/sign-in&response_type=code&scope=openid+email&state=abcdefghijklmnopabcdefghijklmnop"
|
||||
with:
|
||||
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
|
||||
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
|
||||
@@ -82,7 +81,6 @@ jobs:
|
||||
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
|
||||
--var NEW_RELIC_LICENSE_KEY="$NEW_RELIC_LICENSE_KEY"
|
||||
--var NR_BROWSER_KEY="$NR_BROWSER_KEY"
|
||||
--var COMMIT_HASH="$COMMIT_HASH"
|
||||
--var LOGIN_PEM="$LOGIN_PEM"
|
||||
--var LOGIN_DOT_GOV_CLIENT_ID="$LOGIN_DOT_GOV_CLIENT_ID"
|
||||
--var LOGIN_DOT_GOV_USER_INFO_URL="$LOGIN_DOT_GOV_USER_INFO_URL"
|
||||
|
||||
3
.profile
@@ -4,6 +4,3 @@
|
||||
##
|
||||
|
||||
export NEW_RELIC_PROXY_HOST=$egress_proxy
|
||||
export http_proxy=$egress_proxy
|
||||
export https_proxy=$egress_proxy
|
||||
export no_proxy="apps.internal"
|
||||
|
||||
4
Makefile
@@ -80,8 +80,8 @@ dead-code:
|
||||
|
||||
.PHONY: e2e-test
|
||||
e2e-test: export NEW_RELIC_ENVIRONMENT=test
|
||||
e2e-test: ## Run end-to-end integration tests; note that --browser webkit isn't currently working
|
||||
poetry run pytest -v --browser chromium --browser firefox tests/end_to_end
|
||||
e2e-test: ## Run end-to-end integration tests
|
||||
poetry run pytest -v --browser chromium --browser firefox --browser webkit tests/end_to_end
|
||||
|
||||
.PHONY: js-lint
|
||||
js-lint: ## Run javascript linting scanners
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||

|
||||
|
||||
# Notify.gov Admin UI
|
||||
|
||||
This is the Notify front-end for government users and admins. To see it in
|
||||
|
||||
@@ -21,6 +21,7 @@ from flask_login import LoginManager, current_user
|
||||
from flask_talisman import Talisman
|
||||
from flask_wtf import CSRFProtect
|
||||
from flask_wtf.csrf import CSRFError
|
||||
from govuk_frontend_jinja.flask_ext import init_govuk_frontend
|
||||
from itsdangerous import BadSignature
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from notifications_utils import logging, request_helper
|
||||
@@ -53,7 +54,6 @@ from app.formatters import (
|
||||
format_datetime_normal,
|
||||
format_datetime_relative,
|
||||
format_datetime_short,
|
||||
format_datetime_short_america,
|
||||
format_day_of_week,
|
||||
format_delta,
|
||||
format_delta_days,
|
||||
@@ -113,7 +113,6 @@ from app.notify_client.template_statistics_api_client import template_statistics
|
||||
from app.notify_client.upload_api_client import upload_api_client
|
||||
from app.notify_client.user_api_client import user_api_client
|
||||
from app.url_converters import SimpleDateTypeConverter, TemplateTypeConverter
|
||||
from app.utils.govuk_frontend_jinja.flask_ext import init_govuk_frontend
|
||||
|
||||
login_manager = LoginManager()
|
||||
csrf = CSRFProtect()
|
||||
@@ -381,10 +380,6 @@ def load_service_before_request():
|
||||
request_ctx.service = Service(
|
||||
service_api_client.get_service(service_id)["data"]
|
||||
)
|
||||
stats = service_api_client.get_service_statistics(
|
||||
service_id, limit_days=7
|
||||
)
|
||||
request_ctx.service.stats = stats
|
||||
except HTTPError as exc:
|
||||
# if service id isn't real, then 404 rather than 500ing later because we expect service to be set
|
||||
if exc.status_code == 404:
|
||||
@@ -553,7 +548,6 @@ def add_template_filters(application):
|
||||
format_datetime_24h,
|
||||
format_datetime_normal,
|
||||
format_datetime_short,
|
||||
format_datetime_short_america,
|
||||
valid_phone_number,
|
||||
linkable_name,
|
||||
format_date,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<link rel="stylesheet" media="screen" href="/static/stylesheets/main.css?d077f86473501ab244de0b30600536ee" />
|
||||
<link rel="stylesheet" media="print" href="/static/stylesheets/print.css?28010888ca5719dc83d7c2c80f6ed2b2" />
|
||||
|
||||
<meta property="og:image" content="/static/images/notify-og-image.png">
|
||||
<meta property="og:image" content="/static/images/usa-opengraph-image.png">
|
||||
</head>
|
||||
|
||||
<body class="usa-template__body">
|
||||
@@ -107,4 +107,4 @@
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 994 B After Width: | Height: | Size: 283 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,7 +0,0 @@
|
||||
<svg width="187" height="230" viewBox="0 0 187 230" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M89.1324 230V203.125H57.936C60.0752 226.499 79.7735 230 89.1324 230Z" fill="#2491FF"/>
|
||||
<path d="M89.1324 230V203.125H121.22C119.02 226.499 98.7587 230 89.1324 230Z" fill="#005EA2"/>
|
||||
<path d="M71.4869 19.5185C71.4869 3.35574 82.3456 0 89.1324 0V194.166H0V174.861L22.1703 153.759V87.3122C22.1703 47.8033 53.8415 24.1578 71.4869 19.5185Z" fill="#2491FF"/>
|
||||
<path d="M89.1324 0C95.9192 0 106.778 3.35574 106.778 19.5185C112.193 20.9422 118.928 24.1557 125.664 29.0681C118.405 36.2889 113.911 46.2873 113.911 57.3353C113.911 79.3543 131.761 97.2043 153.78 97.2043C154.557 97.2043 155.293 97.2481 156.058 97.2043L156.094 153.759L178.265 174.861V194.166H89.1324V0Z" fill="#005EA2"/>
|
||||
<path d="M186.435 57.1535C186.435 74.9784 171.985 89.4283 154.16 89.4283C136.335 89.4283 121.885 74.9784 121.885 57.1535C121.885 39.3286 136.335 24.8786 154.16 24.8786C171.985 24.8786 186.435 39.3286 186.435 57.1535Z" fill="#D54309"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="1596" height="400" viewBox="0 0 1596 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M155.013 400V353.26H100.758C104.479 393.912 138.736 400 155.013 400Z" fill="#58B4FF"/>
|
||||
<path d="M155.013 400V353.26H210.818C206.991 393.912 171.754 400 155.013 400Z" fill="#0076D6"/>
|
||||
<path d="M124.325 33.9452C124.325 5.83608 143.21 0 155.013 0V337.68H0V304.105L38.5571 267.407V151.847C38.5571 83.1361 93.6374 42.0136 124.325 33.9452Z" fill="#58B4FF"/>
|
||||
<path d="M155.013 0C166.816 0 185.701 5.83608 185.701 33.9452C195.118 36.4212 206.832 42.0098 218.547 50.5533C205.922 63.1111 198.106 80.4997 198.106 99.7136C198.106 138.008 229.15 169.051 267.444 169.051C268.795 169.051 270.074 169.127 271.406 169.051L271.469 267.407L310.026 304.105V337.68H155.013V0Z" fill="#0076D6"/>
|
||||
<path d="M324.234 99.3973C324.234 130.397 299.104 155.528 268.104 155.528C237.104 155.528 211.974 130.397 211.974 99.3973C211.974 68.3975 237.104 43.2672 268.104 43.2672C299.104 43.2672 324.234 68.3975 324.234 99.3973Z" fill="#D54309"/>
|
||||
<path d="M416.684 290.469C414.747 290.469 413.25 289.94 412.194 288.884C411.137 287.651 410.609 286.154 410.609 284.393V111.909C410.609 109.972 411.137 108.475 412.194 107.418C413.25 106.186 414.747 105.569 416.684 105.569H428.835C430.948 105.569 432.532 106.098 433.589 107.154C434.646 108.035 435.262 108.739 435.438 109.267L523.133 243.98V111.909C523.133 109.972 523.662 108.475 524.718 107.418C525.775 106.186 527.271 105.569 529.208 105.569H541.887C543.824 105.569 545.321 106.186 546.378 107.418C547.61 108.475 548.227 109.972 548.227 111.909V284.129C548.227 285.89 547.61 287.387 546.378 288.62C545.321 289.852 543.912 290.469 542.151 290.469H529.473C527.536 290.469 526.039 289.94 524.982 288.884C524.102 287.827 523.485 287.123 523.133 286.771L435.702 153.379V284.393C435.702 286.154 435.086 287.651 433.853 288.884C432.797 289.94 431.3 290.469 429.363 290.469H416.684Z" fill="#EDEFF0"/>
|
||||
<path d="M636.275 293.11C622.892 293.11 611.71 290.557 602.729 285.45C593.748 280.343 586.88 273.299 582.126 264.319C577.371 255.162 574.73 244.772 574.201 233.15C574.025 230.156 573.937 226.37 573.937 221.792C573.937 217.037 574.025 213.251 574.201 210.434C574.73 198.635 577.371 188.246 582.126 179.265C587.056 170.284 594.012 163.24 602.993 158.133C611.974 153.027 623.068 150.473 636.275 150.473C649.482 150.473 660.576 153.027 669.557 158.133C678.537 163.24 685.405 170.284 690.16 179.265C695.09 188.246 697.82 198.635 698.348 210.434C698.524 213.251 698.612 217.037 698.612 221.792C698.612 226.37 698.524 230.156 698.348 233.15C697.82 244.772 695.178 255.162 690.424 264.319C685.669 273.299 678.802 280.343 669.821 285.45C660.84 290.557 649.658 293.11 636.275 293.11ZM636.275 272.771C647.193 272.771 655.909 269.337 662.425 262.47C668.94 255.426 672.462 245.212 672.99 231.829C673.167 229.188 673.255 225.842 673.255 221.792C673.255 217.742 673.167 214.396 672.99 211.754C672.462 198.371 668.94 188.246 662.425 181.378C655.909 174.334 647.193 170.812 636.275 170.812C625.357 170.812 616.552 174.334 609.861 181.378C603.345 188.246 599.911 198.371 599.559 211.754C599.383 214.396 599.295 217.742 599.295 221.792C599.295 225.842 599.383 229.188 599.559 231.829C599.911 245.212 603.345 255.426 609.861 262.47C616.552 269.337 625.357 272.771 636.275 272.771Z" fill="#EDEFF0"/>
|
||||
<path d="M781.377 290.469C771.516 290.469 763.504 288.62 757.341 284.922C751.177 281.048 746.687 275.677 743.869 268.809C741.052 261.765 739.643 253.489 739.643 243.98V174.246H719.04C717.279 174.246 715.782 173.718 714.549 172.661C713.493 171.429 712.965 169.932 712.965 168.171V159.19C712.965 157.429 713.493 156.02 714.549 154.964C715.782 153.731 717.279 153.115 719.04 153.115H739.643V109.003C739.643 107.242 740.171 105.833 741.228 104.777C742.461 103.544 743.957 102.928 745.718 102.928H758.133C759.894 102.928 761.303 103.544 762.359 104.777C763.592 105.833 764.208 107.242 764.208 109.003V153.115H796.962C798.723 153.115 800.132 153.731 801.188 154.964C802.421 156.02 803.037 157.429 803.037 159.19V168.171C803.037 169.932 802.421 171.429 801.188 172.661C800.132 173.718 798.723 174.246 796.962 174.246H764.208V242.131C764.208 250.407 765.617 256.923 768.434 261.677C771.252 266.432 776.271 268.809 783.491 268.809H799.603C801.364 268.809 802.773 269.425 803.83 270.658C805.062 271.715 805.678 273.123 805.678 274.884V284.393C805.678 286.154 805.062 287.651 803.83 288.884C802.773 289.94 801.364 290.469 799.603 290.469H781.377Z" fill="#EDEFF0"/>
|
||||
<path d="M834.371 290.469C832.61 290.469 831.113 289.94 829.88 288.884C828.824 287.651 828.296 286.154 828.296 284.393V159.19C828.296 157.429 828.824 156.02 829.88 154.964C831.113 153.731 832.61 153.115 834.371 153.115H847.05C848.811 153.115 850.219 153.731 851.276 154.964C852.333 156.02 852.861 157.429 852.861 159.19V284.393C852.861 286.154 852.333 287.651 851.276 288.884C850.219 289.94 848.811 290.469 847.05 290.469H834.371ZM832.258 127.493C830.497 127.493 829 126.965 827.767 125.908C826.711 124.675 826.182 123.179 826.182 121.418V107.154C826.182 105.393 826.711 103.984 827.767 102.928C829 101.695 830.497 101.079 832.258 101.079H848.899C850.66 101.079 852.068 101.695 853.125 102.928C854.358 103.984 854.974 105.393 854.974 107.154V121.418C854.974 123.179 854.358 124.675 853.125 125.908C852.068 126.965 850.66 127.493 848.899 127.493H832.258Z" fill="#EDEFF0"/>
|
||||
<path d="M912.078 290.469C910.317 290.469 908.82 289.94 907.588 288.884C906.531 287.651 906.003 286.154 906.003 284.393V174.246H883.551C881.79 174.246 880.293 173.718 879.06 172.661C878.004 171.429 877.476 169.932 877.476 168.171V159.19C877.476 157.429 878.004 156.02 879.06 154.964C880.293 153.731 881.79 153.115 883.551 153.115H906.003V139.908C906.003 130.927 907.5 123.091 910.493 116.399C913.487 109.531 918.242 104.249 924.757 100.551C931.449 96.8525 940.341 95.0036 951.435 95.0036H967.284C969.045 95.0036 970.454 95.6199 971.51 96.8525C972.567 97.9091 973.095 99.3179 973.095 101.079V110.06C973.095 111.821 972.567 113.317 971.51 114.55C970.454 115.607 969.045 116.135 967.284 116.135H951.964C943.687 116.135 938.052 118.336 935.059 122.738C932.065 126.965 930.568 133.128 930.568 141.228V153.115H964.642C966.403 153.115 967.812 153.731 968.869 154.964C969.925 156.02 970.454 157.429 970.454 159.19V168.171C970.454 169.932 969.925 171.429 968.869 172.661C967.812 173.718 966.403 174.246 964.642 174.246H930.568V284.393C930.568 286.154 929.952 287.651 928.719 288.884C927.663 289.94 926.254 290.469 924.493 290.469H912.078Z" fill="#EDEFF0"/>
|
||||
<path d="M1017.94 340.656C1016.53 340.656 1015.3 340.127 1014.24 339.071C1013.18 338.014 1012.65 336.782 1012.65 335.373C1012.65 334.668 1012.74 333.964 1012.92 333.26C1013.09 332.555 1013.45 331.675 1013.98 330.618L1034.31 282.28L983.863 163.152C982.982 161.039 982.542 159.542 982.542 158.662C982.542 157.077 983.071 155.756 984.127 154.7C985.184 153.643 986.504 153.115 988.089 153.115H1001.03C1002.79 153.115 1004.2 153.555 1005.26 154.435C1006.31 155.316 1007.02 156.373 1007.37 157.605L1047.52 253.753L1088.73 157.605C1089.26 156.373 1089.96 155.316 1090.84 154.435C1091.9 153.555 1093.39 153.115 1095.33 153.115H1107.75C1109.33 153.115 1110.65 153.643 1111.71 154.7C1112.76 155.756 1113.29 156.989 1113.29 158.398C1113.29 159.278 1112.85 160.863 1111.97 163.152L1036.96 336.165C1036.43 337.398 1035.63 338.454 1034.58 339.335C1033.7 340.215 1032.29 340.656 1030.35 340.656H1017.94Z" fill="#EDEFF0"/>
|
||||
<path d="M1128.65 290.469C1126.89 290.469 1125.39 289.94 1124.16 288.884C1123.1 287.651 1122.57 286.154 1122.57 284.393V265.375C1122.57 263.614 1123.1 262.205 1124.16 261.149C1125.39 259.916 1126.89 259.3 1128.65 259.3H1147.67C1149.43 259.3 1150.84 259.916 1151.89 261.149C1153.13 262.205 1153.74 263.614 1153.74 265.375V284.393C1153.74 286.154 1153.13 287.651 1151.89 288.884C1150.84 289.94 1149.43 290.469 1147.67 290.469H1128.65Z" fill="#EDEFF0"/>
|
||||
<path d="M1244.66 348.58C1232.68 348.58 1222.82 346.995 1215.07 343.825C1207.5 340.656 1201.51 336.782 1197.11 332.203C1192.89 327.801 1189.8 323.31 1187.87 318.732C1186.11 314.329 1185.14 310.808 1184.96 308.166C1184.79 306.405 1185.31 304.908 1186.55 303.676C1187.96 302.443 1189.45 301.827 1191.04 301.827H1203.72C1205.3 301.827 1206.62 302.179 1207.68 302.883C1208.73 303.588 1209.61 305.084 1210.32 307.374C1211.38 310.015 1212.96 312.921 1215.07 316.09C1217.19 319.436 1220.44 322.254 1224.85 324.543C1229.25 327.008 1235.41 328.241 1243.34 328.241C1251.61 328.241 1258.57 327.096 1264.2 324.807C1269.84 322.694 1274.07 318.82 1276.88 313.185C1279.88 307.726 1281.37 300.154 1281.37 290.469V272.507C1277.15 277.966 1271.6 282.544 1264.73 286.242C1257.86 289.764 1249.24 291.525 1238.85 291.525C1228.99 291.525 1220.53 289.852 1213.49 286.507C1206.45 282.985 1200.63 278.318 1196.06 272.507C1191.65 266.52 1188.31 259.74 1186.02 252.168C1183.91 244.596 1182.67 236.672 1182.32 228.395C1182.14 223.465 1182.14 218.622 1182.32 213.867C1182.67 205.591 1183.91 197.667 1186.02 190.095C1188.31 182.523 1191.65 175.743 1196.06 169.756C1200.63 163.769 1206.45 159.102 1213.49 155.756C1220.53 152.234 1228.99 150.473 1238.85 150.473C1249.41 150.473 1258.13 152.498 1265 156.549C1272.04 160.423 1277.76 165.177 1282.17 170.812V159.454C1282.17 157.693 1282.69 156.196 1283.75 154.964C1284.98 153.731 1286.48 153.115 1288.24 153.115H1300.39C1302.15 153.115 1303.65 153.731 1304.88 154.964C1306.11 156.196 1306.73 157.693 1306.73 159.454V287.563C1306.73 299.361 1304.71 309.839 1300.66 318.996C1296.78 328.153 1290.27 335.373 1281.11 340.656C1272.13 345.938 1259.98 348.58 1244.66 348.58ZM1244.13 270.394C1252.93 270.394 1259.98 268.369 1265.26 264.319C1270.72 260.268 1274.68 255.162 1277.15 248.998C1279.79 242.835 1281.2 236.584 1281.37 230.244C1281.55 227.779 1281.64 224.785 1281.64 221.263C1281.64 217.565 1281.55 214.484 1281.37 212.018C1281.2 205.679 1279.79 199.428 1277.15 193.264C1274.68 187.101 1270.72 181.994 1265.26 177.944C1259.98 173.894 1252.93 171.869 1244.13 171.869C1235.32 171.869 1228.28 173.894 1223 177.944C1217.72 181.818 1213.93 187.013 1211.64 193.529C1209.35 199.868 1208.03 206.736 1207.68 214.132C1207.5 218.71 1207.5 223.377 1207.68 228.131C1208.03 235.527 1209.35 242.483 1211.64 248.998C1213.93 255.338 1217.72 260.533 1223 264.583C1228.28 268.457 1235.32 270.394 1244.13 270.394Z" fill="#EDEFF0"/>
|
||||
<path d="M1394.34 293.11C1380.96 293.11 1369.78 290.557 1360.8 285.45C1351.81 280.343 1344.95 273.299 1340.19 264.319C1335.44 255.162 1332.8 244.772 1332.27 233.15C1332.09 230.156 1332 226.37 1332 221.792C1332 217.037 1332.09 213.251 1332.27 210.434C1332.8 198.635 1335.44 188.246 1340.19 179.265C1345.12 170.284 1352.08 163.24 1361.06 158.133C1370.04 153.027 1381.13 150.473 1394.34 150.473C1407.55 150.473 1418.64 153.027 1427.62 158.133C1436.6 163.24 1443.47 170.284 1448.23 179.265C1453.16 188.246 1455.89 198.635 1456.42 210.434C1456.59 213.251 1456.68 217.037 1456.68 221.792C1456.68 226.37 1456.59 230.156 1456.42 233.15C1455.89 244.772 1453.25 255.162 1448.49 264.319C1443.74 273.299 1436.87 280.343 1427.89 285.45C1418.91 290.557 1407.72 293.11 1394.34 293.11ZM1394.34 272.771C1405.26 272.771 1413.98 269.337 1420.49 262.47C1427.01 255.426 1430.53 245.212 1431.06 231.829C1431.23 229.188 1431.32 225.842 1431.32 221.792C1431.32 217.742 1431.23 214.396 1431.06 211.754C1430.53 198.371 1427.01 188.246 1420.49 181.378C1413.98 174.334 1405.26 170.812 1394.34 170.812C1383.42 170.812 1374.62 174.334 1367.93 181.378C1361.41 188.246 1357.98 198.371 1357.63 211.754C1357.45 214.396 1357.36 217.742 1357.36 221.792C1357.36 225.842 1357.45 229.188 1357.63 231.829C1357.98 245.212 1361.41 255.426 1367.93 262.47C1374.62 269.337 1383.42 272.771 1394.34 272.771Z" fill="#EDEFF0"/>
|
||||
<path d="M1524.74 290.469C1522.1 290.469 1519.98 289.852 1518.4 288.62C1516.99 287.387 1515.85 285.802 1514.97 283.865L1466.1 161.303C1465.75 160.423 1465.57 159.542 1465.57 158.662C1465.57 157.077 1466.1 155.756 1467.16 154.7C1468.21 153.643 1469.53 153.115 1471.12 153.115H1483.8C1485.73 153.115 1487.23 153.643 1488.29 154.7C1489.34 155.756 1489.96 156.725 1490.14 157.605L1530.55 261.677L1570.7 157.605C1571.05 156.725 1571.67 155.756 1572.55 154.7C1573.61 153.643 1575.1 153.115 1577.04 153.115H1589.98C1591.39 153.115 1592.62 153.643 1593.68 154.7C1594.91 155.756 1595.53 157.077 1595.53 158.662C1595.53 159.542 1595.35 160.423 1595 161.303L1545.87 283.865C1545.17 285.802 1544.02 287.387 1542.44 288.62C1541.03 289.852 1538.91 290.469 1536.1 290.469H1524.74Z" fill="#EDEFF0"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 21 KiB |
@@ -1,17 +0,0 @@
|
||||
<svg width="1596" height="400" viewBox="0 0 1596 400" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M155.013 400V353.26H100.759C104.479 393.912 138.737 400 155.013 400Z" fill="#2491FF"/>
|
||||
<path d="M155.013 400V353.26H210.818C206.992 393.912 171.755 400 155.013 400Z" fill="#005EA2"/>
|
||||
<path d="M124.326 33.9452C124.326 5.83608 143.21 0 155.014 0V337.68H0V304.105L38.5573 267.407V151.847C38.5573 83.1361 93.6378 42.0136 124.326 33.9452Z" fill="#2491FF"/>
|
||||
<path d="M155.014 0C166.817 0 185.702 5.83608 185.702 33.9452C195.119 36.4212 206.833 42.0098 218.548 50.5533C205.923 63.1111 198.107 80.4997 198.107 99.7136C198.107 138.008 229.151 169.051 267.445 169.051C268.796 169.051 270.075 169.127 271.407 169.051L271.47 267.407L310.027 304.105V337.68H155.014V0Z" fill="#005EA2"/>
|
||||
<path d="M324.236 99.3973C324.236 130.397 299.105 155.528 268.105 155.528C237.105 155.528 211.975 130.397 211.975 99.3973C211.975 68.3975 237.105 43.2672 268.105 43.2672C299.105 43.2672 324.236 68.3975 324.236 99.3973Z" fill="#D54309"/>
|
||||
<path d="M416.686 290.469C414.749 290.469 413.252 289.94 412.195 288.884C411.139 287.651 410.61 286.154 410.61 284.393V111.909C410.61 109.972 411.139 108.475 412.195 107.418C413.252 106.186 414.749 105.569 416.686 105.569H428.836C430.95 105.569 432.534 106.098 433.591 107.154C434.647 108.035 435.264 108.739 435.44 109.267L523.135 243.98V111.909C523.135 109.972 523.664 108.475 524.72 107.418C525.777 106.186 527.274 105.569 529.211 105.569H541.89C543.827 105.569 545.323 106.186 546.38 107.418C547.613 108.475 548.229 109.972 548.229 111.909V284.129C548.229 285.89 547.613 287.387 546.38 288.62C545.323 289.852 543.915 290.469 542.154 290.469H529.475C527.538 290.469 526.041 289.94 524.984 288.884C524.104 287.827 523.488 287.123 523.135 286.771L435.704 153.379V284.393C435.704 286.154 435.088 287.651 433.855 288.884C432.798 289.94 431.302 290.469 429.365 290.469H416.686Z" fill="#2D2E2F"/>
|
||||
<path d="M636.277 293.11C622.894 293.11 611.712 290.557 602.731 285.45C593.75 280.343 586.883 273.299 582.128 264.319C577.373 255.162 574.732 244.772 574.204 233.15C574.028 230.156 573.94 226.37 573.94 221.792C573.94 217.037 574.028 213.251 574.204 210.434C574.732 198.635 577.373 188.246 582.128 179.265C587.059 170.284 594.014 163.24 602.995 158.133C611.976 153.027 623.07 150.473 636.277 150.473C649.485 150.473 660.579 153.027 669.559 158.133C678.54 163.24 685.408 170.284 690.163 179.265C695.093 188.246 697.823 198.635 698.351 210.434C698.527 213.251 698.615 217.037 698.615 221.792C698.615 226.37 698.527 230.156 698.351 233.15C697.823 244.772 695.181 255.162 690.427 264.319C685.672 273.299 678.804 280.343 669.824 285.45C660.843 290.557 649.661 293.11 636.277 293.11ZM636.277 272.771C647.195 272.771 655.912 269.337 662.428 262.47C668.943 255.426 672.465 245.212 672.993 231.829C673.169 229.188 673.257 225.842 673.257 221.792C673.257 217.742 673.169 214.396 672.993 211.754C672.465 198.371 668.943 188.246 662.428 181.378C655.912 174.334 647.195 170.812 636.277 170.812C625.359 170.812 616.555 174.334 609.863 181.378C603.348 188.246 599.914 198.371 599.562 211.754C599.385 214.396 599.297 217.742 599.297 221.792C599.297 225.842 599.385 229.188 599.562 231.829C599.914 245.212 603.348 255.426 609.863 262.47C616.555 269.337 625.359 272.771 636.277 272.771Z" fill="#2D2E2F"/>
|
||||
<path d="M781.381 290.469C771.519 290.469 763.507 288.62 757.344 284.922C751.18 281.048 746.69 275.677 743.872 268.809C741.055 261.765 739.646 253.489 739.646 243.98V174.246H719.043C717.282 174.246 715.785 173.718 714.552 172.661C713.496 171.429 712.968 169.932 712.968 168.171V159.19C712.968 157.429 713.496 156.02 714.552 154.964C715.785 153.731 717.282 153.115 719.043 153.115H739.646V109.003C739.646 107.242 740.174 105.833 741.231 104.777C742.464 103.544 743.96 102.928 745.721 102.928H758.136C759.897 102.928 761.306 103.544 762.362 104.777C763.595 105.833 764.211 107.242 764.211 109.003V153.115H796.965C798.726 153.115 800.135 153.731 801.191 154.964C802.424 156.02 803.04 157.429 803.04 159.19V168.171C803.04 169.932 802.424 171.429 801.191 172.661C800.135 173.718 798.726 174.246 796.965 174.246H764.211V242.131C764.211 250.407 765.62 256.923 768.438 261.677C771.255 266.432 776.274 268.809 783.494 268.809H799.607C801.368 268.809 802.776 269.425 803.833 270.658C805.066 271.715 805.682 273.123 805.682 274.884V284.393C805.682 286.154 805.066 287.651 803.833 288.884C802.776 289.94 801.368 290.469 799.607 290.469H781.381Z" fill="#2D2E2F"/>
|
||||
<path d="M834.374 290.469C832.613 290.469 831.117 289.94 829.884 288.884C828.827 287.651 828.299 286.154 828.299 284.393V159.19C828.299 157.429 828.827 156.02 829.884 154.964C831.117 153.731 832.613 153.115 834.374 153.115H847.053C848.814 153.115 850.223 153.731 851.28 154.964C852.336 156.02 852.864 157.429 852.864 159.19V284.393C852.864 286.154 852.336 287.651 851.28 288.884C850.223 289.94 848.814 290.469 847.053 290.469H834.374ZM832.261 127.493C830.5 127.493 829.004 126.965 827.771 125.908C826.714 124.675 826.186 123.179 826.186 121.418V107.154C826.186 105.393 826.714 103.984 827.771 102.928C829.004 101.695 830.5 101.079 832.261 101.079H848.902C850.663 101.079 852.072 101.695 853.129 102.928C854.361 103.984 854.978 105.393 854.978 107.154V121.418C854.978 123.179 854.361 124.675 853.129 125.908C852.072 126.965 850.663 127.493 848.902 127.493H832.261Z" fill="#2D2E2F"/>
|
||||
<path d="M912.082 290.469C910.321 290.469 908.824 289.94 907.592 288.884C906.535 287.651 906.007 286.154 906.007 284.393V174.246H883.555C881.794 174.246 880.297 173.718 879.064 172.661C878.008 171.429 877.479 169.932 877.479 168.171V159.19C877.479 157.429 878.008 156.02 879.064 154.964C880.297 153.731 881.794 153.115 883.555 153.115H906.007V139.908C906.007 130.927 907.504 123.091 910.497 116.399C913.491 109.531 918.245 104.249 924.761 100.551C931.453 96.8525 940.345 95.0035 951.439 95.0035H967.288C969.049 95.0035 970.458 95.6199 971.514 96.8525C972.571 97.9091 973.099 99.3179 973.099 101.079V110.06C973.099 111.821 972.571 113.317 971.514 114.55C970.458 115.607 969.049 116.135 967.288 116.135H951.968C943.691 116.135 938.056 118.336 935.062 122.738C932.069 126.965 930.572 133.128 930.572 141.228V153.115H964.646C966.407 153.115 967.816 153.731 968.873 154.964C969.929 156.02 970.458 157.429 970.458 159.19V168.171C970.458 169.932 969.929 171.429 968.873 172.661C967.816 173.718 966.407 174.246 964.646 174.246H930.572V284.393C930.572 286.154 929.956 287.651 928.723 288.884C927.667 289.94 926.258 290.469 924.497 290.469H912.082Z" fill="#2D2E2F"/>
|
||||
<path d="M1017.94 340.656C1016.53 340.656 1015.3 340.127 1014.24 339.071C1013.19 338.014 1012.66 336.782 1012.66 335.373C1012.66 334.668 1012.75 333.964 1012.92 333.26C1013.1 332.555 1013.45 331.675 1013.98 330.618L1034.32 282.28L983.867 163.152C982.987 161.039 982.546 159.542 982.546 158.662C982.546 157.077 983.075 155.756 984.131 154.7C985.188 153.643 986.509 153.115 988.093 153.115H1001.04C1002.8 153.115 1004.21 153.555 1005.26 154.435C1006.32 155.316 1007.02 156.373 1007.38 157.605L1047.53 253.753L1088.73 157.605C1089.26 156.373 1089.96 155.316 1090.84 154.435C1091.9 153.555 1093.4 153.115 1095.34 153.115H1107.75C1109.33 153.115 1110.66 153.643 1111.71 154.7C1112.77 155.756 1113.3 156.989 1113.3 158.398C1113.3 159.278 1112.86 160.863 1111.98 163.152L1036.96 336.165C1036.43 337.398 1035.64 338.454 1034.58 339.335C1033.7 340.215 1032.29 340.656 1030.36 340.656H1017.94Z" fill="#2D2E2F"/>
|
||||
<path d="M1128.65 290.469C1126.89 290.469 1125.4 289.94 1124.16 288.884C1123.11 287.651 1122.58 286.154 1122.58 284.393V265.375C1122.58 263.614 1123.11 262.205 1124.16 261.149C1125.4 259.916 1126.89 259.3 1128.65 259.3H1147.67C1149.43 259.3 1150.84 259.916 1151.9 261.149C1153.13 262.205 1153.75 263.614 1153.75 265.375V284.393C1153.75 286.154 1153.13 287.651 1151.9 288.884C1150.84 289.94 1149.43 290.469 1147.67 290.469H1128.65Z" fill="#2D2E2F"/>
|
||||
<path d="M1244.66 348.58C1232.69 348.58 1222.83 346.995 1215.08 343.825C1207.51 340.656 1201.52 336.782 1197.12 332.203C1192.89 327.801 1189.81 323.31 1187.87 318.732C1186.11 314.329 1185.14 310.808 1184.97 308.166C1184.79 306.405 1185.32 304.908 1186.55 303.676C1187.96 302.443 1189.46 301.827 1191.04 301.827H1203.72C1205.31 301.827 1206.63 302.179 1207.68 302.883C1208.74 303.588 1209.62 305.084 1210.32 307.374C1211.38 310.015 1212.97 312.921 1215.08 316.09C1217.19 319.436 1220.45 322.254 1224.85 324.543C1229.25 327.008 1235.42 328.241 1243.34 328.241C1251.62 328.241 1258.57 327.096 1264.21 324.807C1269.84 322.694 1274.07 318.82 1276.89 313.185C1279.88 307.726 1281.38 300.154 1281.38 290.469V272.507C1277.15 277.966 1271.61 282.544 1264.74 286.242C1257.87 289.764 1249.24 291.525 1238.85 291.525C1228.99 291.525 1220.54 289.852 1213.49 286.507C1206.45 282.985 1200.64 278.318 1196.06 272.507C1191.66 266.52 1188.31 259.74 1186.02 252.168C1183.91 244.596 1182.68 236.672 1182.33 228.395C1182.15 223.465 1182.15 218.622 1182.33 213.867C1182.68 205.591 1183.91 197.667 1186.02 190.095C1188.31 182.523 1191.66 175.743 1196.06 169.756C1200.64 163.769 1206.45 159.102 1213.49 155.756C1220.54 152.234 1228.99 150.473 1238.85 150.473C1249.42 150.473 1258.13 152.498 1265 156.549C1272.05 160.423 1277.77 165.177 1282.17 170.812V159.454C1282.17 157.693 1282.7 156.196 1283.76 154.964C1284.99 153.731 1286.49 153.115 1288.25 153.115H1300.4C1302.16 153.115 1303.65 153.731 1304.89 154.964C1306.12 156.196 1306.74 157.693 1306.74 159.454V287.563C1306.74 299.361 1304.71 309.839 1300.66 318.996C1296.79 328.153 1290.27 335.373 1281.11 340.656C1272.13 345.938 1259.98 348.58 1244.66 348.58ZM1244.13 270.394C1252.94 270.394 1259.98 268.369 1265.27 264.319C1270.73 260.268 1274.69 255.162 1277.15 248.998C1279.79 242.835 1281.2 236.584 1281.38 230.244C1281.55 227.779 1281.64 224.785 1281.64 221.263C1281.64 217.565 1281.55 214.484 1281.38 212.018C1281.2 205.679 1279.79 199.428 1277.15 193.264C1274.69 187.101 1270.73 181.994 1265.27 177.944C1259.98 173.894 1252.94 171.869 1244.13 171.869C1235.33 171.869 1228.29 173.894 1223 177.944C1217.72 181.818 1213.93 187.013 1211.65 193.529C1209.36 199.868 1208.04 206.736 1207.68 214.132C1207.51 218.71 1207.51 223.377 1207.68 228.131C1208.04 235.527 1209.36 242.483 1211.65 248.998C1213.93 255.338 1217.72 260.533 1223 264.583C1228.29 268.457 1235.33 270.394 1244.13 270.394Z" fill="#2D2E2F"/>
|
||||
<path d="M1394.35 293.11C1380.96 293.11 1369.78 290.557 1360.8 285.45C1351.82 280.343 1344.95 273.299 1340.2 264.319C1335.44 255.162 1332.8 244.772 1332.27 233.15C1332.1 230.156 1332.01 226.37 1332.01 221.792C1332.01 217.037 1332.1 213.251 1332.27 210.434C1332.8 198.635 1335.44 188.246 1340.2 179.265C1345.13 170.284 1352.08 163.24 1361.07 158.133C1370.05 153.027 1381.14 150.473 1394.35 150.473C1407.55 150.473 1418.65 153.027 1427.63 158.133C1436.61 163.24 1443.48 170.284 1448.23 179.265C1453.16 188.246 1455.89 198.635 1456.42 210.434C1456.6 213.251 1456.69 217.037 1456.69 221.792C1456.69 226.37 1456.6 230.156 1456.42 233.15C1455.89 244.772 1453.25 255.162 1448.5 264.319C1443.74 273.299 1436.87 280.343 1427.89 285.45C1418.91 290.557 1407.73 293.11 1394.35 293.11ZM1394.35 272.771C1405.27 272.771 1413.98 269.337 1420.5 262.47C1427.01 255.426 1430.53 245.212 1431.06 231.829C1431.24 229.188 1431.33 225.842 1431.33 221.792C1431.33 217.742 1431.24 214.396 1431.06 211.754C1430.53 198.371 1427.01 188.246 1420.5 181.378C1413.98 174.334 1405.27 170.812 1394.35 170.812C1383.43 170.812 1374.62 174.334 1367.93 181.378C1361.42 188.246 1357.98 198.371 1357.63 211.754C1357.46 214.396 1357.37 217.742 1357.37 221.792C1357.37 225.842 1357.46 229.188 1357.63 231.829C1357.98 245.212 1361.42 255.426 1367.93 262.47C1374.62 269.337 1383.43 272.771 1394.35 272.771Z" fill="#2D2E2F"/>
|
||||
<path d="M1524.75 290.469C1522.1 290.469 1519.99 289.852 1518.41 288.62C1517 287.387 1515.85 285.802 1514.97 283.865L1466.11 161.303C1465.75 160.423 1465.58 159.542 1465.58 158.662C1465.58 157.077 1466.11 155.756 1467.16 154.7C1468.22 153.643 1469.54 153.115 1471.12 153.115H1483.8C1485.74 153.115 1487.24 153.643 1488.29 154.7C1489.35 155.756 1489.97 156.725 1490.14 157.605L1530.56 261.677L1570.71 157.605C1571.06 156.725 1571.67 155.756 1572.56 154.7C1573.61 153.643 1575.11 153.115 1577.05 153.115H1589.99C1591.4 153.115 1592.63 153.643 1593.69 154.7C1594.92 155.756 1595.54 157.077 1595.54 158.662C1595.54 159.542 1595.36 160.423 1595.01 161.303L1545.88 283.865C1545.17 285.802 1544.03 287.387 1542.44 288.62C1541.03 289.852 1538.92 290.469 1536.1 290.469H1524.75Z" fill="#2D2E2F"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 56 KiB |
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="260.000000pt" height="260.000000pt" viewBox="0 0 260.000000 260.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,260.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1177 2586 c-67 -29 -117 -107 -117 -183 0 -12 -15 -24 -48 -37 -175
|
||||
-69 -342 -229 -426 -406 -74 -156 -77 -183 -78 -657 l-1 -423 -123 -116 -124
|
||||
-117 0 -109 0 -108 996 2 997 3 -1 105 -1 105 -123 117 -123 118 -1 310 c0
|
||||
170 -3 313 -7 316 -4 4 -26 8 -50 9 -105 6 -216 59 -292 140 -152 161 -161
|
||||
417 -22 583 35 42 31 48 -71 97 -42 20 -84 41 -92 45 -8 4 -18 28 -22 54 -14
|
||||
100 -80 158 -182 162 -34 1 -74 -4 -89 -10z"/>
|
||||
<path d="M1854 2297 c-64 -28 -95 -48 -134 -89 -65 -68 -99 -163 -94 -265 11
|
||||
-271 317 -431 547 -286 163 103 215 311 122 482 -35 64 -91 114 -165 149 -77
|
||||
37 -202 41 -276 9z"/>
|
||||
<path d="M910 282 c0 -32 32 -116 59 -154 114 -161 427 -170 566 -16 42 47 75
|
||||
119 75 165 l0 23 -350 0 c-332 0 -350 -1 -350 -18z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,19 +1 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
return $(`<div class="selection-footer${stickyClass}">
|
||||
<button
|
||||
class="govuk-button govuk-button--secondary selection-footer__button usa-button usa-button--outline"
|
||||
class="govuk-button govuk-button--secondary selection-footer__button"
|
||||
aria-expanded="${expanded ? 'true' : 'false'}"
|
||||
aria-controls="${this.fieldsetId}">
|
||||
${buttonContent}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
(function (window) {
|
||||
|
||||
// Set the target date (10 days before March 15th, 2024)
|
||||
const targetDate = new Date("April 16, 2024 00:00:00").getTime();
|
||||
|
||||
// Set a const for the container to write a conditional statement and only run if it exists
|
||||
const countdownContainer = document.getElementById("countdown-container");
|
||||
|
||||
// Function to update the countdown display
|
||||
function updateCountdown() {
|
||||
|
||||
if (countdownContainer) {
|
||||
const now = new Date().getTime();
|
||||
const difference = targetDate - now;
|
||||
|
||||
// Time calculations for days only
|
||||
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
||||
|
||||
// Visibility logic
|
||||
if (days < 0 || days > 10) {
|
||||
// Hide if more than 10 days away OR if already past the date
|
||||
countdownContainer.style.display = "none";
|
||||
} else {
|
||||
// Show if 10 days or less remaining
|
||||
countdownContainer.style.display = "block";
|
||||
document.getElementById("countdown").innerHTML = days + " days ";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Expose the updateCountdown function to the outside world
|
||||
window.updateCountdown = updateCountdown;
|
||||
|
||||
// Initial display update
|
||||
updateCountdown();
|
||||
|
||||
// Update the countdown every second (inside the IIFE)
|
||||
setInterval(updateCountdown, 1000);
|
||||
|
||||
})(window);
|
||||
@@ -48,13 +48,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.sms-message-sender, .sms-message-file-name, .sms-message-scheduler, .sms-message-template, .sms-message-sender {
|
||||
margin:0.25rem 0 0;
|
||||
.sms-message-sender {
|
||||
margin: units(1) 0 0;
|
||||
}
|
||||
|
||||
.sms-message-recipient {
|
||||
color: color('gray-cool-90');
|
||||
margin: units(1) 0 units(1);
|
||||
margin: 0 0 units(1);
|
||||
}
|
||||
|
||||
.sms-message-status {
|
||||
@@ -131,7 +131,7 @@
|
||||
&-label,
|
||||
&-button-label {
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
font-size: 19px;
|
||||
display: block;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
@@ -22,46 +22,27 @@ i.e.
|
||||
|
||||
@use "uswds-core" as *;
|
||||
|
||||
iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus {
|
||||
outline: units(1px) dotted color('blue-40v');
|
||||
outline: units(1px) auto color('blue-40v');
|
||||
outline-offset: 0.3rem;
|
||||
}
|
||||
|
||||
.usa-header--extended {
|
||||
.usa-logo {
|
||||
font-family: family("sans");
|
||||
margin: units(3) 0;
|
||||
@include at-media-max('desktop') {
|
||||
margin: units(4) 0;
|
||||
@include at-media-max('mobile-lg') {
|
||||
margin: units(4) 0 units(4) units(2);
|
||||
}
|
||||
a {
|
||||
display: flex;
|
||||
img {
|
||||
height: 35px;
|
||||
@include at-media('desktop') {
|
||||
height: 60px;
|
||||
}
|
||||
img {
|
||||
@include at-media($theme-header-min-width) {
|
||||
width: 80px;
|
||||
height: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.usa-nav__secondary {
|
||||
.usa-nav__link {
|
||||
padding: 0;
|
||||
&.usa-current {
|
||||
text-decoration: underline;
|
||||
}
|
||||
padding: 0;
|
||||
&.usa-current {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@include at-media-max('desktop') {
|
||||
padding: 0 units(2);
|
||||
ul li {
|
||||
padding-bottom: units(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.usa-nav-container {
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,23 +52,18 @@ iframe:focus, [href]:focus, [tabindex]:focus, [contentEditable=true]:focus {
|
||||
|
||||
.usa-nav__primary {
|
||||
&> .usa-nav__primary-item > a {
|
||||
font-size: size("body", 5);
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
font-size: size("body", 4);
|
||||
}
|
||||
// &> .usa-nav__primary-item:last-child {
|
||||
// margin-left: auto;
|
||||
// @include u-margin-right(-4);
|
||||
// }
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.usa-section--dark h1 {
|
||||
color: color('blue-30v');
|
||||
@include at-media-max('desktop') {
|
||||
font-size: size("body", 12);
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
.list li {
|
||||
font-family: family("sans");
|
||||
@@ -148,7 +124,6 @@ td.table-empty-message {
|
||||
.button-flex-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-bottom: units(2);
|
||||
@include at-media(desktop) {
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
@@ -164,17 +139,14 @@ td.table-empty-message {
|
||||
}
|
||||
|
||||
.user-list {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
gap: units(2);
|
||||
.user-list-item {
|
||||
@include at-media(desktop) {
|
||||
width: calc(50% - units(1));
|
||||
margin-bottom: 1rem;
|
||||
box-sizing: border-box;
|
||||
flex: 1 1 calc(50% - units(2));
|
||||
}
|
||||
// flex: 1 1 calc(100% - units(2));
|
||||
flex: 1 1 calc(100% - units(2));
|
||||
border: 1px solid color('gray-cool-10');
|
||||
padding: units(2);
|
||||
.tick-cross-list-permissions {
|
||||
@@ -369,9 +341,6 @@ td.table-empty-message {
|
||||
background-image: url(../img/material-icons/description.svg);
|
||||
}
|
||||
}
|
||||
.table-wrapper {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-table {
|
||||
@@ -383,7 +352,6 @@ td.table-empty-message {
|
||||
}
|
||||
.file-list-hint {
|
||||
margin: 0;
|
||||
word-break: break-word;
|
||||
}
|
||||
.table-field, .table-field-right-aligned {
|
||||
width: 50%;
|
||||
@@ -407,36 +375,6 @@ td.table-empty-message {
|
||||
}
|
||||
}
|
||||
|
||||
.job-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
td.file-name {
|
||||
width: 25%;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
td.template {
|
||||
width: 20%;
|
||||
}
|
||||
td.time-sent {
|
||||
width: 20%;
|
||||
}
|
||||
td.sender {
|
||||
width: 15%;
|
||||
}
|
||||
td.count-of-recipients {
|
||||
width: 5%;
|
||||
}
|
||||
td.report {
|
||||
width: 5%;
|
||||
}
|
||||
th {
|
||||
padding: 0.5rem 1rem
|
||||
}
|
||||
td {
|
||||
padding: 0.5rem 1rem
|
||||
}
|
||||
}
|
||||
|
||||
#template-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
@@ -582,9 +520,3 @@ details form {
|
||||
.edit-textbox-error-mt {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
// Login page
|
||||
|
||||
#countdown-container {
|
||||
display: none; // Hide the countdown timer
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ class Config(object):
|
||||
NR_BROWSER_KEY = getenv("NR_BROWSER_KEY")
|
||||
settings = newrelic.agent.global_settings()
|
||||
NR_MONITOR_ON = settings and settings.monitor_mode
|
||||
COMMIT_HASH = getenv("COMMIT_HASH", "--------")[0:7]
|
||||
|
||||
TEMPLATE_PREVIEW_API_HOST = getenv(
|
||||
"TEMPLATE_PREVIEW_API_HOST", "http://localhost:9999"
|
||||
@@ -82,7 +81,8 @@ class Config(object):
|
||||
"IBAN": "GB33BUKB20201555555555",
|
||||
"swift": "ABCDEF12",
|
||||
"notify_billing_email_addresses": [
|
||||
"tts-benefits-studio@gsa.gov",
|
||||
"generic@digital.cabinet-office.gov.uk",
|
||||
"first.last@digital.cabinet-office.gov.uk",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -152,34 +152,6 @@ class Staging(Production):
|
||||
HEADER_COLOUR = "#00ff00" # $green
|
||||
|
||||
|
||||
class E2ETest(Staging):
|
||||
"""
|
||||
An environment config that is intended to operate as if it were in the
|
||||
staging environment but with the configuration of the development and test
|
||||
environments so the E2E tests work.
|
||||
"""
|
||||
|
||||
# Borrowed from development environment
|
||||
DEBUG = True
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SESSION_PROTECTION = None
|
||||
HTTP_PROTOCOL = "http"
|
||||
ASSET_DOMAIN = ""
|
||||
ASSET_PATH = "/static/"
|
||||
|
||||
# Borrowed from test environment
|
||||
TESTING = True
|
||||
WTF_CSRF_ENABLED = False
|
||||
|
||||
# buckets - mirror staging
|
||||
CSV_UPLOAD_BUCKET = cloud_config.s3_credentials(
|
||||
"notify-api-csv-upload-bucket-staging"
|
||||
)
|
||||
LOGO_UPLOAD_BUCKET = cloud_config.s3_credentials(
|
||||
"notify-admin-logo-upload-bucket-staging"
|
||||
)
|
||||
|
||||
|
||||
class Demo(Staging):
|
||||
HEADER_COLOUR = "#6F72AF" # $mauve
|
||||
|
||||
@@ -201,7 +173,6 @@ class Scanning(Production):
|
||||
configs = {
|
||||
"development": Development,
|
||||
"test": Test,
|
||||
"e2etest": E2ETest,
|
||||
"scanning": Scanning,
|
||||
"staging": Staging,
|
||||
"demo": Demo,
|
||||
|
||||
@@ -1,21 +1,43 @@
|
||||
# Get started
|
||||
|
||||
|
||||
1. ## Everyone starts in [Trial Mode](/using-notify/trial-mode)
|
||||
Explore Notify, add team members, and practice [sending messages to teammates](/using-notify/trial-mode).
|
||||
1. ## Check if Notify.gov is right for you
|
||||
<p>Read about our <a class="usa-link" href="{{ url_for('main.features') }}">features</a>, <a class="usa-link" href="{{ url_for('.pricing') }}">pricing</a> and <a class="usa-link" href="{{ url_for('main.roadmap') }}">roadmap</a>.</p>
|
||||
|
||||
2. ## Personalize content
|
||||
Learn how to [personalize messages](/using-notify/guidance) to increase response.
|
||||
|
||||
3. ## Check delivery status
|
||||
[Analyze the delivery](/using-notify/delivery-status) of your messages and download reports
|
||||
2. ## Create an account
|
||||
{% if not current_user.is_authenticated %}
|
||||
<p><a class="usa-link" href="{{ url_for('.register') }}">Create an account</a> for free and add your first Notify service. When you add a new service it will start in <a class="usa-link" href="{{ url_for('main.trial_mode_new') }}">trial mode</a>.</p>
|
||||
{% else %}
|
||||
<p>Create an account for free and add your first Notify service. When you add a new service, it will start in <a class="usa-link" href="{{ url_for('main.trial_mode_new') }}">trial mode</a>.</p>
|
||||
{% endif %}
|
||||
|
||||
3. ## Write some messages
|
||||
{% if True %}
|
||||
<p>Add message templates with examples of the content you plan to send. You can use our <a class="usa-link" href="{{ url_for('main.guidance_index') }}">guidance</a> to help you.</p>
|
||||
{% endif %}
|
||||
|
||||
4. ## Set up your service
|
||||
{% if not current_user.is_authenticated or not current_service %}
|
||||
<p>Review your settings to add message branding and sender information.</p>
|
||||
<p>Add team members and check their permissions.</p>
|
||||
{% else %}
|
||||
<p>Review your <a class="usa-link" href="{{ url_for('.service_settings', service_id=current_service.id) }}">settings</a> to add message branding and sender information.</p>
|
||||
<p>Add <a class="usa-link" href="{{ url_for('.manage_users', service_id=current_service.id) }}">team members</a> and check their permissions.</p>
|
||||
{% endif %}
|
||||
|
||||
4. ## View messaging limits
|
||||
Learn about message _parts_ and [how limits are calculated](/using-notify/pricing).
|
||||
|
||||
5. ## Start sending messages
|
||||
To remove the restrictions of Trial Mode and begin sending messages to clients complete the [Live Campaign Form](https://airtable.com/appe4n7jYOALPLcyU/shrIPWnLTw9U1fclL).
|
||||
We’ll respond within one business day.
|
||||
{% if not current_user.is_authenticated or not current_service %}
|
||||
<p>When you’re ready to send messages to people outside your team, go to the <b class="bold">Settings</b> page and select <b class="bold">Request to go live</b>. We’ll approve your request within one working day.</p>
|
||||
{% else %}
|
||||
<p>You should <a class="usa-link" href="{{ url_for('.support') }}">request to go live</a> when you’re ready to send messages to people outside your team. We’ll approve your request within one working day.</p>
|
||||
{% endif %}
|
||||
|
||||
### Questions?
|
||||
[Contact the Notify team](/support)
|
||||
<!-- <li class="get-started-list__item">
|
||||
<h2 class="usa-process-list__heading line-height-sans-1">Set up an API integration (optional)</h2>
|
||||
<p>You can use the Notify API to send messages automatically.</p>
|
||||
<p>Our <a class="usa-link" href="{{ url_for('main.documentation') }}">documentation</a> explains how to integrate the API with a web application or back office system.</p>
|
||||
</li> -->
|
||||
|
||||
<!-- <p>Check <a class="usa-link" href="{{ url_for('main.how_to_pay') }}">how to pay</a> if you’re planning to exceed the <a class="usa-link" href="{{ url_for('.pricing', _anchor='text-messages') }}">free text message allowance</a>.</p> -->
|
||||
|
||||
@@ -98,28 +98,6 @@ def format_datetime_short(date):
|
||||
)
|
||||
|
||||
|
||||
def format_datetime_short_america(date):
|
||||
return "{} at {}".format(format_date_numeric_america(date), format_time_12h(date))
|
||||
|
||||
|
||||
def format_date_numeric_america(date):
|
||||
date = parse_naive_dt(date)
|
||||
|
||||
preferred_tz = pytz.timezone(get_user_preferred_timezone())
|
||||
return (
|
||||
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%m-%d-%Y")
|
||||
)
|
||||
|
||||
|
||||
def format_time_12h(date):
|
||||
date = parse_naive_dt(date)
|
||||
|
||||
preferred_tz = pytz.timezone(get_user_preferred_timezone())
|
||||
return (
|
||||
date.replace(tzinfo=timezone.utc).astimezone(preferred_tz).strftime("%I:%M %p")
|
||||
)
|
||||
|
||||
|
||||
def format_datetime_relative(date):
|
||||
return "{} at {} {}".format(
|
||||
get_human_day(date), format_time_24h(date), get_user_preferred_timezone()
|
||||
@@ -470,12 +448,6 @@ def message_count_noun(count, template_type):
|
||||
else:
|
||||
return "text messages"
|
||||
|
||||
if template_type == "parts":
|
||||
if count == 1:
|
||||
return "text message part"
|
||||
else:
|
||||
return "text message parts"
|
||||
|
||||
elif template_type == "email":
|
||||
if count == 1:
|
||||
return "email"
|
||||
|
||||
@@ -70,7 +70,7 @@ from app.utils.user_permissions import all_ui_permissions, permission_options
|
||||
def get_time_value_and_label(future_time):
|
||||
preferred_tz = pytz.timezone(get_user_preferred_timezone())
|
||||
return (
|
||||
future_time.astimezone(preferred_tz).isoformat(),
|
||||
future_time.astimezone(preferred_tz).replace(tzinfo=None).isoformat(),
|
||||
"{} at {} {}".format(
|
||||
get_human_day(future_time.astimezone(preferred_tz)),
|
||||
get_human_time(future_time.astimezone(preferred_tz)),
|
||||
@@ -1226,11 +1226,7 @@ class CsvUploadForm(StripWhitespaceForm):
|
||||
validators=[
|
||||
DataRequired(message="Please pick a file"),
|
||||
CsvFileValidator(),
|
||||
FileSize(
|
||||
max_size=10e6,
|
||||
message="File must be smaller than 10Mb. If you are trying to upload an Excel file, \
|
||||
please export the contents in the CSV format and then try again.",
|
||||
), # 10Mb
|
||||
FileSize(max_size=10e6, message="File must be smaller than 10Mb"), # 10Mb
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1257,6 +1253,7 @@ class ChangePreferredTimezoneForm(StripWhitespaceForm):
|
||||
|
||||
new_preferred_timezone = GovukRadiosField(
|
||||
"What timezone would you like to use?",
|
||||
default="US/Eastern",
|
||||
)
|
||||
|
||||
|
||||
@@ -1785,16 +1782,12 @@ class TemplateAndFoldersSelectionForm(Form):
|
||||
None,
|
||||
[
|
||||
# ('email', 'Email') if 'email' in available_template_types else None,
|
||||
(
|
||||
("sms", "Start with a blank template")
|
||||
if "sms" in available_template_types
|
||||
else None
|
||||
),
|
||||
(
|
||||
("copy-existing", "Copy an existing template")
|
||||
if allow_adding_copy_of_template
|
||||
else None
|
||||
),
|
||||
("sms", "Start with a blank template")
|
||||
if "sms" in available_template_types
|
||||
else None,
|
||||
("copy-existing", "Copy an existing template")
|
||||
if allow_adding_copy_of_template
|
||||
else None,
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -168,14 +168,12 @@ def api_callbacks(service_id):
|
||||
|
||||
return render_template(
|
||||
"views/api/callbacks.html",
|
||||
received_text_messages_callback=(
|
||||
received_text_messages_callback["url"]
|
||||
if received_text_messages_callback
|
||||
else None
|
||||
),
|
||||
delivery_status_callback=(
|
||||
delivery_status_callback["url"] if delivery_status_callback else None
|
||||
),
|
||||
received_text_messages_callback=received_text_messages_callback["url"]
|
||||
if received_text_messages_callback
|
||||
else None,
|
||||
delivery_status_callback=delivery_status_callback["url"]
|
||||
if delivery_status_callback
|
||||
else None,
|
||||
)
|
||||
|
||||
|
||||
@@ -264,11 +262,9 @@ def received_text_messages_callback(service_id):
|
||||
|
||||
received_text_messages_callback = get_received_text_messages_callback()
|
||||
form = CallbackForm(
|
||||
url=(
|
||||
received_text_messages_callback.get("url")
|
||||
if received_text_messages_callback
|
||||
else ""
|
||||
),
|
||||
url=received_text_messages_callback.get("url")
|
||||
if received_text_messages_callback
|
||||
else "",
|
||||
bearer_token=dummy_bearer_token if received_text_messages_callback else "",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import calendar
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
from itertools import groupby
|
||||
@@ -48,41 +47,54 @@ def service_dashboard(service_id):
|
||||
if not current_user.has_permissions("view_activity"):
|
||||
return redirect(url_for("main.choose_template", service_id=service_id))
|
||||
|
||||
job_response = job_api_client.get_jobs(service_id)["data"]
|
||||
notifications_response = notification_api_client.get_notifications_for_service(
|
||||
service_id
|
||||
download_availability = []
|
||||
|
||||
notifications = notification_api_client.get_notifications_for_service(
|
||||
service_id=service_id,
|
||||
)["notifications"]
|
||||
service_data_retention_days = 7
|
||||
|
||||
aggregate_notifications_by_job = defaultdict(list)
|
||||
for notification in notifications_response:
|
||||
job_id = notification.get("job", {}).get("id", None)
|
||||
if job_id:
|
||||
aggregate_notifications_by_job[job_id].append(notification)
|
||||
|
||||
job_and_notifications = [
|
||||
{
|
||||
"job_id": job["id"],
|
||||
"time_left": get_time_left(job["created_at"]),
|
||||
"download_link": url_for(
|
||||
".view_job_csv", service_id=current_service.id, job_id=job["id"]
|
||||
),
|
||||
"view_job_link": url_for(
|
||||
".view_job", service_id=current_service.id, job_id=job["id"]
|
||||
),
|
||||
"created_at": job["created_at"],
|
||||
"notification_count": job["notification_count"],
|
||||
"created_by": job["created_by"],
|
||||
"notifications": aggregate_notifications_by_job.get(job["id"], []),
|
||||
}
|
||||
for job in job_response
|
||||
if aggregate_notifications_by_job.get(job["id"], [])
|
||||
notificaton_job_ids = [
|
||||
notification["job"]["id"]
|
||||
for notification in notifications
|
||||
if "job" in notification
|
||||
]
|
||||
|
||||
jobs = []
|
||||
|
||||
for notificaton_job_id in notificaton_job_ids:
|
||||
job_data = job_api_client.get_job(service_id, notificaton_job_id)["data"]
|
||||
if job_data:
|
||||
jobs.append(job_data)
|
||||
|
||||
service_data_retention_days = 7
|
||||
download_availability = []
|
||||
for job in jobs:
|
||||
message_type = job.get("template_type")
|
||||
if message_type is not None:
|
||||
service_data_retention_days = current_service.get_days_of_retention(
|
||||
message_type
|
||||
)
|
||||
time_left = get_time_left(job["created_at"])
|
||||
download_link = (
|
||||
url_for(
|
||||
".view_job_csv",
|
||||
service_id=current_service.id,
|
||||
job_id=job["id"],
|
||||
),
|
||||
)
|
||||
download_availability.append(
|
||||
{
|
||||
"job_id": job["id"],
|
||||
"time_left": time_left,
|
||||
"download_link": download_link,
|
||||
}
|
||||
)
|
||||
return render_template(
|
||||
"views/dashboard/dashboard.html",
|
||||
updates_url=url_for(".service_dashboard_updates", service_id=service_id),
|
||||
partials=get_dashboard_partials(service_id),
|
||||
job_and_notifications=job_and_notifications,
|
||||
notifications=notifications,
|
||||
download_availability=download_availability,
|
||||
service_data_retention_days=service_data_retention_days,
|
||||
)
|
||||
|
||||
|
||||
@@ -102,6 +102,15 @@ def security():
|
||||
return render_template("views/security.html", navigation_links=features_nav())
|
||||
|
||||
|
||||
@main.route("/features/terms", endpoint="terms")
|
||||
@user_is_logged_in
|
||||
def terms():
|
||||
return render_template(
|
||||
"views/terms-of-use.html",
|
||||
navigation_links=features_nav(),
|
||||
)
|
||||
|
||||
|
||||
@main.route("/features/using_notify")
|
||||
@user_is_logged_in
|
||||
def using_notify():
|
||||
@@ -205,6 +214,7 @@ def send_files_by_email():
|
||||
|
||||
|
||||
@main.route("/roadmap", endpoint="old_roadmap")
|
||||
@main.route("/terms", endpoint="old_terms")
|
||||
@main.route("/information-security", endpoint="information_security")
|
||||
@main.route("/using_notify", endpoint="old_using_notify")
|
||||
@main.route("/information-risk-management", endpoint="information_risk_management")
|
||||
@@ -212,6 +222,7 @@ def send_files_by_email():
|
||||
def old_page_redirects():
|
||||
redirects = {
|
||||
"main.old_roadmap": "main.roadmap",
|
||||
"main.old_terms": "main.terms",
|
||||
"main.information_security": "main.using_notify",
|
||||
"main.old_using_notify": "main.using_notify",
|
||||
"main.information_risk_management": "main.security",
|
||||
|
||||
@@ -276,16 +276,12 @@ def get_status_filters(service, message_type, statistics):
|
||||
}
|
||||
else:
|
||||
stats = statistics[message_type]
|
||||
|
||||
if stats.get("failure") is not None:
|
||||
stats["failed"] = stats["failure"]
|
||||
|
||||
stats["pending"] = stats["requested"] - stats["delivered"] - stats["failed"]
|
||||
stats["sending"] = stats["requested"] - stats["delivered"] - stats["failed"]
|
||||
|
||||
filters = [
|
||||
# key, label, option
|
||||
("requested", "total", "sending,delivered,failed"),
|
||||
("pending", "pending", "pending"),
|
||||
("sending", "pending", "pending"),
|
||||
("delivered", "delivered", "delivered"),
|
||||
("failed", "failed", "failed"),
|
||||
]
|
||||
@@ -300,7 +296,7 @@ def get_status_filters(service, message_type, statistics):
|
||||
message_type=message_type,
|
||||
status=option,
|
||||
),
|
||||
stats.get(key),
|
||||
stats[key],
|
||||
)
|
||||
for key, label, option in filters
|
||||
]
|
||||
|
||||
@@ -122,18 +122,16 @@ def edit_user_permissions(service_id, user_id):
|
||||
form = form_class.from_user(
|
||||
user,
|
||||
service_id,
|
||||
folder_permissions=(
|
||||
None
|
||||
if user.platform_admin
|
||||
else [
|
||||
f["id"]
|
||||
for f in current_service.all_template_folders
|
||||
if user.has_template_folder_permission(f)
|
||||
]
|
||||
),
|
||||
all_template_folders=(
|
||||
None if user.platform_admin else current_service.all_template_folders
|
||||
),
|
||||
folder_permissions=None
|
||||
if user.platform_admin
|
||||
else [
|
||||
f["id"]
|
||||
for f in current_service.all_template_folders
|
||||
if user.has_template_folder_permission(f)
|
||||
],
|
||||
all_template_folders=None
|
||||
if user.platform_admin
|
||||
else current_service.all_template_folders,
|
||||
)
|
||||
|
||||
if form.validate_on_submit():
|
||||
|
||||
@@ -11,7 +11,12 @@ from flask import (
|
||||
url_for,
|
||||
)
|
||||
|
||||
from app import current_service, job_api_client, notification_api_client
|
||||
from app import (
|
||||
current_service,
|
||||
format_date_numeric,
|
||||
job_api_client,
|
||||
notification_api_client,
|
||||
)
|
||||
from app.main import main
|
||||
from app.notify_client.api_key_api_client import KEY_TYPE_TEST
|
||||
from app.utils import (
|
||||
@@ -21,7 +26,7 @@ from app.utils import (
|
||||
parse_filter_args,
|
||||
set_status_filters,
|
||||
)
|
||||
from app.utils.csv import generate_notifications_csv, get_user_preferred_timezone
|
||||
from app.utils.csv import generate_notifications_csv
|
||||
from app.utils.templates import get_template
|
||||
from app.utils.user import user_has_permissions
|
||||
|
||||
@@ -90,7 +95,7 @@ def view_notification(service_id, notification_id, error_message=None):
|
||||
partials=get_single_notification_partials(notification),
|
||||
created_by=notification.get("created_by"),
|
||||
created_at=notification["created_at"],
|
||||
updated_at=notification["sent_at"],
|
||||
updated_at=notification["updated_at"],
|
||||
help=get_help_argument(),
|
||||
notification_id=notification["id"],
|
||||
can_receive_inbound=(current_service.has_permission("inbound_sms")),
|
||||
@@ -141,9 +146,6 @@ def download_notifications_csv(service_id):
|
||||
service_data_retention_days = current_service.get_days_of_retention(
|
||||
filter_args.get("message_type")[0]
|
||||
)
|
||||
file_time = datetime.now().strftime("%Y-%m-%d %I:%M:%S %p")
|
||||
file_time = f"{file_time} {get_user_preferred_timezone()}"
|
||||
|
||||
return Response(
|
||||
stream_with_context(
|
||||
generate_notifications_csv(
|
||||
@@ -160,7 +162,7 @@ def download_notifications_csv(service_id):
|
||||
mimetype="text/csv",
|
||||
headers={
|
||||
"Content-Disposition": 'inline; filename="{} - {} - {} report.csv"'.format(
|
||||
file_time,
|
||||
format_date_numeric(datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")),
|
||||
filter_args["message_type"][0],
|
||||
current_service.name,
|
||||
)
|
||||
|
||||
@@ -34,8 +34,8 @@ def performance():
|
||||
stats["average_percentage_under_10_seconds"] = mean(
|
||||
[row["percentage_under_10_seconds"] for row in stats["processing_time"]] or [0]
|
||||
)
|
||||
stats["count_of_live_services_and_organizations"] = (
|
||||
status_api_client.get_count_of_live_services_and_organizations()
|
||||
)
|
||||
stats[
|
||||
"count_of_live_services_and_organizations"
|
||||
] = status_api_client.get_count_of_live_services_and_organizations()
|
||||
|
||||
return render_template("views/performance.html", **stats)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import itertools
|
||||
import time
|
||||
import uuid
|
||||
from string import ascii_uppercase
|
||||
@@ -5,7 +6,6 @@ from zipfile import BadZipFile
|
||||
|
||||
from flask import abort, flash, redirect, render_template, request, session, url_for
|
||||
from flask_login import current_user
|
||||
from markupsafe import Markup
|
||||
from notifications_python_client.errors import HTTPError
|
||||
from notifications_utils import SMS_CHAR_COUNT_LIMIT
|
||||
from notifications_utils.insensitive_dict import InsensitiveDict
|
||||
@@ -52,14 +52,12 @@ def get_example_csv_fields(column_headers, use_example_as_example, submitted_fie
|
||||
|
||||
def get_example_csv_rows(template, use_example_as_example=True, submitted_fields=False):
|
||||
return {
|
||||
"email": (
|
||||
["test@example.com"]
|
||||
if use_example_as_example
|
||||
else [current_user.email_address]
|
||||
),
|
||||
"sms": (
|
||||
["12223334444"] if use_example_as_example else [current_user.mobile_number]
|
||||
),
|
||||
"email": ["test@example.com"]
|
||||
if use_example_as_example
|
||||
else [current_user.email_address],
|
||||
"sms": ["12223334444"]
|
||||
if use_example_as_example
|
||||
else [current_user.mobile_number],
|
||||
}[template.template_type] + get_example_csv_fields(
|
||||
(
|
||||
placeholder
|
||||
@@ -151,11 +149,8 @@ def send_messages(service_id, template_id):
|
||||
# just show the first error, as we don't expect the form to have more
|
||||
# than one, since it only has one field
|
||||
first_field_errors = list(form.errors.values())[0]
|
||||
error_message = '<span class="usa-error-message">'
|
||||
error_message = f"{error_message}{first_field_errors[0]}"
|
||||
error_message = f"{error_message}</span>"
|
||||
error_message = Markup(error_message)
|
||||
flash(error_message)
|
||||
flash(first_field_errors[0])
|
||||
|
||||
column_headings = get_spreadsheet_column_headings_from_template(template)
|
||||
|
||||
return render_template(
|
||||
@@ -507,38 +502,21 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
show_recipient=False,
|
||||
show_recipient=True,
|
||||
email_reply_to=email_reply_to,
|
||||
sms_sender=sms_sender,
|
||||
)
|
||||
simplifed_template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
show_recipient=False,
|
||||
)
|
||||
|
||||
allow_list = []
|
||||
if current_service.trial_mode:
|
||||
# Adding the simulated numbers to allow list
|
||||
# so they can be sent in trial mode
|
||||
for user in Users(service_id):
|
||||
allow_list.extend([user.name, user.mobile_number, user.email_address])
|
||||
# Failed sms number
|
||||
allow_list.extend(
|
||||
["simulated user (fail)", "+14254147167", "simulated@simulated.gov"]
|
||||
)
|
||||
# Success sms number
|
||||
allow_list.extend(
|
||||
["simulated user (success)", "+14254147755", "simulatedtwo@simulated.gov"]
|
||||
)
|
||||
else:
|
||||
allow_list = None
|
||||
recipients = RecipientCSV(
|
||||
contents,
|
||||
template=template or simplifed_template,
|
||||
template=template,
|
||||
max_initial_rows_shown=50,
|
||||
max_errors_shown=50,
|
||||
guestlist=allow_list,
|
||||
guestlist=itertools.chain.from_iterable(
|
||||
[user.name, user.mobile_number, user.email_address]
|
||||
for user in Users(service_id)
|
||||
)
|
||||
if current_service.trial_mode
|
||||
else None,
|
||||
remaining_messages=remaining_messages,
|
||||
allow_international_sms=current_service.has_permission("international_sms"),
|
||||
)
|
||||
@@ -548,20 +526,11 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
back_link = url_for(
|
||||
"main.send_one_off", service_id=service_id, template_id=template.id
|
||||
)
|
||||
back_link_from_preview = url_for(
|
||||
"main.send_one_off", service_id=service_id, template_id=template.id
|
||||
)
|
||||
choose_time_form = None
|
||||
else:
|
||||
back_link = url_for(
|
||||
"main.send_messages", service_id=service_id, template_id=template.id
|
||||
)
|
||||
back_link_from_preview = url_for(
|
||||
"main.check_messages",
|
||||
service_id=service_id,
|
||||
template_id=template.id,
|
||||
upload_id=upload_id,
|
||||
)
|
||||
choose_time_form = ChooseTimeForm()
|
||||
|
||||
if preview_row < 2:
|
||||
@@ -569,9 +538,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
|
||||
if preview_row < len(recipients) + 2:
|
||||
template.values = recipients[preview_row - 2].recipient_and_personalisation
|
||||
simplifed_template.values = recipients[
|
||||
preview_row - 2
|
||||
].recipient_and_personalisation
|
||||
elif preview_row > 2:
|
||||
abort(404)
|
||||
|
||||
@@ -592,14 +558,11 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
|
||||
remaining_messages=remaining_messages,
|
||||
choose_time_form=choose_time_form,
|
||||
back_link=back_link,
|
||||
back_link_from_preview=back_link_from_preview,
|
||||
first_recipient_column=recipients.recipient_column_headers[0],
|
||||
preview_row=preview_row,
|
||||
sent_previously=job_api_client.has_sent_previously(
|
||||
service_id, template.id, db_template["version"], original_file_name
|
||||
),
|
||||
template_id=template_id,
|
||||
simplifed_template=simplifed_template,
|
||||
)
|
||||
|
||||
|
||||
@@ -647,34 +610,13 @@ def check_messages(service_id, template_id, upload_id, row_index=2):
|
||||
return render_template("views/check/ok.html", **data)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/<uuid:template_id>/check/<uuid:upload_id>/preview",
|
||||
methods=["POST"],
|
||||
)
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/<uuid:template_id>/check/<uuid:upload_id>/preview/row-<int:row_index>",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def preview_job(service_id, template_id, upload_id, row_index=2):
|
||||
session["scheduled_for"] = request.form.get("scheduled_for", "")
|
||||
data = _check_messages(service_id, template_id, upload_id, row_index)
|
||||
|
||||
return render_template(
|
||||
"views/check/preview.html",
|
||||
scheduled_for=session["scheduled_for"],
|
||||
**data,
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/start-job/<uuid:upload_id>", methods=["POST"])
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def start_job(service_id, upload_id):
|
||||
scheduled_for = session.pop("scheduled_for", None)
|
||||
job_api_client.create_job(
|
||||
upload_id,
|
||||
service_id,
|
||||
scheduled_for=scheduled_for,
|
||||
scheduled_for=request.form.get("scheduled_for", ""),
|
||||
)
|
||||
|
||||
session.pop("sender_id", None)
|
||||
@@ -733,20 +675,7 @@ def get_send_test_page_title(template_type, entering_recipient, name=None):
|
||||
return "Personalize this message"
|
||||
|
||||
|
||||
def get_back_link(
|
||||
service_id,
|
||||
template,
|
||||
step_index,
|
||||
placeholders=None,
|
||||
preview=False,
|
||||
):
|
||||
if preview:
|
||||
return url_for(
|
||||
"main.check_notification",
|
||||
service_id=service_id,
|
||||
template_id=template.id,
|
||||
)
|
||||
|
||||
def get_back_link(service_id, template, step_index, placeholders=None):
|
||||
if step_index == 0:
|
||||
if should_skip_template_page(template._template):
|
||||
return url_for(
|
||||
@@ -846,20 +775,11 @@ def _check_notification(service_id, template_id, exception=None):
|
||||
email_reply_to=email_reply_to,
|
||||
sms_sender=sms_sender,
|
||||
)
|
||||
simplifed_template = get_template(
|
||||
db_template,
|
||||
current_service,
|
||||
)
|
||||
|
||||
placeholders = fields_to_fill_in(template)
|
||||
|
||||
back_link = get_back_link(service_id, template, len(placeholders), placeholders)
|
||||
|
||||
back_link_from_preview = get_back_link(
|
||||
service_id, template, len(placeholders), placeholders, preview=True
|
||||
)
|
||||
|
||||
choose_time_form = ChooseTimeForm()
|
||||
|
||||
if (not session.get("recipient")) or not all_placeholders_in_session(
|
||||
template.placeholders
|
||||
):
|
||||
@@ -871,10 +791,7 @@ def _check_notification(service_id, template_id, exception=None):
|
||||
return dict(
|
||||
template=template,
|
||||
back_link=back_link,
|
||||
back_link_from_preview=back_link_from_preview,
|
||||
choose_time_form=choose_time_form,
|
||||
**(get_template_error_dict(exception) if exception else {}),
|
||||
simplifed_template=simplifed_template,
|
||||
)
|
||||
|
||||
|
||||
@@ -904,39 +821,12 @@ def get_template_error_dict(exception):
|
||||
}
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/template/<uuid:template_id>/notification/check/preview",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def preview_notification(service_id, template_id):
|
||||
recipient = get_recipient()
|
||||
if not recipient:
|
||||
return redirect(
|
||||
url_for(
|
||||
".send_one_off",
|
||||
service_id=service_id,
|
||||
template_id=template_id,
|
||||
)
|
||||
)
|
||||
|
||||
session["scheduled_for"] = request.form.get("scheduled_for", "")
|
||||
|
||||
return render_template(
|
||||
"views/notifications/preview.html",
|
||||
**_check_notification(service_id, template_id),
|
||||
scheduled_for=session["scheduled_for"],
|
||||
recipient=recipient,
|
||||
)
|
||||
|
||||
|
||||
@main.route(
|
||||
"/services/<uuid:service_id>/template/<uuid:template_id>/notification/check",
|
||||
methods=["POST"],
|
||||
)
|
||||
@user_has_permissions("send_messages", restrict_admin_usage=True)
|
||||
def send_notification(service_id, template_id):
|
||||
scheduled_for = session.pop("scheduled_for", "")
|
||||
recipient = get_recipient()
|
||||
if not recipient:
|
||||
return redirect(
|
||||
@@ -971,7 +861,7 @@ def send_notification(service_id, template_id):
|
||||
job_api_client.create_job(
|
||||
upload_id,
|
||||
service_id,
|
||||
scheduled_for=scheduled_for,
|
||||
scheduled_for="",
|
||||
template_id=template_id,
|
||||
original_file_name=filename,
|
||||
notification_count=1,
|
||||
@@ -987,20 +877,14 @@ def send_notification(service_id, template_id):
|
||||
service_id, job_id=upload_id, include_one_off=True
|
||||
)
|
||||
attempts = 0
|
||||
|
||||
# The response can come back in different forms of incompleteness
|
||||
while (
|
||||
notifications["total"] == 0
|
||||
and notifications["notifications"] == []
|
||||
and attempts < 50
|
||||
):
|
||||
while notifications["total"] == 0 and attempts < 5:
|
||||
notifications = notification_api_client.get_notifications_for_service(
|
||||
service_id, job_id=upload_id, include_one_off=True
|
||||
)
|
||||
time.sleep(0.1)
|
||||
attempts = attempts + 1
|
||||
|
||||
if notifications["total"] == 0 and attempts == 50:
|
||||
if notifications["total"] == 0 and attempts == 5:
|
||||
# This shows the job we auto-generated for the user
|
||||
return redirect(
|
||||
url_for(
|
||||
|
||||
@@ -477,11 +477,9 @@ def service_edit_email_reply_to(service_id, reply_to_email_id):
|
||||
current_service.id,
|
||||
reply_to_email_id=reply_to_email_id,
|
||||
email_address=form.email_address.data,
|
||||
is_default=(
|
||||
True
|
||||
if reply_to_email_address["is_default"]
|
||||
else form.is_default.data
|
||||
),
|
||||
is_default=True
|
||||
if reply_to_email_address["is_default"]
|
||||
else form.is_default.data,
|
||||
)
|
||||
return redirect(url_for(".service_email_reply_to", service_id=service_id))
|
||||
try:
|
||||
@@ -501,11 +499,9 @@ def service_edit_email_reply_to(service_id, reply_to_email_id):
|
||||
".service_verify_reply_to_address",
|
||||
service_id=service_id,
|
||||
notification_id=notification_id,
|
||||
is_default=(
|
||||
True
|
||||
if reply_to_email_address["is_default"]
|
||||
else form.is_default.data
|
||||
),
|
||||
is_default=True
|
||||
if reply_to_email_address["is_default"]
|
||||
else form.is_default.data,
|
||||
replace=reply_to_email_id,
|
||||
)
|
||||
)
|
||||
@@ -706,11 +702,9 @@ def service_edit_sms_sender(service_id, sms_sender_id):
|
||||
service_api_client.update_sms_sender(
|
||||
current_service.id,
|
||||
sms_sender_id=sms_sender_id,
|
||||
sms_sender=(
|
||||
sms_sender["sms_sender"]
|
||||
if is_inbound_number
|
||||
else form.sms_sender.data.replace("\r", "")
|
||||
),
|
||||
sms_sender=sms_sender["sms_sender"]
|
||||
if is_inbound_number
|
||||
else form.sms_sender.data.replace("\r", ""),
|
||||
is_default=True if sms_sender["is_default"] else form.is_default.data,
|
||||
)
|
||||
return redirect(url_for(".service_sms_senders", service_id=service_id))
|
||||
|
||||
@@ -53,19 +53,22 @@ def _get_access_token(code, state):
|
||||
# JWT expiration time (10 minute maximum)
|
||||
"exp": int(time.time()) + (10 * 60),
|
||||
}
|
||||
current_app.logger.warning(f"Here is the raw payload {payload}")
|
||||
token = jwt.encode(payload, keystring, algorithm="RS256")
|
||||
base_url = f"{access_token_url}?"
|
||||
cli_assert = f"client_assertion={token}"
|
||||
cli_assert_type = "client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer"
|
||||
code_param = f"code={code}"
|
||||
url = f"{base_url}{cli_assert}&{cli_assert_type}&{code_param}&grant_type=authorization_code"
|
||||
current_app.logger.info(f"This is the url we use to get the access token: {url}")
|
||||
headers = {"Authorization": "Bearer %s" % token}
|
||||
response = requests.post(url, headers=headers)
|
||||
current_app.logger.info(f"GOT A RESPONSE {response.json()}")
|
||||
access_token = response.json()["access_token"]
|
||||
return access_token
|
||||
|
||||
|
||||
def _get_user_email_and_uuid(access_token):
|
||||
def _get_user_email(access_token):
|
||||
headers = {"Authorization": "Bearer %s" % access_token}
|
||||
user_info_url = os.getenv("LOGIN_DOT_GOV_USER_INFO_URL")
|
||||
user_attributes = requests.get(
|
||||
@@ -73,8 +76,7 @@ def _get_user_email_and_uuid(access_token):
|
||||
headers=headers,
|
||||
)
|
||||
user_email = user_attributes.json()["email"]
|
||||
user_uuid = user_attributes.json()["sub"]
|
||||
return user_email, user_uuid
|
||||
return user_email
|
||||
|
||||
|
||||
@main.route("/sign-in", methods=(["GET", "POST"]))
|
||||
@@ -86,11 +88,11 @@ def sign_in():
|
||||
login_gov_error = request.args.get("error")
|
||||
if code and state:
|
||||
access_token = _get_access_token(code, state)
|
||||
user_email, user_uuid = _get_user_email_and_uuid(access_token)
|
||||
user_email = _get_user_email(access_token)
|
||||
redirect_url = request.args.get("next")
|
||||
|
||||
# activate the user
|
||||
user = user_api_client.get_user_by_uuid_or_email(user_uuid, user_email)
|
||||
user = user_api_client.get_user_by_email(user_email)
|
||||
activate_user(user["id"])
|
||||
return redirect(url_for("main.show_accounts_or_dashboard", next=redirect_url))
|
||||
|
||||
@@ -196,7 +198,7 @@ def sign_in():
|
||||
form=form,
|
||||
again=bool(redirect_url),
|
||||
other_device=other_device,
|
||||
login_gov_enabled=True,
|
||||
login_gov_enabled=bool(notify_env in ["development", "staging"]),
|
||||
password_reset_url=password_reset_url,
|
||||
initial_signin_url=initial_signin_url,
|
||||
)
|
||||
|
||||
@@ -18,6 +18,10 @@ def features_nav():
|
||||
"name": "Security",
|
||||
"link": "main.security",
|
||||
},
|
||||
{
|
||||
"name": "Terms of use",
|
||||
"link": "main.terms",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ class HeaderNavigation(Navigation):
|
||||
"features_sms",
|
||||
"roadmap",
|
||||
"security",
|
||||
"terms",
|
||||
},
|
||||
"using_notify": {
|
||||
"get_started",
|
||||
|
||||
@@ -44,16 +44,6 @@ class UserApiClient(NotifyAdminAPIClient):
|
||||
user_data = self.post("/user/email", data={"email": email_address})
|
||||
return user_data["data"]
|
||||
|
||||
def get_user_by_uuid_or_email(self, user_uuid, email_address):
|
||||
|
||||
user_data = self.post(
|
||||
"/user/get-login-gov-user",
|
||||
data={"login_uuid": user_uuid, "email": email_address},
|
||||
)
|
||||
if user_data is None:
|
||||
raise Exception("User not found")
|
||||
return user_data["data"]
|
||||
|
||||
def get_user_by_email_or_none(self, email_address):
|
||||
try:
|
||||
return self.get_user_by_email(email_address)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{% endblock %}
|
||||
{% block meta %}
|
||||
<meta property="og:site_name" content="Notify.gov">
|
||||
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
|
||||
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
|
||||
{% endblock %}
|
||||
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
|
||||
{% endblock %}
|
||||
@@ -175,13 +175,11 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<!-- lines 179 - 251 can all be deleted and replaced with {% include "new/components/footer/footer.html" %} -->
|
||||
|
||||
{% if current_service and current_service.research_mode %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a><span id="research-mode" class="research-mode">research mode</span>' %}
|
||||
{% else %}
|
||||
{% set commit_hash = ", Latest version: " + config['COMMIT_HASH'] %}
|
||||
{% set long_link = '<a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
|
||||
{% set meta_suffix = "Built by the " + long_link + commit_hash %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
|
||||
{% endif %}
|
||||
|
||||
{{ usaFooter({
|
||||
@@ -203,6 +201,10 @@
|
||||
"href": url_for("main.security"),
|
||||
"text": "Security"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.terms"),
|
||||
"text": "Terms of use"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -242,7 +244,7 @@
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us"
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
@@ -251,7 +253,7 @@
|
||||
"html": meta_suffix
|
||||
}
|
||||
}) }}
|
||||
<!-- {% include "new/components/footer/footer.html" %} -->
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% block sessionUserWarning %}
|
||||
<dialog class="usa-modal" id="sessionTimer" aria-labelledby="sessionTimerHeading" aria-describedby="timerWarning">
|
||||
|
||||
@@ -44,62 +44,61 @@
|
||||
|
||||
<!-- usa header -->
|
||||
<header class="usa-header usa-header--extended">
|
||||
<div class="usa-nav-container">
|
||||
<div class="usa-navbar">
|
||||
<div class="usa-logo display-flex flex-align-center flex-justify" id="-logo">
|
||||
<div class="logo-img display-flex">
|
||||
<a href="/">
|
||||
<span class="usa-sr-only">Notify.gov logo</span>
|
||||
<image src="{{ params.assetsPath | default('/static/images') }}/notify-logo.png" alt="Notify.gov logo" xlink:href=""
|
||||
class="usa-flag-logo margin-right-1"></image>
|
||||
</a>
|
||||
</div>
|
||||
{% if params.navigation %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
{% endif %}
|
||||
<div class="usa-navbar">
|
||||
<div class="usa-logo display-flex flex-align-center" id="-logo">
|
||||
{# <div class="logo-img display-flex">
|
||||
<span class="usa-sr-only">US Notify Logo</span>
|
||||
<image src="{{ params.assetsPath | default('/static/images') }}/us-notify-color.png" alt="US Notify logo" xlink:href=""
|
||||
class="usa-flag-logo margin-right-1" width="40" height="35"></image>
|
||||
</div> #}
|
||||
<em class="logo-text usa-logo__text">
|
||||
<a href="/" title="notify.gov">notify.gov</a>
|
||||
</em>
|
||||
</div>
|
||||
{% if current_user.is_authenticated %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<nav aria-label="Primary navigation" class="usa-nav">
|
||||
<div class="usa-nav__inner">
|
||||
<button type="button" class="usa-nav__close">
|
||||
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
|
||||
</button>
|
||||
<ul class="usa-nav__primary usa-accordion margin-right-1">
|
||||
{% for item in params.navigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__primary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="usa-nav__secondary margin-bottom-2">
|
||||
<ul class="usa-nav__secondary-links">
|
||||
{% for item in params.secondaryNavigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__secondary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- <section aria-label="Search component">
|
||||
<form class="usa-search usa-search--small margin-bottom-2" role="search">
|
||||
<label class="usa-sr-only" for="search-field">Search</label>
|
||||
<input class="usa-input" id="search-field" type="search" name="search" />
|
||||
<button class="usa-button" type="submit">
|
||||
<img src="/static/images/usa-icons-bg/search--white.svg" class="usa-search__submit-icon" alt="Search" />
|
||||
</button>
|
||||
</form>
|
||||
</section> -->
|
||||
</div>
|
||||
</div>
|
||||
<nav aria-label="Primary navigation" class="usa-nav">
|
||||
<div class="usa-nav__inner">
|
||||
<button type="button" class="usa-nav__close">
|
||||
<img src="/static/images/usa-icons/close.svg" role="img" alt="Close" />
|
||||
</button>
|
||||
<ul class="usa-nav__primary usa-accordion margin-right-1">
|
||||
{% for item in params.navigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__primary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="usa-nav__secondary margin-bottom-6">
|
||||
<ul class="usa-nav__secondary-links">
|
||||
{% for item in params.secondaryNavigation %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-nav__secondary-item{{ ' is-current' if item.active }}">
|
||||
<a class="usa-nav__link {{ ' usa-current' if item.active }}" href="{{ item.href }}" {% for attribute, value in
|
||||
item.attributes %} {{attribute}}="{{value}}" {% endfor %}>
|
||||
<span>{{ item.text }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<!-- <section aria-label="Search component">
|
||||
<form class="usa-search usa-search--small margin-bottom-2" role="search">
|
||||
<label class="usa-sr-only" for="search-field">Search</label>
|
||||
<input class="usa-input" id="search-field" type="search" name="search" />
|
||||
<button class="usa-button" type="submit">
|
||||
<img src="/static/images/usa-icons-bg/search--white.svg" class="usa-search__submit-icon" alt="Search" />
|
||||
</button>
|
||||
</form>
|
||||
</section> -->
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@@ -171,8 +171,8 @@
|
||||
{% endif %}
|
||||
<p class="status-hint margin-0 width-card ">
|
||||
{{ notification.status|format_notification_status_as_time(
|
||||
notification.created_at|format_datetime_short_america,
|
||||
(notification.sent_at or notification.created_at)|format_datetime_short_america
|
||||
notification.created_at|format_datetime_short,
|
||||
(notification.updated_at or notification.created_at)|format_datetime_short
|
||||
) }}
|
||||
</p>
|
||||
{% if displayed_on_single_line %}</span>{% endif %}
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<div class="grid-row">
|
||||
|
||||
{% if navigation_links %}
|
||||
<div class="tablet:grid-col-2 margin-bottom-4">
|
||||
<div class="tablet:grid-col-2">
|
||||
{{ sub_navigation(navigation_links) }}
|
||||
</div>
|
||||
<div class="tablet:grid-col-10 tablet:padding-left-4 usa-prose site-prose">
|
||||
<div class="tablet:grid-col-10 padding-left-4 usa-prose site-prose">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-10">
|
||||
{% endif %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% if help %}
|
||||
{% include 'partials/tour.html' %}
|
||||
{% else %}
|
||||
<nav class="nav margin-bottom-4">
|
||||
<nav class="nav">
|
||||
<a class="usa-button margin-top-1 margin-bottom-5 width-full"
|
||||
href="{{ url_for('.choose_template', service_id=current_service.id) }}">Send messages</a>
|
||||
<ul class="usa-sidenav">
|
||||
|
||||
@@ -19,22 +19,17 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
|
||||
{% block headIcons %}
|
||||
<link rel="shortcut icon" href="{{ assetPath | default('/assets') }}/images/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="32x32"href="{{ assetPath | default('/assets') }}/images/favicon-32x32.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ assetPath | default('/assets') }}/images/favicon-16x16.png" />
|
||||
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ assetPath | default('/assets') }}/images/favicon.ico" type="image/x-icon" />
|
||||
<link rel="mask-icon" href="{{ assetPath | default('/assets') }}/images/usa-mask-icon.svg" color="{{ themeColor | default('#F0F0F0') }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<link href="{{ assetPath | default('/assets') }}/images/notify-dark-favicon.png" rel="icon" media="(prefers-color-scheme: dark)">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link rel="apple-touch-icon" href="{{ assetPath | default('/assets') }}/images/apple-touch-icon.png">
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
|
||||
{% block meta %}
|
||||
<meta property="og:site_name" content="Notify.gov">
|
||||
<meta property="og:image" content="{{ asset_url('images/notify-og-image.png') }}">
|
||||
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="usa-template__body {{ bodyClasses }}">
|
||||
|
||||
@@ -1,372 +0,0 @@
|
||||
{% from "../components/banner.html" import banner %}
|
||||
{% from "../components/components/skip-link/macro.njk" import usaSkipLink -%}
|
||||
{% from "../components/components/header/macro.njk" import usaHeader -%}
|
||||
{% from "../components/components/footer/macro.njk" import usaFooter -%}
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ htmlLang | default('en') }}" class="{{ htmlClasses }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
{% block pageTitle %}
|
||||
{% block per_page_title %} {% endblock %}Notify.gov
|
||||
<!-- on templates that were using content_template.html, we might need to use the {{ content_page_title }} variable for the per_page_title -->
|
||||
{% endblock %}
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="f0f0f0" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="1b1b1b" />
|
||||
{% if config['NR_MONITOR_ON'] %}
|
||||
{% include "partials/newrelic.html" -%}
|
||||
{% endif %}
|
||||
{# Ensure that older IE versions always render with the correct rendering engine #}
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
{% block headIcons %}
|
||||
<link rel="shortcut icon" sizes="16x16 32x32 48x48" href="{{ asset_url('images/favicon.ico') }}" type="image/x-icon" />
|
||||
<link rel="mask-icon" href="{{ asset_url('images/usa-mask-icon.svg') }}" color="{{ themeColor | default('#F0F0F0') }}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ asset_url('images/apple-touch-icon.png') }}">
|
||||
<link rel="apple-touch-icon" href="{{ asset_url('images/apple-touch-icon.png') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" media="screen" href="{{ asset_url('css/styles.css') }}" />
|
||||
{% block extra_stylesheets %}
|
||||
{% endblock %}
|
||||
{% if g.hide_from_search_engines %}
|
||||
<meta name="robots" content="noindex" />
|
||||
{% endif %}
|
||||
<meta name="google-site-verification" content="niWnSqImOWz6mVQTYqNb5tFK8HaKSB4b3ED4Z9gtUQ0" />
|
||||
{# The default og:image is added below head so that scrapers see any custom metatags first, and this is just a fallback #}
|
||||
{% block meta_format_detection %}
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
{% endblock %}
|
||||
{% block meta %}
|
||||
<meta property="og:site_name" content="Notify.gov">
|
||||
<meta property="og:image" content="{{ asset_url('images/usa-opengraph-image.png') }}">
|
||||
{% endblock %}
|
||||
<script type="text/javascript" src="{{ asset_url('js/gtm_head.js') }}"></script>
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="usa-template__body {{ bodyClasses }}">
|
||||
<script nonce="{{ csp_nonce() }}">document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>
|
||||
{% block bodyStart %}
|
||||
{% block extra_javascripts_before_body %}
|
||||
<!-- Google Tag Manager (noscript) -->
|
||||
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WX5NGWF"
|
||||
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
<!-- End Google Tag Manager (noscript) -->
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block skipLink %}
|
||||
{{ usaSkipLink({
|
||||
"href": '#main-content',
|
||||
"text": 'Skip to main content'
|
||||
}) }}
|
||||
{% endblock %}
|
||||
|
||||
<!-- \\#region header -->
|
||||
{% block header %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.platform_admin %}
|
||||
{% set navigation = [
|
||||
{
|
||||
"href": url_for("main.show_accounts_or_dashboard"),
|
||||
"text": "Current service",
|
||||
"active": header_navigation.is_selected('accounts-or-dashboard')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.platform_admin_splash_page'),
|
||||
"text": "Platform admin",
|
||||
"active": header_navigation.is_selected('platform-admin')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
}
|
||||
] %}
|
||||
{% if current_service %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.service_settings', service_id=current_service.id),
|
||||
"text": "Settings",
|
||||
"active": secondary_navigation.is_selected('settings')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% else %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set navigation = [
|
||||
{
|
||||
"href": url_for("main.show_accounts_or_dashboard"),
|
||||
"text": "Current service",
|
||||
"active": header_navigation.is_selected('accounts-or-dashboard')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.user_profile'),
|
||||
"text": "User profile",
|
||||
"active": header_navigation.is_selected('user-profile')
|
||||
}
|
||||
] %}
|
||||
{% if current_service %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.service_settings', service_id=current_service.id),
|
||||
"text": "Settings",
|
||||
"active": secondary_navigation.is_selected('settings')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% else %}
|
||||
{% set secondaryNavigation = [
|
||||
{
|
||||
"href": url_for('main.sign_out'),
|
||||
"text": "Sign out"
|
||||
}
|
||||
] %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<!-- Add navigation back after pilot -->
|
||||
{# {% set navigation = [
|
||||
{
|
||||
"href": url_for('main.get_started'),
|
||||
"text": "Using Notify",
|
||||
"active": header_navigation.is_selected('using_notify')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.features'),
|
||||
"text": "Features",
|
||||
"active": header_navigation.is_selected('features')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us",
|
||||
"active": header_navigation.is_selected('support')
|
||||
},
|
||||
{
|
||||
"href": url_for('main.sign_in'),
|
||||
"text": "Sign in",
|
||||
"active": header_navigation.is_selected('sign-in')
|
||||
}
|
||||
] %} #}
|
||||
{% endif %}
|
||||
|
||||
{{ usaHeader({
|
||||
"homepageUrl": url_for('main.show_accounts_or_dashboard'),
|
||||
"productName": "Notify",
|
||||
"navigation": navigation,
|
||||
"navigationClasses": "govuk-header__navigation--end",
|
||||
"secondaryNavigation": secondaryNavigation,
|
||||
"assetsPath": asset_path + "images"
|
||||
}) }}
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
<!-- \\#region block main -->
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
{% block mainClasses %}
|
||||
<!-- notes set mainClasses = "margin-top-5 padding-bottom-5" where withoutnav_template was used and maybe templates that are using content_template -->
|
||||
<main class="{{ mainClasses }}" id="main-content" role="main">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% block flash_messages %}
|
||||
<!-- flash_message.html was from the withoutnav_template and is only included on child templates that was using withoutnav_template. Now, we can add in flash_message blocks and include 'flash_messages.html' to child templates that was using withoutnav_template. This will help to eliminate the use of a whole other parent template. -->
|
||||
{% endblock %}
|
||||
{% block maincolumn_content %}
|
||||
{% block fromContentTemplatetwoColumnGrid %}
|
||||
<div class="grid-row">
|
||||
{% if navigation_links %}
|
||||
<div class="tablet:grid-col-2">
|
||||
{{ sub_navigation(navigation_links) }}
|
||||
</div>
|
||||
<div class="tablet:grid-col-10 padding-left-4 usa-prose site-prose">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-10">
|
||||
{% endif %}
|
||||
{% block content_column_content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- content_column_content block is from the content_template.html. We do not need this template. We can consolidate and move it to the base.html template. We can call on this block where child templates were using the content_template.html -->
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
<!-- \\#region block footer -->
|
||||
{% block footer %}
|
||||
|
||||
{% if current_service and current_service.research_mode %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a><span id="research-mode" class="research-mode">research mode</span>' %}
|
||||
{% else %}
|
||||
{% set meta_suffix = 'Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>' %}
|
||||
{% endif %}
|
||||
|
||||
{{ usaFooter({
|
||||
"classes": "js-footer",
|
||||
"navigation": [
|
||||
{
|
||||
"title": "About Notify",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for("main.features"),
|
||||
"text": "Features"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.roadmap"),
|
||||
"text": "Roadmap"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.security"),
|
||||
"text": "Security"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.terms"),
|
||||
"text": "Terms of use"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Using Notify",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for("main.get_started"),
|
||||
"text": "Get started"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.pricing"),
|
||||
"text": "Pricing"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.trial_mode_new"),
|
||||
"text": "Trial mode"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.message_status"),
|
||||
"text": "Delivery status"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.guidance_index"),
|
||||
"text": "Guidance"
|
||||
},
|
||||
{
|
||||
"href": url_for("main.documentation"),
|
||||
"text": "API documentation"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Support",
|
||||
"columns": 1,
|
||||
"items": [
|
||||
{
|
||||
"href": url_for('main.support'),
|
||||
"text": "Contact us"
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
"meta": {
|
||||
"items": meta_items,
|
||||
"html": meta_suffix
|
||||
}
|
||||
}) }}
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% block sessionUserWarning %}
|
||||
<dialog class="usa-modal" id="sessionTimer" aria-labelledby="sessionTimerHeading" aria-describedby="timerWarning">
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading" id="sessionTimerHeading">
|
||||
Your session will end soon.
|
||||
<span class="usa-sr-only">Please choose to extend your session or sign out. Your session will expire in 5 minutes or less.</span>
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p>You have been inactive for too long.
|
||||
Your session will expire in <span id="timeLeft" role="timer"></span>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="usa-modal__footer">
|
||||
<ul class="usa-button-group">
|
||||
<li class="usa-button-group__item">
|
||||
<button type="button" class="usa-button" id="extendSessionTimer" data-close-modal>
|
||||
Extend Session
|
||||
</button>
|
||||
</li>
|
||||
<li class="usa-button-group__item">
|
||||
<button type="button" class="usa-button usa-button--unstyled padding-105 text-center" id="logOutTimer"
|
||||
data-close-modal>
|
||||
Sign out
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
<!-- \\#endregion -->
|
||||
|
||||
{% block bodyEnd %}
|
||||
{% block extra_javascripts %}
|
||||
{% endblock %}
|
||||
<!--[if gt IE 8]><!-->
|
||||
<script type="text/javascript" src="{{ asset_url('javascripts/all.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ asset_url('js/uswds.min.js') }}"></script>
|
||||
<!--<![endif]-->
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,106 +0,0 @@
|
||||
<footer class="usa-footer usa-footer--big">
|
||||
{# <div class="grid-container usa-footer__return-to-top">
|
||||
<a href="#">Return to top</a>
|
||||
</div> #}
|
||||
<div class="usa-footer__primary-section">
|
||||
<div class="grid-container">
|
||||
<div class="grid-row">
|
||||
<div class="grid-col">
|
||||
{# {% if params.navigation %}
|
||||
<nav class="usa-footer__nav" aria-label="Footer navigation,,">
|
||||
<div class="grid-row grid-gap-2">
|
||||
{% for nav in params.navigation %}
|
||||
<div class="mobile-lg:grid-col-6 desktop:grid-col-4">
|
||||
<section class="
|
||||
usa-footer__primary-content
|
||||
usa-footer__primary-content--collapsible
|
||||
">
|
||||
<h4 class="usa-footer__primary-link">{{ nav.title }}</h4>
|
||||
<ul class="usa-list usa-list--unstyled">
|
||||
{% for item in nav.items %}
|
||||
{% if item.href and item.text %}
|
||||
<li class="usa-footer__secondary-link">
|
||||
<a href="{{ item.href }}" {% for attribute, value in item.attributes %}
|
||||
{{attribute}}="{{value}}" {% endfor %}>
|
||||
{{ item.text }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %} #}
|
||||
</div>
|
||||
</div>
|
||||
<div class="padding-y-1">
|
||||
<h2 class="usa-sr-only">{{ params.meta.visuallyHiddenTitle | default("Support links") }}</h2>
|
||||
<div>
|
||||
{% if current_service and current_service.research_mode %}
|
||||
Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a><span id="research-mode" class="research-mode">research mode</span>
|
||||
{% else %}
|
||||
Built by the <a href="https://www.gsa.gov/about-us/organization/federal-acquisition-service/technology-transformation-services/tts-solutions" class="usa-link">Technology Transformation Services</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="usa-identifier site-identifier">
|
||||
<section class="usa-identifier__section usa-identifier__section--masthead" aria-label="Agency identifier,,,,,,">
|
||||
<div class="usa-identifier__container">
|
||||
<div class="usa-identifier__logos"><a href="javascript:void(0);" class="usa-identifier__logo">
|
||||
<img class="usa-identifier__logo-img" src="{{ params.assetsPath | default('/static/images') }}/gsa-logo.svg" alt="GSA Logo" role="img" width="48"
|
||||
height="48">
|
||||
</a></div>
|
||||
<section class="usa-identifier__identity" aria-label="Agency description">
|
||||
<p class="usa-identifier__identity-domain">beta.notify.gov</p>
|
||||
<p class="usa-identifier__identity-disclaimer">An official website of the <a href="https://gsa.gov">General Services Administration</a></p>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="usa-identifier__section usa-identifier__section--required-links" aria-label="Important links">
|
||||
<div class="usa-identifier__container">
|
||||
<ul class="usa-identifier__required-links-list">
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/about-us" class="usa-identifier__required-link">About GSA</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/website-information/accessibility-aids"
|
||||
class="usa-identifier__required-link">Accessibility support</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/reference/freedom-of-information-act-foia"
|
||||
class="usa-identifier__required-link">FOIA requests</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/reference/civil-rights-programs/notification-and-federal-employee-antidiscrimination-and-retaliation-act-of-2002"
|
||||
class="usa-identifier__required-link">No FEAR Act data</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsaig.gov/" class="usa-identifier__required-link">Office of the Inspector General</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/reference/reports/budget-performance"
|
||||
class="usa-identifier__required-link">Performance reports</a>
|
||||
</li>
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a href="https://www.gsa.gov/website-information/website-policies"
|
||||
class="usa-identifier__required-link">Privacy policy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="usa-identifier__section usa-identifier__section--usagov"
|
||||
aria-label="Government information and services">
|
||||
<div class="usa-identifier__container">
|
||||
<div class="usa-identifier__usagov-description">
|
||||
Looking for U.S. government information and services?
|
||||
</div>
|
||||
<a href="https://www.usa.gov/" class="usa-link">Visit USA.gov</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,35 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block org_page_title %}{% endblock %} – {{ current_org.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
<div class="navigation-service usa-breadcrumb">
|
||||
{% if current_user.platform_admin %}
|
||||
<a href="{{ url_for('.organizations') }}" class="usa-link navigation-organization-link">Organizations</a>
|
||||
{% endif %}
|
||||
<div class="navigation-service">
|
||||
{{ current_org.name }}
|
||||
</div>
|
||||
<a href="{{ url_for('main.choose_account') }}" class="usa-link navigation-service">Switch service</a>
|
||||
</div>
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-3">
|
||||
{% include "org_nav.html" %}
|
||||
</div>
|
||||
<div class="grid-col-9">
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main class="main" id="main-content" role="main" >
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,41 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
{% block service_page_title %}{% endblock %} – {{ current_service.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class="grid-container">
|
||||
{% block serviceNavigation %}
|
||||
{% include "service_navigation.html" %}
|
||||
{% endblock %}
|
||||
<!-- The withnav_template can be used to replace the settings_template. when it comes to setting_template and withnav_template, this service_navigation.html is only used in withnav_template and in settings_template, it was not used. That is one out of the two differences between settings template and withnav template. Child templates that extends settings_template, include the block serviceNavigation but leave it empty. Within the app, the only pages settings_template.html is used: manage-users.html, service-settings.html, and user-profile.html -->
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="grid-col-3">
|
||||
{% else %}
|
||||
<div class="grid-col-3">
|
||||
{% endif %}
|
||||
{% block sideNavigation %}
|
||||
{% include "main_nav.html" %}
|
||||
<!-- include settings_nav.html for child templates that used settings_template -->
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
{% endblock %}
|
||||
<main id="main-content" role="main" class="usa-prose site-prose margin-bottom-10">
|
||||
{% block content %}
|
||||
{% include 'flash_messages.html' %}
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
# New Templates Glossary
|
||||
|
||||
This document serves as a glossary for the templates directory structure of the project.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
- `/templates`
|
||||
- `base.html`: The main base template from which all other templates inherit. This template is a combination of `main_template`, `admin_template`, `withoutnav_template` and `content_template`.
|
||||
- **/layouts**: Contains base templates and shared layouts used across the site. Simply put, it defines the overall structure or skeleton of the application (less frequently revised).
|
||||
- `withnav_template.html`: A variation of the base layout that includes a sidebar.
|
||||
- `org_template.html`: A variaton of the withnav_template
|
||||
- **/components**: Houses reusable UI components that can be included in multiple templates and can be tailored with different content or links depending on the context.(more frequently revised or customized)
|
||||
- `header.html`: Template for the site's header, included in `base.html`.
|
||||
- `footer.html`: Template for the site's footer, included in `base.html`.
|
||||
- **/views** (or **/pages**): Individual page templates that use the base layouts, components, and partials to present content.
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Use **inheritance** (`{% extends %}`) to build on base layouts.
|
||||
- Employ **components** (`{% include %}`) for reusable UI elements to keep the code DRY and facilitate easier updates.
|
||||
|
||||
### Observation Notes
|
||||
- The macro-options.json files in the header and footer component act as structural guides. They aren't directly used as data passed to the usaFooter function/macro. Instead, these files outline the expected properties and provide a description of their purpose. The `usaFooter` macro component is currently only invoked in the `admin_template`, which will eventually serve as the `base.html` template. This will simplify the approach when we change the footer macros to componenets by eliminating the need to dynamically pass this data from the base.html template.
|
||||
|
||||
|
||||
|
||||
### Old Layout Templates We Don't Need
|
||||
- withoutnav_template.html Delete
|
||||
- main_template.html Delete
|
||||
- settings_templates.html `withnav_template` can be used to replace `settings_template`.
|
||||
- settings_nav.html (move to /new/navigation directory)
|
||||
- main_nav.html (move to /new/navigation directory)
|
||||
- service_navigation.html (move to /new/navigation directory)
|
||||
- org_template, could be under it's own directory called /layout/organization
|
||||
- org_nav.html (move to /new/navigation directory)
|
||||
- content_template.html Delete
|
||||
@@ -28,14 +28,14 @@
|
||||
<p class="bottom-gutter hint">
|
||||
Report is {{ "{:.0f}%".format(job.percentage_complete * 0.99) }} complete…
|
||||
</p>
|
||||
{% elif notifications and time_left != "Data no longer available" %}
|
||||
{% elif notifications %}
|
||||
<p class="bottom-gutter">
|
||||
<a href="{{ download_link }}" download class="usa-link heading-small">Download this report (<abbr title="Comma separated values">CSV</abbr>)</a>
|
||||
 
|
||||
<span id="time-left">{{ time_left }}</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% call(item, row_number) list_table(
|
||||
notifications,
|
||||
caption=uploaded_file_name,
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
<p class='bottom-gutter'>
|
||||
{% if job.scheduled_for %}
|
||||
{% if job.processing_started %}
|
||||
Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short_america }}
|
||||
Sent by {{ job.created_by.name }} on {{ job.processing_started|format_datetime_short }}
|
||||
{% else %}
|
||||
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
|
||||
Uploaded by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short_america }}
|
||||
Sent by {{ job.created_by.name }} on {{ job.created_at|format_datetime_short }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if job.status == 'sending limits exceeded'%}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading, notification_status_field %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
<div class="ajax-block-container table-wrapper" aria-labelledby='pill-selected-item'>
|
||||
<div class="ajax-block-container" aria-labelledby='pill-selected-item'>
|
||||
<div class="dashboard-table bottom-gutter-3-2">
|
||||
|
||||
{% call(item, row_number) list_table(
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
<div class="grid-container">
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="tablet:grid-col-3">
|
||||
<div class="grid-col-3">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-3 margin-bottom-4">
|
||||
<div class="grid-col-3">
|
||||
{% endif %}
|
||||
{% include "settings_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="tablet:grid-col-8">
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<div class="ajax-block-container" id='pill-selected-item'>
|
||||
|
||||
{% if notifications %}
|
||||
<div class="table-wrapper">
|
||||
<div class='dashboard-table'>
|
||||
{% endif %}
|
||||
{% call(item, row_number) list_table(
|
||||
@@ -30,7 +29,6 @@
|
||||
{% endcall %}
|
||||
{% if notifications %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if show_pagination %}
|
||||
|
||||
@@ -14,26 +14,24 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
{{ page_header('Callbacks') }}
|
||||
<div class="table-wrapper">
|
||||
<div class="bottom-gutter-3-2 dashboard-table body-copy-table">
|
||||
{% call mapping_table(
|
||||
caption='General',
|
||||
field_headings=['Label', 'Value', 'Action'],
|
||||
field_headings_visible=False,
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% call row() %}
|
||||
{{ text_field('Delivery receipts') }}
|
||||
{{ optional_text_field(delivery_status_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.delivery_status_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
<div class="bottom-gutter-3-2 dashboard-table body-copy-table">
|
||||
{% call mapping_table(
|
||||
caption='General',
|
||||
field_headings=['Label', 'Value', 'Action'],
|
||||
field_headings_visible=False,
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% call row() %}
|
||||
{{ text_field('Delivery receipts') }}
|
||||
{{ optional_text_field(delivery_status_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.delivery_status_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
|
||||
{% call row() %}
|
||||
{{ text_field('Received text messages') }}
|
||||
{{ optional_text_field(received_text_messages_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.received_text_messages_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% call row() %}
|
||||
{{ text_field('Received text messages') }}
|
||||
{{ optional_text_field(received_text_messages_callback, truncate=true) }}
|
||||
{{ edit_field('Change', url_for('.received_text_messages_callback', service_id=current_service.id)) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/table.html" import list_table, field, text_field, index_field, hidden_field_heading %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
{% from "components/components/skip-link/macro.njk" import usaSkipLink %}
|
||||
@@ -8,7 +9,7 @@
|
||||
{% set file_contents_header_id = 'file-preview' %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Select delivery time" }}
|
||||
{{ "Preview of {}".format(template.name) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -18,11 +19,11 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{{ page_header('Select delivery time') }}
|
||||
{{ page_header('Preview of {}'.format(template.name)) }}
|
||||
|
||||
{{ template|string }}
|
||||
<div class="bottom-gutter-3-2">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.preview_job', service_id=current_service.id, template_id=template_id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.start_job', service_id=current_service.id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
{% if choose_time_form %}
|
||||
{{ choose_time_form.scheduled_for(param_extensions={
|
||||
@@ -36,11 +37,55 @@
|
||||
{% endif %}
|
||||
|
||||
{% set button_text %}
|
||||
Preview
|
||||
Send {{ count_of_recipients|message_count(template.template_type) }}
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if not request.args.from_test %}
|
||||
|
||||
<h2 class="font-body-lg" id="{{ file_contents_header_id }}">{{ original_file_name }}</h2>
|
||||
|
||||
<div class="fullscreen-content" data-module="fullscreen-table">
|
||||
{% call(item, row_number) list_table(
|
||||
recipients.displayed_rows,
|
||||
caption=original_file_name,
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'<span class="usa-sr-only">Row in file</span><span aria-hidden="true">1</span>'|safe
|
||||
] + recipients.column_headers
|
||||
) %}
|
||||
{% call index_field() %}
|
||||
<span>
|
||||
{% if (item.index + 2) == preview_row %}
|
||||
{{ item.index + 2 }}
|
||||
{% else %}
|
||||
<a class="usa-link" href="{{ url_for('.check_messages', service_id=current_service.id, template_id=template.id, upload_id=upload_id, row_index=(item.index + 2), original_file_name=original_file_name) }}">{{ item.index + 2 }}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endcall %}
|
||||
{% for column in recipients.column_headers %}
|
||||
{% if item[column].ignore %}
|
||||
{{ text_field(item[column].data or '', status='default') }}
|
||||
{% else %}
|
||||
{{ text_field(item[column].data or '') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if item[None].data %}
|
||||
{% for column in item[None].data %}
|
||||
{{ text_field(column, status='default') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if count_of_displayed_recipients < count_of_recipients %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the first {{ count_of_displayed_recipients }} rows
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/table.html" import list_table, field, text_field, hidden_field_heading %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
{% from "components/components/skip-link/macro.njk" import usaSkipLink %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
|
||||
{% set file_contents_header_id = 'file-preview' %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Preview of {}".format(template.name) }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block backLink %}
|
||||
{{ usaBackLink({ "href": back_link_from_preview }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{{ page_header('Preview') }}
|
||||
<div>
|
||||
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for if scheduled_for else 'Now'}}</p>
|
||||
<p class="sms-message-file-name">File: {{original_file_name}}</p>
|
||||
<p class="sms-message-template">Template: {{template.name}}</p>
|
||||
<p class="sms-message-sender" >From: {{ template.sender }}</p>
|
||||
</div>
|
||||
|
||||
<h2 id="{{ file_contents_header_id }}">Message</h2>
|
||||
<div class="preview-message"> {{ simplifed_template|string }}</div>
|
||||
{% if not request.args.from_test %}
|
||||
<h2>Recipients list</h2>
|
||||
<div>
|
||||
<ul class="usa-icon-list">
|
||||
<li class="usa-icon-list__item">
|
||||
<img src="{{ url_for('static', filename='img/material-icons/description.svg') }}" alt="Description Icon">
|
||||
<div class="usa-icon-list__content">
|
||||
<h3>{{ original_file_name }}</h3>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="usa-table-container--scrollable" tabindex="0">
|
||||
{% call(item, row_number) list_table(
|
||||
recipients.displayed_rows,
|
||||
caption="Note: Only the first 5 rows are displayed here.",
|
||||
caption_visible=True,
|
||||
field_headings=recipients.column_headers
|
||||
) %}
|
||||
{% for column in recipients.column_headers %}
|
||||
{% if item[column].ignore %}
|
||||
{{ text_field(item[column].data or '', status='default') }}
|
||||
{% else %}
|
||||
{{ text_field(item[column].data or '') }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if item[None].data %}
|
||||
{% for column in item[None].data %}
|
||||
{{ text_field(column, status='default') }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- <div class="bottom-gutter-3-2">
|
||||
<p>This is a placeholder: This message will be delivered to <b>400 phone numbers</b> and will use a total of <b>800 message parts</b>, leaving Washington DSHS with <b>249,200 message parts remaining</b>.</p>
|
||||
</div> -->
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for('main.start_job', service_id=current_service.id, upload_id=upload_id)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<h3>Does everything look good?</h3>
|
||||
{% set button_text %}
|
||||
{{ "Schedule" if scheduled_for else 'Send'}}
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,117 +1,115 @@
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, row_heading %}
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class='dashboard-table ajax-block-container'>
|
||||
{% call(item, row_number) list_table(
|
||||
jobs,
|
||||
caption="Recent files uploaded",
|
||||
caption_visible=False,
|
||||
empty_message=(
|
||||
'You have not uploaded any files yet.'
|
||||
),
|
||||
field_headings=[
|
||||
'File',
|
||||
'Status'
|
||||
],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% call row_heading() %}
|
||||
<div class="file-list">
|
||||
<a class="file-list-filename-large usa-link" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||
{% if item.scheduled %}
|
||||
<span class="file-list-hint-large">
|
||||
Sending {{
|
||||
item.scheduled_for|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="file-list-hint-large">
|
||||
Sent {{
|
||||
(item.scheduled_for or item.created_at)|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
<div class='dashboard-table ajax-block-container'>
|
||||
{% call(item, row_number) list_table(
|
||||
jobs,
|
||||
caption="Recent files uploaded",
|
||||
caption_visible=False,
|
||||
empty_message=(
|
||||
'You have not uploaded any files yet.'
|
||||
),
|
||||
field_headings=[
|
||||
'File',
|
||||
'Status'
|
||||
],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% call row_heading() %}
|
||||
<div class="file-list">
|
||||
<a class="file-list-filename-large usa-link" href="{{ url_for('.view_job', service_id=current_service.id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||
{% if item.scheduled %}
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notification_count is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notification_count) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notification_count) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notification_count }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if item.notification_count %}
|
||||
<span class="big-number-label">{{ item.notification_count|message_count_label(item.template_type,suffix='waiting to send') }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<span class="file-list-hint-large">
|
||||
Sending {{
|
||||
item.scheduled_for|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% else %}
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{{ "{:,}".format(item.notifications_sending) }}
|
||||
</span>
|
||||
<span class="big-number-label">pending</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
<span class="file-list-hint-large">
|
||||
Sent {{
|
||||
(item.scheduled_for or item.created_at)|format_datetime_relative
|
||||
}}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{% if item.scheduled %}
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_delivered is number %}
|
||||
{{ "{:,}".format(item.notifications_delivered) }}
|
||||
{% if item.notification_count is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notification_count) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notification_count) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notifications_delivered }}
|
||||
{{ item.notification_count }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">delivered</span>
|
||||
{% if item.notification_count %}
|
||||
<span class="big-number-label">{{ item.notification_count|message_count_label(item.template_type,suffix='waiting to send') }}</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_failed is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notifications_failed) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notifications_failed) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notifications_failed }}
|
||||
{% endif %}
|
||||
{{ "{:,}".format(item.notifications_sending) }}
|
||||
</span>
|
||||
<span class="big-number-label">failed</span>
|
||||
<span class="big-number-label">pending</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</div></div>
|
||||
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_delivered is number %}
|
||||
{{ "{:,}".format(item.notifications_delivered) }}
|
||||
{% else %}
|
||||
{{ item.notifications_delivered }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">delivered</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid-col-4">
|
||||
{% if link %}
|
||||
<a class="usa-link display-flex" href="{{ link }}">
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
<span class="big-number-smallest">
|
||||
<span class="big-number-number">
|
||||
{% if item.notifications_failed is number %}
|
||||
{% if currency %}
|
||||
{{ "{}{:,.2f}".format(currency, item.notifications_failed) }}
|
||||
{% else %}
|
||||
{{ "{:,}".format(item.notifications_failed) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ item.notifications_failed }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="big-number-label">failed</span>
|
||||
</span>
|
||||
{% if link %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</div></div>
|
||||
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
@@ -28,74 +28,52 @@
|
||||
{{ ajax_block(partials, updates_url, 'totals') }}
|
||||
|
||||
{{ ajax_block(partials, updates_url, 'template-statistics') }}
|
||||
<h2 class="margin-top-4 margin-bottom-1">Recent Batches</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="usa-table usa-table--borderless job-table">
|
||||
<thead class="table-field-headings">
|
||||
<tr>
|
||||
<th scope="col" class="table-field-heading-first">
|
||||
<span>File name</span>
|
||||
</th>
|
||||
<th scope="col" class="table-field-heading">
|
||||
<span>Template</span>
|
||||
</th>
|
||||
<th scope="col" class="table-field-heading">
|
||||
<span>Time sent</span>
|
||||
</th>
|
||||
<th scope="col" class="table-field-heading">
|
||||
<span>Sender</span>
|
||||
</th>
|
||||
<th scope="col" class="table-field-heading">
|
||||
<span># of Recipients</span>
|
||||
</th>
|
||||
<th scope="col" class="table-field-heading">
|
||||
<span>Report</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if job_and_notifications %}
|
||||
{% for job in job_and_notifications[:5] %}
|
||||
{% if job.job_id and job.notifications %}
|
||||
{% set notification = job.notifications[0] %}
|
||||
<tr class="table-row" id="{{ job.job_id }}">
|
||||
<td class="table-field file-name">
|
||||
{{ notification.job.original_file_name if notification.job.original_file_name else 'Manually entered number'}}
|
||||
<br>
|
||||
<a class="usa-link file-list-filename" href="{{ job.view_job_link }}">View Batch</a>
|
||||
</td>
|
||||
<td class="table-field template">
|
||||
{{ notification.template.name }}
|
||||
</td>
|
||||
<td class="table-field time-sent">
|
||||
{{ job.created_at | format_datetime_short_america }}
|
||||
</td>
|
||||
<td class="table-field sender">
|
||||
{{ notification.created_by.name }}
|
||||
</td>
|
||||
<td class="table-field count-of-recipients">
|
||||
{{ job.notification_count}}
|
||||
</td>
|
||||
<td class="table-field report">
|
||||
{% if notification and job.time_left != "Data no longer available" %}
|
||||
<a class="usa-link file-list-filename" href="{{ job.download_link }}">Download</a>
|
||||
<span class="usa-hint">{{ job.time_left }}</span>
|
||||
{% elif job %}
|
||||
<span>{{ job.time_left }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<h2 class="margin-top-4 margin-bottom-1">Batched Jobs</h2>
|
||||
|
||||
<div class='job-table'>
|
||||
{% call(item, row_number) list_table(
|
||||
notifications,
|
||||
caption="Batched Jobs",
|
||||
caption_visible=False,
|
||||
border_visible=True,
|
||||
empty_message='No batched job messages found  (messages are kept for {} days)'.format(service_data_retention_days)|safe,
|
||||
field_headings=['Template Name','Date/Time', 'Download (CSV) Report'],
|
||||
field_headings_visible=False
|
||||
) %}
|
||||
{% if item.job.original_file_name and item.job.id %}
|
||||
{% call row_heading() %}
|
||||
<a class="usa-link file-list-filename" href="/services/{{ item.service }}/jobs/{{ item.job.id }}">{{ item.job.original_file_name|replace('.csv', '') if item.job.id else '' }}</a>
|
||||
{% endcall %}
|
||||
{% call row_heading() %}
|
||||
{{ item.status|format_notification_status_as_time(
|
||||
item.created_at|format_datetime_short,
|
||||
(item.updated_at or item.created_at)|format_datetime_short)
|
||||
}}
|
||||
{% endcall %}
|
||||
{% call row_heading() %}
|
||||
{% set availability = download_availability|selectattr('job_id', 'equalto', item.job.id)|first %}
|
||||
{% if availability and availability.time_left != "Data no longer available" %}
|
||||
<a class="usa-link file-list-filename" href="{{ availability.download_link[0] }}">{{ "Download" if item.job.original_file_name else '' }}</a>
|
||||
<span>- {{ availability.time_left }}</span>
|
||||
{% elif availability %}
|
||||
<span>{{ availability.time_left }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr class="table-row">
|
||||
<td class="table-empty-message" colspan="10">No batched job messages found  (messages are kept for {{ service_data_retention_days }} days).</td>
|
||||
</tr>
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
|
||||
{% if show_pagination %}
|
||||
{{ previous_next_navigation(prev_page, next_page) }}
|
||||
{% elif next_page %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the first 50 messages
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h2 class="margin-top-4 margin-bottom-1">Usage</h2>
|
||||
<h3 class="margin-bottom-0">Daily</h3>
|
||||
<p class="margin-0">Across all services</p>
|
||||
|
||||
@@ -11,232 +11,115 @@
|
||||
|
||||
<h1 class="font-body-2xl margin-bottom-3">Guidance</h1>
|
||||
|
||||
<p>Notify allows you easily to create templates for messages for your recipients. You can customize messages to encourage
|
||||
your recipient to manage their benefits and increase follow through.</p>
|
||||
<p>Below we explain how to:</p>
|
||||
<p>This guidance is for teams using Notify.gov to send text messages.</p>
|
||||
|
||||
<p>It explains how to:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li><a class="usa-link" href="#format-content">Format your content</a></li>
|
||||
<li><a class="usa-link" href="#add-links">Add links</a></li>
|
||||
<li><a class="usa-link" href="#personalize-content">Personalize your content</a></li>
|
||||
<li><a class="usa-link" href="#conditional-content">Add conditional content</a></li>
|
||||
<li><a class="usa-link" href="#indentify-program">Identify your program</a></li>
|
||||
<li><a class="usa-link" href="#prepare-data">Prepare your data</a></li>
|
||||
<li><a class="usa-link" href="#prevent-fraud">Prevent fraud</a></li>
|
||||
<li><a class="usa-link" href="#editandformat">edit and format messages</a></li>
|
||||
<li><a class="usa-link" href="#customization">add customization</a></li>
|
||||
<!-- <li><a class="usa-link" href="{{ url_for('.send_files_by_email') }}">send files by email</a></li> -->
|
||||
</ul>
|
||||
|
||||
{# Format content #}
|
||||
<h2 class="padding-top-1" id="format-content">Format your content</h2>
|
||||
<p>Effective texts will help your message recipients take the steps needed to secure and keep the benefits and services
|
||||
they depend on. To craft an effective text:</p>
|
||||
<h2 class="font-body-lg" id="#formatting">Edit and format messages</h2>
|
||||
|
||||
<p>This section explains how to:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li><a class="usa-link" href="#formatting">format your content</a></li>
|
||||
<li><a class="usa-link" href="#links">add links</a></li>
|
||||
<li><a class="usa-link" href="#personalised-content">personalize your content</a></li>
|
||||
<li><a class="usa-link" href="#optional-content">add optional content</a></li>
|
||||
</ul>
|
||||
|
||||
<h3 class="font-body-lg" id="formatting">Format your content</h3>
|
||||
|
||||
<p>You can see a list of formatting instructions on the edit template page:</p>
|
||||
|
||||
<ol class="list list-number">
|
||||
<li>Go to the {{ service_link(current_service, 'main.choose_template', 'templates') }} page.</li>
|
||||
<li>Add a new template or choose an existing template and select <b class="bold">Edit</b>.</li>
|
||||
</ol>
|
||||
|
||||
<h3 class="font-body-lg" id="links">Add links</h3>
|
||||
|
||||
<p>When composing a text message, write URLs in full and Notify will convert them into links for you.
|
||||
</p>
|
||||
|
||||
<p>You cannot convert text into a link.
|
||||
<!-- Research shows that people like to check a URL looks genuine before clicking the link in an email. This is hard to do if the URL is hidden behind clickable link text.-->
|
||||
</p>
|
||||
|
||||
<p>We do not recommend using a third-party link shortening service because:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li>Choose your messages thoughtfully. Text messages are best to remind/nudge someone to take a specific action or
|
||||
communicate information at a particular time rather than pushing out broad or overly general information.</li>
|
||||
<li>Clearly state the issue and expected response. Tell your recipient what you expect from them.</li>
|
||||
<li>Say only one important thing per message.</li>
|
||||
<li>Frame the message to encourage action and explain the consequences of not completing the desired action.</li>
|
||||
<li>your users cannot see where the link will take them</li>
|
||||
<li>your link might stop working if there’s a service outage</li>
|
||||
<li>you can no longer control where the redirect goes</li>
|
||||
</ul>
|
||||
|
||||
<h3>To create and format your message</h3>
|
||||
<ol class="list">
|
||||
<li>All messages start from a template</li>
|
||||
<li>Click "<a href={{ url_for('.choose_template', service_id=current_service.id) }}>Send Messages</a>". You'll see existing templates.</li>
|
||||
<li>Add a new template or choose an existing template and select Edit.</li>
|
||||
<h3 class="font-body-lg" id="personalised-content">Personalize your content</h3>
|
||||
|
||||
<p>To personalize the content of your messages, add a placeholder to the template.</p>
|
||||
|
||||
<p>Placeholders are filled in with details, like a name or reference number, each time you send a
|
||||
message.</p>
|
||||
|
||||
<p>To add a placeholder to the template:</p>
|
||||
|
||||
<ol class="list list-number">
|
||||
<li>Go to the {{ service_link(current_service, 'main.choose_template', 'templates') }} page.</li>
|
||||
<li>Add a new template or choose an existing template and select <b class="bold">Edit</b>.</li>
|
||||
<li>Add a placeholder using double brackets. For example: Hello ((first name)), your reference is
|
||||
((ref number)).</li>
|
||||
<li>Select <b class="bold">Save</b>.</li>
|
||||
</ol>
|
||||
|
||||
{# Add links #}
|
||||
<h2 class="padding-top-1" id="add-links">Add links</h2>
|
||||
<p>When composing a text message, links to websites or online applications can help your recipient respond quickly.</p>
|
||||
<p>When you send a message you can either:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li>Write URLs in full and Notify will convert them into links for you. Note that you cannot hyperlink text in Notify
|
||||
messages.</li>
|
||||
<li>For link click tracking, you can consider adding campaign parameters to URLs.</li>
|
||||
<li>All links should point to a government domain.</li>
|
||||
<li>Link directly to where your recipient needs to take action, not to more information.</li>
|
||||
<li>manually fill in the placeholders yourself</li>
|
||||
<li>upload a list of personal details and let Notify do it for you</li>
|
||||
</ul>
|
||||
|
||||
<h3>About link-shortening services</h3>
|
||||
<p>We do not recommend using a third-party link-shortening service because:</p>
|
||||
<p>If you upload a list, the column names need to match the placeholders in your template.</p>
|
||||
|
||||
<h3 class="font-body-lg" id="optional-content">Add optional content</h3>
|
||||
|
||||
<p>To add optional content to your messages:</p>
|
||||
|
||||
<ol class="list list-number">
|
||||
<li>Go to the {{ service_link(current_service, 'main.choose_template', 'templates') }} page.</li>
|
||||
<li>Add a new template or choose an existing template and select <b class="bold">Edit</b>.</li>
|
||||
<li>Use double brackets and ?? to define optional content. For example, if you only want to show something to people
|
||||
who are under 18: ((under18??Please get your application signed by a parent or guardian.))</li>
|
||||
<li>Select <b class="bold">Save</b>.</li>
|
||||
</ol>
|
||||
|
||||
<p>For each person you send this message to, specify ‘yes’ or ‘no’ to show or hide this content. You
|
||||
can either:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li>Your recipient cannot see where the link will take them, which could make them suspect the link is spam/scam.</li>
|
||||
<li>Your link might stop working if there’s a service outage.</li>
|
||||
<li>You can no longer control where the redirect goes.</li>
|
||||
<li>do this yourself</li>
|
||||
<li>upload a list of personal details and let Notify do it for you</li>
|
||||
</ul>
|
||||
|
||||
{# Personalize content #}
|
||||
<h2 class="padding-top-1" id="personalize-content">Personalize your content</h2>
|
||||
<p>Personalizing your content can increase response rates and help the recipient know the text is legitimate.</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>Including a person's first name increases response rates.</li>
|
||||
<li>Specific details such as time and location of an appointment or where suspected fraud use occurred encourages action.</li>
|
||||
</ul>
|
||||
<p>If you upload a list, the column names need to match the optional content in your template.</p>
|
||||
|
||||
<h3>To personalize your content</h3>
|
||||
<ol class="list">
|
||||
<li>Add a placeholder to your content by placing two brackets around the personalized elements.</li>
|
||||
<li>You can manually enter the personalized content or you can upload a spreadsheet with the details and let Notify do the
|
||||
work for you. See <a href="#prepare-data">data preparation</a>.</li>
|
||||
</ol>
|
||||
<h2 class="font-body-lg" id="customization">Message customization</h2>
|
||||
|
||||
<h4>Example</h4>
|
||||
<p>To personalize with the recipient's first name and include a reference number:</p>
|
||||
<p class="padding-2 bg-base-lightest">State WIC: Hello ((first name)), your reference is ((ref number)). Please provide this number when you call 555-123-1234 to make an appointment.</p>
|
||||
<h3 class="font-body-lg">Change the text message sender</h3>
|
||||
|
||||
<p>Note that variations in the length of personalized content can impact the length of specific messages, and may affect
|
||||
the number of parts used.</p>
|
||||
<p>The text message sender tells your users who the message is from.</p>
|
||||
|
||||
{# Add conditional content #}
|
||||
<h2 class="padding-top-1" id="conditional-content">Add conditional content</h2>
|
||||
<p>Conditional (or optional) content appears only when a recipient meets certain criteria. This feature allows you to make
|
||||
all or part of the message contingent upon specific criteria associated with the recipient.</p>
|
||||
<p>To change the text message sender from the default of ‘Notify.gov’:</p>
|
||||
|
||||
<h3>To add conditional content</h3>
|
||||
<ol class="list">
|
||||
<li>Use two brackets and ?? to define the conditional content.</li>
|
||||
<li>You can manually enter the conditional content or you can upload a spreadsheet with the personal details and let Notify
|
||||
do the work for you. See <a href="#prepare-data">data preparation</a>.</li>
|
||||
</ol>
|
||||
|
||||
<h4>Examples</h4>
|
||||
<ol class="list">
|
||||
<li>If you only want to show something to people who are under 18:
|
||||
</br>
|
||||
<p class="padding-2 bg-base-lightest">State SNAP: Renewal applications are due by March 15. ((under18??Please get your application signed by a parent or
|
||||
guardian.))</p>
|
||||
</li>
|
||||
<li>
|
||||
If you want to make people who are homebound aware of the option of virtual visits (but not other message recipients):
|
||||
</br>
|
||||
<p class="padding-2 bg-base-lightest">State Medicaid: Please call 555-123-1234 to schedule an appointment. ((homebound??Virtual visits are available.))</p>
|
||||
</li>
|
||||
<li>
|
||||
If you want to send a messages in different languages to different recipients:
|
||||
</br>
|
||||
<p class="padding-2 bg-base-lightest">((English??We’ve identified unauthorized use on your EBT account. Call the phone number on the back of your card to
|
||||
cancel or go to your local CSO for immediate replacement.))((Spanish??Hemos identificado un uso no autorizado en su
|
||||
cuenta EBT. Llame al número de teléfono que aparece en el reverso de su tarjeta para cancelarla o diríjase a su CSO
|
||||
local para que se la sustituyan inmediatamente.))</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
{# Identify your program #}
|
||||
<h2 class="padding-top-1" id="indentify-program">Identify your program</h2>
|
||||
<p>You can help your recipients identify your texts as legitimate by customizing your messages to clearly state who they
|
||||
are from. Consider using the program or benefit name that is most familiar to your recipients.</p>
|
||||
|
||||
<h3>To customize your program name</h3>
|
||||
<p>To change the text message sender from the default service name:</p>
|
||||
<ol class="list">
|
||||
<li>Go to the <a href="{{ url_for('main.service_settings', service_id=current_service.id) }}">Settings page</a></li>
|
||||
<li>Select “Start text messages with service name.”</li>
|
||||
<li>Change the service name to a familiar program or benefit.</li>
|
||||
</ol>
|
||||
|
||||
{# Prepare your data #}
|
||||
<h2 class="padding-top-1" id="prepare-data">Prepare your data</h2>
|
||||
<p>The easiest and most efficient way to personalize your content is by uploading a spreadsheet. Notify can accommodate
|
||||
many file formats and structures.</p>
|
||||
|
||||
<h3>File format</h3>
|
||||
<p>Notify can accept files in the following formats: CSV, TSV, ODS, and Microsoft Excel.</p>
|
||||
|
||||
<h3>File structure</h3>
|
||||
<ul class="list">
|
||||
<li>The phone number must be in the first column (Column A) and must be labeled <strong>Phone number</strong>.</li>
|
||||
<li>Each column must have a unique name.</li>
|
||||
<li>If you are using Excel, you must either disable the”auto-date/time format” for time and date columns or convert the file
|
||||
to a CSV prior to loading. (If you do not, Excel will display date/time data in a confusing format.)</li>
|
||||
</ul>
|
||||
|
||||
<h3>Formatting personalized content</h3>
|
||||
<p>If you are sending a message with personalized content, such as the first name of the recipient or the appointment time
|
||||
and location, the names of the column headings have to match the indicator included in the message template.</p>
|
||||
<p>For example, if the personalized content is the first name of the recipient, and we are using the spreadsheet below, the
|
||||
indicator in the message needs to be ((First name)), not ((firstname)) or ((name)).</p>
|
||||
|
||||
<h3>Formatting conditional content</h3>
|
||||
<p>If you are sending messages with conditional content, such as content based on the recipient’s preferred language or
|
||||
location, the flag to receive the content needs to be captured in its own column with a Yes or No (Y/N) flag.</p>
|
||||
|
||||
<h3>Example</h3>
|
||||
<table class="usa-table">
|
||||
<caption class="usa-sr-only">
|
||||
Example
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Phone number</th>
|
||||
<th scope="col">First name</th>
|
||||
<th scope="col">Last name</th>
|
||||
<th scope="col">Spanish</th>
|
||||
<th scope="col">English</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Time</th>
|
||||
<th scope="col">Location</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>123-456-7890</td>
|
||||
<td>Lulu</td>
|
||||
<td>Praether</td>
|
||||
<td>N</td>
|
||||
<td>Y</td>
|
||||
<td>November 2, 2023</td>
|
||||
<td>3:25</td>
|
||||
<td>123 Ford Rd.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>234-567-8901</td>
|
||||
<td>Sela</td>
|
||||
<td>Appel</td>
|
||||
<td>Y</td>
|
||||
<td>N</td>
|
||||
<td>November 2, 2023</td>
|
||||
<td>4:00</td>
|
||||
<td>123 Ford Rd.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>123-456-7890</td>
|
||||
<td>Dexter</td>
|
||||
<td>Moseley</td>
|
||||
<td>N</td>
|
||||
<td>Y</td>
|
||||
<td>November 2, 2023</td>
|
||||
<td>2:00</td>
|
||||
<td>123 Ford Rd.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{# Preventing fraud #}
|
||||
<h2 class="padding-top-1" id="prevent-fraud">Preventing fraud</h2>
|
||||
<p>Texting fraud is ever prevalent and while we can’t eliminate the risk, we can reduce the chances that recipients will
|
||||
fall victim to fraud.</p>
|
||||
<ol class="list">
|
||||
<li>Never send unnecessary or protected private information.</li>
|
||||
<li>Remind your recipients that text messaging is not a secure means of communication and they should not send you private
|
||||
information via text.</li>
|
||||
<li>Only include links to websites and online applications that are secure.</li>
|
||||
<li>Send an introductory text letting recipients know you will be texting them and to save the number in their phone.</li>
|
||||
<li>Include an auto-response with the name of your organization and a contact phone number.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Examples</h3>
|
||||
<ol class="list">
|
||||
<li>Introductory text:
|
||||
</br>
|
||||
<p class="padding-2 bg-base-lightest">State Transit Dept: We're piloting a new way to get important reminders to our staff. Save this number to your phone,
|
||||
you'll receive updates from us here.</p>
|
||||
</li>
|
||||
<li>Auto-response text:
|
||||
</br>
|
||||
<p class="padding-2 bg-base-lightest">State Agency: This number is unmonitored. To contact us, call us at 555-123-1234. We will never ask for personal details
|
||||
in a text. If you have questions about how to protect your privacy, see statename.gov/privacy.</p>
|
||||
</li>
|
||||
<ol class="list list-number">
|
||||
<li>Go to the <b class="bold">Text message settings</b> section of the {{
|
||||
service_link(current_service, 'main.service_settings', 'settings') }} page.</li>
|
||||
<li>Select <b class="bold">Manage</b> on the <b class="bold">Text message
|
||||
senders</b> row.</li>
|
||||
<li>Select <b class="bold">Change</b> or <b class="bold">Add text message
|
||||
sender</b>.</li>
|
||||
</ol>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -38,9 +38,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="notification-status">
|
||||
Messages will remain in pending state until carrier status is received, typically 5 minutes.
|
||||
</p>
|
||||
{% if not job.processing_finished %}
|
||||
<div
|
||||
data-module="update-content"
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<h1 class="font-body-2xl margin-bottom-3">Delivery status</h1>
|
||||
|
||||
<p>Notify’s real-time dashboard lets you check the status of any message.</p>
|
||||
<p>For <a class="usa-link" href="{{ url_for('main.security') }}">security</a>, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.</p>
|
||||
<p>This page describes the statuses you'll see when you're signed in to Notify.</p>
|
||||
<p>For <a class="usa-link" href="{{ url_for("main.security") }}">security</a>, this information is only available for seven days after a message has been sent. You can download a report, including a list of sent messages, for your own records.</p>
|
||||
<p>This page describes the statuses you’ll see when you’re signed in to Notify.</p>
|
||||
|
||||
<!-- <p>If you’re using the Notify API, read our <a class="usa-link" href="{{ url_for('.documentation') }}">documentation</a> for a list of API statuses.<p>
|
||||
|
||||
@@ -53,13 +53,13 @@
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% for message_status, description in [
|
||||
('Total', 'The total number of messages that have been sent during the last seven days.'),
|
||||
('Pending', 'Notify has sent the message to the provider. The provider will try to deliver the message to the recipient for up to 72 hours. Notify is waiting for delivery information.'),
|
||||
('Sent', 'The mobile networks may not provide any more delivery information.'),
|
||||
('Delivered', 'The message was successfully delivered. Notify cannot tell you if a user has opened or read a message.'),
|
||||
('Failed', 'The provider could not deliver the message. This can happen if the phone number was wrong or if the network operator
|
||||
rejects the message. If you’re sure that these phone numbers are correct, you should <a class="usa-link" href="/support">contact us</a>. If not, you should remove them from your database. You’ll still be charged for text messages that
|
||||
cannot be delivered.' | safe),
|
||||
] %}
|
||||
('Not delivered', ('The provider could not deliver the message. This can happen if the phone number was wrong or if the network operator rejects the message. If you’re sure that these phone numbers are correct, you should <a class="usa-link" href="' + url_for(".support") + '">contact us</a>. If not, you should remove them from your database. You’ll still be charged for text messages that cannot be delivered.')|safe),
|
||||
('Phone not accepting messages right now', 'The provider could not deliver the message. This can happen when the recipient’s phone is off, has no signal, or their text message inbox is full. You can try to send the message again. You’ll still be charged for text messages to phones that are not accepting messages.'),
|
||||
('Technical failure', 'Your message was not sent because there was a problem between Notify and the provider. You’ll have to try sending your messages again. You will not be charged for text messages that are affected by a technical failure.'),
|
||||
] %}
|
||||
{% call row() %}
|
||||
{{ text_field(message_status) }}
|
||||
{{ text_field(description) }}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Error" if error else "Select delivery time" }}
|
||||
{{ "Error" if error else "Preview of ‘{}’".format(template.name) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block backLink %}
|
||||
@@ -40,32 +40,21 @@
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ page_header('Select delivery time') }}
|
||||
{{ page_header('Preview of ‘{}’'.format(template.name)) }}
|
||||
{% endif %}
|
||||
|
||||
{{ template|string }}
|
||||
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for(
|
||||
'main.preview_notification',
|
||||
'main.send_notification',
|
||||
service_id=current_service.id,
|
||||
template_id=template.id
|
||||
template_id=template.id,
|
||||
help='3' if help else 0
|
||||
)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
{% if not error %}
|
||||
{% if choose_time_form %}
|
||||
{{ choose_time_form.scheduled_for(param_extensions={
|
||||
'formGroup': {'classes': 'bottom-gutter-2-3'},
|
||||
'attributes': {
|
||||
'data-module': 'radio-select',
|
||||
'data-categories': choose_time_form.scheduled_for.categories|join(','),
|
||||
'data-show-now-as-default': 'true'
|
||||
}
|
||||
}) }}
|
||||
{% endif %}
|
||||
{% set button_text %}
|
||||
Preview
|
||||
{% endset %}
|
||||
{% set button_text %}Send 1 {{ 1|message_count_label(template.template_type, suffix='') }}{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/page-header.html" import page_header %}
|
||||
{% from "components/components/back-link/macro.njk" import usaBackLink %}
|
||||
{% from "components/components/button/macro.njk" import usaButton %}
|
||||
|
||||
{% block service_page_title %}
|
||||
{{ "Error" if error else "Preview" }}
|
||||
{% endblock %}
|
||||
|
||||
{% block backLink %}
|
||||
{{ usaBackLink({ "href": back_link_from_preview }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
{% if error == 'not-allowed-to-send-to' %}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% with
|
||||
count_of_recipients=1,
|
||||
template_type_label=(
|
||||
'phone number' if template.template_type == 'sms' else 'email address'
|
||||
)
|
||||
%}
|
||||
{% include "partials/check/not-allowed-to-send-to.html" %}
|
||||
{% endwith %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% elif error == 'too-many-messages' %}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% include "partials/check/too-many-messages.html" %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% elif error == 'message-too-long' %}
|
||||
{# the only row_errors we can get when sending one off messages is that the message is too long #}
|
||||
<div class="bottom-gutter">
|
||||
{% call banner_wrapper(type='dangerous') %}
|
||||
{% include "partials/check/message-too-long.html" %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ page_header('Preview') }}
|
||||
{% endif %}
|
||||
<div>
|
||||
<p class="sms-message-scheduler">Scheduled: {{ scheduled_for if scheduled_for else 'Now'}}</p>
|
||||
<p class="sms-message-template">Template: {{template.name}}</p>
|
||||
<p class="sms-message-sender" >From: {{ template.sender }}</p>
|
||||
<p class="sms-message-sender" >To: {{ recipient }}</p>
|
||||
</div>
|
||||
|
||||
<h2 id="{{ file_contents_header_id }}">Message</h2>
|
||||
<div class="preview-message"> {{ simplifed_template|string }}</div>
|
||||
|
||||
<div class="js-stick-at-bottom-when-scrolling">
|
||||
<form method="post" enctype="multipart/form-data" action="{{url_for(
|
||||
'main.send_notification',
|
||||
service_id=current_service.id,
|
||||
template_id=template.id,
|
||||
help='3' if help else 0
|
||||
)}}" class='page-footer'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<!-- <p>Placeholder: This message will be delivered to <b>400 phone numbers</b> and will use a total of <b>800 message parts</b>, leaving Washington DSHS with <b>249,200 message parts remaining</b>.</p> -->
|
||||
<h3>Does everything look good?</h3>
|
||||
{% if not error %}
|
||||
{% set button_text %}
|
||||
{{ "Schedule" if scheduled_for else 'Send'}}
|
||||
{% endset %}
|
||||
{{ usaButton({ "text": button_text }) }}
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -44,33 +44,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
notifications_by_type|reverse,
|
||||
caption='Messages sent since May 2023',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date',
|
||||
99|message_count_noun('email')|capitalize,
|
||||
99|message_count_noun('sms')|capitalize,
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.emails|format_thousands }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.sms|format_thousands }}
|
||||
{% endcall %}
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
notifications_by_type|reverse,
|
||||
caption='Messages sent since May 2023',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date',
|
||||
99|message_count_noun('email')|capitalize,
|
||||
99|message_count_noun('sms')|capitalize,
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ notifications_by_type|length }} days
|
||||
</p>
|
||||
</div>
|
||||
{% call field() %}
|
||||
{{ item.emails|format_thousands }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.sms|format_thousands }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ notifications_by_type|length }} days
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 class="govuk-heading-m">
|
||||
@@ -84,28 +82,26 @@
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
processing_time | reverse,
|
||||
caption='Messages sent within 10 seconds',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date', 'Percentage'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ '{:.2f}%'.format(item.percentage_under_10_seconds) }}
|
||||
{% endcall %}
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
processing_time | reverse,
|
||||
caption='Messages sent within 10 seconds',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Date', 'Percentage'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.date | format_date_normal }}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ processing_time|length }} days
|
||||
</p>
|
||||
</div>
|
||||
{% call field() %}
|
||||
{{ '{:.2f}%'.format(item.percentage_under_10_seconds) }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
<p class="table-show-more-link">
|
||||
Only showing the last {{ processing_time|length }} days
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 class="govuk-heading-m">
|
||||
@@ -124,25 +120,23 @@
|
||||
<span class="usa-sr-only">using Notify.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
organizations_using_notify,
|
||||
caption='Organizations using Notify',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Organization', 'Number of live services'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.organization_name }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.count_of_live_services }}
|
||||
{% endcall %}
|
||||
<div class="dashboard-table">
|
||||
{% call(item, row_number) list_table(
|
||||
organizations_using_notify,
|
||||
caption='Organizations using Notify',
|
||||
caption_visible=False,
|
||||
field_headings=[
|
||||
'Organization', 'Number of live services'
|
||||
],
|
||||
empty_message='No data to show'
|
||||
) %}
|
||||
{% call field() %}
|
||||
{{ item.organization_name }}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% call field() %}
|
||||
{{ item.count_of_live_services }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -6,32 +6,73 @@
|
||||
{% extends "content_template.html" %}
|
||||
|
||||
{% block per_page_title %}
|
||||
Message parts
|
||||
Pricing
|
||||
{% endblock %}
|
||||
|
||||
{% block content_column_content %}
|
||||
|
||||
<h1 class="font-body-2xl margin-bottom-3">Message parts</h1>
|
||||
<h1 class="font-body-2xl margin-bottom-3">Pricing</h1>
|
||||
|
||||
{{ content_metadata(
|
||||
data={
|
||||
"Last updated": "February 5, 2024"
|
||||
"Last updated": "Feb 27, 2023"
|
||||
}
|
||||
) }}
|
||||
|
||||
<p>During the pilot period, each service has an allowance of 250,000 message parts. Once this allowance is met, the
|
||||
application will stop delivering messages. There's no monthly charge, no setup fee, and no procurement cost. You'll use
|
||||
more parts towards the allowance if you:</p>
|
||||
<p>During pilot period, each service has an allowance of 250,000 free text messages. Once this allowance is met, the
|
||||
application will stop delivering messages. There's no monthly charge, no setup fee and no procurement cost.</p>
|
||||
<p>When a new service is added, it will start in <a class="usa-link" href="{{ url_for('main.trial_mode_new') }}">trial mode</a>. Moving a service out of trial mode is subject to approval by
|
||||
the Notify.gov team.</p>
|
||||
<p>You'll use more messages towards the allowance if you send text messages longer than 160 characters.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% if not current_user.is_authenticated %}
|
||||
<a class="usa-link" href="{{ url_for('main.register') }}">Create an account,</a> then
|
||||
add your unique service(s).
|
||||
</p>
|
||||
{% else %}
|
||||
Additional unique services may be added, although moving each service out of trial mode is subject to approval by the
|
||||
Notify.gov team.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<p>When you add a new service it will start in <a class="usa-link"
|
||||
href="{{ url_for('main.trial_mode_new') }}">trial mode</a>.</p>
|
||||
|
||||
<p>You’ll use more messages towards the allowance if you send text messages longer than 160 characters.</p>
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="emails">Emails</h2>
|
||||
<p>It’s free to send emails through Notify.</p> -->
|
||||
|
||||
<h2 class="font-body-lg" id="text-messages">Text messages</h2>
|
||||
<p>During pilot phase, each service has an annual allowance of 250,000 free text messages. Once this allowance is met,
|
||||
the application will stop delivering messages.</p>
|
||||
<!-- <p>When a service has used its annual allowance, it costs {{ sms_rate }} pence (plus VAT) for each text message you send.</p> -->
|
||||
<p>You’ll use more messages towards the allowance, if you send <a class="usa-link"
|
||||
href="#long-text-messages">text messages longer than 160 characters</a>.</p>
|
||||
<p>You may also use more messages if you:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>send text messages longer than 160 characters</a></li>
|
||||
<li>use certain <a class="usa-link" href="#symbols">signs and symbols</a></li>
|
||||
<li>use <a class="usa-link" href="#accents">accents and accented letters</a></li>
|
||||
<li>send text messages to <a class="usa-link"
|
||||
href="#international-numbers">international numbers</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- <p>See <a class="usa-link" href="{{ url_for('main.how_to_pay') }}">how to pay</a>.
|
||||
|
||||
<h3 class="font-body-lg" id="free-text-message-allowance">Free text message allowance</h3>
|
||||
|
||||
<p>The current allowance is:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>40,000 free text messages for national services</li>
|
||||
<li>20,000 free text messages for regional services</li>
|
||||
<li>10,000 free text messages for state-funded schools and GP practices</li>
|
||||
</ul>
|
||||
<p>Each unique service you add has a separate allowance.</p> -->
|
||||
|
||||
<h3 class="font-body-lg" id="long-text-messages">Long text messages</h3>
|
||||
<p>If a text message is longer than 160 characters (including spaces and service name), it counts as more than one message
|
||||
part.</p>
|
||||
<p>If a text message is longer than 160 characters (including spaces), it counts as more than one message.</p>
|
||||
|
||||
<div class="bottom-gutter-3-2">
|
||||
{% call mapping_table(
|
||||
@@ -41,12 +82,12 @@ part.</p>
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% for message_length, charge in [
|
||||
('Up to 160 characters', '1 part'),
|
||||
('Up to 306 characters', '2 parts'),
|
||||
('Up to 459 characters', '3 parts'),
|
||||
('Up to 612 characters', '4 parts'),
|
||||
('Up to 765 characters', '5 parts'),
|
||||
('Up to 918 characters', '6 parts'),
|
||||
('Up to 160 characters', '1 text message'),
|
||||
('Up to 306 characters', '2 text messages'),
|
||||
('Up to 459 characters', '3 text messages'),
|
||||
('Up to 612 characters', '4 text messages'),
|
||||
('Up to 765 characters', '5 text messages'),
|
||||
('Up to 918 characters', '6 text messages'),
|
||||
] %}
|
||||
{% call row() %}
|
||||
{{ text_field(message_length) }}
|
||||
@@ -56,13 +97,6 @@ part.</p>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
<h3 class="font-body-lg" id="Personalization">Personalization</h3>
|
||||
<p>Personalization can change the length of messages. For example, if you are personalizing with a first name, Fred is
|
||||
significantly shorter than Alexander. When you are evaluating how long a message is, you need to consider variations in
|
||||
message length based on personalization.</p>
|
||||
<p>Before you send messages, Notify will let you know how many messages you are sending, the number of parts you are using,
|
||||
and the number of parts you’ll have left.</p>
|
||||
|
||||
<h3 class="font-body-lg" id="symbols">Signs and symbols</h3>
|
||||
|
||||
<p>
|
||||
@@ -152,11 +186,11 @@ and the number of parts you’ll have left.</p>
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% for message_length, charge in [
|
||||
('Up to 70 characters', '1 part'),
|
||||
('Up to 134 characters', '2 parts'),
|
||||
('Up to 201 characters', '3 parts'),
|
||||
('Up to 268 characters', '4 parts'),
|
||||
('Each additional 67 characters', '1 additional part'),
|
||||
('Up to 70 characters', '1 text message'),
|
||||
('Up to 134 characters', '2 text messages'),
|
||||
('Up to 201 characters', '3 text messages'),
|
||||
('Up to 268 characters', '4 text messages'),
|
||||
('Each additional 67 characters', '1 additional text message'),
|
||||
] %}
|
||||
{% call row() %}
|
||||
{{ text_field(message_length) }}
|
||||
@@ -166,4 +200,43 @@ and the number of parts you’ll have left.</p>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
<h3 class="font-body-lg" id="international-numbers">Sending text messages to international numbers</h3>
|
||||
<p>It might cost more to send text messages to international numbers than U.S. ones, depending on the country.</p>
|
||||
<!-- {% set smsIntRates %}
|
||||
{{ live_search(target_selector='#international-pricing .table-row', show=True, form=search_form, label='Search by country name or code') }}
|
||||
|
||||
<div id="international-pricing" class="bottom-gutter-3-2">
|
||||
{% call mapping_table(
|
||||
caption='International text message rates',
|
||||
field_headings=['Country code', 'Country', 'Cost multiplier'],
|
||||
field_headings_visible=True,
|
||||
caption_visible=False
|
||||
) %}
|
||||
{% for cc, names, billable_units in international_sms_rates %}
|
||||
{% call row() %}
|
||||
{{ text_field('+' + cc) }}
|
||||
{% call field() %}
|
||||
{% for name in names %}
|
||||
{{ name }}<br>
|
||||
{% endfor %}
|
||||
{% endcall %}
|
||||
{{ text_field('{} ×'.format(billable_units)|safe) }}
|
||||
{% endcall %}
|
||||
{% endfor %}
|
||||
|
||||
{% call row() %}
|
||||
{{ text_field('+882') }}
|
||||
{% call field() %}Worldwide{% endcall %}
|
||||
{{ text_field('Not supported') }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ usaDetails({
|
||||
"summaryText": "International text message rates",
|
||||
"id": "sms-pricing-details",
|
||||
"html": smsIntRates
|
||||
}) }} -->
|
||||
|
||||
{% endblock %}
|
||||
@@ -22,22 +22,18 @@
|
||||
<p>This roadmap is only a guide. It does not cover everything we do, and some things may change.</p>
|
||||
<p>You can <a class="usa-link" href="{{url_for('.support')}}">contact us</a> if you have any questions about the roadmap or suggestions for new features.</p>
|
||||
|
||||
<h2 id="things-we-are-working-on">What we’re working on</h2>
|
||||
<h2 class="font-body-lg" id="things-we-are-working-on">What we’re working on</h2>
|
||||
|
||||
<h3 id="now">Now</h3>
|
||||
<h3 class="font-body-lg" id="now">Now</h3>
|
||||
|
||||
<p>We are investigating the Notify concept, building on the notifications tool pioneered by the UK.</p>
|
||||
<p>To do this, we are convening a pilot with a small set of partners.</p>
|
||||
<p>Goals during this stage:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li>Achieve compliance to begin piloting, such as ATO and privacy standards.
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
||||
use xlink:href="/assets/img/sprite.svg#check"></use>
|
||||
</svg>
|
||||
</li>
|
||||
<li>Demonstrate that a government-run notifications tool provides a unique value.</li>
|
||||
<li>Gather data from the pilot to improve the product.</li>
|
||||
<li>Achieve compliance to begin piloting, such as ATO and privacy standards</li>
|
||||
<li>Demonstrate that a government-run notifications tool provides a unique value</li>
|
||||
<li>Gather data from the pilot to improve the product</li>
|
||||
</ul>
|
||||
|
||||
<p>Features prioritized during this stage:</p>
|
||||
@@ -50,38 +46,53 @@
|
||||
<li>Message send/failure analytics</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="next">Next</h3>
|
||||
<h3 class="font-body-lg" id="next">Next</h3>
|
||||
|
||||
<p>If the pilot is successful, we hope to recruit additional partners to improve outcomes for low-income individuals and families.</p>
|
||||
<p>If the pilot is successful, we hope to recruit additional high-impact partners to improve outcomes for low-income individuals and families.</p>
|
||||
|
||||
<p>Goals during this stage:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
<li>Complement Notify with practical guidance and support services.</li>
|
||||
<li>Iterate on existing features and implement new features based on what we've learned so far.</li>
|
||||
<li>Hone our measurement approaches to better quantify impact.</li>
|
||||
<li>Complement Notify with practical guidance and support services</li>
|
||||
<li>Iterate on existing features and implement new features based on what we've learned so far</li>
|
||||
<li>Hone our measurement approaches to better quantify impact</li>
|
||||
</ul>
|
||||
|
||||
<p>Features prioritized during this stage:</p>
|
||||
|
||||
<ul class="list">
|
||||
<ul class="list list-bullet">
|
||||
<li>SMS sending via API integration</li>
|
||||
<li>Single-level decision two-way messaging (e.g. reply “YES” if, or “NO” if…)</li>
|
||||
<li>Self-service account creation</li>
|
||||
<li>Application status page</li>
|
||||
<li>Improved scheduled send option</li>
|
||||
<li>Scheduled send option</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="later">Later</h3>
|
||||
<h3 class="font-body-lg" id="later">Later</h3>
|
||||
|
||||
<p>In the future, we may decide to expand beyond SMS, or to offer the service government-wide.</p>
|
||||
|
||||
<p>Features to be considered during this stage:</p>
|
||||
|
||||
<ul class="list">
|
||||
<ul class="list list-bullet">
|
||||
|
||||
<li>Two-way messaging</li>
|
||||
<li>Email sending via UI and API</li>
|
||||
<li>Multiple-level decision two-way messaging (greater than one layer decision-tree)</li>
|
||||
<li>Open-text reply two-way messaging (rather than reply yes or no, 1 or 2, etc.)</li>
|
||||
<li>Multilingual interface and content library options</li>
|
||||
<li>Recurring scheduled send (such as “Send each Monday for 3 weeks”)</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="things-we-have-done">Things we’ve done</h2>
|
||||
|
||||
<p>Recently we have:</p>
|
||||
|
||||
<ul class="list list-bullet">
|
||||
|
||||
<li></li>
|
||||
<li></li>
|
||||
|
||||
</ul> -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -15,59 +15,29 @@
|
||||
<li>manage risks around information</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="infrastructure">Infrastructure</h2>
|
||||
|
||||
<p>Notify.gov is comprised of two applications both running on <a href="https://www.cloud.gov">cloud.gov</a>:</p>
|
||||
<ul class="list">
|
||||
<li>Admin, a Flask website running on the python_buildpack which hosts agency user-facing UI</li>
|
||||
<li>API, a Flask application running on the python_buildpack hosting the Notify API</li>
|
||||
<h2 class="font-body-lg" id="data">Data</h2>
|
||||
<p>On Notify, data is encrypted:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>when it passes through the service</li>
|
||||
<li>when it’s stored on the service</li>
|
||||
</ul>
|
||||
<p>Any recipient data you upload is only held for seven days.</p>
|
||||
<!-- <p>If you <a class="usa-link" href="/features/email#send-files">send a file by email</a>, the file will be available for the recipient to download for 18 months.</p> -->
|
||||
|
||||
<p>Notify.gov utilizes several cloud.gov-provided services through Amazon Web Services (AWS):</p>
|
||||
<ul class="list">
|
||||
<li>AWS S3 buckets for temporary file storage</li>
|
||||
<li>AWS Elasticache (Redis) for caching data and enqueueing background tasks</li>
|
||||
<li>AWS RDS (PostgreSQL) for system data storage</li>
|
||||
</ul>
|
||||
|
||||
<p>Notify.gov also provisions and uses two AWS services via a <a href="https://github.com/GSA/datagov-ssb">supplemental service broker</a>:</p>
|
||||
<ul class="list">
|
||||
<li><a href="https://aws.amazon.com/sns/">AWS SNS</a> for sending SMS messages</li>
|
||||
<li><a href="https://aws.amazon.com/ses/">AWS SES</a> for sending email messages (note: email function is currently unavailable to users for pilot)</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="current-security-review">Current security review</h2>
|
||||
<p>Currently, Notify.gov operates under a GSA <a href="https://www.gsa.gov/system/files/Lightweight-Security-Authorization-Process-%28LATO%29%20%5BCIO-IT-Security-14-68-Rev-7%5D%2009-17-2021docx%20%281%29.pdf">Lightweight Authority to Operate (LATO)</a>. This federal security authorization
|
||||
process leverages security controls provided by National Institute of Standards and Technology (NIST). The process is
|
||||
focused on operational security from both a functional and assurance perspective.</p>
|
||||
<p>We are pursuing a full Authority to Operate (ATO)</p>
|
||||
|
||||
<h2 id="data">Data</h2>
|
||||
<p>To send a message, agencies upload a spreadsheet of phone numbers and other necessary data from their existing data
|
||||
management system. On Notify.gov, data is encrypted when it passes through the service and when it’s stored on the
|
||||
service.</p>
|
||||
<p>Notify.gov is not a system of record and as a result does not have a SORN. Agencies are responsible for managing their
|
||||
data outside of Notify.gov.</p>
|
||||
<ul class="list">
|
||||
<li>Notify stores data within the cloud.gov-managed PostgreSQL database and <a href="https://cloud.gov/docs/services/s3/#encryption">S3 buckets</a>. The entire database is <a href="https://cloud.gov/docs/services/relational-database/#encryption">encrypted at
|
||||
the disk level</a>.</li>
|
||||
<li>Additionally, data is encrypted at the field level when it is stored in the database, using OpenSSL 3.0.8 via the Python
|
||||
cryptography library. The implementation is available <a href="https://github.com/GSA/notifications-utils/blob/main/notifications_utils/clients/encryption/encryption_client.py">on GitHub</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="data-retention">Data retention</h2>
|
||||
<p>Any recipient data uploaded is only held for seven days; all personally identifiable information (PII) is deleted for
|
||||
successful messages, so data is retained only for unsuccessful messages.
|
||||
</p>
|
||||
|
||||
<h2 id="technical-security">Technical security</h2>
|
||||
<h2 class="font-body-lg" id="technical-security">Technical security</h2>
|
||||
<!-- <p>Other technical security controls on Notify include:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>compliance with National Cyber Security Centre (NCSC) Cloud Security Principles</li>
|
||||
<li>protective monitoring to record activity, and raise alerts about any suspicious activity</li>
|
||||
<li>using JSON Web Tokens, to avoid sending API keys when your service talks to Notify</li>
|
||||
</ul> -->
|
||||
|
||||
<h3 class="font-body-lg">Protect sensitive information</h3>
|
||||
<p>Some messages include sensitive information like security codes or password reset links.</p>
|
||||
<p>If you’re sending a message with sensitive information, you can choose to hide those details on the Notify dashboard once the message has been sent. This means that only the message recipient will be able to see that information.</p>
|
||||
<img src="{{ asset_url('images/product/security-review-message.png') }}"
|
||||
alt="Screenshot of a test message in review with the link to 'hide personalization after sending' circled.">
|
||||
|
||||
<h2 class="font-body-lg" id="user-permissions-signing-in">User permissions and signing in</h2>
|
||||
<p>You can set different user permissions in Notify. This lets you control who in your team has access to certain parts of the service.</p>
|
||||
<h3 class="font-body-lg">Two-factor authentication</h3>
|
||||
<p>To sign in to Notify, you’ll need to enter:</p>
|
||||
<ul class="list list-bullet">
|
||||
@@ -76,16 +46,32 @@
|
||||
</ul>
|
||||
<p>If signing in with a text message is a problem for your team, <a class="usa-link" href="{{ url_for('main.support') }}">contact us</a> to find out about using an email link instead.</p>
|
||||
|
||||
<h3>User permissions and signing in</h3>
|
||||
<p>You can set different user permissions in Notify. This lets you control who in your team has access to certain parts of
|
||||
the service.</p>
|
||||
|
||||
<h3>Multi-factor authentication (MFA)</h3>
|
||||
<p>Notify.gov uses <a href="https://login.gov/">Login.gov</a> to authenticate users.</p>
|
||||
<ul class="list">
|
||||
<li>Your email address and password</li>
|
||||
<li>A code from an authenticator app or sent by Login.gov in the form of a text message</li>
|
||||
<!-- <h2 class="font-body-lg" id="information-risk-management">Information risk management</h2>
|
||||
<p>Our approach to information risk management follows NCSC guidance. It assesses:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>how Notify is built</li>
|
||||
<li>the infrastructure Notify is built upon</li>
|
||||
<li>support for the Notify service</li>
|
||||
</ul>
|
||||
<p>If signing in with a text message is a problem for your team, <a href="https://beta.notify.gov/support">contact us</a> to find out about using an email link instead.</p>
|
||||
<p>This approach also applies to the service providers Notify uses to send messages.</p> -->
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="how-we-manage-risk">How we manage risks on Notify</h2>
|
||||
<p>Things we do to manage risks on Notify include:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>formal risk assessments based on <a class="usa-link" href="http://www.iso.org/iso/catalogue_detail?csnumber=56742">ISO 27005:2011</a> and National Cyber Security Centre guidance</li>
|
||||
<li><a class="usa-link" href="https://www.ncsc.gov.uk/information/check-penetration-testing">CHECK</a>-based testing, both annually and when any major changes are made to Notify</li>
|
||||
<li>residual risk statement preparation and active management of the risk treatment plan</li>
|
||||
<li>regular updates to the Privacy Impact Assessment</li>
|
||||
<li>security impact assessments</li>
|
||||
</ul> -->
|
||||
|
||||
<!-- <h2 class="font-body-lg" id="cabinet-office-approval">Cabinet Office approval</h2>
|
||||
<p>Notify has been assessed and approved by the Cabinet Office Senior Information Risk Officer (SIRO). The SIRO checks this approval once a year.</p>
|
||||
<p>Notify also has approval from the Office of the Government’s SIRO to host data within the EEA.</p>
|
||||
|
||||
<h2 class="font-body-lg" id="classifications-and-security-vetting">Classifications and security vetting</h2>
|
||||
<p>You can use Notify to send messages classified as ‘OFFICIAL’ or ‘OFFICIAL-SENSITIVE’ under the <a class="usa-link" href="https://www.gov.uk/government/publications/government-security-classifications">Government Security Classifications</a> policy.</p>
|
||||
<p>Notify does not process data classified as ‘SECRET’ or ‘TOP SECRET’.</p>
|
||||
<p>The Notify team has Security Check (SC) level clearance from <a class="usa-link" href="https://www.gov.uk/government/organizations/united-kingdom-security-vetting">United Kingdom Security Vetting</a> (UKSV).</p> -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -12,20 +12,9 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% if login_gov_enabled %}
|
||||
<div class="grid-row">
|
||||
<div id="countdown-container" class="usa-alert usa-alert--warning width-full margin-bottom-4">
|
||||
<div class="usa-alert__body">
|
||||
<h4 class="usa-alert__heading">Login.gov is required by April 16, 2024</h4>
|
||||
<p class="usa-alert__text">
|
||||
You have <span id="countdown"></span> left to use Login.gov to sign in
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="grid-row margin-bottom-4">
|
||||
<div class="tablet:grid-col-5">
|
||||
<div class="tablet:grid-col-12">
|
||||
|
||||
{% if again %}
|
||||
<h1 class="font-body-2xl margin-bottom-3">You need to sign in again</h1>
|
||||
{% if other_device %}
|
||||
@@ -40,9 +29,10 @@
|
||||
{% else %}
|
||||
<h1 class="font-body-2xl margin-bottom-3">Sign in</h1>
|
||||
{% if login_gov_enabled %}
|
||||
<p>You can access your account by signing in with one of the options below:</p>
|
||||
<a class="usa-link usa-button usa-button--outline" href="{{ initial_signin_url }}">Sign in with Login.gov</a>
|
||||
<p class="margin-y-3"><strong>Or:</strong></p>
|
||||
<p>
|
||||
Test login.gov authentication:
|
||||
<a class="usa-link" href="{{ initial_signin_url }}">Login.gov</a>.
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -52,24 +42,6 @@
|
||||
{{ page_footer("Continue", secondary_link=password_reset_url, secondary_link_text="Forgot your password?") }}
|
||||
{% endcall %}
|
||||
</div>
|
||||
{% if login_gov_enabled %}
|
||||
<div class="tablet:grid-col-6 tablet:grid-offset-1 margin-top-2 padding-y-2 padding-x-4 bg-base-lightest">
|
||||
<h2 class="font-body-lg">Notify.gov is changing the sign-in experience to Login.gov effective<br>April 16, 2024</h2>
|
||||
<p>Why are we doing this?</p>
|
||||
<ul class="usa-list">
|
||||
<li><strong>Enhanced security:</strong> Login.gov is really secure and trustworthy</li>
|
||||
<li><strong>One single source for signing in:</strong> You can use Login.gov for other services within the federal government</li>
|
||||
<li><strong>2FA flexibility:</strong> Login.gov supports multiple methods for users to verify their identity.</li>
|
||||
</ul>
|
||||
<p>What do I need to do?</p>
|
||||
<ul class="usa-list">
|
||||
<li>If you have a Login.gov account, start using it to sign in to Notify today.</li>
|
||||
<li>If you don’t have a Login.gov account, you must create one by April 16, 2024 to continue to access Notify.</li>
|
||||
</ul>
|
||||
<div class="border-bottom border-base-lighter margin-y-4"></div>
|
||||
<a class="usa-link usa-button margin-bottom-3" href="{{ initial_signin_url }}">Create Login.gov account</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -8,20 +8,43 @@
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="usa-prose margin-bottom-10">
|
||||
|
||||
<h1 class="font-body-2xl margin-bottom-3">Contact us</h1>
|
||||
<p>Notify is designed to be easy to use.</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>For information on personalization and data preparation, see <a href={{ url_for("main.guidance_index") }}>Guidance</a>.</li>
|
||||
<li>For help interpreting delivery reports, see <a href={{ url_for("main.message_status") }}>Delivery Status</a>.</li>
|
||||
<li>For details on pricing and what counts as a message part, see <a href={{ url_for("main.pricing") }}>Pricing</a>.</li>
|
||||
</ul>
|
||||
|
||||
<p>If you have other questions, we are available at <a class="usa-link" href="mailto:notify-support@gsa.gov">notify-support@gsa.gov</a>.</p>
|
||||
|
||||
<p>We are available at <a class="usa-link" href="mailto:notify-support@gsa.gov">notify-support@gsa.gov</a>.</p>
|
||||
|
||||
<p>You can expect a response within one business day.</p>
|
||||
|
||||
<!-- <p>Notify.gov provides 24-hour online support.</p> -->
|
||||
|
||||
</div>
|
||||
<!-- {% call form_wrapper() %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{{ form.support_type }}
|
||||
{% else %}
|
||||
<p>
|
||||
What do you need help with?
|
||||
</p>
|
||||
{{ form.who }}
|
||||
{% endif %}
|
||||
{{ page_footer('Continue') }}
|
||||
{% endcall %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="grid-col-8">
|
||||
<h2 class="font-body-lg">Office hours</h2>
|
||||
<p>Our office hours are 9:30am to 5:30pm, Monday to Friday.</p>
|
||||
<p>When you report a problem in office hours, we’ll aim to read it within 30 minutes and reply within one working day.</p>
|
||||
|
||||
<h2 class="font-body-lg">Out-of-hours</h2>
|
||||
<p>Outside office hours, response times depend on whether you’re reporting an emergency.</p>
|
||||
<p>If it’s an emergency, we’ll reply within 30 minutes and update you every hour until the problem’s fixed.</p>
|
||||
<p>If your problem is not an emergency, we’ll reply within one working day.</p>
|
||||
<p>A problem is only classed as an emergency if you have a live service and:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>nobody on your team can sign in</li>
|
||||
<li>a ‘technical difficulties’ error appears when you try to upload a file</li>
|
||||
<li>a 500 response code appears when you try to send messages using the API</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -7,33 +7,37 @@
|
||||
{% block content_column_content %}
|
||||
|
||||
<h1 class="font-body-2xl margin-bottom-3">Trial mode</h1>
|
||||
|
||||
<p>When you add a new service it will start in trial mode. This lets you try out Notify.gov, with a few restrictions.</p>
|
||||
|
||||
<h2 class="font-body-lg">Limits while in trial mode</h2>
|
||||
<p>While your service is in trial mode you can only:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>send 50 text message parts per day</li>
|
||||
<li>send messages to yourself and other people in your team</li>
|
||||
</ul>
|
||||
<p>Each text message is made up of one or more parts.</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>Generally, 160 characters is one part (and one text message).</li>
|
||||
<li>A text message of 160-306 characters is two parts.</li>
|
||||
</ul>
|
||||
<p>For more information on how message parts are calculated, see
|
||||
<a href="/using-notify/pricing">Pricing</a>.</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>send 50 text messages per day</li>
|
||||
<li>send messages to yourself and other people in your team</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="font-body-lg">Before going Live</h2>
|
||||
<p>Before you request to make your service live so you can send messages to clients:</p>
|
||||
{% if current_service and current_service.trial_mode %}
|
||||
<p>
|
||||
To remove these restrictions, you can <a class="usa-link" href="{{ url_for('.support') }}">request to go live</a>.</p>
|
||||
{% else %}
|
||||
<p>
|
||||
To remove these restrictions:
|
||||
</p>
|
||||
<ol class="list list-number">
|
||||
<li><a class="usa-link" href="{{ url_for('.sign_in') }}">Sign in to Notify</a>.</li>
|
||||
<li>Go to the <b class="bold">Settings</b> page.</li>
|
||||
<li>Select <b class="bold">Request to go live</b>.</li>
|
||||
</ol>
|
||||
{% endif %}
|
||||
<p>
|
||||
When we receive your request we’ll get back to you within one working day.
|
||||
</p>
|
||||
<h2 class="font-body-lg" id="before-you-request-to-go-live">Before you request to go live</h2>
|
||||
<p>You must:</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>Add examples of the messages you want to send into Notify.</li>
|
||||
<li>Update your settings so you’re ready to send and receive messages.</li>
|
||||
<li>Review your Terms of Use in the Memorandum of Understanding.</li>
|
||||
<li>add examples of the messages you want to send</li>
|
||||
<li>update your settings so you’re ready to send and receive messages</li>
|
||||
<li>accept our terms of use</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="font-body-lg">Going Live</h3>
|
||||
|
||||
<p>To remove the restrictions of Trial Mode and begin sending messages to clients complete the <a href="https://airtable.com/appe4n7jYOALPLcyU/shrIPWnLTw9U1fclL">Live Campaign Form</a>.</p>
|
||||
<p>We'll get back to you within one working day. </p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -19,25 +19,25 @@
|
||||
</div>
|
||||
<div id='pill-selected-item'>
|
||||
<div class='grid-row'>
|
||||
|
||||
|
||||
<div class='grid-col-12'>
|
||||
<h2 class="heading-small margin-bottom-1">Daily messages across all services</h2>
|
||||
<p class="margin-0">You have sent {{ global_message_limit - daily_global_messages_remaining }} of your {{ global_message_limit }} daily messages allowance.</p>
|
||||
<p class="margin-0"></p>You have {{ daily_global_messages_remaining }} messages remaining.</p>
|
||||
<h2 class='heading-small margin-bottom-1'>Text message parts</h2>
|
||||
<h2 class='heading-small margin-bottom-1'>Text messages</h2>
|
||||
<div class="keyline-block">
|
||||
You have sent
|
||||
{{ big_number(sms_sent, 'text message parts of your', smaller=True) }}
|
||||
{{ big_number(sms_free_allowance, 'free message parts allowance.', smaller=True) }}
|
||||
{{ big_number(sms_sent, 'messages of your', smaller=True) }}
|
||||
{{ big_number(sms_free_allowance, 'free messages allowance.', smaller=True) }}
|
||||
<br />
|
||||
You have
|
||||
{% if sms_free_allowance > 0 %}
|
||||
{{ big_number(sms_allowance_remaining, 'message parts remaining.', smaller=True) }}
|
||||
{{ big_number(sms_allowance_remaining, 'messages remaining.', smaller=True) }}
|
||||
{% endif %}
|
||||
{# {% for row in sms_breakdown %}
|
||||
{% if row.charged_units > 0 %}
|
||||
{{ big_number(
|
||||
row.charged_units,
|
||||
row.charged_units,
|
||||
'at {:.2f} pence per message'.format(row.rate * 100),
|
||||
smaller=True
|
||||
) }}
|
||||
@@ -73,7 +73,6 @@
|
||||
</div> #}
|
||||
|
||||
{% if months %}
|
||||
<div class="table-wrapper">
|
||||
<div class="dashboard-table usage-table body-copy-table margin-top-4">
|
||||
{% call(item, row_index) list_table(
|
||||
months,
|
||||
@@ -91,7 +90,7 @@
|
||||
{{ item.month }}
|
||||
{% endcall %}
|
||||
{% for counts, template_type in [
|
||||
(item.sms_counts.0, 'parts'),
|
||||
(item.sms_counts.0, 'sms'),
|
||||
] %}
|
||||
{% call field(align='left') %}
|
||||
{{ big_number(
|
||||
@@ -127,10 +126,9 @@
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endcall %}
|
||||
|
||||
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -142,7 +140,7 @@
|
||||
</div>
|
||||
<div class="grid-col-8">
|
||||
<p class="align-with-heading-copy">
|
||||
What counts as 1 text message part?<br />
|
||||
What counts as 1 text message?<br />
|
||||
See <a class="usa-link" href="{{ url_for('.pricing') }}">pricing</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -9,16 +9,16 @@
|
||||
{% include "service_navigation.html" %}
|
||||
<div class="grid-row margin-top-5">
|
||||
{% if help %}
|
||||
<div class="tablet:grid-col-3">
|
||||
<div class="grid-col-3">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-3">
|
||||
<div class="grid-col-3">
|
||||
{% endif %}
|
||||
{% include "main_nav.html" %}
|
||||
</div>
|
||||
{% if help %}
|
||||
<div class="grid-col-8">
|
||||
{% else %}
|
||||
<div class="tablet:grid-col-9 tablet:padding-left-4">
|
||||
<div class="grid-col-9 padding-left-4">
|
||||
{% endif %}
|
||||
{% block beforeContent %}
|
||||
{% block backLink %}{% endblock %}
|
||||
|
||||
@@ -71,7 +71,6 @@ def generate_notifications_csv(**kwargs):
|
||||
if "page" not in kwargs:
|
||||
kwargs["page"] = 1
|
||||
|
||||
# This generates the "batch" csv report
|
||||
if kwargs.get("job_id"):
|
||||
original_file_contents = s3download(kwargs["service_id"], kwargs["job_id"])
|
||||
original_upload = RecipientCSV(
|
||||
@@ -79,26 +78,28 @@ def generate_notifications_csv(**kwargs):
|
||||
template=get_sample_template(kwargs["template_type"]),
|
||||
)
|
||||
original_column_headers = original_upload.column_headers
|
||||
fieldnames = [
|
||||
"Phone Number",
|
||||
"Template",
|
||||
"Sent by",
|
||||
"Batch File",
|
||||
"Carrier Response",
|
||||
"Status",
|
||||
"Time",
|
||||
]
|
||||
for header in original_column_headers:
|
||||
if header.lower() != "phone number":
|
||||
fieldnames.append(header)
|
||||
|
||||
fieldnames = (
|
||||
["Row number"]
|
||||
+ original_column_headers
|
||||
+ [
|
||||
"Template",
|
||||
"Type",
|
||||
"Sent by",
|
||||
"Job",
|
||||
"Carrier",
|
||||
"Carrier Response",
|
||||
"Status",
|
||||
"Time",
|
||||
]
|
||||
)
|
||||
else:
|
||||
# This generates the "full" csv report
|
||||
fieldnames = [
|
||||
"Phone Number",
|
||||
"Recipient",
|
||||
"Template",
|
||||
"Type",
|
||||
"Sent by",
|
||||
"Batch File",
|
||||
"Job",
|
||||
"Carrier",
|
||||
"Carrier Response",
|
||||
"Status",
|
||||
"Time",
|
||||
@@ -115,30 +116,35 @@ def generate_notifications_csv(**kwargs):
|
||||
notification["created_at"]
|
||||
)
|
||||
|
||||
current_app.logger.info(f"\n\n{notification}")
|
||||
if kwargs.get("job_id"):
|
||||
values = [
|
||||
notification["recipient"],
|
||||
notification["template_name"],
|
||||
notification["created_by_name"],
|
||||
notification["job_name"],
|
||||
notification["provider_response"],
|
||||
notification["status"],
|
||||
preferred_tz_created_at,
|
||||
]
|
||||
for header in original_column_headers:
|
||||
if header.lower() != "phone number":
|
||||
values.append(
|
||||
original_upload[notification["row_number"] - 1]
|
||||
.get(header)
|
||||
.data
|
||||
)
|
||||
|
||||
values = (
|
||||
[
|
||||
notification["row_number"],
|
||||
]
|
||||
+ [
|
||||
original_upload[notification["row_number"] - 1].get(header).data
|
||||
for header in original_column_headers
|
||||
]
|
||||
+ [
|
||||
notification["template_name"],
|
||||
notification["template_type"],
|
||||
notification["created_by_name"],
|
||||
notification["job_name"],
|
||||
notification["carrier"],
|
||||
notification["provider_response"],
|
||||
notification["status"],
|
||||
preferred_tz_created_at,
|
||||
]
|
||||
)
|
||||
else:
|
||||
values = [
|
||||
notification["recipient"],
|
||||
notification["template_name"],
|
||||
notification["template_type"],
|
||||
notification["created_by_name"] or "",
|
||||
notification["job_name"] or "",
|
||||
notification["carrier"],
|
||||
notification["provider_response"],
|
||||
notification["status"],
|
||||
preferred_tz_created_at,
|
||||
@@ -164,7 +170,7 @@ def convert_report_date_to_preferred_timezone(db_date_str_in_utc):
|
||||
utc_date_obj = utc_date_obj.astimezone(pytz.utc)
|
||||
preferred_timezone = pytz.timezone(get_user_preferred_timezone())
|
||||
preferred_date_obj = utc_date_obj.astimezone(preferred_timezone)
|
||||
preferred_tz_created_at = preferred_date_obj.strftime("%Y-%m-%d %I:%M:%S %p")
|
||||
preferred_tz_created_at = preferred_date_obj.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
return f"{preferred_tz_created_at} {get_user_preferred_timezone()}"
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .templates import Environment # noqa
|
||||
@@ -1,30 +0,0 @@
|
||||
from flask.templating import Environment as FlaskEnvironment
|
||||
from jinja2 import select_autoescape
|
||||
|
||||
from app.utils.govuk_frontend_jinja.templates import Environment as NunjucksEnvironment
|
||||
from app.utils.govuk_frontend_jinja.templates import (
|
||||
NunjucksExtension,
|
||||
NunjucksUndefined,
|
||||
)
|
||||
|
||||
|
||||
class Environment(NunjucksEnvironment, FlaskEnvironment):
|
||||
pass
|
||||
|
||||
|
||||
def init_govuk_frontend(app):
|
||||
"""Use the govuk_frontend_jinja Jinja environment in a Flask app
|
||||
|
||||
>>> from flask import Flask
|
||||
>>> app = Flask("cheeseshop_service")
|
||||
>>> init_govuk_frontend(app)
|
||||
"""
|
||||
app.jinja_environment = Environment
|
||||
app.select_jinja_autoescape = select_autoescape(
|
||||
("html", "htm", "xml", "xhtml", "njk")
|
||||
)
|
||||
jinja_options = app.jinja_options.copy()
|
||||
jinja_options["extensions"].append(NunjucksExtension)
|
||||
jinja_options["undefined"] = NunjucksUndefined
|
||||
app.jinja_options = jinja_options
|
||||
return app
|
||||
@@ -1,292 +0,0 @@
|
||||
import builtins
|
||||
import os.path as path
|
||||
import re
|
||||
from collections.abc import Sized
|
||||
|
||||
import jinja2
|
||||
import jinja2.ext
|
||||
from jinja2.lexer import Token
|
||||
from markupsafe import Markup
|
||||
|
||||
|
||||
def njk_to_j2(template):
|
||||
# Some component templates (such as radios) use `items` as the key of
|
||||
# an object element. However `items` is also the name of a dictionary
|
||||
# method in Python, and Jinja2 will prefer to return this attribute
|
||||
# over the dict item. Handle specially.
|
||||
template = re.sub(r"\.items\b", ".items__njk", template)
|
||||
|
||||
# Some component templates (such as radios) append the loop index to a
|
||||
# string. As the loop index is an integer this causes a TypeError in
|
||||
# Python. Jinja2 has an operator `~` for string concatenation that
|
||||
# converts integers to strings.
|
||||
template = template.replace("+ loop.index", "~ loop.index")
|
||||
|
||||
# The Character Count component in version 3 concatenates the word count
|
||||
# with the hint text. As the word count is an integer this causes a
|
||||
# TypeError in Python. Jinja2 has an operator `~` for string
|
||||
# concatenation that converts integers to strings.
|
||||
template = template.replace(
|
||||
"+ (params.maxlength or params.maxwords) +",
|
||||
"~ (params.maxlength or params.maxwords) ~",
|
||||
)
|
||||
|
||||
# Nunjucks uses elseif, Jinja uses elif
|
||||
template = template.replace("elseif", "elif")
|
||||
|
||||
# Some component templates (such as input) call macros with params as
|
||||
# an object which has unqoted keys. This causes Jinja to silently
|
||||
# ignore the values.
|
||||
template = re.sub(
|
||||
r"""^([ ]*)([^ '"#\r\n:]+?)\s*:""", r"\1'\2':", template, flags=re.M
|
||||
)
|
||||
|
||||
# govukFieldset can accept a call block argument, however the Jinja
|
||||
# compiler does not detect this as the macro body is included from
|
||||
# the template file. A workaround is to patch the declaration of the
|
||||
# macro to include an explicit caller argument.
|
||||
template = template.replace(
|
||||
"macro govukFieldset(params)", "macro govukFieldset(params, caller=none)"
|
||||
)
|
||||
|
||||
# Many components feature an attributes field, which is supposed to be
|
||||
# a dictionary. In the template for these components, the keys and values
|
||||
# are iterated. In Python, the default iterator for a dict is .keys(), but
|
||||
# we want .items().
|
||||
# This only works because our undefined implements .items()
|
||||
# We've tested this explicitly with: govukInput, govukCheckbox, govukTable,
|
||||
# govukSummaryList
|
||||
template = re.sub(
|
||||
r"for attribute, value in (params|item|cell|action).attributes",
|
||||
r"for attribute, value in \1.attributes.items()",
|
||||
template,
|
||||
flags=re.M,
|
||||
)
|
||||
|
||||
# Some templates try to set a variable in an outer block, which is not
|
||||
# supported in Jinja. We create a namespace in those templates to get
|
||||
# around this.
|
||||
template = re.sub(
|
||||
r"""^([ ]*)({% set describedBy =( params.*describedBy if params.*describedBy else)? "" %})""",
|
||||
r"\1{%- set nonlocal = namespace() -%}\n\1\2",
|
||||
template,
|
||||
flags=re.M,
|
||||
)
|
||||
# Change any references to describedBy to be nonlocal.describedBy,
|
||||
# unless describedBy is a dictionary key (i.e. quoted or dotted).
|
||||
template = re.sub(r"""(?<!['".])describedBy""", r"nonlocal.describedBy", template)
|
||||
# govukSummaryList
|
||||
template = re.sub(
|
||||
r"""^([ ]*)({% set anyRowHasActions = false %})""",
|
||||
r"\1{%- set nonlocal = namespace() -%}\n\1\2",
|
||||
template,
|
||||
flags=re.M,
|
||||
)
|
||||
template = re.sub(
|
||||
r"""(?<!['".])anyRowHasActions""", r"nonlocal.anyRowHasActions", template
|
||||
)
|
||||
# govukRadios and govukCheckboxes
|
||||
# Since both of these templates set describedBy before isConditional, we can use
|
||||
# the existing nonlocal.
|
||||
template = re.sub(
|
||||
r"""(?<!['".])isConditional""", r"nonlocal.isConditional", template
|
||||
)
|
||||
|
||||
# Issue#16: some component templates test the length of an array by trying
|
||||
# to get an attribute `.length`. We need to handle this specially because
|
||||
# .length isn't a thing in python
|
||||
template = re.sub(r"\.length\b", ".length__njk", template)
|
||||
|
||||
# see `indent_njk`
|
||||
template = re.sub(re.escape("| indent") + r"\b", "| indent_njk", template)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def indent_njk(s, width=4, first=False, blank=False, indentfirst=None):
|
||||
"""Return a copy of the string with each line indented by 4 spaces."""
|
||||
|
||||
# Copied from
|
||||
# https://github.com/pallets/jinja/blob/a2f5e2c7972c4d5148c1c75c724e24950d8605bc/jinja2/filters.py#L536-L580
|
||||
# to include an unreleased fix for https://github.com/pallets/jinja/pull/826
|
||||
# which causes the file upload component to escape HTML markup.
|
||||
# TODO: Remove once jinja2 2.11 is released and in use.
|
||||
|
||||
if indentfirst is not None:
|
||||
first = indentfirst
|
||||
|
||||
indention = " " * width
|
||||
newline = "\n"
|
||||
|
||||
if isinstance(s, Markup):
|
||||
indention = Markup(indention)
|
||||
newline = Markup(newline)
|
||||
|
||||
s += newline # this quirk is necessary for splitlines method
|
||||
|
||||
if blank:
|
||||
rv = (newline + indention).join(s.splitlines())
|
||||
else:
|
||||
lines = s.splitlines()
|
||||
rv = lines.pop(0)
|
||||
|
||||
if lines:
|
||||
rv += newline + newline.join(
|
||||
indention + line if line else line for line in lines
|
||||
)
|
||||
|
||||
if first:
|
||||
rv = indention + rv
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
class NunjucksExtension(jinja2.ext.Extension):
|
||||
def filter_stream(self, stream):
|
||||
if stream.filename and stream.filename.endswith(".njk"):
|
||||
return self.filter_njk_stream(stream)
|
||||
else:
|
||||
return stream
|
||||
|
||||
def filter_njk_stream(self, stream):
|
||||
for token in stream:
|
||||
# patch strict equality operator `===`
|
||||
if token.test("eq:==") and stream.current.test("assign:="):
|
||||
yield Token(token.lineno, "name", "is")
|
||||
yield Token(token.lineno, "name", "sameas")
|
||||
stream.skip(1)
|
||||
else:
|
||||
yield token
|
||||
|
||||
def preprocess(self, source, name, filename=None):
|
||||
if filename and filename.endswith(".njk"):
|
||||
return njk_to_j2(source)
|
||||
else:
|
||||
return source
|
||||
|
||||
|
||||
class NunjucksUndefined(jinja2.runtime.Undefined):
|
||||
__slots__ = ()
|
||||
|
||||
# copied from https://github.com/pallets/jinja/commit/19133d40593ced72eb28e230588abcc70d8b9f82
|
||||
def __getattr__(self, _):
|
||||
"""Make undefined that is chainable, where both
|
||||
__getattr__ and __getitem__ return itself rather than
|
||||
raising an :exc:`UndefinedError`:
|
||||
>>> foo = ChainableUndefined(name='foo')
|
||||
>>> str(foo.bar['baz'])
|
||||
''
|
||||
>>> foo.bar['baz'] + 42
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
jinja2.exceptions.UndefinedError: 'foo' is undefined
|
||||
"""
|
||||
return self
|
||||
|
||||
__getitem__ = __getattr__
|
||||
|
||||
# Allow treating undefined as an (empty) dictionary.
|
||||
# This works because Undefined is an iterable.
|
||||
def items(self):
|
||||
return self
|
||||
|
||||
# Allow escaping with Markup. This is required when
|
||||
# autoescape is enabled. Debugging this issue was
|
||||
# annoying; the error messages were not clear as to
|
||||
# the cause of the issue (see upstream pull request
|
||||
# for info https://github.com/pallets/jinja/pull/1047)
|
||||
def __html__(self):
|
||||
return str(self)
|
||||
|
||||
# attempt to behave a bit like js's `undefined` when concatenation is attempted
|
||||
def __add__(self, other):
|
||||
if isinstance(other, str):
|
||||
return "undefined" + other
|
||||
return super().__add__(other)
|
||||
|
||||
def __radd__(self, other):
|
||||
if isinstance(other, str):
|
||||
return other + "undefined"
|
||||
return super().__radd__(other)
|
||||
|
||||
|
||||
class NunjucksCodeGenerator(jinja2.compiler.CodeGenerator):
|
||||
def visit_CondExpr(self, node, frame):
|
||||
if not (self.filename or "").endswith(".njk"):
|
||||
return super().visit_CondExpr(node, frame)
|
||||
|
||||
# else our replacement, which is based on that in
|
||||
# https://github.com/pallets/jinja/blob/c4c4088945a2c12535f539be7f5453b9ca94666c/jinja2/compiler.py#L1613
|
||||
def write_expr2():
|
||||
if node.expr2 is not None:
|
||||
return self.visit(node.expr2, frame)
|
||||
# rather than complaining about a missing else
|
||||
# clause we just assume it to be the empty
|
||||
# string for nunjucks compatibility
|
||||
return self.write('""')
|
||||
|
||||
self.write("(")
|
||||
self.visit(node.expr1, frame)
|
||||
self.write(" if ")
|
||||
self.visit(node.test, frame)
|
||||
self.write(" else ")
|
||||
write_expr2()
|
||||
self.write(")")
|
||||
|
||||
|
||||
_njk_signature = "__njk"
|
||||
_builtin_function_or_method_type = type({}.keys)
|
||||
|
||||
|
||||
class Environment(jinja2.Environment):
|
||||
code_generator_class = NunjucksCodeGenerator
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("extensions", [NunjucksExtension])
|
||||
kwargs.setdefault("undefined", NunjucksUndefined)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.filters["indent_njk"] = indent_njk
|
||||
|
||||
def join_path(self, template, parent):
|
||||
"""Enable the use of relative paths in template import statements"""
|
||||
if template.startswith(("./", "../")):
|
||||
return path.normpath(path.join(path.dirname(parent), template))
|
||||
else:
|
||||
return template
|
||||
|
||||
def _handle_njk(method_name):
|
||||
def inner(self, obj, argument):
|
||||
if isinstance(argument, str) and argument.endswith(_njk_signature):
|
||||
# a njk-originated access will always be assuming a dict lookup before an attr
|
||||
final_method_name = "getitem"
|
||||
final_argument = argument[: -len(_njk_signature)]
|
||||
else:
|
||||
final_argument = argument
|
||||
final_method_name = method_name
|
||||
|
||||
# pleasantly surprised that super() works in this context
|
||||
retval = builtins.getattr(super(), final_method_name)(obj, final_argument)
|
||||
|
||||
if (
|
||||
argument == f"length{_njk_signature}"
|
||||
and isinstance(retval, jinja2.runtime.Undefined)
|
||||
and isinstance(obj, Sized)
|
||||
):
|
||||
return len(obj)
|
||||
if (
|
||||
isinstance(argument, str)
|
||||
and argument.endswith(_njk_signature)
|
||||
and isinstance(retval, _builtin_function_or_method_type)
|
||||
):
|
||||
# the lookup has probably gone looking for attributes and found a builtin method. because
|
||||
# any njk-originated lookup will have been made to prefer dict lookups over attributes, we
|
||||
# can be fairly sure there isn't a dict key matching this - so we should just call this a
|
||||
# failure.
|
||||
return self.undefined(obj=obj, name=final_argument)
|
||||
return retval
|
||||
|
||||
return inner
|
||||
|
||||
getitem = _handle_njk("getitem")
|
||||
|
||||
getattr = _handle_njk("getattr")
|
||||
@@ -1,4 +1,2 @@
|
||||
gov-collector.newrelic.com
|
||||
egress-proxy-notify-admin-demo.apps.internal
|
||||
idp.int.identitysandbox.gov
|
||||
secure.login.gov
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
gov-collector.newrelic.com
|
||||
egress-proxy-notify-admin-production.apps.internal
|
||||
idp.int.identitysandbox.gov
|
||||
secure.login.gov
|
||||
|
||||
@@ -106,104 +106,6 @@ All of the E2E tests are found in the `tests/end_to_end` folder and are
|
||||
written as `pytest` scripts using
|
||||
[Playwright's Python Framework](https://playwright.dev/python/docs/writing-tests).
|
||||
|
||||
Inside the `tests/end_to_end` folder you'll see a `conftest.py` file,
|
||||
which is similar to the one found in the root `tests` folder but is
|
||||
specific to the E2E tests.
|
||||
|
||||
There a few fixtures defined in here, but the two most important at this
|
||||
time are these:
|
||||
|
||||
- `end_to_end_context`: A Playwright context object needed to interact
|
||||
with a browser instance.
|
||||
- `authenticated_page`: A Playwright page object that has gone through
|
||||
the sign in process the E2E user is authenticated.
|
||||
|
||||
In short, if you're starting a test from scratch and testing pages that
|
||||
do not require authentication, you'll start with the
|
||||
`end_to_end_context` fixture and work from there.
|
||||
|
||||
Any test that requires you to be authenticated, you'll start with the
|
||||
`authenticated_page` object as that'll have taken care of getting
|
||||
everything set for you and logged into the site with the E2E test user.
|
||||
|
||||
|
||||
### Creating a new test file
|
||||
|
||||
If you want to create a new test file to help organize tests (a great
|
||||
idea!), it will be handy to import the Playwright `expect` and set the
|
||||
base URL/URI for yourself, like this:
|
||||
|
||||
```python
|
||||
from playwright.sync_api import expect
|
||||
|
||||
E2E_TEST_URI = os.getenv("NOTIFY_E2E_TEST_URI")
|
||||
```
|
||||
|
||||
By importing Playwright's `expect` object for tests and setting
|
||||
something like `E2E_TEST_URI` for yourself, it will make writing tests
|
||||
much easier.
|
||||
|
||||
|
||||
### Using the fixtures
|
||||
|
||||
To use the `authenticated_page` or `end_to_end_context` fixtures, you
|
||||
start by defining a test function and then passing in the fixture you
|
||||
need as a positional argument. This works the same as the other
|
||||
functions defined to create a test for pytest.
|
||||
|
||||
For example, the test for the landing page starts with this:
|
||||
|
||||
```python
|
||||
def test_landing_page(end_to_end_context):
|
||||
# Open a new page and go to the site.
|
||||
page = end_to_end_context.browser.new_page()
|
||||
page.goto(f"{E2E_TEST_URI}/")
|
||||
|
||||
# Check to make sure that we've arrived at the next page.
|
||||
page.wait_for_load_state("domcontentloaded")
|
||||
...
|
||||
```
|
||||
|
||||
Note the passing in of the `end_to_end_context` fixture - there is no
|
||||
need to import this or anything, just pass it into the function. pytest
|
||||
takes care of everything else for you.
|
||||
|
||||
The second line that defines a `page` variable is a convenience, since
|
||||
you'll be referencing the page object a lot. This is recommended to
|
||||
help keep tests readable while keeping fixture names descriptive.
|
||||
|
||||
If you need to test an authenticate page, such as the accounts page,
|
||||
use the `authenticated_page` fixture instead, like so:
|
||||
|
||||
```python
|
||||
def test_add_new_service_workflow(authenticated_page):
|
||||
page = authenticated_page
|
||||
...
|
||||
```
|
||||
|
||||
Again, it's helpful to assign the fixture to a `page` variable for easy
|
||||
reference throughout the test.
|
||||
|
||||
Lastly, if you need want access to the Playwright context object that is
|
||||
used behind the page fixtures, you can reference it directly as well
|
||||
using the `end_to_end_context` fixture:
|
||||
|
||||
```python
|
||||
def test_add_new_service_workflow(authenticated_page, end_to_end_context):
|
||||
page = authenticated_page
|
||||
|
||||
# Prepare for adding a new service later in the test.
|
||||
current_date_time = datetime.datetime.now()
|
||||
new_service_name = "E2E Federal Test Service {now} - {browser_type}".format(
|
||||
now=current_date_time.strftime("%m/%d/%Y %H:%M:%S"),
|
||||
browser_type=end_to_end_context.browser.browser_type.name,
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
In this example, I've used the context to get to the browser object
|
||||
itself to get the name of the browser for test data.
|
||||
|
||||
|
||||
## Maintaining E2E Tests with GitHub
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
# Notify Sprint Goals Log
|
||||
|
||||
## Sprint: B (1/18/24)
|
||||
2024 Sprints will be named after mythical creatures
|
||||
|
||||
| | Goals | Impact | Milestone(s) |
|
||||
|-------------|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| Engineering | Get `staging` stable and bugs fixed in order to do a prod push, finalize Login.gov implementation, complete epic to [remove phone numbers from db](https://github.com/GSA/notifications-api/issues/667), [calculate pre-send message parts](https://github.com/GSA/notifications-admin/issues/1027) to add to confirmation page, finish [E2E testing implementation](https://github.com/GSA/notifications-admin/issues/677), set up [opt-out lists by phone number](https://github.com/GSA/notifications-api/issues/312) | Install required auth, expand message send volume for partners, allow users to see how many parts a send will cost, improve app stability, partition opt-outs so that they aren't across the system | Building Notify.gov |
|
||||
| UX | [Tackle `Usage and Performance` epic](https://github.com/GSA/notifications-admin/issues/1108) as highest priority group of user stories, break out UX work into more user-stories and correlating epics, prioritize epic/story order, improve [pre-send confirmation page](https://github.com/GSA/notifications-admin/issues/815)| Improve UX | Building Notify.gov|
|
||||
| Security | Complete necessary control families | Aim to have package completed with enough time to allow for long assessment| Secure ATO |
|
||||
| Content | Get [refreshed content for `Using Notify` pages](https://github.com/GSA/notifications-admin/issues/449) in place, make content [more clear about limits](https://github.com/GSA/notifications-admin/issues/963), make content clearer by [adding `parts` where necessary](https://github.com/GSA/notifications-admin/issues/963) | Improve clarity of message parts for users, bring existing content into current state| Building Notify.gov |
|
||||
| Ops | Assist in Tech to Gov process, plan gap coverage for Tim's departure 2/9 |
|
||||
|
||||
## Sprint: Akhlut (1/3/24)
|
||||
## Sprint: A (1/3/24)
|
||||
2024 Sprints will be named after mythical creatures
|
||||
|
||||
| | Goals | Impact | Milestone(s) |
|
||||
|
||||
@@ -123,7 +123,6 @@ const javascripts = () => {
|
||||
paths.src + 'javascripts/homepage.js',
|
||||
paths.src + 'javascripts/timeoutPopup.js',
|
||||
paths.src + 'javascripts/date.js',
|
||||
paths.src + 'javascripts/loginAlert.js',
|
||||
paths.src + 'javascripts/main.js',
|
||||
])
|
||||
.pipe(plugins.prettyerror())
|
||||
|
||||
@@ -47,8 +47,6 @@ applications:
|
||||
REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt"
|
||||
NEW_RELIC_CA_BUNDLE_PATH: "/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
COMMIT_HASH: ((COMMIT_HASH))
|
||||
|
||||
# login.gov variables
|
||||
|
||||
LOGIN_PEM: ((LOGIN_PEM))
|
||||
|
||||
562
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "ago"
|
||||
@@ -41,24 +41,24 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.7.7"
|
||||
version = "1.7.6"
|
||||
description = "Security oriented static analyser for python code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"},
|
||||
{file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"},
|
||||
{file = "bandit-1.7.6-py3-none-any.whl", hash = "sha256:36da17c67fc87579a5d20c323c8d0b1643a890a2b93f00b3d1229966624694ff"},
|
||||
{file = "bandit-1.7.6.tar.gz", hash = "sha256:72ce7bc9741374d96fb2f1c9a8960829885f1243ffde743de70a19cee353e8f3"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
|
||||
GitPython = ">=3.1.30"
|
||||
PyYAML = ">=5.3.1"
|
||||
rich = "*"
|
||||
stevedore = ">=1.20.0"
|
||||
|
||||
[package.extras]
|
||||
baseline = ["GitPython (>=3.1.30)"]
|
||||
test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"]
|
||||
test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"]
|
||||
toml = ["tomli (>=1.1.0)"]
|
||||
yaml = ["PyYAML"]
|
||||
|
||||
@@ -85,33 +85,33 @@ lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.2.0"
|
||||
version = "23.12.1"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"},
|
||||
{file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"},
|
||||
{file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"},
|
||||
{file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"},
|
||||
{file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"},
|
||||
{file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"},
|
||||
{file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"},
|
||||
{file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"},
|
||||
{file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"},
|
||||
{file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"},
|
||||
{file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"},
|
||||
{file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"},
|
||||
{file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"},
|
||||
{file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"},
|
||||
{file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"},
|
||||
{file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"},
|
||||
{file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"},
|
||||
{file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"},
|
||||
{file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"},
|
||||
{file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"},
|
||||
{file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"},
|
||||
{file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"},
|
||||
{file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"},
|
||||
{file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"},
|
||||
{file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"},
|
||||
{file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"},
|
||||
{file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"},
|
||||
{file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"},
|
||||
{file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"},
|
||||
{file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"},
|
||||
{file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"},
|
||||
{file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"},
|
||||
{file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"},
|
||||
{file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"},
|
||||
{file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"},
|
||||
{file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"},
|
||||
{file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"},
|
||||
{file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"},
|
||||
{file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"},
|
||||
{file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"},
|
||||
{file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"},
|
||||
{file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"},
|
||||
{file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"},
|
||||
{file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -465,63 +465,63 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.4.4"
|
||||
version = "7.4.0"
|
||||
description = "Code coverage measurement for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fa497a8ab37784fbb20ab699c246053ac294d13fc7eb40ec007a5043ec91f8"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1a93009cb80730c9bca5d6d4665494b725b6e8e157c1cb7f2db5b4b122ea562"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:690db6517f09336559dc0b5f55342df62370a48f5469fabf502db2c6d1cffcd2"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:09c3255458533cb76ef55da8cc49ffab9e33f083739c8bd4f58e79fecfe288f7"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ce1415194b4a6bd0cdcc3a1dfbf58b63f910dcb7330fe15bdff542c56949f87"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b91cbc4b195444e7e258ba27ac33769c41b94967919f10037e6355e998af255c"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win32.whl", hash = "sha256:598825b51b81c808cb6f078dcb972f96af96b078faa47af7dfcdf282835baa8d"},
|
||||
{file = "coverage-7.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:09ef9199ed6653989ebbcaacc9b62b514bb63ea2f90256e71fea3ed74bd8ff6f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0f9f50e7ef2a71e2fae92774c99170eb8304e3fdf9c8c3c7ae9bab3e7229c5cf"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:623512f8ba53c422fcfb2ce68362c97945095b864cda94a92edbaf5994201083"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0513b9508b93da4e1716744ef6ebc507aff016ba115ffe8ecff744d1322a7b63"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40209e141059b9370a2657c9b15607815359ab3ef9918f0196b6fccce8d3230f"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a2b2b78c78293782fd3767d53e6474582f62443d0504b1554370bde86cc8227"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:73bfb9c09951125d06ee473bed216e2c3742f530fc5acc1383883125de76d9cd"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f384c3cc76aeedce208643697fb3e8437604b512255de6d18dae3f27655a384"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:54eb8d1bf7cacfbf2a3186019bcf01d11c666bd495ed18717162f7eb1e9dd00b"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win32.whl", hash = "sha256:cac99918c7bba15302a2d81f0312c08054a3359eaa1929c7e4b26ebe41e9b286"},
|
||||
{file = "coverage-7.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:b14706df8b2de49869ae03a5ccbc211f4041750cd4a66f698df89d44f4bd30ec"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:201bef2eea65e0e9c56343115ba3814e896afe6d36ffd37bab783261db430f76"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41c9c5f3de16b903b610d09650e5e27adbfa7f500302718c9ffd1c12cf9d6818"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d898fe162d26929b5960e4e138651f7427048e72c853607f2b200909794ed978"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ea79bb50e805cd6ac058dfa3b5c8f6c040cb87fe83de10845857f5535d1db70"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce4b94265ca988c3f8e479e741693d143026632672e3ff924f25fab50518dd51"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00838a35b882694afda09f85e469c96367daa3f3f2b097d846a7216993d37f4c"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fdfafb32984684eb03c2d83e1e51f64f0906b11e64482df3c5db936ce3839d48"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:69eb372f7e2ece89f14751fbcbe470295d73ed41ecd37ca36ed2eb47512a6ab9"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win32.whl", hash = "sha256:137eb07173141545e07403cca94ab625cc1cc6bc4c1e97b6e3846270e7e1fea0"},
|
||||
{file = "coverage-7.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:d71eec7d83298f1af3326ce0ff1d0ea83c7cb98f72b577097f9083b20bdaf05e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ae728ff3b5401cc320d792866987e7e7e880e6ebd24433b70a33b643bb0384"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc4f1358cb0c78edef3ed237ef2c86056206bb8d9140e73b6b89fbcfcbdd40e1"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8130a2aa2acb8788e0b56938786c33c7c98562697bf9f4c7d6e8e5e3a0501e4a"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf271892d13e43bc2b51e6908ec9a6a5094a4df1d8af0bfc360088ee6c684409"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4cdc86d54b5da0df6d3d3a2f0b710949286094c3a6700c21e9015932b81447e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae71e7ddb7a413dd60052e90528f2f65270aad4b509563af6d03d53e979feafd"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:38dd60d7bf242c4ed5b38e094baf6401faa114fc09e9e6632374388a404f98e7"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa5b1c1bfc28384f1f53b69a023d789f72b2e0ab1b3787aae16992a7ca21056c"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win32.whl", hash = "sha256:dfa8fe35a0bb90382837b238fff375de15f0dcdb9ae68ff85f7a63649c98527e"},
|
||||
{file = "coverage-7.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2991665420a803495e0b90a79233c1433d6ed77ef282e8e152a324bbbc5e0c8"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b799445b9f7ee8bf299cfaed6f5b226c0037b74886a4e11515e569b36fe310d"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b4d33f418f46362995f1e9d4f3a35a1b6322cb959c31d88ae56b0298e1c22357"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aadacf9a2f407a4688d700e4ebab33a7e2e408f2ca04dbf4aef17585389eff3e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c95949560050d04d46b919301826525597f07b33beba6187d04fa64d47ac82e"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff7687ca3d7028d8a5f0ebae95a6e4827c5616b31a4ee1192bdfde697db110d4"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5fc1de20b2d4a061b3df27ab9b7c7111e9a710f10dc2b84d33a4ab25065994ec"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c74880fc64d4958159fbd537a091d2a585448a8f8508bf248d72112723974cbd"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:742a76a12aa45b44d236815d282b03cfb1de3b4323f3e4ec933acfae08e54ade"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win32.whl", hash = "sha256:d89d7b2974cae412400e88f35d86af72208e1ede1a541954af5d944a8ba46c57"},
|
||||
{file = "coverage-7.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:9ca28a302acb19b6af89e90f33ee3e1906961f94b54ea37de6737b7ca9d8827c"},
|
||||
{file = "coverage-7.4.4-pp38.pp39.pp310-none-any.whl", hash = "sha256:b2c5edc4ac10a7ef6605a966c58929ec6c1bd0917fb8c15cb3363f65aa40e677"},
|
||||
{file = "coverage-7.4.4.tar.gz", hash = "sha256:c901df83d097649e257e803be22592aedfd5182f07b3cc87d640bbb9afd50f49"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"},
|
||||
{file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"},
|
||||
{file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"},
|
||||
{file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"},
|
||||
{file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"},
|
||||
{file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"},
|
||||
{file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"},
|
||||
{file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -529,56 +529,47 @@ toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "42.0.4"
|
||||
version = "41.0.7"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-win32.whl", hash = "sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925"},
|
||||
{file = "cryptography-42.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-win32.whl", hash = "sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129"},
|
||||
{file = "cryptography-42.0.4-cp39-abi3-win_amd64.whl", hash = "sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854"},
|
||||
{file = "cryptography-42.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298"},
|
||||
{file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88"},
|
||||
{file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20"},
|
||||
{file = "cryptography-42.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce"},
|
||||
{file = "cryptography-42.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74"},
|
||||
{file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd"},
|
||||
{file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b"},
|
||||
{file = "cryptography-42.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660"},
|
||||
{file = "cryptography-42.0.4.tar.gz", hash = "sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c78451b78313fa81607fa1b3f1ae0a5ddd8014c38a02d9db0616133987b9cdf"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:928258ba5d6f8ae644e764d0f996d61a8777559f72dfeb2eea7e2fe0ad6e782d"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a1b41bc97f1ad230a41657d9155113c7521953869ae57ac39ac7f1bb471469a"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:841df4caa01008bad253bce2a6f7b47f86dc9f08df4b433c404def869f590a15"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5429ec739a29df2e29e15d082f1d9ad683701f0ec7709ca479b3ff2708dae65a"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:43f2552a2378b44869fe8827aa19e69512e3245a219104438692385b0ee119d1"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:af03b32695b24d85a75d40e1ba39ffe7db7ffcb099fe507b39fd41a565f1b157"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:49f0805fc0b2ac8d4882dd52f4a3b935b210935d500b6b805f321addc8177406"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-win32.whl", hash = "sha256:f983596065a18a2183e7f79ab3fd4c475205b839e02cbc0efbbf9666c4b3083d"},
|
||||
{file = "cryptography-41.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:90452ba79b8788fa380dfb587cca692976ef4e757b194b093d845e8d99f612f2"},
|
||||
{file = "cryptography-41.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:079b85658ea2f59c4f43b70f8119a52414cdb7be34da5d019a77bf96d473b960"},
|
||||
{file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b640981bf64a3e978a56167594a0e97db71c89a479da8e175d8bb5be5178c003"},
|
||||
{file = "cryptography-41.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e3114da6d7f95d2dee7d3f4eec16dacff819740bbab931aff8648cb13c5ff5e7"},
|
||||
{file = "cryptography-41.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d5ec85080cce7b0513cfd233914eb8b7bbd0633f1d1703aa28d1dd5a72f678ec"},
|
||||
{file = "cryptography-41.0.7-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7a698cb1dac82c35fcf8fe3417a3aaba97de16a01ac914b89a0889d364d2f6be"},
|
||||
{file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:37a138589b12069efb424220bf78eac59ca68b95696fc622b6ccc1c0a197204a"},
|
||||
{file = "cryptography-41.0.7-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:68a2dec79deebc5d26d617bfdf6e8aab065a4f34934b22d3b5010df3ba36612c"},
|
||||
{file = "cryptography-41.0.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:09616eeaef406f99046553b8a40fbf8b1e70795a91885ba4c96a70793de5504a"},
|
||||
{file = "cryptography-41.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48a0476626da912a44cc078f9893f292f0b3e4c739caf289268168d8f4702a39"},
|
||||
{file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c7f3201ec47d5207841402594f1d7950879ef890c0c495052fa62f58283fde1a"},
|
||||
{file = "cryptography-41.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c5ca78485a255e03c32b513f8c2bc39fedb7f5c5f8535545bdc223a03b24f248"},
|
||||
{file = "cryptography-41.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6c391c021ab1f7a82da5d8d0b3cee2f4b2c455ec86c8aebbc84837a631ff309"},
|
||||
{file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
|
||||
cffi = ">=1.12"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
|
||||
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
|
||||
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
|
||||
nox = ["nox"]
|
||||
pep8test = ["check-sdist", "click", "mypy", "ruff"]
|
||||
pep8test = ["black", "check-sdist", "mypy", "ruff"]
|
||||
sdist = ["build"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
|
||||
test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
|
||||
test-randomorder = ["pytest-randomly"]
|
||||
|
||||
[[package]]
|
||||
@@ -727,29 +718,29 @@ typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "flake8"
|
||||
version = "7.0.0"
|
||||
version = "6.1.0"
|
||||
description = "the modular source code checker: pep8 pyflakes and co"
|
||||
optional = false
|
||||
python-versions = ">=3.8.1"
|
||||
files = [
|
||||
{file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"},
|
||||
{file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"},
|
||||
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"},
|
||||
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
mccabe = ">=0.7.0,<0.8.0"
|
||||
pycodestyle = ">=2.11.0,<2.12.0"
|
||||
pyflakes = ">=3.2.0,<3.3.0"
|
||||
pyflakes = ">=3.1.0,<3.2.0"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-bugbear"
|
||||
version = "24.2.6"
|
||||
version = "24.1.17"
|
||||
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
|
||||
optional = false
|
||||
python-versions = ">=3.8.1"
|
||||
files = [
|
||||
{file = "flake8-bugbear-24.2.6.tar.gz", hash = "sha256:f9cb5f2a9e792dd80ff68e89a14c12eed8620af8b41a49d823b7a33064ac9658"},
|
||||
{file = "flake8_bugbear-24.2.6-py3-none-any.whl", hash = "sha256:663ef5de80cd32aacd39d362212983bc4636435a6f83700b4ed35acbd0b7d1b8"},
|
||||
{file = "flake8-bugbear-24.1.17.tar.gz", hash = "sha256:bcb388a4f3b516258749b1e690ee394c082eff742f44595e3754cf5c7781c2c7"},
|
||||
{file = "flake8_bugbear-24.1.17-py3-none-any.whl", hash = "sha256:46cc840ddaed26507cd0ada530d1526418b717ee76c9b5dfdbd238b5eab34139"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -924,6 +915,37 @@ files = [
|
||||
{file = "geojson-3.1.0.tar.gz", hash = "sha256:58a7fa40727ea058efc28b0e9ff0099eadf6d0965e04690830208d3ef571adac"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.11"
|
||||
description = "Git Object Database"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"},
|
||||
{file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
smmap = ">=3.0.1,<6"
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.41"
|
||||
description = "GitPython is a Python library used to interact with Git repositories"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "GitPython-3.1.41-py3-none-any.whl", hash = "sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c"},
|
||||
{file = "GitPython-3.1.41.tar.gz", hash = "sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
gitdb = ">=4.0.1,<5"
|
||||
|
||||
[package.extras]
|
||||
test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "sumtypes"]
|
||||
|
||||
[[package]]
|
||||
name = "govuk-bank-holidays"
|
||||
version = "0.13"
|
||||
@@ -938,6 +960,28 @@ files = [
|
||||
[package.dependencies]
|
||||
requests = "*"
|
||||
|
||||
[[package]]
|
||||
name = "govuk-frontend-jinja"
|
||||
version = "0.5.8-alpha"
|
||||
description = "Tools to use the GOV.UK Design System with Jinja-powered Python apps"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = []
|
||||
develop = false
|
||||
|
||||
[package.dependencies]
|
||||
jinja2 = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["pytest", "pytest-flakes", "pytest-helpers-namespace"]
|
||||
flask = ["Flask"]
|
||||
|
||||
[package.source]
|
||||
type = "git"
|
||||
url = "https://github.com/alphagov/govuk-frontend-jinja.git"
|
||||
reference = "v0.5.8-alpha"
|
||||
resolved_reference = "15845e4cca3a05df72c6e13ec6a7e35acc682f52"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.0.1"
|
||||
@@ -1080,13 +1124,13 @@ license = ["ukkonen"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
version = "3.4"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
|
||||
{file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
|
||||
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
|
||||
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1390,71 +1434,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.5"
|
||||
version = "2.1.3"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
|
||||
{file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
|
||||
{file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
|
||||
{file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
|
||||
{file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
|
||||
{file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
|
||||
{file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
|
||||
{file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1492,13 +1536,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "moto"
|
||||
version = "4.2.14"
|
||||
version = "4.2.13"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "moto-4.2.14-py2.py3-none-any.whl", hash = "sha256:6d242dbbabe925bb385ddb6958449e5c827670b13b8e153ed63f91dbdb50372c"},
|
||||
{file = "moto-4.2.14.tar.gz", hash = "sha256:8f9263ca70b646f091edcc93e97cda864a542e6d16ed04066b1370ed217bd190"},
|
||||
{file = "moto-4.2.13-py2.py3-none-any.whl", hash = "sha256:93e0fd13b624bd79115494f833308c3641b2be0fc9f4f18aa9264aa01f6168e0"},
|
||||
{file = "moto-4.2.13.tar.gz", hash = "sha256:01aef6a489a725c8d725bd3dc6f70ff1bedaee3e2641752e4b471ff0ede4b4d7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1521,13 +1565,18 @@ awslambda = ["docker (>=3.0.0)"]
|
||||
batch = ["docker (>=3.0.0)"]
|
||||
cloudformation = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
|
||||
cognitoidp = ["ecdsa (!=0.15)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"]
|
||||
ds = ["sshpubkeys (>=3.1.0)"]
|
||||
dynamodb = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"]
|
||||
dynamodbstreams = ["docker (>=3.0.0)", "py-partiql-parser (==0.5.0)"]
|
||||
ebs = ["sshpubkeys (>=3.1.0)"]
|
||||
ec2 = ["sshpubkeys (>=3.1.0)"]
|
||||
efs = ["sshpubkeys (>=3.1.0)"]
|
||||
eks = ["sshpubkeys (>=3.1.0)"]
|
||||
glue = ["pyparsing (>=3.0.7)"]
|
||||
iotdata = ["jsondiff (>=1.1.2)"]
|
||||
proxy = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=2.5.1)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "multipart", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
|
||||
resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)"]
|
||||
resourcegroupstaggingapi = ["PyYAML (>=5.1)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "sshpubkeys (>=3.1.0)"]
|
||||
route53resolver = ["sshpubkeys (>=3.1.0)"]
|
||||
s3 = ["PyYAML (>=5.1)", "py-partiql-parser (==0.5.0)"]
|
||||
s3crc32c = ["PyYAML (>=5.1)", "crc32c", "py-partiql-parser (==0.5.0)"]
|
||||
server = ["PyYAML (>=5.1)", "aws-xray-sdk (>=0.93,!=0.96)", "cfn-lint (>=0.40.0)", "docker (>=3.0.0)", "ecdsa (!=0.15)", "flask (!=2.2.0,!=2.2.1)", "flask-cors", "graphql-core", "jsondiff (>=1.1.2)", "openapi-spec-validator (>=0.5.0)", "py-partiql-parser (==0.5.0)", "pyparsing (>=3.0.7)", "python-jose[cryptography] (>=3.1.0,<4.0.0)", "setuptools", "sshpubkeys (>=3.1.0)"]
|
||||
@@ -1612,40 +1661,40 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "newrelic"
|
||||
version = "9.7.1"
|
||||
version = "9.5.0"
|
||||
description = "New Relic Python Agent"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
files = [
|
||||
{file = "newrelic-9.7.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:1c1d24be69d5316af7be99d6c87686d900e708bc421ca55977cb021fd29de0bd"},
|
||||
{file = "newrelic-9.7.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8dba147f13457bd22b4509bfa700ce12bfcb8294f8b9abd4c66d4e90f90cefc2"},
|
||||
{file = "newrelic-9.7.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bbbd24bd0054a978b5d6c1be7794289c760b20d44fea526e3fb1078d060a6fe7"},
|
||||
{file = "newrelic-9.7.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7d7f79bd828ab56446b0be941a6acb0af287ad97fe4ac5052c59ad0518f5456d"},
|
||||
{file = "newrelic-9.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fed29c65100966472bef1603c479b3b60be47078810a0d1c407e5ee133e606d7"},
|
||||
{file = "newrelic-9.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d85b7d08e7fe130951de1f2225e69c321ece620da18bbc4385905c72e0aa51b"},
|
||||
{file = "newrelic-9.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:102f6e8e65e6fa5044a0d433066a46ce5b382f96335576dfa16217c1855ebc2b"},
|
||||
{file = "newrelic-9.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0798e85b738a24843da9aa0e4175b42441d9b10af6b17ee8de137cf83d5bb222"},
|
||||
{file = "newrelic-9.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28343d596de29b7b0adcbcd2b872a1657d85c2467482792d8190814faec46c80"},
|
||||
{file = "newrelic-9.7.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a41340ce1d58bcc4dda39784d244e8a42c11b150665d8bec0527ea88bf02f53"},
|
||||
{file = "newrelic-9.7.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7c62934b8ae447bda4273a2dc4c3a65b0c7dc995af611b5003e75a34faa926f2"},
|
||||
{file = "newrelic-9.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:783c560439a08715eb00c79be64cd9471ce843f07b194592d15978909a8c85ad"},
|
||||
{file = "newrelic-9.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d61c374e4d698ee36eab80e33c485054514bd6f57c25cd8e2c9f0a40f159ebc"},
|
||||
{file = "newrelic-9.7.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08a062f6b0483de744b3085e70b88ccb7599ba4f242977bf1cbb602ed4385980"},
|
||||
{file = "newrelic-9.7.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e06c7991367e848c4f8e08e7197b0a71c145a8e32c5f92158ed64b4f6d5b4a22"},
|
||||
{file = "newrelic-9.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:166e365a6334d6b591a6af91e07dd191f043fb10893474ad1b60ed0b99a78f4e"},
|
||||
{file = "newrelic-9.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feb83b708350947846fd898debb1538ab5e0458ff56627f01e2174e73c0fe079"},
|
||||
{file = "newrelic-9.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd572a79e1848b67307be25e15f0804b9e9fc30a0d669a0fad668f3678a8869"},
|
||||
{file = "newrelic-9.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ed776ced21ebf57379cb38885485460ffd7df29cca9666197876d2045849b927"},
|
||||
{file = "newrelic-9.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b4f741be5f34e17caa57c72924045776a865efd5f9deab6ebb3b7c4f1190273b"},
|
||||
{file = "newrelic-9.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:647a5f4ff3514e7e2acbbc884319499b0ae90ec4ec93e83e7f41474cf8666e0e"},
|
||||
{file = "newrelic-9.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8faecb1fce2a25201f5496ad96181933e60b4b833f99dc143a84d5d2494a46f6"},
|
||||
{file = "newrelic-9.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:56a6b322b228ee0b3afbb59d686fad0c58b6b284fc6bb3227e7746ca0d458858"},
|
||||
{file = "newrelic-9.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6361b3be951e3520ea2b138ca56783b03f8a6c85085885fcf597d1ee28c59153"},
|
||||
{file = "newrelic-9.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82076bf4e84a1378ccd1c699e8890a8f469c3ebeec110ae5c4f03cfab25cd09b"},
|
||||
{file = "newrelic-9.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53273e8fbea3df48265b15ce3a5aee8e7950036a8463e973ef949d79072b5d74"},
|
||||
{file = "newrelic-9.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:365ec3b3795f43a70895652dff4ece28c11ecf0337aabf8da762b746cfda4c2e"},
|
||||
{file = "newrelic-9.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d143adbc38df9a576a34220d18f564208ddf88f691a1aaaf7b78c7fc653f2428"},
|
||||
{file = "newrelic-9.7.1.tar.gz", hash = "sha256:5824e599b972b5931caa13f5f34eb60df4cf3c7048604d0efe34b9ad41923739"},
|
||||
{file = "newrelic-9.5.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:87c5ea1db9b2f573c7fb0961b7a856a91db0c9f130303de1839c542586b8dc87"},
|
||||
{file = "newrelic-9.5.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:8c62c07a69b86c54d3b9ce187551f36683ad8b70b6301273e7c1e89b121b8245"},
|
||||
{file = "newrelic-9.5.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:847dd07961adec25c4c1128e8666f72ba0ab03640a44a65bcf25ffebd6fc2a75"},
|
||||
{file = "newrelic-9.5.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:12be024a5dfd1667d05d7cce968189e2490b6f6d24f194695365607b2340be20"},
|
||||
{file = "newrelic-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c381d300d904e2a5eb60953a29a729dbd4d8213f6dd629cecaa3033db70a9060"},
|
||||
{file = "newrelic-9.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2e4ccd5fe97496806fe0ea1acb8b15791c1f012aa44fb047fc8016bcfb80ab9"},
|
||||
{file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:14ef5e94d490315a73cf4bea4cd0186b2ec68009e90ab2c16a0b501942fa8629"},
|
||||
{file = "newrelic-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:306e54751fe1eba61884b0ed63e9583711b37e24db9c7b36725317c21aa609f5"},
|
||||
{file = "newrelic-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ac2b3603cc781ef58e51a95b0b58220feb3e322c282c758c3bf5430398a47bc"},
|
||||
{file = "newrelic-9.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b0a3fbbd6ef1dca0d2434c8ddd15d945fef2cd343d09b3a352b8de825d2e83"},
|
||||
{file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab045fe37dcfd9c1fefc84f72373c54abd546d7517909fabd6920ea1d87c3f7f"},
|
||||
{file = "newrelic-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e54492c5f70cf0951ffc97ddd71b3cf64d68b4d670012d7a7d5ff3dc19417dab"},
|
||||
{file = "newrelic-9.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92968b7c6951f3ee0fe85a465ae79d448e587a3d10bf71d85b2cab09aa319776"},
|
||||
{file = "newrelic-9.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847d75a08ca3ba356b6d6317618243e546dc7725251a453d4730ac6e9f043b71"},
|
||||
{file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ec0fe958c5ed52cd8b496339df8661badbd24bdc290dea8744de94da705ccef0"},
|
||||
{file = "newrelic-9.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a2af10be7893f60588e2103b2f5ff616baf8132ecce90d9a8c7bfb832da08748"},
|
||||
{file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2556ae2b9c13127dc46a6326c1ea682eae2c2b64b6873cfd4d5e177c7e0ce58a"},
|
||||
{file = "newrelic-9.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e9818646a7a72332e5ab3853277187af5b0d51312c51585a5581453fbdc1508"},
|
||||
{file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b370179f644c0265e4467221e895dccc75440388c5cce8d6a89d150c6b265021"},
|
||||
{file = "newrelic-9.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c514f82942f7759bc4a360d908c4be37b8764a3ff1589ba3ad8b4baf201bad36"},
|
||||
{file = "newrelic-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15d4c2dd584c7604a03b2c73464ed3054cd8f0a513ed25832f1a3af4eb6e80ab"},
|
||||
{file = "newrelic-9.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2af79b43e3244ce7775eb7a0bf2e8fe2cb1624f6eb1be03cb6c35f9e8a5be77"},
|
||||
{file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1dab3ef8baffebcf3f088a4131d93f3ac0e1fc08fa6045927c6bd2746b4decf4"},
|
||||
{file = "newrelic-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78ca19f47b2bc38e18516c2a7c485eeac6979b271a1a5a4ac3b950581ca8a230"},
|
||||
{file = "newrelic-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63314277c983cd412b87dfa3ed101b88c40ccab683a98046d76bf44a33cb7637"},
|
||||
{file = "newrelic-9.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68917968adba4abde385be1c23d8fdb13714968883c3a3d1a83fb676ea614245"},
|
||||
{file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fb49e3a2e8ab9b509c8d276382f7ce0f6b4e51758b0edec38131ae583532b121"},
|
||||
{file = "newrelic-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4638e6c28ebf2b5a8a860ae33105a1816425f6ece0113df626c88e5626b22f41"},
|
||||
{file = "newrelic-9.5.0.tar.gz", hash = "sha256:2442449c4798989d92f527a82e60c446821c97896d739492b607bf6d4018ba1b"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -1667,12 +1716,12 @@ setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "notifications-python-client"
|
||||
version = "9.0.0"
|
||||
version = "8.2.0"
|
||||
description = "Python API client for GOV.UK Notify."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "notifications_python_client-9.0.0-py3-none-any.whl", hash = "sha256:664a5b5da2aa1a00efa8106bfa4855db04da95d79586e5edfb0411637d20d2d9"},
|
||||
{file = "notifications_python_client-8.2.0-py3-none-any.whl", hash = "sha256:8cd8bd01ae603a972a5413c430ca42b16f6481f37d98406c3e9b68c1c192f59e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1682,7 +1731,7 @@ requests = ">=2.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "notifications-utils"
|
||||
version = "0.3.0"
|
||||
version = "0.2.6"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.9,<3.12"
|
||||
@@ -1700,16 +1749,16 @@ certifi = "^2023.7.22"
|
||||
cffi = "^1.16.0"
|
||||
charset-normalizer = "^3.1.0"
|
||||
click = "^8.1.3"
|
||||
cryptography = "^42.0.4"
|
||||
cryptography = "^41.0.6"
|
||||
flask = "^2.3.2"
|
||||
flask-redis = "^0.4.0"
|
||||
geojson = "^3.0.1"
|
||||
govuk-bank-holidays = "^0.13"
|
||||
idna = "^3.6"
|
||||
idna = "^3.4"
|
||||
itsdangerous = "^2.1.2"
|
||||
jinja2 = "^3.1.3"
|
||||
jmespath = "^1.0.1"
|
||||
markupsafe = "^2.1.5"
|
||||
markupsafe = "^2.1.2"
|
||||
mistune = "==0.8.4"
|
||||
numpy = "^1.24.2"
|
||||
orderedset = "^2.0.3"
|
||||
@@ -1719,7 +1768,7 @@ python-dateutil = "^2.8.2"
|
||||
python-json-logger = "^2.0.7"
|
||||
pytz = "^2023.3"
|
||||
pyyaml = "^6.0"
|
||||
redis = "^5.0.3"
|
||||
redis = "^5.0.1"
|
||||
regex = "^2023.10.3"
|
||||
requests = "^2.31.0"
|
||||
s3transfer = "^0.7.0"
|
||||
@@ -1734,7 +1783,7 @@ werkzeug = "^3.0.1"
|
||||
type = "git"
|
||||
url = "https://github.com/GSA/notifications-utils.git"
|
||||
reference = "HEAD"
|
||||
resolved_reference = "5a623566c7f71db48699cee93415f45317541524"
|
||||
resolved_reference = "f183d120a8b86e655405694adde1f0d95e4e5a51"
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
@@ -2163,13 +2212,13 @@ pyexcel-io = ">=0.6.2"
|
||||
|
||||
[[package]]
|
||||
name = "pyflakes"
|
||||
version = "3.2.0"
|
||||
version = "3.1.0"
|
||||
description = "passive checker of Python programs"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"},
|
||||
{file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"},
|
||||
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"},
|
||||
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2331,18 +2380,18 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-playwright"
|
||||
version = "0.4.4"
|
||||
version = "0.4.3"
|
||||
description = "A pytest wrapper with fixtures for Playwright to automate web browsers"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pytest-playwright-0.4.4.tar.gz", hash = "sha256:5488db4cc49028491c5130af0a2bb6b1d0b222a202217f6d14491d4c9aa67ff9"},
|
||||
{file = "pytest_playwright-0.4.4-py3-none-any.whl", hash = "sha256:df306f3a60a8631a3cfde1b95a2ed5a89203a3408dfa1154de049ca7de87c90b"},
|
||||
{file = "pytest-playwright-0.4.3.tar.gz", hash = "sha256:699e2c47fbb1e6a57895454693eba278cf55d04d44c15017709b00e1de1d9ccd"},
|
||||
{file = "pytest_playwright-0.4.3-py3-none-any.whl", hash = "sha256:c9ff6e7ebfd967b562f5c3d67f1ae6b45a061d6ea51ad304fdd95aca9db20774"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
playwright = ">=1.18"
|
||||
pytest = ">=6.2.4,<9.0.0"
|
||||
pytest = ">=6.2.4,<8.0.0"
|
||||
pytest-base-url = ">=1.0.0,<3.0.0"
|
||||
python-slugify = ">=6.0.0,<9.0.0"
|
||||
|
||||
@@ -2382,13 +2431,13 @@ six = ">=1.5"
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.1"
|
||||
version = "1.0.0"
|
||||
description = "Read key-value pairs from a .env file and set them as environment variables"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
|
||||
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
|
||||
{file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
|
||||
{file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -2512,17 +2561,17 @@ toml = ["tomli (>=2.0.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.3"
|
||||
version = "5.0.1"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-5.0.3-py3-none-any.whl", hash = "sha256:5da9b8fe9e1254293756c16c008e8620b3d15fcc6dde6babde9541850e72a32d"},
|
||||
{file = "redis-5.0.3.tar.gz", hash = "sha256:4973bae7444c0fbed64a06b87446f79361cb7e4ec1538c022d696ed7a5015580"},
|
||||
{file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"},
|
||||
{file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""}
|
||||
async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""}
|
||||
|
||||
[package.extras]
|
||||
hiredis = ["hiredis (>=1.0.0)"]
|
||||
@@ -2827,6 +2876,17 @@ files = [
|
||||
{file = "smartypants-2.0.1-py2.py3-none-any.whl", hash = "sha256:8db97f7cbdf08d15b158a86037cd9e116b4cf37703d24e0419a0d64ca5808f0d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smmap"
|
||||
version = "5.0.1"
|
||||
description = "A pure Python implementation of a sliding window memory map manager"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"},
|
||||
{file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
@@ -2956,17 +3016,17 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
|
||||
|
||||
[[package]]
|
||||
name = "vulture"
|
||||
version = "2.11"
|
||||
version = "2.10"
|
||||
description = "Find dead code"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "vulture-2.11-py2.py3-none-any.whl", hash = "sha256:12d745f7710ffbf6aeb8279ba9068a24d4e52e8ed333b8b044035c9d6b823aba"},
|
||||
{file = "vulture-2.11.tar.gz", hash = "sha256:f0fbb60bce6511aad87ee0736c502456737490a82d919a44e6d92262cb35f1c2"},
|
||||
{file = "vulture-2.10-py2.py3-none-any.whl", hash = "sha256:568a4176db7468d0157817ae3bb1847a19f1ddc629849af487f9d3b279bff77d"},
|
||||
{file = "vulture-2.10.tar.gz", hash = "sha256:2a5c3160bffba77595b6e6dfcc412016bd2a09cd4b66cdf7fbba913684899f6f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
toml = "*"
|
||||
|
||||
[[package]]
|
||||
name = "webencodings"
|
||||
@@ -3069,4 +3129,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.12"
|
||||
content-hash = "30e3f9f8e1a094e67cc780b496641dfbe33e639cefccf35ee3b1553c9a683b71"
|
||||
content-hash = "4794f0a44c97a4e7b2b49341f56b826d10ac6652dfdbc29323f7e433e10dc65d"
|
||||
|
||||
@@ -17,12 +17,13 @@ flask-login = "^0.6"
|
||||
flask-talisman = "*"
|
||||
flask-wtf = "^1.2"
|
||||
govuk-bank-holidays = "==0.13"
|
||||
govuk-frontend-jinja = {git = "https://github.com/alphagov/govuk-frontend-jinja.git", tag = "v0.5.8-alpha"}
|
||||
gunicorn = {version = "==21.2.0", extras = ["eventlet"]}
|
||||
humanize = "~=4.9"
|
||||
itsdangerous = "~=2.1"
|
||||
jinja2 = "~=3.1"
|
||||
newrelic = "*"
|
||||
notifications-python-client = "==9.0.0"
|
||||
notifications-python-client = "==8.2.0"
|
||||
notifications-utils = {git = "https://github.com/GSA/notifications-utils.git"}
|
||||
pyexcel = "==0.7.0"
|
||||
pyexcel-io = "==0.6.6"
|
||||
@@ -31,7 +32,7 @@ pyexcel-xls = "==0.7.0"
|
||||
pyexcel-xlsx = "==0.6.0"
|
||||
openpyxl = "==3.0.10"
|
||||
pyproj = "==3.6.1"
|
||||
python-dotenv = "==1.0.1"
|
||||
python-dotenv = "==1.0.0"
|
||||
pytz = "==2023.3.post1"
|
||||
rtreelib = "==0.2.0"
|
||||
werkzeug = "^3.0.1"
|
||||
@@ -41,10 +42,10 @@ markdown = "^3.5.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
bandit = "*"
|
||||
black = "^24.2.0"
|
||||
black = "^23.12.1"
|
||||
coverage = "*"
|
||||
freezegun = "^1.4.0"
|
||||
flake8 = "^7.0.0"
|
||||
flake8 = "^6.1.0"
|
||||
flake8-bugbear = "^24.1.17"
|
||||
flake8-print = "^5.0.0"
|
||||
flake8-pytest-style = "^1.7.2"
|
||||
@@ -60,7 +61,7 @@ pytest-playwright = "^0.4.3"
|
||||
pytest-xdist = "^3.5.0"
|
||||
radon = "^6.0.1"
|
||||
requests-mock = "^1.11.0"
|
||||
vulture = "^2.11"
|
||||
vulture = "^2.10"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
@@ -88,8 +88,6 @@ The below steps rely on you first configuring access to the Terraform state in s
|
||||
terraform plan
|
||||
```
|
||||
|
||||
If the `terraform init` command fails, you may need to run `terraform init -upgrade` to make sure new module versions are picked up.
|
||||
|
||||
1. Apply changes with `terraform apply`.
|
||||
|
||||
1. Remove the space deployer service instance if it doesn't need to be used again, such as when manually running terraform once.
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudfoundry = {
|
||||
source = "cloudfoundry-community/cloudfoundry"
|
||||
version = "0.53.0"
|
||||
version = "0.15.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ locals {
|
||||
}
|
||||
|
||||
module "redis" {
|
||||
source = "github.com/18f/terraform-cloudgov//redis?ref=v0.7.1"
|
||||
source = "github.com/18f/terraform-cloudgov//redis?ref=v0.2.0"
|
||||
|
||||
cf_org_name = local.cf_org_name
|
||||
cf_space_name = local.cf_space_name
|
||||
@@ -17,7 +17,7 @@ module "redis" {
|
||||
}
|
||||
|
||||
module "logo_upload_bucket" {
|
||||
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.7.1"
|
||||
source = "github.com/18f/terraform-cloudgov//s3?ref=v0.2.0"
|
||||
|
||||
cf_org_name = local.cf_org_name
|
||||
cf_space_name = local.cf_space_name
|
||||
|
||||
@@ -3,7 +3,7 @@ terraform {
|
||||
required_providers {
|
||||
cloudfoundry = {
|
||||
source = "cloudfoundry-community/cloudfoundry"
|
||||
version = "0.53.0"
|
||||
version = "0.15.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ terraform {
|
||||
key = "admin.tfstate.demo"
|
||||
encrypt = "true"
|
||||
region = "us-gov-west-1"
|
||||
profile = "notify-terraform-backend"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||