Skip to content

Commit 69ddffa

Browse files
committed
Merge branch 'master' into add-cache-tables-script
2 parents f877772 + 79fe1a9 commit 69ddffa

23 files changed

+108
-117
lines changed

Diff for: frontend/js/src/about/add-data/AddData.tsx

-8
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,6 @@ export default function AddData() {
330330
</em>
331331
, a scrobbling application for Android Devices
332332
</li>
333-
<li>
334-
<em>
335-
<a href="https://github.com/tgwizard/sls">
336-
Simple Last.fm Scrobbler
337-
</a>
338-
</em>
339-
, for Android devices
340-
</li>
341333
</ul>
342334

343335
<h4>Scripts</h4>

Diff for: listenbrainz/db/external_service_oauth.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22
from typing import List, Optional, Union
33

44
from sqlalchemy import text
55

66
from data.model.external_service import ExternalServiceType
7-
from listenbrainz import db, utils
87
import sqlalchemy
98

109

@@ -32,7 +31,7 @@ def save_token(db_conn, user_id: int, service: ExternalServiceType, access_token
3231
# to use the new values. any column which does not have a new value to be set should
3332
# be explicitly set to the default value (which would have been used if the row was
3433
# inserted instead).
35-
token_expires = utils.unix_timestamp_to_datetime(token_expires_ts) if token_expires_ts else None
34+
token_expires = datetime.fromtimestamp(token_expires_ts, timezone.utc) if token_expires_ts else None
3635
result = db_conn.execute(sqlalchemy.text("""
3736
INSERT INTO external_service_oauth AS eso
3837
(user_id, external_user_id, service, access_token, refresh_token, token_expires, scopes)
@@ -121,7 +120,7 @@ def update_token(db_conn, user_id: int, service: ExternalServiceType, access_tok
121120
refresh_token: the new token used to refresh access tokens, if omitted the old token in the database remains unchanged
122121
expires_at: the unix timestamp at which the access token expires
123122
"""
124-
token_expires = utils.unix_timestamp_to_datetime(expires_at)
123+
token_expires = datetime.fromtimestamp(expires_at, timezone.utc)
125124
params = {
126125
"access_token": access_token,
127126
"token_expires": token_expires,

Diff for: listenbrainz/db/listens_importer.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import datetime
1+
from datetime import datetime, timezone
22
from typing import Optional, Union
33

44
from sqlalchemy import text
55

66
from data.model.external_service import ExternalServiceType
7-
from listenbrainz import utils
87
import sqlalchemy
98

109

@@ -51,12 +50,12 @@ def update_latest_listened_at(db_conn, user_id: int, service: ExternalServiceTyp
5150
"""), {
5251
'user_id': user_id,
5352
'service': service.value,
54-
'timestamp': utils.unix_timestamp_to_datetime(timestamp),
53+
'timestamp': datetime.fromtimestamp(timestamp, timezone.utc),
5554
})
5655
db_conn.commit()
5756

5857

59-
def get_latest_listened_at(db_conn, user_id: int, service: ExternalServiceType) -> Optional[datetime.datetime]:
58+
def get_latest_listened_at(db_conn, user_id: int, service: ExternalServiceType) -> Optional[datetime]:
6059
""" Returns the timestamp of the last listen imported for the user with
6160
specified LB user ID from the given service.
6261

Diff for: listenbrainz/db/pinned_recording.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import sqlalchemy
2-
from datetime import datetime
2+
from datetime import datetime, timezone
33

4-
from listenbrainz import db
54
from listenbrainz.db.model.pinned_recording import PinnedRecording, WritablePinnedRecording
65
from typing import List, Iterable
76

@@ -223,8 +222,8 @@ def get_pins_for_feed(db_conn, user_ids: Iterable[int], min_ts: int, max_ts: int
223222
LIMIT :count
224223
""".format(columns=','.join(PINNED_REC_GET_COLUMNS))), {
225224
"user_ids": tuple(user_ids),
226-
"min_ts": datetime.utcfromtimestamp(min_ts),
227-
"max_ts": datetime.utcfromtimestamp(max_ts),
225+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
226+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
228227
"count": count,
229228
})
230229
return [PinnedRecording(**row) for row in result.mappings()]

Diff for: listenbrainz/db/user_relationship.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# with this program; if not, write to the Free Software Foundation, Inc.,
1717
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818

19-
from datetime import datetime
19+
from datetime import datetime, timezone
2020
from typing import List, Iterable
2121

2222
import sqlalchemy
@@ -158,8 +158,8 @@ def get_follow_events(db_conn, user_ids: Iterable[int], min_ts: float, max_ts: f
158158
LIMIT :count
159159
"""), {
160160
"user_ids": tuple(user_ids),
161-
"min_ts": datetime.utcfromtimestamp(min_ts),
162-
"max_ts": datetime.utcfromtimestamp(max_ts),
161+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
162+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
163163
"count": count
164164
})
165165

Diff for: listenbrainz/db/user_timeline_event.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import sqlalchemy
2020
import orjson
2121

22-
from datetime import datetime
22+
from datetime import datetime, timezone
2323

2424
from sqlalchemy import text
2525

@@ -34,9 +34,8 @@
3434
HiddenUserTimelineEvent,
3535
PersonalRecordingRecommendationMetadata, WritePersonalRecordingRecommendationMetadata
3636
)
37-
from listenbrainz import db
3837
from listenbrainz.db.exceptions import DatabaseException
39-
from typing import List, Tuple, Iterable
38+
from typing import List, Iterable
4039

4140
from listenbrainz.db.model.review import CBReviewTimelineMetadata
4241

@@ -196,8 +195,8 @@ def get_user_timeline_events(
196195
LIMIT :count
197196
"""), {
198197
"user_ids": tuple(user_ids),
199-
"min_ts": datetime.utcfromtimestamp(min_ts),
200-
"max_ts": datetime.utcfromtimestamp(max_ts),
198+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
199+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
201200
"event_type": event_type.value,
202201
"count": count,
203202
})
@@ -255,8 +254,8 @@ def get_personal_recommendation_events_for_feed(db_conn, user_id: int, min_ts: i
255254
LIMIT :count
256255
"""), {
257256
"user_id": user_id,
258-
"min_ts": datetime.utcfromtimestamp(min_ts),
259-
"max_ts": datetime.utcfromtimestamp(max_ts),
257+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
258+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
260259
"count": count,
261260
"event_type": UserTimelineEventType.PERSONAL_RECORDING_RECOMMENDATION.value,
262261
})
@@ -297,8 +296,8 @@ def get_thanks_events_for_feed(db_conn, user_id: int, min_ts: int, max_ts: int,
297296
LIMIT :count
298297
"""), {
299298
"user_id": user_id,
300-
"min_ts": datetime.utcfromtimestamp(min_ts),
301-
"max_ts": datetime.utcfromtimestamp(max_ts),
299+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
300+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
302301
"count": count,
303302
"event_type": UserTimelineEventType.THANKS.value,
304303
})

Diff for: listenbrainz/listen.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
# coding=utf-8
2-
import calendar
31
from copy import deepcopy
4-
from datetime import datetime
2+
from datetime import datetime, timezone
53

64
import orjson
75

@@ -76,7 +74,7 @@ def __init__(self, user_id=None, user_name=None, timestamp=None, recording_msid=
7674
# determine the type of timestamp and do the right thing
7775
if isinstance(timestamp, int) or isinstance(timestamp, float):
7876
self.ts_since_epoch = int(timestamp)
79-
self.timestamp = datetime.utcfromtimestamp(self.ts_since_epoch)
77+
self.timestamp = datetime.fromtimestamp(self.ts_since_epoch, timezone.utc)
8078
else:
8179
if timestamp:
8280
self.timestamp = timestamp
@@ -105,12 +103,12 @@ def from_json(cls, j):
105103
"""Factory to make Listen() objects from a dict"""
106104
# Let's go play whack-a-mole with our lovely whicket of timestamp fields. Hopefully one will work!
107105
try:
108-
j['listened_at'] = datetime.utcfromtimestamp(float(j['listened_at']))
106+
j['listened_at'] = datetime.fromtimestamp(float(j['listened_at']), timezone.utc)
109107
except KeyError:
110108
try:
111-
j['listened_at'] = datetime.utcfromtimestamp(float(j['timestamp']))
109+
j['listened_at'] = datetime.fromtimestamp(float(j['timestamp']), timezone.utc)
112110
except KeyError:
113-
j['listened_at'] = datetime.utcfromtimestamp(float(j['ts_since_epoch']))
111+
j['listened_at'] = datetime.fromtimestamp(float(j['ts_since_epoch']), timezone.utc)
114112

115113
return cls(
116114
user_id=j.get('user_id'),

Diff for: listenbrainz/listenstore/tests/test_timescale_utils.py

+31-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22

33
from sqlalchemy import text
44

@@ -35,8 +35,8 @@ def _get_count_and_timestamp(self, user):
3535
row = result.fetchone()
3636
return {
3737
"count": row.count,
38-
"min_listened_at": row.min_listened_at.replace(tzinfo=None),
39-
"max_listened_at": row.max_listened_at.replace(tzinfo=None)
38+
"min_listened_at": row.min_listened_at,
39+
"max_listened_at": row.max_listened_at
4040
}
4141

4242
def test_delete_listens_update_metadata(self):
@@ -49,35 +49,51 @@ def test_delete_listens_update_metadata(self):
4949
update_user_listen_data()
5050

5151
metadata_1 = self._get_count_and_timestamp(user_1)
52-
self.assertEqual(metadata_1["min_listened_at"], datetime.utcfromtimestamp(1400000000))
53-
self.assertEqual(metadata_1["max_listened_at"], datetime.utcfromtimestamp(1400000200))
52+
self.assertEqual(metadata_1["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
53+
self.assertEqual(metadata_1["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
5454
self.assertEqual(metadata_1["count"], 5)
5555

5656
metadata_2 = self._get_count_and_timestamp(user_2)
57-
self.assertEqual(metadata_2["min_listened_at"], datetime.utcfromtimestamp(1400000000))
58-
self.assertEqual(metadata_2["max_listened_at"], datetime.utcfromtimestamp(1400000200))
57+
self.assertEqual(metadata_2["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
58+
self.assertEqual(metadata_2["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
5959
self.assertEqual(metadata_2["count"], 5)
6060

6161
# to test the case when the update script has not run since delete, so metadata in listen_user_metadata does
6262
# account for this listen and deleting should not affect it either.
6363
self._create_test_data(user_1, "timescale_listenstore_test_listens_2.json")
64-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000500), user_1["id"], "4269ddbc-9241-46da-935d-4fa9e0f7f371")
64+
self.ls.delete_listen(
65+
datetime.fromtimestamp(1400000500, timezone.utc),
66+
user_1["id"],
67+
"4269ddbc-9241-46da-935d-4fa9e0f7f371"
68+
)
6569

6670
# test min_listened_at is updated if that listen is deleted for a user
67-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000000), user_1["id"], "4269ddbc-9241-46da-935d-4fa9e0f7f371")
71+
self.ls.delete_listen(
72+
datetime.fromtimestamp(1400000000, timezone.utc),
73+
user_1["id"],
74+
"4269ddbc-9241-46da-935d-4fa9e0f7f371"
75+
)
6876
# test max_listened_at is updated if that listen is deleted for a user
69-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000200), user_1["id"], "db072fa7-0c7f-4f55-b90f-a88da531b219")
77+
self.ls.delete_listen(
78+
datetime.fromtimestamp(1400000200, timezone.utc),
79+
user_1["id"],
80+
"db072fa7-0c7f-4f55-b90f-a88da531b219"
81+
)
7082
# test normal listen delete updates correctly
71-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000100), user_2["id"], "08ade1eb-800e-4ad8-8184-32941664ac02")
83+
self.ls.delete_listen(
84+
datetime.fromtimestamp(1400000100, timezone.utc),
85+
user_2["id"],
86+
"08ade1eb-800e-4ad8-8184-32941664ac02"
87+
)
7288

7389
delete_listens()
7490

7591
metadata_1 = self._get_count_and_timestamp(user_1)
76-
self.assertEqual(metadata_1["min_listened_at"], datetime.utcfromtimestamp(1400000050))
77-
self.assertEqual(metadata_1["max_listened_at"], datetime.utcfromtimestamp(1400000150))
92+
self.assertEqual(metadata_1["min_listened_at"], datetime.fromtimestamp(1400000050, timezone.utc))
93+
self.assertEqual(metadata_1["max_listened_at"], datetime.fromtimestamp(1400000150, timezone.utc))
7894
self.assertEqual(metadata_1["count"], 3)
7995

8096
metadata_2 = self._get_count_and_timestamp(user_2)
81-
self.assertEqual(metadata_2["min_listened_at"], datetime.utcfromtimestamp(1400000000))
82-
self.assertEqual(metadata_2["max_listened_at"], datetime.utcfromtimestamp(1400000200))
97+
self.assertEqual(metadata_2["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
98+
self.assertEqual(metadata_2["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
8399
self.assertEqual(metadata_2["count"], 4)

Diff for: listenbrainz/testdata/sitewide_top_artists_db.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
],
1515
"to_ts": 1593043183,
1616
"stats_range": "all_time",
17-
"count": 2
17+
"count": 2,
18+
"total_artist_count": 123
1819
}

Diff for: listenbrainz/testdata/sitewide_top_artists_db_data_for_api_test.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,6 @@
301301
"artist_mbid": "12379346-99d9-4941-b4a2-f04144a39863"
302302
}
303303
],
304-
"count": 60
305-
}
304+
"count": 60,
305+
"total_artist_count": 60
306+
}

Diff for: listenbrainz/testdata/sitewide_top_artists_db_data_for_api_test_month.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,6 @@
301301
"artist_mbid": "78df245f-0fe0-4a21-b1d4-e79f695354fd"
302302
}
303303
],
304-
"count": 60
305-
}
304+
"count": 60,
305+
"total_artist_count": 60
306+
}

Diff for: listenbrainz/testdata/sitewide_top_artists_db_data_for_api_test_too_many.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1011,5 +1011,6 @@
10111011
"artist_mbid": "218bbe3d-63cc-46bc-8e43-e56a2f6c9829"
10121012
}
10131013
],
1014-
"count": 202
1015-
}
1014+
"count": 202,
1015+
"total_artist_count": 202
1016+
}

Diff for: listenbrainz/testdata/sitewide_top_artists_db_data_for_api_test_week.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1001,5 +1001,6 @@
10011001
"artist_mbid": "a2c1ed01-991f-4a9d-81e0-9b04c8df1c80"
10021002
}
10031003
],
1004-
"count": 200
1005-
}
1004+
"count": 200,
1005+
"total_artist_count": 200
1006+
}

Diff for: listenbrainz/testdata/sitewide_top_artists_db_data_for_api_test_year.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,6 @@
301301
"artist_mbid": "a63d3de8-9c39-4320-8199-784df3f4c74c"
302302
}
303303
],
304-
"count": 60
305-
}
304+
"count": 60,
305+
"total_artist_count": 60
306+
}

Diff for: listenbrainz/tests/integration/test_stats_api.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ def assertSitewideStatEqual(self, sent, response, entity, stats_range, count, of
245245

246246
received = orjson.loads(response.data)['payload']
247247

248-
self.assertEqual(sent['count'], received['count'])
248+
singular_entity = entity[:-1] if entity.endswith('s') else entity
249+
self.assertEqual(sent[f'total_{singular_entity}_count'], received[f'total_{singular_entity}_count'])
250+
self.assertEqual(count, received['count'])
249251
self.assertEqual(sent['from_ts'], received['from_ts'])
250252
self.assertEqual(sent['to_ts'], received['to_ts'])
251253
self.assertEqual(stats_range, received['range'])

Diff for: listenbrainz/tests/unit/test_utils.py

-9
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@
1616
# with this program; if not, write to the Free Software Foundation, Inc.,
1717
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818

19-
import listenbrainz.utils as utils
20-
import time
2119
import unittest
2220
import uuid
2321

24-
from datetime import datetime
2522
from listenbrainz.webserver import create_app
2623
from listenbrainz.webserver.views.api_tools import is_valid_uuid
2724

@@ -31,12 +28,6 @@ class ListenBrainzUtilsTestCase(unittest.TestCase):
3128
def setUp(self):
3229
self.app = create_app(debug=True) # create an app for config value access
3330

34-
def test_unix_timestamp_to_datetime(self):
35-
t = int(time.time())
36-
x = utils.unix_timestamp_to_datetime(t)
37-
self.assertIsInstance(x, datetime)
38-
self.assertEqual(int(x.strftime('%s')), t)
39-
4031
def test_valid_uuid(self):
4132
self.assertTrue(is_valid_uuid(str(uuid.uuid4())))
4233
self.assertFalse(is_valid_uuid('hjjkghjk'))

Diff for: listenbrainz/tests/utils.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# coding=utf-8
22

3-
from datetime import datetime, date
3+
from datetime import datetime, timezone
44
import time
5-
import sys
6-
import os
75

86
from listenbrainz.listen import Listen
97
import uuid
@@ -21,7 +19,7 @@ def generate_data(db_conn, from_date, num_records, user_name):
2119
item = Listen(
2220
user_id=user['id'],
2321
user_name=user_name,
24-
timestamp=datetime.utcfromtimestamp(current_date),
22+
timestamp=datetime.fromtimestamp(current_date, timezone.utc),
2523
recording_msid=str(uuid.uuid4()),
2624
data={
2725
'artist_name': 'Test Artist Pls ignore',

0 commit comments

Comments
 (0)