From e65f43e40177afd35dd9af0616ba6bf9476f7a9d Mon Sep 17 00:00:00 2001 From: xemeds Date: Sat, 22 Aug 2020 15:36:09 +0300 Subject: [PATCH] Added click tracking --- tiny0/forms.py | 42 ++++++++++++++++++++++++++++++++---- tiny0/models.py | 3 ++- tiny0/routes.py | 27 +++++++++++++++++++++-- tiny0/static/style.css | 37 +++++++++++++++++++++++++++++++ tiny0/templates/clicks.html | 11 ++++++++++ tiny0/templates/index.html | 5 ++++- tiny0/templates/layout.html | 4 ++-- tiny0/templates/tracker.html | 18 ++++++++++++++++ tiny0/templates/url.html | 3 +++ 9 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 tiny0/templates/clicks.html create mode 100644 tiny0/templates/tracker.html diff --git a/tiny0/forms.py b/tiny0/forms.py index c546bea..9bbfe7f 100644 --- a/tiny0/forms.py +++ b/tiny0/forms.py @@ -5,7 +5,7 @@ from tiny0.config import WEBSITE_DOMAIN from tiny0 import db from tiny0.models import URL -# Validates a URL +# Validates a url def validate_URL(form, field): # Make sure the url is not too short or long if len(field.data) < 4 or len(field.data) > 2000: @@ -64,11 +64,45 @@ def validate_token(form, field): # Raise a ValidationError 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): - url = StringField(validators=[DataRequired(), - Length(min=4, max=2000, message="Invalid URL length"), - validate_URL]) + url = StringField(validators=[DataRequired(), 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]) 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") diff --git a/tiny0/models.py b/tiny0/models.py index 55d397d..1d59f0f 100644 --- a/tiny0/models.py +++ b/tiny0/models.py @@ -4,6 +4,7 @@ class URL(db.Model): id = db.Column(db.Integer, primary_key=True) token = db.Column(db.String(16), index=True, unique=True, nullable=False) url = db.Column(db.String(2000), nullable=False) + clicks = db.Column(db.Integer, nullable=False, default=0) def __repr__(self): - return f"'{self.id}' '{self.token}' '{self.url}'" + return f"'{self.id}' '{self.token}' '{self.url} '{self.clicks}'" diff --git a/tiny0/routes.py b/tiny0/routes.py index aa180b7..445108e 100644 --- a/tiny0/routes.py +++ b/tiny0/routes.py @@ -1,6 +1,6 @@ from flask import render_template, redirect, url_for from tiny0 import app, db -from tiny0.forms import URLForm +from tiny0.forms import URLForm, TrackerForm from tiny0.models import URL from tiny0.token import gen_valid_token from tiny0.config import WEBSITE_DOMAIN @@ -35,7 +35,7 @@ def index(): # Return the url page with the shortened url 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: # Return the index page with the form return render_template("index.html", form=form) @@ -53,9 +53,32 @@ def short_url(token): # Else if the query response contained data else: + # Addd one to the clicks of the shortened url + query.clicks += 1 + db.session.commit() + # Redirect to the url of the token 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 @app.route("/donate") def donate(): diff --git a/tiny0/static/style.css b/tiny0/static/style.css index 621d3e8..457e7df 100644 --- a/tiny0/static/style.css +++ b/tiny0/static/style.css @@ -110,6 +110,43 @@ body { 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 { text-align: center; color: #ffffff; diff --git a/tiny0/templates/clicks.html b/tiny0/templates/clicks.html new file mode 100644 index 0000000..6cda8a2 --- /dev/null +++ b/tiny0/templates/clicks.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block body %} + +
+ Track Another +
+{% endblock %} diff --git a/tiny0/templates/index.html b/tiny0/templates/index.html index 7754587..cae8ce7 100644 --- a/tiny0/templates/index.html +++ b/tiny0/templates/index.html @@ -2,7 +2,7 @@ {% block body %}
- {% if form.url.errors or form.token.errors %} + {% if form.errors %}
+
+ Click Tracker +
{% endblock %} diff --git a/tiny0/templates/layout.html b/tiny0/templates/layout.html index 397d906..f3c4bc1 100644 --- a/tiny0/templates/layout.html +++ b/tiny0/templates/layout.html @@ -14,7 +14,7 @@
{% if form %} - {% if not form.url.errors and not form.token.errors %} + {% if not form.errors %}
@@ -38,7 +38,7 @@ {% block body %}{% endblock %} {% if form %} - {% if not form.url.errors and not form.token.errors %} + {% if not form.errors %}