-
Notifications
You must be signed in to change notification settings - Fork 650
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #754 from justinjohnson-dev/main
Feat: URL Shortener Project
- Loading branch information
Showing
11 changed files
with
225 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.mypy_cache | ||
.ruff_cache | ||
venv/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# URL Shortener | ||
|
||
## Goal is to have a resuable class that engineers can use or copy/paste into their apps for shortening URLs. Engineer will be able to use any shortening library of their choice. In my example I will be using `pyshorteners`. I will also build an example with bringing a different library into the codebase. | ||
|
||
## Using url shortener within this project | ||
|
||
### setup | ||
1. Navigate to `projects\url_shortener` | ||
2. Create virtual environment `python -m venv venv` | ||
3. Activate virtual environment (I am on windows, different on MacOs) `venv\Scripts\activate` | ||
4. `pip install -r requirements.txt` | ||
|
||
### execution | ||
1. `python app/main.py` | ||
|
||
### example of execution via terminal | ||
``` | ||
(venv) python-beginner-projects\projects\url_shortener>`python app/main.py` | ||
Enter the URL you want to shorten: https://www.bing.com/search?q=bing&cvid=e42b0995bcd045849a216437b88847be&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIGCAEQRRg8MgYIAhBFGDwyBggDEEUYPDIGCAQQRRg8MgYIBRBFGDzSAQczOTlqMGo0qAIIsAIB&FORM=ANAB01&PC=U531 | ||
Shortened URL: https://tinyurl.com/275h2qwv | ||
``` | ||
|
||
### Extras | ||
I built code quality tools into project (linting, formatting, type annotation checks) | ||
|
||
1. `ruff check .` - linter | ||
2. `ruff check --fix` - fix linting | ||
3. `ruff format` - formatting | ||
4. `mypy .` - type annotation checking | ||
|
||
|
||
Author: [justinjohnson-dev](https://github.com/justinjohnson-dev) | ||
Last Update: 05/25/2024 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import os | ||
import sys | ||
|
||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) | ||
|
||
from app.models.url_shortener import UrlShortenerValidation | ||
from app.services.url_shortener import URLShortener | ||
|
||
|
||
def run_url_shortener(): | ||
entered_url = input("Enter the URL you want to shorten: ") | ||
|
||
# Validate the entered URL | ||
url_validation = UrlShortenerValidation(url=entered_url) | ||
|
||
# Extract the validated URL and convert it to string | ||
validated_url = str(url_validation.url) | ||
|
||
# Pass the string URL to URLShortener | ||
url_shortener = URLShortener(url=validated_url) | ||
|
||
result = url_shortener.shorten_url() | ||
if isinstance(result, str): | ||
print(f"Shortened URL: {result}") | ||
else: | ||
print(f"Error occurred: {result}") | ||
return result | ||
|
||
if __name__ == "__main__": | ||
run_url_shortener() | ||
# run_url_shortener_with_other_library() | ||
|
||
# def run_url_shortener_with_other_library(): | ||
# entered_url = input("Enter the URL you want to shorten: ") | ||
|
||
# # Validate the entered URL | ||
# url_validation = UrlShortenerValidation(url=entered_url) | ||
|
||
# # Extract the validated URL and convert it to string | ||
# validated_url = str(url_validation.url) | ||
|
||
# # Pass the string URL to URLShortener | ||
# url_shortener = URLShortener(url=validated_url, shortener=None) # enter the shortener you want to use | ||
|
||
# result = url_shortener.shorten_url() | ||
# if isinstance(result, str): | ||
# print(f"Shortened URL: {result}") | ||
# else: | ||
# print(f"Error occurred: {result}") | ||
# return result |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from pydantic import BaseModel, HttpUrl | ||
|
||
|
||
class UrlShortenerValidation(BaseModel): | ||
url: HttpUrl |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from configparser import ParsingError | ||
from typing import Optional, Union | ||
|
||
import pyshorteners | ||
from pydantic import BaseModel, PrivateAttr, ValidationError | ||
|
||
|
||
class URLShortener(BaseModel): | ||
url: str | ||
_shortener: Optional[pyshorteners.Shortener] = PrivateAttr() | ||
|
||
class Config: | ||
arbitrary_types_allowed = True | ||
|
||
def __init__(self, **data): | ||
""" | ||
Initialize the URLShortener. If no 'shortener' is provided in data, | ||
a new pyshorteners.Shortener instance is created. | ||
""" | ||
shortener = data.pop("shortener", None) or pyshorteners.Shortener() | ||
super().__init__(**data) | ||
self._shortener = shortener | ||
|
||
def shorten_url(self) -> Union[str, Exception]: | ||
if self._shortener is None: | ||
raise ValueError("Shortener is not initialized.") | ||
|
||
try: | ||
return str(self._shortener.tinyurl.short(self.url)) | ||
except (ParsingError, ValidationError) as error: | ||
return error | ||
except Exception as generic_error: | ||
return generic_error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Global options: | ||
[mypy] | ||
warn_return_any = True | ||
warn_unused_configs = True | ||
|
||
# Per-module options: | ||
[mypy-pyshorteners.*] | ||
ignore_missing_imports = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
[tool.ruff] | ||
# Exclude a variety of commonly ignored directories. | ||
exclude = [ | ||
".bzr", | ||
".direnv", | ||
".eggs", | ||
".git", | ||
".git-rewrite", | ||
".hg", | ||
".ipynb_checkpoints", | ||
".mypy_cache", | ||
".nox", | ||
".pants.d", | ||
".pyenv", | ||
".pytest_cache", | ||
".pytype", | ||
".ruff_cache", | ||
".svn", | ||
".tox", | ||
".venv", | ||
".vscode", | ||
"__pypackages__", | ||
"_build", | ||
"buck-out", | ||
"build", | ||
"dist", | ||
"node_modules", | ||
"site-packages", | ||
"venv", | ||
] | ||
|
||
# Same as Black. | ||
line-length = 88 | ||
indent-width = 4 | ||
|
||
# Assume Python 3.10 | ||
target-version = "py310" | ||
|
||
[tool.ruff.lint] | ||
# Enable the isort rules. | ||
extend-select = ["I"] | ||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. | ||
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or | ||
# McCabe complexity (`C901`) by default. | ||
select = ["E4", "E7", "E9", "F"] | ||
ignore = [] | ||
|
||
# Allow fix for all enabled rules (when `--fix`) is provided. | ||
fixable = ["ALL"] | ||
unfixable = [] | ||
|
||
# Allow unused variables when underscore-prefixed. | ||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" | ||
|
||
[tool.ruff.format] | ||
# Like Black, use double quotes for strings. | ||
quote-style = "double" | ||
|
||
# Like Black, indent with spaces, rather than tabs. | ||
indent-style = "tab" | ||
|
||
# Like Black, respect magic trailing commas. | ||
skip-magic-trailing-comma = false | ||
|
||
# Like Black, automatically detect the appropriate line ending. | ||
line-ending = "auto" | ||
|
||
# Enable auto-formatting of code examples in docstrings. Markdown, | ||
# reStructuredText code/literal blocks and doctests are all supported. | ||
docstring-code-format = true | ||
|
||
# Set the line length limit used when formatting code snippets in | ||
# docstrings. | ||
# | ||
# This only has an effect when the `docstring-code-format` setting is | ||
# enabled. | ||
docstring-code-line-length = "dynamic" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
annotated-types==0.7.0 | ||
certifi==2024.2.2 | ||
charset-normalizer==3.3.2 | ||
idna==3.7 | ||
mypy==1.10.0 | ||
mypy-extensions==1.0.0 | ||
pydantic==2.7.1 | ||
pydantic_core==2.18.2 | ||
pypiwin32==223 | ||
pyshorteners==1.0.1 | ||
pywin32==306 | ||
requests==2.32.2 | ||
ruff==0.4.5 | ||
setuptools==70.0.0 | ||
typing_extensions==4.12.0 | ||
urllib3==2.2.1 |