Added click tracking
This commit is contained in:
+38
-4
@@ -5,7 +5,7 @@ from tiny0.config import WEBSITE_DOMAIN
|
|||||||
from tiny0 import db
|
from tiny0 import db
|
||||||
from tiny0.models import URL
|
from tiny0.models import URL
|
||||||
|
|
||||||
# Validates a URL
|
# Validates a url
|
||||||
def validate_URL(form, field):
|
def validate_URL(form, field):
|
||||||
# Make sure the url is not too short or long
|
# Make sure the url is not too short or long
|
||||||
if len(field.data) < 4 or len(field.data) > 2000:
|
if len(field.data) < 4 or len(field.data) > 2000:
|
||||||
@@ -64,11 +64,45 @@ def validate_token(form, field):
|
|||||||
# Raise a ValidationError
|
# Raise a ValidationError
|
||||||
raise ValidationError("Token already exists")
|
raise ValidationError("Token already exists")
|
||||||
|
|
||||||
|
# Validates a short url
|
||||||
|
def validate_short_URL(form, field):
|
||||||
|
# Make sure the short url is not too short or long
|
||||||
|
if len(field.data) < (len(WEBSITE_DOMAIN) + 7) or len(field.data) > (len(WEBSITE_DOMAIN) + 25):
|
||||||
|
return
|
||||||
|
|
||||||
|
# If the start of the short url is not valid
|
||||||
|
if (not(field.data.lower().startswith(WEBSITE_DOMAIN + "/"))
|
||||||
|
and not(field.data.lower().startswith("http://" + WEBSITE_DOMAIN + "/"))
|
||||||
|
and not(field.data.lower().startswith("https://" + WEBSITE_DOMAIN + "/"))):
|
||||||
|
# Raise a ValidationError
|
||||||
|
raise ValidationError("Invalid short URL")
|
||||||
|
|
||||||
|
# Get the token of the short url
|
||||||
|
if field.data.lower().startswith(WEBSITE_DOMAIN + "/"):
|
||||||
|
token = field.data[len(WEBSITE_DOMAIN) + 1:]
|
||||||
|
|
||||||
|
elif field.data.lower().startswith("http://" + WEBSITE_DOMAIN + "/"):
|
||||||
|
token = field.data[len(WEBSITE_DOMAIN) + 8:]
|
||||||
|
|
||||||
|
elif field.data.lower().startswith("https://" + WEBSITE_DOMAIN + "/"):
|
||||||
|
token = field.data[len(WEBSITE_DOMAIN) + 9:]
|
||||||
|
|
||||||
|
# If the token of the short url does not exist in the database
|
||||||
|
if not db.session.query(db.session.query(URL).filter_by(token=token).exists()).scalar():
|
||||||
|
# Raise a ValidationError
|
||||||
|
raise ValidationError("That short URL does not exists")
|
||||||
|
|
||||||
|
# After all the validation is done set the forms url value as the token
|
||||||
|
field.data = token
|
||||||
|
|
||||||
class URLForm(FlaskForm):
|
class URLForm(FlaskForm):
|
||||||
url = StringField(validators=[DataRequired(),
|
url = StringField(validators=[DataRequired(), Length(min=4, max=2000, message="Invalid URL length"), validate_URL])
|
||||||
Length(min=4, max=2000, message="Invalid URL length"),
|
|
||||||
validate_URL])
|
|
||||||
|
|
||||||
token = StringField(validators=[Optional(), Length(min=6, max=16, message="Invalid token length"), validate_token])
|
token = StringField(validators=[Optional(), Length(min=6, max=16, message="Invalid token length"), validate_token])
|
||||||
|
|
||||||
submit = SubmitField("Shorten")
|
submit = SubmitField("Shorten")
|
||||||
|
|
||||||
|
class TrackerForm(FlaskForm):
|
||||||
|
url = StringField(validators=[DataRequired(), Length(min=len(WEBSITE_DOMAIN) + 7, max=len(WEBSITE_DOMAIN) + 25, message="Invalid short URL"), validate_short_URL])
|
||||||
|
|
||||||
|
submit = SubmitField("Track")
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ class URL(db.Model):
|
|||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
token = db.Column(db.String(16), index=True, unique=True, nullable=False)
|
token = db.Column(db.String(16), index=True, unique=True, nullable=False)
|
||||||
url = db.Column(db.String(2000), nullable=False)
|
url = db.Column(db.String(2000), nullable=False)
|
||||||
|
clicks = db.Column(db.Integer, nullable=False, default=0)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"'{self.id}' '{self.token}' '{self.url}'"
|
return f"'{self.id}' '{self.token}' '{self.url} '{self.clicks}'"
|
||||||
|
|||||||
+25
-2
@@ -1,6 +1,6 @@
|
|||||||
from flask import render_template, redirect, url_for
|
from flask import render_template, redirect, url_for
|
||||||
from tiny0 import app, db
|
from tiny0 import app, db
|
||||||
from tiny0.forms import URLForm
|
from tiny0.forms import URLForm, TrackerForm
|
||||||
from tiny0.models import URL
|
from tiny0.models import URL
|
||||||
from tiny0.token import gen_valid_token
|
from tiny0.token import gen_valid_token
|
||||||
from tiny0.config import WEBSITE_DOMAIN
|
from tiny0.config import WEBSITE_DOMAIN
|
||||||
@@ -35,7 +35,7 @@ def index():
|
|||||||
# Return the url page with the shortened url
|
# Return the url page with the shortened url
|
||||||
return render_template("url.html", url=WEBSITE_DOMAIN + "/" + token)
|
return render_template("url.html", url=WEBSITE_DOMAIN + "/" + token)
|
||||||
|
|
||||||
# If the form was invalid or not submitted
|
# Else if the form was invalid or not submitted
|
||||||
else:
|
else:
|
||||||
# Return the index page with the form
|
# Return the index page with the form
|
||||||
return render_template("index.html", form=form)
|
return render_template("index.html", form=form)
|
||||||
@@ -53,9 +53,32 @@ def short_url(token):
|
|||||||
|
|
||||||
# Else if the query response contained data
|
# Else if the query response contained data
|
||||||
else:
|
else:
|
||||||
|
# Addd one to the clicks of the shortened url
|
||||||
|
query.clicks += 1
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
# Redirect to the url of the token
|
# Redirect to the url of the token
|
||||||
return redirect(query.url)
|
return redirect(query.url)
|
||||||
|
|
||||||
|
# Click Tracker route
|
||||||
|
@app.route("/tracker", methods=['GET', 'POST'])
|
||||||
|
def tracker():
|
||||||
|
# Create a instance of the form
|
||||||
|
form = TrackerForm()
|
||||||
|
|
||||||
|
# If the form was valid
|
||||||
|
if form.validate_on_submit():
|
||||||
|
# Get the clicks of the given token
|
||||||
|
clicks = URL.query.filter_by(token=form.url.data).first().clicks
|
||||||
|
|
||||||
|
# Return the clicks page with the clicks of that token
|
||||||
|
return render_template("clicks.html", clicks=clicks)
|
||||||
|
|
||||||
|
# Else if the form was invalid or not submitted
|
||||||
|
else:
|
||||||
|
# Return the tracker page with the form
|
||||||
|
return render_template("tracker.html", form=form)
|
||||||
|
|
||||||
# Donate route
|
# Donate route
|
||||||
@app.route("/donate")
|
@app.route("/donate")
|
||||||
def donate():
|
def donate():
|
||||||
|
|||||||
@@ -110,6 +110,43 @@ body {
|
|||||||
margin-bottom: 2vh;
|
margin-bottom: 2vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.click-tracker {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 5vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.click-tracker a:link {
|
||||||
|
color: #bb86fc;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.click-tracker a:visited {
|
||||||
|
color: #bb86fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.click-tracker a:hover {
|
||||||
|
color: #7b59a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clicks-list {
|
||||||
|
text-align: center;
|
||||||
|
margin: 30vh 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clicks-number {
|
||||||
|
color: #bb86fc;
|
||||||
|
font-size: 125px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clicks-text {
|
||||||
|
color: #bb86fc;
|
||||||
|
font-size: 25px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.error-message {
|
.error-message {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<ul class="clicks-list">
|
||||||
|
<li><p class="clicks-number">{{ clicks }}</p></li>
|
||||||
|
<li><p class="clicks-text">Click(s)</p></li>
|
||||||
|
</ul>
|
||||||
|
<div class="click-tracker">
|
||||||
|
<a href="{{ url_for('tracker') }}">Track Another</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<form method="POST" action="" class="url-form">
|
<form method="POST" action="" class="url-form">
|
||||||
{% if form.url.errors or form.token.errors %}
|
{% if form.errors %}
|
||||||
<ul class="form-error-message">
|
<ul class="form-error-message">
|
||||||
{% for error in form.url.errors %}
|
{% for error in form.url.errors %}
|
||||||
<li>{{ error }}</li>
|
<li>{{ error }}</li>
|
||||||
@@ -24,4 +24,7 @@
|
|||||||
<li>{{ form.submit(class="button") }}</li>
|
<li>{{ form.submit(class="button") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="click-tracker">
|
||||||
|
<a href="{{ url_for('tracker') }}">Click Tracker</a>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
{% if form %}
|
{% if form %}
|
||||||
{% if not form.url.errors and not form.token.errors %}
|
{% if not form.errors %}
|
||||||
<body onload="typeTitle()">
|
<body onload="typeTitle()">
|
||||||
<div class="headers">
|
<div class="headers">
|
||||||
<a class="title" id="title" href="{{ url_for('index') }}"></a>
|
<a class="title" id="title" href="{{ url_for('index') }}"></a>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
{% block body %}{% endblock %}
|
{% block body %}{% endblock %}
|
||||||
|
|
||||||
{% if form %}
|
{% if form %}
|
||||||
{% if not form.url.errors and not form.token.errors %}
|
{% if not form.errors %}
|
||||||
<script>
|
<script>
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var title_text = "tiny0";
|
var title_text = "tiny0";
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<form method="POST" action="" class="url-form">
|
||||||
|
{% if form.errors %}
|
||||||
|
<ul class="form-error-message">
|
||||||
|
{% for error in form.url.errors %}
|
||||||
|
<li>{{ error }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<ul class='input-list'>
|
||||||
|
<li>{{ form.url(placeholder="Enter the short URL here", autofocus=true, class="url") }}</li>
|
||||||
|
<li>{{ form.submit(class="button") }}</li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -7,4 +7,7 @@
|
|||||||
<li><button onclick="copyURL()" class="button">Copy</button></li>
|
<li><button onclick="copyURL()" class="button">Copy</button></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="click-tracker">
|
||||||
|
<a href="{{ url_for('tracker') }}">Click Tracker</a>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user