A lot works!

This commit is contained in:
Marijn Jansen
2019-03-21 14:53:41 +01:00
parent ddbf330ab7
commit bfe6ae46fc
11 changed files with 176 additions and 29 deletions

3
.idea/mvl.iml generated
View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="Flask">
<option name="enabled" value="true" />
</component>
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Pipenv (mvl)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Pipenv (mvl)" jdkType="Python SDK" />

69
app.py
View File

@@ -1,22 +1,28 @@
import os import os
from flask import Flask, render_template, jsonify, abort from flask import Flask, render_template, jsonify, abort, send_file, request, redirect
from sqlalchemy import func from sqlalchemy import func
from base64 import standard_b64decode, standard_b64encode
from io import BytesIO
from model import Post, Category, db from model import db, Post, Category, ImageBase64
app = Flask(__name__)
# Check for environment variable # Check for environment variable
if not os.getenv("DATABASE_URL"): env_vars = ["DATABASE_URL", "PASSWORD"]
raise RuntimeError("DATABASE_URL is not set") for env_var in env_vars:
if not os.getenv(env_var):
raise RuntimeError(f"{env_var} is not set")
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("DATABASE_URL") app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("DATABASE_URL")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JSON_SORT_KEYS'] = False app.config['JSON_SORT_KEYS'] = False
# Bind db to application
db.init_app(app) db.init_app(app)
@app.route("/") @app.route("/")
def index(): def index():
posts = Post.get_posts() posts = Post.get_posts()
@@ -25,7 +31,6 @@ def index():
@app.route('/<string:category_name>') @app.route('/<string:category_name>')
def category(category_name): def category(category_name):
category_item = Category.query.filter(func.lower(Category.name) == category_name.replace('_', ' ')).first() category_item = Category.query.filter(func.lower(Category.name) == category_name.replace('_', ' ')).first()
if not category_item: if not category_item:
return abort(404) return abort(404)
@@ -35,6 +40,11 @@ def category(category_name):
return render_template("main/index.html", posts=posts) return render_template("main/index.html", posts=posts)
@app.route('/contact')
def contact():
return render_template("main/contact.html")
@app.route("/api/post/<int:post_id>", methods=["POST"]) @app.route("/api/post/<int:post_id>", methods=["POST"])
def get_post(post_id): def get_post(post_id):
post = Post.query.get(post_id) post = Post.query.get(post_id)
@@ -44,8 +54,47 @@ def get_post(post_id):
return jsonify({"success": False}) return jsonify({"success": False})
@app.route("/login", methods=["GET"]) @app.route('/images/<string:filename>')
def login(): def get_image(filename):
return render_template("adm/login.html") image_b64 = ImageBase64.query.filter(ImageBase64.filename == filename).first()
if not image_b64:
return abort(404)
image = standard_b64decode(image_b64.data)
return send_file(BytesIO(image), mimetype=image_b64.mimetype, attachment_filename=filename)
@app.route('/adm/uploadfile', methods=['POST', 'GET'])
def file_uploaded():
if request.method == 'POST':
if not request.form.get('password') == os.getenv('PASSWORD'):
abort(401)
# check if the post request has the file part
if 'file' not in request.files:
# flash('No file part')
return abort(400)
# return redirect(request.url)
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if file.filename == '':
# flash('No selected file')
return redirect(request.url)
if file:
data = standard_b64encode(file.read()).decode()
print(file.filename, file.mimetype)
print(data)
database_object = ImageBase64(filename=file.filename, mimetype=file.mimetype, data=data)
db.session.add(database_object)
db.session.commit()
# if file and allowed_file(file.filename):
# filename = secure_filename(file.filename)
# file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
# return redirect(url_for('uploaded_file',
# filename=filename))
return render_template("adm/uploadfile.html")
if __name__ == '__main__':
app.app_context().push()
db.create_all()

View File

@@ -26,7 +26,7 @@ class Post(db.Model):
"title": self.title, "title": self.title,
"intro": self.intro, "intro": self.intro,
"description": self.description, "description": self.description,
"images": self.images} "images": [image.uri for image in self.images]}
@staticmethod @staticmethod
def get_posts(): def get_posts():
@@ -44,6 +44,14 @@ class Image(db.Model):
post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=False) post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=False)
class ImageBase64(db.Model):
__tablename__ = "image_store"
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(30), unique=True, nullable=False)
mimetype = db.Column(db.String(127), nullable=False)
data = db.Column(db.Text, nullable=False)
class Category(db.Model): class Category(db.Model):
__tablename__ = "category" __tablename__ = "category"
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

View File

@@ -1,6 +1,10 @@
/* /*
* Start of Custom CSS * Start of Custom CSS
*/ */
.nav-link, .navbar-brand {
font-family: 'Amatic SC', cursive;
font-size: 40px; }
.card-img-overlay { .card-img-overlay {
opacity: 0; } opacity: 0; }
@@ -17,8 +21,11 @@
.card .card-img-overlay .card-title-bg .card-text { .card .card-img-overlay .card-title-bg .card-text {
opacity: 1; } opacity: 1; }
.social-icon {
margin: 10px; }
body { body {
padding-top: 4.5rem; } padding-top: 6.5rem; }
footer { footer {
margin-top: 10px; margin-top: 10px;

View File

@@ -5,6 +5,15 @@
$margin: 20px; $margin: 20px;
$half-margin: $margin / 2; $half-margin: $margin / 2;
// Nav
.nav-link, .navbar-brand {
font-family: 'Amatic SC', cursive;
font-size: 40px;
}
.navbar-brand {
}
// Overlay is hidden // Overlay is hidden
.card-img-overlay { .card-img-overlay {
@@ -36,8 +45,13 @@ $half-margin: $margin / 2;
} }
} }
// Contact page
.social-icon {
margin: $half-margin;
}
body { body {
padding-top: 4.5rem; padding-top: 6.5rem;
} }

View File

@@ -0,0 +1,21 @@
{% extends 'adm/layout.html' %}
{% block main %}
<main class="container">
<h1>Upload new Photo</h1>
<form class="" method=post enctype=multipart/form-data>
<div class="form-group">
<label for=password>Password</label>
<input class="form-control" id=password type=password name=password>
</div>
<div class="form-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="file" name="file">
<label class="custom-file-label" for="file">Choose file</label>
</div>
</div>
<div class="form-group">
<input class="btn btn-primary form-control" type=submit value=Upload>
</div>
</form>
</main>
{% endblock %}

View File

@@ -5,8 +5,13 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet">
<!-- Bootstrap first, then custom style --> <!-- Bootstrap first, then custom style -->
<link rel="stylesheet" href="/static/bootstrap.min.css"> <link rel="stylesheet" href="/static/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css"
integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr"
crossorigin="anonymous">
<link rel="stylesheet" href="/static/style.css"> <link rel="stylesheet" href="/static/style.css">
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %}</title>
@@ -22,6 +27,7 @@
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
</head> </head>
<body> <body>
{% block nav %}{% endblock %} {% block nav %}{% endblock %}

View File

@@ -0,0 +1,22 @@
{% extends "main/layout.html" %}
{% block main %}
<main class="container">
<div class="row justify-content-center">
<div class="col-12 col-md-6 align-content-center">
<h1 class="text-center">Hoi!</h1>
<p>Na het behalen van mijn diploma 'Product Design' van de Hogeschool van Amsterdam in februari 2018 ben
ik gaan werken bij een grafisch ontwerpbureau in Haarlem.
Daarnaast ben ik steeds meer bezig met eigen opdrachten, zoals te zien is in dit portfolio.</p>
<div class="text-center ">
<a href="https://www.linkedin.com/in/marlousvanleeuwen/"><i
class="social-icon fab fa-linkedin-in fa-3x fa-fw text-dark"></i></a>
<a href="mailto:marlousvleeuwen@live.nl"><i
class="social-icon far fa-envelope fa-3x fa-fw text-dark"></i></a>
<a href="https://www.instagram.com/marlousvleeuwen/"><i
class="social-icon fab fa-instagram fa-3x fa-fw text-dark"></i></a>
</div>
</div>
</div>
</main>
{% endblock %}

View File

@@ -12,7 +12,7 @@
data-post_id="{{ post.id }}" data-post_id="{{ post.id }}"
> >
<div class="card border-light"> <div class="card border-light">
<img class="card-img" src="https://placeimg.com/400/400/any" alt="random image"> <img class="card-img" src="{{ post.images[0].uri }}" alt="random image">
<div class="card-img-overlay d-flex align-items-center justify-content-center"> <div class="card-img-overlay d-flex align-items-center justify-content-center">
<div class="card-title-bg bg-dark w-100 d-flex align-items-center justify-content-center"> <div class="card-title-bg bg-dark w-100 d-flex align-items-center justify-content-center">

View File

@@ -1,6 +1,6 @@
<div class="modal fade" id="detailModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" <div class="modal fade" id="detailModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true"> aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title text-center" id="details-title">Modal title</h5> <h5 class="modal-title text-center" id="details-title">Modal title</h5>
@@ -9,7 +9,8 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="details-intro"></div> <div id="details-intro" class="font-weight-bold"></div>
<div id="details-images"></div>
<div id="details-description"></div> <div id="details-description"></div>
</div> </div>
</div> </div>
@@ -19,20 +20,25 @@
<script> <script>
$('#detailModal').on('show.bs.modal', function (event) { $('#detailModal').on('show.bs.modal', function (event) {
let card = $(event.relatedTarget); // Button that triggered the modal let card = $(event.relatedTarget); // Button that triggered the modal
{#let name = card.data('name'); // Extract info from data-* attributes#}
let post_id = card.data('post_id'); let post_id = card.data('post_id');
$.post(`/api/post/${post_id}`, data => { $.post(`/api/post/${post_id}`, data => {
if (!data.success) {
console.error("No data for post"); let post = data.post;
return; console.log(post);
}
let modal = $(this); $('#details-title').text(post.title);
console.log(data); $('#details-intro').text(post.intro);
modal.find('#details-title').text(data.post.title); $('#details-description').text(post.description);
modal.find('#details-intro').text(data.post.intro);
modal.find('#details-description').text(data.post.description); $('#details-images').text('');
post.images.forEach(image => {
$('#details-images').append(`<img src=${image}>`)
});
}); });
}) })
.on('shown.bs.modal', function () {
$('#detailModal').scrollTop(0);
});
</script> </script>

View File

@@ -1,5 +1,5 @@
<nav class="navbar navbar-expand-md navbar-light bg-light fixed-top"> <nav class="navbar navbar-expand-md navbar-light bg-light fixed-top">
<a class="navbar-brand" href="{{ url_for("index") }}">Marlous</a> <a class="navbar-brand font-weight-bold" href="{{ url_for("index") }}">Marlous</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
@@ -8,8 +8,19 @@
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item active"> {% set items = [("Product Design", 'product_design'), ("Grafisch", "grafisch"), ("Fotografie", "fotografie")] %}
<a class="nav-link" href="#">Home<span class="sr-only">(current)</span></a> {% for item in items %}
<li class="nav-item">
<a class="nav-link {% if item[1] == request.path[1:] %}active{% endif %}"
href={{ url_for("category", category_name=item[1]) }}>
{{ item[0] }}
</a>
</li>
{% endfor %}
<li class="nav-item">
{% set name = "contact" %}
<a class="nav-link {% if name == request.path[1:] %}active{% endif %}" href="{{ url_for("contact") }}">Contact</a>
</li> </li>
</ul> </ul>
</div> </div>