QasimWani / LeetHub

Automatically sync your leetcode solutions to your github account - top 5 trending GitHub repository
https://chrome.google.com/webstore/detail/leethub/aciombdipochlnkbpcbgdpjffcfdbggi
MIT License
3.97k stars 1.39k forks source link

[Feature Request] Add "sync all solved problems" button #77

Closed JasperSui closed 3 years ago

JasperSui commented 3 years ago

Hi Qasim Wani,

It's a very useful project, and how about having a "sync all solved problems" button to sync all solved problems on LeetCode but not in the specific repository?

It will be helpful for those who used to solve problems on LeetCode but are new to your extension.

Thank you for your contribution, I really appreciate it!

QasimWani commented 3 years ago

@JasperSui see #15 the main problem with this is that the only way to do this is by brute-forcing every single solved problem. Getting a list of solved problem along with URL is straightforward, taking less than a second. See the following code to do that:


const getAllSolvedProblems = () => {
  const LEETCODE_API_URL = 'https://leetcode.com/api/problems/algorithms';

  const LEETCODE_PROBLEM_URL_PREFIX = "https://leetcode.com/problems/";

  const xhr = new XMLHttpRequest();

  /* Gather data */
  var problems_data = [];

  xhr.addEventListener('readystatechange', function () {
    if (xhr.readyState === 4) {
      let problems = JSON.parse(xhr.responseText)["stat_status_pairs"];
      problems.forEach(problem => {
        if(problem["status"] && problem["status"] === "ac")
        {
          var stat = problem["stat"];
          let problem_data = {"title" : stat["question__title"], "url" : LEETCODE_PROBLEM_URL_PREFIX + stat["question__title_slug"], "id" : stat["frontend_question_id"]};
          problems_data.push(problem_data);
        }
      });
      console.log(problems_data[0]);
    }
  });

  xhr.open('POST', LEETCODE_API_URL, true);
  xhr.send();
};

The problem lies in getting the actual code for an existing problem. There's no public facing leetcode api that does this. This means that LeetHub would need to run N requests (N = number of solved leetcode problems that aren't already on LeetHub) causing the browser to be very slow. Since we can take care of it asynchronously, the process can very easily break down if you close the page (say welcome.html) that performs this action. Sure we can store this, but the main drawback that I see is that this will be very slow.

I personally think this is a great idea (see link above for past discussion) but I can't come up with a more time efficient approach. If you (or anyone else) has a better way of doing it, please submit a PR!

JasperSui commented 3 years ago

@QasimWani First, thank you for instant reply!

I totally agree with your opinion that sync all problems one by one will facing some unexpected errors (closing welcome page, network issue, e.g.), I'll try to think about a better way after fully understanding this project.

Thank you again about explaining the reason why you chose not to do this and having an open mind!

Have a great day!

LeeHeonSik commented 2 years ago

77

Hi, thank you for making Leethub. I have another question about this. How can I use this? Please explain more specific if you can. thank U so much and I'm sorry if I wasn't polite :)

rockharshitmaurya commented 2 years ago

@JasperSui see #15 the main problem with this is that the only way to do this is by brute-forcing every single solved problem. Getting a list of solved problem along with URL is straightforward, taking less than a second. See the following code to do that:

const getAllSolvedProblems = () => {
  const LEETCODE_API_URL = 'https://leetcode.com/api/problems/algorithms';

  const LEETCODE_PROBLEM_URL_PREFIX = "https://leetcode.com/problems/";

  const xhr = new XMLHttpRequest();

  /* Gather data */
  var problems_data = [];

  xhr.addEventListener('readystatechange', function () {
    if (xhr.readyState === 4) {
      let problems = JSON.parse(xhr.responseText)["stat_status_pairs"];
      problems.forEach(problem => {
        if(problem["status"] && problem["status"] === "ac")
        {
          var stat = problem["stat"];
          let problem_data = {"title" : stat["question__title"], "url" : LEETCODE_PROBLEM_URL_PREFIX + stat["question__title_slug"], "id" : stat["frontend_question_id"]};
          problems_data.push(problem_data);
        }
      });
      console.log(problems_data[0]);
    }
  });

  xhr.open('POST', LEETCODE_API_URL, true);
  xhr.send();
};

The problem lies in getting the actual code for an existing problem. There's no public facing leetcode api that does this. This means that LeetHub would need to run N requests (N = number of solved leetcode problems that aren't already on LeetHub) causing the browser to be very slow. Since we can take care of it asynchronously, the process can very easily break down if you close the page (say welcome.html) that performs this action. Sure we can store this, but the main drawback that I see is that this will be very slow.

I personally think this is a great idea (see link above for past discussion) but I can't come up with a more time efficient approach. If you (or anyone else) has a better way of doing it, please submit a PR!

there is a pre-existing repo on GitHub which can sync all previous submissions please take a look at that repo's code to get some idea and add a new feature for adding previously solved problem

check this https://github.com/joshcai/leetcode-sync

Ifechukwudaniel commented 2 years ago

Actually, Leetcode stores all your previously solved problems in local storage so it is very possible, all I have to do is reverse engineer it , you also need the question number

Screenshot 2022-02-17 at 17 39 01 Screenshot 2022-02-17 at 17 39 05
Ifechukwudaniel commented 2 years ago

All i just need is the go ahead

QasimWani commented 2 years ago

All i just need is the go ahead

Sure thing. Plz join our discord (see readMe) and we can chat over there for updates

Ifechukwudaniel commented 2 years ago

😎😎😎😎

Sanojkumaryadav commented 2 years ago

https://github.com/QasimWani/LeetHub/issues/83

vigneshkumar28 commented 9 months ago

@JasperSui see #15 the main problem with this is that the only way to do this is by brute-forcing every single solved problem. Getting a list of solved problem along with URL is straightforward, taking less than a second. See the following code to do that:

const getAllSolvedProblems = () => {
  const LEETCODE_API_URL = 'https://leetcode.com/api/problems/algorithms';

  const LEETCODE_PROBLEM_URL_PREFIX = "https://leetcode.com/problems/";

  const xhr = new XMLHttpRequest();

  /* Gather data */
  var problems_data = [];

  xhr.addEventListener('readystatechange', function () {
    if (xhr.readyState === 4) {
      let problems = JSON.parse(xhr.responseText)["stat_status_pairs"];
      problems.forEach(problem => {
        if(problem["status"] && problem["status"] === "ac")
        {
          var stat = problem["stat"];
          let problem_data = {"title" : stat["question__title"], "url" : LEETCODE_PROBLEM_URL_PREFIX + stat["question__title_slug"], "id" : stat["frontend_question_id"]};
          problems_data.push(problem_data);
        }
      });
      console.log(problems_data[0]);
    }
  });

  xhr.open('POST', LEETCODE_API_URL, true);
  xhr.send();
};

The problem lies in getting the actual code for an existing problem. There's no public facing leetcode api that does this. This means that LeetHub would need to run N requests (N = number of solved leetcode problems that aren't already on LeetHub) causing the browser to be very slow. Since we can take care of it asynchronously, the process can very easily break down if you close the page (say welcome.html) that performs this action. Sure we can store this, but the main drawback that I see is that this will be very slow.

I personally think this is a great idea (see link above for past discussion) but I can't come up with a more time efficient approach. If you (or anyone else) has a better way of doing it, please submit a PR!

Hi @QasimWani / Someone,

How to use this. Can someone please explain. I want push the old submission codes to github with same dates(if i do manully its pushing with today date not the date which i submitted the leetcode question). Thanks in advance!

akshat-1 commented 6 months ago

I think it can be a feasible if we check if number of solved problems are less than let say 50 , then only this feature will work

nikhtog commented 2 months ago

I was able to find a work-around by simulating a click on submit button using selenium. Steps:

  1. Get all the solved problems using @QasimWani 's script above. Paste the script in console, modify console.log(problems_data[0]); to console.log(problems_data);
  2. Save the contents in a JSON file leetcode_problems.json
  3. Start chrome on mac like this /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir="~/ChromeProfile"
  4. Login to leetcode.com, choose default programming language, setup leethub chrome extension on this chrome instance
  5. Run following script
import json
import time
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# Load JSON data
with open('leetcode_problems.json') as f:
    data = json.load(f)

# Setup Chrome options to connect to the existing browser
chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", "localhost:9222")

# Setup Chrome driver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service, options=chrome_options)

# Function to visit the problem submissions and perform the clicks
def visit_problem_submissions_and_sync(url):
    submissions_url = url + '/submissions/'
    driver.get(submissions_url)
    time.sleep(10)  # Wait for the page to load

    # Print current page URL for debugging
    print(f"Current URL: {driver.current_url}")

    # Print page source for debugging
    with open("page_source.html", "w") as f:
        f.write(driver.page_source)

    # Find and click the "Accepted" span
    try:
        accepted_span = WebDriverWait(driver, 20).until(
            EC.element_to_be_clickable((By.XPATH,
                                        '//span[@class="text-green-s dark:text-dark-green-s text-sm font-medium" and text()="Accepted"]'))
        )
        accepted_span.click()
        time.sleep(5)  # Wait for the click to be processed
    except Exception as e:
        print(f"'Accepted' span not found or could not click: {e}")

    # Find and click the "Sync w/ LeetHub" button
    try:
        sync_button = WebDriverWait(driver, 20).until(
            EC.element_to_be_clickable((By.XPATH, '//button[text()="Sync w/ LeetHub"]'))
        )
        sync_button.click()
        time.sleep(5)  # Wait for the sync to complete
    except Exception as e:
        print(f"'Sync w/ LeetHub' button not found or could not click: {e}")

# Loop through each problem and perform the task
for problem in data:
    url = problem['url']
    visit_problem_submissions_and_sync(url)

# Close the browser
driver.quit()

Enjoy!