mirror of
https://github.com/zulip/zulip.git
synced 2025-10-28 10:33:54 +00:00
timezone: Use standard library datetime.timezone.utc consistently.
datetime.timezone is available in Python ≥ 3.2. This also lets us remove a pytz dependency from the PostgreSQL scripts. Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
committed by
Tim Abbott
parent
29b8e11e20
commit
1f565a9f41
@@ -2,14 +2,13 @@ import datetime
|
|||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandParser
|
from django.core.management.base import BaseCommand, CommandParser
|
||||||
from django.utils.timezone import utc
|
|
||||||
|
|
||||||
from zerver.lib.statistics import seconds_usage_between
|
from zerver.lib.statistics import seconds_usage_between
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
|
||||||
def analyze_activity(options: Dict[str, Any]) -> None:
|
def analyze_activity(options: Dict[str, Any]) -> None:
|
||||||
day_start = datetime.datetime.strptime(options["date"], "%Y-%m-%d").replace(tzinfo=utc)
|
day_start = datetime.datetime.strptime(options["date"], "%Y-%m-%d").replace(tzinfo=datetime.timezone.utc)
|
||||||
day_end = day_start + datetime.timedelta(days=options["duration"])
|
day_end = day_start + datetime.timedelta(days=options["duration"])
|
||||||
|
|
||||||
user_profile_query = UserProfile.objects.all()
|
user_profile_query = UserProfile.objects.all()
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import os
|
|||||||
import time
|
import time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
from datetime import timezone
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, logger, process_count_stat
|
from analytics.lib.counts import COUNT_STATS, logger, process_count_stat
|
||||||
from scripts.lib.zulip_tools import ENDC, WARNING
|
from scripts.lib.zulip_tools import ENDC, WARNING
|
||||||
@@ -60,11 +60,11 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
fill_to_time = parse_datetime(options['time'])
|
fill_to_time = parse_datetime(options['time'])
|
||||||
if options['utc']:
|
if options['utc']:
|
||||||
fill_to_time = fill_to_time.replace(tzinfo=timezone_utc)
|
fill_to_time = fill_to_time.replace(tzinfo=timezone.utc)
|
||||||
if fill_to_time.tzinfo is None:
|
if fill_to_time.tzinfo is None:
|
||||||
raise ValueError("--time must be timezone aware. Maybe you meant to use the --utc option?")
|
raise ValueError("--time must be timezone aware. Maybe you meant to use the --utc option?")
|
||||||
|
|
||||||
fill_to_time = floor_to_hour(fill_to_time.astimezone(timezone_utc))
|
fill_to_time = floor_to_hour(fill_to_time.astimezone(timezone.utc))
|
||||||
|
|
||||||
if options['stat'] is not None:
|
if options['stat'] is not None:
|
||||||
stats = [COUNT_STATS[options['stat']]]
|
stats = [COUNT_STATS[options['stat']]]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type
|
from typing import Any, Dict, List, Optional, Tuple, Type
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
@@ -8,7 +8,6 @@ from django.db import models
|
|||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, CountStat, get_count_stats, \
|
from analytics.lib.counts import COUNT_STATS, CountStat, get_count_stats, \
|
||||||
DependentCountStat, LoggingCountStat, do_aggregate_to_summary_table, \
|
DependentCountStat, LoggingCountStat, do_aggregate_to_summary_table, \
|
||||||
@@ -33,7 +32,7 @@ class AnalyticsTestCase(TestCase):
|
|||||||
MINUTE = timedelta(seconds = 60)
|
MINUTE = timedelta(seconds = 60)
|
||||||
HOUR = MINUTE * 60
|
HOUR = MINUTE * 60
|
||||||
DAY = HOUR * 24
|
DAY = HOUR * 24
|
||||||
TIME_ZERO = datetime(1988, 3, 14).replace(tzinfo=timezone_utc)
|
TIME_ZERO = datetime(1988, 3, 14, tzinfo=timezone.utc)
|
||||||
TIME_LAST_HOUR = TIME_ZERO - HOUR
|
TIME_LAST_HOUR = TIME_ZERO - HOUR
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from django.utils.timezone import utc
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
import ujson
|
import ujson
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
@@ -607,8 +606,8 @@ class TestGetChartDataHelpers(ZulipTestCase):
|
|||||||
# the only function that uses it at the moment
|
# the only function that uses it at the moment
|
||||||
def test_last_successful_fill(self) -> None:
|
def test_last_successful_fill(self) -> None:
|
||||||
self.assertIsNone(last_successful_fill('non-existant'))
|
self.assertIsNone(last_successful_fill('non-existant'))
|
||||||
a_time = datetime(2016, 3, 14, 19).replace(tzinfo=utc)
|
a_time = datetime(2016, 3, 14, 19, tzinfo=timezone.utc)
|
||||||
one_hour_before = datetime(2016, 3, 14, 18).replace(tzinfo=utc)
|
one_hour_before = datetime(2016, 3, 14, 18, tzinfo=timezone.utc)
|
||||||
fillstate = FillState.objects.create(property='property', end_time=a_time,
|
fillstate = FillState.objects.create(property='property', end_time=a_time,
|
||||||
state=FillState.DONE)
|
state=FillState.DONE)
|
||||||
self.assertEqual(last_successful_fill('property'), a_time)
|
self.assertEqual(last_successful_fill('property'), a_time)
|
||||||
@@ -631,9 +630,9 @@ class TestTimeRange(ZulipTestCase):
|
|||||||
HOUR = timedelta(hours=1)
|
HOUR = timedelta(hours=1)
|
||||||
DAY = timedelta(days=1)
|
DAY = timedelta(days=1)
|
||||||
|
|
||||||
a_time = datetime(2016, 3, 14, 22, 59).replace(tzinfo=utc)
|
a_time = datetime(2016, 3, 14, 22, 59, tzinfo=timezone.utc)
|
||||||
floor_hour = datetime(2016, 3, 14, 22).replace(tzinfo=utc)
|
floor_hour = datetime(2016, 3, 14, 22, tzinfo=timezone.utc)
|
||||||
floor_day = datetime(2016, 3, 14).replace(tzinfo=utc)
|
floor_day = datetime(2016, 3, 14, tzinfo=timezone.utc)
|
||||||
|
|
||||||
# test start == end
|
# test start == end
|
||||||
self.assertEqual(time_range(a_time, a_time, CountStat.HOUR, None), [])
|
self.assertEqual(time_range(a_time, a_time, CountStat.HOUR, None), [])
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import re
|
|||||||
import time
|
import time
|
||||||
import urllib
|
import urllib
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from typing import Any, Callable, Dict, List, \
|
from typing import Any, Callable, Dict, List, \
|
||||||
@@ -18,7 +18,7 @@ from django.db.models.query import QuerySet
|
|||||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
|
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils.timezone import now as timezone_now, utc as timezone_utc
|
from django.utils.timezone import now as timezone_now
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.timesince import timesince
|
from django.utils.timesince import timesince
|
||||||
from django.core.validators import URLValidator
|
from django.core.validators import URLValidator
|
||||||
@@ -253,7 +253,7 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name:
|
|||||||
start = realm.date_created
|
start = realm.date_created
|
||||||
if end is None:
|
if end is None:
|
||||||
end = max(last_successful_fill(stat.property) or
|
end = max(last_successful_fill(stat.property) or
|
||||||
datetime.min.replace(tzinfo=timezone_utc) for stat in stats)
|
datetime.min.replace(tzinfo=timezone.utc) for stat in stats)
|
||||||
|
|
||||||
if start > end and (timezone_now() - start > MAX_TIME_FOR_FULL_ANALYTICS_GENERATION):
|
if start > end and (timezone_now() - start > MAX_TIME_FOR_FULL_ANALYTICS_GENERATION):
|
||||||
logging.warning("User from realm %s attempted to access /stats, but the computed "
|
logging.warning("User from realm %s attempted to access /stats, but the computed "
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
@@ -14,7 +14,6 @@ import responses
|
|||||||
from django.core import signing
|
from django.core import signing
|
||||||
from django.urls.resolvers import get_resolver
|
from django.urls.resolvers import get_resolver
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
@@ -265,9 +264,9 @@ class StripeTestCase(ZulipTestCase):
|
|||||||
self.signed_seat_count, self.salt = sign_string(str(self.seat_count))
|
self.signed_seat_count, self.salt = sign_string(str(self.seat_count))
|
||||||
# Choosing dates with corresponding timestamps below 1500000000 so that they are
|
# Choosing dates with corresponding timestamps below 1500000000 so that they are
|
||||||
# not caught by our timestamp normalization regex in normalize_fixture_data
|
# not caught by our timestamp normalization regex in normalize_fixture_data
|
||||||
self.now = datetime(2012, 1, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
|
self.now = datetime(2012, 1, 2, 3, 4, 5, tzinfo=timezone.utc)
|
||||||
self.next_month = datetime(2012, 2, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
|
self.next_month = datetime(2012, 2, 2, 3, 4, 5, tzinfo=timezone.utc)
|
||||||
self.next_year = datetime(2013, 1, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
|
self.next_year = datetime(2013, 1, 2, 3, 4, 5, tzinfo=timezone.utc)
|
||||||
|
|
||||||
def get_signed_seat_count_from_response(self, response: HttpResponse) -> Optional[str]:
|
def get_signed_seat_count_from_response(self, response: HttpResponse) -> Optional[str]:
|
||||||
match = re.search(r'name=\"signed_seat_count\" value=\"(.+)\"', response.content.decode("utf-8"))
|
match = re.search(r'name=\"signed_seat_count\" value=\"(.+)\"', response.content.decode("utf-8"))
|
||||||
@@ -1552,24 +1551,24 @@ class RequiresBillingAccessTest(ZulipTestCase):
|
|||||||
|
|
||||||
class BillingHelpersTest(ZulipTestCase):
|
class BillingHelpersTest(ZulipTestCase):
|
||||||
def test_next_month(self) -> None:
|
def test_next_month(self) -> None:
|
||||||
anchor = datetime(2019, 12, 31, 1, 2, 3).replace(tzinfo=timezone_utc)
|
anchor = datetime(2019, 12, 31, 1, 2, 3, tzinfo=timezone.utc)
|
||||||
period_boundaries = [
|
period_boundaries = [
|
||||||
anchor,
|
anchor,
|
||||||
datetime(2020, 1, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 1, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
# Test that this is the 28th even during leap years
|
# Test that this is the 28th even during leap years
|
||||||
datetime(2020, 2, 28, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 2, 28, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 3, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 3, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 4, 30, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 4, 30, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 5, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 5, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 6, 30, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 6, 30, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 7, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 7, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 8, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 8, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 9, 30, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 9, 30, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 10, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 10, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 11, 30, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 11, 30, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2020, 12, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2020, 12, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2021, 1, 31, 1, 2, 3).replace(tzinfo=timezone_utc),
|
datetime(2021, 1, 31, 1, 2, 3, tzinfo=timezone.utc),
|
||||||
datetime(2021, 2, 28, 1, 2, 3).replace(tzinfo=timezone_utc)]
|
datetime(2021, 2, 28, 1, 2, 3, tzinfo=timezone.utc)]
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
add_months(anchor, -1)
|
add_months(anchor, -1)
|
||||||
# Explicitly test add_months for each value of MAX_DAY_FOR_MONTH and
|
# Explicitly test add_months for each value of MAX_DAY_FOR_MONTH and
|
||||||
@@ -1586,9 +1585,9 @@ class BillingHelpersTest(ZulipTestCase):
|
|||||||
|
|
||||||
def test_compute_plan_parameters(self) -> None:
|
def test_compute_plan_parameters(self) -> None:
|
||||||
# TODO: test rounding down microseconds
|
# TODO: test rounding down microseconds
|
||||||
anchor = datetime(2019, 12, 31, 1, 2, 3).replace(tzinfo=timezone_utc)
|
anchor = datetime(2019, 12, 31, 1, 2, 3, tzinfo=timezone.utc)
|
||||||
month_later = datetime(2020, 1, 31, 1, 2, 3).replace(tzinfo=timezone_utc)
|
month_later = datetime(2020, 1, 31, 1, 2, 3, tzinfo=timezone.utc)
|
||||||
year_later = datetime(2020, 12, 31, 1, 2, 3).replace(tzinfo=timezone_utc)
|
year_later = datetime(2020, 12, 31, 1, 2, 3, tzinfo=timezone.utc)
|
||||||
test_cases = [
|
test_cases = [
|
||||||
# TODO test with Decimal(85), not 85
|
# TODO test with Decimal(85), not 85
|
||||||
# TODO fix the mypy error by specifying the exact type
|
# TODO fix the mypy error by specifying the exact type
|
||||||
|
|||||||
@@ -153,19 +153,19 @@ Python allows datetime objects to not have an associated timezone, which can
|
|||||||
cause time-related bugs that are hard to catch with a test suite, or bugs
|
cause time-related bugs that are hard to catch with a test suite, or bugs
|
||||||
that only show up during daylight savings time.
|
that only show up during daylight savings time.
|
||||||
|
|
||||||
Good ways to make timezone-aware datetimes are below. We import `timezone`
|
Good ways to make timezone-aware datetimes are below. We import timezone
|
||||||
functions as `from django.utils.timezone import now as timezone_now` and
|
libraries as `from datetime import datetime, timezone` and `from
|
||||||
`from django.utils.timezone import utc as timezone_utc`.
|
django.utils.timezone import now as timezone_now`.
|
||||||
|
|
||||||
Use:
|
Use:
|
||||||
* `timezone_now()` to get a datetime when Django is available, such as
|
* `timezone_now()` to get a datetime when Django is available, such as
|
||||||
in `zerver/`.
|
in `zerver/`.
|
||||||
* `datetime.now(tz=pytz.utc)` when Django is not available, such as
|
* `datetime.now(tz=timezone.utc)` when Django is not available, such as
|
||||||
for bots and scripts.
|
for bots and scripts.
|
||||||
* `datetime.fromtimestamp(timestamp, tz=timezone_utc)` if creating a
|
* `datetime.fromtimestamp(timestamp, tz=timezone.utc)` if creating a
|
||||||
datetime from a timestamp. This is also available as
|
datetime from a timestamp. This is also available as
|
||||||
`zerver.lib.timestamp.timestamp_to_datetime`.
|
`zerver.lib.timestamp.timestamp_to_datetime`.
|
||||||
* `datetime.strptime(date_string, format).replace(tzinfo=timezone_utc)` if
|
* `datetime.strptime(date_string, format).replace(tzinfo=timezone.utc)` if
|
||||||
creating a datetime from a formatted string that is in UTC.
|
creating a datetime from a formatted string that is in UTC.
|
||||||
|
|
||||||
Idioms that result in timezone-naive datetimes, and should be avoided, are
|
Idioms that result in timezone-naive datetimes, and should be avoided, are
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import pytz
|
|
||||||
|
|
||||||
states = {
|
states = {
|
||||||
"OK": 0,
|
"OK": 0,
|
||||||
@@ -28,7 +27,7 @@ try:
|
|||||||
except OSError:
|
except OSError:
|
||||||
report('UNKNOWN', 'could not determine completion time of last Postgres backup')
|
report('UNKNOWN', 'could not determine completion time of last Postgres backup')
|
||||||
|
|
||||||
if datetime.now(tz=pytz.utc) - last_backup > timedelta(hours=25):
|
if datetime.now(tz=timezone.utc) - last_backup > timedelta(hours=25):
|
||||||
report('CRITICAL', 'last Postgres backup completed more than 25 hours ago: %s' % (last_backup,))
|
report('CRITICAL', 'last Postgres backup completed more than 25 hours ago: %s' % (last_backup,))
|
||||||
|
|
||||||
report('OK', 'last Postgres backup completed less than 25 hours ago: %s' % (last_backup,))
|
report('OK', 'last Postgres backup completed less than 25 hours ago: %s' % (last_backup,))
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import shlex
|
|||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import pytz
|
|
||||||
import time
|
import time
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
logging.Formatter.converter = time.gmtime
|
logging.Formatter.converter = time.gmtime
|
||||||
@@ -48,7 +47,7 @@ if len(pg_data_paths) != 1:
|
|||||||
pg_data_path = pg_data_paths[0]
|
pg_data_path = pg_data_paths[0]
|
||||||
run(['env-wal-e', 'backup-push', pg_data_path])
|
run(['env-wal-e', 'backup-push', pg_data_path])
|
||||||
|
|
||||||
now = datetime.now(tz=pytz.utc)
|
now = datetime.now(tz=timezone.utc)
|
||||||
with open('/var/lib/nagios_state/last_postgres_backup', 'w') as f:
|
with open('/var/lib/nagios_state/last_postgres_backup', 'w') as f:
|
||||||
f.write(now.isoformat())
|
f.write(now.isoformat())
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|||||||
@@ -15,10 +15,7 @@ class zulip::postgres_common {
|
|||||||
# Postgres Nagios check plugin
|
# Postgres Nagios check plugin
|
||||||
'check-postgres',
|
'check-postgres',
|
||||||
# Python modules used in our monitoring/worker threads
|
# Python modules used in our monitoring/worker threads
|
||||||
'python3-tz', # TODO: use a virtualenv instead
|
|
||||||
'python-tz', # TODO: use a virtualenv instead
|
|
||||||
'python3-dateutil', # TODO: use a virtualenv instead
|
'python3-dateutil', # TODO: use a virtualenv instead
|
||||||
'python-dateutil', # TODO: use a virtualenv instead
|
|
||||||
]
|
]
|
||||||
$postgres_user_reqs = [
|
$postgres_user_reqs = [
|
||||||
Package[$postgresql],
|
Package[$postgresql],
|
||||||
@@ -39,12 +36,8 @@ class zulip::postgres_common {
|
|||||||
# https://bucardo.org/check_postgres/
|
# https://bucardo.org/check_postgres/
|
||||||
# 'check-postgres', # TODO
|
# 'check-postgres', # TODO
|
||||||
]
|
]
|
||||||
exec {'pip2_deps':
|
|
||||||
# Python modules used in our monitoring/worker threads
|
|
||||||
command => '/usr/bin/pip2 install pytz python-dateutil'
|
|
||||||
}
|
|
||||||
exec {'pip3_deps':
|
exec {'pip3_deps':
|
||||||
command => 'python3 -m pip install pytz python-dateutil'
|
command => 'python3 -m pip install python-dateutil'
|
||||||
}
|
}
|
||||||
group { 'ssl-cert':
|
group { 'ssl-cert':
|
||||||
ensure => present,
|
ensure => present,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from typing import Any, Dict, List, Set, Tuple, Union
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import pytz
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
@@ -168,7 +167,7 @@ def handle_digest_email(user_profile_id: int, cutoff: float,
|
|||||||
user_profile = get_user_profile_by_id(user_profile_id)
|
user_profile = get_user_profile_by_id(user_profile_id)
|
||||||
|
|
||||||
# Convert from epoch seconds to a datetime object.
|
# Convert from epoch seconds to a datetime object.
|
||||||
cutoff_date = datetime.datetime.fromtimestamp(int(cutoff), tz=pytz.utc)
|
cutoff_date = datetime.datetime.fromtimestamp(int(cutoff), tz=datetime.timezone.utc)
|
||||||
|
|
||||||
context = common_context(user_profile)
|
context = common_context(user_profile)
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from bs4 import BeautifulSoup
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.utils.timezone import utc as timezone_utc, now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from typing import Any, Dict, List, Optional, Set, Tuple, \
|
from typing import Any, Dict, List, Optional, Set, Tuple, \
|
||||||
Iterable, cast
|
Iterable, cast
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ def fix_datetime_fields(data: TableData, table: TableName) -> None:
|
|||||||
for item in data[table]:
|
for item in data[table]:
|
||||||
for field_name in DATE_FIELDS[table]:
|
for field_name in DATE_FIELDS[table]:
|
||||||
if item[field_name] is not None:
|
if item[field_name] is not None:
|
||||||
item[field_name] = datetime.datetime.fromtimestamp(item[field_name], tz=timezone_utc)
|
item[field_name] = datetime.datetime.fromtimestamp(item[field_name], tz=datetime.timezone.utc)
|
||||||
|
|
||||||
def fix_upload_links(data: TableData, message_table: TableName) -> None:
|
def fix_upload_links(data: TableData, message_table: TableName) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
# System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html
|
# System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html
|
||||||
|
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta, timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
@@ -28,7 +27,7 @@ class _RateLimitFilter:
|
|||||||
Adapted from https://djangosnippets.org/snippets/2242/.
|
Adapted from https://djangosnippets.org/snippets/2242/.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
last_error = datetime.min.replace(tzinfo=timezone_utc)
|
last_error = datetime.min.replace(tzinfo=timezone.utc)
|
||||||
# This thread-local variable is used to detect recursive
|
# This thread-local variable is used to detect recursive
|
||||||
# exceptions during exception handling (primarily intended for
|
# exceptions during exception handling (primarily intended for
|
||||||
# when accessing the shared cache throws an exception).
|
# when accessing the shared cache throws an exception).
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
class TimezoneNotUTCException(Exception):
|
class TimezoneNotUTCException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def verify_UTC(dt: datetime.datetime) -> None:
|
def verify_UTC(dt: datetime.datetime) -> None:
|
||||||
if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) != timezone_utc.utcoffset(dt):
|
if dt.tzinfo is None or dt.tzinfo.utcoffset(dt) != datetime.timezone.utc.utcoffset(dt):
|
||||||
raise TimezoneNotUTCException("Datetime %s does not have a UTC timezone." % (dt,))
|
raise TimezoneNotUTCException("Datetime %s does not have a UTC timezone." % (dt,))
|
||||||
|
|
||||||
def convert_to_UTC(dt: datetime.datetime) -> datetime.datetime:
|
def convert_to_UTC(dt: datetime.datetime) -> datetime.datetime:
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
return dt.replace(tzinfo=timezone_utc)
|
return dt.replace(tzinfo=datetime.timezone.utc)
|
||||||
return dt.astimezone(timezone_utc)
|
return dt.astimezone(datetime.timezone.utc)
|
||||||
|
|
||||||
def floor_to_hour(dt: datetime.datetime) -> datetime.datetime:
|
def floor_to_hour(dt: datetime.datetime) -> datetime.datetime:
|
||||||
verify_UTC(dt)
|
verify_UTC(dt)
|
||||||
return datetime.datetime(*dt.timetuple()[:4]) \
|
return datetime.datetime(*dt.timetuple()[:4], tzinfo=datetime.timezone.utc)
|
||||||
.replace(tzinfo=timezone_utc)
|
|
||||||
|
|
||||||
def floor_to_day(dt: datetime.datetime) -> datetime.datetime:
|
def floor_to_day(dt: datetime.datetime) -> datetime.datetime:
|
||||||
verify_UTC(dt)
|
verify_UTC(dt)
|
||||||
return datetime.datetime(*dt.timetuple()[:3]) \
|
return datetime.datetime(*dt.timetuple()[:3], tzinfo=datetime.timezone.utc)
|
||||||
.replace(tzinfo=timezone_utc)
|
|
||||||
|
|
||||||
def ceiling_to_hour(dt: datetime.datetime) -> datetime.datetime:
|
def ceiling_to_hour(dt: datetime.datetime) -> datetime.datetime:
|
||||||
floor = floor_to_hour(dt)
|
floor = floor_to_hour(dt)
|
||||||
@@ -37,7 +34,7 @@ def ceiling_to_day(dt: datetime.datetime) -> datetime.datetime:
|
|||||||
return floor + datetime.timedelta(days=1)
|
return floor + datetime.timedelta(days=1)
|
||||||
|
|
||||||
def timestamp_to_datetime(timestamp: float) -> datetime.datetime:
|
def timestamp_to_datetime(timestamp: float) -> datetime.datetime:
|
||||||
return datetime.datetime.fromtimestamp(float(timestamp), tz=timezone_utc)
|
return datetime.datetime.fromtimestamp(float(timestamp), tz=datetime.timezone.utc)
|
||||||
|
|
||||||
def datetime_to_timestamp(dt: datetime.datetime) -> int:
|
def datetime_to_timestamp(dt: datetime.datetime) -> int:
|
||||||
verify_UTC(dt)
|
verify_UTC(dt)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import time
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import CommandParser
|
from django.core.management.base import CommandParser
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
from zerver.lib.management import ZulipBaseCommand
|
from zerver.lib.management import ZulipBaseCommand
|
||||||
from zerver.models import Message, Recipient, Stream
|
from zerver.models import Message, Recipient, Stream
|
||||||
@@ -26,7 +25,7 @@ class Command(ZulipBaseCommand):
|
|||||||
streams = Stream.objects.filter(realm=realm, invite_only=False)
|
streams = Stream.objects.filter(realm=realm, invite_only=False)
|
||||||
recipients = Recipient.objects.filter(
|
recipients = Recipient.objects.filter(
|
||||||
type=Recipient.STREAM, type_id__in=[stream.id for stream in streams])
|
type=Recipient.STREAM, type_id__in=[stream.id for stream in streams])
|
||||||
cutoff = datetime.datetime.fromtimestamp(options["since"], tz=timezone_utc)
|
cutoff = datetime.datetime.fromtimestamp(options["since"], tz=datetime.timezone.utc)
|
||||||
messages = Message.objects.filter(date_sent__gt=cutoff, recipient__in=recipients)
|
messages = Message.objects.filter(date_sent__gt=cutoff, recipient__in=recipients)
|
||||||
|
|
||||||
for message in messages:
|
for message in messages:
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ from django.http import HttpResponse
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
from analytics.lib.counts import CountStat, LoggingCountStat
|
from analytics.lib.counts import CountStat, LoggingCountStat
|
||||||
from analytics.models import InstallationCount, RealmCount
|
from analytics.models import InstallationCount, RealmCount
|
||||||
@@ -383,7 +382,7 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
|||||||
self.assertEqual(len(tokens), 0)
|
self.assertEqual(len(tokens), 0)
|
||||||
|
|
||||||
class AnalyticsBouncerTest(BouncerTestCase):
|
class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
TIME_ZERO = datetime.datetime(1988, 3, 14).replace(tzinfo=timezone_utc)
|
TIME_ZERO = datetime.datetime(1988, 3, 14, tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL='https://push.zulip.org.example.com')
|
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL='https://push.zulip.org.example.com')
|
||||||
@mock.patch('zerver.lib.remote_server.requests.request')
|
@mock.patch('zerver.lib.remote_server.requests.request')
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ import ujson
|
|||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
import urllib
|
import urllib
|
||||||
import pytz
|
|
||||||
|
|
||||||
class RedirectAndLogIntoSubdomainTestCase(ZulipTestCase):
|
class RedirectAndLogIntoSubdomainTestCase(ZulipTestCase):
|
||||||
def test_data(self) -> None:
|
def test_data(self) -> None:
|
||||||
@@ -4087,22 +4086,22 @@ class FollowupEmailTest(ZulipTestCase):
|
|||||||
def test_followup_day2_email(self) -> None:
|
def test_followup_day2_email(self) -> None:
|
||||||
user_profile = self.example_user('hamlet')
|
user_profile = self.example_user('hamlet')
|
||||||
# Test date_joined == Sunday
|
# Test date_joined == Sunday
|
||||||
user_profile.date_joined = datetime.datetime(2018, 1, 7, 1, 0, 0, 0, pytz.UTC)
|
user_profile.date_joined = datetime.datetime(2018, 1, 7, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1))
|
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1))
|
||||||
# Test date_joined == Tuesday
|
# Test date_joined == Tuesday
|
||||||
user_profile.date_joined = datetime.datetime(2018, 1, 2, 1, 0, 0, 0, pytz.UTC)
|
user_profile.date_joined = datetime.datetime(2018, 1, 2, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1))
|
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=2, hours=-1))
|
||||||
# Test date_joined == Thursday
|
# Test date_joined == Thursday
|
||||||
user_profile.date_joined = datetime.datetime(2018, 1, 4, 1, 0, 0, 0, pytz.UTC)
|
user_profile.date_joined = datetime.datetime(2018, 1, 4, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1))
|
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1))
|
||||||
# Test date_joined == Friday
|
# Test date_joined == Friday
|
||||||
user_profile.date_joined = datetime.datetime(2018, 1, 5, 1, 0, 0, 0, pytz.UTC)
|
user_profile.date_joined = datetime.datetime(2018, 1, 5, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=3, hours=-1))
|
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=3, hours=-1))
|
||||||
|
|
||||||
# Time offset of America/Phoenix is -07:00
|
# Time offset of America/Phoenix is -07:00
|
||||||
user_profile.timezone = 'America/Phoenix'
|
user_profile.timezone = 'America/Phoenix'
|
||||||
# Test date_joined == Friday in UTC, but Thursday in the user's timezone
|
# Test date_joined == Friday in UTC, but Thursday in the user's timezone
|
||||||
user_profile.date_joined = datetime.datetime(2018, 1, 5, 1, 0, 0, 0, pytz.UTC)
|
user_profile.date_joined = datetime.datetime(2018, 1, 5, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1))
|
self.assertEqual(followup_day2_email_delay(user_profile), datetime.timedelta(days=1, hours=-1))
|
||||||
|
|
||||||
class NoReplyEmailTest(ZulipTestCase):
|
class NoReplyEmailTest(ZulipTestCase):
|
||||||
|
|||||||
@@ -1,21 +1,17 @@
|
|||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
|
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
from zerver.lib.timestamp import floor_to_hour, floor_to_day, ceiling_to_hour, \
|
from zerver.lib.timestamp import floor_to_hour, floor_to_day, ceiling_to_hour, \
|
||||||
timestamp_to_datetime, datetime_to_timestamp, \
|
timestamp_to_datetime, datetime_to_timestamp, \
|
||||||
TimezoneNotUTCException, convert_to_UTC
|
TimezoneNotUTCException, convert_to_UTC
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta, timezone
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
import pytz
|
|
||||||
|
|
||||||
class TestTimestamp(ZulipTestCase):
|
class TestTimestamp(ZulipTestCase):
|
||||||
def test_datetime_and_timestamp_conversions(self) -> None:
|
def test_datetime_and_timestamp_conversions(self) -> None:
|
||||||
timestamp = 1483228800
|
timestamp = 1483228800
|
||||||
for dt in [
|
for dt in [
|
||||||
parser.parse('2017-01-01 00:00:00.123 UTC'),
|
parser.parse('2017-01-01 00:00:00.123 UTC'),
|
||||||
parser.parse('2017-01-01 00:00:00.123').replace(tzinfo=timezone_utc),
|
parser.parse('2017-01-01 00:00:00.123').replace(tzinfo=timezone.utc)]:
|
||||||
parser.parse('2017-01-01 00:00:00.123').replace(tzinfo=pytz.utc)]:
|
|
||||||
self.assertEqual(timestamp_to_datetime(timestamp), dt-timedelta(microseconds=123000))
|
self.assertEqual(timestamp_to_datetime(timestamp), dt-timedelta(microseconds=123000))
|
||||||
self.assertEqual(datetime_to_timestamp(dt), timestamp)
|
self.assertEqual(datetime_to_timestamp(dt), timestamp)
|
||||||
|
|
||||||
@@ -28,7 +24,7 @@ class TestTimestamp(ZulipTestCase):
|
|||||||
def test_convert_to_UTC(self) -> None:
|
def test_convert_to_UTC(self) -> None:
|
||||||
utc_datetime = parser.parse('2017-01-01 00:00:00.123 UTC')
|
utc_datetime = parser.parse('2017-01-01 00:00:00.123 UTC')
|
||||||
for dt in [
|
for dt in [
|
||||||
parser.parse('2017-01-01 00:00:00.123').replace(tzinfo=timezone_utc),
|
parser.parse('2017-01-01 00:00:00.123').replace(tzinfo=timezone.utc),
|
||||||
parser.parse('2017-01-01 00:00:00.123'),
|
parser.parse('2017-01-01 00:00:00.123'),
|
||||||
parser.parse('2017-01-01 05:00:00.123+05')]:
|
parser.parse('2017-01-01 05:00:00.123+05')]:
|
||||||
self.assertEqual(convert_to_UTC(dt), utc_datetime)
|
self.assertEqual(convert_to_UTC(dt), utc_datetime)
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Callable, Dict, List, Tuple
|
from typing import Any, Callable, Dict, List, Tuple
|
||||||
|
|
||||||
import ujson
|
import ujson
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from zerver.decorator import api_key_only_webhook_view
|
from zerver.decorator import api_key_only_webhook_view
|
||||||
@@ -41,7 +40,7 @@ class LibratoWebhookParser:
|
|||||||
def parse_violation(self, violation: Dict[str, Any]) -> Tuple[str, str]:
|
def parse_violation(self, violation: Dict[str, Any]) -> Tuple[str, str]:
|
||||||
metric_name = violation['metric']
|
metric_name = violation['metric']
|
||||||
recorded_at = datetime.fromtimestamp((violation['recorded_at']),
|
recorded_at = datetime.fromtimestamp((violation['recorded_at']),
|
||||||
tz=timezone_utc).strftime('%Y-%m-%d %H:%M:%S')
|
tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
return metric_name, recorded_at
|
return metric_name, recorded_at
|
||||||
|
|
||||||
def parse_conditions(self) -> List[Dict[str, Any]]:
|
def parse_conditions(self) -> List[Dict[str, Any]]:
|
||||||
@@ -90,7 +89,7 @@ class LibratoWebhookHandler(LibratoWebhookParser):
|
|||||||
def handle_alert_clear_message(self) -> str:
|
def handle_alert_clear_message(self) -> str:
|
||||||
alert_clear_template = "Alert [alert_name]({alert_url}) has cleared at {trigger_time} UTC!"
|
alert_clear_template = "Alert [alert_name]({alert_url}) has cleared at {trigger_time} UTC!"
|
||||||
trigger_time = datetime.fromtimestamp((self.payload['trigger_time']),
|
trigger_time = datetime.fromtimestamp((self.payload['trigger_time']),
|
||||||
tz=timezone_utc).strftime('%Y-%m-%d %H:%M:%S')
|
tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
alert_id, alert_name, alert_url, alert_runbook_url = self.parse_alert()
|
alert_id, alert_name, alert_url, alert_runbook_url = self.parse_alert()
|
||||||
content = alert_clear_template.format(alert_name=alert_name,
|
content = alert_clear_template.format(alert_name=alert_name,
|
||||||
alert_url=alert_url,
|
alert_url=alert_url,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from django.core.validators import validate_email, URLValidator
|
|||||||
from django.db import IntegrityError, transaction
|
from django.db import IntegrityError, transaction
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.timezone import utc as timezone_utc
|
|
||||||
from django.utils.translation import ugettext as _, ugettext as err_
|
from django.utils.translation import ugettext as _, ugettext as err_
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
@@ -224,7 +223,7 @@ def remote_server_post_analytics(request: HttpRequest,
|
|||||||
realm_id=row['realm'],
|
realm_id=row['realm'],
|
||||||
remote_id=row['id'],
|
remote_id=row['id'],
|
||||||
server=server,
|
server=server,
|
||||||
end_time=datetime.datetime.fromtimestamp(row['end_time'], tz=timezone_utc),
|
end_time=datetime.datetime.fromtimestamp(row['end_time'], tz=datetime.timezone.utc),
|
||||||
subgroup=row['subgroup'],
|
subgroup=row['subgroup'],
|
||||||
value=row['value']) for row in realm_counts]
|
value=row['value']) for row in realm_counts]
|
||||||
batch_create_table_data(server, RemoteRealmCount, row_objects)
|
batch_create_table_data(server, RemoteRealmCount, row_objects)
|
||||||
@@ -233,7 +232,7 @@ def remote_server_post_analytics(request: HttpRequest,
|
|||||||
property=row['property'],
|
property=row['property'],
|
||||||
remote_id=row['id'],
|
remote_id=row['id'],
|
||||||
server=server,
|
server=server,
|
||||||
end_time=datetime.datetime.fromtimestamp(row['end_time'], tz=timezone_utc),
|
end_time=datetime.datetime.fromtimestamp(row['end_time'], tz=datetime.timezone.utc),
|
||||||
subgroup=row['subgroup'],
|
subgroup=row['subgroup'],
|
||||||
value=row['value']) for row in installation_counts]
|
value=row['value']) for row in installation_counts]
|
||||||
batch_create_table_data(server, RemoteInstallationCount, row_objects)
|
batch_create_table_data(server, RemoteInstallationCount, row_objects)
|
||||||
@@ -243,7 +242,7 @@ def remote_server_post_analytics(request: HttpRequest,
|
|||||||
realm_id=row['realm'],
|
realm_id=row['realm'],
|
||||||
remote_id=row['id'],
|
remote_id=row['id'],
|
||||||
server=server,
|
server=server,
|
||||||
event_time=datetime.datetime.fromtimestamp(row['event_time'], tz=timezone_utc),
|
event_time=datetime.datetime.fromtimestamp(row['event_time'], tz=datetime.timezone.utc),
|
||||||
backfilled=row['backfilled'],
|
backfilled=row['backfilled'],
|
||||||
extra_data=row['extra_data'],
|
extra_data=row['extra_data'],
|
||||||
event_type=row['event_type']) for row in realmauditlog_rows]
|
event_type=row['event_type']) for row in realmauditlog_rows]
|
||||||
|
|||||||
Reference in New Issue
Block a user