Skip to content

Commit 1690263

Browse files
authored
Merge pull request #42 from sudiptob2/refactor/41-strict-flake8
Refactor/41 strict flake8
2 parents 2be4b17 + be7182a commit 1690263

File tree

12 files changed

+444
-78
lines changed

12 files changed

+444
-78
lines changed

.github/check.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env sh
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
pyclean() {
7+
# Cleaning cache:
8+
find . |
9+
grep -E '(__pycache__|\.hypothesis|\.perm|\.cache|\.static|\.py[cod]$)' |
10+
xargs rm -rf
11+
}
12+
13+
run_checks() {
14+
echo '[Check Started]'
15+
set -x # we want to print commands during the CI process.
16+
17+
# Running linting for all python files in the project:
18+
python -m flake8
19+
20+
# Running type checking, see https://github.com/typeddjango/django-stubs
21+
python -m mypy leeteasy tests
22+
23+
# Running tests:
24+
python -m pytest --cov
25+
26+
# Checking dependencies status:
27+
python -m pip check
28+
29+
set +x
30+
echo '[checks completed]'
31+
}
32+
33+
# Remove any cache before the script:
34+
pyclean
35+
36+
# Clean everything up:
37+
trap pyclean EXIT INT TERM
38+
39+
# Run the CI process:
40+
run_checks

Pipfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ notify-py = "*"
1313
flake8 = "*"
1414
pytest = "*"
1515
pytest-mock = "*"
16+
mypy = "*"
17+
darglint = "*"
18+
flake8-docstrings = "*"
19+
wemake-python-styleguide = "*"
20+
flake8-pytest-style = "*"
21+
flake8-bandit = "*"
22+
pytest-cov = "*"
1623

1724
[requires]
1825
python_version = "3.8"

Pipfile.lock

Lines changed: 275 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

leeteasy/__main__.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import click
66
import schedule
77

8+
from leeteasy.constant import Constant
89
from leeteasy.services.notification_service import Notifier
910
from leeteasy.utils.validatiors import TimeValidator
1011

@@ -14,13 +15,13 @@
1415
'-d',
1516
'--difficulty',
1617
type=click.Choice(['Medium', 'Hard'], case_sensitive=False),
17-
help='Additional problem difficulty for notification.'
18+
help='Additional problem difficulty for notification.',
1819
)
1920
@click.option(
20-
"--sleep_duration",
21-
default=3600,
22-
type=click.IntRange(1, 3600, clamp=True),
23-
help='Sleep duration in seconds.'
21+
'--sleep_duration',
22+
default=Constant.DEFAULT_SLEEP,
23+
type=click.IntRange(1, Constant.DEFAULT_SLEEP, clamp=True),
24+
help='Sleep duration in seconds.',
2425
)
2526
@click.argument('time')
2627
def execute_start(time, difficulty, sleep_duration) -> None:
@@ -33,30 +34,32 @@ def execute_start(time, difficulty, sleep_duration) -> None:
3334
Notifier.target_difficulty.append(difficulty)
3435
schedule.every().day.at(time).do(Notifier.notify)
3536

36-
while True:
37+
while True: # NOQA: WPS457
3738
schedule.run_pending()
3839
clock.sleep(sleep_duration)
3940

4041

4142
@click.command('stop')
4243
def execute_stop() -> None:
43-
"""Stops leeteasy process"""
44+
"""Stop leeteasy process."""
4445
os.system('pkill -9 -f leeteasy')
4546

4647

4748
@click.group('leeteasy')
4849
def execute_root():
49-
"""v0.4.0 | supported version strings: 0.7.2"""
50-
pass
50+
"""Group child command."""
5151

5252

5353
execute_root.add_command(execute_start)
5454
execute_root.add_command(execute_stop)
5555

5656
if __name__ == '__main__':
5757
if platform != 'win32':
58-
import pwd
58+
import pwd # NOQA: WPS433
5959

60-
os.environ[
61-
'DBUS_SESSION_BUS_ADDRESS'] = f'unix:path=/run/user/{pwd.getpwuid(os.getuid()).pw_uid}/bus' # NOQA: E501
60+
bus_addr = 'unix:path=/run/user/{0}/bus'.format(
61+
pwd.getpwuid(os.getuid()).pw_uid,
62+
)
63+
64+
os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_addr
6265
execute_root()

leeteasy/constant.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ class Constant:
3434

3535
# http call retries
3636
HTTP_CALL_RETRIES = 3
37+
38+
# default sleep duration
39+
DEFAULT_SLEEP = 3600

leeteasy/models/challenge.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
1-
from typing import List
1+
from typing import Dict, List, Optional
22

33

44
class Challenge:
55
"""Singleton Model class for daily challenge."""
66

77
title: str = ''
8-
raw_tags: List[dict] = None
8+
raw_tags: List[Dict[str, str]] = []
99
ac_rate: float = 0
10-
difficulty: str = None
11-
question_id: int = None
10+
difficulty: str = ''
11+
question_id: int = 0
1212
title_slug: str = ''
13-
date: str = None
13+
date: str = ''
1414

1515
def __new__(cls):
16-
if not hasattr(cls, 'instance'):
17-
cls.instance = super(Challenge, cls).__new__(cls)
16+
"""Override default class creation logic."""
17+
if not hasattr(cls, 'instance'): # NOQA : WPS421
18+
cls.instance = super(Challenge, cls).__new__(cls) # NOQA: WPS608
1819
return cls.instance
1920

2021
@property
2122
def problem_link(self) -> str:
22-
"""Returns the link of the problem."""
23+
"""Return the link of the problem."""
2324
return 'https://leetcode.com/problems/{0}/'.format(
2425
self.title_slug,
2526
)
2627

2728
@property
28-
def tags(self) -> List[str]:
29-
"""Returns the link of the problem."""
29+
def tags(self) -> List[Optional[str]]:
30+
"""Return the link of the problem."""
3031
tags = []
3132
for tag in self.raw_tags:
3233
tags.append(tag.get('name'))
3334
return tags
3435

3536
def __str__(self):
36-
"""Returns the string rep of the class."""
37-
return f"Title: {self.title}\nAcceptance Rate: {self.ac_rate}" \
38-
f"\nDifficulty: {self.difficulty}\n" + \
39-
f"id: {self.question_id}\nTags: {self.tags}"
37+
"""Return the string rep of the class."""
38+
return 'Title: {0}\nAcceptance: {1}\nDifficulty: {2}\nID: {3}\nTags: {4}\n'.format(
39+
self.title,
40+
self.ac_rate,
41+
self.difficulty,
42+
self.question_id,
43+
self.tags,
44+
)

leeteasy/services/notification_service.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from notifypy import Notify
44

5+
from leeteasy.models.challenge import Challenge
56
from leeteasy.services.request_handler import RequestHandler
67
from leeteasy.services.request_parser import RequestParser
78

@@ -11,11 +12,11 @@ class Notifier:
1112

1213
target_difficulty = ['Easy']
1314
app_name = 'LeetEasy'
14-
challenge = None
15+
challenge: Challenge
1516

1617
@classmethod
1718
def prepare_notification(cls):
18-
"""Prepares notification msg and triggers notification."""
19+
"""Prepare notification msg and triggers notification."""
1920
challenge_info = RequestHandler.get_challenge_info()
2021
cls.challenge = RequestParser.parse(challenge_info)
2122
if cls.challenge.difficulty in cls.target_difficulty:
@@ -27,14 +28,16 @@ def prepare_notification(cls):
2728
@classmethod
2829
def notify(cls):
2930
"""Send desktop notification."""
30-
app_name_with_subtitle = f'{cls.app_name} - Easy Problem Notification'
31+
app_name_with_subtitle = '{0} - Easy Problem Notification'.format(cls.app_name)
3132
icon_path = Path(__file__).parent.parent / 'assets/leetcoin.png'
3233
notification = Notify(
3334
default_notification_application_name=app_name_with_subtitle,
3435
default_notification_icon=icon_path,
3536
)
3637
notification.message = cls.prepare_notification()
37-
notification.title = f'{cls.app_name} - {cls.challenge.difficulty} ' \
38-
f'Problem Alert \U0001F514'
38+
notification.title = '{0} - {1} Problem Alert \U0001F514'.format(
39+
cls.app_name,
40+
cls.challenge.difficulty,
41+
)
3942
if notification.message:
4043
notification.send()
Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import time
2-
from typing import Union
2+
from typing import Dict
33

44
import requests
55

@@ -9,23 +9,17 @@
99
class RequestHandler:
1010
"""Provides services for requesting leetcode API."""
1111

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

19-
for i in range(max_retries):
16+
@classmethod
17+
def get_challenge_info(cls) -> Dict:
18+
"""Get daily challenge info from leetcode API."""
19+
for iteration in range(cls.max_retries):
2020
try:
21-
response = requests.post(url, json={'query': query})
21+
response = requests.post(cls.url, json={'query': cls.query})
2222
return response.json().get('data').get('activeDailyCodingChallengeQuestion')
2323
except Exception:
24-
"""
25-
On first hit sleep 10 minutes.
26-
On second hit sleep 20 minutes.
27-
On third hit sleep 30 minutes.
28-
"""
29-
time.sleep(((i+1)*10)*60)
30-
raise SystemExit(f"""Connection to leetcode failed and max retries have been exceeded.
31-
Total attempts made: {max_retries}. Try again""")
24+
time.sleep(((iteration + 1) * 10) * 60)
25+
raise SystemExit('Could not connect to the leetcode server.')
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1+
from typing import Dict
2+
13
from leeteasy.models.challenge import Challenge
24

35

46
class RequestParser:
57
"""Parse responses of leetcode API."""
68

79
@classmethod
8-
def parse(cls, challenge_info: dict) -> Challenge:
10+
def parse(cls, challenge_info: Dict) -> Challenge:
911
"""Parse API data ans update challenge model."""
10-
return RequestParser._parse_challenge_info(challenge_info)
12+
return cls._parse_challenge_info(challenge_info)
1113

1214
@classmethod
1315
def _parse_challenge_info(cls, challenge_info) -> Challenge:
1416
"""Parse and update challenge model."""
17+
question = challenge_info.get('question')
1518
challenge = Challenge()
16-
challenge.title = challenge_info.get('question').get('title')
17-
challenge.ac_rate = challenge_info.get('question').get('acRate')
18-
challenge.difficulty = challenge_info.get('question').get('difficulty')
19-
challenge.question_id = challenge_info.get('question').get('frontendQuestionId')
19+
challenge.title = question.get('title')
20+
challenge.ac_rate = question.get('acRate')
21+
challenge.difficulty = question.get('difficulty')
22+
challenge.question_id = question.get('frontendQuestionId')
2023
challenge.date = challenge_info.get('date')
21-
challenge.title_slug = challenge_info.get('question').get('titleSlug')
22-
challenge.raw_tags = challenge_info.get('question').get('topicTags')
24+
challenge.title_slug = question.get('titleSlug')
25+
challenge.raw_tags = question.get('topicTags')
2326
return challenge

leeteasy/utils/validatiors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class TimeValidator:
88

99
@classmethod
1010
def validate(cls, time: str):
11-
"""Validates the given string is in valid time format."""
11+
"""Validate the given string is in valid time format."""
1212
try:
1313
return datetime.strptime(time, cls.time_format).time()
1414
except ValueError:

0 commit comments

Comments
 (0)