Beispiel #1
0
def get_listen_events(
    db_conn: TimescaleListenStore,
    musicbrainz_ids: List[str],
    min_ts: int,
    max_ts: int,
    count: int,
    time_range: int,
) -> List[APITimelineEvent]:
    """ Gets all listen events in the feed.
    """

    # NOTE: For now, we get a bunch of listens for the users the current
    # user is following and take a max of 2 out of them per user. This
    # could be done better by writing a complex query to get exactly 2 listens for each user,
    # but I'm happy with this heuristic for now and we can change later.
    db_conn = webserver.create_timescale(current_app)
    listens = db_conn.fetch_listens_for_multiple_users_from_storage(
        musicbrainz_ids,
        limit=count,
        from_ts=min_ts,
        to_ts=max_ts,
        time_range=time_range,
        order=0,  # descending
    )

    user_listens_map = defaultdict(list)
    for listen in listens:
        if len(user_listens_map[
                listen.user_name]) < MAX_LISTEN_EVENTS_PER_USER:
            user_listens_map[listen.user_name].append(listen)

    events = []
    for user in user_listens_map:
        for listen in user_listens_map[user]:
            try:
                listen_dict = listen.to_api()
                listen_dict['inserted_at'] = listen_dict[
                    'inserted_at'].timestamp()
                api_listen = APIListen(**listen_dict)
                events.append(
                    APITimelineEvent(
                        event_type=UserTimelineEventType.LISTEN,
                        user_name=api_listen.user_name,
                        created=api_listen.listened_at,
                        metadata=api_listen,
                    ))
            except pydantic.ValidationError as e:
                current_app.logger.error('Validation error: ' + str(e),
                                         exc_info=True)
                continue

    return events
def get_recording_recommendation_events(users_for_events: List[dict],
                                        min_ts: int, max_ts: int,
                                        count: int) -> List[APITimelineEvent]:
    """ Gets all recording recommendation events in the feed.
    """

    id_username_map = {
        user['id']: user['musicbrainz_id']
        for user in users_for_events
    }
    recording_recommendation_events_db = db_user_timeline_event.get_recording_recommendation_events_for_feed(
        user_ids=(user['id'] for user in users_for_events),
        min_ts=min_ts,
        max_ts=max_ts,
        count=count,
    )

    events = []
    for event in recording_recommendation_events_db:
        try:
            listen = APIListen(
                user_name=id_username_map[event.user_id],
                track_metadata=TrackMetadata(
                    artist_name=event.metadata.artist_name,
                    track_name=event.metadata.track_name,
                    release_name=event.metadata.release_name,
                    additional_info=AdditionalInfo(
                        recording_msid=event.metadata.recording_msid,
                        recording_mbid=event.metadata.recording_mbid,
                        artist_msid=event.metadata.artist_msid,
                    )),
            )

            events.append(
                APITimelineEvent(
                    id=event.id,
                    event_type=UserTimelineEventType.RECORDING_RECOMMENDATION,
                    user_name=listen.user_name,
                    created=event.created.timestamp(),
                    metadata=listen,
                ))
        except pydantic.ValidationError as e:
            current_app.logger.error('Validation error: ' + str(e),
                                     exc_info=True)
            continue
    return events
def get_listen_events(
    users: List[Dict],
    min_ts: int,
    max_ts: int,
) -> List[APITimelineEvent]:
    """ Gets all listen events in the feed.
    """
    # to avoid timeouts while fetching listen events, we want to make
    # sure that both min_ts and max_ts are defined. if only one of those
    # is set, calculate the other from it using a default window length.
    # if neither is set, use current time as max_ts and subtract window
    # length to get min_ts.
    if not min_ts and max_ts:
        min_ts = max_ts - DEFAULT_LISTEN_EVENT_WINDOW
    elif min_ts and not max_ts:
        max_ts = min_ts + DEFAULT_LISTEN_EVENT_WINDOW
    elif not min_ts and not max_ts:
        max_ts = int(datetime.now().timestamp())
        min_ts = max_ts - DEFAULT_LISTEN_EVENT_WINDOW

    listens = timescale_connection._ts.fetch_recent_listens_for_users(
        users,
        min_ts=min_ts,
        max_ts=max_ts,
        per_user_limit=MAX_LISTEN_EVENTS_PER_USER,
        limit=MAX_LISTEN_EVENTS_OVERALL)

    events = []
    for listen in listens:
        try:
            listen_dict = listen.to_api()
            listen_dict['inserted_at'] = listen_dict['inserted_at'].timestamp()
            api_listen = APIListen(**listen_dict)
            events.append(
                APITimelineEvent(
                    event_type=UserTimelineEventType.LISTEN,
                    user_name=api_listen.user_name,
                    created=api_listen.listened_at,
                    metadata=api_listen,
                ))
        except pydantic.ValidationError as e:
            current_app.logger.error('Validation error: ' + str(e),
                                     exc_info=True)
            continue
    return events