Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a1aa93d

Browse files
committed
Create the hlper script yoo
1 parent ad2bfc8 commit a1aa93d

File tree

6 files changed

+322
-0
lines changed

6 files changed

+322
-0
lines changed

‎requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
markdownify
2+
beautifulsoup4
3+
pytermgui
4+
lxml
5+
selenium
6+
requests

‎solve.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import os # For getting the current working directory and making directories
2+
import sys # For getting command line arguments
3+
from enum import Enum # For the enum class
4+
from selenium import webdriver # For getting the HTML of the problem
5+
import requests # For downloading images
6+
from bs4 import BeautifulSoup # For parsing HTML
7+
import markdownify as md # For converting HTML to Markdown
8+
sys.path.append("utils") # Add the utils directory to the path
9+
import pytermgui_tui as tui # Import the TUI
10+
from data import Data # Import the Data class
11+
from projcets_helpers import * # Import the projects creation methods
12+
13+
BASE_URL = "https://leetcode.com/problems/" # The base URL of the problem
14+
15+
BASE_URL = BASE_URL + "merge-two-sorted-lists" # The URL of the problem for testing
16+
17+
# Stup the tui
18+
tui.setup()
19+
20+
# Check if user provided a URL as an argument
21+
if len(sys.argv) < 2:
22+
# If not, ask for one
23+
problem_url = tui.get_the_url(BASE_URL)
24+
else:
25+
# If so, use it
26+
problem_url = sys.argv[1]
27+
# Check if user provided a problem title instead of a URL, if so, add the base URL
28+
if not problem_url.startswith(BASE_URL) and not problem_url.startswith("http"):
29+
problem_url = BASE_URL + problem_url
30+
31+
# Check if the URL is valid
32+
if not problem_url.startswith(BASE_URL):
33+
print("Invalid URL, please enter a valid URL(LeeCode problem URL)")
34+
exit(1)
35+
36+
# Setup the driver (firefox)
37+
driver = webdriver.Firefox()
38+
39+
driver.get(problem_url) # Open the URL in the browser
40+
41+
soup = BeautifulSoup(driver.page_source, "lxml") # Parse the HTML
42+
43+
driver.quit() # Close the driver
44+
45+
# Get the main div (the problem details are in this div)
46+
main_div = soup.find("div", {"class": "ssg__qd-splitter-primary-w"}).find("div", {"class": "ssg__qd-splitter-primary-h"})
47+
48+
# Get the title of the problem
49+
title = soup.title.string.lower().replace(" - leetcode", "").replace(" ", "_")
50+
51+
level = main_div.find("div", {"class": "mt-3 flex space-x-4"}).find("div", {"class": "py-1"}).text.lower()
52+
53+
# Check if the level directory exists, if not, create it
54+
if not os.path.exists(level):
55+
os.mkdir(level)
56+
57+
# Check if the problem directory exists, if not, create it
58+
problem_path = os.path.join(level, title)
59+
if not os.path.exists(problem_path):
60+
os.mkdir(problem_path)
61+
62+
# Get the description of the problem
63+
discription = main_div.find("div", {"class": "_1l1MA"})
64+
65+
# Show the tui for confirm the data and choose the language to solve the problem
66+
data = tui.confirm_data(Data(title, level, problem_path))
67+
68+
# Download the images if there are any
69+
for img in discription.find_all("img"):
70+
src = img["src"]
71+
req = requests.get(src)
72+
if req.status_code == 200:
73+
img_name = src.split("/")[-1]
74+
img_path = os.path.join(data.problem_path, "images", img_name)
75+
if not os.path.exists(img_path):
76+
if not os.path.exists(os.path.dirname(img_path)):
77+
os.makedirs(os.path.dirname(img_path))
78+
with open(img_path, "wb") as f:
79+
f.write(req.content)
80+
img["src"] = img_path.replace(data.problem_path, ".")
81+
82+
# Convert the discription to Markdown
83+
discription = md.markdownify(str(discription), heading_style="ATX")
84+
85+
# Add the title to the discription
86+
discription = "# " + data.title.capitalize().replace("_", " ") + "\n" + discription
87+
88+
# Add the problem URL to the discription
89+
discription = discription + "\n- [Problem URL](" + problem_url + ")"
90+
91+
# Write the discription to the README.md file, if it doesn't exist
92+
if not os.path.exists(os.path.join(data.problem_path, "README.md")):
93+
with open(os.path.join(data.problem_path, "README.md"), "w") as f:
94+
f.write(discription)
95+
96+
# Create the NOTE.md file, if it doesn't exist
97+
if not os.path.exists(os.path.join(data.problem_path, "NOTE.md")):
98+
with open(os.path.join(data.problem_path, "NOTE.md"), "w") as f:
99+
f.write("## There is no note for this problem yet ̄⁠⁠⁠\(⁠ツ⁠)⁠⁠/⁠ ̄")
100+
101+
# Create the solution project for each language
102+
for lang in data.solve_with:
103+
lang_path = os.path.join(data.problem_path, lang)
104+
if not os.path.exists(lang_path):
105+
os.mkdir(lang_path)
106+
match lang:
107+
case "python" | "py":
108+
create_python_project(lang_path)
109+
case "java":
110+
create_java_project(lang_path)
111+
case "c++" | "cpp":
112+
create_cpp_project(lang_path)
113+
case "c":
114+
create_c_project(lang_path)
115+
case "rust":
116+
create_rust_project(lang_path)
117+
case "go":
118+
create_go_project(lang_path)
119+
case _: # If other language, do nothing
120+
pass
121+

‎utils/data.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Data:
2+
def __init__(self, title: str, level: str, problem_path: str, solve_with: list = None):
3+
self.title = title
4+
self.level = level
5+
self.problem_path = problem_path
6+
self.solve_with = solve_with
7+
8+
def __str__(self) -> str:
9+
return f"Title: {self.title} | Level: {self.level} | Path: {self.problem_path} | Solve with: {self.solve_with}"

‎utils/projcets_helpers.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import subprocess
2+
from os import sep, mkdir, chdir
3+
4+
def run(command: str, path: str = None) -> bool:
5+
""" Run a command utility"""
6+
# Go to the directory of the project if there is one
7+
if path:
8+
chdir(path)
9+
result = subprocess.run(command.split(' '), capture_output=True)
10+
print(result.stdout.decode('utf-8'))
11+
12+
# Go back to the root directory of the project
13+
if path:
14+
chdir(sep.join(__file__.split(sep)[:-2]))
15+
16+
# Print the error if there is one
17+
if result.stderr:
18+
print(f"There was an error while running the command: {command}")
19+
print(result.stderr.decode('utf-8'))
20+
21+
return result.returncode == 0
22+
23+
def create_rust_project(path: str) -> None:
24+
""" Create a rust project """
25+
run(f"cargo new --vcs none --lib {path}")
26+
27+
def create_c_project(path: str) -> None:
28+
""" Create a c project """
29+
run(f"touch {path}/main.c")
30+
run(f"touch {path}/Makefile")
31+
run(f"touch {path}/.gitignore")
32+
33+
def create_python_project(path: str) -> None:
34+
""" Create a python project """
35+
run(f"touch {path}/main.py")
36+
with open(f"{path}/test.py", "w") as f:
37+
f.write("import unittest\n")
38+
f.write("from main import *\n\n")
39+
f.write("class Test(unittest.TestCase):\n")
40+
f.write("\t def test(self):\n")
41+
f.write("\t\t self.assertEqual(True, True)\n\n")
42+
f.write("if __name__ == '__main__':\n")
43+
f.write("\t unittest.main()\n")
44+
45+
def create_java_project(path: str) -> None:
46+
if not run(f"mvn archetype:generate \
47+
-DarchetypeGroupId=org.apache.maven.archetypes \
48+
-DarchetypeArtifactId=maven-archetype-quickstart \
49+
-DarchetypeVersion=1.4 \
50+
-DgroupId=com.anas.leetcode.{path.split(sep)[-2]} \
51+
-DartifactId={path.split(sep)[-2]} \
52+
-Dversion=1.0-SNAPSHOT"):
53+
print("Failed to create java project")
54+
return
55+
56+
def create_cpp_project(path: str) -> None:
57+
with open(f"{path}/main.cpp", "w") as f:
58+
f.write("#include <iostream>\n\n")
59+
f.write("using namespace std;\n\n")
60+
61+
with open(f"{path}/test.cpp", "w") as f:
62+
f.write("#include <catch2/catch.hpp>\n")
63+
f.write("#include \"main.cpp\"\n\n")
64+
65+
with open(f"{path}/Makefile", "w") as f:
66+
f.write("all: main.cpp test.cpp\n")
67+
f.write("\t g++ -o main main.cpp\n")
68+
f.write("\t g++ -o test test.cpp -I /usr/local/include -L /usr/local/lib -lcatch2\n")
69+
f.write("\t ./test\n")
70+
71+
def create_go_project(path: str) -> None:
72+
if run(f"go mod init letcode/{path.split(sep)[-2]}", path):
73+
mkdir(f"{path}/src")
74+
with open(f"{path}/src/main.go", "w") as f:
75+
f.write("package main\n\n")
76+
f.write("import \"fmt\"\n\n")
77+
f.write("func main() {\n")
78+
f.write("\t fmt.Println(\"Hello, World!\")\n")
79+
f.write("}\n")
80+
81+
with open(f"{path}/src/_test.go", "w") as f:
82+
f.write("package main\n\n")
83+
f.write("import \"testing\"\n\n")
84+
f.write("func TestHello(t *testing.T) {\n")
85+
f.write("\t fmt.Println(\"Hello, World!\")\n")
86+
f.write("}\n")
87+

‎utils/pytermgui_tui.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pytermgui as ptg
2+
from data import Data
3+
4+
CONFIG = """
5+
config:
6+
InputField:
7+
styles:
8+
prompt: dim italic
9+
cursor: '@72'
10+
Label:
11+
styles:
12+
value: dim bold
13+
14+
Window:
15+
styles:
16+
border: '60'
17+
corner: '60'
18+
19+
Container:
20+
styles:
21+
border: '96'
22+
corner: '96'
23+
"""
24+
25+
problem_url = "" # The URL of the problem
26+
27+
def setup() -> None:
28+
"""
29+
Stup the pytermgui
30+
load the config styles
31+
"""
32+
with ptg.YamlLoader() as loader:
33+
loader.load(CONFIG)
34+
35+
def submit_url(manager: ptg.WindowManager, window: ptg.Window) -> None:
36+
"""Submit the URL of the problem"""
37+
global problem_url
38+
for widget in window:
39+
if isinstance(widget, ptg.InputField):
40+
problem_url = widget.value
41+
break
42+
manager.stop()
43+
44+
def get_the_url(base_url: str) -> str:
45+
""" Show the tui using the pytermgui for enter the URL of the problem """
46+
with ptg.WindowManager() as wm:
47+
wm.layout.add_slot("Body")
48+
window = (
49+
ptg.Window(
50+
"",
51+
ptg.InputField(base_url, prompt="URL: "),
52+
"",
53+
["Submit", lambda *_: submit_url(wm, window)],
54+
width=60,
55+
box="DOUBLE",
56+
)
57+
.set_title("[green bold]Enter the URL of the problem")
58+
.center()
59+
)
60+
wm.add(window)
61+
wm.run()
62+
return problem_url
63+
64+
def confirm_data(data: Data) -> Data:
65+
""" Show the tui using the pytermgui for confirm the data and choose the language to solve the problem """
66+
with ptg.WindowManager() as wm:
67+
wm.layout.add_slot("Body")
68+
window = (
69+
ptg.Window(
70+
"",
71+
ptg.InputField(data.title, prompt="Title: "),
72+
ptg.InputField(data.level, prompt="Level: "),
73+
ptg.InputField(data.problem_path, prompt="Base path:"),
74+
"",
75+
["Confirm", lambda *_: wm.stop()],
76+
width=60,
77+
box="DOUBLE",
78+
)
79+
.set_title("[green bold]Confirm the data")
80+
.center()
81+
)
82+
wm.add(window)
83+
wm.run()
84+
85+
data.solve_with = ["python", "java", "c++", "c", "go"]
86+
return data

‎utils/tui.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import zope.interface
2+
from data import Data
3+
4+
class ITextUserInterface(zope.interface.Interface):
5+
def setup(self) -> None:
6+
"""Stup the tui"""
7+
pass
8+
def get_the_url(self, base_url: str) -> str:
9+
"""Show the tui for enter the URL of the problem"""
10+
pass
11+
def confirm_data(self, data: Data) -> Data:
12+
"""Show the tui for confirm the data and choose the language to solve the problem"""
13+
pass

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /