├── .dockerignore ├── .editorconfig ├── .github └── workflows │ └── master.yml ├── .gitignore ├── .python-version ├── .tool-versions ├── Dockerfile ├── LICENSE ├── Makefile ├── Procfile ├── README.md ├── codecov.yml ├── docker-compose.yml ├── project ├── __init__.py ├── controllers │ ├── __init__.py │ └── printer.py ├── models │ ├── Printer.py │ └── __init__.py ├── static │ └── css │ │ └── style.css └── templates │ ├── layout │ └── layout.html │ └── printer │ ├── index.html │ └── print.html ├── requirements-test.txt ├── requirements.txt ├── runserver.py ├── script ├── setup └── test └── setup.cfg /.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .cache 3 | .coverage 4 | .coverage.* 5 | .dockerignore 6 | .drone.yml 7 | .git 8 | .gitignore 9 | .Python 10 | .tox 11 | *,cover 12 | *.log 13 | *.pyc 14 | *.pyd 15 | *.pyo 16 | coverage.xml 17 | db.sqlite3 18 | docker-compose.yml 19 | Dockerfile 20 | env 21 | pip-delete-this-directory.txt 22 | pip-log.txt 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | #trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.sh] 12 | indent_style = tabs 13 | indent_size = 4 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | 17 | [*.js,*.json] 18 | indent_style = space 19 | indent_size = 2 20 | trim_trailing_whitespace = true 21 | insert_final_newline = true 22 | 23 | [*.md] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Flask-mvc 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python 3.10 18 | uses: actions/setup-python@v3 19 | with: 20 | python-version: "3.10" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install flake8 pytest 25 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 26 | - name: Lint with flake8 27 | run: | 28 | # stop the build if there are Python syntax errors or undefined names 29 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc 3 | */__pycache__ 4 | *.DS_STORE 5 | config/config.py 6 | !config/config.py.example 7 | .*-venv*/ 8 | data/* 9 | temp/ 10 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.10.11 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | python 3.10.11 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-alpine 2 | MAINTAINER me@salimane.com 3 | 4 | # Set locale to UTF-8 5 | ENV LANG en_US.UTF-8 6 | 7 | RUN apk --no-cache add g++ gcc git jpeg-dev libffi-dev libjpeg libxml2-dev \ 8 | libxslt-dev linux-headers musl-dev openldap-dev openssl postgresql-dev zlib zlib-dev && \ 9 | rm -rf /var/cache/apk/* 10 | 11 | ENV LIBRARY_PATH=/lib:/usr/lib 12 | WORKDIR /opt/flask 13 | RUN pip install -U pip setuptools 14 | RUN pip install -r /opt/flask/requirements.txt 15 | RUN pip install uwsgi 16 | 17 | EXPOSE 16000 18 | CMD ["sh", "-c", "sleep 5 ; uwsgi --http 0.0.0.0:16000 --wsgi-file runserver.py --callable app --processes 8 --threads 2"] 19 | 20 | ARG BUILD_DATE 21 | ARG VCS_REF 22 | ARG VCS_REF_MSG 23 | ARG VCS_URL 24 | ARG VERSION 25 | 26 | LABEL vendor="salimane" \ 27 | name="salimane/flask-mvc" \ 28 | maintainer="me@salimane.com" \ 29 | description="python boilerplate application following MVC pattern using flask micro framework." \ 30 | com.salimane.component.name="flask-mvc" \ 31 | com.salimane.component.build-date="$BUILD_DATE" \ 32 | com.salimane.component.vcs-url="$VCS_URL" \ 33 | com.salimane.component.vcs-ref="$VCS_REF" \ 34 | com.salimane.component.vcs-ref-msg="$VCS_REF_MSG" \ 35 | com.salimane.component.version="$VERSION" \ 36 | com.salimane.component.distribution-scope="public" \ 37 | com.salimane.component.changelog-url="https://github.com/salimane/flask-mvc/releases" \ 38 | com.salimane.component.url="https://github.com/salimane/flask-mvc" \ 39 | com.salimane.component.run="docker run -e ENV_NAME=ENV_VALUE -p 16000:16000/tcp IMAGE" \ 40 | com.salimane.component.environment.required="" \ 41 | com.salimane.component.environment.optional="" \ 42 | com.salimane.component.dockerfile="/usr/src/app/Dockerfile" 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Salimane Adjao Moustapha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all check-version docker-build docker-push docker-run run set-revision clean setup 2 | VERSION := $(strip $(shell [ -d .git ] && git describe --always --tags --dirty)) 3 | BUILD_DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%S%Z") 4 | VCS_URL := $(shell [ -d .git ] && git config --get remote.origin.url) 5 | VCS_REF := $(strip $(shell [ -d .git ] && git rev-parse --short HEAD)) 6 | # IS_TAG := $(shell [ -d .git ] && git describe --exact-match HEAD || echo 'no tags') 7 | VCS_REF_MSG := $(shell if [ "$(IS_TAG)" != "" ]; then git tag -l -n1 $(IS_TAG) | awk '{$$1 = ""; print $$0;}'; else git log --format='%s' -n 1 $(VCS_REF); fi) 8 | 9 | all: docker-build; 10 | 11 | check-version: 12 | $(info $(VERSION)) 13 | ifneq (,$(findstring dirty,$(VERSION))) 14 | $(error Working copy dirty, aborting) 15 | endif 16 | 17 | docker-build: 18 | docker build \ 19 | --build-arg BUILD_DATE="$(BUILD_DATE)" \ 20 | --build-arg VERSION="$(VERSION)" \ 21 | --build-arg VCS_URL="$(VCS_URL)" \ 22 | --build-arg VCS_REF="$(VCS_REF)" \ 23 | --build-arg VCS_REF_MSG="$(VCS_REF_MSG)" \ 24 | --compress \ 25 | -t salimane/flask-mvc:latest . 26 | 27 | docker-push: #check-version 28 | if [ "$(IS_TAG)" != "" ]; then \ 29 | docker tag salimane/flask-mvc:latest salimane/flask-mvc:$(VERSION);\ 30 | fi 31 | docker push salimane/flask-mvc 32 | 33 | docker-run: 34 | docker-compose up -d 35 | 36 | run: 37 | python runserver.py 38 | 39 | set-revision: 40 | echo $(BUILD_DATE) > BUILD_TIME 41 | if [ -d ".git" ]; then echo "$(VERSION)"; fi > BUILD_REVISION 42 | 43 | clean: 44 | find . -name '*venv*' | xargs rm -rf 45 | rm -rf "htmlcov" ".cache" ".coverage" 46 | 47 | setup: 48 | script/setup 49 | 50 | test: 51 | script/test 52 | 53 | flake: 54 | flake8 --config=setup.cfg --statistics --count . 55 | 56 | autopep8: 57 | autopep8 -ira . 58 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python runserver.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flask-mvc 2 | 3 | [](https://github.com/salimane/flask-mvc/actions) 4 | [](https://github.com/salimane/flask-mvc/commits/master) 5 | 6 | 7 | A simple boilerplate application following the MVC pattern using Flask micro python framework. 8 | It basically here to be a base skeleton for new python web applications 9 | 10 | It includes: 11 | 12 | | environment | url | 13 | | --- | --- | 14 | | Demo | http://flask-mvc-salimane.herokuapp.com/ | 15 | 16 | This repository is maintained by [Salimane Adjao Moustapha](https://github.com/salimane). 17 | If you need to make changes to or have any ideas for improvement at this, please send a PR 18 | 19 | ## Prerequisites 20 | 21 | * Git 22 | * A working [Python](https://www.python.org/) 3.10 installation with [virtualenv](https://virtualenv.pypa.io/en/stable/) and [pip](https://pypi.python.org/pypi/pip). 23 | ** 24 | ```shell 25 | # Mac OS X 26 | brew update 27 | brew install asdf pipx 28 | asdf plugin add python 29 | asdf install python 3.10.11 30 | pipx ensurepath 31 | pipx install virtualenv 32 | ``` 33 | 34 | ## Setup 35 | 36 | ```Bash 37 | $ git clone git@github.com:salimane/flask-mvc.git 38 | $ cd flask-mvc 39 | $ asdf local python 3.10.11 40 | $ pipx install virtualenv 41 | $ Run ``make setup`` 42 | ``` 43 | 44 | ## Running 45 | 46 | * Run in console with 47 | ``` 48 | $ make run 49 | ``` 50 | 51 | ## Contributing 52 | 53 | It basically here to be a base skeleton for new python web applications, so we'd love your input! Got a question or an idea? Create an issue or a pull-request. 54 | 55 | ## Maintainers 56 | 57 | * [Salimane Adjao Moustapha - @salimane](https://github.com/salimane) 58 | 59 | ## Copyright Notice 60 | 61 | Copyright (C) 2023 Salimane Adjao Moustapha, authors, and contributors. Licensed under the [MIT License](/LICENSE). 62 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: # files and folders for processing 3 | - .*test/.* 4 | - .*test.py 5 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | postgres: 4 | image: postgres:15.2-alpine 5 | ports: 6 | - 5432:5432 7 | environment: 8 | POSTGRES_DB: flask_dev 9 | POSTGRES_USER: flask 10 | POSTGRES_PASSWORD: flask 11 | -------------------------------------------------------------------------------- /project/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '0.1' 3 | from flask import Flask 4 | from flask_debugtoolbar import DebugToolbarExtension 5 | app = Flask('project') 6 | app.config['SECRET_KEY'] = 'random' 7 | app.debug = True 8 | toolbar = DebugToolbarExtension(app) 9 | from project.controllers import * 10 | -------------------------------------------------------------------------------- /project/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | __all__ = [os.path.basename( 4 | f)[:-3] for f in glob.glob(os.path.dirname(__file__) + "/*.py") if not f.endswith('__init__.py')] 5 | -------------------------------------------------------------------------------- /project/controllers/printer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from project import app 3 | from flask import render_template, request 4 | from flask_wtf import FlaskForm 5 | from wtforms import StringField 6 | from wtforms.validators import DataRequired 7 | 8 | 9 | class CreateForm(FlaskForm): 10 | text = StringField('name', validators=[DataRequired()]) 11 | 12 | 13 | @app.route('/') 14 | def start(): 15 | return render_template('printer/index.html') 16 | 17 | 18 | @app.route('/print', methods=['GET', 'POST']) 19 | def printer(): 20 | form = CreateForm(request.form) 21 | if request.method == 'POST' and form.validate(): 22 | from project.models.Printer import Printer 23 | printer = Printer() 24 | printer.show_string(form.text.data) 25 | return render_template('printer/index.html') 26 | return render_template('printer/print.html', form=form) 27 | -------------------------------------------------------------------------------- /project/models/Printer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from flask import flash 3 | 4 | 5 | class Printer(object): 6 | 7 | def show_string(self, text): 8 | if text == '': 9 | flash("You didn't enter any text to flash") 10 | else: 11 | flash(text + "!!!") 12 | -------------------------------------------------------------------------------- /project/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/salimane/flask-mvc/86d0a3ca14b3cd92a9f0b9810d69677ca064089b/project/models/__init__.py -------------------------------------------------------------------------------- /project/static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | background: #eee; 4 | } 5 | 6 | a,h1,h2 { 7 | color: #377BA8; 8 | } 9 | 10 | h1,h2 { 11 | font-family: 'Georgia', serif; 12 | margin: 0; 13 | } 14 | 15 | h1 { 16 | border-bottom: 2px solid #eee; 17 | } 18 | 19 | h2 { 20 | font-size: 1.2em; 21 | } 22 | 23 | .page { 24 | margin: 2em auto; 25 | width: 35em; 26 | border: 5px solid #ccc; 27 | padding: 0.8em; 28 | background: white; 29 | } 30 | 31 | .entries { 32 | list-style: none; 33 | margin: 0; 34 | padding: 0; 35 | } 36 | 37 | .entries li { 38 | margin: 0.8em 1.2em; 39 | } 40 | 41 | .entries li h2 { 42 | margin-left: -1em; 43 | } 44 | 45 | .add-entry { 46 | font-size: 0.9em; 47 | border-bottom: 1px solid #ccc; 48 | } 49 | 50 | .add-entry dl { 51 | font-weight: bold; 52 | } 53 | 54 | .metanav { 55 | text-align: right; 56 | font-size: 0.8em; 57 | padding: 0.3em; 58 | margin-bottom: 1em; 59 | background: #fafafa; 60 | } 61 | 62 | .flash { 63 | background: #CEE5F5; 64 | padding: 0.5em; 65 | border: 1px solid #AACBE2; 66 | } 67 | 68 | .error { 69 | background: #F0D6D6; 70 | padding: 0.5em; 71 | } -------------------------------------------------------------------------------- /project/templates/layout/layout.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |