Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit 2d02ebf

Browse files
Merge pull request #754 from justinjohnson-dev/main
Feat: URL Shortener Project
2 parents 22a356e + 82cda9c commit 2d02ebf

File tree

11 files changed

+225
-0
lines changed

11 files changed

+225
-0
lines changed

projects/url_shortener/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.mypy_cache
2+
.ruff_cache
3+
venv/

projects/url_shortener/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# URL Shortener
2+
3+
## 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.
4+
5+
## Using url shortener within this project
6+
7+
### setup
8+
1. Navigate to `projects\url_shortener`
9+
2. Create virtual environment `python -m venv venv`
10+
3. Activate virtual environment (I am on windows, different on MacOs) `venv\Scripts\activate`
11+
4. `pip install -r requirements.txt`
12+
13+
### execution
14+
1. `python app/main.py`
15+
16+
### example of execution via terminal
17+
```
18+
(venv) python-beginner-projects\projects\url_shortener>`python app/main.py`
19+
Enter the URL you want to shorten: https://www.bing.com/search?q=bing&cvid=e42b0995bcd045849a216437b88847be&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIGCAEQRRg8MgYIAhBFGDwyBggDEEUYPDIGCAQQRRg8MgYIBRBFGDzSAQczOTlqMGo0qAIIsAIB&FORM=ANAB01&PC=U531
20+
Shortened URL: https://tinyurl.com/275h2qwv
21+
```
22+
23+
### Extras
24+
I built code quality tools into project (linting, formatting, type annotation checks)
25+
26+
1. `ruff check .` - linter
27+
2. `ruff check --fix` - fix linting
28+
3. `ruff format` - formatting
29+
4. `mypy .` - type annotation checking
30+
31+
32+
Author: [justinjohnson-dev](https://github.com/justinjohnson-dev)
33+
Last Update: 05/25/2024

projects/url_shortener/app/__init__.py

Whitespace-only changes.

projects/url_shortener/app/main.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import os
2+
import sys
3+
4+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
5+
6+
from app.models.url_shortener import UrlShortenerValidation
7+
from app.services.url_shortener import URLShortener
8+
9+
10+
def run_url_shortener():
11+
entered_url = input("Enter the URL you want to shorten: ")
12+
13+
# Validate the entered URL
14+
url_validation = UrlShortenerValidation(url=entered_url)
15+
16+
# Extract the validated URL and convert it to string
17+
validated_url = str(url_validation.url)
18+
19+
# Pass the string URL to URLShortener
20+
url_shortener = URLShortener(url=validated_url)
21+
22+
result = url_shortener.shorten_url()
23+
if isinstance(result, str):
24+
print(f"Shortened URL: {result}")
25+
else:
26+
print(f"Error occurred: {result}")
27+
return result
28+
29+
if __name__ == "__main__":
30+
run_url_shortener()
31+
# run_url_shortener_with_other_library()
32+
33+
# def run_url_shortener_with_other_library():
34+
# entered_url = input("Enter the URL you want to shorten: ")
35+
36+
# # Validate the entered URL
37+
# url_validation = UrlShortenerValidation(url=entered_url)
38+
39+
# # Extract the validated URL and convert it to string
40+
# validated_url = str(url_validation.url)
41+
42+
# # Pass the string URL to URLShortener
43+
# url_shortener = URLShortener(url=validated_url, shortener=None) # enter the shortener you want to use
44+
45+
# result = url_shortener.shorten_url()
46+
# if isinstance(result, str):
47+
# print(f"Shortened URL: {result}")
48+
# else:
49+
# print(f"Error occurred: {result}")
50+
# return result

projects/url_shortener/app/models/__init__.py

Whitespace-only changes.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pydantic import BaseModel, HttpUrl
2+
3+
4+
class UrlShortenerValidation(BaseModel):
5+
url: HttpUrl

projects/url_shortener/app/services/__init__.py

Whitespace-only changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from configparser import ParsingError
2+
from typing import Optional, Union
3+
4+
import pyshorteners
5+
from pydantic import BaseModel, PrivateAttr, ValidationError
6+
7+
8+
class URLShortener(BaseModel):
9+
url: str
10+
_shortener: Optional[pyshorteners.Shortener] = PrivateAttr()
11+
12+
class Config:
13+
arbitrary_types_allowed = True
14+
15+
def __init__(self, **data):
16+
"""
17+
Initialize the URLShortener. If no 'shortener' is provided in data,
18+
a new pyshorteners.Shortener instance is created.
19+
"""
20+
shortener = data.pop("shortener", None) or pyshorteners.Shortener()
21+
super().__init__(**data)
22+
self._shortener = shortener
23+
24+
def shorten_url(self) -> Union[str, Exception]:
25+
if self._shortener is None:
26+
raise ValueError("Shortener is not initialized.")
27+
28+
try:
29+
return str(self._shortener.tinyurl.short(self.url))
30+
except (ParsingError, ValidationError) as error:
31+
return error
32+
except Exception as generic_error:
33+
return generic_error

projects/url_shortener/mypy.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Global options:
2+
[mypy]
3+
warn_return_any = True
4+
warn_unused_configs = True
5+
6+
# Per-module options:
7+
[mypy-pyshorteners.*]
8+
ignore_missing_imports = True
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
[tool.ruff]
2+
# Exclude a variety of commonly ignored directories.
3+
exclude = [
4+
".bzr",
5+
".direnv",
6+
".eggs",
7+
".git",
8+
".git-rewrite",
9+
".hg",
10+
".ipynb_checkpoints",
11+
".mypy_cache",
12+
".nox",
13+
".pants.d",
14+
".pyenv",
15+
".pytest_cache",
16+
".pytype",
17+
".ruff_cache",
18+
".svn",
19+
".tox",
20+
".venv",
21+
".vscode",
22+
"__pypackages__",
23+
"_build",
24+
"buck-out",
25+
"build",
26+
"dist",
27+
"node_modules",
28+
"site-packages",
29+
"venv",
30+
]
31+
32+
# Same as Black.
33+
line-length = 88
34+
indent-width = 4
35+
36+
# Assume Python 3.10
37+
target-version = "py310"
38+
39+
[tool.ruff.lint]
40+
# Enable the isort rules.
41+
extend-select = ["I"]
42+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
43+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
44+
# McCabe complexity (`C901`) by default.
45+
select = ["E4", "E7", "E9", "F"]
46+
ignore = []
47+
48+
# Allow fix for all enabled rules (when `--fix`) is provided.
49+
fixable = ["ALL"]
50+
unfixable = []
51+
52+
# Allow unused variables when underscore-prefixed.
53+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
54+
55+
[tool.ruff.format]
56+
# Like Black, use double quotes for strings.
57+
quote-style = "double"
58+
59+
# Like Black, indent with spaces, rather than tabs.
60+
indent-style = "tab"
61+
62+
# Like Black, respect magic trailing commas.
63+
skip-magic-trailing-comma = false
64+
65+
# Like Black, automatically detect the appropriate line ending.
66+
line-ending = "auto"
67+
68+
# Enable auto-formatting of code examples in docstrings. Markdown,
69+
# reStructuredText code/literal blocks and doctests are all supported.
70+
docstring-code-format = true
71+
72+
# Set the line length limit used when formatting code snippets in
73+
# docstrings.
74+
#
75+
# This only has an effect when the `docstring-code-format` setting is
76+
# enabled.
77+
docstring-code-line-length = "dynamic"

0 commit comments

Comments
 (0)