Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
sudiptob2 authored Oct 13, 2022
2 parents 2e812a1 + 4f1f538 commit 75c6158
Show file tree
Hide file tree
Showing 16 changed files with 546 additions and 85 deletions.
40 changes: 40 additions & 0 deletions .github/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env sh

set -o errexit
set -o nounset

pyclean() {
# Cleaning cache:
find . |
grep -E '(__pycache__|\.hypothesis|\.perm|\.cache|\.static|\.py[cod]$)' |
xargs rm -rf
}

run_checks() {
echo '[Check Started]'
set -x # we want to print commands during the CI process.

# Running linting for all python files in the project:
python -m flake8

# Running type checking, see https://github.com/typeddjango/django-stubs
python -m mypy leeteasy tests

# Running tests:
python -m pytest --cov

# Checking dependencies status:
python -m pip check

set +x
echo '[checks completed]'
}

# Remove any cache before the script:
pyclean

# Clean everything up:
trap pyclean EXIT INT TERM

# Run the CI process:
run_checks
50 changes: 50 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Test
on:
pull_request:
types: [ready_for_review]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest, macOS-latest]
python-version: [ '3.8', '3.9', '3.10' ]

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
id: setup-python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Python version
run: python -c "import sys; print(sys.version)"

# Install pipenv
- name: Install pipenv
run: python3 -m pip install --upgrade pipenv

# create .venv folder
- name: create .venv folder
run: mkdir -p .venv

# caching dependencies
- name: Caching Dependencies
uses: actions/cache@v2
id: cache-dependencies
with:
path: .venv
key: ${{ matrix.os }}-python-${{ steps.setup-python.outputs.python-version }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}

# install dependencies
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
run: |
pipenv install --dev --verbose
pipenv install types-requests --dev
# Run bash script
- name: run Bash script
run: pipenv run bash ./.github/check.sh
7 changes: 7 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ notify-py = "*"
flake8 = "*"
pytest = "*"
pytest-mock = "*"
mypy = "*"
darglint = "*"
flake8-docstrings = "*"
wemake-python-styleguide = "*"
flake8-pytest-style = "*"
flake8-bandit = "*"
pytest-cov = "*"

[requires]
python_version = "3.8"
281 changes: 275 additions & 6 deletions Pipfile.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ crontab -e
@reboot python -m leeteasy start 14:30 &

```
## Contribution guideline
## Contributing

If you like this project and want to improve by adding features, fixing bugs or anything, please follow
the [contributing guidelines](docs/CONTRIBUTING.md).
We are very happy to see you here. Before sending your pull requests, make sure that you read the whole workflow and the naming conventions mentioned in the [contributing guidelines](docs/CONTRIBUTING.md).

If you have any doubts regarding the contributing guidelines, please feel free to [state it clearly in an issue](https://github.com/sudiptob2/leet-easy/issues/new/choose). All the best!
48 changes: 46 additions & 2 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,55 @@
Given below is the general workflow we expect you to follow while making contributions to this project.


### Workflow
1. Go to the issues tab and find an issue you would like to work on.

1.1. Clarify any doubts in the comments section of the issue.

2. Fork the project

3. Create a branch and make small changes on it.

4. Create a **draft PR**

5. Then make other changes and push to the remote branch you created. In this way, the maintainers will be able to provide early reviews and comments for your commits which will save time later on.


6. Once the above steps are done, you can change the PR status from **draft to active**


7. Once the PR is approved, make sure to update and sync your branch

8. Wait for the maintainers to merge your contribution

9. Congratulations! You made your first contribution to Leet Easy

<br>

### Fixing a bug, or adding a new feature

This section generally defines how you can make code contribution. Please follow the below instructions to make code contributions.

Code, PR, commit message format, etc convension I follow when I code. Please follow the links below to get details.
Please follow the links given below to see the code, PR, commit message, etc. conventions which we follow.

- [Git branch naming convension](BRANCH-NAMING.md)
- [Git branch naming convention](BRANCH-NAMING.md)

- [Conventional Commits](https://www.conventionalcommits.org/) for commit messages, and [Commit message format](https://gist.github.com/digitaljhelms/3761873) for what to write

- [Linking a PR to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)

<br>

### Examples

#### Branch Names: `{branch_type}/{issue-tracker-id-}issue-one-liner`
1. feature/2234-infinite-scroll
2. documentation/3344-linux-installation
3. test/2222-unit-tests

#### Commits
1. [#1234] feature: Submit button added.
2. [#1232] fix: Infinite scroll fixed
3. [#333] test: Add unit tests for xyz feature

Keep contributing. We're eager to see your contributions!
27 changes: 15 additions & 12 deletions leeteasy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import click
import schedule

from leeteasy.constant import Constant
from leeteasy.services.notification_service import Notifier
from leeteasy.utils.validatiors import TimeValidator

Expand All @@ -14,13 +15,13 @@
'-d',
'--difficulty',
type=click.Choice(['Medium', 'Hard'], case_sensitive=False),
help='Additional problem difficulty for notification.'
help='Additional problem difficulty for notification.',
)
@click.option(
"--sleep_duration",
default=3600,
type=click.IntRange(1, 3600, clamp=True),
help='Sleep duration in seconds.'
'--sleep_duration',
default=Constant.DEFAULT_SLEEP,
type=click.IntRange(1, Constant.DEFAULT_SLEEP, clamp=True),
help='Sleep duration in seconds.',
)
@click.argument('time')
def execute_start(time, difficulty, sleep_duration) -> None:
Expand All @@ -33,30 +34,32 @@ def execute_start(time, difficulty, sleep_duration) -> None:
Notifier.target_difficulty.append(difficulty)
schedule.every().day.at(time).do(Notifier.notify)

while True:
while True: # NOQA: WPS457
schedule.run_pending()
clock.sleep(sleep_duration)


@click.command('stop')
def execute_stop() -> None:
"""Stops leeteasy process"""
"""Stop leeteasy process."""
os.system('pkill -9 -f leeteasy')


@click.group('leeteasy')
def execute_root():
"""v0.4.0 | supported version strings: 0.7.2"""
pass
"""Group child command."""


execute_root.add_command(execute_start)
execute_root.add_command(execute_stop)

if __name__ == '__main__':
if platform != 'win32':
import pwd
import pwd # NOQA: WPS433

os.environ[
'DBUS_SESSION_BUS_ADDRESS'] = f'unix:path=/run/user/{pwd.getpwuid(os.getuid()).pw_uid}/bus' # NOQA: E501
bus_addr = 'unix:path=/run/user/{0}/bus'.format(
pwd.getpwuid(os.getuid()).pw_uid,
)

os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_addr
execute_root()
3 changes: 3 additions & 0 deletions leeteasy/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ class Constant:

# http call retries
HTTP_CALL_RETRIES = 3

# default sleep duration
DEFAULT_SLEEP = 3600
33 changes: 19 additions & 14 deletions leeteasy/models/challenge.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
from typing import List
from typing import Dict, List, Optional


class Challenge:
"""Singleton Model class for daily challenge."""

title: str = ''
raw_tags: List[dict] = None
raw_tags: List[Dict[str, str]] = []
ac_rate: float = 0
difficulty: str = None
question_id: int = None
difficulty: str = ''
question_id: int = 0
title_slug: str = ''
date: str = None
date: str = ''

def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(Challenge, cls).__new__(cls)
"""Override default class creation logic."""
if not hasattr(cls, 'instance'): # NOQA : WPS421
cls.instance = super(Challenge, cls).__new__(cls) # NOQA: WPS608
return cls.instance

@property
def problem_link(self) -> str:
"""Returns the link of the problem."""
"""Return the link of the problem."""
return 'https://leetcode.com/problems/{0}/'.format(
self.title_slug,
)

@property
def tags(self) -> List[str]:
"""Returns the link of the problem."""
def tags(self) -> List[Optional[str]]:
"""Return the link of the problem."""
tags = []
for tag in self.raw_tags:
tags.append(tag.get('name'))
return tags

def __str__(self):
"""Returns the string rep of the class."""
return f"Title: {self.title}\nAcceptance Rate: {self.ac_rate}" \
f"\nDifficulty: {self.difficulty}\n" + \
f"id: {self.question_id}\nTags: {self.tags}"
"""Return the string rep of the class."""
return 'Title: {0}\nAcceptance: {1}\nDifficulty: {2}\nID: {3}\nTags: {4}\n'.format(
self.title,
self.ac_rate,
self.difficulty,
self.question_id,
self.tags,
)
13 changes: 8 additions & 5 deletions leeteasy/services/notification_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from notifypy import Notify

from leeteasy.models.challenge import Challenge
from leeteasy.services.request_handler import RequestHandler
from leeteasy.services.request_parser import RequestParser

Expand All @@ -11,11 +12,11 @@ class Notifier:

target_difficulty = ['Easy']
app_name = 'LeetEasy'
challenge = None
challenge: Challenge

@classmethod
def prepare_notification(cls):
"""Prepares notification msg and triggers notification."""
"""Prepare notification msg and triggers notification."""
challenge_info = RequestHandler.get_challenge_info()
cls.challenge = RequestParser.parse(challenge_info)
if cls.challenge.difficulty in cls.target_difficulty:
Expand All @@ -27,14 +28,16 @@ def prepare_notification(cls):
@classmethod
def notify(cls):
"""Send desktop notification."""
app_name_with_subtitle = f'{cls.app_name} - Easy Problem Notification'
app_name_with_subtitle = '{0} - Easy Problem Notification'.format(cls.app_name)
icon_path = Path(__file__).parent.parent / 'assets/leetcoin.png'
notification = Notify(
default_notification_application_name=app_name_with_subtitle,
default_notification_icon=icon_path,
)
notification.message = cls.prepare_notification()
notification.title = f'{cls.app_name} - {cls.challenge.difficulty} ' \
f'Problem Alert \U0001F514'
notification.title = '{0} - {1} Problem Alert \U0001F514'.format(
cls.app_name,
cls.challenge.difficulty,
)
if notification.message:
notification.send()
28 changes: 11 additions & 17 deletions leeteasy/services/request_handler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import time
from typing import Union
from typing import Dict

import requests

Expand All @@ -9,23 +9,17 @@
class RequestHandler:
"""Provides services for requesting leetcode API."""

@classmethod
def get_challenge_info(cls) -> Union[dict, None]:
"""Gets daily challenge info from leetcode API."""
url = Constant.LEETCODE_API_ENDPOINT
query = Constant.DAILY_CODING_CHALLENGE_QUERY
max_retries = Constant.HTTP_CALL_RETRIES # Change HTTP_CALL_RETRIES for more retries
url = Constant.LEETCODE_API_ENDPOINT
query = Constant.DAILY_CODING_CHALLENGE_QUERY
max_retries = Constant.HTTP_CALL_RETRIES

for i in range(max_retries):
@classmethod
def get_challenge_info(cls) -> Dict:
"""Get daily challenge info from leetcode API."""
for iteration in range(cls.max_retries):
try:
response = requests.post(url, json={'query': query})
response = requests.post(cls.url, json={'query': cls.query})
return response.json().get('data').get('activeDailyCodingChallengeQuestion')
except Exception:
"""
On first hit sleep 10 minutes.
On second hit sleep 20 minutes.
On third hit sleep 30 minutes.
"""
time.sleep(((i+1)*10)*60)
raise SystemExit(f"""Connection to leetcode failed and max retries have been exceeded.
Total attempts made: {max_retries}. Try again""")
time.sleep(((iteration + 1) * 10) * 60)
raise SystemExit('Could not connect to the leetcode server.')
Loading

0 comments on commit 75c6158

Please sign in to comment.