diff --git a/README.md b/README.md index fb2b3f4..535ce9b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## Features -* **Problem Fetching:** LeetCode.py fetches the problem details directly from `leetcodeDb.json` ~~LeetCode's official API, allowing you to access all the necessary information about a problem~~. +* **Problem Fetching:** LeetCode.py fetches the problem details directly from ~~`leetcodeDb.json`~~ LeetCode's official API, allowing you to access all the necessary information about a problem. * **Code Snippet Retrieval:** With LeetCode.py, you can easily retrieve code snippets for a specific problem. @@ -12,45 +12,62 @@ * **ASCII Art:** LeetCode.py provides a beautiful ASCII art for each problem, making your coding experience more enjoyable. -* **Language Support:** LeetCode.py supports only **Python**. +* **Language Support:** LeetCode.py supports ~~only **Python**~~ all languages except Python2. ### Installation ``` git clone https://github.com/hrdkmishra/leetcode.py.git cd leetcode.py +python3 -m venv venv +source venv/bin/activate pip install -r requirements.txt ``` ### Usage +#### note: `lc.py --lib` for fixing python-leetcode lib issue + First you need to enter your leetcode session and crsf token ``` -python leetcode.py +python lc.py ``` to fetch the problem ``` -python leetcode.py -q/--question +python lc.py -q/--question ``` ![img_1.png](images/img_1.png) -to fetch problems in range +to fetch problems in range (might not work always) ``` -python leetcode.py -q/--question : +python lc.py -q/--question : ``` ![](./images/image.png) to solve the problem ``` -python leetcode.py -s/--solve +python lc.py -s/--solve ``` ![img.png](images/img.png) +to test the code +``` +python lc.py -t/--test code_editor/filename +``` + +to submit the code +``` +python lc.py -u/--submit code_editor/filename +``` + ## Features to be added -1. [ ] code submission -2. [ ] testing the user code -3. [ ] code submission status -4. [ ] code submission result -5. [ ] code submission result details -6. [ ] -h/--help +1. [x] code submission +2. [x] testing the user code +3. [x] code submission status +4. [x] code submission result +5. [x] code submission result details +6. [x] -h/--help 7. [ ] color theme +8. [x] fixed python-leetcode lib issue +9. [x] add support for other languages except python2 + diff --git a/code_editor/1_two-sum.py b/code_editor/1_two-sum.py deleted file mode 100644 index afb3048..0000000 --- a/code_editor/1_two-sum.py +++ /dev/null @@ -1,3 +0,0 @@ -class Solution: - def twoSum(self, nums: List[int], target: int) -> List[int]: - \ No newline at end of file diff --git a/color.py b/color.py index 03c7809..d082934 100644 --- a/color.py +++ b/color.py @@ -1,4 +1,5 @@ class Colors: + YELLOW = "\033[1;33m" # Bold yellow GREEN = "\033[1;32m" # Bold green ORANGE = "\033[1;33m" # Bold orange RED = "\033[1;31m" # Bold red diff --git a/config_setup.py b/config_setup.py index f67ec74..b6c2f7c 100644 --- a/config_setup.py +++ b/config_setup.py @@ -4,15 +4,52 @@ CONFIG_FILE_PATH = "config.toml" -def save_credentials_to_config(leetcode_session, csrf_token): - config_data = {"LEETCODE_SESSION": leetcode_session, "CSRF_TOKEN": csrf_token} +def update_config(key_value_dict): + if os.path.exists(CONFIG_FILE_PATH): + with open(CONFIG_FILE_PATH, "r") as config_file: + config_data = toml.load(config_file) + config_data.update(key_value_dict) + else: + config_data = key_value_dict + with open(CONFIG_FILE_PATH, "w") as config_file: toml.dump(config_data, config_file) -def load_credentials_from_config(): +def load_config_from_file(): if os.path.exists(CONFIG_FILE_PATH): with open(CONFIG_FILE_PATH, "r") as config_file: - config_data = toml.load(config_file) - return config_data.get("LEETCODE_SESSION"), config_data.get("CSRF_TOKEN") - return None, None + return toml.load(config_file) + return {} + + +def save_credentials_to_config(leetcode_session, csrf_token): + config_data = { + "LEETCODE_SESSION": leetcode_session, + "CSRF_TOKEN": csrf_token + } + update_config(config_data) + + +def load_credentials_from_config(): + config_data = load_config_from_file() + return config_data.get("LEETCODE_SESSION"), config_data.get("CSRF_TOKEN") + + +def load_user_data_from_config(): + config_data = load_config_from_file() + return config_data.get("USER_LANG", "").lower(), config_data.get("EDITOR_CLI", "").lower() + + +def save_user_data_to_config(user_lang): + config_data = {"USER_LANG": user_lang} + update_config(config_data) + + +def save_user_path_to_config(path): + config_data = {"LEETCODE_PATH": path} + update_config(config_data) + +def load_user_path_from_config(): + config_data = load_config_from_file() + return config_data.get("LEETCODE_PATH", "") diff --git a/custom_lib_file/base_submission_result.py b/custom_lib_file/base_submission_result.py new file mode 100644 index 0000000..101ad2f --- /dev/null +++ b/custom_lib_file/base_submission_result.py @@ -0,0 +1,782 @@ +# coding: utf-8 + +""" + Leetcode API + + Leetcode API implementation. # noqa: E501 + + OpenAPI spec version: 1.0.1-1 + Contact: pv.safronov@gmail.com + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + +import pprint +import re # noqa: F401 + +import six + + + +class BaseSubmissionResult(object): #modified + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + "code_output": "list[str]", + "elapsed_time": "int", + "full_runtime_error": "str", + "lang": "str", + "memory": "int", + "memory_percentile": "float", + "pretty_lang": "str", + "run_success": "bool", + "runtime_error": "str", + "runtime_percentile": "float", + "state": "str", + "status_code": "int", + "status_memory": "str", + "status_msg": "str", + "status_runtime": "str", + "submission_id": "str", + "task_finish_time": "int", + "total_correct": "int", + "total_testcases": "int", + "question_id": "int", + "std_output_list": "list[str]", + "task_name": "str", + "expected_std_output_list": "list[str]", + "expected_task_name": "str", + "compare_result": "bool", + "finished": "bool", + # Add std_output_list here + } + + attribute_map = { + "code_output": "code_output", + "elapsed_time": "elapsed_time", + "full_runtime_error": "full_runtime_error", + "lang": "lang", + "memory": "memory", + "memory_percentile": "memory_percentile", + "pretty_lang": "pretty_lang", + "run_success": "run_success", + "runtime_error": "runtime_error", + "runtime_percentile": "runtime_percentile", + "state": "state", + "status_code": "status_code", + "status_memory": "status_memory", + "status_msg": "status_msg", + "status_runtime": "status_runtime", + "submission_id": "submission_id", + "task_finish_time": "task_finish_time", + "total_correct": "total_correct", + "total_testcases": "total_testcases", + "question_id": "question_id", + "std_output_list": "std_output_list", # Add std_output_list here + "task_name": "task_name", + "expected_std_output_list": "expected_std_output_list", + "expected_task_name": "expected_task_name", + "compare_result": "compare_result", + "finished": "finished", + } + + def __init__( + self, + code_output=None, + elapsed_time=None, + full_runtime_error=None, + lang=None, + memory=None, + memory_percentile=None, + pretty_lang=None, + run_success=None, + runtime_error=None, + runtime_percentile=None, + state=None, + status_code=None, + status_memory=None, + status_msg=None, + status_runtime=None, + submission_id=None, + task_finish_time=None, + total_correct=None, + total_testcases=None, + question_id=None, + std_output_list=None, # Add std_output_list here + task_name=None, + expected_std_output_list=None, + expected_task_name=None, + compare_result=None, + finished=None, + ): # noqa: E501 + """BaseSubmissionResult - a model defined in Swagger""" # noqa: E501 + self._code_output = None + self._elapsed_time = None + self._full_runtime_error = None + self._lang = None + self._memory = None + self._memory_percentile = None + self._pretty_lang = None + self._run_success = None + self._runtime_error = None + self._runtime_percentile = None + self._state = None + self._status_code = None + self._status_memory = None + self._status_msg = None + self._status_runtime = None + self._submission_id = None + self._task_finish_time = None + self._total_correct = None + self._total_testcases = None + self._question_id = None + self._std_output_list = None # Add std_output_list here + self._task_name = None + self._expected_std_output_list = None + self._expected_task_name = None + self.compare_result = None + self.finished = None + self.discriminator = None + if code_output is not None: + self.code_output = code_output + self.elapsed_time = elapsed_time + if full_runtime_error is not None: + self.full_runtime_error = full_runtime_error + self.lang = lang + self.memory = memory + if memory_percentile is not None: + self.memory_percentile = memory_percentile + self.pretty_lang = pretty_lang + self.run_success = run_success + if runtime_error is not None: + self.runtime_error = runtime_error + if runtime_percentile is not None: + self.runtime_percentile = runtime_percentile + self.state = state + self.status_code = status_code + if status_memory is not None: + self.status_memory = status_memory + self.status_msg = status_msg + self.status_runtime = status_runtime + self.submission_id = submission_id + self.task_finish_time = task_finish_time + if total_correct is not None: + self.total_correct = total_correct + if total_testcases is not None: + self.total_testcases = total_testcases + if question_id is not None: + self.question_id = question_id + if std_output_list is not None: # Add std_output_list here + self.std_output_list = std_output_list # Add std_output_list here + if task_name is not None: + self.task_name = task_name + if expected_std_output_list is not None: + self.expected_std_output_list = expected_std_output_list + if expected_task_name is not None: + self.expected_task_name = expected_task_name + if compare_result is not None: + self.compare_result = compare_result + if finished is not None: + self.finished = finished + + @property + def code_output(self): + """Gets the code_output of this BaseSubmissionResult. # noqa: E501 + + + :return: The code_output of this BaseSubmissionResult. # noqa: E501 + :rtype: list[str] + """ + return self._code_output + + @code_output.setter + def code_output(self, code_output): + """Sets the code_output of this BaseSubmissionResult. + + + :param code_output: The code_output of this BaseSubmissionResult. # noqa: E501 + :type: list[str] + """ + + self._code_output = code_output + + @property + def elapsed_time(self): + """Gets the elapsed_time of this BaseSubmissionResult. # noqa: E501 + + + :return: The elapsed_time of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._elapsed_time + + @elapsed_time.setter + def elapsed_time(self, elapsed_time): + """Sets the elapsed_time of this BaseSubmissionResult. + + + :param elapsed_time: The elapsed_time of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + if elapsed_time is None: + raise ValueError( + "Invalid value for `elapsed_time`, must not be `None`" + ) # noqa: E501 + + self._elapsed_time = elapsed_time + + @property + def full_runtime_error(self): + """Gets the full_runtime_error of this BaseSubmissionResult. # noqa: E501 + + + :return: The full_runtime_error of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._full_runtime_error + + @full_runtime_error.setter + def full_runtime_error(self, full_runtime_error): + """Sets the full_runtime_error of this BaseSubmissionResult. + + + :param full_runtime_error: The full_runtime_error of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + + self._full_runtime_error = full_runtime_error + + @property + def lang(self): + """Gets the lang of this BaseSubmissionResult. # noqa: E501 + + + :return: The lang of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._lang + + @lang.setter + def lang(self, lang): + """Sets the lang of this BaseSubmissionResult. + + + :param lang: The lang of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if lang is None: + raise ValueError( + "Invalid value for `lang`, must not be `None`" + ) # noqa: E501 + + self._lang = lang + + @property + def memory(self): + """Gets the memory of this BaseSubmissionResult. # noqa: E501 + + + :return: The memory of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._memory + + @memory.setter + def memory(self, memory): + """Sets the memory of this BaseSubmissionResult. + + + :param memory: The memory of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + if memory is None: + raise ValueError( + "Invalid value for `memory`, must not be `None`" + ) # noqa: E501 + + self._memory = memory + + @property + def memory_percentile(self): + """Gets the memory_percentile of this BaseSubmissionResult. # noqa: E501 + + + :return: The memory_percentile of this BaseSubmissionResult. # noqa: E501 + :rtype: float + """ + return self._memory_percentile + + @memory_percentile.setter + def memory_percentile(self, memory_percentile): + """Sets the memory_percentile of this BaseSubmissionResult. + + + :param memory_percentile: The memory_percentile of this BaseSubmissionResult. # noqa: E501 + :type: float + """ + + self._memory_percentile = memory_percentile + + @property + def pretty_lang(self): + """Gets the pretty_lang of this BaseSubmissionResult. # noqa: E501 + + + :return: The pretty_lang of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._pretty_lang + + @pretty_lang.setter + def pretty_lang(self, pretty_lang): + """Sets the pretty_lang of this BaseSubmissionResult. + + + :param pretty_lang: The pretty_lang of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if pretty_lang is None: + raise ValueError( + "Invalid value for `pretty_lang`, must not be `None`" + ) # noqa: E501 + + self._pretty_lang = pretty_lang + + @property + def run_success(self): + """Gets the run_success of this BaseSubmissionResult. # noqa: E501 + + + :return: The run_success of this BaseSubmissionResult. # noqa: E501 + :rtype: bool + """ + return self._run_success + + @run_success.setter + def run_success(self, run_success): + """Sets the run_success of this BaseSubmissionResult. + + + :param run_success: The run_success of this BaseSubmissionResult. # noqa: E501 + :type: bool + """ + if run_success is None: + raise ValueError( + "Invalid value for `run_success`, must not be `None`" + ) # noqa: E501 + + self._run_success = run_success + + @property + def runtime_error(self): + """Gets the runtime_error of this BaseSubmissionResult. # noqa: E501 + + + :return: The runtime_error of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._runtime_error + + @runtime_error.setter + def runtime_error(self, runtime_error): + """Sets the runtime_error of this BaseSubmissionResult. + + + :param runtime_error: The runtime_error of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + + self._runtime_error = runtime_error + + @property + def runtime_percentile(self): + """Gets the runtime_percentile of this BaseSubmissionResult. # noqa: E501 + + + :return: The runtime_percentile of this BaseSubmissionResult. # noqa: E501 + :rtype: float + """ + return self._runtime_percentile + + @runtime_percentile.setter + def runtime_percentile(self, runtime_percentile): + """Sets the runtime_percentile of this BaseSubmissionResult. + + + :param runtime_percentile: The runtime_percentile of this BaseSubmissionResult. # noqa: E501 + :type: float + """ + + self._runtime_percentile = runtime_percentile + + @property + def state(self): + """Gets the state of this BaseSubmissionResult. # noqa: E501 + + + :return: The state of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._state + + @state.setter + def state(self, state): + """Sets the state of this BaseSubmissionResult. + + + :param state: The state of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if state is None: + raise ValueError( + "Invalid value for `state`, must not be `None`" + ) # noqa: E501 + allowed_values = ["SUCCESS"] # noqa: E501 + if state not in allowed_values: + raise ValueError( + "Invalid value for `state` ({0}), must be one of {1}".format( # noqa: E501 + state, allowed_values + ) + ) + + self._state = state + + @property + def status_code(self): + """Gets the status_code of this BaseSubmissionResult. # noqa: E501 + + + :return: The status_code of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._status_code + + @status_code.setter + def status_code(self, status_code): + """Sets the status_code of this BaseSubmissionResult. + + + :param status_code: The status_code of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + if status_code is None: + raise ValueError( + "Invalid value for `status_code`, must not be `None`" + ) # noqa: E501 + allowed_values = [10, 11, 15] # noqa: E501 + if status_code not in allowed_values: + raise ValueError( + "Invalid value for `status_code` ({0}), must be one of {1}".format( # noqa: E501 + status_code, allowed_values + ) + ) + + self._status_code = status_code + + @property + def status_memory(self): + """Gets the status_memory of this BaseSubmissionResult. # noqa: E501 + + + :return: The status_memory of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._status_memory + + @status_memory.setter + def status_memory(self, status_memory): + """Sets the status_memory of this BaseSubmissionResult. + + + :param status_memory: The status_memory of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + + self._status_memory = status_memory + + @property + def status_msg(self): + """Gets the status_msg of this BaseSubmissionResult. # noqa: E501 + + + :return: The status_msg of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._status_msg + + @status_msg.setter + def status_msg(self, status_msg): + """Sets the status_msg of this BaseSubmissionResult. + + + :param status_msg: The status_msg of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if status_msg is None: + raise ValueError( + "Invalid value for `status_msg`, must not be `None`" + ) # noqa: E501 + + self._status_msg = status_msg + + @property + def status_runtime(self): + """Gets the status_runtime of this BaseSubmissionResult. # noqa: E501 + + + :return: The status_runtime of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._status_runtime + + @status_runtime.setter + def status_runtime(self, status_runtime): + """Sets the status_runtime of this BaseSubmissionResult. + + + :param status_runtime: The status_runtime of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if status_runtime is None: + raise ValueError( + "Invalid value for `status_runtime`, must not be `None`" + ) # noqa: E501 + + self._status_runtime = status_runtime + + @property + def submission_id(self): + """Gets the submission_id of this BaseSubmissionResult. # noqa: E501 + + + :return: The submission_id of this BaseSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._submission_id + + @submission_id.setter + def submission_id(self, submission_id): + """Sets the submission_id of this BaseSubmissionResult. + + + :param submission_id: The submission_id of this BaseSubmissionResult. # noqa: E501 + :type: str + """ + if submission_id is None: + raise ValueError( + "Invalid value for `submission_id`, must not be `None`" + ) # noqa: E501 + + self._submission_id = submission_id + + @property + def task_finish_time(self): + """Gets the task_finish_time of this BaseSubmissionResult. # noqa: E501 + + + :return: The task_finish_time of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._task_finish_time + + @task_finish_time.setter + def task_finish_time(self, task_finish_time): + """Sets the task_finish_time of this BaseSubmissionResult. + + + :param task_finish_time: The task_finish_time of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + if task_finish_time is None: + raise ValueError( + "Invalid value for `task_finish_time`, must not be `None`" + ) # noqa: E501 + + self._task_finish_time = task_finish_time + + @property + def total_correct(self): + """Gets the total_correct of this BaseSubmissionResult. # noqa: E501 + + + :return: The total_correct of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._total_correct + + @total_correct.setter + def total_correct(self, total_correct): + """Sets the total_correct of this BaseSubmissionResult. + + + :param total_correct: The total_correct of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + + self._total_correct = total_correct + + @property + def total_testcases(self): + """Gets the total_testcases of this BaseSubmissionResult. # noqa: E501 + + + :return: The total_testcases of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._total_testcases + + @total_testcases.setter + def total_testcases(self, total_testcases): + """Sets the total_testcases of this BaseSubmissionResult. + + + :param total_testcases: The total_testcases of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + + self._total_testcases = total_testcases + + @property + def question_id(self): + """Gets the question_id of this BaseSubmissionResult. # noqa: E501 + + + :return: The question_id of this BaseSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._question_id + + @question_id.setter + def question_id(self, question_id): + """Sets the question_id of this BaseSubmissionResult. + + + :param question_id: The question_id of this BaseSubmissionResult. # noqa: E501 + :type: int + """ + + self._question_id = question_id + + @property + def std_output_list(self): + self._std_output_list = [] + + @std_output_list.setter + def std_output_list(self, std_output_list): + self._std_output_list = std_output_list + + @property + def task_name(self): + self._task_name = "" + + @task_name.setter + def task_name(self, task_name): + self._task_name = task_name + + @property + def expected_std_output_list(self): + self._expected_std_output_list = [] + + @expected_std_output_list.setter + def expected_std_output_list(self, expected_std_output_list): + self._expected_std_output_list = expected_std_output_list + + @property + def expected_task_name(self): + self._expected_task_name = "" + + @expected_task_name.setter + def expected_task_name(self, expected_task_name): + self._expected_task_name = expected_task_name + + @property + def compare_result(self): + """Gets the compare_result of this TestSubmissionResult. # noqa: E501 + + :return: The compare_result of this TestSubmissionResult. # noqa: E501 + :rtype: bool + """ + return self._compare_result + + @compare_result.setter + def compare_result(self, compare_result): + """Sets the compare_result of this TestSubmissionResult. + + :param compare_result: The compare_result of this TestSubmissionResult. # noqa: E501 + :type: bool + """ + self._compare_result = compare_result + + @property + def finished(self): + """Gets the finished of this TestSubmissionResult. # noqa: E501 + + :return: The finished of this TestSubmissionResult. # noqa: E501 + :rtype: bool + """ + return self._finished + + @finished.setter + def finished(self, finished): + """Sets the finished of this TestSubmissionResult. + + :param finished: The finished of this TestSubmissionResult. # noqa: E501 + :type: bool + """ + self._finished = finished + + + + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list( + map(lambda x: x.to_dict() if hasattr(x, "to_dict") else x, value) + ) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict( + map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") + else item, + value.items(), + ) + ) + else: + result[attr] = value + if issubclass(BaseSubmissionResult, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, BaseSubmissionResult): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/custom_lib_file/submission_result.py b/custom_lib_file/submission_result.py new file mode 100644 index 0000000..9e0008d --- /dev/null +++ b/custom_lib_file/submission_result.py @@ -0,0 +1,261 @@ +# coding: utf-8 + +""" + Leetcode API + + Leetcode API implementation. # noqa: E501 + + OpenAPI spec version: 1.0.1-1 + Contact: pv.safronov@gmail.com + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + +import pprint +import re # noqa: F401 + +import six + +from leetcode.models.base_submission_result import ( # noqa: F401,E501 + BaseSubmissionResult, +) + + + +class SubmissionResult(BaseSubmissionResult): #Modified + + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + "compare_result": "str", + "std_output": "str", + "last_testcase": "str", + "expected_output": "str", + "input_formatted": "str", + "input": "str", + } + if hasattr(BaseSubmissionResult, "swagger_types"): + swagger_types.update(BaseSubmissionResult.swagger_types) + + attribute_map = { + "compare_result": "compare_result", + "std_output": "std_output", + "last_testcase": "last_testcase", + "expected_output": "expected_output", + "input_formatted": "input_formatted", + "input": "input", + } + if hasattr(BaseSubmissionResult, "attribute_map"): + attribute_map.update(BaseSubmissionResult.attribute_map) + + def __init__( + self, + compare_result=None, + std_output=None, + last_testcase=None, + expected_output=None, + input_formatted=None, + input=None, + *args, + **kwargs + ): # noqa: E501 + """SubmissionResult - a model defined in Swagger""" # noqa: E501 + self._compare_result = compare_result or "" + self._std_output = None + self._last_testcase = None + self._expected_output = None + self._input_formatted = input_formatted or kwargs.get("submission_result", {}).get("input_formatted", "") + self._input = None + self.discriminator = None + self.std_output = std_output + self.last_testcase = last_testcase + self.expected_output = expected_output + self.input_formatted = input_formatted + self.input = input + BaseSubmissionResult.__init__(self, *args, **kwargs) + + @property + def compare_result(self): + """Gets the compare_result of this SubmissionResult. # noqa: E501 + + + :return: The compare_result of this SubmissionResult. # noqa: E501 + :rtype: str + """ + return self._compare_result + + @compare_result.setter + def compare_result(self, compare_result): + """Sets the compare_result of this SubmissionResult. + + + :param compare_result: The compare_result of this SubmissionResult. # noqa: E501 + :type: str + """ + # if compare_result is None: + # raise ValueError( + # "Invalid value for `compare_result`, must not be `None`" + # ) # noqa: E501 + + self._compare_result = compare_result + + @property + def std_output(self): + """Gets the std_output of this SubmissionResult. # noqa: E501 + + + :return: The std_output of this SubmissionResult. # noqa: E501 + :rtype: str + """ + return self._std_output + + @std_output.setter + def std_output(self, std_output): + """Sets the std_output of this SubmissionResult. + + + :param std_output: The std_output of this SubmissionResult. # noqa: E501 + :type: str + """ + if std_output is None: + raise ValueError( + "Invalid value for `std_output`, must not be `None`" + ) # noqa: E501 + + self._std_output = std_output + + @property + def last_testcase(self): + """Gets the last_testcase of this SubmissionResult. # noqa: E501 + + + :return: The last_testcase of this SubmissionResult. # noqa: E501 + :rtype: str + """ + return self._last_testcase + + @last_testcase.setter + def last_testcase(self, last_testcase): + """Sets the last_testcase of this SubmissionResult. + + + :param last_testcase: The last_testcase of this SubmissionResult. # noqa: E501 + :type: str + """ + if last_testcase is None: + raise ValueError( + "Invalid value for `last_testcase`, must not be `None`" + ) # noqa: E501 + + self._last_testcase = last_testcase + + @property + def expected_output(self): + """Gets the expected_output of this SubmissionResult. # noqa: E501 + + + :return: The expected_output of this SubmissionResult. # noqa: E501 + :rtype: str + """ + return self._expected_output + + @expected_output.setter + def expected_output(self, expected_output): + """Sets the expected_output of this SubmissionResult. + + + :param expected_output: The expected_output of this SubmissionResult. # noqa: E501 + :type: str + """ + if expected_output is None: + raise ValueError( + "Invalid value for `expected_output`, must not be `None`" + ) # noqa: E501 + + self._expected_output = expected_output + + @property + def input_formatted(self): + """Gets the input_formatted of this SubmissionResult. # noqa: E501""" + return self._input_formatted + + @input_formatted.setter + def input_formatted(self, input_formatted): + """Sets the input_formatted of this SubmissionResult. # noqa: E501""" + if input_formatted is None: + # Provide a default value if input_formatted is None + input_formatted = "" + + self._input_formatted = input_formatted + + @property + def input(self): + """Gets the input of this SubmissionResult. # noqa: E501""" + return self._input + + @input.setter + def input(self, input): + """Sets the input of this SubmissionResult. # noqa: E501""" + if input is None: + # Provide a default value if input is None + input = "" + + self._input = input + + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list( + map(lambda x: x.to_dict() if hasattr(x, "to_dict") else x, value) + ) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict( + map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") + else item, + value.items(), + ) + ) + else: + result[attr] = value + if issubclass(SubmissionResult, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, SubmissionResult): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/custom_lib_file/test_submission_result.py b/custom_lib_file/test_submission_result.py new file mode 100644 index 0000000..849af75 --- /dev/null +++ b/custom_lib_file/test_submission_result.py @@ -0,0 +1,410 @@ +# coding: utf-8 + +""" + Leetcode API + + Leetcode API implementation. # noqa: E501 + + OpenAPI spec version: 1.0.1-1 + Contact: pv.safronov@gmail.com + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + +import pprint +import re # noqa: F401 + +import six + +from leetcode.models.base_submission_result import ( # noqa: F401,E501 + BaseSubmissionResult, +) + + + +class TestSubmissionResult(BaseSubmissionResult): + + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + "code_answer": "list[str]", + "correct_answer": "bool", + "expected_status_code": "int", + "expected_lang": "str", + "expected_run_success": "bool", + "expected_status_runtime": "str", + "expected_memory": "int", + "expected_code_answer": "list[str]", + "expected_code_output": "list[str]", + "expected_elapsed_time": "int", + "expected_task_finish_time": "int", + } + if hasattr(BaseSubmissionResult, "swagger_types"): + swagger_types.update(BaseSubmissionResult.swagger_types) + + attribute_map = { + "code_answer": "code_answer", + "correct_answer": "correct_answer", + "expected_status_code": "expected_status_code", + "expected_lang": "expected_lang", + "expected_run_success": "expected_run_success", + "expected_status_runtime": "expected_status_runtime", + "expected_memory": "expected_memory", + "expected_code_answer": "expected_code_answer", + "expected_code_output": "expected_code_output", + "expected_elapsed_time": "expected_elapsed_time", + "expected_task_finish_time": "expected_task_finish_time", + } + if hasattr(BaseSubmissionResult, "attribute_map"): + attribute_map.update(BaseSubmissionResult.attribute_map) + + def __init__( + self, + code_answer=None, + correct_answer=None, + expected_status_code=None, + expected_lang=None, + expected_run_success=None, + expected_status_runtime=None, + expected_memory=None, + expected_code_answer=None, + expected_code_output=None, + expected_elapsed_time=None, + expected_task_finish_time=None, + *args, + **kwargs + ): # noqa: E501 + """TestSubmissionResult - a model defined in Swagger""" # noqa: E501 + self._code_answer = None + self._correct_answer = None + self._expected_status_code = None + self._expected_lang = None + self._expected_run_success = None + self._expected_status_runtime = None + self._expected_memory = None + self._expected_code_answer = None + self._expected_code_output = None + self._expected_elapsed_time = None + self._expected_task_finish_time = None + self.discriminator = None + self.code_answer = code_answer + if correct_answer is not None: + self.correct_answer = correct_answer + if expected_status_code is not None: + self.expected_status_code = expected_status_code + if expected_lang is not None: + self.expected_lang = expected_lang + if expected_run_success is not None: + self.expected_run_success = expected_run_success + if expected_status_runtime is not None: + self.expected_status_runtime = expected_status_runtime + if expected_memory is not None: + self.expected_memory = expected_memory + if expected_code_answer is not None: + self.expected_code_answer = expected_code_answer + if expected_code_output is not None: + self.expected_code_output = expected_code_output + if expected_elapsed_time is not None: + self.expected_elapsed_time = expected_elapsed_time + if expected_task_finish_time is not None: + self.expected_task_finish_time = expected_task_finish_time + BaseSubmissionResult.__init__(self, *args, **kwargs) + + @property + def code_answer(self): + """Gets the code_answer of this TestSubmissionResult. # noqa: E501 + + + :return: The code_answer of this TestSubmissionResult. # noqa: E501 + :rtype: list[str] + """ + return self._code_answer + + @code_answer.setter + def code_answer(self, code_answer): + """Sets the code_answer of this TestSubmissionResult. + + + :param code_answer: The code_answer of this TestSubmissionResult. # noqa: E501 + :type: list[str] + """ + # if code_answer is None: + # raise ValueError( + # "Invalid value for `code_answer`, must not be `None`" + # ) # noqa: E501 + + self._code_answer = code_answer + + @property + def correct_answer(self): + """Gets the correct_answer of this TestSubmissionResult. # noqa: E501 + + + :return: The correct_answer of this TestSubmissionResult. # noqa: E501 + :rtype: bool + """ + return self._correct_answer + + @correct_answer.setter + def correct_answer(self, correct_answer): + """Sets the correct_answer of this TestSubmissionResult. + + + :param correct_answer: The correct_answer of this TestSubmissionResult. # noqa: E501 + :type: bool + """ + + self._correct_answer = correct_answer + + @property + def expected_status_code(self): + """Gets the expected_status_code of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_status_code of this TestSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._expected_status_code + + @expected_status_code.setter + def expected_status_code(self, expected_status_code): + """Sets the expected_status_code of this TestSubmissionResult. + + + :param expected_status_code: The expected_status_code of this TestSubmissionResult. # noqa: E501 + :type: int + """ + allowed_values = [10, 11, 15] # noqa: E501 + if expected_status_code not in allowed_values: + raise ValueError( + "Invalid value for `expected_status_code` ({0}), must be one of {1}".format( # noqa: E501 + expected_status_code, allowed_values + ) + ) + + self._expected_status_code = expected_status_code + + @property + def expected_lang(self): + """Gets the expected_lang of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_lang of this TestSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._expected_lang + + @expected_lang.setter + def expected_lang(self, expected_lang): + """Sets the expected_lang of this TestSubmissionResult. + + + :param expected_lang: The expected_lang of this TestSubmissionResult. # noqa: E501 + :type: str + """ + + self._expected_lang = expected_lang + + @property + def expected_run_success(self): + """Gets the expected_run_success of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_run_success of this TestSubmissionResult. # noqa: E501 + :rtype: bool + """ + return self._expected_run_success + + @expected_run_success.setter + def expected_run_success(self, expected_run_success): + """Sets the expected_run_success of this TestSubmissionResult. + + + :param expected_run_success: The expected_run_success of this TestSubmissionResult. # noqa: E501 + :type: bool + """ + + self._expected_run_success = expected_run_success + + @property + def expected_status_runtime(self): + """Gets the expected_status_runtime of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_status_runtime of this TestSubmissionResult. # noqa: E501 + :rtype: str + """ + return self._expected_status_runtime + + @expected_status_runtime.setter + def expected_status_runtime(self, expected_status_runtime): + """Sets the expected_status_runtime of this TestSubmissionResult. + + + :param expected_status_runtime: The expected_status_runtime of this TestSubmissionResult. # noqa: E501 + :type: str + """ + + self._expected_status_runtime = expected_status_runtime + + @property + def expected_memory(self): + """Gets the expected_memory of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_memory of this TestSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._expected_memory + + @expected_memory.setter + def expected_memory(self, expected_memory): + """Sets the expected_memory of this TestSubmissionResult. + + + :param expected_memory: The expected_memory of this TestSubmissionResult. # noqa: E501 + :type: int + """ + + self._expected_memory = expected_memory + + @property + def expected_code_answer(self): + """Gets the expected_code_answer of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_code_answer of this TestSubmissionResult. # noqa: E501 + :rtype: list[str] + """ + return self._expected_code_answer + + @expected_code_answer.setter + def expected_code_answer(self, expected_code_answer): + """Sets the expected_code_answer of this TestSubmissionResult. + + + :param expected_code_answer: The expected_code_answer of this TestSubmissionResult. # noqa: E501 + :type: list[str] + """ + + self._expected_code_answer = expected_code_answer + + @property + def expected_code_output(self): + """Gets the expected_code_output of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_code_output of this TestSubmissionResult. # noqa: E501 + :rtype: list[str] + """ + return self._expected_code_output + + @expected_code_output.setter + def expected_code_output(self, expected_code_output): + """Sets the expected_code_output of this TestSubmissionResult. + + + :param expected_code_output: The expected_code_output of this TestSubmissionResult. # noqa: E501 + :type: list[str] + """ + + self._expected_code_output = expected_code_output + + @property + def expected_elapsed_time(self): + """Gets the expected_elapsed_time of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_elapsed_time of this TestSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._expected_elapsed_time + + @expected_elapsed_time.setter + def expected_elapsed_time(self, expected_elapsed_time): + """Sets the expected_elapsed_time of this TestSubmissionResult. + + + :param expected_elapsed_time: The expected_elapsed_time of this TestSubmissionResult. # noqa: E501 + :type: int + """ + + self._expected_elapsed_time = expected_elapsed_time + + @property + def expected_task_finish_time(self): + """Gets the expected_task_finish_time of this TestSubmissionResult. # noqa: E501 + + + :return: The expected_task_finish_time of this TestSubmissionResult. # noqa: E501 + :rtype: int + """ + return self._expected_task_finish_time + + @expected_task_finish_time.setter + def expected_task_finish_time(self, expected_task_finish_time): + """Sets the expected_task_finish_time of this TestSubmissionResult. + + + :param expected_task_finish_time: The expected_task_finish_time of this TestSubmissionResult. # noqa: E501 + :type: int + """ + + self._expected_task_finish_time = expected_task_finish_time + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list( + map(lambda x: x.to_dict() if hasattr(x, "to_dict") else x, value) + ) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict( + map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") + else item, + value.items(), + ) + ) + else: + result[attr] = value + if issubclass(TestSubmissionResult, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, TestSubmissionResult): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/lc.sh b/lc.sh new file mode 100644 index 0000000..2f7a1ef --- /dev/null +++ b/lc.sh @@ -0,0 +1,5 @@ +#!/bin/bash +VENV_ACTIVATE="/d/prog/leetcode.py/venv/Scripts/activate" +source "$VENV_ACTIVATE" +python "/d/prog/leetcode.py/main.py" "$@" + diff --git a/main.py b/main.py index adedb32..87bc944 100644 --- a/main.py +++ b/main.py @@ -1,16 +1,26 @@ -import json -import os import time import click from bs4 import BeautifulSoup - from color import Colors -from config_setup import ( - save_credentials_to_config, - load_credentials_from_config, -) - +from config_setup import * +import leetcode +import leetcode.auth import requests +import os +import shutil +import glob + + +def non_lib_configuration(): # had to change name because of python-leetcode lib + leetcode_session, csrf_token = load_credentials_from_config() + if not leetcode_session or not csrf_token: + leetcode_session = click.prompt("Enter your LeetCode session", type=str) + csrf_token = click.prompt("Enter your CSRF token", type=str) + save_credentials_to_config(leetcode_session, csrf_token) + return leetcode_session, csrf_token + + +# print def print_question_data(question): @@ -19,8 +29,7 @@ def print_question_data(question): difficulty = question.get("difficulty") ac_rate = question.get("acRate") status = question.get("status") - - # Fix ac_rate position regardless of the length of difficulty + is_paid = question.get("paidOnly") difficulty_color = "" if difficulty == "Easy": difficulty_color = Colors.GREEN @@ -28,51 +37,232 @@ def print_question_data(question): difficulty_color = Colors.ORANGE elif difficulty == "Hard": difficulty_color = Colors.RED - - # Set fixed widths for the title and difficulty columns title_width = 50 - difficulty_width = 10 - # Width of the tick button - - # Align and pad the title and difficulty columns + difficulty_width = 1 title_formatted = title.ljust(title_width)[:title_width] difficulty_formatted = ( f"{difficulty_color}{difficulty.ljust(difficulty_width)}{Colors.RESET}" ) - # Determine the status symbol and color + paid_indicator = "$$$" if is_paid else "" + if status == "ac": status_symbol = "✔" status_color = Colors.GREEN else: status_symbol = "✘" status_color = Colors.RED - print( f"({status_color}{status_symbol.center(2)}{Colors.RESET})" - f"[{str(question_id).rjust(4)}] {title_formatted} {difficulty_formatted} ({ac_rate:.2f}%)" + f"[{str(question_id).rjust(4)}] {title_formatted} {difficulty_formatted} ({ac_rate:.2f}%) {paid_indicator}" ) +def print_test_result(test_data, data_input): + status_msg = test_data.get("status_msg") + + if status_msg == "Accepted": + status_runtime = test_data.get("status_runtime") + code_answer = test_data.get("code_answer") + expected_code_answer = test_data.get("expected_code_answer") + status_color = Colors.GREEN + + print("".center(40, "=")) + print(f"{status_color}{status_msg}{Colors.RESET} ({status_runtime})") + print("".center(40, "=")) + print("input".center(40, "-")) + print(data_input) + print("your code output".center(40, "-")) + print(code_answer) + print("expected output".center(40, "-")) + print(expected_code_answer) + print("".center(40, "=")) + else: + runtime_error = test_data.get("runtime_error") + full_runtime_error = test_data.get("full_runtime_error") + status_color = Colors.RED + + # Use BeautifulSoup to convert the runtime error message from HTML to plain text + soup = BeautifulSoup(full_runtime_error, "html.parser") + plain_runtime_error = soup.get_text() + + print("".center(40, "=")) + print(f"{status_color}{status_msg}{Colors.RESET}") + print("".center(40, "=")) + print("input".center(40, "-")) + print(data_input) + print("runtime error".center(40, "-")) + print(runtime_error) + print("full runtime error".center(40, "-")) + print(plain_runtime_error) + print("".center(40, "=")) + + +def print_submission_result(submission): # used python-leetcode library + run_success = submission.get("run_success") + status_msg = submission.get("status_msg") + if run_success and status_msg == "Accepted": + runtime_percentile = submission.get("runtime_percentile") + status_runtime = submission.get("status_runtime") + status_memory = submission.get("status_memory") + status_symbol = "✔" + status_color = Colors.GREEN + runtime_percentile_str = ( + f"{runtime_percentile:.2f}%" if runtime_percentile else "N/A" + ) + status_runtime_str = status_runtime.split()[0] if status_runtime else "N/A" + status_memory_str = status_memory.split()[0] if status_memory else "N/A" + print("".center(40, "=")) + print(f"{status_color}{status_msg}{Colors.RESET}") + print("Runtime".center(40, "-")) + print(f"{status_runtime_str}ms") + print(f"Beats {runtime_percentile_str} of users with Python3") + print("Memory".center(40, "-")) + print(f"{status_memory_str}mb") + print( + f"Beats {submission.get('memory_percentile', 0.0):.2f}% of users with Python3" + ) + print("".center(40, "=")) + elif run_success and status_msg == "Wrong Answer": + last_testcase = submission.get("last_testcase", "") + expected_output = submission.get("expected_output", "") + status_color = Colors.RED + print("".center(40, "=")) + print(f"{status_color}{status_msg}{Colors.RESET}") + print("".center(40, "=")) + print("last testcase".center(40, "-")) + print(last_testcase) + print("expected output".center(40, "-")) + print(expected_output) + print("your output".center(40, "-")) + print(submission.get("code_output", "")) + print("".center(40, "=")) + + elif not run_success: + runtime_error = submission.get("runtime_error", "") + full_runtime_error = submission.get("full_runtime_error", "") + last_testcase = submission.get("last_testcase", "") + status_color = Colors.RED + + runtime_error_text = BeautifulSoup(runtime_error, "html.parser") + full_runtime_error_text = BeautifulSoup(full_runtime_error, "html.parser") + + print("".center(40, "=")) + print(f"{status_color}{status_msg}{Colors.RESET}") + print("".center(40, "=")) + print("error".center(40, "-")) + print(runtime_error_text) + print(full_runtime_error_text) + print("last test case".center(40, "-")) + print(f"{Colors.RED}{last_testcase}{Colors.RESET}") + print("".center(40, "=")) + + +# leetcode-lib + + +def initialize_leetcode_api_instance( + leetcode_session, leetcode_csrf_token +): # used python-leetcode library + configuration = leetcode.Configuration() + csrf_token = leetcode_csrf_token + configuration.api_key["x-csrftoken"] = csrf_token + configuration.api_key["csrftoken"] = csrf_token + configuration.api_key["LEETCODE_SESSION"] = leetcode_session + configuration.api_key["Referer"] = "https://leetcode.com" + configuration.debug = False + + api_instance = leetcode.DefaultApi(leetcode.ApiClient(configuration)) + return api_instance + + +def interpret_solution(title_slug, payload, api_instance): + test_submission = leetcode.TestSubmission( + data_input=payload["data_input"], + typed_code=payload["typed_code"], + question_id=payload["question_id"], + test_mode=False, + lang=payload["lang"], # change this + ) + interpretation_id = api_instance.problems_problem_interpret_solution_post( + problem=title_slug, body=test_submission + ) + time.sleep(3) + test_submission_result = api_instance.submissions_detail_id_check_get( + id=interpretation_id.interpret_id + ) + print_test_result(test_submission_result, payload["data_input"]) + + +# --submit +def submit_solution( + api_instance, title_slug, code, question_id, lang_name +): # used python-leetcode library + submission = leetcode.Submission( + judge_type="large", + typed_code=code, + question_id=question_id, + test_mode=False, + lang=lang_name, # change this + ) + submission_id = api_instance.problems_problem_submit_post( + problem=title_slug, body=submission + ) + print("Submission has been queued. Result:") + time.sleep(3) + submission_result = api_instance.submissions_detail_id_check_get( + id=submission_id.submission_id + ) + print_submission_result(submission_result) + + +def process_test_file(leetcode_api_instance, api_instance, test): + title_slug, lang_name = title_and_file_extension(test) + question_detail_data = get_question_detail(api_instance, title_slug) + if question_detail_data: + question_id = question_detail_data.get("questionId") + sample_test_case = question_detail_data.get("sampleTestCase") + with open(test, "r") as file: + code = file.read() + payload = { + "lang": lang_name, + "question_id": question_id, + "typed_code": code, + "data_input": sample_test_case, + } + interpret_solution(title_slug, payload, leetcode_api_instance) + else: + print(f"Question with title slug '{title_slug}' not found.") + + +def process_submit_file(leetcode_api_instance, api_instance, submit_file): + with open(submit_file, "r") as file: + code = file.read() + title_slug, lang_name = title_and_file_extension(submit_file) + print(f"Title slug: {title_slug}") + question_detail_data = get_question_detail(api_instance, title_slug) + if question_detail_data: + question_id = question_detail_data.get("questionId") + print(f"Question ID: {question_id}") + submit_solution(leetcode_api_instance, title_slug, code, question_id, lang_name) + else: + print(f"Question with title slug '{title_slug}' not found.") + + def execute_graphql_query(api_instance, data): api_url = "https://leetcode.com/graphql/" - csrf_token, leetcode_session = api_instance - headers = { "Content-Type": "application/json", "Cookie": f"csrftoken={csrf_token}; LEETCODE_SESSION={leetcode_session}", - "Referer": "https://leetcode.com" + "Referer": "https://leetcode.com", } - data = { "operationName": data.get("operationName"), "query": data.get("query"), - "variables": data.get("variables") + "variables": data.get("variables"), } - response = requests.post(api_url, json=data, headers=headers) - if response.status_code == 200: return response.json() else: @@ -119,9 +309,7 @@ def get_question_data_by_id(api_instance, q): if ":" in q: start, end = map(int, q.split(":")) skip = start - print(skip) limit = end - print(limit) filters = {} else: limit = 1 @@ -132,18 +320,22 @@ def get_question_data_by_id(api_instance, q): "categorySlug": "", "skip": skip, "limit": limit, - "filters": filters + "filters": filters, } data = { "operationName": "problemsetQuestionList", "query": query, - "variables": query_variables + "variables": query_variables, } api_response = execute_graphql_query(api_instance, data) - if api_response and "data" in api_response and "problemsetQuestionList" in api_response["data"]: + if ( + api_response + and "data" in api_response + and "problemsetQuestionList" in api_response["data"] + ): return api_response["data"]["problemsetQuestionList"]["questions"] return None @@ -217,7 +409,7 @@ def get_question_detail(api_instance, title_slug): data = { "operationName": "getQuestionDetail", "query": query, - "variables": query_variables + "variables": query_variables, } api_response = execute_graphql_query(api_instance, data) @@ -227,63 +419,43 @@ def get_question_detail(api_instance, title_slug): return None +# --solve + LANG_EXTENSIONS = { - 'cpp': 'cpp', - 'java': 'java', - 'python': 'py', - 'python3': 'py', - 'c': 'c', - 'csharp': 'cs', - 'javascript': 'js', - 'ruby': 'rb', - 'swift': 'swift', - 'golang': 'go', - 'scala': 'scala', - 'kotlin': 'kt', - 'rust': 'rs', - 'php': 'php', - 'typescript': 'ts', - 'racket': 'rkt', - 'erlang': 'erl', - 'elixir': 'ex', - 'dart': 'dart' + "cpp": "cpp", + "java": "java", + # "python": "py", + "python3": "py", + "c": "c", + "csharp": "cs", + "javascript": "js", + "ruby": "rb", + "swift": "swift", + "golang": "go", + "scala": "scala", + "kotlin": "kt", + "rust": "rs", + "php": "php", + "typescript": "ts", + "racket": "rkt", + "erlang": "erl", + "elixir": "ex", + "dart": "dart", } -def get_code_snippets(question_detail_data, lang_slug): +def get_available_languages_and_code_snippets(question_detail_data): code_snippets = question_detail_data.get("codeSnippets", []) - return next((snippet['code'] for snippet in code_snippets if snippet['langSlug'] == lang_slug), None) + available_languages = [] + for snippet in code_snippets: + lang_slug = snippet.get("langSlug") + lang_name = snippet.get("text") or lang_slug + if lang_slug.lower() not in ["python"]: + available_languages.append((lang_slug, lang_name)) + return available_languages -def write_code_snippet_to_file(question_detail_data, lang, title_slug): - code = get_code_snippets(question_detail_data, lang) - if code: - lang_extension = LANG_EXTENSIONS.get(lang) - if lang_extension: - file_path = os.path.join("code_editor", - f"{question_detail_data['questionFrontendId']}_{title_slug}.{lang_extension}") - with open(file_path, "w") as file: - file.write(code) - print(f"Code snippet for {lang} has been written to {file_path}.") - else: - print(f"Language extension for {lang} is not available.") - else: - print(f"Code snippet for {lang} is not available for this question.") - - -def display_available_languages(question_detail_data): - code_snippets = question_detail_data.get('codeSnippets', []) - if code_snippets: - print("Available Languages:") - for index, snippet in enumerate(code_snippets): - lang_slug = snippet.get('langSlug') - lang_name = snippet.get('text') or lang_slug - print(f"{index + 1}. {lang_name} ({lang_slug})") - else: - print("No code snippets available.") - - -def display_question_detail(api_instance, title_slug): +def display_question_detail(api_instance, title_slug): # did changes here question_detail_data = get_question_detail(api_instance, title_slug) if question_detail_data: question_url = f"https://leetcode.com/problems/{title_slug}/" @@ -293,67 +465,163 @@ def display_question_detail(api_instance, title_slug): content_text = BeautifulSoup(content_html, "html.parser").get_text() print("Question Content:\n", content_text) - display_available_languages(question_detail_data) - - lang_input = input("Enter the index of the language you want to code: ").strip().lower() - try: - lang_index = int(lang_input) - if 1 <= lang_index <= len(question_detail_data.get('codeSnippets', [])): - selected_lang = question_detail_data['codeSnippets'][lang_index - 1]['langSlug'] - write_code_snippet_to_file(question_detail_data, selected_lang, title_slug) + ( + user_lang, + editor_cli, + ) = load_user_data_from_config() # Load the USER_LANG from config + if user_lang: + file_path = write_code_snippet_to_file( + question_detail_data, user_lang, title_slug + ) + # write_code_snippet_to_file(question_detail_data, user_lang, title_slug) + input("Press any key to continue...") + os.system(f"{editor_cli} {file_path}") + else: + available_languages = get_available_languages_and_code_snippets( + question_detail_data + ) + if not available_languages: + print("No code snippets available.") + return + print("Available Languages:") + for index, (lang_slug, lang_name) in enumerate(available_languages, 1): + print(f"{index}. {lang_slug}") + lang_input = ( + input("Enter the displayed index of the language you want to code: ") + .strip() + .lower() + ) + try: + lang_index = int(lang_input) + if 1 <= lang_index <= len(available_languages): + selected_lang = available_languages[lang_index - 1][0] + print(selected_lang) + write_code_snippet_to_file( + question_detail_data, selected_lang, title_slug + ) + else: + print("Invalid index. Please enter a valid index.") + except ValueError: + print("Invalid input. Please enter a valid index.") + + +def write_code_snippet_to_file(question_detail_data, lang, title_slug): # tags:path + code_snippets = question_detail_data.get("codeSnippets", []) + code = next( + (snippet["code"] for snippet in code_snippets if snippet["langSlug"] == lang), + None, + ) + if code: + lang_extension = LANG_EXTENSIONS.get(lang) + if lang_extension: + leetcode_path = load_user_path_from_config() + if not leetcode_path: + home_directory = os.path.expanduser("~") + leetcode_folder = os.path.join(home_directory, ".leetcode") else: - print("Invalid index. Please enter a valid index.") - except ValueError: - print("Invalid input. Please enter a valid index.") - - -def configuration(): - leetcode_session, csrf_token = load_credentials_from_config() - if not leetcode_session or not csrf_token: - leetcode_session = click.prompt("Enter your LeetCode session", type=str) - csrf_token = click.prompt("Enter your CSRF token", type=str) - save_credentials_to_config(leetcode_session, csrf_token) - return leetcode_session, csrf_token + leetcode_folder = leetcode_path + + if not os.path.exists(leetcode_folder): + os.makedirs(leetcode_folder) + file_path = os.path.join( + leetcode_folder, + f"{question_detail_data['questionFrontendId']}_{title_slug}.{lang_extension}", + ) + with open(file_path, "w") as file: + file.write(code) + print(f"Code snippet for {lang} has been written to {file_path}.") + return file_path + else: + print(f"Language extension for {lang} is not available.") + else: + print(f"Code snippet for {lang} is not available for this question.") def get_title_slug_from_filename(filepath): base_name = os.path.basename(filepath) title_slug, _ = os.path.splitext(base_name) - parts = title_slug.split('_') + parts = title_slug.split("_") return "_".join(parts[1:]) -def interpret_solution(api_instance, title_slug, payload): - csrf_token, leetcode_session = api_instance +def title_and_file_extension(file): + title_slug = get_title_slug_from_filename(file) + file_extension = file.split(".")[-1].lower() + lang_name = list(LANG_EXTENSIONS.keys())[ + list(LANG_EXTENSIONS.values()).index(file_extension) + ] + + return title_slug, lang_name + + +def print_help_usage(): + help_message = """ + IMPORTANT: python main.py --lib + + Usage: + python main.py --config + python main.py --config --user-lang + python main.py --question/-q + python main.py --solve + python main.py --test/-t + python main.py --submit/-sb + + Examples: + python main.py --config --user-lang=python3 + python main.py --config --user-path=/d/prog/lc + python main.py --question 1 + python main.py --question add-two-numbers + python main.py --question 10:20 + python main.py --solve/-s add-two-numbers + python main.py --solve 1 + python main.py --test test_file.py + python main.py --submit submit_file.py + + For any issues or feature requests, please visit: + https://github.com/hrdkmishra/leetcode.py + """ + print(help_message) - api_url = f"https://leetcode.com/problems/{title_slug}/interpret_solution/" - headers = { - "User-Agent": "curl/8.0.1", - "Host": "leetcode.com", - "Accept": "*/*", - "content-type": "application/json", - "Origin": "https://leetcode.com", - "Content-Type": "application/json", - "Referer": f"https://leetcode.com/problems/{title_slug}/", - "x-csrftoken": csrf_token, - "Cookie": f"LEETCODE_SESSION={leetcode_session};csrftoken={csrf_token};" - } +def replace_files(): + source_dir = "custom_lib_file/" + destination_dir = "venv/Lib/site-packages/leetcode/models/" + file_paths = glob.glob(os.path.join(source_dir, "*")) - response = requests.post(api_url, json=payload, headers=headers) + if not file_paths: + print(f"No files found in the source directory '{source_dir}'.") + return - time.sleep(10) + for src_path in file_paths: + filename = os.path.basename(src_path) + dest_path = os.path.join(destination_dir, filename) - if response.status_code == 200: - return response.json() - else: - print(f"Interpret solution request failed with status code {response.status_code}:") - print(response.text) - return None + if os.path.exists(dest_path): + try: + os.remove(dest_path) + shutil.copy(src_path, dest_path) + print(f"File '{src_path}' replaced successfully.") + except Exception as e: + print(f"An error occurred while replacing the file: {e}") + else: + print(f"Destination path '{dest_path}' does not exist.") @click.command() @click.option("--config", is_flag=True, help="Enter credentials and save to config") +@click.option( + "--user-lang", + type=str, + default="", + help="Set user preferred language (e.g., python3)", +) +@click.option( + "--user-path", + type=str, + default="", + help="Set user preferred path", +) +@click.option("--lib", is_flag=True, default=False, help="Show usage information") @click.option( "--question", "-q", @@ -361,13 +629,46 @@ def interpret_solution(api_instance, title_slug, payload): default="", help="Specify the question ID, title, or range (e.g., 10:20)", ) -@click.option("--solve", "-s", type=str, default="", - help="Specify the question title slug to solve (e.g., add-two-numbers)") -@click.option("--test", "-t", type=str, default="", - help="Specify the filename containing the code and input for testing") -def main(config, question, solve, test): +@click.option( + "--solve", + "-s", + type=str, + default="", + help="Specify the question title slug to solve (e.g., add-two-numbers)", +) +@click.option( + "--test", + "-t", + type=str, + default="", + help="Specify the filename containing the code and input for testing", +) +@click.option( + "--submit", + "-sb", + type=str, + default="", + help="Specify the filename containing the code to be submitted", +) +@click.option( + "--help", "-h", is_flag=True, default=False, help="Show usage information" +) +def main( + config, user_lang, user_path, question, solve, test, submit, lib, help +): # remove help_cmd + if lib: + replace_files() + exit() if config: - leetcode_session, csrf_token = configuration() + leetcode_session, csrf_token = non_lib_configuration() + # If the --user-lang option is provided, save it to config + if user_lang: + save_user_data_to_config(user_lang) + exit() + # allow user to enter their preferred path + elif user_path: + save_user_path_to_config(user_path) + exit() else: leetcode_session, csrf_token = load_credentials_from_config() @@ -379,43 +680,29 @@ def main(config, question, solve, test): elif question: question_data = get_question_data_by_id(api_instance, question) if question_data: - sorted_question_data = sorted(question_data, key=lambda x: int(x["frontendQuestionId"])) + sorted_question_data = sorted( + question_data, key=lambda x: int(x["frontendQuestionId"]) + ) for question_item in sorted_question_data: print_question_data(question_item) else: print(f"Question with ID or title '{question}' not found.") elif test: - print(f"Test file: {test}") - title_slug = get_title_slug_from_filename(test) - print(f"Title slug: {title_slug}") - question_detail_data = get_question_detail(api_instance, title_slug) - if question_detail_data: - question_id = question_detail_data.get("questionId") - print(f"Question ID: {question_id}") - sample_test_case = question_detail_data.get("sampleTestCase") - print(f"Sample Test Case: {sample_test_case}") - - with open(test, "r") as file: - code = file.read() - - payload = { - "lang": "python3", - "question_id": question_id, - "typed_code": code, - "data_input": sample_test_case - } - - json_payload = json.dumps(payload, indent=4) # Convert payload to JSON string - print(json_payload) - - result = interpret_solution(api_instance, title_slug, json_payload) - if result and "interpret_id" in result: - interpret_id = result["interpret_id"] - print(f"Interpret ID: {interpret_id}") - else: - print("Interpret solution failed.") - else: - print(f"Question with title slug '{title_slug}' not found.") + leetcode_api_instance = initialize_leetcode_api_instance( + leetcode_session, csrf_token + ) + process_test_file(leetcode_api_instance, api_instance, test) + elif submit: + leetcode_api_instance = initialize_leetcode_api_instance( + leetcode_session, csrf_token + ) + process_submit_file(leetcode_api_instance, api_instance, submit) + elif help: + print_help_usage() + else: + print( + "Please provide valid command line options. Use --help for usage information." + ) if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index 8e64431..2845ea4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,11 @@ beautifulsoup4==4.12.2 -black==23.7.0 certifi==2023.7.22 charset-normalizer==3.2.0 -click==8.1.6 +click==8.1.7 colorama==0.4.6 idna==3.4 -markdown-it-py==3.0.0 -mdurl==0.1.2 -mypy-extensions==1.0.0 -packaging==23.1 -pathspec==0.11.1 -platformdirs==3.9.1 -Pygments==2.15.1 python-dateutil==2.8.2 -python-dotenv==1.0.0 +python-leetcode==1.2.1 requests==2.31.0 six==1.16.0 soupsieve==2.4.1