Commit Graph

5040 Commits

Author SHA1 Message Date
Pea Tyczynska
00a04ebf54 Merge pull request #3529 from alphagov/multiple_rates_sql_magic_for_org_view
Show correct sms cost in org view if sms rates change within financial year
2022-05-19 12:09:42 +01:00
Ben Thorner
2ad8c9d353 Merge pull request #3543 from alphagov/run-billing-for-longer-178125825
Recalculate billing rows for 10 days (prev. 4)
2022-05-19 11:27:28 +01:00
Pea Tyczynska
c4162748de Rename variable names for consistency between similar functions
Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
2022-05-18 12:30:35 +01:00
Pea Tyczynska
112c2ddf72 Use the new subquery in fetch_sms_billing_for_organisation()
This is so we have granular data about billable units and costs
so that we can handle multiple sms rates within one financial
year.

We also cast chargeable_units_used_so_far in that subquery
to integer so we don't have type mismatch.

Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
2022-05-18 12:30:24 +01:00
Pea Tyczynska
150eaf019b Add query_organisation_sms_usage_for_year to help fetch sms totals for org
This is functionally very similar to query_service_sms_usage_for_year,
except this query filters by organisation and returns for all live services
within that organisation.
To ensure that the cumulative free allowance counter resets properly for
each service, we use the `partition_by` flag to group up the window
function[^1]. This magically handles all the free allowances
independently for each service.

[^1]: https://www.postgresql.org/docs/current/tutorial-window.html

Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
2022-05-18 12:30:24 +01:00
Ben Thorner
7e536d1c2b Merge pull request #3542 from alphagov/optimise-historic-billing-query-182116071
Optimise billing query for notification history
2022-05-18 10:27:43 +01:00
Ben Thorner
458e997706 Recalculate billing rows for 10 days (prev. 4)
This effectively reverts [^1], which was only a temporary change.
I suspect the performance problem will go away with [^2].

While we've clearly been managing without this change, it resulted
in several rows being left as incorrect when letter receipts were
delayed. It makes sense for us to run this task for the same period
as we do to aggregate statuses, as status affects billing.

[^1]: e5c76ffda7
[^2]: https://github.com/alphagov/notifications-api/pull/3542
2022-05-17 17:38:08 +01:00
Ben Thorner
4a520bce78 Optimise billing query for notification history
This follows the same pattern as for status aggregations [^1]. We
haven't seen this problem for a long time because of [^2], but now
we're trying to re-run the aggregation for some incorrect rows it's
becoming apparent we need to fix it.

The following query currently fails in Production after the 30 min
SQLAlchemy timeout:

      select template_id, rate_multiplier, international, sum(billable_units), count(*)
      from notification_history
      where notification_status in ('delivered', 'sending')
      and key_type != 'test'
      and notification_type = 'sms'
      and service_id = '539d63a1-701d-400d-ab11-f3ee2319d4d4'
      and created_at >= '2021-07-07 23:00'
      and created_at < '2021-07-08 23:00'
      group by 1,2,3,4;

Running a quick "explain analyze" with this change applied returns
near immediately, but hangs without it. This is enough evidence for
me that this change will fix the issue.

[^1]: https://github.com/alphagov/notifications-api/pull/3417
[^2]: e5c76ffda7
2022-05-17 17:29:50 +01:00
Ben Thorner
e4a45047b3 Merge pull request #3538 from alphagov/fix-out-of-date-status-182116071
Fix out-of-date rows in ft_notification_status
2022-05-17 10:26:22 +01:00
Ben Thorner
318cc1284a Merge pull request #3534 from alphagov/remove-redundant-usage-fields-181935935
Remove redundant fields from service usage APIs
2022-05-11 15:12:31 +01:00
Ben Thorner
ed379a3724 Fix out-of-date rows in ft_notification_status
This can happen in the following scenario (primarily for letters):

1. A service has a mixture of "delivered" and "sending" letters,
which the status task aggregates into two rows:

  sending | 123
  delivered | 456

2. After the 7 day retention has passed, only the "delivered" letters
will be archived [^1].

3. The status task now looks at the history table [^2], which means
it only sees the "delivered" letters.

4. The "sending" letters are eventually "delivered" and archived (before
the 10 day aggregation cutoff).

5. But the status aggregation task doesn't run.

This commit fixes (5).

[^1]: https://github.com/alphagov/notifications-api/pull/3063
[^2]: f87ebb094d/app/dao/fact_notification_status_dao.py (L51)
2022-05-11 11:04:56 +01:00
David McDonald
177b860865 Merge pull request #3539 from alphagov/adjust-resting-rates
Adjust resting priorities for SMS providers
2022-05-11 09:48:24 +01:00
Ben Thorner
1d157836ad Remove redundant fields from service usage APIs
These are no longer used since [^1] and [^2].

[^1]: https://github.com/alphagov/notifications-admin/pull/4225
[^2]: https://github.com/alphagov/notifications-admin/pull/4229
2022-05-10 15:50:22 +01:00
David McDonald
f000e5a52b Adjust resting priorities for SMS providers 2022-05-10 15:07:25 +01:00
Katie Smith
dd213b8d55 Ensure UserUpdatePasswordSchema only loads _password
We had the `only` defined in the Meta class, and this wasn't working -
any extra fields were also being loaded. This moves it to the point
where the class is instantiated, which now works.
2022-05-06 10:14:50 +01:00
Katie Smith
685959de00 Don't serialize broadcast_messages in TemplateHistorySchema
In 0282a76 we excluded serializing `broadcast_messages` on some schemas.
This also excludes it from the TemplateHistory schema.
2022-05-06 10:14:50 +01:00
Katie Smith
ab199b6b05 Remove unused excluded fields from Marshmallow schemas
We have a lot of cases in the schemas where we're excluding a field that
doesn't actually exist on the schema anyway. This is often because a
model has been deleted, and the schema has not been updated. These
excluded fields have no effect at the moment, but Marshmallow 3 does
raise an error if you try and exclude non-existent fields.

There should be no change to what gets (de)serialized after this change.
2022-05-06 10:14:50 +01:00
Katie Smith
bd42bded0a Delete unused Marshmallow schema validation functions 2022-05-04 14:37:04 +01:00
Leo Hemsted
51646af92e remove provider_rates table
this was added five years ago but never used. if we want to bring back
variable rates per client we might as well get a fresh start since a lot
has changed since then.
2022-05-03 14:42:59 +01:00
Ben Thorner
bb62d22f25 Merge pull request #3526 from alphagov/remove-reach
Remove support for Reach provider
2022-04-29 13:21:27 +01:00
Ben Thorner
c27107fa74 Remove support for Reach provider
This provider was never active and support was never completed, so
there's little value in keeping all this potentially confusing code.
2022-04-29 12:28:08 +01:00
Ben Thorner
ebaef4b57b Add "charged_units" to service usage APIs
This can be calculated from the "free_allowance_used" field and the
"chargeable_units" field, but having it included separately is more
convenient as it can be used directly in Admin [^1].

[^1]: 417e7370bb/app/templates/views/usage.html (L38-L39)
2022-04-27 15:57:35 +01:00
Ben Thorner
555868c442 Add "free_allowance_units" to service usage APIs
This represents the number of chargeable_units that were actually
free due to the free allowance - they won't be included in "cost".

Although the existing calculations in Admin [^1][^2] will still be
correct with a change in SMS rates - it's cost that's the problem
- it makes sense to have all the knowledge about calculating usage
consistently in these two APIs.

Note that the Integer casting is covered by the API-level tests in
test_rest.

[^1]: 474d7dfda8/app/main/views/dashboard.py (L490)
[^2]: c63660d56d/app/main/views/dashboard.py (L350)
2022-04-27 15:57:34 +01:00
Ben Thorner
cd84928a1e Add costs to each row in yearly usage API
This will replace the manual calculations in Admin [^1][^2] for SMS
and also in API [^3] for annual letter costs.

Doing the calculation here also means we correctly attribute free
allowance to the earliest rows in the billing table - Admin doesn't
know when a given rate was applied so can't do this without making
assumptions about when we change our rates.

Since the calculation now depends on annual billing, we need to
change all the tests to make sure a suitable row exists. I've also
adjusted the test data to match the assumption that there can only
be one SMS rate per bst_date.

Note about "OVER" clause
========================

Using "rows=" ("ROWS BETWEEN") makes more sense than "range=" as
we want the remainder to be incremental within each group in a
"GROUP BY" clause, as well as between groups i.e

  # ROWS BETWEEN (arbitrary numbers to illustrate)
  date=2021-04-03, units=3, cost=3.29
  date=2021-04-03, units=2, cost=4.17
  date=2021-04-04, units=2, cost=5.10

  vs.

  # RANGE BETWEEN
  date=2021-04-03, units=3, cost=4.17
  date=2021-04-03, units=2, cost=4.17
  date=2021-04-04, units=2, cost=5.10

See [^4] for more details and examples.

[^1]: https://github.com/alphagov/notifications-admin/blob/master/app/templates/views/usage.html#L60
[^2]: 072c3b2079/app/billing/billing_schemas.py (L37)
[^3]: 474d7dfda8/app/templates/views/usage.html (L98)
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-27 15:57:33 +01:00
Ben Thorner
fc378fed96 Prepare to replace "billing_units" in usage APIs
There is no such thing as a "billing unit". The data this field
contained was also a confusing mixture of two types:

- For emails and letters, it was just "notifications_sent".

- For SMS, it was the "chargeable_units" (billable * multiplier).

This replaces the single, ambiguous "billing_units" field with
"chargeable_units" and "notifications_sent" in both usage APIs.
Once Admin is using them we can remove the old field.
2022-04-27 15:57:30 +01:00
Ben Thorner
80efdd2ec6 Refactor usage API queries into functions per type
This makes it easier to extend each function with costs and free
allowances - especially for SMS.

I've chosen to duplicate the "WHERE" clause in each subquery vs.
the top-level query. This will make more sense in later commits
where we start adding free allowance calculations, which need to
be done on a yearly basis - knowledge the subqueries should have.
2022-04-27 15:17:18 +01:00
Ben Thorner
ee4da698fe Standardise timezones for service usage APIs
We want to query for service usage in the BST financial year:

    2022-04-01T00:00:00+01:00 to 2023-03-31T23:59:59+01:00 =>
    2022-04-01 to 2023-03-31  # bst_date

Previously we were only doing this explicitly for the monthly API
and it seemed like the yearly usage API was incorrectly querying:

    2022-03-31T23:00:00+00:00 to 2023-03-30T23:00:00+00:00 =>
    2022-03-31 to 2023-03-30  # "bst_date"

However, it turns out this isn't a problem for two reasons:

1. We've been lucky that none of our rates have changed since 2017,
which is long ago enough that no one would care.

2. There's a quirk somewhere in Sqlalchemy / Postgres that has been
compensating for the lack of explicit BST conversion.

To help ensure we do this consistently in future I've DRYed-up the
BST conversion into a new utility. I could have just hard-coded the
dates but it seemed strange to have the knowledge twice.

I've also adjusted the tests so they detect if we accidentally use
data from a different financial year. (2) is why none of the test
assertions actually need changing and users won't be affected.

Sqlalchemy / Postgres quirk
===========================

The following queries were run on the same data but results differ:

    FactBilling.query.filter(FactBilling.bst_date >= datetime(2021,3,31,23,0), FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
    datetime.date(2021, 4, 1)

    FactBilling.query.filter(FactBilling.bst_date >= '2021-03-31 23:00:00', FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
    datetime.date(2021, 3, 31)

Looking at the actual query for the first item above still suggests
the results should be the same, but for the use of "timestamp".

    SELECT ...
    FROM ft_billing
    WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type IN ('email', 'letter') GROUP BY ft_billing.rate, ft_billing.notification_type UNION ALL SELECT sum(ft_billing.notifications_sent) AS notifications_sent, sum(ft_billing.billable_units * ft_billing.rate_multiplier) AS billable_units, ft_billing.rate AS ft_billing_rate, ft_billing.notification_type AS ft_billing_notification_type
    FROM ft_billing
    WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type = 'sms' GROUP BY ft_billing.rate, ft_billing.notification_type) AS anon_1 ORDER BY anon_1.notification_type, anon_1.rate

If we try some manual queries with and without '::timestamp' we get:

    select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00' order by bst_date desc;
      bst_date
    ------------
     2022-04-21
     2022-04-20

    select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00'::timestamp order by bst_date desc;
      bst_date
    ------------
     2022-04-21
     2022-04-20

It looks like this is happening because all client connections are
aware of the local timezone, and naive datetimes are interpreted as
being in UTC - not necessarily true, but saves us here!

The monthly API datetimes were pre-converted to dates, so none of
this was relevant for deciding exactly which date to use.
2022-04-26 13:11:34 +01:00
Ben Thorner
86b3d60c8f Refactor billing_schemas to use list comprehension 2022-04-26 13:07:48 +01:00
Ben Thorner
a4fe11a3aa Merge pull request #3521 from alphagov/refactor-billing-tests-181934027
Small refactorings to billing APIs and tests
2022-04-26 13:05:29 +01:00
Leo Hemsted
ae896c9880 remove REDIS_ENABLED flag from creds
you can still use this flag locally but we have it enabled for all
environments and it doesn't need to be toggleable from credentials as it
isn't a secret value.

If we wish to turn redis off for a specific environment we can create a
PR to change the config.
2022-04-22 12:05:19 +01:00
Ben Thorner
81063ba77a Remove redundant URLs for billing API endpoints
These aren't used in Admin.
2022-04-21 15:43:03 +01:00
Leo Hemsted
0457850fc0 Remove redundant conditional for CF Redis
This is now used in all environments and we've removed support for
non-CF Redis.
2022-04-20 11:41:33 +01:00
Ben Thorner
f67f5d987d Merge pull request #3514 from alphagov/remove-redundant-cf-code
Remove redundant CloudFoundry config code
2022-04-20 11:25:13 +01:00
Pea Tyczynska
61b6e45da5 Merge pull request #3510 from alphagov/nhs_branding_default_for_nhs_org
When creating a new NHS org, set default email branding to NHS
2022-04-19 15:33:03 +01:00
Pea Tyczynska
769b71cdc0 When updating org type to NHS type also update email branding if none set 2022-04-19 12:07:27 +01:00
Katie Smith
5feb38f50a Bump jsonschema from 3.2.0 to 4.4.0
The big breaking change for our code (not mentioned in the changelog) is
that the built-in validator for the `date-time` format now requires the
`rfc3339-validator` package instead of the `strict-rfc3339` package.
This updates the requirements file to use `rfc3339-validator`. Without
this change, wrong `date-time` formats would always silently pass validation.
2022-04-14 14:47:42 +01:00
Katie Smith
b440f3f904 Use Draft-07 and Draft7Validator everywhere
We were using the Draft4Validator in one place, so this updates it to
the Draft7Validator instead.

The schemas were mostly using draft 4 of the JSON schema, though there
were a couple of schemas that were already of version 7. This updates
them all to version 7, which is the latest version fully supported by
the jsonschema Python package. There are some breaking changes in the
newer version of the schema, but I could not see anywhere would these
affect us. Some of these schemas were not valid in version 4, but are
now valid in version 7 because `"required": []` was not valid in earlier
versions.
2022-04-14 14:46:10 +01:00
Ben Thorner
95c5f0c079 Remove redundant CloudFoundry config code
These env vars can be set directly in the manifest, like we do for
Template Preview [^1].

[^1]: c08036189b/manifest.yml.j2 (L23-L26)
2022-04-13 14:46:52 +01:00
Pea Tyczynska
b1ed722252 When creating a new NHS org, set default email branding to NHS
This is more appropriate default for that org than gov.uk branding
and will help us with our work to make setting the branding more
self-service.
2022-04-12 17:24:32 +01:00
Ben Thorner
8c7ad16452 Merge pull request #3503 from alphagov/allow-repeat-send-letter
Don't error sending a letter that's sent already
2022-04-12 16:04:54 +01:00
Ben Thorner
413c6c4c26 Move check for existing letter earlier in endpoint
In response to: [^1].

[^1]: https://github.com/alphagov/notifications-api/pull/3503#discussion_r848426047
2022-04-12 15:51:06 +01:00
Leo Hemsted
91200a2088 Merge pull request #3502 from alphagov/provider-report
add new daily sms provider volume report
2022-04-12 15:48:24 +01:00
Ben Thorner
a5e0fd6104 Merge pull request #3508 from alphagov/redis-ssl-181796569
Prepare to switch to Redis on PaaS
2022-04-12 15:47:46 +01:00
Ben Thorner
44d90b0a4f Remove redundant ternary on SMS client FROM_NUMBER
Logs over the past 14 days confirm we never call this code with
None as the sender, so it's safe to remove the ternary.
2022-04-12 14:59:21 +01:00
Ben Thorner
fb405977fa Allow REDIS_URL to optionally come from PaaS
This is to support a migration from Redislabs to PaaS native Redis,
allowing us to toggle between old and new using the env vars for
the instance - without needing to change the code.
2022-04-12 14:48:08 +01:00
Leo Hemsted
259d4a0569 add new daily sms provider volume report
code generally lifted almost exactly from the daily_volumes_report, but
per provider and only for SMS.
2022-04-11 13:42:40 +01:00
Ben Thorner
3f5a811e8f Further tweaks to SMS provider resting points 2022-04-11 10:44:57 +01:00
Ben Thorner
5810d46d35 Don't error sending a letter that's sent already
Fixes this error (in Admin):

      File "/home/vcap/app/app/notify_client/notification_api_client.py", line 74, in send_precompiled_letter
        return self.post(url='/service/{}/send-pdf-letter'.format(service_id), data=data)
      File "/home/vcap/app/app/notify_client/__init__.py", line 59, in post
        return super().post(*args, **kwargs)
      File "/home/vcap/deps/0/python/lib/python3.9/site-packages/notifications_python_client/base.py", line 48, in post
        return self.request("POST", url, data=data)
      File "/home/vcap/deps/0/python/lib/python3.9/site-packages/notifications_python_client/base.py", line 64, in request
        response = self._perform_request(method, url, kwargs)
      File "/home/vcap/deps/0/python/lib/python3.9/site-packages/notifications_python_client/base.py", line 118, in _perform_request
        raise api_error
    notifications_python_client.errors.HTTPError: 500 - Internal server error

Due to this error (in API):

      File "/home/vcap/app/app/service/send_notification.py", line 178, in send_pdf_letter_notification
        raise e
      File "/home/vcap/app/app/service/send_notification.py", line 173, in send_pdf_letter_notification
        letter = utils_s3download(current_app.config['TRANSIENT_UPLOADED_LETTERS'], file_location)
      File "/home/vcap/deps/0/python/lib/python3.9/site-packages/notifications_utils/s3.py", line 53, in s3download
        raise S3ObjectNotFound(error.response, error.operation_name)
    notifications_utils.s3.S3ObjectNotFound: An error occurred (NoSuchKey) when calling the GetObject operation: The specified key does not exist.

I checked the DB to verify the letter does actually exist i.e. it
is an instance of the problem we're fixing here.
2022-04-08 17:20:44 +01:00
Ben Thorner
6ca550a1a6 Merge pull request #3497 from alphagov/broadcast-alert-earlier-181423293
Alert earlier when a broadcast is approved
2022-04-07 10:34:23 +01:00
Ben Thorner
190c2b5122 Fix broadcast alert not triggering PagerDuty
We previously decided against this [^1] but the lambda alerts are
not a replacement for this one.

Even if both sets of alerts go off, we expect PagerDuty to aggregate
them into a single notification.

[^1]: 65c21f694c
2022-04-06 14:06:03 +01:00