-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
540 additions
and
51 deletions.
There are no files selected for viewing
71 changes: 71 additions & 0 deletions
71
docs/generated_examples/url_shortener_web_application/README.md
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,71 @@ | ||
# URL Shortening Service | ||
|
||
This project is a simple URL shortening service built with Flask. It allows users to submit long URLs and receive a shortened version for ease of sharing. Additionally, it supports user registration and login, URL analytics, and admin functionalities. | ||
|
||
## Getting Started | ||
|
||
### Prerequisites | ||
|
||
- Python 3.6 or higher | ||
- pip | ||
|
||
### Installation | ||
|
||
1. Clone the repository to your local machine. | ||
2. Navigate to the project directory. | ||
3. Install the required dependencies: | ||
|
||
``` | ||
pip install -r requirements.txt | ||
``` | ||
|
||
Or run the provided shell script: | ||
|
||
``` | ||
./run.sh | ||
``` | ||
|
||
### Running the Application | ||
|
||
To run the Flask application: | ||
|
||
``` | ||
python app.py | ||
``` | ||
|
||
Or use the entry point created by the setup.py: | ||
|
||
``` | ||
url-shortener | ||
``` | ||
|
||
### Using the Service | ||
|
||
- To shorten a URL, navigate to the home page, enter the URL in the provided form, and click `Shorten`. | ||
- To register or login, use the links provided on the home page. Once logged in, you can access additional features such as viewing and managing your shortened URLs and viewing analytics. | ||
- Admin users can view all shortened URLs, delete URLs or user accounts, and monitor system performance from the admin dashboard. | ||
|
||
### Running Tests | ||
|
||
To run the tests, execute the following command: | ||
|
||
``` | ||
pytest | ||
``` | ||
|
||
## Project Structure | ||
|
||
- `app.py`: The main Flask application file. | ||
- `requirements.txt`: Lists all the project dependencies. | ||
- `templates/`: Contains HTML templates for the web interface. | ||
- `static/`: Contains CSS and JavaScript files for the web interface. | ||
- `tests/`: Contains pytest tests for the application. | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! Please feel free to submit a pull request. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the LICENSE file for details. | ||
|
1 change: 1 addition & 0 deletions
1
docs/generated_examples/url_shortener_web_application/__init__.py
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 @@ | ||
|
191 changes: 191 additions & 0 deletions
191
docs/generated_examples/url_shortener_web_application/app.py
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,191 @@ | ||
import random | ||
import string | ||
from datetime import datetime, timedelta | ||
|
||
import requests | ||
from flask import Flask, jsonify, redirect, render_template, request, session | ||
from flask_session import Session | ||
|
||
app = Flask(__name__) | ||
app.config["SECRET_KEY"] = "a_very_secret_key" | ||
app.config["SESSION_TYPE"] = "filesystem" | ||
Session(app) | ||
|
||
# Mock database | ||
urls_db = {} | ||
users_db = {"admin": {"password": "admin", "urls": [], "is_admin": True}} | ||
# Analytics database | ||
analytics_db = {} | ||
|
||
|
||
@app.route("/") | ||
def home(): | ||
return render_template("index.html") | ||
|
||
|
||
@app.route("/register", methods=["POST"]) | ||
def register(): | ||
username = request.form["username"] | ||
password = request.form["password"] | ||
if username in users_db: | ||
return jsonify({"error": "Username already exists"}), 400 | ||
users_db[username] = {"password": password, "urls": []} | ||
return jsonify({"message": "User registered successfully"}), 200 | ||
|
||
|
||
@app.route("/login", methods=["POST"]) | ||
def login(): | ||
username = request.form["username"] | ||
password = request.form["password"] | ||
user = users_db.get(username) | ||
if user and user["password"] == password: | ||
session["user"] = username | ||
return jsonify({"message": "Logged in successfully"}), 200 | ||
else: | ||
return jsonify({"error": "Invalid username or password"}), 401 | ||
|
||
|
||
@app.route("/logout") | ||
def logout(): | ||
if "user" in session: | ||
session.pop("user", None) | ||
return jsonify({"message": "Logged out successfully"}), 200 | ||
|
||
|
||
@app.route("/shorten", methods=["POST"]) | ||
def shorten_url(): | ||
original_url = request.form["url"] | ||
expiration_date = request.form.get("expiration_date") | ||
if expiration_date: | ||
expiration_date = datetime.strptime(expiration_date, "%Y-%m-%d %H:%M:%S") | ||
else: | ||
expiration_date = datetime.now() + timedelta(days=365) # Default expiration 1 year from now | ||
if validate_url(original_url): | ||
short_url = generate_short_url() | ||
urls_db[short_url] = {"url": original_url, "expiration": expiration_date} | ||
if "user" in session: | ||
users_db[session["user"]]["urls"].append(short_url) | ||
# Initialize analytics for the new short URL | ||
analytics_db[short_url] = {"clicks": 0, "click_details": []} | ||
return ( | ||
jsonify( | ||
{ | ||
"original_url": original_url, | ||
"short_url": short_url, | ||
"expiration_date": expiration_date.strftime("%Y-%m-%d %H:%M:%S"), | ||
} | ||
), | ||
200, | ||
) | ||
else: | ||
return jsonify({"error": "Invalid URL"}), 400 | ||
|
||
|
||
@app.route("/<short_url>") | ||
def redirect_to_original(short_url): | ||
if short_url in urls_db and datetime.now() <= urls_db[short_url]["expiration"]: | ||
# Update analytics | ||
update_analytics(short_url, request.remote_addr) | ||
return redirect(urls_db[short_url]["url"]) | ||
elif short_url in urls_db and datetime.now() > urls_db[short_url]["expiration"]: | ||
return jsonify({"error": "URL has expired"}), 410 | ||
else: | ||
return jsonify({"error": "URL not found"}), 404 | ||
|
||
|
||
@app.route("/analytics/<short_url>") | ||
def view_analytics(short_url): | ||
if short_url in analytics_db: | ||
return jsonify(analytics_db[short_url]), 200 | ||
else: | ||
return jsonify({"error": "Analytics not found for the given URL"}), 404 | ||
|
||
|
||
@app.route("/user/urls") | ||
def user_urls(): | ||
if "user" in session: | ||
user_urls = users_db[session["user"]]["urls"] | ||
return jsonify({"urls": user_urls}), 200 | ||
else: | ||
return jsonify({"error": "Unauthorized"}), 401 | ||
|
||
|
||
@app.route("/user/edit/<short_url>", methods=["POST"]) | ||
def edit_url(short_url): | ||
if "user" in session and short_url in users_db[session["user"]]["urls"]: | ||
new_url = request.form["url"] | ||
if validate_url(new_url): | ||
urls_db[short_url] = new_url | ||
return jsonify({"message": "URL updated successfully"}), 200 | ||
else: | ||
return jsonify({"error": "Invalid URL"}), 400 | ||
else: | ||
return jsonify({"error": "Unauthorized or URL not found"}), 401 | ||
|
||
|
||
@app.route("/user/delete/<short_url>", methods=["DELETE"]) | ||
def delete_url(short_url): | ||
if "user" in session and short_url in users_db[session["user"]]["urls"]: | ||
users_db[session["user"]]["urls"].remove(short_url) | ||
urls_db.pop(short_url, None) | ||
analytics_db.pop(short_url, None) | ||
return jsonify({"message": "URL deleted successfully"}), 200 | ||
else: | ||
return jsonify({"error": "Unauthorized or URL not found"}), 401 | ||
|
||
|
||
# Admin routes | ||
@app.route("/admin/urls") | ||
def admin_view_urls(): | ||
if "user" in session and users_db.get(session["user"], {}).get("is_admin", False): | ||
return jsonify({"urls": list(urls_db.keys())}), 200 | ||
else: | ||
return jsonify({"error": "Unauthorized"}), 401 | ||
|
||
|
||
@app.route("/admin/delete/url/<short_url>", methods=["DELETE"]) | ||
def admin_delete_url(short_url): | ||
if "user" in session and users_db.get(session["user"], {}).get("is_admin", False): | ||
urls_db.pop(short_url, None) | ||
analytics_db.pop(short_url, None) | ||
return jsonify({"message": "URL deleted successfully"}), 200 | ||
else: | ||
return jsonify({"error": "Unauthorized"}), 401 | ||
|
||
|
||
@app.route("/admin/delete/user/<username>", methods=["DELETE"]) | ||
def admin_delete_user(username): | ||
if "user" in session and users_db.get(session["user"], {}).get("is_admin", False): | ||
if username in users_db: | ||
del users_db[username] | ||
return jsonify({"message": "User deleted successfully"}), 200 | ||
else: | ||
return jsonify({"error": "User not found"}), 404 | ||
else: | ||
return jsonify({"error": "Unauthorized"}), 401 | ||
|
||
|
||
def validate_url(url): | ||
try: | ||
response = requests.head(url, allow_redirects=True) | ||
return response.status_code == 200 | ||
except requests.RequestException: | ||
return False | ||
|
||
|
||
def generate_short_url(length=6): | ||
characters = string.ascii_letters + string.digits | ||
short_url = "".join(random.choice(characters) for _ in range(length)) | ||
while short_url in urls_db: | ||
short_url = "".join(random.choice(characters) for _ in range(length)) | ||
return short_url | ||
|
||
|
||
def update_analytics(short_url, ip_address): | ||
analytics_db[short_url]["clicks"] += 1 | ||
click_detail = {"timestamp": datetime.now().isoformat(), "ip_address": ip_address} | ||
analytics_db[short_url]["click_details"].append(click_detail) | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run(debug=True) |
5 changes: 5 additions & 0 deletions
5
docs/generated_examples/url_shortener_web_application/requirements.txt
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 @@ | ||
Flask==2.0.2 | ||
requests==2.26.0 | ||
pytest==6.2.5 | ||
Flask-Session==0.4.0 | ||
|
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 @@ | ||
#!/bin/bash | ||
|
||
# Install dependencies | ||
pip install -r requirements.txt | ||
|
||
# Run the Flask application | ||
FLASK_APP=app.py flask run | ||
|
19 changes: 19 additions & 0 deletions
19
docs/generated_examples/url_shortener_web_application/setup.py
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,19 @@ | ||
from setuptools import find_packages, setup | ||
|
||
setup( | ||
name="url_shortener", | ||
version="0.1.0", | ||
packages=find_packages(), | ||
include_package_data=True, | ||
install_requires=[ | ||
"Flask==2.0.2", | ||
"requests==2.26.0", | ||
"pytest==6.2.5", | ||
"Flask-Session==0.4.0", | ||
], | ||
entry_points={ | ||
"console_scripts": [ | ||
"url-shortener=app:app.run", | ||
], | ||
}, | ||
) |
57 changes: 57 additions & 0 deletions
57
docs/generated_examples/url_shortener_web_application/static/scripts.js
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,57 @@ | ||
document.getElementById('shortenForm')?.addEventListener('submit', function(e) { | ||
e.preventDefault(); | ||
const url = e.target.url.value; | ||
fetch('/shorten', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: `url=${encodeURIComponent(url)}` | ||
}).then(response => response.json()).then(data => { | ||
if(data.error) { | ||
document.getElementById('result').textContent = data.error; | ||
} else { | ||
document.getElementById('result').innerHTML = `Shortened URL: <a href="/${data.short_url}">${data.short_url}</a>`; | ||
} | ||
}).catch(error => console.error('Error:', error)); | ||
}); | ||
|
||
document.getElementById('registerForm')?.addEventListener('submit', function(e) { | ||
e.preventDefault(); | ||
const username = e.target.username.value; | ||
const password = e.target.password.value; | ||
fetch('/register', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}` | ||
}).then(response => response.json()).then(data => { | ||
if(data.error) { | ||
document.getElementById('result').textContent = data.error; | ||
} else { | ||
document.getElementById('result').textContent = 'Registration successful. Please login.'; | ||
setTimeout(() => window.location.href = '/login', 2000); | ||
} | ||
}).catch(error => console.error('Error:', error)); | ||
}); | ||
|
||
document.getElementById('loginForm')?.addEventListener('submit', function(e) { | ||
e.preventDefault(); | ||
const username = e.target.username.value; | ||
const password = e.target.password.value; | ||
fetch('/login', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}` | ||
}).then(response => response.json()).then(data => { | ||
if(data.error) { | ||
document.getElementById('result').textContent = data.error; | ||
} else { | ||
document.getElementById('result').textContent = 'Login successful.'; | ||
setTimeout(() => window.location.href = '/', 2000); | ||
} | ||
}).catch(error => console.error('Error:', error)); | ||
}); |
12 changes: 12 additions & 0 deletions
12
docs/generated_examples/url_shortener_web_application/static/styles.css
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,12 @@ | ||
body { | ||
font-family: Arial, sans-serif; | ||
} | ||
|
||
form { | ||
margin-bottom: 20px; | ||
} | ||
|
||
input[type="url"], input[type="submit"] { | ||
margin-top: 10px; | ||
} | ||
|
15 changes: 15 additions & 0 deletions
15
docs/generated_examples/url_shortener_web_application/templates/analytics.html
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,15 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Analytics</title> | ||
<link rel="stylesheet" href="/static/styles.css"> | ||
</head> | ||
<body> | ||
<h1>Analytics</h1> | ||
<div id="analyticsResult"></div> | ||
|
||
<script src="/static/scripts.js"></script> | ||
</body> | ||
</html> |
Oops, something went wrong.