GSoC 2022: Proposal Draft for "Add Timezone Support to ListenBrainz"

Personal information

Name: Youzhi Liang
IRC nick: yuzie
Email: youzhi797@gmail.com
GitHub: YouzhiL (Youzhi) · GitHub
Time Zone:UTC-04:00

Overview

ListenBrainz helps users to track their music listening habits and analyzes their music taste. Such technology requires accurate user data to make the best decisions. However, at current state of the product, all the listens are collected in the UTC instead of their local timestamp, without taking DST in to consideration. Adding timezone preference for users and adding the time zone field in submitted listens will contribute to the analysis and thus provide better user experience. This project aims to add users’ timezone preference paired with their submitted listens.

Goals

  1. Users can set timezone preference in the setting page
  • Design and implement an appropriate setting table which contains the user timezone preference, this table can be reused to store more user preferences.
  • Design a React UI for the settings page so that users can set and edit their timezone in the setting page.
  • Add an API endpoint to fetch/set user timezone.
  1. Users can add local timestamp field in the submit listens
  • Add local timestamp in submission json of new listens
  • validate and test the new field and modify the functions of inserting and fetching listens regarding the new field

Ideas

Currently, all the timezone-sensitive data are stored in UTC timezone, which is a good practice, because this avoid possible inconsistency if stored in user timezone or server timezone. So the basic idea is to store dates in UTC and how we query and display it to users varies in different cases.

  • For any existing listens that have no timezones and user has a preference for timezone, show it in user selected timezone. If user does not have a preference for timezone, use the default.
  • For future listens, if the submission does have a local timestamp, show it in the user selected timezone. If user does not have a preference for timezone, use the default.
  • For future listens, if the submission has a local timestamp, show it in that timestamp.

Implementations

Task1: Users can set Timezone Preference in the setting page

database

For users to edit their preferences, we will create a new table user_setting in our database. We store user timezone and other preferences in this table. We choose to store the timezone name instead of the offset because the offset can change for daylight savings. PostgreSQL supports identifying IANA timezone identifier.

CREATE TABLE "user_setting" (
    id                  SERIAL,
    user_id             INTEGER NOT NULL,
    zone_name           TEXT NOT NULL CHECK (now() AT TIME ZONE zone_name IS NOT NULL)
)

now() AT TIME ZONE zone_name IS NOT NULL will check if the zone_name can be recognized by PostgreSQL. If not, it will throw an error timezone not recognized, which can be catched by SQLAlchemy at the endpoint. Since we provides a dropbox for the users to select the timezone in frontend, which already limit the options to what PG can recognize, this validation will serve as a backup.

UI Mock-up

  1. Add a Reset Timezone UI module in the general setting page.

  2. After clicking the button”Reset Timezone” in the general setting page, users will be routed to a Reset Timezone page. This page includes a dropdown list, which contains all world timezones that PostgreSQL supports(perhaps part of all the 589 timezones).

API endpint

@profile_bp.route("/reset_time_zone/", methods=["GET", "POST"])
@login_required
def reset_time_zone():
    if request.method == 'POST':
         time_zone = request.form.get('time_zone')
         try:
              update_user_timezone(current_user.id, time_zone)
              flash.info('Successfully update the timezone for %s.' % current_user.musicbrainz_id)
              return redirect(url_for('profile.info'))
              except Exception:
                   # Handle exception
    else:
        #render template

This API design is based on the assumption that we directly add the timezone setting to the existing settings page. If we have a new setting page in the future to allow users editing more preferences. We can have a general setting API.

@profile_bp.route('/settings/', methods=["GET", "POST"])
@login_required
def user_settings():     
    if request.method == 'POST':
        time_zone = request.form.get('time_zone')
        other_preference = request.form.get('other_preference')
        if time_zone:
            try:
                update_user_timezone(current_user.id, time_zone)
                flash.info('Successfully update the timezone for %s.' % current_user.musicbrainz_id)
                return redirect(url_for('setting'))
            except Exception:
               # Handle exception
        elif other_preference:
            # Update other preference
    else:
        #render template

In ListenBrainz/db, we can add a user_setting.py for sql expression.

def update_timezone(user_id: int, zone_name:str):
    with db.engine.connect() as connection:
        try:
            connection.execute(sqlalchemy.text("""
                UPDATE "user_setting"
                   SET zone_name = :zone_name
                 WHERE user_id = :user_id
                """), {
                "user_id": user_id,
                "zone_name": zone_name,
            })
        except sqlalchemy.exc.ProgrammingError as err:
            logger.error(err)
            raise DatabaseException(
                "Couldn't update user's timezone: %s" % str(err))

Task2: Users can add timezone field in the listens submission json

This part is a stretch from the initial project. The basic idea on how to deal with local listen_time in API can be referenced in [LB-642] Specify a field for "local time" for listens - MetaBrainz JIRA.

  1. We alter json format by adding timezone field to allow users submitting their local timestamps in an ISO8601 string. The format should be limited to one of following types:
    YYYY-MM-DDTHH:MM:SS.ffffff+HH:MM[:SS[.ffffff]] if microsecond is not 0
    YYYY-MM-DDTHH:MM:SS+HH:MM[:SS[.ffffff]] if microsecond is 0
    A possible json example will be:
{
  "listened_at": 1443521965,
  "track_metadata": {
    "additional_info": {
      "listened_at_local": "2015-09-29T03:19:25-07:00",
      "release_mbid": "bf9e91ea-8029-4a04-a26a-224e00a83266",
    },
    "artist_name": "Rick Astley",
    "track_name": "Never Gonna Give You Up",
    "release_name": "Whenever you need somebody"
  }
}
  1. If listened_at_local is specified, then listened_at will be ignored. When parsing json file in API endpoint, we convert it to python datetime object:
>>> listened_at_local = datetime.fromisoformat("2011-11-04T00:05:23+05:00")
>>> listened_at_local
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone(datetime.timedelta(seconds=18000)))
  1. We can extract the timezone information from the datetime object and store it as a separate row in database.
>>> listened_at_local.tzname()
'UTC+05:00'

Schedule

This is a 175-hour projects, so I keep it short in schedule (around 10 weeks). However, I’d like to go beyond after completing this project. I’m open to new ideas and would like to contribute to more implementations.

Pre-Community Bonding Period ( April ):

In this period, I will work on tickets to be more familiar with the code and workflow.

Community Bonding Period ( May ):

In this period, I will continue working on the pending PRs. I will also discuss with my mentors to finalize the UI and decide on a clear roadmap.

Week 1

  1. Design and add a user timezone table in the database.
  2. Design and implement the front-end UI for the setting page.
  3. Design and implement the API endpoint and db side operations.

Week 2,3

  1. Continue to implement the API endpoint.
  2. Test for the UI and API endpoint.

Week 4

  1. Add listened_at_local field to submit json.
  2. Update the document for users by clarifying the accepted format of local timestamp.
  3. Write validation for the listened_at_local field.

Week5,6

  1. Modify the listen table by adding listened_at_local field.
  2. Modify the functions of inserting and fetching listens regarding listened_at field

Week 7,8

  1. Write tests and debug for the modification.

Week 9

  1. Continue to debug and test if not finished.
  2. Write document for the work done.

Week 10

  1. Finish any left work.
  2. Submit code and document.
  3. prepare to go beyond with the community :slight_smile:

About me

I am Youzhi Liang (feel free to call me yuzie, which is also my nickname in irc). I am a first year master student at Duke University. I majored in Electrical Engineering in my undergraduate study and am currently studying Computer science(minor in Economics). I have solid background in web development, iOS mobile app development and Natural Language Processing. I feel privileged to join this open-source community and I am really looking forward to making contributions to make our current product better.

I am new to open source, but I have been joining the discussions in the MetaBrainz IRC and community forum for some days. I find that I really love the way people discuss and help with each other in this community. This the first time I get in touch with a complex and influential projects, and I mastered lots of new skills during the process of getting familiar with the project. This is really a good start for me to become a professional software engineer. I really appreciate all the help from the community.

Other Information

Tell us about the computer(s) you have available for working on your SoC project!

  • Mac mini M1
  • MacBook pro Intel

When did you first start programming?

  • Since my first undergraduate year, around 2016

What type of music do you listen to?

  • Classic music and country music

What aspects of the project you’re applying for interest you the most?

  • One aspect is that I like to do work which includes both front – end and back- end. Another is that when I look through the posts on the forum, I find some of people suggest the timezone feature would be useful, so I will be happy if my work can help improve the user experience.

Have you ever used MusicBrainz to tag your files?

  • Yes, and still exploring features of Picard.

Have you contributed to other open source projects?

  • I am new to open source, but I myself is benefitted a lot from the open source community while learning to develop software. So I hope to become an open source contributor so that I can serve for the open source community.

Recent project?

How much time do you have available, and how would you plan to use it?

  • I plan to distribute 18 hour per week for this project from May and spend 10weeks to finish it.
3 Likes

Hi!

Thanks for the proposal. Some initial comments and questions:

  1. As discussed in IRC, we want to reuse the frontend/backend for timezone preference for other settings in future. So we shouldn’t include timezone in the table or api name.
  2. Do you suggest one endpoint for each setting when we add more settings or one endpoint for all? And any other thoughts you have on how it’ll work?
  3. Why do we need to store both zone_name and utc_offset?
  4. We should include a constraint to validate the data stored in the zone_name column.
  5. For including the timezone info in the API, I think its fine to include it as a stretch goal. Only to be done if time permits.
  6. On how to do timezone info in the API, we want to have something like [LB-642] Specify a field for "local time" for listens - MetaBrainz JIRA . There are some nuances around it which need to be taken care of. I’ll add those in the ticket as a comment.

Hi!

Thanks for your proposal! Some questions:

  1. Is there a specific reason why you decided to use the “reset timezone” UI concept? I would expect that the user would be shown a general preferences dialog with a timezone drop down that lists all the timezones and UTC. For users who have never set a timezone I would expect that the UTC timezone was selected. The user could pick another timezone and simply hit save. Does it need to be more complicated than this?

I think this sentence is a bit inaccurate, since you also propose to add timezones to the listen submission. Perhaps it would be more correct to say “listens that have no specified timezone will be shown with the user selected timezone.”

In general we think that timezones should work as follows:

  1. For any existing listens that have no timezones, show the listens using the user selected timezone.
  2. For future listens, if the submission does not have a timezone, use the user selected timezone.
  3. For future listens, if the submission does have a timezone, ignore the user selected timezone.

As far as how timezones should be submitted, @lucifer already mentioned LB-642.

1 Like

Hi,

Thanks for the reply!

  1. For now, we decide to put the timezone preference in the existing setting page. So like other preferences in that page, users will be directed to a detail page by clicking “Reset Timezone” on the general setting page. The detail page is where the drop down locates and triggers the API endpoint. There maybe changes for the setting page, which I will continue discussing with mentors.
  2. Yes, these are the cases that needs consider for how to use timezone preference. I add those cases in my proposal now. Thanks for pointing it out.
  3. For how listen_at_local should be submitted in api, my current thought is to ask user to submit a local ISO format timestamp. When we store it in database, we convert it to timestamp with time zone type.

Please let me know if you have any questions or comments.