Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
samholt committed Apr 21, 2024
1 parent db76aaa commit 11e4fb0
Show file tree
Hide file tree
Showing 15 changed files with 540 additions and 51 deletions.
71 changes: 71 additions & 0 deletions docs/generated_examples/url_shortener_web_application/README.md
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

191 changes: 191 additions & 0 deletions docs/generated_examples/url_shortener_web_application/app.py
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)
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

8 changes: 8 additions & 0 deletions docs/generated_examples/url_shortener_web_application/run.sh
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 docs/generated_examples/url_shortener_web_application/setup.py
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",
],
},
)
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));
});
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;
}

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>
Loading

0 comments on commit 11e4fb0

Please sign in to comment.